blob: f6998eaafb30f8a1fe249e6056db2c95ebabd1a6 [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
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001410void 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
Lee Thomason7f7b1622012-03-24 12:49:03 -07001420 if ( fp ) {
1421 XMLPrinter stream( fp );
1422 Print( &stream );
1423 fclose( fp );
1424 }
1425 else {
1426 SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, filename, 0 );
1427 }
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001428}
1429
Lee Thomason1ff38e02012-02-14 18:18:16 -08001430
Lee Thomason7c913cd2012-01-26 18:32:34 -08001431int XMLDocument::Parse( const char* p )
Lee Thomason3f57d272012-01-11 15:30:03 -08001432{
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001433 DeleteChildren();
Lee Thomason18d68bd2012-01-26 18:17:26 -08001434 InitDocument();
1435
1436 if ( !p || !*p ) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -07001437 SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
Lee Thomasond6277762012-02-22 16:00:12 -08001438 return errorID;
Lee Thomason18d68bd2012-01-26 18:17:26 -08001439 }
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001440 p = XMLUtil::SkipWhiteSpace( p );
1441 p = XMLUtil::ReadBOM( p, &writeBOM );
1442 if ( !p || !*p ) {
Guillermo A. Amaral2eb70032012-03-20 11:26:57 -07001443 SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
Lee Thomasond6277762012-02-22 16:00:12 -08001444 return errorID;
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001445 }
1446
Lee Thomason18d68bd2012-01-26 18:17:26 -08001447 size_t len = strlen( p );
1448 charBuffer = new char[ len+1 ];
1449 memcpy( charBuffer, p, len+1 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001450
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001451
Lee Thomason (grinliz)7468f112012-02-24 08:56:50 -08001452 ParseDeep( charBuffer, 0 );
Lee Thomason7c913cd2012-01-26 18:32:34 -08001453 return errorID;
Lee Thomason3f57d272012-01-11 15:30:03 -08001454}
1455
1456
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001457void XMLDocument::Print( XMLPrinter* streamer )
Lee Thomason3f57d272012-01-11 15:30:03 -08001458{
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001459 XMLPrinter stdStreamer( stdout );
Lee Thomason5cae8972012-01-24 18:03:07 -08001460 if ( !streamer )
1461 streamer = &stdStreamer;
Lee Thomason751da522012-02-10 08:50:51 -08001462 Accept( streamer );
Lee Thomason3f57d272012-01-11 15:30:03 -08001463}
1464
1465
Lee Thomason67d61312012-01-24 16:01:51 -08001466void XMLDocument::SetError( int error, const char* str1, const char* str2 )
1467{
Lee Thomason18d68bd2012-01-26 18:17:26 -08001468 errorID = error;
Lee Thomason18d68bd2012-01-26 18:17:26 -08001469 errorStr1 = str1;
1470 errorStr2 = str2;
Lee Thomason67d61312012-01-24 16:01:51 -08001471}
1472
Lee Thomason5cae8972012-01-24 18:03:07 -08001473
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001474void XMLDocument::PrintError() const
1475{
1476 if ( errorID ) {
Lee Thomason (grinliz)5ce4d972012-02-26 21:14:23 -08001477 static const int LEN = 20;
1478 char buf1[LEN] = { 0 };
1479 char buf2[LEN] = { 0 };
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001480
1481 if ( errorStr1 ) {
Lee Thomason (grinliz)5ce4d972012-02-26 21:14:23 -08001482 TIXML_SNPRINTF( buf1, LEN, "%s", errorStr1 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001483 }
1484 if ( errorStr2 ) {
Lee Thomason (grinliz)5ce4d972012-02-26 21:14:23 -08001485 TIXML_SNPRINTF( buf2, LEN, "%s", errorStr2 );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001486 }
1487
1488 printf( "XMLDocument error id=%d str1=%s str2=%s\n",
1489 errorID, buf1, buf2 );
1490 }
1491}
1492
1493
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001494XMLPrinter::XMLPrinter( FILE* file ) :
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001495 elementJustOpened( false ),
1496 firstElement( true ),
1497 fp( file ),
1498 depth( 0 ),
Lee Thomason6f381b72012-03-02 12:59:39 -08001499 textDepth( -1 ),
1500 processEntities( true )
Lee Thomason5cae8972012-01-24 18:03:07 -08001501{
Lee Thomason857b8682012-01-25 17:50:25 -08001502 for( int i=0; i<ENTITY_RANGE; ++i ) {
1503 entityFlag[i] = false;
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001504 restrictedEntityFlag[i] = false;
Lee Thomason857b8682012-01-25 17:50:25 -08001505 }
1506 for( int i=0; i<NUM_ENTITIES; ++i ) {
1507 TIXMLASSERT( entities[i].value < ENTITY_RANGE );
1508 if ( entities[i].value < ENTITY_RANGE ) {
Lee Thomason (grinliz)9b093cc2012-02-25 21:30:18 -08001509 entityFlag[ (int)entities[i].value ] = true;
Lee Thomason857b8682012-01-25 17:50:25 -08001510 }
1511 }
Lee Thomason (grinliz)9b093cc2012-02-25 21:30:18 -08001512 restrictedEntityFlag[(int)'&'] = true;
1513 restrictedEntityFlag[(int)'<'] = true;
1514 restrictedEntityFlag[(int)'>'] = true; // not required, but consistency is nice
U-Stream\Leeae25a442012-02-17 17:48:16 -08001515 buffer.Push( 0 );
1516}
1517
1518
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001519void XMLPrinter::Print( const char* format, ... )
U-Stream\Leeae25a442012-02-17 17:48:16 -08001520{
1521 va_list va;
1522 va_start( va, format );
1523
1524 if ( fp ) {
1525 vfprintf( fp, format, va );
1526 }
1527 else {
1528 // This seems brutally complex. Haven't figured out a better
1529 // way on windows.
1530 #ifdef _MSC_VER
1531 int len = -1;
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001532 int expand = 1000;
U-Stream\Leeae25a442012-02-17 17:48:16 -08001533 while ( len < 0 ) {
Lee Thomason (grinliz)598c13e2012-04-06 21:18:23 -07001534 len = vsnprintf_s( accumulator.Mem(), accumulator.Capacity(), _TRUNCATE, format, va );
U-Stream\Leeae25a442012-02-17 17:48:16 -08001535 if ( len < 0 ) {
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001536 expand *= 3/2;
Lee Thomason (grinliz)598c13e2012-04-06 21:18:23 -07001537 accumulator.PushArr( expand );
U-Stream\Leeae25a442012-02-17 17:48:16 -08001538 }
1539 }
1540 char* p = buffer.PushArr( len ) - 1;
1541 memcpy( p, accumulator.Mem(), len+1 );
1542 #else
1543 int len = vsnprintf( 0, 0, format, va );
Lee Thomason4de93472012-03-13 17:33:35 -07001544 // Close out and re-start the va-args
1545 va_end( va );
1546 va_start( va, format );
U-Stream\Leeae25a442012-02-17 17:48:16 -08001547 char* p = buffer.PushArr( len ) - 1;
Lee Thomason (grinliz)9b093cc2012-02-25 21:30:18 -08001548 vsnprintf( p, len+1, format, va );
U-Stream\Leeae25a442012-02-17 17:48:16 -08001549 #endif
1550 }
1551 va_end( va );
Lee Thomason5cae8972012-01-24 18:03:07 -08001552}
1553
1554
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001555void XMLPrinter::PrintSpace( int depth )
Lee Thomason5cae8972012-01-24 18:03:07 -08001556{
1557 for( int i=0; i<depth; ++i ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001558 Print( " " );
Lee Thomason5cae8972012-01-24 18:03:07 -08001559 }
1560}
1561
1562
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001563void XMLPrinter::PrintString( const char* p, bool restricted )
Lee Thomason857b8682012-01-25 17:50:25 -08001564{
Lee Thomason951d8832012-01-26 08:47:06 -08001565 // Look for runs of bytes between entities to print.
1566 const char* q = p;
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001567 const bool* flag = restricted ? restrictedEntityFlag : entityFlag;
Lee Thomason857b8682012-01-25 17:50:25 -08001568
Lee Thomason6f381b72012-03-02 12:59:39 -08001569 if ( processEntities ) {
1570 while ( *q ) {
1571 // Remember, char is sometimes signed. (How many times has that bitten me?)
1572 if ( *q > 0 && *q < ENTITY_RANGE ) {
1573 // Check for entities. If one is found, flush
1574 // the stream up until the entity, write the
1575 // entity, and keep looking.
Lee Thomason (grinliz)8a0975d2012-03-31 20:09:20 -07001576 if ( flag[(unsigned)(*q)] ) {
Lee Thomason6f381b72012-03-02 12:59:39 -08001577 while ( p < q ) {
1578 Print( "%c", *p );
1579 ++p;
1580 }
1581 for( int i=0; i<NUM_ENTITIES; ++i ) {
1582 if ( entities[i].value == *q ) {
1583 Print( "&%s;", entities[i].pattern );
1584 break;
1585 }
1586 }
Lee Thomason951d8832012-01-26 08:47:06 -08001587 ++p;
1588 }
Lee Thomason951d8832012-01-26 08:47:06 -08001589 }
Lee Thomason6f381b72012-03-02 12:59:39 -08001590 ++q;
Lee Thomason951d8832012-01-26 08:47:06 -08001591 }
Lee Thomason951d8832012-01-26 08:47:06 -08001592 }
1593 // Flush the remaining string. This will be the entire
1594 // string if an entity wasn't found.
Lee Thomason6f381b72012-03-02 12:59:39 -08001595 if ( !processEntities || (q-p > 0) ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001596 Print( "%s", p );
Lee Thomason951d8832012-01-26 08:47:06 -08001597 }
Lee Thomason857b8682012-01-25 17:50:25 -08001598}
1599
U-Stream\Leeae25a442012-02-17 17:48:16 -08001600
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001601void XMLPrinter::PushHeader( bool writeBOM, bool writeDec )
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001602{
1603 static const unsigned char bom[] = { TIXML_UTF_LEAD_0, TIXML_UTF_LEAD_1, TIXML_UTF_LEAD_2, 0 };
1604 if ( writeBOM ) {
1605 Print( "%s", bom );
1606 }
1607 if ( writeDec ) {
1608 PushDeclaration( "xml version=\"1.0\"" );
1609 }
1610}
1611
1612
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001613void XMLPrinter::OpenElement( const char* name )
Lee Thomason5cae8972012-01-24 18:03:07 -08001614{
1615 if ( elementJustOpened ) {
1616 SealElement();
1617 }
Lee Thomason56bdd022012-02-09 18:16:58 -08001618 stack.Push( name );
1619
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001620 if ( textDepth < 0 && !firstElement ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001621 Print( "\n" );
Lee Thomason24767b02012-01-25 17:16:23 -08001622 PrintSpace( depth );
1623 }
Lee Thomason5cae8972012-01-24 18:03:07 -08001624
U-Stream\Leeae25a442012-02-17 17:48:16 -08001625 Print( "<%s", name );
Lee Thomason5cae8972012-01-24 18:03:07 -08001626 elementJustOpened = true;
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001627 firstElement = false;
Lee Thomason5cae8972012-01-24 18:03:07 -08001628 ++depth;
1629}
1630
1631
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001632void XMLPrinter::PushAttribute( const char* name, const char* value )
Lee Thomason5cae8972012-01-24 18:03:07 -08001633{
1634 TIXMLASSERT( elementJustOpened );
U-Stream\Leeae25a442012-02-17 17:48:16 -08001635 Print( " %s=\"", name );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001636 PrintString( value, false );
U-Stream\Leeae25a442012-02-17 17:48:16 -08001637 Print( "\"" );
Lee Thomason5cae8972012-01-24 18:03:07 -08001638}
1639
1640
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001641void XMLPrinter::PushAttribute( const char* name, int v )
1642{
1643 char buf[BUF_SIZE];
Lee Thomason (grinliz)a4a36ba2012-04-06 21:24:29 -07001644 TIXML_SNPRINTF( buf, BUF_SIZE, "%d", v );
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001645 PushAttribute( name, buf );
1646}
1647
1648
1649void XMLPrinter::PushAttribute( const char* name, unsigned v )
1650{
1651 char buf[BUF_SIZE];
Lee Thomason (grinliz)a4a36ba2012-04-06 21:24:29 -07001652 TIXML_SNPRINTF( buf, BUF_SIZE, "%u", v );
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001653 PushAttribute( name, buf );
1654}
1655
1656
1657void XMLPrinter::PushAttribute( const char* name, bool v )
1658{
1659 char buf[BUF_SIZE];
Lee Thomason (grinliz)a4a36ba2012-04-06 21:24:29 -07001660 TIXML_SNPRINTF( buf, BUF_SIZE, "%d", v ? 1 : 0 );
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001661 PushAttribute( name, buf );
1662}
1663
1664
1665void XMLPrinter::PushAttribute( const char* name, double v )
1666{
1667 char buf[BUF_SIZE];
Lee Thomason (grinliz)a4a36ba2012-04-06 21:24:29 -07001668 TIXML_SNPRINTF( buf, BUF_SIZE, "%f", v );
Lee Thomason7d00b9a2012-02-27 17:54:22 -08001669 PushAttribute( name, buf );
1670}
1671
1672
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001673void XMLPrinter::CloseElement()
Lee Thomason5cae8972012-01-24 18:03:07 -08001674{
1675 --depth;
1676 const char* name = stack.Pop();
Lee Thomason5cae8972012-01-24 18:03:07 -08001677
1678 if ( elementJustOpened ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001679 Print( "/>" );
Lee Thomason5cae8972012-01-24 18:03:07 -08001680 }
1681 else {
Lee Thomason56bdd022012-02-09 18:16:58 -08001682 if ( textDepth < 0 ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001683 Print( "\n" );
Lee Thomason24767b02012-01-25 17:16:23 -08001684 PrintSpace( depth );
1685 }
U-Stream\Leeae25a442012-02-17 17:48:16 -08001686 Print( "</%s>", name );
Lee Thomason5cae8972012-01-24 18:03:07 -08001687 }
Lee Thomason56bdd022012-02-09 18:16:58 -08001688
1689 if ( textDepth == depth )
1690 textDepth = -1;
1691 if ( depth == 0 )
U-Stream\Leeae25a442012-02-17 17:48:16 -08001692 Print( "\n" );
Lee Thomason5cae8972012-01-24 18:03:07 -08001693 elementJustOpened = false;
1694}
1695
1696
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001697void XMLPrinter::SealElement()
Lee Thomason5cae8972012-01-24 18:03:07 -08001698{
1699 elementJustOpened = false;
U-Stream\Leeae25a442012-02-17 17:48:16 -08001700 Print( ">" );
Lee Thomason5cae8972012-01-24 18:03:07 -08001701}
1702
1703
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001704void XMLPrinter::PushText( const char* text, bool cdata )
Lee Thomason5cae8972012-01-24 18:03:07 -08001705{
Lee Thomason56bdd022012-02-09 18:16:58 -08001706 textDepth = depth-1;
1707
Lee Thomason5cae8972012-01-24 18:03:07 -08001708 if ( elementJustOpened ) {
1709 SealElement();
1710 }
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001711 if ( cdata ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001712 Print( "<![CDATA[" );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001713 Print( "%s", text );
U-Stream\Leeae25a442012-02-17 17:48:16 -08001714 Print( "]]>" );
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001715 }
1716 else {
1717 PrintString( text, true );
1718 }
Lee Thomason5cae8972012-01-24 18:03:07 -08001719}
1720
1721
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001722void XMLPrinter::PushComment( const char* comment )
Lee Thomason5cae8972012-01-24 18:03:07 -08001723{
1724 if ( elementJustOpened ) {
1725 SealElement();
1726 }
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001727 if ( textDepth < 0 && !firstElement ) {
U-Stream\Leeae25a442012-02-17 17:48:16 -08001728 Print( "\n" );
U-Stream\Lee09a11c52012-02-17 08:31:16 -08001729 PrintSpace( depth );
1730 }
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001731 firstElement = false;
U-Stream\Leeae25a442012-02-17 17:48:16 -08001732 Print( "<!--%s-->", comment );
Lee Thomason5cae8972012-01-24 18:03:07 -08001733}
Lee Thomason751da522012-02-10 08:50:51 -08001734
1735
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001736void XMLPrinter::PushDeclaration( const char* value )
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001737{
1738 if ( elementJustOpened ) {
1739 SealElement();
1740 }
1741 if ( textDepth < 0 && !firstElement) {
1742 Print( "\n" );
1743 PrintSpace( depth );
1744 }
1745 firstElement = false;
1746 Print( "<?%s?>", value );
1747}
1748
1749
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001750void XMLPrinter::PushUnknown( const char* value )
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001751{
1752 if ( elementJustOpened ) {
1753 SealElement();
1754 }
1755 if ( textDepth < 0 && !firstElement ) {
1756 Print( "\n" );
1757 PrintSpace( depth );
1758 }
1759 firstElement = false;
1760 Print( "<!%s>", value );
1761}
1762
1763
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001764bool XMLPrinter::VisitEnter( const XMLDocument& doc )
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001765{
Lee Thomason6f381b72012-03-02 12:59:39 -08001766 processEntities = doc.ProcessEntities();
Lee Thomason (grinliz)68db57e2012-02-21 09:08:12 -08001767 if ( doc.HasBOM() ) {
1768 PushHeader( true, false );
1769 }
1770 return true;
1771}
1772
1773
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001774bool XMLPrinter::VisitEnter( const XMLElement& element, const XMLAttribute* attribute )
Lee Thomason751da522012-02-10 08:50:51 -08001775{
1776 OpenElement( element.Name() );
1777 while ( attribute ) {
1778 PushAttribute( attribute->Name(), attribute->Value() );
1779 attribute = attribute->Next();
1780 }
1781 return true;
1782}
1783
1784
Lee Thomason (grinliz)9b093cc2012-02-25 21:30:18 -08001785bool XMLPrinter::VisitExit( const XMLElement& )
Lee Thomason751da522012-02-10 08:50:51 -08001786{
1787 CloseElement();
1788 return true;
1789}
1790
1791
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001792bool XMLPrinter::Visit( const XMLText& text )
Lee Thomason751da522012-02-10 08:50:51 -08001793{
Lee Thomasond6277762012-02-22 16:00:12 -08001794 PushText( text.Value(), text.CData() );
Lee Thomason751da522012-02-10 08:50:51 -08001795 return true;
1796}
1797
1798
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001799bool XMLPrinter::Visit( const XMLComment& comment )
Lee Thomason751da522012-02-10 08:50:51 -08001800{
1801 PushComment( comment.Value() );
1802 return true;
1803}
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001804
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001805bool XMLPrinter::Visit( const XMLDeclaration& declaration )
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001806{
1807 PushDeclaration( declaration.Value() );
1808 return true;
1809}
1810
1811
Lee Thomason (grinliz)2a1cd272012-02-24 17:37:53 -08001812bool XMLPrinter::Visit( const XMLUnknown& unknown )
Lee Thomason (grinliz)bd0a8ac2012-02-20 20:14:33 -08001813{
1814 PushUnknown( unknown.Value() );
1815 return true;
1816}