blob: 2287341549ef5e6cfa164fb5d05b9c0c08f04869 [file] [log] [blame]
U-Lama\Lee560bd472011-12-28 19:42:49 -08001#include "tinyxml2.h"
2
3#include <string.h>
4#include <stdlib.h>
5#include <stdio.h>
U-Lama\Lee4cee6112011-12-31 14:58:18 -08006#include <ctype.h>
Lee Thomasond1983222012-02-06 08:41:24 -08007#include <new.h>
8
9//#pragma warning ( disable : 4291 )
U-Lama\Lee560bd472011-12-28 19:42:49 -080010
11using namespace tinyxml2;
12
Lee Thomasone4422302012-01-20 17:59:50 -080013static const char LINE_FEED = (char)0x0a; // all line endings are normalized to LF
Lee Thomasonfde6a752012-01-14 18:08:12 -080014static const char LF = LINE_FEED;
15static const char CARRIAGE_RETURN = (char)0x0d; // CR gets filtered out
16static const char CR = CARRIAGE_RETURN;
Lee Thomasone4422302012-01-20 17:59:50 -080017static const char SINGLE_QUOTE = '\'';
18static const char DOUBLE_QUOTE = '\"';
Lee Thomasonfde6a752012-01-14 18:08:12 -080019
Lee Thomason43f59302012-02-06 18:18:11 -080020#define DELETE_NODE( node ) { MemPool* pool = node->memPool; node->~XMLNode(); pool->Free( node ); }
21#define DELETE_ATTRIBUTE( attrib ) { MemPool* pool = attrib->memPool; attrib->~XMLAttribute(); pool->Free( attrib ); }
22
Lee Thomason8ee79892012-01-25 17:44:30 -080023struct Entity {
24 const char* pattern;
25 int length;
26 char value;
27};
28
29static const int NUM_ENTITIES = 5;
30static const Entity entities[NUM_ENTITIES] =
31{
Lee Thomason18d68bd2012-01-26 18:17:26 -080032 { "quot", 4, DOUBLE_QUOTE },
Lee Thomason8ee79892012-01-25 17:44:30 -080033 { "amp", 3, '&' },
Lee Thomason18d68bd2012-01-26 18:17:26 -080034 { "apos", 4, SINGLE_QUOTE },
Lee Thomason8ee79892012-01-25 17:44:30 -080035 { "lt", 2, '<' },
36 { "gt", 2, '>' }
37};
38
Lee Thomasonfde6a752012-01-14 18:08:12 -080039
Lee Thomason1a1d4a72012-02-15 09:09:25 -080040StrPair::~StrPair()
41{
42 Reset();
43}
44
45
46void StrPair::Reset()
47{
48 if ( flags & NEEDS_DELETE ) {
49 delete [] start;
50 }
51 flags = 0;
52 start = 0;
53 end = 0;
54}
55
56
57void StrPair::SetStr( const char* str, int flags )
58{
59 Reset();
60 size_t len = strlen( str );
61 start = new char[ len+1 ];
U-Stream\Lee09a11c52012-02-17 08:31:16 -080062 memcpy( start, str, len+1 );
Lee Thomason1a1d4a72012-02-15 09:09:25 -080063 end = start + len;
64 this->flags = flags | NEEDS_DELETE;
65}
66
67
68char* StrPair::ParseText( char* p, const char* endTag, int strFlags )
69{
70 TIXMLASSERT( endTag && *endTag );
71
72 char* start = p; // fixme: hides a member
73 char endChar = *endTag;
74 int length = strlen( endTag );
75
76 // Inner loop of text parsing.
77 while ( *p ) {
78 if ( *p == endChar && strncmp( p, endTag, length ) == 0 ) {
79 Set( start, p, strFlags );
80 return p + length;
81 }
82 ++p;
83 }
84 return p;
85}
86
87
88char* StrPair::ParseName( char* p )
89{
90 char* start = p;
91
92 start = p;
93 if ( !start || !(*start) ) {
94 return 0;
95 }
96
97 if ( !XMLUtil::IsAlpha( *p ) ) {
98 return 0;
99 }
100
101 while( *p && (
102 XMLUtil::IsAlphaNum( (unsigned char) *p )
103 || *p == '_'
104 || *p == '-'
105 || *p == '.'
106 || *p == ':' ))
107 {
108 ++p;
109 }
110
111 if ( p > start ) {
112 Set( start, p, 0 );
113 return p;
114 }
115 return 0;
116}
117
118
Lee Thomasone4422302012-01-20 17:59:50 -0800119const char* StrPair::GetStr()
120{
121 if ( flags & NEEDS_FLUSH ) {
122 *end = 0;
Lee Thomason8ee79892012-01-25 17:44:30 -0800123 flags ^= NEEDS_FLUSH;
Lee Thomasone4422302012-01-20 17:59:50 -0800124
Lee Thomason8ee79892012-01-25 17:44:30 -0800125 if ( flags ) {
Lee Thomasone4422302012-01-20 17:59:50 -0800126 char* p = start;
127 char* q = start;
128
129 while( p < end ) {
Lee Thomason8ee79892012-01-25 17:44:30 -0800130 if ( (flags & NEEDS_NEWLINE_NORMALIZATION) && *p == CR ) {
Lee Thomasone4422302012-01-20 17:59:50 -0800131 // CR-LF pair becomes LF
132 // CR alone becomes LF
133 // LF-CR becomes LF
134 if ( *(p+1) == LF ) {
135 p += 2;
136 }
137 else {
138 ++p;
139 }
Lee Thomasone9ecdab2012-02-13 18:11:20 -0800140 *q++ = LF;
Lee Thomasone4422302012-01-20 17:59:50 -0800141 }
Lee Thomason8ee79892012-01-25 17:44:30 -0800142 else if ( (flags & NEEDS_NEWLINE_NORMALIZATION) && *p == LF ) {
Lee Thomasone4422302012-01-20 17:59:50 -0800143 if ( *(p+1) == CR ) {
144 p += 2;
145 }
146 else {
147 ++p;
148 }
Lee Thomasone9ecdab2012-02-13 18:11:20 -0800149 *q++ = LF;
Lee Thomasone4422302012-01-20 17:59:50 -0800150 }
Lee Thomason8ee79892012-01-25 17:44:30 -0800151 else if ( (flags & NEEDS_ENTITY_PROCESSING) && *p == '&' ) {
152 int i=0;
153 for( i=0; i<NUM_ENTITIES; ++i ) {
154 if ( strncmp( p+1, entities[i].pattern, entities[i].length ) == 0
155 && *(p+entities[i].length+1) == ';' )
156 {
157 // Found an entity convert;
158 *q = entities[i].value;
159 ++q;
160 p += entities[i].length + 2;
161 break;
162 }
163 }
164 if ( i == NUM_ENTITIES ) {
165 // fixme: treat as error?
166 ++p;
167 ++q;
168 }
169 }
Lee Thomasone4422302012-01-20 17:59:50 -0800170 else {
171 *q = *p;
172 ++p;
Lee Thomasonec975ce2012-01-23 11:42:06 -0800173 ++q;
Lee Thomasone4422302012-01-20 17:59:50 -0800174 }
175 }
Lee Thomason8ee79892012-01-25 17:44:30 -0800176 *q = 0;
Lee Thomasone4422302012-01-20 17:59:50 -0800177 }
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800178 flags = (flags & NEEDS_DELETE);
Lee Thomasone4422302012-01-20 17:59:50 -0800179 }
180 return start;
181}
182
Lee Thomason2c85a712012-01-31 08:24:24 -0800183
Lee Thomasone4422302012-01-20 17:59:50 -0800184
Lee Thomason56bdd022012-02-09 18:16:58 -0800185// --------- XMLUtil ----------- //
Lee Thomasond1983222012-02-06 08:41:24 -0800186
Lee Thomasond1983222012-02-06 08:41:24 -0800187char* XMLDocument::Identify( char* p, XMLNode** node )
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800188{
189 XMLNode* returnNode = 0;
Lee Thomason5492a1c2012-01-23 15:32:10 -0800190 char* start = p;
Lee Thomason56bdd022012-02-09 18:16:58 -0800191 p = XMLUtil::SkipWhiteSpace( p );
Lee Thomason5492a1c2012-01-23 15:32:10 -0800192 if( !p || !*p )
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800193 {
194 return 0;
195 }
196
197 // What is this thing?
198 // - Elements start with a letter or underscore, but xml is reserved.
199 // - Comments: <!--
200 // - Decleration: <?xml
201 // - Everthing else is unknown to tinyxml.
202 //
203
204 static const char* xmlHeader = { "<?xml" };
205 static const char* commentHeader = { "<!--" };
206 static const char* dtdHeader = { "<!" };
207 static const char* cdataHeader = { "<![CDATA[" };
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800208 static const char* elementHeader = { "<" }; // and a header for everything else; check last.
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800209
210 static const int xmlHeaderLen = 5;
211 static const int commentHeaderLen = 4;
212 static const int dtdHeaderLen = 2;
213 static const int cdataHeaderLen = 9;
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800214 static const int elementHeaderLen = 1;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800215
Lee Thomason50f97b22012-02-11 16:33:40 -0800216 TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLUnknown ) ); // use same memory pool
217 TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLDeclaration ) ); // use same memory pool
218
219 if ( XMLUtil::StringEqual( p, xmlHeader, xmlHeaderLen ) ) {
220 returnNode = new (commentPool.Alloc()) XMLDeclaration( this );
221 returnNode->memPool = &commentPool;
222 p += xmlHeaderLen;
223 }
224 else if ( XMLUtil::StringEqual( p, commentHeader, commentHeaderLen ) ) {
Lee Thomasond1983222012-02-06 08:41:24 -0800225 returnNode = new (commentPool.Alloc()) XMLComment( this );
226 returnNode->memPool = &commentPool;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800227 p += commentHeaderLen;
228 }
Lee Thomason50f97b22012-02-11 16:33:40 -0800229 else if ( XMLUtil::StringEqual( p, cdataHeader, cdataHeaderLen ) ) {
230 XMLText* text = new (textPool.Alloc()) XMLText( this );
231 returnNode = text;
232 returnNode->memPool = &textPool;
233 p += cdataHeaderLen;
234 text->SetCData( true );
235 }
236 else if ( XMLUtil::StringEqual( p, dtdHeader, dtdHeaderLen ) ) {
237 returnNode = new (commentPool.Alloc()) XMLUnknown( this );
238 returnNode->memPool = &commentPool;
239 p += dtdHeaderLen;
240 }
Lee Thomason56bdd022012-02-09 18:16:58 -0800241 else if ( XMLUtil::StringEqual( p, elementHeader, elementHeaderLen ) ) {
Lee Thomasond1983222012-02-06 08:41:24 -0800242 returnNode = new (elementPool.Alloc()) XMLElement( this );
243 returnNode->memPool = &elementPool;
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800244 p += elementHeaderLen;
245 }
Lee Thomason56bdd022012-02-09 18:16:58 -0800246 else if ( (*p != '<') && XMLUtil::IsAlphaNum( *p ) ) {
Lee Thomasond1983222012-02-06 08:41:24 -0800247 returnNode = new (textPool.Alloc()) XMLText( this );
248 returnNode->memPool = &textPool;
Lee Thomason5492a1c2012-01-23 15:32:10 -0800249 p = start; // Back it up, all the text counts.
250 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800251 else {
Lee Thomason50f97b22012-02-11 16:33:40 -0800252 this->SetError( ERROR_IDENTIFYING_TAG, p, 0 );
253 p = 0;
254 returnNode = 0;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800255 }
256
257 *node = returnNode;
258 return p;
259}
260
261
Lee Thomason751da522012-02-10 08:50:51 -0800262bool XMLDocument::Accept( XMLVisitor* visitor ) const
263{
264 if ( visitor->VisitEnter( *this ) )
265 {
266 for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() )
267 {
268 if ( !node->Accept( visitor ) )
269 break;
270 }
271 }
272 return visitor->VisitExit( *this );
273}
Lee Thomason56bdd022012-02-09 18:16:58 -0800274
275
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800276// --------- XMLNode ----------- //
277
278XMLNode::XMLNode( XMLDocument* doc ) :
279 document( doc ),
280 parent( 0 ),
281 firstChild( 0 ), lastChild( 0 ),
282 prev( 0 ), next( 0 )
283{
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800284}
285
286
287XMLNode::~XMLNode()
288{
Lee Thomason18d68bd2012-01-26 18:17:26 -0800289 ClearChildren();
Lee Thomason7c913cd2012-01-26 18:32:34 -0800290 if ( parent ) {
291 parent->Unlink( this );
292 }
Lee Thomason18d68bd2012-01-26 18:17:26 -0800293}
294
295
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800296void XMLNode::SetValue( const char* str, bool staticMem )
297{
298 if ( staticMem )
299 value.SetInternedStr( str );
300 else
301 value.SetStr( str );
302}
303
304
Lee Thomason18d68bd2012-01-26 18:17:26 -0800305void XMLNode::ClearChildren()
306{
Lee Thomasond923c672012-01-23 08:44:25 -0800307 while( firstChild ) {
308 XMLNode* node = firstChild;
309 Unlink( node );
Lee Thomasond1983222012-02-06 08:41:24 -0800310
Lee Thomason43f59302012-02-06 18:18:11 -0800311 DELETE_NODE( node );
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800312 }
Lee Thomason18d68bd2012-01-26 18:17:26 -0800313 firstChild = lastChild = 0;
Lee Thomasond923c672012-01-23 08:44:25 -0800314}
315
316
317void XMLNode::Unlink( XMLNode* child )
318{
319 TIXMLASSERT( child->parent == this );
320 if ( child == firstChild )
321 firstChild = firstChild->next;
322 if ( child == lastChild )
323 lastChild = lastChild->prev;
324
325 if ( child->prev ) {
326 child->prev->next = child->next;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800327 }
Lee Thomasond923c672012-01-23 08:44:25 -0800328 if ( child->next ) {
329 child->next->prev = child->prev;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800330 }
Lee Thomasond923c672012-01-23 08:44:25 -0800331 child->parent = 0;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800332}
333
334
335XMLNode* XMLNode::InsertEndChild( XMLNode* addThis )
336{
337 if ( lastChild ) {
338 TIXMLASSERT( firstChild );
339 TIXMLASSERT( lastChild->next == 0 );
340 lastChild->next = addThis;
341 addThis->prev = lastChild;
342 lastChild = addThis;
343
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800344 addThis->next = 0;
345 }
346 else {
347 TIXMLASSERT( firstChild == 0 );
348 firstChild = lastChild = addThis;
349
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800350 addThis->prev = 0;
351 addThis->next = 0;
352 }
U-Stream\Lee09a11c52012-02-17 08:31:16 -0800353 addThis->parent = this;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800354 return addThis;
355}
356
357
Lee Thomason1ff38e02012-02-14 18:18:16 -0800358XMLNode* XMLNode::InsertFirstChild( XMLNode* addThis )
359{
360 if ( firstChild ) {
361 TIXMLASSERT( lastChild );
362 TIXMLASSERT( firstChild->prev == 0 );
363
364 firstChild->prev = addThis;
365 addThis->next = firstChild;
366 firstChild = addThis;
367
Lee Thomason1ff38e02012-02-14 18:18:16 -0800368 addThis->prev = 0;
369 }
370 else {
371 TIXMLASSERT( lastChild == 0 );
372 firstChild = lastChild = addThis;
373
Lee Thomason1ff38e02012-02-14 18:18:16 -0800374 addThis->prev = 0;
375 addThis->next = 0;
376 }
U-Stream\Lee09a11c52012-02-17 08:31:16 -0800377 addThis->parent = this;
Lee Thomason1ff38e02012-02-14 18:18:16 -0800378 return addThis;
379}
380
381
382XMLNode* XMLNode::InsertAfterChild( XMLNode* afterThis, XMLNode* addThis )
383{
384 TIXMLASSERT( afterThis->parent == this );
385 if ( afterThis->parent != this )
386 return 0;
387
388 if ( afterThis->next == 0 ) {
389 // The last node or the only node.
390 return InsertEndChild( addThis );
391 }
392 addThis->prev = afterThis;
393 addThis->next = afterThis->next;
394 afterThis->next->prev = addThis;
395 afterThis->next = addThis;
U-Stream\Lee09a11c52012-02-17 08:31:16 -0800396 addThis->parent = this;
Lee Thomason1ff38e02012-02-14 18:18:16 -0800397 return addThis;
398}
399
400
401
402
Lee Thomason56bdd022012-02-09 18:16:58 -0800403const XMLElement* XMLNode::FirstChildElement( const char* value ) const
Lee Thomason2c85a712012-01-31 08:24:24 -0800404{
405 for( XMLNode* node=firstChild; node; node=node->next ) {
406 XMLElement* element = node->ToElement();
407 if ( element ) {
Lee Thomason56bdd022012-02-09 18:16:58 -0800408 if ( !value || XMLUtil::StringEqual( element->Name(), value ) ) {
Lee Thomason2c85a712012-01-31 08:24:24 -0800409 return element;
410 }
411 }
412 }
413 return 0;
414}
415
416
Lee Thomason56bdd022012-02-09 18:16:58 -0800417const XMLElement* XMLNode::LastChildElement( const char* value ) const
418{
419 for( XMLNode* node=lastChild; node; node=node->prev ) {
420 XMLElement* element = node->ToElement();
421 if ( element ) {
422 if ( !value || XMLUtil::StringEqual( element->Name(), value ) ) {
423 return element;
424 }
425 }
426 }
427 return 0;
428}
429
430
431void XMLNode::DeleteChild( XMLNode* node )
432{
433 TIXMLASSERT( node->parent == this );
434 TIXMLASSERT( 0 );
435}
436
437
Lee Thomason67d61312012-01-24 16:01:51 -0800438char* XMLNode::ParseDeep( char* p )
439{
440 while( p && *p ) {
441 XMLNode* node = 0;
Lee Thomasond1983222012-02-06 08:41:24 -0800442 p = document->Identify( p, &node );
Lee Thomason67d61312012-01-24 16:01:51 -0800443 if ( p && node ) {
444 p = node->ParseDeep( p );
445 // FIXME: is it the correct closing element?
446 if ( node->IsClosingElement() ) {
Lee Thomason43f59302012-02-06 18:18:11 -0800447 DELETE_NODE( node );
Lee Thomason67d61312012-01-24 16:01:51 -0800448 return p;
449 }
450 this->InsertEndChild( node );
451 }
452 }
453 return 0;
454}
455
Lee Thomason5492a1c2012-01-23 15:32:10 -0800456// --------- XMLText ---------- //
457char* XMLText::ParseDeep( char* p )
458{
Lee Thomason50f97b22012-02-11 16:33:40 -0800459 if ( this->CData() ) {
460 p = value.ParseText( p, "]]>", StrPair::NEEDS_NEWLINE_NORMALIZATION );
461 return p;
462 }
463 else {
464 p = value.ParseText( p, "<", StrPair::TEXT_ELEMENT );
465 // consumes the end tag.
466 if ( p && *p ) {
467 return p-1;
468 }
Lee Thomason5492a1c2012-01-23 15:32:10 -0800469 }
470 return 0;
471}
472
473
Lee Thomason56bdd022012-02-09 18:16:58 -0800474bool XMLText::Accept( XMLVisitor* visitor ) const
475{
476 return visitor->Visit( *this );
477}
478
479
Lee Thomason3f57d272012-01-11 15:30:03 -0800480// --------- XMLComment ---------- //
481
Lee Thomasone4422302012-01-20 17:59:50 -0800482XMLComment::XMLComment( XMLDocument* doc ) : XMLNode( doc )
Lee Thomason3f57d272012-01-11 15:30:03 -0800483{
484}
485
486
Lee Thomasonce0763e2012-01-11 15:43:54 -0800487XMLComment::~XMLComment()
Lee Thomason3f57d272012-01-11 15:30:03 -0800488{
Lee Thomasond923c672012-01-23 08:44:25 -0800489 //printf( "~XMLComment\n" );
Lee Thomason3f57d272012-01-11 15:30:03 -0800490}
491
492
Lee Thomasonce0763e2012-01-11 15:43:54 -0800493char* XMLComment::ParseDeep( char* p )
Lee Thomason3f57d272012-01-11 15:30:03 -0800494{
495 // Comment parses as text.
Lee Thomason56bdd022012-02-09 18:16:58 -0800496 return value.ParseText( p, "-->", StrPair::COMMENT );
U-Lama\Lee4cee6112011-12-31 14:58:18 -0800497}
498
499
Lee Thomason751da522012-02-10 08:50:51 -0800500bool XMLComment::Accept( XMLVisitor* visitor ) const
501{
502 return visitor->Visit( *this );
503}
Lee Thomason56bdd022012-02-09 18:16:58 -0800504
505
Lee Thomason50f97b22012-02-11 16:33:40 -0800506// --------- XMLDeclaration ---------- //
507
508XMLDeclaration::XMLDeclaration( XMLDocument* doc ) : XMLNode( doc )
509{
510}
511
512
513XMLDeclaration::~XMLDeclaration()
514{
515 //printf( "~XMLDeclaration\n" );
516}
517
518
519char* XMLDeclaration::ParseDeep( char* p )
520{
521 // Declaration parses as text.
522 return value.ParseText( p, ">", StrPair::NEEDS_NEWLINE_NORMALIZATION );
523}
524
525
526bool XMLDeclaration::Accept( XMLVisitor* visitor ) const
527{
528 return visitor->Visit( *this );
529}
530
531// --------- XMLUnknown ---------- //
532
533XMLUnknown::XMLUnknown( XMLDocument* doc ) : XMLNode( doc )
534{
535}
536
537
538XMLUnknown::~XMLUnknown()
539{
540}
541
542
543char* XMLUnknown::ParseDeep( char* p )
544{
545 // Unknown parses as text.
546 return value.ParseText( p, ">", StrPair::NEEDS_NEWLINE_NORMALIZATION );
547}
548
549
550bool XMLUnknown::Accept( XMLVisitor* visitor ) const
551{
552 return visitor->Visit( *this );
553}
554
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800555// --------- XMLAttribute ---------- //
556char* XMLAttribute::ParseDeep( char* p )
557{
Lee Thomason56bdd022012-02-09 18:16:58 -0800558 p = name.ParseText( p, "=", StrPair::ATTRIBUTE_NAME );
Lee Thomason22aead12012-01-23 13:29:35 -0800559 if ( !p || !*p ) return 0;
560
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800561 char endTag[2] = { *p, 0 };
562 ++p;
Lee Thomason56bdd022012-02-09 18:16:58 -0800563 p = value.ParseText( p, endTag, StrPair::ATTRIBUTE_VALUE );
Lee Thomasone4422302012-01-20 17:59:50 -0800564 if ( value.Empty() ) return 0;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800565 return p;
566}
567
568
U-Stream\Lee09a11c52012-02-17 08:31:16 -0800569void XMLAttribute::SetName( const char* n )
570{
571 name.SetStr( n );
572}
573
574
Lee Thomason1ff38e02012-02-14 18:18:16 -0800575int XMLAttribute::QueryIntAttribute( int* value ) const
576{
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800577 if ( TIXML_SSCANF( Value(), "%d", value ) == 1 )
578 return ATTRIBUTE_SUCCESS;
Lee Thomason1ff38e02012-02-14 18:18:16 -0800579 return WRONG_ATTRIBUTE_TYPE;
580}
581
582
583int XMLAttribute::QueryUnsignedAttribute( unsigned int* value ) const
584{
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800585 if ( TIXML_SSCANF( Value(), "%u", value ) == 1 )
586 return ATTRIBUTE_SUCCESS;
Lee Thomason1ff38e02012-02-14 18:18:16 -0800587 return WRONG_ATTRIBUTE_TYPE;
588}
589
590
591int XMLAttribute::QueryBoolAttribute( bool* value ) const
592{
593 int ival = -1;
594 QueryIntAttribute( &ival );
595
596 if ( ival > 0 || XMLUtil::StringEqual( Value(), "true" ) ) {
597 *value = true;
598 return ATTRIBUTE_SUCCESS;
599 }
600 else if ( ival == 0 || XMLUtil::StringEqual( Value(), "false" ) ) {
601 *value = false;
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800602 return ATTRIBUTE_SUCCESS;
603 }
Lee Thomason1ff38e02012-02-14 18:18:16 -0800604 return WRONG_ATTRIBUTE_TYPE;
605}
606
607
608int XMLAttribute::QueryDoubleAttribute( double* value ) const
609{
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800610 if ( TIXML_SSCANF( Value(), "%lf", value ) == 1 )
611 return ATTRIBUTE_SUCCESS;
Lee Thomason1ff38e02012-02-14 18:18:16 -0800612 return WRONG_ATTRIBUTE_TYPE;
613}
614
615
616int XMLAttribute::QueryFloatAttribute( float* value ) const
617{
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800618 if ( TIXML_SSCANF( Value(), "%f", value ) == 1 )
619 return ATTRIBUTE_SUCCESS;
Lee Thomason1ff38e02012-02-14 18:18:16 -0800620 return WRONG_ATTRIBUTE_TYPE;
621}
622
623
624void XMLAttribute::SetAttribute( const char* v )
625{
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800626 value.SetStr( v );
Lee Thomason1ff38e02012-02-14 18:18:16 -0800627}
628
629
Lee Thomason1ff38e02012-02-14 18:18:16 -0800630void XMLAttribute::SetAttribute( int v )
631{
632 char buf[BUF_SIZE];
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800633 TIXML_SNPRINTF( buf, BUF_SIZE-1, "%d", v );
634 value.SetStr( buf );
Lee Thomason1ff38e02012-02-14 18:18:16 -0800635}
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800636
637
638void XMLAttribute::SetAttribute( unsigned v )
639{
640 char buf[BUF_SIZE];
641 TIXML_SNPRINTF( buf, BUF_SIZE-1, "%u", v );
642 value.SetStr( buf );
643}
644
645
646void XMLAttribute::SetAttribute( bool v )
647{
648 char buf[BUF_SIZE];
649 TIXML_SNPRINTF( buf, BUF_SIZE-1, "%d", v ? 1 : 0 );
650 value.SetStr( buf );
651}
652
653void XMLAttribute::SetAttribute( double v )
654{
655 char buf[BUF_SIZE];
656 TIXML_SNPRINTF( buf, BUF_SIZE-1, "%f", v );
657 value.SetStr( buf );
658}
659
660void XMLAttribute::SetAttribute( float v )
661{
662 char buf[BUF_SIZE];
663 TIXML_SNPRINTF( buf, BUF_SIZE-1, "%f", v );
664 value.SetStr( buf );
665}
666
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800667
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800668// --------- XMLElement ---------- //
669XMLElement::XMLElement( XMLDocument* doc ) : XMLNode( doc ),
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800670 closing( false ),
U-Stream\Lee09a11c52012-02-17 08:31:16 -0800671 rootAttribute( 0 )
672 //lastAttribute( 0 )
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800673{
674}
675
676
677XMLElement::~XMLElement()
678{
U-Stream\Lee09a11c52012-02-17 08:31:16 -0800679 while( rootAttribute ) {
680 XMLAttribute* next = rootAttribute->next;
681 DELETE_ATTRIBUTE( rootAttribute );
682 rootAttribute = next;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800683 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800684}
685
686
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800687XMLAttribute* XMLElement::FindAttribute( const char* name )
688{
689 XMLAttribute* a = 0;
690 for( a=rootAttribute; a; a = a->next ) {
691 if ( XMLUtil::StringEqual( a->Name(), name ) )
692 return a;
693 }
694 return 0;
695}
696
697
698const XMLAttribute* XMLElement::FindAttribute( const char* name ) const
699{
700 XMLAttribute* a = 0;
701 for( a=rootAttribute; a; a = a->next ) {
702 if ( XMLUtil::StringEqual( a->Name(), name ) )
703 return a;
704 }
705 return 0;
706}
707
708
709XMLAttribute* XMLElement::FindOrCreateAttribute( const char* name )
710{
711 XMLAttribute* attrib = FindAttribute( name );
712 if ( !attrib ) {
U-Stream\Lee09a11c52012-02-17 08:31:16 -0800713 attrib = new (document->attributePool.Alloc() ) XMLAttribute( this );
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800714 attrib->memPool = &document->attributePool;
U-Stream\Lee09a11c52012-02-17 08:31:16 -0800715 LinkAttribute( attrib );
716 attrib->SetName( name );
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800717 }
718 return attrib;
719}
720
721
U-Stream\Lee09a11c52012-02-17 08:31:16 -0800722void XMLElement::LinkAttribute( XMLAttribute* attrib )
723{
724 if ( rootAttribute ) {
725 XMLAttribute* end = rootAttribute;
726 while ( end->next )
727 end = end->next;
728 end->next = attrib;
729 }
730 else {
731 rootAttribute = attrib;
732 }
733}
734
735
Lee Thomason67d61312012-01-24 16:01:51 -0800736char* XMLElement::ParseAttributes( char* p, bool* closedElement )
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800737{
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800738 const char* start = p;
Lee Thomason67d61312012-01-24 16:01:51 -0800739 *closedElement = false;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800740
741 // Read the attributes.
742 while( p ) {
Lee Thomason56bdd022012-02-09 18:16:58 -0800743 p = XMLUtil::SkipWhiteSpace( p );
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800744 if ( !p || !(*p) ) {
Lee Thomasond1983222012-02-06 08:41:24 -0800745 document->SetError( XMLDocument::ERROR_PARSING_ELEMENT, start, Name() );
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800746 return 0;
747 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800748
749 // attribute.
Lee Thomason56bdd022012-02-09 18:16:58 -0800750 if ( XMLUtil::IsAlpha( *p ) ) {
Lee Thomason43f59302012-02-06 18:18:11 -0800751 XMLAttribute* attrib = new (document->attributePool.Alloc() ) XMLAttribute( this );
752 attrib->memPool = &document->attributePool;
Lee Thomasond1983222012-02-06 08:41:24 -0800753
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800754 p = attrib->ParseDeep( p );
755 if ( !p ) {
Lee Thomason43f59302012-02-06 18:18:11 -0800756 DELETE_ATTRIBUTE( attrib );
Lee Thomasone4422302012-01-20 17:59:50 -0800757 document->SetError( XMLDocument::ERROR_PARSING_ATTRIBUTE, start, p );
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800758 return 0;
759 }
U-Stream\Lee09a11c52012-02-17 08:31:16 -0800760 LinkAttribute( attrib );
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800761 }
Lee Thomasone4422302012-01-20 17:59:50 -0800762 // end of the tag
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800763 else if ( *p == '/' && *(p+1) == '>' ) {
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800764 if ( closing ) {
765 document->SetError( XMLDocument::ERROR_PARSING_ELEMENT, start, p );
766 return 0;
767 }
Lee Thomason67d61312012-01-24 16:01:51 -0800768 *closedElement = true;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800769 return p+2; // done; sealed element.
770 }
Lee Thomasone4422302012-01-20 17:59:50 -0800771 // end of the tag
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800772 else if ( *p == '>' ) {
773 ++p;
774 break;
775 }
Lee Thomasone4422302012-01-20 17:59:50 -0800776 else {
777 document->SetError( XMLDocument::ERROR_PARSING_ELEMENT, start, p );
778 return 0;
779 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800780 }
Lee Thomason67d61312012-01-24 16:01:51 -0800781 return p;
782}
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800783
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800784
Lee Thomason67d61312012-01-24 16:01:51 -0800785//
786// <ele></ele>
787// <ele>foo<b>bar</b></ele>
788//
789char* XMLElement::ParseDeep( char* p )
790{
791 // Read the element name.
Lee Thomason56bdd022012-02-09 18:16:58 -0800792 p = XMLUtil::SkipWhiteSpace( p );
Lee Thomason67d61312012-01-24 16:01:51 -0800793 if ( !p ) return 0;
794 const char* start = p;
795
796 // The closing element is the </element> form. It is
797 // parsed just like a regular element then deleted from
798 // the DOM.
799 if ( *p == '/' ) {
800 closing = true;
801 ++p;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800802 }
Lee Thomason67d61312012-01-24 16:01:51 -0800803
Lee Thomason56bdd022012-02-09 18:16:58 -0800804 p = value.ParseName( p );
Lee Thomasond1983222012-02-06 08:41:24 -0800805 if ( value.Empty() ) return 0;
Lee Thomason67d61312012-01-24 16:01:51 -0800806
807 bool elementClosed=false;
808 p = ParseAttributes( p, &elementClosed );
809 if ( !p || !*p || elementClosed || closing )
810 return p;
811
812 p = XMLNode::ParseDeep( p );
813 return p;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800814}
815
816
Lee Thomason751da522012-02-10 08:50:51 -0800817bool XMLElement::Accept( XMLVisitor* visitor ) const
818{
819 if ( visitor->VisitEnter( *this, rootAttribute ) )
820 {
821 for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() )
822 {
823 if ( !node->Accept( visitor ) )
824 break;
825 }
826 }
827 return visitor->VisitExit( *this );
828
829}
Lee Thomason56bdd022012-02-09 18:16:58 -0800830
831
Lee Thomason3f57d272012-01-11 15:30:03 -0800832// --------- XMLDocument ----------- //
Lee Thomason67d61312012-01-24 16:01:51 -0800833XMLDocument::XMLDocument() :
Lee Thomason18d68bd2012-01-26 18:17:26 -0800834 XMLNode( 0 ),
U-Lama\Lee560bd472011-12-28 19:42:49 -0800835 charBuffer( 0 )
836{
Lee Thomason18d68bd2012-01-26 18:17:26 -0800837 document = this; // avoid warning about 'this' in initializer list
U-Lama\Lee560bd472011-12-28 19:42:49 -0800838}
U-Lama\Leee13c3e62011-12-28 14:36:55 -0800839
840
Lee Thomason3f57d272012-01-11 15:30:03 -0800841XMLDocument::~XMLDocument()
842{
Lee Thomasond1983222012-02-06 08:41:24 -0800843 ClearChildren();
Lee Thomason18d68bd2012-01-26 18:17:26 -0800844 delete [] charBuffer;
Lee Thomasond1983222012-02-06 08:41:24 -0800845
Lee Thomasonec5a7b42012-02-13 18:16:52 -0800846#if 0
Lee Thomason455c9d42012-02-06 09:14:14 -0800847 textPool.Trace( "text" );
848 elementPool.Trace( "element" );
849 commentPool.Trace( "comment" );
850 attributePool.Trace( "attribute" );
Lee Thomasone9ecdab2012-02-13 18:11:20 -0800851#endif
852
Lee Thomason455c9d42012-02-06 09:14:14 -0800853 TIXMLASSERT( textPool.CurrentAllocs() == 0 );
854 TIXMLASSERT( elementPool.CurrentAllocs() == 0 );
855 TIXMLASSERT( commentPool.CurrentAllocs() == 0 );
856 TIXMLASSERT( attributePool.CurrentAllocs() == 0 );
Lee Thomason3f57d272012-01-11 15:30:03 -0800857}
858
859
Lee Thomason18d68bd2012-01-26 18:17:26 -0800860void XMLDocument::InitDocument()
861{
862 errorID = NO_ERROR;
863 errorStr1 = 0;
864 errorStr2 = 0;
865
866 delete [] charBuffer;
867 charBuffer = 0;
868
869}
870
Lee Thomason3f57d272012-01-11 15:30:03 -0800871
Lee Thomason2c85a712012-01-31 08:24:24 -0800872XMLElement* XMLDocument::NewElement( const char* name )
873{
Lee Thomasond1983222012-02-06 08:41:24 -0800874 XMLElement* ele = new (elementPool.Alloc()) XMLElement( this );
875 ele->memPool = &elementPool;
Lee Thomason2c85a712012-01-31 08:24:24 -0800876 ele->SetName( name );
877 return ele;
878}
879
880
Lee Thomason1ff38e02012-02-14 18:18:16 -0800881XMLComment* XMLDocument::NewComment( const char* str )
882{
883 XMLComment* comment = new (commentPool.Alloc()) XMLComment( this );
884 comment->memPool = &commentPool;
885 comment->SetValue( str );
886 return comment;
887}
888
889
890XMLText* XMLDocument::NewText( const char* str )
891{
U-Stream\Lee09a11c52012-02-17 08:31:16 -0800892 XMLText* text = new (textPool.Alloc()) XMLText( this );
893 text->memPool = &textPool;
894 text->SetValue( str );
895 return text;
Lee Thomason1ff38e02012-02-14 18:18:16 -0800896}
897
898
899
Lee Thomason7c913cd2012-01-26 18:32:34 -0800900int XMLDocument::Parse( const char* p )
Lee Thomason3f57d272012-01-11 15:30:03 -0800901{
Lee Thomason18d68bd2012-01-26 18:17:26 -0800902 ClearChildren();
903 InitDocument();
904
905 if ( !p || !*p ) {
906 return true; // correctly parse an empty string?
907 }
908 size_t len = strlen( p );
909 charBuffer = new char[ len+1 ];
910 memcpy( charBuffer, p, len+1 );
Lee Thomason3f57d272012-01-11 15:30:03 -0800911 XMLNode* node = 0;
Lee Thomason85403d82012-01-11 15:55:05 -0800912
Lee Thomason18d68bd2012-01-26 18:17:26 -0800913 char* q = ParseDeep( charBuffer );
Lee Thomason7c913cd2012-01-26 18:32:34 -0800914 return errorID;
Lee Thomason3f57d272012-01-11 15:30:03 -0800915}
916
917
Lee Thomason5cae8972012-01-24 18:03:07 -0800918void XMLDocument::Print( XMLStreamer* streamer )
Lee Thomason3f57d272012-01-11 15:30:03 -0800919{
Lee Thomason5cae8972012-01-24 18:03:07 -0800920 XMLStreamer stdStreamer( stdout );
921 if ( !streamer )
922 streamer = &stdStreamer;
Lee Thomason751da522012-02-10 08:50:51 -0800923 //for( XMLNode* node = firstChild; node; node=node->next ) {
924 // node->Print( streamer );
925 //}
926 Accept( streamer );
Lee Thomason3f57d272012-01-11 15:30:03 -0800927}
928
929
Lee Thomason67d61312012-01-24 16:01:51 -0800930void XMLDocument::SetError( int error, const char* str1, const char* str2 )
931{
Lee Thomason18d68bd2012-01-26 18:17:26 -0800932 errorID = error;
933 printf( "ERROR: id=%d '%s' '%s'\n", error, str1, str2 ); // fixme: remove
934 errorStr1 = str1;
935 errorStr2 = str2;
Lee Thomason67d61312012-01-24 16:01:51 -0800936}
937
Lee Thomason5cae8972012-01-24 18:03:07 -0800938
Lee Thomason56bdd022012-02-09 18:16:58 -0800939XMLStreamer::XMLStreamer( FILE* file ) : fp( file ), depth( 0 ), elementJustOpened( false ), textDepth( -1 )
Lee Thomason5cae8972012-01-24 18:03:07 -0800940{
Lee Thomason857b8682012-01-25 17:50:25 -0800941 for( int i=0; i<ENTITY_RANGE; ++i ) {
942 entityFlag[i] = false;
943 }
944 for( int i=0; i<NUM_ENTITIES; ++i ) {
945 TIXMLASSERT( entities[i].value < ENTITY_RANGE );
946 if ( entities[i].value < ENTITY_RANGE ) {
947 entityFlag[ entities[i].value ] = true;
948 }
949 }
Lee Thomason5cae8972012-01-24 18:03:07 -0800950}
951
952
953void XMLStreamer::PrintSpace( int depth )
954{
955 for( int i=0; i<depth; ++i ) {
956 fprintf( fp, " " );
957 }
958}
959
960
Lee Thomason951d8832012-01-26 08:47:06 -0800961void XMLStreamer::PrintString( const char* p )
Lee Thomason857b8682012-01-25 17:50:25 -0800962{
Lee Thomason951d8832012-01-26 08:47:06 -0800963 // Look for runs of bytes between entities to print.
964 const char* q = p;
Lee Thomason857b8682012-01-25 17:50:25 -0800965
Lee Thomason951d8832012-01-26 08:47:06 -0800966 while ( *q ) {
967 if ( *q < ENTITY_RANGE ) {
968 // Check for entities. If one is found, flush
969 // the stream up until the entity, write the
970 // entity, and keep looking.
971 if ( entityFlag[*q] ) {
972 while ( p < q ) {
973 fputc( *p, fp );
974 ++p;
975 }
976 for( int i=0; i<NUM_ENTITIES; ++i ) {
977 if ( entities[i].value == *q ) {
978 fprintf( fp, "&%s;", entities[i].pattern );
979 break;
980 }
981 }
982 ++p;
983 }
984 }
985 ++q;
986 }
987 // Flush the remaining string. This will be the entire
988 // string if an entity wasn't found.
989 if ( q-p > 0 ) {
990 fprintf( fp, "%s", p );
991 }
Lee Thomason857b8682012-01-25 17:50:25 -0800992}
993
Lee Thomason56bdd022012-02-09 18:16:58 -0800994void XMLStreamer::OpenElement( const char* name )
Lee Thomason5cae8972012-01-24 18:03:07 -0800995{
996 if ( elementJustOpened ) {
997 SealElement();
998 }
Lee Thomason56bdd022012-02-09 18:16:58 -0800999 stack.Push( name );
1000
1001 if ( textDepth < 0 && depth > 0) {
1002 fprintf( fp, "\n" );
Lee Thomason24767b02012-01-25 17:16:23 -08001003 PrintSpace( depth );
1004 }
Lee Thomason5cae8972012-01-24 18:03:07 -08001005
Lee Thomason5cae8972012-01-24 18:03:07 -08001006 fprintf( fp, "<%s", name );
1007 elementJustOpened = true;
1008 ++depth;
1009}
1010
1011
1012void XMLStreamer::PushAttribute( const char* name, const char* value )
1013{
1014 TIXMLASSERT( elementJustOpened );
Lee Thomason18d68bd2012-01-26 18:17:26 -08001015 fprintf( fp, " %s=\"", name );
1016 PrintString( value );
1017 fprintf( fp, "\"" );
Lee Thomason5cae8972012-01-24 18:03:07 -08001018}
1019
1020
1021void XMLStreamer::CloseElement()
1022{
1023 --depth;
1024 const char* name = stack.Pop();
Lee Thomason5cae8972012-01-24 18:03:07 -08001025
1026 if ( elementJustOpened ) {
1027 fprintf( fp, "/>" );
Lee Thomason5cae8972012-01-24 18:03:07 -08001028 }
1029 else {
Lee Thomason56bdd022012-02-09 18:16:58 -08001030 if ( textDepth < 0 ) {
1031 fprintf( fp, "\n" );
Lee Thomason24767b02012-01-25 17:16:23 -08001032 PrintSpace( depth );
1033 }
Lee Thomason5cae8972012-01-24 18:03:07 -08001034 fprintf( fp, "</%s>", name );
Lee Thomason5cae8972012-01-24 18:03:07 -08001035 }
Lee Thomason56bdd022012-02-09 18:16:58 -08001036
1037 if ( textDepth == depth )
1038 textDepth = -1;
1039 if ( depth == 0 )
1040 fprintf( fp, "\n" );
Lee Thomason5cae8972012-01-24 18:03:07 -08001041 elementJustOpened = false;
1042}
1043
1044
1045void XMLStreamer::SealElement()
1046{
1047 elementJustOpened = false;
1048 fprintf( fp, ">" );
Lee Thomason5cae8972012-01-24 18:03:07 -08001049}
1050
1051
Lee Thomason50f97b22012-02-11 16:33:40 -08001052void XMLStreamer::PushText( const char* text, bool cdata )
Lee Thomason5cae8972012-01-24 18:03:07 -08001053{
Lee Thomason56bdd022012-02-09 18:16:58 -08001054 textDepth = depth-1;
1055
Lee Thomason5cae8972012-01-24 18:03:07 -08001056 if ( elementJustOpened ) {
1057 SealElement();
1058 }
Lee Thomason50f97b22012-02-11 16:33:40 -08001059 if ( cdata )
1060 fprintf( fp, "<![CDATA[" );
Lee Thomason951d8832012-01-26 08:47:06 -08001061 PrintString( text );
Lee Thomason50f97b22012-02-11 16:33:40 -08001062 if ( cdata )
1063 fprintf( fp, "]]>" );
Lee Thomason5cae8972012-01-24 18:03:07 -08001064}
1065
1066
1067void XMLStreamer::PushComment( const char* comment )
1068{
1069 if ( elementJustOpened ) {
1070 SealElement();
1071 }
U-Stream\Lee09a11c52012-02-17 08:31:16 -08001072 if ( textDepth < 0 && depth > 0) {
1073 fprintf( fp, "\n" );
1074 PrintSpace( depth );
1075 }
1076 fprintf( fp, "<!--%s-->", comment );
Lee Thomason5cae8972012-01-24 18:03:07 -08001077}
Lee Thomason751da522012-02-10 08:50:51 -08001078
1079
1080bool XMLStreamer::VisitEnter( const XMLElement& element, const XMLAttribute* attribute )
1081{
1082 OpenElement( element.Name() );
1083 while ( attribute ) {
1084 PushAttribute( attribute->Name(), attribute->Value() );
1085 attribute = attribute->Next();
1086 }
1087 return true;
1088}
1089
1090
1091bool XMLStreamer::VisitExit( const XMLElement& element )
1092{
1093 CloseElement();
1094 return true;
1095}
1096
1097
1098bool XMLStreamer::Visit( const XMLText& text )
1099{
1100 PushText( text.Value() );
1101 return true;
1102}
1103
1104
1105bool XMLStreamer::Visit( const XMLComment& comment )
1106{
1107 PushComment( comment.Value() );
1108 return true;
1109}