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