blob: ff974ec7fd7d469eaeccf6827e5c3a7dbbf7665b [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
26#include <string.h>
27#include <stdlib.h>
28#include <stdio.h>
U-Lama\Lee4cee6112011-12-31 14:58:18 -080029#include <ctype.h>
Lee Thomason (grinliz)9b093cc2012-02-25 21:30:18 -080030#include <new>
U-Stream\Leeae25a442012-02-17 17:48:16 -080031#include <stdarg.h>
Lee Thomasond1983222012-02-06 08:41:24 -080032
U-Lama\Lee560bd472011-12-28 19:42:49 -080033
34using namespace tinyxml2;
35
Lee Thomasone4422302012-01-20 17:59:50 -080036static const char LINE_FEED = (char)0x0a; // all line endings are normalized to LF
Lee Thomasonfde6a752012-01-14 18:08:12 -080037static const char LF = LINE_FEED;
38static const char CARRIAGE_RETURN = (char)0x0d; // CR gets filtered out
39static const char CR = CARRIAGE_RETURN;
Lee Thomasone4422302012-01-20 17:59:50 -080040static const char SINGLE_QUOTE = '\'';
41static const char DOUBLE_QUOTE = '\"';
Lee Thomasonfde6a752012-01-14 18:08:12 -080042
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -080043// Bunch of unicode info at:
44// http://www.unicode.org/faq/utf_bom.html
45// ef bb bf (Microsoft "lead bytes") - designates UTF-8
46
47static const unsigned char TIXML_UTF_LEAD_0 = 0xefU;
48static const unsigned char TIXML_UTF_LEAD_1 = 0xbbU;
49static const unsigned char TIXML_UTF_LEAD_2 = 0xbfU;
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -080050
51
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -080052#define DELETE_NODE( node ) { \
53 if ( node ) { \
54 MemPool* pool = node->memPool; \
55 node->~XMLNode(); \
56 pool->Free( node ); \
57 } \
58}
59#define DELETE_ATTRIBUTE( attrib ) { \
60 if ( attrib ) { \
61 MemPool* pool = attrib->memPool; \
62 attrib->~XMLAttribute(); \
63 pool->Free( attrib ); \
64 } \
65}
Lee Thomason43f59302012-02-06 18:18:11 -080066
Lee Thomason8ee79892012-01-25 17:44:30 -080067struct Entity {
68 const char* pattern;
69 int length;
70 char value;
71};
72
73static const int NUM_ENTITIES = 5;
74static const Entity entities[NUM_ENTITIES] =
75{
Lee Thomason18d68bd2012-01-26 18:17:26 -080076 { "quot", 4, DOUBLE_QUOTE },
Lee Thomason8ee79892012-01-25 17:44:30 -080077 { "amp", 3, '&' },
Lee Thomason18d68bd2012-01-26 18:17:26 -080078 { "apos", 4, SINGLE_QUOTE },
Lee Thomason8ee79892012-01-25 17:44:30 -080079 { "lt", 2, '<' },
80 { "gt", 2, '>' }
81};
82
Lee Thomasonfde6a752012-01-14 18:08:12 -080083
Lee Thomason1a1d4a72012-02-15 09:09:25 -080084StrPair::~StrPair()
85{
86 Reset();
87}
88
89
90void StrPair::Reset()
91{
92 if ( flags & NEEDS_DELETE ) {
93 delete [] start;
94 }
95 flags = 0;
96 start = 0;
97 end = 0;
98}
99
100
101void StrPair::SetStr( const char* str, int flags )
102{
103 Reset();
104 size_t len = strlen( str );
105 start = new char[ len+1 ];
U-Stream\Lee09a11c52012-02-17 08:31:16 -0800106 memcpy( start, str, len+1 );
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800107 end = start + len;
108 this->flags = flags | NEEDS_DELETE;
109}
110
111
112char* StrPair::ParseText( char* p, const char* endTag, int strFlags )
113{
114 TIXMLASSERT( endTag && *endTag );
115
116 char* start = p; // fixme: hides a member
117 char endChar = *endTag;
118 int length = strlen( endTag );
119
120 // Inner loop of text parsing.
121 while ( *p ) {
122 if ( *p == endChar && strncmp( p, endTag, length ) == 0 ) {
123 Set( start, p, strFlags );
124 return p + length;
125 }
126 ++p;
127 }
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800128 return 0;
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800129}
130
131
132char* StrPair::ParseName( char* p )
133{
134 char* start = p;
135
136 start = p;
137 if ( !start || !(*start) ) {
138 return 0;
139 }
140
141 if ( !XMLUtil::IsAlpha( *p ) ) {
142 return 0;
143 }
144
145 while( *p && (
146 XMLUtil::IsAlphaNum( (unsigned char) *p )
147 || *p == '_'
148 || *p == '-'
149 || *p == '.'
150 || *p == ':' ))
151 {
152 ++p;
153 }
154
155 if ( p > start ) {
156 Set( start, p, 0 );
157 return p;
158 }
159 return 0;
160}
161
162
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800163
Lee Thomasone4422302012-01-20 17:59:50 -0800164const char* StrPair::GetStr()
165{
166 if ( flags & NEEDS_FLUSH ) {
167 *end = 0;
Lee Thomason8ee79892012-01-25 17:44:30 -0800168 flags ^= NEEDS_FLUSH;
Lee Thomasone4422302012-01-20 17:59:50 -0800169
Lee Thomason8ee79892012-01-25 17:44:30 -0800170 if ( flags ) {
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800171 char* p = start; // the read pointer
172 char* q = start; // the write pointer
Lee Thomasone4422302012-01-20 17:59:50 -0800173
174 while( p < end ) {
Lee Thomason8ee79892012-01-25 17:44:30 -0800175 if ( (flags & NEEDS_NEWLINE_NORMALIZATION) && *p == CR ) {
Lee Thomasone4422302012-01-20 17:59:50 -0800176 // CR-LF pair becomes LF
177 // CR alone becomes LF
178 // LF-CR becomes LF
179 if ( *(p+1) == LF ) {
180 p += 2;
181 }
182 else {
183 ++p;
184 }
Lee Thomasone9ecdab2012-02-13 18:11:20 -0800185 *q++ = LF;
Lee Thomasone4422302012-01-20 17:59:50 -0800186 }
Lee Thomason8ee79892012-01-25 17:44:30 -0800187 else if ( (flags & NEEDS_NEWLINE_NORMALIZATION) && *p == LF ) {
Lee Thomasone4422302012-01-20 17:59:50 -0800188 if ( *(p+1) == CR ) {
189 p += 2;
190 }
191 else {
192 ++p;
193 }
Lee Thomasone9ecdab2012-02-13 18:11:20 -0800194 *q++ = LF;
Lee Thomasone4422302012-01-20 17:59:50 -0800195 }
Lee Thomason8ee79892012-01-25 17:44:30 -0800196 else if ( (flags & NEEDS_ENTITY_PROCESSING) && *p == '&' ) {
197 int i=0;
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800198
199 // Entities handled by tinyXML2:
200 // - special entities in the entity table [in/out]
201 // - numeric character reference [in]
202 // &#20013; or &#x4e2d;
203
204 if ( *(p+1) == '#' ) {
205 char buf[10] = { 0 };
206 int len;
207 p = const_cast<char*>( XMLUtil::GetCharacterRef( p, buf, &len ) );
208 for( int i=0; i<len; ++i ) {
209 *q++ = buf[i];
Lee Thomason8ee79892012-01-25 17:44:30 -0800210 }
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800211 TIXMLASSERT( q <= p );
Lee Thomason8ee79892012-01-25 17:44:30 -0800212 }
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800213 else {
214 for( i=0; i<NUM_ENTITIES; ++i ) {
215 if ( strncmp( p+1, entities[i].pattern, entities[i].length ) == 0
216 && *(p+entities[i].length+1) == ';' )
217 {
218 // Found an entity convert;
219 *q = entities[i].value;
220 ++q;
221 p += entities[i].length + 2;
222 break;
223 }
224 }
225 if ( i == NUM_ENTITIES ) {
226 // fixme: treat as error?
227 ++p;
228 ++q;
229 }
Lee Thomason8ee79892012-01-25 17:44:30 -0800230 }
231 }
Lee Thomasone4422302012-01-20 17:59:50 -0800232 else {
233 *q = *p;
234 ++p;
Lee Thomasonec975ce2012-01-23 11:42:06 -0800235 ++q;
Lee Thomasone4422302012-01-20 17:59:50 -0800236 }
237 }
Lee Thomason8ee79892012-01-25 17:44:30 -0800238 *q = 0;
Lee Thomasone4422302012-01-20 17:59:50 -0800239 }
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800240 flags = (flags & NEEDS_DELETE);
Lee Thomasone4422302012-01-20 17:59:50 -0800241 }
242 return start;
243}
244
Lee Thomason2c85a712012-01-31 08:24:24 -0800245
Lee Thomasone4422302012-01-20 17:59:50 -0800246
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800247
Lee Thomason56bdd022012-02-09 18:16:58 -0800248// --------- XMLUtil ----------- //
Lee Thomasond1983222012-02-06 08:41:24 -0800249
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800250const char* XMLUtil::ReadBOM( const char* p, bool* bom )
251{
252 *bom = false;
253 const unsigned char* pu = reinterpret_cast<const unsigned char*>(p);
254 // Check for BOM:
255 if ( *(pu+0) == TIXML_UTF_LEAD_0
256 && *(pu+1) == TIXML_UTF_LEAD_1
257 && *(pu+2) == TIXML_UTF_LEAD_2 )
258 {
259 *bom = true;
260 p += 3;
261 }
262 return p;
263}
264
265
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800266void XMLUtil::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length )
267{
268 const unsigned long BYTE_MASK = 0xBF;
269 const unsigned long BYTE_MARK = 0x80;
270 const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
271
272 if (input < 0x80)
273 *length = 1;
274 else if ( input < 0x800 )
275 *length = 2;
276 else if ( input < 0x10000 )
277 *length = 3;
278 else if ( input < 0x200000 )
279 *length = 4;
280 else
281 { *length = 0; return; } // This code won't covert this correctly anyway.
282
283 output += *length;
284
285 // Scary scary fall throughs.
286 switch (*length)
287 {
288 case 4:
289 --output;
290 *output = (char)((input | BYTE_MARK) & BYTE_MASK);
291 input >>= 6;
292 case 3:
293 --output;
294 *output = (char)((input | BYTE_MARK) & BYTE_MASK);
295 input >>= 6;
296 case 2:
297 --output;
298 *output = (char)((input | BYTE_MARK) & BYTE_MASK);
299 input >>= 6;
300 case 1:
301 --output;
302 *output = (char)(input | FIRST_BYTE_MARK[*length]);
303 }
304}
305
306
307const char* XMLUtil::GetCharacterRef( const char* p, char* value, int* length )
308{
309 // Presume an entity, and pull it out.
310 *length = 0;
311
312 if ( *(p+1) == '#' && *(p+2) )
313 {
314 unsigned long ucs = 0;
Lee Thomason (grinliz)7ca55582012-03-07 21:54:57 -0800315 int delta = 0;
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800316 unsigned mult = 1;
317
318 if ( *(p+2) == 'x' )
319 {
320 // Hexadecimal.
321 if ( !*(p+3) ) return 0;
322
323 const char* q = p+3;
324 q = strchr( q, ';' );
325
326 if ( !q || !*q ) return 0;
327
Lee Thomason (grinliz)7ca55582012-03-07 21:54:57 -0800328 delta = (q-p);
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800329 --q;
330
331 while ( *q != 'x' )
332 {
333 if ( *q >= '0' && *q <= '9' )
334 ucs += mult * (*q - '0');
335 else if ( *q >= 'a' && *q <= 'f' )
336 ucs += mult * (*q - 'a' + 10);
337 else if ( *q >= 'A' && *q <= 'F' )
338 ucs += mult * (*q - 'A' + 10 );
339 else
340 return 0;
341 mult *= 16;
342 --q;
343 }
344 }
345 else
346 {
347 // Decimal.
348 if ( !*(p+2) ) return 0;
349
350 const char* q = p+2;
351 q = strchr( q, ';' );
352
353 if ( !q || !*q ) return 0;
354
355 delta = q-p;
356 --q;
357
358 while ( *q != '#' )
359 {
360 if ( *q >= '0' && *q <= '9' )
361 ucs += mult * (*q - '0');
362 else
363 return 0;
364 mult *= 10;
365 --q;
366 }
367 }
368 // convert the UCS to UTF-8
369 ConvertUTF32ToUTF8( ucs, value, length );
370 return p + delta + 1;
371 }
372 return p+1;
373}
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800374
375
Lee Thomasond1983222012-02-06 08:41:24 -0800376char* XMLDocument::Identify( char* p, XMLNode** node )
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800377{
378 XMLNode* returnNode = 0;
Lee Thomason5492a1c2012-01-23 15:32:10 -0800379 char* start = p;
Lee Thomason56bdd022012-02-09 18:16:58 -0800380 p = XMLUtil::SkipWhiteSpace( p );
Lee Thomason5492a1c2012-01-23 15:32:10 -0800381 if( !p || !*p )
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800382 {
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800383 return p;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800384 }
385
386 // What is this thing?
387 // - Elements start with a letter or underscore, but xml is reserved.
388 // - Comments: <!--
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800389 // - Decleration: <?
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800390 // - Everthing else is unknown to tinyxml.
391 //
392
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800393 static const char* xmlHeader = { "<?" };
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800394 static const char* commentHeader = { "<!--" };
395 static const char* dtdHeader = { "<!" };
396 static const char* cdataHeader = { "<![CDATA[" };
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800397 static const char* elementHeader = { "<" }; // and a header for everything else; check last.
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800398
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800399 static const int xmlHeaderLen = 2;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800400 static const int commentHeaderLen = 4;
401 static const int dtdHeaderLen = 2;
402 static const int cdataHeaderLen = 9;
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800403 static const int elementHeaderLen = 1;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800404
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800405#if defined(_MSC_VER)
Lee Thomason (grinliz)9b093cc2012-02-25 21:30:18 -0800406#pragma warning ( push )
407#pragma warning ( disable : 4127 )
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800408#endif
Lee Thomason50f97b22012-02-11 16:33:40 -0800409 TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLUnknown ) ); // use same memory pool
410 TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLDeclaration ) ); // use same memory pool
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800411#if defined(_MSC_VER)
Lee Thomason (grinliz)9b093cc2012-02-25 21:30:18 -0800412#pragma warning (pop)
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800413#endif
Lee Thomason50f97b22012-02-11 16:33:40 -0800414 if ( XMLUtil::StringEqual( p, xmlHeader, xmlHeaderLen ) ) {
415 returnNode = new (commentPool.Alloc()) XMLDeclaration( this );
416 returnNode->memPool = &commentPool;
417 p += xmlHeaderLen;
418 }
419 else if ( XMLUtil::StringEqual( p, commentHeader, commentHeaderLen ) ) {
Lee Thomasond1983222012-02-06 08:41:24 -0800420 returnNode = new (commentPool.Alloc()) XMLComment( this );
421 returnNode->memPool = &commentPool;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800422 p += commentHeaderLen;
423 }
Lee Thomason50f97b22012-02-11 16:33:40 -0800424 else if ( XMLUtil::StringEqual( p, cdataHeader, cdataHeaderLen ) ) {
425 XMLText* text = new (textPool.Alloc()) XMLText( this );
426 returnNode = text;
427 returnNode->memPool = &textPool;
428 p += cdataHeaderLen;
429 text->SetCData( true );
430 }
431 else if ( XMLUtil::StringEqual( p, dtdHeader, dtdHeaderLen ) ) {
432 returnNode = new (commentPool.Alloc()) XMLUnknown( this );
433 returnNode->memPool = &commentPool;
434 p += dtdHeaderLen;
435 }
Lee Thomason56bdd022012-02-09 18:16:58 -0800436 else if ( XMLUtil::StringEqual( p, elementHeader, elementHeaderLen ) ) {
Lee Thomasond1983222012-02-06 08:41:24 -0800437 returnNode = new (elementPool.Alloc()) XMLElement( this );
438 returnNode->memPool = &elementPool;
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800439 p += elementHeaderLen;
440 }
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800441 else {
Lee Thomasond1983222012-02-06 08:41:24 -0800442 returnNode = new (textPool.Alloc()) XMLText( this );
443 returnNode->memPool = &textPool;
Lee Thomason5492a1c2012-01-23 15:32:10 -0800444 p = start; // Back it up, all the text counts.
445 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800446
447 *node = returnNode;
448 return p;
449}
450
451
Lee Thomason751da522012-02-10 08:50:51 -0800452bool XMLDocument::Accept( XMLVisitor* visitor ) const
453{
454 if ( visitor->VisitEnter( *this ) )
455 {
456 for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() )
457 {
458 if ( !node->Accept( visitor ) )
459 break;
460 }
461 }
462 return visitor->VisitExit( *this );
463}
Lee Thomason56bdd022012-02-09 18:16:58 -0800464
465
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800466// --------- XMLNode ----------- //
467
468XMLNode::XMLNode( XMLDocument* doc ) :
469 document( doc ),
470 parent( 0 ),
471 firstChild( 0 ), lastChild( 0 ),
472 prev( 0 ), next( 0 )
473{
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800474}
475
476
477XMLNode::~XMLNode()
478{
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -0800479 DeleteChildren();
Lee Thomason7c913cd2012-01-26 18:32:34 -0800480 if ( parent ) {
481 parent->Unlink( this );
482 }
Lee Thomason18d68bd2012-01-26 18:17:26 -0800483}
484
485
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800486void XMLNode::SetValue( const char* str, bool staticMem )
487{
488 if ( staticMem )
489 value.SetInternedStr( str );
490 else
491 value.SetStr( str );
492}
493
494
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -0800495void XMLNode::DeleteChildren()
Lee Thomason18d68bd2012-01-26 18:17:26 -0800496{
Lee Thomasond923c672012-01-23 08:44:25 -0800497 while( firstChild ) {
498 XMLNode* node = firstChild;
499 Unlink( node );
Lee Thomasond1983222012-02-06 08:41:24 -0800500
Lee Thomason43f59302012-02-06 18:18:11 -0800501 DELETE_NODE( node );
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800502 }
Lee Thomason18d68bd2012-01-26 18:17:26 -0800503 firstChild = lastChild = 0;
Lee Thomasond923c672012-01-23 08:44:25 -0800504}
505
506
507void XMLNode::Unlink( XMLNode* child )
508{
509 TIXMLASSERT( child->parent == this );
510 if ( child == firstChild )
511 firstChild = firstChild->next;
512 if ( child == lastChild )
513 lastChild = lastChild->prev;
514
515 if ( child->prev ) {
516 child->prev->next = child->next;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800517 }
Lee Thomasond923c672012-01-23 08:44:25 -0800518 if ( child->next ) {
519 child->next->prev = child->prev;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800520 }
Lee Thomasond923c672012-01-23 08:44:25 -0800521 child->parent = 0;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800522}
523
524
U-Stream\Leeae25a442012-02-17 17:48:16 -0800525void XMLNode::DeleteChild( XMLNode* node )
526{
527 TIXMLASSERT( node->parent == this );
528 DELETE_NODE( node );
529}
530
531
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800532XMLNode* XMLNode::InsertEndChild( XMLNode* addThis )
533{
534 if ( lastChild ) {
535 TIXMLASSERT( firstChild );
536 TIXMLASSERT( lastChild->next == 0 );
537 lastChild->next = addThis;
538 addThis->prev = lastChild;
539 lastChild = addThis;
540
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800541 addThis->next = 0;
542 }
543 else {
544 TIXMLASSERT( firstChild == 0 );
545 firstChild = lastChild = addThis;
546
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800547 addThis->prev = 0;
548 addThis->next = 0;
549 }
U-Stream\Lee09a11c52012-02-17 08:31:16 -0800550 addThis->parent = this;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800551 return addThis;
552}
553
554
Lee Thomason1ff38e02012-02-14 18:18:16 -0800555XMLNode* XMLNode::InsertFirstChild( XMLNode* addThis )
556{
557 if ( firstChild ) {
558 TIXMLASSERT( lastChild );
559 TIXMLASSERT( firstChild->prev == 0 );
560
561 firstChild->prev = addThis;
562 addThis->next = firstChild;
563 firstChild = addThis;
564
Lee Thomason1ff38e02012-02-14 18:18:16 -0800565 addThis->prev = 0;
566 }
567 else {
568 TIXMLASSERT( lastChild == 0 );
569 firstChild = lastChild = addThis;
570
Lee Thomason1ff38e02012-02-14 18:18:16 -0800571 addThis->prev = 0;
572 addThis->next = 0;
573 }
U-Stream\Lee09a11c52012-02-17 08:31:16 -0800574 addThis->parent = this;
Lee Thomason1ff38e02012-02-14 18:18:16 -0800575 return addThis;
576}
577
578
579XMLNode* XMLNode::InsertAfterChild( XMLNode* afterThis, XMLNode* addThis )
580{
581 TIXMLASSERT( afterThis->parent == this );
582 if ( afterThis->parent != this )
583 return 0;
584
585 if ( afterThis->next == 0 ) {
586 // The last node or the only node.
587 return InsertEndChild( addThis );
588 }
589 addThis->prev = afterThis;
590 addThis->next = afterThis->next;
591 afterThis->next->prev = addThis;
592 afterThis->next = addThis;
U-Stream\Lee09a11c52012-02-17 08:31:16 -0800593 addThis->parent = this;
Lee Thomason1ff38e02012-02-14 18:18:16 -0800594 return addThis;
595}
596
597
598
599
Lee Thomason56bdd022012-02-09 18:16:58 -0800600const XMLElement* XMLNode::FirstChildElement( const char* value ) const
Lee Thomason2c85a712012-01-31 08:24:24 -0800601{
602 for( XMLNode* node=firstChild; node; node=node->next ) {
603 XMLElement* element = node->ToElement();
604 if ( element ) {
Lee Thomason56bdd022012-02-09 18:16:58 -0800605 if ( !value || XMLUtil::StringEqual( element->Name(), value ) ) {
Lee Thomason2c85a712012-01-31 08:24:24 -0800606 return element;
607 }
608 }
609 }
610 return 0;
611}
612
613
Lee Thomason56bdd022012-02-09 18:16:58 -0800614const XMLElement* XMLNode::LastChildElement( const char* value ) const
615{
616 for( XMLNode* node=lastChild; node; node=node->prev ) {
617 XMLElement* element = node->ToElement();
618 if ( element ) {
619 if ( !value || XMLUtil::StringEqual( element->Name(), value ) ) {
620 return element;
621 }
622 }
623 }
624 return 0;
625}
626
627
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800628const XMLElement* XMLNode::NextSiblingElement( const char* value ) const
629{
630 for( XMLNode* element=this->next; element; element = element->next ) {
631 if ( element->ToElement()
632 && (!value || XMLUtil::StringEqual( value, element->Value() )))
633 {
634 return element->ToElement();
635 }
636 }
637 return 0;
638}
639
640
641const XMLElement* XMLNode::PreviousSiblingElement( const char* value ) const
642{
643 for( XMLNode* element=this->prev; element; element = element->prev ) {
644 if ( element->ToElement()
645 && (!value || XMLUtil::StringEqual( value, element->Value() )))
646 {
647 return element->ToElement();
648 }
649 }
650 return 0;
651}
652
653
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800654char* XMLNode::ParseDeep( char* p, StrPair* parentEnd )
Lee Thomason67d61312012-01-24 16:01:51 -0800655{
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800656 // This is a recursive method, but thinking about it "at the current level"
657 // it is a pretty simple flat list:
658 // <foo/>
659 // <!-- comment -->
660 //
661 // With a special case:
662 // <foo>
663 // </foo>
664 // <!-- comment -->
665 //
666 // Where the closing element (/foo) *must* be the next thing after the opening
667 // element, and the names must match. BUT the tricky bit is that the closing
668 // element will be read by the child.
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800669 //
670 // 'endTag' is the end tag for this node, it is returned by a call to a child.
671 // 'parentEnd' is the end tag for the parent, which is filled in and returned.
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800672
Lee Thomason67d61312012-01-24 16:01:51 -0800673 while( p && *p ) {
674 XMLNode* node = 0;
Lee Thomasond6277762012-02-22 16:00:12 -0800675
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800676 p = document->Identify( p, &node );
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800677 if ( p == 0 || node == 0 ) {
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800678 break;
679 }
680
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800681 StrPair endTag;
682 p = node->ParseDeep( p, &endTag );
683 if ( !p ) {
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800684 DELETE_NODE( node );
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800685 node = 0;
Lee Thomason (grinliz)784607f2012-02-24 16:23:40 -0800686 if ( !document->Error() ) {
687 document->SetError( ERROR_PARSING, 0, 0 );
688 }
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800689 break;
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800690 }
691
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800692 // We read the end tag. Return it to the parent.
693 if ( node->ToElement() && node->ToElement()->ClosingType() == XMLElement::CLOSING ) {
694 if ( parentEnd ) {
695 *parentEnd = ((XMLElement*)node)->value;
Lee Thomason67d61312012-01-24 16:01:51 -0800696 }
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800697 DELETE_NODE( node );
698 return p;
699 }
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800700
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800701 // Handle an end tag returned to this level.
702 // And handle a bunch of annoying errors.
703 XMLElement* ele = node->ToElement();
704 if ( ele ) {
705 if ( endTag.Empty() && ele->ClosingType() == XMLElement::OPEN ) {
706 document->SetError( ERROR_MISMATCHED_ELEMENT, node->Value(), 0 );
707 p = 0;
708 }
709 else if ( !endTag.Empty() && ele->ClosingType() != XMLElement::OPEN ) {
710 document->SetError( ERROR_MISMATCHED_ELEMENT, node->Value(), 0 );
711 p = 0;
712 }
713 else if ( !endTag.Empty() ) {
714 if ( !XMLUtil::StringEqual( endTag.GetStr(), node->Value() )) {
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800715 document->SetError( ERROR_MISMATCHED_ELEMENT, node->Value(), 0 );
716 p = 0;
717 }
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800718 }
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800719 }
720 if ( p == 0 ) {
721 DELETE_NODE( node );
722 node = 0;
723 }
724 if ( node ) {
725 this->InsertEndChild( node );
Lee Thomason67d61312012-01-24 16:01:51 -0800726 }
727 }
728 return 0;
729}
730
Lee Thomason5492a1c2012-01-23 15:32:10 -0800731// --------- XMLText ---------- //
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800732char* XMLText::ParseDeep( char* p, StrPair* )
Lee Thomason5492a1c2012-01-23 15:32:10 -0800733{
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800734 const char* start = p;
Lee Thomason50f97b22012-02-11 16:33:40 -0800735 if ( this->CData() ) {
736 p = value.ParseText( p, "]]>", StrPair::NEEDS_NEWLINE_NORMALIZATION );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800737 if ( !p ) {
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800738 document->SetError( ERROR_PARSING_CDATA, start, 0 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800739 }
Lee Thomason50f97b22012-02-11 16:33:40 -0800740 return p;
741 }
742 else {
Lee Thomason6f381b72012-03-02 12:59:39 -0800743 p = value.ParseText( p, "<", document->ProcessEntities() ? StrPair::TEXT_ELEMENT : StrPair::TEXT_ELEMENT_LEAVE_ENTITIES );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800744 if ( !p ) {
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800745 document->SetError( ERROR_PARSING_TEXT, start, 0 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800746 }
Lee Thomason50f97b22012-02-11 16:33:40 -0800747 if ( p && *p ) {
748 return p-1;
749 }
Lee Thomason5492a1c2012-01-23 15:32:10 -0800750 }
751 return 0;
752}
753
754
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800755XMLNode* XMLText::ShallowClone( XMLDocument* doc ) const
756{
757 if ( !doc ) {
758 doc = document;
759 }
760 XMLText* text = doc->NewText( Value() ); // fixme: this will always allocate memory. Intern?
761 text->SetCData( this->CData() );
762 return text;
763}
764
765
766bool XMLText::ShallowEqual( const XMLNode* compare ) const
767{
768 return ( compare->ToText() && XMLUtil::StringEqual( compare->ToText()->Value(), Value() ));
769}
770
771
Lee Thomason56bdd022012-02-09 18:16:58 -0800772bool XMLText::Accept( XMLVisitor* visitor ) const
773{
774 return visitor->Visit( *this );
775}
776
777
Lee Thomason3f57d272012-01-11 15:30:03 -0800778// --------- XMLComment ---------- //
779
Lee Thomasone4422302012-01-20 17:59:50 -0800780XMLComment::XMLComment( XMLDocument* doc ) : XMLNode( doc )
Lee Thomason3f57d272012-01-11 15:30:03 -0800781{
782}
783
784
Lee Thomasonce0763e2012-01-11 15:43:54 -0800785XMLComment::~XMLComment()
Lee Thomason3f57d272012-01-11 15:30:03 -0800786{
Lee Thomasond923c672012-01-23 08:44:25 -0800787 //printf( "~XMLComment\n" );
Lee Thomason3f57d272012-01-11 15:30:03 -0800788}
789
790
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800791char* XMLComment::ParseDeep( char* p, StrPair* )
Lee Thomason3f57d272012-01-11 15:30:03 -0800792{
793 // Comment parses as text.
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800794 const char* start = p;
795 p = value.ParseText( p, "-->", StrPair::COMMENT );
796 if ( p == 0 ) {
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800797 document->SetError( ERROR_PARSING_COMMENT, start, 0 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800798 }
799 return p;
U-Lama\Lee4cee6112011-12-31 14:58:18 -0800800}
801
802
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800803XMLNode* XMLComment::ShallowClone( XMLDocument* doc ) const
804{
805 if ( !doc ) {
806 doc = document;
807 }
808 XMLComment* comment = doc->NewComment( Value() ); // fixme: this will always allocate memory. Intern?
809 return comment;
810}
811
812
813bool XMLComment::ShallowEqual( const XMLNode* compare ) const
814{
815 return ( compare->ToComment() && XMLUtil::StringEqual( compare->ToComment()->Value(), Value() ));
816}
817
818
Lee Thomason751da522012-02-10 08:50:51 -0800819bool XMLComment::Accept( XMLVisitor* visitor ) const
820{
821 return visitor->Visit( *this );
822}
Lee Thomason56bdd022012-02-09 18:16:58 -0800823
824
Lee Thomason50f97b22012-02-11 16:33:40 -0800825// --------- XMLDeclaration ---------- //
826
827XMLDeclaration::XMLDeclaration( XMLDocument* doc ) : XMLNode( doc )
828{
829}
830
831
832XMLDeclaration::~XMLDeclaration()
833{
834 //printf( "~XMLDeclaration\n" );
835}
836
837
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800838char* XMLDeclaration::ParseDeep( char* p, StrPair* )
Lee Thomason50f97b22012-02-11 16:33:40 -0800839{
840 // Declaration parses as text.
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800841 const char* start = p;
842 p = value.ParseText( p, "?>", StrPair::NEEDS_NEWLINE_NORMALIZATION );
843 if ( p == 0 ) {
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800844 document->SetError( ERROR_PARSING_DECLARATION, start, 0 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800845 }
846 return p;
Lee Thomason50f97b22012-02-11 16:33:40 -0800847}
848
849
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800850XMLNode* XMLDeclaration::ShallowClone( XMLDocument* doc ) const
851{
852 if ( !doc ) {
853 doc = document;
854 }
855 XMLDeclaration* dec = doc->NewDeclaration( Value() ); // fixme: this will always allocate memory. Intern?
856 return dec;
857}
858
859
860bool XMLDeclaration::ShallowEqual( const XMLNode* compare ) const
861{
862 return ( compare->ToDeclaration() && XMLUtil::StringEqual( compare->ToDeclaration()->Value(), Value() ));
863}
864
865
866
Lee Thomason50f97b22012-02-11 16:33:40 -0800867bool XMLDeclaration::Accept( XMLVisitor* visitor ) const
868{
869 return visitor->Visit( *this );
870}
871
872// --------- XMLUnknown ---------- //
873
874XMLUnknown::XMLUnknown( XMLDocument* doc ) : XMLNode( doc )
875{
876}
877
878
879XMLUnknown::~XMLUnknown()
880{
881}
882
883
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800884char* XMLUnknown::ParseDeep( char* p, StrPair* )
Lee Thomason50f97b22012-02-11 16:33:40 -0800885{
886 // Unknown parses as text.
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800887 const char* start = p;
888
889 p = value.ParseText( p, ">", StrPair::NEEDS_NEWLINE_NORMALIZATION );
890 if ( !p ) {
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800891 document->SetError( ERROR_PARSING_UNKNOWN, start, 0 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800892 }
893 return p;
Lee Thomason50f97b22012-02-11 16:33:40 -0800894}
895
896
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800897XMLNode* XMLUnknown::ShallowClone( XMLDocument* doc ) const
898{
899 if ( !doc ) {
900 doc = document;
901 }
902 XMLUnknown* text = doc->NewUnknown( Value() ); // fixme: this will always allocate memory. Intern?
903 return text;
904}
905
906
907bool XMLUnknown::ShallowEqual( const XMLNode* compare ) const
908{
909 return ( compare->ToUnknown() && XMLUtil::StringEqual( compare->ToUnknown()->Value(), Value() ));
910}
911
912
Lee Thomason50f97b22012-02-11 16:33:40 -0800913bool XMLUnknown::Accept( XMLVisitor* visitor ) const
914{
915 return visitor->Visit( *this );
916}
917
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800918// --------- XMLAttribute ---------- //
Lee Thomason6f381b72012-03-02 12:59:39 -0800919char* XMLAttribute::ParseDeep( char* p, bool processEntities )
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800920{
Lee Thomason56bdd022012-02-09 18:16:58 -0800921 p = name.ParseText( p, "=", StrPair::ATTRIBUTE_NAME );
Lee Thomason22aead12012-01-23 13:29:35 -0800922 if ( !p || !*p ) return 0;
923
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800924 char endTag[2] = { *p, 0 };
925 ++p;
Lee Thomason6f381b72012-03-02 12:59:39 -0800926 p = value.ParseText( p, endTag, processEntities ? StrPair::ATTRIBUTE_VALUE : StrPair::ATTRIBUTE_VALUE_LEAVE_ENTITIES );
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800927 //if ( value.Empty() ) return 0;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800928 return p;
929}
930
931
U-Stream\Lee09a11c52012-02-17 08:31:16 -0800932void XMLAttribute::SetName( const char* n )
933{
934 name.SetStr( n );
935}
936
937
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800938int XMLAttribute::QueryIntValue( int* value ) const
Lee Thomason1ff38e02012-02-14 18:18:16 -0800939{
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800940 if ( TIXML_SSCANF( Value(), "%d", value ) == 1 )
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800941 return XML_NO_ERROR;
Lee Thomason1ff38e02012-02-14 18:18:16 -0800942 return WRONG_ATTRIBUTE_TYPE;
943}
944
945
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800946int XMLAttribute::QueryUnsignedValue( unsigned int* value ) const
Lee Thomason1ff38e02012-02-14 18:18:16 -0800947{
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800948 if ( TIXML_SSCANF( Value(), "%u", value ) == 1 )
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800949 return XML_NO_ERROR;
Lee Thomason1ff38e02012-02-14 18:18:16 -0800950 return WRONG_ATTRIBUTE_TYPE;
951}
952
953
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800954int XMLAttribute::QueryBoolValue( bool* value ) const
Lee Thomason1ff38e02012-02-14 18:18:16 -0800955{
956 int ival = -1;
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800957 QueryIntValue( &ival );
Lee Thomason1ff38e02012-02-14 18:18:16 -0800958
959 if ( ival > 0 || XMLUtil::StringEqual( Value(), "true" ) ) {
960 *value = true;
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800961 return XML_NO_ERROR;
Lee Thomason1ff38e02012-02-14 18:18:16 -0800962 }
963 else if ( ival == 0 || XMLUtil::StringEqual( Value(), "false" ) ) {
964 *value = false;
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800965 return XML_NO_ERROR;
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800966 }
Lee Thomason1ff38e02012-02-14 18:18:16 -0800967 return WRONG_ATTRIBUTE_TYPE;
968}
969
970
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800971int XMLAttribute::QueryDoubleValue( double* value ) const
Lee Thomason1ff38e02012-02-14 18:18:16 -0800972{
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800973 if ( TIXML_SSCANF( Value(), "%lf", value ) == 1 )
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800974 return XML_NO_ERROR;
Lee Thomason1ff38e02012-02-14 18:18:16 -0800975 return WRONG_ATTRIBUTE_TYPE;
976}
977
978
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800979int XMLAttribute::QueryFloatValue( float* value ) const
Lee Thomason1ff38e02012-02-14 18:18:16 -0800980{
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800981 if ( TIXML_SSCANF( Value(), "%f", value ) == 1 )
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800982 return XML_NO_ERROR;
Lee Thomason1ff38e02012-02-14 18:18:16 -0800983 return WRONG_ATTRIBUTE_TYPE;
984}
985
986
987void XMLAttribute::SetAttribute( const char* v )
988{
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800989 value.SetStr( v );
Lee Thomason1ff38e02012-02-14 18:18:16 -0800990}
991
992
Lee Thomason1ff38e02012-02-14 18:18:16 -0800993void XMLAttribute::SetAttribute( int v )
994{
995 char buf[BUF_SIZE];
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800996 TIXML_SNPRINTF( buf, BUF_SIZE-1, "%d", v );
997 value.SetStr( buf );
Lee Thomason1ff38e02012-02-14 18:18:16 -0800998}
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800999
1000
1001void XMLAttribute::SetAttribute( unsigned v )
1002{
1003 char buf[BUF_SIZE];
1004 TIXML_SNPRINTF( buf, BUF_SIZE-1, "%u", v );
1005 value.SetStr( buf );
1006}
1007
1008
1009void XMLAttribute::SetAttribute( bool v )
1010{
1011 char buf[BUF_SIZE];
1012 TIXML_SNPRINTF( buf, BUF_SIZE-1, "%d", v ? 1 : 0 );
1013 value.SetStr( buf );
1014}
1015
1016void XMLAttribute::SetAttribute( double v )
1017{
1018 char buf[BUF_SIZE];
1019 TIXML_SNPRINTF( buf, BUF_SIZE-1, "%f", v );
1020 value.SetStr( buf );
1021}
1022
1023void XMLAttribute::SetAttribute( float v )
1024{
1025 char buf[BUF_SIZE];
1026 TIXML_SNPRINTF( buf, BUF_SIZE-1, "%f", v );
1027 value.SetStr( buf );
1028}
1029
Lee Thomasondadcdfa2012-01-18 17:55:48 -08001030
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001031// --------- XMLElement ---------- //
1032XMLElement::XMLElement( XMLDocument* doc ) : XMLNode( doc ),
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -08001033 closingType( 0 ),
U-Stream\Lee09a11c52012-02-17 08:31:16 -08001034 rootAttribute( 0 )
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001035{
1036}
1037
1038
1039XMLElement::~XMLElement()
1040{
U-Stream\Lee09a11c52012-02-17 08:31:16 -08001041 while( rootAttribute ) {
1042 XMLAttribute* next = rootAttribute->next;
1043 DELETE_ATTRIBUTE( rootAttribute );
1044 rootAttribute = next;
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001045 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001046}
1047
1048
Lee Thomason1a1d4a72012-02-15 09:09:25 -08001049XMLAttribute* XMLElement::FindAttribute( const char* name )
1050{
1051 XMLAttribute* a = 0;
1052 for( a=rootAttribute; a; a = a->next ) {
1053 if ( XMLUtil::StringEqual( a->Name(), name ) )
1054 return a;
1055 }
1056 return 0;
1057}
1058
1059
1060const XMLAttribute* XMLElement::FindAttribute( const char* name ) const
1061{
1062 XMLAttribute* a = 0;
1063 for( a=rootAttribute; a; a = a->next ) {
1064 if ( XMLUtil::StringEqual( a->Name(), name ) )
1065 return a;
1066 }
1067 return 0;
1068}
1069
1070
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001071const char* XMLElement::GetText() const
1072{
1073 if ( FirstChild() && FirstChild()->ToText() ) {
1074 return FirstChild()->ToText()->Value();
1075 }
1076 return 0;
1077}
1078
1079
1080
Lee Thomason1a1d4a72012-02-15 09:09:25 -08001081XMLAttribute* XMLElement::FindOrCreateAttribute( const char* name )
1082{
1083 XMLAttribute* attrib = FindAttribute( name );
1084 if ( !attrib ) {
Lee Thomason (grinliz)9b093cc2012-02-25 21:30:18 -08001085 attrib = new (document->attributePool.Alloc() ) XMLAttribute();
Lee Thomason1a1d4a72012-02-15 09:09:25 -08001086 attrib->memPool = &document->attributePool;
U-Stream\Lee09a11c52012-02-17 08:31:16 -08001087 LinkAttribute( attrib );
1088 attrib->SetName( name );
Lee Thomason1a1d4a72012-02-15 09:09:25 -08001089 }
1090 return attrib;
1091}
1092
1093
U-Stream\Lee09a11c52012-02-17 08:31:16 -08001094void XMLElement::LinkAttribute( XMLAttribute* attrib )
1095{
1096 if ( rootAttribute ) {
1097 XMLAttribute* end = rootAttribute;
1098 while ( end->next )
1099 end = end->next;
1100 end->next = attrib;
1101 }
1102 else {
1103 rootAttribute = attrib;
1104 }
1105}
1106
1107
U-Stream\Leeae25a442012-02-17 17:48:16 -08001108void XMLElement::DeleteAttribute( const char* name )
1109{
1110 XMLAttribute* prev = 0;
1111 for( XMLAttribute* a=rootAttribute; a; a=a->next ) {
1112 if ( XMLUtil::StringEqual( name, a->Name() ) ) {
1113 if ( prev ) {
1114 prev->next = a->next;
1115 }
1116 else {
1117 rootAttribute = a->next;
1118 }
1119 DELETE_ATTRIBUTE( a );
1120 break;
1121 }
1122 prev = a;
1123 }
1124}
1125
1126
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -08001127char* XMLElement::ParseAttributes( char* p )
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001128{
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001129 const char* start = p;
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001130
1131 // Read the attributes.
1132 while( p ) {
Lee Thomason56bdd022012-02-09 18:16:58 -08001133 p = XMLUtil::SkipWhiteSpace( p );
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001134 if ( !p || !(*p) ) {
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001135 document->SetError( ERROR_PARSING_ELEMENT, start, Name() );
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001136 return 0;
1137 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001138
1139 // attribute.
Lee Thomason56bdd022012-02-09 18:16:58 -08001140 if ( XMLUtil::IsAlpha( *p ) ) {
Lee Thomason (grinliz)9b093cc2012-02-25 21:30:18 -08001141 XMLAttribute* attrib = new (document->attributePool.Alloc() ) XMLAttribute();
Lee Thomason43f59302012-02-06 18:18:11 -08001142 attrib->memPool = &document->attributePool;
Lee Thomasond1983222012-02-06 08:41:24 -08001143
Lee Thomason6f381b72012-03-02 12:59:39 -08001144 p = attrib->ParseDeep( p, document->ProcessEntities() );
Lee Thomasond6277762012-02-22 16:00:12 -08001145 if ( !p || Attribute( attrib->Name() ) ) {
Lee Thomason43f59302012-02-06 18:18:11 -08001146 DELETE_ATTRIBUTE( attrib );
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001147 document->SetError( ERROR_PARSING_ATTRIBUTE, start, p );
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001148 return 0;
1149 }
U-Stream\Lee09a11c52012-02-17 08:31:16 -08001150 LinkAttribute( attrib );
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001151 }
Lee Thomasone4422302012-01-20 17:59:50 -08001152 // end of the tag
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001153 else if ( *p == '/' && *(p+1) == '>' ) {
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -08001154 closingType = CLOSED;
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001155 return p+2; // done; sealed element.
1156 }
Lee Thomasone4422302012-01-20 17:59:50 -08001157 // end of the tag
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001158 else if ( *p == '>' ) {
1159 ++p;
1160 break;
1161 }
Lee Thomasone4422302012-01-20 17:59:50 -08001162 else {
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001163 document->SetError( ERROR_PARSING_ELEMENT, start, p );
Lee Thomasone4422302012-01-20 17:59:50 -08001164 return 0;
1165 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001166 }
Lee Thomason67d61312012-01-24 16:01:51 -08001167 return p;
1168}
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001169
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001170
Lee Thomason67d61312012-01-24 16:01:51 -08001171//
1172// <ele></ele>
1173// <ele>foo<b>bar</b></ele>
1174//
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -08001175char* XMLElement::ParseDeep( char* p, StrPair* strPair )
Lee Thomason67d61312012-01-24 16:01:51 -08001176{
1177 // Read the element name.
Lee Thomason56bdd022012-02-09 18:16:58 -08001178 p = XMLUtil::SkipWhiteSpace( p );
Lee Thomason67d61312012-01-24 16:01:51 -08001179 if ( !p ) return 0;
Lee Thomason67d61312012-01-24 16:01:51 -08001180
1181 // The closing element is the </element> form. It is
1182 // parsed just like a regular element then deleted from
1183 // the DOM.
1184 if ( *p == '/' ) {
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -08001185 closingType = CLOSING;
Lee Thomason67d61312012-01-24 16:01:51 -08001186 ++p;
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001187 }
Lee Thomason67d61312012-01-24 16:01:51 -08001188
Lee Thomason56bdd022012-02-09 18:16:58 -08001189 p = value.ParseName( p );
Lee Thomasond1983222012-02-06 08:41:24 -08001190 if ( value.Empty() ) return 0;
Lee Thomason67d61312012-01-24 16:01:51 -08001191
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -08001192 p = ParseAttributes( p );
1193 if ( !p || !*p || closingType )
Lee Thomason67d61312012-01-24 16:01:51 -08001194 return p;
1195
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -08001196 p = XMLNode::ParseDeep( p, strPair );
Lee Thomason67d61312012-01-24 16:01:51 -08001197 return p;
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001198}
1199
1200
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001201
1202XMLNode* XMLElement::ShallowClone( XMLDocument* doc ) const
1203{
1204 if ( !doc ) {
1205 doc = document;
1206 }
1207 XMLElement* element = doc->NewElement( Value() ); // fixme: this will always allocate memory. Intern?
1208 for( const XMLAttribute* a=FirstAttribute(); a; a=a->Next() ) {
1209 element->SetAttribute( a->Name(), a->Value() ); // fixme: this will always allocate memory. Intern?
1210 }
1211 return element;
1212}
1213
1214
1215bool XMLElement::ShallowEqual( const XMLNode* compare ) const
1216{
1217 const XMLElement* other = compare->ToElement();
1218 if ( other && XMLUtil::StringEqual( other->Value(), Value() )) {
1219
1220 const XMLAttribute* a=FirstAttribute();
1221 const XMLAttribute* b=other->FirstAttribute();
1222
1223 while ( a && b ) {
1224 if ( !XMLUtil::StringEqual( a->Value(), b->Value() ) ) {
1225 return false;
1226 }
1227 }
1228 if ( a || b ) {
1229 // different count
1230 return false;
1231 }
1232 return true;
1233 }
1234 return false;
1235}
1236
1237
Lee Thomason751da522012-02-10 08:50:51 -08001238bool XMLElement::Accept( XMLVisitor* visitor ) const
1239{
1240 if ( visitor->VisitEnter( *this, rootAttribute ) )
1241 {
1242 for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() )
1243 {
1244 if ( !node->Accept( visitor ) )
1245 break;
1246 }
1247 }
1248 return visitor->VisitExit( *this );
Lee Thomason751da522012-02-10 08:50:51 -08001249}
Lee Thomason56bdd022012-02-09 18:16:58 -08001250
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001251
Lee Thomason3f57d272012-01-11 15:30:03 -08001252// --------- XMLDocument ----------- //
Lee Thomason6f381b72012-03-02 12:59:39 -08001253XMLDocument::XMLDocument( bool _processEntities ) :
Lee Thomason18d68bd2012-01-26 18:17:26 -08001254 XMLNode( 0 ),
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001255 writeBOM( false ),
Lee Thomason6f381b72012-03-02 12:59:39 -08001256 processEntities( _processEntities ),
1257 errorID( 0 ),
1258 errorStr1( 0 ),
1259 errorStr2( 0 ),
U-Lama\Lee560bd472011-12-28 19:42:49 -08001260 charBuffer( 0 )
1261{
Lee Thomason18d68bd2012-01-26 18:17:26 -08001262 document = this; // avoid warning about 'this' in initializer list
U-Lama\Lee560bd472011-12-28 19:42:49 -08001263}
U-Lama\Leee13c3e62011-12-28 14:36:55 -08001264
1265
Lee Thomason3f57d272012-01-11 15:30:03 -08001266XMLDocument::~XMLDocument()
1267{
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001268 DeleteChildren();
Lee Thomason18d68bd2012-01-26 18:17:26 -08001269 delete [] charBuffer;
Lee Thomasond1983222012-02-06 08:41:24 -08001270
Lee Thomasonec5a7b42012-02-13 18:16:52 -08001271#if 0
Lee Thomason455c9d42012-02-06 09:14:14 -08001272 textPool.Trace( "text" );
1273 elementPool.Trace( "element" );
1274 commentPool.Trace( "comment" );
1275 attributePool.Trace( "attribute" );
Lee Thomasone9ecdab2012-02-13 18:11:20 -08001276#endif
1277
Lee Thomason455c9d42012-02-06 09:14:14 -08001278 TIXMLASSERT( textPool.CurrentAllocs() == 0 );
1279 TIXMLASSERT( elementPool.CurrentAllocs() == 0 );
1280 TIXMLASSERT( commentPool.CurrentAllocs() == 0 );
1281 TIXMLASSERT( attributePool.CurrentAllocs() == 0 );
Lee Thomason3f57d272012-01-11 15:30:03 -08001282}
1283
1284
Lee Thomason18d68bd2012-01-26 18:17:26 -08001285void XMLDocument::InitDocument()
1286{
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001287 errorID = XML_NO_ERROR;
Lee Thomason18d68bd2012-01-26 18:17:26 -08001288 errorStr1 = 0;
1289 errorStr2 = 0;
1290
1291 delete [] charBuffer;
1292 charBuffer = 0;
1293
1294}
1295
Lee Thomason3f57d272012-01-11 15:30:03 -08001296
Lee Thomason2c85a712012-01-31 08:24:24 -08001297XMLElement* XMLDocument::NewElement( const char* name )
1298{
Lee Thomasond1983222012-02-06 08:41:24 -08001299 XMLElement* ele = new (elementPool.Alloc()) XMLElement( this );
1300 ele->memPool = &elementPool;
Lee Thomason2c85a712012-01-31 08:24:24 -08001301 ele->SetName( name );
1302 return ele;
1303}
1304
1305
Lee Thomason1ff38e02012-02-14 18:18:16 -08001306XMLComment* XMLDocument::NewComment( const char* str )
1307{
1308 XMLComment* comment = new (commentPool.Alloc()) XMLComment( this );
1309 comment->memPool = &commentPool;
1310 comment->SetValue( str );
1311 return comment;
1312}
1313
1314
1315XMLText* XMLDocument::NewText( const char* str )
1316{
U-Stream\Lee09a11c52012-02-17 08:31:16 -08001317 XMLText* text = new (textPool.Alloc()) XMLText( this );
1318 text->memPool = &textPool;
1319 text->SetValue( str );
1320 return text;
Lee Thomason1ff38e02012-02-14 18:18:16 -08001321}
1322
1323
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001324XMLDeclaration* XMLDocument::NewDeclaration( const char* str )
1325{
1326 XMLDeclaration* dec = new (commentPool.Alloc()) XMLDeclaration( this );
1327 dec->memPool = &commentPool;
1328 dec->SetValue( str );
1329 return dec;
1330}
1331
1332
1333XMLUnknown* XMLDocument::NewUnknown( const char* str )
1334{
1335 XMLUnknown* unk = new (commentPool.Alloc()) XMLUnknown( this );
1336 unk->memPool = &commentPool;
1337 unk->SetValue( str );
1338 return unk;
1339}
1340
1341
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001342int XMLDocument::LoadFile( const char* filename )
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001343{
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001344 DeleteChildren();
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001345 InitDocument();
1346
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001347#if defined(_MSC_VER)
Lee Thomason (grinliz)5ce4d972012-02-26 21:14:23 -08001348#pragma warning ( push )
1349#pragma warning ( disable : 4996 ) // Fail to see a compelling reason why this should be deprecated.
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001350#endif
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001351 FILE* fp = fopen( filename, "rb" );
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001352#if defined(_MSC_VER)
Lee Thomason (grinliz)5ce4d972012-02-26 21:14:23 -08001353#pragma warning ( pop )
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001354#endif
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001355 if ( !fp ) {
1356 SetError( ERROR_FILE_NOT_FOUND, filename, 0 );
1357 return errorID;
1358 }
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001359 LoadFile( fp );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001360 fclose( fp );
1361 return errorID;
1362}
1363
1364
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001365int XMLDocument::LoadFile( FILE* fp )
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001366{
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001367 DeleteChildren();
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001368 InitDocument();
1369
1370 fseek( fp, 0, SEEK_END );
1371 unsigned size = ftell( fp );
1372 fseek( fp, 0, SEEK_SET );
1373
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001374 if ( size == 0 ) {
1375 return errorID;
1376 }
1377
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001378 charBuffer = new char[size+1];
1379 fread( charBuffer, size, 1, fp );
1380 charBuffer[size] = 0;
1381
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001382 const char* p = charBuffer;
1383 p = XMLUtil::SkipWhiteSpace( p );
1384 p = XMLUtil::ReadBOM( p, &writeBOM );
1385 if ( !p || !*p ) {
Lee Thomasond6277762012-02-22 16:00:12 -08001386 SetError( ERROR_EMPTY_DOCUMENT, 0, 0 );
1387 return errorID;
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001388 }
1389
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -08001390 ParseDeep( charBuffer + (p-charBuffer), 0 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001391 return errorID;
1392}
1393
1394
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001395void XMLDocument::SaveFile( const char* filename )
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001396{
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001397#if defined(_MSC_VER)
Lee Thomason (grinliz)5ce4d972012-02-26 21:14:23 -08001398#pragma warning ( push )
1399#pragma warning ( disable : 4996 ) // Fail to see a compelling reason why this should be deprecated.
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001400#endif
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001401 FILE* fp = fopen( filename, "w" );
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001402#if defined(_MSC_VER)
Lee Thomason (grinliz)5ce4d972012-02-26 21:14:23 -08001403#pragma warning ( pop )
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001404#endif
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001405 XMLPrinter stream( fp );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001406 Print( &stream );
1407 fclose( fp );
1408}
1409
Lee Thomason1ff38e02012-02-14 18:18:16 -08001410
Lee Thomason7c913cd2012-01-26 18:32:34 -08001411int XMLDocument::Parse( const char* p )
Lee Thomason3f57d272012-01-11 15:30:03 -08001412{
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001413 DeleteChildren();
Lee Thomason18d68bd2012-01-26 18:17:26 -08001414 InitDocument();
1415
1416 if ( !p || !*p ) {
Lee Thomasond6277762012-02-22 16:00:12 -08001417 SetError( ERROR_EMPTY_DOCUMENT, 0, 0 );
1418 return errorID;
Lee Thomason18d68bd2012-01-26 18:17:26 -08001419 }
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001420 p = XMLUtil::SkipWhiteSpace( p );
1421 p = XMLUtil::ReadBOM( p, &writeBOM );
1422 if ( !p || !*p ) {
Lee Thomasond6277762012-02-22 16:00:12 -08001423 SetError( ERROR_EMPTY_DOCUMENT, 0, 0 );
1424 return errorID;
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001425 }
1426
Lee Thomason18d68bd2012-01-26 18:17:26 -08001427 size_t len = strlen( p );
1428 charBuffer = new char[ len+1 ];
1429 memcpy( charBuffer, p, len+1 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001430
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001431
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -08001432 ParseDeep( charBuffer, 0 );
Lee Thomason7c913cd2012-01-26 18:32:34 -08001433 return errorID;
Lee Thomason3f57d272012-01-11 15:30:03 -08001434}
1435
1436
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001437void XMLDocument::Print( XMLPrinter* streamer )
Lee Thomason3f57d272012-01-11 15:30:03 -08001438{
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001439 XMLPrinter stdStreamer( stdout );
Lee Thomason5cae8972012-01-24 18:03:07 -08001440 if ( !streamer )
1441 streamer = &stdStreamer;
Lee Thomason751da522012-02-10 08:50:51 -08001442 Accept( streamer );
Lee Thomason3f57d272012-01-11 15:30:03 -08001443}
1444
1445
Lee Thomason67d61312012-01-24 16:01:51 -08001446void XMLDocument::SetError( int error, const char* str1, const char* str2 )
1447{
Lee Thomason18d68bd2012-01-26 18:17:26 -08001448 errorID = error;
Lee Thomason18d68bd2012-01-26 18:17:26 -08001449 errorStr1 = str1;
1450 errorStr2 = str2;
Lee Thomason67d61312012-01-24 16:01:51 -08001451}
1452
Lee Thomason5cae8972012-01-24 18:03:07 -08001453
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001454void XMLDocument::PrintError() const
1455{
1456 if ( errorID ) {
Lee Thomason (grinliz)5ce4d972012-02-26 21:14:23 -08001457 static const int LEN = 20;
1458 char buf1[LEN] = { 0 };
1459 char buf2[LEN] = { 0 };
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001460
1461 if ( errorStr1 ) {
Lee Thomason (grinliz)5ce4d972012-02-26 21:14:23 -08001462 TIXML_SNPRINTF( buf1, LEN, "%s", errorStr1 );
1463 buf1[LEN-1] = 0;
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001464 }
1465 if ( errorStr2 ) {
Lee Thomason (grinliz)5ce4d972012-02-26 21:14:23 -08001466 TIXML_SNPRINTF( buf2, LEN, "%s", errorStr2 );
1467 buf2[LEN-1] = 0;
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001468 }
1469
1470 printf( "XMLDocument error id=%d str1=%s str2=%s\n",
1471 errorID, buf1, buf2 );
1472 }
1473}
1474
1475
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001476XMLPrinter::XMLPrinter( FILE* file ) :
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001477 elementJustOpened( false ),
1478 firstElement( true ),
1479 fp( file ),
1480 depth( 0 ),
Lee Thomason6f381b72012-03-02 12:59:39 -08001481 textDepth( -1 ),
1482 processEntities( true )
Lee Thomason5cae8972012-01-24 18:03:07 -08001483{
Lee Thomason857b8682012-01-25 17:50:25 -08001484 for( int i=0; i<ENTITY_RANGE; ++i ) {
1485 entityFlag[i] = false;
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001486 restrictedEntityFlag[i] = false;
Lee Thomason857b8682012-01-25 17:50:25 -08001487 }
1488 for( int i=0; i<NUM_ENTITIES; ++i ) {
1489 TIXMLASSERT( entities[i].value < ENTITY_RANGE );
1490 if ( entities[i].value < ENTITY_RANGE ) {
Lee Thomason (grinliz)9b093cc2012-02-25 21:30:18 -08001491 entityFlag[ (int)entities[i].value ] = true;
Lee Thomason857b8682012-01-25 17:50:25 -08001492 }
1493 }
Lee Thomason (grinliz)9b093cc2012-02-25 21:30:18 -08001494 restrictedEntityFlag[(int)'&'] = true;
1495 restrictedEntityFlag[(int)'<'] = true;
1496 restrictedEntityFlag[(int)'>'] = true; // not required, but consistency is nice
U-Stream\Leeae25a442012-02-17 17:48:16 -08001497 buffer.Push( 0 );
1498}
1499
1500
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001501void XMLPrinter::Print( const char* format, ... )
U-Stream\Leeae25a442012-02-17 17:48:16 -08001502{
1503 va_list va;
1504 va_start( va, format );
1505
1506 if ( fp ) {
1507 vfprintf( fp, format, va );
1508 }
1509 else {
1510 // This seems brutally complex. Haven't figured out a better
1511 // way on windows.
1512 #ifdef _MSC_VER
1513 int len = -1;
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001514 int expand = 1000;
U-Stream\Leeae25a442012-02-17 17:48:16 -08001515 while ( len < 0 ) {
1516 len = vsnprintf_s( accumulator.Mem(), accumulator.Capacity(), accumulator.Capacity()-1, format, va );
1517 if ( len < 0 ) {
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001518 accumulator.PushArr( expand );
1519 expand *= 3/2;
U-Stream\Leeae25a442012-02-17 17:48:16 -08001520 }
1521 }
1522 char* p = buffer.PushArr( len ) - 1;
1523 memcpy( p, accumulator.Mem(), len+1 );
1524 #else
1525 int len = vsnprintf( 0, 0, format, va );
1526 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}