blob: f45ae74429410c97810efbde6e6a8dc869157590 [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 Thomasone4422302012-01-20 17:59:50 -080040const char* StrPair::GetStr()
41{
42 if ( flags & NEEDS_FLUSH ) {
43 *end = 0;
Lee Thomason8ee79892012-01-25 17:44:30 -080044 flags ^= NEEDS_FLUSH;
Lee Thomasone4422302012-01-20 17:59:50 -080045
Lee Thomason8ee79892012-01-25 17:44:30 -080046 if ( flags ) {
Lee Thomasone4422302012-01-20 17:59:50 -080047 char* p = start;
48 char* q = start;
49
50 while( p < end ) {
Lee Thomason8ee79892012-01-25 17:44:30 -080051 if ( (flags & NEEDS_NEWLINE_NORMALIZATION) && *p == CR ) {
Lee Thomasone4422302012-01-20 17:59:50 -080052 // CR-LF pair becomes LF
53 // CR alone becomes LF
54 // LF-CR becomes LF
55 if ( *(p+1) == LF ) {
56 p += 2;
57 }
58 else {
59 ++p;
60 }
Lee Thomasone9ecdab2012-02-13 18:11:20 -080061 *q++ = LF;
Lee Thomasone4422302012-01-20 17:59:50 -080062 }
Lee Thomason8ee79892012-01-25 17:44:30 -080063 else if ( (flags & NEEDS_NEWLINE_NORMALIZATION) && *p == LF ) {
Lee Thomasone4422302012-01-20 17:59:50 -080064 if ( *(p+1) == CR ) {
65 p += 2;
66 }
67 else {
68 ++p;
69 }
Lee Thomasone9ecdab2012-02-13 18:11:20 -080070 *q++ = LF;
Lee Thomasone4422302012-01-20 17:59:50 -080071 }
Lee Thomason8ee79892012-01-25 17:44:30 -080072 else if ( (flags & NEEDS_ENTITY_PROCESSING) && *p == '&' ) {
73 int i=0;
74 for( i=0; i<NUM_ENTITIES; ++i ) {
75 if ( strncmp( p+1, entities[i].pattern, entities[i].length ) == 0
76 && *(p+entities[i].length+1) == ';' )
77 {
78 // Found an entity convert;
79 *q = entities[i].value;
80 ++q;
81 p += entities[i].length + 2;
82 break;
83 }
84 }
85 if ( i == NUM_ENTITIES ) {
86 // fixme: treat as error?
87 ++p;
88 ++q;
89 }
90 }
Lee Thomasone4422302012-01-20 17:59:50 -080091 else {
92 *q = *p;
93 ++p;
Lee Thomasonec975ce2012-01-23 11:42:06 -080094 ++q;
Lee Thomasone4422302012-01-20 17:59:50 -080095 }
96 }
Lee Thomason8ee79892012-01-25 17:44:30 -080097 *q = 0;
Lee Thomasone4422302012-01-20 17:59:50 -080098 }
99 flags = 0;
100 }
101 return start;
102}
103
Lee Thomason2c85a712012-01-31 08:24:24 -0800104/*
105const char* StringPool::Intern( const char* str )
106{
107 // Treat the array as a linear, inplace hash table.
108 // Nothing can get deleted, so that's handy.
109 if ( size > pool.Size()*3/4 ) {
110 DynArray< const char*, 20 > store;
111 for( int i=0; i<pool.Size(); ++i ) {
112 if ( pool[i] != 0 ) {
113 store.Push( pool[i] );
114 }
115 }
116 int newSize = pool.Size() * 2;
117 pool.PopArr( pool.Size() );
118
119 const char** mem = pool.PushArr( newSize );
120 memset( (void*)mem, 0, sizeof(char)*newSize );
121
122 while ( !store.Empty() ) {
123 Intern( store.Pop() );
124 }
125 }
126
127}
128*/
129
Lee Thomasone4422302012-01-20 17:59:50 -0800130
Lee Thomason56bdd022012-02-09 18:16:58 -0800131// --------- XMLUtil ----------- //
Lee Thomasond1983222012-02-06 08:41:24 -0800132
Lee Thomason56bdd022012-02-09 18:16:58 -0800133char* StrPair::ParseText( char* p, const char* endTag, int strFlags )
Lee Thomason3f57d272012-01-11 15:30:03 -0800134{
135 TIXMLASSERT( endTag && *endTag );
136
Lee Thomasonfde6a752012-01-14 18:08:12 -0800137 char* start = p;
Lee Thomasonfde6a752012-01-14 18:08:12 -0800138 char endChar = *endTag;
139 int length = strlen( endTag );
Lee Thomason3f57d272012-01-11 15:30:03 -0800140
Lee Thomasonfde6a752012-01-14 18:08:12 -0800141 // Inner loop of text parsing.
Lee Thomason3f57d272012-01-11 15:30:03 -0800142 while ( *p ) {
Lee Thomasonfde6a752012-01-14 18:08:12 -0800143 if ( *p == endChar && strncmp( p, endTag, length ) == 0 ) {
Lee Thomason56bdd022012-02-09 18:16:58 -0800144 Set( start, p, strFlags );
Lee Thomasonec975ce2012-01-23 11:42:06 -0800145 return p + length;
Lee Thomason3f57d272012-01-11 15:30:03 -0800146 }
Lee Thomasonec975ce2012-01-23 11:42:06 -0800147 ++p;
Lee Thomason3f57d272012-01-11 15:30:03 -0800148 }
Lee Thomasone4422302012-01-20 17:59:50 -0800149 return p;
Lee Thomason3f57d272012-01-11 15:30:03 -0800150}
151
152
Lee Thomason56bdd022012-02-09 18:16:58 -0800153char* StrPair::ParseName( char* p )
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800154{
155 char* start = p;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800156
157 start = p;
158 if ( !start || !(*start) ) {
159 return 0;
160 }
161
Lee Thomason56bdd022012-02-09 18:16:58 -0800162 if ( !XMLUtil::IsAlpha( *p ) ) {
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800163 return 0;
164 }
165
166 while( *p && (
Lee Thomason56bdd022012-02-09 18:16:58 -0800167 XMLUtil::IsAlphaNum( (unsigned char) *p )
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800168 || *p == '_'
169 || *p == '-'
170 || *p == '.'
171 || *p == ':' ))
172 {
173 ++p;
174 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800175
176 if ( p > start ) {
Lee Thomason56bdd022012-02-09 18:16:58 -0800177 Set( start, p, 0 );
Lee Thomasone4422302012-01-20 17:59:50 -0800178 return p;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800179 }
Lee Thomason39ede242012-01-20 11:27:56 -0800180 return 0;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800181}
182
183
Lee Thomasond1983222012-02-06 08:41:24 -0800184char* XMLDocument::Identify( char* p, XMLNode** node )
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800185{
186 XMLNode* returnNode = 0;
Lee Thomason5492a1c2012-01-23 15:32:10 -0800187 char* start = p;
Lee Thomason56bdd022012-02-09 18:16:58 -0800188 p = XMLUtil::SkipWhiteSpace( p );
Lee Thomason5492a1c2012-01-23 15:32:10 -0800189 if( !p || !*p )
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800190 {
191 return 0;
192 }
193
194 // What is this thing?
195 // - Elements start with a letter or underscore, but xml is reserved.
196 // - Comments: <!--
197 // - Decleration: <?xml
198 // - Everthing else is unknown to tinyxml.
199 //
200
201 static const char* xmlHeader = { "<?xml" };
202 static const char* commentHeader = { "<!--" };
203 static const char* dtdHeader = { "<!" };
204 static const char* cdataHeader = { "<![CDATA[" };
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800205 static const char* elementHeader = { "<" }; // and a header for everything else; check last.
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800206
207 static const int xmlHeaderLen = 5;
208 static const int commentHeaderLen = 4;
209 static const int dtdHeaderLen = 2;
210 static const int cdataHeaderLen = 9;
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800211 static const int elementHeaderLen = 1;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800212
Lee Thomason50f97b22012-02-11 16:33:40 -0800213 TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLUnknown ) ); // use same memory pool
214 TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLDeclaration ) ); // use same memory pool
215
216 if ( XMLUtil::StringEqual( p, xmlHeader, xmlHeaderLen ) ) {
217 returnNode = new (commentPool.Alloc()) XMLDeclaration( this );
218 returnNode->memPool = &commentPool;
219 p += xmlHeaderLen;
220 }
221 else if ( XMLUtil::StringEqual( p, commentHeader, commentHeaderLen ) ) {
Lee Thomasond1983222012-02-06 08:41:24 -0800222 returnNode = new (commentPool.Alloc()) XMLComment( this );
223 returnNode->memPool = &commentPool;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800224 p += commentHeaderLen;
225 }
Lee Thomason50f97b22012-02-11 16:33:40 -0800226 else if ( XMLUtil::StringEqual( p, cdataHeader, cdataHeaderLen ) ) {
227 XMLText* text = new (textPool.Alloc()) XMLText( this );
228 returnNode = text;
229 returnNode->memPool = &textPool;
230 p += cdataHeaderLen;
231 text->SetCData( true );
232 }
233 else if ( XMLUtil::StringEqual( p, dtdHeader, dtdHeaderLen ) ) {
234 returnNode = new (commentPool.Alloc()) XMLUnknown( this );
235 returnNode->memPool = &commentPool;
236 p += dtdHeaderLen;
237 }
Lee Thomason56bdd022012-02-09 18:16:58 -0800238 else if ( XMLUtil::StringEqual( p, elementHeader, elementHeaderLen ) ) {
Lee Thomasond1983222012-02-06 08:41:24 -0800239 returnNode = new (elementPool.Alloc()) XMLElement( this );
240 returnNode->memPool = &elementPool;
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800241 p += elementHeaderLen;
242 }
Lee Thomason56bdd022012-02-09 18:16:58 -0800243 else if ( (*p != '<') && XMLUtil::IsAlphaNum( *p ) ) {
Lee Thomasond1983222012-02-06 08:41:24 -0800244 returnNode = new (textPool.Alloc()) XMLText( this );
245 returnNode->memPool = &textPool;
Lee Thomason5492a1c2012-01-23 15:32:10 -0800246 p = start; // Back it up, all the text counts.
247 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800248 else {
Lee Thomason50f97b22012-02-11 16:33:40 -0800249 this->SetError( ERROR_IDENTIFYING_TAG, p, 0 );
250 p = 0;
251 returnNode = 0;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800252 }
253
254 *node = returnNode;
255 return p;
256}
257
258
Lee Thomason751da522012-02-10 08:50:51 -0800259bool XMLDocument::Accept( XMLVisitor* visitor ) const
260{
261 if ( visitor->VisitEnter( *this ) )
262 {
263 for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() )
264 {
265 if ( !node->Accept( visitor ) )
266 break;
267 }
268 }
269 return visitor->VisitExit( *this );
270}
Lee Thomason56bdd022012-02-09 18:16:58 -0800271
272
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800273// --------- XMLNode ----------- //
274
275XMLNode::XMLNode( XMLDocument* doc ) :
276 document( doc ),
277 parent( 0 ),
278 firstChild( 0 ), lastChild( 0 ),
279 prev( 0 ), next( 0 )
280{
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800281}
282
283
284XMLNode::~XMLNode()
285{
Lee Thomason18d68bd2012-01-26 18:17:26 -0800286 ClearChildren();
Lee Thomason7c913cd2012-01-26 18:32:34 -0800287 if ( parent ) {
288 parent->Unlink( this );
289 }
Lee Thomason18d68bd2012-01-26 18:17:26 -0800290}
291
292
293void XMLNode::ClearChildren()
294{
Lee Thomasond923c672012-01-23 08:44:25 -0800295 while( firstChild ) {
296 XMLNode* node = firstChild;
297 Unlink( node );
Lee Thomasond1983222012-02-06 08:41:24 -0800298
Lee Thomason43f59302012-02-06 18:18:11 -0800299 DELETE_NODE( node );
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800300 }
Lee Thomason18d68bd2012-01-26 18:17:26 -0800301 firstChild = lastChild = 0;
Lee Thomasond923c672012-01-23 08:44:25 -0800302}
303
304
305void XMLNode::Unlink( XMLNode* child )
306{
307 TIXMLASSERT( child->parent == this );
308 if ( child == firstChild )
309 firstChild = firstChild->next;
310 if ( child == lastChild )
311 lastChild = lastChild->prev;
312
313 if ( child->prev ) {
314 child->prev->next = child->next;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800315 }
Lee Thomasond923c672012-01-23 08:44:25 -0800316 if ( child->next ) {
317 child->next->prev = child->prev;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800318 }
Lee Thomasond923c672012-01-23 08:44:25 -0800319 child->parent = 0;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800320}
321
322
323XMLNode* XMLNode::InsertEndChild( XMLNode* addThis )
324{
325 if ( lastChild ) {
326 TIXMLASSERT( firstChild );
327 TIXMLASSERT( lastChild->next == 0 );
328 lastChild->next = addThis;
329 addThis->prev = lastChild;
330 lastChild = addThis;
331
332 addThis->parent = this;
333 addThis->next = 0;
334 }
335 else {
336 TIXMLASSERT( firstChild == 0 );
337 firstChild = lastChild = addThis;
338
339 addThis->parent = this;
340 addThis->prev = 0;
341 addThis->next = 0;
342 }
343 return addThis;
344}
345
346
Lee Thomason56bdd022012-02-09 18:16:58 -0800347const XMLElement* XMLNode::FirstChildElement( const char* value ) const
Lee Thomason2c85a712012-01-31 08:24:24 -0800348{
349 for( XMLNode* node=firstChild; node; node=node->next ) {
350 XMLElement* element = node->ToElement();
351 if ( element ) {
Lee Thomason56bdd022012-02-09 18:16:58 -0800352 if ( !value || XMLUtil::StringEqual( element->Name(), value ) ) {
Lee Thomason2c85a712012-01-31 08:24:24 -0800353 return element;
354 }
355 }
356 }
357 return 0;
358}
359
360
Lee Thomason56bdd022012-02-09 18:16:58 -0800361const XMLElement* XMLNode::LastChildElement( const char* value ) const
362{
363 for( XMLNode* node=lastChild; node; node=node->prev ) {
364 XMLElement* element = node->ToElement();
365 if ( element ) {
366 if ( !value || XMLUtil::StringEqual( element->Name(), value ) ) {
367 return element;
368 }
369 }
370 }
371 return 0;
372}
373
374
375void XMLNode::DeleteChild( XMLNode* node )
376{
377 TIXMLASSERT( node->parent == this );
378 TIXMLASSERT( 0 );
379}
380
381
Lee Thomason67d61312012-01-24 16:01:51 -0800382char* XMLNode::ParseDeep( char* p )
383{
384 while( p && *p ) {
385 XMLNode* node = 0;
Lee Thomasond1983222012-02-06 08:41:24 -0800386 p = document->Identify( p, &node );
Lee Thomason67d61312012-01-24 16:01:51 -0800387 if ( p && node ) {
388 p = node->ParseDeep( p );
389 // FIXME: is it the correct closing element?
390 if ( node->IsClosingElement() ) {
Lee Thomason43f59302012-02-06 18:18:11 -0800391 DELETE_NODE( node );
Lee Thomason67d61312012-01-24 16:01:51 -0800392 return p;
393 }
394 this->InsertEndChild( node );
395 }
396 }
397 return 0;
398}
399
Lee Thomason5492a1c2012-01-23 15:32:10 -0800400// --------- XMLText ---------- //
401char* XMLText::ParseDeep( char* p )
402{
Lee Thomason50f97b22012-02-11 16:33:40 -0800403 if ( this->CData() ) {
404 p = value.ParseText( p, "]]>", StrPair::NEEDS_NEWLINE_NORMALIZATION );
405 return p;
406 }
407 else {
408 p = value.ParseText( p, "<", StrPair::TEXT_ELEMENT );
409 // consumes the end tag.
410 if ( p && *p ) {
411 return p-1;
412 }
Lee Thomason5492a1c2012-01-23 15:32:10 -0800413 }
414 return 0;
415}
416
417
Lee Thomason56bdd022012-02-09 18:16:58 -0800418bool XMLText::Accept( XMLVisitor* visitor ) const
419{
420 return visitor->Visit( *this );
421}
422
423
Lee Thomason3f57d272012-01-11 15:30:03 -0800424// --------- XMLComment ---------- //
425
Lee Thomasone4422302012-01-20 17:59:50 -0800426XMLComment::XMLComment( XMLDocument* doc ) : XMLNode( doc )
Lee Thomason3f57d272012-01-11 15:30:03 -0800427{
428}
429
430
Lee Thomasonce0763e2012-01-11 15:43:54 -0800431XMLComment::~XMLComment()
Lee Thomason3f57d272012-01-11 15:30:03 -0800432{
Lee Thomasond923c672012-01-23 08:44:25 -0800433 //printf( "~XMLComment\n" );
Lee Thomason3f57d272012-01-11 15:30:03 -0800434}
435
436
Lee Thomasonce0763e2012-01-11 15:43:54 -0800437char* XMLComment::ParseDeep( char* p )
Lee Thomason3f57d272012-01-11 15:30:03 -0800438{
439 // Comment parses as text.
Lee Thomason56bdd022012-02-09 18:16:58 -0800440 return value.ParseText( p, "-->", StrPair::COMMENT );
U-Lama\Lee4cee6112011-12-31 14:58:18 -0800441}
442
443
Lee Thomason751da522012-02-10 08:50:51 -0800444bool XMLComment::Accept( XMLVisitor* visitor ) const
445{
446 return visitor->Visit( *this );
447}
Lee Thomason56bdd022012-02-09 18:16:58 -0800448
449
Lee Thomason50f97b22012-02-11 16:33:40 -0800450// --------- XMLDeclaration ---------- //
451
452XMLDeclaration::XMLDeclaration( XMLDocument* doc ) : XMLNode( doc )
453{
454}
455
456
457XMLDeclaration::~XMLDeclaration()
458{
459 //printf( "~XMLDeclaration\n" );
460}
461
462
463char* XMLDeclaration::ParseDeep( char* p )
464{
465 // Declaration parses as text.
466 return value.ParseText( p, ">", StrPair::NEEDS_NEWLINE_NORMALIZATION );
467}
468
469
470bool XMLDeclaration::Accept( XMLVisitor* visitor ) const
471{
472 return visitor->Visit( *this );
473}
474
475// --------- XMLUnknown ---------- //
476
477XMLUnknown::XMLUnknown( XMLDocument* doc ) : XMLNode( doc )
478{
479}
480
481
482XMLUnknown::~XMLUnknown()
483{
484}
485
486
487char* XMLUnknown::ParseDeep( char* p )
488{
489 // Unknown parses as text.
490 return value.ParseText( p, ">", StrPair::NEEDS_NEWLINE_NORMALIZATION );
491}
492
493
494bool XMLUnknown::Accept( XMLVisitor* visitor ) const
495{
496 return visitor->Visit( *this );
497}
498
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800499// --------- XMLAttribute ---------- //
500char* XMLAttribute::ParseDeep( char* p )
501{
Lee Thomason56bdd022012-02-09 18:16:58 -0800502 p = name.ParseText( p, "=", StrPair::ATTRIBUTE_NAME );
Lee Thomason22aead12012-01-23 13:29:35 -0800503 if ( !p || !*p ) return 0;
504
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800505 char endTag[2] = { *p, 0 };
506 ++p;
Lee Thomason56bdd022012-02-09 18:16:58 -0800507 p = value.ParseText( p, endTag, StrPair::ATTRIBUTE_VALUE );
Lee Thomasone4422302012-01-20 17:59:50 -0800508 if ( value.Empty() ) return 0;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800509 return p;
510}
511
512
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800513
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800514// --------- XMLElement ---------- //
515XMLElement::XMLElement( XMLDocument* doc ) : XMLNode( doc ),
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800516 closing( false ),
517 rootAttribute( 0 ),
518 lastAttribute( 0 )
519{
520}
521
522
523XMLElement::~XMLElement()
524{
525 XMLAttribute* attribute = rootAttribute;
526 while( attribute ) {
527 XMLAttribute* next = attribute->next;
Lee Thomason43f59302012-02-06 18:18:11 -0800528 DELETE_ATTRIBUTE( attribute );
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800529 attribute = next;
530 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800531}
532
533
Lee Thomason67d61312012-01-24 16:01:51 -0800534char* XMLElement::ParseAttributes( char* p, bool* closedElement )
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800535{
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800536 const char* start = p;
Lee Thomason67d61312012-01-24 16:01:51 -0800537 *closedElement = false;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800538
539 // Read the attributes.
540 while( p ) {
Lee Thomason56bdd022012-02-09 18:16:58 -0800541 p = XMLUtil::SkipWhiteSpace( p );
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800542 if ( !p || !(*p) ) {
Lee Thomasond1983222012-02-06 08:41:24 -0800543 document->SetError( XMLDocument::ERROR_PARSING_ELEMENT, start, Name() );
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800544 return 0;
545 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800546
547 // attribute.
Lee Thomason56bdd022012-02-09 18:16:58 -0800548 if ( XMLUtil::IsAlpha( *p ) ) {
Lee Thomason43f59302012-02-06 18:18:11 -0800549 XMLAttribute* attrib = new (document->attributePool.Alloc() ) XMLAttribute( this );
550 attrib->memPool = &document->attributePool;
Lee Thomasond1983222012-02-06 08:41:24 -0800551
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800552 p = attrib->ParseDeep( p );
553 if ( !p ) {
Lee Thomason43f59302012-02-06 18:18:11 -0800554 DELETE_ATTRIBUTE( attrib );
Lee Thomasone4422302012-01-20 17:59:50 -0800555 document->SetError( XMLDocument::ERROR_PARSING_ATTRIBUTE, start, p );
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800556 return 0;
557 }
558 if ( rootAttribute ) {
559 TIXMLASSERT( lastAttribute );
560 lastAttribute->next = attrib;
561 lastAttribute = attrib;
562 }
563 else {
564 rootAttribute = lastAttribute = attrib;
565 }
566 }
Lee Thomasone4422302012-01-20 17:59:50 -0800567 // end of the tag
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800568 else if ( *p == '/' && *(p+1) == '>' ) {
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800569 if ( closing ) {
570 document->SetError( XMLDocument::ERROR_PARSING_ELEMENT, start, p );
571 return 0;
572 }
Lee Thomason67d61312012-01-24 16:01:51 -0800573 *closedElement = true;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800574 return p+2; // done; sealed element.
575 }
Lee Thomasone4422302012-01-20 17:59:50 -0800576 // end of the tag
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800577 else if ( *p == '>' ) {
578 ++p;
579 break;
580 }
Lee Thomasone4422302012-01-20 17:59:50 -0800581 else {
582 document->SetError( XMLDocument::ERROR_PARSING_ELEMENT, start, p );
583 return 0;
584 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800585 }
Lee Thomason67d61312012-01-24 16:01:51 -0800586 return p;
587}
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800588
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800589
Lee Thomason67d61312012-01-24 16:01:51 -0800590//
591// <ele></ele>
592// <ele>foo<b>bar</b></ele>
593//
594char* XMLElement::ParseDeep( char* p )
595{
596 // Read the element name.
Lee Thomason56bdd022012-02-09 18:16:58 -0800597 p = XMLUtil::SkipWhiteSpace( p );
Lee Thomason67d61312012-01-24 16:01:51 -0800598 if ( !p ) return 0;
599 const char* start = p;
600
601 // The closing element is the </element> form. It is
602 // parsed just like a regular element then deleted from
603 // the DOM.
604 if ( *p == '/' ) {
605 closing = true;
606 ++p;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800607 }
Lee Thomason67d61312012-01-24 16:01:51 -0800608
Lee Thomason56bdd022012-02-09 18:16:58 -0800609 p = value.ParseName( p );
Lee Thomasond1983222012-02-06 08:41:24 -0800610 if ( value.Empty() ) return 0;
Lee Thomason67d61312012-01-24 16:01:51 -0800611
612 bool elementClosed=false;
613 p = ParseAttributes( p, &elementClosed );
614 if ( !p || !*p || elementClosed || closing )
615 return p;
616
617 p = XMLNode::ParseDeep( p );
618 return p;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800619}
620
621
Lee Thomason751da522012-02-10 08:50:51 -0800622bool XMLElement::Accept( XMLVisitor* visitor ) const
623{
624 if ( visitor->VisitEnter( *this, rootAttribute ) )
625 {
626 for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() )
627 {
628 if ( !node->Accept( visitor ) )
629 break;
630 }
631 }
632 return visitor->VisitExit( *this );
633
634}
Lee Thomason56bdd022012-02-09 18:16:58 -0800635
636
Lee Thomason3f57d272012-01-11 15:30:03 -0800637// --------- XMLDocument ----------- //
Lee Thomason67d61312012-01-24 16:01:51 -0800638XMLDocument::XMLDocument() :
Lee Thomason18d68bd2012-01-26 18:17:26 -0800639 XMLNode( 0 ),
U-Lama\Lee560bd472011-12-28 19:42:49 -0800640 charBuffer( 0 )
641{
Lee Thomason18d68bd2012-01-26 18:17:26 -0800642 document = this; // avoid warning about 'this' in initializer list
U-Lama\Lee560bd472011-12-28 19:42:49 -0800643}
U-Lama\Leee13c3e62011-12-28 14:36:55 -0800644
645
Lee Thomason3f57d272012-01-11 15:30:03 -0800646XMLDocument::~XMLDocument()
647{
Lee Thomasond1983222012-02-06 08:41:24 -0800648 ClearChildren();
Lee Thomason18d68bd2012-01-26 18:17:26 -0800649 delete [] charBuffer;
Lee Thomasond1983222012-02-06 08:41:24 -0800650
Lee Thomasonec5a7b42012-02-13 18:16:52 -0800651#if 0
Lee Thomason455c9d42012-02-06 09:14:14 -0800652 textPool.Trace( "text" );
653 elementPool.Trace( "element" );
654 commentPool.Trace( "comment" );
655 attributePool.Trace( "attribute" );
Lee Thomasone9ecdab2012-02-13 18:11:20 -0800656#endif
657
Lee Thomason455c9d42012-02-06 09:14:14 -0800658 TIXMLASSERT( textPool.CurrentAllocs() == 0 );
659 TIXMLASSERT( elementPool.CurrentAllocs() == 0 );
660 TIXMLASSERT( commentPool.CurrentAllocs() == 0 );
661 TIXMLASSERT( attributePool.CurrentAllocs() == 0 );
Lee Thomason3f57d272012-01-11 15:30:03 -0800662}
663
664
Lee Thomason18d68bd2012-01-26 18:17:26 -0800665void XMLDocument::InitDocument()
666{
667 errorID = NO_ERROR;
668 errorStr1 = 0;
669 errorStr2 = 0;
670
671 delete [] charBuffer;
672 charBuffer = 0;
673
674}
675
Lee Thomason3f57d272012-01-11 15:30:03 -0800676
Lee Thomason2c85a712012-01-31 08:24:24 -0800677XMLElement* XMLDocument::NewElement( const char* name )
678{
Lee Thomasond1983222012-02-06 08:41:24 -0800679 XMLElement* ele = new (elementPool.Alloc()) XMLElement( this );
680 ele->memPool = &elementPool;
Lee Thomason2c85a712012-01-31 08:24:24 -0800681 ele->SetName( name );
682 return ele;
683}
684
685
Lee Thomason7c913cd2012-01-26 18:32:34 -0800686int XMLDocument::Parse( const char* p )
Lee Thomason3f57d272012-01-11 15:30:03 -0800687{
Lee Thomason18d68bd2012-01-26 18:17:26 -0800688 ClearChildren();
689 InitDocument();
690
691 if ( !p || !*p ) {
692 return true; // correctly parse an empty string?
693 }
694 size_t len = strlen( p );
695 charBuffer = new char[ len+1 ];
696 memcpy( charBuffer, p, len+1 );
Lee Thomason3f57d272012-01-11 15:30:03 -0800697 XMLNode* node = 0;
Lee Thomason85403d82012-01-11 15:55:05 -0800698
Lee Thomason18d68bd2012-01-26 18:17:26 -0800699 char* q = ParseDeep( charBuffer );
Lee Thomason7c913cd2012-01-26 18:32:34 -0800700 return errorID;
Lee Thomason3f57d272012-01-11 15:30:03 -0800701}
702
703
Lee Thomason5cae8972012-01-24 18:03:07 -0800704void XMLDocument::Print( XMLStreamer* streamer )
Lee Thomason3f57d272012-01-11 15:30:03 -0800705{
Lee Thomason5cae8972012-01-24 18:03:07 -0800706 XMLStreamer stdStreamer( stdout );
707 if ( !streamer )
708 streamer = &stdStreamer;
Lee Thomason751da522012-02-10 08:50:51 -0800709 //for( XMLNode* node = firstChild; node; node=node->next ) {
710 // node->Print( streamer );
711 //}
712 Accept( streamer );
Lee Thomason3f57d272012-01-11 15:30:03 -0800713}
714
715
Lee Thomason67d61312012-01-24 16:01:51 -0800716void XMLDocument::SetError( int error, const char* str1, const char* str2 )
717{
Lee Thomason18d68bd2012-01-26 18:17:26 -0800718 errorID = error;
719 printf( "ERROR: id=%d '%s' '%s'\n", error, str1, str2 ); // fixme: remove
720 errorStr1 = str1;
721 errorStr2 = str2;
Lee Thomason67d61312012-01-24 16:01:51 -0800722}
723
Lee Thomason5cae8972012-01-24 18:03:07 -0800724
Lee Thomason2c85a712012-01-31 08:24:24 -0800725/*
Lee Thomason5cae8972012-01-24 18:03:07 -0800726StringStack::StringStack()
727{
Lee Thomason5cae8972012-01-24 18:03:07 -0800728 nPositive = 0;
Lee Thomason1270ae52012-01-27 17:58:30 -0800729 mem.Push( 0 ); // start with null. makes later code simpler.
Lee Thomason5cae8972012-01-24 18:03:07 -0800730}
731
732
Lee Thomason24767b02012-01-25 17:16:23 -0800733StringStack::~StringStack()
734{
Lee Thomason24767b02012-01-25 17:16:23 -0800735}
736
737
Lee Thomason5cae8972012-01-24 18:03:07 -0800738void StringStack::Push( const char* str ) {
739 int needed = strlen( str ) + 1;
Lee Thomason1270ae52012-01-27 17:58:30 -0800740 char* p = mem.PushArr( needed );
741 strcpy( p, str );
742 if ( needed > 1 )
Lee Thomason5cae8972012-01-24 18:03:07 -0800743 nPositive++;
Lee Thomason5cae8972012-01-24 18:03:07 -0800744}
745
746
747const char* StringStack::Pop() {
Lee Thomason1270ae52012-01-27 17:58:30 -0800748 TIXMLASSERT( mem.Size() > 1 );
749 const char* p = mem.Mem() + mem.Size() - 2; // end of final string.
Lee Thomason5cae8972012-01-24 18:03:07 -0800750 if ( *p ) {
751 nPositive--;
752 }
753 while( *p ) { // stack starts with a null, don't need to check for 'mem'
Lee Thomason1270ae52012-01-27 17:58:30 -0800754 TIXMLASSERT( p > mem.Mem() );
Lee Thomason5cae8972012-01-24 18:03:07 -0800755 --p;
756 }
Lee Thomason1270ae52012-01-27 17:58:30 -0800757 mem.PopArr( strlen(p)+1 );
Lee Thomason5cae8972012-01-24 18:03:07 -0800758 return p+1;
759}
Lee Thomason2c85a712012-01-31 08:24:24 -0800760*/
Lee Thomason5cae8972012-01-24 18:03:07 -0800761
762
Lee Thomason56bdd022012-02-09 18:16:58 -0800763XMLStreamer::XMLStreamer( FILE* file ) : fp( file ), depth( 0 ), elementJustOpened( false ), textDepth( -1 )
Lee Thomason5cae8972012-01-24 18:03:07 -0800764{
Lee Thomason857b8682012-01-25 17:50:25 -0800765 for( int i=0; i<ENTITY_RANGE; ++i ) {
766 entityFlag[i] = false;
767 }
768 for( int i=0; i<NUM_ENTITIES; ++i ) {
769 TIXMLASSERT( entities[i].value < ENTITY_RANGE );
770 if ( entities[i].value < ENTITY_RANGE ) {
771 entityFlag[ entities[i].value ] = true;
772 }
773 }
Lee Thomason5cae8972012-01-24 18:03:07 -0800774}
775
776
777void XMLStreamer::PrintSpace( int depth )
778{
779 for( int i=0; i<depth; ++i ) {
780 fprintf( fp, " " );
781 }
782}
783
784
Lee Thomason951d8832012-01-26 08:47:06 -0800785void XMLStreamer::PrintString( const char* p )
Lee Thomason857b8682012-01-25 17:50:25 -0800786{
Lee Thomason951d8832012-01-26 08:47:06 -0800787 // Look for runs of bytes between entities to print.
788 const char* q = p;
Lee Thomason857b8682012-01-25 17:50:25 -0800789
Lee Thomason951d8832012-01-26 08:47:06 -0800790 while ( *q ) {
791 if ( *q < ENTITY_RANGE ) {
792 // Check for entities. If one is found, flush
793 // the stream up until the entity, write the
794 // entity, and keep looking.
795 if ( entityFlag[*q] ) {
796 while ( p < q ) {
797 fputc( *p, fp );
798 ++p;
799 }
800 for( int i=0; i<NUM_ENTITIES; ++i ) {
801 if ( entities[i].value == *q ) {
802 fprintf( fp, "&%s;", entities[i].pattern );
803 break;
804 }
805 }
806 ++p;
807 }
808 }
809 ++q;
810 }
811 // Flush the remaining string. This will be the entire
812 // string if an entity wasn't found.
813 if ( q-p > 0 ) {
814 fprintf( fp, "%s", p );
815 }
Lee Thomason857b8682012-01-25 17:50:25 -0800816}
817
Lee Thomason56bdd022012-02-09 18:16:58 -0800818void XMLStreamer::OpenElement( const char* name )
Lee Thomason5cae8972012-01-24 18:03:07 -0800819{
820 if ( elementJustOpened ) {
821 SealElement();
822 }
Lee Thomason56bdd022012-02-09 18:16:58 -0800823 stack.Push( name );
824
825 if ( textDepth < 0 && depth > 0) {
826 fprintf( fp, "\n" );
Lee Thomason24767b02012-01-25 17:16:23 -0800827 PrintSpace( depth );
828 }
Lee Thomason5cae8972012-01-24 18:03:07 -0800829
Lee Thomason5cae8972012-01-24 18:03:07 -0800830 fprintf( fp, "<%s", name );
831 elementJustOpened = true;
832 ++depth;
833}
834
835
836void XMLStreamer::PushAttribute( const char* name, const char* value )
837{
838 TIXMLASSERT( elementJustOpened );
Lee Thomason18d68bd2012-01-26 18:17:26 -0800839 fprintf( fp, " %s=\"", name );
840 PrintString( value );
841 fprintf( fp, "\"" );
Lee Thomason5cae8972012-01-24 18:03:07 -0800842}
843
844
845void XMLStreamer::CloseElement()
846{
847 --depth;
848 const char* name = stack.Pop();
Lee Thomason5cae8972012-01-24 18:03:07 -0800849
850 if ( elementJustOpened ) {
851 fprintf( fp, "/>" );
Lee Thomason5cae8972012-01-24 18:03:07 -0800852 }
853 else {
Lee Thomason56bdd022012-02-09 18:16:58 -0800854 if ( textDepth < 0 ) {
855 fprintf( fp, "\n" );
Lee Thomason24767b02012-01-25 17:16:23 -0800856 PrintSpace( depth );
857 }
Lee Thomason5cae8972012-01-24 18:03:07 -0800858 fprintf( fp, "</%s>", name );
Lee Thomason5cae8972012-01-24 18:03:07 -0800859 }
Lee Thomason56bdd022012-02-09 18:16:58 -0800860
861 if ( textDepth == depth )
862 textDepth = -1;
863 if ( depth == 0 )
864 fprintf( fp, "\n" );
Lee Thomason5cae8972012-01-24 18:03:07 -0800865 elementJustOpened = false;
866}
867
868
869void XMLStreamer::SealElement()
870{
871 elementJustOpened = false;
872 fprintf( fp, ">" );
Lee Thomason5cae8972012-01-24 18:03:07 -0800873}
874
875
Lee Thomason50f97b22012-02-11 16:33:40 -0800876void XMLStreamer::PushText( const char* text, bool cdata )
Lee Thomason5cae8972012-01-24 18:03:07 -0800877{
Lee Thomason56bdd022012-02-09 18:16:58 -0800878 textDepth = depth-1;
879
Lee Thomason5cae8972012-01-24 18:03:07 -0800880 if ( elementJustOpened ) {
881 SealElement();
882 }
Lee Thomason50f97b22012-02-11 16:33:40 -0800883 if ( cdata )
884 fprintf( fp, "<![CDATA[" );
Lee Thomason951d8832012-01-26 08:47:06 -0800885 PrintString( text );
Lee Thomason50f97b22012-02-11 16:33:40 -0800886 if ( cdata )
887 fprintf( fp, "]]>" );
Lee Thomason5cae8972012-01-24 18:03:07 -0800888}
889
890
891void XMLStreamer::PushComment( const char* comment )
892{
893 if ( elementJustOpened ) {
894 SealElement();
895 }
896 PrintSpace( depth );
897 fprintf( fp, "<!--%s-->\n", comment );
898}
Lee Thomason751da522012-02-10 08:50:51 -0800899
900
901bool XMLStreamer::VisitEnter( const XMLElement& element, const XMLAttribute* attribute )
902{
903 OpenElement( element.Name() );
904 while ( attribute ) {
905 PushAttribute( attribute->Name(), attribute->Value() );
906 attribute = attribute->Next();
907 }
908 return true;
909}
910
911
912bool XMLStreamer::VisitExit( const XMLElement& element )
913{
914 CloseElement();
915 return true;
916}
917
918
919bool XMLStreamer::Visit( const XMLText& text )
920{
921 PushText( text.Value() );
922 return true;
923}
924
925
926bool XMLStreamer::Visit( const XMLComment& comment )
927{
928 PushComment( comment.Value() );
929 return true;
930}