blob: 08917e1fb95df1db9d51af8633a05e40187e57a7 [file] [log] [blame]
Lee Thomason (grinliz)28129862012-02-25 21:11:20 -08001/*
2Original code by Lee Thomason (www.grinninglizard.com)
3
4This software is provided 'as-is', without any express or implied
5warranty. In no event will the authors be held liable for any
6damages arising from the use of this software.
7
8Permission is granted to anyone to use this software for any
9purpose, including commercial applications, and to alter it and
10redistribute it freely, subject to the following restrictions:
11
121. The origin of this software must not be misrepresented; you must
13not claim that you wrote the original software. If you use this
14software in a product, an acknowledgment in the product documentation
15would be appreciated but is not required.
16
172. Altered source versions must be plainly marked as such, and
18must not be misrepresented as being the original software.
19
203. This notice may not be removed or altered from any source
21distribution.
22*/
Lee Thomason (grinliz)9c38d132012-02-24 21:50:50 -080023
U-Lama\Lee560bd472011-12-28 19:42:49 -080024#include "tinyxml2.h"
25
Lee Thomasonee87c622012-05-14 09:27:47 -070026#include <cstdio>
27#include <cstdlib>
28#include <new>
29#include <cstddef>
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;
Thomas Roß7d7a9a32012-05-10 00:23:19 +0200115 size_t length = strlen( endTag );
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800116
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
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800133 if ( !start || !(*start) ) {
134 return 0;
135 }
136
137 if ( !XMLUtil::IsAlpha( *p ) ) {
138 return 0;
139 }
140
141 while( *p && (
142 XMLUtil::IsAlphaNum( (unsigned char) *p )
143 || *p == '_'
144 || *p == '-'
145 || *p == '.'
146 || *p == ':' ))
147 {
148 ++p;
149 }
150
151 if ( p > start ) {
152 Set( start, p, 0 );
153 return p;
154 }
155 return 0;
156}
157
158
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800159
Lee Thomasone4422302012-01-20 17:59:50 -0800160const char* StrPair::GetStr()
161{
162 if ( flags & NEEDS_FLUSH ) {
163 *end = 0;
Lee Thomason8ee79892012-01-25 17:44:30 -0800164 flags ^= NEEDS_FLUSH;
Lee Thomasone4422302012-01-20 17:59:50 -0800165
Lee Thomason8ee79892012-01-25 17:44:30 -0800166 if ( flags ) {
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800167 char* p = start; // the read pointer
168 char* q = start; // the write pointer
Lee Thomasone4422302012-01-20 17:59:50 -0800169
170 while( p < end ) {
Lee Thomason8ee79892012-01-25 17:44:30 -0800171 if ( (flags & NEEDS_NEWLINE_NORMALIZATION) && *p == CR ) {
Lee Thomasone4422302012-01-20 17:59:50 -0800172 // CR-LF pair becomes LF
173 // CR alone becomes LF
174 // LF-CR becomes LF
175 if ( *(p+1) == LF ) {
176 p += 2;
177 }
178 else {
179 ++p;
180 }
Lee Thomasone9ecdab2012-02-13 18:11:20 -0800181 *q++ = LF;
Lee Thomasone4422302012-01-20 17:59:50 -0800182 }
Lee Thomason8ee79892012-01-25 17:44:30 -0800183 else if ( (flags & NEEDS_NEWLINE_NORMALIZATION) && *p == LF ) {
Lee Thomasone4422302012-01-20 17:59:50 -0800184 if ( *(p+1) == CR ) {
185 p += 2;
186 }
187 else {
188 ++p;
189 }
Lee Thomasone9ecdab2012-02-13 18:11:20 -0800190 *q++ = LF;
Lee Thomasone4422302012-01-20 17:59:50 -0800191 }
Lee Thomason8ee79892012-01-25 17:44:30 -0800192 else if ( (flags & NEEDS_ENTITY_PROCESSING) && *p == '&' ) {
193 int i=0;
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800194
195 // Entities handled by tinyXML2:
196 // - special entities in the entity table [in/out]
197 // - numeric character reference [in]
198 // &#20013; or &#x4e2d;
199
200 if ( *(p+1) == '#' ) {
201 char buf[10] = { 0 };
202 int len;
203 p = const_cast<char*>( XMLUtil::GetCharacterRef( p, buf, &len ) );
204 for( int i=0; i<len; ++i ) {
205 *q++ = buf[i];
Lee Thomason8ee79892012-01-25 17:44:30 -0800206 }
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800207 TIXMLASSERT( q <= p );
Lee Thomason8ee79892012-01-25 17:44:30 -0800208 }
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800209 else {
210 for( i=0; i<NUM_ENTITIES; ++i ) {
211 if ( strncmp( p+1, entities[i].pattern, entities[i].length ) == 0
212 && *(p+entities[i].length+1) == ';' )
213 {
214 // Found an entity convert;
215 *q = entities[i].value;
216 ++q;
217 p += entities[i].length + 2;
218 break;
219 }
220 }
221 if ( i == NUM_ENTITIES ) {
222 // fixme: treat as error?
223 ++p;
224 ++q;
225 }
Lee Thomason8ee79892012-01-25 17:44:30 -0800226 }
227 }
Lee Thomasone4422302012-01-20 17:59:50 -0800228 else {
229 *q = *p;
230 ++p;
Lee Thomasonec975ce2012-01-23 11:42:06 -0800231 ++q;
Lee Thomasone4422302012-01-20 17:59:50 -0800232 }
233 }
Lee Thomason8ee79892012-01-25 17:44:30 -0800234 *q = 0;
Lee Thomasone4422302012-01-20 17:59:50 -0800235 }
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800236 flags = (flags & NEEDS_DELETE);
Lee Thomasone4422302012-01-20 17:59:50 -0800237 }
238 return start;
239}
240
Lee Thomason2c85a712012-01-31 08:24:24 -0800241
Lee Thomasone4422302012-01-20 17:59:50 -0800242
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800243
Lee Thomason56bdd022012-02-09 18:16:58 -0800244// --------- XMLUtil ----------- //
Lee Thomasond1983222012-02-06 08:41:24 -0800245
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800246const char* XMLUtil::ReadBOM( const char* p, bool* bom )
247{
248 *bom = false;
249 const unsigned char* pu = reinterpret_cast<const unsigned char*>(p);
250 // Check for BOM:
251 if ( *(pu+0) == TIXML_UTF_LEAD_0
252 && *(pu+1) == TIXML_UTF_LEAD_1
253 && *(pu+2) == TIXML_UTF_LEAD_2 )
254 {
255 *bom = true;
256 p += 3;
257 }
258 return p;
259}
260
261
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800262void XMLUtil::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length )
263{
264 const unsigned long BYTE_MASK = 0xBF;
265 const unsigned long BYTE_MARK = 0x80;
266 const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
267
268 if (input < 0x80)
269 *length = 1;
270 else if ( input < 0x800 )
271 *length = 2;
272 else if ( input < 0x10000 )
273 *length = 3;
274 else if ( input < 0x200000 )
275 *length = 4;
276 else
277 { *length = 0; return; } // This code won't covert this correctly anyway.
278
279 output += *length;
280
281 // Scary scary fall throughs.
282 switch (*length)
283 {
284 case 4:
285 --output;
286 *output = (char)((input | BYTE_MARK) & BYTE_MASK);
287 input >>= 6;
288 case 3:
289 --output;
290 *output = (char)((input | BYTE_MARK) & BYTE_MASK);
291 input >>= 6;
292 case 2:
293 --output;
294 *output = (char)((input | BYTE_MARK) & BYTE_MASK);
295 input >>= 6;
296 case 1:
297 --output;
298 *output = (char)(input | FIRST_BYTE_MARK[*length]);
299 }
300}
301
302
303const char* XMLUtil::GetCharacterRef( const char* p, char* value, int* length )
304{
305 // Presume an entity, and pull it out.
306 *length = 0;
307
308 if ( *(p+1) == '#' && *(p+2) )
309 {
310 unsigned long ucs = 0;
Thomas Roß7d7a9a32012-05-10 00:23:19 +0200311 ptrdiff_t delta = 0;
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800312 unsigned mult = 1;
313
314 if ( *(p+2) == 'x' )
315 {
316 // Hexadecimal.
317 if ( !*(p+3) ) return 0;
318
319 const char* q = p+3;
320 q = strchr( q, ';' );
321
322 if ( !q || !*q ) return 0;
323
Thomas Roß7d7a9a32012-05-10 00:23:19 +0200324 delta = q-p;
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800325 --q;
326
327 while ( *q != 'x' )
328 {
329 if ( *q >= '0' && *q <= '9' )
330 ucs += mult * (*q - '0');
331 else if ( *q >= 'a' && *q <= 'f' )
332 ucs += mult * (*q - 'a' + 10);
333 else if ( *q >= 'A' && *q <= 'F' )
334 ucs += mult * (*q - 'A' + 10 );
335 else
336 return 0;
337 mult *= 16;
338 --q;
339 }
340 }
341 else
342 {
343 // Decimal.
344 if ( !*(p+2) ) return 0;
345
346 const char* q = p+2;
347 q = strchr( q, ';' );
348
349 if ( !q || !*q ) return 0;
350
351 delta = q-p;
352 --q;
353
354 while ( *q != '#' )
355 {
356 if ( *q >= '0' && *q <= '9' )
357 ucs += mult * (*q - '0');
358 else
359 return 0;
360 mult *= 10;
361 --q;
362 }
363 }
364 // convert the UCS to UTF-8
365 ConvertUTF32ToUTF8( ucs, value, length );
366 return p + delta + 1;
367 }
368 return p+1;
369}
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800370
371
Lee Thomasond1983222012-02-06 08:41:24 -0800372char* XMLDocument::Identify( char* p, XMLNode** node )
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800373{
374 XMLNode* returnNode = 0;
Lee Thomason5492a1c2012-01-23 15:32:10 -0800375 char* start = p;
Lee Thomason56bdd022012-02-09 18:16:58 -0800376 p = XMLUtil::SkipWhiteSpace( p );
Lee Thomason5492a1c2012-01-23 15:32:10 -0800377 if( !p || !*p )
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800378 {
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800379 return p;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800380 }
381
382 // What is this thing?
383 // - Elements start with a letter or underscore, but xml is reserved.
384 // - Comments: <!--
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800385 // - Decleration: <?
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800386 // - Everthing else is unknown to tinyxml.
387 //
388
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800389 static const char* xmlHeader = { "<?" };
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800390 static const char* commentHeader = { "<!--" };
391 static const char* dtdHeader = { "<!" };
392 static const char* cdataHeader = { "<![CDATA[" };
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800393 static const char* elementHeader = { "<" }; // and a header for everything else; check last.
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800394
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800395 static const int xmlHeaderLen = 2;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800396 static const int commentHeaderLen = 4;
397 static const int dtdHeaderLen = 2;
398 static const int cdataHeaderLen = 9;
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800399 static const int elementHeaderLen = 1;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800400
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800401#if defined(_MSC_VER)
Lee Thomason (grinliz)9b093cc2012-02-25 21:30:18 -0800402#pragma warning ( push )
403#pragma warning ( disable : 4127 )
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800404#endif
Lee Thomason50f97b22012-02-11 16:33:40 -0800405 TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLUnknown ) ); // use same memory pool
406 TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLDeclaration ) ); // use same memory pool
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800407#if defined(_MSC_VER)
Lee Thomason (grinliz)9b093cc2012-02-25 21:30:18 -0800408#pragma warning (pop)
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800409#endif
Lee Thomason50f97b22012-02-11 16:33:40 -0800410 if ( XMLUtil::StringEqual( p, xmlHeader, xmlHeaderLen ) ) {
411 returnNode = new (commentPool.Alloc()) XMLDeclaration( this );
412 returnNode->memPool = &commentPool;
413 p += xmlHeaderLen;
414 }
415 else if ( XMLUtil::StringEqual( p, commentHeader, commentHeaderLen ) ) {
Lee Thomasond1983222012-02-06 08:41:24 -0800416 returnNode = new (commentPool.Alloc()) XMLComment( this );
417 returnNode->memPool = &commentPool;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800418 p += commentHeaderLen;
419 }
Lee Thomason50f97b22012-02-11 16:33:40 -0800420 else if ( XMLUtil::StringEqual( p, cdataHeader, cdataHeaderLen ) ) {
421 XMLText* text = new (textPool.Alloc()) XMLText( this );
422 returnNode = text;
423 returnNode->memPool = &textPool;
424 p += cdataHeaderLen;
425 text->SetCData( true );
426 }
427 else if ( XMLUtil::StringEqual( p, dtdHeader, dtdHeaderLen ) ) {
428 returnNode = new (commentPool.Alloc()) XMLUnknown( this );
429 returnNode->memPool = &commentPool;
430 p += dtdHeaderLen;
431 }
Lee Thomason56bdd022012-02-09 18:16:58 -0800432 else if ( XMLUtil::StringEqual( p, elementHeader, elementHeaderLen ) ) {
Lee Thomasond1983222012-02-06 08:41:24 -0800433 returnNode = new (elementPool.Alloc()) XMLElement( this );
434 returnNode->memPool = &elementPool;
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800435 p += elementHeaderLen;
436 }
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800437 else {
Lee Thomasond1983222012-02-06 08:41:24 -0800438 returnNode = new (textPool.Alloc()) XMLText( this );
439 returnNode->memPool = &textPool;
Lee Thomason5492a1c2012-01-23 15:32:10 -0800440 p = start; // Back it up, all the text counts.
441 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800442
443 *node = returnNode;
444 return p;
445}
446
447
Lee Thomason751da522012-02-10 08:50:51 -0800448bool XMLDocument::Accept( XMLVisitor* visitor ) const
449{
450 if ( visitor->VisitEnter( *this ) )
451 {
452 for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() )
453 {
454 if ( !node->Accept( visitor ) )
455 break;
456 }
457 }
458 return visitor->VisitExit( *this );
459}
Lee Thomason56bdd022012-02-09 18:16:58 -0800460
461
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800462// --------- XMLNode ----------- //
463
464XMLNode::XMLNode( XMLDocument* doc ) :
465 document( doc ),
466 parent( 0 ),
467 firstChild( 0 ), lastChild( 0 ),
468 prev( 0 ), next( 0 )
469{
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800470}
471
472
473XMLNode::~XMLNode()
474{
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -0800475 DeleteChildren();
Lee Thomason7c913cd2012-01-26 18:32:34 -0800476 if ( parent ) {
477 parent->Unlink( this );
478 }
Lee Thomason18d68bd2012-01-26 18:17:26 -0800479}
480
481
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800482void XMLNode::SetValue( const char* str, bool staticMem )
483{
484 if ( staticMem )
485 value.SetInternedStr( str );
486 else
487 value.SetStr( str );
488}
489
490
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -0800491void XMLNode::DeleteChildren()
Lee Thomason18d68bd2012-01-26 18:17:26 -0800492{
Lee Thomasond923c672012-01-23 08:44:25 -0800493 while( firstChild ) {
494 XMLNode* node = firstChild;
495 Unlink( node );
Lee Thomasond1983222012-02-06 08:41:24 -0800496
Lee Thomason43f59302012-02-06 18:18:11 -0800497 DELETE_NODE( node );
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800498 }
Lee Thomason18d68bd2012-01-26 18:17:26 -0800499 firstChild = lastChild = 0;
Lee Thomasond923c672012-01-23 08:44:25 -0800500}
501
502
503void XMLNode::Unlink( XMLNode* child )
504{
505 TIXMLASSERT( child->parent == this );
506 if ( child == firstChild )
507 firstChild = firstChild->next;
508 if ( child == lastChild )
509 lastChild = lastChild->prev;
510
511 if ( child->prev ) {
512 child->prev->next = child->next;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800513 }
Lee Thomasond923c672012-01-23 08:44:25 -0800514 if ( child->next ) {
515 child->next->prev = child->prev;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800516 }
Lee Thomasond923c672012-01-23 08:44:25 -0800517 child->parent = 0;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800518}
519
520
U-Stream\Leeae25a442012-02-17 17:48:16 -0800521void XMLNode::DeleteChild( XMLNode* node )
522{
523 TIXMLASSERT( node->parent == this );
524 DELETE_NODE( node );
525}
526
527
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800528XMLNode* XMLNode::InsertEndChild( XMLNode* addThis )
529{
530 if ( lastChild ) {
531 TIXMLASSERT( firstChild );
532 TIXMLASSERT( lastChild->next == 0 );
533 lastChild->next = addThis;
534 addThis->prev = lastChild;
535 lastChild = addThis;
536
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800537 addThis->next = 0;
538 }
539 else {
540 TIXMLASSERT( firstChild == 0 );
541 firstChild = lastChild = addThis;
542
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800543 addThis->prev = 0;
544 addThis->next = 0;
545 }
U-Stream\Lee09a11c52012-02-17 08:31:16 -0800546 addThis->parent = this;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800547 return addThis;
548}
549
550
Lee Thomason1ff38e02012-02-14 18:18:16 -0800551XMLNode* XMLNode::InsertFirstChild( XMLNode* addThis )
552{
553 if ( firstChild ) {
554 TIXMLASSERT( lastChild );
555 TIXMLASSERT( firstChild->prev == 0 );
556
557 firstChild->prev = addThis;
558 addThis->next = firstChild;
559 firstChild = addThis;
560
Lee Thomason1ff38e02012-02-14 18:18:16 -0800561 addThis->prev = 0;
562 }
563 else {
564 TIXMLASSERT( lastChild == 0 );
565 firstChild = lastChild = addThis;
566
Lee Thomason1ff38e02012-02-14 18:18:16 -0800567 addThis->prev = 0;
568 addThis->next = 0;
569 }
U-Stream\Lee09a11c52012-02-17 08:31:16 -0800570 addThis->parent = this;
Lee Thomason1ff38e02012-02-14 18:18:16 -0800571 return addThis;
572}
573
574
575XMLNode* XMLNode::InsertAfterChild( XMLNode* afterThis, XMLNode* addThis )
576{
577 TIXMLASSERT( afterThis->parent == this );
578 if ( afterThis->parent != this )
579 return 0;
580
581 if ( afterThis->next == 0 ) {
582 // The last node or the only node.
583 return InsertEndChild( addThis );
584 }
585 addThis->prev = afterThis;
586 addThis->next = afterThis->next;
587 afterThis->next->prev = addThis;
588 afterThis->next = addThis;
U-Stream\Lee09a11c52012-02-17 08:31:16 -0800589 addThis->parent = this;
Lee Thomason1ff38e02012-02-14 18:18:16 -0800590 return addThis;
591}
592
593
594
595
Lee Thomason56bdd022012-02-09 18:16:58 -0800596const XMLElement* XMLNode::FirstChildElement( const char* value ) const
Lee Thomason2c85a712012-01-31 08:24:24 -0800597{
598 for( XMLNode* node=firstChild; node; node=node->next ) {
599 XMLElement* element = node->ToElement();
600 if ( element ) {
Lee Thomason56bdd022012-02-09 18:16:58 -0800601 if ( !value || XMLUtil::StringEqual( element->Name(), value ) ) {
Lee Thomason2c85a712012-01-31 08:24:24 -0800602 return element;
603 }
604 }
605 }
606 return 0;
607}
608
609
Lee Thomason56bdd022012-02-09 18:16:58 -0800610const XMLElement* XMLNode::LastChildElement( const char* value ) const
611{
612 for( XMLNode* node=lastChild; node; node=node->prev ) {
613 XMLElement* element = node->ToElement();
614 if ( element ) {
615 if ( !value || XMLUtil::StringEqual( element->Name(), value ) ) {
616 return element;
617 }
618 }
619 }
620 return 0;
621}
622
623
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800624const XMLElement* XMLNode::NextSiblingElement( const char* value ) const
625{
626 for( XMLNode* element=this->next; element; element = element->next ) {
627 if ( element->ToElement()
628 && (!value || XMLUtil::StringEqual( value, element->Value() )))
629 {
630 return element->ToElement();
631 }
632 }
633 return 0;
634}
635
636
637const XMLElement* XMLNode::PreviousSiblingElement( const char* value ) const
638{
639 for( XMLNode* element=this->prev; element; element = element->prev ) {
640 if ( element->ToElement()
641 && (!value || XMLUtil::StringEqual( value, element->Value() )))
642 {
643 return element->ToElement();
644 }
645 }
646 return 0;
647}
648
649
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800650char* XMLNode::ParseDeep( char* p, StrPair* parentEnd )
Lee Thomason67d61312012-01-24 16:01:51 -0800651{
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800652 // This is a recursive method, but thinking about it "at the current level"
653 // it is a pretty simple flat list:
654 // <foo/>
655 // <!-- comment -->
656 //
657 // With a special case:
658 // <foo>
659 // </foo>
660 // <!-- comment -->
661 //
662 // Where the closing element (/foo) *must* be the next thing after the opening
663 // element, and the names must match. BUT the tricky bit is that the closing
664 // element will be read by the child.
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800665 //
666 // 'endTag' is the end tag for this node, it is returned by a call to a child.
667 // 'parentEnd' is the end tag for the parent, which is filled in and returned.
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800668
Lee Thomason67d61312012-01-24 16:01:51 -0800669 while( p && *p ) {
670 XMLNode* node = 0;
Lee Thomasond6277762012-02-22 16:00:12 -0800671
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800672 p = document->Identify( p, &node );
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800673 if ( p == 0 || node == 0 ) {
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800674 break;
675 }
676
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800677 StrPair endTag;
678 p = node->ParseDeep( p, &endTag );
679 if ( !p ) {
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800680 DELETE_NODE( node );
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800681 node = 0;
Lee Thomason (grinliz)784607f2012-02-24 16:23:40 -0800682 if ( !document->Error() ) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -0700683 document->SetError( XML_ERROR_PARSING, 0, 0 );
Lee Thomason (grinliz)784607f2012-02-24 16:23:40 -0800684 }
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800685 break;
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800686 }
687
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800688 // We read the end tag. Return it to the parent.
689 if ( node->ToElement() && node->ToElement()->ClosingType() == XMLElement::CLOSING ) {
690 if ( parentEnd ) {
691 *parentEnd = ((XMLElement*)node)->value;
Lee Thomason67d61312012-01-24 16:01:51 -0800692 }
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800693 DELETE_NODE( node );
694 return p;
695 }
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800696
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800697 // Handle an end tag returned to this level.
698 // And handle a bunch of annoying errors.
699 XMLElement* ele = node->ToElement();
700 if ( ele ) {
701 if ( endTag.Empty() && ele->ClosingType() == XMLElement::OPEN ) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -0700702 document->SetError( XML_ERROR_MISMATCHED_ELEMENT, node->Value(), 0 );
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800703 p = 0;
704 }
705 else if ( !endTag.Empty() && ele->ClosingType() != XMLElement::OPEN ) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -0700706 document->SetError( XML_ERROR_MISMATCHED_ELEMENT, node->Value(), 0 );
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800707 p = 0;
708 }
709 else if ( !endTag.Empty() ) {
710 if ( !XMLUtil::StringEqual( endTag.GetStr(), node->Value() )) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -0700711 document->SetError( XML_ERROR_MISMATCHED_ELEMENT, node->Value(), 0 );
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800712 p = 0;
713 }
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800714 }
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800715 }
716 if ( p == 0 ) {
717 DELETE_NODE( node );
718 node = 0;
719 }
720 if ( node ) {
721 this->InsertEndChild( node );
Lee Thomason67d61312012-01-24 16:01:51 -0800722 }
723 }
724 return 0;
725}
726
Lee Thomason5492a1c2012-01-23 15:32:10 -0800727// --------- XMLText ---------- //
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800728char* XMLText::ParseDeep( char* p, StrPair* )
Lee Thomason5492a1c2012-01-23 15:32:10 -0800729{
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800730 const char* start = p;
Lee Thomason50f97b22012-02-11 16:33:40 -0800731 if ( this->CData() ) {
732 p = value.ParseText( p, "]]>", StrPair::NEEDS_NEWLINE_NORMALIZATION );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800733 if ( !p ) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -0700734 document->SetError( XML_ERROR_PARSING_CDATA, start, 0 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800735 }
Lee Thomason50f97b22012-02-11 16:33:40 -0800736 return p;
737 }
738 else {
Lee Thomason6f381b72012-03-02 12:59:39 -0800739 p = value.ParseText( p, "<", document->ProcessEntities() ? StrPair::TEXT_ELEMENT : StrPair::TEXT_ELEMENT_LEAVE_ENTITIES );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800740 if ( !p ) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -0700741 document->SetError( XML_ERROR_PARSING_TEXT, start, 0 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800742 }
Lee Thomason50f97b22012-02-11 16:33:40 -0800743 if ( p && *p ) {
744 return p-1;
745 }
Lee Thomason5492a1c2012-01-23 15:32:10 -0800746 }
747 return 0;
748}
749
750
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800751XMLNode* XMLText::ShallowClone( XMLDocument* doc ) const
752{
753 if ( !doc ) {
754 doc = document;
755 }
756 XMLText* text = doc->NewText( Value() ); // fixme: this will always allocate memory. Intern?
757 text->SetCData( this->CData() );
758 return text;
759}
760
761
762bool XMLText::ShallowEqual( const XMLNode* compare ) const
763{
764 return ( compare->ToText() && XMLUtil::StringEqual( compare->ToText()->Value(), Value() ));
765}
766
767
Lee Thomason56bdd022012-02-09 18:16:58 -0800768bool XMLText::Accept( XMLVisitor* visitor ) const
769{
770 return visitor->Visit( *this );
771}
772
773
Lee Thomason3f57d272012-01-11 15:30:03 -0800774// --------- XMLComment ---------- //
775
Lee Thomasone4422302012-01-20 17:59:50 -0800776XMLComment::XMLComment( XMLDocument* doc ) : XMLNode( doc )
Lee Thomason3f57d272012-01-11 15:30:03 -0800777{
778}
779
780
Lee Thomasonce0763e2012-01-11 15:43:54 -0800781XMLComment::~XMLComment()
Lee Thomason3f57d272012-01-11 15:30:03 -0800782{
Lee Thomasond923c672012-01-23 08:44:25 -0800783 //printf( "~XMLComment\n" );
Lee Thomason3f57d272012-01-11 15:30:03 -0800784}
785
786
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800787char* XMLComment::ParseDeep( char* p, StrPair* )
Lee Thomason3f57d272012-01-11 15:30:03 -0800788{
789 // Comment parses as text.
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800790 const char* start = p;
791 p = value.ParseText( p, "-->", StrPair::COMMENT );
792 if ( p == 0 ) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -0700793 document->SetError( XML_ERROR_PARSING_COMMENT, start, 0 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800794 }
795 return p;
U-Lama\Lee4cee6112011-12-31 14:58:18 -0800796}
797
798
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800799XMLNode* XMLComment::ShallowClone( XMLDocument* doc ) const
800{
801 if ( !doc ) {
802 doc = document;
803 }
804 XMLComment* comment = doc->NewComment( Value() ); // fixme: this will always allocate memory. Intern?
805 return comment;
806}
807
808
809bool XMLComment::ShallowEqual( const XMLNode* compare ) const
810{
811 return ( compare->ToComment() && XMLUtil::StringEqual( compare->ToComment()->Value(), Value() ));
812}
813
814
Lee Thomason751da522012-02-10 08:50:51 -0800815bool XMLComment::Accept( XMLVisitor* visitor ) const
816{
817 return visitor->Visit( *this );
818}
Lee Thomason56bdd022012-02-09 18:16:58 -0800819
820
Lee Thomason50f97b22012-02-11 16:33:40 -0800821// --------- XMLDeclaration ---------- //
822
823XMLDeclaration::XMLDeclaration( XMLDocument* doc ) : XMLNode( doc )
824{
825}
826
827
828XMLDeclaration::~XMLDeclaration()
829{
830 //printf( "~XMLDeclaration\n" );
831}
832
833
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800834char* XMLDeclaration::ParseDeep( char* p, StrPair* )
Lee Thomason50f97b22012-02-11 16:33:40 -0800835{
836 // Declaration parses as text.
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800837 const char* start = p;
838 p = value.ParseText( p, "?>", StrPair::NEEDS_NEWLINE_NORMALIZATION );
839 if ( p == 0 ) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -0700840 document->SetError( XML_ERROR_PARSING_DECLARATION, start, 0 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800841 }
842 return p;
Lee Thomason50f97b22012-02-11 16:33:40 -0800843}
844
845
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800846XMLNode* XMLDeclaration::ShallowClone( XMLDocument* doc ) const
847{
848 if ( !doc ) {
849 doc = document;
850 }
851 XMLDeclaration* dec = doc->NewDeclaration( Value() ); // fixme: this will always allocate memory. Intern?
852 return dec;
853}
854
855
856bool XMLDeclaration::ShallowEqual( const XMLNode* compare ) const
857{
858 return ( compare->ToDeclaration() && XMLUtil::StringEqual( compare->ToDeclaration()->Value(), Value() ));
859}
860
861
862
Lee Thomason50f97b22012-02-11 16:33:40 -0800863bool XMLDeclaration::Accept( XMLVisitor* visitor ) const
864{
865 return visitor->Visit( *this );
866}
867
868// --------- XMLUnknown ---------- //
869
870XMLUnknown::XMLUnknown( XMLDocument* doc ) : XMLNode( doc )
871{
872}
873
874
875XMLUnknown::~XMLUnknown()
876{
877}
878
879
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800880char* XMLUnknown::ParseDeep( char* p, StrPair* )
Lee Thomason50f97b22012-02-11 16:33:40 -0800881{
882 // Unknown parses as text.
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800883 const char* start = p;
884
885 p = value.ParseText( p, ">", StrPair::NEEDS_NEWLINE_NORMALIZATION );
886 if ( !p ) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -0700887 document->SetError( XML_ERROR_PARSING_UNKNOWN, start, 0 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800888 }
889 return p;
Lee Thomason50f97b22012-02-11 16:33:40 -0800890}
891
892
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800893XMLNode* XMLUnknown::ShallowClone( XMLDocument* doc ) const
894{
895 if ( !doc ) {
896 doc = document;
897 }
898 XMLUnknown* text = doc->NewUnknown( Value() ); // fixme: this will always allocate memory. Intern?
899 return text;
900}
901
902
903bool XMLUnknown::ShallowEqual( const XMLNode* compare ) const
904{
905 return ( compare->ToUnknown() && XMLUtil::StringEqual( compare->ToUnknown()->Value(), Value() ));
906}
907
908
Lee Thomason50f97b22012-02-11 16:33:40 -0800909bool XMLUnknown::Accept( XMLVisitor* visitor ) const
910{
911 return visitor->Visit( *this );
912}
913
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800914// --------- XMLAttribute ---------- //
Lee Thomason6f381b72012-03-02 12:59:39 -0800915char* XMLAttribute::ParseDeep( char* p, bool processEntities )
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800916{
Lee Thomason78a773d2012-07-02 10:10:19 -0700917 // Parse using the name rules: bug fix, was using ParseText before
918 p = name.ParseName( p );
Lee Thomason22aead12012-01-23 13:29:35 -0800919 if ( !p || !*p ) return 0;
920
Lee Thomason78a773d2012-07-02 10:10:19 -0700921 // Skip white space before =
922 p = XMLUtil::SkipWhiteSpace( p );
923 if ( !p || *p != '=' ) return 0;
924
925 ++p; // move up to opening quote
926 p = XMLUtil::SkipWhiteSpace( p );
927 if ( *p != '\"' && *p != '\'' ) return 0;
928
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800929 char endTag[2] = { *p, 0 };
Lee Thomason78a773d2012-07-02 10:10:19 -0700930 ++p; // move past opening quote
931
Lee Thomason6f381b72012-03-02 12:59:39 -0800932 p = value.ParseText( p, endTag, processEntities ? StrPair::ATTRIBUTE_VALUE : StrPair::ATTRIBUTE_VALUE_LEAVE_ENTITIES );
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800933 return p;
934}
935
936
U-Stream\Lee09a11c52012-02-17 08:31:16 -0800937void XMLAttribute::SetName( const char* n )
938{
939 name.SetStr( n );
940}
941
942
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800943int XMLAttribute::QueryIntValue( int* value ) const
Lee Thomason1ff38e02012-02-14 18:18:16 -0800944{
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800945 if ( TIXML_SSCANF( Value(), "%d", 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::QueryUnsignedValue( unsigned int* value ) const
Lee Thomason1ff38e02012-02-14 18:18:16 -0800952{
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800953 if ( TIXML_SSCANF( Value(), "%u", value ) == 1 )
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800954 return XML_NO_ERROR;
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -0700955 return XML_WRONG_ATTRIBUTE_TYPE;
Lee Thomason1ff38e02012-02-14 18:18:16 -0800956}
957
958
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800959int XMLAttribute::QueryBoolValue( bool* value ) const
Lee Thomason1ff38e02012-02-14 18:18:16 -0800960{
961 int ival = -1;
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800962 QueryIntValue( &ival );
Lee Thomason1ff38e02012-02-14 18:18:16 -0800963
964 if ( ival > 0 || XMLUtil::StringEqual( Value(), "true" ) ) {
965 *value = true;
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800966 return XML_NO_ERROR;
Lee Thomason1ff38e02012-02-14 18:18:16 -0800967 }
968 else if ( ival == 0 || XMLUtil::StringEqual( Value(), "false" ) ) {
969 *value = false;
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800970 return XML_NO_ERROR;
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800971 }
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::QueryDoubleValue( double* value ) const
Lee Thomason1ff38e02012-02-14 18:18:16 -0800977{
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800978 if ( TIXML_SSCANF( Value(), "%lf", 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
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800984int XMLAttribute::QueryFloatValue( float* value ) const
Lee Thomason1ff38e02012-02-14 18:18:16 -0800985{
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800986 if ( TIXML_SSCANF( Value(), "%f", value ) == 1 )
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800987 return XML_NO_ERROR;
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -0700988 return XML_WRONG_ATTRIBUTE_TYPE;
Lee Thomason1ff38e02012-02-14 18:18:16 -0800989}
990
991
992void XMLAttribute::SetAttribute( const char* v )
993{
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800994 value.SetStr( v );
Lee Thomason1ff38e02012-02-14 18:18:16 -0800995}
996
997
Lee Thomason1ff38e02012-02-14 18:18:16 -0800998void XMLAttribute::SetAttribute( int v )
999{
1000 char buf[BUF_SIZE];
Lee Thomason (grinliz)598c13e2012-04-06 21:18:23 -07001001 TIXML_SNPRINTF( buf, BUF_SIZE, "%d", v );
Lee Thomason1a1d4a72012-02-15 09:09:25 -08001002 value.SetStr( buf );
Lee Thomason1ff38e02012-02-14 18:18:16 -08001003}
Lee Thomason1a1d4a72012-02-15 09:09:25 -08001004
1005
1006void XMLAttribute::SetAttribute( unsigned v )
1007{
1008 char buf[BUF_SIZE];
Lee Thomason (grinliz)a4a36ba2012-04-06 21:24:29 -07001009 TIXML_SNPRINTF( buf, BUF_SIZE, "%u", v );
Lee Thomason1a1d4a72012-02-15 09:09:25 -08001010 value.SetStr( buf );
1011}
1012
1013
1014void XMLAttribute::SetAttribute( bool v )
1015{
1016 char buf[BUF_SIZE];
Lee Thomason (grinliz)a4a36ba2012-04-06 21:24:29 -07001017 TIXML_SNPRINTF( buf, BUF_SIZE, "%d", v ? 1 : 0 );
Lee Thomason1a1d4a72012-02-15 09:09:25 -08001018 value.SetStr( buf );
1019}
1020
1021void XMLAttribute::SetAttribute( double v )
1022{
1023 char buf[BUF_SIZE];
Lee Thomason (grinliz)a4a36ba2012-04-06 21:24:29 -07001024 TIXML_SNPRINTF( buf, BUF_SIZE, "%f", v );
Lee Thomason1a1d4a72012-02-15 09:09:25 -08001025 value.SetStr( buf );
1026}
1027
1028void XMLAttribute::SetAttribute( float v )
1029{
1030 char buf[BUF_SIZE];
Lee Thomason (grinliz)a4a36ba2012-04-06 21:24:29 -07001031 TIXML_SNPRINTF( buf, BUF_SIZE, "%f", v );
Lee Thomason1a1d4a72012-02-15 09:09:25 -08001032 value.SetStr( buf );
1033}
1034
Lee Thomasondadcdfa2012-01-18 17:55:48 -08001035
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001036// --------- XMLElement ---------- //
1037XMLElement::XMLElement( XMLDocument* doc ) : XMLNode( doc ),
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -08001038 closingType( 0 ),
U-Stream\Lee09a11c52012-02-17 08:31:16 -08001039 rootAttribute( 0 )
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001040{
1041}
1042
1043
1044XMLElement::~XMLElement()
1045{
U-Stream\Lee09a11c52012-02-17 08:31:16 -08001046 while( rootAttribute ) {
1047 XMLAttribute* next = rootAttribute->next;
1048 DELETE_ATTRIBUTE( rootAttribute );
1049 rootAttribute = next;
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001050 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001051}
1052
1053
Lee Thomason1a1d4a72012-02-15 09:09:25 -08001054XMLAttribute* XMLElement::FindAttribute( const char* name )
1055{
1056 XMLAttribute* a = 0;
1057 for( a=rootAttribute; a; a = a->next ) {
1058 if ( XMLUtil::StringEqual( a->Name(), name ) )
1059 return a;
1060 }
1061 return 0;
1062}
1063
1064
1065const XMLAttribute* XMLElement::FindAttribute( const char* name ) const
1066{
1067 XMLAttribute* a = 0;
1068 for( a=rootAttribute; a; a = a->next ) {
1069 if ( XMLUtil::StringEqual( a->Name(), name ) )
1070 return a;
1071 }
1072 return 0;
1073}
1074
1075
Lee Thomason8ba7f7d2012-03-24 13:04:04 -07001076const char* XMLElement::Attribute( const char* name, const char* value ) const
1077{
1078 const XMLAttribute* a = FindAttribute( name );
1079 if ( !a )
1080 return 0;
1081 if ( !value || XMLUtil::StringEqual( a->Value(), value ))
1082 return a->Value();
1083 return 0;
1084}
1085
1086
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001087const char* XMLElement::GetText() const
1088{
1089 if ( FirstChild() && FirstChild()->ToText() ) {
1090 return FirstChild()->ToText()->Value();
1091 }
1092 return 0;
1093}
1094
1095
Lee Thomason1a1d4a72012-02-15 09:09:25 -08001096XMLAttribute* XMLElement::FindOrCreateAttribute( const char* name )
1097{
Lee Thomason5e3803c2012-04-16 08:57:05 -07001098 XMLAttribute* last = 0;
1099 XMLAttribute* attrib = 0;
1100 for( attrib = rootAttribute;
1101 attrib;
1102 last = attrib, attrib = attrib->next )
1103 {
1104 if ( XMLUtil::StringEqual( attrib->Name(), name ) ) {
1105 break;
1106 }
1107 }
Lee Thomason1a1d4a72012-02-15 09:09:25 -08001108 if ( !attrib ) {
Lee Thomason (grinliz)9b093cc2012-02-25 21:30:18 -08001109 attrib = new (document->attributePool.Alloc() ) XMLAttribute();
Lee Thomason1a1d4a72012-02-15 09:09:25 -08001110 attrib->memPool = &document->attributePool;
Lee Thomason5e3803c2012-04-16 08:57:05 -07001111 if ( last ) {
1112 last->next = attrib;
1113 }
1114 else {
1115 rootAttribute = attrib;
1116 }
U-Stream\Lee09a11c52012-02-17 08:31:16 -08001117 attrib->SetName( name );
Lee Thomason1a1d4a72012-02-15 09:09:25 -08001118 }
1119 return attrib;
1120}
1121
1122
U-Stream\Leeae25a442012-02-17 17:48:16 -08001123void XMLElement::DeleteAttribute( const char* name )
1124{
1125 XMLAttribute* prev = 0;
1126 for( XMLAttribute* a=rootAttribute; a; a=a->next ) {
1127 if ( XMLUtil::StringEqual( name, a->Name() ) ) {
1128 if ( prev ) {
1129 prev->next = a->next;
1130 }
1131 else {
1132 rootAttribute = a->next;
1133 }
1134 DELETE_ATTRIBUTE( a );
1135 break;
1136 }
1137 prev = a;
1138 }
1139}
1140
1141
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -08001142char* XMLElement::ParseAttributes( char* p )
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001143{
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001144 const char* start = p;
Lee Thomason5e3803c2012-04-16 08:57:05 -07001145 XMLAttribute* prevAttribute = 0;
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001146
1147 // Read the attributes.
1148 while( p ) {
Lee Thomason56bdd022012-02-09 18:16:58 -08001149 p = XMLUtil::SkipWhiteSpace( p );
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001150 if ( !p || !(*p) ) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -07001151 document->SetError( XML_ERROR_PARSING_ELEMENT, start, Name() );
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001152 return 0;
1153 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001154
1155 // attribute.
Lee Thomason56bdd022012-02-09 18:16:58 -08001156 if ( XMLUtil::IsAlpha( *p ) ) {
Lee Thomason (grinliz)9b093cc2012-02-25 21:30:18 -08001157 XMLAttribute* attrib = new (document->attributePool.Alloc() ) XMLAttribute();
Lee Thomason43f59302012-02-06 18:18:11 -08001158 attrib->memPool = &document->attributePool;
Lee Thomasond1983222012-02-06 08:41:24 -08001159
Lee Thomason6f381b72012-03-02 12:59:39 -08001160 p = attrib->ParseDeep( p, document->ProcessEntities() );
Lee Thomasond6277762012-02-22 16:00:12 -08001161 if ( !p || Attribute( attrib->Name() ) ) {
Lee Thomason43f59302012-02-06 18:18:11 -08001162 DELETE_ATTRIBUTE( attrib );
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -07001163 document->SetError( XML_ERROR_PARSING_ATTRIBUTE, start, p );
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001164 return 0;
1165 }
Lee Thomason5e3803c2012-04-16 08:57:05 -07001166 // There is a minor bug here: if the attribute in the source xml
1167 // document is duplicated, it will not be detected and the
1168 // attribute will be doubly added. However, tracking the 'prevAttribute'
1169 // avoids re-scanning the attribute list. Preferring performance for
1170 // now, may reconsider in the future.
1171 if ( prevAttribute ) {
1172 prevAttribute->next = attrib;
Lee Thomason5e3803c2012-04-16 08:57:05 -07001173 }
1174 else {
1175 rootAttribute = attrib;
Lee Thomason5e3803c2012-04-16 08:57:05 -07001176 }
Lee Thomason97088852012-04-18 11:39:42 -07001177 prevAttribute = attrib;
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001178 }
Lee Thomasone4422302012-01-20 17:59:50 -08001179 // end of the tag
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001180 else if ( *p == '/' && *(p+1) == '>' ) {
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -08001181 closingType = CLOSED;
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001182 return p+2; // done; sealed element.
1183 }
Lee Thomasone4422302012-01-20 17:59:50 -08001184 // end of the tag
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001185 else if ( *p == '>' ) {
1186 ++p;
1187 break;
1188 }
Lee Thomasone4422302012-01-20 17:59:50 -08001189 else {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -07001190 document->SetError( XML_ERROR_PARSING_ELEMENT, start, p );
Lee Thomasone4422302012-01-20 17:59:50 -08001191 return 0;
1192 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001193 }
Lee Thomason67d61312012-01-24 16:01:51 -08001194 return p;
1195}
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001196
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001197
Lee Thomason67d61312012-01-24 16:01:51 -08001198//
1199// <ele></ele>
1200// <ele>foo<b>bar</b></ele>
1201//
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -08001202char* XMLElement::ParseDeep( char* p, StrPair* strPair )
Lee Thomason67d61312012-01-24 16:01:51 -08001203{
1204 // Read the element name.
Lee Thomason56bdd022012-02-09 18:16:58 -08001205 p = XMLUtil::SkipWhiteSpace( p );
Lee Thomason67d61312012-01-24 16:01:51 -08001206 if ( !p ) return 0;
Lee Thomason67d61312012-01-24 16:01:51 -08001207
1208 // The closing element is the </element> form. It is
1209 // parsed just like a regular element then deleted from
1210 // the DOM.
1211 if ( *p == '/' ) {
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -08001212 closingType = CLOSING;
Lee Thomason67d61312012-01-24 16:01:51 -08001213 ++p;
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001214 }
Lee Thomason67d61312012-01-24 16:01:51 -08001215
Lee Thomason56bdd022012-02-09 18:16:58 -08001216 p = value.ParseName( p );
Lee Thomasond1983222012-02-06 08:41:24 -08001217 if ( value.Empty() ) return 0;
Lee Thomason67d61312012-01-24 16:01:51 -08001218
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -08001219 p = ParseAttributes( p );
1220 if ( !p || !*p || closingType )
Lee Thomason67d61312012-01-24 16:01:51 -08001221 return p;
1222
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -08001223 p = XMLNode::ParseDeep( p, strPair );
Lee Thomason67d61312012-01-24 16:01:51 -08001224 return p;
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001225}
1226
1227
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001228
1229XMLNode* XMLElement::ShallowClone( XMLDocument* doc ) const
1230{
1231 if ( !doc ) {
1232 doc = document;
1233 }
1234 XMLElement* element = doc->NewElement( Value() ); // fixme: this will always allocate memory. Intern?
1235 for( const XMLAttribute* a=FirstAttribute(); a; a=a->Next() ) {
1236 element->SetAttribute( a->Name(), a->Value() ); // fixme: this will always allocate memory. Intern?
1237 }
1238 return element;
1239}
1240
1241
1242bool XMLElement::ShallowEqual( const XMLNode* compare ) const
1243{
1244 const XMLElement* other = compare->ToElement();
1245 if ( other && XMLUtil::StringEqual( other->Value(), Value() )) {
1246
1247 const XMLAttribute* a=FirstAttribute();
1248 const XMLAttribute* b=other->FirstAttribute();
1249
1250 while ( a && b ) {
1251 if ( !XMLUtil::StringEqual( a->Value(), b->Value() ) ) {
1252 return false;
1253 }
Lee Thomason (grinliz)390e9782012-07-01 21:22:53 -07001254 a = a->Next();
1255 b = b->Next();
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001256 }
1257 if ( a || b ) {
1258 // different count
1259 return false;
1260 }
1261 return true;
1262 }
1263 return false;
1264}
1265
1266
Lee Thomason751da522012-02-10 08:50:51 -08001267bool XMLElement::Accept( XMLVisitor* visitor ) const
1268{
1269 if ( visitor->VisitEnter( *this, rootAttribute ) )
1270 {
1271 for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() )
1272 {
1273 if ( !node->Accept( visitor ) )
1274 break;
1275 }
1276 }
1277 return visitor->VisitExit( *this );
Lee Thomason751da522012-02-10 08:50:51 -08001278}
Lee Thomason56bdd022012-02-09 18:16:58 -08001279
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001280
Lee Thomason3f57d272012-01-11 15:30:03 -08001281// --------- XMLDocument ----------- //
Lee Thomason6f381b72012-03-02 12:59:39 -08001282XMLDocument::XMLDocument( bool _processEntities ) :
Lee Thomason18d68bd2012-01-26 18:17:26 -08001283 XMLNode( 0 ),
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001284 writeBOM( false ),
Lee Thomason6f381b72012-03-02 12:59:39 -08001285 processEntities( _processEntities ),
1286 errorID( 0 ),
1287 errorStr1( 0 ),
1288 errorStr2( 0 ),
U-Lama\Lee560bd472011-12-28 19:42:49 -08001289 charBuffer( 0 )
1290{
Lee Thomason18d68bd2012-01-26 18:17:26 -08001291 document = this; // avoid warning about 'this' in initializer list
U-Lama\Lee560bd472011-12-28 19:42:49 -08001292}
U-Lama\Leee13c3e62011-12-28 14:36:55 -08001293
1294
Lee Thomason3f57d272012-01-11 15:30:03 -08001295XMLDocument::~XMLDocument()
1296{
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001297 DeleteChildren();
Lee Thomason18d68bd2012-01-26 18:17:26 -08001298 delete [] charBuffer;
Lee Thomasond1983222012-02-06 08:41:24 -08001299
Lee Thomasonec5a7b42012-02-13 18:16:52 -08001300#if 0
Lee Thomason455c9d42012-02-06 09:14:14 -08001301 textPool.Trace( "text" );
1302 elementPool.Trace( "element" );
1303 commentPool.Trace( "comment" );
1304 attributePool.Trace( "attribute" );
Lee Thomasone9ecdab2012-02-13 18:11:20 -08001305#endif
1306
Lee Thomason455c9d42012-02-06 09:14:14 -08001307 TIXMLASSERT( textPool.CurrentAllocs() == 0 );
1308 TIXMLASSERT( elementPool.CurrentAllocs() == 0 );
1309 TIXMLASSERT( commentPool.CurrentAllocs() == 0 );
1310 TIXMLASSERT( attributePool.CurrentAllocs() == 0 );
Lee Thomason3f57d272012-01-11 15:30:03 -08001311}
1312
1313
Lee Thomason18d68bd2012-01-26 18:17:26 -08001314void XMLDocument::InitDocument()
1315{
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001316 errorID = XML_NO_ERROR;
Lee Thomason18d68bd2012-01-26 18:17:26 -08001317 errorStr1 = 0;
1318 errorStr2 = 0;
1319
1320 delete [] charBuffer;
1321 charBuffer = 0;
1322
1323}
1324
Lee Thomason3f57d272012-01-11 15:30:03 -08001325
Lee Thomason2c85a712012-01-31 08:24:24 -08001326XMLElement* XMLDocument::NewElement( const char* name )
1327{
Lee Thomasond1983222012-02-06 08:41:24 -08001328 XMLElement* ele = new (elementPool.Alloc()) XMLElement( this );
1329 ele->memPool = &elementPool;
Lee Thomason2c85a712012-01-31 08:24:24 -08001330 ele->SetName( name );
1331 return ele;
1332}
1333
1334
Lee Thomason1ff38e02012-02-14 18:18:16 -08001335XMLComment* XMLDocument::NewComment( const char* str )
1336{
1337 XMLComment* comment = new (commentPool.Alloc()) XMLComment( this );
1338 comment->memPool = &commentPool;
1339 comment->SetValue( str );
1340 return comment;
1341}
1342
1343
1344XMLText* XMLDocument::NewText( const char* str )
1345{
U-Stream\Lee09a11c52012-02-17 08:31:16 -08001346 XMLText* text = new (textPool.Alloc()) XMLText( this );
1347 text->memPool = &textPool;
1348 text->SetValue( str );
1349 return text;
Lee Thomason1ff38e02012-02-14 18:18:16 -08001350}
1351
1352
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001353XMLDeclaration* XMLDocument::NewDeclaration( const char* str )
1354{
1355 XMLDeclaration* dec = new (commentPool.Alloc()) XMLDeclaration( this );
1356 dec->memPool = &commentPool;
Lee Thomasonf68c4382012-04-28 14:37:11 -07001357 dec->SetValue( str ? str : "xml version=\"1.0\" encoding=\"UTF-8\"" );
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001358 return dec;
1359}
1360
1361
1362XMLUnknown* XMLDocument::NewUnknown( const char* str )
1363{
1364 XMLUnknown* unk = new (commentPool.Alloc()) XMLUnknown( this );
1365 unk->memPool = &commentPool;
1366 unk->SetValue( str );
1367 return unk;
1368}
1369
1370
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001371int XMLDocument::LoadFile( const char* filename )
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001372{
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001373 DeleteChildren();
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001374 InitDocument();
1375
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001376#if defined(_MSC_VER)
Lee Thomason (grinliz)5ce4d972012-02-26 21:14:23 -08001377#pragma warning ( push )
1378#pragma warning ( disable : 4996 ) // Fail to see a compelling reason why this should be deprecated.
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001379#endif
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001380 FILE* fp = fopen( filename, "rb" );
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001381#if defined(_MSC_VER)
Lee Thomason (grinliz)5ce4d972012-02-26 21:14:23 -08001382#pragma warning ( pop )
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001383#endif
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001384 if ( !fp ) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -07001385 SetError( XML_ERROR_FILE_NOT_FOUND, filename, 0 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001386 return errorID;
1387 }
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001388 LoadFile( fp );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001389 fclose( fp );
1390 return errorID;
1391}
1392
1393
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001394int XMLDocument::LoadFile( FILE* fp )
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001395{
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001396 DeleteChildren();
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001397 InitDocument();
1398
1399 fseek( fp, 0, SEEK_END );
1400 unsigned size = ftell( fp );
1401 fseek( fp, 0, SEEK_SET );
1402
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001403 if ( size == 0 ) {
1404 return errorID;
1405 }
1406
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001407 charBuffer = new char[size+1];
Lee Thomasona3efec02012-06-15 14:30:44 -07001408 size_t read = fread( charBuffer, 1, size, fp );
1409 if ( read != size ) {
1410 SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
1411 return errorID;
1412 }
1413
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001414 charBuffer[size] = 0;
1415
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001416 const char* p = charBuffer;
1417 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 Thomason (grinliz)7468f112012-02-24 08:56:50 -08001424 ParseDeep( charBuffer + (p-charBuffer), 0 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001425 return errorID;
1426}
1427
1428
Ken Miller81da1fb2012-04-09 23:32:26 -05001429int XMLDocument::SaveFile( const char* filename )
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001430{
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001431#if defined(_MSC_VER)
Lee Thomason (grinliz)5ce4d972012-02-26 21:14:23 -08001432#pragma warning ( push )
1433#pragma warning ( disable : 4996 ) // Fail to see a compelling reason why this should be deprecated.
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001434#endif
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001435 FILE* fp = fopen( filename, "w" );
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001436#if defined(_MSC_VER)
Lee Thomason (grinliz)5ce4d972012-02-26 21:14:23 -08001437#pragma warning ( pop )
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001438#endif
Ken Miller81da1fb2012-04-09 23:32:26 -05001439 if ( !fp ) {
Lee Thomason7f7b1622012-03-24 12:49:03 -07001440 SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, filename, 0 );
Ken Miller81da1fb2012-04-09 23:32:26 -05001441 return errorID;
Lee Thomason7f7b1622012-03-24 12:49:03 -07001442 }
Ken Miller81da1fb2012-04-09 23:32:26 -05001443 SaveFile(fp);
1444 fclose( fp );
1445 return errorID;
1446}
1447
1448
1449int XMLDocument::SaveFile( FILE* fp )
1450{
1451 XMLPrinter stream( fp );
1452 Print( &stream );
1453 return errorID;
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001454}
1455
Lee Thomason1ff38e02012-02-14 18:18:16 -08001456
Lee Thomason7c913cd2012-01-26 18:32:34 -08001457int XMLDocument::Parse( const char* p )
Lee Thomason3f57d272012-01-11 15:30:03 -08001458{
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001459 DeleteChildren();
Lee Thomason18d68bd2012-01-26 18:17:26 -08001460 InitDocument();
1461
1462 if ( !p || !*p ) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -07001463 SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
Lee Thomasond6277762012-02-22 16:00:12 -08001464 return errorID;
Lee Thomason18d68bd2012-01-26 18:17:26 -08001465 }
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001466 p = XMLUtil::SkipWhiteSpace( p );
1467 p = XMLUtil::ReadBOM( p, &writeBOM );
1468 if ( !p || !*p ) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -07001469 SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
Lee Thomasond6277762012-02-22 16:00:12 -08001470 return errorID;
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001471 }
1472
Lee Thomason18d68bd2012-01-26 18:17:26 -08001473 size_t len = strlen( p );
1474 charBuffer = new char[ len+1 ];
1475 memcpy( charBuffer, p, len+1 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001476
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001477
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -08001478 ParseDeep( charBuffer, 0 );
Lee Thomason7c913cd2012-01-26 18:32:34 -08001479 return errorID;
Lee Thomason3f57d272012-01-11 15:30:03 -08001480}
1481
1482
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001483void XMLDocument::Print( XMLPrinter* streamer )
Lee Thomason3f57d272012-01-11 15:30:03 -08001484{
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001485 XMLPrinter stdStreamer( stdout );
Lee Thomason5cae8972012-01-24 18:03:07 -08001486 if ( !streamer )
1487 streamer = &stdStreamer;
Lee Thomason751da522012-02-10 08:50:51 -08001488 Accept( streamer );
Lee Thomason3f57d272012-01-11 15:30:03 -08001489}
1490
1491
Lee Thomason67d61312012-01-24 16:01:51 -08001492void XMLDocument::SetError( int error, const char* str1, const char* str2 )
1493{
Lee Thomason18d68bd2012-01-26 18:17:26 -08001494 errorID = error;
Lee Thomason18d68bd2012-01-26 18:17:26 -08001495 errorStr1 = str1;
1496 errorStr2 = str2;
Lee Thomason67d61312012-01-24 16:01:51 -08001497}
1498
Lee Thomason5cae8972012-01-24 18:03:07 -08001499
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001500void XMLDocument::PrintError() const
1501{
1502 if ( errorID ) {
Lee Thomason (grinliz)5ce4d972012-02-26 21:14:23 -08001503 static const int LEN = 20;
1504 char buf1[LEN] = { 0 };
1505 char buf2[LEN] = { 0 };
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001506
1507 if ( errorStr1 ) {
Lee Thomason (grinliz)5ce4d972012-02-26 21:14:23 -08001508 TIXML_SNPRINTF( buf1, LEN, "%s", errorStr1 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001509 }
1510 if ( errorStr2 ) {
Lee Thomason (grinliz)5ce4d972012-02-26 21:14:23 -08001511 TIXML_SNPRINTF( buf2, LEN, "%s", errorStr2 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001512 }
1513
1514 printf( "XMLDocument error id=%d str1=%s str2=%s\n",
1515 errorID, buf1, buf2 );
1516 }
1517}
1518
1519
sniperbat25900882012-05-28 17:22:07 +08001520XMLPrinter::XMLPrinter( FILE* file, bool compact ) :
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001521 elementJustOpened( false ),
1522 firstElement( true ),
1523 fp( file ),
1524 depth( 0 ),
Lee Thomason6f381b72012-03-02 12:59:39 -08001525 textDepth( -1 ),
sniperbat25900882012-05-28 17:22:07 +08001526 processEntities( true ),
1527 compactMode( compact )
Lee Thomason5cae8972012-01-24 18:03:07 -08001528{
Lee Thomason857b8682012-01-25 17:50:25 -08001529 for( int i=0; i<ENTITY_RANGE; ++i ) {
1530 entityFlag[i] = false;
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001531 restrictedEntityFlag[i] = false;
Lee Thomason857b8682012-01-25 17:50:25 -08001532 }
1533 for( int i=0; i<NUM_ENTITIES; ++i ) {
1534 TIXMLASSERT( entities[i].value < ENTITY_RANGE );
1535 if ( entities[i].value < ENTITY_RANGE ) {
Lee Thomason (grinliz)9b093cc2012-02-25 21:30:18 -08001536 entityFlag[ (int)entities[i].value ] = true;
Lee Thomason857b8682012-01-25 17:50:25 -08001537 }
1538 }
Lee Thomason (grinliz)9b093cc2012-02-25 21:30:18 -08001539 restrictedEntityFlag[(int)'&'] = true;
1540 restrictedEntityFlag[(int)'<'] = true;
1541 restrictedEntityFlag[(int)'>'] = true; // not required, but consistency is nice
U-Stream\Leeae25a442012-02-17 17:48:16 -08001542 buffer.Push( 0 );
1543}
1544
1545
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001546void XMLPrinter::Print( const char* format, ... )
U-Stream\Leeae25a442012-02-17 17:48:16 -08001547{
1548 va_list va;
1549 va_start( va, format );
1550
1551 if ( fp ) {
1552 vfprintf( fp, format, va );
1553 }
1554 else {
1555 // This seems brutally complex. Haven't figured out a better
1556 // way on windows.
1557 #ifdef _MSC_VER
1558 int len = -1;
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001559 int expand = 1000;
U-Stream\Leeae25a442012-02-17 17:48:16 -08001560 while ( len < 0 ) {
Lee Thomason (grinliz)598c13e2012-04-06 21:18:23 -07001561 len = vsnprintf_s( accumulator.Mem(), accumulator.Capacity(), _TRUNCATE, format, va );
U-Stream\Leeae25a442012-02-17 17:48:16 -08001562 if ( len < 0 ) {
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001563 expand *= 3/2;
Lee Thomason (grinliz)598c13e2012-04-06 21:18:23 -07001564 accumulator.PushArr( expand );
U-Stream\Leeae25a442012-02-17 17:48:16 -08001565 }
1566 }
1567 char* p = buffer.PushArr( len ) - 1;
1568 memcpy( p, accumulator.Mem(), len+1 );
1569 #else
1570 int len = vsnprintf( 0, 0, format, va );
Lee Thomason4de93472012-03-13 17:33:35 -07001571 // Close out and re-start the va-args
1572 va_end( va );
1573 va_start( va, format );
U-Stream\Leeae25a442012-02-17 17:48:16 -08001574 char* p = buffer.PushArr( len ) - 1;
Lee Thomason (grinliz)9b093cc2012-02-25 21:30:18 -08001575 vsnprintf( p, len+1, format, va );
U-Stream\Leeae25a442012-02-17 17:48:16 -08001576 #endif
1577 }
1578 va_end( va );
Lee Thomason5cae8972012-01-24 18:03:07 -08001579}
1580
1581
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001582void XMLPrinter::PrintSpace( int depth )
Lee Thomason5cae8972012-01-24 18:03:07 -08001583{
1584 for( int i=0; i<depth; ++i ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001585 Print( " " );
Lee Thomason5cae8972012-01-24 18:03:07 -08001586 }
1587}
1588
1589
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001590void XMLPrinter::PrintString( const char* p, bool restricted )
Lee Thomason857b8682012-01-25 17:50:25 -08001591{
Lee Thomason951d8832012-01-26 08:47:06 -08001592 // Look for runs of bytes between entities to print.
1593 const char* q = p;
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001594 const bool* flag = restricted ? restrictedEntityFlag : entityFlag;
Lee Thomason857b8682012-01-25 17:50:25 -08001595
Lee Thomason6f381b72012-03-02 12:59:39 -08001596 if ( processEntities ) {
1597 while ( *q ) {
1598 // Remember, char is sometimes signed. (How many times has that bitten me?)
1599 if ( *q > 0 && *q < ENTITY_RANGE ) {
1600 // Check for entities. If one is found, flush
1601 // the stream up until the entity, write the
1602 // entity, and keep looking.
Lee Thomason (grinliz)8a0975d2012-03-31 20:09:20 -07001603 if ( flag[(unsigned)(*q)] ) {
Lee Thomason6f381b72012-03-02 12:59:39 -08001604 while ( p < q ) {
1605 Print( "%c", *p );
1606 ++p;
1607 }
1608 for( int i=0; i<NUM_ENTITIES; ++i ) {
1609 if ( entities[i].value == *q ) {
1610 Print( "&%s;", entities[i].pattern );
1611 break;
1612 }
1613 }
Lee Thomason951d8832012-01-26 08:47:06 -08001614 ++p;
1615 }
Lee Thomason951d8832012-01-26 08:47:06 -08001616 }
Lee Thomason6f381b72012-03-02 12:59:39 -08001617 ++q;
Lee Thomason951d8832012-01-26 08:47:06 -08001618 }
Lee Thomason951d8832012-01-26 08:47:06 -08001619 }
1620 // Flush the remaining string. This will be the entire
1621 // string if an entity wasn't found.
Lee Thomason6f381b72012-03-02 12:59:39 -08001622 if ( !processEntities || (q-p > 0) ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001623 Print( "%s", p );
Lee Thomason951d8832012-01-26 08:47:06 -08001624 }
Lee Thomason857b8682012-01-25 17:50:25 -08001625}
1626
U-Stream\Leeae25a442012-02-17 17:48:16 -08001627
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001628void XMLPrinter::PushHeader( bool writeBOM, bool writeDec )
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001629{
1630 static const unsigned char bom[] = { TIXML_UTF_LEAD_0, TIXML_UTF_LEAD_1, TIXML_UTF_LEAD_2, 0 };
1631 if ( writeBOM ) {
1632 Print( "%s", bom );
1633 }
1634 if ( writeDec ) {
1635 PushDeclaration( "xml version=\"1.0\"" );
1636 }
1637}
1638
1639
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001640void XMLPrinter::OpenElement( const char* name )
Lee Thomason5cae8972012-01-24 18:03:07 -08001641{
1642 if ( elementJustOpened ) {
1643 SealElement();
1644 }
Lee Thomason56bdd022012-02-09 18:16:58 -08001645 stack.Push( name );
1646
sniperbat25900882012-05-28 17:22:07 +08001647 if ( textDepth < 0 && !firstElement && !compactMode ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001648 Print( "\n" );
Lee Thomason24767b02012-01-25 17:16:23 -08001649 PrintSpace( depth );
1650 }
Lee Thomason5cae8972012-01-24 18:03:07 -08001651
U-Stream\Leeae25a442012-02-17 17:48:16 -08001652 Print( "<%s", name );
Lee Thomason5cae8972012-01-24 18:03:07 -08001653 elementJustOpened = true;
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001654 firstElement = false;
Lee Thomason5cae8972012-01-24 18:03:07 -08001655 ++depth;
1656}
1657
1658
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001659void XMLPrinter::PushAttribute( const char* name, const char* value )
Lee Thomason5cae8972012-01-24 18:03:07 -08001660{
1661 TIXMLASSERT( elementJustOpened );
U-Stream\Leeae25a442012-02-17 17:48:16 -08001662 Print( " %s=\"", name );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001663 PrintString( value, false );
U-Stream\Leeae25a442012-02-17 17:48:16 -08001664 Print( "\"" );
Lee Thomason5cae8972012-01-24 18:03:07 -08001665}
1666
1667
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001668void XMLPrinter::PushAttribute( const char* name, int v )
1669{
1670 char buf[BUF_SIZE];
Lee Thomason (grinliz)a4a36ba2012-04-06 21:24:29 -07001671 TIXML_SNPRINTF( buf, BUF_SIZE, "%d", v );
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001672 PushAttribute( name, buf );
1673}
1674
1675
1676void XMLPrinter::PushAttribute( const char* name, unsigned v )
1677{
1678 char buf[BUF_SIZE];
Lee Thomason (grinliz)a4a36ba2012-04-06 21:24:29 -07001679 TIXML_SNPRINTF( buf, BUF_SIZE, "%u", v );
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001680 PushAttribute( name, buf );
1681}
1682
1683
1684void XMLPrinter::PushAttribute( const char* name, bool v )
1685{
1686 char buf[BUF_SIZE];
Lee Thomason (grinliz)a4a36ba2012-04-06 21:24:29 -07001687 TIXML_SNPRINTF( buf, BUF_SIZE, "%d", v ? 1 : 0 );
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001688 PushAttribute( name, buf );
1689}
1690
1691
1692void XMLPrinter::PushAttribute( const char* name, double v )
1693{
1694 char buf[BUF_SIZE];
Lee Thomason (grinliz)a4a36ba2012-04-06 21:24:29 -07001695 TIXML_SNPRINTF( buf, BUF_SIZE, "%f", v );
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001696 PushAttribute( name, buf );
1697}
1698
1699
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001700void XMLPrinter::CloseElement()
Lee Thomason5cae8972012-01-24 18:03:07 -08001701{
1702 --depth;
1703 const char* name = stack.Pop();
Lee Thomason5cae8972012-01-24 18:03:07 -08001704
1705 if ( elementJustOpened ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001706 Print( "/>" );
Lee Thomason5cae8972012-01-24 18:03:07 -08001707 }
1708 else {
sniperbat25900882012-05-28 17:22:07 +08001709 if ( textDepth < 0 && !compactMode) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001710 Print( "\n" );
Lee Thomason24767b02012-01-25 17:16:23 -08001711 PrintSpace( depth );
1712 }
U-Stream\Leeae25a442012-02-17 17:48:16 -08001713 Print( "</%s>", name );
Lee Thomason5cae8972012-01-24 18:03:07 -08001714 }
Lee Thomason56bdd022012-02-09 18:16:58 -08001715
1716 if ( textDepth == depth )
1717 textDepth = -1;
sniperbat25900882012-05-28 17:22:07 +08001718 if ( depth == 0 && !compactMode)
U-Stream\Leeae25a442012-02-17 17:48:16 -08001719 Print( "\n" );
Lee Thomason5cae8972012-01-24 18:03:07 -08001720 elementJustOpened = false;
1721}
1722
1723
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001724void XMLPrinter::SealElement()
Lee Thomason5cae8972012-01-24 18:03:07 -08001725{
1726 elementJustOpened = false;
U-Stream\Leeae25a442012-02-17 17:48:16 -08001727 Print( ">" );
Lee Thomason5cae8972012-01-24 18:03:07 -08001728}
1729
1730
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001731void XMLPrinter::PushText( const char* text, bool cdata )
Lee Thomason5cae8972012-01-24 18:03:07 -08001732{
Lee Thomason56bdd022012-02-09 18:16:58 -08001733 textDepth = depth-1;
1734
Lee Thomason5cae8972012-01-24 18:03:07 -08001735 if ( elementJustOpened ) {
1736 SealElement();
1737 }
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001738 if ( cdata ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001739 Print( "<![CDATA[" );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001740 Print( "%s", text );
U-Stream\Leeae25a442012-02-17 17:48:16 -08001741 Print( "]]>" );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001742 }
1743 else {
1744 PrintString( text, true );
1745 }
Lee Thomason5cae8972012-01-24 18:03:07 -08001746}
1747
1748
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001749void XMLPrinter::PushComment( const char* comment )
Lee Thomason5cae8972012-01-24 18:03:07 -08001750{
1751 if ( elementJustOpened ) {
1752 SealElement();
1753 }
sniperbat25900882012-05-28 17:22:07 +08001754 if ( textDepth < 0 && !firstElement && !compactMode) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001755 Print( "\n" );
U-Stream\Lee09a11c52012-02-17 08:31:16 -08001756 PrintSpace( depth );
1757 }
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001758 firstElement = false;
U-Stream\Leeae25a442012-02-17 17:48:16 -08001759 Print( "<!--%s-->", comment );
Lee Thomason5cae8972012-01-24 18:03:07 -08001760}
Lee Thomason751da522012-02-10 08:50:51 -08001761
1762
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001763void XMLPrinter::PushDeclaration( const char* value )
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001764{
1765 if ( elementJustOpened ) {
1766 SealElement();
1767 }
sniperbat25900882012-05-28 17:22:07 +08001768 if ( textDepth < 0 && !firstElement && !compactMode) {
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001769 Print( "\n" );
1770 PrintSpace( depth );
1771 }
1772 firstElement = false;
1773 Print( "<?%s?>", value );
1774}
1775
1776
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001777void XMLPrinter::PushUnknown( const char* value )
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001778{
1779 if ( elementJustOpened ) {
1780 SealElement();
1781 }
sniperbat25900882012-05-28 17:22:07 +08001782 if ( textDepth < 0 && !firstElement && !compactMode) {
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001783 Print( "\n" );
1784 PrintSpace( depth );
1785 }
1786 firstElement = false;
1787 Print( "<!%s>", value );
1788}
1789
1790
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001791bool XMLPrinter::VisitEnter( const XMLDocument& doc )
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001792{
Lee Thomason6f381b72012-03-02 12:59:39 -08001793 processEntities = doc.ProcessEntities();
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001794 if ( doc.HasBOM() ) {
1795 PushHeader( true, false );
1796 }
1797 return true;
1798}
1799
1800
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001801bool XMLPrinter::VisitEnter( const XMLElement& element, const XMLAttribute* attribute )
Lee Thomason751da522012-02-10 08:50:51 -08001802{
1803 OpenElement( element.Name() );
1804 while ( attribute ) {
1805 PushAttribute( attribute->Name(), attribute->Value() );
1806 attribute = attribute->Next();
1807 }
1808 return true;
1809}
1810
1811
Lee Thomason (grinliz)9b093cc2012-02-25 21:30:18 -08001812bool XMLPrinter::VisitExit( const XMLElement& )
Lee Thomason751da522012-02-10 08:50:51 -08001813{
1814 CloseElement();
1815 return true;
1816}
1817
1818
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001819bool XMLPrinter::Visit( const XMLText& text )
Lee Thomason751da522012-02-10 08:50:51 -08001820{
Lee Thomasond6277762012-02-22 16:00:12 -08001821 PushText( text.Value(), text.CData() );
Lee Thomason751da522012-02-10 08:50:51 -08001822 return true;
1823}
1824
1825
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001826bool XMLPrinter::Visit( const XMLComment& comment )
Lee Thomason751da522012-02-10 08:50:51 -08001827{
1828 PushComment( comment.Value() );
1829 return true;
1830}
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001831
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001832bool XMLPrinter::Visit( const XMLDeclaration& declaration )
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001833{
1834 PushDeclaration( declaration.Value() );
1835 return true;
1836}
1837
1838
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001839bool XMLPrinter::Visit( const XMLUnknown& unknown )
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001840{
1841 PushUnknown( unknown.Value() );
1842 return true;
1843}