blob: b0be17ce68d39d502c0de3c0a80ad0d4eada845d [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
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -070026#include <cstdarg>
27#include <cstdio>
28#include <cstdlib>
Lee Thomason (grinliz)9b093cc2012-02-25 21:30:18 -080029#include <new>
U-Lama\Lee560bd472011-12-28 19:42:49 -080030
31using namespace tinyxml2;
32
Lee Thomasone4422302012-01-20 17:59:50 -080033static const char LINE_FEED = (char)0x0a; // all line endings are normalized to LF
Lee Thomasonfde6a752012-01-14 18:08:12 -080034static const char LF = LINE_FEED;
35static const char CARRIAGE_RETURN = (char)0x0d; // CR gets filtered out
36static const char CR = CARRIAGE_RETURN;
Lee Thomasone4422302012-01-20 17:59:50 -080037static const char SINGLE_QUOTE = '\'';
38static const char DOUBLE_QUOTE = '\"';
Lee Thomasonfde6a752012-01-14 18:08:12 -080039
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -080040// Bunch of unicode info at:
41// http://www.unicode.org/faq/utf_bom.html
42// ef bb bf (Microsoft "lead bytes") - designates UTF-8
43
44static const unsigned char TIXML_UTF_LEAD_0 = 0xefU;
45static const unsigned char TIXML_UTF_LEAD_1 = 0xbbU;
46static const unsigned char TIXML_UTF_LEAD_2 = 0xbfU;
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -080047
48
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -080049#define DELETE_NODE( node ) { \
50 if ( node ) { \
51 MemPool* pool = node->memPool; \
52 node->~XMLNode(); \
53 pool->Free( node ); \
54 } \
55}
56#define DELETE_ATTRIBUTE( attrib ) { \
57 if ( attrib ) { \
58 MemPool* pool = attrib->memPool; \
59 attrib->~XMLAttribute(); \
60 pool->Free( attrib ); \
61 } \
62}
Lee Thomason43f59302012-02-06 18:18:11 -080063
Lee Thomason8ee79892012-01-25 17:44:30 -080064struct Entity {
65 const char* pattern;
66 int length;
67 char value;
68};
69
70static const int NUM_ENTITIES = 5;
71static const Entity entities[NUM_ENTITIES] =
72{
Lee Thomason18d68bd2012-01-26 18:17:26 -080073 { "quot", 4, DOUBLE_QUOTE },
Lee Thomason8ee79892012-01-25 17:44:30 -080074 { "amp", 3, '&' },
Lee Thomason18d68bd2012-01-26 18:17:26 -080075 { "apos", 4, SINGLE_QUOTE },
Lee Thomason8ee79892012-01-25 17:44:30 -080076 { "lt", 2, '<' },
77 { "gt", 2, '>' }
78};
79
Lee Thomasonfde6a752012-01-14 18:08:12 -080080
Lee Thomason1a1d4a72012-02-15 09:09:25 -080081StrPair::~StrPair()
82{
83 Reset();
84}
85
86
87void StrPair::Reset()
88{
89 if ( flags & NEEDS_DELETE ) {
90 delete [] start;
91 }
92 flags = 0;
93 start = 0;
94 end = 0;
95}
96
97
98void StrPair::SetStr( const char* str, int flags )
99{
100 Reset();
101 size_t len = strlen( str );
102 start = new char[ len+1 ];
U-Stream\Lee09a11c52012-02-17 08:31:16 -0800103 memcpy( start, str, len+1 );
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800104 end = start + len;
105 this->flags = flags | NEEDS_DELETE;
106}
107
108
109char* StrPair::ParseText( char* p, const char* endTag, int strFlags )
110{
111 TIXMLASSERT( endTag && *endTag );
112
113 char* start = p; // fixme: hides a member
114 char endChar = *endTag;
115 int length = strlen( endTag );
116
117 // Inner loop of text parsing.
118 while ( *p ) {
119 if ( *p == endChar && strncmp( p, endTag, length ) == 0 ) {
120 Set( start, p, strFlags );
121 return p + length;
122 }
123 ++p;
124 }
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800125 return 0;
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800126}
127
128
129char* StrPair::ParseName( char* p )
130{
131 char* start = p;
132
133 start = p;
134 if ( !start || !(*start) ) {
135 return 0;
136 }
137
138 if ( !XMLUtil::IsAlpha( *p ) ) {
139 return 0;
140 }
141
142 while( *p && (
143 XMLUtil::IsAlphaNum( (unsigned char) *p )
144 || *p == '_'
145 || *p == '-'
146 || *p == '.'
147 || *p == ':' ))
148 {
149 ++p;
150 }
151
152 if ( p > start ) {
153 Set( start, p, 0 );
154 return p;
155 }
156 return 0;
157}
158
159
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800160
Lee Thomasone4422302012-01-20 17:59:50 -0800161const char* StrPair::GetStr()
162{
163 if ( flags & NEEDS_FLUSH ) {
164 *end = 0;
Lee Thomason8ee79892012-01-25 17:44:30 -0800165 flags ^= NEEDS_FLUSH;
Lee Thomasone4422302012-01-20 17:59:50 -0800166
Lee Thomason8ee79892012-01-25 17:44:30 -0800167 if ( flags ) {
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800168 char* p = start; // the read pointer
169 char* q = start; // the write pointer
Lee Thomasone4422302012-01-20 17:59:50 -0800170
171 while( p < end ) {
Lee Thomason8ee79892012-01-25 17:44:30 -0800172 if ( (flags & NEEDS_NEWLINE_NORMALIZATION) && *p == CR ) {
Lee Thomasone4422302012-01-20 17:59:50 -0800173 // CR-LF pair becomes LF
174 // CR alone becomes LF
175 // LF-CR becomes LF
176 if ( *(p+1) == LF ) {
177 p += 2;
178 }
179 else {
180 ++p;
181 }
Lee Thomasone9ecdab2012-02-13 18:11:20 -0800182 *q++ = LF;
Lee Thomasone4422302012-01-20 17:59:50 -0800183 }
Lee Thomason8ee79892012-01-25 17:44:30 -0800184 else if ( (flags & NEEDS_NEWLINE_NORMALIZATION) && *p == LF ) {
Lee Thomasone4422302012-01-20 17:59:50 -0800185 if ( *(p+1) == CR ) {
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_ENTITY_PROCESSING) && *p == '&' ) {
194 int i=0;
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800195
196 // Entities handled by tinyXML2:
197 // - special entities in the entity table [in/out]
198 // - numeric character reference [in]
199 // &#20013; or &#x4e2d;
200
201 if ( *(p+1) == '#' ) {
202 char buf[10] = { 0 };
203 int len;
204 p = const_cast<char*>( XMLUtil::GetCharacterRef( p, buf, &len ) );
205 for( int i=0; i<len; ++i ) {
206 *q++ = buf[i];
Lee Thomason8ee79892012-01-25 17:44:30 -0800207 }
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800208 TIXMLASSERT( q <= p );
Lee Thomason8ee79892012-01-25 17:44:30 -0800209 }
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800210 else {
211 for( i=0; i<NUM_ENTITIES; ++i ) {
212 if ( strncmp( p+1, entities[i].pattern, entities[i].length ) == 0
213 && *(p+entities[i].length+1) == ';' )
214 {
215 // Found an entity convert;
216 *q = entities[i].value;
217 ++q;
218 p += entities[i].length + 2;
219 break;
220 }
221 }
222 if ( i == NUM_ENTITIES ) {
223 // fixme: treat as error?
224 ++p;
225 ++q;
226 }
Lee Thomason8ee79892012-01-25 17:44:30 -0800227 }
228 }
Lee Thomasone4422302012-01-20 17:59:50 -0800229 else {
230 *q = *p;
231 ++p;
Lee Thomasonec975ce2012-01-23 11:42:06 -0800232 ++q;
Lee Thomasone4422302012-01-20 17:59:50 -0800233 }
234 }
Lee Thomason8ee79892012-01-25 17:44:30 -0800235 *q = 0;
Lee Thomasone4422302012-01-20 17:59:50 -0800236 }
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800237 flags = (flags & NEEDS_DELETE);
Lee Thomasone4422302012-01-20 17:59:50 -0800238 }
239 return start;
240}
241
Lee Thomason2c85a712012-01-31 08:24:24 -0800242
Lee Thomasone4422302012-01-20 17:59:50 -0800243
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800244
Lee Thomason56bdd022012-02-09 18:16:58 -0800245// --------- XMLUtil ----------- //
Lee Thomasond1983222012-02-06 08:41:24 -0800246
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800247const char* XMLUtil::ReadBOM( const char* p, bool* bom )
248{
249 *bom = false;
250 const unsigned char* pu = reinterpret_cast<const unsigned char*>(p);
251 // Check for BOM:
252 if ( *(pu+0) == TIXML_UTF_LEAD_0
253 && *(pu+1) == TIXML_UTF_LEAD_1
254 && *(pu+2) == TIXML_UTF_LEAD_2 )
255 {
256 *bom = true;
257 p += 3;
258 }
259 return p;
260}
261
262
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800263void XMLUtil::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length )
264{
265 const unsigned long BYTE_MASK = 0xBF;
266 const unsigned long BYTE_MARK = 0x80;
267 const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
268
269 if (input < 0x80)
270 *length = 1;
271 else if ( input < 0x800 )
272 *length = 2;
273 else if ( input < 0x10000 )
274 *length = 3;
275 else if ( input < 0x200000 )
276 *length = 4;
277 else
278 { *length = 0; return; } // This code won't covert this correctly anyway.
279
280 output += *length;
281
282 // Scary scary fall throughs.
283 switch (*length)
284 {
285 case 4:
286 --output;
287 *output = (char)((input | BYTE_MARK) & BYTE_MASK);
288 input >>= 6;
289 case 3:
290 --output;
291 *output = (char)((input | BYTE_MARK) & BYTE_MASK);
292 input >>= 6;
293 case 2:
294 --output;
295 *output = (char)((input | BYTE_MARK) & BYTE_MASK);
296 input >>= 6;
297 case 1:
298 --output;
299 *output = (char)(input | FIRST_BYTE_MARK[*length]);
300 }
301}
302
303
304const char* XMLUtil::GetCharacterRef( const char* p, char* value, int* length )
305{
306 // Presume an entity, and pull it out.
307 *length = 0;
308
309 if ( *(p+1) == '#' && *(p+2) )
310 {
311 unsigned long ucs = 0;
Lee Thomason (grinliz)7ca55582012-03-07 21:54:57 -0800312 int delta = 0;
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800313 unsigned mult = 1;
314
315 if ( *(p+2) == 'x' )
316 {
317 // Hexadecimal.
318 if ( !*(p+3) ) return 0;
319
320 const char* q = p+3;
321 q = strchr( q, ';' );
322
323 if ( !q || !*q ) return 0;
324
Lee Thomason (grinliz)7ca55582012-03-07 21:54:57 -0800325 delta = (q-p);
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800326 --q;
327
328 while ( *q != 'x' )
329 {
330 if ( *q >= '0' && *q <= '9' )
331 ucs += mult * (*q - '0');
332 else if ( *q >= 'a' && *q <= 'f' )
333 ucs += mult * (*q - 'a' + 10);
334 else if ( *q >= 'A' && *q <= 'F' )
335 ucs += mult * (*q - 'A' + 10 );
336 else
337 return 0;
338 mult *= 16;
339 --q;
340 }
341 }
342 else
343 {
344 // Decimal.
345 if ( !*(p+2) ) return 0;
346
347 const char* q = p+2;
348 q = strchr( q, ';' );
349
350 if ( !q || !*q ) return 0;
351
352 delta = q-p;
353 --q;
354
355 while ( *q != '#' )
356 {
357 if ( *q >= '0' && *q <= '9' )
358 ucs += mult * (*q - '0');
359 else
360 return 0;
361 mult *= 10;
362 --q;
363 }
364 }
365 // convert the UCS to UTF-8
366 ConvertUTF32ToUTF8( ucs, value, length );
367 return p + delta + 1;
368 }
369 return p+1;
370}
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800371
372
Lee Thomasond1983222012-02-06 08:41:24 -0800373char* XMLDocument::Identify( char* p, XMLNode** node )
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800374{
375 XMLNode* returnNode = 0;
Lee Thomason5492a1c2012-01-23 15:32:10 -0800376 char* start = p;
Lee Thomason56bdd022012-02-09 18:16:58 -0800377 p = XMLUtil::SkipWhiteSpace( p );
Lee Thomason5492a1c2012-01-23 15:32:10 -0800378 if( !p || !*p )
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800379 {
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800380 return p;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800381 }
382
383 // What is this thing?
384 // - Elements start with a letter or underscore, but xml is reserved.
385 // - Comments: <!--
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800386 // - Decleration: <?
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800387 // - Everthing else is unknown to tinyxml.
388 //
389
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800390 static const char* xmlHeader = { "<?" };
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800391 static const char* commentHeader = { "<!--" };
392 static const char* dtdHeader = { "<!" };
393 static const char* cdataHeader = { "<![CDATA[" };
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800394 static const char* elementHeader = { "<" }; // and a header for everything else; check last.
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800395
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800396 static const int xmlHeaderLen = 2;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800397 static const int commentHeaderLen = 4;
398 static const int dtdHeaderLen = 2;
399 static const int cdataHeaderLen = 9;
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800400 static const int elementHeaderLen = 1;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800401
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800402#if defined(_MSC_VER)
Lee Thomason (grinliz)9b093cc2012-02-25 21:30:18 -0800403#pragma warning ( push )
404#pragma warning ( disable : 4127 )
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800405#endif
Lee Thomason50f97b22012-02-11 16:33:40 -0800406 TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLUnknown ) ); // use same memory pool
407 TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLDeclaration ) ); // use same memory pool
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800408#if defined(_MSC_VER)
Lee Thomason (grinliz)9b093cc2012-02-25 21:30:18 -0800409#pragma warning (pop)
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800410#endif
Lee Thomason50f97b22012-02-11 16:33:40 -0800411 if ( XMLUtil::StringEqual( p, xmlHeader, xmlHeaderLen ) ) {
412 returnNode = new (commentPool.Alloc()) XMLDeclaration( this );
413 returnNode->memPool = &commentPool;
414 p += xmlHeaderLen;
415 }
416 else if ( XMLUtil::StringEqual( p, commentHeader, commentHeaderLen ) ) {
Lee Thomasond1983222012-02-06 08:41:24 -0800417 returnNode = new (commentPool.Alloc()) XMLComment( this );
418 returnNode->memPool = &commentPool;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800419 p += commentHeaderLen;
420 }
Lee Thomason50f97b22012-02-11 16:33:40 -0800421 else if ( XMLUtil::StringEqual( p, cdataHeader, cdataHeaderLen ) ) {
422 XMLText* text = new (textPool.Alloc()) XMLText( this );
423 returnNode = text;
424 returnNode->memPool = &textPool;
425 p += cdataHeaderLen;
426 text->SetCData( true );
427 }
428 else if ( XMLUtil::StringEqual( p, dtdHeader, dtdHeaderLen ) ) {
429 returnNode = new (commentPool.Alloc()) XMLUnknown( this );
430 returnNode->memPool = &commentPool;
431 p += dtdHeaderLen;
432 }
Lee Thomason56bdd022012-02-09 18:16:58 -0800433 else if ( XMLUtil::StringEqual( p, elementHeader, elementHeaderLen ) ) {
Lee Thomasond1983222012-02-06 08:41:24 -0800434 returnNode = new (elementPool.Alloc()) XMLElement( this );
435 returnNode->memPool = &elementPool;
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800436 p += elementHeaderLen;
437 }
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800438 else {
Lee Thomasond1983222012-02-06 08:41:24 -0800439 returnNode = new (textPool.Alloc()) XMLText( this );
440 returnNode->memPool = &textPool;
Lee Thomason5492a1c2012-01-23 15:32:10 -0800441 p = start; // Back it up, all the text counts.
442 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800443
444 *node = returnNode;
445 return p;
446}
447
448
Lee Thomason751da522012-02-10 08:50:51 -0800449bool XMLDocument::Accept( XMLVisitor* visitor ) const
450{
451 if ( visitor->VisitEnter( *this ) )
452 {
453 for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() )
454 {
455 if ( !node->Accept( visitor ) )
456 break;
457 }
458 }
459 return visitor->VisitExit( *this );
460}
Lee Thomason56bdd022012-02-09 18:16:58 -0800461
462
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800463// --------- XMLNode ----------- //
464
465XMLNode::XMLNode( XMLDocument* doc ) :
466 document( doc ),
467 parent( 0 ),
468 firstChild( 0 ), lastChild( 0 ),
469 prev( 0 ), next( 0 )
470{
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800471}
472
473
474XMLNode::~XMLNode()
475{
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -0800476 DeleteChildren();
Lee Thomason7c913cd2012-01-26 18:32:34 -0800477 if ( parent ) {
478 parent->Unlink( this );
479 }
Lee Thomason18d68bd2012-01-26 18:17:26 -0800480}
481
482
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800483void XMLNode::SetValue( const char* str, bool staticMem )
484{
485 if ( staticMem )
486 value.SetInternedStr( str );
487 else
488 value.SetStr( str );
489}
490
491
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -0800492void XMLNode::DeleteChildren()
Lee Thomason18d68bd2012-01-26 18:17:26 -0800493{
Lee Thomasond923c672012-01-23 08:44:25 -0800494 while( firstChild ) {
495 XMLNode* node = firstChild;
496 Unlink( node );
Lee Thomasond1983222012-02-06 08:41:24 -0800497
Lee Thomason43f59302012-02-06 18:18:11 -0800498 DELETE_NODE( node );
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800499 }
Lee Thomason18d68bd2012-01-26 18:17:26 -0800500 firstChild = lastChild = 0;
Lee Thomasond923c672012-01-23 08:44:25 -0800501}
502
503
504void XMLNode::Unlink( XMLNode* child )
505{
506 TIXMLASSERT( child->parent == this );
507 if ( child == firstChild )
508 firstChild = firstChild->next;
509 if ( child == lastChild )
510 lastChild = lastChild->prev;
511
512 if ( child->prev ) {
513 child->prev->next = child->next;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800514 }
Lee Thomasond923c672012-01-23 08:44:25 -0800515 if ( child->next ) {
516 child->next->prev = child->prev;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800517 }
Lee Thomasond923c672012-01-23 08:44:25 -0800518 child->parent = 0;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800519}
520
521
U-Stream\Leeae25a442012-02-17 17:48:16 -0800522void XMLNode::DeleteChild( XMLNode* node )
523{
524 TIXMLASSERT( node->parent == this );
525 DELETE_NODE( node );
526}
527
528
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800529XMLNode* XMLNode::InsertEndChild( XMLNode* addThis )
530{
531 if ( lastChild ) {
532 TIXMLASSERT( firstChild );
533 TIXMLASSERT( lastChild->next == 0 );
534 lastChild->next = addThis;
535 addThis->prev = lastChild;
536 lastChild = addThis;
537
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800538 addThis->next = 0;
539 }
540 else {
541 TIXMLASSERT( firstChild == 0 );
542 firstChild = lastChild = addThis;
543
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800544 addThis->prev = 0;
545 addThis->next = 0;
546 }
U-Stream\Lee09a11c52012-02-17 08:31:16 -0800547 addThis->parent = this;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800548 return addThis;
549}
550
551
Lee Thomason1ff38e02012-02-14 18:18:16 -0800552XMLNode* XMLNode::InsertFirstChild( XMLNode* addThis )
553{
554 if ( firstChild ) {
555 TIXMLASSERT( lastChild );
556 TIXMLASSERT( firstChild->prev == 0 );
557
558 firstChild->prev = addThis;
559 addThis->next = firstChild;
560 firstChild = addThis;
561
Lee Thomason1ff38e02012-02-14 18:18:16 -0800562 addThis->prev = 0;
563 }
564 else {
565 TIXMLASSERT( lastChild == 0 );
566 firstChild = lastChild = addThis;
567
Lee Thomason1ff38e02012-02-14 18:18:16 -0800568 addThis->prev = 0;
569 addThis->next = 0;
570 }
U-Stream\Lee09a11c52012-02-17 08:31:16 -0800571 addThis->parent = this;
Lee Thomason1ff38e02012-02-14 18:18:16 -0800572 return addThis;
573}
574
575
576XMLNode* XMLNode::InsertAfterChild( XMLNode* afterThis, XMLNode* addThis )
577{
578 TIXMLASSERT( afterThis->parent == this );
579 if ( afterThis->parent != this )
580 return 0;
581
582 if ( afterThis->next == 0 ) {
583 // The last node or the only node.
584 return InsertEndChild( addThis );
585 }
586 addThis->prev = afterThis;
587 addThis->next = afterThis->next;
588 afterThis->next->prev = addThis;
589 afterThis->next = addThis;
U-Stream\Lee09a11c52012-02-17 08:31:16 -0800590 addThis->parent = this;
Lee Thomason1ff38e02012-02-14 18:18:16 -0800591 return addThis;
592}
593
594
595
596
Lee Thomason56bdd022012-02-09 18:16:58 -0800597const XMLElement* XMLNode::FirstChildElement( const char* value ) const
Lee Thomason2c85a712012-01-31 08:24:24 -0800598{
599 for( XMLNode* node=firstChild; node; node=node->next ) {
600 XMLElement* element = node->ToElement();
601 if ( element ) {
Lee Thomason56bdd022012-02-09 18:16:58 -0800602 if ( !value || XMLUtil::StringEqual( element->Name(), value ) ) {
Lee Thomason2c85a712012-01-31 08:24:24 -0800603 return element;
604 }
605 }
606 }
607 return 0;
608}
609
610
Lee Thomason56bdd022012-02-09 18:16:58 -0800611const XMLElement* XMLNode::LastChildElement( const char* value ) const
612{
613 for( XMLNode* node=lastChild; node; node=node->prev ) {
614 XMLElement* element = node->ToElement();
615 if ( element ) {
616 if ( !value || XMLUtil::StringEqual( element->Name(), value ) ) {
617 return element;
618 }
619 }
620 }
621 return 0;
622}
623
624
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800625const XMLElement* XMLNode::NextSiblingElement( const char* value ) const
626{
627 for( XMLNode* element=this->next; element; element = element->next ) {
628 if ( element->ToElement()
629 && (!value || XMLUtil::StringEqual( value, element->Value() )))
630 {
631 return element->ToElement();
632 }
633 }
634 return 0;
635}
636
637
638const XMLElement* XMLNode::PreviousSiblingElement( const char* value ) const
639{
640 for( XMLNode* element=this->prev; element; element = element->prev ) {
641 if ( element->ToElement()
642 && (!value || XMLUtil::StringEqual( value, element->Value() )))
643 {
644 return element->ToElement();
645 }
646 }
647 return 0;
648}
649
650
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800651char* XMLNode::ParseDeep( char* p, StrPair* parentEnd )
Lee Thomason67d61312012-01-24 16:01:51 -0800652{
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800653 // This is a recursive method, but thinking about it "at the current level"
654 // it is a pretty simple flat list:
655 // <foo/>
656 // <!-- comment -->
657 //
658 // With a special case:
659 // <foo>
660 // </foo>
661 // <!-- comment -->
662 //
663 // Where the closing element (/foo) *must* be the next thing after the opening
664 // element, and the names must match. BUT the tricky bit is that the closing
665 // element will be read by the child.
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800666 //
667 // 'endTag' is the end tag for this node, it is returned by a call to a child.
668 // 'parentEnd' is the end tag for the parent, which is filled in and returned.
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800669
Lee Thomason67d61312012-01-24 16:01:51 -0800670 while( p && *p ) {
671 XMLNode* node = 0;
Lee Thomasond6277762012-02-22 16:00:12 -0800672
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800673 p = document->Identify( p, &node );
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800674 if ( p == 0 || node == 0 ) {
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800675 break;
676 }
677
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800678 StrPair endTag;
679 p = node->ParseDeep( p, &endTag );
680 if ( !p ) {
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800681 DELETE_NODE( node );
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800682 node = 0;
Lee Thomason (grinliz)784607f2012-02-24 16:23:40 -0800683 if ( !document->Error() ) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -0700684 document->SetError( XML_ERROR_PARSING, 0, 0 );
Lee Thomason (grinliz)784607f2012-02-24 16:23:40 -0800685 }
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800686 break;
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800687 }
688
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800689 // We read the end tag. Return it to the parent.
690 if ( node->ToElement() && node->ToElement()->ClosingType() == XMLElement::CLOSING ) {
691 if ( parentEnd ) {
692 *parentEnd = ((XMLElement*)node)->value;
Lee Thomason67d61312012-01-24 16:01:51 -0800693 }
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800694 DELETE_NODE( node );
695 return p;
696 }
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800697
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800698 // Handle an end tag returned to this level.
699 // And handle a bunch of annoying errors.
700 XMLElement* ele = node->ToElement();
701 if ( ele ) {
702 if ( endTag.Empty() && ele->ClosingType() == XMLElement::OPEN ) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -0700703 document->SetError( XML_ERROR_MISMATCHED_ELEMENT, node->Value(), 0 );
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800704 p = 0;
705 }
706 else if ( !endTag.Empty() && ele->ClosingType() != XMLElement::OPEN ) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -0700707 document->SetError( XML_ERROR_MISMATCHED_ELEMENT, node->Value(), 0 );
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800708 p = 0;
709 }
710 else if ( !endTag.Empty() ) {
711 if ( !XMLUtil::StringEqual( endTag.GetStr(), node->Value() )) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -0700712 document->SetError( XML_ERROR_MISMATCHED_ELEMENT, node->Value(), 0 );
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800713 p = 0;
714 }
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800715 }
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800716 }
717 if ( p == 0 ) {
718 DELETE_NODE( node );
719 node = 0;
720 }
721 if ( node ) {
722 this->InsertEndChild( node );
Lee Thomason67d61312012-01-24 16:01:51 -0800723 }
724 }
725 return 0;
726}
727
Lee Thomason5492a1c2012-01-23 15:32:10 -0800728// --------- XMLText ---------- //
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800729char* XMLText::ParseDeep( char* p, StrPair* )
Lee Thomason5492a1c2012-01-23 15:32:10 -0800730{
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800731 const char* start = p;
Lee Thomason50f97b22012-02-11 16:33:40 -0800732 if ( this->CData() ) {
733 p = value.ParseText( p, "]]>", StrPair::NEEDS_NEWLINE_NORMALIZATION );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800734 if ( !p ) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -0700735 document->SetError( XML_ERROR_PARSING_CDATA, start, 0 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800736 }
Lee Thomason50f97b22012-02-11 16:33:40 -0800737 return p;
738 }
739 else {
Lee Thomason6f381b72012-03-02 12:59:39 -0800740 p = value.ParseText( p, "<", document->ProcessEntities() ? StrPair::TEXT_ELEMENT : StrPair::TEXT_ELEMENT_LEAVE_ENTITIES );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800741 if ( !p ) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -0700742 document->SetError( XML_ERROR_PARSING_TEXT, start, 0 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800743 }
Lee Thomason50f97b22012-02-11 16:33:40 -0800744 if ( p && *p ) {
745 return p-1;
746 }
Lee Thomason5492a1c2012-01-23 15:32:10 -0800747 }
748 return 0;
749}
750
751
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800752XMLNode* XMLText::ShallowClone( XMLDocument* doc ) const
753{
754 if ( !doc ) {
755 doc = document;
756 }
757 XMLText* text = doc->NewText( Value() ); // fixme: this will always allocate memory. Intern?
758 text->SetCData( this->CData() );
759 return text;
760}
761
762
763bool XMLText::ShallowEqual( const XMLNode* compare ) const
764{
765 return ( compare->ToText() && XMLUtil::StringEqual( compare->ToText()->Value(), Value() ));
766}
767
768
Lee Thomason56bdd022012-02-09 18:16:58 -0800769bool XMLText::Accept( XMLVisitor* visitor ) const
770{
771 return visitor->Visit( *this );
772}
773
774
Lee Thomason3f57d272012-01-11 15:30:03 -0800775// --------- XMLComment ---------- //
776
Lee Thomasone4422302012-01-20 17:59:50 -0800777XMLComment::XMLComment( XMLDocument* doc ) : XMLNode( doc )
Lee Thomason3f57d272012-01-11 15:30:03 -0800778{
779}
780
781
Lee Thomasonce0763e2012-01-11 15:43:54 -0800782XMLComment::~XMLComment()
Lee Thomason3f57d272012-01-11 15:30:03 -0800783{
Lee Thomasond923c672012-01-23 08:44:25 -0800784 //printf( "~XMLComment\n" );
Lee Thomason3f57d272012-01-11 15:30:03 -0800785}
786
787
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800788char* XMLComment::ParseDeep( char* p, StrPair* )
Lee Thomason3f57d272012-01-11 15:30:03 -0800789{
790 // Comment parses as text.
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800791 const char* start = p;
792 p = value.ParseText( p, "-->", StrPair::COMMENT );
793 if ( p == 0 ) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -0700794 document->SetError( XML_ERROR_PARSING_COMMENT, start, 0 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800795 }
796 return p;
U-Lama\Lee4cee6112011-12-31 14:58:18 -0800797}
798
799
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800800XMLNode* XMLComment::ShallowClone( XMLDocument* doc ) const
801{
802 if ( !doc ) {
803 doc = document;
804 }
805 XMLComment* comment = doc->NewComment( Value() ); // fixme: this will always allocate memory. Intern?
806 return comment;
807}
808
809
810bool XMLComment::ShallowEqual( const XMLNode* compare ) const
811{
812 return ( compare->ToComment() && XMLUtil::StringEqual( compare->ToComment()->Value(), Value() ));
813}
814
815
Lee Thomason751da522012-02-10 08:50:51 -0800816bool XMLComment::Accept( XMLVisitor* visitor ) const
817{
818 return visitor->Visit( *this );
819}
Lee Thomason56bdd022012-02-09 18:16:58 -0800820
821
Lee Thomason50f97b22012-02-11 16:33:40 -0800822// --------- XMLDeclaration ---------- //
823
824XMLDeclaration::XMLDeclaration( XMLDocument* doc ) : XMLNode( doc )
825{
826}
827
828
829XMLDeclaration::~XMLDeclaration()
830{
831 //printf( "~XMLDeclaration\n" );
832}
833
834
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800835char* XMLDeclaration::ParseDeep( char* p, StrPair* )
Lee Thomason50f97b22012-02-11 16:33:40 -0800836{
837 // Declaration parses as text.
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800838 const char* start = p;
839 p = value.ParseText( p, "?>", StrPair::NEEDS_NEWLINE_NORMALIZATION );
840 if ( p == 0 ) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -0700841 document->SetError( XML_ERROR_PARSING_DECLARATION, start, 0 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800842 }
843 return p;
Lee Thomason50f97b22012-02-11 16:33:40 -0800844}
845
846
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800847XMLNode* XMLDeclaration::ShallowClone( XMLDocument* doc ) const
848{
849 if ( !doc ) {
850 doc = document;
851 }
852 XMLDeclaration* dec = doc->NewDeclaration( Value() ); // fixme: this will always allocate memory. Intern?
853 return dec;
854}
855
856
857bool XMLDeclaration::ShallowEqual( const XMLNode* compare ) const
858{
859 return ( compare->ToDeclaration() && XMLUtil::StringEqual( compare->ToDeclaration()->Value(), Value() ));
860}
861
862
863
Lee Thomason50f97b22012-02-11 16:33:40 -0800864bool XMLDeclaration::Accept( XMLVisitor* visitor ) const
865{
866 return visitor->Visit( *this );
867}
868
869// --------- XMLUnknown ---------- //
870
871XMLUnknown::XMLUnknown( XMLDocument* doc ) : XMLNode( doc )
872{
873}
874
875
876XMLUnknown::~XMLUnknown()
877{
878}
879
880
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800881char* XMLUnknown::ParseDeep( char* p, StrPair* )
Lee Thomason50f97b22012-02-11 16:33:40 -0800882{
883 // Unknown parses as text.
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800884 const char* start = p;
885
886 p = value.ParseText( p, ">", StrPair::NEEDS_NEWLINE_NORMALIZATION );
887 if ( !p ) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -0700888 document->SetError( XML_ERROR_PARSING_UNKNOWN, start, 0 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800889 }
890 return p;
Lee Thomason50f97b22012-02-11 16:33:40 -0800891}
892
893
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800894XMLNode* XMLUnknown::ShallowClone( XMLDocument* doc ) const
895{
896 if ( !doc ) {
897 doc = document;
898 }
899 XMLUnknown* text = doc->NewUnknown( Value() ); // fixme: this will always allocate memory. Intern?
900 return text;
901}
902
903
904bool XMLUnknown::ShallowEqual( const XMLNode* compare ) const
905{
906 return ( compare->ToUnknown() && XMLUtil::StringEqual( compare->ToUnknown()->Value(), Value() ));
907}
908
909
Lee Thomason50f97b22012-02-11 16:33:40 -0800910bool XMLUnknown::Accept( XMLVisitor* visitor ) const
911{
912 return visitor->Visit( *this );
913}
914
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800915// --------- XMLAttribute ---------- //
Lee Thomason6f381b72012-03-02 12:59:39 -0800916char* XMLAttribute::ParseDeep( char* p, bool processEntities )
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800917{
Lee Thomason56bdd022012-02-09 18:16:58 -0800918 p = name.ParseText( p, "=", StrPair::ATTRIBUTE_NAME );
Lee Thomason22aead12012-01-23 13:29:35 -0800919 if ( !p || !*p ) return 0;
920
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800921 char endTag[2] = { *p, 0 };
922 ++p;
Lee Thomason6f381b72012-03-02 12:59:39 -0800923 p = value.ParseText( p, endTag, processEntities ? StrPair::ATTRIBUTE_VALUE : StrPair::ATTRIBUTE_VALUE_LEAVE_ENTITIES );
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800924 //if ( value.Empty() ) return 0;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800925 return p;
926}
927
928
U-Stream\Lee09a11c52012-02-17 08:31:16 -0800929void XMLAttribute::SetName( const char* n )
930{
931 name.SetStr( n );
932}
933
934
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800935int XMLAttribute::QueryIntValue( int* value ) const
Lee Thomason1ff38e02012-02-14 18:18:16 -0800936{
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800937 if ( TIXML_SSCANF( Value(), "%d", value ) == 1 )
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800938 return XML_NO_ERROR;
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -0700939 return XML_WRONG_ATTRIBUTE_TYPE;
Lee Thomason1ff38e02012-02-14 18:18:16 -0800940}
941
942
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800943int XMLAttribute::QueryUnsignedValue( unsigned int* value ) const
Lee Thomason1ff38e02012-02-14 18:18:16 -0800944{
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800945 if ( TIXML_SSCANF( Value(), "%u", value ) == 1 )
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800946 return XML_NO_ERROR;
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -0700947 return XML_WRONG_ATTRIBUTE_TYPE;
Lee Thomason1ff38e02012-02-14 18:18:16 -0800948}
949
950
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800951int XMLAttribute::QueryBoolValue( bool* value ) const
Lee Thomason1ff38e02012-02-14 18:18:16 -0800952{
953 int ival = -1;
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800954 QueryIntValue( &ival );
Lee Thomason1ff38e02012-02-14 18:18:16 -0800955
956 if ( ival > 0 || XMLUtil::StringEqual( Value(), "true" ) ) {
957 *value = true;
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800958 return XML_NO_ERROR;
Lee Thomason1ff38e02012-02-14 18:18:16 -0800959 }
960 else if ( ival == 0 || XMLUtil::StringEqual( Value(), "false" ) ) {
961 *value = false;
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800962 return XML_NO_ERROR;
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800963 }
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -0700964 return XML_WRONG_ATTRIBUTE_TYPE;
Lee Thomason1ff38e02012-02-14 18:18:16 -0800965}
966
967
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800968int XMLAttribute::QueryDoubleValue( double* value ) const
Lee Thomason1ff38e02012-02-14 18:18:16 -0800969{
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800970 if ( TIXML_SSCANF( Value(), "%lf", value ) == 1 )
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800971 return XML_NO_ERROR;
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -0700972 return XML_WRONG_ATTRIBUTE_TYPE;
Lee Thomason1ff38e02012-02-14 18:18:16 -0800973}
974
975
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800976int XMLAttribute::QueryFloatValue( float* value ) const
Lee Thomason1ff38e02012-02-14 18:18:16 -0800977{
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800978 if ( TIXML_SSCANF( Value(), "%f", value ) == 1 )
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800979 return XML_NO_ERROR;
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -0700980 return XML_WRONG_ATTRIBUTE_TYPE;
Lee Thomason1ff38e02012-02-14 18:18:16 -0800981}
982
983
984void XMLAttribute::SetAttribute( const char* v )
985{
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800986 value.SetStr( v );
Lee Thomason1ff38e02012-02-14 18:18:16 -0800987}
988
989
Lee Thomason1ff38e02012-02-14 18:18:16 -0800990void XMLAttribute::SetAttribute( int v )
991{
992 char buf[BUF_SIZE];
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800993 TIXML_SNPRINTF( buf, BUF_SIZE-1, "%d", v );
994 value.SetStr( buf );
Lee Thomason1ff38e02012-02-14 18:18:16 -0800995}
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800996
997
998void XMLAttribute::SetAttribute( unsigned v )
999{
1000 char buf[BUF_SIZE];
1001 TIXML_SNPRINTF( buf, BUF_SIZE-1, "%u", v );
1002 value.SetStr( buf );
1003}
1004
1005
1006void XMLAttribute::SetAttribute( bool v )
1007{
1008 char buf[BUF_SIZE];
1009 TIXML_SNPRINTF( buf, BUF_SIZE-1, "%d", v ? 1 : 0 );
1010 value.SetStr( buf );
1011}
1012
1013void XMLAttribute::SetAttribute( double v )
1014{
1015 char buf[BUF_SIZE];
1016 TIXML_SNPRINTF( buf, BUF_SIZE-1, "%f", v );
1017 value.SetStr( buf );
1018}
1019
1020void XMLAttribute::SetAttribute( float v )
1021{
1022 char buf[BUF_SIZE];
1023 TIXML_SNPRINTF( buf, BUF_SIZE-1, "%f", v );
1024 value.SetStr( buf );
1025}
1026
Lee Thomasondadcdfa2012-01-18 17:55:48 -08001027
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001028// --------- XMLElement ---------- //
1029XMLElement::XMLElement( XMLDocument* doc ) : XMLNode( doc ),
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -08001030 closingType( 0 ),
U-Stream\Lee09a11c52012-02-17 08:31:16 -08001031 rootAttribute( 0 )
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001032{
1033}
1034
1035
1036XMLElement::~XMLElement()
1037{
U-Stream\Lee09a11c52012-02-17 08:31:16 -08001038 while( rootAttribute ) {
1039 XMLAttribute* next = rootAttribute->next;
1040 DELETE_ATTRIBUTE( rootAttribute );
1041 rootAttribute = next;
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001042 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001043}
1044
1045
Lee Thomason1a1d4a72012-02-15 09:09:25 -08001046XMLAttribute* XMLElement::FindAttribute( const char* name )
1047{
1048 XMLAttribute* a = 0;
1049 for( a=rootAttribute; a; a = a->next ) {
1050 if ( XMLUtil::StringEqual( a->Name(), name ) )
1051 return a;
1052 }
1053 return 0;
1054}
1055
1056
1057const XMLAttribute* XMLElement::FindAttribute( const char* name ) const
1058{
1059 XMLAttribute* a = 0;
1060 for( a=rootAttribute; a; a = a->next ) {
1061 if ( XMLUtil::StringEqual( a->Name(), name ) )
1062 return a;
1063 }
1064 return 0;
1065}
1066
1067
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001068const char* XMLElement::GetText() const
1069{
1070 if ( FirstChild() && FirstChild()->ToText() ) {
1071 return FirstChild()->ToText()->Value();
1072 }
1073 return 0;
1074}
1075
1076
1077
Lee Thomason1a1d4a72012-02-15 09:09:25 -08001078XMLAttribute* XMLElement::FindOrCreateAttribute( const char* name )
1079{
1080 XMLAttribute* attrib = FindAttribute( name );
1081 if ( !attrib ) {
Lee Thomason (grinliz)9b093cc2012-02-25 21:30:18 -08001082 attrib = new (document->attributePool.Alloc() ) XMLAttribute();
Lee Thomason1a1d4a72012-02-15 09:09:25 -08001083 attrib->memPool = &document->attributePool;
U-Stream\Lee09a11c52012-02-17 08:31:16 -08001084 LinkAttribute( attrib );
1085 attrib->SetName( name );
Lee Thomason1a1d4a72012-02-15 09:09:25 -08001086 }
1087 return attrib;
1088}
1089
1090
U-Stream\Lee09a11c52012-02-17 08:31:16 -08001091void XMLElement::LinkAttribute( XMLAttribute* attrib )
1092{
1093 if ( rootAttribute ) {
1094 XMLAttribute* end = rootAttribute;
1095 while ( end->next )
1096 end = end->next;
1097 end->next = attrib;
1098 }
1099 else {
1100 rootAttribute = attrib;
1101 }
1102}
1103
1104
U-Stream\Leeae25a442012-02-17 17:48:16 -08001105void XMLElement::DeleteAttribute( const char* name )
1106{
1107 XMLAttribute* prev = 0;
1108 for( XMLAttribute* a=rootAttribute; a; a=a->next ) {
1109 if ( XMLUtil::StringEqual( name, a->Name() ) ) {
1110 if ( prev ) {
1111 prev->next = a->next;
1112 }
1113 else {
1114 rootAttribute = a->next;
1115 }
1116 DELETE_ATTRIBUTE( a );
1117 break;
1118 }
1119 prev = a;
1120 }
1121}
1122
1123
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -08001124char* XMLElement::ParseAttributes( char* p )
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001125{
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001126 const char* start = p;
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001127
1128 // Read the attributes.
1129 while( p ) {
Lee Thomason56bdd022012-02-09 18:16:58 -08001130 p = XMLUtil::SkipWhiteSpace( p );
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001131 if ( !p || !(*p) ) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -07001132 document->SetError( XML_ERROR_PARSING_ELEMENT, start, Name() );
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001133 return 0;
1134 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001135
1136 // attribute.
Lee Thomason56bdd022012-02-09 18:16:58 -08001137 if ( XMLUtil::IsAlpha( *p ) ) {
Lee Thomason (grinliz)9b093cc2012-02-25 21:30:18 -08001138 XMLAttribute* attrib = new (document->attributePool.Alloc() ) XMLAttribute();
Lee Thomason43f59302012-02-06 18:18:11 -08001139 attrib->memPool = &document->attributePool;
Lee Thomasond1983222012-02-06 08:41:24 -08001140
Lee Thomason6f381b72012-03-02 12:59:39 -08001141 p = attrib->ParseDeep( p, document->ProcessEntities() );
Lee Thomasond6277762012-02-22 16:00:12 -08001142 if ( !p || Attribute( attrib->Name() ) ) {
Lee Thomason43f59302012-02-06 18:18:11 -08001143 DELETE_ATTRIBUTE( attrib );
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -07001144 document->SetError( XML_ERROR_PARSING_ATTRIBUTE, start, p );
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001145 return 0;
1146 }
U-Stream\Lee09a11c52012-02-17 08:31:16 -08001147 LinkAttribute( attrib );
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001148 }
Lee Thomasone4422302012-01-20 17:59:50 -08001149 // end of the tag
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001150 else if ( *p == '/' && *(p+1) == '>' ) {
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -08001151 closingType = CLOSED;
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001152 return p+2; // done; sealed element.
1153 }
Lee Thomasone4422302012-01-20 17:59:50 -08001154 // end of the tag
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001155 else if ( *p == '>' ) {
1156 ++p;
1157 break;
1158 }
Lee Thomasone4422302012-01-20 17:59:50 -08001159 else {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -07001160 document->SetError( XML_ERROR_PARSING_ELEMENT, start, p );
Lee Thomasone4422302012-01-20 17:59:50 -08001161 return 0;
1162 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001163 }
Lee Thomason67d61312012-01-24 16:01:51 -08001164 return p;
1165}
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001166
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001167
Lee Thomason67d61312012-01-24 16:01:51 -08001168//
1169// <ele></ele>
1170// <ele>foo<b>bar</b></ele>
1171//
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -08001172char* XMLElement::ParseDeep( char* p, StrPair* strPair )
Lee Thomason67d61312012-01-24 16:01:51 -08001173{
1174 // Read the element name.
Lee Thomason56bdd022012-02-09 18:16:58 -08001175 p = XMLUtil::SkipWhiteSpace( p );
Lee Thomason67d61312012-01-24 16:01:51 -08001176 if ( !p ) return 0;
Lee Thomason67d61312012-01-24 16:01:51 -08001177
1178 // The closing element is the </element> form. It is
1179 // parsed just like a regular element then deleted from
1180 // the DOM.
1181 if ( *p == '/' ) {
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -08001182 closingType = CLOSING;
Lee Thomason67d61312012-01-24 16:01:51 -08001183 ++p;
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001184 }
Lee Thomason67d61312012-01-24 16:01:51 -08001185
Lee Thomason56bdd022012-02-09 18:16:58 -08001186 p = value.ParseName( p );
Lee Thomasond1983222012-02-06 08:41:24 -08001187 if ( value.Empty() ) return 0;
Lee Thomason67d61312012-01-24 16:01:51 -08001188
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -08001189 p = ParseAttributes( p );
1190 if ( !p || !*p || closingType )
Lee Thomason67d61312012-01-24 16:01:51 -08001191 return p;
1192
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -08001193 p = XMLNode::ParseDeep( p, strPair );
Lee Thomason67d61312012-01-24 16:01:51 -08001194 return p;
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001195}
1196
1197
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001198
1199XMLNode* XMLElement::ShallowClone( XMLDocument* doc ) const
1200{
1201 if ( !doc ) {
1202 doc = document;
1203 }
1204 XMLElement* element = doc->NewElement( Value() ); // fixme: this will always allocate memory. Intern?
1205 for( const XMLAttribute* a=FirstAttribute(); a; a=a->Next() ) {
1206 element->SetAttribute( a->Name(), a->Value() ); // fixme: this will always allocate memory. Intern?
1207 }
1208 return element;
1209}
1210
1211
1212bool XMLElement::ShallowEqual( const XMLNode* compare ) const
1213{
1214 const XMLElement* other = compare->ToElement();
1215 if ( other && XMLUtil::StringEqual( other->Value(), Value() )) {
1216
1217 const XMLAttribute* a=FirstAttribute();
1218 const XMLAttribute* b=other->FirstAttribute();
1219
1220 while ( a && b ) {
1221 if ( !XMLUtil::StringEqual( a->Value(), b->Value() ) ) {
1222 return false;
1223 }
1224 }
1225 if ( a || b ) {
1226 // different count
1227 return false;
1228 }
1229 return true;
1230 }
1231 return false;
1232}
1233
1234
Lee Thomason751da522012-02-10 08:50:51 -08001235bool XMLElement::Accept( XMLVisitor* visitor ) const
1236{
1237 if ( visitor->VisitEnter( *this, rootAttribute ) )
1238 {
1239 for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() )
1240 {
1241 if ( !node->Accept( visitor ) )
1242 break;
1243 }
1244 }
1245 return visitor->VisitExit( *this );
Lee Thomason751da522012-02-10 08:50:51 -08001246}
Lee Thomason56bdd022012-02-09 18:16:58 -08001247
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001248
Lee Thomason3f57d272012-01-11 15:30:03 -08001249// --------- XMLDocument ----------- //
Lee Thomason6f381b72012-03-02 12:59:39 -08001250XMLDocument::XMLDocument( bool _processEntities ) :
Lee Thomason18d68bd2012-01-26 18:17:26 -08001251 XMLNode( 0 ),
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001252 writeBOM( false ),
Lee Thomason6f381b72012-03-02 12:59:39 -08001253 processEntities( _processEntities ),
1254 errorID( 0 ),
1255 errorStr1( 0 ),
1256 errorStr2( 0 ),
U-Lama\Lee560bd472011-12-28 19:42:49 -08001257 charBuffer( 0 )
1258{
Lee Thomason18d68bd2012-01-26 18:17:26 -08001259 document = this; // avoid warning about 'this' in initializer list
U-Lama\Lee560bd472011-12-28 19:42:49 -08001260}
U-Lama\Leee13c3e62011-12-28 14:36:55 -08001261
1262
Lee Thomason3f57d272012-01-11 15:30:03 -08001263XMLDocument::~XMLDocument()
1264{
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001265 DeleteChildren();
Lee Thomason18d68bd2012-01-26 18:17:26 -08001266 delete [] charBuffer;
Lee Thomasond1983222012-02-06 08:41:24 -08001267
Lee Thomasonec5a7b42012-02-13 18:16:52 -08001268#if 0
Lee Thomason455c9d42012-02-06 09:14:14 -08001269 textPool.Trace( "text" );
1270 elementPool.Trace( "element" );
1271 commentPool.Trace( "comment" );
1272 attributePool.Trace( "attribute" );
Lee Thomasone9ecdab2012-02-13 18:11:20 -08001273#endif
1274
Lee Thomason455c9d42012-02-06 09:14:14 -08001275 TIXMLASSERT( textPool.CurrentAllocs() == 0 );
1276 TIXMLASSERT( elementPool.CurrentAllocs() == 0 );
1277 TIXMLASSERT( commentPool.CurrentAllocs() == 0 );
1278 TIXMLASSERT( attributePool.CurrentAllocs() == 0 );
Lee Thomason3f57d272012-01-11 15:30:03 -08001279}
1280
1281
Lee Thomason18d68bd2012-01-26 18:17:26 -08001282void XMLDocument::InitDocument()
1283{
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001284 errorID = XML_NO_ERROR;
Lee Thomason18d68bd2012-01-26 18:17:26 -08001285 errorStr1 = 0;
1286 errorStr2 = 0;
1287
1288 delete [] charBuffer;
1289 charBuffer = 0;
1290
1291}
1292
Lee Thomason3f57d272012-01-11 15:30:03 -08001293
Lee Thomason2c85a712012-01-31 08:24:24 -08001294XMLElement* XMLDocument::NewElement( const char* name )
1295{
Lee Thomasond1983222012-02-06 08:41:24 -08001296 XMLElement* ele = new (elementPool.Alloc()) XMLElement( this );
1297 ele->memPool = &elementPool;
Lee Thomason2c85a712012-01-31 08:24:24 -08001298 ele->SetName( name );
1299 return ele;
1300}
1301
1302
Lee Thomason1ff38e02012-02-14 18:18:16 -08001303XMLComment* XMLDocument::NewComment( const char* str )
1304{
1305 XMLComment* comment = new (commentPool.Alloc()) XMLComment( this );
1306 comment->memPool = &commentPool;
1307 comment->SetValue( str );
1308 return comment;
1309}
1310
1311
1312XMLText* XMLDocument::NewText( const char* str )
1313{
U-Stream\Lee09a11c52012-02-17 08:31:16 -08001314 XMLText* text = new (textPool.Alloc()) XMLText( this );
1315 text->memPool = &textPool;
1316 text->SetValue( str );
1317 return text;
Lee Thomason1ff38e02012-02-14 18:18:16 -08001318}
1319
1320
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001321XMLDeclaration* XMLDocument::NewDeclaration( const char* str )
1322{
1323 XMLDeclaration* dec = new (commentPool.Alloc()) XMLDeclaration( this );
1324 dec->memPool = &commentPool;
1325 dec->SetValue( str );
1326 return dec;
1327}
1328
1329
1330XMLUnknown* XMLDocument::NewUnknown( const char* str )
1331{
1332 XMLUnknown* unk = new (commentPool.Alloc()) XMLUnknown( this );
1333 unk->memPool = &commentPool;
1334 unk->SetValue( str );
1335 return unk;
1336}
1337
1338
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001339int XMLDocument::LoadFile( const char* filename )
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001340{
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001341 DeleteChildren();
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001342 InitDocument();
1343
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001344#if defined(_MSC_VER)
Lee Thomason (grinliz)5ce4d972012-02-26 21:14:23 -08001345#pragma warning ( push )
1346#pragma warning ( disable : 4996 ) // Fail to see a compelling reason why this should be deprecated.
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001347#endif
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001348 FILE* fp = fopen( filename, "rb" );
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001349#if defined(_MSC_VER)
Lee Thomason (grinliz)5ce4d972012-02-26 21:14:23 -08001350#pragma warning ( pop )
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001351#endif
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001352 if ( !fp ) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -07001353 SetError( XML_ERROR_FILE_NOT_FOUND, filename, 0 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001354 return errorID;
1355 }
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001356 LoadFile( fp );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001357 fclose( fp );
1358 return errorID;
1359}
1360
1361
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001362int XMLDocument::LoadFile( FILE* fp )
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001363{
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001364 DeleteChildren();
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001365 InitDocument();
1366
1367 fseek( fp, 0, SEEK_END );
1368 unsigned size = ftell( fp );
1369 fseek( fp, 0, SEEK_SET );
1370
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001371 if ( size == 0 ) {
1372 return errorID;
1373 }
1374
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001375 charBuffer = new char[size+1];
1376 fread( charBuffer, size, 1, fp );
1377 charBuffer[size] = 0;
1378
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001379 const char* p = charBuffer;
1380 p = XMLUtil::SkipWhiteSpace( p );
1381 p = XMLUtil::ReadBOM( p, &writeBOM );
1382 if ( !p || !*p ) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -07001383 SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
Lee Thomasond6277762012-02-22 16:00:12 -08001384 return errorID;
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001385 }
1386
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -08001387 ParseDeep( charBuffer + (p-charBuffer), 0 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001388 return errorID;
1389}
1390
1391
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001392void XMLDocument::SaveFile( const char* filename )
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001393{
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001394#if defined(_MSC_VER)
Lee Thomason (grinliz)5ce4d972012-02-26 21:14:23 -08001395#pragma warning ( push )
1396#pragma warning ( disable : 4996 ) // Fail to see a compelling reason why this should be deprecated.
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001397#endif
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001398 FILE* fp = fopen( filename, "w" );
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001399#if defined(_MSC_VER)
Lee Thomason (grinliz)5ce4d972012-02-26 21:14:23 -08001400#pragma warning ( pop )
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001401#endif
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001402 XMLPrinter stream( fp );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001403 Print( &stream );
1404 fclose( fp );
1405}
1406
Lee Thomason1ff38e02012-02-14 18:18:16 -08001407
Lee Thomason7c913cd2012-01-26 18:32:34 -08001408int XMLDocument::Parse( const char* p )
Lee Thomason3f57d272012-01-11 15:30:03 -08001409{
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001410 DeleteChildren();
Lee Thomason18d68bd2012-01-26 18:17:26 -08001411 InitDocument();
1412
1413 if ( !p || !*p ) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -07001414 SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
Lee Thomasond6277762012-02-22 16:00:12 -08001415 return errorID;
Lee Thomason18d68bd2012-01-26 18:17:26 -08001416 }
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001417 p = XMLUtil::SkipWhiteSpace( p );
1418 p = XMLUtil::ReadBOM( p, &writeBOM );
1419 if ( !p || !*p ) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -07001420 SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
Lee Thomasond6277762012-02-22 16:00:12 -08001421 return errorID;
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001422 }
1423
Lee Thomason18d68bd2012-01-26 18:17:26 -08001424 size_t len = strlen( p );
1425 charBuffer = new char[ len+1 ];
1426 memcpy( charBuffer, p, len+1 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001427
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001428
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -08001429 ParseDeep( charBuffer, 0 );
Lee Thomason7c913cd2012-01-26 18:32:34 -08001430 return errorID;
Lee Thomason3f57d272012-01-11 15:30:03 -08001431}
1432
1433
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001434void XMLDocument::Print( XMLPrinter* streamer )
Lee Thomason3f57d272012-01-11 15:30:03 -08001435{
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001436 XMLPrinter stdStreamer( stdout );
Lee Thomason5cae8972012-01-24 18:03:07 -08001437 if ( !streamer )
1438 streamer = &stdStreamer;
Lee Thomason751da522012-02-10 08:50:51 -08001439 Accept( streamer );
Lee Thomason3f57d272012-01-11 15:30:03 -08001440}
1441
1442
Lee Thomason67d61312012-01-24 16:01:51 -08001443void XMLDocument::SetError( int error, const char* str1, const char* str2 )
1444{
Lee Thomason18d68bd2012-01-26 18:17:26 -08001445 errorID = error;
Lee Thomason18d68bd2012-01-26 18:17:26 -08001446 errorStr1 = str1;
1447 errorStr2 = str2;
Lee Thomason67d61312012-01-24 16:01:51 -08001448}
1449
Lee Thomason5cae8972012-01-24 18:03:07 -08001450
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001451void XMLDocument::PrintError() const
1452{
1453 if ( errorID ) {
Lee Thomason (grinliz)5ce4d972012-02-26 21:14:23 -08001454 static const int LEN = 20;
1455 char buf1[LEN] = { 0 };
1456 char buf2[LEN] = { 0 };
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001457
1458 if ( errorStr1 ) {
Lee Thomason (grinliz)5ce4d972012-02-26 21:14:23 -08001459 TIXML_SNPRINTF( buf1, LEN, "%s", errorStr1 );
1460 buf1[LEN-1] = 0;
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001461 }
1462 if ( errorStr2 ) {
Lee Thomason (grinliz)5ce4d972012-02-26 21:14:23 -08001463 TIXML_SNPRINTF( buf2, LEN, "%s", errorStr2 );
1464 buf2[LEN-1] = 0;
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001465 }
1466
1467 printf( "XMLDocument error id=%d str1=%s str2=%s\n",
1468 errorID, buf1, buf2 );
1469 }
1470}
1471
1472
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001473XMLPrinter::XMLPrinter( FILE* file ) :
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001474 elementJustOpened( false ),
1475 firstElement( true ),
1476 fp( file ),
1477 depth( 0 ),
Lee Thomason6f381b72012-03-02 12:59:39 -08001478 textDepth( -1 ),
1479 processEntities( true )
Lee Thomason5cae8972012-01-24 18:03:07 -08001480{
Lee Thomason857b8682012-01-25 17:50:25 -08001481 for( int i=0; i<ENTITY_RANGE; ++i ) {
1482 entityFlag[i] = false;
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001483 restrictedEntityFlag[i] = false;
Lee Thomason857b8682012-01-25 17:50:25 -08001484 }
1485 for( int i=0; i<NUM_ENTITIES; ++i ) {
1486 TIXMLASSERT( entities[i].value < ENTITY_RANGE );
1487 if ( entities[i].value < ENTITY_RANGE ) {
Lee Thomason (grinliz)9b093cc2012-02-25 21:30:18 -08001488 entityFlag[ (int)entities[i].value ] = true;
Lee Thomason857b8682012-01-25 17:50:25 -08001489 }
1490 }
Lee Thomason (grinliz)9b093cc2012-02-25 21:30:18 -08001491 restrictedEntityFlag[(int)'&'] = true;
1492 restrictedEntityFlag[(int)'<'] = true;
1493 restrictedEntityFlag[(int)'>'] = true; // not required, but consistency is nice
U-Stream\Leeae25a442012-02-17 17:48:16 -08001494 buffer.Push( 0 );
1495}
1496
1497
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001498void XMLPrinter::Print( const char* format, ... )
U-Stream\Leeae25a442012-02-17 17:48:16 -08001499{
1500 va_list va;
1501 va_start( va, format );
1502
1503 if ( fp ) {
1504 vfprintf( fp, format, va );
1505 }
1506 else {
1507 // This seems brutally complex. Haven't figured out a better
1508 // way on windows.
1509 #ifdef _MSC_VER
1510 int len = -1;
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001511 int expand = 1000;
U-Stream\Leeae25a442012-02-17 17:48:16 -08001512 while ( len < 0 ) {
1513 len = vsnprintf_s( accumulator.Mem(), accumulator.Capacity(), accumulator.Capacity()-1, format, va );
1514 if ( len < 0 ) {
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001515 accumulator.PushArr( expand );
1516 expand *= 3/2;
U-Stream\Leeae25a442012-02-17 17:48:16 -08001517 }
1518 }
1519 char* p = buffer.PushArr( len ) - 1;
1520 memcpy( p, accumulator.Mem(), len+1 );
1521 #else
1522 int len = vsnprintf( 0, 0, format, va );
Lee Thomason4de93472012-03-13 17:33:35 -07001523 // Close out and re-start the va-args
1524 va_end( va );
1525 va_start( va, format );
U-Stream\Leeae25a442012-02-17 17:48:16 -08001526 char* p = buffer.PushArr( len ) - 1;
Lee Thomason (grinliz)9b093cc2012-02-25 21:30:18 -08001527 vsnprintf( p, len+1, format, va );
U-Stream\Leeae25a442012-02-17 17:48:16 -08001528 #endif
1529 }
1530 va_end( va );
Lee Thomason5cae8972012-01-24 18:03:07 -08001531}
1532
1533
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001534void XMLPrinter::PrintSpace( int depth )
Lee Thomason5cae8972012-01-24 18:03:07 -08001535{
1536 for( int i=0; i<depth; ++i ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001537 Print( " " );
Lee Thomason5cae8972012-01-24 18:03:07 -08001538 }
1539}
1540
1541
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001542void XMLPrinter::PrintString( const char* p, bool restricted )
Lee Thomason857b8682012-01-25 17:50:25 -08001543{
Lee Thomason951d8832012-01-26 08:47:06 -08001544 // Look for runs of bytes between entities to print.
1545 const char* q = p;
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001546 const bool* flag = restricted ? restrictedEntityFlag : entityFlag;
Lee Thomason857b8682012-01-25 17:50:25 -08001547
Lee Thomason6f381b72012-03-02 12:59:39 -08001548 if ( processEntities ) {
1549 while ( *q ) {
1550 // Remember, char is sometimes signed. (How many times has that bitten me?)
1551 if ( *q > 0 && *q < ENTITY_RANGE ) {
1552 // Check for entities. If one is found, flush
1553 // the stream up until the entity, write the
1554 // entity, and keep looking.
1555 if ( flag[*q] ) {
1556 while ( p < q ) {
1557 Print( "%c", *p );
1558 ++p;
1559 }
1560 for( int i=0; i<NUM_ENTITIES; ++i ) {
1561 if ( entities[i].value == *q ) {
1562 Print( "&%s;", entities[i].pattern );
1563 break;
1564 }
1565 }
Lee Thomason951d8832012-01-26 08:47:06 -08001566 ++p;
1567 }
Lee Thomason951d8832012-01-26 08:47:06 -08001568 }
Lee Thomason6f381b72012-03-02 12:59:39 -08001569 ++q;
Lee Thomason951d8832012-01-26 08:47:06 -08001570 }
Lee Thomason951d8832012-01-26 08:47:06 -08001571 }
1572 // Flush the remaining string. This will be the entire
1573 // string if an entity wasn't found.
Lee Thomason6f381b72012-03-02 12:59:39 -08001574 if ( !processEntities || (q-p > 0) ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001575 Print( "%s", p );
Lee Thomason951d8832012-01-26 08:47:06 -08001576 }
Lee Thomason857b8682012-01-25 17:50:25 -08001577}
1578
U-Stream\Leeae25a442012-02-17 17:48:16 -08001579
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001580void XMLPrinter::PushHeader( bool writeBOM, bool writeDec )
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001581{
1582 static const unsigned char bom[] = { TIXML_UTF_LEAD_0, TIXML_UTF_LEAD_1, TIXML_UTF_LEAD_2, 0 };
1583 if ( writeBOM ) {
1584 Print( "%s", bom );
1585 }
1586 if ( writeDec ) {
1587 PushDeclaration( "xml version=\"1.0\"" );
1588 }
1589}
1590
1591
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001592void XMLPrinter::OpenElement( const char* name )
Lee Thomason5cae8972012-01-24 18:03:07 -08001593{
1594 if ( elementJustOpened ) {
1595 SealElement();
1596 }
Lee Thomason56bdd022012-02-09 18:16:58 -08001597 stack.Push( name );
1598
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001599 if ( textDepth < 0 && !firstElement ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001600 Print( "\n" );
Lee Thomason24767b02012-01-25 17:16:23 -08001601 PrintSpace( depth );
1602 }
Lee Thomason5cae8972012-01-24 18:03:07 -08001603
U-Stream\Leeae25a442012-02-17 17:48:16 -08001604 Print( "<%s", name );
Lee Thomason5cae8972012-01-24 18:03:07 -08001605 elementJustOpened = true;
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001606 firstElement = false;
Lee Thomason5cae8972012-01-24 18:03:07 -08001607 ++depth;
1608}
1609
1610
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001611void XMLPrinter::PushAttribute( const char* name, const char* value )
Lee Thomason5cae8972012-01-24 18:03:07 -08001612{
1613 TIXMLASSERT( elementJustOpened );
U-Stream\Leeae25a442012-02-17 17:48:16 -08001614 Print( " %s=\"", name );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001615 PrintString( value, false );
U-Stream\Leeae25a442012-02-17 17:48:16 -08001616 Print( "\"" );
Lee Thomason5cae8972012-01-24 18:03:07 -08001617}
1618
1619
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001620void XMLPrinter::PushAttribute( const char* name, int v )
1621{
1622 char buf[BUF_SIZE];
1623 TIXML_SNPRINTF( buf, BUF_SIZE-1, "%d", v );
1624 PushAttribute( name, buf );
1625}
1626
1627
1628void XMLPrinter::PushAttribute( const char* name, unsigned v )
1629{
1630 char buf[BUF_SIZE];
1631 TIXML_SNPRINTF( buf, BUF_SIZE-1, "%u", v );
1632 PushAttribute( name, buf );
1633}
1634
1635
1636void XMLPrinter::PushAttribute( const char* name, bool v )
1637{
1638 char buf[BUF_SIZE];
1639 TIXML_SNPRINTF( buf, BUF_SIZE-1, "%d", v ? 1 : 0 );
1640 PushAttribute( name, buf );
1641}
1642
1643
1644void XMLPrinter::PushAttribute( const char* name, double v )
1645{
1646 char buf[BUF_SIZE];
1647 TIXML_SNPRINTF( buf, BUF_SIZE-1, "%f", v );
1648 PushAttribute( name, buf );
1649}
1650
1651
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001652void XMLPrinter::CloseElement()
Lee Thomason5cae8972012-01-24 18:03:07 -08001653{
1654 --depth;
1655 const char* name = stack.Pop();
Lee Thomason5cae8972012-01-24 18:03:07 -08001656
1657 if ( elementJustOpened ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001658 Print( "/>" );
Lee Thomason5cae8972012-01-24 18:03:07 -08001659 }
1660 else {
Lee Thomason56bdd022012-02-09 18:16:58 -08001661 if ( textDepth < 0 ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001662 Print( "\n" );
Lee Thomason24767b02012-01-25 17:16:23 -08001663 PrintSpace( depth );
1664 }
U-Stream\Leeae25a442012-02-17 17:48:16 -08001665 Print( "</%s>", name );
Lee Thomason5cae8972012-01-24 18:03:07 -08001666 }
Lee Thomason56bdd022012-02-09 18:16:58 -08001667
1668 if ( textDepth == depth )
1669 textDepth = -1;
1670 if ( depth == 0 )
U-Stream\Leeae25a442012-02-17 17:48:16 -08001671 Print( "\n" );
Lee Thomason5cae8972012-01-24 18:03:07 -08001672 elementJustOpened = false;
1673}
1674
1675
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001676void XMLPrinter::SealElement()
Lee Thomason5cae8972012-01-24 18:03:07 -08001677{
1678 elementJustOpened = false;
U-Stream\Leeae25a442012-02-17 17:48:16 -08001679 Print( ">" );
Lee Thomason5cae8972012-01-24 18:03:07 -08001680}
1681
1682
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001683void XMLPrinter::PushText( const char* text, bool cdata )
Lee Thomason5cae8972012-01-24 18:03:07 -08001684{
Lee Thomason56bdd022012-02-09 18:16:58 -08001685 textDepth = depth-1;
1686
Lee Thomason5cae8972012-01-24 18:03:07 -08001687 if ( elementJustOpened ) {
1688 SealElement();
1689 }
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001690 if ( cdata ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001691 Print( "<![CDATA[" );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001692 Print( "%s", text );
U-Stream\Leeae25a442012-02-17 17:48:16 -08001693 Print( "]]>" );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001694 }
1695 else {
1696 PrintString( text, true );
1697 }
Lee Thomason5cae8972012-01-24 18:03:07 -08001698}
1699
1700
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001701void XMLPrinter::PushComment( const char* comment )
Lee Thomason5cae8972012-01-24 18:03:07 -08001702{
1703 if ( elementJustOpened ) {
1704 SealElement();
1705 }
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001706 if ( textDepth < 0 && !firstElement ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001707 Print( "\n" );
U-Stream\Lee09a11c52012-02-17 08:31:16 -08001708 PrintSpace( depth );
1709 }
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001710 firstElement = false;
U-Stream\Leeae25a442012-02-17 17:48:16 -08001711 Print( "<!--%s-->", comment );
Lee Thomason5cae8972012-01-24 18:03:07 -08001712}
Lee Thomason751da522012-02-10 08:50:51 -08001713
1714
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001715void XMLPrinter::PushDeclaration( const char* value )
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001716{
1717 if ( elementJustOpened ) {
1718 SealElement();
1719 }
1720 if ( textDepth < 0 && !firstElement) {
1721 Print( "\n" );
1722 PrintSpace( depth );
1723 }
1724 firstElement = false;
1725 Print( "<?%s?>", value );
1726}
1727
1728
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001729void XMLPrinter::PushUnknown( const char* value )
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001730{
1731 if ( elementJustOpened ) {
1732 SealElement();
1733 }
1734 if ( textDepth < 0 && !firstElement ) {
1735 Print( "\n" );
1736 PrintSpace( depth );
1737 }
1738 firstElement = false;
1739 Print( "<!%s>", value );
1740}
1741
1742
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001743bool XMLPrinter::VisitEnter( const XMLDocument& doc )
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001744{
Lee Thomason6f381b72012-03-02 12:59:39 -08001745 processEntities = doc.ProcessEntities();
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001746 if ( doc.HasBOM() ) {
1747 PushHeader( true, false );
1748 }
1749 return true;
1750}
1751
1752
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001753bool XMLPrinter::VisitEnter( const XMLElement& element, const XMLAttribute* attribute )
Lee Thomason751da522012-02-10 08:50:51 -08001754{
1755 OpenElement( element.Name() );
1756 while ( attribute ) {
1757 PushAttribute( attribute->Name(), attribute->Value() );
1758 attribute = attribute->Next();
1759 }
1760 return true;
1761}
1762
1763
Lee Thomason (grinliz)9b093cc2012-02-25 21:30:18 -08001764bool XMLPrinter::VisitExit( const XMLElement& )
Lee Thomason751da522012-02-10 08:50:51 -08001765{
1766 CloseElement();
1767 return true;
1768}
1769
1770
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001771bool XMLPrinter::Visit( const XMLText& text )
Lee Thomason751da522012-02-10 08:50:51 -08001772{
Lee Thomasond6277762012-02-22 16:00:12 -08001773 PushText( text.Value(), text.CData() );
Lee Thomason751da522012-02-10 08:50:51 -08001774 return true;
1775}
1776
1777
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001778bool XMLPrinter::Visit( const XMLComment& comment )
Lee Thomason751da522012-02-10 08:50:51 -08001779{
1780 PushComment( comment.Value() );
1781 return true;
1782}
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001783
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001784bool XMLPrinter::Visit( const XMLDeclaration& declaration )
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001785{
1786 PushDeclaration( declaration.Value() );
1787 return true;
1788}
1789
1790
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001791bool XMLPrinter::Visit( const XMLUnknown& unknown )
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001792{
1793 PushUnknown( unknown.Value() );
1794 return true;
1795}