stratgame

view level/src/tinyxml2.cc @ 0:86b53f76899f

foo
author John Tsiombikas <nuclear@member.fsf.org>
date Mon, 21 May 2012 19:07:40 +0300
parents
children
line source
1 /*
2 Original code by Lee Thomason (www.grinninglizard.com)
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.
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:
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.
17 2. Altered source versions must be plainly marked as such, and
18 must not be misrepresented as being the original software.
20 3. This notice may not be removed or altered from any source
21 distribution.
22 */
24 #include "tinyxml2.h"
26 #include <cstdio>
27 #include <cstdlib>
28 #include <new>
29 #include <cstddef>
31 using namespace tinyxml2;
33 static const char LINE_FEED = (char)0x0a; // all line endings are normalized to LF
34 static const char LF = LINE_FEED;
35 static const char CARRIAGE_RETURN = (char)0x0d; // CR gets filtered out
36 static const char CR = CARRIAGE_RETURN;
37 static const char SINGLE_QUOTE = '\'';
38 static const char DOUBLE_QUOTE = '\"';
40 // Bunch of unicode info at:
41 // http://www.unicode.org/faq/utf_bom.html
42 // ef bb bf (Microsoft "lead bytes") - designates UTF-8
44 static const unsigned char TIXML_UTF_LEAD_0 = 0xefU;
45 static const unsigned char TIXML_UTF_LEAD_1 = 0xbbU;
46 static const unsigned char TIXML_UTF_LEAD_2 = 0xbfU;
49 #define DELETE_NODE( node ) { \
50 if ( node ) { \
51 MemPool* pool = node->memPool; \
52 node->~XMLNode(); \
53 pool->Free( node ); \
54 } \
55 }
56 #define DELETE_ATTRIBUTE( attrib ) { \
57 if ( attrib ) { \
58 MemPool* pool = attrib->memPool; \
59 attrib->~XMLAttribute(); \
60 pool->Free( attrib ); \
61 } \
62 }
64 struct Entity {
65 const char* pattern;
66 int length;
67 char value;
68 };
70 static const int NUM_ENTITIES = 5;
71 static const Entity entities[NUM_ENTITIES] =
72 {
73 { "quot", 4, DOUBLE_QUOTE },
74 { "amp", 3, '&' },
75 { "apos", 4, SINGLE_QUOTE },
76 { "lt", 2, '<' },
77 { "gt", 2, '>' }
78 };
81 StrPair::~StrPair()
82 {
83 Reset();
84 }
87 void StrPair::Reset()
88 {
89 if ( flags & NEEDS_DELETE ) {
90 delete [] start;
91 }
92 flags = 0;
93 start = 0;
94 end = 0;
95 }
98 void StrPair::SetStr( const char* str, int flags )
99 {
100 Reset();
101 size_t len = strlen( str );
102 start = new char[ len+1 ];
103 memcpy( start, str, len+1 );
104 end = start + len;
105 this->flags = flags | NEEDS_DELETE;
106 }
109 char* StrPair::ParseText( char* p, const char* endTag, int strFlags )
110 {
111 TIXMLASSERT( endTag && *endTag );
113 char* start = p; // fixme: hides a member
114 char endChar = *endTag;
115 size_t length = strlen( endTag );
117 // Inner loop of text parsing.
118 while ( *p ) {
119 if ( *p == endChar && strncmp( p, endTag, length ) == 0 ) {
120 Set( start, p, strFlags );
121 return p + length;
122 }
123 ++p;
124 }
125 return 0;
126 }
129 char* StrPair::ParseName( char* p )
130 {
131 char* start = p;
133 if ( !start || !(*start) ) {
134 return 0;
135 }
137 if ( !XMLUtil::IsAlpha( *p ) ) {
138 return 0;
139 }
141 while( *p && (
142 XMLUtil::IsAlphaNum( (unsigned char) *p )
143 || *p == '_'
144 || *p == '-'
145 || *p == '.'
146 || *p == ':' ))
147 {
148 ++p;
149 }
151 if ( p > start ) {
152 Set( start, p, 0 );
153 return p;
154 }
155 return 0;
156 }
160 const char* StrPair::GetStr()
161 {
162 if ( flags & NEEDS_FLUSH ) {
163 *end = 0;
164 flags ^= NEEDS_FLUSH;
166 if ( flags ) {
167 char* p = start; // the read pointer
168 char* q = start; // the write pointer
170 while( p < end ) {
171 if ( (flags & NEEDS_NEWLINE_NORMALIZATION) && *p == CR ) {
172 // CR-LF pair becomes LF
173 // CR alone becomes LF
174 // LF-CR becomes LF
175 if ( *(p+1) == LF ) {
176 p += 2;
177 }
178 else {
179 ++p;
180 }
181 *q++ = LF;
182 }
183 else if ( (flags & NEEDS_NEWLINE_NORMALIZATION) && *p == LF ) {
184 if ( *(p+1) == CR ) {
185 p += 2;
186 }
187 else {
188 ++p;
189 }
190 *q++ = LF;
191 }
192 else if ( (flags & NEEDS_ENTITY_PROCESSING) && *p == '&' ) {
193 int i=0;
195 // Entities handled by tinyXML2:
196 // - special entities in the entity table [in/out]
197 // - numeric character reference [in]
198 // &#20013; or &#x4e2d;
200 if ( *(p+1) == '#' ) {
201 char buf[10] = { 0 };
202 int len;
203 p = const_cast<char*>( XMLUtil::GetCharacterRef( p, buf, &len ) );
204 for( int i=0; i<len; ++i ) {
205 *q++ = buf[i];
206 }
207 TIXMLASSERT( q <= p );
208 }
209 else {
210 for( i=0; i<NUM_ENTITIES; ++i ) {
211 if ( strncmp( p+1, entities[i].pattern, entities[i].length ) == 0
212 && *(p+entities[i].length+1) == ';' )
213 {
214 // Found an entity convert;
215 *q = entities[i].value;
216 ++q;
217 p += entities[i].length + 2;
218 break;
219 }
220 }
221 if ( i == NUM_ENTITIES ) {
222 // fixme: treat as error?
223 ++p;
224 ++q;
225 }
226 }
227 }
228 else {
229 *q = *p;
230 ++p;
231 ++q;
232 }
233 }
234 *q = 0;
235 }
236 flags = (flags & NEEDS_DELETE);
237 }
238 return start;
239 }
244 // --------- XMLUtil ----------- //
246 const char* XMLUtil::ReadBOM( const char* p, bool* bom )
247 {
248 *bom = false;
249 const unsigned char* pu = reinterpret_cast<const unsigned char*>(p);
250 // Check for BOM:
251 if ( *(pu+0) == TIXML_UTF_LEAD_0
252 && *(pu+1) == TIXML_UTF_LEAD_1
253 && *(pu+2) == TIXML_UTF_LEAD_2 )
254 {
255 *bom = true;
256 p += 3;
257 }
258 return p;
259 }
262 void XMLUtil::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length )
263 {
264 const unsigned long BYTE_MASK = 0xBF;
265 const unsigned long BYTE_MARK = 0x80;
266 const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
268 if (input < 0x80)
269 *length = 1;
270 else if ( input < 0x800 )
271 *length = 2;
272 else if ( input < 0x10000 )
273 *length = 3;
274 else if ( input < 0x200000 )
275 *length = 4;
276 else
277 { *length = 0; return; } // This code won't covert this correctly anyway.
279 output += *length;
281 // Scary scary fall throughs.
282 switch (*length)
283 {
284 case 4:
285 --output;
286 *output = (char)((input | BYTE_MARK) & BYTE_MASK);
287 input >>= 6;
288 case 3:
289 --output;
290 *output = (char)((input | BYTE_MARK) & BYTE_MASK);
291 input >>= 6;
292 case 2:
293 --output;
294 *output = (char)((input | BYTE_MARK) & BYTE_MASK);
295 input >>= 6;
296 case 1:
297 --output;
298 *output = (char)(input | FIRST_BYTE_MARK[*length]);
299 }
300 }
303 const char* XMLUtil::GetCharacterRef( const char* p, char* value, int* length )
304 {
305 // Presume an entity, and pull it out.
306 *length = 0;
308 if ( *(p+1) == '#' && *(p+2) )
309 {
310 unsigned long ucs = 0;
311 ptrdiff_t delta = 0;
312 unsigned mult = 1;
314 if ( *(p+2) == 'x' )
315 {
316 // Hexadecimal.
317 if ( !*(p+3) ) return 0;
319 const char* q = p+3;
320 q = strchr( q, ';' );
322 if ( !q || !*q ) return 0;
324 delta = q-p;
325 --q;
327 while ( *q != 'x' )
328 {
329 if ( *q >= '0' && *q <= '9' )
330 ucs += mult * (*q - '0');
331 else if ( *q >= 'a' && *q <= 'f' )
332 ucs += mult * (*q - 'a' + 10);
333 else if ( *q >= 'A' && *q <= 'F' )
334 ucs += mult * (*q - 'A' + 10 );
335 else
336 return 0;
337 mult *= 16;
338 --q;
339 }
340 }
341 else
342 {
343 // Decimal.
344 if ( !*(p+2) ) return 0;
346 const char* q = p+2;
347 q = strchr( q, ';' );
349 if ( !q || !*q ) return 0;
351 delta = q-p;
352 --q;
354 while ( *q != '#' )
355 {
356 if ( *q >= '0' && *q <= '9' )
357 ucs += mult * (*q - '0');
358 else
359 return 0;
360 mult *= 10;
361 --q;
362 }
363 }
364 // convert the UCS to UTF-8
365 ConvertUTF32ToUTF8( ucs, value, length );
366 return p + delta + 1;
367 }
368 return p+1;
369 }
372 char* XMLDocument::Identify( char* p, XMLNode** node )
373 {
374 XMLNode* returnNode = 0;
375 char* start = p;
376 p = XMLUtil::SkipWhiteSpace( p );
377 if( !p || !*p )
378 {
379 return p;
380 }
382 // What is this thing?
383 // - Elements start with a letter or underscore, but xml is reserved.
384 // - Comments: <!--
385 // - Decleration: <?
386 // - Everthing else is unknown to tinyxml.
387 //
389 static const char* xmlHeader = { "<?" };
390 static const char* commentHeader = { "<!--" };
391 static const char* dtdHeader = { "<!" };
392 static const char* cdataHeader = { "<![CDATA[" };
393 static const char* elementHeader = { "<" }; // and a header for everything else; check last.
395 static const int xmlHeaderLen = 2;
396 static const int commentHeaderLen = 4;
397 static const int dtdHeaderLen = 2;
398 static const int cdataHeaderLen = 9;
399 static const int elementHeaderLen = 1;
401 #if defined(_MSC_VER)
402 #pragma warning ( push )
403 #pragma warning ( disable : 4127 )
404 #endif
405 TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLUnknown ) ); // use same memory pool
406 TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLDeclaration ) ); // use same memory pool
407 #if defined(_MSC_VER)
408 #pragma warning (pop)
409 #endif
410 if ( XMLUtil::StringEqual( p, xmlHeader, xmlHeaderLen ) ) {
411 returnNode = new (commentPool.Alloc()) XMLDeclaration( this );
412 returnNode->memPool = &commentPool;
413 p += xmlHeaderLen;
414 }
415 else if ( XMLUtil::StringEqual( p, commentHeader, commentHeaderLen ) ) {
416 returnNode = new (commentPool.Alloc()) XMLComment( this );
417 returnNode->memPool = &commentPool;
418 p += commentHeaderLen;
419 }
420 else if ( XMLUtil::StringEqual( p, cdataHeader, cdataHeaderLen ) ) {
421 XMLText* text = new (textPool.Alloc()) XMLText( this );
422 returnNode = text;
423 returnNode->memPool = &textPool;
424 p += cdataHeaderLen;
425 text->SetCData( true );
426 }
427 else if ( XMLUtil::StringEqual( p, dtdHeader, dtdHeaderLen ) ) {
428 returnNode = new (commentPool.Alloc()) XMLUnknown( this );
429 returnNode->memPool = &commentPool;
430 p += dtdHeaderLen;
431 }
432 else if ( XMLUtil::StringEqual( p, elementHeader, elementHeaderLen ) ) {
433 returnNode = new (elementPool.Alloc()) XMLElement( this );
434 returnNode->memPool = &elementPool;
435 p += elementHeaderLen;
436 }
437 else {
438 returnNode = new (textPool.Alloc()) XMLText( this );
439 returnNode->memPool = &textPool;
440 p = start; // Back it up, all the text counts.
441 }
443 *node = returnNode;
444 return p;
445 }
448 bool XMLDocument::Accept( XMLVisitor* visitor ) const
449 {
450 if ( visitor->VisitEnter( *this ) )
451 {
452 for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() )
453 {
454 if ( !node->Accept( visitor ) )
455 break;
456 }
457 }
458 return visitor->VisitExit( *this );
459 }
462 // --------- XMLNode ----------- //
464 XMLNode::XMLNode( XMLDocument* doc ) :
465 document( doc ),
466 parent( 0 ),
467 firstChild( 0 ), lastChild( 0 ),
468 prev( 0 ), next( 0 )
469 {
470 }
473 XMLNode::~XMLNode()
474 {
475 DeleteChildren();
476 if ( parent ) {
477 parent->Unlink( this );
478 }
479 }
482 void XMLNode::SetValue( const char* str, bool staticMem )
483 {
484 if ( staticMem )
485 value.SetInternedStr( str );
486 else
487 value.SetStr( str );
488 }
491 void XMLNode::DeleteChildren()
492 {
493 while( firstChild ) {
494 XMLNode* node = firstChild;
495 Unlink( node );
497 DELETE_NODE( node );
498 }
499 firstChild = lastChild = 0;
500 }
503 void XMLNode::Unlink( XMLNode* child )
504 {
505 TIXMLASSERT( child->parent == this );
506 if ( child == firstChild )
507 firstChild = firstChild->next;
508 if ( child == lastChild )
509 lastChild = lastChild->prev;
511 if ( child->prev ) {
512 child->prev->next = child->next;
513 }
514 if ( child->next ) {
515 child->next->prev = child->prev;
516 }
517 child->parent = 0;
518 }
521 void XMLNode::DeleteChild( XMLNode* node )
522 {
523 TIXMLASSERT( node->parent == this );
524 DELETE_NODE( node );
525 }
528 XMLNode* XMLNode::InsertEndChild( XMLNode* addThis )
529 {
530 if ( lastChild ) {
531 TIXMLASSERT( firstChild );
532 TIXMLASSERT( lastChild->next == 0 );
533 lastChild->next = addThis;
534 addThis->prev = lastChild;
535 lastChild = addThis;
537 addThis->next = 0;
538 }
539 else {
540 TIXMLASSERT( firstChild == 0 );
541 firstChild = lastChild = addThis;
543 addThis->prev = 0;
544 addThis->next = 0;
545 }
546 addThis->parent = this;
547 return addThis;
548 }
551 XMLNode* XMLNode::InsertFirstChild( XMLNode* addThis )
552 {
553 if ( firstChild ) {
554 TIXMLASSERT( lastChild );
555 TIXMLASSERT( firstChild->prev == 0 );
557 firstChild->prev = addThis;
558 addThis->next = firstChild;
559 firstChild = addThis;
561 addThis->prev = 0;
562 }
563 else {
564 TIXMLASSERT( lastChild == 0 );
565 firstChild = lastChild = addThis;
567 addThis->prev = 0;
568 addThis->next = 0;
569 }
570 addThis->parent = this;
571 return addThis;
572 }
575 XMLNode* XMLNode::InsertAfterChild( XMLNode* afterThis, XMLNode* addThis )
576 {
577 TIXMLASSERT( afterThis->parent == this );
578 if ( afterThis->parent != this )
579 return 0;
581 if ( afterThis->next == 0 ) {
582 // The last node or the only node.
583 return InsertEndChild( addThis );
584 }
585 addThis->prev = afterThis;
586 addThis->next = afterThis->next;
587 afterThis->next->prev = addThis;
588 afterThis->next = addThis;
589 addThis->parent = this;
590 return addThis;
591 }
596 const XMLElement* XMLNode::FirstChildElement( const char* value ) const
597 {
598 for( XMLNode* node=firstChild; node; node=node->next ) {
599 XMLElement* element = node->ToElement();
600 if ( element ) {
601 if ( !value || XMLUtil::StringEqual( element->Name(), value ) ) {
602 return element;
603 }
604 }
605 }
606 return 0;
607 }
610 const XMLElement* XMLNode::LastChildElement( const char* value ) const
611 {
612 for( XMLNode* node=lastChild; node; node=node->prev ) {
613 XMLElement* element = node->ToElement();
614 if ( element ) {
615 if ( !value || XMLUtil::StringEqual( element->Name(), value ) ) {
616 return element;
617 }
618 }
619 }
620 return 0;
621 }
624 const XMLElement* XMLNode::NextSiblingElement( const char* value ) const
625 {
626 for( XMLNode* element=this->next; element; element = element->next ) {
627 if ( element->ToElement()
628 && (!value || XMLUtil::StringEqual( value, element->Value() )))
629 {
630 return element->ToElement();
631 }
632 }
633 return 0;
634 }
637 const XMLElement* XMLNode::PreviousSiblingElement( const char* value ) const
638 {
639 for( XMLNode* element=this->prev; element; element = element->prev ) {
640 if ( element->ToElement()
641 && (!value || XMLUtil::StringEqual( value, element->Value() )))
642 {
643 return element->ToElement();
644 }
645 }
646 return 0;
647 }
650 char* XMLNode::ParseDeep( char* p, StrPair* parentEnd )
651 {
652 // This is a recursive method, but thinking about it "at the current level"
653 // it is a pretty simple flat list:
654 // <foo/>
655 // <!-- comment -->
656 //
657 // With a special case:
658 // <foo>
659 // </foo>
660 // <!-- comment -->
661 //
662 // Where the closing element (/foo) *must* be the next thing after the opening
663 // element, and the names must match. BUT the tricky bit is that the closing
664 // element will be read by the child.
665 //
666 // 'endTag' is the end tag for this node, it is returned by a call to a child.
667 // 'parentEnd' is the end tag for the parent, which is filled in and returned.
669 while( p && *p ) {
670 XMLNode* node = 0;
672 p = document->Identify( p, &node );
673 if ( p == 0 || node == 0 ) {
674 break;
675 }
677 StrPair endTag;
678 p = node->ParseDeep( p, &endTag );
679 if ( !p ) {
680 DELETE_NODE( node );
681 node = 0;
682 if ( !document->Error() ) {
683 document->SetError( XML_ERROR_PARSING, 0, 0 );
684 }
685 break;
686 }
688 // We read the end tag. Return it to the parent.
689 if ( node->ToElement() && node->ToElement()->ClosingType() == XMLElement::CLOSING ) {
690 if ( parentEnd ) {
691 *parentEnd = ((XMLElement*)node)->value;
692 }
693 DELETE_NODE( node );
694 return p;
695 }
697 // Handle an end tag returned to this level.
698 // And handle a bunch of annoying errors.
699 XMLElement* ele = node->ToElement();
700 if ( ele ) {
701 if ( endTag.Empty() && ele->ClosingType() == XMLElement::OPEN ) {
702 document->SetError( XML_ERROR_MISMATCHED_ELEMENT, node->Value(), 0 );
703 p = 0;
704 }
705 else if ( !endTag.Empty() && ele->ClosingType() != XMLElement::OPEN ) {
706 document->SetError( XML_ERROR_MISMATCHED_ELEMENT, node->Value(), 0 );
707 p = 0;
708 }
709 else if ( !endTag.Empty() ) {
710 if ( !XMLUtil::StringEqual( endTag.GetStr(), node->Value() )) {
711 document->SetError( XML_ERROR_MISMATCHED_ELEMENT, node->Value(), 0 );
712 p = 0;
713 }
714 }
715 }
716 if ( p == 0 ) {
717 DELETE_NODE( node );
718 node = 0;
719 }
720 if ( node ) {
721 this->InsertEndChild( node );
722 }
723 }
724 return 0;
725 }
727 // --------- XMLText ---------- //
728 char* XMLText::ParseDeep( char* p, StrPair* )
729 {
730 const char* start = p;
731 if ( this->CData() ) {
732 p = value.ParseText( p, "]]>", StrPair::NEEDS_NEWLINE_NORMALIZATION );
733 if ( !p ) {
734 document->SetError( XML_ERROR_PARSING_CDATA, start, 0 );
735 }
736 return p;
737 }
738 else {
739 p = value.ParseText( p, "<", document->ProcessEntities() ? StrPair::TEXT_ELEMENT : StrPair::TEXT_ELEMENT_LEAVE_ENTITIES );
740 if ( !p ) {
741 document->SetError( XML_ERROR_PARSING_TEXT, start, 0 );
742 }
743 if ( p && *p ) {
744 return p-1;
745 }
746 }
747 return 0;
748 }
751 XMLNode* XMLText::ShallowClone( XMLDocument* doc ) const
752 {
753 if ( !doc ) {
754 doc = document;
755 }
756 XMLText* text = doc->NewText( Value() ); // fixme: this will always allocate memory. Intern?
757 text->SetCData( this->CData() );
758 return text;
759 }
762 bool XMLText::ShallowEqual( const XMLNode* compare ) const
763 {
764 return ( compare->ToText() && XMLUtil::StringEqual( compare->ToText()->Value(), Value() ));
765 }
768 bool XMLText::Accept( XMLVisitor* visitor ) const
769 {
770 return visitor->Visit( *this );
771 }
774 // --------- XMLComment ---------- //
776 XMLComment::XMLComment( XMLDocument* doc ) : XMLNode( doc )
777 {
778 }
781 XMLComment::~XMLComment()
782 {
783 //printf( "~XMLComment\n" );
784 }
787 char* XMLComment::ParseDeep( char* p, StrPair* )
788 {
789 // Comment parses as text.
790 const char* start = p;
791 p = value.ParseText( p, "-->", StrPair::COMMENT );
792 if ( p == 0 ) {
793 document->SetError( XML_ERROR_PARSING_COMMENT, start, 0 );
794 }
795 return p;
796 }
799 XMLNode* XMLComment::ShallowClone( XMLDocument* doc ) const
800 {
801 if ( !doc ) {
802 doc = document;
803 }
804 XMLComment* comment = doc->NewComment( Value() ); // fixme: this will always allocate memory. Intern?
805 return comment;
806 }
809 bool XMLComment::ShallowEqual( const XMLNode* compare ) const
810 {
811 return ( compare->ToComment() && XMLUtil::StringEqual( compare->ToComment()->Value(), Value() ));
812 }
815 bool XMLComment::Accept( XMLVisitor* visitor ) const
816 {
817 return visitor->Visit( *this );
818 }
821 // --------- XMLDeclaration ---------- //
823 XMLDeclaration::XMLDeclaration( XMLDocument* doc ) : XMLNode( doc )
824 {
825 }
828 XMLDeclaration::~XMLDeclaration()
829 {
830 //printf( "~XMLDeclaration\n" );
831 }
834 char* XMLDeclaration::ParseDeep( char* p, StrPair* )
835 {
836 // Declaration parses as text.
837 const char* start = p;
838 p = value.ParseText( p, "?>", StrPair::NEEDS_NEWLINE_NORMALIZATION );
839 if ( p == 0 ) {
840 document->SetError( XML_ERROR_PARSING_DECLARATION, start, 0 );
841 }
842 return p;
843 }
846 XMLNode* XMLDeclaration::ShallowClone( XMLDocument* doc ) const
847 {
848 if ( !doc ) {
849 doc = document;
850 }
851 XMLDeclaration* dec = doc->NewDeclaration( Value() ); // fixme: this will always allocate memory. Intern?
852 return dec;
853 }
856 bool XMLDeclaration::ShallowEqual( const XMLNode* compare ) const
857 {
858 return ( compare->ToDeclaration() && XMLUtil::StringEqual( compare->ToDeclaration()->Value(), Value() ));
859 }
863 bool XMLDeclaration::Accept( XMLVisitor* visitor ) const
864 {
865 return visitor->Visit( *this );
866 }
868 // --------- XMLUnknown ---------- //
870 XMLUnknown::XMLUnknown( XMLDocument* doc ) : XMLNode( doc )
871 {
872 }
875 XMLUnknown::~XMLUnknown()
876 {
877 }
880 char* XMLUnknown::ParseDeep( char* p, StrPair* )
881 {
882 // Unknown parses as text.
883 const char* start = p;
885 p = value.ParseText( p, ">", StrPair::NEEDS_NEWLINE_NORMALIZATION );
886 if ( !p ) {
887 document->SetError( XML_ERROR_PARSING_UNKNOWN, start, 0 );
888 }
889 return p;
890 }
893 XMLNode* XMLUnknown::ShallowClone( XMLDocument* doc ) const
894 {
895 if ( !doc ) {
896 doc = document;
897 }
898 XMLUnknown* text = doc->NewUnknown( Value() ); // fixme: this will always allocate memory. Intern?
899 return text;
900 }
903 bool XMLUnknown::ShallowEqual( const XMLNode* compare ) const
904 {
905 return ( compare->ToUnknown() && XMLUtil::StringEqual( compare->ToUnknown()->Value(), Value() ));
906 }
909 bool XMLUnknown::Accept( XMLVisitor* visitor ) const
910 {
911 return visitor->Visit( *this );
912 }
914 // --------- XMLAttribute ---------- //
915 char* XMLAttribute::ParseDeep( char* p, bool processEntities )
916 {
917 p = name.ParseText( p, "=", StrPair::ATTRIBUTE_NAME );
918 if ( !p || !*p ) return 0;
920 char endTag[2] = { *p, 0 };
921 ++p;
922 p = value.ParseText( p, endTag, processEntities ? StrPair::ATTRIBUTE_VALUE : StrPair::ATTRIBUTE_VALUE_LEAVE_ENTITIES );
923 //if ( value.Empty() ) return 0;
924 return p;
925 }
928 void XMLAttribute::SetName( const char* n )
929 {
930 name.SetStr( n );
931 }
934 int XMLAttribute::QueryIntValue( int* value ) const
935 {
936 if ( TIXML_SSCANF( Value(), "%d", value ) == 1 )
937 return XML_NO_ERROR;
938 return XML_WRONG_ATTRIBUTE_TYPE;
939 }
942 int XMLAttribute::QueryUnsignedValue( unsigned int* value ) const
943 {
944 if ( TIXML_SSCANF( Value(), "%u", value ) == 1 )
945 return XML_NO_ERROR;
946 return XML_WRONG_ATTRIBUTE_TYPE;
947 }
950 int XMLAttribute::QueryBoolValue( bool* value ) const
951 {
952 int ival = -1;
953 QueryIntValue( &ival );
955 if ( ival > 0 || XMLUtil::StringEqual( Value(), "true" ) ) {
956 *value = true;
957 return XML_NO_ERROR;
958 }
959 else if ( ival == 0 || XMLUtil::StringEqual( Value(), "false" ) ) {
960 *value = false;
961 return XML_NO_ERROR;
962 }
963 return XML_WRONG_ATTRIBUTE_TYPE;
964 }
967 int XMLAttribute::QueryDoubleValue( double* value ) const
968 {
969 if ( TIXML_SSCANF( Value(), "%lf", value ) == 1 )
970 return XML_NO_ERROR;
971 return XML_WRONG_ATTRIBUTE_TYPE;
972 }
975 int XMLAttribute::QueryFloatValue( float* value ) const
976 {
977 if ( TIXML_SSCANF( Value(), "%f", value ) == 1 )
978 return XML_NO_ERROR;
979 return XML_WRONG_ATTRIBUTE_TYPE;
980 }
983 void XMLAttribute::SetAttribute( const char* v )
984 {
985 value.SetStr( v );
986 }
989 void XMLAttribute::SetAttribute( int v )
990 {
991 char buf[BUF_SIZE];
992 TIXML_SNPRINTF( buf, BUF_SIZE, "%d", v );
993 value.SetStr( buf );
994 }
997 void XMLAttribute::SetAttribute( unsigned v )
998 {
999 char buf[BUF_SIZE];
1000 TIXML_SNPRINTF( buf, BUF_SIZE, "%u", v );
1001 value.SetStr( buf );
1005 void XMLAttribute::SetAttribute( bool v )
1007 char buf[BUF_SIZE];
1008 TIXML_SNPRINTF( buf, BUF_SIZE, "%d", v ? 1 : 0 );
1009 value.SetStr( buf );
1012 void XMLAttribute::SetAttribute( double v )
1014 char buf[BUF_SIZE];
1015 TIXML_SNPRINTF( buf, BUF_SIZE, "%f", v );
1016 value.SetStr( buf );
1019 void XMLAttribute::SetAttribute( float v )
1021 char buf[BUF_SIZE];
1022 TIXML_SNPRINTF( buf, BUF_SIZE, "%f", v );
1023 value.SetStr( buf );
1027 // --------- XMLElement ---------- //
1028 XMLElement::XMLElement( XMLDocument* doc ) : XMLNode( doc ),
1029 closingType( 0 ),
1030 rootAttribute( 0 )
1035 XMLElement::~XMLElement()
1037 while( rootAttribute ) {
1038 XMLAttribute* next = rootAttribute->next;
1039 DELETE_ATTRIBUTE( rootAttribute );
1040 rootAttribute = next;
1045 XMLAttribute* XMLElement::FindAttribute( const char* name )
1047 XMLAttribute* a = 0;
1048 for( a=rootAttribute; a; a = a->next ) {
1049 if ( XMLUtil::StringEqual( a->Name(), name ) )
1050 return a;
1052 return 0;
1056 const XMLAttribute* XMLElement::FindAttribute( const char* name ) const
1058 XMLAttribute* a = 0;
1059 for( a=rootAttribute; a; a = a->next ) {
1060 if ( XMLUtil::StringEqual( a->Name(), name ) )
1061 return a;
1063 return 0;
1067 const char* XMLElement::Attribute( const char* name, const char* value ) const
1069 const XMLAttribute* a = FindAttribute( name );
1070 if ( !a )
1071 return 0;
1072 if ( !value || XMLUtil::StringEqual( a->Value(), value ))
1073 return a->Value();
1074 return 0;
1078 const char* XMLElement::GetText() const
1080 if ( FirstChild() && FirstChild()->ToText() ) {
1081 return FirstChild()->ToText()->Value();
1083 return 0;
1087 XMLAttribute* XMLElement::FindOrCreateAttribute( const char* name )
1089 XMLAttribute* last = 0;
1090 XMLAttribute* attrib = 0;
1091 for( attrib = rootAttribute;
1092 attrib;
1093 last = attrib, attrib = attrib->next )
1095 if ( XMLUtil::StringEqual( attrib->Name(), name ) ) {
1096 break;
1099 if ( !attrib ) {
1100 attrib = new (document->attributePool.Alloc() ) XMLAttribute();
1101 attrib->memPool = &document->attributePool;
1102 if ( last ) {
1103 last->next = attrib;
1105 else {
1106 rootAttribute = attrib;
1108 attrib->SetName( name );
1110 return attrib;
1114 void XMLElement::DeleteAttribute( const char* name )
1116 XMLAttribute* prev = 0;
1117 for( XMLAttribute* a=rootAttribute; a; a=a->next ) {
1118 if ( XMLUtil::StringEqual( name, a->Name() ) ) {
1119 if ( prev ) {
1120 prev->next = a->next;
1122 else {
1123 rootAttribute = a->next;
1125 DELETE_ATTRIBUTE( a );
1126 break;
1128 prev = a;
1133 char* XMLElement::ParseAttributes( char* p )
1135 const char* start = p;
1136 XMLAttribute* prevAttribute = 0;
1138 // Read the attributes.
1139 while( p ) {
1140 p = XMLUtil::SkipWhiteSpace( p );
1141 if ( !p || !(*p) ) {
1142 document->SetError( XML_ERROR_PARSING_ELEMENT, start, Name() );
1143 return 0;
1146 // attribute.
1147 if ( XMLUtil::IsAlpha( *p ) ) {
1148 XMLAttribute* attrib = new (document->attributePool.Alloc() ) XMLAttribute();
1149 attrib->memPool = &document->attributePool;
1151 p = attrib->ParseDeep( p, document->ProcessEntities() );
1152 if ( !p || Attribute( attrib->Name() ) ) {
1153 DELETE_ATTRIBUTE( attrib );
1154 document->SetError( XML_ERROR_PARSING_ATTRIBUTE, start, p );
1155 return 0;
1157 // There is a minor bug here: if the attribute in the source xml
1158 // document is duplicated, it will not be detected and the
1159 // attribute will be doubly added. However, tracking the 'prevAttribute'
1160 // avoids re-scanning the attribute list. Preferring performance for
1161 // now, may reconsider in the future.
1162 if ( prevAttribute ) {
1163 prevAttribute->next = attrib;
1165 else {
1166 rootAttribute = attrib;
1168 prevAttribute = attrib;
1170 // end of the tag
1171 else if ( *p == '/' && *(p+1) == '>' ) {
1172 closingType = CLOSED;
1173 return p+2; // done; sealed element.
1175 // end of the tag
1176 else if ( *p == '>' ) {
1177 ++p;
1178 break;
1180 else {
1181 document->SetError( XML_ERROR_PARSING_ELEMENT, start, p );
1182 return 0;
1185 return p;
1189 //
1190 // <ele></ele>
1191 // <ele>foo<b>bar</b></ele>
1192 //
1193 char* XMLElement::ParseDeep( char* p, StrPair* strPair )
1195 // Read the element name.
1196 p = XMLUtil::SkipWhiteSpace( p );
1197 if ( !p ) return 0;
1199 // The closing element is the </element> form. It is
1200 // parsed just like a regular element then deleted from
1201 // the DOM.
1202 if ( *p == '/' ) {
1203 closingType = CLOSING;
1204 ++p;
1207 p = value.ParseName( p );
1208 if ( value.Empty() ) return 0;
1210 p = ParseAttributes( p );
1211 if ( !p || !*p || closingType )
1212 return p;
1214 p = XMLNode::ParseDeep( p, strPair );
1215 return p;
1220 XMLNode* XMLElement::ShallowClone( XMLDocument* doc ) const
1222 if ( !doc ) {
1223 doc = document;
1225 XMLElement* element = doc->NewElement( Value() ); // fixme: this will always allocate memory. Intern?
1226 for( const XMLAttribute* a=FirstAttribute(); a; a=a->Next() ) {
1227 element->SetAttribute( a->Name(), a->Value() ); // fixme: this will always allocate memory. Intern?
1229 return element;
1233 bool XMLElement::ShallowEqual( const XMLNode* compare ) const
1235 const XMLElement* other = compare->ToElement();
1236 if ( other && XMLUtil::StringEqual( other->Value(), Value() )) {
1238 const XMLAttribute* a=FirstAttribute();
1239 const XMLAttribute* b=other->FirstAttribute();
1241 while ( a && b ) {
1242 if ( !XMLUtil::StringEqual( a->Value(), b->Value() ) ) {
1243 return false;
1246 if ( a || b ) {
1247 // different count
1248 return false;
1250 return true;
1252 return false;
1256 bool XMLElement::Accept( XMLVisitor* visitor ) const
1258 if ( visitor->VisitEnter( *this, rootAttribute ) )
1260 for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() )
1262 if ( !node->Accept( visitor ) )
1263 break;
1266 return visitor->VisitExit( *this );
1270 // --------- XMLDocument ----------- //
1271 XMLDocument::XMLDocument( bool _processEntities ) :
1272 XMLNode( 0 ),
1273 writeBOM( false ),
1274 processEntities( _processEntities ),
1275 errorID( 0 ),
1276 errorStr1( 0 ),
1277 errorStr2( 0 ),
1278 charBuffer( 0 )
1280 document = this; // avoid warning about 'this' in initializer list
1284 XMLDocument::~XMLDocument()
1286 DeleteChildren();
1287 delete [] charBuffer;
1289 #if 0
1290 textPool.Trace( "text" );
1291 elementPool.Trace( "element" );
1292 commentPool.Trace( "comment" );
1293 attributePool.Trace( "attribute" );
1294 #endif
1296 TIXMLASSERT( textPool.CurrentAllocs() == 0 );
1297 TIXMLASSERT( elementPool.CurrentAllocs() == 0 );
1298 TIXMLASSERT( commentPool.CurrentAllocs() == 0 );
1299 TIXMLASSERT( attributePool.CurrentAllocs() == 0 );
1303 void XMLDocument::InitDocument()
1305 errorID = XML_NO_ERROR;
1306 errorStr1 = 0;
1307 errorStr2 = 0;
1309 delete [] charBuffer;
1310 charBuffer = 0;
1315 XMLElement* XMLDocument::NewElement( const char* name )
1317 XMLElement* ele = new (elementPool.Alloc()) XMLElement( this );
1318 ele->memPool = &elementPool;
1319 ele->SetName( name );
1320 return ele;
1324 XMLComment* XMLDocument::NewComment( const char* str )
1326 XMLComment* comment = new (commentPool.Alloc()) XMLComment( this );
1327 comment->memPool = &commentPool;
1328 comment->SetValue( str );
1329 return comment;
1333 XMLText* XMLDocument::NewText( const char* str )
1335 XMLText* text = new (textPool.Alloc()) XMLText( this );
1336 text->memPool = &textPool;
1337 text->SetValue( str );
1338 return text;
1342 XMLDeclaration* XMLDocument::NewDeclaration( const char* str )
1344 XMLDeclaration* dec = new (commentPool.Alloc()) XMLDeclaration( this );
1345 dec->memPool = &commentPool;
1346 dec->SetValue( str ? str : "xml version=\"1.0\" encoding=\"UTF-8\"" );
1347 return dec;
1351 XMLUnknown* XMLDocument::NewUnknown( const char* str )
1353 XMLUnknown* unk = new (commentPool.Alloc()) XMLUnknown( this );
1354 unk->memPool = &commentPool;
1355 unk->SetValue( str );
1356 return unk;
1360 int XMLDocument::LoadFile( const char* filename )
1362 DeleteChildren();
1363 InitDocument();
1365 #if defined(_MSC_VER)
1366 #pragma warning ( push )
1367 #pragma warning ( disable : 4996 ) // Fail to see a compelling reason why this should be deprecated.
1368 #endif
1369 FILE* fp = fopen( filename, "rb" );
1370 #if defined(_MSC_VER)
1371 #pragma warning ( pop )
1372 #endif
1373 if ( !fp ) {
1374 SetError( XML_ERROR_FILE_NOT_FOUND, filename, 0 );
1375 return errorID;
1377 LoadFile( fp );
1378 fclose( fp );
1379 return errorID;
1383 int XMLDocument::LoadFile( FILE* fp )
1385 DeleteChildren();
1386 InitDocument();
1388 fseek( fp, 0, SEEK_END );
1389 unsigned size = ftell( fp );
1390 fseek( fp, 0, SEEK_SET );
1392 if ( size == 0 ) {
1393 return errorID;
1396 charBuffer = new char[size+1];
1397 fread( charBuffer, size, 1, fp );
1398 charBuffer[size] = 0;
1400 const char* p = charBuffer;
1401 p = XMLUtil::SkipWhiteSpace( p );
1402 p = XMLUtil::ReadBOM( p, &writeBOM );
1403 if ( !p || !*p ) {
1404 SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
1405 return errorID;
1408 ParseDeep( charBuffer + (p-charBuffer), 0 );
1409 return errorID;
1413 int XMLDocument::SaveFile( const char* filename )
1415 #if defined(_MSC_VER)
1416 #pragma warning ( push )
1417 #pragma warning ( disable : 4996 ) // Fail to see a compelling reason why this should be deprecated.
1418 #endif
1419 FILE* fp = fopen( filename, "w" );
1420 #if defined(_MSC_VER)
1421 #pragma warning ( pop )
1422 #endif
1423 if ( !fp ) {
1424 SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, filename, 0 );
1425 return errorID;
1427 SaveFile(fp);
1428 fclose( fp );
1429 return errorID;
1433 int XMLDocument::SaveFile( FILE* fp )
1435 XMLPrinter stream( fp );
1436 Print( &stream );
1437 return errorID;
1441 int XMLDocument::Parse( const char* p )
1443 DeleteChildren();
1444 InitDocument();
1446 if ( !p || !*p ) {
1447 SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
1448 return errorID;
1450 p = XMLUtil::SkipWhiteSpace( p );
1451 p = XMLUtil::ReadBOM( p, &writeBOM );
1452 if ( !p || !*p ) {
1453 SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
1454 return errorID;
1457 size_t len = strlen( p );
1458 charBuffer = new char[ len+1 ];
1459 memcpy( charBuffer, p, len+1 );
1462 ParseDeep( charBuffer, 0 );
1463 return errorID;
1467 void XMLDocument::Print( XMLPrinter* streamer )
1469 XMLPrinter stdStreamer( stdout );
1470 if ( !streamer )
1471 streamer = &stdStreamer;
1472 Accept( streamer );
1476 void XMLDocument::SetError( int error, const char* str1, const char* str2 )
1478 errorID = error;
1479 errorStr1 = str1;
1480 errorStr2 = str2;
1484 void XMLDocument::PrintError() const
1486 if ( errorID ) {
1487 static const int LEN = 20;
1488 char buf1[LEN] = { 0 };
1489 char buf2[LEN] = { 0 };
1491 if ( errorStr1 ) {
1492 TIXML_SNPRINTF( buf1, LEN, "%s", errorStr1 );
1494 if ( errorStr2 ) {
1495 TIXML_SNPRINTF( buf2, LEN, "%s", errorStr2 );
1498 printf( "XMLDocument error id=%d str1=%s str2=%s\n",
1499 errorID, buf1, buf2 );
1504 XMLPrinter::XMLPrinter( FILE* file ) :
1505 elementJustOpened( false ),
1506 firstElement( true ),
1507 fp( file ),
1508 depth( 0 ),
1509 textDepth( -1 ),
1510 processEntities( true )
1512 for( int i=0; i<ENTITY_RANGE; ++i ) {
1513 entityFlag[i] = false;
1514 restrictedEntityFlag[i] = false;
1516 for( int i=0; i<NUM_ENTITIES; ++i ) {
1517 TIXMLASSERT( entities[i].value < ENTITY_RANGE );
1518 if ( entities[i].value < ENTITY_RANGE ) {
1519 entityFlag[ (int)entities[i].value ] = true;
1522 restrictedEntityFlag[(int)'&'] = true;
1523 restrictedEntityFlag[(int)'<'] = true;
1524 restrictedEntityFlag[(int)'>'] = true; // not required, but consistency is nice
1525 buffer.Push( 0 );
1529 void XMLPrinter::Print( const char* format, ... )
1531 va_list va;
1532 va_start( va, format );
1534 if ( fp ) {
1535 vfprintf( fp, format, va );
1537 else {
1538 // This seems brutally complex. Haven't figured out a better
1539 // way on windows.
1540 #ifdef _MSC_VER
1541 int len = -1;
1542 int expand = 1000;
1543 while ( len < 0 ) {
1544 len = vsnprintf_s( accumulator.Mem(), accumulator.Capacity(), _TRUNCATE, format, va );
1545 if ( len < 0 ) {
1546 expand *= 3/2;
1547 accumulator.PushArr( expand );
1550 char* p = buffer.PushArr( len ) - 1;
1551 memcpy( p, accumulator.Mem(), len+1 );
1552 #else
1553 int len = vsnprintf( 0, 0, format, va );
1554 // Close out and re-start the va-args
1555 va_end( va );
1556 va_start( va, format );
1557 char* p = buffer.PushArr( len ) - 1;
1558 vsnprintf( p, len+1, format, va );
1559 #endif
1561 va_end( va );
1565 void XMLPrinter::PrintSpace( int depth )
1567 for( int i=0; i<depth; ++i ) {
1568 Print( " " );
1573 void XMLPrinter::PrintString( const char* p, bool restricted )
1575 // Look for runs of bytes between entities to print.
1576 const char* q = p;
1577 const bool* flag = restricted ? restrictedEntityFlag : entityFlag;
1579 if ( processEntities ) {
1580 while ( *q ) {
1581 // Remember, char is sometimes signed. (How many times has that bitten me?)
1582 if ( *q > 0 && *q < ENTITY_RANGE ) {
1583 // Check for entities. If one is found, flush
1584 // the stream up until the entity, write the
1585 // entity, and keep looking.
1586 if ( flag[(unsigned)(*q)] ) {
1587 while ( p < q ) {
1588 Print( "%c", *p );
1589 ++p;
1591 for( int i=0; i<NUM_ENTITIES; ++i ) {
1592 if ( entities[i].value == *q ) {
1593 Print( "&%s;", entities[i].pattern );
1594 break;
1597 ++p;
1600 ++q;
1603 // Flush the remaining string. This will be the entire
1604 // string if an entity wasn't found.
1605 if ( !processEntities || (q-p > 0) ) {
1606 Print( "%s", p );
1611 void XMLPrinter::PushHeader( bool writeBOM, bool writeDec )
1613 static const unsigned char bom[] = { TIXML_UTF_LEAD_0, TIXML_UTF_LEAD_1, TIXML_UTF_LEAD_2, 0 };
1614 if ( writeBOM ) {
1615 Print( "%s", bom );
1617 if ( writeDec ) {
1618 PushDeclaration( "xml version=\"1.0\"" );
1623 void XMLPrinter::OpenElement( const char* name )
1625 if ( elementJustOpened ) {
1626 SealElement();
1628 stack.Push( name );
1630 if ( textDepth < 0 && !firstElement ) {
1631 Print( "\n" );
1632 PrintSpace( depth );
1635 Print( "<%s", name );
1636 elementJustOpened = true;
1637 firstElement = false;
1638 ++depth;
1642 void XMLPrinter::PushAttribute( const char* name, const char* value )
1644 TIXMLASSERT( elementJustOpened );
1645 Print( " %s=\"", name );
1646 PrintString( value, false );
1647 Print( "\"" );
1651 void XMLPrinter::PushAttribute( const char* name, int v )
1653 char buf[BUF_SIZE];
1654 TIXML_SNPRINTF( buf, BUF_SIZE, "%d", v );
1655 PushAttribute( name, buf );
1659 void XMLPrinter::PushAttribute( const char* name, unsigned v )
1661 char buf[BUF_SIZE];
1662 TIXML_SNPRINTF( buf, BUF_SIZE, "%u", v );
1663 PushAttribute( name, buf );
1667 void XMLPrinter::PushAttribute( const char* name, bool v )
1669 char buf[BUF_SIZE];
1670 TIXML_SNPRINTF( buf, BUF_SIZE, "%d", v ? 1 : 0 );
1671 PushAttribute( name, buf );
1675 void XMLPrinter::PushAttribute( const char* name, double v )
1677 char buf[BUF_SIZE];
1678 TIXML_SNPRINTF( buf, BUF_SIZE, "%f", v );
1679 PushAttribute( name, buf );
1683 void XMLPrinter::CloseElement()
1685 --depth;
1686 const char* name = stack.Pop();
1688 if ( elementJustOpened ) {
1689 Print( "/>" );
1691 else {
1692 if ( textDepth < 0 ) {
1693 Print( "\n" );
1694 PrintSpace( depth );
1696 Print( "</%s>", name );
1699 if ( textDepth == depth )
1700 textDepth = -1;
1701 if ( depth == 0 )
1702 Print( "\n" );
1703 elementJustOpened = false;
1707 void XMLPrinter::SealElement()
1709 elementJustOpened = false;
1710 Print( ">" );
1714 void XMLPrinter::PushText( const char* text, bool cdata )
1716 textDepth = depth-1;
1718 if ( elementJustOpened ) {
1719 SealElement();
1721 if ( cdata ) {
1722 Print( "<![CDATA[" );
1723 Print( "%s", text );
1724 Print( "]]>" );
1726 else {
1727 PrintString( text, true );
1732 void XMLPrinter::PushComment( const char* comment )
1734 if ( elementJustOpened ) {
1735 SealElement();
1737 if ( textDepth < 0 && !firstElement ) {
1738 Print( "\n" );
1739 PrintSpace( depth );
1741 firstElement = false;
1742 Print( "<!--%s-->", comment );
1746 void XMLPrinter::PushDeclaration( const char* value )
1748 if ( elementJustOpened ) {
1749 SealElement();
1751 if ( textDepth < 0 && !firstElement) {
1752 Print( "\n" );
1753 PrintSpace( depth );
1755 firstElement = false;
1756 Print( "<?%s?>", value );
1760 void XMLPrinter::PushUnknown( const char* value )
1762 if ( elementJustOpened ) {
1763 SealElement();
1765 if ( textDepth < 0 && !firstElement ) {
1766 Print( "\n" );
1767 PrintSpace( depth );
1769 firstElement = false;
1770 Print( "<!%s>", value );
1774 bool XMLPrinter::VisitEnter( const XMLDocument& doc )
1776 processEntities = doc.ProcessEntities();
1777 if ( doc.HasBOM() ) {
1778 PushHeader( true, false );
1780 return true;
1784 bool XMLPrinter::VisitEnter( const XMLElement& element, const XMLAttribute* attribute )
1786 OpenElement( element.Name() );
1787 while ( attribute ) {
1788 PushAttribute( attribute->Name(), attribute->Value() );
1789 attribute = attribute->Next();
1791 return true;
1795 bool XMLPrinter::VisitExit( const XMLElement& )
1797 CloseElement();
1798 return true;
1802 bool XMLPrinter::Visit( const XMLText& text )
1804 PushText( text.Value(), text.CData() );
1805 return true;
1809 bool XMLPrinter::Visit( const XMLComment& comment )
1811 PushComment( comment.Value() );
1812 return true;
1815 bool XMLPrinter::Visit( const XMLDeclaration& declaration )
1817 PushDeclaration( declaration.Value() );
1818 return true;
1822 bool XMLPrinter::Visit( const XMLUnknown& unknown )
1824 PushUnknown( unknown.Value() );
1825 return true;