diff options
Diffstat (limited to 'tinyxml2.cpp')
-rw-r--r-- | tinyxml2.cpp | 2435 |
1 files changed, 2435 insertions, 0 deletions
diff --git a/tinyxml2.cpp b/tinyxml2.cpp new file mode 100644 index 0000000..382a56e --- /dev/null +++ b/tinyxml2.cpp | |||
@@ -0,0 +1,2435 @@ | |||
1 | /* | ||
2 | Original code by Lee Thomason (www.grinninglizard.com) | ||
3 | |||
4 | This software is provided 'as-is', without any express or implied | ||
5 | warranty. In no event will the authors be held liable for any | ||
6 | damages arising from the use of this software. | ||
7 | |||
8 | Permission is granted to anyone to use this software for any | ||
9 | purpose, including commercial applications, and to alter it and | ||
10 | redistribute it freely, subject to the following restrictions: | ||
11 | |||
12 | 1. The origin of this software must not be misrepresented; you must | ||
13 | not claim that you wrote the original software. If you use this | ||
14 | software in a product, an acknowledgment in the product documentation | ||
15 | would be appreciated but is not required. | ||
16 | |||
17 | 2. Altered source versions must be plainly marked as such, and | ||
18 | must not be misrepresented as being the original software. | ||
19 | |||
20 | 3. This notice may not be removed or altered from any source | ||
21 | distribution. | ||
22 | */ | ||
23 | |||
24 | #include "tinyxml2.h" | ||
25 | |||
26 | #include <new> // yes, this one new style header, is in the Android SDK. | ||
27 | #if defined(ANDROID_NDK) || defined(__QNXNTO__) | ||
28 | # include <stddef.h> | ||
29 | #else | ||
30 | # include <cstddef> | ||
31 | #endif | ||
32 | |||
33 | #if defined(_MSC_VER) && (_MSC_VER >= 1400 ) && (!defined WINCE) | ||
34 | // Microsoft Visual Studio, version 2005 and higher. Not WinCE. | ||
35 | /*int _snprintf_s( | ||
36 | char *buffer, | ||
37 | size_t sizeOfBuffer, | ||
38 | size_t count, | ||
39 | const char *format [, | ||
40 | argument] ... | ||
41 | );*/ | ||
42 | inline int TIXML_SNPRINTF( char* buffer, size_t size, const char* format, ... ) | ||
43 | { | ||
44 | va_list va; | ||
45 | va_start( va, format ); | ||
46 | int result = vsnprintf_s( buffer, size, _TRUNCATE, format, va ); | ||
47 | va_end( va ); | ||
48 | return result; | ||
49 | } | ||
50 | |||
51 | inline int TIXML_VSNPRINTF( char* buffer, size_t size, const char* format, va_list va ) | ||
52 | { | ||
53 | int result = vsnprintf_s( buffer, size, _TRUNCATE, format, va ); | ||
54 | return result; | ||
55 | } | ||
56 | |||
57 | #define TIXML_VSCPRINTF _vscprintf | ||
58 | #define TIXML_SSCANF sscanf_s | ||
59 | #elif defined _MSC_VER | ||
60 | // Microsoft Visual Studio 2003 and earlier or WinCE | ||
61 | #define TIXML_SNPRINTF _snprintf | ||
62 | #define TIXML_VSNPRINTF _vsnprintf | ||
63 | #define TIXML_SSCANF sscanf | ||
64 | #if (_MSC_VER < 1400 ) && (!defined WINCE) | ||
65 | // Microsoft Visual Studio 2003 and not WinCE. | ||
66 | #define TIXML_VSCPRINTF _vscprintf // VS2003's C runtime has this, but VC6 C runtime or WinCE SDK doesn't have. | ||
67 | #else | ||
68 | // Microsoft Visual Studio 2003 and earlier or WinCE. | ||
69 | inline int TIXML_VSCPRINTF( const char* format, va_list va ) | ||
70 | { | ||
71 | int len = 512; | ||
72 | for (;;) { | ||
73 | len = len*2; | ||
74 | char* str = new char[len](); | ||
75 | const int required = _vsnprintf(str, len, format, va); | ||
76 | delete[] str; | ||
77 | if ( required != -1 ) { | ||
78 | len = required; | ||
79 | break; | ||
80 | } | ||
81 | } | ||
82 | return len; | ||
83 | } | ||
84 | #endif | ||
85 | #else | ||
86 | // GCC version 3 and higher | ||
87 | //#warning( "Using sn* functions." ) | ||
88 | #define TIXML_SNPRINTF snprintf | ||
89 | #define TIXML_VSNPRINTF vsnprintf | ||
90 | inline int TIXML_VSCPRINTF( const char* format, va_list va ) | ||
91 | { | ||
92 | int len = vsnprintf( 0, 0, format, va ); | ||
93 | return len; | ||
94 | } | ||
95 | #define TIXML_SSCANF sscanf | ||
96 | #endif | ||
97 | |||
98 | |||
99 | static const char LINE_FEED = (char)0x0a; // all line endings are normalized to LF | ||
100 | static const char LF = LINE_FEED; | ||
101 | static const char CARRIAGE_RETURN = (char)0x0d; // CR gets filtered out | ||
102 | static const char CR = CARRIAGE_RETURN; | ||
103 | static const char SINGLE_QUOTE = '\''; | ||
104 | static const char DOUBLE_QUOTE = '\"'; | ||
105 | |||
106 | // Bunch of unicode info at: | ||
107 | // http://www.unicode.org/faq/utf_bom.html | ||
108 | // ef bb bf (Microsoft "lead bytes") - designates UTF-8 | ||
109 | |||
110 | static const unsigned char TIXML_UTF_LEAD_0 = 0xefU; | ||
111 | static const unsigned char TIXML_UTF_LEAD_1 = 0xbbU; | ||
112 | static const unsigned char TIXML_UTF_LEAD_2 = 0xbfU; | ||
113 | |||
114 | namespace tinyxml2 | ||
115 | { | ||
116 | |||
117 | struct Entity { | ||
118 | const char* pattern; | ||
119 | int length; | ||
120 | char value; | ||
121 | }; | ||
122 | |||
123 | static const int NUM_ENTITIES = 5; | ||
124 | static const Entity entities[NUM_ENTITIES] = { | ||
125 | { "quot", 4, DOUBLE_QUOTE }, | ||
126 | { "amp", 3, '&' }, | ||
127 | { "apos", 4, SINGLE_QUOTE }, | ||
128 | { "lt", 2, '<' }, | ||
129 | { "gt", 2, '>' } | ||
130 | }; | ||
131 | |||
132 | |||
133 | StrPair::~StrPair() | ||
134 | { | ||
135 | Reset(); | ||
136 | } | ||
137 | |||
138 | |||
139 | void StrPair::TransferTo( StrPair* other ) | ||
140 | { | ||
141 | if ( this == other ) { | ||
142 | return; | ||
143 | } | ||
144 | // This in effect implements the assignment operator by "moving" | ||
145 | // ownership (as in auto_ptr). | ||
146 | |||
147 | TIXMLASSERT( other->_flags == 0 ); | ||
148 | TIXMLASSERT( other->_start == 0 ); | ||
149 | TIXMLASSERT( other->_end == 0 ); | ||
150 | |||
151 | other->Reset(); | ||
152 | |||
153 | other->_flags = _flags; | ||
154 | other->_start = _start; | ||
155 | other->_end = _end; | ||
156 | |||
157 | _flags = 0; | ||
158 | _start = 0; | ||
159 | _end = 0; | ||
160 | } | ||
161 | |||
162 | void StrPair::Reset() | ||
163 | { | ||
164 | if ( _flags & NEEDS_DELETE ) { | ||
165 | delete [] _start; | ||
166 | } | ||
167 | _flags = 0; | ||
168 | _start = 0; | ||
169 | _end = 0; | ||
170 | } | ||
171 | |||
172 | |||
173 | void StrPair::SetStr( const char* str, int flags ) | ||
174 | { | ||
175 | Reset(); | ||
176 | size_t len = strlen( str ); | ||
177 | _start = new char[ len+1 ]; | ||
178 | memcpy( _start, str, len+1 ); | ||
179 | _end = _start + len; | ||
180 | _flags = flags | NEEDS_DELETE; | ||
181 | } | ||
182 | |||
183 | |||
184 | char* StrPair::ParseText( char* p, const char* endTag, int strFlags ) | ||
185 | { | ||
186 | TIXMLASSERT( endTag && *endTag ); | ||
187 | |||
188 | char* start = p; | ||
189 | char endChar = *endTag; | ||
190 | size_t length = strlen( endTag ); | ||
191 | |||
192 | // Inner loop of text parsing. | ||
193 | while ( *p ) { | ||
194 | if ( *p == endChar && strncmp( p, endTag, length ) == 0 ) { | ||
195 | Set( start, p, strFlags ); | ||
196 | return p + length; | ||
197 | } | ||
198 | ++p; | ||
199 | } | ||
200 | return 0; | ||
201 | } | ||
202 | |||
203 | |||
204 | char* StrPair::ParseName( char* p ) | ||
205 | { | ||
206 | if ( !p || !(*p) ) { | ||
207 | return 0; | ||
208 | } | ||
209 | if ( !XMLUtil::IsNameStartChar( *p ) ) { | ||
210 | return 0; | ||
211 | } | ||
212 | |||
213 | char* const start = p; | ||
214 | ++p; | ||
215 | while ( *p && XMLUtil::IsNameChar( *p ) ) { | ||
216 | ++p; | ||
217 | } | ||
218 | |||
219 | Set( start, p, 0 ); | ||
220 | return p; | ||
221 | } | ||
222 | |||
223 | |||
224 | void StrPair::CollapseWhitespace() | ||
225 | { | ||
226 | // Adjusting _start would cause undefined behavior on delete[] | ||
227 | TIXMLASSERT( ( _flags & NEEDS_DELETE ) == 0 ); | ||
228 | // Trim leading space. | ||
229 | _start = XMLUtil::SkipWhiteSpace( _start ); | ||
230 | |||
231 | if ( *_start ) { | ||
232 | char* p = _start; // the read pointer | ||
233 | char* q = _start; // the write pointer | ||
234 | |||
235 | while( *p ) { | ||
236 | if ( XMLUtil::IsWhiteSpace( *p )) { | ||
237 | p = XMLUtil::SkipWhiteSpace( p ); | ||
238 | if ( *p == 0 ) { | ||
239 | break; // don't write to q; this trims the trailing space. | ||
240 | } | ||
241 | *q = ' '; | ||
242 | ++q; | ||
243 | } | ||
244 | *q = *p; | ||
245 | ++q; | ||
246 | ++p; | ||
247 | } | ||
248 | *q = 0; | ||
249 | } | ||
250 | } | ||
251 | |||
252 | |||
253 | const char* StrPair::GetStr() | ||
254 | { | ||
255 | TIXMLASSERT( _start ); | ||
256 | TIXMLASSERT( _end ); | ||
257 | if ( _flags & NEEDS_FLUSH ) { | ||
258 | *_end = 0; | ||
259 | _flags ^= NEEDS_FLUSH; | ||
260 | |||
261 | if ( _flags ) { | ||
262 | char* p = _start; // the read pointer | ||
263 | char* q = _start; // the write pointer | ||
264 | |||
265 | while( p < _end ) { | ||
266 | if ( (_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == CR ) { | ||
267 | // CR-LF pair becomes LF | ||
268 | // CR alone becomes LF | ||
269 | // LF-CR becomes LF | ||
270 | if ( *(p+1) == LF ) { | ||
271 | p += 2; | ||
272 | } | ||
273 | else { | ||
274 | ++p; | ||
275 | } | ||
276 | *q++ = LF; | ||
277 | } | ||
278 | else if ( (_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == LF ) { | ||
279 | if ( *(p+1) == CR ) { | ||
280 | p += 2; | ||
281 | } | ||
282 | else { | ||
283 | ++p; | ||
284 | } | ||
285 | *q++ = LF; | ||
286 | } | ||
287 | else if ( (_flags & NEEDS_ENTITY_PROCESSING) && *p == '&' ) { | ||
288 | // Entities handled by tinyXML2: | ||
289 | // - special entities in the entity table [in/out] | ||
290 | // - numeric character reference [in] | ||
291 | // 中 or 中 | ||
292 | |||
293 | if ( *(p+1) == '#' ) { | ||
294 | const int buflen = 10; | ||
295 | char buf[buflen] = { 0 }; | ||
296 | int len = 0; | ||
297 | char* adjusted = const_cast<char*>( XMLUtil::GetCharacterRef( p, buf, &len ) ); | ||
298 | if ( adjusted == 0 ) { | ||
299 | *q = *p; | ||
300 | ++p; | ||
301 | ++q; | ||
302 | } | ||
303 | else { | ||
304 | TIXMLASSERT( 0 <= len && len <= buflen ); | ||
305 | TIXMLASSERT( q + len <= adjusted ); | ||
306 | p = adjusted; | ||
307 | memcpy( q, buf, len ); | ||
308 | q += len; | ||
309 | } | ||
310 | } | ||
311 | else { | ||
312 | bool entityFound = false; | ||
313 | for( int i = 0; i < NUM_ENTITIES; ++i ) { | ||
314 | const Entity& entity = entities[i]; | ||
315 | if ( strncmp( p + 1, entity.pattern, entity.length ) == 0 | ||
316 | && *( p + entity.length + 1 ) == ';' ) { | ||
317 | // Found an entity - convert. | ||
318 | *q = entity.value; | ||
319 | ++q; | ||
320 | p += entity.length + 2; | ||
321 | entityFound = true; | ||
322 | break; | ||
323 | } | ||
324 | } | ||
325 | if ( !entityFound ) { | ||
326 | // fixme: treat as error? | ||
327 | ++p; | ||
328 | ++q; | ||
329 | } | ||
330 | } | ||
331 | } | ||
332 | else { | ||
333 | *q = *p; | ||
334 | ++p; | ||
335 | ++q; | ||
336 | } | ||
337 | } | ||
338 | *q = 0; | ||
339 | } | ||
340 | // The loop below has plenty going on, and this | ||
341 | // is a less useful mode. Break it out. | ||
342 | if ( _flags & NEEDS_WHITESPACE_COLLAPSING ) { | ||
343 | CollapseWhitespace(); | ||
344 | } | ||
345 | _flags = (_flags & NEEDS_DELETE); | ||
346 | } | ||
347 | TIXMLASSERT( _start ); | ||
348 | return _start; | ||
349 | } | ||
350 | |||
351 | |||
352 | |||
353 | |||
354 | // --------- XMLUtil ----------- // | ||
355 | |||
356 | const char* XMLUtil::ReadBOM( const char* p, bool* bom ) | ||
357 | { | ||
358 | TIXMLASSERT( p ); | ||
359 | TIXMLASSERT( bom ); | ||
360 | *bom = false; | ||
361 | const unsigned char* pu = reinterpret_cast<const unsigned char*>(p); | ||
362 | // Check for BOM: | ||
363 | if ( *(pu+0) == TIXML_UTF_LEAD_0 | ||
364 | && *(pu+1) == TIXML_UTF_LEAD_1 | ||
365 | && *(pu+2) == TIXML_UTF_LEAD_2 ) { | ||
366 | *bom = true; | ||
367 | p += 3; | ||
368 | } | ||
369 | TIXMLASSERT( p ); | ||
370 | return p; | ||
371 | } | ||
372 | |||
373 | |||
374 | void XMLUtil::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length ) | ||
375 | { | ||
376 | const unsigned long BYTE_MASK = 0xBF; | ||
377 | const unsigned long BYTE_MARK = 0x80; | ||
378 | const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; | ||
379 | |||
380 | if (input < 0x80) { | ||
381 | *length = 1; | ||
382 | } | ||
383 | else if ( input < 0x800 ) { | ||
384 | *length = 2; | ||
385 | } | ||
386 | else if ( input < 0x10000 ) { | ||
387 | *length = 3; | ||
388 | } | ||
389 | else if ( input < 0x200000 ) { | ||
390 | *length = 4; | ||
391 | } | ||
392 | else { | ||
393 | *length = 0; // This code won't convert this correctly anyway. | ||
394 | return; | ||
395 | } | ||
396 | |||
397 | output += *length; | ||
398 | |||
399 | // Scary scary fall throughs. | ||
400 | switch (*length) { | ||
401 | case 4: | ||
402 | --output; | ||
403 | *output = (char)((input | BYTE_MARK) & BYTE_MASK); | ||
404 | input >>= 6; | ||
405 | case 3: | ||
406 | --output; | ||
407 | *output = (char)((input | BYTE_MARK) & BYTE_MASK); | ||
408 | input >>= 6; | ||
409 | case 2: | ||
410 | --output; | ||
411 | *output = (char)((input | BYTE_MARK) & BYTE_MASK); | ||
412 | input >>= 6; | ||
413 | case 1: | ||
414 | --output; | ||
415 | *output = (char)(input | FIRST_BYTE_MARK[*length]); | ||
416 | break; | ||
417 | default: | ||
418 | TIXMLASSERT( false ); | ||
419 | } | ||
420 | } | ||
421 | |||
422 | |||
423 | const char* XMLUtil::GetCharacterRef( const char* p, char* value, int* length ) | ||
424 | { | ||
425 | // Presume an entity, and pull it out. | ||
426 | *length = 0; | ||
427 | |||
428 | if ( *(p+1) == '#' && *(p+2) ) { | ||
429 | unsigned long ucs = 0; | ||
430 | TIXMLASSERT( sizeof( ucs ) >= 4 ); | ||
431 | ptrdiff_t delta = 0; | ||
432 | unsigned mult = 1; | ||
433 | static const char SEMICOLON = ';'; | ||
434 | |||
435 | if ( *(p+2) == 'x' ) { | ||
436 | // Hexadecimal. | ||
437 | const char* q = p+3; | ||
438 | if ( !(*q) ) { | ||
439 | return 0; | ||
440 | } | ||
441 | |||
442 | q = strchr( q, SEMICOLON ); | ||
443 | |||
444 | if ( !q ) { | ||
445 | return 0; | ||
446 | } | ||
447 | TIXMLASSERT( *q == SEMICOLON ); | ||
448 | |||
449 | delta = q-p; | ||
450 | --q; | ||
451 | |||
452 | while ( *q != 'x' ) { | ||
453 | unsigned int digit = 0; | ||
454 | |||
455 | if ( *q >= '0' && *q <= '9' ) { | ||
456 | digit = *q - '0'; | ||
457 | } | ||
458 | else if ( *q >= 'a' && *q <= 'f' ) { | ||
459 | digit = *q - 'a' + 10; | ||
460 | } | ||
461 | else if ( *q >= 'A' && *q <= 'F' ) { | ||
462 | digit = *q - 'A' + 10; | ||
463 | } | ||
464 | else { | ||
465 | return 0; | ||
466 | } | ||
467 | TIXMLASSERT( digit >= 0 && digit < 16); | ||
468 | TIXMLASSERT( digit == 0 || mult <= UINT_MAX / digit ); | ||
469 | const unsigned int digitScaled = mult * digit; | ||
470 | TIXMLASSERT( ucs <= ULONG_MAX - digitScaled ); | ||
471 | ucs += digitScaled; | ||
472 | TIXMLASSERT( mult <= UINT_MAX / 16 ); | ||
473 | mult *= 16; | ||
474 | --q; | ||
475 | } | ||
476 | } | ||
477 | else { | ||
478 | // Decimal. | ||
479 | const char* q = p+2; | ||
480 | if ( !(*q) ) { | ||
481 | return 0; | ||
482 | } | ||
483 | |||
484 | q = strchr( q, SEMICOLON ); | ||
485 | |||
486 | if ( !q ) { | ||
487 | return 0; | ||
488 | } | ||
489 | TIXMLASSERT( *q == SEMICOLON ); | ||
490 | |||
491 | delta = q-p; | ||
492 | --q; | ||
493 | |||
494 | while ( *q != '#' ) { | ||
495 | if ( *q >= '0' && *q <= '9' ) { | ||
496 | const unsigned int digit = *q - '0'; | ||
497 | TIXMLASSERT( digit >= 0 && digit < 10); | ||
498 | TIXMLASSERT( digit == 0 || mult <= UINT_MAX / digit ); | ||
499 | const unsigned int digitScaled = mult * digit; | ||
500 | TIXMLASSERT( ucs <= ULONG_MAX - digitScaled ); | ||
501 | ucs += digitScaled; | ||
502 | } | ||
503 | else { | ||
504 | return 0; | ||
505 | } | ||
506 | TIXMLASSERT( mult <= UINT_MAX / 10 ); | ||
507 | mult *= 10; | ||
508 | --q; | ||
509 | } | ||
510 | } | ||
511 | // convert the UCS to UTF-8 | ||
512 | ConvertUTF32ToUTF8( ucs, value, length ); | ||
513 | return p + delta + 1; | ||
514 | } | ||
515 | return p+1; | ||
516 | } | ||
517 | |||
518 | |||
519 | void XMLUtil::ToStr( int v, char* buffer, int bufferSize ) | ||
520 | { | ||
521 | TIXML_SNPRINTF( buffer, bufferSize, "%d", v ); | ||
522 | } | ||
523 | |||
524 | |||
525 | void XMLUtil::ToStr( unsigned v, char* buffer, int bufferSize ) | ||
526 | { | ||
527 | TIXML_SNPRINTF( buffer, bufferSize, "%u", v ); | ||
528 | } | ||
529 | |||
530 | |||
531 | void XMLUtil::ToStr( bool v, char* buffer, int bufferSize ) | ||
532 | { | ||
533 | TIXML_SNPRINTF( buffer, bufferSize, "%d", v ? 1 : 0 ); | ||
534 | } | ||
535 | |||
536 | /* | ||
537 | ToStr() of a number is a very tricky topic. | ||
538 | https://github.com/leethomason/tinyxml2/issues/106 | ||
539 | */ | ||
540 | void XMLUtil::ToStr( float v, char* buffer, int bufferSize ) | ||
541 | { | ||
542 | TIXML_SNPRINTF( buffer, bufferSize, "%.8g", v ); | ||
543 | } | ||
544 | |||
545 | |||
546 | void XMLUtil::ToStr( double v, char* buffer, int bufferSize ) | ||
547 | { | ||
548 | TIXML_SNPRINTF( buffer, bufferSize, "%.17g", v ); | ||
549 | } | ||
550 | |||
551 | |||
552 | bool XMLUtil::ToInt( const char* str, int* value ) | ||
553 | { | ||
554 | if ( TIXML_SSCANF( str, "%d", value ) == 1 ) { | ||
555 | return true; | ||
556 | } | ||
557 | return false; | ||
558 | } | ||
559 | |||
560 | bool XMLUtil::ToUnsigned( const char* str, unsigned *value ) | ||
561 | { | ||
562 | if ( TIXML_SSCANF( str, "%u", value ) == 1 ) { | ||
563 | return true; | ||
564 | } | ||
565 | return false; | ||
566 | } | ||
567 | |||
568 | bool XMLUtil::ToBool( const char* str, bool* value ) | ||
569 | { | ||
570 | int ival = 0; | ||
571 | if ( ToInt( str, &ival )) { | ||
572 | *value = (ival==0) ? false : true; | ||
573 | return true; | ||
574 | } | ||
575 | if ( StringEqual( str, "true" ) ) { | ||
576 | *value = true; | ||
577 | return true; | ||
578 | } | ||
579 | else if ( StringEqual( str, "false" ) ) { | ||
580 | *value = false; | ||
581 | return true; | ||
582 | } | ||
583 | return false; | ||
584 | } | ||
585 | |||
586 | |||
587 | bool XMLUtil::ToFloat( const char* str, float* value ) | ||
588 | { | ||
589 | if ( TIXML_SSCANF( str, "%f", value ) == 1 ) { | ||
590 | return true; | ||
591 | } | ||
592 | return false; | ||
593 | } | ||
594 | |||
595 | bool XMLUtil::ToDouble( const char* str, double* value ) | ||
596 | { | ||
597 | if ( TIXML_SSCANF( str, "%lf", value ) == 1 ) { | ||
598 | return true; | ||
599 | } | ||
600 | return false; | ||
601 | } | ||
602 | |||
603 | |||
604 | char* XMLDocument::Identify( char* p, XMLNode** node ) | ||
605 | { | ||
606 | TIXMLASSERT( node ); | ||
607 | TIXMLASSERT( p ); | ||
608 | char* const start = p; | ||
609 | p = XMLUtil::SkipWhiteSpace( p ); | ||
610 | if( !*p ) { | ||
611 | *node = 0; | ||
612 | TIXMLASSERT( p ); | ||
613 | return p; | ||
614 | } | ||
615 | |||
616 | // These strings define the matching patterns: | ||
617 | static const char* xmlHeader = { "<?" }; | ||
618 | static const char* commentHeader = { "<!--" }; | ||
619 | static const char* cdataHeader = { "<![CDATA[" }; | ||
620 | static const char* dtdHeader = { "<!" }; | ||
621 | static const char* elementHeader = { "<" }; // and a header for everything else; check last. | ||
622 | |||
623 | static const int xmlHeaderLen = 2; | ||
624 | static const int commentHeaderLen = 4; | ||
625 | static const int cdataHeaderLen = 9; | ||
626 | static const int dtdHeaderLen = 2; | ||
627 | static const int elementHeaderLen = 1; | ||
628 | |||
629 | TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLUnknown ) ); // use same memory pool | ||
630 | TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLDeclaration ) ); // use same memory pool | ||
631 | XMLNode* returnNode = 0; | ||
632 | if ( XMLUtil::StringEqual( p, xmlHeader, xmlHeaderLen ) ) { | ||
633 | TIXMLASSERT( sizeof( XMLDeclaration ) == _commentPool.ItemSize() ); | ||
634 | returnNode = new (_commentPool.Alloc()) XMLDeclaration( this ); | ||
635 | returnNode->_memPool = &_commentPool; | ||
636 | p += xmlHeaderLen; | ||
637 | } | ||
638 | else if ( XMLUtil::StringEqual( p, commentHeader, commentHeaderLen ) ) { | ||
639 | TIXMLASSERT( sizeof( XMLComment ) == _commentPool.ItemSize() ); | ||
640 | returnNode = new (_commentPool.Alloc()) XMLComment( this ); | ||
641 | returnNode->_memPool = &_commentPool; | ||
642 | p += commentHeaderLen; | ||
643 | } | ||
644 | else if ( XMLUtil::StringEqual( p, cdataHeader, cdataHeaderLen ) ) { | ||
645 | TIXMLASSERT( sizeof( XMLText ) == _textPool.ItemSize() ); | ||
646 | XMLText* text = new (_textPool.Alloc()) XMLText( this ); | ||
647 | returnNode = text; | ||
648 | returnNode->_memPool = &_textPool; | ||
649 | p += cdataHeaderLen; | ||
650 | text->SetCData( true ); | ||
651 | } | ||
652 | else if ( XMLUtil::StringEqual( p, dtdHeader, dtdHeaderLen ) ) { | ||
653 | TIXMLASSERT( sizeof( XMLUnknown ) == _commentPool.ItemSize() ); | ||
654 | returnNode = new (_commentPool.Alloc()) XMLUnknown( this ); | ||
655 | returnNode->_memPool = &_commentPool; | ||
656 | p += dtdHeaderLen; | ||
657 | } | ||
658 | else if ( XMLUtil::StringEqual( p, elementHeader, elementHeaderLen ) ) { | ||
659 | TIXMLASSERT( sizeof( XMLElement ) == _elementPool.ItemSize() ); | ||
660 | returnNode = new (_elementPool.Alloc()) XMLElement( this ); | ||
661 | returnNode->_memPool = &_elementPool; | ||
662 | p += elementHeaderLen; | ||
663 | } | ||
664 | else { | ||
665 | TIXMLASSERT( sizeof( XMLText ) == _textPool.ItemSize() ); | ||
666 | returnNode = new (_textPool.Alloc()) XMLText( this ); | ||
667 | returnNode->_memPool = &_textPool; | ||
668 | p = start; // Back it up, all the text counts. | ||
669 | } | ||
670 | |||
671 | TIXMLASSERT( returnNode ); | ||
672 | TIXMLASSERT( p ); | ||
673 | *node = returnNode; | ||
674 | return p; | ||
675 | } | ||
676 | |||
677 | |||
678 | bool XMLDocument::Accept( XMLVisitor* visitor ) const | ||
679 | { | ||
680 | TIXMLASSERT( visitor ); | ||
681 | if ( visitor->VisitEnter( *this ) ) { | ||
682 | for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) { | ||
683 | if ( !node->Accept( visitor ) ) { | ||
684 | break; | ||
685 | } | ||
686 | } | ||
687 | } | ||
688 | return visitor->VisitExit( *this ); | ||
689 | } | ||
690 | |||
691 | |||
692 | // --------- XMLNode ----------- // | ||
693 | |||
694 | XMLNode::XMLNode( XMLDocument* doc ) : | ||
695 | _document( doc ), | ||
696 | _parent( 0 ), | ||
697 | _firstChild( 0 ), _lastChild( 0 ), | ||
698 | _prev( 0 ), _next( 0 ), | ||
699 | _memPool( 0 ) | ||
700 | { | ||
701 | } | ||
702 | |||
703 | |||
704 | XMLNode::~XMLNode() | ||
705 | { | ||
706 | DeleteChildren(); | ||
707 | if ( _parent ) { | ||
708 | _parent->Unlink( this ); | ||
709 | } | ||
710 | } | ||
711 | |||
712 | const char* XMLNode::Value() const | ||
713 | { | ||
714 | // Catch an edge case: XMLDocuments don't have a a Value. Carefully return nullptr. | ||
715 | if ( this->ToDocument() ) | ||
716 | return 0; | ||
717 | return _value.GetStr(); | ||
718 | } | ||
719 | |||
720 | void XMLNode::SetValue( const char* str, bool staticMem ) | ||
721 | { | ||
722 | if ( staticMem ) { | ||
723 | _value.SetInternedStr( str ); | ||
724 | } | ||
725 | else { | ||
726 | _value.SetStr( str ); | ||
727 | } | ||
728 | } | ||
729 | |||
730 | |||
731 | void XMLNode::DeleteChildren() | ||
732 | { | ||
733 | while( _firstChild ) { | ||
734 | TIXMLASSERT( _lastChild ); | ||
735 | TIXMLASSERT( _firstChild->_document == _document ); | ||
736 | XMLNode* node = _firstChild; | ||
737 | Unlink( node ); | ||
738 | |||
739 | DeleteNode( node ); | ||
740 | } | ||
741 | _firstChild = _lastChild = 0; | ||
742 | } | ||
743 | |||
744 | |||
745 | void XMLNode::Unlink( XMLNode* child ) | ||
746 | { | ||
747 | TIXMLASSERT( child ); | ||
748 | TIXMLASSERT( child->_document == _document ); | ||
749 | TIXMLASSERT( child->_parent == this ); | ||
750 | if ( child == _firstChild ) { | ||
751 | _firstChild = _firstChild->_next; | ||
752 | } | ||
753 | if ( child == _lastChild ) { | ||
754 | _lastChild = _lastChild->_prev; | ||
755 | } | ||
756 | |||
757 | if ( child->_prev ) { | ||
758 | child->_prev->_next = child->_next; | ||
759 | } | ||
760 | if ( child->_next ) { | ||
761 | child->_next->_prev = child->_prev; | ||
762 | } | ||
763 | child->_parent = 0; | ||
764 | } | ||
765 | |||
766 | |||
767 | void XMLNode::DeleteChild( XMLNode* node ) | ||
768 | { | ||
769 | TIXMLASSERT( node ); | ||
770 | TIXMLASSERT( node->_document == _document ); | ||
771 | TIXMLASSERT( node->_parent == this ); | ||
772 | DeleteNode( node ); | ||
773 | } | ||
774 | |||
775 | |||
776 | XMLNode* XMLNode::InsertEndChild( XMLNode* addThis ) | ||
777 | { | ||
778 | TIXMLASSERT( addThis ); | ||
779 | if ( addThis->_document != _document ) { | ||
780 | TIXMLASSERT( false ); | ||
781 | return 0; | ||
782 | } | ||
783 | InsertChildPreamble( addThis ); | ||
784 | |||
785 | if ( _lastChild ) { | ||
786 | TIXMLASSERT( _firstChild ); | ||
787 | TIXMLASSERT( _lastChild->_next == 0 ); | ||
788 | _lastChild->_next = addThis; | ||
789 | addThis->_prev = _lastChild; | ||
790 | _lastChild = addThis; | ||
791 | |||
792 | addThis->_next = 0; | ||
793 | } | ||
794 | else { | ||
795 | TIXMLASSERT( _firstChild == 0 ); | ||
796 | _firstChild = _lastChild = addThis; | ||
797 | |||
798 | addThis->_prev = 0; | ||
799 | addThis->_next = 0; | ||
800 | } | ||
801 | addThis->_parent = this; | ||
802 | return addThis; | ||
803 | } | ||
804 | |||
805 | |||
806 | XMLNode* XMLNode::InsertFirstChild( XMLNode* addThis ) | ||
807 | { | ||
808 | TIXMLASSERT( addThis ); | ||
809 | if ( addThis->_document != _document ) { | ||
810 | TIXMLASSERT( false ); | ||
811 | return 0; | ||
812 | } | ||
813 | InsertChildPreamble( addThis ); | ||
814 | |||
815 | if ( _firstChild ) { | ||
816 | TIXMLASSERT( _lastChild ); | ||
817 | TIXMLASSERT( _firstChild->_prev == 0 ); | ||
818 | |||
819 | _firstChild->_prev = addThis; | ||
820 | addThis->_next = _firstChild; | ||
821 | _firstChild = addThis; | ||
822 | |||
823 | addThis->_prev = 0; | ||
824 | } | ||
825 | else { | ||
826 | TIXMLASSERT( _lastChild == 0 ); | ||
827 | _firstChild = _lastChild = addThis; | ||
828 | |||
829 | addThis->_prev = 0; | ||
830 | addThis->_next = 0; | ||
831 | } | ||
832 | addThis->_parent = this; | ||
833 | return addThis; | ||
834 | } | ||
835 | |||
836 | |||
837 | XMLNode* XMLNode::InsertAfterChild( XMLNode* afterThis, XMLNode* addThis ) | ||
838 | { | ||
839 | TIXMLASSERT( addThis ); | ||
840 | if ( addThis->_document != _document ) { | ||
841 | TIXMLASSERT( false ); | ||
842 | return 0; | ||
843 | } | ||
844 | |||
845 | TIXMLASSERT( afterThis ); | ||
846 | |||
847 | if ( afterThis->_parent != this ) { | ||
848 | TIXMLASSERT( false ); | ||
849 | return 0; | ||
850 | } | ||
851 | |||
852 | if ( afterThis->_next == 0 ) { | ||
853 | // The last node or the only node. | ||
854 | return InsertEndChild( addThis ); | ||
855 | } | ||
856 | InsertChildPreamble( addThis ); | ||
857 | addThis->_prev = afterThis; | ||
858 | addThis->_next = afterThis->_next; | ||
859 | afterThis->_next->_prev = addThis; | ||
860 | afterThis->_next = addThis; | ||
861 | addThis->_parent = this; | ||
862 | return addThis; | ||
863 | } | ||
864 | |||
865 | |||
866 | |||
867 | |||
868 | const XMLElement* XMLNode::FirstChildElement( const char* name ) const | ||
869 | { | ||
870 | for( const XMLNode* node = _firstChild; node; node = node->_next ) { | ||
871 | const XMLElement* element = node->ToElement(); | ||
872 | if ( element ) { | ||
873 | if ( !name || XMLUtil::StringEqual( element->Name(), name ) ) { | ||
874 | return element; | ||
875 | } | ||
876 | } | ||
877 | } | ||
878 | return 0; | ||
879 | } | ||
880 | |||
881 | |||
882 | const XMLElement* XMLNode::LastChildElement( const char* name ) const | ||
883 | { | ||
884 | for( const XMLNode* node = _lastChild; node; node = node->_prev ) { | ||
885 | const XMLElement* element = node->ToElement(); | ||
886 | if ( element ) { | ||
887 | if ( !name || XMLUtil::StringEqual( element->Name(), name ) ) { | ||
888 | return element; | ||
889 | } | ||
890 | } | ||
891 | } | ||
892 | return 0; | ||
893 | } | ||
894 | |||
895 | |||
896 | const XMLElement* XMLNode::NextSiblingElement( const char* name ) const | ||
897 | { | ||
898 | for( const XMLNode* node = _next; node; node = node->_next ) { | ||
899 | const XMLElement* element = node->ToElement(); | ||
900 | if ( element | ||
901 | && (!name || XMLUtil::StringEqual( name, element->Name() ))) { | ||
902 | return element; | ||
903 | } | ||
904 | } | ||
905 | return 0; | ||
906 | } | ||
907 | |||
908 | |||
909 | const XMLElement* XMLNode::PreviousSiblingElement( const char* name ) const | ||
910 | { | ||
911 | for( const XMLNode* node = _prev; node; node = node->_prev ) { | ||
912 | const XMLElement* element = node->ToElement(); | ||
913 | if ( element | ||
914 | && (!name || XMLUtil::StringEqual( name, element->Name() ))) { | ||
915 | return element; | ||
916 | } | ||
917 | } | ||
918 | return 0; | ||
919 | } | ||
920 | |||
921 | |||
922 | char* XMLNode::ParseDeep( char* p, StrPair* parentEnd ) | ||
923 | { | ||
924 | // This is a recursive method, but thinking about it "at the current level" | ||
925 | // it is a pretty simple flat list: | ||
926 | // <foo/> | ||
927 | // <!-- comment --> | ||
928 | // | ||
929 | // With a special case: | ||
930 | // <foo> | ||
931 | // </foo> | ||
932 | // <!-- comment --> | ||
933 | // | ||
934 | // Where the closing element (/foo) *must* be the next thing after the opening | ||
935 | // element, and the names must match. BUT the tricky bit is that the closing | ||
936 | // element will be read by the child. | ||
937 | // | ||
938 | // 'endTag' is the end tag for this node, it is returned by a call to a child. | ||
939 | // 'parentEnd' is the end tag for the parent, which is filled in and returned. | ||
940 | |||
941 | while( p && *p ) { | ||
942 | XMLNode* node = 0; | ||
943 | |||
944 | p = _document->Identify( p, &node ); | ||
945 | if ( node == 0 ) { | ||
946 | break; | ||
947 | } | ||
948 | |||
949 | StrPair endTag; | ||
950 | p = node->ParseDeep( p, &endTag ); | ||
951 | if ( !p ) { | ||
952 | DeleteNode( node ); | ||
953 | if ( !_document->Error() ) { | ||
954 | _document->SetError( XML_ERROR_PARSING, 0, 0 ); | ||
955 | } | ||
956 | break; | ||
957 | } | ||
958 | |||
959 | XMLDeclaration* decl = node->ToDeclaration(); | ||
960 | if ( decl ) { | ||
961 | // A declaration can only be the first child of a document. | ||
962 | // Set error, if document already has children. | ||
963 | if ( !_document->NoChildren() ) { | ||
964 | _document->SetError( XML_ERROR_PARSING_DECLARATION, decl->Value(), 0); | ||
965 | DeleteNode( decl ); | ||
966 | break; | ||
967 | } | ||
968 | } | ||
969 | |||
970 | XMLElement* ele = node->ToElement(); | ||
971 | if ( ele ) { | ||
972 | // We read the end tag. Return it to the parent. | ||
973 | if ( ele->ClosingType() == XMLElement::CLOSING ) { | ||
974 | if ( parentEnd ) { | ||
975 | ele->_value.TransferTo( parentEnd ); | ||
976 | } | ||
977 | node->_memPool->SetTracked(); // created and then immediately deleted. | ||
978 | DeleteNode( node ); | ||
979 | return p; | ||
980 | } | ||
981 | |||
982 | // Handle an end tag returned to this level. | ||
983 | // And handle a bunch of annoying errors. | ||
984 | bool mismatch = false; | ||
985 | if ( endTag.Empty() ) { | ||
986 | if ( ele->ClosingType() == XMLElement::OPEN ) { | ||
987 | mismatch = true; | ||
988 | } | ||
989 | } | ||
990 | else { | ||
991 | if ( ele->ClosingType() != XMLElement::OPEN ) { | ||
992 | mismatch = true; | ||
993 | } | ||
994 | else if ( !XMLUtil::StringEqual( endTag.GetStr(), ele->Name() ) ) { | ||
995 | mismatch = true; | ||
996 | } | ||
997 | } | ||
998 | if ( mismatch ) { | ||
999 | _document->SetError( XML_ERROR_MISMATCHED_ELEMENT, ele->Name(), 0 ); | ||
1000 | DeleteNode( node ); | ||
1001 | break; | ||
1002 | } | ||
1003 | } | ||
1004 | InsertEndChild( node ); | ||
1005 | } | ||
1006 | return 0; | ||
1007 | } | ||
1008 | |||
1009 | void XMLNode::DeleteNode( XMLNode* node ) | ||
1010 | { | ||
1011 | if ( node == 0 ) { | ||
1012 | return; | ||
1013 | } | ||
1014 | MemPool* pool = node->_memPool; | ||
1015 | node->~XMLNode(); | ||
1016 | pool->Free( node ); | ||
1017 | } | ||
1018 | |||
1019 | void XMLNode::InsertChildPreamble( XMLNode* insertThis ) const | ||
1020 | { | ||
1021 | TIXMLASSERT( insertThis ); | ||
1022 | TIXMLASSERT( insertThis->_document == _document ); | ||
1023 | |||
1024 | if ( insertThis->_parent ) | ||
1025 | insertThis->_parent->Unlink( insertThis ); | ||
1026 | else | ||
1027 | insertThis->_memPool->SetTracked(); | ||
1028 | } | ||
1029 | |||
1030 | // --------- XMLText ---------- // | ||
1031 | char* XMLText::ParseDeep( char* p, StrPair* ) | ||
1032 | { | ||
1033 | const char* start = p; | ||
1034 | if ( this->CData() ) { | ||
1035 | p = _value.ParseText( p, "]]>", StrPair::NEEDS_NEWLINE_NORMALIZATION ); | ||
1036 | if ( !p ) { | ||
1037 | _document->SetError( XML_ERROR_PARSING_CDATA, start, 0 ); | ||
1038 | } | ||
1039 | return p; | ||
1040 | } | ||
1041 | else { | ||
1042 | int flags = _document->ProcessEntities() ? StrPair::TEXT_ELEMENT : StrPair::TEXT_ELEMENT_LEAVE_ENTITIES; | ||
1043 | if ( _document->WhitespaceMode() == COLLAPSE_WHITESPACE ) { | ||
1044 | flags |= StrPair::NEEDS_WHITESPACE_COLLAPSING; | ||
1045 | } | ||
1046 | |||
1047 | p = _value.ParseText( p, "<", flags ); | ||
1048 | if ( p && *p ) { | ||
1049 | return p-1; | ||
1050 | } | ||
1051 | if ( !p ) { | ||
1052 | _document->SetError( XML_ERROR_PARSING_TEXT, start, 0 ); | ||
1053 | } | ||
1054 | } | ||
1055 | return 0; | ||
1056 | } | ||
1057 | |||
1058 | |||
1059 | XMLNode* XMLText::ShallowClone( XMLDocument* doc ) const | ||
1060 | { | ||
1061 | if ( !doc ) { | ||
1062 | doc = _document; | ||
1063 | } | ||
1064 | XMLText* text = doc->NewText( Value() ); // fixme: this will always allocate memory. Intern? | ||
1065 | text->SetCData( this->CData() ); | ||
1066 | return text; | ||
1067 | } | ||
1068 | |||
1069 | |||
1070 | bool XMLText::ShallowEqual( const XMLNode* compare ) const | ||
1071 | { | ||
1072 | const XMLText* text = compare->ToText(); | ||
1073 | return ( text && XMLUtil::StringEqual( text->Value(), Value() ) ); | ||
1074 | } | ||
1075 | |||
1076 | |||
1077 | bool XMLText::Accept( XMLVisitor* visitor ) const | ||
1078 | { | ||
1079 | TIXMLASSERT( visitor ); | ||
1080 | return visitor->Visit( *this ); | ||
1081 | } | ||
1082 | |||
1083 | |||
1084 | // --------- XMLComment ---------- // | ||
1085 | |||
1086 | XMLComment::XMLComment( XMLDocument* doc ) : XMLNode( doc ) | ||
1087 | { | ||
1088 | } | ||
1089 | |||
1090 | |||
1091 | XMLComment::~XMLComment() | ||
1092 | { | ||
1093 | } | ||
1094 | |||
1095 | |||
1096 | char* XMLComment::ParseDeep( char* p, StrPair* ) | ||
1097 | { | ||
1098 | // Comment parses as text. | ||
1099 | const char* start = p; | ||
1100 | p = _value.ParseText( p, "-->", StrPair::COMMENT ); | ||
1101 | if ( p == 0 ) { | ||
1102 | _document->SetError( XML_ERROR_PARSING_COMMENT, start, 0 ); | ||
1103 | } | ||
1104 | return p; | ||
1105 | } | ||
1106 | |||
1107 | |||
1108 | XMLNode* XMLComment::ShallowClone( XMLDocument* doc ) const | ||
1109 | { | ||
1110 | if ( !doc ) { | ||
1111 | doc = _document; | ||
1112 | } | ||
1113 | XMLComment* comment = doc->NewComment( Value() ); // fixme: this will always allocate memory. Intern? | ||
1114 | return comment; | ||
1115 | } | ||
1116 | |||
1117 | |||
1118 | bool XMLComment::ShallowEqual( const XMLNode* compare ) const | ||
1119 | { | ||
1120 | TIXMLASSERT( compare ); | ||
1121 | const XMLComment* comment = compare->ToComment(); | ||
1122 | return ( comment && XMLUtil::StringEqual( comment->Value(), Value() )); | ||
1123 | } | ||
1124 | |||
1125 | |||
1126 | bool XMLComment::Accept( XMLVisitor* visitor ) const | ||
1127 | { | ||
1128 | TIXMLASSERT( visitor ); | ||
1129 | return visitor->Visit( *this ); | ||
1130 | } | ||
1131 | |||
1132 | |||
1133 | // --------- XMLDeclaration ---------- // | ||
1134 | |||
1135 | XMLDeclaration::XMLDeclaration( XMLDocument* doc ) : XMLNode( doc ) | ||
1136 | { | ||
1137 | } | ||
1138 | |||
1139 | |||
1140 | XMLDeclaration::~XMLDeclaration() | ||
1141 | { | ||
1142 | //printf( "~XMLDeclaration\n" ); | ||
1143 | } | ||
1144 | |||
1145 | |||
1146 | char* XMLDeclaration::ParseDeep( char* p, StrPair* ) | ||
1147 | { | ||
1148 | // Declaration parses as text. | ||
1149 | const char* start = p; | ||
1150 | p = _value.ParseText( p, "?>", StrPair::NEEDS_NEWLINE_NORMALIZATION ); | ||
1151 | if ( p == 0 ) { | ||
1152 | _document->SetError( XML_ERROR_PARSING_DECLARATION, start, 0 ); | ||
1153 | } | ||
1154 | return p; | ||
1155 | } | ||
1156 | |||
1157 | |||
1158 | XMLNode* XMLDeclaration::ShallowClone( XMLDocument* doc ) const | ||
1159 | { | ||
1160 | if ( !doc ) { | ||
1161 | doc = _document; | ||
1162 | } | ||
1163 | XMLDeclaration* dec = doc->NewDeclaration( Value() ); // fixme: this will always allocate memory. Intern? | ||
1164 | return dec; | ||
1165 | } | ||
1166 | |||
1167 | |||
1168 | bool XMLDeclaration::ShallowEqual( const XMLNode* compare ) const | ||
1169 | { | ||
1170 | TIXMLASSERT( compare ); | ||
1171 | const XMLDeclaration* declaration = compare->ToDeclaration(); | ||
1172 | return ( declaration && XMLUtil::StringEqual( declaration->Value(), Value() )); | ||
1173 | } | ||
1174 | |||
1175 | |||
1176 | |||
1177 | bool XMLDeclaration::Accept( XMLVisitor* visitor ) const | ||
1178 | { | ||
1179 | TIXMLASSERT( visitor ); | ||
1180 | return visitor->Visit( *this ); | ||
1181 | } | ||
1182 | |||
1183 | // --------- XMLUnknown ---------- // | ||
1184 | |||
1185 | XMLUnknown::XMLUnknown( XMLDocument* doc ) : XMLNode( doc ) | ||
1186 | { | ||
1187 | } | ||
1188 | |||
1189 | |||
1190 | XMLUnknown::~XMLUnknown() | ||
1191 | { | ||
1192 | } | ||
1193 | |||
1194 | |||
1195 | char* XMLUnknown::ParseDeep( char* p, StrPair* ) | ||
1196 | { | ||
1197 | // Unknown parses as text. | ||
1198 | const char* start = p; | ||
1199 | |||
1200 | p = _value.ParseText( p, ">", StrPair::NEEDS_NEWLINE_NORMALIZATION ); | ||
1201 | if ( !p ) { | ||
1202 | _document->SetError( XML_ERROR_PARSING_UNKNOWN, start, 0 ); | ||
1203 | } | ||
1204 | return p; | ||
1205 | } | ||
1206 | |||
1207 | |||
1208 | XMLNode* XMLUnknown::ShallowClone( XMLDocument* doc ) const | ||
1209 | { | ||
1210 | if ( !doc ) { | ||
1211 | doc = _document; | ||
1212 | } | ||
1213 | XMLUnknown* text = doc->NewUnknown( Value() ); // fixme: this will always allocate memory. Intern? | ||
1214 | return text; | ||
1215 | } | ||
1216 | |||
1217 | |||
1218 | bool XMLUnknown::ShallowEqual( const XMLNode* compare ) const | ||
1219 | { | ||
1220 | TIXMLASSERT( compare ); | ||
1221 | const XMLUnknown* unknown = compare->ToUnknown(); | ||
1222 | return ( unknown && XMLUtil::StringEqual( unknown->Value(), Value() )); | ||
1223 | } | ||
1224 | |||
1225 | |||
1226 | bool XMLUnknown::Accept( XMLVisitor* visitor ) const | ||
1227 | { | ||
1228 | TIXMLASSERT( visitor ); | ||
1229 | return visitor->Visit( *this ); | ||
1230 | } | ||
1231 | |||
1232 | // --------- XMLAttribute ---------- // | ||
1233 | |||
1234 | const char* XMLAttribute::Name() const | ||
1235 | { | ||
1236 | return _name.GetStr(); | ||
1237 | } | ||
1238 | |||
1239 | const char* XMLAttribute::Value() const | ||
1240 | { | ||
1241 | return _value.GetStr(); | ||
1242 | } | ||
1243 | |||
1244 | char* XMLAttribute::ParseDeep( char* p, bool processEntities ) | ||
1245 | { | ||
1246 | // Parse using the name rules: bug fix, was using ParseText before | ||
1247 | p = _name.ParseName( p ); | ||
1248 | if ( !p || !*p ) { | ||
1249 | return 0; | ||
1250 | } | ||
1251 | |||
1252 | // Skip white space before = | ||
1253 | p = XMLUtil::SkipWhiteSpace( p ); | ||
1254 | if ( *p != '=' ) { | ||
1255 | return 0; | ||
1256 | } | ||
1257 | |||
1258 | ++p; // move up to opening quote | ||
1259 | p = XMLUtil::SkipWhiteSpace( p ); | ||
1260 | if ( *p != '\"' && *p != '\'' ) { | ||
1261 | return 0; | ||
1262 | } | ||
1263 | |||
1264 | char endTag[2] = { *p, 0 }; | ||
1265 | ++p; // move past opening quote | ||
1266 | |||
1267 | p = _value.ParseText( p, endTag, processEntities ? StrPair::ATTRIBUTE_VALUE : StrPair::ATTRIBUTE_VALUE_LEAVE_ENTITIES ); | ||
1268 | return p; | ||
1269 | } | ||
1270 | |||
1271 | |||
1272 | void XMLAttribute::SetName( const char* n ) | ||
1273 | { | ||
1274 | _name.SetStr( n ); | ||
1275 | } | ||
1276 | |||
1277 | |||
1278 | XMLError XMLAttribute::QueryIntValue( int* value ) const | ||
1279 | { | ||
1280 | if ( XMLUtil::ToInt( Value(), value )) { | ||
1281 | return XML_NO_ERROR; | ||
1282 | } | ||
1283 | return XML_WRONG_ATTRIBUTE_TYPE; | ||
1284 | } | ||
1285 | |||
1286 | |||
1287 | XMLError XMLAttribute::QueryUnsignedValue( unsigned int* value ) const | ||
1288 | { | ||
1289 | if ( XMLUtil::ToUnsigned( Value(), value )) { | ||
1290 | return XML_NO_ERROR; | ||
1291 | } | ||
1292 | return XML_WRONG_ATTRIBUTE_TYPE; | ||
1293 | } | ||
1294 | |||
1295 | |||
1296 | XMLError XMLAttribute::QueryBoolValue( bool* value ) const | ||
1297 | { | ||
1298 | if ( XMLUtil::ToBool( Value(), value )) { | ||
1299 | return XML_NO_ERROR; | ||
1300 | } | ||
1301 | return XML_WRONG_ATTRIBUTE_TYPE; | ||
1302 | } | ||
1303 | |||
1304 | |||
1305 | XMLError XMLAttribute::QueryFloatValue( float* value ) const | ||
1306 | { | ||
1307 | if ( XMLUtil::ToFloat( Value(), value )) { | ||
1308 | return XML_NO_ERROR; | ||
1309 | } | ||
1310 | return XML_WRONG_ATTRIBUTE_TYPE; | ||
1311 | } | ||
1312 | |||
1313 | |||
1314 | XMLError XMLAttribute::QueryDoubleValue( double* value ) const | ||
1315 | { | ||
1316 | if ( XMLUtil::ToDouble( Value(), value )) { | ||
1317 | return XML_NO_ERROR; | ||
1318 | } | ||
1319 | return XML_WRONG_ATTRIBUTE_TYPE; | ||
1320 | } | ||
1321 | |||
1322 | |||
1323 | void XMLAttribute::SetAttribute( const char* v ) | ||
1324 | { | ||
1325 | _value.SetStr( v ); | ||
1326 | } | ||
1327 | |||
1328 | |||
1329 | void XMLAttribute::SetAttribute( int v ) | ||
1330 | { | ||
1331 | char buf[BUF_SIZE]; | ||
1332 | XMLUtil::ToStr( v, buf, BUF_SIZE ); | ||
1333 | _value.SetStr( buf ); | ||
1334 | } | ||
1335 | |||
1336 | |||
1337 | void XMLAttribute::SetAttribute( unsigned v ) | ||
1338 | { | ||
1339 | char buf[BUF_SIZE]; | ||
1340 | XMLUtil::ToStr( v, buf, BUF_SIZE ); | ||
1341 | _value.SetStr( buf ); | ||
1342 | } | ||
1343 | |||
1344 | |||
1345 | void XMLAttribute::SetAttribute( bool v ) | ||
1346 | { | ||
1347 | char buf[BUF_SIZE]; | ||
1348 | XMLUtil::ToStr( v, buf, BUF_SIZE ); | ||
1349 | _value.SetStr( buf ); | ||
1350 | } | ||
1351 | |||
1352 | void XMLAttribute::SetAttribute( double v ) | ||
1353 | { | ||
1354 | char buf[BUF_SIZE]; | ||
1355 | XMLUtil::ToStr( v, buf, BUF_SIZE ); | ||
1356 | _value.SetStr( buf ); | ||
1357 | } | ||
1358 | |||
1359 | void XMLAttribute::SetAttribute( float v ) | ||
1360 | { | ||
1361 | char buf[BUF_SIZE]; | ||
1362 | XMLUtil::ToStr( v, buf, BUF_SIZE ); | ||
1363 | _value.SetStr( buf ); | ||
1364 | } | ||
1365 | |||
1366 | |||
1367 | // --------- XMLElement ---------- // | ||
1368 | XMLElement::XMLElement( XMLDocument* doc ) : XMLNode( doc ), | ||
1369 | _closingType( 0 ), | ||
1370 | _rootAttribute( 0 ) | ||
1371 | { | ||
1372 | } | ||
1373 | |||
1374 | |||
1375 | XMLElement::~XMLElement() | ||
1376 | { | ||
1377 | while( _rootAttribute ) { | ||
1378 | XMLAttribute* next = _rootAttribute->_next; | ||
1379 | DeleteAttribute( _rootAttribute ); | ||
1380 | _rootAttribute = next; | ||
1381 | } | ||
1382 | } | ||
1383 | |||
1384 | |||
1385 | const XMLAttribute* XMLElement::FindAttribute( const char* name ) const | ||
1386 | { | ||
1387 | for( XMLAttribute* a = _rootAttribute; a; a = a->_next ) { | ||
1388 | if ( XMLUtil::StringEqual( a->Name(), name ) ) { | ||
1389 | return a; | ||
1390 | } | ||
1391 | } | ||
1392 | return 0; | ||
1393 | } | ||
1394 | |||
1395 | |||
1396 | const char* XMLElement::Attribute( const char* name, const char* value ) const | ||
1397 | { | ||
1398 | const XMLAttribute* a = FindAttribute( name ); | ||
1399 | if ( !a ) { | ||
1400 | return 0; | ||
1401 | } | ||
1402 | if ( !value || XMLUtil::StringEqual( a->Value(), value )) { | ||
1403 | return a->Value(); | ||
1404 | } | ||
1405 | return 0; | ||
1406 | } | ||
1407 | |||
1408 | |||
1409 | const char* XMLElement::GetText() const | ||
1410 | { | ||
1411 | if ( FirstChild() && FirstChild()->ToText() ) { | ||
1412 | return FirstChild()->Value(); | ||
1413 | } | ||
1414 | return 0; | ||
1415 | } | ||
1416 | |||
1417 | |||
1418 | void XMLElement::SetText( const char* inText ) | ||
1419 | { | ||
1420 | if ( FirstChild() && FirstChild()->ToText() ) | ||
1421 | FirstChild()->SetValue( inText ); | ||
1422 | else { | ||
1423 | XMLText* theText = GetDocument()->NewText( inText ); | ||
1424 | InsertFirstChild( theText ); | ||
1425 | } | ||
1426 | } | ||
1427 | |||
1428 | |||
1429 | void XMLElement::SetText( int v ) | ||
1430 | { | ||
1431 | char buf[BUF_SIZE]; | ||
1432 | XMLUtil::ToStr( v, buf, BUF_SIZE ); | ||
1433 | SetText( buf ); | ||
1434 | } | ||
1435 | |||
1436 | |||
1437 | void XMLElement::SetText( unsigned v ) | ||
1438 | { | ||
1439 | char buf[BUF_SIZE]; | ||
1440 | XMLUtil::ToStr( v, buf, BUF_SIZE ); | ||
1441 | SetText( buf ); | ||
1442 | } | ||
1443 | |||
1444 | |||
1445 | void XMLElement::SetText( bool v ) | ||
1446 | { | ||
1447 | char buf[BUF_SIZE]; | ||
1448 | XMLUtil::ToStr( v, buf, BUF_SIZE ); | ||
1449 | SetText( buf ); | ||
1450 | } | ||
1451 | |||
1452 | |||
1453 | void XMLElement::SetText( float v ) | ||
1454 | { | ||
1455 | char buf[BUF_SIZE]; | ||
1456 | XMLUtil::ToStr( v, buf, BUF_SIZE ); | ||
1457 | SetText( buf ); | ||
1458 | } | ||
1459 | |||
1460 | |||
1461 | void XMLElement::SetText( double v ) | ||
1462 | { | ||
1463 | char buf[BUF_SIZE]; | ||
1464 | XMLUtil::ToStr( v, buf, BUF_SIZE ); | ||
1465 | SetText( buf ); | ||
1466 | } | ||
1467 | |||
1468 | |||
1469 | XMLError XMLElement::QueryIntText( int* ival ) const | ||
1470 | { | ||
1471 | if ( FirstChild() && FirstChild()->ToText() ) { | ||
1472 | const char* t = FirstChild()->Value(); | ||
1473 | if ( XMLUtil::ToInt( t, ival ) ) { | ||
1474 | return XML_SUCCESS; | ||
1475 | } | ||
1476 | return XML_CAN_NOT_CONVERT_TEXT; | ||
1477 | } | ||
1478 | return XML_NO_TEXT_NODE; | ||
1479 | } | ||
1480 | |||
1481 | |||
1482 | XMLError XMLElement::QueryUnsignedText( unsigned* uval ) const | ||
1483 | { | ||
1484 | if ( FirstChild() && FirstChild()->ToText() ) { | ||
1485 | const char* t = FirstChild()->Value(); | ||
1486 | if ( XMLUtil::ToUnsigned( t, uval ) ) { | ||
1487 | return XML_SUCCESS; | ||
1488 | } | ||
1489 | return XML_CAN_NOT_CONVERT_TEXT; | ||
1490 | } | ||
1491 | return XML_NO_TEXT_NODE; | ||
1492 | } | ||
1493 | |||
1494 | |||
1495 | XMLError XMLElement::QueryBoolText( bool* bval ) const | ||
1496 | { | ||
1497 | if ( FirstChild() && FirstChild()->ToText() ) { | ||
1498 | const char* t = FirstChild()->Value(); | ||
1499 | if ( XMLUtil::ToBool( t, bval ) ) { | ||
1500 | return XML_SUCCESS; | ||
1501 | } | ||
1502 | return XML_CAN_NOT_CONVERT_TEXT; | ||
1503 | } | ||
1504 | return XML_NO_TEXT_NODE; | ||
1505 | } | ||
1506 | |||
1507 | |||
1508 | XMLError XMLElement::QueryDoubleText( double* dval ) const | ||
1509 | { | ||
1510 | if ( FirstChild() && FirstChild()->ToText() ) { | ||
1511 | const char* t = FirstChild()->Value(); | ||
1512 | if ( XMLUtil::ToDouble( t, dval ) ) { | ||
1513 | return XML_SUCCESS; | ||
1514 | } | ||
1515 | return XML_CAN_NOT_CONVERT_TEXT; | ||
1516 | } | ||
1517 | return XML_NO_TEXT_NODE; | ||
1518 | } | ||
1519 | |||
1520 | |||
1521 | XMLError XMLElement::QueryFloatText( float* fval ) const | ||
1522 | { | ||
1523 | if ( FirstChild() && FirstChild()->ToText() ) { | ||
1524 | const char* t = FirstChild()->Value(); | ||
1525 | if ( XMLUtil::ToFloat( t, fval ) ) { | ||
1526 | return XML_SUCCESS; | ||
1527 | } | ||
1528 | return XML_CAN_NOT_CONVERT_TEXT; | ||
1529 | } | ||
1530 | return XML_NO_TEXT_NODE; | ||
1531 | } | ||
1532 | |||
1533 | |||
1534 | |||
1535 | XMLAttribute* XMLElement::FindOrCreateAttribute( const char* name ) | ||
1536 | { | ||
1537 | XMLAttribute* last = 0; | ||
1538 | XMLAttribute* attrib = 0; | ||
1539 | for( attrib = _rootAttribute; | ||
1540 | attrib; | ||
1541 | last = attrib, attrib = attrib->_next ) { | ||
1542 | if ( XMLUtil::StringEqual( attrib->Name(), name ) ) { | ||
1543 | break; | ||
1544 | } | ||
1545 | } | ||
1546 | if ( !attrib ) { | ||
1547 | TIXMLASSERT( sizeof( XMLAttribute ) == _document->_attributePool.ItemSize() ); | ||
1548 | attrib = new (_document->_attributePool.Alloc() ) XMLAttribute(); | ||
1549 | attrib->_memPool = &_document->_attributePool; | ||
1550 | if ( last ) { | ||
1551 | last->_next = attrib; | ||
1552 | } | ||
1553 | else { | ||
1554 | _rootAttribute = attrib; | ||
1555 | } | ||
1556 | attrib->SetName( name ); | ||
1557 | attrib->_memPool->SetTracked(); // always created and linked. | ||
1558 | } | ||
1559 | return attrib; | ||
1560 | } | ||
1561 | |||
1562 | |||
1563 | void XMLElement::DeleteAttribute( const char* name ) | ||
1564 | { | ||
1565 | XMLAttribute* prev = 0; | ||
1566 | for( XMLAttribute* a=_rootAttribute; a; a=a->_next ) { | ||
1567 | if ( XMLUtil::StringEqual( name, a->Name() ) ) { | ||
1568 | if ( prev ) { | ||
1569 | prev->_next = a->_next; | ||
1570 | } | ||
1571 | else { | ||
1572 | _rootAttribute = a->_next; | ||
1573 | } | ||
1574 | DeleteAttribute( a ); | ||
1575 | break; | ||
1576 | } | ||
1577 | prev = a; | ||
1578 | } | ||
1579 | } | ||
1580 | |||
1581 | |||
1582 | char* XMLElement::ParseAttributes( char* p ) | ||
1583 | { | ||
1584 | const char* start = p; | ||
1585 | XMLAttribute* prevAttribute = 0; | ||
1586 | |||
1587 | // Read the attributes. | ||
1588 | while( p ) { | ||
1589 | p = XMLUtil::SkipWhiteSpace( p ); | ||
1590 | if ( !(*p) ) { | ||
1591 | _document->SetError( XML_ERROR_PARSING_ELEMENT, start, Name() ); | ||
1592 | return 0; | ||
1593 | } | ||
1594 | |||
1595 | // attribute. | ||
1596 | if (XMLUtil::IsNameStartChar( *p ) ) { | ||
1597 | TIXMLASSERT( sizeof( XMLAttribute ) == _document->_attributePool.ItemSize() ); | ||
1598 | XMLAttribute* attrib = new (_document->_attributePool.Alloc() ) XMLAttribute(); | ||
1599 | attrib->_memPool = &_document->_attributePool; | ||
1600 | attrib->_memPool->SetTracked(); | ||
1601 | |||
1602 | p = attrib->ParseDeep( p, _document->ProcessEntities() ); | ||
1603 | if ( !p || Attribute( attrib->Name() ) ) { | ||
1604 | DeleteAttribute( attrib ); | ||
1605 | _document->SetError( XML_ERROR_PARSING_ATTRIBUTE, start, p ); | ||
1606 | return 0; | ||
1607 | } | ||
1608 | // There is a minor bug here: if the attribute in the source xml | ||
1609 | // document is duplicated, it will not be detected and the | ||
1610 | // attribute will be doubly added. However, tracking the 'prevAttribute' | ||
1611 | // avoids re-scanning the attribute list. Preferring performance for | ||
1612 | // now, may reconsider in the future. | ||
1613 | if ( prevAttribute ) { | ||
1614 | prevAttribute->_next = attrib; | ||
1615 | } | ||
1616 | else { | ||
1617 | _rootAttribute = attrib; | ||
1618 | } | ||
1619 | prevAttribute = attrib; | ||
1620 | } | ||
1621 | // end of the tag | ||
1622 | else if ( *p == '>' ) { | ||
1623 | ++p; | ||
1624 | break; | ||
1625 | } | ||
1626 | // end of the tag | ||
1627 | else if ( *p == '/' && *(p+1) == '>' ) { | ||
1628 | _closingType = CLOSED; | ||
1629 | return p+2; // done; sealed element. | ||
1630 | } | ||
1631 | else { | ||
1632 | _document->SetError( XML_ERROR_PARSING_ELEMENT, start, p ); | ||
1633 | return 0; | ||
1634 | } | ||
1635 | } | ||
1636 | return p; | ||
1637 | } | ||
1638 | |||
1639 | void XMLElement::DeleteAttribute( XMLAttribute* attribute ) | ||
1640 | { | ||
1641 | if ( attribute == 0 ) { | ||
1642 | return; | ||
1643 | } | ||
1644 | MemPool* pool = attribute->_memPool; | ||
1645 | attribute->~XMLAttribute(); | ||
1646 | pool->Free( attribute ); | ||
1647 | } | ||
1648 | |||
1649 | // | ||
1650 | // <ele></ele> | ||
1651 | // <ele>foo<b>bar</b></ele> | ||
1652 | // | ||
1653 | char* XMLElement::ParseDeep( char* p, StrPair* strPair ) | ||
1654 | { | ||
1655 | // Read the element name. | ||
1656 | p = XMLUtil::SkipWhiteSpace( p ); | ||
1657 | |||
1658 | // The closing element is the </element> form. It is | ||
1659 | // parsed just like a regular element then deleted from | ||
1660 | // the DOM. | ||
1661 | if ( *p == '/' ) { | ||
1662 | _closingType = CLOSING; | ||
1663 | ++p; | ||
1664 | } | ||
1665 | |||
1666 | p = _value.ParseName( p ); | ||
1667 | if ( _value.Empty() ) { | ||
1668 | return 0; | ||
1669 | } | ||
1670 | |||
1671 | p = ParseAttributes( p ); | ||
1672 | if ( !p || !*p || _closingType ) { | ||
1673 | return p; | ||
1674 | } | ||
1675 | |||
1676 | p = XMLNode::ParseDeep( p, strPair ); | ||
1677 | return p; | ||
1678 | } | ||
1679 | |||
1680 | |||
1681 | |||
1682 | XMLNode* XMLElement::ShallowClone( XMLDocument* doc ) const | ||
1683 | { | ||
1684 | if ( !doc ) { | ||
1685 | doc = _document; | ||
1686 | } | ||
1687 | XMLElement* element = doc->NewElement( Value() ); // fixme: this will always allocate memory. Intern? | ||
1688 | for( const XMLAttribute* a=FirstAttribute(); a; a=a->Next() ) { | ||
1689 | element->SetAttribute( a->Name(), a->Value() ); // fixme: this will always allocate memory. Intern? | ||
1690 | } | ||
1691 | return element; | ||
1692 | } | ||
1693 | |||
1694 | |||
1695 | bool XMLElement::ShallowEqual( const XMLNode* compare ) const | ||
1696 | { | ||
1697 | TIXMLASSERT( compare ); | ||
1698 | const XMLElement* other = compare->ToElement(); | ||
1699 | if ( other && XMLUtil::StringEqual( other->Name(), Name() )) { | ||
1700 | |||
1701 | const XMLAttribute* a=FirstAttribute(); | ||
1702 | const XMLAttribute* b=other->FirstAttribute(); | ||
1703 | |||
1704 | while ( a && b ) { | ||
1705 | if ( !XMLUtil::StringEqual( a->Value(), b->Value() ) ) { | ||
1706 | return false; | ||
1707 | } | ||
1708 | a = a->Next(); | ||
1709 | b = b->Next(); | ||
1710 | } | ||
1711 | if ( a || b ) { | ||
1712 | // different count | ||
1713 | return false; | ||
1714 | } | ||
1715 | return true; | ||
1716 | } | ||
1717 | return false; | ||
1718 | } | ||
1719 | |||
1720 | |||
1721 | bool XMLElement::Accept( XMLVisitor* visitor ) const | ||
1722 | { | ||
1723 | TIXMLASSERT( visitor ); | ||
1724 | if ( visitor->VisitEnter( *this, _rootAttribute ) ) { | ||
1725 | for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) { | ||
1726 | if ( !node->Accept( visitor ) ) { | ||
1727 | break; | ||
1728 | } | ||
1729 | } | ||
1730 | } | ||
1731 | return visitor->VisitExit( *this ); | ||
1732 | } | ||
1733 | |||
1734 | |||
1735 | // --------- XMLDocument ----------- // | ||
1736 | |||
1737 | // Warning: List must match 'enum XMLError' | ||
1738 | const char* XMLDocument::_errorNames[XML_ERROR_COUNT] = { | ||
1739 | "XML_SUCCESS", | ||
1740 | "XML_NO_ATTRIBUTE", | ||
1741 | "XML_WRONG_ATTRIBUTE_TYPE", | ||
1742 | "XML_ERROR_FILE_NOT_FOUND", | ||
1743 | "XML_ERROR_FILE_COULD_NOT_BE_OPENED", | ||
1744 | "XML_ERROR_FILE_READ_ERROR", | ||
1745 | "XML_ERROR_ELEMENT_MISMATCH", | ||
1746 | "XML_ERROR_PARSING_ELEMENT", | ||
1747 | "XML_ERROR_PARSING_ATTRIBUTE", | ||
1748 | "XML_ERROR_IDENTIFYING_TAG", | ||
1749 | "XML_ERROR_PARSING_TEXT", | ||
1750 | "XML_ERROR_PARSING_CDATA", | ||
1751 | "XML_ERROR_PARSING_COMMENT", | ||
1752 | "XML_ERROR_PARSING_DECLARATION", | ||
1753 | "XML_ERROR_PARSING_UNKNOWN", | ||
1754 | "XML_ERROR_EMPTY_DOCUMENT", | ||
1755 | "XML_ERROR_MISMATCHED_ELEMENT", | ||
1756 | "XML_ERROR_PARSING", | ||
1757 | "XML_CAN_NOT_CONVERT_TEXT", | ||
1758 | "XML_NO_TEXT_NODE" | ||
1759 | }; | ||
1760 | |||
1761 | |||
1762 | XMLDocument::XMLDocument( bool processEntities, Whitespace whitespace ) : | ||
1763 | XMLNode( 0 ), | ||
1764 | _writeBOM( false ), | ||
1765 | _processEntities( processEntities ), | ||
1766 | _errorID( XML_NO_ERROR ), | ||
1767 | _whitespace( whitespace ), | ||
1768 | _errorStr1( 0 ), | ||
1769 | _errorStr2( 0 ), | ||
1770 | _charBuffer( 0 ) | ||
1771 | { | ||
1772 | // avoid VC++ C4355 warning about 'this' in initializer list (C4355 is off by default in VS2012+) | ||
1773 | _document = this; | ||
1774 | } | ||
1775 | |||
1776 | |||
1777 | XMLDocument::~XMLDocument() | ||
1778 | { | ||
1779 | Clear(); | ||
1780 | } | ||
1781 | |||
1782 | |||
1783 | void XMLDocument::Clear() | ||
1784 | { | ||
1785 | DeleteChildren(); | ||
1786 | |||
1787 | #ifdef DEBUG | ||
1788 | const bool hadError = Error(); | ||
1789 | #endif | ||
1790 | _errorID = XML_NO_ERROR; | ||
1791 | _errorStr1 = 0; | ||
1792 | _errorStr2 = 0; | ||
1793 | |||
1794 | delete [] _charBuffer; | ||
1795 | _charBuffer = 0; | ||
1796 | |||
1797 | #if 0 | ||
1798 | _textPool.Trace( "text" ); | ||
1799 | _elementPool.Trace( "element" ); | ||
1800 | _commentPool.Trace( "comment" ); | ||
1801 | _attributePool.Trace( "attribute" ); | ||
1802 | #endif | ||
1803 | |||
1804 | #ifdef DEBUG | ||
1805 | if ( !hadError ) { | ||
1806 | TIXMLASSERT( _elementPool.CurrentAllocs() == _elementPool.Untracked() ); | ||
1807 | TIXMLASSERT( _attributePool.CurrentAllocs() == _attributePool.Untracked() ); | ||
1808 | TIXMLASSERT( _textPool.CurrentAllocs() == _textPool.Untracked() ); | ||
1809 | TIXMLASSERT( _commentPool.CurrentAllocs() == _commentPool.Untracked() ); | ||
1810 | } | ||
1811 | #endif | ||
1812 | } | ||
1813 | |||
1814 | |||
1815 | XMLElement* XMLDocument::NewElement( const char* name ) | ||
1816 | { | ||
1817 | TIXMLASSERT( sizeof( XMLElement ) == _elementPool.ItemSize() ); | ||
1818 | XMLElement* ele = new (_elementPool.Alloc()) XMLElement( this ); | ||
1819 | ele->_memPool = &_elementPool; | ||
1820 | ele->SetName( name ); | ||
1821 | return ele; | ||
1822 | } | ||
1823 | |||
1824 | |||
1825 | XMLComment* XMLDocument::NewComment( const char* str ) | ||
1826 | { | ||
1827 | TIXMLASSERT( sizeof( XMLComment ) == _commentPool.ItemSize() ); | ||
1828 | XMLComment* comment = new (_commentPool.Alloc()) XMLComment( this ); | ||
1829 | comment->_memPool = &_commentPool; | ||
1830 | comment->SetValue( str ); | ||
1831 | return comment; | ||
1832 | } | ||
1833 | |||
1834 | |||
1835 | XMLText* XMLDocument::NewText( const char* str ) | ||
1836 | { | ||
1837 | TIXMLASSERT( sizeof( XMLText ) == _textPool.ItemSize() ); | ||
1838 | XMLText* text = new (_textPool.Alloc()) XMLText( this ); | ||
1839 | text->_memPool = &_textPool; | ||
1840 | text->SetValue( str ); | ||
1841 | return text; | ||
1842 | } | ||
1843 | |||
1844 | |||
1845 | XMLDeclaration* XMLDocument::NewDeclaration( const char* str ) | ||
1846 | { | ||
1847 | TIXMLASSERT( sizeof( XMLDeclaration ) == _commentPool.ItemSize() ); | ||
1848 | XMLDeclaration* dec = new (_commentPool.Alloc()) XMLDeclaration( this ); | ||
1849 | dec->_memPool = &_commentPool; | ||
1850 | dec->SetValue( str ? str : "xml version=\"1.0\" encoding=\"UTF-8\"" ); | ||
1851 | return dec; | ||
1852 | } | ||
1853 | |||
1854 | |||
1855 | XMLUnknown* XMLDocument::NewUnknown( const char* str ) | ||
1856 | { | ||
1857 | TIXMLASSERT( sizeof( XMLUnknown ) == _commentPool.ItemSize() ); | ||
1858 | XMLUnknown* unk = new (_commentPool.Alloc()) XMLUnknown( this ); | ||
1859 | unk->_memPool = &_commentPool; | ||
1860 | unk->SetValue( str ); | ||
1861 | return unk; | ||
1862 | } | ||
1863 | |||
1864 | static FILE* callfopen( const char* filepath, const char* mode ) | ||
1865 | { | ||
1866 | TIXMLASSERT( filepath ); | ||
1867 | TIXMLASSERT( mode ); | ||
1868 | #if defined(_MSC_VER) && (_MSC_VER >= 1400 ) && (!defined WINCE) | ||
1869 | FILE* fp = 0; | ||
1870 | errno_t err = fopen_s( &fp, filepath, mode ); | ||
1871 | if ( err ) { | ||
1872 | return 0; | ||
1873 | } | ||
1874 | #else | ||
1875 | FILE* fp = fopen( filepath, mode ); | ||
1876 | #endif | ||
1877 | return fp; | ||
1878 | } | ||
1879 | |||
1880 | void XMLDocument::DeleteNode( XMLNode* node ) { | ||
1881 | TIXMLASSERT( node ); | ||
1882 | TIXMLASSERT(node->_document == this ); | ||
1883 | if (node->_parent) { | ||
1884 | node->_parent->DeleteChild( node ); | ||
1885 | } | ||
1886 | else { | ||
1887 | // Isn't in the tree. | ||
1888 | // Use the parent delete. | ||
1889 | // Also, we need to mark it tracked: we 'know' | ||
1890 | // it was never used. | ||
1891 | node->_memPool->SetTracked(); | ||
1892 | // Call the static XMLNode version: | ||
1893 | XMLNode::DeleteNode(node); | ||
1894 | } | ||
1895 | } | ||
1896 | |||
1897 | |||
1898 | XMLError XMLDocument::LoadFile( const char* filename ) | ||
1899 | { | ||
1900 | Clear(); | ||
1901 | FILE* fp = callfopen( filename, "rb" ); | ||
1902 | if ( !fp ) { | ||
1903 | SetError( XML_ERROR_FILE_NOT_FOUND, filename, 0 ); | ||
1904 | return _errorID; | ||
1905 | } | ||
1906 | LoadFile( fp ); | ||
1907 | fclose( fp ); | ||
1908 | return _errorID; | ||
1909 | } | ||
1910 | |||
1911 | |||
1912 | XMLError XMLDocument::LoadFile( FILE* fp ) | ||
1913 | { | ||
1914 | Clear(); | ||
1915 | |||
1916 | fseek( fp, 0, SEEK_SET ); | ||
1917 | if ( fgetc( fp ) == EOF && ferror( fp ) != 0 ) { | ||
1918 | SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 ); | ||
1919 | return _errorID; | ||
1920 | } | ||
1921 | |||
1922 | fseek( fp, 0, SEEK_END ); | ||
1923 | const long filelength = ftell( fp ); | ||
1924 | fseek( fp, 0, SEEK_SET ); | ||
1925 | if ( filelength == -1L ) { | ||
1926 | SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 ); | ||
1927 | return _errorID; | ||
1928 | } | ||
1929 | |||
1930 | if ( (unsigned long)filelength >= (size_t)-1 ) { | ||
1931 | // Cannot handle files which won't fit in buffer together with null terminator | ||
1932 | SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 ); | ||
1933 | return _errorID; | ||
1934 | } | ||
1935 | |||
1936 | if ( filelength == 0 ) { | ||
1937 | SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 ); | ||
1938 | return _errorID; | ||
1939 | } | ||
1940 | |||
1941 | const size_t size = filelength; | ||
1942 | _charBuffer = new char[size+1]; | ||
1943 | size_t read = fread( _charBuffer, 1, size, fp ); | ||
1944 | if ( read != size ) { | ||
1945 | SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 ); | ||
1946 | return _errorID; | ||
1947 | } | ||
1948 | |||
1949 | _charBuffer[size] = 0; | ||
1950 | |||
1951 | Parse(); | ||
1952 | return _errorID; | ||
1953 | } | ||
1954 | |||
1955 | |||
1956 | XMLError XMLDocument::SaveFile( const char* filename, bool compact ) | ||
1957 | { | ||
1958 | FILE* fp = callfopen( filename, "w" ); | ||
1959 | if ( !fp ) { | ||
1960 | SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, filename, 0 ); | ||
1961 | return _errorID; | ||
1962 | } | ||
1963 | SaveFile(fp, compact); | ||
1964 | fclose( fp ); | ||
1965 | return _errorID; | ||
1966 | } | ||
1967 | |||
1968 | |||
1969 | XMLError XMLDocument::SaveFile( FILE* fp, bool compact ) | ||
1970 | { | ||
1971 | // Clear any error from the last save, otherwise it will get reported | ||
1972 | // for *this* call. | ||
1973 | SetError( XML_NO_ERROR, 0, 0 ); | ||
1974 | XMLPrinter stream( fp, compact ); | ||
1975 | Print( &stream ); | ||
1976 | return _errorID; | ||
1977 | } | ||
1978 | |||
1979 | |||
1980 | XMLError XMLDocument::Parse( const char* p, size_t len ) | ||
1981 | { | ||
1982 | Clear(); | ||
1983 | |||
1984 | if ( len == 0 || !p || !*p ) { | ||
1985 | SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 ); | ||
1986 | return _errorID; | ||
1987 | } | ||
1988 | if ( len == (size_t)(-1) ) { | ||
1989 | len = strlen( p ); | ||
1990 | } | ||
1991 | _charBuffer = new char[ len+1 ]; | ||
1992 | memcpy( _charBuffer, p, len ); | ||
1993 | _charBuffer[len] = 0; | ||
1994 | |||
1995 | Parse(); | ||
1996 | if ( Error() ) { | ||
1997 | // clean up now essentially dangling memory. | ||
1998 | // and the parse fail can put objects in the | ||
1999 | // pools that are dead and inaccessible. | ||
2000 | DeleteChildren(); | ||
2001 | _elementPool.Clear(); | ||
2002 | _attributePool.Clear(); | ||
2003 | _textPool.Clear(); | ||
2004 | _commentPool.Clear(); | ||
2005 | } | ||
2006 | return _errorID; | ||
2007 | } | ||
2008 | |||
2009 | |||
2010 | void XMLDocument::Print( XMLPrinter* streamer ) const | ||
2011 | { | ||
2012 | if ( streamer ) { | ||
2013 | Accept( streamer ); | ||
2014 | } | ||
2015 | else { | ||
2016 | XMLPrinter stdoutStreamer( stdout ); | ||
2017 | Accept( &stdoutStreamer ); | ||
2018 | } | ||
2019 | } | ||
2020 | |||
2021 | |||
2022 | void XMLDocument::SetError( XMLError error, const char* str1, const char* str2 ) | ||
2023 | { | ||
2024 | TIXMLASSERT( error >= 0 && error < XML_ERROR_COUNT ); | ||
2025 | _errorID = error; | ||
2026 | _errorStr1 = str1; | ||
2027 | _errorStr2 = str2; | ||
2028 | } | ||
2029 | |||
2030 | const char* XMLDocument::ErrorName() const | ||
2031 | { | ||
2032 | TIXMLASSERT( _errorID >= 0 && _errorID < XML_ERROR_COUNT ); | ||
2033 | const char* errorName = _errorNames[_errorID]; | ||
2034 | TIXMLASSERT( errorName && errorName[0] ); | ||
2035 | return errorName; | ||
2036 | } | ||
2037 | |||
2038 | void XMLDocument::PrintError() const | ||
2039 | { | ||
2040 | if ( Error() ) { | ||
2041 | static const int LEN = 20; | ||
2042 | char buf1[LEN] = { 0 }; | ||
2043 | char buf2[LEN] = { 0 }; | ||
2044 | |||
2045 | if ( _errorStr1 ) { | ||
2046 | TIXML_SNPRINTF( buf1, LEN, "%s", _errorStr1 ); | ||
2047 | } | ||
2048 | if ( _errorStr2 ) { | ||
2049 | TIXML_SNPRINTF( buf2, LEN, "%s", _errorStr2 ); | ||
2050 | } | ||
2051 | |||
2052 | // Should check INT_MIN <= _errorID && _errorId <= INT_MAX, but that | ||
2053 | // causes a clang "always true" -Wtautological-constant-out-of-range-compare warning | ||
2054 | TIXMLASSERT( 0 <= _errorID && XML_ERROR_COUNT - 1 <= INT_MAX ); | ||
2055 | printf( "XMLDocument error id=%d '%s' str1=%s str2=%s\n", | ||
2056 | static_cast<int>( _errorID ), ErrorName(), buf1, buf2 ); | ||
2057 | } | ||
2058 | } | ||
2059 | |||
2060 | void XMLDocument::Parse() | ||
2061 | { | ||
2062 | TIXMLASSERT( NoChildren() ); // Clear() must have been called previously | ||
2063 | TIXMLASSERT( _charBuffer ); | ||
2064 | char* p = _charBuffer; | ||
2065 | p = XMLUtil::SkipWhiteSpace( p ); | ||
2066 | p = const_cast<char*>( XMLUtil::ReadBOM( p, &_writeBOM ) ); | ||
2067 | if ( !*p ) { | ||
2068 | SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 ); | ||
2069 | return; | ||
2070 | } | ||
2071 | ParseDeep(p, 0 ); | ||
2072 | } | ||
2073 | |||
2074 | XMLPrinter::XMLPrinter( FILE* file, bool compact, int depth ) : | ||
2075 | _elementJustOpened( false ), | ||
2076 | _firstElement( true ), | ||
2077 | _fp( file ), | ||
2078 | _depth( depth ), | ||
2079 | _textDepth( -1 ), | ||
2080 | _processEntities( true ), | ||
2081 | _compactMode( compact ) | ||
2082 | { | ||
2083 | for( int i=0; i<ENTITY_RANGE; ++i ) { | ||
2084 | _entityFlag[i] = false; | ||
2085 | _restrictedEntityFlag[i] = false; | ||
2086 | } | ||
2087 | for( int i=0; i<NUM_ENTITIES; ++i ) { | ||
2088 | const char entityValue = entities[i].value; | ||
2089 | TIXMLASSERT( 0 <= entityValue && entityValue < ENTITY_RANGE ); | ||
2090 | _entityFlag[ (unsigned char)entityValue ] = true; | ||
2091 | } | ||
2092 | _restrictedEntityFlag[(unsigned char)'&'] = true; | ||
2093 | _restrictedEntityFlag[(unsigned char)'<'] = true; | ||
2094 | _restrictedEntityFlag[(unsigned char)'>'] = true; // not required, but consistency is nice | ||
2095 | _buffer.Push( 0 ); | ||
2096 | } | ||
2097 | |||
2098 | |||
2099 | void XMLPrinter::Print( const char* format, ... ) | ||
2100 | { | ||
2101 | va_list va; | ||
2102 | va_start( va, format ); | ||
2103 | |||
2104 | if ( _fp ) { | ||
2105 | vfprintf( _fp, format, va ); | ||
2106 | } | ||
2107 | else { | ||
2108 | int len = TIXML_VSCPRINTF( format, va ); | ||
2109 | // Close out and re-start the va-args | ||
2110 | va_end( va ); | ||
2111 | va_start( va, format ); | ||
2112 | TIXMLASSERT( _buffer.Size() > 0 && _buffer[_buffer.Size() - 1] == 0 ); | ||
2113 | char* p = _buffer.PushArr( len ) - 1; // back up over the null terminator. | ||
2114 | TIXML_VSNPRINTF( p, len+1, format, va ); | ||
2115 | } | ||
2116 | va_end( va ); | ||
2117 | } | ||
2118 | |||
2119 | |||
2120 | void XMLPrinter::PrintSpace( int depth ) | ||
2121 | { | ||
2122 | for( int i=0; i<depth; ++i ) { | ||
2123 | Print( " " ); | ||
2124 | } | ||
2125 | } | ||
2126 | |||
2127 | |||
2128 | void XMLPrinter::PrintString( const char* p, bool restricted ) | ||
2129 | { | ||
2130 | // Look for runs of bytes between entities to print. | ||
2131 | const char* q = p; | ||
2132 | |||
2133 | if ( _processEntities ) { | ||
2134 | const bool* flag = restricted ? _restrictedEntityFlag : _entityFlag; | ||
2135 | while ( *q ) { | ||
2136 | TIXMLASSERT( p <= q ); | ||
2137 | // Remember, char is sometimes signed. (How many times has that bitten me?) | ||
2138 | if ( *q > 0 && *q < ENTITY_RANGE ) { | ||
2139 | // Check for entities. If one is found, flush | ||
2140 | // the stream up until the entity, write the | ||
2141 | // entity, and keep looking. | ||
2142 | if ( flag[(unsigned char)(*q)] ) { | ||
2143 | while ( p < q ) { | ||
2144 | const size_t delta = q - p; | ||
2145 | // %.*s accepts type int as "precision" | ||
2146 | const int toPrint = ( INT_MAX < delta ) ? INT_MAX : (int)delta; | ||
2147 | Print( "%.*s", toPrint, p ); | ||
2148 | p += toPrint; | ||
2149 | } | ||
2150 | bool entityPatternPrinted = false; | ||
2151 | for( int i=0; i<NUM_ENTITIES; ++i ) { | ||
2152 | if ( entities[i].value == *q ) { | ||
2153 | Print( "&%s;", entities[i].pattern ); | ||
2154 | entityPatternPrinted = true; | ||
2155 | break; | ||
2156 | } | ||
2157 | } | ||
2158 | if ( !entityPatternPrinted ) { | ||
2159 | // TIXMLASSERT( entityPatternPrinted ) causes gcc -Wunused-but-set-variable in release | ||
2160 | TIXMLASSERT( false ); | ||
2161 | } | ||
2162 | ++p; | ||
2163 | } | ||
2164 | } | ||
2165 | ++q; | ||
2166 | TIXMLASSERT( p <= q ); | ||
2167 | } | ||
2168 | } | ||
2169 | // Flush the remaining string. This will be the entire | ||
2170 | // string if an entity wasn't found. | ||
2171 | TIXMLASSERT( p <= q ); | ||
2172 | if ( !_processEntities || ( p < q ) ) { | ||
2173 | Print( "%s", p ); | ||
2174 | } | ||
2175 | } | ||
2176 | |||
2177 | |||
2178 | void XMLPrinter::PushHeader( bool writeBOM, bool writeDec ) | ||
2179 | { | ||
2180 | if ( writeBOM ) { | ||
2181 | static const unsigned char bom[] = { TIXML_UTF_LEAD_0, TIXML_UTF_LEAD_1, TIXML_UTF_LEAD_2, 0 }; | ||
2182 | Print( "%s", bom ); | ||
2183 | } | ||
2184 | if ( writeDec ) { | ||
2185 | PushDeclaration( "xml version=\"1.0\"" ); | ||
2186 | } | ||
2187 | } | ||
2188 | |||
2189 | |||
2190 | void XMLPrinter::OpenElement( const char* name, bool compactMode ) | ||
2191 | { | ||
2192 | SealElementIfJustOpened(); | ||
2193 | _stack.Push( name ); | ||
2194 | |||
2195 | if ( _textDepth < 0 && !_firstElement && !compactMode ) { | ||
2196 | Print( "\n" ); | ||
2197 | } | ||
2198 | if ( !compactMode ) { | ||
2199 | PrintSpace( _depth ); | ||
2200 | } | ||
2201 | |||
2202 | Print( "<%s", name ); | ||
2203 | _elementJustOpened = true; | ||
2204 | _firstElement = false; | ||
2205 | ++_depth; | ||
2206 | } | ||
2207 | |||
2208 | |||
2209 | void XMLPrinter::PushAttribute( const char* name, const char* value ) | ||
2210 | { | ||
2211 | TIXMLASSERT( _elementJustOpened ); | ||
2212 | Print( " %s=\"", name ); | ||
2213 | PrintString( value, false ); | ||
2214 | Print( "\"" ); | ||
2215 | } | ||
2216 | |||
2217 | |||
2218 | void XMLPrinter::PushAttribute( const char* name, int v ) | ||
2219 | { | ||
2220 | char buf[BUF_SIZE]; | ||
2221 | XMLUtil::ToStr( v, buf, BUF_SIZE ); | ||
2222 | PushAttribute( name, buf ); | ||
2223 | } | ||
2224 | |||
2225 | |||
2226 | void XMLPrinter::PushAttribute( const char* name, unsigned v ) | ||
2227 | { | ||
2228 | char buf[BUF_SIZE]; | ||
2229 | XMLUtil::ToStr( v, buf, BUF_SIZE ); | ||
2230 | PushAttribute( name, buf ); | ||
2231 | } | ||
2232 | |||
2233 | |||
2234 | void XMLPrinter::PushAttribute( const char* name, bool v ) | ||
2235 | { | ||
2236 | char buf[BUF_SIZE]; | ||
2237 | XMLUtil::ToStr( v, buf, BUF_SIZE ); | ||
2238 | PushAttribute( name, buf ); | ||
2239 | } | ||
2240 | |||
2241 | |||
2242 | void XMLPrinter::PushAttribute( const char* name, double v ) | ||
2243 | { | ||
2244 | char buf[BUF_SIZE]; | ||
2245 | XMLUtil::ToStr( v, buf, BUF_SIZE ); | ||
2246 | PushAttribute( name, buf ); | ||
2247 | } | ||
2248 | |||
2249 | |||
2250 | void XMLPrinter::CloseElement( bool compactMode ) | ||
2251 | { | ||
2252 | --_depth; | ||
2253 | const char* name = _stack.Pop(); | ||
2254 | |||
2255 | if ( _elementJustOpened ) { | ||
2256 | Print( "/>" ); | ||
2257 | } | ||
2258 | else { | ||
2259 | if ( _textDepth < 0 && !compactMode) { | ||
2260 | Print( "\n" ); | ||
2261 | PrintSpace( _depth ); | ||
2262 | } | ||
2263 | Print( "</%s>", name ); | ||
2264 | } | ||
2265 | |||
2266 | if ( _textDepth == _depth ) { | ||
2267 | _textDepth = -1; | ||
2268 | } | ||
2269 | if ( _depth == 0 && !compactMode) { | ||
2270 | Print( "\n" ); | ||
2271 | } | ||
2272 | _elementJustOpened = false; | ||
2273 | } | ||
2274 | |||
2275 | |||
2276 | void XMLPrinter::SealElementIfJustOpened() | ||
2277 | { | ||
2278 | if ( !_elementJustOpened ) { | ||
2279 | return; | ||
2280 | } | ||
2281 | _elementJustOpened = false; | ||
2282 | Print( ">" ); | ||
2283 | } | ||
2284 | |||
2285 | |||
2286 | void XMLPrinter::PushText( const char* text, bool cdata ) | ||
2287 | { | ||
2288 | _textDepth = _depth-1; | ||
2289 | |||
2290 | SealElementIfJustOpened(); | ||
2291 | if ( cdata ) { | ||
2292 | Print( "<![CDATA[%s]]>", text ); | ||
2293 | } | ||
2294 | else { | ||
2295 | PrintString( text, true ); | ||
2296 | } | ||
2297 | } | ||
2298 | |||
2299 | void XMLPrinter::PushText( int value ) | ||
2300 | { | ||
2301 | char buf[BUF_SIZE]; | ||
2302 | XMLUtil::ToStr( value, buf, BUF_SIZE ); | ||
2303 | PushText( buf, false ); | ||
2304 | } | ||
2305 | |||
2306 | |||
2307 | void XMLPrinter::PushText( unsigned value ) | ||
2308 | { | ||
2309 | char buf[BUF_SIZE]; | ||
2310 | XMLUtil::ToStr( value, buf, BUF_SIZE ); | ||
2311 | PushText( buf, false ); | ||
2312 | } | ||
2313 | |||
2314 | |||
2315 | void XMLPrinter::PushText( bool value ) | ||
2316 | { | ||
2317 | char buf[BUF_SIZE]; | ||
2318 | XMLUtil::ToStr( value, buf, BUF_SIZE ); | ||
2319 | PushText( buf, false ); | ||
2320 | } | ||
2321 | |||
2322 | |||
2323 | void XMLPrinter::PushText( float value ) | ||
2324 | { | ||
2325 | char buf[BUF_SIZE]; | ||
2326 | XMLUtil::ToStr( value, buf, BUF_SIZE ); | ||
2327 | PushText( buf, false ); | ||
2328 | } | ||
2329 | |||
2330 | |||
2331 | void XMLPrinter::PushText( double value ) | ||
2332 | { | ||
2333 | char buf[BUF_SIZE]; | ||
2334 | XMLUtil::ToStr( value, buf, BUF_SIZE ); | ||
2335 | PushText( buf, false ); | ||
2336 | } | ||
2337 | |||
2338 | |||
2339 | void XMLPrinter::PushComment( const char* comment ) | ||
2340 | { | ||
2341 | SealElementIfJustOpened(); | ||
2342 | if ( _textDepth < 0 && !_firstElement && !_compactMode) { | ||
2343 | Print( "\n" ); | ||
2344 | PrintSpace( _depth ); | ||
2345 | } | ||
2346 | _firstElement = false; | ||
2347 | Print( "<!--%s-->", comment ); | ||
2348 | } | ||
2349 | |||
2350 | |||
2351 | void XMLPrinter::PushDeclaration( const char* value ) | ||
2352 | { | ||
2353 | SealElementIfJustOpened(); | ||
2354 | if ( _textDepth < 0 && !_firstElement && !_compactMode) { | ||
2355 | Print( "\n" ); | ||
2356 | PrintSpace( _depth ); | ||
2357 | } | ||
2358 | _firstElement = false; | ||
2359 | Print( "<?%s?>", value ); | ||
2360 | } | ||
2361 | |||
2362 | |||
2363 | void XMLPrinter::PushUnknown( const char* value ) | ||
2364 | { | ||
2365 | SealElementIfJustOpened(); | ||
2366 | if ( _textDepth < 0 && !_firstElement && !_compactMode) { | ||
2367 | Print( "\n" ); | ||
2368 | PrintSpace( _depth ); | ||
2369 | } | ||
2370 | _firstElement = false; | ||
2371 | Print( "<!%s>", value ); | ||
2372 | } | ||
2373 | |||
2374 | |||
2375 | bool XMLPrinter::VisitEnter( const XMLDocument& doc ) | ||
2376 | { | ||
2377 | _processEntities = doc.ProcessEntities(); | ||
2378 | if ( doc.HasBOM() ) { | ||
2379 | PushHeader( true, false ); | ||
2380 | } | ||
2381 | return true; | ||
2382 | } | ||
2383 | |||
2384 | |||
2385 | bool XMLPrinter::VisitEnter( const XMLElement& element, const XMLAttribute* attribute ) | ||
2386 | { | ||
2387 | const XMLElement* parentElem = 0; | ||
2388 | if ( element.Parent() ) { | ||
2389 | parentElem = element.Parent()->ToElement(); | ||
2390 | } | ||
2391 | const bool compactMode = parentElem ? CompactMode( *parentElem ) : _compactMode; | ||
2392 | OpenElement( element.Name(), compactMode ); | ||
2393 | while ( attribute ) { | ||
2394 | PushAttribute( attribute->Name(), attribute->Value() ); | ||
2395 | attribute = attribute->Next(); | ||
2396 | } | ||
2397 | return true; | ||
2398 | } | ||
2399 | |||
2400 | |||
2401 | bool XMLPrinter::VisitExit( const XMLElement& element ) | ||
2402 | { | ||
2403 | CloseElement( CompactMode(element) ); | ||
2404 | return true; | ||
2405 | } | ||
2406 | |||
2407 | |||
2408 | bool XMLPrinter::Visit( const XMLText& text ) | ||
2409 | { | ||
2410 | PushText( text.Value(), text.CData() ); | ||
2411 | return true; | ||
2412 | } | ||
2413 | |||
2414 | |||
2415 | bool XMLPrinter::Visit( const XMLComment& comment ) | ||
2416 | { | ||
2417 | PushComment( comment.Value() ); | ||
2418 | return true; | ||
2419 | } | ||
2420 | |||
2421 | bool XMLPrinter::Visit( const XMLDeclaration& declaration ) | ||
2422 | { | ||
2423 | PushDeclaration( declaration.Value() ); | ||
2424 | return true; | ||
2425 | } | ||
2426 | |||
2427 | |||
2428 | bool XMLPrinter::Visit( const XMLUnknown& unknown ) | ||
2429 | { | ||
2430 | PushUnknown( unknown.Value() ); | ||
2431 | return true; | ||
2432 | } | ||
2433 | |||
2434 | } // namespace tinyxml2 | ||
2435 | |||