blob: 9e92ae22c81ab1f3080c40f073d061dcd1b8a89c [file] [log] [blame]
Lee Thomason (grinliz)28129862012-02-25 21:11:20 -08001/*
2Original code by Lee Thomason (www.grinninglizard.com)
3
4This software is provided 'as-is', without any express or implied
5warranty. In no event will the authors be held liable for any
6damages arising from the use of this software.
7
8Permission is granted to anyone to use this software for any
9purpose, including commercial applications, and to alter it and
10redistribute it freely, subject to the following restrictions:
11
121. The origin of this software must not be misrepresented; you must
13not claim that you wrote the original software. If you use this
14software in a product, an acknowledgment in the product documentation
15would be appreciated but is not required.
16
172. Altered source versions must be plainly marked as such, and
18must not be misrepresented as being the original software.
19
203. This notice may not be removed or altered from any source
21distribution.
22*/
Lee Thomason (grinliz)9c38d132012-02-24 21:50:50 -080023
U-Lama\Lee560bd472011-12-28 19:42:49 -080024#include "tinyxml2.h"
25
Lee Thomason5ce89412012-03-20 13:23:44 -070026#if 1
27 #include <cstdarg>
28 #include <cstdio>
29 #include <cstdlib>
30 #include <new>
31#else
32 #include <string.h>
33 #include <stdlib.h>
34 #include <stdio.h>
35 #include <ctype.h>
36 #include <new>
37 #include <stdarg.h>
38#endif
U-Lama\Lee560bd472011-12-28 19:42:49 -080039
40using namespace tinyxml2;
41
Lee Thomasone4422302012-01-20 17:59:50 -080042static const char LINE_FEED = (char)0x0a; // all line endings are normalized to LF
Lee Thomasonfde6a752012-01-14 18:08:12 -080043static const char LF = LINE_FEED;
44static const char CARRIAGE_RETURN = (char)0x0d; // CR gets filtered out
45static const char CR = CARRIAGE_RETURN;
Lee Thomasone4422302012-01-20 17:59:50 -080046static const char SINGLE_QUOTE = '\'';
47static const char DOUBLE_QUOTE = '\"';
Lee Thomasonfde6a752012-01-14 18:08:12 -080048
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -080049// Bunch of unicode info at:
50// http://www.unicode.org/faq/utf_bom.html
51// ef bb bf (Microsoft "lead bytes") - designates UTF-8
52
53static const unsigned char TIXML_UTF_LEAD_0 = 0xefU;
54static const unsigned char TIXML_UTF_LEAD_1 = 0xbbU;
55static const unsigned char TIXML_UTF_LEAD_2 = 0xbfU;
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -080056
57
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -080058#define DELETE_NODE( node ) { \
59 if ( node ) { \
60 MemPool* pool = node->memPool; \
61 node->~XMLNode(); \
62 pool->Free( node ); \
63 } \
64}
65#define DELETE_ATTRIBUTE( attrib ) { \
66 if ( attrib ) { \
67 MemPool* pool = attrib->memPool; \
68 attrib->~XMLAttribute(); \
69 pool->Free( attrib ); \
70 } \
71}
Lee Thomason43f59302012-02-06 18:18:11 -080072
Lee Thomason8ee79892012-01-25 17:44:30 -080073struct Entity {
74 const char* pattern;
75 int length;
76 char value;
77};
78
79static const int NUM_ENTITIES = 5;
80static const Entity entities[NUM_ENTITIES] =
81{
Lee Thomason18d68bd2012-01-26 18:17:26 -080082 { "quot", 4, DOUBLE_QUOTE },
Lee Thomason8ee79892012-01-25 17:44:30 -080083 { "amp", 3, '&' },
Lee Thomason18d68bd2012-01-26 18:17:26 -080084 { "apos", 4, SINGLE_QUOTE },
Lee Thomason8ee79892012-01-25 17:44:30 -080085 { "lt", 2, '<' },
86 { "gt", 2, '>' }
87};
88
Lee Thomasonfde6a752012-01-14 18:08:12 -080089
Lee Thomason1a1d4a72012-02-15 09:09:25 -080090StrPair::~StrPair()
91{
92 Reset();
93}
94
95
96void StrPair::Reset()
97{
98 if ( flags & NEEDS_DELETE ) {
99 delete [] start;
100 }
101 flags = 0;
102 start = 0;
103 end = 0;
104}
105
106
107void StrPair::SetStr( const char* str, int flags )
108{
109 Reset();
110 size_t len = strlen( str );
111 start = new char[ len+1 ];
U-Stream\Lee09a11c52012-02-17 08:31:16 -0800112 memcpy( start, str, len+1 );
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800113 end = start + len;
114 this->flags = flags | NEEDS_DELETE;
115}
116
117
118char* StrPair::ParseText( char* p, const char* endTag, int strFlags )
119{
120 TIXMLASSERT( endTag && *endTag );
121
122 char* start = p; // fixme: hides a member
123 char endChar = *endTag;
124 int length = strlen( endTag );
125
126 // Inner loop of text parsing.
127 while ( *p ) {
128 if ( *p == endChar && strncmp( p, endTag, length ) == 0 ) {
129 Set( start, p, strFlags );
130 return p + length;
131 }
132 ++p;
133 }
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800134 return 0;
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800135}
136
137
138char* StrPair::ParseName( char* p )
139{
140 char* start = p;
141
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800142 if ( !start || !(*start) ) {
143 return 0;
144 }
145
146 if ( !XMLUtil::IsAlpha( *p ) ) {
147 return 0;
148 }
149
150 while( *p && (
151 XMLUtil::IsAlphaNum( (unsigned char) *p )
152 || *p == '_'
153 || *p == '-'
154 || *p == '.'
155 || *p == ':' ))
156 {
157 ++p;
158 }
159
160 if ( p > start ) {
161 Set( start, p, 0 );
162 return p;
163 }
164 return 0;
165}
166
167
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800168
Lee Thomasone4422302012-01-20 17:59:50 -0800169const char* StrPair::GetStr()
170{
171 if ( flags & NEEDS_FLUSH ) {
172 *end = 0;
Lee Thomason8ee79892012-01-25 17:44:30 -0800173 flags ^= NEEDS_FLUSH;
Lee Thomasone4422302012-01-20 17:59:50 -0800174
Lee Thomason8ee79892012-01-25 17:44:30 -0800175 if ( flags ) {
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800176 char* p = start; // the read pointer
177 char* q = start; // the write pointer
Lee Thomasone4422302012-01-20 17:59:50 -0800178
179 while( p < end ) {
Lee Thomason8ee79892012-01-25 17:44:30 -0800180 if ( (flags & NEEDS_NEWLINE_NORMALIZATION) && *p == CR ) {
Lee Thomasone4422302012-01-20 17:59:50 -0800181 // CR-LF pair becomes LF
182 // CR alone becomes LF
183 // LF-CR becomes LF
184 if ( *(p+1) == LF ) {
185 p += 2;
186 }
187 else {
188 ++p;
189 }
Lee Thomasone9ecdab2012-02-13 18:11:20 -0800190 *q++ = LF;
Lee Thomasone4422302012-01-20 17:59:50 -0800191 }
Lee Thomason8ee79892012-01-25 17:44:30 -0800192 else if ( (flags & NEEDS_NEWLINE_NORMALIZATION) && *p == LF ) {
Lee Thomasone4422302012-01-20 17:59:50 -0800193 if ( *(p+1) == CR ) {
194 p += 2;
195 }
196 else {
197 ++p;
198 }
Lee Thomasone9ecdab2012-02-13 18:11:20 -0800199 *q++ = LF;
Lee Thomasone4422302012-01-20 17:59:50 -0800200 }
Lee Thomason8ee79892012-01-25 17:44:30 -0800201 else if ( (flags & NEEDS_ENTITY_PROCESSING) && *p == '&' ) {
202 int i=0;
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800203
204 // Entities handled by tinyXML2:
205 // - special entities in the entity table [in/out]
206 // - numeric character reference [in]
207 // &#20013; or &#x4e2d;
208
209 if ( *(p+1) == '#' ) {
210 char buf[10] = { 0 };
211 int len;
212 p = const_cast<char*>( XMLUtil::GetCharacterRef( p, buf, &len ) );
213 for( int i=0; i<len; ++i ) {
214 *q++ = buf[i];
Lee Thomason8ee79892012-01-25 17:44:30 -0800215 }
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800216 TIXMLASSERT( q <= p );
Lee Thomason8ee79892012-01-25 17:44:30 -0800217 }
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800218 else {
219 for( i=0; i<NUM_ENTITIES; ++i ) {
220 if ( strncmp( p+1, entities[i].pattern, entities[i].length ) == 0
221 && *(p+entities[i].length+1) == ';' )
222 {
223 // Found an entity convert;
224 *q = entities[i].value;
225 ++q;
226 p += entities[i].length + 2;
227 break;
228 }
229 }
230 if ( i == NUM_ENTITIES ) {
231 // fixme: treat as error?
232 ++p;
233 ++q;
234 }
Lee Thomason8ee79892012-01-25 17:44:30 -0800235 }
236 }
Lee Thomasone4422302012-01-20 17:59:50 -0800237 else {
238 *q = *p;
239 ++p;
Lee Thomasonec975ce2012-01-23 11:42:06 -0800240 ++q;
Lee Thomasone4422302012-01-20 17:59:50 -0800241 }
242 }
Lee Thomason8ee79892012-01-25 17:44:30 -0800243 *q = 0;
Lee Thomasone4422302012-01-20 17:59:50 -0800244 }
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800245 flags = (flags & NEEDS_DELETE);
Lee Thomasone4422302012-01-20 17:59:50 -0800246 }
247 return start;
248}
249
Lee Thomason2c85a712012-01-31 08:24:24 -0800250
Lee Thomasone4422302012-01-20 17:59:50 -0800251
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800252
Lee Thomason56bdd022012-02-09 18:16:58 -0800253// --------- XMLUtil ----------- //
Lee Thomasond1983222012-02-06 08:41:24 -0800254
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800255const char* XMLUtil::ReadBOM( const char* p, bool* bom )
256{
257 *bom = false;
258 const unsigned char* pu = reinterpret_cast<const unsigned char*>(p);
259 // Check for BOM:
260 if ( *(pu+0) == TIXML_UTF_LEAD_0
261 && *(pu+1) == TIXML_UTF_LEAD_1
262 && *(pu+2) == TIXML_UTF_LEAD_2 )
263 {
264 *bom = true;
265 p += 3;
266 }
267 return p;
268}
269
270
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800271void XMLUtil::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length )
272{
273 const unsigned long BYTE_MASK = 0xBF;
274 const unsigned long BYTE_MARK = 0x80;
275 const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
276
277 if (input < 0x80)
278 *length = 1;
279 else if ( input < 0x800 )
280 *length = 2;
281 else if ( input < 0x10000 )
282 *length = 3;
283 else if ( input < 0x200000 )
284 *length = 4;
285 else
286 { *length = 0; return; } // This code won't covert this correctly anyway.
287
288 output += *length;
289
290 // Scary scary fall throughs.
291 switch (*length)
292 {
293 case 4:
294 --output;
295 *output = (char)((input | BYTE_MARK) & BYTE_MASK);
296 input >>= 6;
297 case 3:
298 --output;
299 *output = (char)((input | BYTE_MARK) & BYTE_MASK);
300 input >>= 6;
301 case 2:
302 --output;
303 *output = (char)((input | BYTE_MARK) & BYTE_MASK);
304 input >>= 6;
305 case 1:
306 --output;
307 *output = (char)(input | FIRST_BYTE_MARK[*length]);
308 }
309}
310
311
312const char* XMLUtil::GetCharacterRef( const char* p, char* value, int* length )
313{
314 // Presume an entity, and pull it out.
315 *length = 0;
316
317 if ( *(p+1) == '#' && *(p+2) )
318 {
319 unsigned long ucs = 0;
Lee Thomason (grinliz)7ca55582012-03-07 21:54:57 -0800320 int delta = 0;
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800321 unsigned mult = 1;
322
323 if ( *(p+2) == 'x' )
324 {
325 // Hexadecimal.
326 if ( !*(p+3) ) return 0;
327
328 const char* q = p+3;
329 q = strchr( q, ';' );
330
331 if ( !q || !*q ) return 0;
332
Lee Thomason (grinliz)7ca55582012-03-07 21:54:57 -0800333 delta = (q-p);
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800334 --q;
335
336 while ( *q != 'x' )
337 {
338 if ( *q >= '0' && *q <= '9' )
339 ucs += mult * (*q - '0');
340 else if ( *q >= 'a' && *q <= 'f' )
341 ucs += mult * (*q - 'a' + 10);
342 else if ( *q >= 'A' && *q <= 'F' )
343 ucs += mult * (*q - 'A' + 10 );
344 else
345 return 0;
346 mult *= 16;
347 --q;
348 }
349 }
350 else
351 {
352 // Decimal.
353 if ( !*(p+2) ) return 0;
354
355 const char* q = p+2;
356 q = strchr( q, ';' );
357
358 if ( !q || !*q ) return 0;
359
360 delta = q-p;
361 --q;
362
363 while ( *q != '#' )
364 {
365 if ( *q >= '0' && *q <= '9' )
366 ucs += mult * (*q - '0');
367 else
368 return 0;
369 mult *= 10;
370 --q;
371 }
372 }
373 // convert the UCS to UTF-8
374 ConvertUTF32ToUTF8( ucs, value, length );
375 return p + delta + 1;
376 }
377 return p+1;
378}
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800379
380
Lee Thomasond1983222012-02-06 08:41:24 -0800381char* XMLDocument::Identify( char* p, XMLNode** node )
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800382{
383 XMLNode* returnNode = 0;
Lee Thomason5492a1c2012-01-23 15:32:10 -0800384 char* start = p;
Lee Thomason56bdd022012-02-09 18:16:58 -0800385 p = XMLUtil::SkipWhiteSpace( p );
Lee Thomason5492a1c2012-01-23 15:32:10 -0800386 if( !p || !*p )
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800387 {
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800388 return p;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800389 }
390
391 // What is this thing?
392 // - Elements start with a letter or underscore, but xml is reserved.
393 // - Comments: <!--
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800394 // - Decleration: <?
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800395 // - Everthing else is unknown to tinyxml.
396 //
397
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800398 static const char* xmlHeader = { "<?" };
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800399 static const char* commentHeader = { "<!--" };
400 static const char* dtdHeader = { "<!" };
401 static const char* cdataHeader = { "<![CDATA[" };
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800402 static const char* elementHeader = { "<" }; // and a header for everything else; check last.
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800403
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800404 static const int xmlHeaderLen = 2;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800405 static const int commentHeaderLen = 4;
406 static const int dtdHeaderLen = 2;
407 static const int cdataHeaderLen = 9;
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800408 static const int elementHeaderLen = 1;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800409
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800410#if defined(_MSC_VER)
Lee Thomason (grinliz)9b093cc2012-02-25 21:30:18 -0800411#pragma warning ( push )
412#pragma warning ( disable : 4127 )
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800413#endif
Lee Thomason50f97b22012-02-11 16:33:40 -0800414 TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLUnknown ) ); // use same memory pool
415 TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLDeclaration ) ); // use same memory pool
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800416#if defined(_MSC_VER)
Lee Thomason (grinliz)9b093cc2012-02-25 21:30:18 -0800417#pragma warning (pop)
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800418#endif
Lee Thomason50f97b22012-02-11 16:33:40 -0800419 if ( XMLUtil::StringEqual( p, xmlHeader, xmlHeaderLen ) ) {
420 returnNode = new (commentPool.Alloc()) XMLDeclaration( this );
421 returnNode->memPool = &commentPool;
422 p += xmlHeaderLen;
423 }
424 else if ( XMLUtil::StringEqual( p, commentHeader, commentHeaderLen ) ) {
Lee Thomasond1983222012-02-06 08:41:24 -0800425 returnNode = new (commentPool.Alloc()) XMLComment( this );
426 returnNode->memPool = &commentPool;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800427 p += commentHeaderLen;
428 }
Lee Thomason50f97b22012-02-11 16:33:40 -0800429 else if ( XMLUtil::StringEqual( p, cdataHeader, cdataHeaderLen ) ) {
430 XMLText* text = new (textPool.Alloc()) XMLText( this );
431 returnNode = text;
432 returnNode->memPool = &textPool;
433 p += cdataHeaderLen;
434 text->SetCData( true );
435 }
436 else if ( XMLUtil::StringEqual( p, dtdHeader, dtdHeaderLen ) ) {
437 returnNode = new (commentPool.Alloc()) XMLUnknown( this );
438 returnNode->memPool = &commentPool;
439 p += dtdHeaderLen;
440 }
Lee Thomason56bdd022012-02-09 18:16:58 -0800441 else if ( XMLUtil::StringEqual( p, elementHeader, elementHeaderLen ) ) {
Lee Thomasond1983222012-02-06 08:41:24 -0800442 returnNode = new (elementPool.Alloc()) XMLElement( this );
443 returnNode->memPool = &elementPool;
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800444 p += elementHeaderLen;
445 }
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800446 else {
Lee Thomasond1983222012-02-06 08:41:24 -0800447 returnNode = new (textPool.Alloc()) XMLText( this );
448 returnNode->memPool = &textPool;
Lee Thomason5492a1c2012-01-23 15:32:10 -0800449 p = start; // Back it up, all the text counts.
450 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800451
452 *node = returnNode;
453 return p;
454}
455
456
Lee Thomason751da522012-02-10 08:50:51 -0800457bool XMLDocument::Accept( XMLVisitor* visitor ) const
458{
459 if ( visitor->VisitEnter( *this ) )
460 {
461 for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() )
462 {
463 if ( !node->Accept( visitor ) )
464 break;
465 }
466 }
467 return visitor->VisitExit( *this );
468}
Lee Thomason56bdd022012-02-09 18:16:58 -0800469
470
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800471// --------- XMLNode ----------- //
472
473XMLNode::XMLNode( XMLDocument* doc ) :
474 document( doc ),
475 parent( 0 ),
476 firstChild( 0 ), lastChild( 0 ),
477 prev( 0 ), next( 0 )
478{
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800479}
480
481
482XMLNode::~XMLNode()
483{
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -0800484 DeleteChildren();
Lee Thomason7c913cd2012-01-26 18:32:34 -0800485 if ( parent ) {
486 parent->Unlink( this );
487 }
Lee Thomason18d68bd2012-01-26 18:17:26 -0800488}
489
490
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800491void XMLNode::SetValue( const char* str, bool staticMem )
492{
493 if ( staticMem )
494 value.SetInternedStr( str );
495 else
496 value.SetStr( str );
497}
498
499
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -0800500void XMLNode::DeleteChildren()
Lee Thomason18d68bd2012-01-26 18:17:26 -0800501{
Lee Thomasond923c672012-01-23 08:44:25 -0800502 while( firstChild ) {
503 XMLNode* node = firstChild;
504 Unlink( node );
Lee Thomasond1983222012-02-06 08:41:24 -0800505
Lee Thomason43f59302012-02-06 18:18:11 -0800506 DELETE_NODE( node );
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800507 }
Lee Thomason18d68bd2012-01-26 18:17:26 -0800508 firstChild = lastChild = 0;
Lee Thomasond923c672012-01-23 08:44:25 -0800509}
510
511
512void XMLNode::Unlink( XMLNode* child )
513{
514 TIXMLASSERT( child->parent == this );
515 if ( child == firstChild )
516 firstChild = firstChild->next;
517 if ( child == lastChild )
518 lastChild = lastChild->prev;
519
520 if ( child->prev ) {
521 child->prev->next = child->next;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800522 }
Lee Thomasond923c672012-01-23 08:44:25 -0800523 if ( child->next ) {
524 child->next->prev = child->prev;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800525 }
Lee Thomasond923c672012-01-23 08:44:25 -0800526 child->parent = 0;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800527}
528
529
U-Stream\Leeae25a442012-02-17 17:48:16 -0800530void XMLNode::DeleteChild( XMLNode* node )
531{
532 TIXMLASSERT( node->parent == this );
533 DELETE_NODE( node );
534}
535
536
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800537XMLNode* XMLNode::InsertEndChild( XMLNode* addThis )
538{
539 if ( lastChild ) {
540 TIXMLASSERT( firstChild );
541 TIXMLASSERT( lastChild->next == 0 );
542 lastChild->next = addThis;
543 addThis->prev = lastChild;
544 lastChild = addThis;
545
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800546 addThis->next = 0;
547 }
548 else {
549 TIXMLASSERT( firstChild == 0 );
550 firstChild = lastChild = addThis;
551
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800552 addThis->prev = 0;
553 addThis->next = 0;
554 }
U-Stream\Lee09a11c52012-02-17 08:31:16 -0800555 addThis->parent = this;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800556 return addThis;
557}
558
559
Lee Thomason1ff38e02012-02-14 18:18:16 -0800560XMLNode* XMLNode::InsertFirstChild( XMLNode* addThis )
561{
562 if ( firstChild ) {
563 TIXMLASSERT( lastChild );
564 TIXMLASSERT( firstChild->prev == 0 );
565
566 firstChild->prev = addThis;
567 addThis->next = firstChild;
568 firstChild = addThis;
569
Lee Thomason1ff38e02012-02-14 18:18:16 -0800570 addThis->prev = 0;
571 }
572 else {
573 TIXMLASSERT( lastChild == 0 );
574 firstChild = lastChild = addThis;
575
Lee Thomason1ff38e02012-02-14 18:18:16 -0800576 addThis->prev = 0;
577 addThis->next = 0;
578 }
U-Stream\Lee09a11c52012-02-17 08:31:16 -0800579 addThis->parent = this;
Lee Thomason1ff38e02012-02-14 18:18:16 -0800580 return addThis;
581}
582
583
584XMLNode* XMLNode::InsertAfterChild( XMLNode* afterThis, XMLNode* addThis )
585{
586 TIXMLASSERT( afterThis->parent == this );
587 if ( afterThis->parent != this )
588 return 0;
589
590 if ( afterThis->next == 0 ) {
591 // The last node or the only node.
592 return InsertEndChild( addThis );
593 }
594 addThis->prev = afterThis;
595 addThis->next = afterThis->next;
596 afterThis->next->prev = addThis;
597 afterThis->next = addThis;
U-Stream\Lee09a11c52012-02-17 08:31:16 -0800598 addThis->parent = this;
Lee Thomason1ff38e02012-02-14 18:18:16 -0800599 return addThis;
600}
601
602
603
604
Lee Thomason56bdd022012-02-09 18:16:58 -0800605const XMLElement* XMLNode::FirstChildElement( const char* value ) const
Lee Thomason2c85a712012-01-31 08:24:24 -0800606{
607 for( XMLNode* node=firstChild; node; node=node->next ) {
608 XMLElement* element = node->ToElement();
609 if ( element ) {
Lee Thomason56bdd022012-02-09 18:16:58 -0800610 if ( !value || XMLUtil::StringEqual( element->Name(), value ) ) {
Lee Thomason2c85a712012-01-31 08:24:24 -0800611 return element;
612 }
613 }
614 }
615 return 0;
616}
617
618
Lee Thomason56bdd022012-02-09 18:16:58 -0800619const XMLElement* XMLNode::LastChildElement( const char* value ) const
620{
621 for( XMLNode* node=lastChild; node; node=node->prev ) {
622 XMLElement* element = node->ToElement();
623 if ( element ) {
624 if ( !value || XMLUtil::StringEqual( element->Name(), value ) ) {
625 return element;
626 }
627 }
628 }
629 return 0;
630}
631
632
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800633const XMLElement* XMLNode::NextSiblingElement( const char* value ) const
634{
635 for( XMLNode* element=this->next; element; element = element->next ) {
636 if ( element->ToElement()
637 && (!value || XMLUtil::StringEqual( value, element->Value() )))
638 {
639 return element->ToElement();
640 }
641 }
642 return 0;
643}
644
645
646const XMLElement* XMLNode::PreviousSiblingElement( const char* value ) const
647{
648 for( XMLNode* element=this->prev; element; element = element->prev ) {
649 if ( element->ToElement()
650 && (!value || XMLUtil::StringEqual( value, element->Value() )))
651 {
652 return element->ToElement();
653 }
654 }
655 return 0;
656}
657
658
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800659char* XMLNode::ParseDeep( char* p, StrPair* parentEnd )
Lee Thomason67d61312012-01-24 16:01:51 -0800660{
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800661 // This is a recursive method, but thinking about it "at the current level"
662 // it is a pretty simple flat list:
663 // <foo/>
664 // <!-- comment -->
665 //
666 // With a special case:
667 // <foo>
668 // </foo>
669 // <!-- comment -->
670 //
671 // Where the closing element (/foo) *must* be the next thing after the opening
672 // element, and the names must match. BUT the tricky bit is that the closing
673 // element will be read by the child.
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800674 //
675 // 'endTag' is the end tag for this node, it is returned by a call to a child.
676 // 'parentEnd' is the end tag for the parent, which is filled in and returned.
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800677
Lee Thomason67d61312012-01-24 16:01:51 -0800678 while( p && *p ) {
679 XMLNode* node = 0;
Lee Thomasond6277762012-02-22 16:00:12 -0800680
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800681 p = document->Identify( p, &node );
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800682 if ( p == 0 || node == 0 ) {
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800683 break;
684 }
685
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800686 StrPair endTag;
687 p = node->ParseDeep( p, &endTag );
688 if ( !p ) {
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800689 DELETE_NODE( node );
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800690 node = 0;
Lee Thomason (grinliz)784607f2012-02-24 16:23:40 -0800691 if ( !document->Error() ) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -0700692 document->SetError( XML_ERROR_PARSING, 0, 0 );
Lee Thomason (grinliz)784607f2012-02-24 16:23:40 -0800693 }
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800694 break;
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800695 }
696
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800697 // We read the end tag. Return it to the parent.
698 if ( node->ToElement() && node->ToElement()->ClosingType() == XMLElement::CLOSING ) {
699 if ( parentEnd ) {
700 *parentEnd = ((XMLElement*)node)->value;
Lee Thomason67d61312012-01-24 16:01:51 -0800701 }
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800702 DELETE_NODE( node );
703 return p;
704 }
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800705
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800706 // Handle an end tag returned to this level.
707 // And handle a bunch of annoying errors.
708 XMLElement* ele = node->ToElement();
709 if ( ele ) {
710 if ( endTag.Empty() && ele->ClosingType() == XMLElement::OPEN ) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -0700711 document->SetError( XML_ERROR_MISMATCHED_ELEMENT, node->Value(), 0 );
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800712 p = 0;
713 }
714 else if ( !endTag.Empty() && ele->ClosingType() != XMLElement::OPEN ) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -0700715 document->SetError( XML_ERROR_MISMATCHED_ELEMENT, node->Value(), 0 );
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800716 p = 0;
717 }
718 else if ( !endTag.Empty() ) {
719 if ( !XMLUtil::StringEqual( endTag.GetStr(), node->Value() )) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -0700720 document->SetError( XML_ERROR_MISMATCHED_ELEMENT, node->Value(), 0 );
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800721 p = 0;
722 }
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800723 }
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800724 }
725 if ( p == 0 ) {
726 DELETE_NODE( node );
727 node = 0;
728 }
729 if ( node ) {
730 this->InsertEndChild( node );
Lee Thomason67d61312012-01-24 16:01:51 -0800731 }
732 }
733 return 0;
734}
735
Lee Thomason5492a1c2012-01-23 15:32:10 -0800736// --------- XMLText ---------- //
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800737char* XMLText::ParseDeep( char* p, StrPair* )
Lee Thomason5492a1c2012-01-23 15:32:10 -0800738{
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800739 const char* start = p;
Lee Thomason50f97b22012-02-11 16:33:40 -0800740 if ( this->CData() ) {
741 p = value.ParseText( p, "]]>", StrPair::NEEDS_NEWLINE_NORMALIZATION );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800742 if ( !p ) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -0700743 document->SetError( XML_ERROR_PARSING_CDATA, start, 0 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800744 }
Lee Thomason50f97b22012-02-11 16:33:40 -0800745 return p;
746 }
747 else {
Lee Thomason6f381b72012-03-02 12:59:39 -0800748 p = value.ParseText( p, "<", document->ProcessEntities() ? StrPair::TEXT_ELEMENT : StrPair::TEXT_ELEMENT_LEAVE_ENTITIES );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800749 if ( !p ) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -0700750 document->SetError( XML_ERROR_PARSING_TEXT, start, 0 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800751 }
Lee Thomason50f97b22012-02-11 16:33:40 -0800752 if ( p && *p ) {
753 return p-1;
754 }
Lee Thomason5492a1c2012-01-23 15:32:10 -0800755 }
756 return 0;
757}
758
759
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800760XMLNode* XMLText::ShallowClone( XMLDocument* doc ) const
761{
762 if ( !doc ) {
763 doc = document;
764 }
765 XMLText* text = doc->NewText( Value() ); // fixme: this will always allocate memory. Intern?
766 text->SetCData( this->CData() );
767 return text;
768}
769
770
771bool XMLText::ShallowEqual( const XMLNode* compare ) const
772{
773 return ( compare->ToText() && XMLUtil::StringEqual( compare->ToText()->Value(), Value() ));
774}
775
776
Lee Thomason56bdd022012-02-09 18:16:58 -0800777bool XMLText::Accept( XMLVisitor* visitor ) const
778{
779 return visitor->Visit( *this );
780}
781
782
Lee Thomason3f57d272012-01-11 15:30:03 -0800783// --------- XMLComment ---------- //
784
Lee Thomasone4422302012-01-20 17:59:50 -0800785XMLComment::XMLComment( XMLDocument* doc ) : XMLNode( doc )
Lee Thomason3f57d272012-01-11 15:30:03 -0800786{
787}
788
789
Lee Thomasonce0763e2012-01-11 15:43:54 -0800790XMLComment::~XMLComment()
Lee Thomason3f57d272012-01-11 15:30:03 -0800791{
Lee Thomasond923c672012-01-23 08:44:25 -0800792 //printf( "~XMLComment\n" );
Lee Thomason3f57d272012-01-11 15:30:03 -0800793}
794
795
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800796char* XMLComment::ParseDeep( char* p, StrPair* )
Lee Thomason3f57d272012-01-11 15:30:03 -0800797{
798 // Comment parses as text.
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800799 const char* start = p;
800 p = value.ParseText( p, "-->", StrPair::COMMENT );
801 if ( p == 0 ) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -0700802 document->SetError( XML_ERROR_PARSING_COMMENT, start, 0 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800803 }
804 return p;
U-Lama\Lee4cee6112011-12-31 14:58:18 -0800805}
806
807
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800808XMLNode* XMLComment::ShallowClone( XMLDocument* doc ) const
809{
810 if ( !doc ) {
811 doc = document;
812 }
813 XMLComment* comment = doc->NewComment( Value() ); // fixme: this will always allocate memory. Intern?
814 return comment;
815}
816
817
818bool XMLComment::ShallowEqual( const XMLNode* compare ) const
819{
820 return ( compare->ToComment() && XMLUtil::StringEqual( compare->ToComment()->Value(), Value() ));
821}
822
823
Lee Thomason751da522012-02-10 08:50:51 -0800824bool XMLComment::Accept( XMLVisitor* visitor ) const
825{
826 return visitor->Visit( *this );
827}
Lee Thomason56bdd022012-02-09 18:16:58 -0800828
829
Lee Thomason50f97b22012-02-11 16:33:40 -0800830// --------- XMLDeclaration ---------- //
831
832XMLDeclaration::XMLDeclaration( XMLDocument* doc ) : XMLNode( doc )
833{
834}
835
836
837XMLDeclaration::~XMLDeclaration()
838{
839 //printf( "~XMLDeclaration\n" );
840}
841
842
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800843char* XMLDeclaration::ParseDeep( char* p, StrPair* )
Lee Thomason50f97b22012-02-11 16:33:40 -0800844{
845 // Declaration parses as text.
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800846 const char* start = p;
847 p = value.ParseText( p, "?>", StrPair::NEEDS_NEWLINE_NORMALIZATION );
848 if ( p == 0 ) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -0700849 document->SetError( XML_ERROR_PARSING_DECLARATION, start, 0 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800850 }
851 return p;
Lee Thomason50f97b22012-02-11 16:33:40 -0800852}
853
854
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800855XMLNode* XMLDeclaration::ShallowClone( XMLDocument* doc ) const
856{
857 if ( !doc ) {
858 doc = document;
859 }
860 XMLDeclaration* dec = doc->NewDeclaration( Value() ); // fixme: this will always allocate memory. Intern?
861 return dec;
862}
863
864
865bool XMLDeclaration::ShallowEqual( const XMLNode* compare ) const
866{
867 return ( compare->ToDeclaration() && XMLUtil::StringEqual( compare->ToDeclaration()->Value(), Value() ));
868}
869
870
871
Lee Thomason50f97b22012-02-11 16:33:40 -0800872bool XMLDeclaration::Accept( XMLVisitor* visitor ) const
873{
874 return visitor->Visit( *this );
875}
876
877// --------- XMLUnknown ---------- //
878
879XMLUnknown::XMLUnknown( XMLDocument* doc ) : XMLNode( doc )
880{
881}
882
883
884XMLUnknown::~XMLUnknown()
885{
886}
887
888
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800889char* XMLUnknown::ParseDeep( char* p, StrPair* )
Lee Thomason50f97b22012-02-11 16:33:40 -0800890{
891 // Unknown parses as text.
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800892 const char* start = p;
893
894 p = value.ParseText( p, ">", StrPair::NEEDS_NEWLINE_NORMALIZATION );
895 if ( !p ) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -0700896 document->SetError( XML_ERROR_PARSING_UNKNOWN, start, 0 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800897 }
898 return p;
Lee Thomason50f97b22012-02-11 16:33:40 -0800899}
900
901
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800902XMLNode* XMLUnknown::ShallowClone( XMLDocument* doc ) const
903{
904 if ( !doc ) {
905 doc = document;
906 }
907 XMLUnknown* text = doc->NewUnknown( Value() ); // fixme: this will always allocate memory. Intern?
908 return text;
909}
910
911
912bool XMLUnknown::ShallowEqual( const XMLNode* compare ) const
913{
914 return ( compare->ToUnknown() && XMLUtil::StringEqual( compare->ToUnknown()->Value(), Value() ));
915}
916
917
Lee Thomason50f97b22012-02-11 16:33:40 -0800918bool XMLUnknown::Accept( XMLVisitor* visitor ) const
919{
920 return visitor->Visit( *this );
921}
922
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800923// --------- XMLAttribute ---------- //
Lee Thomason6f381b72012-03-02 12:59:39 -0800924char* XMLAttribute::ParseDeep( char* p, bool processEntities )
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800925{
Lee Thomason56bdd022012-02-09 18:16:58 -0800926 p = name.ParseText( p, "=", StrPair::ATTRIBUTE_NAME );
Lee Thomason22aead12012-01-23 13:29:35 -0800927 if ( !p || !*p ) return 0;
928
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800929 char endTag[2] = { *p, 0 };
930 ++p;
Lee Thomason6f381b72012-03-02 12:59:39 -0800931 p = value.ParseText( p, endTag, processEntities ? StrPair::ATTRIBUTE_VALUE : StrPair::ATTRIBUTE_VALUE_LEAVE_ENTITIES );
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800932 //if ( value.Empty() ) return 0;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800933 return p;
934}
935
936
U-Stream\Lee09a11c52012-02-17 08:31:16 -0800937void XMLAttribute::SetName( const char* n )
938{
939 name.SetStr( n );
940}
941
942
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800943int XMLAttribute::QueryIntValue( int* value ) const
Lee Thomason1ff38e02012-02-14 18:18:16 -0800944{
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800945 if ( TIXML_SSCANF( Value(), "%d", value ) == 1 )
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800946 return XML_NO_ERROR;
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -0700947 return XML_WRONG_ATTRIBUTE_TYPE;
Lee Thomason1ff38e02012-02-14 18:18:16 -0800948}
949
950
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800951int XMLAttribute::QueryUnsignedValue( unsigned int* value ) const
Lee Thomason1ff38e02012-02-14 18:18:16 -0800952{
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800953 if ( TIXML_SSCANF( Value(), "%u", value ) == 1 )
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800954 return XML_NO_ERROR;
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -0700955 return XML_WRONG_ATTRIBUTE_TYPE;
Lee Thomason1ff38e02012-02-14 18:18:16 -0800956}
957
958
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800959int XMLAttribute::QueryBoolValue( bool* value ) const
Lee Thomason1ff38e02012-02-14 18:18:16 -0800960{
961 int ival = -1;
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800962 QueryIntValue( &ival );
Lee Thomason1ff38e02012-02-14 18:18:16 -0800963
964 if ( ival > 0 || XMLUtil::StringEqual( Value(), "true" ) ) {
965 *value = true;
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800966 return XML_NO_ERROR;
Lee Thomason1ff38e02012-02-14 18:18:16 -0800967 }
968 else if ( ival == 0 || XMLUtil::StringEqual( Value(), "false" ) ) {
969 *value = false;
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800970 return XML_NO_ERROR;
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800971 }
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -0700972 return XML_WRONG_ATTRIBUTE_TYPE;
Lee Thomason1ff38e02012-02-14 18:18:16 -0800973}
974
975
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800976int XMLAttribute::QueryDoubleValue( double* value ) const
Lee Thomason1ff38e02012-02-14 18:18:16 -0800977{
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800978 if ( TIXML_SSCANF( Value(), "%lf", value ) == 1 )
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800979 return XML_NO_ERROR;
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -0700980 return XML_WRONG_ATTRIBUTE_TYPE;
Lee Thomason1ff38e02012-02-14 18:18:16 -0800981}
982
983
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800984int XMLAttribute::QueryFloatValue( float* value ) const
Lee Thomason1ff38e02012-02-14 18:18:16 -0800985{
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800986 if ( TIXML_SSCANF( Value(), "%f", value ) == 1 )
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800987 return XML_NO_ERROR;
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -0700988 return XML_WRONG_ATTRIBUTE_TYPE;
Lee Thomason1ff38e02012-02-14 18:18:16 -0800989}
990
991
992void XMLAttribute::SetAttribute( const char* v )
993{
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800994 value.SetStr( v );
Lee Thomason1ff38e02012-02-14 18:18:16 -0800995}
996
997
Lee Thomason1ff38e02012-02-14 18:18:16 -0800998void XMLAttribute::SetAttribute( int v )
999{
1000 char buf[BUF_SIZE];
Lee Thomason1a1d4a72012-02-15 09:09:25 -08001001 TIXML_SNPRINTF( buf, BUF_SIZE-1, "%d", v );
1002 value.SetStr( buf );
Lee Thomason1ff38e02012-02-14 18:18:16 -08001003}
Lee Thomason1a1d4a72012-02-15 09:09:25 -08001004
1005
1006void XMLAttribute::SetAttribute( unsigned v )
1007{
1008 char buf[BUF_SIZE];
1009 TIXML_SNPRINTF( buf, BUF_SIZE-1, "%u", v );
1010 value.SetStr( buf );
1011}
1012
1013
1014void XMLAttribute::SetAttribute( bool v )
1015{
1016 char buf[BUF_SIZE];
1017 TIXML_SNPRINTF( buf, BUF_SIZE-1, "%d", v ? 1 : 0 );
1018 value.SetStr( buf );
1019}
1020
1021void XMLAttribute::SetAttribute( double v )
1022{
1023 char buf[BUF_SIZE];
1024 TIXML_SNPRINTF( buf, BUF_SIZE-1, "%f", v );
1025 value.SetStr( buf );
1026}
1027
1028void XMLAttribute::SetAttribute( float v )
1029{
1030 char buf[BUF_SIZE];
1031 TIXML_SNPRINTF( buf, BUF_SIZE-1, "%f", v );
1032 value.SetStr( buf );
1033}
1034
Lee Thomasondadcdfa2012-01-18 17:55:48 -08001035
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001036// --------- XMLElement ---------- //
1037XMLElement::XMLElement( XMLDocument* doc ) : XMLNode( doc ),
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -08001038 closingType( 0 ),
U-Stream\Lee09a11c52012-02-17 08:31:16 -08001039 rootAttribute( 0 )
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001040{
1041}
1042
1043
1044XMLElement::~XMLElement()
1045{
U-Stream\Lee09a11c52012-02-17 08:31:16 -08001046 while( rootAttribute ) {
1047 XMLAttribute* next = rootAttribute->next;
1048 DELETE_ATTRIBUTE( rootAttribute );
1049 rootAttribute = next;
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001050 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001051}
1052
1053
Lee Thomason1a1d4a72012-02-15 09:09:25 -08001054XMLAttribute* XMLElement::FindAttribute( const char* name )
1055{
1056 XMLAttribute* a = 0;
1057 for( a=rootAttribute; a; a = a->next ) {
1058 if ( XMLUtil::StringEqual( a->Name(), name ) )
1059 return a;
1060 }
1061 return 0;
1062}
1063
1064
1065const XMLAttribute* XMLElement::FindAttribute( const char* name ) const
1066{
1067 XMLAttribute* a = 0;
1068 for( a=rootAttribute; a; a = a->next ) {
1069 if ( XMLUtil::StringEqual( a->Name(), name ) )
1070 return a;
1071 }
1072 return 0;
1073}
1074
1075
Lee Thomason8ba7f7d2012-03-24 13:04:04 -07001076const char* XMLElement::Attribute( const char* name, const char* value ) const
1077{
1078 const XMLAttribute* a = FindAttribute( name );
1079 if ( !a )
1080 return 0;
1081 if ( !value || XMLUtil::StringEqual( a->Value(), value ))
1082 return a->Value();
1083 return 0;
1084}
1085
1086
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001087const char* XMLElement::GetText() const
1088{
1089 if ( FirstChild() && FirstChild()->ToText() ) {
1090 return FirstChild()->ToText()->Value();
1091 }
1092 return 0;
1093}
1094
1095
1096
Lee Thomason1a1d4a72012-02-15 09:09:25 -08001097XMLAttribute* XMLElement::FindOrCreateAttribute( const char* name )
1098{
1099 XMLAttribute* attrib = FindAttribute( name );
1100 if ( !attrib ) {
Lee Thomason (grinliz)9b093cc2012-02-25 21:30:18 -08001101 attrib = new (document->attributePool.Alloc() ) XMLAttribute();
Lee Thomason1a1d4a72012-02-15 09:09:25 -08001102 attrib->memPool = &document->attributePool;
U-Stream\Lee09a11c52012-02-17 08:31:16 -08001103 LinkAttribute( attrib );
1104 attrib->SetName( name );
Lee Thomason1a1d4a72012-02-15 09:09:25 -08001105 }
1106 return attrib;
1107}
1108
1109
U-Stream\Lee09a11c52012-02-17 08:31:16 -08001110void XMLElement::LinkAttribute( XMLAttribute* attrib )
1111{
1112 if ( rootAttribute ) {
1113 XMLAttribute* end = rootAttribute;
1114 while ( end->next )
1115 end = end->next;
1116 end->next = attrib;
1117 }
1118 else {
1119 rootAttribute = attrib;
1120 }
1121}
1122
1123
U-Stream\Leeae25a442012-02-17 17:48:16 -08001124void XMLElement::DeleteAttribute( const char* name )
1125{
1126 XMLAttribute* prev = 0;
1127 for( XMLAttribute* a=rootAttribute; a; a=a->next ) {
1128 if ( XMLUtil::StringEqual( name, a->Name() ) ) {
1129 if ( prev ) {
1130 prev->next = a->next;
1131 }
1132 else {
1133 rootAttribute = a->next;
1134 }
1135 DELETE_ATTRIBUTE( a );
1136 break;
1137 }
1138 prev = a;
1139 }
1140}
1141
1142
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -08001143char* XMLElement::ParseAttributes( char* p )
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001144{
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001145 const char* start = p;
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001146
1147 // Read the attributes.
1148 while( p ) {
Lee Thomason56bdd022012-02-09 18:16:58 -08001149 p = XMLUtil::SkipWhiteSpace( p );
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001150 if ( !p || !(*p) ) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -07001151 document->SetError( XML_ERROR_PARSING_ELEMENT, start, Name() );
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001152 return 0;
1153 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001154
1155 // attribute.
Lee Thomason56bdd022012-02-09 18:16:58 -08001156 if ( XMLUtil::IsAlpha( *p ) ) {
Lee Thomason (grinliz)9b093cc2012-02-25 21:30:18 -08001157 XMLAttribute* attrib = new (document->attributePool.Alloc() ) XMLAttribute();
Lee Thomason43f59302012-02-06 18:18:11 -08001158 attrib->memPool = &document->attributePool;
Lee Thomasond1983222012-02-06 08:41:24 -08001159
Lee Thomason6f381b72012-03-02 12:59:39 -08001160 p = attrib->ParseDeep( p, document->ProcessEntities() );
Lee Thomasond6277762012-02-22 16:00:12 -08001161 if ( !p || Attribute( attrib->Name() ) ) {
Lee Thomason43f59302012-02-06 18:18:11 -08001162 DELETE_ATTRIBUTE( attrib );
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -07001163 document->SetError( XML_ERROR_PARSING_ATTRIBUTE, start, p );
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001164 return 0;
1165 }
U-Stream\Lee09a11c52012-02-17 08:31:16 -08001166 LinkAttribute( attrib );
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001167 }
Lee Thomasone4422302012-01-20 17:59:50 -08001168 // end of the tag
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001169 else if ( *p == '/' && *(p+1) == '>' ) {
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -08001170 closingType = CLOSED;
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001171 return p+2; // done; sealed element.
1172 }
Lee Thomasone4422302012-01-20 17:59:50 -08001173 // end of the tag
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001174 else if ( *p == '>' ) {
1175 ++p;
1176 break;
1177 }
Lee Thomasone4422302012-01-20 17:59:50 -08001178 else {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -07001179 document->SetError( XML_ERROR_PARSING_ELEMENT, start, p );
Lee Thomasone4422302012-01-20 17:59:50 -08001180 return 0;
1181 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001182 }
Lee Thomason67d61312012-01-24 16:01:51 -08001183 return p;
1184}
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001185
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001186
Lee Thomason67d61312012-01-24 16:01:51 -08001187//
1188// <ele></ele>
1189// <ele>foo<b>bar</b></ele>
1190//
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -08001191char* XMLElement::ParseDeep( char* p, StrPair* strPair )
Lee Thomason67d61312012-01-24 16:01:51 -08001192{
1193 // Read the element name.
Lee Thomason56bdd022012-02-09 18:16:58 -08001194 p = XMLUtil::SkipWhiteSpace( p );
Lee Thomason67d61312012-01-24 16:01:51 -08001195 if ( !p ) return 0;
Lee Thomason67d61312012-01-24 16:01:51 -08001196
1197 // The closing element is the </element> form. It is
1198 // parsed just like a regular element then deleted from
1199 // the DOM.
1200 if ( *p == '/' ) {
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -08001201 closingType = CLOSING;
Lee Thomason67d61312012-01-24 16:01:51 -08001202 ++p;
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001203 }
Lee Thomason67d61312012-01-24 16:01:51 -08001204
Lee Thomason56bdd022012-02-09 18:16:58 -08001205 p = value.ParseName( p );
Lee Thomasond1983222012-02-06 08:41:24 -08001206 if ( value.Empty() ) return 0;
Lee Thomason67d61312012-01-24 16:01:51 -08001207
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -08001208 p = ParseAttributes( p );
1209 if ( !p || !*p || closingType )
Lee Thomason67d61312012-01-24 16:01:51 -08001210 return p;
1211
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -08001212 p = XMLNode::ParseDeep( p, strPair );
Lee Thomason67d61312012-01-24 16:01:51 -08001213 return p;
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001214}
1215
1216
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001217
1218XMLNode* XMLElement::ShallowClone( XMLDocument* doc ) const
1219{
1220 if ( !doc ) {
1221 doc = document;
1222 }
1223 XMLElement* element = doc->NewElement( Value() ); // fixme: this will always allocate memory. Intern?
1224 for( const XMLAttribute* a=FirstAttribute(); a; a=a->Next() ) {
1225 element->SetAttribute( a->Name(), a->Value() ); // fixme: this will always allocate memory. Intern?
1226 }
1227 return element;
1228}
1229
1230
1231bool XMLElement::ShallowEqual( const XMLNode* compare ) const
1232{
1233 const XMLElement* other = compare->ToElement();
1234 if ( other && XMLUtil::StringEqual( other->Value(), Value() )) {
1235
1236 const XMLAttribute* a=FirstAttribute();
1237 const XMLAttribute* b=other->FirstAttribute();
1238
1239 while ( a && b ) {
1240 if ( !XMLUtil::StringEqual( a->Value(), b->Value() ) ) {
1241 return false;
1242 }
1243 }
1244 if ( a || b ) {
1245 // different count
1246 return false;
1247 }
1248 return true;
1249 }
1250 return false;
1251}
1252
1253
Lee Thomason751da522012-02-10 08:50:51 -08001254bool XMLElement::Accept( XMLVisitor* visitor ) const
1255{
1256 if ( visitor->VisitEnter( *this, rootAttribute ) )
1257 {
1258 for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() )
1259 {
1260 if ( !node->Accept( visitor ) )
1261 break;
1262 }
1263 }
1264 return visitor->VisitExit( *this );
Lee Thomason751da522012-02-10 08:50:51 -08001265}
Lee Thomason56bdd022012-02-09 18:16:58 -08001266
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001267
Lee Thomason3f57d272012-01-11 15:30:03 -08001268// --------- XMLDocument ----------- //
Lee Thomason6f381b72012-03-02 12:59:39 -08001269XMLDocument::XMLDocument( bool _processEntities ) :
Lee Thomason18d68bd2012-01-26 18:17:26 -08001270 XMLNode( 0 ),
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001271 writeBOM( false ),
Lee Thomason6f381b72012-03-02 12:59:39 -08001272 processEntities( _processEntities ),
1273 errorID( 0 ),
1274 errorStr1( 0 ),
1275 errorStr2( 0 ),
U-Lama\Lee560bd472011-12-28 19:42:49 -08001276 charBuffer( 0 )
1277{
Lee Thomason18d68bd2012-01-26 18:17:26 -08001278 document = this; // avoid warning about 'this' in initializer list
U-Lama\Lee560bd472011-12-28 19:42:49 -08001279}
U-Lama\Leee13c3e62011-12-28 14:36:55 -08001280
1281
Lee Thomason3f57d272012-01-11 15:30:03 -08001282XMLDocument::~XMLDocument()
1283{
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001284 DeleteChildren();
Lee Thomason18d68bd2012-01-26 18:17:26 -08001285 delete [] charBuffer;
Lee Thomasond1983222012-02-06 08:41:24 -08001286
Lee Thomasonec5a7b42012-02-13 18:16:52 -08001287#if 0
Lee Thomason455c9d42012-02-06 09:14:14 -08001288 textPool.Trace( "text" );
1289 elementPool.Trace( "element" );
1290 commentPool.Trace( "comment" );
1291 attributePool.Trace( "attribute" );
Lee Thomasone9ecdab2012-02-13 18:11:20 -08001292#endif
1293
Lee Thomason455c9d42012-02-06 09:14:14 -08001294 TIXMLASSERT( textPool.CurrentAllocs() == 0 );
1295 TIXMLASSERT( elementPool.CurrentAllocs() == 0 );
1296 TIXMLASSERT( commentPool.CurrentAllocs() == 0 );
1297 TIXMLASSERT( attributePool.CurrentAllocs() == 0 );
Lee Thomason3f57d272012-01-11 15:30:03 -08001298}
1299
1300
Lee Thomason18d68bd2012-01-26 18:17:26 -08001301void XMLDocument::InitDocument()
1302{
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001303 errorID = XML_NO_ERROR;
Lee Thomason18d68bd2012-01-26 18:17:26 -08001304 errorStr1 = 0;
1305 errorStr2 = 0;
1306
1307 delete [] charBuffer;
1308 charBuffer = 0;
1309
1310}
1311
Lee Thomason3f57d272012-01-11 15:30:03 -08001312
Lee Thomason2c85a712012-01-31 08:24:24 -08001313XMLElement* XMLDocument::NewElement( const char* name )
1314{
Lee Thomasond1983222012-02-06 08:41:24 -08001315 XMLElement* ele = new (elementPool.Alloc()) XMLElement( this );
1316 ele->memPool = &elementPool;
Lee Thomason2c85a712012-01-31 08:24:24 -08001317 ele->SetName( name );
1318 return ele;
1319}
1320
1321
Lee Thomason1ff38e02012-02-14 18:18:16 -08001322XMLComment* XMLDocument::NewComment( const char* str )
1323{
1324 XMLComment* comment = new (commentPool.Alloc()) XMLComment( this );
1325 comment->memPool = &commentPool;
1326 comment->SetValue( str );
1327 return comment;
1328}
1329
1330
1331XMLText* XMLDocument::NewText( const char* str )
1332{
U-Stream\Lee09a11c52012-02-17 08:31:16 -08001333 XMLText* text = new (textPool.Alloc()) XMLText( this );
1334 text->memPool = &textPool;
1335 text->SetValue( str );
1336 return text;
Lee Thomason1ff38e02012-02-14 18:18:16 -08001337}
1338
1339
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001340XMLDeclaration* XMLDocument::NewDeclaration( const char* str )
1341{
1342 XMLDeclaration* dec = new (commentPool.Alloc()) XMLDeclaration( this );
1343 dec->memPool = &commentPool;
1344 dec->SetValue( str );
1345 return dec;
1346}
1347
1348
1349XMLUnknown* XMLDocument::NewUnknown( const char* str )
1350{
1351 XMLUnknown* unk = new (commentPool.Alloc()) XMLUnknown( this );
1352 unk->memPool = &commentPool;
1353 unk->SetValue( str );
1354 return unk;
1355}
1356
1357
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001358int XMLDocument::LoadFile( const char* filename )
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001359{
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001360 DeleteChildren();
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001361 InitDocument();
1362
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001363#if defined(_MSC_VER)
Lee Thomason (grinliz)5ce4d972012-02-26 21:14:23 -08001364#pragma warning ( push )
1365#pragma warning ( disable : 4996 ) // Fail to see a compelling reason why this should be deprecated.
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001366#endif
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001367 FILE* fp = fopen( filename, "rb" );
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001368#if defined(_MSC_VER)
Lee Thomason (grinliz)5ce4d972012-02-26 21:14:23 -08001369#pragma warning ( pop )
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001370#endif
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001371 if ( !fp ) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -07001372 SetError( XML_ERROR_FILE_NOT_FOUND, filename, 0 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001373 return errorID;
1374 }
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001375 LoadFile( fp );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001376 fclose( fp );
1377 return errorID;
1378}
1379
1380
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001381int XMLDocument::LoadFile( FILE* fp )
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001382{
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001383 DeleteChildren();
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001384 InitDocument();
1385
1386 fseek( fp, 0, SEEK_END );
1387 unsigned size = ftell( fp );
1388 fseek( fp, 0, SEEK_SET );
1389
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001390 if ( size == 0 ) {
1391 return errorID;
1392 }
1393
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001394 charBuffer = new char[size+1];
1395 fread( charBuffer, size, 1, fp );
1396 charBuffer[size] = 0;
1397
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001398 const char* p = charBuffer;
1399 p = XMLUtil::SkipWhiteSpace( p );
1400 p = XMLUtil::ReadBOM( p, &writeBOM );
1401 if ( !p || !*p ) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -07001402 SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
Lee Thomasond6277762012-02-22 16:00:12 -08001403 return errorID;
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001404 }
1405
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -08001406 ParseDeep( charBuffer + (p-charBuffer), 0 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001407 return errorID;
1408}
1409
1410
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001411void XMLDocument::SaveFile( const char* filename )
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001412{
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001413#if defined(_MSC_VER)
Lee Thomason (grinliz)5ce4d972012-02-26 21:14:23 -08001414#pragma warning ( push )
1415#pragma warning ( disable : 4996 ) // Fail to see a compelling reason why this should be deprecated.
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001416#endif
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001417 FILE* fp = fopen( filename, "w" );
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001418#if defined(_MSC_VER)
Lee Thomason (grinliz)5ce4d972012-02-26 21:14:23 -08001419#pragma warning ( pop )
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001420#endif
Lee Thomason7f7b1622012-03-24 12:49:03 -07001421 if ( fp ) {
1422 XMLPrinter stream( fp );
1423 Print( &stream );
1424 fclose( fp );
1425 }
1426 else {
1427 SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, filename, 0 );
1428 }
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001429}
1430
Lee Thomason1ff38e02012-02-14 18:18:16 -08001431
Lee Thomason7c913cd2012-01-26 18:32:34 -08001432int XMLDocument::Parse( const char* p )
Lee Thomason3f57d272012-01-11 15:30:03 -08001433{
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001434 DeleteChildren();
Lee Thomason18d68bd2012-01-26 18:17:26 -08001435 InitDocument();
1436
1437 if ( !p || !*p ) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -07001438 SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
Lee Thomasond6277762012-02-22 16:00:12 -08001439 return errorID;
Lee Thomason18d68bd2012-01-26 18:17:26 -08001440 }
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001441 p = XMLUtil::SkipWhiteSpace( p );
1442 p = XMLUtil::ReadBOM( p, &writeBOM );
1443 if ( !p || !*p ) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -07001444 SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
Lee Thomasond6277762012-02-22 16:00:12 -08001445 return errorID;
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001446 }
1447
Lee Thomason18d68bd2012-01-26 18:17:26 -08001448 size_t len = strlen( p );
1449 charBuffer = new char[ len+1 ];
1450 memcpy( charBuffer, p, len+1 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001451
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001452
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -08001453 ParseDeep( charBuffer, 0 );
Lee Thomason7c913cd2012-01-26 18:32:34 -08001454 return errorID;
Lee Thomason3f57d272012-01-11 15:30:03 -08001455}
1456
1457
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001458void XMLDocument::Print( XMLPrinter* streamer )
Lee Thomason3f57d272012-01-11 15:30:03 -08001459{
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001460 XMLPrinter stdStreamer( stdout );
Lee Thomason5cae8972012-01-24 18:03:07 -08001461 if ( !streamer )
1462 streamer = &stdStreamer;
Lee Thomason751da522012-02-10 08:50:51 -08001463 Accept( streamer );
Lee Thomason3f57d272012-01-11 15:30:03 -08001464}
1465
1466
Lee Thomason67d61312012-01-24 16:01:51 -08001467void XMLDocument::SetError( int error, const char* str1, const char* str2 )
1468{
Lee Thomason18d68bd2012-01-26 18:17:26 -08001469 errorID = error;
Lee Thomason18d68bd2012-01-26 18:17:26 -08001470 errorStr1 = str1;
1471 errorStr2 = str2;
Lee Thomason67d61312012-01-24 16:01:51 -08001472}
1473
Lee Thomason5cae8972012-01-24 18:03:07 -08001474
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001475void XMLDocument::PrintError() const
1476{
1477 if ( errorID ) {
Lee Thomason (grinliz)5ce4d972012-02-26 21:14:23 -08001478 static const int LEN = 20;
1479 char buf1[LEN] = { 0 };
1480 char buf2[LEN] = { 0 };
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001481
1482 if ( errorStr1 ) {
Lee Thomason (grinliz)5ce4d972012-02-26 21:14:23 -08001483 TIXML_SNPRINTF( buf1, LEN, "%s", errorStr1 );
1484 buf1[LEN-1] = 0;
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001485 }
1486 if ( errorStr2 ) {
Lee Thomason (grinliz)5ce4d972012-02-26 21:14:23 -08001487 TIXML_SNPRINTF( buf2, LEN, "%s", errorStr2 );
1488 buf2[LEN-1] = 0;
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001489 }
1490
1491 printf( "XMLDocument error id=%d str1=%s str2=%s\n",
1492 errorID, buf1, buf2 );
1493 }
1494}
1495
1496
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001497XMLPrinter::XMLPrinter( FILE* file ) :
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001498 elementJustOpened( false ),
1499 firstElement( true ),
1500 fp( file ),
1501 depth( 0 ),
Lee Thomason6f381b72012-03-02 12:59:39 -08001502 textDepth( -1 ),
1503 processEntities( true )
Lee Thomason5cae8972012-01-24 18:03:07 -08001504{
Lee Thomason857b8682012-01-25 17:50:25 -08001505 for( int i=0; i<ENTITY_RANGE; ++i ) {
1506 entityFlag[i] = false;
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001507 restrictedEntityFlag[i] = false;
Lee Thomason857b8682012-01-25 17:50:25 -08001508 }
1509 for( int i=0; i<NUM_ENTITIES; ++i ) {
1510 TIXMLASSERT( entities[i].value < ENTITY_RANGE );
1511 if ( entities[i].value < ENTITY_RANGE ) {
Lee Thomason (grinliz)9b093cc2012-02-25 21:30:18 -08001512 entityFlag[ (int)entities[i].value ] = true;
Lee Thomason857b8682012-01-25 17:50:25 -08001513 }
1514 }
Lee Thomason (grinliz)9b093cc2012-02-25 21:30:18 -08001515 restrictedEntityFlag[(int)'&'] = true;
1516 restrictedEntityFlag[(int)'<'] = true;
1517 restrictedEntityFlag[(int)'>'] = true; // not required, but consistency is nice
U-Stream\Leeae25a442012-02-17 17:48:16 -08001518 buffer.Push( 0 );
1519}
1520
1521
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001522void XMLPrinter::Print( const char* format, ... )
U-Stream\Leeae25a442012-02-17 17:48:16 -08001523{
1524 va_list va;
1525 va_start( va, format );
1526
1527 if ( fp ) {
1528 vfprintf( fp, format, va );
1529 }
1530 else {
1531 // This seems brutally complex. Haven't figured out a better
1532 // way on windows.
1533 #ifdef _MSC_VER
1534 int len = -1;
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001535 int expand = 1000;
U-Stream\Leeae25a442012-02-17 17:48:16 -08001536 while ( len < 0 ) {
1537 len = vsnprintf_s( accumulator.Mem(), accumulator.Capacity(), accumulator.Capacity()-1, format, va );
1538 if ( len < 0 ) {
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001539 accumulator.PushArr( expand );
1540 expand *= 3/2;
U-Stream\Leeae25a442012-02-17 17:48:16 -08001541 }
1542 }
1543 char* p = buffer.PushArr( len ) - 1;
1544 memcpy( p, accumulator.Mem(), len+1 );
1545 #else
1546 int len = vsnprintf( 0, 0, format, va );
Lee Thomason4de93472012-03-13 17:33:35 -07001547 // Close out and re-start the va-args
1548 va_end( va );
1549 va_start( va, format );
U-Stream\Leeae25a442012-02-17 17:48:16 -08001550 char* p = buffer.PushArr( len ) - 1;
Lee Thomason (grinliz)9b093cc2012-02-25 21:30:18 -08001551 vsnprintf( p, len+1, format, va );
U-Stream\Leeae25a442012-02-17 17:48:16 -08001552 #endif
1553 }
1554 va_end( va );
Lee Thomason5cae8972012-01-24 18:03:07 -08001555}
1556
1557
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001558void XMLPrinter::PrintSpace( int depth )
Lee Thomason5cae8972012-01-24 18:03:07 -08001559{
1560 for( int i=0; i<depth; ++i ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001561 Print( " " );
Lee Thomason5cae8972012-01-24 18:03:07 -08001562 }
1563}
1564
1565
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001566void XMLPrinter::PrintString( const char* p, bool restricted )
Lee Thomason857b8682012-01-25 17:50:25 -08001567{
Lee Thomason951d8832012-01-26 08:47:06 -08001568 // Look for runs of bytes between entities to print.
1569 const char* q = p;
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001570 const bool* flag = restricted ? restrictedEntityFlag : entityFlag;
Lee Thomason857b8682012-01-25 17:50:25 -08001571
Lee Thomason6f381b72012-03-02 12:59:39 -08001572 if ( processEntities ) {
1573 while ( *q ) {
1574 // Remember, char is sometimes signed. (How many times has that bitten me?)
1575 if ( *q > 0 && *q < ENTITY_RANGE ) {
1576 // Check for entities. If one is found, flush
1577 // the stream up until the entity, write the
1578 // entity, and keep looking.
Lee Thomason (grinliz)8a0975d2012-03-31 20:09:20 -07001579 if ( flag[(unsigned)(*q)] ) {
Lee Thomason6f381b72012-03-02 12:59:39 -08001580 while ( p < q ) {
1581 Print( "%c", *p );
1582 ++p;
1583 }
1584 for( int i=0; i<NUM_ENTITIES; ++i ) {
1585 if ( entities[i].value == *q ) {
1586 Print( "&%s;", entities[i].pattern );
1587 break;
1588 }
1589 }
Lee Thomason951d8832012-01-26 08:47:06 -08001590 ++p;
1591 }
Lee Thomason951d8832012-01-26 08:47:06 -08001592 }
Lee Thomason6f381b72012-03-02 12:59:39 -08001593 ++q;
Lee Thomason951d8832012-01-26 08:47:06 -08001594 }
Lee Thomason951d8832012-01-26 08:47:06 -08001595 }
1596 // Flush the remaining string. This will be the entire
1597 // string if an entity wasn't found.
Lee Thomason6f381b72012-03-02 12:59:39 -08001598 if ( !processEntities || (q-p > 0) ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001599 Print( "%s", p );
Lee Thomason951d8832012-01-26 08:47:06 -08001600 }
Lee Thomason857b8682012-01-25 17:50:25 -08001601}
1602
U-Stream\Leeae25a442012-02-17 17:48:16 -08001603
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001604void XMLPrinter::PushHeader( bool writeBOM, bool writeDec )
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001605{
1606 static const unsigned char bom[] = { TIXML_UTF_LEAD_0, TIXML_UTF_LEAD_1, TIXML_UTF_LEAD_2, 0 };
1607 if ( writeBOM ) {
1608 Print( "%s", bom );
1609 }
1610 if ( writeDec ) {
1611 PushDeclaration( "xml version=\"1.0\"" );
1612 }
1613}
1614
1615
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001616void XMLPrinter::OpenElement( const char* name )
Lee Thomason5cae8972012-01-24 18:03:07 -08001617{
1618 if ( elementJustOpened ) {
1619 SealElement();
1620 }
Lee Thomason56bdd022012-02-09 18:16:58 -08001621 stack.Push( name );
1622
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001623 if ( textDepth < 0 && !firstElement ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001624 Print( "\n" );
Lee Thomason24767b02012-01-25 17:16:23 -08001625 PrintSpace( depth );
1626 }
Lee Thomason5cae8972012-01-24 18:03:07 -08001627
U-Stream\Leeae25a442012-02-17 17:48:16 -08001628 Print( "<%s", name );
Lee Thomason5cae8972012-01-24 18:03:07 -08001629 elementJustOpened = true;
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001630 firstElement = false;
Lee Thomason5cae8972012-01-24 18:03:07 -08001631 ++depth;
1632}
1633
1634
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001635void XMLPrinter::PushAttribute( const char* name, const char* value )
Lee Thomason5cae8972012-01-24 18:03:07 -08001636{
1637 TIXMLASSERT( elementJustOpened );
U-Stream\Leeae25a442012-02-17 17:48:16 -08001638 Print( " %s=\"", name );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001639 PrintString( value, false );
U-Stream\Leeae25a442012-02-17 17:48:16 -08001640 Print( "\"" );
Lee Thomason5cae8972012-01-24 18:03:07 -08001641}
1642
1643
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001644void XMLPrinter::PushAttribute( const char* name, int v )
1645{
1646 char buf[BUF_SIZE];
1647 TIXML_SNPRINTF( buf, BUF_SIZE-1, "%d", v );
1648 PushAttribute( name, buf );
1649}
1650
1651
1652void XMLPrinter::PushAttribute( const char* name, unsigned v )
1653{
1654 char buf[BUF_SIZE];
1655 TIXML_SNPRINTF( buf, BUF_SIZE-1, "%u", v );
1656 PushAttribute( name, buf );
1657}
1658
1659
1660void XMLPrinter::PushAttribute( const char* name, bool v )
1661{
1662 char buf[BUF_SIZE];
1663 TIXML_SNPRINTF( buf, BUF_SIZE-1, "%d", v ? 1 : 0 );
1664 PushAttribute( name, buf );
1665}
1666
1667
1668void XMLPrinter::PushAttribute( const char* name, double v )
1669{
1670 char buf[BUF_SIZE];
1671 TIXML_SNPRINTF( buf, BUF_SIZE-1, "%f", v );
1672 PushAttribute( name, buf );
1673}
1674
1675
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001676void XMLPrinter::CloseElement()
Lee Thomason5cae8972012-01-24 18:03:07 -08001677{
1678 --depth;
1679 const char* name = stack.Pop();
Lee Thomason5cae8972012-01-24 18:03:07 -08001680
1681 if ( elementJustOpened ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001682 Print( "/>" );
Lee Thomason5cae8972012-01-24 18:03:07 -08001683 }
1684 else {
Lee Thomason56bdd022012-02-09 18:16:58 -08001685 if ( textDepth < 0 ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001686 Print( "\n" );
Lee Thomason24767b02012-01-25 17:16:23 -08001687 PrintSpace( depth );
1688 }
U-Stream\Leeae25a442012-02-17 17:48:16 -08001689 Print( "</%s>", name );
Lee Thomason5cae8972012-01-24 18:03:07 -08001690 }
Lee Thomason56bdd022012-02-09 18:16:58 -08001691
1692 if ( textDepth == depth )
1693 textDepth = -1;
1694 if ( depth == 0 )
U-Stream\Leeae25a442012-02-17 17:48:16 -08001695 Print( "\n" );
Lee Thomason5cae8972012-01-24 18:03:07 -08001696 elementJustOpened = false;
1697}
1698
1699
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001700void XMLPrinter::SealElement()
Lee Thomason5cae8972012-01-24 18:03:07 -08001701{
1702 elementJustOpened = false;
U-Stream\Leeae25a442012-02-17 17:48:16 -08001703 Print( ">" );
Lee Thomason5cae8972012-01-24 18:03:07 -08001704}
1705
1706
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001707void XMLPrinter::PushText( const char* text, bool cdata )
Lee Thomason5cae8972012-01-24 18:03:07 -08001708{
Lee Thomason56bdd022012-02-09 18:16:58 -08001709 textDepth = depth-1;
1710
Lee Thomason5cae8972012-01-24 18:03:07 -08001711 if ( elementJustOpened ) {
1712 SealElement();
1713 }
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001714 if ( cdata ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001715 Print( "<![CDATA[" );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001716 Print( "%s", text );
U-Stream\Leeae25a442012-02-17 17:48:16 -08001717 Print( "]]>" );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001718 }
1719 else {
1720 PrintString( text, true );
1721 }
Lee Thomason5cae8972012-01-24 18:03:07 -08001722}
1723
1724
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001725void XMLPrinter::PushComment( const char* comment )
Lee Thomason5cae8972012-01-24 18:03:07 -08001726{
1727 if ( elementJustOpened ) {
1728 SealElement();
1729 }
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001730 if ( textDepth < 0 && !firstElement ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001731 Print( "\n" );
U-Stream\Lee09a11c52012-02-17 08:31:16 -08001732 PrintSpace( depth );
1733 }
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001734 firstElement = false;
U-Stream\Leeae25a442012-02-17 17:48:16 -08001735 Print( "<!--%s-->", comment );
Lee Thomason5cae8972012-01-24 18:03:07 -08001736}
Lee Thomason751da522012-02-10 08:50:51 -08001737
1738
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001739void XMLPrinter::PushDeclaration( const char* value )
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001740{
1741 if ( elementJustOpened ) {
1742 SealElement();
1743 }
1744 if ( textDepth < 0 && !firstElement) {
1745 Print( "\n" );
1746 PrintSpace( depth );
1747 }
1748 firstElement = false;
1749 Print( "<?%s?>", value );
1750}
1751
1752
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001753void XMLPrinter::PushUnknown( const char* value )
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001754{
1755 if ( elementJustOpened ) {
1756 SealElement();
1757 }
1758 if ( textDepth < 0 && !firstElement ) {
1759 Print( "\n" );
1760 PrintSpace( depth );
1761 }
1762 firstElement = false;
1763 Print( "<!%s>", value );
1764}
1765
1766
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001767bool XMLPrinter::VisitEnter( const XMLDocument& doc )
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001768{
Lee Thomason6f381b72012-03-02 12:59:39 -08001769 processEntities = doc.ProcessEntities();
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001770 if ( doc.HasBOM() ) {
1771 PushHeader( true, false );
1772 }
1773 return true;
1774}
1775
1776
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001777bool XMLPrinter::VisitEnter( const XMLElement& element, const XMLAttribute* attribute )
Lee Thomason751da522012-02-10 08:50:51 -08001778{
1779 OpenElement( element.Name() );
1780 while ( attribute ) {
1781 PushAttribute( attribute->Name(), attribute->Value() );
1782 attribute = attribute->Next();
1783 }
1784 return true;
1785}
1786
1787
Lee Thomason (grinliz)9b093cc2012-02-25 21:30:18 -08001788bool XMLPrinter::VisitExit( const XMLElement& )
Lee Thomason751da522012-02-10 08:50:51 -08001789{
1790 CloseElement();
1791 return true;
1792}
1793
1794
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001795bool XMLPrinter::Visit( const XMLText& text )
Lee Thomason751da522012-02-10 08:50:51 -08001796{
Lee Thomasond6277762012-02-22 16:00:12 -08001797 PushText( text.Value(), text.CData() );
Lee Thomason751da522012-02-10 08:50:51 -08001798 return true;
1799}
1800
1801
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001802bool XMLPrinter::Visit( const XMLComment& comment )
Lee Thomason751da522012-02-10 08:50:51 -08001803{
1804 PushComment( comment.Value() );
1805 return true;
1806}
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001807
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001808bool XMLPrinter::Visit( const XMLDeclaration& declaration )
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001809{
1810 PushDeclaration( declaration.Value() );
1811 return true;
1812}
1813
1814
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001815bool XMLPrinter::Visit( const XMLUnknown& unknown )
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001816{
1817 PushUnknown( unknown.Value() );
1818 return true;
1819}