blob: 51cf7959375bd991d02f9464bacb59b648220254 [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>
U-Stream\Leeae25a442012-02-17 17:48:16 -08008#include <stdarg.h>
Lee Thomasond1983222012-02-06 08:41:24 -08009
10//#pragma warning ( disable : 4291 )
U-Lama\Lee560bd472011-12-28 19:42:49 -080011
12using namespace tinyxml2;
13
Lee Thomasone4422302012-01-20 17:59:50 -080014static const char LINE_FEED = (char)0x0a; // all line endings are normalized to LF
Lee Thomasonfde6a752012-01-14 18:08:12 -080015static const char LF = LINE_FEED;
16static const char CARRIAGE_RETURN = (char)0x0d; // CR gets filtered out
17static const char CR = CARRIAGE_RETURN;
Lee Thomasone4422302012-01-20 17:59:50 -080018static const char SINGLE_QUOTE = '\'';
19static const char DOUBLE_QUOTE = '\"';
Lee Thomasonfde6a752012-01-14 18:08:12 -080020
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -080021// Bunch of unicode info at:
22// http://www.unicode.org/faq/utf_bom.html
23// ef bb bf (Microsoft "lead bytes") - designates UTF-8
24
25static const unsigned char TIXML_UTF_LEAD_0 = 0xefU;
26static const unsigned char TIXML_UTF_LEAD_1 = 0xbbU;
27static const unsigned char TIXML_UTF_LEAD_2 = 0xbfU;
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -080028
29
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -080030#define DELETE_NODE( node ) { \
31 if ( node ) { \
32 MemPool* pool = node->memPool; \
33 node->~XMLNode(); \
34 pool->Free( node ); \
35 } \
36}
37#define DELETE_ATTRIBUTE( attrib ) { \
38 if ( attrib ) { \
39 MemPool* pool = attrib->memPool; \
40 attrib->~XMLAttribute(); \
41 pool->Free( attrib ); \
42 } \
43}
Lee Thomason43f59302012-02-06 18:18:11 -080044
Lee Thomason8ee79892012-01-25 17:44:30 -080045struct Entity {
46 const char* pattern;
47 int length;
48 char value;
49};
50
51static const int NUM_ENTITIES = 5;
52static const Entity entities[NUM_ENTITIES] =
53{
Lee Thomason18d68bd2012-01-26 18:17:26 -080054 { "quot", 4, DOUBLE_QUOTE },
Lee Thomason8ee79892012-01-25 17:44:30 -080055 { "amp", 3, '&' },
Lee Thomason18d68bd2012-01-26 18:17:26 -080056 { "apos", 4, SINGLE_QUOTE },
Lee Thomason8ee79892012-01-25 17:44:30 -080057 { "lt", 2, '<' },
58 { "gt", 2, '>' }
59};
60
Lee Thomasonfde6a752012-01-14 18:08:12 -080061
Lee Thomason1a1d4a72012-02-15 09:09:25 -080062StrPair::~StrPair()
63{
64 Reset();
65}
66
67
68void StrPair::Reset()
69{
70 if ( flags & NEEDS_DELETE ) {
71 delete [] start;
72 }
73 flags = 0;
74 start = 0;
75 end = 0;
76}
77
78
79void StrPair::SetStr( const char* str, int flags )
80{
81 Reset();
82 size_t len = strlen( str );
83 start = new char[ len+1 ];
U-Stream\Lee09a11c52012-02-17 08:31:16 -080084 memcpy( start, str, len+1 );
Lee Thomason1a1d4a72012-02-15 09:09:25 -080085 end = start + len;
86 this->flags = flags | NEEDS_DELETE;
87}
88
89
90char* StrPair::ParseText( char* p, const char* endTag, int strFlags )
91{
92 TIXMLASSERT( endTag && *endTag );
93
94 char* start = p; // fixme: hides a member
95 char endChar = *endTag;
96 int length = strlen( endTag );
97
98 // Inner loop of text parsing.
99 while ( *p ) {
100 if ( *p == endChar && strncmp( p, endTag, length ) == 0 ) {
101 Set( start, p, strFlags );
102 return p + length;
103 }
104 ++p;
105 }
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800106 return 0;
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800107}
108
109
110char* StrPair::ParseName( char* p )
111{
112 char* start = p;
113
114 start = p;
115 if ( !start || !(*start) ) {
116 return 0;
117 }
118
119 if ( !XMLUtil::IsAlpha( *p ) ) {
120 return 0;
121 }
122
123 while( *p && (
124 XMLUtil::IsAlphaNum( (unsigned char) *p )
125 || *p == '_'
126 || *p == '-'
127 || *p == '.'
128 || *p == ':' ))
129 {
130 ++p;
131 }
132
133 if ( p > start ) {
134 Set( start, p, 0 );
135 return p;
136 }
137 return 0;
138}
139
140
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800141
Lee Thomasone4422302012-01-20 17:59:50 -0800142const char* StrPair::GetStr()
143{
144 if ( flags & NEEDS_FLUSH ) {
145 *end = 0;
Lee Thomason8ee79892012-01-25 17:44:30 -0800146 flags ^= NEEDS_FLUSH;
Lee Thomasone4422302012-01-20 17:59:50 -0800147
Lee Thomason8ee79892012-01-25 17:44:30 -0800148 if ( flags ) {
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800149 char* p = start; // the read pointer
150 char* q = start; // the write pointer
Lee Thomasone4422302012-01-20 17:59:50 -0800151
152 while( p < end ) {
Lee Thomason8ee79892012-01-25 17:44:30 -0800153 if ( (flags & NEEDS_NEWLINE_NORMALIZATION) && *p == CR ) {
Lee Thomasone4422302012-01-20 17:59:50 -0800154 // CR-LF pair becomes LF
155 // CR alone becomes LF
156 // LF-CR becomes LF
157 if ( *(p+1) == LF ) {
158 p += 2;
159 }
160 else {
161 ++p;
162 }
Lee Thomasone9ecdab2012-02-13 18:11:20 -0800163 *q++ = LF;
Lee Thomasone4422302012-01-20 17:59:50 -0800164 }
Lee Thomason8ee79892012-01-25 17:44:30 -0800165 else if ( (flags & NEEDS_NEWLINE_NORMALIZATION) && *p == LF ) {
Lee Thomasone4422302012-01-20 17:59:50 -0800166 if ( *(p+1) == CR ) {
167 p += 2;
168 }
169 else {
170 ++p;
171 }
Lee Thomasone9ecdab2012-02-13 18:11:20 -0800172 *q++ = LF;
Lee Thomasone4422302012-01-20 17:59:50 -0800173 }
Lee Thomason8ee79892012-01-25 17:44:30 -0800174 else if ( (flags & NEEDS_ENTITY_PROCESSING) && *p == '&' ) {
175 int i=0;
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800176
177 // Entities handled by tinyXML2:
178 // - special entities in the entity table [in/out]
179 // - numeric character reference [in]
180 // &#20013; or &#x4e2d;
181
182 if ( *(p+1) == '#' ) {
183 char buf[10] = { 0 };
184 int len;
185 p = const_cast<char*>( XMLUtil::GetCharacterRef( p, buf, &len ) );
186 for( int i=0; i<len; ++i ) {
187 *q++ = buf[i];
Lee Thomason8ee79892012-01-25 17:44:30 -0800188 }
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800189 TIXMLASSERT( q <= p );
Lee Thomason8ee79892012-01-25 17:44:30 -0800190 }
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800191 else {
192 for( i=0; i<NUM_ENTITIES; ++i ) {
193 if ( strncmp( p+1, entities[i].pattern, entities[i].length ) == 0
194 && *(p+entities[i].length+1) == ';' )
195 {
196 // Found an entity convert;
197 *q = entities[i].value;
198 ++q;
199 p += entities[i].length + 2;
200 break;
201 }
202 }
203 if ( i == NUM_ENTITIES ) {
204 // fixme: treat as error?
205 ++p;
206 ++q;
207 }
Lee Thomason8ee79892012-01-25 17:44:30 -0800208 }
209 }
Lee Thomasone4422302012-01-20 17:59:50 -0800210 else {
211 *q = *p;
212 ++p;
Lee Thomasonec975ce2012-01-23 11:42:06 -0800213 ++q;
Lee Thomasone4422302012-01-20 17:59:50 -0800214 }
215 }
Lee Thomason8ee79892012-01-25 17:44:30 -0800216 *q = 0;
Lee Thomasone4422302012-01-20 17:59:50 -0800217 }
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800218 flags = (flags & NEEDS_DELETE);
Lee Thomasone4422302012-01-20 17:59:50 -0800219 }
220 return start;
221}
222
Lee Thomason2c85a712012-01-31 08:24:24 -0800223
Lee Thomasone4422302012-01-20 17:59:50 -0800224
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800225
Lee Thomason56bdd022012-02-09 18:16:58 -0800226// --------- XMLUtil ----------- //
Lee Thomasond1983222012-02-06 08:41:24 -0800227
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800228const char* XMLUtil::ReadBOM( const char* p, bool* bom )
229{
230 *bom = false;
231 const unsigned char* pu = reinterpret_cast<const unsigned char*>(p);
232 // Check for BOM:
233 if ( *(pu+0) == TIXML_UTF_LEAD_0
234 && *(pu+1) == TIXML_UTF_LEAD_1
235 && *(pu+2) == TIXML_UTF_LEAD_2 )
236 {
237 *bom = true;
238 p += 3;
239 }
240 return p;
241}
242
243
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800244void XMLUtil::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length )
245{
246 const unsigned long BYTE_MASK = 0xBF;
247 const unsigned long BYTE_MARK = 0x80;
248 const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
249
250 if (input < 0x80)
251 *length = 1;
252 else if ( input < 0x800 )
253 *length = 2;
254 else if ( input < 0x10000 )
255 *length = 3;
256 else if ( input < 0x200000 )
257 *length = 4;
258 else
259 { *length = 0; return; } // This code won't covert this correctly anyway.
260
261 output += *length;
262
263 // Scary scary fall throughs.
264 switch (*length)
265 {
266 case 4:
267 --output;
268 *output = (char)((input | BYTE_MARK) & BYTE_MASK);
269 input >>= 6;
270 case 3:
271 --output;
272 *output = (char)((input | BYTE_MARK) & BYTE_MASK);
273 input >>= 6;
274 case 2:
275 --output;
276 *output = (char)((input | BYTE_MARK) & BYTE_MASK);
277 input >>= 6;
278 case 1:
279 --output;
280 *output = (char)(input | FIRST_BYTE_MARK[*length]);
281 }
282}
283
284
285const char* XMLUtil::GetCharacterRef( const char* p, char* value, int* length )
286{
287 // Presume an entity, and pull it out.
288 *length = 0;
289
290 if ( *(p+1) == '#' && *(p+2) )
291 {
292 unsigned long ucs = 0;
293 ptrdiff_t delta = 0;
294 unsigned mult = 1;
295
296 if ( *(p+2) == 'x' )
297 {
298 // Hexadecimal.
299 if ( !*(p+3) ) return 0;
300
301 const char* q = p+3;
302 q = strchr( q, ';' );
303
304 if ( !q || !*q ) return 0;
305
306 delta = q-p;
307 --q;
308
309 while ( *q != 'x' )
310 {
311 if ( *q >= '0' && *q <= '9' )
312 ucs += mult * (*q - '0');
313 else if ( *q >= 'a' && *q <= 'f' )
314 ucs += mult * (*q - 'a' + 10);
315 else if ( *q >= 'A' && *q <= 'F' )
316 ucs += mult * (*q - 'A' + 10 );
317 else
318 return 0;
319 mult *= 16;
320 --q;
321 }
322 }
323 else
324 {
325 // Decimal.
326 if ( !*(p+2) ) return 0;
327
328 const char* q = p+2;
329 q = strchr( q, ';' );
330
331 if ( !q || !*q ) return 0;
332
333 delta = q-p;
334 --q;
335
336 while ( *q != '#' )
337 {
338 if ( *q >= '0' && *q <= '9' )
339 ucs += mult * (*q - '0');
340 else
341 return 0;
342 mult *= 10;
343 --q;
344 }
345 }
346 // convert the UCS to UTF-8
347 ConvertUTF32ToUTF8( ucs, value, length );
348 return p + delta + 1;
349 }
350 return p+1;
351}
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800352
353
Lee Thomasond1983222012-02-06 08:41:24 -0800354char* XMLDocument::Identify( char* p, XMLNode** node )
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800355{
356 XMLNode* returnNode = 0;
Lee Thomason5492a1c2012-01-23 15:32:10 -0800357 char* start = p;
Lee Thomason56bdd022012-02-09 18:16:58 -0800358 p = XMLUtil::SkipWhiteSpace( p );
Lee Thomason5492a1c2012-01-23 15:32:10 -0800359 if( !p || !*p )
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800360 {
361 return 0;
362 }
363
364 // What is this thing?
365 // - Elements start with a letter or underscore, but xml is reserved.
366 // - Comments: <!--
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800367 // - Decleration: <?
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800368 // - Everthing else is unknown to tinyxml.
369 //
370
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800371 static const char* xmlHeader = { "<?" };
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800372 static const char* commentHeader = { "<!--" };
373 static const char* dtdHeader = { "<!" };
374 static const char* cdataHeader = { "<![CDATA[" };
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800375 static const char* elementHeader = { "<" }; // and a header for everything else; check last.
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800376
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800377 static const int xmlHeaderLen = 2;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800378 static const int commentHeaderLen = 4;
379 static const int dtdHeaderLen = 2;
380 static const int cdataHeaderLen = 9;
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800381 static const int elementHeaderLen = 1;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800382
Lee Thomason50f97b22012-02-11 16:33:40 -0800383 TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLUnknown ) ); // use same memory pool
384 TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLDeclaration ) ); // use same memory pool
385
386 if ( XMLUtil::StringEqual( p, xmlHeader, xmlHeaderLen ) ) {
387 returnNode = new (commentPool.Alloc()) XMLDeclaration( this );
388 returnNode->memPool = &commentPool;
389 p += xmlHeaderLen;
390 }
391 else if ( XMLUtil::StringEqual( p, commentHeader, commentHeaderLen ) ) {
Lee Thomasond1983222012-02-06 08:41:24 -0800392 returnNode = new (commentPool.Alloc()) XMLComment( this );
393 returnNode->memPool = &commentPool;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800394 p += commentHeaderLen;
395 }
Lee Thomason50f97b22012-02-11 16:33:40 -0800396 else if ( XMLUtil::StringEqual( p, cdataHeader, cdataHeaderLen ) ) {
397 XMLText* text = new (textPool.Alloc()) XMLText( this );
398 returnNode = text;
399 returnNode->memPool = &textPool;
400 p += cdataHeaderLen;
401 text->SetCData( true );
402 }
403 else if ( XMLUtil::StringEqual( p, dtdHeader, dtdHeaderLen ) ) {
404 returnNode = new (commentPool.Alloc()) XMLUnknown( this );
405 returnNode->memPool = &commentPool;
406 p += dtdHeaderLen;
407 }
Lee Thomason56bdd022012-02-09 18:16:58 -0800408 else if ( XMLUtil::StringEqual( p, elementHeader, elementHeaderLen ) ) {
Lee Thomasond1983222012-02-06 08:41:24 -0800409 returnNode = new (elementPool.Alloc()) XMLElement( this );
410 returnNode->memPool = &elementPool;
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800411 p += elementHeaderLen;
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800412
413 p = XMLUtil::SkipWhiteSpace( p );
414 if ( p && *p == '/' ) {
415 ((XMLElement*)returnNode)->closingType = XMLElement::CLOSING;
416 }
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800417 }
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800418 else {
Lee Thomasond1983222012-02-06 08:41:24 -0800419 returnNode = new (textPool.Alloc()) XMLText( this );
420 returnNode->memPool = &textPool;
Lee Thomason5492a1c2012-01-23 15:32:10 -0800421 p = start; // Back it up, all the text counts.
422 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800423
424 *node = returnNode;
425 return p;
426}
427
428
Lee Thomason751da522012-02-10 08:50:51 -0800429bool XMLDocument::Accept( XMLVisitor* visitor ) const
430{
431 if ( visitor->VisitEnter( *this ) )
432 {
433 for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() )
434 {
435 if ( !node->Accept( visitor ) )
436 break;
437 }
438 }
439 return visitor->VisitExit( *this );
440}
Lee Thomason56bdd022012-02-09 18:16:58 -0800441
442
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800443// --------- XMLNode ----------- //
444
445XMLNode::XMLNode( XMLDocument* doc ) :
446 document( doc ),
447 parent( 0 ),
448 firstChild( 0 ), lastChild( 0 ),
449 prev( 0 ), next( 0 )
450{
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800451}
452
453
454XMLNode::~XMLNode()
455{
Lee Thomason18d68bd2012-01-26 18:17:26 -0800456 ClearChildren();
Lee Thomason7c913cd2012-01-26 18:32:34 -0800457 if ( parent ) {
458 parent->Unlink( this );
459 }
Lee Thomason18d68bd2012-01-26 18:17:26 -0800460}
461
462
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800463void XMLNode::SetValue( const char* str, bool staticMem )
464{
465 if ( staticMem )
466 value.SetInternedStr( str );
467 else
468 value.SetStr( str );
469}
470
471
Lee Thomason18d68bd2012-01-26 18:17:26 -0800472void XMLNode::ClearChildren()
473{
Lee Thomasond923c672012-01-23 08:44:25 -0800474 while( firstChild ) {
475 XMLNode* node = firstChild;
476 Unlink( node );
Lee Thomasond1983222012-02-06 08:41:24 -0800477
Lee Thomason43f59302012-02-06 18:18:11 -0800478 DELETE_NODE( node );
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800479 }
Lee Thomason18d68bd2012-01-26 18:17:26 -0800480 firstChild = lastChild = 0;
Lee Thomasond923c672012-01-23 08:44:25 -0800481}
482
483
484void XMLNode::Unlink( XMLNode* child )
485{
486 TIXMLASSERT( child->parent == this );
487 if ( child == firstChild )
488 firstChild = firstChild->next;
489 if ( child == lastChild )
490 lastChild = lastChild->prev;
491
492 if ( child->prev ) {
493 child->prev->next = child->next;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800494 }
Lee Thomasond923c672012-01-23 08:44:25 -0800495 if ( child->next ) {
496 child->next->prev = child->prev;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800497 }
Lee Thomasond923c672012-01-23 08:44:25 -0800498 child->parent = 0;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800499}
500
501
U-Stream\Leeae25a442012-02-17 17:48:16 -0800502void XMLNode::DeleteChild( XMLNode* node )
503{
504 TIXMLASSERT( node->parent == this );
505 DELETE_NODE( node );
506}
507
508
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800509XMLNode* XMLNode::InsertEndChild( XMLNode* addThis )
510{
511 if ( lastChild ) {
512 TIXMLASSERT( firstChild );
513 TIXMLASSERT( lastChild->next == 0 );
514 lastChild->next = addThis;
515 addThis->prev = lastChild;
516 lastChild = addThis;
517
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800518 addThis->next = 0;
519 }
520 else {
521 TIXMLASSERT( firstChild == 0 );
522 firstChild = lastChild = addThis;
523
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800524 addThis->prev = 0;
525 addThis->next = 0;
526 }
U-Stream\Lee09a11c52012-02-17 08:31:16 -0800527 addThis->parent = this;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800528 return addThis;
529}
530
531
Lee Thomason1ff38e02012-02-14 18:18:16 -0800532XMLNode* XMLNode::InsertFirstChild( XMLNode* addThis )
533{
534 if ( firstChild ) {
535 TIXMLASSERT( lastChild );
536 TIXMLASSERT( firstChild->prev == 0 );
537
538 firstChild->prev = addThis;
539 addThis->next = firstChild;
540 firstChild = addThis;
541
Lee Thomason1ff38e02012-02-14 18:18:16 -0800542 addThis->prev = 0;
543 }
544 else {
545 TIXMLASSERT( lastChild == 0 );
546 firstChild = lastChild = addThis;
547
Lee Thomason1ff38e02012-02-14 18:18:16 -0800548 addThis->prev = 0;
549 addThis->next = 0;
550 }
U-Stream\Lee09a11c52012-02-17 08:31:16 -0800551 addThis->parent = this;
Lee Thomason1ff38e02012-02-14 18:18:16 -0800552 return addThis;
553}
554
555
556XMLNode* XMLNode::InsertAfterChild( XMLNode* afterThis, XMLNode* addThis )
557{
558 TIXMLASSERT( afterThis->parent == this );
559 if ( afterThis->parent != this )
560 return 0;
561
562 if ( afterThis->next == 0 ) {
563 // The last node or the only node.
564 return InsertEndChild( addThis );
565 }
566 addThis->prev = afterThis;
567 addThis->next = afterThis->next;
568 afterThis->next->prev = addThis;
569 afterThis->next = addThis;
U-Stream\Lee09a11c52012-02-17 08:31:16 -0800570 addThis->parent = this;
Lee Thomason1ff38e02012-02-14 18:18:16 -0800571 return addThis;
572}
573
574
575
576
Lee Thomason56bdd022012-02-09 18:16:58 -0800577const XMLElement* XMLNode::FirstChildElement( const char* value ) const
Lee Thomason2c85a712012-01-31 08:24:24 -0800578{
579 for( XMLNode* node=firstChild; node; node=node->next ) {
580 XMLElement* element = node->ToElement();
581 if ( element ) {
Lee Thomason56bdd022012-02-09 18:16:58 -0800582 if ( !value || XMLUtil::StringEqual( element->Name(), value ) ) {
Lee Thomason2c85a712012-01-31 08:24:24 -0800583 return element;
584 }
585 }
586 }
587 return 0;
588}
589
590
Lee Thomason56bdd022012-02-09 18:16:58 -0800591const XMLElement* XMLNode::LastChildElement( const char* value ) const
592{
593 for( XMLNode* node=lastChild; node; node=node->prev ) {
594 XMLElement* element = node->ToElement();
595 if ( element ) {
596 if ( !value || XMLUtil::StringEqual( element->Name(), value ) ) {
597 return element;
598 }
599 }
600 }
601 return 0;
602}
603
604
Lee Thomason67d61312012-01-24 16:01:51 -0800605char* XMLNode::ParseDeep( char* p )
606{
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800607 // This is a recursive method, but thinking about it "at the current level"
608 // it is a pretty simple flat list:
609 // <foo/>
610 // <!-- comment -->
611 //
612 // With a special case:
613 // <foo>
614 // </foo>
615 // <!-- comment -->
616 //
617 // Where the closing element (/foo) *must* be the next thing after the opening
618 // element, and the names must match. BUT the tricky bit is that the closing
619 // element will be read by the child.
620
Lee Thomason67d61312012-01-24 16:01:51 -0800621 while( p && *p ) {
622 XMLNode* node = 0;
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800623 char* mark = p;
Lee Thomasond6277762012-02-22 16:00:12 -0800624
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800625 p = document->Identify( p, &node );
626 if ( p == 0 ) {
627 break;
628 }
629
630 // We read the end tag. Back up and return.
631 if ( node && node->ToElement() && node->ToElement()->ClosingType() == XMLElement::CLOSING ) {
632 DELETE_NODE( node );
633 return mark;
634 }
635
636 if ( node ) {
637 p = node->ParseDeep( p );
638 if ( !p ) {
Lee Thomason43f59302012-02-06 18:18:11 -0800639 DELETE_NODE( node );
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800640 node = 0;
641 break;
Lee Thomason67d61312012-01-24 16:01:51 -0800642 }
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800643
644 XMLElement* ele = node->ToElement();
645 if ( ele && ele->ClosingType() == XMLElement::OPEN ) {
646 XMLNode* closingNode = 0;
647 p = document->Identify( p, &closingNode );
648 XMLElement* closingEle = closingNode ? closingNode->ToElement() : 0;
649
650 if ( closingEle == 0 ) {
651 document->SetError( ERROR_MISMATCHED_ELEMENT, node->Value(), 0 );
652 p = 0;
653 }
654 else if ( closingEle->ClosingType() != XMLElement::CLOSING ) {
655 document->SetError( ERROR_MISMATCHED_ELEMENT, node->Value(), 0 );
656 p = 0;
657 }
658 else
659 {
660 p = closingEle->ParseDeep( p );
661 if ( !XMLUtil::StringEqual( closingEle->Value(), node->Value() )) {
662 document->SetError( ERROR_MISMATCHED_ELEMENT, node->Value(), 0 );
663 p = 0;
664 }
665 }
666 // Else everything is fine, but we need to throw away the node.
667 DELETE_NODE( closingNode );
668 if ( p == 0 ) {
669 DELETE_NODE( node );
670 node = 0;
671 }
672 }
673 if ( node ) {
674 this->InsertEndChild( node );
675 }
Lee Thomason67d61312012-01-24 16:01:51 -0800676 }
677 }
678 return 0;
679}
680
Lee Thomason5492a1c2012-01-23 15:32:10 -0800681// --------- XMLText ---------- //
682char* XMLText::ParseDeep( char* p )
683{
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800684 const char* start = p;
Lee Thomason50f97b22012-02-11 16:33:40 -0800685 if ( this->CData() ) {
686 p = value.ParseText( p, "]]>", StrPair::NEEDS_NEWLINE_NORMALIZATION );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800687 if ( !p ) {
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800688 document->SetError( ERROR_PARSING_CDATA, start, 0 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800689 }
Lee Thomason50f97b22012-02-11 16:33:40 -0800690 return p;
691 }
692 else {
693 p = value.ParseText( p, "<", StrPair::TEXT_ELEMENT );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800694 if ( !p ) {
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800695 document->SetError( ERROR_PARSING_TEXT, start, 0 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800696 }
Lee Thomason50f97b22012-02-11 16:33:40 -0800697 if ( p && *p ) {
698 return p-1;
699 }
Lee Thomason5492a1c2012-01-23 15:32:10 -0800700 }
701 return 0;
702}
703
704
Lee Thomason56bdd022012-02-09 18:16:58 -0800705bool XMLText::Accept( XMLVisitor* visitor ) const
706{
707 return visitor->Visit( *this );
708}
709
710
Lee Thomason3f57d272012-01-11 15:30:03 -0800711// --------- XMLComment ---------- //
712
Lee Thomasone4422302012-01-20 17:59:50 -0800713XMLComment::XMLComment( XMLDocument* doc ) : XMLNode( doc )
Lee Thomason3f57d272012-01-11 15:30:03 -0800714{
715}
716
717
Lee Thomasonce0763e2012-01-11 15:43:54 -0800718XMLComment::~XMLComment()
Lee Thomason3f57d272012-01-11 15:30:03 -0800719{
Lee Thomasond923c672012-01-23 08:44:25 -0800720 //printf( "~XMLComment\n" );
Lee Thomason3f57d272012-01-11 15:30:03 -0800721}
722
723
Lee Thomasonce0763e2012-01-11 15:43:54 -0800724char* XMLComment::ParseDeep( char* p )
Lee Thomason3f57d272012-01-11 15:30:03 -0800725{
726 // Comment parses as text.
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800727 const char* start = p;
728 p = value.ParseText( p, "-->", StrPair::COMMENT );
729 if ( p == 0 ) {
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800730 document->SetError( ERROR_PARSING_COMMENT, start, 0 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800731 }
732 return p;
U-Lama\Lee4cee6112011-12-31 14:58:18 -0800733}
734
735
Lee Thomason751da522012-02-10 08:50:51 -0800736bool XMLComment::Accept( XMLVisitor* visitor ) const
737{
738 return visitor->Visit( *this );
739}
Lee Thomason56bdd022012-02-09 18:16:58 -0800740
741
Lee Thomason50f97b22012-02-11 16:33:40 -0800742// --------- XMLDeclaration ---------- //
743
744XMLDeclaration::XMLDeclaration( XMLDocument* doc ) : XMLNode( doc )
745{
746}
747
748
749XMLDeclaration::~XMLDeclaration()
750{
751 //printf( "~XMLDeclaration\n" );
752}
753
754
755char* XMLDeclaration::ParseDeep( char* p )
756{
757 // Declaration parses as text.
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800758 const char* start = p;
759 p = value.ParseText( p, "?>", StrPair::NEEDS_NEWLINE_NORMALIZATION );
760 if ( p == 0 ) {
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800761 document->SetError( ERROR_PARSING_DECLARATION, start, 0 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800762 }
763 return p;
Lee Thomason50f97b22012-02-11 16:33:40 -0800764}
765
766
767bool XMLDeclaration::Accept( XMLVisitor* visitor ) const
768{
769 return visitor->Visit( *this );
770}
771
772// --------- XMLUnknown ---------- //
773
774XMLUnknown::XMLUnknown( XMLDocument* doc ) : XMLNode( doc )
775{
776}
777
778
779XMLUnknown::~XMLUnknown()
780{
781}
782
783
784char* XMLUnknown::ParseDeep( char* p )
785{
786 // Unknown parses as text.
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800787 const char* start = p;
788
789 p = value.ParseText( p, ">", StrPair::NEEDS_NEWLINE_NORMALIZATION );
790 if ( !p ) {
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800791 document->SetError( ERROR_PARSING_UNKNOWN, start, 0 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800792 }
793 return p;
Lee Thomason50f97b22012-02-11 16:33:40 -0800794}
795
796
797bool XMLUnknown::Accept( XMLVisitor* visitor ) const
798{
799 return visitor->Visit( *this );
800}
801
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800802// --------- XMLAttribute ---------- //
803char* XMLAttribute::ParseDeep( char* p )
804{
Lee Thomason56bdd022012-02-09 18:16:58 -0800805 p = name.ParseText( p, "=", StrPair::ATTRIBUTE_NAME );
Lee Thomason22aead12012-01-23 13:29:35 -0800806 if ( !p || !*p ) return 0;
807
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800808 char endTag[2] = { *p, 0 };
809 ++p;
Lee Thomason56bdd022012-02-09 18:16:58 -0800810 p = value.ParseText( p, endTag, StrPair::ATTRIBUTE_VALUE );
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800811 //if ( value.Empty() ) return 0;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800812 return p;
813}
814
815
U-Stream\Lee09a11c52012-02-17 08:31:16 -0800816void XMLAttribute::SetName( const char* n )
817{
818 name.SetStr( n );
819}
820
821
Lee Thomason1ff38e02012-02-14 18:18:16 -0800822int XMLAttribute::QueryIntAttribute( int* value ) const
823{
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800824 if ( TIXML_SSCANF( Value(), "%d", value ) == 1 )
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800825 return XML_NO_ERROR;
Lee Thomason1ff38e02012-02-14 18:18:16 -0800826 return WRONG_ATTRIBUTE_TYPE;
827}
828
829
830int XMLAttribute::QueryUnsignedAttribute( unsigned int* value ) const
831{
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800832 if ( TIXML_SSCANF( Value(), "%u", value ) == 1 )
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800833 return XML_NO_ERROR;
Lee Thomason1ff38e02012-02-14 18:18:16 -0800834 return WRONG_ATTRIBUTE_TYPE;
835}
836
837
838int XMLAttribute::QueryBoolAttribute( bool* value ) const
839{
840 int ival = -1;
841 QueryIntAttribute( &ival );
842
843 if ( ival > 0 || XMLUtil::StringEqual( Value(), "true" ) ) {
844 *value = true;
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800845 return XML_NO_ERROR;
Lee Thomason1ff38e02012-02-14 18:18:16 -0800846 }
847 else if ( ival == 0 || XMLUtil::StringEqual( Value(), "false" ) ) {
848 *value = false;
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800849 return XML_NO_ERROR;
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800850 }
Lee Thomason1ff38e02012-02-14 18:18:16 -0800851 return WRONG_ATTRIBUTE_TYPE;
852}
853
854
855int XMLAttribute::QueryDoubleAttribute( double* value ) const
856{
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800857 if ( TIXML_SSCANF( Value(), "%lf", value ) == 1 )
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800858 return XML_NO_ERROR;
Lee Thomason1ff38e02012-02-14 18:18:16 -0800859 return WRONG_ATTRIBUTE_TYPE;
860}
861
862
863int XMLAttribute::QueryFloatAttribute( float* value ) const
864{
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800865 if ( TIXML_SSCANF( Value(), "%f", value ) == 1 )
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800866 return XML_NO_ERROR;
Lee Thomason1ff38e02012-02-14 18:18:16 -0800867 return WRONG_ATTRIBUTE_TYPE;
868}
869
870
871void XMLAttribute::SetAttribute( const char* v )
872{
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800873 value.SetStr( v );
Lee Thomason1ff38e02012-02-14 18:18:16 -0800874}
875
876
Lee Thomason1ff38e02012-02-14 18:18:16 -0800877void XMLAttribute::SetAttribute( int v )
878{
879 char buf[BUF_SIZE];
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800880 TIXML_SNPRINTF( buf, BUF_SIZE-1, "%d", v );
881 value.SetStr( buf );
Lee Thomason1ff38e02012-02-14 18:18:16 -0800882}
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800883
884
885void XMLAttribute::SetAttribute( unsigned v )
886{
887 char buf[BUF_SIZE];
888 TIXML_SNPRINTF( buf, BUF_SIZE-1, "%u", v );
889 value.SetStr( buf );
890}
891
892
893void XMLAttribute::SetAttribute( bool v )
894{
895 char buf[BUF_SIZE];
896 TIXML_SNPRINTF( buf, BUF_SIZE-1, "%d", v ? 1 : 0 );
897 value.SetStr( buf );
898}
899
900void XMLAttribute::SetAttribute( double v )
901{
902 char buf[BUF_SIZE];
903 TIXML_SNPRINTF( buf, BUF_SIZE-1, "%f", v );
904 value.SetStr( buf );
905}
906
907void XMLAttribute::SetAttribute( float v )
908{
909 char buf[BUF_SIZE];
910 TIXML_SNPRINTF( buf, BUF_SIZE-1, "%f", v );
911 value.SetStr( buf );
912}
913
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800914
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800915// --------- XMLElement ---------- //
916XMLElement::XMLElement( XMLDocument* doc ) : XMLNode( doc ),
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800917 closingType( 0 ),
U-Stream\Lee09a11c52012-02-17 08:31:16 -0800918 rootAttribute( 0 )
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800919{
920}
921
922
923XMLElement::~XMLElement()
924{
U-Stream\Lee09a11c52012-02-17 08:31:16 -0800925 while( rootAttribute ) {
926 XMLAttribute* next = rootAttribute->next;
927 DELETE_ATTRIBUTE( rootAttribute );
928 rootAttribute = next;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800929 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800930}
931
932
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800933XMLAttribute* XMLElement::FindAttribute( const char* name )
934{
935 XMLAttribute* a = 0;
936 for( a=rootAttribute; a; a = a->next ) {
937 if ( XMLUtil::StringEqual( a->Name(), name ) )
938 return a;
939 }
940 return 0;
941}
942
943
944const XMLAttribute* XMLElement::FindAttribute( const char* name ) const
945{
946 XMLAttribute* a = 0;
947 for( a=rootAttribute; a; a = a->next ) {
948 if ( XMLUtil::StringEqual( a->Name(), name ) )
949 return a;
950 }
951 return 0;
952}
953
954
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800955const char* XMLElement::GetText() const
956{
957 if ( FirstChild() && FirstChild()->ToText() ) {
958 return FirstChild()->ToText()->Value();
959 }
960 return 0;
961}
962
963
964
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800965XMLAttribute* XMLElement::FindOrCreateAttribute( const char* name )
966{
967 XMLAttribute* attrib = FindAttribute( name );
968 if ( !attrib ) {
U-Stream\Lee09a11c52012-02-17 08:31:16 -0800969 attrib = new (document->attributePool.Alloc() ) XMLAttribute( this );
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800970 attrib->memPool = &document->attributePool;
U-Stream\Lee09a11c52012-02-17 08:31:16 -0800971 LinkAttribute( attrib );
972 attrib->SetName( name );
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800973 }
974 return attrib;
975}
976
977
U-Stream\Lee09a11c52012-02-17 08:31:16 -0800978void XMLElement::LinkAttribute( XMLAttribute* attrib )
979{
980 if ( rootAttribute ) {
981 XMLAttribute* end = rootAttribute;
982 while ( end->next )
983 end = end->next;
984 end->next = attrib;
985 }
986 else {
987 rootAttribute = attrib;
988 }
989}
990
991
U-Stream\Leeae25a442012-02-17 17:48:16 -0800992void XMLElement::DeleteAttribute( const char* name )
993{
994 XMLAttribute* prev = 0;
995 for( XMLAttribute* a=rootAttribute; a; a=a->next ) {
996 if ( XMLUtil::StringEqual( name, a->Name() ) ) {
997 if ( prev ) {
998 prev->next = a->next;
999 }
1000 else {
1001 rootAttribute = a->next;
1002 }
1003 DELETE_ATTRIBUTE( a );
1004 break;
1005 }
1006 prev = a;
1007 }
1008}
1009
1010
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -08001011char* XMLElement::ParseAttributes( char* p )
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001012{
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001013 const char* start = p;
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001014
1015 // Read the attributes.
1016 while( p ) {
Lee Thomason56bdd022012-02-09 18:16:58 -08001017 p = XMLUtil::SkipWhiteSpace( p );
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001018 if ( !p || !(*p) ) {
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001019 document->SetError( ERROR_PARSING_ELEMENT, start, Name() );
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001020 return 0;
1021 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001022
1023 // attribute.
Lee Thomason56bdd022012-02-09 18:16:58 -08001024 if ( XMLUtil::IsAlpha( *p ) ) {
Lee Thomason43f59302012-02-06 18:18:11 -08001025 XMLAttribute* attrib = new (document->attributePool.Alloc() ) XMLAttribute( this );
1026 attrib->memPool = &document->attributePool;
Lee Thomasond1983222012-02-06 08:41:24 -08001027
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001028 p = attrib->ParseDeep( p );
Lee Thomasond6277762012-02-22 16:00:12 -08001029 if ( !p || Attribute( attrib->Name() ) ) {
Lee Thomason43f59302012-02-06 18:18:11 -08001030 DELETE_ATTRIBUTE( attrib );
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001031 document->SetError( ERROR_PARSING_ATTRIBUTE, start, p );
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001032 return 0;
1033 }
U-Stream\Lee09a11c52012-02-17 08:31:16 -08001034 LinkAttribute( attrib );
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001035 }
Lee Thomasone4422302012-01-20 17:59:50 -08001036 // end of the tag
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001037 else if ( *p == '/' && *(p+1) == '>' ) {
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -08001038 closingType = CLOSED;
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001039 return p+2; // done; sealed element.
1040 }
Lee Thomasone4422302012-01-20 17:59:50 -08001041 // end of the tag
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001042 else if ( *p == '>' ) {
1043 ++p;
1044 break;
1045 }
Lee Thomasone4422302012-01-20 17:59:50 -08001046 else {
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001047 document->SetError( ERROR_PARSING_ELEMENT, start, p );
Lee Thomasone4422302012-01-20 17:59:50 -08001048 return 0;
1049 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001050 }
Lee Thomason67d61312012-01-24 16:01:51 -08001051 return p;
1052}
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001053
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001054
Lee Thomason67d61312012-01-24 16:01:51 -08001055//
1056// <ele></ele>
1057// <ele>foo<b>bar</b></ele>
1058//
1059char* XMLElement::ParseDeep( char* p )
1060{
1061 // Read the element name.
Lee Thomason56bdd022012-02-09 18:16:58 -08001062 p = XMLUtil::SkipWhiteSpace( p );
Lee Thomason67d61312012-01-24 16:01:51 -08001063 if ( !p ) return 0;
1064 const char* start = p;
1065
1066 // The closing element is the </element> form. It is
1067 // parsed just like a regular element then deleted from
1068 // the DOM.
1069 if ( *p == '/' ) {
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -08001070 closingType = CLOSING;
Lee Thomason67d61312012-01-24 16:01:51 -08001071 ++p;
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001072 }
Lee Thomason67d61312012-01-24 16:01:51 -08001073
Lee Thomason56bdd022012-02-09 18:16:58 -08001074 p = value.ParseName( p );
Lee Thomasond1983222012-02-06 08:41:24 -08001075 if ( value.Empty() ) return 0;
Lee Thomason67d61312012-01-24 16:01:51 -08001076
1077 bool elementClosed=false;
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -08001078 p = ParseAttributes( p );
1079 if ( !p || !*p || closingType )
Lee Thomason67d61312012-01-24 16:01:51 -08001080 return p;
1081
1082 p = XMLNode::ParseDeep( p );
1083 return p;
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001084}
1085
1086
Lee Thomason751da522012-02-10 08:50:51 -08001087bool XMLElement::Accept( XMLVisitor* visitor ) const
1088{
1089 if ( visitor->VisitEnter( *this, rootAttribute ) )
1090 {
1091 for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() )
1092 {
1093 if ( !node->Accept( visitor ) )
1094 break;
1095 }
1096 }
1097 return visitor->VisitExit( *this );
1098
1099}
Lee Thomason56bdd022012-02-09 18:16:58 -08001100
Lee Thomason3f57d272012-01-11 15:30:03 -08001101// --------- XMLDocument ----------- //
Lee Thomason67d61312012-01-24 16:01:51 -08001102XMLDocument::XMLDocument() :
Lee Thomason18d68bd2012-01-26 18:17:26 -08001103 XMLNode( 0 ),
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001104 writeBOM( false ),
U-Lama\Lee560bd472011-12-28 19:42:49 -08001105 charBuffer( 0 )
1106{
Lee Thomason18d68bd2012-01-26 18:17:26 -08001107 document = this; // avoid warning about 'this' in initializer list
U-Lama\Lee560bd472011-12-28 19:42:49 -08001108}
U-Lama\Leee13c3e62011-12-28 14:36:55 -08001109
1110
Lee Thomason3f57d272012-01-11 15:30:03 -08001111XMLDocument::~XMLDocument()
1112{
Lee Thomasond1983222012-02-06 08:41:24 -08001113 ClearChildren();
Lee Thomason18d68bd2012-01-26 18:17:26 -08001114 delete [] charBuffer;
Lee Thomasond1983222012-02-06 08:41:24 -08001115
Lee Thomasonec5a7b42012-02-13 18:16:52 -08001116#if 0
Lee Thomason455c9d42012-02-06 09:14:14 -08001117 textPool.Trace( "text" );
1118 elementPool.Trace( "element" );
1119 commentPool.Trace( "comment" );
1120 attributePool.Trace( "attribute" );
Lee Thomasone9ecdab2012-02-13 18:11:20 -08001121#endif
1122
Lee Thomason455c9d42012-02-06 09:14:14 -08001123 TIXMLASSERT( textPool.CurrentAllocs() == 0 );
1124 TIXMLASSERT( elementPool.CurrentAllocs() == 0 );
1125 TIXMLASSERT( commentPool.CurrentAllocs() == 0 );
1126 TIXMLASSERT( attributePool.CurrentAllocs() == 0 );
Lee Thomason3f57d272012-01-11 15:30:03 -08001127}
1128
1129
Lee Thomason18d68bd2012-01-26 18:17:26 -08001130void XMLDocument::InitDocument()
1131{
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001132 errorID = XML_NO_ERROR;
Lee Thomason18d68bd2012-01-26 18:17:26 -08001133 errorStr1 = 0;
1134 errorStr2 = 0;
1135
1136 delete [] charBuffer;
1137 charBuffer = 0;
1138
1139}
1140
Lee Thomason3f57d272012-01-11 15:30:03 -08001141
Lee Thomason2c85a712012-01-31 08:24:24 -08001142XMLElement* XMLDocument::NewElement( const char* name )
1143{
Lee Thomasond1983222012-02-06 08:41:24 -08001144 XMLElement* ele = new (elementPool.Alloc()) XMLElement( this );
1145 ele->memPool = &elementPool;
Lee Thomason2c85a712012-01-31 08:24:24 -08001146 ele->SetName( name );
1147 return ele;
1148}
1149
1150
Lee Thomason1ff38e02012-02-14 18:18:16 -08001151XMLComment* XMLDocument::NewComment( const char* str )
1152{
1153 XMLComment* comment = new (commentPool.Alloc()) XMLComment( this );
1154 comment->memPool = &commentPool;
1155 comment->SetValue( str );
1156 return comment;
1157}
1158
1159
1160XMLText* XMLDocument::NewText( const char* str )
1161{
U-Stream\Lee09a11c52012-02-17 08:31:16 -08001162 XMLText* text = new (textPool.Alloc()) XMLText( this );
1163 text->memPool = &textPool;
1164 text->SetValue( str );
1165 return text;
Lee Thomason1ff38e02012-02-14 18:18:16 -08001166}
1167
1168
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001169int XMLDocument::LoadFile( const char* filename )
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001170{
1171 ClearChildren();
1172 InitDocument();
1173
1174 FILE* fp = fopen( filename, "rb" );
1175 if ( !fp ) {
1176 SetError( ERROR_FILE_NOT_FOUND, filename, 0 );
1177 return errorID;
1178 }
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001179 LoadFile( fp );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001180 fclose( fp );
1181 return errorID;
1182}
1183
1184
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001185int XMLDocument::LoadFile( FILE* fp )
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001186{
1187 ClearChildren();
1188 InitDocument();
1189
1190 fseek( fp, 0, SEEK_END );
1191 unsigned size = ftell( fp );
1192 fseek( fp, 0, SEEK_SET );
1193
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001194 if ( size == 0 ) {
1195 return errorID;
1196 }
1197
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001198 charBuffer = new char[size+1];
1199 fread( charBuffer, size, 1, fp );
1200 charBuffer[size] = 0;
1201
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001202 const char* p = charBuffer;
1203 p = XMLUtil::SkipWhiteSpace( p );
1204 p = XMLUtil::ReadBOM( p, &writeBOM );
1205 if ( !p || !*p ) {
Lee Thomasond6277762012-02-22 16:00:12 -08001206 SetError( ERROR_EMPTY_DOCUMENT, 0, 0 );
1207 return errorID;
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001208 }
1209
1210 ParseDeep( charBuffer + (p-charBuffer) );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001211 return errorID;
1212}
1213
1214
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001215void XMLDocument::SaveFile( const char* filename )
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001216{
1217 FILE* fp = fopen( filename, "w" );
1218 XMLStreamer stream( fp );
1219 Print( &stream );
1220 fclose( fp );
1221}
1222
Lee Thomason1ff38e02012-02-14 18:18:16 -08001223
Lee Thomason7c913cd2012-01-26 18:32:34 -08001224int XMLDocument::Parse( const char* p )
Lee Thomason3f57d272012-01-11 15:30:03 -08001225{
Lee Thomason18d68bd2012-01-26 18:17:26 -08001226 ClearChildren();
1227 InitDocument();
1228
1229 if ( !p || !*p ) {
Lee Thomasond6277762012-02-22 16:00:12 -08001230 SetError( ERROR_EMPTY_DOCUMENT, 0, 0 );
1231 return errorID;
Lee Thomason18d68bd2012-01-26 18:17:26 -08001232 }
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001233 p = XMLUtil::SkipWhiteSpace( p );
1234 p = XMLUtil::ReadBOM( p, &writeBOM );
1235 if ( !p || !*p ) {
Lee Thomasond6277762012-02-22 16:00:12 -08001236 SetError( ERROR_EMPTY_DOCUMENT, 0, 0 );
1237 return errorID;
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001238 }
1239
Lee Thomason18d68bd2012-01-26 18:17:26 -08001240 size_t len = strlen( p );
1241 charBuffer = new char[ len+1 ];
1242 memcpy( charBuffer, p, len+1 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001243
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001244
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001245 ParseDeep( charBuffer );
Lee Thomason7c913cd2012-01-26 18:32:34 -08001246 return errorID;
Lee Thomason3f57d272012-01-11 15:30:03 -08001247}
1248
1249
Lee Thomason5cae8972012-01-24 18:03:07 -08001250void XMLDocument::Print( XMLStreamer* streamer )
Lee Thomason3f57d272012-01-11 15:30:03 -08001251{
Lee Thomason5cae8972012-01-24 18:03:07 -08001252 XMLStreamer stdStreamer( stdout );
1253 if ( !streamer )
1254 streamer = &stdStreamer;
Lee Thomason751da522012-02-10 08:50:51 -08001255 Accept( streamer );
Lee Thomason3f57d272012-01-11 15:30:03 -08001256}
1257
1258
Lee Thomason67d61312012-01-24 16:01:51 -08001259void XMLDocument::SetError( int error, const char* str1, const char* str2 )
1260{
Lee Thomason18d68bd2012-01-26 18:17:26 -08001261 errorID = error;
Lee Thomason18d68bd2012-01-26 18:17:26 -08001262 errorStr1 = str1;
1263 errorStr2 = str2;
Lee Thomason67d61312012-01-24 16:01:51 -08001264}
1265
Lee Thomason5cae8972012-01-24 18:03:07 -08001266
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001267void XMLDocument::PrintError() const
1268{
1269 if ( errorID ) {
1270 char buf1[20] = { 0 };
1271 char buf2[20] = { 0 };
1272
1273 if ( errorStr1 ) {
1274 strncpy( buf1, errorStr1, 20 );
1275 buf1[19] = 0;
1276 }
1277 if ( errorStr2 ) {
1278 strncpy( buf2, errorStr2, 20 );
1279 buf2[19] = 0;
1280 }
1281
1282 printf( "XMLDocument error id=%d str1=%s str2=%s\n",
1283 errorID, buf1, buf2 );
1284 }
1285}
1286
1287
1288XMLStreamer::XMLStreamer( FILE* file ) :
1289 elementJustOpened( false ),
1290 firstElement( true ),
1291 fp( file ),
1292 depth( 0 ),
1293 textDepth( -1 )
Lee Thomason5cae8972012-01-24 18:03:07 -08001294{
Lee Thomason857b8682012-01-25 17:50:25 -08001295 for( int i=0; i<ENTITY_RANGE; ++i ) {
1296 entityFlag[i] = false;
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001297 restrictedEntityFlag[i] = false;
Lee Thomason857b8682012-01-25 17:50:25 -08001298 }
1299 for( int i=0; i<NUM_ENTITIES; ++i ) {
1300 TIXMLASSERT( entities[i].value < ENTITY_RANGE );
1301 if ( entities[i].value < ENTITY_RANGE ) {
1302 entityFlag[ entities[i].value ] = true;
1303 }
1304 }
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001305 restrictedEntityFlag['&'] = true;
1306 restrictedEntityFlag['<'] = true;
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001307 restrictedEntityFlag['>'] = true; // not required, but consistency is nice
U-Stream\Leeae25a442012-02-17 17:48:16 -08001308 buffer.Push( 0 );
1309}
1310
1311
1312void XMLStreamer::Print( const char* format, ... )
1313{
1314 va_list va;
1315 va_start( va, format );
1316
1317 if ( fp ) {
1318 vfprintf( fp, format, va );
1319 }
1320 else {
1321 // This seems brutally complex. Haven't figured out a better
1322 // way on windows.
1323 #ifdef _MSC_VER
1324 int len = -1;
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001325 int expand = 1000;
U-Stream\Leeae25a442012-02-17 17:48:16 -08001326 while ( len < 0 ) {
1327 len = vsnprintf_s( accumulator.Mem(), accumulator.Capacity(), accumulator.Capacity()-1, format, va );
1328 if ( len < 0 ) {
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001329 accumulator.PushArr( expand );
1330 expand *= 3/2;
U-Stream\Leeae25a442012-02-17 17:48:16 -08001331 }
1332 }
1333 char* p = buffer.PushArr( len ) - 1;
1334 memcpy( p, accumulator.Mem(), len+1 );
1335 #else
1336 int len = vsnprintf( 0, 0, format, va );
1337 char* p = buffer.PushArr( len ) - 1;
1338 vsprintf_s( p, len+1, format, va );
1339 #endif
1340 }
1341 va_end( va );
Lee Thomason5cae8972012-01-24 18:03:07 -08001342}
1343
1344
1345void XMLStreamer::PrintSpace( int depth )
1346{
1347 for( int i=0; i<depth; ++i ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001348 Print( " " );
Lee Thomason5cae8972012-01-24 18:03:07 -08001349 }
1350}
1351
1352
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001353void XMLStreamer::PrintString( const char* p, bool restricted )
Lee Thomason857b8682012-01-25 17:50:25 -08001354{
Lee Thomason951d8832012-01-26 08:47:06 -08001355 // Look for runs of bytes between entities to print.
1356 const char* q = p;
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001357 const bool* flag = restricted ? restrictedEntityFlag : entityFlag;
Lee Thomason857b8682012-01-25 17:50:25 -08001358
Lee Thomason951d8832012-01-26 08:47:06 -08001359 while ( *q ) {
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001360 // Remember, char is sometimes signed. (How many times has that bitten me?)
1361 if ( *q > 0 && *q < ENTITY_RANGE ) {
Lee Thomason951d8832012-01-26 08:47:06 -08001362 // Check for entities. If one is found, flush
1363 // the stream up until the entity, write the
1364 // entity, and keep looking.
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001365 if ( flag[*q] ) {
Lee Thomason951d8832012-01-26 08:47:06 -08001366 while ( p < q ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001367 Print( "%c", *p );
Lee Thomason951d8832012-01-26 08:47:06 -08001368 ++p;
1369 }
1370 for( int i=0; i<NUM_ENTITIES; ++i ) {
1371 if ( entities[i].value == *q ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001372 Print( "&%s;", entities[i].pattern );
Lee Thomason951d8832012-01-26 08:47:06 -08001373 break;
1374 }
1375 }
1376 ++p;
1377 }
1378 }
1379 ++q;
1380 }
1381 // Flush the remaining string. This will be the entire
1382 // string if an entity wasn't found.
1383 if ( q-p > 0 ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001384 Print( "%s", p );
Lee Thomason951d8832012-01-26 08:47:06 -08001385 }
Lee Thomason857b8682012-01-25 17:50:25 -08001386}
1387
U-Stream\Leeae25a442012-02-17 17:48:16 -08001388
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001389void XMLStreamer::PushHeader( bool writeBOM, bool writeDec )
1390{
1391 static const unsigned char bom[] = { TIXML_UTF_LEAD_0, TIXML_UTF_LEAD_1, TIXML_UTF_LEAD_2, 0 };
1392 if ( writeBOM ) {
1393 Print( "%s", bom );
1394 }
1395 if ( writeDec ) {
1396 PushDeclaration( "xml version=\"1.0\"" );
1397 }
1398}
1399
1400
Lee Thomason56bdd022012-02-09 18:16:58 -08001401void XMLStreamer::OpenElement( const char* name )
Lee Thomason5cae8972012-01-24 18:03:07 -08001402{
1403 if ( elementJustOpened ) {
1404 SealElement();
1405 }
Lee Thomason56bdd022012-02-09 18:16:58 -08001406 stack.Push( name );
1407
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001408 if ( textDepth < 0 && !firstElement ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001409 Print( "\n" );
Lee Thomason24767b02012-01-25 17:16:23 -08001410 PrintSpace( depth );
1411 }
Lee Thomason5cae8972012-01-24 18:03:07 -08001412
U-Stream\Leeae25a442012-02-17 17:48:16 -08001413 Print( "<%s", name );
Lee Thomason5cae8972012-01-24 18:03:07 -08001414 elementJustOpened = true;
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001415 firstElement = false;
Lee Thomason5cae8972012-01-24 18:03:07 -08001416 ++depth;
1417}
1418
1419
1420void XMLStreamer::PushAttribute( const char* name, const char* value )
1421{
1422 TIXMLASSERT( elementJustOpened );
U-Stream\Leeae25a442012-02-17 17:48:16 -08001423 Print( " %s=\"", name );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001424 PrintString( value, false );
U-Stream\Leeae25a442012-02-17 17:48:16 -08001425 Print( "\"" );
Lee Thomason5cae8972012-01-24 18:03:07 -08001426}
1427
1428
1429void XMLStreamer::CloseElement()
1430{
1431 --depth;
1432 const char* name = stack.Pop();
Lee Thomason5cae8972012-01-24 18:03:07 -08001433
1434 if ( elementJustOpened ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001435 Print( "/>" );
Lee Thomason5cae8972012-01-24 18:03:07 -08001436 }
1437 else {
Lee Thomason56bdd022012-02-09 18:16:58 -08001438 if ( textDepth < 0 ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001439 Print( "\n" );
Lee Thomason24767b02012-01-25 17:16:23 -08001440 PrintSpace( depth );
1441 }
U-Stream\Leeae25a442012-02-17 17:48:16 -08001442 Print( "</%s>", name );
Lee Thomason5cae8972012-01-24 18:03:07 -08001443 }
Lee Thomason56bdd022012-02-09 18:16:58 -08001444
1445 if ( textDepth == depth )
1446 textDepth = -1;
1447 if ( depth == 0 )
U-Stream\Leeae25a442012-02-17 17:48:16 -08001448 Print( "\n" );
Lee Thomason5cae8972012-01-24 18:03:07 -08001449 elementJustOpened = false;
1450}
1451
1452
1453void XMLStreamer::SealElement()
1454{
1455 elementJustOpened = false;
U-Stream\Leeae25a442012-02-17 17:48:16 -08001456 Print( ">" );
Lee Thomason5cae8972012-01-24 18:03:07 -08001457}
1458
1459
Lee Thomason50f97b22012-02-11 16:33:40 -08001460void XMLStreamer::PushText( const char* text, bool cdata )
Lee Thomason5cae8972012-01-24 18:03:07 -08001461{
Lee Thomason56bdd022012-02-09 18:16:58 -08001462 textDepth = depth-1;
1463
Lee Thomason5cae8972012-01-24 18:03:07 -08001464 if ( elementJustOpened ) {
1465 SealElement();
1466 }
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001467 if ( cdata ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001468 Print( "<![CDATA[" );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001469 Print( "%s", text );
U-Stream\Leeae25a442012-02-17 17:48:16 -08001470 Print( "]]>" );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001471 }
1472 else {
1473 PrintString( text, true );
1474 }
Lee Thomason5cae8972012-01-24 18:03:07 -08001475}
1476
1477
1478void XMLStreamer::PushComment( const char* comment )
1479{
1480 if ( elementJustOpened ) {
1481 SealElement();
1482 }
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001483 if ( textDepth < 0 && !firstElement ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001484 Print( "\n" );
U-Stream\Lee09a11c52012-02-17 08:31:16 -08001485 PrintSpace( depth );
1486 }
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001487 firstElement = false;
U-Stream\Leeae25a442012-02-17 17:48:16 -08001488 Print( "<!--%s-->", comment );
Lee Thomason5cae8972012-01-24 18:03:07 -08001489}
Lee Thomason751da522012-02-10 08:50:51 -08001490
1491
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001492void XMLStreamer::PushDeclaration( const char* value )
1493{
1494 if ( elementJustOpened ) {
1495 SealElement();
1496 }
1497 if ( textDepth < 0 && !firstElement) {
1498 Print( "\n" );
1499 PrintSpace( depth );
1500 }
1501 firstElement = false;
1502 Print( "<?%s?>", value );
1503}
1504
1505
1506void XMLStreamer::PushUnknown( const char* value )
1507{
1508 if ( elementJustOpened ) {
1509 SealElement();
1510 }
1511 if ( textDepth < 0 && !firstElement ) {
1512 Print( "\n" );
1513 PrintSpace( depth );
1514 }
1515 firstElement = false;
1516 Print( "<!%s>", value );
1517}
1518
1519
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001520bool XMLStreamer::VisitEnter( const XMLDocument& doc )
1521{
1522 if ( doc.HasBOM() ) {
1523 PushHeader( true, false );
1524 }
1525 return true;
1526}
1527
1528
Lee Thomason751da522012-02-10 08:50:51 -08001529bool XMLStreamer::VisitEnter( const XMLElement& element, const XMLAttribute* attribute )
1530{
1531 OpenElement( element.Name() );
1532 while ( attribute ) {
1533 PushAttribute( attribute->Name(), attribute->Value() );
1534 attribute = attribute->Next();
1535 }
1536 return true;
1537}
1538
1539
1540bool XMLStreamer::VisitExit( const XMLElement& element )
1541{
1542 CloseElement();
1543 return true;
1544}
1545
1546
1547bool XMLStreamer::Visit( const XMLText& text )
1548{
Lee Thomasond6277762012-02-22 16:00:12 -08001549 PushText( text.Value(), text.CData() );
Lee Thomason751da522012-02-10 08:50:51 -08001550 return true;
1551}
1552
1553
1554bool XMLStreamer::Visit( const XMLComment& comment )
1555{
1556 PushComment( comment.Value() );
1557 return true;
1558}
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001559
1560bool XMLStreamer::Visit( const XMLDeclaration& declaration )
1561{
1562 PushDeclaration( declaration.Value() );
1563 return true;
1564}
1565
1566
1567bool XMLStreamer::Visit( const XMLUnknown& unknown )
1568{
1569 PushUnknown( unknown.Value() );
1570 return true;
1571}
1572
1573