Project

General

Profile

Xml Scol plugin
tinyxml2.cpp
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 <new> // yes, this one new style header, is in the Android SDK.
25# ifdef ANDROID_NDK
26# include <stddef.h>
27#else
28# include <cstddef>
29#endif
30
31#include "tinyxml2.h"
32
33using namespace std;
34
35static const char LINE_FEED = (char)0x0a; // all line endings are normalized to LF
36static const char LF = LINE_FEED;
37static const char CARRIAGE_RETURN = (char)0x0d; // CR gets filtered out
38static const char CR = CARRIAGE_RETURN;
39static const char SINGLE_QUOTE = '\'';
40static const char DOUBLE_QUOTE = '\"';
41
42// Bunch of unicode info at:
43// http://www.unicode.org/faq/utf_bom.html
44// ef bb bf (Microsoft "lead bytes") - designates UTF-8
45
46static const unsigned char TIXML_UTF_LEAD_0 = 0xefU;
47static const unsigned char TIXML_UTF_LEAD_1 = 0xbbU;
48static const unsigned char TIXML_UTF_LEAD_2 = 0xbfU;
49
50
51#define DELETE_NODE( node ) { \
52 if ( node ) { \
53 MemPool* pool = node->_memPool; \
54 node->~XMLNode(); \
55 pool->Free( node ); \
56 } \
57 }
58#define DELETE_ATTRIBUTE( attrib ) { \
59 if ( attrib ) { \
60 MemPool* pool = attrib->_memPool; \
61 attrib->~XMLAttribute(); \
62 pool->Free( attrib ); \
63 } \
64 }
65
66namespace tinyxml2
67{
68
69struct Entity {
70 const char* pattern;
71 int length;
72 char value;
73};
74
75static const int NUM_ENTITIES = 5;
76static const Entity entities[NUM_ENTITIES] = {
77 { "quot", 4, DOUBLE_QUOTE },
78 { "amp", 3, '&' },
79 { "apos", 4, SINGLE_QUOTE },
80 { "lt", 2, '<' },
81 { "gt", 2, '>' }
82};
83
84
85StrPair::~StrPair()
86{
87 Reset();
88}
89
90
91void StrPair::Reset()
92{
93 if ( _flags & NEEDS_DELETE ) {
94 delete [] _start;
95 }
96 _flags = 0;
97 _start = 0;
98 _end = 0;
99}
100
101
102void StrPair::SetStr( const char* str, int flags )
103{
104 Reset();
105 size_t len = strlen( str );
106 _start = new char[ len+1 ];
107 memcpy( _start, str, len+1 );
108 _end = _start + len;
109 _flags = flags | NEEDS_DELETE;
110}
111
112
113char* StrPair::ParseText( char* p, const char* endTag, int strFlags )
114{
115 TIXMLASSERT( endTag && *endTag );
116
117 char* start = p; // fixme: hides a member
118 char endChar = *endTag;
119 size_t length = strlen( endTag );
120
121 // Inner loop of text parsing.
122 while ( *p ) {
123 if ( *p == endChar && strncmp( p, endTag, length ) == 0 ) {
124 Set( start, p, strFlags );
125 return p + length;
126 }
127 ++p;
128 }
129 return 0;
130}
131
132
133char* StrPair::ParseName( char* p )
134{
135 char* start = p;
136
137 if ( !start || !(*start) ) {
138 return 0;
139 }
140
141 while( *p && (
142 XMLUtil::IsAlphaNum( (unsigned char) *p )
143 || *p == '_'
144 || *p == ':'
145 || (*p == '-' && p>start ) // can be in a name, but not lead it.
146 || (*p == '.' && p>start ) )) { // can be in a name, but not lead it.
147 ++p;
148 }
149
150 if ( p > start ) {
151 Set( start, p, 0 );
152 return p;
153 }
154 return 0;
155}
156
157
158void StrPair::CollapseWhitespace()
159{
160 // Trim leading space.
161 _start = XMLUtil::SkipWhiteSpace( _start );
162
163 if ( _start && *_start ) {
164 char* p = _start; // the read pointer
165 char* q = _start; // the write pointer
166
167 while( *p ) {
168 if ( XMLUtil::IsWhiteSpace( *p )) {
169 p = XMLUtil::SkipWhiteSpace( p );
170 if ( *p == 0 ) {
171 break; // don't write to q; this trims the trailing space.
172 }
173 *q = ' ';
174 ++q;
175 }
176 *q = *p;
177 ++q;
178 ++p;
179 }
180 *q = 0;
181 }
182}
183
184
185const char* StrPair::GetStr()
186{
187 if ( _flags & NEEDS_FLUSH ) {
188 *_end = 0;
189 _flags ^= NEEDS_FLUSH;
190
191 if ( _flags ) {
192 char* p = _start; // the read pointer
193 char* q = _start; // the write pointer
194
195 while( p < _end ) {
196 if ( (_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == CR ) {
197 // CR-LF pair becomes LF
198 // CR alone becomes LF
199 // LF-CR becomes LF
200 if ( *(p+1) == LF ) {
201 p += 2;
202 }
203 else {
204 ++p;
205 }
206 *q++ = LF;
207 }
208 else if ( (_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == LF ) {
209 if ( *(p+1) == CR ) {
210 p += 2;
211 }
212 else {
213 ++p;
214 }
215 *q++ = LF;
216 }
217 else if ( (_flags & NEEDS_ENTITY_PROCESSING) && *p == '&' ) {
218 // Entities handled by tinyXML2:
219 // - special entities in the entity table [in/out]
220 // - numeric character reference [in]
221 // &#20013; or &#x4e2d;
222
223 if ( *(p+1) == '#' ) {
224 char buf[10] = { 0 };
225 int len;
226 p = const_cast<char*>( XMLUtil::GetCharacterRef( p, buf, &len ) );
227 for( int i=0; i<len; ++i ) {
228 *q++ = buf[i];
229 }
230 TIXMLASSERT( q <= p );
231 }
232 else {
233 int i=0;
234 for(; i<NUM_ENTITIES; ++i ) {
235 if ( strncmp( p+1, entities[i].pattern, entities[i].length ) == 0
236 && *(p+entities[i].length+1) == ';' ) {
237 // Found an entity convert;
238 *q = entities[i].value;
239 ++q;
240 p += entities[i].length + 2;
241 break;
242 }
243 }
244 if ( i == NUM_ENTITIES ) {
245 // fixme: treat as error?
246 ++p;
247 ++q;
248 }
249 }
250 }
251 else {
252 *q = *p;
253 ++p;
254 ++q;
255 }
256 }
257 *q = 0;
258 }
259 // The loop below has plenty going on, and this
260 // is a less useful mode. Break it out.
261 if ( _flags & COLLAPSE_WHITESPACE ) {
262 CollapseWhitespace();
263 }
264 _flags = (_flags & NEEDS_DELETE);
265 }
266 return _start;
267}
268
269
270
271
272// --------- XMLUtil ----------- //
273
274const char* XMLUtil::ReadBOM( const char* p, bool* bom )
275{
276 *bom = false;
277 const unsigned char* pu = reinterpret_cast<const unsigned char*>(p);
278 // Check for BOM:
279 if ( *(pu+0) == TIXML_UTF_LEAD_0
280 && *(pu+1) == TIXML_UTF_LEAD_1
281 && *(pu+2) == TIXML_UTF_LEAD_2 ) {
282 *bom = true;
283 p += 3;
284 }
285 return p;
286}
287
288
289void XMLUtil::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length )
290{
291 const unsigned long BYTE_MASK = 0xBF;
292 const unsigned long BYTE_MARK = 0x80;
293 const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
294
295 if (input < 0x80) {
296 *length = 1;
297 }
298 else if ( input < 0x800 ) {
299 *length = 2;
300 }
301 else if ( input < 0x10000 ) {
302 *length = 3;
303 }
304 else if ( input < 0x200000 ) {
305 *length = 4;
306 }
307 else {
308 *length = 0; // This code won't covert this correctly anyway.
309 return;
310 }
311
312 output += *length;
313
314 // Scary scary fall throughs.
315 switch (*length) {
316 case 4:
317 --output;
318 *output = (char)((input | BYTE_MARK) & BYTE_MASK);
319 input >>= 6;
320 case 3:
321 --output;
322 *output = (char)((input | BYTE_MARK) & BYTE_MASK);
323 input >>= 6;
324 case 2:
325 --output;
326 *output = (char)((input | BYTE_MARK) & BYTE_MASK);
327 input >>= 6;
328 case 1:
329 --output;
330 *output = (char)(input | FIRST_BYTE_MARK[*length]);
331 }
332}
333
334
335const char* XMLUtil::GetCharacterRef( const char* p, char* value, int* length )
336{
337 // Presume an entity, and pull it out.
338 *length = 0;
339
340 if ( *(p+1) == '#' && *(p+2) ) {
341 unsigned long ucs = 0;
342 ptrdiff_t delta = 0;
343 unsigned mult = 1;
344
345 if ( *(p+2) == 'x' ) {
346 // Hexadecimal.
347 if ( !*(p+3) ) {
348 return 0;
349 }
350
351 const char* q = p+3;
352 q = strchr( q, ';' );
353
354 if ( !q || !*q ) {
355 return 0;
356 }
357
358 delta = q-p;
359 --q;
360
361 while ( *q != 'x' ) {
362 if ( *q >= '0' && *q <= '9' ) {
363 ucs += mult * (*q - '0');
364 }
365 else if ( *q >= 'a' && *q <= 'f' ) {
366 ucs += mult * (*q - 'a' + 10);
367 }
368 else if ( *q >= 'A' && *q <= 'F' ) {
369 ucs += mult * (*q - 'A' + 10 );
370 }
371 else {
372 return 0;
373 }
374 mult *= 16;
375 --q;
376 }
377 }
378 else {
379 // Decimal.
380 if ( !*(p+2) ) {
381 return 0;
382 }
383
384 const char* q = p+2;
385 q = strchr( q, ';' );
386
387 if ( !q || !*q ) {
388 return 0;
389 }
390
391 delta = q-p;
392 --q;
393
394 while ( *q != '#' ) {
395 if ( *q >= '0' && *q <= '9' ) {
396 ucs += mult * (*q - '0');
397 }
398 else {
399 return 0;
400 }
401 mult *= 10;
402 --q;
403 }
404 }
405 // convert the UCS to UTF-8
406 ConvertUTF32ToUTF8( ucs, value, length );
407 return p + delta + 1;
408 }
409 return p+1;
410}
411
412
413void XMLUtil::ToStr( int v, char* buffer, int bufferSize )
414{
415 TIXML_SNPRINTF( buffer, bufferSize, "%d", v );
416}
417
418
419void XMLUtil::ToStr( unsigned v, char* buffer, int bufferSize )
420{
421 TIXML_SNPRINTF( buffer, bufferSize, "%u", v );
422}
423
424
425void XMLUtil::ToStr( bool v, char* buffer, int bufferSize )
426{
427 TIXML_SNPRINTF( buffer, bufferSize, "%d", v ? 1 : 0 );
428}
429
430
431void XMLUtil::ToStr( float v, char* buffer, int bufferSize )
432{
433 TIXML_SNPRINTF( buffer, bufferSize, "%g", v );
434}
435
436
437void XMLUtil::ToStr( double v, char* buffer, int bufferSize )
438{
439 TIXML_SNPRINTF( buffer, bufferSize, "%g", v );
440}
441
442
443bool XMLUtil::ToInt( const char* str, int* value )
444{
445 if ( TIXML_SSCANF( str, "%d", value ) == 1 ) {
446 return true;
447 }
448 return false;
449}
450
451bool XMLUtil::ToUnsigned( const char* str, unsigned *value )
452{
453 if ( TIXML_SSCANF( str, "%u", value ) == 1 ) {
454 return true;
455 }
456 return false;
457}
458
459bool XMLUtil::ToBool( const char* str, bool* value )
460{
461 int ival = 0;
462 if ( ToInt( str, &ival )) {
463 *value = (ival==0) ? false : true;
464 return true;
465 }
466 if ( StringEqual( str, "true" ) ) {
467 *value = true;
468 return true;
469 }
470 else if ( StringEqual( str, "false" ) ) {
471 *value = false;
472 return true;
473 }
474 return false;
475}
476
477
478bool XMLUtil::ToFloat( const char* str, float* value )
479{
480 if ( TIXML_SSCANF( str, "%f", value ) == 1 ) {
481 return true;
482 }
483 return false;
484}
485
486bool XMLUtil::ToDouble( const char* str, double* value )
487{
488 if ( TIXML_SSCANF( str, "%lf", value ) == 1 ) {
489 return true;
490 }
491 return false;
492}
493
494
495char* XMLDocument::Identify( char* p, XMLNode** node )
496{
497 XMLNode* returnNode = 0;
498 char* start = p;
499 p = XMLUtil::SkipWhiteSpace( p );
500 if( !p || !*p ) {
501 return p;
502 }
503
504 // What is this thing?
505 // - Elements start with a letter or underscore, but xml is reserved.
506 // - Comments: <!--
507 // - Decleration: <?
508 // - Everthing else is unknown to tinyxml.
509 //
510
511 static const char* xmlHeader = { "<?" };
512 static const char* commentHeader = { "<!--" };
513 static const char* dtdHeader = { "<!" };
514 static const char* cdataHeader = { "<![CDATA[" };
515 static const char* elementHeader = { "<" }; // and a header for everything else; check last.
516
517 static const int xmlHeaderLen = 2;
518 static const int commentHeaderLen = 4;
519 static const int dtdHeaderLen = 2;
520 static const int cdataHeaderLen = 9;
521 static const int elementHeaderLen = 1;
522
523#if defined(_MSC_VER)
524#pragma warning ( push )
525#pragma warning ( disable : 4127 )
526#endif
527 TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLUnknown ) ); // use same memory pool
528 TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLDeclaration ) ); // use same memory pool
529#if defined(_MSC_VER)
530#pragma warning (pop)
531#endif
532 if ( XMLUtil::StringEqual( p, xmlHeader, xmlHeaderLen ) ) {
533 returnNode = new (_commentPool.Alloc()) XMLDeclaration( this );
534 returnNode->_memPool = &_commentPool;
535 p += xmlHeaderLen;
536 }
537 else if ( XMLUtil::StringEqual( p, commentHeader, commentHeaderLen ) ) {
538 returnNode = new (_commentPool.Alloc()) XMLComment( this );
539 returnNode->_memPool = &_commentPool;
540 p += commentHeaderLen;
541 }
542 else if ( XMLUtil::StringEqual( p, cdataHeader, cdataHeaderLen ) ) {
543 XMLText* text = new (_textPool.Alloc()) XMLText( this );
544 returnNode = text;
545 returnNode->_memPool = &_textPool;
546 p += cdataHeaderLen;
547 text->SetCData( true );
548 }
549 else if ( XMLUtil::StringEqual( p, dtdHeader, dtdHeaderLen ) ) {
550 returnNode = new (_commentPool.Alloc()) XMLUnknown( this );
551 returnNode->_memPool = &_commentPool;
552 p += dtdHeaderLen;
553 }
554 else if ( XMLUtil::StringEqual( p, elementHeader, elementHeaderLen ) ) {
555 returnNode = new (_elementPool.Alloc()) XMLElement( this );
556 returnNode->_memPool = &_elementPool;
557 p += elementHeaderLen;
558 }
559 else {
560 returnNode = new (_textPool.Alloc()) XMLText( this );
561 returnNode->_memPool = &_textPool;
562 p = start; // Back it up, all the text counts.
563 }
564
565 *node = returnNode;
566 return p;
567}
568
569
571{
572 if ( visitor->VisitEnter( *this ) ) {
573 for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) {
574 if ( !node->Accept( visitor ) ) {
575 break;
576 }
577 }
578 }
579 return visitor->VisitExit( *this );
580}
581
582
583// --------- XMLNode ----------- //
584
585XMLNode::XMLNode( XMLDocument* doc ) :
586 _document( doc ),
587 _parent( 0 ),
588 _firstChild( 0 ), _lastChild( 0 ),
589 _prev( 0 ), _next( 0 )
590{
591}
592
593
594XMLNode::~XMLNode()
595{
597 if ( _parent ) {
598 _parent->Unlink( this );
599 }
600}
601
602
603void XMLNode::SetValue( const char* str, bool staticMem )
604{
605 if ( staticMem ) {
606 _value.SetInternedStr( str );
607 }
608 else {
609 _value.SetStr( str );
610 }
611}
612
613
615{
616 while( _firstChild ) {
617 XMLNode* node = _firstChild;
618 Unlink( node );
619
620 DELETE_NODE( node );
621 }
622 _firstChild = _lastChild = 0;
623}
624
625
626void XMLNode::Unlink( XMLNode* child )
627{
628 TIXMLASSERT( child->_parent == this );
629 if ( child == _firstChild ) {
630 _firstChild = _firstChild->_next;
631 }
632 if ( child == _lastChild ) {
633 _lastChild = _lastChild->_prev;
634 }
635
636 if ( child->_prev ) {
637 child->_prev->_next = child->_next;
638 }
639 if ( child->_next ) {
640 child->_next->_prev = child->_prev;
641 }
642 child->_parent = 0;
643}
644
645
647{
648 TIXMLASSERT( node->_parent == this );
649 DELETE_NODE( node );
650}
651
652
654{
655 if ( _lastChild ) {
656 TIXMLASSERT( _firstChild );
657 TIXMLASSERT( _lastChild->_next == 0 );
658 _lastChild->_next = addThis;
659 addThis->_prev = _lastChild;
660 _lastChild = addThis;
661
662 addThis->_next = 0;
663 }
664 else {
665 TIXMLASSERT( _firstChild == 0 );
666 _firstChild = _lastChild = addThis;
667
668 addThis->_prev = 0;
669 addThis->_next = 0;
670 }
671 addThis->_parent = this;
672 return addThis;
673}
674
675
677{
678 if ( _firstChild ) {
679 TIXMLASSERT( _lastChild );
680 TIXMLASSERT( _firstChild->_prev == 0 );
681
682 _firstChild->_prev = addThis;
683 addThis->_next = _firstChild;
684 _firstChild = addThis;
685
686 addThis->_prev = 0;
687 }
688 else {
689 TIXMLASSERT( _lastChild == 0 );
690 _firstChild = _lastChild = addThis;
691
692 addThis->_prev = 0;
693 addThis->_next = 0;
694 }
695 addThis->_parent = this;
696 return addThis;
697}
698
699
701{
702 TIXMLASSERT( afterThis->_parent == this );
703 if ( afterThis->_parent != this ) {
704 return 0;
705 }
706
707 if ( afterThis->_next == 0 ) {
708 // The last node or the only node.
709 return InsertEndChild( addThis );
710 }
711 addThis->_prev = afterThis;
712 addThis->_next = afterThis->_next;
713 afterThis->_next->_prev = addThis;
714 afterThis->_next = addThis;
715 addThis->_parent = this;
716 return addThis;
717}
718
719
720
721
722const XMLElement* XMLNode::FirstChildElement( const char* value ) const
723{
724 for( XMLNode* node=_firstChild; node; node=node->_next ) {
725 XMLElement* element = node->ToElement();
726 if ( element ) {
727 if ( !value || XMLUtil::StringEqual( element->Name(), value ) ) {
728 return element;
729 }
730 }
731 }
732 return 0;
733}
734
735
736const XMLElement* XMLNode::LastChildElement( const char* value ) const
737{
738 for( XMLNode* node=_lastChild; node; node=node->_prev ) {
739 XMLElement* element = node->ToElement();
740 if ( element ) {
741 if ( !value || XMLUtil::StringEqual( element->Name(), value ) ) {
742 return element;
743 }
744 }
745 }
746 return 0;
747}
748
749
750const XMLElement* XMLNode::NextSiblingElement( const char* value ) const
751{
752 for( XMLNode* element=this->_next; element; element = element->_next ) {
753 if ( element->ToElement()
754 && (!value || XMLUtil::StringEqual( value, element->Value() ))) {
755 return element->ToElement();
756 }
757 }
758 return 0;
759}
760
761
762const XMLElement* XMLNode::PreviousSiblingElement( const char* value ) const
763{
764 for( XMLNode* element=_prev; element; element = element->_prev ) {
765 if ( element->ToElement()
766 && (!value || XMLUtil::StringEqual( value, element->Value() ))) {
767 return element->ToElement();
768 }
769 }
770 return 0;
771}
772
773
774char* XMLNode::ParseDeep( char* p, StrPair* parentEnd )
775{
776 // This is a recursive method, but thinking about it "at the current level"
777 // it is a pretty simple flat list:
778 // <foo/>
779 // <!-- comment -->
780 //
781 // With a special case:
782 // <foo>
783 // </foo>
784 // <!-- comment -->
785 //
786 // Where the closing element (/foo) *must* be the next thing after the opening
787 // element, and the names must match. BUT the tricky bit is that the closing
788 // element will be read by the child.
789 //
790 // 'endTag' is the end tag for this node, it is returned by a call to a child.
791 // 'parentEnd' is the end tag for the parent, which is filled in and returned.
792
793 while( p && *p ) {
794 XMLNode* node = 0;
795
796 p = _document->Identify( p, &node );
797 if ( p == 0 || node == 0 ) {
798 break;
799 }
800
801 StrPair endTag;
802 p = node->ParseDeep( p, &endTag );
803 if ( !p ) {
804 DELETE_NODE( node );
805 node = 0;
806 if ( !_document->Error() ) {
807 _document->SetError( XML_ERROR_PARSING, 0, 0 );
808 }
809 break;
810 }
811
812 // We read the end tag. Return it to the parent.
813 if ( node->ToElement() && node->ToElement()->ClosingType() == XMLElement::CLOSING ) {
814 if ( parentEnd ) {
815 *parentEnd = static_cast<XMLElement*>(node)->_value;
816 }
817 DELETE_NODE( node );
818 return p;
819 }
820
821 // Handle an end tag returned to this level.
822 // And handle a bunch of annoying errors.
823 XMLElement* ele = node->ToElement();
824 if ( ele ) {
825 if ( endTag.Empty() && ele->ClosingType() == XMLElement::OPEN ) {
826 _document->SetError( XML_ERROR_MISMATCHED_ELEMENT, node->Value(), 0 );
827 p = 0;
828 }
829 else if ( !endTag.Empty() && ele->ClosingType() != XMLElement::OPEN ) {
830 _document->SetError( XML_ERROR_MISMATCHED_ELEMENT, node->Value(), 0 );
831 p = 0;
832 }
833 else if ( !endTag.Empty() ) {
834 if ( !XMLUtil::StringEqual( endTag.GetStr(), node->Value() )) {
835 _document->SetError( XML_ERROR_MISMATCHED_ELEMENT, node->Value(), 0 );
836 p = 0;
837 }
838 }
839 }
840 if ( p == 0 ) {
841 DELETE_NODE( node );
842 node = 0;
843 }
844 if ( node ) {
845 this->InsertEndChild( node );
846 }
847 }
848 return 0;
849}
850
851// --------- XMLText ---------- //
852char* XMLText::ParseDeep( char* p, StrPair* )
853{
854 const char* start = p;
855 if ( this->CData() ) {
856 p = _value.ParseText( p, "]]>", StrPair::NEEDS_NEWLINE_NORMALIZATION );
857 if ( !p ) {
858 _document->SetError( XML_ERROR_PARSING_CDATA, start, 0 );
859 }
860 return p;
861 }
862 else {
863 int flags = _document->ProcessEntities() ? StrPair::TEXT_ELEMENT : StrPair::TEXT_ELEMENT_LEAVE_ENTITIES;
864 if ( _document->WhitespaceMode() == COLLAPSE_WHITESPACE ) {
865 flags |= StrPair::COLLAPSE_WHITESPACE;
866 }
867
868 p = _value.ParseText( p, "<", flags );
869 if ( !p ) {
870 _document->SetError( XML_ERROR_PARSING_TEXT, start, 0 );
871 }
872 if ( p && *p ) {
873 return p-1;
874 }
875 }
876 return 0;
877}
878
879
881{
882 if ( !doc ) {
883 doc = _document;
884 }
885 XMLText* text = doc->NewText( Value() ); // fixme: this will always allocate memory. Intern?
886 text->SetCData( this->CData() );
887 return text;
888}
889
890
892{
893 return ( compare->ToText() && XMLUtil::StringEqual( compare->ToText()->Value(), Value() ));
894}
895
896
898{
899 return visitor->Visit( *this );
900}
901
902
903// --------- XMLComment ---------- //
904
905XMLComment::XMLComment( XMLDocument* doc ) : XMLNode( doc )
906{
907}
908
909
910XMLComment::~XMLComment()
911{
912}
913
914
915char* XMLComment::ParseDeep( char* p, StrPair* )
916{
917 // Comment parses as text.
918 const char* start = p;
919 p = _value.ParseText( p, "-->", StrPair::COMMENT );
920 if ( p == 0 ) {
921 _document->SetError( XML_ERROR_PARSING_COMMENT, start, 0 );
922 }
923 return p;
924}
925
926
928{
929 if ( !doc ) {
930 doc = _document;
931 }
932 XMLComment* comment = doc->NewComment( Value() ); // fixme: this will always allocate memory. Intern?
933 return comment;
934}
935
936
938{
939 return ( compare->ToComment() && XMLUtil::StringEqual( compare->ToComment()->Value(), Value() ));
940}
941
942
944{
945 return visitor->Visit( *this );
946}
947
948
949// --------- XMLDeclaration ---------- //
950
951XMLDeclaration::XMLDeclaration( XMLDocument* doc ) : XMLNode( doc )
952{
953}
954
955
956XMLDeclaration::~XMLDeclaration()
957{
958 //printf( "~XMLDeclaration\n" );
959}
960
961
962char* XMLDeclaration::ParseDeep( char* p, StrPair* )
963{
964 // Declaration parses as text.
965 const char* start = p;
966 p = _value.ParseText( p, "?>", StrPair::NEEDS_NEWLINE_NORMALIZATION );
967 if ( p == 0 ) {
968 _document->SetError( XML_ERROR_PARSING_DECLARATION, start, 0 );
969 }
970 return p;
971}
972
973
975{
976 if ( !doc ) {
977 doc = _document;
978 }
979 XMLDeclaration* dec = doc->NewDeclaration( Value() ); // fixme: this will always allocate memory. Intern?
980 return dec;
981}
982
983
985{
986 return ( compare->ToDeclaration() && XMLUtil::StringEqual( compare->ToDeclaration()->Value(), Value() ));
987}
988
989
990
992{
993 return visitor->Visit( *this );
994}
995
996// --------- XMLUnknown ---------- //
997
998XMLUnknown::XMLUnknown( XMLDocument* doc ) : XMLNode( doc )
999{
1000}
1001
1002
1003XMLUnknown::~XMLUnknown()
1004{
1005}
1006
1007
1008char* XMLUnknown::ParseDeep( char* p, StrPair* )
1009{
1010 // Unknown parses as text.
1011 const char* start = p;
1012
1013 p = _value.ParseText( p, ">", StrPair::NEEDS_NEWLINE_NORMALIZATION );
1014 if ( !p ) {
1015 _document->SetError( XML_ERROR_PARSING_UNKNOWN, start, 0 );
1016 }
1017 return p;
1018}
1019
1020
1022{
1023 if ( !doc ) {
1024 doc = _document;
1025 }
1026 XMLUnknown* text = doc->NewUnknown( Value() ); // fixme: this will always allocate memory. Intern?
1027 return text;
1028}
1029
1030
1032{
1033 return ( compare->ToUnknown() && XMLUtil::StringEqual( compare->ToUnknown()->Value(), Value() ));
1034}
1035
1036
1038{
1039 return visitor->Visit( *this );
1040}
1041
1042// --------- XMLAttribute ---------- //
1043char* XMLAttribute::ParseDeep( char* p, bool processEntities )
1044{
1045 // Parse using the name rules: bug fix, was using ParseText before
1046 p = _name.ParseName( p );
1047 if ( !p || !*p ) {
1048 return 0;
1049 }
1050
1051 // Skip white space before =
1052 p = XMLUtil::SkipWhiteSpace( p );
1053 if ( !p || *p != '=' ) {
1054 return 0;
1055 }
1056
1057 ++p; // move up to opening quote
1058 p = XMLUtil::SkipWhiteSpace( p );
1059 if ( *p != '\"' && *p != '\'' ) {
1060 return 0;
1061 }
1062
1063 char endTag[2] = { *p, 0 };
1064 ++p; // move past opening quote
1065
1066 p = _value.ParseText( p, endTag, processEntities ? StrPair::ATTRIBUTE_VALUE : StrPair::ATTRIBUTE_VALUE_LEAVE_ENTITIES );
1067 return p;
1068}
1069
1070
1071void XMLAttribute::SetName( const char* n )
1072{
1073 _name.SetStr( n );
1074}
1075
1076
1077XMLError XMLAttribute::QueryIntValue( int* value ) const
1078{
1079 if ( XMLUtil::ToInt( Value(), value )) {
1080 return XML_NO_ERROR;
1081 }
1082 return XML_WRONG_ATTRIBUTE_TYPE;
1083}
1084
1085
1086XMLError XMLAttribute::QueryUnsignedValue( unsigned int* value ) const
1087{
1088 if ( XMLUtil::ToUnsigned( Value(), value )) {
1089 return XML_NO_ERROR;
1090 }
1091 return XML_WRONG_ATTRIBUTE_TYPE;
1092}
1093
1094
1095XMLError XMLAttribute::QueryBoolValue( bool* value ) const
1096{
1097 if ( XMLUtil::ToBool( Value(), value )) {
1098 return XML_NO_ERROR;
1099 }
1100 return XML_WRONG_ATTRIBUTE_TYPE;
1101}
1102
1103
1104XMLError XMLAttribute::QueryFloatValue( float* value ) const
1105{
1106 if ( XMLUtil::ToFloat( Value(), value )) {
1107 return XML_NO_ERROR;
1108 }
1109 return XML_WRONG_ATTRIBUTE_TYPE;
1110}
1111
1112
1113XMLError XMLAttribute::QueryDoubleValue( double* value ) const
1114{
1115 if ( XMLUtil::ToDouble( Value(), value )) {
1116 return XML_NO_ERROR;
1117 }
1118 return XML_WRONG_ATTRIBUTE_TYPE;
1119}
1120
1121
1123{
1124 _value.SetStr( v );
1125}
1126
1127
1129{
1130 char buf[BUF_SIZE];
1131 XMLUtil::ToStr( v, buf, BUF_SIZE );
1132 _value.SetStr( buf );
1133}
1134
1135
1137{
1138 char buf[BUF_SIZE];
1139 XMLUtil::ToStr( v, buf, BUF_SIZE );
1140 _value.SetStr( buf );
1141}
1142
1143
1145{
1146 char buf[BUF_SIZE];
1147 XMLUtil::ToStr( v, buf, BUF_SIZE );
1148 _value.SetStr( buf );
1149}
1150
1152{
1153 char buf[BUF_SIZE];
1154 XMLUtil::ToStr( v, buf, BUF_SIZE );
1155 _value.SetStr( buf );
1156}
1157
1159{
1160 char buf[BUF_SIZE];
1161 XMLUtil::ToStr( v, buf, BUF_SIZE );
1162 _value.SetStr( buf );
1163}
1164
1165
1166// --------- XMLElement ---------- //
1167XMLElement::XMLElement( XMLDocument* doc ) : XMLNode( doc ),
1168 _closingType( 0 ),
1169 _rootAttribute( 0 )
1170{
1171}
1172
1173
1174XMLElement::~XMLElement()
1175{
1176 while( _rootAttribute ) {
1177 XMLAttribute* next = _rootAttribute->_next;
1178 DELETE_ATTRIBUTE( _rootAttribute );
1179 _rootAttribute = next;
1180 }
1181}
1182
1183
1184XMLAttribute* XMLElement::FindAttribute( const char* name )
1185{
1186 XMLAttribute* a = 0;
1187 for( a=_rootAttribute; a; a = a->_next ) {
1188 if ( XMLUtil::StringEqual( a->Name(), name ) ) {
1189 return a;
1190 }
1191 }
1192 return 0;
1193}
1194
1195
1197{
1198 XMLAttribute* a = 0;
1199 for( a=_rootAttribute; a; a = a->_next ) {
1200 if ( XMLUtil::StringEqual( a->Name(), name ) ) {
1201 return a;
1202 }
1203 }
1204 return 0;
1205}
1206
1207
1208const char* XMLElement::Attribute( const char* name, const char* value ) const
1209{
1210 const XMLAttribute* a = FindAttribute( name );
1211 if ( !a ) {
1212 return 0;
1213 }
1214 if ( !value || XMLUtil::StringEqual( a->Value(), value )) {
1215 return a->Value();
1216 }
1217 return 0;
1218}
1219
1220
1221const char* XMLElement::GetText() const
1222{
1223 if ( FirstChild() && FirstChild()->ToText() ) {
1224 return FirstChild()->ToText()->Value();
1225 }
1226 return 0;
1227}
1228
1229
1230XMLError XMLElement::QueryIntText( int* _value ) const
1231{
1232 if ( FirstChild() && FirstChild()->ToText() ) {
1233 const char* t = FirstChild()->ToText()->Value();
1234 if ( XMLUtil::ToInt( t, _value ) ) {
1235 return XML_SUCCESS;
1236 }
1237 return XML_CAN_NOT_CONVERT_TEXT;
1238 }
1239 return XML_NO_TEXT_NODE;
1240}
1241
1242
1243XMLError XMLElement::QueryUnsignedText( unsigned* _value ) const
1244{
1245 if ( FirstChild() && FirstChild()->ToText() ) {
1246 const char* t = FirstChild()->ToText()->Value();
1247 if ( XMLUtil::ToUnsigned( t, _value ) ) {
1248 return XML_SUCCESS;
1249 }
1250 return XML_CAN_NOT_CONVERT_TEXT;
1251 }
1252 return XML_NO_TEXT_NODE;
1253}
1254
1255
1256XMLError XMLElement::QueryBoolText( bool* _value ) const
1257{
1258 if ( FirstChild() && FirstChild()->ToText() ) {
1259 const char* t = FirstChild()->ToText()->Value();
1260 if ( XMLUtil::ToBool( t, _value ) ) {
1261 return XML_SUCCESS;
1262 }
1263 return XML_CAN_NOT_CONVERT_TEXT;
1264 }
1265 return XML_NO_TEXT_NODE;
1266}
1267
1268
1269XMLError XMLElement::QueryDoubleText( double* _value ) const
1270{
1271 if ( FirstChild() && FirstChild()->ToText() ) {
1272 const char* t = FirstChild()->ToText()->Value();
1273 if ( XMLUtil::ToDouble( t, _value ) ) {
1274 return XML_SUCCESS;
1275 }
1276 return XML_CAN_NOT_CONVERT_TEXT;
1277 }
1278 return XML_NO_TEXT_NODE;
1279}
1280
1281
1282XMLError XMLElement::QueryFloatText( float* _value ) const
1283{
1284 if ( FirstChild() && FirstChild()->ToText() ) {
1285 const char* t = FirstChild()->ToText()->Value();
1286 if ( XMLUtil::ToFloat( t, _value ) ) {
1287 return XML_SUCCESS;
1288 }
1289 return XML_CAN_NOT_CONVERT_TEXT;
1290 }
1291 return XML_NO_TEXT_NODE;
1292}
1293
1294
1295
1296XMLAttribute* XMLElement::FindOrCreateAttribute( const char* name )
1297{
1298 XMLAttribute* last = 0;
1299 XMLAttribute* attrib = 0;
1300 for( attrib = _rootAttribute;
1301 attrib;
1302 last = attrib, attrib = attrib->_next ) {
1303 if ( XMLUtil::StringEqual( attrib->Name(), name ) ) {
1304 break;
1305 }
1306 }
1307 if ( !attrib ) {
1308 attrib = new (_document->_attributePool.Alloc() ) XMLAttribute();
1309 attrib->_memPool = &_document->_attributePool;
1310 if ( last ) {
1311 last->_next = attrib;
1312 }
1313 else {
1314 _rootAttribute = attrib;
1315 }
1316 attrib->SetName( name );
1317 }
1318 return attrib;
1319}
1320
1321
1323{
1324 XMLAttribute* prev = 0;
1325 for( XMLAttribute* a=_rootAttribute; a; a=a->_next ) {
1326 if ( XMLUtil::StringEqual( name, a->Name() ) ) {
1327 if ( prev ) {
1328 prev->_next = a->_next;
1329 }
1330 else {
1331 _rootAttribute = a->_next;
1332 }
1333 DELETE_ATTRIBUTE( a );
1334 break;
1335 }
1336 prev = a;
1337 }
1338}
1339
1340
1341char* XMLElement::ParseAttributes( char* p )
1342{
1343 const char* start = p;
1345
1346 // Read the attributes.
1347 while( p ) {
1348 p = XMLUtil::SkipWhiteSpace( p );
1349 if ( !p || !(*p) ) {
1350 _document->SetError( XML_ERROR_PARSING_ELEMENT, start, Name() );
1351 return 0;
1352 }
1353
1354 // attribute.
1355 if ( XMLUtil::IsAlpha( *p ) ) {
1356 XMLAttribute* attrib = new (_document->_attributePool.Alloc() ) XMLAttribute();
1357 attrib->_memPool = &_document->_attributePool;
1358
1359 p = attrib->ParseDeep( p, _document->ProcessEntities() );
1360 if ( !p || Attribute( attrib->Name() ) ) {
1361 DELETE_ATTRIBUTE( attrib );
1362 _document->SetError( XML_ERROR_PARSING_ATTRIBUTE, start, p );
1363 return 0;
1364 }
1365 // There is a minor bug here: if the attribute in the source xml
1366 // document is duplicated, it will not be detected and the
1367 // attribute will be doubly added. However, tracking the 'prevAttribute'
1368 // avoids re-scanning the attribute list. Preferring performance for
1369 // now, may reconsider in the future.
1370 if ( prevAttribute ) {
1371 prevAttribute->_next = attrib;
1372 }
1373 else {
1374 _rootAttribute = attrib;
1375 }
1376 prevAttribute = attrib;
1377 }
1378 // end of the tag
1379 else if ( *p == '/' && *(p+1) == '>' ) {
1380 _closingType = CLOSED;
1381 return p+2; // done; sealed element.
1382 }
1383 // end of the tag
1384 else if ( *p == '>' ) {
1385 ++p;
1386 break;
1387 }
1388 else {
1389 _document->SetError( XML_ERROR_PARSING_ELEMENT, start, p );
1390 return 0;
1391 }
1392 }
1393 return p;
1394}
1395
1396
1397//
1398// <ele></ele>
1399// <ele>foo<b>bar</b></ele>
1400//
1401char* XMLElement::ParseDeep( char* p, StrPair* strPair )
1402{
1403 // Read the element name.
1404 p = XMLUtil::SkipWhiteSpace( p );
1405 if ( !p ) {
1406 return 0;
1407 }
1408
1409 // The closing element is the </element> form. It is
1410 // parsed just like a regular element then deleted from
1411 // the DOM.
1412 if ( *p == '/' ) {
1413 _closingType = CLOSING;
1414 ++p;
1415 }
1416
1417 p = _value.ParseName( p );
1418 if ( _value.Empty() ) {
1419 return 0;
1420 }
1421
1422 p = ParseAttributes( p );
1423 if ( !p || !*p || _closingType ) {
1424 return p;
1425 }
1426
1427 p = XMLNode::ParseDeep( p, strPair );
1428 return p;
1429}
1430
1431
1432
1434{
1435 if ( !doc ) {
1436 doc = _document;
1437 }
1438 XMLElement* element = doc->NewElement( Value() ); // fixme: this will always allocate memory. Intern?
1439 for( const XMLAttribute* a=FirstAttribute(); a; a=a->Next() ) {
1440 element->SetAttribute( a->Name(), a->Value() ); // fixme: this will always allocate memory. Intern?
1441 }
1442 return element;
1443}
1444
1445
1447{
1448 const XMLElement* other = compare->ToElement();
1449 if ( other && XMLUtil::StringEqual( other->Value(), Value() )) {
1450
1451 const XMLAttribute* a=FirstAttribute();
1452 const XMLAttribute* b=other->FirstAttribute();
1453
1454 while ( a && b ) {
1455 if ( !XMLUtil::StringEqual( a->Value(), b->Value() ) ) {
1456 return false;
1457 }
1458 a = a->Next();
1459 b = b->Next();
1460 }
1461 if ( a || b ) {
1462 // different count
1463 return false;
1464 }
1465 return true;
1466 }
1467 return false;
1468}
1469
1470
1472{
1473 if ( visitor->VisitEnter( *this, _rootAttribute ) ) {
1474 for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) {
1475 if ( !node->Accept( visitor ) ) {
1476 break;
1477 }
1478 }
1479 }
1480 return visitor->VisitExit( *this );
1481}
1482
1483
1484// --------- XMLDocument ----------- //
1485XMLDocument::XMLDocument( bool processEntities, Whitespace whitespace ) :
1486 XMLNode( 0 ),
1487 _writeBOM( false ),
1488 _processEntities( processEntities ),
1489 _errorID( XML_NO_ERROR ),
1490 _whitespace( whitespace ),
1491 _errorStr1( 0 ),
1492 _errorStr2( 0 ),
1493 _charBuffer( 0 )
1494{
1495 _document = this; // avoid warning about 'this' in initializer list
1496}
1497
1498
1499XMLDocument::~XMLDocument()
1500{
1502 delete [] _charBuffer;
1503
1504#if 0
1505 textPool.Trace( "text" );
1506 elementPool.Trace( "element" );
1507 commentPool.Trace( "comment" );
1508 attributePool.Trace( "attribute" );
1509#endif
1510
1511 TIXMLASSERT( _textPool.CurrentAllocs() == 0 );
1512 TIXMLASSERT( _elementPool.CurrentAllocs() == 0 );
1513 TIXMLASSERT( _commentPool.CurrentAllocs() == 0 );
1514 TIXMLASSERT( _attributePool.CurrentAllocs() == 0 );
1515}
1516
1517
1518void XMLDocument::InitDocument()
1519{
1520 _errorID = XML_NO_ERROR;
1521 _errorStr1 = 0;
1522 _errorStr2 = 0;
1523
1524 delete [] _charBuffer;
1525 _charBuffer = 0;
1526}
1527
1528
1530{
1531 XMLElement* ele = new (_elementPool.Alloc()) XMLElement( this );
1532 ele->_memPool = &_elementPool;
1533 ele->SetName( name );
1534 return ele;
1535}
1536
1537
1539{
1540 XMLComment* comment = new (_commentPool.Alloc()) XMLComment( this );
1541 comment->_memPool = &_commentPool;
1542 comment->SetValue( str );
1543 return comment;
1544}
1545
1546
1548{
1549 XMLText* text = new (_textPool.Alloc()) XMLText( this );
1550 text->_memPool = &_textPool;
1551 text->SetValue( str );
1552 return text;
1553}
1554
1555
1557{
1558 XMLDeclaration* dec = new (_commentPool.Alloc()) XMLDeclaration( this );
1559 dec->_memPool = &_commentPool;
1560 dec->SetValue( str ? str : "xml version=\"1.0\" encoding=\"UTF-8\"" );
1561 return dec;
1562}
1563
1564
1566{
1567 XMLUnknown* unk = new (_commentPool.Alloc()) XMLUnknown( this );
1568 unk->_memPool = &_commentPool;
1569 unk->SetValue( str );
1570 return unk;
1571}
1572
1573
1574XMLError XMLDocument::LoadFile( const char* filename )
1575{
1577 InitDocument();
1578 FILE* fp = 0;
1579
1580 MMechostr(MSKDEBUG, "XMLDocument::LoadFile START\n");
1581
1582#if defined(_MSC_VER) && (_MSC_VER >= 1400 )
1583 errno_t err = fopen_s(&fp, filename, "rb" );
1584 if ( !fp || err) {
1585#else
1586 fp = fopen( filename, "rb" );
1587 if ( !fp) {
1588#endif
1589 MMechostr(MSKDEBUG, "XMLDocument::LoadFile XML_ERROR_FILE_NOT_FOUND\n");
1590 SetError( XML_ERROR_FILE_NOT_FOUND, filename, 0 );
1591 return _errorID;
1592 }
1593 LoadFile( fp );
1594 fclose( fp );
1595 return _errorID;
1596}
1597
1598
1599XMLError XMLDocument::LoadFile( FILE* fp )
1600{
1602 InitDocument();
1603
1604 fseek( fp, 0, SEEK_END );
1605 size_t size = ftell( fp );
1606 fseek( fp, 0, SEEK_SET );
1607
1608 if ( size == 0 ) {
1609 return _errorID;
1610 }
1611
1612 _charBuffer = new char[size+1];
1613 size_t read = fread( _charBuffer, 1, size, fp );
1614 if ( read != size ) {
1615 SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
1616 return _errorID;
1617 }
1618
1619 _charBuffer[size] = 0;
1620
1621 const char* p = _charBuffer;
1622 p = XMLUtil::SkipWhiteSpace( p );
1623 p = XMLUtil::ReadBOM( p, &_writeBOM );
1624 if ( !p || !*p ) {
1625 SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
1626 return _errorID;
1627 }
1628
1629 ParseDeep( _charBuffer + (p-_charBuffer), 0 );
1630 return _errorID;
1631}
1632
1633
1634XMLError XMLDocument::SaveFile( const char* filename, bool compact )
1635{
1636 FILE* fp = 0;
1637#if defined(_MSC_VER) && (_MSC_VER >= 1400 )
1638 errno_t err = fopen_s(&fp, filename, "w" );
1639 if ( !fp || err) {
1640#else
1641 fp = fopen( filename, "w" );
1642 if ( !fp) {
1643#endif
1644 SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, filename, 0 );
1645 return _errorID;
1646 }
1648 fclose( fp );
1649 return _errorID;
1650}
1651
1652
1653XMLError XMLDocument::SaveFile( FILE* fp, bool compact )
1654{
1656 Print( &stream );
1657 return _errorID;
1658}
1659
1660
1661XMLError XMLDocument::Parse( const char* p, size_t len )
1662{
1664 InitDocument();
1665
1666 if ( !p || !*p ) {
1667 SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
1668 return _errorID;
1669 }
1670 if ( len == (size_t)(-1) ) {
1671 len = strlen( p );
1672 }
1673 _charBuffer = new char[ len+1 ];
1674 memcpy( _charBuffer, p, len );
1675 _charBuffer[len] = 0;
1676
1677 p = XMLUtil::SkipWhiteSpace( p );
1678 p = XMLUtil::ReadBOM( p, &_writeBOM );
1679 if ( !p || !*p ) {
1680 SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
1681 return _errorID;
1682 }
1683
1684 ParseDeep( _charBuffer, 0 );
1685 return _errorID;
1686}
1687
1688
1690{
1692 if ( !streamer ) {
1694 }
1695 Accept( streamer );
1696}
1697
1698
1699void XMLDocument::SetError( XMLError error, const char* str1, const char* str2 )
1700{
1701 _errorID = error;
1702 _errorStr1 = str1;
1703 _errorStr2 = str2;
1704}
1705
1706
1708{
1709 if ( _errorID ) {
1710 static const int LEN = 20;
1711 char buf1[LEN] = { 0 };
1712 char buf2[LEN] = { 0 };
1713
1714 if ( _errorStr1 ) {
1715 TIXML_SNPRINTF( buf1, LEN, "%s", _errorStr1 );
1716 }
1717 if ( _errorStr2 ) {
1718 TIXML_SNPRINTF( buf2, LEN, "%s", _errorStr2 );
1719 }
1720
1721 printf( "XMLDocument error id=%d str1=%s str2=%s\n",
1722 _errorID, buf1, buf2 );
1723 }
1724}
1725
1726
1728 _elementJustOpened( false ),
1729 _firstElement( true ),
1730 _fp( file ),
1731 _depth( 0 ),
1732 _textDepth( -1 ),
1733 _processEntities( true ),
1734 _compactMode( compact )
1735{
1736 for( int i=0; i<ENTITY_RANGE; ++i ) {
1737 _entityFlag[i] = false;
1738 _restrictedEntityFlag[i] = false;
1739 }
1740 for( int i=0; i<NUM_ENTITIES; ++i ) {
1741 TIXMLASSERT( entities[i].value < ENTITY_RANGE );
1742 if ( entities[i].value < ENTITY_RANGE ) {
1743 _entityFlag[ (int)entities[i].value ] = true;
1744 }
1745 }
1746 _restrictedEntityFlag[(int)'&'] = true;
1747 _restrictedEntityFlag[(int)'<'] = true;
1748 _restrictedEntityFlag[(int)'>'] = true; // not required, but consistency is nice
1749 _buffer.Push( 0 );
1750}
1751
1752
1753void XMLPrinter::Print( const char* format, ... )
1754{
1755 va_list va;
1756 va_start( va, format );
1757
1758 if ( _fp ) {
1759 vfprintf( _fp, format, va );
1760 }
1761 else {
1762 // This seems brutally complex. Haven't figured out a better
1763 // way on windows.
1764#ifdef _MSC_VER
1765 int len = -1;
1766 int expand = 1000;
1767 while ( len < 0 ) {
1768 len = vsnprintf_s( _accumulator.Mem(), _accumulator.Capacity(), _TRUNCATE, format, va );
1769 if ( len < 0 ) {
1770 expand *= 3/2;
1771 _accumulator.PushArr( expand );
1772 }
1773 }
1774 char* p = _buffer.PushArr( len ) - 1;
1775 memcpy( p, _accumulator.Mem(), len+1 );
1776#else
1777 int len = vsnprintf( 0, 0, format, va );
1778 // Close out and re-start the va-args
1779 va_end( va );
1780 va_start( va, format );
1781 char* p = _buffer.PushArr( len ) - 1;
1782 vsnprintf( p, len+1, format, va );
1783#endif
1784 }
1785 va_end( va );
1786}
1787
1788
1789void XMLPrinter::PrintSpace( int depth )
1790{
1791 for( int i=0; i<depth; ++i ) {
1792 Print( " " );
1793 }
1794}
1795
1796
1797void XMLPrinter::PrintString( const char* p, bool restricted )
1798{
1799 // Look for runs of bytes between entities to print.
1800 const char* q = p;
1801 const bool* flag = restricted ? _restrictedEntityFlag : _entityFlag;
1802
1803 if ( _processEntities ) {
1804 while ( *q ) {
1805 // Remember, char is sometimes signed. (How many times has that bitten me?)
1806 if ( *q > 0 && *q < ENTITY_RANGE ) {
1807 // Check for entities. If one is found, flush
1808 // the stream up until the entity, write the
1809 // entity, and keep looking.
1810 if ( flag[(unsigned)(*q)] ) {
1811 while ( p < q ) {
1812 Print( "%c", *p );
1813 ++p;
1814 }
1815 for( int i=0; i<NUM_ENTITIES; ++i ) {
1816 if ( entities[i].value == *q ) {
1817 Print( "&%s;", entities[i].pattern );
1818 break;
1819 }
1820 }
1821 ++p;
1822 }
1823 }
1824 ++q;
1825 }
1826 }
1827 // Flush the remaining string. This will be the entire
1828 // string if an entity wasn't found.
1829 if ( !_processEntities || (q-p > 0) ) {
1830 Print( "%s", p );
1831 }
1832}
1833
1834
1836{
1837 static const unsigned char bom[] = { TIXML_UTF_LEAD_0, TIXML_UTF_LEAD_1, TIXML_UTF_LEAD_2, 0 };
1838 if ( writeBOM ) {
1839 Print( "%s", bom );
1840 }
1841 if ( writeDec ) {
1842 PushDeclaration( "xml version=\"1.0\"" );
1843 }
1844}
1845
1846
1848{
1849 if ( _elementJustOpened ) {
1850 SealElement();
1851 }
1852 _stack.Push( name );
1853
1854 if ( _textDepth < 0 && !_firstElement && !_compactMode ) {
1855 Print( "\n" );
1856 PrintSpace( _depth );
1857 }
1858
1859 Print( "<%s", name );
1860 _elementJustOpened = true;
1861 _firstElement = false;
1862 ++_depth;
1863}
1864
1865
1866void XMLPrinter::PushAttribute( const char* name, const char* value )
1867{
1868 TIXMLASSERT( _elementJustOpened );
1869 Print( " %s=\"", name );
1870 PrintString( value, false );
1871 Print( "\"" );
1872}
1873
1874
1875void XMLPrinter::PushAttribute( const char* name, int v )
1876{
1877 char buf[BUF_SIZE];
1878 XMLUtil::ToStr( v, buf, BUF_SIZE );
1880}
1881
1882
1883void XMLPrinter::PushAttribute( const char* name, unsigned v )
1884{
1885 char buf[BUF_SIZE];
1886 XMLUtil::ToStr( v, buf, BUF_SIZE );
1887 PushAttribute( name, buf );
1888}
1889
1890
1891void XMLPrinter::PushAttribute( const char* name, bool v )
1892{
1893 char buf[BUF_SIZE];
1894 XMLUtil::ToStr( v, buf, BUF_SIZE );
1895 PushAttribute( name, buf );
1896}
1897
1898
1899void XMLPrinter::PushAttribute( const char* name, double v )
1900{
1901 char buf[BUF_SIZE];
1902 XMLUtil::ToStr( v, buf, BUF_SIZE );
1903 PushAttribute( name, buf );
1904}
1905
1906
1908{
1909 --_depth;
1910 const char* name = _stack.Pop();
1911
1912 if ( _elementJustOpened ) {
1913 Print( "/>" );
1914 }
1915 else {
1916 if ( _textDepth < 0 && !_compactMode) {
1917 Print( "\n" );
1918 PrintSpace( _depth );
1919 }
1920 Print( "</%s>", name );
1921 }
1922
1923 if ( _textDepth == _depth ) {
1924 _textDepth = -1;
1925 }
1926 if ( _depth == 0 && !_compactMode) {
1927 Print( "\n" );
1928 }
1929 _elementJustOpened = false;
1930}
1931
1932
1933void XMLPrinter::SealElement()
1934{
1935 _elementJustOpened = false;
1936 Print( ">" );
1937}
1938
1939
1940void XMLPrinter::PushText( const char* text, bool cdata )
1941{
1942 _textDepth = _depth-1;
1943
1944 if ( _elementJustOpened ) {
1945 SealElement();
1946 }
1947 if ( cdata ) {
1948 Print( "<![CDATA[" );
1949 Print( "%s", text );
1950 Print( "]]>" );
1951 }
1952 else {
1953 PrintString( text, true );
1954 }
1955}
1956
1957void XMLPrinter::PushText( int value )
1958{
1959 char buf[BUF_SIZE];
1960 XMLUtil::ToStr( value, buf, BUF_SIZE );
1961 PushText( buf, false );
1962}
1963
1964
1965void XMLPrinter::PushText( unsigned value )
1966{
1967 char buf[BUF_SIZE];
1968 XMLUtil::ToStr( value, buf, BUF_SIZE );
1969 PushText( buf, false );
1970}
1971
1972
1973void XMLPrinter::PushText( bool value )
1974{
1975 char buf[BUF_SIZE];
1976 XMLUtil::ToStr( value, buf, BUF_SIZE );
1977 PushText( buf, false );
1978}
1979
1980
1981void XMLPrinter::PushText( float value )
1982{
1983 char buf[BUF_SIZE];
1984 XMLUtil::ToStr( value, buf, BUF_SIZE );
1985 PushText( buf, false );
1986}
1987
1988
1989void XMLPrinter::PushText( double value )
1990{
1991 char buf[BUF_SIZE];
1992 XMLUtil::ToStr( value, buf, BUF_SIZE );
1993 PushText( buf, false );
1994}
1995
1996
1998{
1999 if ( _elementJustOpened ) {
2000 SealElement();
2001 }
2002 if ( _textDepth < 0 && !_firstElement && !_compactMode) {
2003 Print( "\n" );
2004 PrintSpace( _depth );
2005 }
2006 _firstElement = false;
2007 Print( "<!--%s-->", comment );
2008}
2009
2010
2011void XMLPrinter::PushDeclaration( const char* value )
2012{
2013 if ( _elementJustOpened ) {
2014 SealElement();
2015 }
2016 if ( _textDepth < 0 && !_firstElement && !_compactMode) {
2017 Print( "\n" );
2018 PrintSpace( _depth );
2019 }
2020 _firstElement = false;
2021 Print( "<?%s?>", value );
2022}
2023
2024
2025void XMLPrinter::PushUnknown( const char* value )
2026{
2027 if ( _elementJustOpened ) {
2028 SealElement();
2029 }
2030 if ( _textDepth < 0 && !_firstElement && !_compactMode) {
2031 Print( "\n" );
2032 PrintSpace( _depth );
2033 }
2034 _firstElement = false;
2035 Print( "<!%s>", value );
2036}
2037
2038
2040{
2041 _processEntities = doc.ProcessEntities();
2042 if ( doc.HasBOM() ) {
2043 PushHeader( true, false );
2044 }
2045 return true;
2046}
2047
2048
2050{
2051 OpenElement( element.Name() );
2052 while ( attribute ) {
2053 PushAttribute( attribute->Name(), attribute->Value() );
2054 attribute = attribute->Next();
2055 }
2056 return true;
2057}
2058
2059
2061{
2062 CloseElement();
2063 return true;
2064}
2065
2066
2068{
2069 PushText( text.Value(), text.CData() );
2070 return true;
2071}
2072
2073
2075{
2076 PushComment( comment.Value() );
2077 return true;
2078}
2079
2081{
2082 PushDeclaration( declaration.Value() );
2083 return true;
2084}
2085
2086
2088{
2089 PushUnknown( unknown.Value() );
2090 return true;
2091}
2092
2093} // namespace tinyxml2
2094
XMLError QueryFloatValue(float *value) const
See QueryIntAttribute.
XMLError QueryDoubleValue(double *value) const
See QueryIntAttribute.
void SetAttribute(const char *value)
Set the attribute to a string value.
XMLError QueryUnsignedValue(unsigned int *value) const
See QueryIntAttribute.
XMLError QueryBoolValue(bool *value) const
See QueryIntAttribute.
XMLError QueryIntValue(int *value) const
const char * Value() const
The value of the attribute.
Definition tinyxml2.h:966
virtual bool Accept(XMLVisitor *visitor) const
Definition tinyxml2.cpp:943
virtual bool ShallowEqual(const XMLNode *compare) const
Definition tinyxml2.cpp:937
virtual XMLNode * ShallowClone(XMLDocument *document) const
Definition tinyxml2.cpp:927
virtual bool Accept(XMLVisitor *visitor) const
Definition tinyxml2.cpp:991
virtual XMLNode * ShallowClone(XMLDocument *document) const
Definition tinyxml2.cpp:974
virtual bool ShallowEqual(const XMLNode *compare) const
Definition tinyxml2.cpp:984
XMLError Parse(const char *xml, size_t nBytes=(size_t)(-1))
void PrintError() const
If there is an error, print it to stdout.
void Print(XMLPrinter *streamer=0)
XMLError LoadFile(const char *filename)
bool Error() const
Return true if there was an error parsing the document.
Definition tinyxml2.h:1493
XMLComment * NewComment(const char *comment)
XMLElement * NewElement(const char *name)
XMLUnknown * NewUnknown(const char *text)
XMLError SaveFile(const char *filename, bool compact=false)
virtual bool Accept(XMLVisitor *visitor) const
Definition tinyxml2.cpp:570
XMLText * NewText(const char *text)
XMLDeclaration * NewDeclaration(const char *text=0)
XMLError QueryDoubleText(double *_value) const
See QueryIntText()
const char * GetText() const
const XMLAttribute * FindAttribute(const char *name) const
Query a specific attribute in the list.
XMLError QueryUnsignedText(unsigned *_value) const
See QueryIntText()
XMLError QueryFloatText(float *_value) const
See QueryIntText()
const char * Attribute(const char *name, const char *value=0) const
const XMLAttribute * FirstAttribute() const
Return the first attribute in the list.
Definition tinyxml2.h:1225
XMLError QueryIntText(int *_value) const
virtual bool ShallowEqual(const XMLNode *compare) const
virtual bool Accept(XMLVisitor *visitor) const
XMLError QueryBoolText(bool *_value) const
See QueryIntText()
virtual XMLNode * ShallowClone(XMLDocument *document) const
const char * Name() const
Get the name of an element (which is the Value() of the node.)
Definition tinyxml2.h:1064
void DeleteAttribute(const char *name)
const char * Value() const
Definition tinyxml2.h:591
void SetValue(const char *val, bool staticMem=false)
Definition tinyxml2.cpp:603
virtual XMLText * ToText()
Safely cast to Text, or null.
Definition tinyxml2.h:543
const XMLElement * LastChildElement(const char *value=0) const
Definition tinyxml2.cpp:736
void DeleteChild(XMLNode *node)
Definition tinyxml2.cpp:646
const XMLElement * NextSiblingElement(const char *value=0) const
Get the next (right) sibling element of this node, with an opitionally supplied name.
Definition tinyxml2.cpp:750
const XMLElement * FirstChildElement(const char *value=0) const
Definition tinyxml2.cpp:722
XMLNode * InsertAfterChild(XMLNode *afterThis, XMLNode *addThis)
Definition tinyxml2.cpp:700
virtual XMLElement * ToElement()
Safely cast to an Element, or null.
Definition tinyxml2.h:539
const XMLElement * PreviousSiblingElement(const char *value=0) const
Get the previous (left) sibling element of this node, with an opitionally supplied name.
Definition tinyxml2.cpp:762
const XMLNode * FirstChild() const
Get the first child node, or null if none exists.
Definition tinyxml2.h:615
XMLNode * InsertFirstChild(XMLNode *addThis)
Definition tinyxml2.cpp:676
XMLNode * InsertEndChild(XMLNode *addThis)
Definition tinyxml2.cpp:653
virtual bool VisitExit(const XMLDocument &)
Visit a document.
Definition tinyxml2.h:1834
void PushHeader(bool writeBOM, bool writeDeclaration)
void PushText(const char *text, bool cdata=false)
Add a text node.
void PushAttribute(const char *name, const char *value)
If streaming, add an attribute to an open element.
virtual bool VisitEnter(const XMLDocument &)
Visit a document.
void OpenElement(const char *name)
XMLPrinter(std::FILE *file=0, bool compact=false)
virtual bool Visit(const XMLText &text)
Visit a text node.
void CloseElement()
If streaming, close the Element.
void PushComment(const char *comment)
Add a comment.
virtual bool Accept(XMLVisitor *visitor) const
Definition tinyxml2.cpp:897
virtual XMLNode * ShallowClone(XMLDocument *document) const
Definition tinyxml2.cpp:880
virtual bool ShallowEqual(const XMLNode *compare) const
Definition tinyxml2.cpp:891
bool CData() const
Returns true if this is a CDATA text element.
Definition tinyxml2.h:808
virtual bool Accept(XMLVisitor *visitor) const
virtual XMLNode * ShallowClone(XMLDocument *document) const
virtual bool ShallowEqual(const XMLNode *compare) const