blob: 4ce9b32d6b54ce44cf42539b22f7c2c2dc1be4ce [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
1095
Lee Thomason1a1d4a72012-02-15 09:09:25 -08001096XMLAttribute* XMLElement::FindOrCreateAttribute( const char* name )
1097{
1098 XMLAttribute* attrib = FindAttribute( name );
1099 if ( !attrib ) {
Lee Thomason (grinliz)9b093cc2012-02-25 21:30:18 -08001100 attrib = new (document->attributePool.Alloc() ) XMLAttribute();
Lee Thomason1a1d4a72012-02-15 09:09:25 -08001101 attrib->memPool = &document->attributePool;
U-Stream\Lee09a11c52012-02-17 08:31:16 -08001102 LinkAttribute( attrib );
1103 attrib->SetName( name );
Lee Thomason1a1d4a72012-02-15 09:09:25 -08001104 }
1105 return attrib;
1106}
1107
1108
U-Stream\Lee09a11c52012-02-17 08:31:16 -08001109void XMLElement::LinkAttribute( XMLAttribute* attrib )
1110{
1111 if ( rootAttribute ) {
1112 XMLAttribute* end = rootAttribute;
1113 while ( end->next )
1114 end = end->next;
1115 end->next = attrib;
1116 }
1117 else {
1118 rootAttribute = attrib;
1119 }
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 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 }
U-Stream\Lee09a11c52012-02-17 08:31:16 -08001165 LinkAttribute( attrib );
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001166 }
Lee Thomasone4422302012-01-20 17:59:50 -08001167 // end of the tag
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001168 else if ( *p == '/' && *(p+1) == '>' ) {
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -08001169 closingType = CLOSED;
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001170 return p+2; // done; sealed element.
1171 }
Lee Thomasone4422302012-01-20 17:59:50 -08001172 // end of the tag
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001173 else if ( *p == '>' ) {
1174 ++p;
1175 break;
1176 }
Lee Thomasone4422302012-01-20 17:59:50 -08001177 else {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -07001178 document->SetError( XML_ERROR_PARSING_ELEMENT, start, p );
Lee Thomasone4422302012-01-20 17:59:50 -08001179 return 0;
1180 }
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001181 }
Lee Thomason67d61312012-01-24 16:01:51 -08001182 return p;
1183}
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001184
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001185
Lee Thomason67d61312012-01-24 16:01:51 -08001186//
1187// <ele></ele>
1188// <ele>foo<b>bar</b></ele>
1189//
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -08001190char* XMLElement::ParseDeep( char* p, StrPair* strPair )
Lee Thomason67d61312012-01-24 16:01:51 -08001191{
1192 // Read the element name.
Lee Thomason56bdd022012-02-09 18:16:58 -08001193 p = XMLUtil::SkipWhiteSpace( p );
Lee Thomason67d61312012-01-24 16:01:51 -08001194 if ( !p ) return 0;
Lee Thomason67d61312012-01-24 16:01:51 -08001195
1196 // The closing element is the </element> form. It is
1197 // parsed just like a regular element then deleted from
1198 // the DOM.
1199 if ( *p == '/' ) {
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -08001200 closingType = CLOSING;
Lee Thomason67d61312012-01-24 16:01:51 -08001201 ++p;
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001202 }
Lee Thomason67d61312012-01-24 16:01:51 -08001203
Lee Thomason56bdd022012-02-09 18:16:58 -08001204 p = value.ParseName( p );
Lee Thomasond1983222012-02-06 08:41:24 -08001205 if ( value.Empty() ) return 0;
Lee Thomason67d61312012-01-24 16:01:51 -08001206
Lee Thomason (grinliz)46a14cf2012-02-23 22:27:28 -08001207 p = ParseAttributes( p );
1208 if ( !p || !*p || closingType )
Lee Thomason67d61312012-01-24 16:01:51 -08001209 return p;
1210
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -08001211 p = XMLNode::ParseDeep( p, strPair );
Lee Thomason67d61312012-01-24 16:01:51 -08001212 return p;
Lee Thomason8a5dfee2012-01-18 17:43:40 -08001213}
1214
1215
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001216
1217XMLNode* XMLElement::ShallowClone( XMLDocument* doc ) const
1218{
1219 if ( !doc ) {
1220 doc = document;
1221 }
1222 XMLElement* element = doc->NewElement( Value() ); // fixme: this will always allocate memory. Intern?
1223 for( const XMLAttribute* a=FirstAttribute(); a; a=a->Next() ) {
1224 element->SetAttribute( a->Name(), a->Value() ); // fixme: this will always allocate memory. Intern?
1225 }
1226 return element;
1227}
1228
1229
1230bool XMLElement::ShallowEqual( const XMLNode* compare ) const
1231{
1232 const XMLElement* other = compare->ToElement();
1233 if ( other && XMLUtil::StringEqual( other->Value(), Value() )) {
1234
1235 const XMLAttribute* a=FirstAttribute();
1236 const XMLAttribute* b=other->FirstAttribute();
1237
1238 while ( a && b ) {
1239 if ( !XMLUtil::StringEqual( a->Value(), b->Value() ) ) {
1240 return false;
1241 }
1242 }
1243 if ( a || b ) {
1244 // different count
1245 return false;
1246 }
1247 return true;
1248 }
1249 return false;
1250}
1251
1252
Lee Thomason751da522012-02-10 08:50:51 -08001253bool XMLElement::Accept( XMLVisitor* visitor ) const
1254{
1255 if ( visitor->VisitEnter( *this, rootAttribute ) )
1256 {
1257 for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() )
1258 {
1259 if ( !node->Accept( visitor ) )
1260 break;
1261 }
1262 }
1263 return visitor->VisitExit( *this );
Lee Thomason751da522012-02-10 08:50:51 -08001264}
Lee Thomason56bdd022012-02-09 18:16:58 -08001265
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001266
Lee Thomason3f57d272012-01-11 15:30:03 -08001267// --------- XMLDocument ----------- //
Lee Thomason6f381b72012-03-02 12:59:39 -08001268XMLDocument::XMLDocument( bool _processEntities ) :
Lee Thomason18d68bd2012-01-26 18:17:26 -08001269 XMLNode( 0 ),
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001270 writeBOM( false ),
Lee Thomason6f381b72012-03-02 12:59:39 -08001271 processEntities( _processEntities ),
1272 errorID( 0 ),
1273 errorStr1( 0 ),
1274 errorStr2( 0 ),
U-Lama\Lee560bd472011-12-28 19:42:49 -08001275 charBuffer( 0 )
1276{
Lee Thomason18d68bd2012-01-26 18:17:26 -08001277 document = this; // avoid warning about 'this' in initializer list
U-Lama\Lee560bd472011-12-28 19:42:49 -08001278}
U-Lama\Leee13c3e62011-12-28 14:36:55 -08001279
1280
Lee Thomason3f57d272012-01-11 15:30:03 -08001281XMLDocument::~XMLDocument()
1282{
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001283 DeleteChildren();
Lee Thomason18d68bd2012-01-26 18:17:26 -08001284 delete [] charBuffer;
Lee Thomasond1983222012-02-06 08:41:24 -08001285
Lee Thomasonec5a7b42012-02-13 18:16:52 -08001286#if 0
Lee Thomason455c9d42012-02-06 09:14:14 -08001287 textPool.Trace( "text" );
1288 elementPool.Trace( "element" );
1289 commentPool.Trace( "comment" );
1290 attributePool.Trace( "attribute" );
Lee Thomasone9ecdab2012-02-13 18:11:20 -08001291#endif
1292
Lee Thomason455c9d42012-02-06 09:14:14 -08001293 TIXMLASSERT( textPool.CurrentAllocs() == 0 );
1294 TIXMLASSERT( elementPool.CurrentAllocs() == 0 );
1295 TIXMLASSERT( commentPool.CurrentAllocs() == 0 );
1296 TIXMLASSERT( attributePool.CurrentAllocs() == 0 );
Lee Thomason3f57d272012-01-11 15:30:03 -08001297}
1298
1299
Lee Thomason18d68bd2012-01-26 18:17:26 -08001300void XMLDocument::InitDocument()
1301{
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001302 errorID = XML_NO_ERROR;
Lee Thomason18d68bd2012-01-26 18:17:26 -08001303 errorStr1 = 0;
1304 errorStr2 = 0;
1305
1306 delete [] charBuffer;
1307 charBuffer = 0;
1308
1309}
1310
Lee Thomason3f57d272012-01-11 15:30:03 -08001311
Lee Thomason2c85a712012-01-31 08:24:24 -08001312XMLElement* XMLDocument::NewElement( const char* name )
1313{
Lee Thomasond1983222012-02-06 08:41:24 -08001314 XMLElement* ele = new (elementPool.Alloc()) XMLElement( this );
1315 ele->memPool = &elementPool;
Lee Thomason2c85a712012-01-31 08:24:24 -08001316 ele->SetName( name );
1317 return ele;
1318}
1319
1320
Lee Thomason1ff38e02012-02-14 18:18:16 -08001321XMLComment* XMLDocument::NewComment( const char* str )
1322{
1323 XMLComment* comment = new (commentPool.Alloc()) XMLComment( this );
1324 comment->memPool = &commentPool;
1325 comment->SetValue( str );
1326 return comment;
1327}
1328
1329
1330XMLText* XMLDocument::NewText( const char* str )
1331{
U-Stream\Lee09a11c52012-02-17 08:31:16 -08001332 XMLText* text = new (textPool.Alloc()) XMLText( this );
1333 text->memPool = &textPool;
1334 text->SetValue( str );
1335 return text;
Lee Thomason1ff38e02012-02-14 18:18:16 -08001336}
1337
1338
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001339XMLDeclaration* XMLDocument::NewDeclaration( const char* str )
1340{
1341 XMLDeclaration* dec = new (commentPool.Alloc()) XMLDeclaration( this );
1342 dec->memPool = &commentPool;
1343 dec->SetValue( str );
1344 return dec;
1345}
1346
1347
1348XMLUnknown* XMLDocument::NewUnknown( const char* str )
1349{
1350 XMLUnknown* unk = new (commentPool.Alloc()) XMLUnknown( this );
1351 unk->memPool = &commentPool;
1352 unk->SetValue( str );
1353 return unk;
1354}
1355
1356
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001357int XMLDocument::LoadFile( const char* filename )
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001358{
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001359 DeleteChildren();
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001360 InitDocument();
1361
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001362#if defined(_MSC_VER)
Lee Thomason (grinliz)5ce4d972012-02-26 21:14:23 -08001363#pragma warning ( push )
1364#pragma warning ( disable : 4996 ) // Fail to see a compelling reason why this should be deprecated.
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001365#endif
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001366 FILE* fp = fopen( filename, "rb" );
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001367#if defined(_MSC_VER)
Lee Thomason (grinliz)5ce4d972012-02-26 21:14:23 -08001368#pragma warning ( pop )
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001369#endif
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001370 if ( !fp ) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -07001371 SetError( XML_ERROR_FILE_NOT_FOUND, filename, 0 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001372 return errorID;
1373 }
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001374 LoadFile( fp );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001375 fclose( fp );
1376 return errorID;
1377}
1378
1379
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001380int XMLDocument::LoadFile( FILE* fp )
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001381{
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001382 DeleteChildren();
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001383 InitDocument();
1384
1385 fseek( fp, 0, SEEK_END );
1386 unsigned size = ftell( fp );
1387 fseek( fp, 0, SEEK_SET );
1388
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001389 if ( size == 0 ) {
1390 return errorID;
1391 }
1392
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001393 charBuffer = new char[size+1];
1394 fread( charBuffer, size, 1, fp );
1395 charBuffer[size] = 0;
1396
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001397 const char* p = charBuffer;
1398 p = XMLUtil::SkipWhiteSpace( p );
1399 p = XMLUtil::ReadBOM( p, &writeBOM );
1400 if ( !p || !*p ) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -07001401 SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
Lee Thomasond6277762012-02-22 16:00:12 -08001402 return errorID;
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001403 }
1404
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -08001405 ParseDeep( charBuffer + (p-charBuffer), 0 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001406 return errorID;
1407}
1408
1409
Ken Miller81da1fb2012-04-09 23:32:26 -05001410int XMLDocument::SaveFile( const char* filename )
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001411{
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001412#if defined(_MSC_VER)
Lee Thomason (grinliz)5ce4d972012-02-26 21:14:23 -08001413#pragma warning ( push )
1414#pragma warning ( disable : 4996 ) // Fail to see a compelling reason why this should be deprecated.
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001415#endif
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001416 FILE* fp = fopen( filename, "w" );
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001417#if defined(_MSC_VER)
Lee Thomason (grinliz)5ce4d972012-02-26 21:14:23 -08001418#pragma warning ( pop )
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001419#endif
Ken Miller81da1fb2012-04-09 23:32:26 -05001420 if ( !fp ) {
Lee Thomason7f7b1622012-03-24 12:49:03 -07001421 SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, filename, 0 );
Ken Miller81da1fb2012-04-09 23:32:26 -05001422 return errorID;
Lee Thomason7f7b1622012-03-24 12:49:03 -07001423 }
Ken Miller81da1fb2012-04-09 23:32:26 -05001424 SaveFile(fp);
1425 fclose( fp );
1426 return errorID;
1427}
1428
1429
1430int XMLDocument::SaveFile( FILE* fp )
1431{
1432 XMLPrinter stream( fp );
1433 Print( &stream );
1434 return errorID;
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001435}
1436
Lee Thomason1ff38e02012-02-14 18:18:16 -08001437
Lee Thomason7c913cd2012-01-26 18:32:34 -08001438int XMLDocument::Parse( const char* p )
Lee Thomason3f57d272012-01-11 15:30:03 -08001439{
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001440 DeleteChildren();
Lee Thomason18d68bd2012-01-26 18:17:26 -08001441 InitDocument();
1442
1443 if ( !p || !*p ) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -07001444 SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
Lee Thomasond6277762012-02-22 16:00:12 -08001445 return errorID;
Lee Thomason18d68bd2012-01-26 18:17:26 -08001446 }
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001447 p = XMLUtil::SkipWhiteSpace( p );
1448 p = XMLUtil::ReadBOM( p, &writeBOM );
1449 if ( !p || !*p ) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -07001450 SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
Lee Thomasond6277762012-02-22 16:00:12 -08001451 return errorID;
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001452 }
1453
Lee Thomason18d68bd2012-01-26 18:17:26 -08001454 size_t len = strlen( p );
1455 charBuffer = new char[ len+1 ];
1456 memcpy( charBuffer, p, len+1 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001457
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001458
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -08001459 ParseDeep( charBuffer, 0 );
Lee Thomason7c913cd2012-01-26 18:32:34 -08001460 return errorID;
Lee Thomason3f57d272012-01-11 15:30:03 -08001461}
1462
1463
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001464void XMLDocument::Print( XMLPrinter* streamer )
Lee Thomason3f57d272012-01-11 15:30:03 -08001465{
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001466 XMLPrinter stdStreamer( stdout );
Lee Thomason5cae8972012-01-24 18:03:07 -08001467 if ( !streamer )
1468 streamer = &stdStreamer;
Lee Thomason751da522012-02-10 08:50:51 -08001469 Accept( streamer );
Lee Thomason3f57d272012-01-11 15:30:03 -08001470}
1471
1472
Lee Thomason67d61312012-01-24 16:01:51 -08001473void XMLDocument::SetError( int error, const char* str1, const char* str2 )
1474{
Lee Thomason18d68bd2012-01-26 18:17:26 -08001475 errorID = error;
Lee Thomason18d68bd2012-01-26 18:17:26 -08001476 errorStr1 = str1;
1477 errorStr2 = str2;
Lee Thomason67d61312012-01-24 16:01:51 -08001478}
1479
Lee Thomason5cae8972012-01-24 18:03:07 -08001480
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001481void XMLDocument::PrintError() const
1482{
1483 if ( errorID ) {
Lee Thomason (grinliz)5ce4d972012-02-26 21:14:23 -08001484 static const int LEN = 20;
1485 char buf1[LEN] = { 0 };
1486 char buf2[LEN] = { 0 };
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001487
1488 if ( errorStr1 ) {
Lee Thomason (grinliz)5ce4d972012-02-26 21:14:23 -08001489 TIXML_SNPRINTF( buf1, LEN, "%s", errorStr1 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001490 }
1491 if ( errorStr2 ) {
Lee Thomason (grinliz)5ce4d972012-02-26 21:14:23 -08001492 TIXML_SNPRINTF( buf2, LEN, "%s", errorStr2 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001493 }
1494
1495 printf( "XMLDocument error id=%d str1=%s str2=%s\n",
1496 errorID, buf1, buf2 );
1497 }
1498}
1499
1500
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001501XMLPrinter::XMLPrinter( FILE* file ) :
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001502 elementJustOpened( false ),
1503 firstElement( true ),
1504 fp( file ),
1505 depth( 0 ),
Lee Thomason6f381b72012-03-02 12:59:39 -08001506 textDepth( -1 ),
1507 processEntities( true )
Lee Thomason5cae8972012-01-24 18:03:07 -08001508{
Lee Thomason857b8682012-01-25 17:50:25 -08001509 for( int i=0; i<ENTITY_RANGE; ++i ) {
1510 entityFlag[i] = false;
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001511 restrictedEntityFlag[i] = false;
Lee Thomason857b8682012-01-25 17:50:25 -08001512 }
1513 for( int i=0; i<NUM_ENTITIES; ++i ) {
1514 TIXMLASSERT( entities[i].value < ENTITY_RANGE );
1515 if ( entities[i].value < ENTITY_RANGE ) {
Lee Thomason (grinliz)9b093cc2012-02-25 21:30:18 -08001516 entityFlag[ (int)entities[i].value ] = true;
Lee Thomason857b8682012-01-25 17:50:25 -08001517 }
1518 }
Lee Thomason (grinliz)9b093cc2012-02-25 21:30:18 -08001519 restrictedEntityFlag[(int)'&'] = true;
1520 restrictedEntityFlag[(int)'<'] = true;
1521 restrictedEntityFlag[(int)'>'] = true; // not required, but consistency is nice
U-Stream\Leeae25a442012-02-17 17:48:16 -08001522 buffer.Push( 0 );
1523}
1524
1525
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001526void XMLPrinter::Print( const char* format, ... )
U-Stream\Leeae25a442012-02-17 17:48:16 -08001527{
1528 va_list va;
1529 va_start( va, format );
1530
1531 if ( fp ) {
1532 vfprintf( fp, format, va );
1533 }
1534 else {
1535 // This seems brutally complex. Haven't figured out a better
1536 // way on windows.
1537 #ifdef _MSC_VER
1538 int len = -1;
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001539 int expand = 1000;
U-Stream\Leeae25a442012-02-17 17:48:16 -08001540 while ( len < 0 ) {
Lee Thomason (grinliz)598c13e2012-04-06 21:18:23 -07001541 len = vsnprintf_s( accumulator.Mem(), accumulator.Capacity(), _TRUNCATE, format, va );
U-Stream\Leeae25a442012-02-17 17:48:16 -08001542 if ( len < 0 ) {
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001543 expand *= 3/2;
Lee Thomason (grinliz)598c13e2012-04-06 21:18:23 -07001544 accumulator.PushArr( expand );
U-Stream\Leeae25a442012-02-17 17:48:16 -08001545 }
1546 }
1547 char* p = buffer.PushArr( len ) - 1;
1548 memcpy( p, accumulator.Mem(), len+1 );
1549 #else
1550 int len = vsnprintf( 0, 0, format, va );
Lee Thomason4de93472012-03-13 17:33:35 -07001551 // Close out and re-start the va-args
1552 va_end( va );
1553 va_start( va, format );
U-Stream\Leeae25a442012-02-17 17:48:16 -08001554 char* p = buffer.PushArr( len ) - 1;
Lee Thomason (grinliz)9b093cc2012-02-25 21:30:18 -08001555 vsnprintf( p, len+1, format, va );
U-Stream\Leeae25a442012-02-17 17:48:16 -08001556 #endif
1557 }
1558 va_end( va );
Lee Thomason5cae8972012-01-24 18:03:07 -08001559}
1560
1561
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001562void XMLPrinter::PrintSpace( int depth )
Lee Thomason5cae8972012-01-24 18:03:07 -08001563{
1564 for( int i=0; i<depth; ++i ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001565 Print( " " );
Lee Thomason5cae8972012-01-24 18:03:07 -08001566 }
1567}
1568
1569
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001570void XMLPrinter::PrintString( const char* p, bool restricted )
Lee Thomason857b8682012-01-25 17:50:25 -08001571{
Lee Thomason951d8832012-01-26 08:47:06 -08001572 // Look for runs of bytes between entities to print.
1573 const char* q = p;
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001574 const bool* flag = restricted ? restrictedEntityFlag : entityFlag;
Lee Thomason857b8682012-01-25 17:50:25 -08001575
Lee Thomason6f381b72012-03-02 12:59:39 -08001576 if ( processEntities ) {
1577 while ( *q ) {
1578 // Remember, char is sometimes signed. (How many times has that bitten me?)
1579 if ( *q > 0 && *q < ENTITY_RANGE ) {
1580 // Check for entities. If one is found, flush
1581 // the stream up until the entity, write the
1582 // entity, and keep looking.
Lee Thomason (grinliz)8a0975d2012-03-31 20:09:20 -07001583 if ( flag[(unsigned)(*q)] ) {
Lee Thomason6f381b72012-03-02 12:59:39 -08001584 while ( p < q ) {
1585 Print( "%c", *p );
1586 ++p;
1587 }
1588 for( int i=0; i<NUM_ENTITIES; ++i ) {
1589 if ( entities[i].value == *q ) {
1590 Print( "&%s;", entities[i].pattern );
1591 break;
1592 }
1593 }
Lee Thomason951d8832012-01-26 08:47:06 -08001594 ++p;
1595 }
Lee Thomason951d8832012-01-26 08:47:06 -08001596 }
Lee Thomason6f381b72012-03-02 12:59:39 -08001597 ++q;
Lee Thomason951d8832012-01-26 08:47:06 -08001598 }
Lee Thomason951d8832012-01-26 08:47:06 -08001599 }
1600 // Flush the remaining string. This will be the entire
1601 // string if an entity wasn't found.
Lee Thomason6f381b72012-03-02 12:59:39 -08001602 if ( !processEntities || (q-p > 0) ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001603 Print( "%s", p );
Lee Thomason951d8832012-01-26 08:47:06 -08001604 }
Lee Thomason857b8682012-01-25 17:50:25 -08001605}
1606
U-Stream\Leeae25a442012-02-17 17:48:16 -08001607
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001608void XMLPrinter::PushHeader( bool writeBOM, bool writeDec )
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001609{
1610 static const unsigned char bom[] = { TIXML_UTF_LEAD_0, TIXML_UTF_LEAD_1, TIXML_UTF_LEAD_2, 0 };
1611 if ( writeBOM ) {
1612 Print( "%s", bom );
1613 }
1614 if ( writeDec ) {
1615 PushDeclaration( "xml version=\"1.0\"" );
1616 }
1617}
1618
1619
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001620void XMLPrinter::OpenElement( const char* name )
Lee Thomason5cae8972012-01-24 18:03:07 -08001621{
1622 if ( elementJustOpened ) {
1623 SealElement();
1624 }
Lee Thomason56bdd022012-02-09 18:16:58 -08001625 stack.Push( name );
1626
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001627 if ( textDepth < 0 && !firstElement ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001628 Print( "\n" );
Lee Thomason24767b02012-01-25 17:16:23 -08001629 PrintSpace( depth );
1630 }
Lee Thomason5cae8972012-01-24 18:03:07 -08001631
U-Stream\Leeae25a442012-02-17 17:48:16 -08001632 Print( "<%s", name );
Lee Thomason5cae8972012-01-24 18:03:07 -08001633 elementJustOpened = true;
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001634 firstElement = false;
Lee Thomason5cae8972012-01-24 18:03:07 -08001635 ++depth;
1636}
1637
1638
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001639void XMLPrinter::PushAttribute( const char* name, const char* value )
Lee Thomason5cae8972012-01-24 18:03:07 -08001640{
1641 TIXMLASSERT( elementJustOpened );
U-Stream\Leeae25a442012-02-17 17:48:16 -08001642 Print( " %s=\"", name );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001643 PrintString( value, false );
U-Stream\Leeae25a442012-02-17 17:48:16 -08001644 Print( "\"" );
Lee Thomason5cae8972012-01-24 18:03:07 -08001645}
1646
1647
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001648void XMLPrinter::PushAttribute( const char* name, int v )
1649{
1650 char buf[BUF_SIZE];
Lee Thomason (grinliz)a4a36ba2012-04-06 21:24:29 -07001651 TIXML_SNPRINTF( buf, BUF_SIZE, "%d", v );
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001652 PushAttribute( name, buf );
1653}
1654
1655
1656void XMLPrinter::PushAttribute( const char* name, unsigned v )
1657{
1658 char buf[BUF_SIZE];
Lee Thomason (grinliz)a4a36ba2012-04-06 21:24:29 -07001659 TIXML_SNPRINTF( buf, BUF_SIZE, "%u", v );
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001660 PushAttribute( name, buf );
1661}
1662
1663
1664void XMLPrinter::PushAttribute( const char* name, bool v )
1665{
1666 char buf[BUF_SIZE];
Lee Thomason (grinliz)a4a36ba2012-04-06 21:24:29 -07001667 TIXML_SNPRINTF( buf, BUF_SIZE, "%d", v ? 1 : 0 );
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001668 PushAttribute( name, buf );
1669}
1670
1671
1672void XMLPrinter::PushAttribute( const char* name, double v )
1673{
1674 char buf[BUF_SIZE];
Lee Thomason (grinliz)a4a36ba2012-04-06 21:24:29 -07001675 TIXML_SNPRINTF( buf, BUF_SIZE, "%f", v );
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001676 PushAttribute( name, buf );
1677}
1678
1679
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001680void XMLPrinter::CloseElement()
Lee Thomason5cae8972012-01-24 18:03:07 -08001681{
1682 --depth;
1683 const char* name = stack.Pop();
Lee Thomason5cae8972012-01-24 18:03:07 -08001684
1685 if ( elementJustOpened ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001686 Print( "/>" );
Lee Thomason5cae8972012-01-24 18:03:07 -08001687 }
1688 else {
Lee Thomason56bdd022012-02-09 18:16:58 -08001689 if ( textDepth < 0 ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001690 Print( "\n" );
Lee Thomason24767b02012-01-25 17:16:23 -08001691 PrintSpace( depth );
1692 }
U-Stream\Leeae25a442012-02-17 17:48:16 -08001693 Print( "</%s>", name );
Lee Thomason5cae8972012-01-24 18:03:07 -08001694 }
Lee Thomason56bdd022012-02-09 18:16:58 -08001695
1696 if ( textDepth == depth )
1697 textDepth = -1;
1698 if ( depth == 0 )
U-Stream\Leeae25a442012-02-17 17:48:16 -08001699 Print( "\n" );
Lee Thomason5cae8972012-01-24 18:03:07 -08001700 elementJustOpened = false;
1701}
1702
1703
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001704void XMLPrinter::SealElement()
Lee Thomason5cae8972012-01-24 18:03:07 -08001705{
1706 elementJustOpened = false;
U-Stream\Leeae25a442012-02-17 17:48:16 -08001707 Print( ">" );
Lee Thomason5cae8972012-01-24 18:03:07 -08001708}
1709
1710
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001711void XMLPrinter::PushText( const char* text, bool cdata )
Lee Thomason5cae8972012-01-24 18:03:07 -08001712{
Lee Thomason56bdd022012-02-09 18:16:58 -08001713 textDepth = depth-1;
1714
Lee Thomason5cae8972012-01-24 18:03:07 -08001715 if ( elementJustOpened ) {
1716 SealElement();
1717 }
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001718 if ( cdata ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001719 Print( "<![CDATA[" );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001720 Print( "%s", text );
U-Stream\Leeae25a442012-02-17 17:48:16 -08001721 Print( "]]>" );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001722 }
1723 else {
1724 PrintString( text, true );
1725 }
Lee Thomason5cae8972012-01-24 18:03:07 -08001726}
1727
1728
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001729void XMLPrinter::PushComment( const char* comment )
Lee Thomason5cae8972012-01-24 18:03:07 -08001730{
1731 if ( elementJustOpened ) {
1732 SealElement();
1733 }
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001734 if ( textDepth < 0 && !firstElement ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001735 Print( "\n" );
U-Stream\Lee09a11c52012-02-17 08:31:16 -08001736 PrintSpace( depth );
1737 }
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001738 firstElement = false;
U-Stream\Leeae25a442012-02-17 17:48:16 -08001739 Print( "<!--%s-->", comment );
Lee Thomason5cae8972012-01-24 18:03:07 -08001740}
Lee Thomason751da522012-02-10 08:50:51 -08001741
1742
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001743void XMLPrinter::PushDeclaration( const char* value )
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001744{
1745 if ( elementJustOpened ) {
1746 SealElement();
1747 }
1748 if ( textDepth < 0 && !firstElement) {
1749 Print( "\n" );
1750 PrintSpace( depth );
1751 }
1752 firstElement = false;
1753 Print( "<?%s?>", value );
1754}
1755
1756
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001757void XMLPrinter::PushUnknown( const char* value )
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001758{
1759 if ( elementJustOpened ) {
1760 SealElement();
1761 }
1762 if ( textDepth < 0 && !firstElement ) {
1763 Print( "\n" );
1764 PrintSpace( depth );
1765 }
1766 firstElement = false;
1767 Print( "<!%s>", value );
1768}
1769
1770
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001771bool XMLPrinter::VisitEnter( const XMLDocument& doc )
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001772{
Lee Thomason6f381b72012-03-02 12:59:39 -08001773 processEntities = doc.ProcessEntities();
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001774 if ( doc.HasBOM() ) {
1775 PushHeader( true, false );
1776 }
1777 return true;
1778}
1779
1780
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001781bool XMLPrinter::VisitEnter( const XMLElement& element, const XMLAttribute* attribute )
Lee Thomason751da522012-02-10 08:50:51 -08001782{
1783 OpenElement( element.Name() );
1784 while ( attribute ) {
1785 PushAttribute( attribute->Name(), attribute->Value() );
1786 attribute = attribute->Next();
1787 }
1788 return true;
1789}
1790
1791
Lee Thomason (grinliz)9b093cc2012-02-25 21:30:18 -08001792bool XMLPrinter::VisitExit( const XMLElement& )
Lee Thomason751da522012-02-10 08:50:51 -08001793{
1794 CloseElement();
1795 return true;
1796}
1797
1798
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001799bool XMLPrinter::Visit( const XMLText& text )
Lee Thomason751da522012-02-10 08:50:51 -08001800{
Lee Thomasond6277762012-02-22 16:00:12 -08001801 PushText( text.Value(), text.CData() );
Lee Thomason751da522012-02-10 08:50:51 -08001802 return true;
1803}
1804
1805
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001806bool XMLPrinter::Visit( const XMLComment& comment )
Lee Thomason751da522012-02-10 08:50:51 -08001807{
1808 PushComment( comment.Value() );
1809 return true;
1810}
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001811
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001812bool XMLPrinter::Visit( const XMLDeclaration& declaration )
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001813{
1814 PushDeclaration( declaration.Value() );
1815 return true;
1816}
1817
1818
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001819bool XMLPrinter::Visit( const XMLUnknown& unknown )
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001820{
1821 PushUnknown( unknown.Value() );
1822 return true;
1823}