blob: fbeca4e813da0423d3e7f227a438d650db3bcbd1 [file] [log] [blame]
Lee Thomason (grinliz)28129862012-02-25 21:11:20 -08001/*
2Original code by Lee Thomason (www.grinninglizard.com)
3
4This software is provided 'as-is', without any express or implied
5warranty. In no event will the authors be held liable for any
6damages arising from the use of this software.
7
8Permission is granted to anyone to use this software for any
9purpose, including commercial applications, and to alter it and
10redistribute it freely, subject to the following restrictions:
11
121. The origin of this software must not be misrepresented; you must
13not claim that you wrote the original software. If you use this
14software in a product, an acknowledgment in the product documentation
15would be appreciated but is not required.
16
172. Altered source versions must be plainly marked as such, and
18must not be misrepresented as being the original software.
19
203. This notice may not be removed or altered from any source
21distribution.
22*/
Lee Thomason (grinliz)9c38d132012-02-24 21:50:50 -080023
U-Lama\Lee560bd472011-12-28 19:42:49 -080024#include "tinyxml2.h"
25
Lee Thomason5ce89412012-03-20 13:23:44 -070026#if 1
Lee Thomason5ce89412012-03-20 13:23:44 -070027 #include <cstdio>
28 #include <cstdlib>
29 #include <new>
30#else
31 #include <string.h>
32 #include <stdlib.h>
33 #include <stdio.h>
34 #include <ctype.h>
35 #include <new>
36 #include <stdarg.h>
37#endif
U-Lama\Lee560bd472011-12-28 19:42:49 -080038
39using namespace tinyxml2;
40
Lee Thomasone4422302012-01-20 17:59:50 -080041static const char LINE_FEED = (char)0x0a; // all line endings are normalized to LF
Lee Thomasonfde6a752012-01-14 18:08:12 -080042static const char LF = LINE_FEED;
43static const char CARRIAGE_RETURN = (char)0x0d; // CR gets filtered out
44static const char CR = CARRIAGE_RETURN;
Lee Thomasone4422302012-01-20 17:59:50 -080045static const char SINGLE_QUOTE = '\'';
46static const char DOUBLE_QUOTE = '\"';
Lee Thomasonfde6a752012-01-14 18:08:12 -080047
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -080048// Bunch of unicode info at:
49// http://www.unicode.org/faq/utf_bom.html
50// ef bb bf (Microsoft "lead bytes") - designates UTF-8
51
52static const unsigned char TIXML_UTF_LEAD_0 = 0xefU;
53static const unsigned char TIXML_UTF_LEAD_1 = 0xbbU;
54static const unsigned char TIXML_UTF_LEAD_2 = 0xbfU;
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -080055
56
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -080057#define DELETE_NODE( node ) { \
58 if ( node ) { \
59 MemPool* pool = node->memPool; \
60 node->~XMLNode(); \
61 pool->Free( node ); \
62 } \
63}
64#define DELETE_ATTRIBUTE( attrib ) { \
65 if ( attrib ) { \
66 MemPool* pool = attrib->memPool; \
67 attrib->~XMLAttribute(); \
68 pool->Free( attrib ); \
69 } \
70}
Lee Thomason43f59302012-02-06 18:18:11 -080071
Lee Thomason8ee79892012-01-25 17:44:30 -080072struct Entity {
73 const char* pattern;
74 int length;
75 char value;
76};
77
78static const int NUM_ENTITIES = 5;
79static const Entity entities[NUM_ENTITIES] =
80{
Lee Thomason18d68bd2012-01-26 18:17:26 -080081 { "quot", 4, DOUBLE_QUOTE },
Lee Thomason8ee79892012-01-25 17:44:30 -080082 { "amp", 3, '&' },
Lee Thomason18d68bd2012-01-26 18:17:26 -080083 { "apos", 4, SINGLE_QUOTE },
Lee Thomason8ee79892012-01-25 17:44:30 -080084 { "lt", 2, '<' },
85 { "gt", 2, '>' }
86};
87
Lee Thomasonfde6a752012-01-14 18:08:12 -080088
Lee Thomason1a1d4a72012-02-15 09:09:25 -080089StrPair::~StrPair()
90{
91 Reset();
92}
93
94
95void StrPair::Reset()
96{
97 if ( flags & NEEDS_DELETE ) {
98 delete [] start;
99 }
100 flags = 0;
101 start = 0;
102 end = 0;
103}
104
105
106void StrPair::SetStr( const char* str, int flags )
107{
108 Reset();
109 size_t len = strlen( str );
110 start = new char[ len+1 ];
U-Stream\Lee09a11c52012-02-17 08:31:16 -0800111 memcpy( start, str, len+1 );
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800112 end = start + len;
113 this->flags = flags | NEEDS_DELETE;
114}
115
116
117char* StrPair::ParseText( char* p, const char* endTag, int strFlags )
118{
119 TIXMLASSERT( endTag && *endTag );
120
121 char* start = p; // fixme: hides a member
122 char endChar = *endTag;
123 int length = strlen( endTag );
124
125 // Inner loop of text parsing.
126 while ( *p ) {
127 if ( *p == endChar && strncmp( p, endTag, length ) == 0 ) {
128 Set( start, p, strFlags );
129 return p + length;
130 }
131 ++p;
132 }
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800133 return 0;
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800134}
135
136
137char* StrPair::ParseName( char* p )
138{
139 char* start = p;
140
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800141 if ( !start || !(*start) ) {
142 return 0;
143 }
144
145 if ( !XMLUtil::IsAlpha( *p ) ) {
146 return 0;
147 }
148
149 while( *p && (
150 XMLUtil::IsAlphaNum( (unsigned char) *p )
151 || *p == '_'
152 || *p == '-'
153 || *p == '.'
154 || *p == ':' ))
155 {
156 ++p;
157 }
158
159 if ( p > start ) {
160 Set( start, p, 0 );
161 return p;
162 }
163 return 0;
164}
165
166
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800167
Lee Thomasone4422302012-01-20 17:59:50 -0800168const char* StrPair::GetStr()
169{
170 if ( flags & NEEDS_FLUSH ) {
171 *end = 0;
Lee Thomason8ee79892012-01-25 17:44:30 -0800172 flags ^= NEEDS_FLUSH;
Lee Thomasone4422302012-01-20 17:59:50 -0800173
Lee Thomason8ee79892012-01-25 17:44:30 -0800174 if ( flags ) {
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800175 char* p = start; // the read pointer
176 char* q = start; // the write pointer
Lee Thomasone4422302012-01-20 17:59:50 -0800177
178 while( p < end ) {
Lee Thomason8ee79892012-01-25 17:44:30 -0800179 if ( (flags & NEEDS_NEWLINE_NORMALIZATION) && *p == CR ) {
Lee Thomasone4422302012-01-20 17:59:50 -0800180 // CR-LF pair becomes LF
181 // CR alone becomes LF
182 // LF-CR becomes LF
183 if ( *(p+1) == LF ) {
184 p += 2;
185 }
186 else {
187 ++p;
188 }
Lee Thomasone9ecdab2012-02-13 18:11:20 -0800189 *q++ = LF;
Lee Thomasone4422302012-01-20 17:59:50 -0800190 }
Lee Thomason8ee79892012-01-25 17:44:30 -0800191 else if ( (flags & NEEDS_NEWLINE_NORMALIZATION) && *p == LF ) {
Lee Thomasone4422302012-01-20 17:59:50 -0800192 if ( *(p+1) == CR ) {
193 p += 2;
194 }
195 else {
196 ++p;
197 }
Lee Thomasone9ecdab2012-02-13 18:11:20 -0800198 *q++ = LF;
Lee Thomasone4422302012-01-20 17:59:50 -0800199 }
Lee Thomason8ee79892012-01-25 17:44:30 -0800200 else if ( (flags & NEEDS_ENTITY_PROCESSING) && *p == '&' ) {
201 int i=0;
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800202
203 // Entities handled by tinyXML2:
204 // - special entities in the entity table [in/out]
205 // - numeric character reference [in]
206 // &#20013; or &#x4e2d;
207
208 if ( *(p+1) == '#' ) {
209 char buf[10] = { 0 };
210 int len;
211 p = const_cast<char*>( XMLUtil::GetCharacterRef( p, buf, &len ) );
212 for( int i=0; i<len; ++i ) {
213 *q++ = buf[i];
Lee Thomason8ee79892012-01-25 17:44:30 -0800214 }
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800215 TIXMLASSERT( q <= p );
Lee Thomason8ee79892012-01-25 17:44:30 -0800216 }
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800217 else {
218 for( i=0; i<NUM_ENTITIES; ++i ) {
219 if ( strncmp( p+1, entities[i].pattern, entities[i].length ) == 0
220 && *(p+entities[i].length+1) == ';' )
221 {
222 // Found an entity convert;
223 *q = entities[i].value;
224 ++q;
225 p += entities[i].length + 2;
226 break;
227 }
228 }
229 if ( i == NUM_ENTITIES ) {
230 // fixme: treat as error?
231 ++p;
232 ++q;
233 }
Lee Thomason8ee79892012-01-25 17:44:30 -0800234 }
235 }
Lee Thomasone4422302012-01-20 17:59:50 -0800236 else {
237 *q = *p;
238 ++p;
Lee Thomasonec975ce2012-01-23 11:42:06 -0800239 ++q;
Lee Thomasone4422302012-01-20 17:59:50 -0800240 }
241 }
Lee Thomason8ee79892012-01-25 17:44:30 -0800242 *q = 0;
Lee Thomasone4422302012-01-20 17:59:50 -0800243 }
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800244 flags = (flags & NEEDS_DELETE);
Lee Thomasone4422302012-01-20 17:59:50 -0800245 }
246 return start;
247}
248
Lee Thomason2c85a712012-01-31 08:24:24 -0800249
Lee Thomasone4422302012-01-20 17:59:50 -0800250
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800251
Lee Thomason56bdd022012-02-09 18:16:58 -0800252// --------- XMLUtil ----------- //
Lee Thomasond1983222012-02-06 08:41:24 -0800253
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800254const char* XMLUtil::ReadBOM( const char* p, bool* bom )
255{
256 *bom = false;
257 const unsigned char* pu = reinterpret_cast<const unsigned char*>(p);
258 // Check for BOM:
259 if ( *(pu+0) == TIXML_UTF_LEAD_0
260 && *(pu+1) == TIXML_UTF_LEAD_1
261 && *(pu+2) == TIXML_UTF_LEAD_2 )
262 {
263 *bom = true;
264 p += 3;
265 }
266 return p;
267}
268
269
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800270void XMLUtil::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length )
271{
272 const unsigned long BYTE_MASK = 0xBF;
273 const unsigned long BYTE_MARK = 0x80;
274 const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
275
276 if (input < 0x80)
277 *length = 1;
278 else if ( input < 0x800 )
279 *length = 2;
280 else if ( input < 0x10000 )
281 *length = 3;
282 else if ( input < 0x200000 )
283 *length = 4;
284 else
285 { *length = 0; return; } // This code won't covert this correctly anyway.
286
287 output += *length;
288
289 // Scary scary fall throughs.
290 switch (*length)
291 {
292 case 4:
293 --output;
294 *output = (char)((input | BYTE_MARK) & BYTE_MASK);
295 input >>= 6;
296 case 3:
297 --output;
298 *output = (char)((input | BYTE_MARK) & BYTE_MASK);
299 input >>= 6;
300 case 2:
301 --output;
302 *output = (char)((input | BYTE_MARK) & BYTE_MASK);
303 input >>= 6;
304 case 1:
305 --output;
306 *output = (char)(input | FIRST_BYTE_MARK[*length]);
307 }
308}
309
310
311const char* XMLUtil::GetCharacterRef( const char* p, char* value, int* length )
312{
313 // Presume an entity, and pull it out.
314 *length = 0;
315
316 if ( *(p+1) == '#' && *(p+2) )
317 {
318 unsigned long ucs = 0;
Lee Thomason (grinliz)7ca55582012-03-07 21:54:57 -0800319 int delta = 0;
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800320 unsigned mult = 1;
321
322 if ( *(p+2) == 'x' )
323 {
324 // Hexadecimal.
325 if ( !*(p+3) ) return 0;
326
327 const char* q = p+3;
328 q = strchr( q, ';' );
329
330 if ( !q || !*q ) return 0;
331
Lee Thomason (grinliz)7ca55582012-03-07 21:54:57 -0800332 delta = (q-p);
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800333 --q;
334
335 while ( *q != 'x' )
336 {
337 if ( *q >= '0' && *q <= '9' )
338 ucs += mult * (*q - '0');
339 else if ( *q >= 'a' && *q <= 'f' )
340 ucs += mult * (*q - 'a' + 10);
341 else if ( *q >= 'A' && *q <= 'F' )
342 ucs += mult * (*q - 'A' + 10 );
343 else
344 return 0;
345 mult *= 16;
346 --q;
347 }
348 }
349 else
350 {
351 // Decimal.
352 if ( !*(p+2) ) return 0;
353
354 const char* q = p+2;
355 q = strchr( q, ';' );
356
357 if ( !q || !*q ) return 0;
358
359 delta = q-p;
360 --q;
361
362 while ( *q != '#' )
363 {
364 if ( *q >= '0' && *q <= '9' )
365 ucs += mult * (*q - '0');
366 else
367 return 0;
368 mult *= 10;
369 --q;
370 }
371 }
372 // convert the UCS to UTF-8
373 ConvertUTF32ToUTF8( ucs, value, length );
374 return p + delta + 1;
375 }
376 return p+1;
377}
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800378
379
Lee Thomasond1983222012-02-06 08:41:24 -0800380char* XMLDocument::Identify( char* p, XMLNode** node )
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800381{
382 XMLNode* returnNode = 0;
Lee Thomason5492a1c2012-01-23 15:32:10 -0800383 char* start = p;
Lee Thomason56bdd022012-02-09 18:16:58 -0800384 p = XMLUtil::SkipWhiteSpace( p );
Lee Thomason5492a1c2012-01-23 15:32:10 -0800385 if( !p || !*p )
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800386 {
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800387 return p;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800388 }
389
390 // What is this thing?
391 // - Elements start with a letter or underscore, but xml is reserved.
392 // - Comments: <!--
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800393 // - Decleration: <?
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800394 // - Everthing else is unknown to tinyxml.
395 //
396
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800397 static const char* xmlHeader = { "<?" };
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800398 static const char* commentHeader = { "<!--" };
399 static const char* dtdHeader = { "<!" };
400 static const char* cdataHeader = { "<![CDATA[" };
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800401 static const char* elementHeader = { "<" }; // and a header for everything else; check last.
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800402
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800403 static const int xmlHeaderLen = 2;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800404 static const int commentHeaderLen = 4;
405 static const int dtdHeaderLen = 2;
406 static const int cdataHeaderLen = 9;
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800407 static const int elementHeaderLen = 1;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800408
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800409#if defined(_MSC_VER)
Lee Thomason (grinliz)9b093cc2012-02-25 21:30:18 -0800410#pragma warning ( push )
411#pragma warning ( disable : 4127 )
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800412#endif
Lee Thomason50f97b22012-02-11 16:33:40 -0800413 TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLUnknown ) ); // use same memory pool
414 TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLDeclaration ) ); // use same memory pool
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800415#if defined(_MSC_VER)
Lee Thomason (grinliz)9b093cc2012-02-25 21:30:18 -0800416#pragma warning (pop)
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800417#endif
Lee Thomason50f97b22012-02-11 16:33:40 -0800418 if ( XMLUtil::StringEqual( p, xmlHeader, xmlHeaderLen ) ) {
419 returnNode = new (commentPool.Alloc()) XMLDeclaration( this );
420 returnNode->memPool = &commentPool;
421 p += xmlHeaderLen;
422 }
423 else if ( XMLUtil::StringEqual( p, commentHeader, commentHeaderLen ) ) {
Lee Thomasond1983222012-02-06 08:41:24 -0800424 returnNode = new (commentPool.Alloc()) XMLComment( this );
425 returnNode->memPool = &commentPool;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800426 p += commentHeaderLen;
427 }
Lee Thomason50f97b22012-02-11 16:33:40 -0800428 else if ( XMLUtil::StringEqual( p, cdataHeader, cdataHeaderLen ) ) {
429 XMLText* text = new (textPool.Alloc()) XMLText( this );
430 returnNode = text;
431 returnNode->memPool = &textPool;
432 p += cdataHeaderLen;
433 text->SetCData( true );
434 }
435 else if ( XMLUtil::StringEqual( p, dtdHeader, dtdHeaderLen ) ) {
436 returnNode = new (commentPool.Alloc()) XMLUnknown( this );
437 returnNode->memPool = &commentPool;
438 p += dtdHeaderLen;
439 }
Lee Thomason56bdd022012-02-09 18:16:58 -0800440 else if ( XMLUtil::StringEqual( p, elementHeader, elementHeaderLen ) ) {
Lee Thomasond1983222012-02-06 08:41:24 -0800441 returnNode = new (elementPool.Alloc()) XMLElement( this );
442 returnNode->memPool = &elementPool;
Lee Thomasondadcdfa2012-01-18 17:55:48 -0800443 p += elementHeaderLen;
444 }
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800445 else {
Lee Thomasond1983222012-02-06 08:41:24 -0800446 returnNode = new (textPool.Alloc()) XMLText( this );
447 returnNode->memPool = &textPool;
Lee Thomason5492a1c2012-01-23 15:32:10 -0800448 p = start; // Back it up, all the text counts.
449 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800450
451 *node = returnNode;
452 return p;
453}
454
455
Lee Thomason751da522012-02-10 08:50:51 -0800456bool XMLDocument::Accept( XMLVisitor* visitor ) const
457{
458 if ( visitor->VisitEnter( *this ) )
459 {
460 for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() )
461 {
462 if ( !node->Accept( visitor ) )
463 break;
464 }
465 }
466 return visitor->VisitExit( *this );
467}
Lee Thomason56bdd022012-02-09 18:16:58 -0800468
469
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800470// --------- XMLNode ----------- //
471
472XMLNode::XMLNode( XMLDocument* doc ) :
473 document( doc ),
474 parent( 0 ),
475 firstChild( 0 ), lastChild( 0 ),
476 prev( 0 ), next( 0 )
477{
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800478}
479
480
481XMLNode::~XMLNode()
482{
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -0800483 DeleteChildren();
Lee Thomason7c913cd2012-01-26 18:32:34 -0800484 if ( parent ) {
485 parent->Unlink( this );
486 }
Lee Thomason18d68bd2012-01-26 18:17:26 -0800487}
488
489
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800490void XMLNode::SetValue( const char* str, bool staticMem )
491{
492 if ( staticMem )
493 value.SetInternedStr( str );
494 else
495 value.SetStr( str );
496}
497
498
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -0800499void XMLNode::DeleteChildren()
Lee Thomason18d68bd2012-01-26 18:17:26 -0800500{
Lee Thomasond923c672012-01-23 08:44:25 -0800501 while( firstChild ) {
502 XMLNode* node = firstChild;
503 Unlink( node );
Lee Thomasond1983222012-02-06 08:41:24 -0800504
Lee Thomason43f59302012-02-06 18:18:11 -0800505 DELETE_NODE( node );
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800506 }
Lee Thomason18d68bd2012-01-26 18:17:26 -0800507 firstChild = lastChild = 0;
Lee Thomasond923c672012-01-23 08:44:25 -0800508}
509
510
511void XMLNode::Unlink( XMLNode* child )
512{
513 TIXMLASSERT( child->parent == this );
514 if ( child == firstChild )
515 firstChild = firstChild->next;
516 if ( child == lastChild )
517 lastChild = lastChild->prev;
518
519 if ( child->prev ) {
520 child->prev->next = child->next;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800521 }
Lee Thomasond923c672012-01-23 08:44:25 -0800522 if ( child->next ) {
523 child->next->prev = child->prev;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800524 }
Lee Thomasond923c672012-01-23 08:44:25 -0800525 child->parent = 0;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800526}
527
528
U-Stream\Leeae25a442012-02-17 17:48:16 -0800529void XMLNode::DeleteChild( XMLNode* node )
530{
531 TIXMLASSERT( node->parent == this );
532 DELETE_NODE( node );
533}
534
535
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800536XMLNode* XMLNode::InsertEndChild( XMLNode* addThis )
537{
538 if ( lastChild ) {
539 TIXMLASSERT( firstChild );
540 TIXMLASSERT( lastChild->next == 0 );
541 lastChild->next = addThis;
542 addThis->prev = lastChild;
543 lastChild = addThis;
544
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800545 addThis->next = 0;
546 }
547 else {
548 TIXMLASSERT( firstChild == 0 );
549 firstChild = lastChild = addThis;
550
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800551 addThis->prev = 0;
552 addThis->next = 0;
553 }
U-Stream\Lee09a11c52012-02-17 08:31:16 -0800554 addThis->parent = this;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800555 return addThis;
556}
557
558
Lee Thomason1ff38e02012-02-14 18:18:16 -0800559XMLNode* XMLNode::InsertFirstChild( XMLNode* addThis )
560{
561 if ( firstChild ) {
562 TIXMLASSERT( lastChild );
563 TIXMLASSERT( firstChild->prev == 0 );
564
565 firstChild->prev = addThis;
566 addThis->next = firstChild;
567 firstChild = addThis;
568
Lee Thomason1ff38e02012-02-14 18:18:16 -0800569 addThis->prev = 0;
570 }
571 else {
572 TIXMLASSERT( lastChild == 0 );
573 firstChild = lastChild = addThis;
574
Lee Thomason1ff38e02012-02-14 18:18:16 -0800575 addThis->prev = 0;
576 addThis->next = 0;
577 }
U-Stream\Lee09a11c52012-02-17 08:31:16 -0800578 addThis->parent = this;
Lee Thomason1ff38e02012-02-14 18:18:16 -0800579 return addThis;
580}
581
582
583XMLNode* XMLNode::InsertAfterChild( XMLNode* afterThis, XMLNode* addThis )
584{
585 TIXMLASSERT( afterThis->parent == this );
586 if ( afterThis->parent != this )
587 return 0;
588
589 if ( afterThis->next == 0 ) {
590 // The last node or the only node.
591 return InsertEndChild( addThis );
592 }
593 addThis->prev = afterThis;
594 addThis->next = afterThis->next;
595 afterThis->next->prev = addThis;
596 afterThis->next = addThis;
U-Stream\Lee09a11c52012-02-17 08:31:16 -0800597 addThis->parent = this;
Lee Thomason1ff38e02012-02-14 18:18:16 -0800598 return addThis;
599}
600
601
602
603
Lee Thomason56bdd022012-02-09 18:16:58 -0800604const XMLElement* XMLNode::FirstChildElement( const char* value ) const
Lee Thomason2c85a712012-01-31 08:24:24 -0800605{
606 for( XMLNode* node=firstChild; node; node=node->next ) {
607 XMLElement* element = node->ToElement();
608 if ( element ) {
Lee Thomason56bdd022012-02-09 18:16:58 -0800609 if ( !value || XMLUtil::StringEqual( element->Name(), value ) ) {
Lee Thomason2c85a712012-01-31 08:24:24 -0800610 return element;
611 }
612 }
613 }
614 return 0;
615}
616
617
Lee Thomason56bdd022012-02-09 18:16:58 -0800618const XMLElement* XMLNode::LastChildElement( const char* value ) const
619{
620 for( XMLNode* node=lastChild; node; node=node->prev ) {
621 XMLElement* element = node->ToElement();
622 if ( element ) {
623 if ( !value || XMLUtil::StringEqual( element->Name(), value ) ) {
624 return element;
625 }
626 }
627 }
628 return 0;
629}
630
631
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800632const XMLElement* XMLNode::NextSiblingElement( const char* value ) const
633{
634 for( XMLNode* element=this->next; element; element = element->next ) {
635 if ( element->ToElement()
636 && (!value || XMLUtil::StringEqual( value, element->Value() )))
637 {
638 return element->ToElement();
639 }
640 }
641 return 0;
642}
643
644
645const XMLElement* XMLNode::PreviousSiblingElement( const char* value ) const
646{
647 for( XMLNode* element=this->prev; element; element = element->prev ) {
648 if ( element->ToElement()
649 && (!value || XMLUtil::StringEqual( value, element->Value() )))
650 {
651 return element->ToElement();
652 }
653 }
654 return 0;
655}
656
657
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800658char* XMLNode::ParseDeep( char* p, StrPair* parentEnd )
Lee Thomason67d61312012-01-24 16:01:51 -0800659{
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800660 // This is a recursive method, but thinking about it "at the current level"
661 // it is a pretty simple flat list:
662 // <foo/>
663 // <!-- comment -->
664 //
665 // With a special case:
666 // <foo>
667 // </foo>
668 // <!-- comment -->
669 //
670 // Where the closing element (/foo) *must* be the next thing after the opening
671 // element, and the names must match. BUT the tricky bit is that the closing
672 // element will be read by the child.
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800673 //
674 // 'endTag' is the end tag for this node, it is returned by a call to a child.
675 // 'parentEnd' is the end tag for the parent, which is filled in and returned.
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800676
Lee Thomason67d61312012-01-24 16:01:51 -0800677 while( p && *p ) {
678 XMLNode* node = 0;
Lee Thomasond6277762012-02-22 16:00:12 -0800679
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800680 p = document->Identify( p, &node );
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800681 if ( p == 0 || node == 0 ) {
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800682 break;
683 }
684
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800685 StrPair endTag;
686 p = node->ParseDeep( p, &endTag );
687 if ( !p ) {
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800688 DELETE_NODE( node );
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800689 node = 0;
Lee Thomason (grinliz)784607f2012-02-24 16:23:40 -0800690 if ( !document->Error() ) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -0700691 document->SetError( XML_ERROR_PARSING, 0, 0 );
Lee Thomason (grinliz)784607f2012-02-24 16:23:40 -0800692 }
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800693 break;
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800694 }
695
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800696 // We read the end tag. Return it to the parent.
697 if ( node->ToElement() && node->ToElement()->ClosingType() == XMLElement::CLOSING ) {
698 if ( parentEnd ) {
699 *parentEnd = ((XMLElement*)node)->value;
Lee Thomason67d61312012-01-24 16:01:51 -0800700 }
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800701 DELETE_NODE( node );
702 return p;
703 }
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800704
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800705 // Handle an end tag returned to this level.
706 // And handle a bunch of annoying errors.
707 XMLElement* ele = node->ToElement();
708 if ( ele ) {
709 if ( endTag.Empty() && ele->ClosingType() == XMLElement::OPEN ) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -0700710 document->SetError( XML_ERROR_MISMATCHED_ELEMENT, node->Value(), 0 );
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800711 p = 0;
712 }
713 else if ( !endTag.Empty() && ele->ClosingType() != XMLElement::OPEN ) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -0700714 document->SetError( XML_ERROR_MISMATCHED_ELEMENT, node->Value(), 0 );
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800715 p = 0;
716 }
717 else if ( !endTag.Empty() ) {
718 if ( !XMLUtil::StringEqual( endTag.GetStr(), node->Value() )) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -0700719 document->SetError( XML_ERROR_MISMATCHED_ELEMENT, node->Value(), 0 );
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800720 p = 0;
721 }
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800722 }
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800723 }
724 if ( p == 0 ) {
725 DELETE_NODE( node );
726 node = 0;
727 }
728 if ( node ) {
729 this->InsertEndChild( node );
Lee Thomason67d61312012-01-24 16:01:51 -0800730 }
731 }
732 return 0;
733}
734
Lee Thomason5492a1c2012-01-23 15:32:10 -0800735// --------- XMLText ---------- //
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800736char* XMLText::ParseDeep( char* p, StrPair* )
Lee Thomason5492a1c2012-01-23 15:32:10 -0800737{
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800738 const char* start = p;
Lee Thomason50f97b22012-02-11 16:33:40 -0800739 if ( this->CData() ) {
740 p = value.ParseText( p, "]]>", StrPair::NEEDS_NEWLINE_NORMALIZATION );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800741 if ( !p ) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -0700742 document->SetError( XML_ERROR_PARSING_CDATA, start, 0 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800743 }
Lee Thomason50f97b22012-02-11 16:33:40 -0800744 return p;
745 }
746 else {
Lee Thomason6f381b72012-03-02 12:59:39 -0800747 p = value.ParseText( p, "<", document->ProcessEntities() ? StrPair::TEXT_ELEMENT : StrPair::TEXT_ELEMENT_LEAVE_ENTITIES );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800748 if ( !p ) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -0700749 document->SetError( XML_ERROR_PARSING_TEXT, start, 0 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800750 }
Lee Thomason50f97b22012-02-11 16:33:40 -0800751 if ( p && *p ) {
752 return p-1;
753 }
Lee Thomason5492a1c2012-01-23 15:32:10 -0800754 }
755 return 0;
756}
757
758
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800759XMLNode* XMLText::ShallowClone( XMLDocument* doc ) const
760{
761 if ( !doc ) {
762 doc = document;
763 }
764 XMLText* text = doc->NewText( Value() ); // fixme: this will always allocate memory. Intern?
765 text->SetCData( this->CData() );
766 return text;
767}
768
769
770bool XMLText::ShallowEqual( const XMLNode* compare ) const
771{
772 return ( compare->ToText() && XMLUtil::StringEqual( compare->ToText()->Value(), Value() ));
773}
774
775
Lee Thomason56bdd022012-02-09 18:16:58 -0800776bool XMLText::Accept( XMLVisitor* visitor ) const
777{
778 return visitor->Visit( *this );
779}
780
781
Lee Thomason3f57d272012-01-11 15:30:03 -0800782// --------- XMLComment ---------- //
783
Lee Thomasone4422302012-01-20 17:59:50 -0800784XMLComment::XMLComment( XMLDocument* doc ) : XMLNode( doc )
Lee Thomason3f57d272012-01-11 15:30:03 -0800785{
786}
787
788
Lee Thomasonce0763e2012-01-11 15:43:54 -0800789XMLComment::~XMLComment()
Lee Thomason3f57d272012-01-11 15:30:03 -0800790{
Lee Thomasond923c672012-01-23 08:44:25 -0800791 //printf( "~XMLComment\n" );
Lee Thomason3f57d272012-01-11 15:30:03 -0800792}
793
794
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800795char* XMLComment::ParseDeep( char* p, StrPair* )
Lee Thomason3f57d272012-01-11 15:30:03 -0800796{
797 // Comment parses as text.
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800798 const char* start = p;
799 p = value.ParseText( p, "-->", StrPair::COMMENT );
800 if ( p == 0 ) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -0700801 document->SetError( XML_ERROR_PARSING_COMMENT, start, 0 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800802 }
803 return p;
U-Lama\Lee4cee6112011-12-31 14:58:18 -0800804}
805
806
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800807XMLNode* XMLComment::ShallowClone( XMLDocument* doc ) const
808{
809 if ( !doc ) {
810 doc = document;
811 }
812 XMLComment* comment = doc->NewComment( Value() ); // fixme: this will always allocate memory. Intern?
813 return comment;
814}
815
816
817bool XMLComment::ShallowEqual( const XMLNode* compare ) const
818{
819 return ( compare->ToComment() && XMLUtil::StringEqual( compare->ToComment()->Value(), Value() ));
820}
821
822
Lee Thomason751da522012-02-10 08:50:51 -0800823bool XMLComment::Accept( XMLVisitor* visitor ) const
824{
825 return visitor->Visit( *this );
826}
Lee Thomason56bdd022012-02-09 18:16:58 -0800827
828
Lee Thomason50f97b22012-02-11 16:33:40 -0800829// --------- XMLDeclaration ---------- //
830
831XMLDeclaration::XMLDeclaration( XMLDocument* doc ) : XMLNode( doc )
832{
833}
834
835
836XMLDeclaration::~XMLDeclaration()
837{
838 //printf( "~XMLDeclaration\n" );
839}
840
841
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800842char* XMLDeclaration::ParseDeep( char* p, StrPair* )
Lee Thomason50f97b22012-02-11 16:33:40 -0800843{
844 // Declaration parses as text.
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800845 const char* start = p;
846 p = value.ParseText( p, "?>", StrPair::NEEDS_NEWLINE_NORMALIZATION );
847 if ( p == 0 ) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -0700848 document->SetError( XML_ERROR_PARSING_DECLARATION, start, 0 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800849 }
850 return p;
Lee Thomason50f97b22012-02-11 16:33:40 -0800851}
852
853
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800854XMLNode* XMLDeclaration::ShallowClone( XMLDocument* doc ) const
855{
856 if ( !doc ) {
857 doc = document;
858 }
859 XMLDeclaration* dec = doc->NewDeclaration( Value() ); // fixme: this will always allocate memory. Intern?
860 return dec;
861}
862
863
864bool XMLDeclaration::ShallowEqual( const XMLNode* compare ) const
865{
866 return ( compare->ToDeclaration() && XMLUtil::StringEqual( compare->ToDeclaration()->Value(), Value() ));
867}
868
869
870
Lee Thomason50f97b22012-02-11 16:33:40 -0800871bool XMLDeclaration::Accept( XMLVisitor* visitor ) const
872{
873 return visitor->Visit( *this );
874}
875
876// --------- XMLUnknown ---------- //
877
878XMLUnknown::XMLUnknown( XMLDocument* doc ) : XMLNode( doc )
879{
880}
881
882
883XMLUnknown::~XMLUnknown()
884{
885}
886
887
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -0800888char* XMLUnknown::ParseDeep( char* p, StrPair* )
Lee Thomason50f97b22012-02-11 16:33:40 -0800889{
890 // Unknown parses as text.
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800891 const char* start = p;
892
893 p = value.ParseText( p, ">", StrPair::NEEDS_NEWLINE_NORMALIZATION );
894 if ( !p ) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -0700895 document->SetError( XML_ERROR_PARSING_UNKNOWN, start, 0 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -0800896 }
897 return p;
Lee Thomason50f97b22012-02-11 16:33:40 -0800898}
899
900
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800901XMLNode* XMLUnknown::ShallowClone( XMLDocument* doc ) const
902{
903 if ( !doc ) {
904 doc = document;
905 }
906 XMLUnknown* text = doc->NewUnknown( Value() ); // fixme: this will always allocate memory. Intern?
907 return text;
908}
909
910
911bool XMLUnknown::ShallowEqual( const XMLNode* compare ) const
912{
913 return ( compare->ToUnknown() && XMLUtil::StringEqual( compare->ToUnknown()->Value(), Value() ));
914}
915
916
Lee Thomason50f97b22012-02-11 16:33:40 -0800917bool XMLUnknown::Accept( XMLVisitor* visitor ) const
918{
919 return visitor->Visit( *this );
920}
921
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800922// --------- XMLAttribute ---------- //
Lee Thomason6f381b72012-03-02 12:59:39 -0800923char* XMLAttribute::ParseDeep( char* p, bool processEntities )
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800924{
Lee Thomason56bdd022012-02-09 18:16:58 -0800925 p = name.ParseText( p, "=", StrPair::ATTRIBUTE_NAME );
Lee Thomason22aead12012-01-23 13:29:35 -0800926 if ( !p || !*p ) return 0;
927
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800928 char endTag[2] = { *p, 0 };
929 ++p;
Lee Thomason6f381b72012-03-02 12:59:39 -0800930 p = value.ParseText( p, endTag, processEntities ? StrPair::ATTRIBUTE_VALUE : StrPair::ATTRIBUTE_VALUE_LEAVE_ENTITIES );
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -0800931 //if ( value.Empty() ) return 0;
Lee Thomason8a5dfee2012-01-18 17:43:40 -0800932 return p;
933}
934
935
U-Stream\Lee09a11c52012-02-17 08:31:16 -0800936void XMLAttribute::SetName( const char* n )
937{
938 name.SetStr( n );
939}
940
941
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800942int XMLAttribute::QueryIntValue( int* value ) const
Lee Thomason1ff38e02012-02-14 18:18:16 -0800943{
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800944 if ( TIXML_SSCANF( Value(), "%d", value ) == 1 )
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800945 return XML_NO_ERROR;
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -0700946 return XML_WRONG_ATTRIBUTE_TYPE;
Lee Thomason1ff38e02012-02-14 18:18:16 -0800947}
948
949
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800950int XMLAttribute::QueryUnsignedValue( unsigned int* value ) const
Lee Thomason1ff38e02012-02-14 18:18:16 -0800951{
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800952 if ( TIXML_SSCANF( Value(), "%u", value ) == 1 )
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800953 return XML_NO_ERROR;
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -0700954 return XML_WRONG_ATTRIBUTE_TYPE;
Lee Thomason1ff38e02012-02-14 18:18:16 -0800955}
956
957
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800958int XMLAttribute::QueryBoolValue( bool* value ) const
Lee Thomason1ff38e02012-02-14 18:18:16 -0800959{
960 int ival = -1;
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800961 QueryIntValue( &ival );
Lee Thomason1ff38e02012-02-14 18:18:16 -0800962
963 if ( ival > 0 || XMLUtil::StringEqual( Value(), "true" ) ) {
964 *value = true;
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800965 return XML_NO_ERROR;
Lee Thomason1ff38e02012-02-14 18:18:16 -0800966 }
967 else if ( ival == 0 || XMLUtil::StringEqual( Value(), "false" ) ) {
968 *value = false;
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800969 return XML_NO_ERROR;
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800970 }
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -0700971 return XML_WRONG_ATTRIBUTE_TYPE;
Lee Thomason1ff38e02012-02-14 18:18:16 -0800972}
973
974
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800975int XMLAttribute::QueryDoubleValue( double* value ) const
Lee Thomason1ff38e02012-02-14 18:18:16 -0800976{
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800977 if ( TIXML_SSCANF( Value(), "%lf", value ) == 1 )
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800978 return XML_NO_ERROR;
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -0700979 return XML_WRONG_ATTRIBUTE_TYPE;
Lee Thomason1ff38e02012-02-14 18:18:16 -0800980}
981
982
Lee Thomason7d00b9a2012-02-27 17:54:22 -0800983int XMLAttribute::QueryFloatValue( float* value ) const
Lee Thomason1ff38e02012-02-14 18:18:16 -0800984{
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800985 if ( TIXML_SSCANF( Value(), "%f", value ) == 1 )
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -0800986 return XML_NO_ERROR;
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -0700987 return XML_WRONG_ATTRIBUTE_TYPE;
Lee Thomason1ff38e02012-02-14 18:18:16 -0800988}
989
990
991void XMLAttribute::SetAttribute( const char* v )
992{
Lee Thomason1a1d4a72012-02-15 09:09:25 -0800993 value.SetStr( v );
Lee Thomason1ff38e02012-02-14 18:18:16 -0800994}
995
996
Lee Thomason1ff38e02012-02-14 18:18:16 -0800997void XMLAttribute::SetAttribute( int v )
998{
999 char buf[BUF_SIZE];
Lee Thomason (grinliz)598c13e2012-04-06 21:18:23 -07001000 TIXML_SNPRINTF( buf, BUF_SIZE, "%d", v );
Lee Thomason1a1d4a72012-02-15 09:09:25 -08001001 value.SetStr( buf );
Lee Thomason1ff38e02012-02-14 18:18:16 -08001002}
Lee Thomason1a1d4a72012-02-15 09:09:25 -08001003
1004
1005void XMLAttribute::SetAttribute( unsigned v )
1006{
1007 char buf[BUF_SIZE];
Lee Thomason (grinliz)a4a36ba2012-04-06 21:24:29 -07001008 TIXML_SNPRINTF( buf, BUF_SIZE, "%u", v );
Lee Thomason1a1d4a72012-02-15 09:09:25 -08001009 value.SetStr( buf );
1010}
1011
1012
1013void XMLAttribute::SetAttribute( bool v )
1014{
1015 char buf[BUF_SIZE];
Lee Thomason (grinliz)a4a36ba2012-04-06 21:24:29 -07001016 TIXML_SNPRINTF( buf, BUF_SIZE, "%d", v ? 1 : 0 );
Lee Thomason1a1d4a72012-02-15 09:09:25 -08001017 value.SetStr( buf );
1018}
1019
1020void XMLAttribute::SetAttribute( double v )
1021{
1022 char buf[BUF_SIZE];
Lee Thomason (grinliz)a4a36ba2012-04-06 21:24:29 -07001023 TIXML_SNPRINTF( buf, BUF_SIZE, "%f", v );
Lee Thomason1a1d4a72012-02-15 09:09:25 -08001024 value.SetStr( buf );
1025}
1026
1027void XMLAttribute::SetAttribute( float v )
1028{
1029 char buf[BUF_SIZE];
Lee Thomason (grinliz)a4a36ba2012-04-06 21:24:29 -07001030 TIXML_SNPRINTF( buf, BUF_SIZE, "%f", v );
Lee Thomason1a1d4a72012-02-15 09:09:25 -08001031 value.SetStr( buf );
1032}
1033
Lee Thomasondadcdfa2012-01-18 17:55:48 -08001034
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001035// --------- XMLElement ---------- //
1036XMLElement::XMLElement( XMLDocument* doc ) : XMLNode( doc ),
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -08001037 closingType( 0 ),
U-Stream\Lee09a11c52012-02-17 08:31:16 -08001038 rootAttribute( 0 )
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001039{
1040}
1041
1042
1043XMLElement::~XMLElement()
1044{
U-Stream\Lee09a11c52012-02-17 08:31:16 -08001045 while( rootAttribute ) {
1046 XMLAttribute* next = rootAttribute->next;
1047 DELETE_ATTRIBUTE( rootAttribute );
1048 rootAttribute = next;
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001049 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001050}
1051
1052
Lee Thomason1a1d4a72012-02-15 09:09:25 -08001053XMLAttribute* XMLElement::FindAttribute( const char* name )
1054{
1055 XMLAttribute* a = 0;
1056 for( a=rootAttribute; a; a = a->next ) {
1057 if ( XMLUtil::StringEqual( a->Name(), name ) )
1058 return a;
1059 }
1060 return 0;
1061}
1062
1063
1064const XMLAttribute* XMLElement::FindAttribute( const char* name ) const
1065{
1066 XMLAttribute* a = 0;
1067 for( a=rootAttribute; a; a = a->next ) {
1068 if ( XMLUtil::StringEqual( a->Name(), name ) )
1069 return a;
1070 }
1071 return 0;
1072}
1073
1074
Lee Thomason8ba7f7d2012-03-24 13:04:04 -07001075const char* XMLElement::Attribute( const char* name, const char* value ) const
1076{
1077 const XMLAttribute* a = FindAttribute( name );
1078 if ( !a )
1079 return 0;
1080 if ( !value || XMLUtil::StringEqual( a->Value(), value ))
1081 return a->Value();
1082 return 0;
1083}
1084
1085
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001086const char* XMLElement::GetText() const
1087{
1088 if ( FirstChild() && FirstChild()->ToText() ) {
1089 return FirstChild()->ToText()->Value();
1090 }
1091 return 0;
1092}
1093
1094
Lee Thomason1a1d4a72012-02-15 09:09:25 -08001095XMLAttribute* XMLElement::FindOrCreateAttribute( const char* name )
1096{
Lee Thomason5e3803c2012-04-16 08:57:05 -07001097 XMLAttribute* last = 0;
1098 XMLAttribute* attrib = 0;
1099 for( attrib = rootAttribute;
1100 attrib;
1101 last = attrib, attrib = attrib->next )
1102 {
1103 if ( XMLUtil::StringEqual( attrib->Name(), name ) ) {
1104 break;
1105 }
1106 }
Lee Thomason1a1d4a72012-02-15 09:09:25 -08001107 if ( !attrib ) {
Lee Thomason (grinliz)9b093cc2012-02-25 21:30:18 -08001108 attrib = new (document->attributePool.Alloc() ) XMLAttribute();
Lee Thomason1a1d4a72012-02-15 09:09:25 -08001109 attrib->memPool = &document->attributePool;
Lee Thomason5e3803c2012-04-16 08:57:05 -07001110 if ( last ) {
1111 last->next = attrib;
1112 }
1113 else {
1114 rootAttribute = attrib;
1115 }
U-Stream\Lee09a11c52012-02-17 08:31:16 -08001116 attrib->SetName( name );
Lee Thomason1a1d4a72012-02-15 09:09:25 -08001117 }
1118 return attrib;
1119}
1120
1121
U-Stream\Leeae25a442012-02-17 17:48:16 -08001122void XMLElement::DeleteAttribute( const char* name )
1123{
1124 XMLAttribute* prev = 0;
1125 for( XMLAttribute* a=rootAttribute; a; a=a->next ) {
1126 if ( XMLUtil::StringEqual( name, a->Name() ) ) {
1127 if ( prev ) {
1128 prev->next = a->next;
1129 }
1130 else {
1131 rootAttribute = a->next;
1132 }
1133 DELETE_ATTRIBUTE( a );
1134 break;
1135 }
1136 prev = a;
1137 }
1138}
1139
1140
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -08001141char* XMLElement::ParseAttributes( char* p )
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001142{
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001143 const char* start = p;
Lee Thomason5e3803c2012-04-16 08:57:05 -07001144 XMLAttribute* prevAttribute = 0;
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001145
1146 // Read the attributes.
1147 while( p ) {
Lee Thomason56bdd022012-02-09 18:16:58 -08001148 p = XMLUtil::SkipWhiteSpace( p );
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001149 if ( !p || !(*p) ) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -07001150 document->SetError( XML_ERROR_PARSING_ELEMENT, start, Name() );
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001151 return 0;
1152 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001153
1154 // attribute.
Lee Thomason56bdd022012-02-09 18:16:58 -08001155 if ( XMLUtil::IsAlpha( *p ) ) {
Lee Thomason (grinliz)9b093cc2012-02-25 21:30:18 -08001156 XMLAttribute* attrib = new (document->attributePool.Alloc() ) XMLAttribute();
Lee Thomason43f59302012-02-06 18:18:11 -08001157 attrib->memPool = &document->attributePool;
Lee Thomasond1983222012-02-06 08:41:24 -08001158
Lee Thomason6f381b72012-03-02 12:59:39 -08001159 p = attrib->ParseDeep( p, document->ProcessEntities() );
Lee Thomasond6277762012-02-22 16:00:12 -08001160 if ( !p || Attribute( attrib->Name() ) ) {
Lee Thomason43f59302012-02-06 18:18:11 -08001161 DELETE_ATTRIBUTE( attrib );
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -07001162 document->SetError( XML_ERROR_PARSING_ATTRIBUTE, start, p );
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001163 return 0;
1164 }
Lee Thomason5e3803c2012-04-16 08:57:05 -07001165 // There is a minor bug here: if the attribute in the source xml
1166 // document is duplicated, it will not be detected and the
1167 // attribute will be doubly added. However, tracking the 'prevAttribute'
1168 // avoids re-scanning the attribute list. Preferring performance for
1169 // now, may reconsider in the future.
1170 if ( prevAttribute ) {
1171 prevAttribute->next = attrib;
1172 prevAttribute = attrib;
1173 }
1174 else {
1175 rootAttribute = attrib;
1176 prevAttribute = rootAttribute;
1177 }
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 }
1254 }
1255 if ( a || b ) {
1256 // different count
1257 return false;
1258 }
1259 return true;
1260 }
1261 return false;
1262}
1263
1264
Lee Thomason751da522012-02-10 08:50:51 -08001265bool XMLElement::Accept( XMLVisitor* visitor ) const
1266{
1267 if ( visitor->VisitEnter( *this, rootAttribute ) )
1268 {
1269 for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() )
1270 {
1271 if ( !node->Accept( visitor ) )
1272 break;
1273 }
1274 }
1275 return visitor->VisitExit( *this );
Lee Thomason751da522012-02-10 08:50:51 -08001276}
Lee Thomason56bdd022012-02-09 18:16:58 -08001277
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001278
Lee Thomason3f57d272012-01-11 15:30:03 -08001279// --------- XMLDocument ----------- //
Lee Thomason6f381b72012-03-02 12:59:39 -08001280XMLDocument::XMLDocument( bool _processEntities ) :
Lee Thomason18d68bd2012-01-26 18:17:26 -08001281 XMLNode( 0 ),
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001282 writeBOM( false ),
Lee Thomason6f381b72012-03-02 12:59:39 -08001283 processEntities( _processEntities ),
1284 errorID( 0 ),
1285 errorStr1( 0 ),
1286 errorStr2( 0 ),
U-Lama\Lee560bd472011-12-28 19:42:49 -08001287 charBuffer( 0 )
1288{
Lee Thomason18d68bd2012-01-26 18:17:26 -08001289 document = this; // avoid warning about 'this' in initializer list
U-Lama\Lee560bd472011-12-28 19:42:49 -08001290}
U-Lama\Leee13c3e62011-12-28 14:36:55 -08001291
1292
Lee Thomason3f57d272012-01-11 15:30:03 -08001293XMLDocument::~XMLDocument()
1294{
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001295 DeleteChildren();
Lee Thomason18d68bd2012-01-26 18:17:26 -08001296 delete [] charBuffer;
Lee Thomasond1983222012-02-06 08:41:24 -08001297
Lee Thomasonec5a7b42012-02-13 18:16:52 -08001298#if 0
Lee Thomason455c9d42012-02-06 09:14:14 -08001299 textPool.Trace( "text" );
1300 elementPool.Trace( "element" );
1301 commentPool.Trace( "comment" );
1302 attributePool.Trace( "attribute" );
Lee Thomasone9ecdab2012-02-13 18:11:20 -08001303#endif
1304
Lee Thomason455c9d42012-02-06 09:14:14 -08001305 TIXMLASSERT( textPool.CurrentAllocs() == 0 );
1306 TIXMLASSERT( elementPool.CurrentAllocs() == 0 );
1307 TIXMLASSERT( commentPool.CurrentAllocs() == 0 );
1308 TIXMLASSERT( attributePool.CurrentAllocs() == 0 );
Lee Thomason3f57d272012-01-11 15:30:03 -08001309}
1310
1311
Lee Thomason18d68bd2012-01-26 18:17:26 -08001312void XMLDocument::InitDocument()
1313{
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001314 errorID = XML_NO_ERROR;
Lee Thomason18d68bd2012-01-26 18:17:26 -08001315 errorStr1 = 0;
1316 errorStr2 = 0;
1317
1318 delete [] charBuffer;
1319 charBuffer = 0;
1320
1321}
1322
Lee Thomason3f57d272012-01-11 15:30:03 -08001323
Lee Thomason2c85a712012-01-31 08:24:24 -08001324XMLElement* XMLDocument::NewElement( const char* name )
1325{
Lee Thomasond1983222012-02-06 08:41:24 -08001326 XMLElement* ele = new (elementPool.Alloc()) XMLElement( this );
1327 ele->memPool = &elementPool;
Lee Thomason2c85a712012-01-31 08:24:24 -08001328 ele->SetName( name );
1329 return ele;
1330}
1331
1332
Lee Thomason1ff38e02012-02-14 18:18:16 -08001333XMLComment* XMLDocument::NewComment( const char* str )
1334{
1335 XMLComment* comment = new (commentPool.Alloc()) XMLComment( this );
1336 comment->memPool = &commentPool;
1337 comment->SetValue( str );
1338 return comment;
1339}
1340
1341
1342XMLText* XMLDocument::NewText( const char* str )
1343{
U-Stream\Lee09a11c52012-02-17 08:31:16 -08001344 XMLText* text = new (textPool.Alloc()) XMLText( this );
1345 text->memPool = &textPool;
1346 text->SetValue( str );
1347 return text;
Lee Thomason1ff38e02012-02-14 18:18:16 -08001348}
1349
1350
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001351XMLDeclaration* XMLDocument::NewDeclaration( const char* str )
1352{
1353 XMLDeclaration* dec = new (commentPool.Alloc()) XMLDeclaration( this );
1354 dec->memPool = &commentPool;
1355 dec->SetValue( str );
1356 return dec;
1357}
1358
1359
1360XMLUnknown* XMLDocument::NewUnknown( const char* str )
1361{
1362 XMLUnknown* unk = new (commentPool.Alloc()) XMLUnknown( this );
1363 unk->memPool = &commentPool;
1364 unk->SetValue( str );
1365 return unk;
1366}
1367
1368
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001369int XMLDocument::LoadFile( const char* filename )
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001370{
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001371 DeleteChildren();
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001372 InitDocument();
1373
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001374#if defined(_MSC_VER)
Lee Thomason (grinliz)5ce4d972012-02-26 21:14:23 -08001375#pragma warning ( push )
1376#pragma warning ( disable : 4996 ) // Fail to see a compelling reason why this should be deprecated.
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001377#endif
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001378 FILE* fp = fopen( filename, "rb" );
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001379#if defined(_MSC_VER)
Lee Thomason (grinliz)5ce4d972012-02-26 21:14:23 -08001380#pragma warning ( pop )
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001381#endif
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001382 if ( !fp ) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -07001383 SetError( XML_ERROR_FILE_NOT_FOUND, filename, 0 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001384 return errorID;
1385 }
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001386 LoadFile( fp );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001387 fclose( fp );
1388 return errorID;
1389}
1390
1391
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001392int XMLDocument::LoadFile( FILE* fp )
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001393{
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001394 DeleteChildren();
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001395 InitDocument();
1396
1397 fseek( fp, 0, SEEK_END );
1398 unsigned size = ftell( fp );
1399 fseek( fp, 0, SEEK_SET );
1400
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001401 if ( size == 0 ) {
1402 return errorID;
1403 }
1404
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001405 charBuffer = new char[size+1];
1406 fread( charBuffer, size, 1, fp );
1407 charBuffer[size] = 0;
1408
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001409 const char* p = charBuffer;
1410 p = XMLUtil::SkipWhiteSpace( p );
1411 p = XMLUtil::ReadBOM( p, &writeBOM );
1412 if ( !p || !*p ) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -07001413 SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
Lee Thomasond6277762012-02-22 16:00:12 -08001414 return errorID;
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001415 }
1416
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -08001417 ParseDeep( charBuffer + (p-charBuffer), 0 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001418 return errorID;
1419}
1420
1421
Ken Miller81da1fb2012-04-09 23:32:26 -05001422int XMLDocument::SaveFile( const char* filename )
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001423{
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001424#if defined(_MSC_VER)
Lee Thomason (grinliz)5ce4d972012-02-26 21:14:23 -08001425#pragma warning ( push )
1426#pragma warning ( disable : 4996 ) // Fail to see a compelling reason why this should be deprecated.
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001427#endif
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001428 FILE* fp = fopen( filename, "w" );
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001429#if defined(_MSC_VER)
Lee Thomason (grinliz)5ce4d972012-02-26 21:14:23 -08001430#pragma warning ( pop )
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001431#endif
Ken Miller81da1fb2012-04-09 23:32:26 -05001432 if ( !fp ) {
Lee Thomason7f7b1622012-03-24 12:49:03 -07001433 SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, filename, 0 );
Ken Miller81da1fb2012-04-09 23:32:26 -05001434 return errorID;
Lee Thomason7f7b1622012-03-24 12:49:03 -07001435 }
Ken Miller81da1fb2012-04-09 23:32:26 -05001436 SaveFile(fp);
1437 fclose( fp );
1438 return errorID;
1439}
1440
1441
1442int XMLDocument::SaveFile( FILE* fp )
1443{
1444 XMLPrinter stream( fp );
1445 Print( &stream );
1446 return errorID;
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001447}
1448
Lee Thomason1ff38e02012-02-14 18:18:16 -08001449
Lee Thomason7c913cd2012-01-26 18:32:34 -08001450int XMLDocument::Parse( const char* p )
Lee Thomason3f57d272012-01-11 15:30:03 -08001451{
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001452 DeleteChildren();
Lee Thomason18d68bd2012-01-26 18:17:26 -08001453 InitDocument();
1454
1455 if ( !p || !*p ) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -07001456 SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
Lee Thomasond6277762012-02-22 16:00:12 -08001457 return errorID;
Lee Thomason18d68bd2012-01-26 18:17:26 -08001458 }
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001459 p = XMLUtil::SkipWhiteSpace( p );
1460 p = XMLUtil::ReadBOM( p, &writeBOM );
1461 if ( !p || !*p ) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -07001462 SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
Lee Thomasond6277762012-02-22 16:00:12 -08001463 return errorID;
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001464 }
1465
Lee Thomason18d68bd2012-01-26 18:17:26 -08001466 size_t len = strlen( p );
1467 charBuffer = new char[ len+1 ];
1468 memcpy( charBuffer, p, len+1 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001469
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001470
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -08001471 ParseDeep( charBuffer, 0 );
Lee Thomason7c913cd2012-01-26 18:32:34 -08001472 return errorID;
Lee Thomason3f57d272012-01-11 15:30:03 -08001473}
1474
1475
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001476void XMLDocument::Print( XMLPrinter* streamer )
Lee Thomason3f57d272012-01-11 15:30:03 -08001477{
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001478 XMLPrinter stdStreamer( stdout );
Lee Thomason5cae8972012-01-24 18:03:07 -08001479 if ( !streamer )
1480 streamer = &stdStreamer;
Lee Thomason751da522012-02-10 08:50:51 -08001481 Accept( streamer );
Lee Thomason3f57d272012-01-11 15:30:03 -08001482}
1483
1484
Lee Thomason67d61312012-01-24 16:01:51 -08001485void XMLDocument::SetError( int error, const char* str1, const char* str2 )
1486{
Lee Thomason18d68bd2012-01-26 18:17:26 -08001487 errorID = error;
Lee Thomason18d68bd2012-01-26 18:17:26 -08001488 errorStr1 = str1;
1489 errorStr2 = str2;
Lee Thomason67d61312012-01-24 16:01:51 -08001490}
1491
Lee Thomason5cae8972012-01-24 18:03:07 -08001492
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001493void XMLDocument::PrintError() const
1494{
1495 if ( errorID ) {
Lee Thomason (grinliz)5ce4d972012-02-26 21:14:23 -08001496 static const int LEN = 20;
1497 char buf1[LEN] = { 0 };
1498 char buf2[LEN] = { 0 };
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001499
1500 if ( errorStr1 ) {
Lee Thomason (grinliz)5ce4d972012-02-26 21:14:23 -08001501 TIXML_SNPRINTF( buf1, LEN, "%s", errorStr1 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001502 }
1503 if ( errorStr2 ) {
Lee Thomason (grinliz)5ce4d972012-02-26 21:14:23 -08001504 TIXML_SNPRINTF( buf2, LEN, "%s", errorStr2 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001505 }
1506
1507 printf( "XMLDocument error id=%d str1=%s str2=%s\n",
1508 errorID, buf1, buf2 );
1509 }
1510}
1511
1512
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001513XMLPrinter::XMLPrinter( FILE* file ) :
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001514 elementJustOpened( false ),
1515 firstElement( true ),
1516 fp( file ),
1517 depth( 0 ),
Lee Thomason6f381b72012-03-02 12:59:39 -08001518 textDepth( -1 ),
1519 processEntities( true )
Lee Thomason5cae8972012-01-24 18:03:07 -08001520{
Lee Thomason857b8682012-01-25 17:50:25 -08001521 for( int i=0; i<ENTITY_RANGE; ++i ) {
1522 entityFlag[i] = false;
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001523 restrictedEntityFlag[i] = false;
Lee Thomason857b8682012-01-25 17:50:25 -08001524 }
1525 for( int i=0; i<NUM_ENTITIES; ++i ) {
1526 TIXMLASSERT( entities[i].value < ENTITY_RANGE );
1527 if ( entities[i].value < ENTITY_RANGE ) {
Lee Thomason (grinliz)9b093cc2012-02-25 21:30:18 -08001528 entityFlag[ (int)entities[i].value ] = true;
Lee Thomason857b8682012-01-25 17:50:25 -08001529 }
1530 }
Lee Thomason (grinliz)9b093cc2012-02-25 21:30:18 -08001531 restrictedEntityFlag[(int)'&'] = true;
1532 restrictedEntityFlag[(int)'<'] = true;
1533 restrictedEntityFlag[(int)'>'] = true; // not required, but consistency is nice
U-Stream\Leeae25a442012-02-17 17:48:16 -08001534 buffer.Push( 0 );
1535}
1536
1537
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001538void XMLPrinter::Print( const char* format, ... )
U-Stream\Leeae25a442012-02-17 17:48:16 -08001539{
1540 va_list va;
1541 va_start( va, format );
1542
1543 if ( fp ) {
1544 vfprintf( fp, format, va );
1545 }
1546 else {
1547 // This seems brutally complex. Haven't figured out a better
1548 // way on windows.
1549 #ifdef _MSC_VER
1550 int len = -1;
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001551 int expand = 1000;
U-Stream\Leeae25a442012-02-17 17:48:16 -08001552 while ( len < 0 ) {
Lee Thomason (grinliz)598c13e2012-04-06 21:18:23 -07001553 len = vsnprintf_s( accumulator.Mem(), accumulator.Capacity(), _TRUNCATE, format, va );
U-Stream\Leeae25a442012-02-17 17:48:16 -08001554 if ( len < 0 ) {
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001555 expand *= 3/2;
Lee Thomason (grinliz)598c13e2012-04-06 21:18:23 -07001556 accumulator.PushArr( expand );
U-Stream\Leeae25a442012-02-17 17:48:16 -08001557 }
1558 }
1559 char* p = buffer.PushArr( len ) - 1;
1560 memcpy( p, accumulator.Mem(), len+1 );
1561 #else
1562 int len = vsnprintf( 0, 0, format, va );
Lee Thomason4de93472012-03-13 17:33:35 -07001563 // Close out and re-start the va-args
1564 va_end( va );
1565 va_start( va, format );
U-Stream\Leeae25a442012-02-17 17:48:16 -08001566 char* p = buffer.PushArr( len ) - 1;
Lee Thomason (grinliz)9b093cc2012-02-25 21:30:18 -08001567 vsnprintf( p, len+1, format, va );
U-Stream\Leeae25a442012-02-17 17:48:16 -08001568 #endif
1569 }
1570 va_end( va );
Lee Thomason5cae8972012-01-24 18:03:07 -08001571}
1572
1573
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001574void XMLPrinter::PrintSpace( int depth )
Lee Thomason5cae8972012-01-24 18:03:07 -08001575{
1576 for( int i=0; i<depth; ++i ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001577 Print( " " );
Lee Thomason5cae8972012-01-24 18:03:07 -08001578 }
1579}
1580
1581
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001582void XMLPrinter::PrintString( const char* p, bool restricted )
Lee Thomason857b8682012-01-25 17:50:25 -08001583{
Lee Thomason951d8832012-01-26 08:47:06 -08001584 // Look for runs of bytes between entities to print.
1585 const char* q = p;
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001586 const bool* flag = restricted ? restrictedEntityFlag : entityFlag;
Lee Thomason857b8682012-01-25 17:50:25 -08001587
Lee Thomason6f381b72012-03-02 12:59:39 -08001588 if ( processEntities ) {
1589 while ( *q ) {
1590 // Remember, char is sometimes signed. (How many times has that bitten me?)
1591 if ( *q > 0 && *q < ENTITY_RANGE ) {
1592 // Check for entities. If one is found, flush
1593 // the stream up until the entity, write the
1594 // entity, and keep looking.
Lee Thomason (grinliz)8a0975d2012-03-31 20:09:20 -07001595 if ( flag[(unsigned)(*q)] ) {
Lee Thomason6f381b72012-03-02 12:59:39 -08001596 while ( p < q ) {
1597 Print( "%c", *p );
1598 ++p;
1599 }
1600 for( int i=0; i<NUM_ENTITIES; ++i ) {
1601 if ( entities[i].value == *q ) {
1602 Print( "&%s;", entities[i].pattern );
1603 break;
1604 }
1605 }
Lee Thomason951d8832012-01-26 08:47:06 -08001606 ++p;
1607 }
Lee Thomason951d8832012-01-26 08:47:06 -08001608 }
Lee Thomason6f381b72012-03-02 12:59:39 -08001609 ++q;
Lee Thomason951d8832012-01-26 08:47:06 -08001610 }
Lee Thomason951d8832012-01-26 08:47:06 -08001611 }
1612 // Flush the remaining string. This will be the entire
1613 // string if an entity wasn't found.
Lee Thomason6f381b72012-03-02 12:59:39 -08001614 if ( !processEntities || (q-p > 0) ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001615 Print( "%s", p );
Lee Thomason951d8832012-01-26 08:47:06 -08001616 }
Lee Thomason857b8682012-01-25 17:50:25 -08001617}
1618
U-Stream\Leeae25a442012-02-17 17:48:16 -08001619
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001620void XMLPrinter::PushHeader( bool writeBOM, bool writeDec )
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001621{
1622 static const unsigned char bom[] = { TIXML_UTF_LEAD_0, TIXML_UTF_LEAD_1, TIXML_UTF_LEAD_2, 0 };
1623 if ( writeBOM ) {
1624 Print( "%s", bom );
1625 }
1626 if ( writeDec ) {
1627 PushDeclaration( "xml version=\"1.0\"" );
1628 }
1629}
1630
1631
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001632void XMLPrinter::OpenElement( const char* name )
Lee Thomason5cae8972012-01-24 18:03:07 -08001633{
1634 if ( elementJustOpened ) {
1635 SealElement();
1636 }
Lee Thomason56bdd022012-02-09 18:16:58 -08001637 stack.Push( name );
1638
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001639 if ( textDepth < 0 && !firstElement ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001640 Print( "\n" );
Lee Thomason24767b02012-01-25 17:16:23 -08001641 PrintSpace( depth );
1642 }
Lee Thomason5cae8972012-01-24 18:03:07 -08001643
U-Stream\Leeae25a442012-02-17 17:48:16 -08001644 Print( "<%s", name );
Lee Thomason5cae8972012-01-24 18:03:07 -08001645 elementJustOpened = true;
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001646 firstElement = false;
Lee Thomason5cae8972012-01-24 18:03:07 -08001647 ++depth;
1648}
1649
1650
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001651void XMLPrinter::PushAttribute( const char* name, const char* value )
Lee Thomason5cae8972012-01-24 18:03:07 -08001652{
1653 TIXMLASSERT( elementJustOpened );
U-Stream\Leeae25a442012-02-17 17:48:16 -08001654 Print( " %s=\"", name );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001655 PrintString( value, false );
U-Stream\Leeae25a442012-02-17 17:48:16 -08001656 Print( "\"" );
Lee Thomason5cae8972012-01-24 18:03:07 -08001657}
1658
1659
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001660void XMLPrinter::PushAttribute( const char* name, int v )
1661{
1662 char buf[BUF_SIZE];
Lee Thomason (grinliz)a4a36ba2012-04-06 21:24:29 -07001663 TIXML_SNPRINTF( buf, BUF_SIZE, "%d", v );
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001664 PushAttribute( name, buf );
1665}
1666
1667
1668void XMLPrinter::PushAttribute( const char* name, unsigned v )
1669{
1670 char buf[BUF_SIZE];
Lee Thomason (grinliz)a4a36ba2012-04-06 21:24:29 -07001671 TIXML_SNPRINTF( buf, BUF_SIZE, "%u", v );
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001672 PushAttribute( name, buf );
1673}
1674
1675
1676void XMLPrinter::PushAttribute( const char* name, bool v )
1677{
1678 char buf[BUF_SIZE];
Lee Thomason (grinliz)a4a36ba2012-04-06 21:24:29 -07001679 TIXML_SNPRINTF( buf, BUF_SIZE, "%d", v ? 1 : 0 );
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001680 PushAttribute( name, buf );
1681}
1682
1683
1684void XMLPrinter::PushAttribute( const char* name, double v )
1685{
1686 char buf[BUF_SIZE];
Lee Thomason (grinliz)a4a36ba2012-04-06 21:24:29 -07001687 TIXML_SNPRINTF( buf, BUF_SIZE, "%f", v );
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001688 PushAttribute( name, buf );
1689}
1690
1691
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001692void XMLPrinter::CloseElement()
Lee Thomason5cae8972012-01-24 18:03:07 -08001693{
1694 --depth;
1695 const char* name = stack.Pop();
Lee Thomason5cae8972012-01-24 18:03:07 -08001696
1697 if ( elementJustOpened ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001698 Print( "/>" );
Lee Thomason5cae8972012-01-24 18:03:07 -08001699 }
1700 else {
Lee Thomason56bdd022012-02-09 18:16:58 -08001701 if ( textDepth < 0 ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001702 Print( "\n" );
Lee Thomason24767b02012-01-25 17:16:23 -08001703 PrintSpace( depth );
1704 }
U-Stream\Leeae25a442012-02-17 17:48:16 -08001705 Print( "</%s>", name );
Lee Thomason5cae8972012-01-24 18:03:07 -08001706 }
Lee Thomason56bdd022012-02-09 18:16:58 -08001707
1708 if ( textDepth == depth )
1709 textDepth = -1;
1710 if ( depth == 0 )
U-Stream\Leeae25a442012-02-17 17:48:16 -08001711 Print( "\n" );
Lee Thomason5cae8972012-01-24 18:03:07 -08001712 elementJustOpened = false;
1713}
1714
1715
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001716void XMLPrinter::SealElement()
Lee Thomason5cae8972012-01-24 18:03:07 -08001717{
1718 elementJustOpened = false;
U-Stream\Leeae25a442012-02-17 17:48:16 -08001719 Print( ">" );
Lee Thomason5cae8972012-01-24 18:03:07 -08001720}
1721
1722
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001723void XMLPrinter::PushText( const char* text, bool cdata )
Lee Thomason5cae8972012-01-24 18:03:07 -08001724{
Lee Thomason56bdd022012-02-09 18:16:58 -08001725 textDepth = depth-1;
1726
Lee Thomason5cae8972012-01-24 18:03:07 -08001727 if ( elementJustOpened ) {
1728 SealElement();
1729 }
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001730 if ( cdata ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001731 Print( "<![CDATA[" );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001732 Print( "%s", text );
U-Stream\Leeae25a442012-02-17 17:48:16 -08001733 Print( "]]>" );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001734 }
1735 else {
1736 PrintString( text, true );
1737 }
Lee Thomason5cae8972012-01-24 18:03:07 -08001738}
1739
1740
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001741void XMLPrinter::PushComment( const char* comment )
Lee Thomason5cae8972012-01-24 18:03:07 -08001742{
1743 if ( elementJustOpened ) {
1744 SealElement();
1745 }
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001746 if ( textDepth < 0 && !firstElement ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001747 Print( "\n" );
U-Stream\Lee09a11c52012-02-17 08:31:16 -08001748 PrintSpace( depth );
1749 }
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001750 firstElement = false;
U-Stream\Leeae25a442012-02-17 17:48:16 -08001751 Print( "<!--%s-->", comment );
Lee Thomason5cae8972012-01-24 18:03:07 -08001752}
Lee Thomason751da522012-02-10 08:50:51 -08001753
1754
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001755void XMLPrinter::PushDeclaration( const char* value )
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001756{
1757 if ( elementJustOpened ) {
1758 SealElement();
1759 }
1760 if ( textDepth < 0 && !firstElement) {
1761 Print( "\n" );
1762 PrintSpace( depth );
1763 }
1764 firstElement = false;
1765 Print( "<?%s?>", value );
1766}
1767
1768
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001769void XMLPrinter::PushUnknown( const char* value )
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001770{
1771 if ( elementJustOpened ) {
1772 SealElement();
1773 }
1774 if ( textDepth < 0 && !firstElement ) {
1775 Print( "\n" );
1776 PrintSpace( depth );
1777 }
1778 firstElement = false;
1779 Print( "<!%s>", value );
1780}
1781
1782
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001783bool XMLPrinter::VisitEnter( const XMLDocument& doc )
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001784{
Lee Thomason6f381b72012-03-02 12:59:39 -08001785 processEntities = doc.ProcessEntities();
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001786 if ( doc.HasBOM() ) {
1787 PushHeader( true, false );
1788 }
1789 return true;
1790}
1791
1792
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001793bool XMLPrinter::VisitEnter( const XMLElement& element, const XMLAttribute* attribute )
Lee Thomason751da522012-02-10 08:50:51 -08001794{
1795 OpenElement( element.Name() );
1796 while ( attribute ) {
1797 PushAttribute( attribute->Name(), attribute->Value() );
1798 attribute = attribute->Next();
1799 }
1800 return true;
1801}
1802
1803
Lee Thomason (grinliz)9b093cc2012-02-25 21:30:18 -08001804bool XMLPrinter::VisitExit( const XMLElement& )
Lee Thomason751da522012-02-10 08:50:51 -08001805{
1806 CloseElement();
1807 return true;
1808}
1809
1810
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001811bool XMLPrinter::Visit( const XMLText& text )
Lee Thomason751da522012-02-10 08:50:51 -08001812{
Lee Thomasond6277762012-02-22 16:00:12 -08001813 PushText( text.Value(), text.CData() );
Lee Thomason751da522012-02-10 08:50:51 -08001814 return true;
1815}
1816
1817
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001818bool XMLPrinter::Visit( const XMLComment& comment )
Lee Thomason751da522012-02-10 08:50:51 -08001819{
1820 PushComment( comment.Value() );
1821 return true;
1822}
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001823
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001824bool XMLPrinter::Visit( const XMLDeclaration& declaration )
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001825{
1826 PushDeclaration( declaration.Value() );
1827 return true;
1828}
1829
1830
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001831bool XMLPrinter::Visit( const XMLUnknown& unknown )
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001832{
1833 PushUnknown( unknown.Value() );
1834 return true;
1835}