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