blob: 96ec1bf45053f20e012d994d8e550896c18f26f7 [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 Thomason8ba7f7d2012-03-24 13:04:04 -07001077const char* XMLElement::Attribute( const char* name, const char* value ) const
1078{
1079 const XMLAttribute* a = FindAttribute( name );
1080 if ( !a )
1081 return 0;
1082 if ( !value || XMLUtil::StringEqual( a->Value(), value ))
1083 return a->Value();
1084 return 0;
1085}
1086
1087
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001088const char* XMLElement::GetText() const
1089{
1090 if ( FirstChild() && FirstChild()->ToText() ) {
1091 return FirstChild()->ToText()->Value();
1092 }
1093 return 0;
1094}
1095
1096
1097
Lee Thomason1a1d4a72012-02-15 09:09:25 -08001098XMLAttribute* XMLElement::FindOrCreateAttribute( const char* name )
1099{
1100 XMLAttribute* attrib = FindAttribute( name );
1101 if ( !attrib ) {
Lee Thomason (grinliz)9b093cc2012-02-25 21:30:18 -08001102 attrib = new (document->attributePool.Alloc() ) XMLAttribute();
Lee Thomason1a1d4a72012-02-15 09:09:25 -08001103 attrib->memPool = &document->attributePool;
U-Stream\Lee09a11c52012-02-17 08:31:16 -08001104 LinkAttribute( attrib );
1105 attrib->SetName( name );
Lee Thomason1a1d4a72012-02-15 09:09:25 -08001106 }
1107 return attrib;
1108}
1109
1110
U-Stream\Lee09a11c52012-02-17 08:31:16 -08001111void XMLElement::LinkAttribute( XMLAttribute* attrib )
1112{
1113 if ( rootAttribute ) {
1114 XMLAttribute* end = rootAttribute;
1115 while ( end->next )
1116 end = end->next;
1117 end->next = attrib;
1118 }
1119 else {
1120 rootAttribute = attrib;
1121 }
1122}
1123
1124
U-Stream\Leeae25a442012-02-17 17:48:16 -08001125void XMLElement::DeleteAttribute( const char* name )
1126{
1127 XMLAttribute* prev = 0;
1128 for( XMLAttribute* a=rootAttribute; a; a=a->next ) {
1129 if ( XMLUtil::StringEqual( name, a->Name() ) ) {
1130 if ( prev ) {
1131 prev->next = a->next;
1132 }
1133 else {
1134 rootAttribute = a->next;
1135 }
1136 DELETE_ATTRIBUTE( a );
1137 break;
1138 }
1139 prev = a;
1140 }
1141}
1142
1143
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -08001144char* XMLElement::ParseAttributes( char* p )
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001145{
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001146 const char* start = p;
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001147
1148 // Read the attributes.
1149 while( p ) {
Lee Thomason56bdd022012-02-09 18:16:58 -08001150 p = XMLUtil::SkipWhiteSpace( p );
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001151 if ( !p || !(*p) ) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -07001152 document->SetError( XML_ERROR_PARSING_ELEMENT, start, Name() );
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001153 return 0;
1154 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001155
1156 // attribute.
Lee Thomason56bdd022012-02-09 18:16:58 -08001157 if ( XMLUtil::IsAlpha( *p ) ) {
Lee Thomason (grinliz)9b093cc2012-02-25 21:30:18 -08001158 XMLAttribute* attrib = new (document->attributePool.Alloc() ) XMLAttribute();
Lee Thomason43f59302012-02-06 18:18:11 -08001159 attrib->memPool = &document->attributePool;
Lee Thomasond1983222012-02-06 08:41:24 -08001160
Lee Thomason6f381b72012-03-02 12:59:39 -08001161 p = attrib->ParseDeep( p, document->ProcessEntities() );
Lee Thomasond6277762012-02-22 16:00:12 -08001162 if ( !p || Attribute( attrib->Name() ) ) {
Lee Thomason43f59302012-02-06 18:18:11 -08001163 DELETE_ATTRIBUTE( attrib );
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -07001164 document->SetError( XML_ERROR_PARSING_ATTRIBUTE, start, p );
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001165 return 0;
1166 }
U-Stream\Lee09a11c52012-02-17 08:31:16 -08001167 LinkAttribute( attrib );
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001168 }
Lee Thomasone4422302012-01-20 17:59:50 -08001169 // end of the tag
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001170 else if ( *p == '/' && *(p+1) == '>' ) {
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -08001171 closingType = CLOSED;
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001172 return p+2; // done; sealed element.
1173 }
Lee Thomasone4422302012-01-20 17:59:50 -08001174 // end of the tag
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001175 else if ( *p == '>' ) {
1176 ++p;
1177 break;
1178 }
Lee Thomasone4422302012-01-20 17:59:50 -08001179 else {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -07001180 document->SetError( XML_ERROR_PARSING_ELEMENT, start, p );
Lee Thomasone4422302012-01-20 17:59:50 -08001181 return 0;
1182 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001183 }
Lee Thomason67d61312012-01-24 16:01:51 -08001184 return p;
1185}
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001186
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001187
Lee Thomason67d61312012-01-24 16:01:51 -08001188//
1189// <ele></ele>
1190// <ele>foo<b>bar</b></ele>
1191//
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -08001192char* XMLElement::ParseDeep( char* p, StrPair* strPair )
Lee Thomason67d61312012-01-24 16:01:51 -08001193{
1194 // Read the element name.
Lee Thomason56bdd022012-02-09 18:16:58 -08001195 p = XMLUtil::SkipWhiteSpace( p );
Lee Thomason67d61312012-01-24 16:01:51 -08001196 if ( !p ) return 0;
Lee Thomason67d61312012-01-24 16:01:51 -08001197
1198 // The closing element is the </element> form. It is
1199 // parsed just like a regular element then deleted from
1200 // the DOM.
1201 if ( *p == '/' ) {
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -08001202 closingType = CLOSING;
Lee Thomason67d61312012-01-24 16:01:51 -08001203 ++p;
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001204 }
Lee Thomason67d61312012-01-24 16:01:51 -08001205
Lee Thomason56bdd022012-02-09 18:16:58 -08001206 p = value.ParseName( p );
Lee Thomasond1983222012-02-06 08:41:24 -08001207 if ( value.Empty() ) return 0;
Lee Thomason67d61312012-01-24 16:01:51 -08001208
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -08001209 p = ParseAttributes( p );
1210 if ( !p || !*p || closingType )
Lee Thomason67d61312012-01-24 16:01:51 -08001211 return p;
1212
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -08001213 p = XMLNode::ParseDeep( p, strPair );
Lee Thomason67d61312012-01-24 16:01:51 -08001214 return p;
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001215}
1216
1217
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001218
1219XMLNode* XMLElement::ShallowClone( XMLDocument* doc ) const
1220{
1221 if ( !doc ) {
1222 doc = document;
1223 }
1224 XMLElement* element = doc->NewElement( Value() ); // fixme: this will always allocate memory. Intern?
1225 for( const XMLAttribute* a=FirstAttribute(); a; a=a->Next() ) {
1226 element->SetAttribute( a->Name(), a->Value() ); // fixme: this will always allocate memory. Intern?
1227 }
1228 return element;
1229}
1230
1231
1232bool XMLElement::ShallowEqual( const XMLNode* compare ) const
1233{
1234 const XMLElement* other = compare->ToElement();
1235 if ( other && XMLUtil::StringEqual( other->Value(), Value() )) {
1236
1237 const XMLAttribute* a=FirstAttribute();
1238 const XMLAttribute* b=other->FirstAttribute();
1239
1240 while ( a && b ) {
1241 if ( !XMLUtil::StringEqual( a->Value(), b->Value() ) ) {
1242 return false;
1243 }
1244 }
1245 if ( a || b ) {
1246 // different count
1247 return false;
1248 }
1249 return true;
1250 }
1251 return false;
1252}
1253
1254
Lee Thomason751da522012-02-10 08:50:51 -08001255bool XMLElement::Accept( XMLVisitor* visitor ) const
1256{
1257 if ( visitor->VisitEnter( *this, rootAttribute ) )
1258 {
1259 for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() )
1260 {
1261 if ( !node->Accept( visitor ) )
1262 break;
1263 }
1264 }
1265 return visitor->VisitExit( *this );
Lee Thomason751da522012-02-10 08:50:51 -08001266}
Lee Thomason56bdd022012-02-09 18:16:58 -08001267
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001268
Lee Thomason3f57d272012-01-11 15:30:03 -08001269// --------- XMLDocument ----------- //
Lee Thomason6f381b72012-03-02 12:59:39 -08001270XMLDocument::XMLDocument( bool _processEntities ) :
Lee Thomason18d68bd2012-01-26 18:17:26 -08001271 XMLNode( 0 ),
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001272 writeBOM( false ),
Lee Thomason6f381b72012-03-02 12:59:39 -08001273 processEntities( _processEntities ),
1274 errorID( 0 ),
1275 errorStr1( 0 ),
1276 errorStr2( 0 ),
U-Lama\Lee560bd472011-12-28 19:42:49 -08001277 charBuffer( 0 )
1278{
Lee Thomason18d68bd2012-01-26 18:17:26 -08001279 document = this; // avoid warning about 'this' in initializer list
U-Lama\Lee560bd472011-12-28 19:42:49 -08001280}
U-Lama\Leee13c3e62011-12-28 14:36:55 -08001281
1282
Lee Thomason3f57d272012-01-11 15:30:03 -08001283XMLDocument::~XMLDocument()
1284{
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001285 DeleteChildren();
Lee Thomason18d68bd2012-01-26 18:17:26 -08001286 delete [] charBuffer;
Lee Thomasond1983222012-02-06 08:41:24 -08001287
Lee Thomasonec5a7b42012-02-13 18:16:52 -08001288#if 0
Lee Thomason455c9d42012-02-06 09:14:14 -08001289 textPool.Trace( "text" );
1290 elementPool.Trace( "element" );
1291 commentPool.Trace( "comment" );
1292 attributePool.Trace( "attribute" );
Lee Thomasone9ecdab2012-02-13 18:11:20 -08001293#endif
1294
Lee Thomason455c9d42012-02-06 09:14:14 -08001295 TIXMLASSERT( textPool.CurrentAllocs() == 0 );
1296 TIXMLASSERT( elementPool.CurrentAllocs() == 0 );
1297 TIXMLASSERT( commentPool.CurrentAllocs() == 0 );
1298 TIXMLASSERT( attributePool.CurrentAllocs() == 0 );
Lee Thomason3f57d272012-01-11 15:30:03 -08001299}
1300
1301
Lee Thomason18d68bd2012-01-26 18:17:26 -08001302void XMLDocument::InitDocument()
1303{
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001304 errorID = XML_NO_ERROR;
Lee Thomason18d68bd2012-01-26 18:17:26 -08001305 errorStr1 = 0;
1306 errorStr2 = 0;
1307
1308 delete [] charBuffer;
1309 charBuffer = 0;
1310
1311}
1312
Lee Thomason3f57d272012-01-11 15:30:03 -08001313
Lee Thomason2c85a712012-01-31 08:24:24 -08001314XMLElement* XMLDocument::NewElement( const char* name )
1315{
Lee Thomasond1983222012-02-06 08:41:24 -08001316 XMLElement* ele = new (elementPool.Alloc()) XMLElement( this );
1317 ele->memPool = &elementPool;
Lee Thomason2c85a712012-01-31 08:24:24 -08001318 ele->SetName( name );
1319 return ele;
1320}
1321
1322
Lee Thomason1ff38e02012-02-14 18:18:16 -08001323XMLComment* XMLDocument::NewComment( const char* str )
1324{
1325 XMLComment* comment = new (commentPool.Alloc()) XMLComment( this );
1326 comment->memPool = &commentPool;
1327 comment->SetValue( str );
1328 return comment;
1329}
1330
1331
1332XMLText* XMLDocument::NewText( const char* str )
1333{
U-Stream\Lee09a11c52012-02-17 08:31:16 -08001334 XMLText* text = new (textPool.Alloc()) XMLText( this );
1335 text->memPool = &textPool;
1336 text->SetValue( str );
1337 return text;
Lee Thomason1ff38e02012-02-14 18:18:16 -08001338}
1339
1340
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001341XMLDeclaration* XMLDocument::NewDeclaration( const char* str )
1342{
1343 XMLDeclaration* dec = new (commentPool.Alloc()) XMLDeclaration( this );
1344 dec->memPool = &commentPool;
1345 dec->SetValue( str );
1346 return dec;
1347}
1348
1349
1350XMLUnknown* XMLDocument::NewUnknown( const char* str )
1351{
1352 XMLUnknown* unk = new (commentPool.Alloc()) XMLUnknown( this );
1353 unk->memPool = &commentPool;
1354 unk->SetValue( str );
1355 return unk;
1356}
1357
1358
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001359int XMLDocument::LoadFile( const char* filename )
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001360{
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001361 DeleteChildren();
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001362 InitDocument();
1363
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001364#if defined(_MSC_VER)
Lee Thomason (grinliz)5ce4d972012-02-26 21:14:23 -08001365#pragma warning ( push )
1366#pragma warning ( disable : 4996 ) // Fail to see a compelling reason why this should be deprecated.
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001367#endif
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001368 FILE* fp = fopen( filename, "rb" );
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001369#if defined(_MSC_VER)
Lee Thomason (grinliz)5ce4d972012-02-26 21:14:23 -08001370#pragma warning ( pop )
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001371#endif
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001372 if ( !fp ) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -07001373 SetError( XML_ERROR_FILE_NOT_FOUND, filename, 0 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001374 return errorID;
1375 }
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001376 LoadFile( fp );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001377 fclose( fp );
1378 return errorID;
1379}
1380
1381
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001382int XMLDocument::LoadFile( FILE* fp )
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001383{
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001384 DeleteChildren();
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001385 InitDocument();
1386
1387 fseek( fp, 0, SEEK_END );
1388 unsigned size = ftell( fp );
1389 fseek( fp, 0, SEEK_SET );
1390
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001391 if ( size == 0 ) {
1392 return errorID;
1393 }
1394
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001395 charBuffer = new char[size+1];
1396 fread( charBuffer, size, 1, fp );
1397 charBuffer[size] = 0;
1398
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001399 const char* p = charBuffer;
1400 p = XMLUtil::SkipWhiteSpace( p );
1401 p = XMLUtil::ReadBOM( p, &writeBOM );
1402 if ( !p || !*p ) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -07001403 SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
Lee Thomasond6277762012-02-22 16:00:12 -08001404 return errorID;
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001405 }
1406
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -08001407 ParseDeep( charBuffer + (p-charBuffer), 0 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001408 return errorID;
1409}
1410
1411
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001412void XMLDocument::SaveFile( const char* filename )
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001413{
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001414#if defined(_MSC_VER)
Lee Thomason (grinliz)5ce4d972012-02-26 21:14:23 -08001415#pragma warning ( push )
1416#pragma warning ( disable : 4996 ) // Fail to see a compelling reason why this should be deprecated.
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001417#endif
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001418 FILE* fp = fopen( filename, "w" );
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001419#if defined(_MSC_VER)
Lee Thomason (grinliz)5ce4d972012-02-26 21:14:23 -08001420#pragma warning ( pop )
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001421#endif
Lee Thomason7f7b1622012-03-24 12:49:03 -07001422 if ( fp ) {
1423 XMLPrinter stream( fp );
1424 Print( &stream );
1425 fclose( fp );
1426 }
1427 else {
1428 SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, filename, 0 );
1429 }
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001430}
1431
Lee Thomason1ff38e02012-02-14 18:18:16 -08001432
Lee Thomason7c913cd2012-01-26 18:32:34 -08001433int XMLDocument::Parse( const char* p )
Lee Thomason3f57d272012-01-11 15:30:03 -08001434{
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001435 DeleteChildren();
Lee Thomason18d68bd2012-01-26 18:17:26 -08001436 InitDocument();
1437
1438 if ( !p || !*p ) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -07001439 SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
Lee Thomasond6277762012-02-22 16:00:12 -08001440 return errorID;
Lee Thomason18d68bd2012-01-26 18:17:26 -08001441 }
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001442 p = XMLUtil::SkipWhiteSpace( p );
1443 p = XMLUtil::ReadBOM( p, &writeBOM );
1444 if ( !p || !*p ) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -07001445 SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
Lee Thomasond6277762012-02-22 16:00:12 -08001446 return errorID;
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001447 }
1448
Lee Thomason18d68bd2012-01-26 18:17:26 -08001449 size_t len = strlen( p );
1450 charBuffer = new char[ len+1 ];
1451 memcpy( charBuffer, p, len+1 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001452
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001453
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -08001454 ParseDeep( charBuffer, 0 );
Lee Thomason7c913cd2012-01-26 18:32:34 -08001455 return errorID;
Lee Thomason3f57d272012-01-11 15:30:03 -08001456}
1457
1458
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001459void XMLDocument::Print( XMLPrinter* streamer )
Lee Thomason3f57d272012-01-11 15:30:03 -08001460{
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001461 XMLPrinter stdStreamer( stdout );
Lee Thomason5cae8972012-01-24 18:03:07 -08001462 if ( !streamer )
1463 streamer = &stdStreamer;
Lee Thomason751da522012-02-10 08:50:51 -08001464 Accept( streamer );
Lee Thomason3f57d272012-01-11 15:30:03 -08001465}
1466
1467
Lee Thomason67d61312012-01-24 16:01:51 -08001468void XMLDocument::SetError( int error, const char* str1, const char* str2 )
1469{
Lee Thomason18d68bd2012-01-26 18:17:26 -08001470 errorID = error;
Lee Thomason18d68bd2012-01-26 18:17:26 -08001471 errorStr1 = str1;
1472 errorStr2 = str2;
Lee Thomason67d61312012-01-24 16:01:51 -08001473}
1474
Lee Thomason5cae8972012-01-24 18:03:07 -08001475
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001476void XMLDocument::PrintError() const
1477{
1478 if ( errorID ) {
Lee Thomason (grinliz)5ce4d972012-02-26 21:14:23 -08001479 static const int LEN = 20;
1480 char buf1[LEN] = { 0 };
1481 char buf2[LEN] = { 0 };
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001482
1483 if ( errorStr1 ) {
Lee Thomason (grinliz)5ce4d972012-02-26 21:14:23 -08001484 TIXML_SNPRINTF( buf1, LEN, "%s", errorStr1 );
1485 buf1[LEN-1] = 0;
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001486 }
1487 if ( errorStr2 ) {
Lee Thomason (grinliz)5ce4d972012-02-26 21:14:23 -08001488 TIXML_SNPRINTF( buf2, LEN, "%s", errorStr2 );
1489 buf2[LEN-1] = 0;
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001490 }
1491
1492 printf( "XMLDocument error id=%d str1=%s str2=%s\n",
1493 errorID, buf1, buf2 );
1494 }
1495}
1496
1497
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001498XMLPrinter::XMLPrinter( FILE* file ) :
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001499 elementJustOpened( false ),
1500 firstElement( true ),
1501 fp( file ),
1502 depth( 0 ),
Lee Thomason6f381b72012-03-02 12:59:39 -08001503 textDepth( -1 ),
1504 processEntities( true )
Lee Thomason5cae8972012-01-24 18:03:07 -08001505{
Lee Thomason857b8682012-01-25 17:50:25 -08001506 for( int i=0; i<ENTITY_RANGE; ++i ) {
1507 entityFlag[i] = false;
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001508 restrictedEntityFlag[i] = false;
Lee Thomason857b8682012-01-25 17:50:25 -08001509 }
1510 for( int i=0; i<NUM_ENTITIES; ++i ) {
1511 TIXMLASSERT( entities[i].value < ENTITY_RANGE );
1512 if ( entities[i].value < ENTITY_RANGE ) {
Lee Thomason (grinliz)9b093cc2012-02-25 21:30:18 -08001513 entityFlag[ (int)entities[i].value ] = true;
Lee Thomason857b8682012-01-25 17:50:25 -08001514 }
1515 }
Lee Thomason (grinliz)9b093cc2012-02-25 21:30:18 -08001516 restrictedEntityFlag[(int)'&'] = true;
1517 restrictedEntityFlag[(int)'<'] = true;
1518 restrictedEntityFlag[(int)'>'] = true; // not required, but consistency is nice
U-Stream\Leeae25a442012-02-17 17:48:16 -08001519 buffer.Push( 0 );
1520}
1521
1522
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001523void XMLPrinter::Print( const char* format, ... )
U-Stream\Leeae25a442012-02-17 17:48:16 -08001524{
1525 va_list va;
1526 va_start( va, format );
1527
1528 if ( fp ) {
1529 vfprintf( fp, format, va );
1530 }
1531 else {
1532 // This seems brutally complex. Haven't figured out a better
1533 // way on windows.
1534 #ifdef _MSC_VER
1535 int len = -1;
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001536 int expand = 1000;
U-Stream\Leeae25a442012-02-17 17:48:16 -08001537 while ( len < 0 ) {
1538 len = vsnprintf_s( accumulator.Mem(), accumulator.Capacity(), accumulator.Capacity()-1, format, va );
1539 if ( len < 0 ) {
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001540 accumulator.PushArr( expand );
1541 expand *= 3/2;
U-Stream\Leeae25a442012-02-17 17:48:16 -08001542 }
1543 }
1544 char* p = buffer.PushArr( len ) - 1;
1545 memcpy( p, accumulator.Mem(), len+1 );
1546 #else
1547 int len = vsnprintf( 0, 0, format, va );
Lee Thomason4de93472012-03-13 17:33:35 -07001548 // Close out and re-start the va-args
1549 va_end( va );
1550 va_start( va, format );
U-Stream\Leeae25a442012-02-17 17:48:16 -08001551 char* p = buffer.PushArr( len ) - 1;
Lee Thomason (grinliz)9b093cc2012-02-25 21:30:18 -08001552 vsnprintf( p, len+1, format, va );
U-Stream\Leeae25a442012-02-17 17:48:16 -08001553 #endif
1554 }
1555 va_end( va );
Lee Thomason5cae8972012-01-24 18:03:07 -08001556}
1557
1558
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001559void XMLPrinter::PrintSpace( int depth )
Lee Thomason5cae8972012-01-24 18:03:07 -08001560{
1561 for( int i=0; i<depth; ++i ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001562 Print( " " );
Lee Thomason5cae8972012-01-24 18:03:07 -08001563 }
1564}
1565
1566
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001567void XMLPrinter::PrintString( const char* p, bool restricted )
Lee Thomason857b8682012-01-25 17:50:25 -08001568{
Lee Thomason951d8832012-01-26 08:47:06 -08001569 // Look for runs of bytes between entities to print.
1570 const char* q = p;
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001571 const bool* flag = restricted ? restrictedEntityFlag : entityFlag;
Lee Thomason857b8682012-01-25 17:50:25 -08001572
Lee Thomason6f381b72012-03-02 12:59:39 -08001573 if ( processEntities ) {
1574 while ( *q ) {
1575 // Remember, char is sometimes signed. (How many times has that bitten me?)
1576 if ( *q > 0 && *q < ENTITY_RANGE ) {
1577 // Check for entities. If one is found, flush
1578 // the stream up until the entity, write the
1579 // entity, and keep looking.
1580 if ( flag[*q] ) {
1581 while ( p < q ) {
1582 Print( "%c", *p );
1583 ++p;
1584 }
1585 for( int i=0; i<NUM_ENTITIES; ++i ) {
1586 if ( entities[i].value == *q ) {
1587 Print( "&%s;", entities[i].pattern );
1588 break;
1589 }
1590 }
Lee Thomason951d8832012-01-26 08:47:06 -08001591 ++p;
1592 }
Lee Thomason951d8832012-01-26 08:47:06 -08001593 }
Lee Thomason6f381b72012-03-02 12:59:39 -08001594 ++q;
Lee Thomason951d8832012-01-26 08:47:06 -08001595 }
Lee Thomason951d8832012-01-26 08:47:06 -08001596 }
1597 // Flush the remaining string. This will be the entire
1598 // string if an entity wasn't found.
Lee Thomason6f381b72012-03-02 12:59:39 -08001599 if ( !processEntities || (q-p > 0) ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001600 Print( "%s", p );
Lee Thomason951d8832012-01-26 08:47:06 -08001601 }
Lee Thomason857b8682012-01-25 17:50:25 -08001602}
1603
U-Stream\Leeae25a442012-02-17 17:48:16 -08001604
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001605void XMLPrinter::PushHeader( bool writeBOM, bool writeDec )
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001606{
1607 static const unsigned char bom[] = { TIXML_UTF_LEAD_0, TIXML_UTF_LEAD_1, TIXML_UTF_LEAD_2, 0 };
1608 if ( writeBOM ) {
1609 Print( "%s", bom );
1610 }
1611 if ( writeDec ) {
1612 PushDeclaration( "xml version=\"1.0\"" );
1613 }
1614}
1615
1616
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001617void XMLPrinter::OpenElement( const char* name )
Lee Thomason5cae8972012-01-24 18:03:07 -08001618{
1619 if ( elementJustOpened ) {
1620 SealElement();
1621 }
Lee Thomason56bdd022012-02-09 18:16:58 -08001622 stack.Push( name );
1623
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001624 if ( textDepth < 0 && !firstElement ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001625 Print( "\n" );
Lee Thomason24767b02012-01-25 17:16:23 -08001626 PrintSpace( depth );
1627 }
Lee Thomason5cae8972012-01-24 18:03:07 -08001628
U-Stream\Leeae25a442012-02-17 17:48:16 -08001629 Print( "<%s", name );
Lee Thomason5cae8972012-01-24 18:03:07 -08001630 elementJustOpened = true;
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001631 firstElement = false;
Lee Thomason5cae8972012-01-24 18:03:07 -08001632 ++depth;
1633}
1634
1635
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001636void XMLPrinter::PushAttribute( const char* name, const char* value )
Lee Thomason5cae8972012-01-24 18:03:07 -08001637{
1638 TIXMLASSERT( elementJustOpened );
U-Stream\Leeae25a442012-02-17 17:48:16 -08001639 Print( " %s=\"", name );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001640 PrintString( value, false );
U-Stream\Leeae25a442012-02-17 17:48:16 -08001641 Print( "\"" );
Lee Thomason5cae8972012-01-24 18:03:07 -08001642}
1643
1644
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001645void XMLPrinter::PushAttribute( const char* name, int v )
1646{
1647 char buf[BUF_SIZE];
1648 TIXML_SNPRINTF( buf, BUF_SIZE-1, "%d", v );
1649 PushAttribute( name, buf );
1650}
1651
1652
1653void XMLPrinter::PushAttribute( const char* name, unsigned v )
1654{
1655 char buf[BUF_SIZE];
1656 TIXML_SNPRINTF( buf, BUF_SIZE-1, "%u", v );
1657 PushAttribute( name, buf );
1658}
1659
1660
1661void XMLPrinter::PushAttribute( const char* name, bool v )
1662{
1663 char buf[BUF_SIZE];
1664 TIXML_SNPRINTF( buf, BUF_SIZE-1, "%d", v ? 1 : 0 );
1665 PushAttribute( name, buf );
1666}
1667
1668
1669void XMLPrinter::PushAttribute( const char* name, double v )
1670{
1671 char buf[BUF_SIZE];
1672 TIXML_SNPRINTF( buf, BUF_SIZE-1, "%f", v );
1673 PushAttribute( name, buf );
1674}
1675
1676
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001677void XMLPrinter::CloseElement()
Lee Thomason5cae8972012-01-24 18:03:07 -08001678{
1679 --depth;
1680 const char* name = stack.Pop();
Lee Thomason5cae8972012-01-24 18:03:07 -08001681
1682 if ( elementJustOpened ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001683 Print( "/>" );
Lee Thomason5cae8972012-01-24 18:03:07 -08001684 }
1685 else {
Lee Thomason56bdd022012-02-09 18:16:58 -08001686 if ( textDepth < 0 ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001687 Print( "\n" );
Lee Thomason24767b02012-01-25 17:16:23 -08001688 PrintSpace( depth );
1689 }
U-Stream\Leeae25a442012-02-17 17:48:16 -08001690 Print( "</%s>", name );
Lee Thomason5cae8972012-01-24 18:03:07 -08001691 }
Lee Thomason56bdd022012-02-09 18:16:58 -08001692
1693 if ( textDepth == depth )
1694 textDepth = -1;
1695 if ( depth == 0 )
U-Stream\Leeae25a442012-02-17 17:48:16 -08001696 Print( "\n" );
Lee Thomason5cae8972012-01-24 18:03:07 -08001697 elementJustOpened = false;
1698}
1699
1700
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001701void XMLPrinter::SealElement()
Lee Thomason5cae8972012-01-24 18:03:07 -08001702{
1703 elementJustOpened = false;
U-Stream\Leeae25a442012-02-17 17:48:16 -08001704 Print( ">" );
Lee Thomason5cae8972012-01-24 18:03:07 -08001705}
1706
1707
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001708void XMLPrinter::PushText( const char* text, bool cdata )
Lee Thomason5cae8972012-01-24 18:03:07 -08001709{
Lee Thomason56bdd022012-02-09 18:16:58 -08001710 textDepth = depth-1;
1711
Lee Thomason5cae8972012-01-24 18:03:07 -08001712 if ( elementJustOpened ) {
1713 SealElement();
1714 }
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001715 if ( cdata ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001716 Print( "<![CDATA[" );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001717 Print( "%s", text );
U-Stream\Leeae25a442012-02-17 17:48:16 -08001718 Print( "]]>" );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001719 }
1720 else {
1721 PrintString( text, true );
1722 }
Lee Thomason5cae8972012-01-24 18:03:07 -08001723}
1724
1725
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001726void XMLPrinter::PushComment( const char* comment )
Lee Thomason5cae8972012-01-24 18:03:07 -08001727{
1728 if ( elementJustOpened ) {
1729 SealElement();
1730 }
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001731 if ( textDepth < 0 && !firstElement ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001732 Print( "\n" );
U-Stream\Lee09a11c52012-02-17 08:31:16 -08001733 PrintSpace( depth );
1734 }
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001735 firstElement = false;
U-Stream\Leeae25a442012-02-17 17:48:16 -08001736 Print( "<!--%s-->", comment );
Lee Thomason5cae8972012-01-24 18:03:07 -08001737}
Lee Thomason751da522012-02-10 08:50:51 -08001738
1739
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001740void XMLPrinter::PushDeclaration( const char* value )
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001741{
1742 if ( elementJustOpened ) {
1743 SealElement();
1744 }
1745 if ( textDepth < 0 && !firstElement) {
1746 Print( "\n" );
1747 PrintSpace( depth );
1748 }
1749 firstElement = false;
1750 Print( "<?%s?>", value );
1751}
1752
1753
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001754void XMLPrinter::PushUnknown( const char* value )
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001755{
1756 if ( elementJustOpened ) {
1757 SealElement();
1758 }
1759 if ( textDepth < 0 && !firstElement ) {
1760 Print( "\n" );
1761 PrintSpace( depth );
1762 }
1763 firstElement = false;
1764 Print( "<!%s>", value );
1765}
1766
1767
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001768bool XMLPrinter::VisitEnter( const XMLDocument& doc )
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001769{
Lee Thomason6f381b72012-03-02 12:59:39 -08001770 processEntities = doc.ProcessEntities();
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001771 if ( doc.HasBOM() ) {
1772 PushHeader( true, false );
1773 }
1774 return true;
1775}
1776
1777
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001778bool XMLPrinter::VisitEnter( const XMLElement& element, const XMLAttribute* attribute )
Lee Thomason751da522012-02-10 08:50:51 -08001779{
1780 OpenElement( element.Name() );
1781 while ( attribute ) {
1782 PushAttribute( attribute->Name(), attribute->Value() );
1783 attribute = attribute->Next();
1784 }
1785 return true;
1786}
1787
1788
Lee Thomason (grinliz)9b093cc2012-02-25 21:30:18 -08001789bool XMLPrinter::VisitExit( const XMLElement& )
Lee Thomason751da522012-02-10 08:50:51 -08001790{
1791 CloseElement();
1792 return true;
1793}
1794
1795
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001796bool XMLPrinter::Visit( const XMLText& text )
Lee Thomason751da522012-02-10 08:50:51 -08001797{
Lee Thomasond6277762012-02-22 16:00:12 -08001798 PushText( text.Value(), text.CData() );
Lee Thomason751da522012-02-10 08:50:51 -08001799 return true;
1800}
1801
1802
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001803bool XMLPrinter::Visit( const XMLComment& comment )
Lee Thomason751da522012-02-10 08:50:51 -08001804{
1805 PushComment( comment.Value() );
1806 return true;
1807}
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001808
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001809bool XMLPrinter::Visit( const XMLDeclaration& declaration )
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001810{
1811 PushDeclaration( declaration.Value() );
1812 return true;
1813}
1814
1815
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001816bool XMLPrinter::Visit( const XMLUnknown& unknown )
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001817{
1818 PushUnknown( unknown.Value() );
1819 return true;
1820}