Merge branch 'ntfs_rebase'
[reactos.git] / sdk / tools / xml2sdb / tinyxml2.cpp
1 /*
2 Original code by Lee Thomason (www.grinninglizard.com)
3
4 This software is provided 'as-is', without any express or implied
5 warranty. In no event will the authors be held liable for any
6 damages arising from the use of this software.
7
8 Permission is granted to anyone to use this software for any
9 purpose, including commercial applications, and to alter it and
10 redistribute it freely, subject to the following restrictions:
11
12 1. The origin of this software must not be misrepresented; you must
13 not claim that you wrote the original software. If you use this
14 software in a product, an acknowledgment in the product documentation
15 would be appreciated but is not required.
16
17 2. Altered source versions must be plainly marked as such, and
18 must not be misrepresented as being the original software.
19
20 3. This notice may not be removed or altered from any source
21 distribution.
22 */
23
24 #include "tinyxml2.h"
25
26 #include <new> // yes, this one new style header, is in the Android SDK.
27 #if defined(ANDROID_NDK) || defined(__BORLANDC__) || defined(__QNXNTO__)
28 # include <stddef.h>
29 # include <stdarg.h>
30 #else
31 # include <cstddef>
32 # include <cstdarg>
33 #endif
34
35 #if defined(_MSC_VER) && (_MSC_VER >= 1400 ) && (!defined WINCE)
36 // Microsoft Visual Studio, version 2005 and higher. Not WinCE.
37 /*int _snprintf_s(
38 char *buffer,
39 size_t sizeOfBuffer,
40 size_t count,
41 const char *format [,
42 argument] ...
43 );*/
44 static inline int TIXML_SNPRINTF( char* buffer, size_t size, const char* format, ... )
45 {
46 va_list va;
47 va_start( va, format );
48 int result = vsnprintf_s( buffer, size, _TRUNCATE, format, va );
49 va_end( va );
50 return result;
51 }
52
53 static inline int TIXML_VSNPRINTF( char* buffer, size_t size, const char* format, va_list va )
54 {
55 int result = vsnprintf_s( buffer, size, _TRUNCATE, format, va );
56 return result;
57 }
58
59 #define TIXML_VSCPRINTF _vscprintf
60 #define TIXML_SSCANF sscanf_s
61 #elif defined _MSC_VER
62 // Microsoft Visual Studio 2003 and earlier or WinCE
63 #define TIXML_SNPRINTF _snprintf
64 #define TIXML_VSNPRINTF _vsnprintf
65 #define TIXML_SSCANF sscanf
66 #if (_MSC_VER < 1400 ) && (!defined WINCE)
67 // Microsoft Visual Studio 2003 and not WinCE.
68 #define TIXML_VSCPRINTF _vscprintf // VS2003's C runtime has this, but VC6 C runtime or WinCE SDK doesn't have.
69 #else
70 // Microsoft Visual Studio 2003 and earlier or WinCE.
71 static inline int TIXML_VSCPRINTF( const char* format, va_list va )
72 {
73 int len = 512;
74 for (;;) {
75 len = len*2;
76 char* str = new char[len]();
77 const int required = _vsnprintf(str, len, format, va);
78 delete[] str;
79 if ( required != -1 ) {
80 TIXMLASSERT( required >= 0 );
81 len = required;
82 break;
83 }
84 }
85 TIXMLASSERT( len >= 0 );
86 return len;
87 }
88 #endif
89 #else
90 // GCC version 3 and higher
91 //#warning( "Using sn* functions." )
92 #define TIXML_SNPRINTF snprintf
93 #define TIXML_VSNPRINTF vsnprintf
94 static inline int TIXML_VSCPRINTF( const char* format, va_list va )
95 {
96 int len = vsnprintf( 0, 0, format, va );
97 TIXMLASSERT( len >= 0 );
98 return len;
99 }
100 #define TIXML_SSCANF sscanf
101 #endif
102
103
104 static const char LINE_FEED = (char)0x0a; // all line endings are normalized to LF
105 static const char LF = LINE_FEED;
106 static const char CARRIAGE_RETURN = (char)0x0d; // CR gets filtered out
107 static const char CR = CARRIAGE_RETURN;
108 static const char SINGLE_QUOTE = '\'';
109 static const char DOUBLE_QUOTE = '\"';
110
111 // Bunch of unicode info at:
112 // http://www.unicode.org/faq/utf_bom.html
113 // ef bb bf (Microsoft "lead bytes") - designates UTF-8
114
115 static const unsigned char TIXML_UTF_LEAD_0 = 0xefU;
116 static const unsigned char TIXML_UTF_LEAD_1 = 0xbbU;
117 static const unsigned char TIXML_UTF_LEAD_2 = 0xbfU;
118
119 namespace tinyxml2
120 {
121
122 struct Entity {
123 const char* pattern;
124 int length;
125 char value;
126 };
127
128 static const int NUM_ENTITIES = 5;
129 static const Entity entities[NUM_ENTITIES] = {
130 { "quot", 4, DOUBLE_QUOTE },
131 { "amp", 3, '&' },
132 { "apos", 4, SINGLE_QUOTE },
133 { "lt", 2, '<' },
134 { "gt", 2, '>' }
135 };
136
137
138 StrPair::~StrPair()
139 {
140 Reset();
141 }
142
143
144 void StrPair::TransferTo( StrPair* other )
145 {
146 if ( this == other ) {
147 return;
148 }
149 // This in effect implements the assignment operator by "moving"
150 // ownership (as in auto_ptr).
151
152 TIXMLASSERT( other->_flags == 0 );
153 TIXMLASSERT( other->_start == 0 );
154 TIXMLASSERT( other->_end == 0 );
155
156 other->Reset();
157
158 other->_flags = _flags;
159 other->_start = _start;
160 other->_end = _end;
161
162 _flags = 0;
163 _start = 0;
164 _end = 0;
165 }
166
167 void StrPair::Reset()
168 {
169 if ( _flags & NEEDS_DELETE ) {
170 delete [] _start;
171 }
172 _flags = 0;
173 _start = 0;
174 _end = 0;
175 }
176
177
178 void StrPair::SetStr( const char* str, int flags )
179 {
180 TIXMLASSERT( str );
181 Reset();
182 size_t len = strlen( str );
183 TIXMLASSERT( _start == 0 );
184 _start = new char[ len+1 ];
185 memcpy( _start, str, len+1 );
186 _end = _start + len;
187 _flags = flags | NEEDS_DELETE;
188 }
189
190
191 char* StrPair::ParseText( char* p, const char* endTag, int strFlags )
192 {
193 TIXMLASSERT( endTag && *endTag );
194
195 char* start = p;
196 char endChar = *endTag;
197 size_t length = strlen( endTag );
198
199 // Inner loop of text parsing.
200 while ( *p ) {
201 if ( *p == endChar && strncmp( p, endTag, length ) == 0 ) {
202 Set( start, p, strFlags );
203 return p + length;
204 }
205 ++p;
206 }
207 return 0;
208 }
209
210
211 char* StrPair::ParseName( char* p )
212 {
213 if ( !p || !(*p) ) {
214 return 0;
215 }
216 if ( !XMLUtil::IsNameStartChar( *p ) ) {
217 return 0;
218 }
219
220 char* const start = p;
221 ++p;
222 while ( *p && XMLUtil::IsNameChar( *p ) ) {
223 ++p;
224 }
225
226 Set( start, p, 0 );
227 return p;
228 }
229
230
231 void StrPair::CollapseWhitespace()
232 {
233 // Adjusting _start would cause undefined behavior on delete[]
234 TIXMLASSERT( ( _flags & NEEDS_DELETE ) == 0 );
235 // Trim leading space.
236 _start = XMLUtil::SkipWhiteSpace( _start );
237
238 if ( *_start ) {
239 char* p = _start; // the read pointer
240 char* q = _start; // the write pointer
241
242 while( *p ) {
243 if ( XMLUtil::IsWhiteSpace( *p )) {
244 p = XMLUtil::SkipWhiteSpace( p );
245 if ( *p == 0 ) {
246 break; // don't write to q; this trims the trailing space.
247 }
248 *q = ' ';
249 ++q;
250 }
251 *q = *p;
252 ++q;
253 ++p;
254 }
255 *q = 0;
256 }
257 }
258
259
260 const char* StrPair::GetStr()
261 {
262 TIXMLASSERT( _start );
263 TIXMLASSERT( _end );
264 if ( _flags & NEEDS_FLUSH ) {
265 *_end = 0;
266 _flags ^= NEEDS_FLUSH;
267
268 if ( _flags ) {
269 char* p = _start; // the read pointer
270 char* q = _start; // the write pointer
271
272 while( p < _end ) {
273 if ( (_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == CR ) {
274 // CR-LF pair becomes LF
275 // CR alone becomes LF
276 // LF-CR becomes LF
277 if ( *(p+1) == LF ) {
278 p += 2;
279 }
280 else {
281 ++p;
282 }
283 *q++ = LF;
284 }
285 else if ( (_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == LF ) {
286 if ( *(p+1) == CR ) {
287 p += 2;
288 }
289 else {
290 ++p;
291 }
292 *q++ = LF;
293 }
294 else if ( (_flags & NEEDS_ENTITY_PROCESSING) && *p == '&' ) {
295 // Entities handled by tinyXML2:
296 // - special entities in the entity table [in/out]
297 // - numeric character reference [in]
298 // &#20013; or &#x4e2d;
299
300 if ( *(p+1) == '#' ) {
301 const int buflen = 10;
302 char buf[buflen] = { 0 };
303 int len = 0;
304 char* adjusted = const_cast<char*>( XMLUtil::GetCharacterRef( p, buf, &len ) );
305 if ( adjusted == 0 ) {
306 *q = *p;
307 ++p;
308 ++q;
309 }
310 else {
311 TIXMLASSERT( 0 <= len && len <= buflen );
312 TIXMLASSERT( q + len <= adjusted );
313 p = adjusted;
314 memcpy( q, buf, len );
315 q += len;
316 }
317 }
318 else {
319 bool entityFound = false;
320 for( int i = 0; i < NUM_ENTITIES; ++i ) {
321 const Entity& entity = entities[i];
322 if ( strncmp( p + 1, entity.pattern, entity.length ) == 0
323 && *( p + entity.length + 1 ) == ';' ) {
324 // Found an entity - convert.
325 *q = entity.value;
326 ++q;
327 p += entity.length + 2;
328 entityFound = true;
329 break;
330 }
331 }
332 if ( !entityFound ) {
333 // fixme: treat as error?
334 ++p;
335 ++q;
336 }
337 }
338 }
339 else {
340 *q = *p;
341 ++p;
342 ++q;
343 }
344 }
345 *q = 0;
346 }
347 // The loop below has plenty going on, and this
348 // is a less useful mode. Break it out.
349 if ( _flags & NEEDS_WHITESPACE_COLLAPSING ) {
350 CollapseWhitespace();
351 }
352 _flags = (_flags & NEEDS_DELETE);
353 }
354 TIXMLASSERT( _start );
355 return _start;
356 }
357
358
359
360
361 // --------- XMLUtil ----------- //
362
363 const char* XMLUtil::ReadBOM( const char* p, bool* bom )
364 {
365 TIXMLASSERT( p );
366 TIXMLASSERT( bom );
367 *bom = false;
368 const unsigned char* pu = reinterpret_cast<const unsigned char*>(p);
369 // Check for BOM:
370 if ( *(pu+0) == TIXML_UTF_LEAD_0
371 && *(pu+1) == TIXML_UTF_LEAD_1
372 && *(pu+2) == TIXML_UTF_LEAD_2 ) {
373 *bom = true;
374 p += 3;
375 }
376 TIXMLASSERT( p );
377 return p;
378 }
379
380
381 void XMLUtil::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length )
382 {
383 const unsigned long BYTE_MASK = 0xBF;
384 const unsigned long BYTE_MARK = 0x80;
385 const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
386
387 if (input < 0x80) {
388 *length = 1;
389 }
390 else if ( input < 0x800 ) {
391 *length = 2;
392 }
393 else if ( input < 0x10000 ) {
394 *length = 3;
395 }
396 else if ( input < 0x200000 ) {
397 *length = 4;
398 }
399 else {
400 *length = 0; // This code won't convert this correctly anyway.
401 return;
402 }
403
404 output += *length;
405
406 // Scary scary fall throughs.
407 switch (*length) {
408 case 4:
409 --output;
410 *output = (char)((input | BYTE_MARK) & BYTE_MASK);
411 input >>= 6;
412 case 3:
413 --output;
414 *output = (char)((input | BYTE_MARK) & BYTE_MASK);
415 input >>= 6;
416 case 2:
417 --output;
418 *output = (char)((input | BYTE_MARK) & BYTE_MASK);
419 input >>= 6;
420 case 1:
421 --output;
422 *output = (char)(input | FIRST_BYTE_MARK[*length]);
423 break;
424 default:
425 TIXMLASSERT( false );
426 }
427 }
428
429
430 const char* XMLUtil::GetCharacterRef( const char* p, char* value, int* length )
431 {
432 // Presume an entity, and pull it out.
433 *length = 0;
434
435 if ( *(p+1) == '#' && *(p+2) ) {
436 unsigned long ucs = 0;
437 TIXMLASSERT( sizeof( ucs ) >= 4 );
438 ptrdiff_t delta = 0;
439 unsigned mult = 1;
440 static const char SEMICOLON = ';';
441
442 if ( *(p+2) == 'x' ) {
443 // Hexadecimal.
444 const char* q = p+3;
445 if ( !(*q) ) {
446 return 0;
447 }
448
449 q = strchr( q, SEMICOLON );
450
451 if ( !q ) {
452 return 0;
453 }
454 TIXMLASSERT( *q == SEMICOLON );
455
456 delta = q-p;
457 --q;
458
459 while ( *q != 'x' ) {
460 unsigned int digit = 0;
461
462 if ( *q >= '0' && *q <= '9' ) {
463 digit = *q - '0';
464 }
465 else if ( *q >= 'a' && *q <= 'f' ) {
466 digit = *q - 'a' + 10;
467 }
468 else if ( *q >= 'A' && *q <= 'F' ) {
469 digit = *q - 'A' + 10;
470 }
471 else {
472 return 0;
473 }
474 TIXMLASSERT( digit < 16 );
475 TIXMLASSERT( digit == 0 || mult <= UINT_MAX / digit );
476 const unsigned int digitScaled = mult * digit;
477 TIXMLASSERT( ucs <= ULONG_MAX - digitScaled );
478 ucs += digitScaled;
479 TIXMLASSERT( mult <= UINT_MAX / 16 );
480 mult *= 16;
481 --q;
482 }
483 }
484 else {
485 // Decimal.
486 const char* q = p+2;
487 if ( !(*q) ) {
488 return 0;
489 }
490
491 q = strchr( q, SEMICOLON );
492
493 if ( !q ) {
494 return 0;
495 }
496 TIXMLASSERT( *q == SEMICOLON );
497
498 delta = q-p;
499 --q;
500
501 while ( *q != '#' ) {
502 if ( *q >= '0' && *q <= '9' ) {
503 const unsigned int digit = *q - '0';
504 TIXMLASSERT( digit < 10 );
505 TIXMLASSERT( digit == 0 || mult <= UINT_MAX / digit );
506 const unsigned int digitScaled = mult * digit;
507 TIXMLASSERT( ucs <= ULONG_MAX - digitScaled );
508 ucs += digitScaled;
509 }
510 else {
511 return 0;
512 }
513 TIXMLASSERT( mult <= UINT_MAX / 10 );
514 mult *= 10;
515 --q;
516 }
517 }
518 // convert the UCS to UTF-8
519 ConvertUTF32ToUTF8( ucs, value, length );
520 return p + delta + 1;
521 }
522 return p+1;
523 }
524
525
526 void XMLUtil::ToStr( int v, char* buffer, int bufferSize )
527 {
528 TIXML_SNPRINTF( buffer, bufferSize, "%d", v );
529 }
530
531
532 void XMLUtil::ToStr( unsigned v, char* buffer, int bufferSize )
533 {
534 TIXML_SNPRINTF( buffer, bufferSize, "%u", v );
535 }
536
537
538 void XMLUtil::ToStr( bool v, char* buffer, int bufferSize )
539 {
540 TIXML_SNPRINTF( buffer, bufferSize, "%d", v ? 1 : 0 );
541 }
542
543 /*
544 ToStr() of a number is a very tricky topic.
545 https://github.com/leethomason/tinyxml2/issues/106
546 */
547 void XMLUtil::ToStr( float v, char* buffer, int bufferSize )
548 {
549 TIXML_SNPRINTF( buffer, bufferSize, "%.8g", v );
550 }
551
552
553 void XMLUtil::ToStr( double v, char* buffer, int bufferSize )
554 {
555 TIXML_SNPRINTF( buffer, bufferSize, "%.17g", v );
556 }
557
558
559 bool XMLUtil::ToInt( const char* str, int* value )
560 {
561 if ( TIXML_SSCANF( str, "%d", value ) == 1 ) {
562 return true;
563 }
564 return false;
565 }
566
567 bool XMLUtil::ToUnsigned( const char* str, unsigned *value )
568 {
569 if ( TIXML_SSCANF( str, "%u", value ) == 1 ) {
570 return true;
571 }
572 return false;
573 }
574
575 bool XMLUtil::ToBool( const char* str, bool* value )
576 {
577 int ival = 0;
578 if ( ToInt( str, &ival )) {
579 *value = (ival==0) ? false : true;
580 return true;
581 }
582 if ( StringEqual( str, "true" ) ) {
583 *value = true;
584 return true;
585 }
586 else if ( StringEqual( str, "false" ) ) {
587 *value = false;
588 return true;
589 }
590 return false;
591 }
592
593
594 bool XMLUtil::ToFloat( const char* str, float* value )
595 {
596 if ( TIXML_SSCANF( str, "%f", value ) == 1 ) {
597 return true;
598 }
599 return false;
600 }
601
602 bool XMLUtil::ToDouble( const char* str, double* value )
603 {
604 if ( TIXML_SSCANF( str, "%lf", value ) == 1 ) {
605 return true;
606 }
607 return false;
608 }
609
610
611 char* XMLDocument::Identify( char* p, XMLNode** node )
612 {
613 TIXMLASSERT( node );
614 TIXMLASSERT( p );
615 char* const start = p;
616 p = XMLUtil::SkipWhiteSpace( p );
617 if( !*p ) {
618 *node = 0;
619 TIXMLASSERT( p );
620 return p;
621 }
622
623 // These strings define the matching patterns:
624 static const char* xmlHeader = { "<?" };
625 static const char* commentHeader = { "<!--" };
626 static const char* cdataHeader = { "<![CDATA[" };
627 static const char* dtdHeader = { "<!" };
628 static const char* elementHeader = { "<" }; // and a header for everything else; check last.
629
630 static const int xmlHeaderLen = 2;
631 static const int commentHeaderLen = 4;
632 static const int cdataHeaderLen = 9;
633 static const int dtdHeaderLen = 2;
634 static const int elementHeaderLen = 1;
635
636 TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLUnknown ) ); // use same memory pool
637 TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLDeclaration ) ); // use same memory pool
638 XMLNode* returnNode = 0;
639 if ( XMLUtil::StringEqual( p, xmlHeader, xmlHeaderLen ) ) {
640 TIXMLASSERT( sizeof( XMLDeclaration ) == _commentPool.ItemSize() );
641 returnNode = new (_commentPool.Alloc()) XMLDeclaration( this );
642 returnNode->_memPool = &_commentPool;
643 p += xmlHeaderLen;
644 }
645 else if ( XMLUtil::StringEqual( p, commentHeader, commentHeaderLen ) ) {
646 TIXMLASSERT( sizeof( XMLComment ) == _commentPool.ItemSize() );
647 returnNode = new (_commentPool.Alloc()) XMLComment( this );
648 returnNode->_memPool = &_commentPool;
649 p += commentHeaderLen;
650 }
651 else if ( XMLUtil::StringEqual( p, cdataHeader, cdataHeaderLen ) ) {
652 TIXMLASSERT( sizeof( XMLText ) == _textPool.ItemSize() );
653 XMLText* text = new (_textPool.Alloc()) XMLText( this );
654 returnNode = text;
655 returnNode->_memPool = &_textPool;
656 p += cdataHeaderLen;
657 text->SetCData( true );
658 }
659 else if ( XMLUtil::StringEqual( p, dtdHeader, dtdHeaderLen ) ) {
660 TIXMLASSERT( sizeof( XMLUnknown ) == _commentPool.ItemSize() );
661 returnNode = new (_commentPool.Alloc()) XMLUnknown( this );
662 returnNode->_memPool = &_commentPool;
663 p += dtdHeaderLen;
664 }
665 else if ( XMLUtil::StringEqual( p, elementHeader, elementHeaderLen ) ) {
666 TIXMLASSERT( sizeof( XMLElement ) == _elementPool.ItemSize() );
667 returnNode = new (_elementPool.Alloc()) XMLElement( this );
668 returnNode->_memPool = &_elementPool;
669 p += elementHeaderLen;
670 }
671 else {
672 TIXMLASSERT( sizeof( XMLText ) == _textPool.ItemSize() );
673 returnNode = new (_textPool.Alloc()) XMLText( this );
674 returnNode->_memPool = &_textPool;
675 p = start; // Back it up, all the text counts.
676 }
677
678 TIXMLASSERT( returnNode );
679 TIXMLASSERT( p );
680 *node = returnNode;
681 return p;
682 }
683
684
685 bool XMLDocument::Accept( XMLVisitor* visitor ) const
686 {
687 TIXMLASSERT( visitor );
688 if ( visitor->VisitEnter( *this ) ) {
689 for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) {
690 if ( !node->Accept( visitor ) ) {
691 break;
692 }
693 }
694 }
695 return visitor->VisitExit( *this );
696 }
697
698
699 // --------- XMLNode ----------- //
700
701 XMLNode::XMLNode( XMLDocument* doc ) :
702 _document( doc ),
703 _parent( 0 ),
704 _firstChild( 0 ), _lastChild( 0 ),
705 _prev( 0 ), _next( 0 ),
706 _memPool( 0 )
707 {
708 }
709
710
711 XMLNode::~XMLNode()
712 {
713 DeleteChildren();
714 if ( _parent ) {
715 _parent->Unlink( this );
716 }
717 }
718
719 const char* XMLNode::Value() const
720 {
721 // Catch an edge case: XMLDocuments don't have a a Value. Carefully return nullptr.
722 if ( this->ToDocument() )
723 return 0;
724 return _value.GetStr();
725 }
726
727 void XMLNode::SetValue( const char* str, bool staticMem )
728 {
729 if ( staticMem ) {
730 _value.SetInternedStr( str );
731 }
732 else {
733 _value.SetStr( str );
734 }
735 }
736
737
738 void XMLNode::DeleteChildren()
739 {
740 while( _firstChild ) {
741 TIXMLASSERT( _lastChild );
742 TIXMLASSERT( _firstChild->_document == _document );
743 XMLNode* node = _firstChild;
744 Unlink( node );
745
746 DeleteNode( node );
747 }
748 _firstChild = _lastChild = 0;
749 }
750
751
752 void XMLNode::Unlink( XMLNode* child )
753 {
754 TIXMLASSERT( child );
755 TIXMLASSERT( child->_document == _document );
756 TIXMLASSERT( child->_parent == this );
757 if ( child == _firstChild ) {
758 _firstChild = _firstChild->_next;
759 }
760 if ( child == _lastChild ) {
761 _lastChild = _lastChild->_prev;
762 }
763
764 if ( child->_prev ) {
765 child->_prev->_next = child->_next;
766 }
767 if ( child->_next ) {
768 child->_next->_prev = child->_prev;
769 }
770 child->_parent = 0;
771 }
772
773
774 void XMLNode::DeleteChild( XMLNode* node )
775 {
776 TIXMLASSERT( node );
777 TIXMLASSERT( node->_document == _document );
778 TIXMLASSERT( node->_parent == this );
779 Unlink( node );
780 DeleteNode( node );
781 }
782
783
784 XMLNode* XMLNode::InsertEndChild( XMLNode* addThis )
785 {
786 TIXMLASSERT( addThis );
787 if ( addThis->_document != _document ) {
788 TIXMLASSERT( false );
789 return 0;
790 }
791 InsertChildPreamble( addThis );
792
793 if ( _lastChild ) {
794 TIXMLASSERT( _firstChild );
795 TIXMLASSERT( _lastChild->_next == 0 );
796 _lastChild->_next = addThis;
797 addThis->_prev = _lastChild;
798 _lastChild = addThis;
799
800 addThis->_next = 0;
801 }
802 else {
803 TIXMLASSERT( _firstChild == 0 );
804 _firstChild = _lastChild = addThis;
805
806 addThis->_prev = 0;
807 addThis->_next = 0;
808 }
809 addThis->_parent = this;
810 return addThis;
811 }
812
813
814 XMLNode* XMLNode::InsertFirstChild( XMLNode* addThis )
815 {
816 TIXMLASSERT( addThis );
817 if ( addThis->_document != _document ) {
818 TIXMLASSERT( false );
819 return 0;
820 }
821 InsertChildPreamble( addThis );
822
823 if ( _firstChild ) {
824 TIXMLASSERT( _lastChild );
825 TIXMLASSERT( _firstChild->_prev == 0 );
826
827 _firstChild->_prev = addThis;
828 addThis->_next = _firstChild;
829 _firstChild = addThis;
830
831 addThis->_prev = 0;
832 }
833 else {
834 TIXMLASSERT( _lastChild == 0 );
835 _firstChild = _lastChild = addThis;
836
837 addThis->_prev = 0;
838 addThis->_next = 0;
839 }
840 addThis->_parent = this;
841 return addThis;
842 }
843
844
845 XMLNode* XMLNode::InsertAfterChild( XMLNode* afterThis, XMLNode* addThis )
846 {
847 TIXMLASSERT( addThis );
848 if ( addThis->_document != _document ) {
849 TIXMLASSERT( false );
850 return 0;
851 }
852
853 TIXMLASSERT( afterThis );
854
855 if ( afterThis->_parent != this ) {
856 TIXMLASSERT( false );
857 return 0;
858 }
859
860 if ( afterThis->_next == 0 ) {
861 // The last node or the only node.
862 return InsertEndChild( addThis );
863 }
864 InsertChildPreamble( addThis );
865 addThis->_prev = afterThis;
866 addThis->_next = afterThis->_next;
867 afterThis->_next->_prev = addThis;
868 afterThis->_next = addThis;
869 addThis->_parent = this;
870 return addThis;
871 }
872
873
874
875
876 const XMLElement* XMLNode::FirstChildElement( const char* name ) const
877 {
878 for( const XMLNode* node = _firstChild; node; node = node->_next ) {
879 const XMLElement* element = node->ToElement();
880 if ( element ) {
881 if ( !name || XMLUtil::StringEqual( element->Name(), name ) ) {
882 return element;
883 }
884 }
885 }
886 return 0;
887 }
888
889
890 const XMLElement* XMLNode::LastChildElement( const char* name ) const
891 {
892 for( const XMLNode* node = _lastChild; node; node = node->_prev ) {
893 const XMLElement* element = node->ToElement();
894 if ( element ) {
895 if ( !name || XMLUtil::StringEqual( element->Name(), name ) ) {
896 return element;
897 }
898 }
899 }
900 return 0;
901 }
902
903
904 const XMLElement* XMLNode::NextSiblingElement( const char* name ) const
905 {
906 for( const XMLNode* node = _next; node; node = node->_next ) {
907 const XMLElement* element = node->ToElement();
908 if ( element
909 && (!name || XMLUtil::StringEqual( name, element->Name() ))) {
910 return element;
911 }
912 }
913 return 0;
914 }
915
916
917 const XMLElement* XMLNode::PreviousSiblingElement( const char* name ) const
918 {
919 for( const XMLNode* node = _prev; node; node = node->_prev ) {
920 const XMLElement* element = node->ToElement();
921 if ( element
922 && (!name || XMLUtil::StringEqual( name, element->Name() ))) {
923 return element;
924 }
925 }
926 return 0;
927 }
928
929
930 char* XMLNode::ParseDeep( char* p, StrPair* parentEnd )
931 {
932 // This is a recursive method, but thinking about it "at the current level"
933 // it is a pretty simple flat list:
934 // <foo/>
935 // <!-- comment -->
936 //
937 // With a special case:
938 // <foo>
939 // </foo>
940 // <!-- comment -->
941 //
942 // Where the closing element (/foo) *must* be the next thing after the opening
943 // element, and the names must match. BUT the tricky bit is that the closing
944 // element will be read by the child.
945 //
946 // 'endTag' is the end tag for this node, it is returned by a call to a child.
947 // 'parentEnd' is the end tag for the parent, which is filled in and returned.
948
949 while( p && *p ) {
950 XMLNode* node = 0;
951
952 p = _document->Identify( p, &node );
953 if ( node == 0 ) {
954 break;
955 }
956
957 StrPair endTag;
958 p = node->ParseDeep( p, &endTag );
959 if ( !p ) {
960 DeleteNode( node );
961 if ( !_document->Error() ) {
962 _document->SetError( XML_ERROR_PARSING, 0, 0 );
963 }
964 break;
965 }
966
967 XMLDeclaration* decl = node->ToDeclaration();
968 if ( decl ) {
969 // A declaration can only be the first child of a document.
970 // Set error, if document already has children.
971 if ( !_document->NoChildren() ) {
972 _document->SetError( XML_ERROR_PARSING_DECLARATION, decl->Value(), 0);
973 DeleteNode( decl );
974 break;
975 }
976 }
977
978 XMLElement* ele = node->ToElement();
979 if ( ele ) {
980 // We read the end tag. Return it to the parent.
981 if ( ele->ClosingType() == XMLElement::CLOSING ) {
982 if ( parentEnd ) {
983 ele->_value.TransferTo( parentEnd );
984 }
985 node->_memPool->SetTracked(); // created and then immediately deleted.
986 DeleteNode( node );
987 return p;
988 }
989
990 // Handle an end tag returned to this level.
991 // And handle a bunch of annoying errors.
992 bool mismatch = false;
993 if ( endTag.Empty() ) {
994 if ( ele->ClosingType() == XMLElement::OPEN ) {
995 mismatch = true;
996 }
997 }
998 else {
999 if ( ele->ClosingType() != XMLElement::OPEN ) {
1000 mismatch = true;
1001 }
1002 else if ( !XMLUtil::StringEqual( endTag.GetStr(), ele->Name() ) ) {
1003 mismatch = true;
1004 }
1005 }
1006 if ( mismatch ) {
1007 _document->SetError( XML_ERROR_MISMATCHED_ELEMENT, ele->Name(), 0 );
1008 DeleteNode( node );
1009 break;
1010 }
1011 }
1012 InsertEndChild( node );
1013 }
1014 return 0;
1015 }
1016
1017 void XMLNode::DeleteNode( XMLNode* node )
1018 {
1019 if ( node == 0 ) {
1020 return;
1021 }
1022 MemPool* pool = node->_memPool;
1023 node->~XMLNode();
1024 pool->Free( node );
1025 }
1026
1027 void XMLNode::InsertChildPreamble( XMLNode* insertThis ) const
1028 {
1029 TIXMLASSERT( insertThis );
1030 TIXMLASSERT( insertThis->_document == _document );
1031
1032 if ( insertThis->_parent )
1033 insertThis->_parent->Unlink( insertThis );
1034 else
1035 insertThis->_memPool->SetTracked();
1036 }
1037
1038 // --------- XMLText ---------- //
1039 char* XMLText::ParseDeep( char* p, StrPair* )
1040 {
1041 const char* start = p;
1042 if ( this->CData() ) {
1043 p = _value.ParseText( p, "]]>", StrPair::NEEDS_NEWLINE_NORMALIZATION );
1044 if ( !p ) {
1045 _document->SetError( XML_ERROR_PARSING_CDATA, start, 0 );
1046 }
1047 return p;
1048 }
1049 else {
1050 int flags = _document->ProcessEntities() ? StrPair::TEXT_ELEMENT : StrPair::TEXT_ELEMENT_LEAVE_ENTITIES;
1051 if ( _document->WhitespaceMode() == COLLAPSE_WHITESPACE ) {
1052 flags |= StrPair::NEEDS_WHITESPACE_COLLAPSING;
1053 }
1054
1055 p = _value.ParseText( p, "<", flags );
1056 if ( p && *p ) {
1057 return p-1;
1058 }
1059 if ( !p ) {
1060 _document->SetError( XML_ERROR_PARSING_TEXT, start, 0 );
1061 }
1062 }
1063 return 0;
1064 }
1065
1066
1067 XMLNode* XMLText::ShallowClone( XMLDocument* doc ) const
1068 {
1069 if ( !doc ) {
1070 doc = _document;
1071 }
1072 XMLText* text = doc->NewText( Value() ); // fixme: this will always allocate memory. Intern?
1073 text->SetCData( this->CData() );
1074 return text;
1075 }
1076
1077
1078 bool XMLText::ShallowEqual( const XMLNode* compare ) const
1079 {
1080 const XMLText* text = compare->ToText();
1081 return ( text && XMLUtil::StringEqual( text->Value(), Value() ) );
1082 }
1083
1084
1085 bool XMLText::Accept( XMLVisitor* visitor ) const
1086 {
1087 TIXMLASSERT( visitor );
1088 return visitor->Visit( *this );
1089 }
1090
1091
1092 // --------- XMLComment ---------- //
1093
1094 XMLComment::XMLComment( XMLDocument* doc ) : XMLNode( doc )
1095 {
1096 }
1097
1098
1099 XMLComment::~XMLComment()
1100 {
1101 }
1102
1103
1104 char* XMLComment::ParseDeep( char* p, StrPair* )
1105 {
1106 // Comment parses as text.
1107 const char* start = p;
1108 p = _value.ParseText( p, "-->", StrPair::COMMENT );
1109 if ( p == 0 ) {
1110 _document->SetError( XML_ERROR_PARSING_COMMENT, start, 0 );
1111 }
1112 return p;
1113 }
1114
1115
1116 XMLNode* XMLComment::ShallowClone( XMLDocument* doc ) const
1117 {
1118 if ( !doc ) {
1119 doc = _document;
1120 }
1121 XMLComment* comment = doc->NewComment( Value() ); // fixme: this will always allocate memory. Intern?
1122 return comment;
1123 }
1124
1125
1126 bool XMLComment::ShallowEqual( const XMLNode* compare ) const
1127 {
1128 TIXMLASSERT( compare );
1129 const XMLComment* comment = compare->ToComment();
1130 return ( comment && XMLUtil::StringEqual( comment->Value(), Value() ));
1131 }
1132
1133
1134 bool XMLComment::Accept( XMLVisitor* visitor ) const
1135 {
1136 TIXMLASSERT( visitor );
1137 return visitor->Visit( *this );
1138 }
1139
1140
1141 // --------- XMLDeclaration ---------- //
1142
1143 XMLDeclaration::XMLDeclaration( XMLDocument* doc ) : XMLNode( doc )
1144 {
1145 }
1146
1147
1148 XMLDeclaration::~XMLDeclaration()
1149 {
1150 //printf( "~XMLDeclaration\n" );
1151 }
1152
1153
1154 char* XMLDeclaration::ParseDeep( char* p, StrPair* )
1155 {
1156 // Declaration parses as text.
1157 const char* start = p;
1158 p = _value.ParseText( p, "?>", StrPair::NEEDS_NEWLINE_NORMALIZATION );
1159 if ( p == 0 ) {
1160 _document->SetError( XML_ERROR_PARSING_DECLARATION, start, 0 );
1161 }
1162 return p;
1163 }
1164
1165
1166 XMLNode* XMLDeclaration::ShallowClone( XMLDocument* doc ) const
1167 {
1168 if ( !doc ) {
1169 doc = _document;
1170 }
1171 XMLDeclaration* dec = doc->NewDeclaration( Value() ); // fixme: this will always allocate memory. Intern?
1172 return dec;
1173 }
1174
1175
1176 bool XMLDeclaration::ShallowEqual( const XMLNode* compare ) const
1177 {
1178 TIXMLASSERT( compare );
1179 const XMLDeclaration* declaration = compare->ToDeclaration();
1180 return ( declaration && XMLUtil::StringEqual( declaration->Value(), Value() ));
1181 }
1182
1183
1184
1185 bool XMLDeclaration::Accept( XMLVisitor* visitor ) const
1186 {
1187 TIXMLASSERT( visitor );
1188 return visitor->Visit( *this );
1189 }
1190
1191 // --------- XMLUnknown ---------- //
1192
1193 XMLUnknown::XMLUnknown( XMLDocument* doc ) : XMLNode( doc )
1194 {
1195 }
1196
1197
1198 XMLUnknown::~XMLUnknown()
1199 {
1200 }
1201
1202
1203 char* XMLUnknown::ParseDeep( char* p, StrPair* )
1204 {
1205 // Unknown parses as text.
1206 const char* start = p;
1207
1208 p = _value.ParseText( p, ">", StrPair::NEEDS_NEWLINE_NORMALIZATION );
1209 if ( !p ) {
1210 _document->SetError( XML_ERROR_PARSING_UNKNOWN, start, 0 );
1211 }
1212 return p;
1213 }
1214
1215
1216 XMLNode* XMLUnknown::ShallowClone( XMLDocument* doc ) const
1217 {
1218 if ( !doc ) {
1219 doc = _document;
1220 }
1221 XMLUnknown* text = doc->NewUnknown( Value() ); // fixme: this will always allocate memory. Intern?
1222 return text;
1223 }
1224
1225
1226 bool XMLUnknown::ShallowEqual( const XMLNode* compare ) const
1227 {
1228 TIXMLASSERT( compare );
1229 const XMLUnknown* unknown = compare->ToUnknown();
1230 return ( unknown && XMLUtil::StringEqual( unknown->Value(), Value() ));
1231 }
1232
1233
1234 bool XMLUnknown::Accept( XMLVisitor* visitor ) const
1235 {
1236 TIXMLASSERT( visitor );
1237 return visitor->Visit( *this );
1238 }
1239
1240 // --------- XMLAttribute ---------- //
1241
1242 const char* XMLAttribute::Name() const
1243 {
1244 return _name.GetStr();
1245 }
1246
1247 const char* XMLAttribute::Value() const
1248 {
1249 return _value.GetStr();
1250 }
1251
1252 char* XMLAttribute::ParseDeep( char* p, bool processEntities )
1253 {
1254 // Parse using the name rules: bug fix, was using ParseText before
1255 p = _name.ParseName( p );
1256 if ( !p || !*p ) {
1257 return 0;
1258 }
1259
1260 // Skip white space before =
1261 p = XMLUtil::SkipWhiteSpace( p );
1262 if ( *p != '=' ) {
1263 return 0;
1264 }
1265
1266 ++p; // move up to opening quote
1267 p = XMLUtil::SkipWhiteSpace( p );
1268 if ( *p != '\"' && *p != '\'' ) {
1269 return 0;
1270 }
1271
1272 char endTag[2] = { *p, 0 };
1273 ++p; // move past opening quote
1274
1275 p = _value.ParseText( p, endTag, processEntities ? StrPair::ATTRIBUTE_VALUE : StrPair::ATTRIBUTE_VALUE_LEAVE_ENTITIES );
1276 return p;
1277 }
1278
1279
1280 void XMLAttribute::SetName( const char* n )
1281 {
1282 _name.SetStr( n );
1283 }
1284
1285
1286 XMLError XMLAttribute::QueryIntValue( int* value ) const
1287 {
1288 if ( XMLUtil::ToInt( Value(), value )) {
1289 return XML_NO_ERROR;
1290 }
1291 return XML_WRONG_ATTRIBUTE_TYPE;
1292 }
1293
1294
1295 XMLError XMLAttribute::QueryUnsignedValue( unsigned int* value ) const
1296 {
1297 if ( XMLUtil::ToUnsigned( Value(), value )) {
1298 return XML_NO_ERROR;
1299 }
1300 return XML_WRONG_ATTRIBUTE_TYPE;
1301 }
1302
1303
1304 XMLError XMLAttribute::QueryBoolValue( bool* value ) const
1305 {
1306 if ( XMLUtil::ToBool( Value(), value )) {
1307 return XML_NO_ERROR;
1308 }
1309 return XML_WRONG_ATTRIBUTE_TYPE;
1310 }
1311
1312
1313 XMLError XMLAttribute::QueryFloatValue( float* value ) const
1314 {
1315 if ( XMLUtil::ToFloat( Value(), value )) {
1316 return XML_NO_ERROR;
1317 }
1318 return XML_WRONG_ATTRIBUTE_TYPE;
1319 }
1320
1321
1322 XMLError XMLAttribute::QueryDoubleValue( double* value ) const
1323 {
1324 if ( XMLUtil::ToDouble( Value(), value )) {
1325 return XML_NO_ERROR;
1326 }
1327 return XML_WRONG_ATTRIBUTE_TYPE;
1328 }
1329
1330
1331 void XMLAttribute::SetAttribute( const char* v )
1332 {
1333 _value.SetStr( v );
1334 }
1335
1336
1337 void XMLAttribute::SetAttribute( int v )
1338 {
1339 char buf[BUF_SIZE];
1340 XMLUtil::ToStr( v, buf, BUF_SIZE );
1341 _value.SetStr( buf );
1342 }
1343
1344
1345 void XMLAttribute::SetAttribute( unsigned v )
1346 {
1347 char buf[BUF_SIZE];
1348 XMLUtil::ToStr( v, buf, BUF_SIZE );
1349 _value.SetStr( buf );
1350 }
1351
1352
1353 void XMLAttribute::SetAttribute( bool v )
1354 {
1355 char buf[BUF_SIZE];
1356 XMLUtil::ToStr( v, buf, BUF_SIZE );
1357 _value.SetStr( buf );
1358 }
1359
1360 void XMLAttribute::SetAttribute( double v )
1361 {
1362 char buf[BUF_SIZE];
1363 XMLUtil::ToStr( v, buf, BUF_SIZE );
1364 _value.SetStr( buf );
1365 }
1366
1367 void XMLAttribute::SetAttribute( float v )
1368 {
1369 char buf[BUF_SIZE];
1370 XMLUtil::ToStr( v, buf, BUF_SIZE );
1371 _value.SetStr( buf );
1372 }
1373
1374
1375 // --------- XMLElement ---------- //
1376 XMLElement::XMLElement( XMLDocument* doc ) : XMLNode( doc ),
1377 _closingType( 0 ),
1378 _rootAttribute( 0 )
1379 {
1380 }
1381
1382
1383 XMLElement::~XMLElement()
1384 {
1385 while( _rootAttribute ) {
1386 XMLAttribute* next = _rootAttribute->_next;
1387 DeleteAttribute( _rootAttribute );
1388 _rootAttribute = next;
1389 }
1390 }
1391
1392
1393 const XMLAttribute* XMLElement::FindAttribute( const char* name ) const
1394 {
1395 for( XMLAttribute* a = _rootAttribute; a; a = a->_next ) {
1396 if ( XMLUtil::StringEqual( a->Name(), name ) ) {
1397 return a;
1398 }
1399 }
1400 return 0;
1401 }
1402
1403
1404 const char* XMLElement::Attribute( const char* name, const char* value ) const
1405 {
1406 const XMLAttribute* a = FindAttribute( name );
1407 if ( !a ) {
1408 return 0;
1409 }
1410 if ( !value || XMLUtil::StringEqual( a->Value(), value )) {
1411 return a->Value();
1412 }
1413 return 0;
1414 }
1415
1416
1417 const char* XMLElement::GetText() const
1418 {
1419 if ( FirstChild() && FirstChild()->ToText() ) {
1420 return FirstChild()->Value();
1421 }
1422 return 0;
1423 }
1424
1425
1426 void XMLElement::SetText( const char* inText )
1427 {
1428 if ( FirstChild() && FirstChild()->ToText() )
1429 FirstChild()->SetValue( inText );
1430 else {
1431 XMLText* theText = GetDocument()->NewText( inText );
1432 InsertFirstChild( theText );
1433 }
1434 }
1435
1436
1437 void XMLElement::SetText( int v )
1438 {
1439 char buf[BUF_SIZE];
1440 XMLUtil::ToStr( v, buf, BUF_SIZE );
1441 SetText( buf );
1442 }
1443
1444
1445 void XMLElement::SetText( unsigned v )
1446 {
1447 char buf[BUF_SIZE];
1448 XMLUtil::ToStr( v, buf, BUF_SIZE );
1449 SetText( buf );
1450 }
1451
1452
1453 void XMLElement::SetText( bool v )
1454 {
1455 char buf[BUF_SIZE];
1456 XMLUtil::ToStr( v, buf, BUF_SIZE );
1457 SetText( buf );
1458 }
1459
1460
1461 void XMLElement::SetText( float v )
1462 {
1463 char buf[BUF_SIZE];
1464 XMLUtil::ToStr( v, buf, BUF_SIZE );
1465 SetText( buf );
1466 }
1467
1468
1469 void XMLElement::SetText( double v )
1470 {
1471 char buf[BUF_SIZE];
1472 XMLUtil::ToStr( v, buf, BUF_SIZE );
1473 SetText( buf );
1474 }
1475
1476
1477 XMLError XMLElement::QueryIntText( int* ival ) const
1478 {
1479 if ( FirstChild() && FirstChild()->ToText() ) {
1480 const char* t = FirstChild()->Value();
1481 if ( XMLUtil::ToInt( t, ival ) ) {
1482 return XML_SUCCESS;
1483 }
1484 return XML_CAN_NOT_CONVERT_TEXT;
1485 }
1486 return XML_NO_TEXT_NODE;
1487 }
1488
1489
1490 XMLError XMLElement::QueryUnsignedText( unsigned* uval ) const
1491 {
1492 if ( FirstChild() && FirstChild()->ToText() ) {
1493 const char* t = FirstChild()->Value();
1494 if ( XMLUtil::ToUnsigned( t, uval ) ) {
1495 return XML_SUCCESS;
1496 }
1497 return XML_CAN_NOT_CONVERT_TEXT;
1498 }
1499 return XML_NO_TEXT_NODE;
1500 }
1501
1502
1503 XMLError XMLElement::QueryBoolText( bool* bval ) const
1504 {
1505 if ( FirstChild() && FirstChild()->ToText() ) {
1506 const char* t = FirstChild()->Value();
1507 if ( XMLUtil::ToBool( t, bval ) ) {
1508 return XML_SUCCESS;
1509 }
1510 return XML_CAN_NOT_CONVERT_TEXT;
1511 }
1512 return XML_NO_TEXT_NODE;
1513 }
1514
1515
1516 XMLError XMLElement::QueryDoubleText( double* dval ) const
1517 {
1518 if ( FirstChild() && FirstChild()->ToText() ) {
1519 const char* t = FirstChild()->Value();
1520 if ( XMLUtil::ToDouble( t, dval ) ) {
1521 return XML_SUCCESS;
1522 }
1523 return XML_CAN_NOT_CONVERT_TEXT;
1524 }
1525 return XML_NO_TEXT_NODE;
1526 }
1527
1528
1529 XMLError XMLElement::QueryFloatText( float* fval ) const
1530 {
1531 if ( FirstChild() && FirstChild()->ToText() ) {
1532 const char* t = FirstChild()->Value();
1533 if ( XMLUtil::ToFloat( t, fval ) ) {
1534 return XML_SUCCESS;
1535 }
1536 return XML_CAN_NOT_CONVERT_TEXT;
1537 }
1538 return XML_NO_TEXT_NODE;
1539 }
1540
1541
1542
1543 XMLAttribute* XMLElement::FindOrCreateAttribute( const char* name )
1544 {
1545 XMLAttribute* last = 0;
1546 XMLAttribute* attrib = 0;
1547 for( attrib = _rootAttribute;
1548 attrib;
1549 last = attrib, attrib = attrib->_next ) {
1550 if ( XMLUtil::StringEqual( attrib->Name(), name ) ) {
1551 break;
1552 }
1553 }
1554 if ( !attrib ) {
1555 TIXMLASSERT( sizeof( XMLAttribute ) == _document->_attributePool.ItemSize() );
1556 attrib = new (_document->_attributePool.Alloc() ) XMLAttribute();
1557 attrib->_memPool = &_document->_attributePool;
1558 if ( last ) {
1559 last->_next = attrib;
1560 }
1561 else {
1562 _rootAttribute = attrib;
1563 }
1564 attrib->SetName( name );
1565 attrib->_memPool->SetTracked(); // always created and linked.
1566 }
1567 return attrib;
1568 }
1569
1570
1571 void XMLElement::DeleteAttribute( const char* name )
1572 {
1573 XMLAttribute* prev = 0;
1574 for( XMLAttribute* a=_rootAttribute; a; a=a->_next ) {
1575 if ( XMLUtil::StringEqual( name, a->Name() ) ) {
1576 if ( prev ) {
1577 prev->_next = a->_next;
1578 }
1579 else {
1580 _rootAttribute = a->_next;
1581 }
1582 DeleteAttribute( a );
1583 break;
1584 }
1585 prev = a;
1586 }
1587 }
1588
1589
1590 char* XMLElement::ParseAttributes( char* p )
1591 {
1592 const char* start = p;
1593 XMLAttribute* prevAttribute = 0;
1594
1595 // Read the attributes.
1596 while( p ) {
1597 p = XMLUtil::SkipWhiteSpace( p );
1598 if ( !(*p) ) {
1599 _document->SetError( XML_ERROR_PARSING_ELEMENT, start, Name() );
1600 return 0;
1601 }
1602
1603 // attribute.
1604 if (XMLUtil::IsNameStartChar( *p ) ) {
1605 TIXMLASSERT( sizeof( XMLAttribute ) == _document->_attributePool.ItemSize() );
1606 XMLAttribute* attrib = new (_document->_attributePool.Alloc() ) XMLAttribute();
1607 attrib->_memPool = &_document->_attributePool;
1608 attrib->_memPool->SetTracked();
1609
1610 p = attrib->ParseDeep( p, _document->ProcessEntities() );
1611 if ( !p || Attribute( attrib->Name() ) ) {
1612 DeleteAttribute( attrib );
1613 _document->SetError( XML_ERROR_PARSING_ATTRIBUTE, start, p );
1614 return 0;
1615 }
1616 // There is a minor bug here: if the attribute in the source xml
1617 // document is duplicated, it will not be detected and the
1618 // attribute will be doubly added. However, tracking the 'prevAttribute'
1619 // avoids re-scanning the attribute list. Preferring performance for
1620 // now, may reconsider in the future.
1621 if ( prevAttribute ) {
1622 prevAttribute->_next = attrib;
1623 }
1624 else {
1625 _rootAttribute = attrib;
1626 }
1627 prevAttribute = attrib;
1628 }
1629 // end of the tag
1630 else if ( *p == '>' ) {
1631 ++p;
1632 break;
1633 }
1634 // end of the tag
1635 else if ( *p == '/' && *(p+1) == '>' ) {
1636 _closingType = CLOSED;
1637 return p+2; // done; sealed element.
1638 }
1639 else {
1640 _document->SetError( XML_ERROR_PARSING_ELEMENT, start, p );
1641 return 0;
1642 }
1643 }
1644 return p;
1645 }
1646
1647 void XMLElement::DeleteAttribute( XMLAttribute* attribute )
1648 {
1649 if ( attribute == 0 ) {
1650 return;
1651 }
1652 MemPool* pool = attribute->_memPool;
1653 attribute->~XMLAttribute();
1654 pool->Free( attribute );
1655 }
1656
1657 //
1658 // <ele></ele>
1659 // <ele>foo<b>bar</b></ele>
1660 //
1661 char* XMLElement::ParseDeep( char* p, StrPair* strPair )
1662 {
1663 // Read the element name.
1664 p = XMLUtil::SkipWhiteSpace( p );
1665
1666 // The closing element is the </element> form. It is
1667 // parsed just like a regular element then deleted from
1668 // the DOM.
1669 if ( *p == '/' ) {
1670 _closingType = CLOSING;
1671 ++p;
1672 }
1673
1674 p = _value.ParseName( p );
1675 if ( _value.Empty() ) {
1676 return 0;
1677 }
1678
1679 p = ParseAttributes( p );
1680 if ( !p || !*p || _closingType ) {
1681 return p;
1682 }
1683
1684 p = XMLNode::ParseDeep( p, strPair );
1685 return p;
1686 }
1687
1688
1689
1690 XMLNode* XMLElement::ShallowClone( XMLDocument* doc ) const
1691 {
1692 if ( !doc ) {
1693 doc = _document;
1694 }
1695 XMLElement* element = doc->NewElement( Value() ); // fixme: this will always allocate memory. Intern?
1696 for( const XMLAttribute* a=FirstAttribute(); a; a=a->Next() ) {
1697 element->SetAttribute( a->Name(), a->Value() ); // fixme: this will always allocate memory. Intern?
1698 }
1699 return element;
1700 }
1701
1702
1703 bool XMLElement::ShallowEqual( const XMLNode* compare ) const
1704 {
1705 TIXMLASSERT( compare );
1706 const XMLElement* other = compare->ToElement();
1707 if ( other && XMLUtil::StringEqual( other->Name(), Name() )) {
1708
1709 const XMLAttribute* a=FirstAttribute();
1710 const XMLAttribute* b=other->FirstAttribute();
1711
1712 while ( a && b ) {
1713 if ( !XMLUtil::StringEqual( a->Value(), b->Value() ) ) {
1714 return false;
1715 }
1716 a = a->Next();
1717 b = b->Next();
1718 }
1719 if ( a || b ) {
1720 // different count
1721 return false;
1722 }
1723 return true;
1724 }
1725 return false;
1726 }
1727
1728
1729 bool XMLElement::Accept( XMLVisitor* visitor ) const
1730 {
1731 TIXMLASSERT( visitor );
1732 if ( visitor->VisitEnter( *this, _rootAttribute ) ) {
1733 for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) {
1734 if ( !node->Accept( visitor ) ) {
1735 break;
1736 }
1737 }
1738 }
1739 return visitor->VisitExit( *this );
1740 }
1741
1742
1743 // --------- XMLDocument ----------- //
1744
1745 // Warning: List must match 'enum XMLError'
1746 const char* XMLDocument::_errorNames[XML_ERROR_COUNT] = {
1747 "XML_SUCCESS",
1748 "XML_NO_ATTRIBUTE",
1749 "XML_WRONG_ATTRIBUTE_TYPE",
1750 "XML_ERROR_FILE_NOT_FOUND",
1751 "XML_ERROR_FILE_COULD_NOT_BE_OPENED",
1752 "XML_ERROR_FILE_READ_ERROR",
1753 "XML_ERROR_ELEMENT_MISMATCH",
1754 "XML_ERROR_PARSING_ELEMENT",
1755 "XML_ERROR_PARSING_ATTRIBUTE",
1756 "XML_ERROR_IDENTIFYING_TAG",
1757 "XML_ERROR_PARSING_TEXT",
1758 "XML_ERROR_PARSING_CDATA",
1759 "XML_ERROR_PARSING_COMMENT",
1760 "XML_ERROR_PARSING_DECLARATION",
1761 "XML_ERROR_PARSING_UNKNOWN",
1762 "XML_ERROR_EMPTY_DOCUMENT",
1763 "XML_ERROR_MISMATCHED_ELEMENT",
1764 "XML_ERROR_PARSING",
1765 "XML_CAN_NOT_CONVERT_TEXT",
1766 "XML_NO_TEXT_NODE"
1767 };
1768
1769
1770 XMLDocument::XMLDocument( bool processEntities, Whitespace whitespace ) :
1771 XMLNode( 0 ),
1772 _writeBOM( false ),
1773 _processEntities( processEntities ),
1774 _errorID( XML_NO_ERROR ),
1775 _whitespace( whitespace ),
1776 _errorStr1( 0 ),
1777 _errorStr2( 0 ),
1778 _charBuffer( 0 )
1779 {
1780 // avoid VC++ C4355 warning about 'this' in initializer list (C4355 is off by default in VS2012+)
1781 _document = this;
1782 }
1783
1784
1785 XMLDocument::~XMLDocument()
1786 {
1787 Clear();
1788 }
1789
1790
1791 void XMLDocument::Clear()
1792 {
1793 DeleteChildren();
1794
1795 #ifdef DEBUG
1796 const bool hadError = Error();
1797 #endif
1798 _errorID = XML_NO_ERROR;
1799 _errorStr1 = 0;
1800 _errorStr2 = 0;
1801
1802 delete [] _charBuffer;
1803 _charBuffer = 0;
1804
1805 #if 0
1806 _textPool.Trace( "text" );
1807 _elementPool.Trace( "element" );
1808 _commentPool.Trace( "comment" );
1809 _attributePool.Trace( "attribute" );
1810 #endif
1811
1812 #ifdef DEBUG
1813 if ( !hadError ) {
1814 TIXMLASSERT( _elementPool.CurrentAllocs() == _elementPool.Untracked() );
1815 TIXMLASSERT( _attributePool.CurrentAllocs() == _attributePool.Untracked() );
1816 TIXMLASSERT( _textPool.CurrentAllocs() == _textPool.Untracked() );
1817 TIXMLASSERT( _commentPool.CurrentAllocs() == _commentPool.Untracked() );
1818 }
1819 #endif
1820 }
1821
1822
1823 XMLElement* XMLDocument::NewElement( const char* name )
1824 {
1825 TIXMLASSERT( sizeof( XMLElement ) == _elementPool.ItemSize() );
1826 XMLElement* ele = new (_elementPool.Alloc()) XMLElement( this );
1827 ele->_memPool = &_elementPool;
1828 ele->SetName( name );
1829 return ele;
1830 }
1831
1832
1833 XMLComment* XMLDocument::NewComment( const char* str )
1834 {
1835 TIXMLASSERT( sizeof( XMLComment ) == _commentPool.ItemSize() );
1836 XMLComment* comment = new (_commentPool.Alloc()) XMLComment( this );
1837 comment->_memPool = &_commentPool;
1838 comment->SetValue( str );
1839 return comment;
1840 }
1841
1842
1843 XMLText* XMLDocument::NewText( const char* str )
1844 {
1845 TIXMLASSERT( sizeof( XMLText ) == _textPool.ItemSize() );
1846 XMLText* text = new (_textPool.Alloc()) XMLText( this );
1847 text->_memPool = &_textPool;
1848 text->SetValue( str );
1849 return text;
1850 }
1851
1852
1853 XMLDeclaration* XMLDocument::NewDeclaration( const char* str )
1854 {
1855 TIXMLASSERT( sizeof( XMLDeclaration ) == _commentPool.ItemSize() );
1856 XMLDeclaration* dec = new (_commentPool.Alloc()) XMLDeclaration( this );
1857 dec->_memPool = &_commentPool;
1858 dec->SetValue( str ? str : "xml version=\"1.0\" encoding=\"UTF-8\"" );
1859 return dec;
1860 }
1861
1862
1863 XMLUnknown* XMLDocument::NewUnknown( const char* str )
1864 {
1865 TIXMLASSERT( sizeof( XMLUnknown ) == _commentPool.ItemSize() );
1866 XMLUnknown* unk = new (_commentPool.Alloc()) XMLUnknown( this );
1867 unk->_memPool = &_commentPool;
1868 unk->SetValue( str );
1869 return unk;
1870 }
1871
1872 static FILE* callfopen( const char* filepath, const char* mode )
1873 {
1874 TIXMLASSERT( filepath );
1875 TIXMLASSERT( mode );
1876 #if defined(_MSC_VER) && (_MSC_VER >= 1400 ) && (!defined WINCE)
1877 FILE* fp = 0;
1878 errno_t err = fopen_s( &fp, filepath, mode );
1879 if ( err ) {
1880 return 0;
1881 }
1882 #else
1883 FILE* fp = fopen( filepath, mode );
1884 #endif
1885 return fp;
1886 }
1887
1888 void XMLDocument::DeleteNode( XMLNode* node ) {
1889 TIXMLASSERT( node );
1890 TIXMLASSERT(node->_document == this );
1891 if (node->_parent) {
1892 node->_parent->DeleteChild( node );
1893 }
1894 else {
1895 // Isn't in the tree.
1896 // Use the parent delete.
1897 // Also, we need to mark it tracked: we 'know'
1898 // it was never used.
1899 node->_memPool->SetTracked();
1900 // Call the static XMLNode version:
1901 XMLNode::DeleteNode(node);
1902 }
1903 }
1904
1905
1906 XMLError XMLDocument::LoadFile( const char* filename )
1907 {
1908 Clear();
1909 FILE* fp = callfopen( filename, "rb" );
1910 if ( !fp ) {
1911 SetError( XML_ERROR_FILE_NOT_FOUND, filename, 0 );
1912 return _errorID;
1913 }
1914 LoadFile( fp );
1915 fclose( fp );
1916 return _errorID;
1917 }
1918
1919 // This is likely overengineered template art to have a check that unsigned long value incremented
1920 // by one still fits into size_t. If size_t type is larger than unsigned long type
1921 // (x86_64-w64-mingw32 target) then the check is redundant and gcc and clang emit
1922 // -Wtype-limits warning. This piece makes the compiler select code with a check when a check
1923 // is useful and code with no check when a check is redundant depending on how size_t and unsigned long
1924 // types sizes relate to each other.
1925 template
1926 <bool = (sizeof(unsigned long) >= sizeof(size_t))>
1927 struct LongFitsIntoSizeTMinusOne {
1928 static bool Fits( unsigned long value )
1929 {
1930 return value < (size_t)-1;
1931 }
1932 };
1933
1934 template <>
1935 bool LongFitsIntoSizeTMinusOne<false>::Fits( unsigned long /*value*/ )
1936 {
1937 return true;
1938 }
1939
1940 XMLError XMLDocument::LoadFile( FILE* fp )
1941 {
1942 Clear();
1943
1944 fseek( fp, 0, SEEK_SET );
1945 if ( fgetc( fp ) == EOF && ferror( fp ) != 0 ) {
1946 SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
1947 return _errorID;
1948 }
1949
1950 fseek( fp, 0, SEEK_END );
1951 const long filelength = ftell( fp );
1952 fseek( fp, 0, SEEK_SET );
1953 if ( filelength == -1L ) {
1954 SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
1955 return _errorID;
1956 }
1957 TIXMLASSERT( filelength >= 0 );
1958
1959 if ( !LongFitsIntoSizeTMinusOne<>::Fits( filelength ) ) {
1960 // Cannot handle files which won't fit in buffer together with null terminator
1961 SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
1962 return _errorID;
1963 }
1964
1965 if ( filelength == 0 ) {
1966 SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
1967 return _errorID;
1968 }
1969
1970 const size_t size = filelength;
1971 TIXMLASSERT( _charBuffer == 0 );
1972 _charBuffer = new char[size+1];
1973 size_t read = fread( _charBuffer, 1, size, fp );
1974 if ( read != size ) {
1975 SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
1976 return _errorID;
1977 }
1978
1979 _charBuffer[size] = 0;
1980
1981 Parse();
1982 return _errorID;
1983 }
1984
1985
1986 XMLError XMLDocument::SaveFile( const char* filename, bool compact )
1987 {
1988 FILE* fp = callfopen( filename, "w" );
1989 if ( !fp ) {
1990 SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, filename, 0 );
1991 return _errorID;
1992 }
1993 SaveFile(fp, compact);
1994 fclose( fp );
1995 return _errorID;
1996 }
1997
1998
1999 XMLError XMLDocument::SaveFile( FILE* fp, bool compact )
2000 {
2001 // Clear any error from the last save, otherwise it will get reported
2002 // for *this* call.
2003 SetError( XML_NO_ERROR, 0, 0 );
2004 XMLPrinter stream( fp, compact );
2005 Print( &stream );
2006 return _errorID;
2007 }
2008
2009
2010 XMLError XMLDocument::Parse( const char* p, size_t len )
2011 {
2012 Clear();
2013
2014 if ( len == 0 || !p || !*p ) {
2015 SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
2016 return _errorID;
2017 }
2018 if ( len == (size_t)(-1) ) {
2019 len = strlen( p );
2020 }
2021 TIXMLASSERT( _charBuffer == 0 );
2022 _charBuffer = new char[ len+1 ];
2023 memcpy( _charBuffer, p, len );
2024 _charBuffer[len] = 0;
2025
2026 Parse();
2027 if ( Error() ) {
2028 // clean up now essentially dangling memory.
2029 // and the parse fail can put objects in the
2030 // pools that are dead and inaccessible.
2031 DeleteChildren();
2032 _elementPool.Clear();
2033 _attributePool.Clear();
2034 _textPool.Clear();
2035 _commentPool.Clear();
2036 }
2037 return _errorID;
2038 }
2039
2040
2041 void XMLDocument::Print( XMLPrinter* streamer ) const
2042 {
2043 if ( streamer ) {
2044 Accept( streamer );
2045 }
2046 else {
2047 XMLPrinter stdoutStreamer( stdout );
2048 Accept( &stdoutStreamer );
2049 }
2050 }
2051
2052
2053 void XMLDocument::SetError( XMLError error, const char* str1, const char* str2 )
2054 {
2055 TIXMLASSERT( error >= 0 && error < XML_ERROR_COUNT );
2056 _errorID = error;
2057 _errorStr1 = str1;
2058 _errorStr2 = str2;
2059 }
2060
2061 const char* XMLDocument::ErrorName() const
2062 {
2063 TIXMLASSERT( _errorID >= 0 && _errorID < XML_ERROR_COUNT );
2064 const char* errorName = _errorNames[_errorID];
2065 TIXMLASSERT( errorName && errorName[0] );
2066 return errorName;
2067 }
2068
2069 void XMLDocument::PrintError() const
2070 {
2071 if ( Error() ) {
2072 static const int LEN = 20;
2073 char buf1[LEN] = { 0 };
2074 char buf2[LEN] = { 0 };
2075
2076 if ( _errorStr1 ) {
2077 TIXML_SNPRINTF( buf1, LEN, "%s", _errorStr1 );
2078 }
2079 if ( _errorStr2 ) {
2080 TIXML_SNPRINTF( buf2, LEN, "%s", _errorStr2 );
2081 }
2082
2083 // Should check INT_MIN <= _errorID && _errorId <= INT_MAX, but that
2084 // causes a clang "always true" -Wtautological-constant-out-of-range-compare warning
2085 TIXMLASSERT( 0 <= _errorID && XML_ERROR_COUNT - 1 <= INT_MAX );
2086 printf( "XMLDocument error id=%d '%s' str1=%s str2=%s\n",
2087 static_cast<int>( _errorID ), ErrorName(), buf1, buf2 );
2088 }
2089 }
2090
2091 void XMLDocument::Parse()
2092 {
2093 TIXMLASSERT( NoChildren() ); // Clear() must have been called previously
2094 TIXMLASSERT( _charBuffer );
2095 char* p = _charBuffer;
2096 p = XMLUtil::SkipWhiteSpace( p );
2097 p = const_cast<char*>( XMLUtil::ReadBOM( p, &_writeBOM ) );
2098 if ( !*p ) {
2099 SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
2100 return;
2101 }
2102 ParseDeep(p, 0 );
2103 }
2104
2105 XMLPrinter::XMLPrinter( FILE* file, bool compact, int depth ) :
2106 _elementJustOpened( false ),
2107 _firstElement( true ),
2108 _fp( file ),
2109 _depth( depth ),
2110 _textDepth( -1 ),
2111 _processEntities( true ),
2112 _compactMode( compact )
2113 {
2114 for( int i=0; i<ENTITY_RANGE; ++i ) {
2115 _entityFlag[i] = false;
2116 _restrictedEntityFlag[i] = false;
2117 }
2118 for( int i=0; i<NUM_ENTITIES; ++i ) {
2119 const char entityValue = entities[i].value;
2120 TIXMLASSERT( 0 <= entityValue && entityValue < ENTITY_RANGE );
2121 _entityFlag[ (unsigned char)entityValue ] = true;
2122 }
2123 _restrictedEntityFlag[(unsigned char)'&'] = true;
2124 _restrictedEntityFlag[(unsigned char)'<'] = true;
2125 _restrictedEntityFlag[(unsigned char)'>'] = true; // not required, but consistency is nice
2126 _buffer.Push( 0 );
2127 }
2128
2129
2130 void XMLPrinter::Print( const char* format, ... )
2131 {
2132 va_list va;
2133 va_start( va, format );
2134
2135 if ( _fp ) {
2136 vfprintf( _fp, format, va );
2137 }
2138 else {
2139 const int len = TIXML_VSCPRINTF( format, va );
2140 // Close out and re-start the va-args
2141 va_end( va );
2142 TIXMLASSERT( len >= 0 );
2143 va_start( va, format );
2144 TIXMLASSERT( _buffer.Size() > 0 && _buffer[_buffer.Size() - 1] == 0 );
2145 char* p = _buffer.PushArr( len ) - 1; // back up over the null terminator.
2146 TIXML_VSNPRINTF( p, len+1, format, va );
2147 }
2148 va_end( va );
2149 }
2150
2151
2152 void XMLPrinter::PrintSpace( int depth )
2153 {
2154 for( int i=0; i<depth; ++i ) {
2155 Print( " " );
2156 }
2157 }
2158
2159
2160 void XMLPrinter::PrintString( const char* p, bool restricted )
2161 {
2162 // Look for runs of bytes between entities to print.
2163 const char* q = p;
2164
2165 if ( _processEntities ) {
2166 const bool* flag = restricted ? _restrictedEntityFlag : _entityFlag;
2167 while ( *q ) {
2168 TIXMLASSERT( p <= q );
2169 // Remember, char is sometimes signed. (How many times has that bitten me?)
2170 if ( *q > 0 && *q < ENTITY_RANGE ) {
2171 // Check for entities. If one is found, flush
2172 // the stream up until the entity, write the
2173 // entity, and keep looking.
2174 if ( flag[(unsigned char)(*q)] ) {
2175 while ( p < q ) {
2176 const size_t delta = q - p;
2177 // %.*s accepts type int as "precision"
2178 const int toPrint = ( INT_MAX < delta ) ? INT_MAX : (int)delta;
2179 Print( "%.*s", toPrint, p );
2180 p += toPrint;
2181 }
2182 bool entityPatternPrinted = false;
2183 for( int i=0; i<NUM_ENTITIES; ++i ) {
2184 if ( entities[i].value == *q ) {
2185 Print( "&%s;", entities[i].pattern );
2186 entityPatternPrinted = true;
2187 break;
2188 }
2189 }
2190 if ( !entityPatternPrinted ) {
2191 // TIXMLASSERT( entityPatternPrinted ) causes gcc -Wunused-but-set-variable in release
2192 TIXMLASSERT( false );
2193 }
2194 ++p;
2195 }
2196 }
2197 ++q;
2198 TIXMLASSERT( p <= q );
2199 }
2200 }
2201 // Flush the remaining string. This will be the entire
2202 // string if an entity wasn't found.
2203 TIXMLASSERT( p <= q );
2204 if ( !_processEntities || ( p < q ) ) {
2205 Print( "%s", p );
2206 }
2207 }
2208
2209
2210 void XMLPrinter::PushHeader( bool writeBOM, bool writeDec )
2211 {
2212 if ( writeBOM ) {
2213 static const unsigned char bom[] = { TIXML_UTF_LEAD_0, TIXML_UTF_LEAD_1, TIXML_UTF_LEAD_2, 0 };
2214 Print( "%s", bom );
2215 }
2216 if ( writeDec ) {
2217 PushDeclaration( "xml version=\"1.0\"" );
2218 }
2219 }
2220
2221
2222 void XMLPrinter::OpenElement( const char* name, bool compactMode )
2223 {
2224 SealElementIfJustOpened();
2225 _stack.Push( name );
2226
2227 if ( _textDepth < 0 && !_firstElement && !compactMode ) {
2228 Print( "\n" );
2229 }
2230 if ( !compactMode ) {
2231 PrintSpace( _depth );
2232 }
2233
2234 Print( "<%s", name );
2235 _elementJustOpened = true;
2236 _firstElement = false;
2237 ++_depth;
2238 }
2239
2240
2241 void XMLPrinter::PushAttribute( const char* name, const char* value )
2242 {
2243 TIXMLASSERT( _elementJustOpened );
2244 Print( " %s=\"", name );
2245 PrintString( value, false );
2246 Print( "\"" );
2247 }
2248
2249
2250 void XMLPrinter::PushAttribute( const char* name, int v )
2251 {
2252 char buf[BUF_SIZE];
2253 XMLUtil::ToStr( v, buf, BUF_SIZE );
2254 PushAttribute( name, buf );
2255 }
2256
2257
2258 void XMLPrinter::PushAttribute( const char* name, unsigned v )
2259 {
2260 char buf[BUF_SIZE];
2261 XMLUtil::ToStr( v, buf, BUF_SIZE );
2262 PushAttribute( name, buf );
2263 }
2264
2265
2266 void XMLPrinter::PushAttribute( const char* name, bool v )
2267 {
2268 char buf[BUF_SIZE];
2269 XMLUtil::ToStr( v, buf, BUF_SIZE );
2270 PushAttribute( name, buf );
2271 }
2272
2273
2274 void XMLPrinter::PushAttribute( const char* name, double v )
2275 {
2276 char buf[BUF_SIZE];
2277 XMLUtil::ToStr( v, buf, BUF_SIZE );
2278 PushAttribute( name, buf );
2279 }
2280
2281
2282 void XMLPrinter::CloseElement( bool compactMode )
2283 {
2284 --_depth;
2285 const char* name = _stack.Pop();
2286
2287 if ( _elementJustOpened ) {
2288 Print( "/>" );
2289 }
2290 else {
2291 if ( _textDepth < 0 && !compactMode) {
2292 Print( "\n" );
2293 PrintSpace( _depth );
2294 }
2295 Print( "</%s>", name );
2296 }
2297
2298 if ( _textDepth == _depth ) {
2299 _textDepth = -1;
2300 }
2301 if ( _depth == 0 && !compactMode) {
2302 Print( "\n" );
2303 }
2304 _elementJustOpened = false;
2305 }
2306
2307
2308 void XMLPrinter::SealElementIfJustOpened()
2309 {
2310 if ( !_elementJustOpened ) {
2311 return;
2312 }
2313 _elementJustOpened = false;
2314 Print( ">" );
2315 }
2316
2317
2318 void XMLPrinter::PushText( const char* text, bool cdata )
2319 {
2320 _textDepth = _depth-1;
2321
2322 SealElementIfJustOpened();
2323 if ( cdata ) {
2324 Print( "<![CDATA[%s]]>", text );
2325 }
2326 else {
2327 PrintString( text, true );
2328 }
2329 }
2330
2331 void XMLPrinter::PushText( int value )
2332 {
2333 char buf[BUF_SIZE];
2334 XMLUtil::ToStr( value, buf, BUF_SIZE );
2335 PushText( buf, false );
2336 }
2337
2338
2339 void XMLPrinter::PushText( unsigned value )
2340 {
2341 char buf[BUF_SIZE];
2342 XMLUtil::ToStr( value, buf, BUF_SIZE );
2343 PushText( buf, false );
2344 }
2345
2346
2347 void XMLPrinter::PushText( bool value )
2348 {
2349 char buf[BUF_SIZE];
2350 XMLUtil::ToStr( value, buf, BUF_SIZE );
2351 PushText( buf, false );
2352 }
2353
2354
2355 void XMLPrinter::PushText( float value )
2356 {
2357 char buf[BUF_SIZE];
2358 XMLUtil::ToStr( value, buf, BUF_SIZE );
2359 PushText( buf, false );
2360 }
2361
2362
2363 void XMLPrinter::PushText( double value )
2364 {
2365 char buf[BUF_SIZE];
2366 XMLUtil::ToStr( value, buf, BUF_SIZE );
2367 PushText( buf, false );
2368 }
2369
2370
2371 void XMLPrinter::PushComment( const char* comment )
2372 {
2373 SealElementIfJustOpened();
2374 if ( _textDepth < 0 && !_firstElement && !_compactMode) {
2375 Print( "\n" );
2376 PrintSpace( _depth );
2377 }
2378 _firstElement = false;
2379 Print( "<!--%s-->", comment );
2380 }
2381
2382
2383 void XMLPrinter::PushDeclaration( const char* value )
2384 {
2385 SealElementIfJustOpened();
2386 if ( _textDepth < 0 && !_firstElement && !_compactMode) {
2387 Print( "\n" );
2388 PrintSpace( _depth );
2389 }
2390 _firstElement = false;
2391 Print( "<?%s?>", value );
2392 }
2393
2394
2395 void XMLPrinter::PushUnknown( const char* value )
2396 {
2397 SealElementIfJustOpened();
2398 if ( _textDepth < 0 && !_firstElement && !_compactMode) {
2399 Print( "\n" );
2400 PrintSpace( _depth );
2401 }
2402 _firstElement = false;
2403 Print( "<!%s>", value );
2404 }
2405
2406
2407 bool XMLPrinter::VisitEnter( const XMLDocument& doc )
2408 {
2409 _processEntities = doc.ProcessEntities();
2410 if ( doc.HasBOM() ) {
2411 PushHeader( true, false );
2412 }
2413 return true;
2414 }
2415
2416
2417 bool XMLPrinter::VisitEnter( const XMLElement& element, const XMLAttribute* attribute )
2418 {
2419 const XMLElement* parentElem = 0;
2420 if ( element.Parent() ) {
2421 parentElem = element.Parent()->ToElement();
2422 }
2423 const bool compactMode = parentElem ? CompactMode( *parentElem ) : _compactMode;
2424 OpenElement( element.Name(), compactMode );
2425 while ( attribute ) {
2426 PushAttribute( attribute->Name(), attribute->Value() );
2427 attribute = attribute->Next();
2428 }
2429 return true;
2430 }
2431
2432
2433 bool XMLPrinter::VisitExit( const XMLElement& element )
2434 {
2435 CloseElement( CompactMode(element) );
2436 return true;
2437 }
2438
2439
2440 bool XMLPrinter::Visit( const XMLText& text )
2441 {
2442 PushText( text.Value(), text.CData() );
2443 return true;
2444 }
2445
2446
2447 bool XMLPrinter::Visit( const XMLComment& comment )
2448 {
2449 PushComment( comment.Value() );
2450 return true;
2451 }
2452
2453 bool XMLPrinter::Visit( const XMLDeclaration& declaration )
2454 {
2455 PushDeclaration( declaration.Value() );
2456 return true;
2457 }
2458
2459
2460 bool XMLPrinter::Visit( const XMLUnknown& unknown )
2461 {
2462 PushUnknown( unknown.Value() );
2463 return true;
2464 }
2465
2466 } // namespace tinyxml2
2467