blob: 8ee8ca2e5661a8bab00ad77cb1f2d049a37d74a7 [file] [log] [blame]
Lee Thomason (grinliz)28129862012-02-25 21:11:20 -08001/*
2Original code by Lee Thomason (www.grinninglizard.com)
3
4This software is provided 'as-is', without any express or implied
5warranty. In no event will the authors be held liable for any
6damages arising from the use of this software.
7
8Permission is granted to anyone to use this software for any
9purpose, including commercial applications, and to alter it and
10redistribute it freely, subject to the following restrictions:
11
121. The origin of this software must not be misrepresented; you must
13not claim that you wrote the original software. If you use this
14software in a product, an acknowledgment in the product documentation
15would be appreciated but is not required.
16
172. Altered source versions must be plainly marked as such, and
18must not be misrepresented as being the original software.
19
203. This notice may not be removed or altered from any source
21distribution.
22*/
Lee Thomason (grinliz)9c38d132012-02-24 21:50:50 -080023
U-Lama\Lee560bd472011-12-28 19:42:49 -080024#include "tinyxml2.h"
25
Lee Thomason5ce89412012-03-20 13:23:44 -070026#if 1
27 #include <cstdarg>
28 #include <cstdio>
29 #include <cstdlib>
30 #include <new>
31#else
32 #include <string.h>
33 #include <stdlib.h>
34 #include <stdio.h>
35 #include <ctype.h>
36 #include <new>
37 #include <stdarg.h>
38#endif
U-Lama\Lee560bd472011-12-28 19:42:49 -080039
40using namespace tinyxml2;
41
Lee Thomasone4422302012-01-20 17:59:50 -080042static const char LINE_FEED = (char)0x0a; // all line endings are normalized to LF
Lee Thomasonfde6a752012-01-14 18:08:12 -080043static const char LF = LINE_FEED;
44static const char CARRIAGE_RETURN = (char)0x0d; // CR gets filtered out
45static const char CR = CARRIAGE_RETURN;
Lee Thomasone4422302012-01-20 17:59:50 -080046static const char SINGLE_QUOTE = '\'';
47static const char DOUBLE_QUOTE = '\"';
Lee Thomasonfde6a752012-01-14 18:08:12 -080048
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -080049// Bunch of unicode info at:
50// http://www.unicode.org/faq/utf_bom.html
51// ef bb bf (Microsoft "lead bytes") - designates UTF-8
52
53static const unsigned char TIXML_UTF_LEAD_0 = 0xefU;
54static const unsigned char TIXML_UTF_LEAD_1 = 0xbbU;
55static const unsigned char TIXML_UTF_LEAD_2 = 0xbfU;
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -080056
57
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -080058#define DELETE_NODE( node ) { \
59 if ( node ) { \
60 MemPool* pool = node->memPool; \
61 node->~XMLNode(); \
62 pool->Free( node ); \
63 } \
64}
65#define DELETE_ATTRIBUTE( attrib ) { \
66 if ( attrib ) { \
67 MemPool* pool = attrib->memPool; \
68 attrib->~XMLAttribute(); \
69 pool->Free( attrib ); \
70 } \
71}
Lee Thomason43f59302012-02-06 18:18:11 -080072
Lee Thomason8ee79892012-01-25 17:44:30 -080073struct Entity {
74 const char* pattern;
75 int length;
76 char value;
77};
78
79static const int NUM_ENTITIES = 5;
80static const Entity entities[NUM_ENTITIES] =
81{
Lee Thomason18d68bd2012-01-26 18:17:26 -080082 { "quot", 4, DOUBLE_QUOTE },
Lee Thomason8ee79892012-01-25 17:44:30 -080083 { "amp", 3, '&' },
Lee Thomason18d68bd2012-01-26 18:17:26 -080084 { "apos", 4, SINGLE_QUOTE },
Lee Thomason8ee79892012-01-25 17:44:30 -080085 { "lt", 2, '<' },
86 { "gt", 2, '>' }
87};
88
Lee Thomasonfde6a752012-01-14 18:08:12 -080089
Lee Thomason1a1d4a72012-02-15 09:09:25 -080090StrPair::~StrPair()
91{
92 Reset();
93}
94
95
96void StrPair::Reset()
97{
98 if ( flags & NEEDS_DELETE ) {
99 delete [] start;
100 }
101 flags = 0;
102 start = 0;
103 end = 0;
104}
105
106
107void StrPair::SetStr( const char* str, int flags )
108{
109 Reset();
110 size_t len = strlen( str );
111 start = new char[ len+1 ];
U-Stream\Lee09a11c52012-02-17 08:31:16 -0800112 memcpy( start, str, len+1 );
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800113 end = start + len;
114 this->flags = flags | NEEDS_DELETE;
115}
116
117
118char* StrPair::ParseText( char* p, const char* endTag, int strFlags )
119{
120 TIXMLASSERT( endTag && *endTag );
121
122 char* start = p; // fixme: hides a member
123 char endChar = *endTag;
124 int length = strlen( endTag );
125
126 // Inner loop of text parsing.
127 while ( *p ) {
128 if ( *p == endChar && strncmp( p, endTag, length ) == 0 ) {
129 Set( start, p, strFlags );
130 return p + length;
131 }
132 ++p;
133 }
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800134 return 0;
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800135}
136
137
138char* StrPair::ParseName( char* p )
139{
140 char* start = p;
141
142 start = p;
143 if ( !start || !(*start) ) {
144 return 0;
145 }
146
147 if ( !XMLUtil::IsAlpha( *p ) ) {
148 return 0;
149 }
150
151 while( *p && (
152 XMLUtil::IsAlphaNum( (unsigned char) *p )
153 || *p == '_'
154 || *p == '-'
155 || *p == '.'
156 || *p == ':' ))
157 {
158 ++p;
159 }
160
161 if ( p > start ) {
162 Set( start, p, 0 );
163 return p;
164 }
165 return 0;
166}
167
168
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800169
Lee Thomasone4422302012-01-20 17:59:50 -0800170const char* StrPair::GetStr()
171{
172 if ( flags & NEEDS_FLUSH ) {
173 *end = 0;
Lee Thomason8ee79892012-01-25 17:44:30 -0800174 flags ^= NEEDS_FLUSH;
Lee Thomasone4422302012-01-20 17:59:50 -0800175
Lee Thomason8ee79892012-01-25 17:44:30 -0800176 if ( flags ) {
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800177 char* p = start; // the read pointer
178 char* q = start; // the write pointer
Lee Thomasone4422302012-01-20 17:59:50 -0800179
180 while( p < end ) {
Lee Thomason8ee79892012-01-25 17:44:30 -0800181 if ( (flags & NEEDS_NEWLINE_NORMALIZATION) && *p == CR ) {
Lee Thomasone4422302012-01-20 17:59:50 -0800182 // CR-LF pair becomes LF
183 // CR alone becomes LF
184 // LF-CR becomes LF
185 if ( *(p+1) == LF ) {
186 p += 2;
187 }
188 else {
189 ++p;
190 }
Lee Thomasone9ecdab2012-02-13 18:11:20 -0800191 *q++ = LF;
Lee Thomasone4422302012-01-20 17:59:50 -0800192 }
Lee Thomason8ee79892012-01-25 17:44:30 -0800193 else if ( (flags & NEEDS_NEWLINE_NORMALIZATION) && *p == LF ) {
Lee Thomasone4422302012-01-20 17:59:50 -0800194 if ( *(p+1) == CR ) {
195 p += 2;
196 }
197 else {
198 ++p;
199 }
Lee Thomasone9ecdab2012-02-13 18:11:20 -0800200 *q++ = LF;
Lee Thomasone4422302012-01-20 17:59:50 -0800201 }
Lee Thomason8ee79892012-01-25 17:44:30 -0800202 else if ( (flags & NEEDS_ENTITY_PROCESSING) && *p == '&' ) {
203 int i=0;
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800204
205 // Entities handled by tinyXML2:
206 // - special entities in the entity table [in/out]
207 // - numeric character reference [in]
208 // &#20013; or &#x4e2d;
209
210 if ( *(p+1) == '#' ) {
211 char buf[10] = { 0 };
212 int len;
213 p = const_cast<char*>( XMLUtil::GetCharacterRef( p, buf, &len ) );
214 for( int i=0; i<len; ++i ) {
215 *q++ = buf[i];
Lee Thomason8ee79892012-01-25 17:44:30 -0800216 }
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800217 TIXMLASSERT( q <= p );
Lee Thomason8ee79892012-01-25 17:44:30 -0800218 }
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800219 else {
220 for( i=0; i<NUM_ENTITIES; ++i ) {
221 if ( strncmp( p+1, entities[i].pattern, entities[i].length ) == 0
222 && *(p+entities[i].length+1) == ';' )
223 {
224 // Found an entity convert;
225 *q = entities[i].value;
226 ++q;
227 p += entities[i].length + 2;
228 break;
229 }
230 }
231 if ( i == NUM_ENTITIES ) {
232 // fixme: treat as error?
233 ++p;
234 ++q;
235 }
Lee Thomason8ee79892012-01-25 17:44:30 -0800236 }
237 }
Lee Thomasone4422302012-01-20 17:59:50 -0800238 else {
239 *q = *p;
240 ++p;
Lee Thomasonec975ce2012-01-23 11:42:06 -0800241 ++q;
Lee Thomasone4422302012-01-20 17:59:50 -0800242 }
243 }
Lee Thomason8ee79892012-01-25 17:44:30 -0800244 *q = 0;
Lee Thomasone4422302012-01-20 17:59:50 -0800245 }
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800246 flags = (flags & NEEDS_DELETE);
Lee Thomasone4422302012-01-20 17:59:50 -0800247 }
248 return start;
249}
250
Lee Thomason2c85a712012-01-31 08:24:24 -0800251
Lee Thomasone4422302012-01-20 17:59:50 -0800252
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800253
Lee Thomason56bdd022012-02-09 18:16:58 -0800254// --------- XMLUtil ----------- //
Lee Thomasond1983222012-02-06 08:41:24 -0800255
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800256const char* XMLUtil::ReadBOM( const char* p, bool* bom )
257{
258 *bom = false;
259 const unsigned char* pu = reinterpret_cast<const unsigned char*>(p);
260 // Check for BOM:
261 if ( *(pu+0) == TIXML_UTF_LEAD_0
262 && *(pu+1) == TIXML_UTF_LEAD_1
263 && *(pu+2) == TIXML_UTF_LEAD_2 )
264 {
265 *bom = true;
266 p += 3;
267 }
268 return p;
269}
270
271
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800272void XMLUtil::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length )
273{
274 const unsigned long BYTE_MASK = 0xBF;
275 const unsigned long BYTE_MARK = 0x80;
276 const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
277
278 if (input < 0x80)
279 *length = 1;
280 else if ( input < 0x800 )
281 *length = 2;
282 else if ( input < 0x10000 )
283 *length = 3;
284 else if ( input < 0x200000 )
285 *length = 4;
286 else
287 { *length = 0; return; } // This code won't covert this correctly anyway.
288
289 output += *length;
290
291 // Scary scary fall throughs.
292 switch (*length)
293 {
294 case 4:
295 --output;
296 *output = (char)((input | BYTE_MARK) & BYTE_MASK);
297 input >>= 6;
298 case 3:
299 --output;
300 *output = (char)((input | BYTE_MARK) & BYTE_MASK);
301 input >>= 6;
302 case 2:
303 --output;
304 *output = (char)((input | BYTE_MARK) & BYTE_MASK);
305 input >>= 6;
306 case 1:
307 --output;
308 *output = (char)(input | FIRST_BYTE_MARK[*length]);
309 }
310}
311
312
313const char* XMLUtil::GetCharacterRef( const char* p, char* value, int* length )
314{
315 // Presume an entity, and pull it out.
316 *length = 0;
317
318 if ( *(p+1) == '#' && *(p+2) )
319 {
320 unsigned long ucs = 0;
Lee Thomason (grinliz)7ca55582012-03-07 21:54:57 -0800321 int delta = 0;
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800322 unsigned mult = 1;
323
324 if ( *(p+2) == 'x' )
325 {
326 // Hexadecimal.
327 if ( !*(p+3) ) return 0;
328
329 const char* q = p+3;
330 q = strchr( q, ';' );
331
332 if ( !q || !*q ) return 0;
333
Lee Thomason (grinliz)7ca55582012-03-07 21:54:57 -0800334 delta = (q-p);
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800335 --q;
336
337 while ( *q != 'x' )
338 {
339 if ( *q >= '0' && *q <= '9' )
340 ucs += mult * (*q - '0');
341 else if ( *q >= 'a' && *q <= 'f' )
342 ucs += mult * (*q - 'a' + 10);
343 else if ( *q >= 'A' && *q <= 'F' )
344 ucs += mult * (*q - 'A' + 10 );
345 else
346 return 0;
347 mult *= 16;
348 --q;
349 }
350 }
351 else
352 {
353 // Decimal.
354 if ( !*(p+2) ) return 0;
355
356 const char* q = p+2;
357 q = strchr( q, ';' );
358
359 if ( !q || !*q ) return 0;
360
361 delta = q-p;
362 --q;
363
364 while ( *q != '#' )
365 {
366 if ( *q >= '0' && *q <= '9' )
367 ucs += mult * (*q - '0');
368 else
369 return 0;
370 mult *= 10;
371 --q;
372 }
373 }
374 // convert the UCS to UTF-8
375 ConvertUTF32ToUTF8( ucs, value, length );
376 return p + delta + 1;
377 }
378 return p+1;
379}
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800380
381
Lee Thomasond1983222012-02-06 08:41:24 -0800382char* XMLDocument::Identify( char* p, XMLNode** node )
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800383{
384 XMLNode* returnNode = 0;
Lee Thomason5492a1c2012-01-23 15:32:10 -0800385 char* start = p;
Lee Thomason56bdd022012-02-09 18:16:58 -0800386 p = XMLUtil::SkipWhiteSpace( p );
Lee Thomason5492a1c2012-01-23 15:32:10 -0800387 if( !p || !*p )
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800388 {
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800389 return p;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800390 }
391
392 // What is this thing?
393 // - Elements start with a letter or underscore, but xml is reserved.
394 // - Comments: <!--
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800395 // - Decleration: <?
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800396 // - Everthing else is unknown to tinyxml.
397 //
398
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800399 static const char* xmlHeader = { "<?" };
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800400 static const char* commentHeader = { "<!--" };
401 static const char* dtdHeader = { "<!" };
402 static const char* cdataHeader = { "<![CDATA[" };
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800403 static const char* elementHeader = { "<" }; // and a header for everything else; check last.
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800404
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800405 static const int xmlHeaderLen = 2;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800406 static const int commentHeaderLen = 4;
407 static const int dtdHeaderLen = 2;
408 static const int cdataHeaderLen = 9;
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800409 static const int elementHeaderLen = 1;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800410
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800411#if defined(_MSC_VER)
Lee Thomason (grinliz)9b093cc2012-02-25 21:30:18 -0800412#pragma warning ( push )
413#pragma warning ( disable : 4127 )
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800414#endif
Lee Thomason50f97b22012-02-11 16:33:40 -0800415 TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLUnknown ) ); // use same memory pool
416 TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLDeclaration ) ); // use same memory pool
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800417#if defined(_MSC_VER)
Lee Thomason (grinliz)9b093cc2012-02-25 21:30:18 -0800418#pragma warning (pop)
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800419#endif
Lee Thomason50f97b22012-02-11 16:33:40 -0800420 if ( XMLUtil::StringEqual( p, xmlHeader, xmlHeaderLen ) ) {
421 returnNode = new (commentPool.Alloc()) XMLDeclaration( this );
422 returnNode->memPool = &commentPool;
423 p += xmlHeaderLen;
424 }
425 else if ( XMLUtil::StringEqual( p, commentHeader, commentHeaderLen ) ) {
Lee Thomasond1983222012-02-06 08:41:24 -0800426 returnNode = new (commentPool.Alloc()) XMLComment( this );
427 returnNode->memPool = &commentPool;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800428 p += commentHeaderLen;
429 }
Lee Thomason50f97b22012-02-11 16:33:40 -0800430 else if ( XMLUtil::StringEqual( p, cdataHeader, cdataHeaderLen ) ) {
431 XMLText* text = new (textPool.Alloc()) XMLText( this );
432 returnNode = text;
433 returnNode->memPool = &textPool;
434 p += cdataHeaderLen;
435 text->SetCData( true );
436 }
437 else if ( XMLUtil::StringEqual( p, dtdHeader, dtdHeaderLen ) ) {
438 returnNode = new (commentPool.Alloc()) XMLUnknown( this );
439 returnNode->memPool = &commentPool;
440 p += dtdHeaderLen;
441 }
Lee Thomason56bdd022012-02-09 18:16:58 -0800442 else if ( XMLUtil::StringEqual( p, elementHeader, elementHeaderLen ) ) {
Lee Thomasond1983222012-02-06 08:41:24 -0800443 returnNode = new (elementPool.Alloc()) XMLElement( this );
444 returnNode->memPool = &elementPool;
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800445 p += elementHeaderLen;
446 }
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800447 else {
Lee Thomasond1983222012-02-06 08:41:24 -0800448 returnNode = new (textPool.Alloc()) XMLText( this );
449 returnNode->memPool = &textPool;
Lee Thomason5492a1c2012-01-23 15:32:10 -0800450 p = start; // Back it up, all the text counts.
451 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800452
453 *node = returnNode;
454 return p;
455}
456
457
Lee Thomason751da522012-02-10 08:50:51 -0800458bool XMLDocument::Accept( XMLVisitor* visitor ) const
459{
460 if ( visitor->VisitEnter( *this ) )
461 {
462 for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() )
463 {
464 if ( !node->Accept( visitor ) )
465 break;
466 }
467 }
468 return visitor->VisitExit( *this );
469}
Lee Thomason56bdd022012-02-09 18:16:58 -0800470
471
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800472// --------- XMLNode ----------- //
473
474XMLNode::XMLNode( XMLDocument* doc ) :
475 document( doc ),
476 parent( 0 ),
477 firstChild( 0 ), lastChild( 0 ),
478 prev( 0 ), next( 0 )
479{
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800480}
481
482
483XMLNode::~XMLNode()
484{
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -0800485 DeleteChildren();
Lee Thomason7c913cd2012-01-26 18:32:34 -0800486 if ( parent ) {
487 parent->Unlink( this );
488 }
Lee Thomason18d68bd2012-01-26 18:17:26 -0800489}
490
491
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800492void XMLNode::SetValue( const char* str, bool staticMem )
493{
494 if ( staticMem )
495 value.SetInternedStr( str );
496 else
497 value.SetStr( str );
498}
499
500
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -0800501void XMLNode::DeleteChildren()
Lee Thomason18d68bd2012-01-26 18:17:26 -0800502{
Lee Thomasond923c672012-01-23 08:44:25 -0800503 while( firstChild ) {
504 XMLNode* node = firstChild;
505 Unlink( node );
Lee Thomasond1983222012-02-06 08:41:24 -0800506
Lee Thomason43f59302012-02-06 18:18:11 -0800507 DELETE_NODE( node );
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800508 }
Lee Thomason18d68bd2012-01-26 18:17:26 -0800509 firstChild = lastChild = 0;
Lee Thomasond923c672012-01-23 08:44:25 -0800510}
511
512
513void XMLNode::Unlink( XMLNode* child )
514{
515 TIXMLASSERT( child->parent == this );
516 if ( child == firstChild )
517 firstChild = firstChild->next;
518 if ( child == lastChild )
519 lastChild = lastChild->prev;
520
521 if ( child->prev ) {
522 child->prev->next = child->next;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800523 }
Lee Thomasond923c672012-01-23 08:44:25 -0800524 if ( child->next ) {
525 child->next->prev = child->prev;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800526 }
Lee Thomasond923c672012-01-23 08:44:25 -0800527 child->parent = 0;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800528}
529
530
U-Stream\Leeae25a442012-02-17 17:48:16 -0800531void XMLNode::DeleteChild( XMLNode* node )
532{
533 TIXMLASSERT( node->parent == this );
534 DELETE_NODE( node );
535}
536
537
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800538XMLNode* XMLNode::InsertEndChild( XMLNode* addThis )
539{
540 if ( lastChild ) {
541 TIXMLASSERT( firstChild );
542 TIXMLASSERT( lastChild->next == 0 );
543 lastChild->next = addThis;
544 addThis->prev = lastChild;
545 lastChild = addThis;
546
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800547 addThis->next = 0;
548 }
549 else {
550 TIXMLASSERT( firstChild == 0 );
551 firstChild = lastChild = addThis;
552
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800553 addThis->prev = 0;
554 addThis->next = 0;
555 }
U-Stream\Lee09a11c52012-02-17 08:31:16 -0800556 addThis->parent = this;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800557 return addThis;
558}
559
560
Lee Thomason1ff38e02012-02-14 18:18:16 -0800561XMLNode* XMLNode::InsertFirstChild( XMLNode* addThis )
562{
563 if ( firstChild ) {
564 TIXMLASSERT( lastChild );
565 TIXMLASSERT( firstChild->prev == 0 );
566
567 firstChild->prev = addThis;
568 addThis->next = firstChild;
569 firstChild = addThis;
570
Lee Thomason1ff38e02012-02-14 18:18:16 -0800571 addThis->prev = 0;
572 }
573 else {
574 TIXMLASSERT( lastChild == 0 );
575 firstChild = lastChild = addThis;
576
Lee Thomason1ff38e02012-02-14 18:18:16 -0800577 addThis->prev = 0;
578 addThis->next = 0;
579 }
U-Stream\Lee09a11c52012-02-17 08:31:16 -0800580 addThis->parent = this;
Lee Thomason1ff38e02012-02-14 18:18:16 -0800581 return addThis;
582}
583
584
585XMLNode* XMLNode::InsertAfterChild( XMLNode* afterThis, XMLNode* addThis )
586{
587 TIXMLASSERT( afterThis->parent == this );
588 if ( afterThis->parent != this )
589 return 0;
590
591 if ( afterThis->next == 0 ) {
592 // The last node or the only node.
593 return InsertEndChild( addThis );
594 }
595 addThis->prev = afterThis;
596 addThis->next = afterThis->next;
597 afterThis->next->prev = addThis;
598 afterThis->next = addThis;
U-Stream\Lee09a11c52012-02-17 08:31:16 -0800599 addThis->parent = this;
Lee Thomason1ff38e02012-02-14 18:18:16 -0800600 return addThis;
601}
602
603
604
605
Lee Thomason56bdd022012-02-09 18:16:58 -0800606const XMLElement* XMLNode::FirstChildElement( const char* value ) const
Lee Thomason2c85a712012-01-31 08:24:24 -0800607{
608 for( XMLNode* node=firstChild; node; node=node->next ) {
609 XMLElement* element = node->ToElement();
610 if ( element ) {
Lee Thomason56bdd022012-02-09 18:16:58 -0800611 if ( !value || XMLUtil::StringEqual( element->Name(), value ) ) {
Lee Thomason2c85a712012-01-31 08:24:24 -0800612 return element;
613 }
614 }
615 }
616 return 0;
617}
618
619
Lee Thomason56bdd022012-02-09 18:16:58 -0800620const XMLElement* XMLNode::LastChildElement( const char* value ) const
621{
622 for( XMLNode* node=lastChild; node; node=node->prev ) {
623 XMLElement* element = node->ToElement();
624 if ( element ) {
625 if ( !value || XMLUtil::StringEqual( element->Name(), value ) ) {
626 return element;
627 }
628 }
629 }
630 return 0;
631}
632
633
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800634const XMLElement* XMLNode::NextSiblingElement( const char* value ) const
635{
636 for( XMLNode* element=this->next; element; element = element->next ) {
637 if ( element->ToElement()
638 && (!value || XMLUtil::StringEqual( value, element->Value() )))
639 {
640 return element->ToElement();
641 }
642 }
643 return 0;
644}
645
646
647const XMLElement* XMLNode::PreviousSiblingElement( const char* value ) const
648{
649 for( XMLNode* element=this->prev; element; element = element->prev ) {
650 if ( element->ToElement()
651 && (!value || XMLUtil::StringEqual( value, element->Value() )))
652 {
653 return element->ToElement();
654 }
655 }
656 return 0;
657}
658
659
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800660char* XMLNode::ParseDeep( char* p, StrPair* parentEnd )
Lee Thomason67d61312012-01-24 16:01:51 -0800661{
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800662 // This is a recursive method, but thinking about it "at the current level"
663 // it is a pretty simple flat list:
664 // <foo/>
665 // <!-- comment -->
666 //
667 // With a special case:
668 // <foo>
669 // </foo>
670 // <!-- comment -->
671 //
672 // Where the closing element (/foo) *must* be the next thing after the opening
673 // element, and the names must match. BUT the tricky bit is that the closing
674 // element will be read by the child.
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800675 //
676 // 'endTag' is the end tag for this node, it is returned by a call to a child.
677 // 'parentEnd' is the end tag for the parent, which is filled in and returned.
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800678
Lee Thomason67d61312012-01-24 16:01:51 -0800679 while( p && *p ) {
680 XMLNode* node = 0;
Lee Thomasond6277762012-02-22 16:00:12 -0800681
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800682 p = document->Identify( p, &node );
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800683 if ( p == 0 || node == 0 ) {
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800684 break;
685 }
686
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800687 StrPair endTag;
688 p = node->ParseDeep( p, &endTag );
689 if ( !p ) {
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800690 DELETE_NODE( node );
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800691 node = 0;
Lee Thomason (grinliz)784607f2012-02-24 16:23:40 -0800692 if ( !document->Error() ) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -0700693 document->SetError( XML_ERROR_PARSING, 0, 0 );
Lee Thomason (grinliz)784607f2012-02-24 16:23:40 -0800694 }
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800695 break;
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800696 }
697
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800698 // We read the end tag. Return it to the parent.
699 if ( node->ToElement() && node->ToElement()->ClosingType() == XMLElement::CLOSING ) {
700 if ( parentEnd ) {
701 *parentEnd = ((XMLElement*)node)->value;
Lee Thomason67d61312012-01-24 16:01:51 -0800702 }
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800703 DELETE_NODE( node );
704 return p;
705 }
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800706
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800707 // Handle an end tag returned to this level.
708 // And handle a bunch of annoying errors.
709 XMLElement* ele = node->ToElement();
710 if ( ele ) {
711 if ( endTag.Empty() && ele->ClosingType() == XMLElement::OPEN ) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -0700712 document->SetError( XML_ERROR_MISMATCHED_ELEMENT, node->Value(), 0 );
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800713 p = 0;
714 }
715 else if ( !endTag.Empty() && ele->ClosingType() != XMLElement::OPEN ) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -0700716 document->SetError( XML_ERROR_MISMATCHED_ELEMENT, node->Value(), 0 );
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800717 p = 0;
718 }
719 else if ( !endTag.Empty() ) {
720 if ( !XMLUtil::StringEqual( endTag.GetStr(), node->Value() )) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -0700721 document->SetError( XML_ERROR_MISMATCHED_ELEMENT, node->Value(), 0 );
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800722 p = 0;
723 }
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800724 }
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800725 }
726 if ( p == 0 ) {
727 DELETE_NODE( node );
728 node = 0;
729 }
730 if ( node ) {
731 this->InsertEndChild( node );
Lee Thomason67d61312012-01-24 16:01:51 -0800732 }
733 }
734 return 0;
735}
736
Lee Thomason5492a1c2012-01-23 15:32:10 -0800737// --------- XMLText ---------- //
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800738char* XMLText::ParseDeep( char* p, StrPair* )
Lee Thomason5492a1c2012-01-23 15:32:10 -0800739{
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800740 const char* start = p;
Lee Thomason50f97b22012-02-11 16:33:40 -0800741 if ( this->CData() ) {
742 p = value.ParseText( p, "]]>", StrPair::NEEDS_NEWLINE_NORMALIZATION );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800743 if ( !p ) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -0700744 document->SetError( XML_ERROR_PARSING_CDATA, start, 0 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800745 }
Lee Thomason50f97b22012-02-11 16:33:40 -0800746 return p;
747 }
748 else {
Lee Thomason6f381b72012-03-02 12:59:39 -0800749 p = value.ParseText( p, "<", document->ProcessEntities() ? StrPair::TEXT_ELEMENT : StrPair::TEXT_ELEMENT_LEAVE_ENTITIES );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800750 if ( !p ) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -0700751 document->SetError( XML_ERROR_PARSING_TEXT, start, 0 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800752 }
Lee Thomason50f97b22012-02-11 16:33:40 -0800753 if ( p && *p ) {
754 return p-1;
755 }
Lee Thomason5492a1c2012-01-23 15:32:10 -0800756 }
757 return 0;
758}
759
760
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800761XMLNode* XMLText::ShallowClone( XMLDocument* doc ) const
762{
763 if ( !doc ) {
764 doc = document;
765 }
766 XMLText* text = doc->NewText( Value() ); // fixme: this will always allocate memory. Intern?
767 text->SetCData( this->CData() );
768 return text;
769}
770
771
772bool XMLText::ShallowEqual( const XMLNode* compare ) const
773{
774 return ( compare->ToText() && XMLUtil::StringEqual( compare->ToText()->Value(), Value() ));
775}
776
777
Lee Thomason56bdd022012-02-09 18:16:58 -0800778bool XMLText::Accept( XMLVisitor* visitor ) const
779{
780 return visitor->Visit( *this );
781}
782
783
Lee Thomason3f57d272012-01-11 15:30:03 -0800784// --------- XMLComment ---------- //
785
Lee Thomasone4422302012-01-20 17:59:50 -0800786XMLComment::XMLComment( XMLDocument* doc ) : XMLNode( doc )
Lee Thomason3f57d272012-01-11 15:30:03 -0800787{
788}
789
790
Lee Thomasonce0763e2012-01-11 15:43:54 -0800791XMLComment::~XMLComment()
Lee Thomason3f57d272012-01-11 15:30:03 -0800792{
Lee Thomasond923c672012-01-23 08:44:25 -0800793 //printf( "~XMLComment\n" );
Lee Thomason3f57d272012-01-11 15:30:03 -0800794}
795
796
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800797char* XMLComment::ParseDeep( char* p, StrPair* )
Lee Thomason3f57d272012-01-11 15:30:03 -0800798{
799 // Comment parses as text.
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800800 const char* start = p;
801 p = value.ParseText( p, "-->", StrPair::COMMENT );
802 if ( p == 0 ) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -0700803 document->SetError( XML_ERROR_PARSING_COMMENT, start, 0 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800804 }
805 return p;
U-Lama\Lee4cee6112011-12-31 14:58:18 -0800806}
807
808
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800809XMLNode* XMLComment::ShallowClone( XMLDocument* doc ) const
810{
811 if ( !doc ) {
812 doc = document;
813 }
814 XMLComment* comment = doc->NewComment( Value() ); // fixme: this will always allocate memory. Intern?
815 return comment;
816}
817
818
819bool XMLComment::ShallowEqual( const XMLNode* compare ) const
820{
821 return ( compare->ToComment() && XMLUtil::StringEqual( compare->ToComment()->Value(), Value() ));
822}
823
824
Lee Thomason751da522012-02-10 08:50:51 -0800825bool XMLComment::Accept( XMLVisitor* visitor ) const
826{
827 return visitor->Visit( *this );
828}
Lee Thomason56bdd022012-02-09 18:16:58 -0800829
830
Lee Thomason50f97b22012-02-11 16:33:40 -0800831// --------- XMLDeclaration ---------- //
832
833XMLDeclaration::XMLDeclaration( XMLDocument* doc ) : XMLNode( doc )
834{
835}
836
837
838XMLDeclaration::~XMLDeclaration()
839{
840 //printf( "~XMLDeclaration\n" );
841}
842
843
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800844char* XMLDeclaration::ParseDeep( char* p, StrPair* )
Lee Thomason50f97b22012-02-11 16:33:40 -0800845{
846 // Declaration parses as text.
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800847 const char* start = p;
848 p = value.ParseText( p, "?>", StrPair::NEEDS_NEWLINE_NORMALIZATION );
849 if ( p == 0 ) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -0700850 document->SetError( XML_ERROR_PARSING_DECLARATION, start, 0 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800851 }
852 return p;
Lee Thomason50f97b22012-02-11 16:33:40 -0800853}
854
855
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800856XMLNode* XMLDeclaration::ShallowClone( XMLDocument* doc ) const
857{
858 if ( !doc ) {
859 doc = document;
860 }
861 XMLDeclaration* dec = doc->NewDeclaration( Value() ); // fixme: this will always allocate memory. Intern?
862 return dec;
863}
864
865
866bool XMLDeclaration::ShallowEqual( const XMLNode* compare ) const
867{
868 return ( compare->ToDeclaration() && XMLUtil::StringEqual( compare->ToDeclaration()->Value(), Value() ));
869}
870
871
872
Lee Thomason50f97b22012-02-11 16:33:40 -0800873bool XMLDeclaration::Accept( XMLVisitor* visitor ) const
874{
875 return visitor->Visit( *this );
876}
877
878// --------- XMLUnknown ---------- //
879
880XMLUnknown::XMLUnknown( XMLDocument* doc ) : XMLNode( doc )
881{
882}
883
884
885XMLUnknown::~XMLUnknown()
886{
887}
888
889
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800890char* XMLUnknown::ParseDeep( char* p, StrPair* )
Lee Thomason50f97b22012-02-11 16:33:40 -0800891{
892 // Unknown parses as text.
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800893 const char* start = p;
894
895 p = value.ParseText( p, ">", StrPair::NEEDS_NEWLINE_NORMALIZATION );
896 if ( !p ) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -0700897 document->SetError( XML_ERROR_PARSING_UNKNOWN, start, 0 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800898 }
899 return p;
Lee Thomason50f97b22012-02-11 16:33:40 -0800900}
901
902
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800903XMLNode* XMLUnknown::ShallowClone( XMLDocument* doc ) const
904{
905 if ( !doc ) {
906 doc = document;
907 }
908 XMLUnknown* text = doc->NewUnknown( Value() ); // fixme: this will always allocate memory. Intern?
909 return text;
910}
911
912
913bool XMLUnknown::ShallowEqual( const XMLNode* compare ) const
914{
915 return ( compare->ToUnknown() && XMLUtil::StringEqual( compare->ToUnknown()->Value(), Value() ));
916}
917
918
Lee Thomason50f97b22012-02-11 16:33:40 -0800919bool XMLUnknown::Accept( XMLVisitor* visitor ) const
920{
921 return visitor->Visit( *this );
922}
923
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800924// --------- XMLAttribute ---------- //
Lee Thomason6f381b72012-03-02 12:59:39 -0800925char* XMLAttribute::ParseDeep( char* p, bool processEntities )
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800926{
Lee Thomason56bdd022012-02-09 18:16:58 -0800927 p = name.ParseText( p, "=", StrPair::ATTRIBUTE_NAME );
Lee Thomason22aead12012-01-23 13:29:35 -0800928 if ( !p || !*p ) return 0;
929
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800930 char endTag[2] = { *p, 0 };
931 ++p;
Lee Thomason6f381b72012-03-02 12:59:39 -0800932 p = value.ParseText( p, endTag, processEntities ? StrPair::ATTRIBUTE_VALUE : StrPair::ATTRIBUTE_VALUE_LEAVE_ENTITIES );
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800933 //if ( value.Empty() ) return 0;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800934 return p;
935}
936
937
U-Stream\Lee09a11c52012-02-17 08:31:16 -0800938void XMLAttribute::SetName( const char* n )
939{
940 name.SetStr( n );
941}
942
943
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800944int XMLAttribute::QueryIntValue( int* value ) const
Lee Thomason1ff38e02012-02-14 18:18:16 -0800945{
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800946 if ( TIXML_SSCANF( Value(), "%d", value ) == 1 )
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800947 return XML_NO_ERROR;
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -0700948 return XML_WRONG_ATTRIBUTE_TYPE;
Lee Thomason1ff38e02012-02-14 18:18:16 -0800949}
950
951
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800952int XMLAttribute::QueryUnsignedValue( unsigned int* value ) const
Lee Thomason1ff38e02012-02-14 18:18:16 -0800953{
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800954 if ( TIXML_SSCANF( Value(), "%u", value ) == 1 )
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800955 return XML_NO_ERROR;
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -0700956 return XML_WRONG_ATTRIBUTE_TYPE;
Lee Thomason1ff38e02012-02-14 18:18:16 -0800957}
958
959
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800960int XMLAttribute::QueryBoolValue( bool* value ) const
Lee Thomason1ff38e02012-02-14 18:18:16 -0800961{
962 int ival = -1;
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800963 QueryIntValue( &ival );
Lee Thomason1ff38e02012-02-14 18:18:16 -0800964
965 if ( ival > 0 || XMLUtil::StringEqual( Value(), "true" ) ) {
966 *value = true;
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800967 return XML_NO_ERROR;
Lee Thomason1ff38e02012-02-14 18:18:16 -0800968 }
969 else if ( ival == 0 || XMLUtil::StringEqual( Value(), "false" ) ) {
970 *value = false;
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800971 return XML_NO_ERROR;
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800972 }
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -0700973 return XML_WRONG_ATTRIBUTE_TYPE;
Lee Thomason1ff38e02012-02-14 18:18:16 -0800974}
975
976
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800977int XMLAttribute::QueryDoubleValue( double* value ) const
Lee Thomason1ff38e02012-02-14 18:18:16 -0800978{
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800979 if ( TIXML_SSCANF( Value(), "%lf", value ) == 1 )
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800980 return XML_NO_ERROR;
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -0700981 return XML_WRONG_ATTRIBUTE_TYPE;
Lee Thomason1ff38e02012-02-14 18:18:16 -0800982}
983
984
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800985int XMLAttribute::QueryFloatValue( float* value ) const
Lee Thomason1ff38e02012-02-14 18:18:16 -0800986{
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800987 if ( TIXML_SSCANF( Value(), "%f", value ) == 1 )
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800988 return XML_NO_ERROR;
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -0700989 return XML_WRONG_ATTRIBUTE_TYPE;
Lee Thomason1ff38e02012-02-14 18:18:16 -0800990}
991
992
993void XMLAttribute::SetAttribute( const char* v )
994{
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800995 value.SetStr( v );
Lee Thomason1ff38e02012-02-14 18:18:16 -0800996}
997
998
Lee Thomason1ff38e02012-02-14 18:18:16 -0800999void XMLAttribute::SetAttribute( int v )
1000{
1001 char buf[BUF_SIZE];
Lee Thomason1a1d4a72012-02-15 09:09:25 -08001002 TIXML_SNPRINTF( buf, BUF_SIZE-1, "%d", v );
1003 value.SetStr( buf );
Lee Thomason1ff38e02012-02-14 18:18:16 -08001004}
Lee Thomason1a1d4a72012-02-15 09:09:25 -08001005
1006
1007void XMLAttribute::SetAttribute( unsigned v )
1008{
1009 char buf[BUF_SIZE];
1010 TIXML_SNPRINTF( buf, BUF_SIZE-1, "%u", v );
1011 value.SetStr( buf );
1012}
1013
1014
1015void XMLAttribute::SetAttribute( bool v )
1016{
1017 char buf[BUF_SIZE];
1018 TIXML_SNPRINTF( buf, BUF_SIZE-1, "%d", v ? 1 : 0 );
1019 value.SetStr( buf );
1020}
1021
1022void XMLAttribute::SetAttribute( double v )
1023{
1024 char buf[BUF_SIZE];
1025 TIXML_SNPRINTF( buf, BUF_SIZE-1, "%f", v );
1026 value.SetStr( buf );
1027}
1028
1029void XMLAttribute::SetAttribute( float v )
1030{
1031 char buf[BUF_SIZE];
1032 TIXML_SNPRINTF( buf, BUF_SIZE-1, "%f", v );
1033 value.SetStr( buf );
1034}
1035
Lee Thomasondadcdfa2012-01-18 17:55:48 -08001036
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001037// --------- XMLElement ---------- //
1038XMLElement::XMLElement( XMLDocument* doc ) : XMLNode( doc ),
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -08001039 closingType( 0 ),
U-Stream\Lee09a11c52012-02-17 08:31:16 -08001040 rootAttribute( 0 )
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001041{
1042}
1043
1044
1045XMLElement::~XMLElement()
1046{
U-Stream\Lee09a11c52012-02-17 08:31:16 -08001047 while( rootAttribute ) {
1048 XMLAttribute* next = rootAttribute->next;
1049 DELETE_ATTRIBUTE( rootAttribute );
1050 rootAttribute = next;
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001051 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001052}
1053
1054
Lee Thomason1a1d4a72012-02-15 09:09:25 -08001055XMLAttribute* XMLElement::FindAttribute( const char* name )
1056{
1057 XMLAttribute* a = 0;
1058 for( a=rootAttribute; a; a = a->next ) {
1059 if ( XMLUtil::StringEqual( a->Name(), name ) )
1060 return a;
1061 }
1062 return 0;
1063}
1064
1065
1066const XMLAttribute* XMLElement::FindAttribute( const char* name ) const
1067{
1068 XMLAttribute* a = 0;
1069 for( a=rootAttribute; a; a = a->next ) {
1070 if ( XMLUtil::StringEqual( a->Name(), name ) )
1071 return a;
1072 }
1073 return 0;
1074}
1075
1076
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001077const char* XMLElement::GetText() const
1078{
1079 if ( FirstChild() && FirstChild()->ToText() ) {
1080 return FirstChild()->ToText()->Value();
1081 }
1082 return 0;
1083}
1084
1085
1086
Lee Thomason1a1d4a72012-02-15 09:09:25 -08001087XMLAttribute* XMLElement::FindOrCreateAttribute( const char* name )
1088{
1089 XMLAttribute* attrib = FindAttribute( name );
1090 if ( !attrib ) {
Lee Thomason (grinliz)9b093cc2012-02-25 21:30:18 -08001091 attrib = new (document->attributePool.Alloc() ) XMLAttribute();
Lee Thomason1a1d4a72012-02-15 09:09:25 -08001092 attrib->memPool = &document->attributePool;
U-Stream\Lee09a11c52012-02-17 08:31:16 -08001093 LinkAttribute( attrib );
1094 attrib->SetName( name );
Lee Thomason1a1d4a72012-02-15 09:09:25 -08001095 }
1096 return attrib;
1097}
1098
1099
U-Stream\Lee09a11c52012-02-17 08:31:16 -08001100void XMLElement::LinkAttribute( XMLAttribute* attrib )
1101{
1102 if ( rootAttribute ) {
1103 XMLAttribute* end = rootAttribute;
1104 while ( end->next )
1105 end = end->next;
1106 end->next = attrib;
1107 }
1108 else {
1109 rootAttribute = attrib;
1110 }
1111}
1112
1113
U-Stream\Leeae25a442012-02-17 17:48:16 -08001114void XMLElement::DeleteAttribute( const char* name )
1115{
1116 XMLAttribute* prev = 0;
1117 for( XMLAttribute* a=rootAttribute; a; a=a->next ) {
1118 if ( XMLUtil::StringEqual( name, a->Name() ) ) {
1119 if ( prev ) {
1120 prev->next = a->next;
1121 }
1122 else {
1123 rootAttribute = a->next;
1124 }
1125 DELETE_ATTRIBUTE( a );
1126 break;
1127 }
1128 prev = a;
1129 }
1130}
1131
1132
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -08001133char* XMLElement::ParseAttributes( char* p )
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001134{
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001135 const char* start = p;
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001136
1137 // Read the attributes.
1138 while( p ) {
Lee Thomason56bdd022012-02-09 18:16:58 -08001139 p = XMLUtil::SkipWhiteSpace( p );
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001140 if ( !p || !(*p) ) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -07001141 document->SetError( XML_ERROR_PARSING_ELEMENT, start, Name() );
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001142 return 0;
1143 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001144
1145 // attribute.
Lee Thomason56bdd022012-02-09 18:16:58 -08001146 if ( XMLUtil::IsAlpha( *p ) ) {
Lee Thomason (grinliz)9b093cc2012-02-25 21:30:18 -08001147 XMLAttribute* attrib = new (document->attributePool.Alloc() ) XMLAttribute();
Lee Thomason43f59302012-02-06 18:18:11 -08001148 attrib->memPool = &document->attributePool;
Lee Thomasond1983222012-02-06 08:41:24 -08001149
Lee Thomason6f381b72012-03-02 12:59:39 -08001150 p = attrib->ParseDeep( p, document->ProcessEntities() );
Lee Thomasond6277762012-02-22 16:00:12 -08001151 if ( !p || Attribute( attrib->Name() ) ) {
Lee Thomason43f59302012-02-06 18:18:11 -08001152 DELETE_ATTRIBUTE( attrib );
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -07001153 document->SetError( XML_ERROR_PARSING_ATTRIBUTE, start, p );
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001154 return 0;
1155 }
U-Stream\Lee09a11c52012-02-17 08:31:16 -08001156 LinkAttribute( attrib );
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001157 }
Lee Thomasone4422302012-01-20 17:59:50 -08001158 // end of the tag
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001159 else if ( *p == '/' && *(p+1) == '>' ) {
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -08001160 closingType = CLOSED;
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001161 return p+2; // done; sealed element.
1162 }
Lee Thomasone4422302012-01-20 17:59:50 -08001163 // end of the tag
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001164 else if ( *p == '>' ) {
1165 ++p;
1166 break;
1167 }
Lee Thomasone4422302012-01-20 17:59:50 -08001168 else {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -07001169 document->SetError( XML_ERROR_PARSING_ELEMENT, start, p );
Lee Thomasone4422302012-01-20 17:59:50 -08001170 return 0;
1171 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001172 }
Lee Thomason67d61312012-01-24 16:01:51 -08001173 return p;
1174}
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001175
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001176
Lee Thomason67d61312012-01-24 16:01:51 -08001177//
1178// <ele></ele>
1179// <ele>foo<b>bar</b></ele>
1180//
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -08001181char* XMLElement::ParseDeep( char* p, StrPair* strPair )
Lee Thomason67d61312012-01-24 16:01:51 -08001182{
1183 // Read the element name.
Lee Thomason56bdd022012-02-09 18:16:58 -08001184 p = XMLUtil::SkipWhiteSpace( p );
Lee Thomason67d61312012-01-24 16:01:51 -08001185 if ( !p ) return 0;
Lee Thomason67d61312012-01-24 16:01:51 -08001186
1187 // The closing element is the </element> form. It is
1188 // parsed just like a regular element then deleted from
1189 // the DOM.
1190 if ( *p == '/' ) {
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -08001191 closingType = CLOSING;
Lee Thomason67d61312012-01-24 16:01:51 -08001192 ++p;
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001193 }
Lee Thomason67d61312012-01-24 16:01:51 -08001194
Lee Thomason56bdd022012-02-09 18:16:58 -08001195 p = value.ParseName( p );
Lee Thomasond1983222012-02-06 08:41:24 -08001196 if ( value.Empty() ) return 0;
Lee Thomason67d61312012-01-24 16:01:51 -08001197
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -08001198 p = ParseAttributes( p );
1199 if ( !p || !*p || closingType )
Lee Thomason67d61312012-01-24 16:01:51 -08001200 return p;
1201
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -08001202 p = XMLNode::ParseDeep( p, strPair );
Lee Thomason67d61312012-01-24 16:01:51 -08001203 return p;
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001204}
1205
1206
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001207
1208XMLNode* XMLElement::ShallowClone( XMLDocument* doc ) const
1209{
1210 if ( !doc ) {
1211 doc = document;
1212 }
1213 XMLElement* element = doc->NewElement( Value() ); // fixme: this will always allocate memory. Intern?
1214 for( const XMLAttribute* a=FirstAttribute(); a; a=a->Next() ) {
1215 element->SetAttribute( a->Name(), a->Value() ); // fixme: this will always allocate memory. Intern?
1216 }
1217 return element;
1218}
1219
1220
1221bool XMLElement::ShallowEqual( const XMLNode* compare ) const
1222{
1223 const XMLElement* other = compare->ToElement();
1224 if ( other && XMLUtil::StringEqual( other->Value(), Value() )) {
1225
1226 const XMLAttribute* a=FirstAttribute();
1227 const XMLAttribute* b=other->FirstAttribute();
1228
1229 while ( a && b ) {
1230 if ( !XMLUtil::StringEqual( a->Value(), b->Value() ) ) {
1231 return false;
1232 }
1233 }
1234 if ( a || b ) {
1235 // different count
1236 return false;
1237 }
1238 return true;
1239 }
1240 return false;
1241}
1242
1243
Lee Thomason751da522012-02-10 08:50:51 -08001244bool XMLElement::Accept( XMLVisitor* visitor ) const
1245{
1246 if ( visitor->VisitEnter( *this, rootAttribute ) )
1247 {
1248 for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() )
1249 {
1250 if ( !node->Accept( visitor ) )
1251 break;
1252 }
1253 }
1254 return visitor->VisitExit( *this );
Lee Thomason751da522012-02-10 08:50:51 -08001255}
Lee Thomason56bdd022012-02-09 18:16:58 -08001256
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001257
Lee Thomason3f57d272012-01-11 15:30:03 -08001258// --------- XMLDocument ----------- //
Lee Thomason6f381b72012-03-02 12:59:39 -08001259XMLDocument::XMLDocument( bool _processEntities ) :
Lee Thomason18d68bd2012-01-26 18:17:26 -08001260 XMLNode( 0 ),
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001261 writeBOM( false ),
Lee Thomason6f381b72012-03-02 12:59:39 -08001262 processEntities( _processEntities ),
1263 errorID( 0 ),
1264 errorStr1( 0 ),
1265 errorStr2( 0 ),
U-Lama\Lee560bd472011-12-28 19:42:49 -08001266 charBuffer( 0 )
1267{
Lee Thomason18d68bd2012-01-26 18:17:26 -08001268 document = this; // avoid warning about 'this' in initializer list
U-Lama\Lee560bd472011-12-28 19:42:49 -08001269}
U-Lama\Leee13c3e62011-12-28 14:36:55 -08001270
1271
Lee Thomason3f57d272012-01-11 15:30:03 -08001272XMLDocument::~XMLDocument()
1273{
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001274 DeleteChildren();
Lee Thomason18d68bd2012-01-26 18:17:26 -08001275 delete [] charBuffer;
Lee Thomasond1983222012-02-06 08:41:24 -08001276
Lee Thomasonec5a7b42012-02-13 18:16:52 -08001277#if 0
Lee Thomason455c9d42012-02-06 09:14:14 -08001278 textPool.Trace( "text" );
1279 elementPool.Trace( "element" );
1280 commentPool.Trace( "comment" );
1281 attributePool.Trace( "attribute" );
Lee Thomasone9ecdab2012-02-13 18:11:20 -08001282#endif
1283
Lee Thomason455c9d42012-02-06 09:14:14 -08001284 TIXMLASSERT( textPool.CurrentAllocs() == 0 );
1285 TIXMLASSERT( elementPool.CurrentAllocs() == 0 );
1286 TIXMLASSERT( commentPool.CurrentAllocs() == 0 );
1287 TIXMLASSERT( attributePool.CurrentAllocs() == 0 );
Lee Thomason3f57d272012-01-11 15:30:03 -08001288}
1289
1290
Lee Thomason18d68bd2012-01-26 18:17:26 -08001291void XMLDocument::InitDocument()
1292{
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001293 errorID = XML_NO_ERROR;
Lee Thomason18d68bd2012-01-26 18:17:26 -08001294 errorStr1 = 0;
1295 errorStr2 = 0;
1296
1297 delete [] charBuffer;
1298 charBuffer = 0;
1299
1300}
1301
Lee Thomason3f57d272012-01-11 15:30:03 -08001302
Lee Thomason2c85a712012-01-31 08:24:24 -08001303XMLElement* XMLDocument::NewElement( const char* name )
1304{
Lee Thomasond1983222012-02-06 08:41:24 -08001305 XMLElement* ele = new (elementPool.Alloc()) XMLElement( this );
1306 ele->memPool = &elementPool;
Lee Thomason2c85a712012-01-31 08:24:24 -08001307 ele->SetName( name );
1308 return ele;
1309}
1310
1311
Lee Thomason1ff38e02012-02-14 18:18:16 -08001312XMLComment* XMLDocument::NewComment( const char* str )
1313{
1314 XMLComment* comment = new (commentPool.Alloc()) XMLComment( this );
1315 comment->memPool = &commentPool;
1316 comment->SetValue( str );
1317 return comment;
1318}
1319
1320
1321XMLText* XMLDocument::NewText( const char* str )
1322{
U-Stream\Lee09a11c52012-02-17 08:31:16 -08001323 XMLText* text = new (textPool.Alloc()) XMLText( this );
1324 text->memPool = &textPool;
1325 text->SetValue( str );
1326 return text;
Lee Thomason1ff38e02012-02-14 18:18:16 -08001327}
1328
1329
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001330XMLDeclaration* XMLDocument::NewDeclaration( const char* str )
1331{
1332 XMLDeclaration* dec = new (commentPool.Alloc()) XMLDeclaration( this );
1333 dec->memPool = &commentPool;
1334 dec->SetValue( str );
1335 return dec;
1336}
1337
1338
1339XMLUnknown* XMLDocument::NewUnknown( const char* str )
1340{
1341 XMLUnknown* unk = new (commentPool.Alloc()) XMLUnknown( this );
1342 unk->memPool = &commentPool;
1343 unk->SetValue( str );
1344 return unk;
1345}
1346
1347
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001348int XMLDocument::LoadFile( const char* filename )
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001349{
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001350 DeleteChildren();
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001351 InitDocument();
1352
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001353#if defined(_MSC_VER)
Lee Thomason (grinliz)5ce4d972012-02-26 21:14:23 -08001354#pragma warning ( push )
1355#pragma warning ( disable : 4996 ) // Fail to see a compelling reason why this should be deprecated.
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001356#endif
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001357 FILE* fp = fopen( filename, "rb" );
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001358#if defined(_MSC_VER)
Lee Thomason (grinliz)5ce4d972012-02-26 21:14:23 -08001359#pragma warning ( pop )
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001360#endif
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001361 if ( !fp ) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -07001362 SetError( XML_ERROR_FILE_NOT_FOUND, filename, 0 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001363 return errorID;
1364 }
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001365 LoadFile( fp );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001366 fclose( fp );
1367 return errorID;
1368}
1369
1370
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001371int XMLDocument::LoadFile( FILE* fp )
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001372{
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001373 DeleteChildren();
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001374 InitDocument();
1375
1376 fseek( fp, 0, SEEK_END );
1377 unsigned size = ftell( fp );
1378 fseek( fp, 0, SEEK_SET );
1379
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001380 if ( size == 0 ) {
1381 return errorID;
1382 }
1383
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001384 charBuffer = new char[size+1];
1385 fread( charBuffer, size, 1, fp );
1386 charBuffer[size] = 0;
1387
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001388 const char* p = charBuffer;
1389 p = XMLUtil::SkipWhiteSpace( p );
1390 p = XMLUtil::ReadBOM( p, &writeBOM );
1391 if ( !p || !*p ) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -07001392 SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
Lee Thomasond6277762012-02-22 16:00:12 -08001393 return errorID;
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001394 }
1395
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -08001396 ParseDeep( charBuffer + (p-charBuffer), 0 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001397 return errorID;
1398}
1399
1400
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001401void XMLDocument::SaveFile( const char* filename )
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001402{
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001403#if defined(_MSC_VER)
Lee Thomason (grinliz)5ce4d972012-02-26 21:14:23 -08001404#pragma warning ( push )
1405#pragma warning ( disable : 4996 ) // Fail to see a compelling reason why this should be deprecated.
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001406#endif
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001407 FILE* fp = fopen( filename, "w" );
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001408#if defined(_MSC_VER)
Lee Thomason (grinliz)5ce4d972012-02-26 21:14:23 -08001409#pragma warning ( pop )
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001410#endif
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001411 XMLPrinter stream( fp );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001412 Print( &stream );
1413 fclose( fp );
1414}
1415
Lee Thomason1ff38e02012-02-14 18:18:16 -08001416
Lee Thomason7c913cd2012-01-26 18:32:34 -08001417int XMLDocument::Parse( const char* p )
Lee Thomason3f57d272012-01-11 15:30:03 -08001418{
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001419 DeleteChildren();
Lee Thomason18d68bd2012-01-26 18:17:26 -08001420 InitDocument();
1421
1422 if ( !p || !*p ) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -07001423 SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
Lee Thomasond6277762012-02-22 16:00:12 -08001424 return errorID;
Lee Thomason18d68bd2012-01-26 18:17:26 -08001425 }
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001426 p = XMLUtil::SkipWhiteSpace( p );
1427 p = XMLUtil::ReadBOM( p, &writeBOM );
1428 if ( !p || !*p ) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -07001429 SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
Lee Thomasond6277762012-02-22 16:00:12 -08001430 return errorID;
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001431 }
1432
Lee Thomason18d68bd2012-01-26 18:17:26 -08001433 size_t len = strlen( p );
1434 charBuffer = new char[ len+1 ];
1435 memcpy( charBuffer, p, len+1 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001436
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001437
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -08001438 ParseDeep( charBuffer, 0 );
Lee Thomason7c913cd2012-01-26 18:32:34 -08001439 return errorID;
Lee Thomason3f57d272012-01-11 15:30:03 -08001440}
1441
1442
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001443void XMLDocument::Print( XMLPrinter* streamer )
Lee Thomason3f57d272012-01-11 15:30:03 -08001444{
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001445 XMLPrinter stdStreamer( stdout );
Lee Thomason5cae8972012-01-24 18:03:07 -08001446 if ( !streamer )
1447 streamer = &stdStreamer;
Lee Thomason751da522012-02-10 08:50:51 -08001448 Accept( streamer );
Lee Thomason3f57d272012-01-11 15:30:03 -08001449}
1450
1451
Lee Thomason67d61312012-01-24 16:01:51 -08001452void XMLDocument::SetError( int error, const char* str1, const char* str2 )
1453{
Lee Thomason18d68bd2012-01-26 18:17:26 -08001454 errorID = error;
Lee Thomason18d68bd2012-01-26 18:17:26 -08001455 errorStr1 = str1;
1456 errorStr2 = str2;
Lee Thomason67d61312012-01-24 16:01:51 -08001457}
1458
Lee Thomason5cae8972012-01-24 18:03:07 -08001459
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001460void XMLDocument::PrintError() const
1461{
1462 if ( errorID ) {
Lee Thomason (grinliz)5ce4d972012-02-26 21:14:23 -08001463 static const int LEN = 20;
1464 char buf1[LEN] = { 0 };
1465 char buf2[LEN] = { 0 };
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001466
1467 if ( errorStr1 ) {
Lee Thomason (grinliz)5ce4d972012-02-26 21:14:23 -08001468 TIXML_SNPRINTF( buf1, LEN, "%s", errorStr1 );
1469 buf1[LEN-1] = 0;
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001470 }
1471 if ( errorStr2 ) {
Lee Thomason (grinliz)5ce4d972012-02-26 21:14:23 -08001472 TIXML_SNPRINTF( buf2, LEN, "%s", errorStr2 );
1473 buf2[LEN-1] = 0;
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001474 }
1475
1476 printf( "XMLDocument error id=%d str1=%s str2=%s\n",
1477 errorID, buf1, buf2 );
1478 }
1479}
1480
1481
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001482XMLPrinter::XMLPrinter( FILE* file ) :
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001483 elementJustOpened( false ),
1484 firstElement( true ),
1485 fp( file ),
1486 depth( 0 ),
Lee Thomason6f381b72012-03-02 12:59:39 -08001487 textDepth( -1 ),
1488 processEntities( true )
Lee Thomason5cae8972012-01-24 18:03:07 -08001489{
Lee Thomason857b8682012-01-25 17:50:25 -08001490 for( int i=0; i<ENTITY_RANGE; ++i ) {
1491 entityFlag[i] = false;
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001492 restrictedEntityFlag[i] = false;
Lee Thomason857b8682012-01-25 17:50:25 -08001493 }
1494 for( int i=0; i<NUM_ENTITIES; ++i ) {
1495 TIXMLASSERT( entities[i].value < ENTITY_RANGE );
1496 if ( entities[i].value < ENTITY_RANGE ) {
Lee Thomason (grinliz)9b093cc2012-02-25 21:30:18 -08001497 entityFlag[ (int)entities[i].value ] = true;
Lee Thomason857b8682012-01-25 17:50:25 -08001498 }
1499 }
Lee Thomason (grinliz)9b093cc2012-02-25 21:30:18 -08001500 restrictedEntityFlag[(int)'&'] = true;
1501 restrictedEntityFlag[(int)'<'] = true;
1502 restrictedEntityFlag[(int)'>'] = true; // not required, but consistency is nice
U-Stream\Leeae25a442012-02-17 17:48:16 -08001503 buffer.Push( 0 );
1504}
1505
1506
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001507void XMLPrinter::Print( const char* format, ... )
U-Stream\Leeae25a442012-02-17 17:48:16 -08001508{
1509 va_list va;
1510 va_start( va, format );
1511
1512 if ( fp ) {
1513 vfprintf( fp, format, va );
1514 }
1515 else {
1516 // This seems brutally complex. Haven't figured out a better
1517 // way on windows.
1518 #ifdef _MSC_VER
1519 int len = -1;
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001520 int expand = 1000;
U-Stream\Leeae25a442012-02-17 17:48:16 -08001521 while ( len < 0 ) {
1522 len = vsnprintf_s( accumulator.Mem(), accumulator.Capacity(), accumulator.Capacity()-1, format, va );
1523 if ( len < 0 ) {
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001524 accumulator.PushArr( expand );
1525 expand *= 3/2;
U-Stream\Leeae25a442012-02-17 17:48:16 -08001526 }
1527 }
1528 char* p = buffer.PushArr( len ) - 1;
1529 memcpy( p, accumulator.Mem(), len+1 );
1530 #else
1531 int len = vsnprintf( 0, 0, format, va );
Lee Thomason4de93472012-03-13 17:33:35 -07001532 // Close out and re-start the va-args
1533 va_end( va );
1534 va_start( va, format );
U-Stream\Leeae25a442012-02-17 17:48:16 -08001535 char* p = buffer.PushArr( len ) - 1;
Lee Thomason (grinliz)9b093cc2012-02-25 21:30:18 -08001536 vsnprintf( p, len+1, format, va );
U-Stream\Leeae25a442012-02-17 17:48:16 -08001537 #endif
1538 }
1539 va_end( va );
Lee Thomason5cae8972012-01-24 18:03:07 -08001540}
1541
1542
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001543void XMLPrinter::PrintSpace( int depth )
Lee Thomason5cae8972012-01-24 18:03:07 -08001544{
1545 for( int i=0; i<depth; ++i ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001546 Print( " " );
Lee Thomason5cae8972012-01-24 18:03:07 -08001547 }
1548}
1549
1550
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001551void XMLPrinter::PrintString( const char* p, bool restricted )
Lee Thomason857b8682012-01-25 17:50:25 -08001552{
Lee Thomason951d8832012-01-26 08:47:06 -08001553 // Look for runs of bytes between entities to print.
1554 const char* q = p;
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001555 const bool* flag = restricted ? restrictedEntityFlag : entityFlag;
Lee Thomason857b8682012-01-25 17:50:25 -08001556
Lee Thomason6f381b72012-03-02 12:59:39 -08001557 if ( processEntities ) {
1558 while ( *q ) {
1559 // Remember, char is sometimes signed. (How many times has that bitten me?)
1560 if ( *q > 0 && *q < ENTITY_RANGE ) {
1561 // Check for entities. If one is found, flush
1562 // the stream up until the entity, write the
1563 // entity, and keep looking.
1564 if ( flag[*q] ) {
1565 while ( p < q ) {
1566 Print( "%c", *p );
1567 ++p;
1568 }
1569 for( int i=0; i<NUM_ENTITIES; ++i ) {
1570 if ( entities[i].value == *q ) {
1571 Print( "&%s;", entities[i].pattern );
1572 break;
1573 }
1574 }
Lee Thomason951d8832012-01-26 08:47:06 -08001575 ++p;
1576 }
Lee Thomason951d8832012-01-26 08:47:06 -08001577 }
Lee Thomason6f381b72012-03-02 12:59:39 -08001578 ++q;
Lee Thomason951d8832012-01-26 08:47:06 -08001579 }
Lee Thomason951d8832012-01-26 08:47:06 -08001580 }
1581 // Flush the remaining string. This will be the entire
1582 // string if an entity wasn't found.
Lee Thomason6f381b72012-03-02 12:59:39 -08001583 if ( !processEntities || (q-p > 0) ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001584 Print( "%s", p );
Lee Thomason951d8832012-01-26 08:47:06 -08001585 }
Lee Thomason857b8682012-01-25 17:50:25 -08001586}
1587
U-Stream\Leeae25a442012-02-17 17:48:16 -08001588
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001589void XMLPrinter::PushHeader( bool writeBOM, bool writeDec )
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001590{
1591 static const unsigned char bom[] = { TIXML_UTF_LEAD_0, TIXML_UTF_LEAD_1, TIXML_UTF_LEAD_2, 0 };
1592 if ( writeBOM ) {
1593 Print( "%s", bom );
1594 }
1595 if ( writeDec ) {
1596 PushDeclaration( "xml version=\"1.0\"" );
1597 }
1598}
1599
1600
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001601void XMLPrinter::OpenElement( const char* name )
Lee Thomason5cae8972012-01-24 18:03:07 -08001602{
1603 if ( elementJustOpened ) {
1604 SealElement();
1605 }
Lee Thomason56bdd022012-02-09 18:16:58 -08001606 stack.Push( name );
1607
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001608 if ( textDepth < 0 && !firstElement ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001609 Print( "\n" );
Lee Thomason24767b02012-01-25 17:16:23 -08001610 PrintSpace( depth );
1611 }
Lee Thomason5cae8972012-01-24 18:03:07 -08001612
U-Stream\Leeae25a442012-02-17 17:48:16 -08001613 Print( "<%s", name );
Lee Thomason5cae8972012-01-24 18:03:07 -08001614 elementJustOpened = true;
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001615 firstElement = false;
Lee Thomason5cae8972012-01-24 18:03:07 -08001616 ++depth;
1617}
1618
1619
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001620void XMLPrinter::PushAttribute( const char* name, const char* value )
Lee Thomason5cae8972012-01-24 18:03:07 -08001621{
1622 TIXMLASSERT( elementJustOpened );
U-Stream\Leeae25a442012-02-17 17:48:16 -08001623 Print( " %s=\"", name );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001624 PrintString( value, false );
U-Stream\Leeae25a442012-02-17 17:48:16 -08001625 Print( "\"" );
Lee Thomason5cae8972012-01-24 18:03:07 -08001626}
1627
1628
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001629void XMLPrinter::PushAttribute( const char* name, int v )
1630{
1631 char buf[BUF_SIZE];
1632 TIXML_SNPRINTF( buf, BUF_SIZE-1, "%d", v );
1633 PushAttribute( name, buf );
1634}
1635
1636
1637void XMLPrinter::PushAttribute( const char* name, unsigned v )
1638{
1639 char buf[BUF_SIZE];
1640 TIXML_SNPRINTF( buf, BUF_SIZE-1, "%u", v );
1641 PushAttribute( name, buf );
1642}
1643
1644
1645void XMLPrinter::PushAttribute( const char* name, bool v )
1646{
1647 char buf[BUF_SIZE];
1648 TIXML_SNPRINTF( buf, BUF_SIZE-1, "%d", v ? 1 : 0 );
1649 PushAttribute( name, buf );
1650}
1651
1652
1653void XMLPrinter::PushAttribute( const char* name, double v )
1654{
1655 char buf[BUF_SIZE];
1656 TIXML_SNPRINTF( buf, BUF_SIZE-1, "%f", v );
1657 PushAttribute( name, buf );
1658}
1659
1660
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001661void XMLPrinter::CloseElement()
Lee Thomason5cae8972012-01-24 18:03:07 -08001662{
1663 --depth;
1664 const char* name = stack.Pop();
Lee Thomason5cae8972012-01-24 18:03:07 -08001665
1666 if ( elementJustOpened ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001667 Print( "/>" );
Lee Thomason5cae8972012-01-24 18:03:07 -08001668 }
1669 else {
Lee Thomason56bdd022012-02-09 18:16:58 -08001670 if ( textDepth < 0 ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001671 Print( "\n" );
Lee Thomason24767b02012-01-25 17:16:23 -08001672 PrintSpace( depth );
1673 }
U-Stream\Leeae25a442012-02-17 17:48:16 -08001674 Print( "</%s>", name );
Lee Thomason5cae8972012-01-24 18:03:07 -08001675 }
Lee Thomason56bdd022012-02-09 18:16:58 -08001676
1677 if ( textDepth == depth )
1678 textDepth = -1;
1679 if ( depth == 0 )
U-Stream\Leeae25a442012-02-17 17:48:16 -08001680 Print( "\n" );
Lee Thomason5cae8972012-01-24 18:03:07 -08001681 elementJustOpened = false;
1682}
1683
1684
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001685void XMLPrinter::SealElement()
Lee Thomason5cae8972012-01-24 18:03:07 -08001686{
1687 elementJustOpened = false;
U-Stream\Leeae25a442012-02-17 17:48:16 -08001688 Print( ">" );
Lee Thomason5cae8972012-01-24 18:03:07 -08001689}
1690
1691
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001692void XMLPrinter::PushText( const char* text, bool cdata )
Lee Thomason5cae8972012-01-24 18:03:07 -08001693{
Lee Thomason56bdd022012-02-09 18:16:58 -08001694 textDepth = depth-1;
1695
Lee Thomason5cae8972012-01-24 18:03:07 -08001696 if ( elementJustOpened ) {
1697 SealElement();
1698 }
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001699 if ( cdata ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001700 Print( "<![CDATA[" );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001701 Print( "%s", text );
U-Stream\Leeae25a442012-02-17 17:48:16 -08001702 Print( "]]>" );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001703 }
1704 else {
1705 PrintString( text, true );
1706 }
Lee Thomason5cae8972012-01-24 18:03:07 -08001707}
1708
1709
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001710void XMLPrinter::PushComment( const char* comment )
Lee Thomason5cae8972012-01-24 18:03:07 -08001711{
1712 if ( elementJustOpened ) {
1713 SealElement();
1714 }
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001715 if ( textDepth < 0 && !firstElement ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001716 Print( "\n" );
U-Stream\Lee09a11c52012-02-17 08:31:16 -08001717 PrintSpace( depth );
1718 }
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001719 firstElement = false;
U-Stream\Leeae25a442012-02-17 17:48:16 -08001720 Print( "<!--%s-->", comment );
Lee Thomason5cae8972012-01-24 18:03:07 -08001721}
Lee Thomason751da522012-02-10 08:50:51 -08001722
1723
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001724void XMLPrinter::PushDeclaration( const char* value )
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001725{
1726 if ( elementJustOpened ) {
1727 SealElement();
1728 }
1729 if ( textDepth < 0 && !firstElement) {
1730 Print( "\n" );
1731 PrintSpace( depth );
1732 }
1733 firstElement = false;
1734 Print( "<?%s?>", value );
1735}
1736
1737
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001738void XMLPrinter::PushUnknown( const char* value )
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001739{
1740 if ( elementJustOpened ) {
1741 SealElement();
1742 }
1743 if ( textDepth < 0 && !firstElement ) {
1744 Print( "\n" );
1745 PrintSpace( depth );
1746 }
1747 firstElement = false;
1748 Print( "<!%s>", value );
1749}
1750
1751
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001752bool XMLPrinter::VisitEnter( const XMLDocument& doc )
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001753{
Lee Thomason6f381b72012-03-02 12:59:39 -08001754 processEntities = doc.ProcessEntities();
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001755 if ( doc.HasBOM() ) {
1756 PushHeader( true, false );
1757 }
1758 return true;
1759}
1760
1761
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001762bool XMLPrinter::VisitEnter( const XMLElement& element, const XMLAttribute* attribute )
Lee Thomason751da522012-02-10 08:50:51 -08001763{
1764 OpenElement( element.Name() );
1765 while ( attribute ) {
1766 PushAttribute( attribute->Name(), attribute->Value() );
1767 attribute = attribute->Next();
1768 }
1769 return true;
1770}
1771
1772
Lee Thomason (grinliz)9b093cc2012-02-25 21:30:18 -08001773bool XMLPrinter::VisitExit( const XMLElement& )
Lee Thomason751da522012-02-10 08:50:51 -08001774{
1775 CloseElement();
1776 return true;
1777}
1778
1779
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001780bool XMLPrinter::Visit( const XMLText& text )
Lee Thomason751da522012-02-10 08:50:51 -08001781{
Lee Thomasond6277762012-02-22 16:00:12 -08001782 PushText( text.Value(), text.CData() );
Lee Thomason751da522012-02-10 08:50:51 -08001783 return true;
1784}
1785
1786
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001787bool XMLPrinter::Visit( const XMLComment& comment )
Lee Thomason751da522012-02-10 08:50:51 -08001788{
1789 PushComment( comment.Value() );
1790 return true;
1791}
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001792
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001793bool XMLPrinter::Visit( const XMLDeclaration& declaration )
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001794{
1795 PushDeclaration( declaration.Value() );
1796 return true;
1797}
1798
1799
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001800bool XMLPrinter::Visit( const XMLUnknown& unknown )
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001801{
1802 PushUnknown( unknown.Value() );
1803 return true;
1804}