2842 lines
		
	
	
		
			72 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			2842 lines
		
	
	
		
			72 KiB
		
	
	
	
		
			C++
		
	
	
	
/*
 | 
						|
Original code by Lee Thomason (www.grinninglizard.com)
 | 
						|
 | 
						|
This software is provided 'as-is', without any express or implied
 | 
						|
warranty. In no event will the authors be held liable for any
 | 
						|
damages arising from the use of this software.
 | 
						|
 | 
						|
Permission is granted to anyone to use this software for any
 | 
						|
purpose, including commercial applications, and to alter it and
 | 
						|
redistribute it freely, subject to the following restrictions:
 | 
						|
 | 
						|
1. The origin of this software must not be misrepresented; you must
 | 
						|
not claim that you wrote the original software. If you use this
 | 
						|
software in a product, an acknowledgment in the product documentation
 | 
						|
would be appreciated but is not required.
 | 
						|
 | 
						|
2. Altered source versions must be plainly marked as such, and
 | 
						|
must not be misrepresented as being the original software.
 | 
						|
 | 
						|
3. This notice may not be removed or altered from any source
 | 
						|
distribution.
 | 
						|
*/
 | 
						|
 | 
						|
#include "tinyxml2.h"
 | 
						|
 | 
						|
#include <new>		// yes, this one new style header, is in the Android SDK.
 | 
						|
#if defined(ANDROID_NDK) || defined(__BORLANDC__) || defined(__QNXNTO__)
 | 
						|
#   include <stddef.h>
 | 
						|
#   include <stdarg.h>
 | 
						|
#else
 | 
						|
#   include <cstddef>
 | 
						|
#   include <cstdarg>
 | 
						|
#endif
 | 
						|
 | 
						|
#if defined(_MSC_VER) && (_MSC_VER >= 1400 ) && (!defined WINCE)
 | 
						|
	// Microsoft Visual Studio, version 2005 and higher. Not WinCE.
 | 
						|
	/*int _snprintf_s(
 | 
						|
	   wchar_t *buffer,
 | 
						|
	   size_t sizeOfBuffer,
 | 
						|
	   size_t count,
 | 
						|
	   const wchar_t *format [,
 | 
						|
		  argument] ...
 | 
						|
	);*/
 | 
						|
 | 
						|
	#define vsnprintf_s		_vsnwprintf_s
 | 
						|
	#define _vsnprintf		_vsnwprintf
 | 
						|
	#define strlen			wcslen
 | 
						|
	#define strncmp			wcsncmp
 | 
						|
	#define strchr			wcschr
 | 
						|
	#define fopen_s			_wfopen_s
 | 
						|
	#define vfprintf		vfwprintf
 | 
						|
	#define TIXML_VSCPRINTF	_vscwprintf
 | 
						|
	#define TIXML_SSCANF	swscanf_s
 | 
						|
 | 
						|
	static inline int TIXML_SNPRINTF( wchar_t* buffer, size_t size, const wchar_t* format, ... )
 | 
						|
	{
 | 
						|
		va_list va;
 | 
						|
		va_start( va, format );
 | 
						|
		const int result = vsnprintf_s( buffer, size, _TRUNCATE, format, va );
 | 
						|
		va_end( va );
 | 
						|
		return result;
 | 
						|
	}
 | 
						|
 | 
						|
	static inline int TIXML_VSNPRINTF( wchar_t* buffer, size_t size, const wchar_t* format, va_list va )
 | 
						|
	{
 | 
						|
		const int result = vsnprintf_s( buffer, size, _TRUNCATE, format, va );
 | 
						|
		return result;
 | 
						|
	}
 | 
						|
 | 
						|
#elif defined _MSC_VER
 | 
						|
	// Microsoft Visual Studio 2003 and earlier or WinCE
 | 
						|
	#define TIXML_SNPRINTF	_snprintf
 | 
						|
	#define TIXML_VSNPRINTF _vsnprintf
 | 
						|
	#define TIXML_SSCANF	sscanf
 | 
						|
	#if (_MSC_VER < 1400 ) && (!defined WINCE)
 | 
						|
		// Microsoft Visual Studio 2003 and not WinCE.
 | 
						|
		#define TIXML_VSCPRINTF   _vscprintf // VS2003's C runtime has this, but VC6 C runtime or WinCE SDK doesn't have.
 | 
						|
	#else
 | 
						|
		// Microsoft Visual Studio 2003 and earlier or WinCE.
 | 
						|
		static inline int TIXML_VSCPRINTF( const wchar_t* format, va_list va )
 | 
						|
		{
 | 
						|
			int len = 512;
 | 
						|
			for (;;) {
 | 
						|
				len = len*2;
 | 
						|
				wchar_t* str = new wchar_t[len]();
 | 
						|
				const int required = _vsnprintf(str, len, format, va);
 | 
						|
				delete[] str;
 | 
						|
				if ( required != -1 ) {
 | 
						|
					TIXMLASSERT( required >= 0 );
 | 
						|
					len = required;
 | 
						|
					break;
 | 
						|
				}
 | 
						|
			}
 | 
						|
			TIXMLASSERT( len >= 0 );
 | 
						|
			return len;
 | 
						|
		}
 | 
						|
	#endif
 | 
						|
#else
 | 
						|
	// GCC version 3 and higher
 | 
						|
	//#warning( TINYXML2_STR("Using sn* functions.") )
 | 
						|
	#define TIXML_SNPRINTF	snprintf
 | 
						|
	#define TIXML_VSNPRINTF	vsnprintf
 | 
						|
	static inline int TIXML_VSCPRINTF( const wchar_t* format, va_list va )
 | 
						|
	{
 | 
						|
		int len = vsnprintf( 0, 0, format, va );
 | 
						|
		TIXMLASSERT( len >= 0 );
 | 
						|
		return len;
 | 
						|
	}
 | 
						|
	#define TIXML_SSCANF   sscanf
 | 
						|
#endif
 | 
						|
 | 
						|
 | 
						|
static const wchar_t LINE_FEED				= (wchar_t)0x0a;			// all line endings are normalized to LF
 | 
						|
static const wchar_t LF = LINE_FEED;
 | 
						|
static const wchar_t CARRIAGE_RETURN		= (wchar_t)0x0d;			// CR gets filtered out
 | 
						|
static const wchar_t CR = CARRIAGE_RETURN;
 | 
						|
static const wchar_t SINGLE_QUOTE			= TINYXML2_CHAR('\'');
 | 
						|
static const wchar_t DOUBLE_QUOTE			= TINYXML2_CHAR('\"');
 | 
						|
 | 
						|
// Bunch of unicode info at:
 | 
						|
//		http://www.unicode.org/faq/utf_bom.html
 | 
						|
//	ef bb bf (Microsoft TINYXML2_STR("lead bytes")) - designates UTF-8
 | 
						|
 | 
						|
static const TINYXML2_UNSIGNED_CHAR TIXML_UTF_LEAD_0 = 0xefU;
 | 
						|
static const TINYXML2_UNSIGNED_CHAR TIXML_UTF_LEAD_1 = 0xbbU;
 | 
						|
static const TINYXML2_UNSIGNED_CHAR TIXML_UTF_LEAD_2 = 0xbfU;
 | 
						|
 | 
						|
namespace tinyxml2
 | 
						|
{
 | 
						|
 | 
						|
struct Entity {
 | 
						|
    const wchar_t* pattern;
 | 
						|
    int length;
 | 
						|
    wchar_t value;
 | 
						|
};
 | 
						|
 | 
						|
static const int NUM_ENTITIES = 5;
 | 
						|
static const Entity entities[NUM_ENTITIES] = {
 | 
						|
    { TINYXML2_STR("quot"), 4,	DOUBLE_QUOTE },
 | 
						|
    { TINYXML2_STR("amp"), 3,		TINYXML2_CHAR('&')  },
 | 
						|
    { TINYXML2_STR("apos"), 4,	SINGLE_QUOTE },
 | 
						|
    { TINYXML2_STR("lt"),	2, 		TINYXML2_CHAR('<')	 },
 | 
						|
    { TINYXML2_STR("gt"),	2,		TINYXML2_CHAR('>')	 }
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
StrPair::~StrPair()
 | 
						|
{
 | 
						|
    Reset();
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void StrPair::TransferTo( StrPair* other )
 | 
						|
{
 | 
						|
    if ( this == other ) {
 | 
						|
        return;
 | 
						|
    }
 | 
						|
    // This in effect implements the assignment operator by TINYXML2_STR("moving")
 | 
						|
    // ownership (as in auto_ptr).
 | 
						|
 | 
						|
    TIXMLASSERT( other != 0 );
 | 
						|
    TIXMLASSERT( other->_flags == 0 );
 | 
						|
    TIXMLASSERT( other->_start == 0 );
 | 
						|
    TIXMLASSERT( other->_end == 0 );
 | 
						|
 | 
						|
    other->Reset();
 | 
						|
 | 
						|
    other->_flags = _flags;
 | 
						|
    other->_start = _start;
 | 
						|
    other->_end = _end;
 | 
						|
 | 
						|
    _flags = 0;
 | 
						|
    _start = 0;
 | 
						|
    _end = 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void StrPair::Reset()
 | 
						|
{
 | 
						|
    if ( _flags & NEEDS_DELETE ) {
 | 
						|
        delete [] _start;
 | 
						|
    }
 | 
						|
    _flags = 0;
 | 
						|
    _start = 0;
 | 
						|
    _end = 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void StrPair::SetStr( const wchar_t* str, int flags )
 | 
						|
{
 | 
						|
    TIXMLASSERT( str );
 | 
						|
    Reset();
 | 
						|
    size_t len = strlen( str );
 | 
						|
    TIXMLASSERT( _start == 0 );
 | 
						|
    _start = new wchar_t[ len+1 ];
 | 
						|
    memcpy( _start, str, (len+1) * sizeof(wchar_t));
 | 
						|
    _end = _start + len;
 | 
						|
    _flags = flags | NEEDS_DELETE;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
wchar_t* StrPair::ParseText( wchar_t* p, const wchar_t* endTag, int strFlags, int* curLineNumPtr )
 | 
						|
{
 | 
						|
    TIXMLASSERT( p );
 | 
						|
    TIXMLASSERT( endTag && *endTag );
 | 
						|
	TIXMLASSERT(curLineNumPtr);
 | 
						|
 | 
						|
    wchar_t* start = p;
 | 
						|
    const wchar_t  endChar = *endTag;
 | 
						|
    size_t length = strlen( endTag );
 | 
						|
 | 
						|
    // Inner loop of text parsing.
 | 
						|
    while ( *p ) {
 | 
						|
        if ( *p == endChar && strncmp( p, endTag, length ) == 0 ) {
 | 
						|
            Set( start, p, strFlags );
 | 
						|
            return p + length;
 | 
						|
        } else if (*p == TINYXML2_CHAR('\n')) {
 | 
						|
            ++(*curLineNumPtr);
 | 
						|
        }
 | 
						|
        ++p;
 | 
						|
        TIXMLASSERT( p );
 | 
						|
    }
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
wchar_t* StrPair::ParseName( wchar_t* p )
 | 
						|
{
 | 
						|
    if ( !p || !(*p) ) {
 | 
						|
        return 0;
 | 
						|
    }
 | 
						|
    if ( !XMLUtil::IsNameStartChar( *p ) ) {
 | 
						|
        return 0;
 | 
						|
    }
 | 
						|
 | 
						|
    wchar_t* const start = p;
 | 
						|
    ++p;
 | 
						|
    while ( *p && XMLUtil::IsNameChar( *p ) ) {
 | 
						|
        ++p;
 | 
						|
    }
 | 
						|
 | 
						|
    Set( start, p, 0 );
 | 
						|
    return p;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void StrPair::CollapseWhitespace()
 | 
						|
{
 | 
						|
    // Adjusting _start would cause undefined behavior on delete[]
 | 
						|
    TIXMLASSERT( ( _flags & NEEDS_DELETE ) == 0 );
 | 
						|
    // Trim leading space.
 | 
						|
    _start = XMLUtil::SkipWhiteSpace( _start, 0 );
 | 
						|
 | 
						|
    if ( *_start ) {
 | 
						|
        const wchar_t* p = _start;	// the read pointer
 | 
						|
        wchar_t* q = _start;	// the write pointer
 | 
						|
 | 
						|
        while( *p ) {
 | 
						|
            if ( XMLUtil::IsWhiteSpace( *p )) {
 | 
						|
                p = XMLUtil::SkipWhiteSpace( p, 0 );
 | 
						|
                if ( *p == 0 ) {
 | 
						|
                    break;    // don't write to q; this trims the trailing space.
 | 
						|
                }
 | 
						|
                *q = TINYXML2_CHAR(' ');
 | 
						|
                ++q;
 | 
						|
            }
 | 
						|
            *q = *p;
 | 
						|
            ++q;
 | 
						|
            ++p;
 | 
						|
        }
 | 
						|
        *q = 0;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
const wchar_t* StrPair::GetStr()
 | 
						|
{
 | 
						|
    TIXMLASSERT( _start );
 | 
						|
    TIXMLASSERT( _end );
 | 
						|
    if ( _flags & NEEDS_FLUSH ) {
 | 
						|
        *_end = 0;
 | 
						|
        _flags ^= NEEDS_FLUSH;
 | 
						|
 | 
						|
        if ( _flags ) {
 | 
						|
            const wchar_t* p = _start;	// the read pointer
 | 
						|
            wchar_t* q = _start;	// the write pointer
 | 
						|
 | 
						|
            while( p < _end ) {
 | 
						|
                if ( (_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == CR ) {
 | 
						|
                    // CR-LF pair becomes LF
 | 
						|
                    // CR alone becomes LF
 | 
						|
                    // LF-CR becomes LF
 | 
						|
                    if ( *(p+1) == LF ) {
 | 
						|
                        p += 2;
 | 
						|
                    }
 | 
						|
                    else {
 | 
						|
                        ++p;
 | 
						|
                    }
 | 
						|
                    *q = LF;
 | 
						|
                    ++q;
 | 
						|
                }
 | 
						|
                else if ( (_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == LF ) {
 | 
						|
                    if ( *(p+1) == CR ) {
 | 
						|
                        p += 2;
 | 
						|
                    }
 | 
						|
                    else {
 | 
						|
                        ++p;
 | 
						|
                    }
 | 
						|
                    *q = LF;
 | 
						|
                    ++q;
 | 
						|
                }
 | 
						|
                else if ( (_flags & NEEDS_ENTITY_PROCESSING) && *p == TINYXML2_CHAR('&') ) {
 | 
						|
                    // Entities handled by tinyXML2:
 | 
						|
                    // - special entities in the entity table [in/out]
 | 
						|
                    // - numeric wchar_tacter reference [in]
 | 
						|
                    //   中 or 中
 | 
						|
 | 
						|
                    if ( *(p+1) == TINYXML2_CHAR('#') ) {
 | 
						|
                        const int buflen = 10;
 | 
						|
                        wchar_t buf[buflen] = { 0 };
 | 
						|
                        int len = 0;
 | 
						|
                        const wchar_t* adjusted = const_cast<wchar_t*>( XMLUtil::GetCharacterRef( p, buf, &len ) );
 | 
						|
                        if ( adjusted == 0 ) {
 | 
						|
                            *q = *p;
 | 
						|
                            ++p;
 | 
						|
                            ++q;
 | 
						|
                        }
 | 
						|
                        else {
 | 
						|
                            TIXMLASSERT( 0 <= len && len <= buflen );
 | 
						|
                            TIXMLASSERT( q + len <= adjusted );
 | 
						|
                            p = adjusted;
 | 
						|
                            memcpy( q, buf, len * sizeof(wchar_t));
 | 
						|
                            q += len;
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                    else {
 | 
						|
                        bool entityFound = false;
 | 
						|
                        for( int i = 0; i < NUM_ENTITIES; ++i ) {
 | 
						|
                            const Entity& entity = entities[i];
 | 
						|
                            if ( strncmp( p + 1, entity.pattern, entity.length ) == 0
 | 
						|
                                    && *( p + entity.length + 1 ) == TINYXML2_CHAR(';') ) {
 | 
						|
                                // Found an entity - convert.
 | 
						|
                                *q = entity.value;
 | 
						|
                                ++q;
 | 
						|
                                p += entity.length + 2;
 | 
						|
                                entityFound = true;
 | 
						|
                                break;
 | 
						|
                            }
 | 
						|
                        }
 | 
						|
                        if ( !entityFound ) {
 | 
						|
                            // fixme: treat as error?
 | 
						|
                            ++p;
 | 
						|
                            ++q;
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                }
 | 
						|
                else {
 | 
						|
                    *q = *p;
 | 
						|
                    ++p;
 | 
						|
                    ++q;
 | 
						|
                }
 | 
						|
            }
 | 
						|
            *q = 0;
 | 
						|
        }
 | 
						|
        // The loop below has plenty going on, and this
 | 
						|
        // is a less useful mode. Break it out.
 | 
						|
        if ( _flags & NEEDS_WHITESPACE_COLLAPSING ) {
 | 
						|
            CollapseWhitespace();
 | 
						|
        }
 | 
						|
        _flags = (_flags & NEEDS_DELETE);
 | 
						|
    }
 | 
						|
    TIXMLASSERT( _start );
 | 
						|
    return _start;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
// --------- XMLUtil ----------- //
 | 
						|
 | 
						|
const wchar_t* XMLUtil::writeBoolTrue  = TINYXML2_STR("true");
 | 
						|
const wchar_t* XMLUtil::writeBoolFalse = TINYXML2_STR("false");
 | 
						|
 | 
						|
void XMLUtil::SetBoolSerialization(const wchar_t* writeTrue, const wchar_t* writeFalse)
 | 
						|
{
 | 
						|
	static const wchar_t* defTrue  = TINYXML2_STR("true");
 | 
						|
	static const wchar_t* defFalse = TINYXML2_STR("false");
 | 
						|
 | 
						|
	writeBoolTrue = (writeTrue) ? writeTrue : defTrue;
 | 
						|
	writeBoolFalse = (writeFalse) ? writeFalse : defFalse;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
const wchar_t* XMLUtil::ReadBOM( const wchar_t* p, bool* bom )
 | 
						|
{
 | 
						|
    TIXMLASSERT( p );
 | 
						|
    TIXMLASSERT( bom );
 | 
						|
    *bom = false;
 | 
						|
    const TINYXML2_UNSIGNED_CHAR* pu = reinterpret_cast<const TINYXML2_UNSIGNED_CHAR*>(p);
 | 
						|
    // Check for BOM:
 | 
						|
    if (    *(pu+0) == TIXML_UTF_LEAD_0
 | 
						|
            && *(pu+1) == TIXML_UTF_LEAD_1
 | 
						|
            && *(pu+2) == TIXML_UTF_LEAD_2 ) {
 | 
						|
        *bom = true;
 | 
						|
        p += 3;
 | 
						|
    }
 | 
						|
    TIXMLASSERT( p );
 | 
						|
    return p;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void XMLUtil::ConvertUTF32ToUTF8( unsigned long input, wchar_t* output, int* length )
 | 
						|
{
 | 
						|
    const unsigned long BYTE_MASK = 0xBF;
 | 
						|
    const unsigned long BYTE_MARK = 0x80;
 | 
						|
    const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
 | 
						|
 | 
						|
    if (input < 0x80) {
 | 
						|
        *length = 1;
 | 
						|
    }
 | 
						|
    else if ( input < 0x800 ) {
 | 
						|
        *length = 2;
 | 
						|
    }
 | 
						|
    else if ( input < 0x10000 ) {
 | 
						|
        *length = 3;
 | 
						|
    }
 | 
						|
    else if ( input < 0x200000 ) {
 | 
						|
        *length = 4;
 | 
						|
    }
 | 
						|
    else {
 | 
						|
        *length = 0;    // This code won't convert this correctly anyway.
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    output += *length;
 | 
						|
 | 
						|
    // Scary scary fall throughs are annotated with carefully designed comments
 | 
						|
    // to suppress compiler warnings such as -Wimplicit-fallthrough in gcc
 | 
						|
    switch (*length) {
 | 
						|
        case 4:
 | 
						|
            --output;
 | 
						|
            *output = (wchar_t)((input | BYTE_MARK) & BYTE_MASK);
 | 
						|
            input >>= 6;
 | 
						|
            //fall through
 | 
						|
        case 3:
 | 
						|
            --output;
 | 
						|
            *output = (wchar_t)((input | BYTE_MARK) & BYTE_MASK);
 | 
						|
            input >>= 6;
 | 
						|
            //fall through
 | 
						|
        case 2:
 | 
						|
            --output;
 | 
						|
            *output = (wchar_t)((input | BYTE_MARK) & BYTE_MASK);
 | 
						|
            input >>= 6;
 | 
						|
            //fall through
 | 
						|
        case 1:
 | 
						|
            --output;
 | 
						|
            *output = (wchar_t)(input | FIRST_BYTE_MARK[*length]);
 | 
						|
            break;
 | 
						|
        default:
 | 
						|
            TIXMLASSERT( false );
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
const wchar_t* XMLUtil::GetCharacterRef( const wchar_t* p, wchar_t* value, int* length )
 | 
						|
{
 | 
						|
    // Presume an entity, and pull it out.
 | 
						|
    *length = 0;
 | 
						|
 | 
						|
    if ( *(p+1) == TINYXML2_CHAR('#') && *(p+2) ) {
 | 
						|
        unsigned long ucs = 0;
 | 
						|
        TIXMLASSERT( sizeof( ucs ) >= 4 );
 | 
						|
        ptrdiff_t delta = 0;
 | 
						|
        unsigned mult = 1;
 | 
						|
        static const wchar_t SEMICOLON = TINYXML2_CHAR(';');
 | 
						|
 | 
						|
        if ( *(p+2) == TINYXML2_CHAR('x') ) {
 | 
						|
            // Hexadecimal.
 | 
						|
            const wchar_t* q = p+3;
 | 
						|
            if ( !(*q) ) {
 | 
						|
                return 0;
 | 
						|
            }
 | 
						|
 | 
						|
            q = strchr( q, SEMICOLON );
 | 
						|
 | 
						|
            if ( !q ) {
 | 
						|
                return 0;
 | 
						|
            }
 | 
						|
            TIXMLASSERT( *q == SEMICOLON );
 | 
						|
 | 
						|
            delta = q-p;
 | 
						|
            --q;
 | 
						|
 | 
						|
            while ( *q != TINYXML2_CHAR('x') ) {
 | 
						|
                unsigned int digit = 0;
 | 
						|
 | 
						|
                if ( *q >= TINYXML2_CHAR('0') && *q <= TINYXML2_CHAR('9') ) {
 | 
						|
                    digit = *q - TINYXML2_CHAR('0');
 | 
						|
                }
 | 
						|
                else if ( *q >= TINYXML2_CHAR('a') && *q <= TINYXML2_CHAR('f') ) {
 | 
						|
                    digit = *q - TINYXML2_CHAR('a') + 10;
 | 
						|
                }
 | 
						|
                else if ( *q >= TINYXML2_CHAR('A') && *q <= TINYXML2_CHAR('F') ) {
 | 
						|
                    digit = *q - TINYXML2_CHAR('A') + 10;
 | 
						|
                }
 | 
						|
                else {
 | 
						|
                    return 0;
 | 
						|
                }
 | 
						|
                TIXMLASSERT( digit < 16 );
 | 
						|
                TIXMLASSERT( digit == 0 || mult <= UINT_MAX / digit );
 | 
						|
                const unsigned int digitScaled = mult * digit;
 | 
						|
                TIXMLASSERT( ucs <= ULONG_MAX - digitScaled );
 | 
						|
                ucs += digitScaled;
 | 
						|
                TIXMLASSERT( mult <= UINT_MAX / 16 );
 | 
						|
                mult *= 16;
 | 
						|
                --q;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            // Decimal.
 | 
						|
            const wchar_t* q = p+2;
 | 
						|
            if ( !(*q) ) {
 | 
						|
                return 0;
 | 
						|
            }
 | 
						|
 | 
						|
            q = strchr( q, SEMICOLON );
 | 
						|
 | 
						|
            if ( !q ) {
 | 
						|
                return 0;
 | 
						|
            }
 | 
						|
            TIXMLASSERT( *q == SEMICOLON );
 | 
						|
 | 
						|
            delta = q-p;
 | 
						|
            --q;
 | 
						|
 | 
						|
            while ( *q != TINYXML2_CHAR('#') ) {
 | 
						|
                if ( *q >= TINYXML2_CHAR('0') && *q <= TINYXML2_CHAR('9') ) {
 | 
						|
                    const unsigned int digit = *q - TINYXML2_CHAR('0');
 | 
						|
                    TIXMLASSERT( digit < 10 );
 | 
						|
                    TIXMLASSERT( digit == 0 || mult <= UINT_MAX / digit );
 | 
						|
                    const unsigned int digitScaled = mult * digit;
 | 
						|
                    TIXMLASSERT( ucs <= ULONG_MAX - digitScaled );
 | 
						|
                    ucs += digitScaled;
 | 
						|
                }
 | 
						|
                else {
 | 
						|
                    return 0;
 | 
						|
                }
 | 
						|
                TIXMLASSERT( mult <= UINT_MAX / 10 );
 | 
						|
                mult *= 10;
 | 
						|
                --q;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        // convert the UCS to UTF-8
 | 
						|
        ConvertUTF32ToUTF8( ucs, value, length );
 | 
						|
        return p + delta + 1;
 | 
						|
    }
 | 
						|
    return p+1;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void XMLUtil::ToStr( int v, wchar_t* buffer, int bufferSize )
 | 
						|
{
 | 
						|
    TIXML_SNPRINTF( buffer, bufferSize, TINYXML2_STR("%d"), v );
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void XMLUtil::ToStr( unsigned v, wchar_t* buffer, int bufferSize )
 | 
						|
{
 | 
						|
    TIXML_SNPRINTF( buffer, bufferSize, TINYXML2_STR("%u"), v );
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void XMLUtil::ToStr( bool v, wchar_t* buffer, int bufferSize )
 | 
						|
{
 | 
						|
    TIXML_SNPRINTF( buffer, bufferSize, TINYXML2_STR("%s"), v ? writeBoolTrue : writeBoolFalse);
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
	ToStr() of a number is a very tricky topic.
 | 
						|
	https://github.com/leethomason/tinyxml2/issues/106
 | 
						|
*/
 | 
						|
void XMLUtil::ToStr( float v, wchar_t* buffer, int bufferSize )
 | 
						|
{
 | 
						|
    TIXML_SNPRINTF( buffer, bufferSize, TINYXML2_STR("%.8g"), v );
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void XMLUtil::ToStr( double v, wchar_t* buffer, int bufferSize )
 | 
						|
{
 | 
						|
    TIXML_SNPRINTF( buffer, bufferSize, TINYXML2_STR("%.17g"), v );
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void XMLUtil::ToStr(int64_t v, wchar_t* buffer, int bufferSize)
 | 
						|
{
 | 
						|
	// horrible syntax trick to make the compiler happy about %lld
 | 
						|
	TIXML_SNPRINTF(buffer, bufferSize, TINYXML2_STR("%lld"), (long long)v);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool XMLUtil::ToInt( const wchar_t* str, int* value )
 | 
						|
{
 | 
						|
    if ( TIXML_SSCANF( str, TINYXML2_STR("%d"), value ) == 1 ) {
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
    return false;
 | 
						|
}
 | 
						|
 | 
						|
bool XMLUtil::ToUnsigned( const wchar_t* str, unsigned *value )
 | 
						|
{
 | 
						|
    if ( TIXML_SSCANF( str, TINYXML2_STR("%u"), value ) == 1 ) {
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
    return false;
 | 
						|
}
 | 
						|
 | 
						|
bool XMLUtil::ToBool( const wchar_t* str, bool* value )
 | 
						|
{
 | 
						|
    int ival = 0;
 | 
						|
    if ( ToInt( str, &ival )) {
 | 
						|
        *value = (ival==0) ? false : true;
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
    if ( StringEqual( str, TINYXML2_STR("true") ) ) {
 | 
						|
        *value = true;
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
    else if ( StringEqual( str, TINYXML2_STR("false") ) ) {
 | 
						|
        *value = false;
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
    return false;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool XMLUtil::ToFloat( const wchar_t* str, float* value )
 | 
						|
{
 | 
						|
    if ( TIXML_SSCANF( str, TINYXML2_STR("%f"), value ) == 1 ) {
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
    return false;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool XMLUtil::ToDouble( const wchar_t* str, double* value )
 | 
						|
{
 | 
						|
    if ( TIXML_SSCANF( str, TINYXML2_STR("%lf"), value ) == 1 ) {
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
    return false;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool XMLUtil::ToInt64(const wchar_t* str, int64_t* value)
 | 
						|
{
 | 
						|
	long long v = 0;	// horrible syntax trick to make the compiler happy about %lld
 | 
						|
	if (TIXML_SSCANF(str, TINYXML2_STR("%lld"), &v) == 1) {
 | 
						|
		*value = (int64_t)v;
 | 
						|
		return true;
 | 
						|
	}
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
wchar_t* XMLDocument::Identify( wchar_t* p, XMLNode** node )
 | 
						|
{
 | 
						|
    TIXMLASSERT( node );
 | 
						|
    TIXMLASSERT( p );
 | 
						|
    wchar_t* const start = p;
 | 
						|
    int const startLine = _parseCurLineNum;
 | 
						|
    p = XMLUtil::SkipWhiteSpace( p, &_parseCurLineNum );
 | 
						|
    if( !*p ) {
 | 
						|
        *node = 0;
 | 
						|
        TIXMLASSERT( p );
 | 
						|
        return p;
 | 
						|
    }
 | 
						|
 | 
						|
    // These strings define the matching patterns:
 | 
						|
    static const wchar_t* xmlHeader		= { TINYXML2_STR("<?") };
 | 
						|
    static const wchar_t* commentHeader	= { TINYXML2_STR("<!--") };
 | 
						|
    static const wchar_t* cdataHeader		= { TINYXML2_STR("<![CDATA[") };
 | 
						|
    static const wchar_t* dtdHeader		= { TINYXML2_STR("<!") };
 | 
						|
    static const wchar_t* elementHeader	= { TINYXML2_STR("<") };	// and a header for everything else; check last.
 | 
						|
 | 
						|
    static const int xmlHeaderLen		= 2;
 | 
						|
    static const int commentHeaderLen	= 4;
 | 
						|
    static const int cdataHeaderLen		= 9;
 | 
						|
    static const int dtdHeaderLen		= 2;
 | 
						|
    static const int elementHeaderLen	= 1;
 | 
						|
 | 
						|
    TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLUnknown ) );		// use same memory pool
 | 
						|
    TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLDeclaration ) );	// use same memory pool
 | 
						|
    XMLNode* returnNode = 0;
 | 
						|
    if ( XMLUtil::StringEqual( p, xmlHeader, xmlHeaderLen ) ) {
 | 
						|
        returnNode = CreateUnlinkedNode<XMLDeclaration>( _commentPool );
 | 
						|
        returnNode->_parseLineNum = _parseCurLineNum;
 | 
						|
        p += xmlHeaderLen;
 | 
						|
    }
 | 
						|
    else if ( XMLUtil::StringEqual( p, commentHeader, commentHeaderLen ) ) {
 | 
						|
        returnNode = CreateUnlinkedNode<XMLComment>( _commentPool );
 | 
						|
        returnNode->_parseLineNum = _parseCurLineNum;
 | 
						|
        p += commentHeaderLen;
 | 
						|
    }
 | 
						|
    else if ( XMLUtil::StringEqual( p, cdataHeader, cdataHeaderLen ) ) {
 | 
						|
        XMLText* text = CreateUnlinkedNode<XMLText>( _textPool );
 | 
						|
        returnNode = text;
 | 
						|
        returnNode->_parseLineNum = _parseCurLineNum;
 | 
						|
        p += cdataHeaderLen;
 | 
						|
        text->SetCData( true );
 | 
						|
    }
 | 
						|
    else if ( XMLUtil::StringEqual( p, dtdHeader, dtdHeaderLen ) ) {
 | 
						|
        returnNode = CreateUnlinkedNode<XMLUnknown>( _commentPool );
 | 
						|
        returnNode->_parseLineNum = _parseCurLineNum;
 | 
						|
        p += dtdHeaderLen;
 | 
						|
    }
 | 
						|
    else if ( XMLUtil::StringEqual( p, elementHeader, elementHeaderLen ) ) {
 | 
						|
        returnNode =  CreateUnlinkedNode<XMLElement>( _elementPool );
 | 
						|
        returnNode->_parseLineNum = _parseCurLineNum;
 | 
						|
        p += elementHeaderLen;
 | 
						|
    }
 | 
						|
    else {
 | 
						|
        returnNode = CreateUnlinkedNode<XMLText>( _textPool );
 | 
						|
        returnNode->_parseLineNum = _parseCurLineNum; // Report line of first non-whitespace wchar_tacter
 | 
						|
        p = start;	// Back it up, all the text counts.
 | 
						|
        _parseCurLineNum = startLine;
 | 
						|
    }
 | 
						|
 | 
						|
    TIXMLASSERT( returnNode );
 | 
						|
    TIXMLASSERT( p );
 | 
						|
    *node = returnNode;
 | 
						|
    return p;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool XMLDocument::Accept( XMLVisitor* visitor ) const
 | 
						|
{
 | 
						|
    TIXMLASSERT( visitor );
 | 
						|
    if ( visitor->VisitEnter( *this ) ) {
 | 
						|
        for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) {
 | 
						|
            if ( !node->Accept( visitor ) ) {
 | 
						|
                break;
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
    return visitor->VisitExit( *this );
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
// --------- XMLNode ----------- //
 | 
						|
 | 
						|
XMLNode::XMLNode( XMLDocument* doc ) :
 | 
						|
    _document( doc ),
 | 
						|
    _parent( 0 ),
 | 
						|
    _value(),
 | 
						|
    _parseLineNum( 0 ),
 | 
						|
    _firstChild( 0 ), _lastChild( 0 ),
 | 
						|
    _prev( 0 ), _next( 0 ),
 | 
						|
	_userData( 0 ),
 | 
						|
    _memPool( 0 )
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
XMLNode::~XMLNode()
 | 
						|
{
 | 
						|
    DeleteChildren();
 | 
						|
    if ( _parent ) {
 | 
						|
        _parent->Unlink( this );
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
const wchar_t* XMLNode::Value() const
 | 
						|
{
 | 
						|
    // Edge case: XMLDocuments don't have a Value. Return null.
 | 
						|
    if ( this->ToDocument() )
 | 
						|
        return 0;
 | 
						|
    return _value.GetStr();
 | 
						|
}
 | 
						|
 | 
						|
void XMLNode::SetValue( const wchar_t* str, bool staticMem )
 | 
						|
{
 | 
						|
    if ( staticMem ) {
 | 
						|
        _value.SetInternedStr( str );
 | 
						|
    }
 | 
						|
    else {
 | 
						|
        _value.SetStr( str );
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
XMLNode* XMLNode::DeepClone(XMLDocument* target) const
 | 
						|
{
 | 
						|
	XMLNode* clone = this->ShallowClone(target);
 | 
						|
	if (!clone) return 0;
 | 
						|
 | 
						|
	for (const XMLNode* child = this->FirstChild(); child; child = child->NextSibling()) {
 | 
						|
		XMLNode* childClone = child->DeepClone(target);
 | 
						|
		TIXMLASSERT(childClone);
 | 
						|
		clone->InsertEndChild(childClone);
 | 
						|
	}
 | 
						|
	return clone;
 | 
						|
}
 | 
						|
 | 
						|
void XMLNode::DeleteChildren()
 | 
						|
{
 | 
						|
    while( _firstChild ) {
 | 
						|
        TIXMLASSERT( _lastChild );
 | 
						|
        DeleteChild( _firstChild );
 | 
						|
    }
 | 
						|
    _firstChild = _lastChild = 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void XMLNode::Unlink( XMLNode* child )
 | 
						|
{
 | 
						|
    TIXMLASSERT( child );
 | 
						|
    TIXMLASSERT( child->_document == _document );
 | 
						|
    TIXMLASSERT( child->_parent == this );
 | 
						|
    if ( child == _firstChild ) {
 | 
						|
        _firstChild = _firstChild->_next;
 | 
						|
    }
 | 
						|
    if ( child == _lastChild ) {
 | 
						|
        _lastChild = _lastChild->_prev;
 | 
						|
    }
 | 
						|
 | 
						|
    if ( child->_prev ) {
 | 
						|
        child->_prev->_next = child->_next;
 | 
						|
    }
 | 
						|
    if ( child->_next ) {
 | 
						|
        child->_next->_prev = child->_prev;
 | 
						|
    }
 | 
						|
	child->_next = 0;
 | 
						|
	child->_prev = 0;
 | 
						|
	child->_parent = 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void XMLNode::DeleteChild( XMLNode* node )
 | 
						|
{
 | 
						|
    TIXMLASSERT( node );
 | 
						|
    TIXMLASSERT( node->_document == _document );
 | 
						|
    TIXMLASSERT( node->_parent == this );
 | 
						|
    Unlink( node );
 | 
						|
	TIXMLASSERT(node->_prev == 0);
 | 
						|
	TIXMLASSERT(node->_next == 0);
 | 
						|
	TIXMLASSERT(node->_parent == 0);
 | 
						|
    DeleteNode( node );
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
XMLNode* XMLNode::InsertEndChild( XMLNode* addThis )
 | 
						|
{
 | 
						|
    TIXMLASSERT( addThis );
 | 
						|
    if ( addThis->_document != _document ) {
 | 
						|
        TIXMLASSERT( false );
 | 
						|
        return 0;
 | 
						|
    }
 | 
						|
    InsertChildPreamble( addThis );
 | 
						|
 | 
						|
    if ( _lastChild ) {
 | 
						|
        TIXMLASSERT( _firstChild );
 | 
						|
        TIXMLASSERT( _lastChild->_next == 0 );
 | 
						|
        _lastChild->_next = addThis;
 | 
						|
        addThis->_prev = _lastChild;
 | 
						|
        _lastChild = addThis;
 | 
						|
 | 
						|
        addThis->_next = 0;
 | 
						|
    }
 | 
						|
    else {
 | 
						|
        TIXMLASSERT( _firstChild == 0 );
 | 
						|
        _firstChild = _lastChild = addThis;
 | 
						|
 | 
						|
        addThis->_prev = 0;
 | 
						|
        addThis->_next = 0;
 | 
						|
    }
 | 
						|
    addThis->_parent = this;
 | 
						|
    return addThis;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
XMLNode* XMLNode::InsertFirstChild( XMLNode* addThis )
 | 
						|
{
 | 
						|
    TIXMLASSERT( addThis );
 | 
						|
    if ( addThis->_document != _document ) {
 | 
						|
        TIXMLASSERT( false );
 | 
						|
        return 0;
 | 
						|
    }
 | 
						|
    InsertChildPreamble( addThis );
 | 
						|
 | 
						|
    if ( _firstChild ) {
 | 
						|
        TIXMLASSERT( _lastChild );
 | 
						|
        TIXMLASSERT( _firstChild->_prev == 0 );
 | 
						|
 | 
						|
        _firstChild->_prev = addThis;
 | 
						|
        addThis->_next = _firstChild;
 | 
						|
        _firstChild = addThis;
 | 
						|
 | 
						|
        addThis->_prev = 0;
 | 
						|
    }
 | 
						|
    else {
 | 
						|
        TIXMLASSERT( _lastChild == 0 );
 | 
						|
        _firstChild = _lastChild = addThis;
 | 
						|
 | 
						|
        addThis->_prev = 0;
 | 
						|
        addThis->_next = 0;
 | 
						|
    }
 | 
						|
    addThis->_parent = this;
 | 
						|
    return addThis;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
XMLNode* XMLNode::InsertAfterChild( XMLNode* afterThis, XMLNode* addThis )
 | 
						|
{
 | 
						|
    TIXMLASSERT( addThis );
 | 
						|
    if ( addThis->_document != _document ) {
 | 
						|
        TIXMLASSERT( false );
 | 
						|
        return 0;
 | 
						|
    }
 | 
						|
 | 
						|
    TIXMLASSERT( afterThis );
 | 
						|
 | 
						|
    if ( afterThis->_parent != this ) {
 | 
						|
        TIXMLASSERT( false );
 | 
						|
        return 0;
 | 
						|
    }
 | 
						|
    if ( afterThis == addThis ) {
 | 
						|
        // Current state: BeforeThis -> AddThis -> OneAfterAddThis
 | 
						|
        // Now AddThis must disappear from it's location and then
 | 
						|
        // reappear between BeforeThis and OneAfterAddThis.
 | 
						|
        // So just leave it where it is.
 | 
						|
        return addThis;
 | 
						|
    }
 | 
						|
 | 
						|
    if ( afterThis->_next == 0 ) {
 | 
						|
        // The last node or the only node.
 | 
						|
        return InsertEndChild( addThis );
 | 
						|
    }
 | 
						|
    InsertChildPreamble( addThis );
 | 
						|
    addThis->_prev = afterThis;
 | 
						|
    addThis->_next = afterThis->_next;
 | 
						|
    afterThis->_next->_prev = addThis;
 | 
						|
    afterThis->_next = addThis;
 | 
						|
    addThis->_parent = this;
 | 
						|
    return addThis;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
const XMLElement* XMLNode::FirstChildElement( const wchar_t* name ) const
 | 
						|
{
 | 
						|
    for( const XMLNode* node = _firstChild; node; node = node->_next ) {
 | 
						|
        const XMLElement* element = node->ToElementWithName( name );
 | 
						|
        if ( element ) {
 | 
						|
            return element;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
const XMLElement* XMLNode::LastChildElement( const wchar_t* name ) const
 | 
						|
{
 | 
						|
    for( const XMLNode* node = _lastChild; node; node = node->_prev ) {
 | 
						|
        const XMLElement* element = node->ToElementWithName( name );
 | 
						|
        if ( element ) {
 | 
						|
            return element;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
const XMLElement* XMLNode::NextSiblingElement( const wchar_t* name ) const
 | 
						|
{
 | 
						|
    for( const XMLNode* node = _next; node; node = node->_next ) {
 | 
						|
        const XMLElement* element = node->ToElementWithName( name );
 | 
						|
        if ( element ) {
 | 
						|
            return element;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
const XMLElement* XMLNode::PreviousSiblingElement( const wchar_t* name ) const
 | 
						|
{
 | 
						|
    for( const XMLNode* node = _prev; node; node = node->_prev ) {
 | 
						|
        const XMLElement* element = node->ToElementWithName( name );
 | 
						|
        if ( element ) {
 | 
						|
            return element;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
wchar_t* XMLNode::ParseDeep( wchar_t* p, StrPair* parentEndTag, int* curLineNumPtr )
 | 
						|
{
 | 
						|
    // This is a recursive method, but thinking about it TINYXML2_STR("at the current level")
 | 
						|
    // it is a pretty simple flat list:
 | 
						|
    //		<foo/>
 | 
						|
    //		<!-- comment -->
 | 
						|
    //
 | 
						|
    // With a special case:
 | 
						|
    //		<foo>
 | 
						|
    //		</foo>
 | 
						|
    //		<!-- comment -->
 | 
						|
    //
 | 
						|
    // Where the closing element (/foo) *must* be the next thing after the opening
 | 
						|
    // element, and the names must match. BUT the tricky bit is that the closing
 | 
						|
    // element will be read by the child.
 | 
						|
    //
 | 
						|
    // 'endTag' is the end tag for this node, it is returned by a call to a child.
 | 
						|
    // 'parentEnd' is the end tag for the parent, which is filled in and returned.
 | 
						|
 | 
						|
	XMLDocument::DepthTracker tracker(_document);
 | 
						|
	if (_document->Error())
 | 
						|
		return 0;
 | 
						|
 | 
						|
	while( p && *p ) {
 | 
						|
        XMLNode* node = 0;
 | 
						|
 | 
						|
        p = _document->Identify( p, &node );
 | 
						|
        TIXMLASSERT( p );
 | 
						|
        if ( node == 0 ) {
 | 
						|
            break;
 | 
						|
        }
 | 
						|
 | 
						|
       const int initialLineNum = node->_parseLineNum;
 | 
						|
 | 
						|
        StrPair endTag;
 | 
						|
        p = node->ParseDeep( p, &endTag, curLineNumPtr );
 | 
						|
        if ( !p ) {
 | 
						|
            DeleteNode( node );
 | 
						|
            if ( !_document->Error() ) {
 | 
						|
                _document->SetError( XML_ERROR_PARSING, initialLineNum, 0);
 | 
						|
            }
 | 
						|
            break;
 | 
						|
        }
 | 
						|
 | 
						|
        const XMLDeclaration* const decl = node->ToDeclaration();
 | 
						|
        if ( decl ) {
 | 
						|
            // Declarations are only allowed at document level
 | 
						|
            //
 | 
						|
            // Multiple declarations are allowed but all declarations
 | 
						|
            // must occur before anything else. 
 | 
						|
            //
 | 
						|
            // Optimized due to a security test case. If the first node is 
 | 
						|
            // a declaration, and the last node is a declaration, then only 
 | 
						|
            // declarations have so far been added.
 | 
						|
            bool wellLocated = false;
 | 
						|
 | 
						|
            if (ToDocument()) {
 | 
						|
                if (FirstChild()) {
 | 
						|
                    wellLocated =
 | 
						|
                        FirstChild() &&
 | 
						|
                        FirstChild()->ToDeclaration() &&
 | 
						|
                        LastChild() &&
 | 
						|
                        LastChild()->ToDeclaration();
 | 
						|
                }
 | 
						|
                else {
 | 
						|
                    wellLocated = true;
 | 
						|
                }
 | 
						|
            }
 | 
						|
            if ( !wellLocated ) {
 | 
						|
                _document->SetError( XML_ERROR_PARSING_DECLARATION, initialLineNum, TINYXML2_STR("XMLDeclaration value=%s"), decl->Value());
 | 
						|
                DeleteNode( node );
 | 
						|
                break;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        XMLElement* ele = node->ToElement();
 | 
						|
        if ( ele ) {
 | 
						|
            // We read the end tag. Return it to the parent.
 | 
						|
            if ( ele->ClosingType() == XMLElement::CLOSING ) {
 | 
						|
                if ( parentEndTag ) {
 | 
						|
                    ele->_value.TransferTo( parentEndTag );
 | 
						|
                }
 | 
						|
                node->_memPool->SetTracked();   // created and then immediately deleted.
 | 
						|
                DeleteNode( node );
 | 
						|
                return p;
 | 
						|
            }
 | 
						|
 | 
						|
            // Handle an end tag returned to this level.
 | 
						|
            // And handle a bunch of annoying errors.
 | 
						|
            bool mismatch = false;
 | 
						|
            if ( endTag.Empty() ) {
 | 
						|
                if ( ele->ClosingType() == XMLElement::OPEN ) {
 | 
						|
                    mismatch = true;
 | 
						|
                }
 | 
						|
            }
 | 
						|
            else {
 | 
						|
                if ( ele->ClosingType() != XMLElement::OPEN ) {
 | 
						|
                    mismatch = true;
 | 
						|
                }
 | 
						|
                else if ( !XMLUtil::StringEqual( endTag.GetStr(), ele->Name() ) ) {
 | 
						|
                    mismatch = true;
 | 
						|
                }
 | 
						|
            }
 | 
						|
            if ( mismatch ) {
 | 
						|
                _document->SetError( XML_ERROR_MISMATCHED_ELEMENT, initialLineNum, TINYXML2_STR("XMLElement name=%s"), ele->Name());
 | 
						|
                DeleteNode( node );
 | 
						|
                break;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        InsertEndChild( node );
 | 
						|
    }
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
/*static*/ void XMLNode::DeleteNode( XMLNode* node )
 | 
						|
{
 | 
						|
    if ( node == 0 ) {
 | 
						|
        return;
 | 
						|
    }
 | 
						|
	TIXMLASSERT(node->_document);
 | 
						|
	if (!node->ToDocument()) {
 | 
						|
		node->_document->MarkInUse(node);
 | 
						|
	}
 | 
						|
 | 
						|
    MemPool* pool = node->_memPool;
 | 
						|
    node->~XMLNode();
 | 
						|
    pool->Free( node );
 | 
						|
}
 | 
						|
 | 
						|
void XMLNode::InsertChildPreamble( XMLNode* insertThis ) const
 | 
						|
{
 | 
						|
    TIXMLASSERT( insertThis );
 | 
						|
    TIXMLASSERT( insertThis->_document == _document );
 | 
						|
 | 
						|
	if (insertThis->_parent) {
 | 
						|
        insertThis->_parent->Unlink( insertThis );
 | 
						|
	}
 | 
						|
	else {
 | 
						|
		insertThis->_document->MarkInUse(insertThis);
 | 
						|
        insertThis->_memPool->SetTracked();
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
const XMLElement* XMLNode::ToElementWithName( const wchar_t* name ) const
 | 
						|
{
 | 
						|
    const XMLElement* element = this->ToElement();
 | 
						|
    if ( element == 0 ) {
 | 
						|
        return 0;
 | 
						|
    }
 | 
						|
    if ( name == 0 ) {
 | 
						|
        return element;
 | 
						|
    }
 | 
						|
    if ( XMLUtil::StringEqual( element->Name(), name ) ) {
 | 
						|
       return element;
 | 
						|
    }
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
// --------- XMLText ---------- //
 | 
						|
wchar_t* XMLText::ParseDeep( wchar_t* p, StrPair*, int* curLineNumPtr )
 | 
						|
{
 | 
						|
    if ( this->CData() ) {
 | 
						|
        p = _value.ParseText( p, TINYXML2_STR("]]>"), StrPair::NEEDS_NEWLINE_NORMALIZATION, curLineNumPtr );
 | 
						|
        if ( !p ) {
 | 
						|
            _document->SetError( XML_ERROR_PARSING_CDATA, _parseLineNum, 0 );
 | 
						|
        }
 | 
						|
        return p;
 | 
						|
    }
 | 
						|
    else {
 | 
						|
        int flags = _document->ProcessEntities() ? StrPair::TEXT_ELEMENT : StrPair::TEXT_ELEMENT_LEAVE_ENTITIES;
 | 
						|
        if ( _document->WhitespaceMode() == COLLAPSE_WHITESPACE ) {
 | 
						|
            flags |= StrPair::NEEDS_WHITESPACE_COLLAPSING;
 | 
						|
        }
 | 
						|
 | 
						|
        p = _value.ParseText( p, TINYXML2_STR("<"), flags, curLineNumPtr );
 | 
						|
        if ( p && *p ) {
 | 
						|
            return p-1;
 | 
						|
        }
 | 
						|
        if ( !p ) {
 | 
						|
            _document->SetError( XML_ERROR_PARSING_TEXT, _parseLineNum, 0 );
 | 
						|
        }
 | 
						|
    }
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
XMLNode* XMLText::ShallowClone( XMLDocument* doc ) const
 | 
						|
{
 | 
						|
    if ( !doc ) {
 | 
						|
        doc = _document;
 | 
						|
    }
 | 
						|
    XMLText* text = doc->NewText( Value() );	// fixme: this will always allocate memory. Intern?
 | 
						|
    text->SetCData( this->CData() );
 | 
						|
    return text;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool XMLText::ShallowEqual( const XMLNode* compare ) const
 | 
						|
{
 | 
						|
    TIXMLASSERT( compare );
 | 
						|
    const XMLText* text = compare->ToText();
 | 
						|
    return ( text && XMLUtil::StringEqual( text->Value(), Value() ) );
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool XMLText::Accept( XMLVisitor* visitor ) const
 | 
						|
{
 | 
						|
    TIXMLASSERT( visitor );
 | 
						|
    return visitor->Visit( *this );
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
// --------- XMLComment ---------- //
 | 
						|
 | 
						|
XMLComment::XMLComment( XMLDocument* doc ) : XMLNode( doc )
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
XMLComment::~XMLComment()
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
wchar_t* XMLComment::ParseDeep( wchar_t* p, StrPair*, int* curLineNumPtr )
 | 
						|
{
 | 
						|
    // Comment parses as text.
 | 
						|
    p = _value.ParseText( p, TINYXML2_STR("-->"), StrPair::COMMENT, curLineNumPtr );
 | 
						|
    if ( p == 0 ) {
 | 
						|
        _document->SetError( XML_ERROR_PARSING_COMMENT, _parseLineNum, 0 );
 | 
						|
    }
 | 
						|
    return p;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
XMLNode* XMLComment::ShallowClone( XMLDocument* doc ) const
 | 
						|
{
 | 
						|
    if ( !doc ) {
 | 
						|
        doc = _document;
 | 
						|
    }
 | 
						|
    XMLComment* comment = doc->NewComment( Value() );	// fixme: this will always allocate memory. Intern?
 | 
						|
    return comment;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool XMLComment::ShallowEqual( const XMLNode* compare ) const
 | 
						|
{
 | 
						|
    TIXMLASSERT( compare );
 | 
						|
    const XMLComment* comment = compare->ToComment();
 | 
						|
    return ( comment && XMLUtil::StringEqual( comment->Value(), Value() ));
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool XMLComment::Accept( XMLVisitor* visitor ) const
 | 
						|
{
 | 
						|
    TIXMLASSERT( visitor );
 | 
						|
    return visitor->Visit( *this );
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
// --------- XMLDeclaration ---------- //
 | 
						|
 | 
						|
XMLDeclaration::XMLDeclaration( XMLDocument* doc ) : XMLNode( doc )
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
XMLDeclaration::~XMLDeclaration()
 | 
						|
{
 | 
						|
    //printf( TINYXML2_STR("~XMLDeclaration\n") );
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
wchar_t* XMLDeclaration::ParseDeep( wchar_t* p, StrPair*, int* curLineNumPtr )
 | 
						|
{
 | 
						|
    // Declaration parses as text.
 | 
						|
    p = _value.ParseText( p, TINYXML2_STR("?>"), StrPair::NEEDS_NEWLINE_NORMALIZATION, curLineNumPtr );
 | 
						|
    if ( p == 0 ) {
 | 
						|
        _document->SetError( XML_ERROR_PARSING_DECLARATION, _parseLineNum, 0 );
 | 
						|
    }
 | 
						|
    return p;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
XMLNode* XMLDeclaration::ShallowClone( XMLDocument* doc ) const
 | 
						|
{
 | 
						|
    if ( !doc ) {
 | 
						|
        doc = _document;
 | 
						|
    }
 | 
						|
    XMLDeclaration* dec = doc->NewDeclaration( Value() );	// fixme: this will always allocate memory. Intern?
 | 
						|
    return dec;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool XMLDeclaration::ShallowEqual( const XMLNode* compare ) const
 | 
						|
{
 | 
						|
    TIXMLASSERT( compare );
 | 
						|
    const XMLDeclaration* declaration = compare->ToDeclaration();
 | 
						|
    return ( declaration && XMLUtil::StringEqual( declaration->Value(), Value() ));
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
bool XMLDeclaration::Accept( XMLVisitor* visitor ) const
 | 
						|
{
 | 
						|
    TIXMLASSERT( visitor );
 | 
						|
    return visitor->Visit( *this );
 | 
						|
}
 | 
						|
 | 
						|
// --------- XMLUnknown ---------- //
 | 
						|
 | 
						|
XMLUnknown::XMLUnknown( XMLDocument* doc ) : XMLNode( doc )
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
XMLUnknown::~XMLUnknown()
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
wchar_t* XMLUnknown::ParseDeep( wchar_t* p, StrPair*, int* curLineNumPtr )
 | 
						|
{
 | 
						|
    // Unknown parses as text.
 | 
						|
    p = _value.ParseText( p, TINYXML2_STR(">"), StrPair::NEEDS_NEWLINE_NORMALIZATION, curLineNumPtr );
 | 
						|
    if ( !p ) {
 | 
						|
        _document->SetError( XML_ERROR_PARSING_UNKNOWN, _parseLineNum, 0 );
 | 
						|
    }
 | 
						|
    return p;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
XMLNode* XMLUnknown::ShallowClone( XMLDocument* doc ) const
 | 
						|
{
 | 
						|
    if ( !doc ) {
 | 
						|
        doc = _document;
 | 
						|
    }
 | 
						|
    XMLUnknown* text = doc->NewUnknown( Value() );	// fixme: this will always allocate memory. Intern?
 | 
						|
    return text;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool XMLUnknown::ShallowEqual( const XMLNode* compare ) const
 | 
						|
{
 | 
						|
    TIXMLASSERT( compare );
 | 
						|
    const XMLUnknown* unknown = compare->ToUnknown();
 | 
						|
    return ( unknown && XMLUtil::StringEqual( unknown->Value(), Value() ));
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool XMLUnknown::Accept( XMLVisitor* visitor ) const
 | 
						|
{
 | 
						|
    TIXMLASSERT( visitor );
 | 
						|
    return visitor->Visit( *this );
 | 
						|
}
 | 
						|
 | 
						|
// --------- XMLAttribute ---------- //
 | 
						|
 | 
						|
const wchar_t* XMLAttribute::Name() const
 | 
						|
{
 | 
						|
    return _name.GetStr();
 | 
						|
}
 | 
						|
 | 
						|
const wchar_t* XMLAttribute::Value() const
 | 
						|
{
 | 
						|
    return _value.GetStr();
 | 
						|
}
 | 
						|
 | 
						|
wchar_t* XMLAttribute::ParseDeep( wchar_t* p, bool processEntities, int* curLineNumPtr )
 | 
						|
{
 | 
						|
    // Parse using the name rules: bug fix, was using ParseText before
 | 
						|
    p = _name.ParseName( p );
 | 
						|
    if ( !p || !*p ) {
 | 
						|
        return 0;
 | 
						|
    }
 | 
						|
 | 
						|
    // Skip white space before =
 | 
						|
    p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr );
 | 
						|
    if ( *p != TINYXML2_CHAR('=') ) {
 | 
						|
        return 0;
 | 
						|
    }
 | 
						|
 | 
						|
    ++p;	// move up to opening quote
 | 
						|
    p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr );
 | 
						|
    if ( *p != TINYXML2_CHAR('\"') && *p != TINYXML2_CHAR('\'') ) {
 | 
						|
        return 0;
 | 
						|
    }
 | 
						|
 | 
						|
    const wchar_t endTag[2] = { *p, 0 };
 | 
						|
    ++p;	// move past opening quote
 | 
						|
 | 
						|
    p = _value.ParseText( p, endTag, processEntities ? StrPair::ATTRIBUTE_VALUE : StrPair::ATTRIBUTE_VALUE_LEAVE_ENTITIES, curLineNumPtr );
 | 
						|
    return p;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void XMLAttribute::SetName( const wchar_t* n )
 | 
						|
{
 | 
						|
    _name.SetStr( n );
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
XMLError XMLAttribute::QueryIntValue( int* value ) const
 | 
						|
{
 | 
						|
    if ( XMLUtil::ToInt( Value(), value )) {
 | 
						|
        return XML_SUCCESS;
 | 
						|
    }
 | 
						|
    return XML_WRONG_ATTRIBUTE_TYPE;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
XMLError XMLAttribute::QueryUnsignedValue( unsigned int* value ) const
 | 
						|
{
 | 
						|
    if ( XMLUtil::ToUnsigned( Value(), value )) {
 | 
						|
        return XML_SUCCESS;
 | 
						|
    }
 | 
						|
    return XML_WRONG_ATTRIBUTE_TYPE;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
XMLError XMLAttribute::QueryInt64Value(int64_t* value) const
 | 
						|
{
 | 
						|
	if (XMLUtil::ToInt64(Value(), value)) {
 | 
						|
		return XML_SUCCESS;
 | 
						|
	}
 | 
						|
	return XML_WRONG_ATTRIBUTE_TYPE;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
XMLError XMLAttribute::QueryBoolValue( bool* value ) const
 | 
						|
{
 | 
						|
    if ( XMLUtil::ToBool( Value(), value )) {
 | 
						|
        return XML_SUCCESS;
 | 
						|
    }
 | 
						|
    return XML_WRONG_ATTRIBUTE_TYPE;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
XMLError XMLAttribute::QueryFloatValue( float* value ) const
 | 
						|
{
 | 
						|
    if ( XMLUtil::ToFloat( Value(), value )) {
 | 
						|
        return XML_SUCCESS;
 | 
						|
    }
 | 
						|
    return XML_WRONG_ATTRIBUTE_TYPE;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
XMLError XMLAttribute::QueryDoubleValue( double* value ) const
 | 
						|
{
 | 
						|
    if ( XMLUtil::ToDouble( Value(), value )) {
 | 
						|
        return XML_SUCCESS;
 | 
						|
    }
 | 
						|
    return XML_WRONG_ATTRIBUTE_TYPE;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void XMLAttribute::SetAttribute( const wchar_t* v )
 | 
						|
{
 | 
						|
    _value.SetStr( v );
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void XMLAttribute::SetAttribute( int v )
 | 
						|
{
 | 
						|
    wchar_t buf[BUF_SIZE];
 | 
						|
    XMLUtil::ToStr( v, buf, BUF_SIZE );
 | 
						|
    _value.SetStr( buf );
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void XMLAttribute::SetAttribute( unsigned v )
 | 
						|
{
 | 
						|
    wchar_t buf[BUF_SIZE];
 | 
						|
    XMLUtil::ToStr( v, buf, BUF_SIZE );
 | 
						|
    _value.SetStr( buf );
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void XMLAttribute::SetAttribute(int64_t v)
 | 
						|
{
 | 
						|
	wchar_t buf[BUF_SIZE];
 | 
						|
	XMLUtil::ToStr(v, buf, BUF_SIZE);
 | 
						|
	_value.SetStr(buf);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
void XMLAttribute::SetAttribute( bool v )
 | 
						|
{
 | 
						|
    wchar_t buf[BUF_SIZE];
 | 
						|
    XMLUtil::ToStr( v, buf, BUF_SIZE );
 | 
						|
    _value.SetStr( buf );
 | 
						|
}
 | 
						|
 | 
						|
void XMLAttribute::SetAttribute( double v )
 | 
						|
{
 | 
						|
    wchar_t buf[BUF_SIZE];
 | 
						|
    XMLUtil::ToStr( v, buf, BUF_SIZE );
 | 
						|
    _value.SetStr( buf );
 | 
						|
}
 | 
						|
 | 
						|
void XMLAttribute::SetAttribute( float v )
 | 
						|
{
 | 
						|
    wchar_t buf[BUF_SIZE];
 | 
						|
    XMLUtil::ToStr( v, buf, BUF_SIZE );
 | 
						|
    _value.SetStr( buf );
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
// --------- XMLElement ---------- //
 | 
						|
XMLElement::XMLElement( XMLDocument* doc ) : XMLNode( doc ),
 | 
						|
    _closingType( OPEN ),
 | 
						|
    _rootAttribute( 0 )
 | 
						|
{
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
XMLElement::~XMLElement()
 | 
						|
{
 | 
						|
    while( _rootAttribute ) {
 | 
						|
        XMLAttribute* next = _rootAttribute->_next;
 | 
						|
        DeleteAttribute( _rootAttribute );
 | 
						|
        _rootAttribute = next;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
const XMLAttribute* XMLElement::FindAttribute( const wchar_t* name ) const
 | 
						|
{
 | 
						|
    for( XMLAttribute* a = _rootAttribute; a; a = a->_next ) {
 | 
						|
        if ( XMLUtil::StringEqual( a->Name(), name ) ) {
 | 
						|
            return a;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
const wchar_t* XMLElement::Attribute( const wchar_t* name, const wchar_t* value ) const
 | 
						|
{
 | 
						|
    const XMLAttribute* a = FindAttribute( name );
 | 
						|
    if ( !a ) {
 | 
						|
        return 0;
 | 
						|
    }
 | 
						|
    if ( !value || XMLUtil::StringEqual( a->Value(), value )) {
 | 
						|
        return a->Value();
 | 
						|
    }
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
int XMLElement::IntAttribute(const wchar_t* name, int defaultValue) const
 | 
						|
{
 | 
						|
	int i = defaultValue;
 | 
						|
	QueryIntAttribute(name, &i);
 | 
						|
	return i;
 | 
						|
}
 | 
						|
 | 
						|
unsigned XMLElement::UnsignedAttribute(const wchar_t* name, unsigned defaultValue) const
 | 
						|
{
 | 
						|
	unsigned i = defaultValue;
 | 
						|
	QueryUnsignedAttribute(name, &i);
 | 
						|
	return i;
 | 
						|
}
 | 
						|
 | 
						|
int64_t XMLElement::Int64Attribute(const wchar_t* name, int64_t defaultValue) const
 | 
						|
{
 | 
						|
	int64_t i = defaultValue;
 | 
						|
	QueryInt64Attribute(name, &i);
 | 
						|
	return i;
 | 
						|
}
 | 
						|
 | 
						|
bool XMLElement::BoolAttribute(const wchar_t* name, bool defaultValue) const
 | 
						|
{
 | 
						|
	bool b = defaultValue;
 | 
						|
	QueryBoolAttribute(name, &b);
 | 
						|
	return b;
 | 
						|
}
 | 
						|
 | 
						|
double XMLElement::DoubleAttribute(const wchar_t* name, double defaultValue) const
 | 
						|
{
 | 
						|
	double d = defaultValue;
 | 
						|
	QueryDoubleAttribute(name, &d);
 | 
						|
	return d;
 | 
						|
}
 | 
						|
 | 
						|
float XMLElement::FloatAttribute(const wchar_t* name, float defaultValue) const
 | 
						|
{
 | 
						|
	float f = defaultValue;
 | 
						|
	QueryFloatAttribute(name, &f);
 | 
						|
	return f;
 | 
						|
}
 | 
						|
 | 
						|
const wchar_t* XMLElement::GetText() const
 | 
						|
{
 | 
						|
    if ( FirstChild() && FirstChild()->ToText() ) {
 | 
						|
        return FirstChild()->Value();
 | 
						|
    }
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void	XMLElement::SetText( const wchar_t* inText )
 | 
						|
{
 | 
						|
	if ( FirstChild() && FirstChild()->ToText() )
 | 
						|
		FirstChild()->SetValue( inText );
 | 
						|
	else {
 | 
						|
		XMLText*	theText = GetDocument()->NewText( inText );
 | 
						|
		InsertFirstChild( theText );
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void XMLElement::SetText( int v )
 | 
						|
{
 | 
						|
    wchar_t buf[BUF_SIZE];
 | 
						|
    XMLUtil::ToStr( v, buf, BUF_SIZE );
 | 
						|
    SetText( buf );
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void XMLElement::SetText( unsigned v )
 | 
						|
{
 | 
						|
    wchar_t buf[BUF_SIZE];
 | 
						|
    XMLUtil::ToStr( v, buf, BUF_SIZE );
 | 
						|
    SetText( buf );
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void XMLElement::SetText(int64_t v)
 | 
						|
{
 | 
						|
	wchar_t buf[BUF_SIZE];
 | 
						|
	XMLUtil::ToStr(v, buf, BUF_SIZE);
 | 
						|
	SetText(buf);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void XMLElement::SetText( bool v )
 | 
						|
{
 | 
						|
    wchar_t buf[BUF_SIZE];
 | 
						|
    XMLUtil::ToStr( v, buf, BUF_SIZE );
 | 
						|
    SetText( buf );
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void XMLElement::SetText( float v )
 | 
						|
{
 | 
						|
    wchar_t buf[BUF_SIZE];
 | 
						|
    XMLUtil::ToStr( v, buf, BUF_SIZE );
 | 
						|
    SetText( buf );
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void XMLElement::SetText( double v )
 | 
						|
{
 | 
						|
    wchar_t buf[BUF_SIZE];
 | 
						|
    XMLUtil::ToStr( v, buf, BUF_SIZE );
 | 
						|
    SetText( buf );
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
XMLError XMLElement::QueryIntText( int* ival ) const
 | 
						|
{
 | 
						|
    if ( FirstChild() && FirstChild()->ToText() ) {
 | 
						|
        const wchar_t* t = FirstChild()->Value();
 | 
						|
        if ( XMLUtil::ToInt( t, ival ) ) {
 | 
						|
            return XML_SUCCESS;
 | 
						|
        }
 | 
						|
        return XML_CAN_NOT_CONVERT_TEXT;
 | 
						|
    }
 | 
						|
    return XML_NO_TEXT_NODE;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
XMLError XMLElement::QueryUnsignedText( unsigned* uval ) const
 | 
						|
{
 | 
						|
    if ( FirstChild() && FirstChild()->ToText() ) {
 | 
						|
        const wchar_t* t = FirstChild()->Value();
 | 
						|
        if ( XMLUtil::ToUnsigned( t, uval ) ) {
 | 
						|
            return XML_SUCCESS;
 | 
						|
        }
 | 
						|
        return XML_CAN_NOT_CONVERT_TEXT;
 | 
						|
    }
 | 
						|
    return XML_NO_TEXT_NODE;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
XMLError XMLElement::QueryInt64Text(int64_t* ival) const
 | 
						|
{
 | 
						|
	if (FirstChild() && FirstChild()->ToText()) {
 | 
						|
		const wchar_t* t = FirstChild()->Value();
 | 
						|
		if (XMLUtil::ToInt64(t, ival)) {
 | 
						|
			return XML_SUCCESS;
 | 
						|
		}
 | 
						|
		return XML_CAN_NOT_CONVERT_TEXT;
 | 
						|
	}
 | 
						|
	return XML_NO_TEXT_NODE;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
XMLError XMLElement::QueryBoolText( bool* bval ) const
 | 
						|
{
 | 
						|
    if ( FirstChild() && FirstChild()->ToText() ) {
 | 
						|
        const wchar_t* t = FirstChild()->Value();
 | 
						|
        if ( XMLUtil::ToBool( t, bval ) ) {
 | 
						|
            return XML_SUCCESS;
 | 
						|
        }
 | 
						|
        return XML_CAN_NOT_CONVERT_TEXT;
 | 
						|
    }
 | 
						|
    return XML_NO_TEXT_NODE;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
XMLError XMLElement::QueryDoubleText( double* dval ) const
 | 
						|
{
 | 
						|
    if ( FirstChild() && FirstChild()->ToText() ) {
 | 
						|
        const wchar_t* t = FirstChild()->Value();
 | 
						|
        if ( XMLUtil::ToDouble( t, dval ) ) {
 | 
						|
            return XML_SUCCESS;
 | 
						|
        }
 | 
						|
        return XML_CAN_NOT_CONVERT_TEXT;
 | 
						|
    }
 | 
						|
    return XML_NO_TEXT_NODE;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
XMLError XMLElement::QueryFloatText( float* fval ) const
 | 
						|
{
 | 
						|
    if ( FirstChild() && FirstChild()->ToText() ) {
 | 
						|
        const wchar_t* t = FirstChild()->Value();
 | 
						|
        if ( XMLUtil::ToFloat( t, fval ) ) {
 | 
						|
            return XML_SUCCESS;
 | 
						|
        }
 | 
						|
        return XML_CAN_NOT_CONVERT_TEXT;
 | 
						|
    }
 | 
						|
    return XML_NO_TEXT_NODE;
 | 
						|
}
 | 
						|
 | 
						|
int XMLElement::IntText(int defaultValue) const
 | 
						|
{
 | 
						|
	int i = defaultValue;
 | 
						|
	QueryIntText(&i);
 | 
						|
	return i;
 | 
						|
}
 | 
						|
 | 
						|
unsigned XMLElement::UnsignedText(unsigned defaultValue) const
 | 
						|
{
 | 
						|
	unsigned i = defaultValue;
 | 
						|
	QueryUnsignedText(&i);
 | 
						|
	return i;
 | 
						|
}
 | 
						|
 | 
						|
int64_t XMLElement::Int64Text(int64_t defaultValue) const
 | 
						|
{
 | 
						|
	int64_t i = defaultValue;
 | 
						|
	QueryInt64Text(&i);
 | 
						|
	return i;
 | 
						|
}
 | 
						|
 | 
						|
bool XMLElement::BoolText(bool defaultValue) const
 | 
						|
{
 | 
						|
	bool b = defaultValue;
 | 
						|
	QueryBoolText(&b);
 | 
						|
	return b;
 | 
						|
}
 | 
						|
 | 
						|
double XMLElement::DoubleText(double defaultValue) const
 | 
						|
{
 | 
						|
	double d = defaultValue;
 | 
						|
	QueryDoubleText(&d);
 | 
						|
	return d;
 | 
						|
}
 | 
						|
 | 
						|
float XMLElement::FloatText(float defaultValue) const
 | 
						|
{
 | 
						|
	float f = defaultValue;
 | 
						|
	QueryFloatText(&f);
 | 
						|
	return f;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
XMLAttribute* XMLElement::FindOrCreateAttribute( const wchar_t* name )
 | 
						|
{
 | 
						|
    XMLAttribute* last = 0;
 | 
						|
    XMLAttribute* attrib = 0;
 | 
						|
    for( attrib = _rootAttribute;
 | 
						|
            attrib;
 | 
						|
            last = attrib, attrib = attrib->_next ) {
 | 
						|
        if ( XMLUtil::StringEqual( attrib->Name(), name ) ) {
 | 
						|
            break;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    if ( !attrib ) {
 | 
						|
        attrib = CreateAttribute();
 | 
						|
        TIXMLASSERT( attrib );
 | 
						|
        if ( last ) {
 | 
						|
            TIXMLASSERT( last->_next == 0 );
 | 
						|
            last->_next = attrib;
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            TIXMLASSERT( _rootAttribute == 0 );
 | 
						|
            _rootAttribute = attrib;
 | 
						|
        }
 | 
						|
        attrib->SetName( name );
 | 
						|
    }
 | 
						|
    return attrib;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void XMLElement::DeleteAttribute( const wchar_t* name )
 | 
						|
{
 | 
						|
    XMLAttribute* prev = 0;
 | 
						|
    for( XMLAttribute* a=_rootAttribute; a; a=a->_next ) {
 | 
						|
        if ( XMLUtil::StringEqual( name, a->Name() ) ) {
 | 
						|
            if ( prev ) {
 | 
						|
                prev->_next = a->_next;
 | 
						|
            }
 | 
						|
            else {
 | 
						|
                _rootAttribute = a->_next;
 | 
						|
            }
 | 
						|
            DeleteAttribute( a );
 | 
						|
            break;
 | 
						|
        }
 | 
						|
        prev = a;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
wchar_t* XMLElement::ParseAttributes( wchar_t* p, int* curLineNumPtr )
 | 
						|
{
 | 
						|
    XMLAttribute* prevAttribute = 0;
 | 
						|
 | 
						|
    // Read the attributes.
 | 
						|
    while( p ) {
 | 
						|
        p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr );
 | 
						|
        if ( !(*p) ) {
 | 
						|
            _document->SetError( XML_ERROR_PARSING_ELEMENT, _parseLineNum, TINYXML2_STR("XMLElement name=%s"), Name() );
 | 
						|
            return 0;
 | 
						|
        }
 | 
						|
 | 
						|
        // attribute.
 | 
						|
        if (XMLUtil::IsNameStartChar( *p ) ) {
 | 
						|
            XMLAttribute* attrib = CreateAttribute();
 | 
						|
            TIXMLASSERT( attrib );
 | 
						|
            attrib->_parseLineNum = _document->_parseCurLineNum;
 | 
						|
 | 
						|
            const int attrLineNum = attrib->_parseLineNum;
 | 
						|
 | 
						|
            p = attrib->ParseDeep( p, _document->ProcessEntities(), curLineNumPtr );
 | 
						|
            if ( !p || Attribute( attrib->Name() ) ) {
 | 
						|
                DeleteAttribute( attrib );
 | 
						|
                _document->SetError( XML_ERROR_PARSING_ATTRIBUTE, attrLineNum, TINYXML2_STR("XMLElement name=%s"), Name() );
 | 
						|
                return 0;
 | 
						|
            }
 | 
						|
            // There is a minor bug here: if the attribute in the source xml
 | 
						|
            // document is duplicated, it will not be detected and the
 | 
						|
            // attribute will be doubly added. However, tracking the 'prevAttribute'
 | 
						|
            // avoids re-scanning the attribute list. Preferring performance for
 | 
						|
            // now, may reconsider in the future.
 | 
						|
            if ( prevAttribute ) {
 | 
						|
                TIXMLASSERT( prevAttribute->_next == 0 );
 | 
						|
                prevAttribute->_next = attrib;
 | 
						|
            }
 | 
						|
            else {
 | 
						|
                TIXMLASSERT( _rootAttribute == 0 );
 | 
						|
                _rootAttribute = attrib;
 | 
						|
            }
 | 
						|
            prevAttribute = attrib;
 | 
						|
        }
 | 
						|
        // end of the tag
 | 
						|
        else if ( *p == TINYXML2_CHAR('>') ) {
 | 
						|
            ++p;
 | 
						|
            break;
 | 
						|
        }
 | 
						|
        // end of the tag
 | 
						|
        else if ( *p == TINYXML2_CHAR('/') && *(p+1) == TINYXML2_CHAR('>') ) {
 | 
						|
            _closingType = CLOSED;
 | 
						|
            return p+2;	// done; sealed element.
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            _document->SetError( XML_ERROR_PARSING_ELEMENT, _parseLineNum, 0 );
 | 
						|
            return 0;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    return p;
 | 
						|
}
 | 
						|
 | 
						|
void XMLElement::DeleteAttribute( XMLAttribute* attribute )
 | 
						|
{
 | 
						|
    if ( attribute == 0 ) {
 | 
						|
        return;
 | 
						|
    }
 | 
						|
    MemPool* pool = attribute->_memPool;
 | 
						|
    attribute->~XMLAttribute();
 | 
						|
    pool->Free( attribute );
 | 
						|
}
 | 
						|
 | 
						|
XMLAttribute* XMLElement::CreateAttribute()
 | 
						|
{
 | 
						|
    TIXMLASSERT( sizeof( XMLAttribute ) == _document->_attributePool.ItemSize() );
 | 
						|
    XMLAttribute* attrib = new (_document->_attributePool.Alloc() ) XMLAttribute();
 | 
						|
    TIXMLASSERT( attrib );
 | 
						|
    attrib->_memPool = &_document->_attributePool;
 | 
						|
    attrib->_memPool->SetTracked();
 | 
						|
    return attrib;
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
//	<ele></ele>
 | 
						|
//	<ele>foo<b>bar</b></ele>
 | 
						|
//
 | 
						|
wchar_t* XMLElement::ParseDeep( wchar_t* p, StrPair* parentEndTag, int* curLineNumPtr )
 | 
						|
{
 | 
						|
    // Read the element name.
 | 
						|
    p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr );
 | 
						|
 | 
						|
    // The closing element is the </element> form. It is
 | 
						|
    // parsed just like a regular element then deleted from
 | 
						|
    // the DOM.
 | 
						|
    if ( *p == TINYXML2_CHAR('/') ) {
 | 
						|
        _closingType = CLOSING;
 | 
						|
        ++p;
 | 
						|
    }
 | 
						|
 | 
						|
    p = _value.ParseName( p );
 | 
						|
    if ( _value.Empty() ) {
 | 
						|
        return 0;
 | 
						|
    }
 | 
						|
 | 
						|
    p = ParseAttributes( p, curLineNumPtr );
 | 
						|
    if ( !p || !*p || _closingType != OPEN ) {
 | 
						|
        return p;
 | 
						|
    }
 | 
						|
 | 
						|
    p = XMLNode::ParseDeep( p, parentEndTag, curLineNumPtr );
 | 
						|
    return p;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
XMLNode* XMLElement::ShallowClone( XMLDocument* doc ) const
 | 
						|
{
 | 
						|
    if ( !doc ) {
 | 
						|
        doc = _document;
 | 
						|
    }
 | 
						|
    XMLElement* element = doc->NewElement( Value() );					// fixme: this will always allocate memory. Intern?
 | 
						|
    for( const XMLAttribute* a=FirstAttribute(); a; a=a->Next() ) {
 | 
						|
        element->SetAttribute( a->Name(), a->Value() );					// fixme: this will always allocate memory. Intern?
 | 
						|
    }
 | 
						|
    return element;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool XMLElement::ShallowEqual( const XMLNode* compare ) const
 | 
						|
{
 | 
						|
    TIXMLASSERT( compare );
 | 
						|
    const XMLElement* other = compare->ToElement();
 | 
						|
    if ( other && XMLUtil::StringEqual( other->Name(), Name() )) {
 | 
						|
 | 
						|
        const XMLAttribute* a=FirstAttribute();
 | 
						|
        const XMLAttribute* b=other->FirstAttribute();
 | 
						|
 | 
						|
        while ( a && b ) {
 | 
						|
            if ( !XMLUtil::StringEqual( a->Value(), b->Value() ) ) {
 | 
						|
                return false;
 | 
						|
            }
 | 
						|
            a = a->Next();
 | 
						|
            b = b->Next();
 | 
						|
        }
 | 
						|
        if ( a || b ) {
 | 
						|
            // different count
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
    return false;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool XMLElement::Accept( XMLVisitor* visitor ) const
 | 
						|
{
 | 
						|
    TIXMLASSERT( visitor );
 | 
						|
    if ( visitor->VisitEnter( *this, _rootAttribute ) ) {
 | 
						|
        for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) {
 | 
						|
            if ( !node->Accept( visitor ) ) {
 | 
						|
                break;
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
    return visitor->VisitExit( *this );
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
// --------- XMLDocument ----------- //
 | 
						|
 | 
						|
// Warning: List must match 'enum XMLError'
 | 
						|
const wchar_t* XMLDocument::_errorNames[XML_ERROR_COUNT] = {
 | 
						|
    TINYXML2_STR("XML_SUCCESS"),
 | 
						|
    TINYXML2_STR("XML_NO_ATTRIBUTE"),
 | 
						|
    TINYXML2_STR("XML_WRONG_ATTRIBUTE_TYPE"),
 | 
						|
    TINYXML2_STR("XML_ERROR_FILE_NOT_FOUND"),
 | 
						|
    TINYXML2_STR("XML_ERROR_FILE_COULD_NOT_BE_OPENED"),
 | 
						|
    TINYXML2_STR("XML_ERROR_FILE_READ_ERROR"),
 | 
						|
    TINYXML2_STR("XML_ERROR_PARSING_ELEMENT"),
 | 
						|
    TINYXML2_STR("XML_ERROR_PARSING_ATTRIBUTE"),
 | 
						|
    TINYXML2_STR("XML_ERROR_PARSING_TEXT"),
 | 
						|
    TINYXML2_STR("XML_ERROR_PARSING_CDATA"),
 | 
						|
    TINYXML2_STR("XML_ERROR_PARSING_COMMENT"),
 | 
						|
    TINYXML2_STR("XML_ERROR_PARSING_DECLARATION"),
 | 
						|
    TINYXML2_STR("XML_ERROR_PARSING_UNKNOWN"),
 | 
						|
    TINYXML2_STR("XML_ERROR_EMPTY_DOCUMENT"),
 | 
						|
    TINYXML2_STR("XML_ERROR_MISMATCHED_ELEMENT"),
 | 
						|
    TINYXML2_STR("XML_ERROR_PARSING"),
 | 
						|
    TINYXML2_STR("XML_CAN_NOT_CONVERT_TEXT"),
 | 
						|
    TINYXML2_STR("XML_NO_TEXT_NODE"),
 | 
						|
	TINYXML2_STR("XML_ELEMENT_DEPTH_EXCEEDED")
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
XMLDocument::XMLDocument( bool processEntities, Whitespace whitespaceMode ) :
 | 
						|
    XMLNode( 0 ),
 | 
						|
    _writeBOM( false ),
 | 
						|
    _processEntities( processEntities ),
 | 
						|
    _errorID(XML_SUCCESS),
 | 
						|
    _whitespaceMode( whitespaceMode ),
 | 
						|
    _errorStr(),
 | 
						|
    _errorLineNum( 0 ),
 | 
						|
    _wcharBuffer( 0 ),
 | 
						|
    _parseCurLineNum( 0 ),
 | 
						|
	_parsingDepth(0),
 | 
						|
    _unlinked(),
 | 
						|
    _elementPool(),
 | 
						|
    _attributePool(),
 | 
						|
    _textPool(),
 | 
						|
    _commentPool()
 | 
						|
{
 | 
						|
    // avoid VC++ C4355 warning about 'this' in initializer list (C4355 is off by default in VS2012+)
 | 
						|
    _document = this;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
XMLDocument::~XMLDocument()
 | 
						|
{
 | 
						|
    Clear();
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void XMLDocument::MarkInUse(XMLNode* node)
 | 
						|
{
 | 
						|
	TIXMLASSERT(node);
 | 
						|
	TIXMLASSERT(node->_parent == 0);
 | 
						|
 | 
						|
	for (int i = 0; i < _unlinked.Size(); ++i) {
 | 
						|
		if (node == _unlinked[i]) {
 | 
						|
			_unlinked.SwapRemove(i);
 | 
						|
			break;
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void XMLDocument::Clear()
 | 
						|
{
 | 
						|
    DeleteChildren();
 | 
						|
	while( _unlinked.Size()) {
 | 
						|
		DeleteNode(_unlinked[0]);	// Will remove from _unlinked as part of delete.
 | 
						|
	}
 | 
						|
 | 
						|
#ifdef TINYXML2_DEBUG
 | 
						|
    const bool hadError = Error();
 | 
						|
#endif
 | 
						|
    ClearError();
 | 
						|
 | 
						|
    delete [] _wcharBuffer;
 | 
						|
    _wcharBuffer = 0;
 | 
						|
	_parsingDepth = 0;
 | 
						|
 | 
						|
#if 0
 | 
						|
    _textPool.Trace( TINYXML2_STR("text") );
 | 
						|
    _elementPool.Trace( TINYXML2_STR("element") );
 | 
						|
    _commentPool.Trace( TINYXML2_STR("comment") );
 | 
						|
    _attributePool.Trace( TINYXML2_STR("attribute") );
 | 
						|
#endif
 | 
						|
 | 
						|
#ifdef TINYXML2_DEBUG
 | 
						|
    if ( !hadError ) {
 | 
						|
        TIXMLASSERT( _elementPool.CurrentAllocs()   == _elementPool.Untracked() );
 | 
						|
        TIXMLASSERT( _attributePool.CurrentAllocs() == _attributePool.Untracked() );
 | 
						|
        TIXMLASSERT( _textPool.CurrentAllocs()      == _textPool.Untracked() );
 | 
						|
        TIXMLASSERT( _commentPool.CurrentAllocs()   == _commentPool.Untracked() );
 | 
						|
    }
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void XMLDocument::DeepCopy(XMLDocument* target) const
 | 
						|
{
 | 
						|
	TIXMLASSERT(target);
 | 
						|
    if (target == this) {
 | 
						|
        return; // technically success - a no-op.
 | 
						|
    }
 | 
						|
 | 
						|
	target->Clear();
 | 
						|
	for (const XMLNode* node = this->FirstChild(); node; node = node->NextSibling()) {
 | 
						|
		target->InsertEndChild(node->DeepClone(target));
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
XMLElement* XMLDocument::NewElement( const wchar_t* name )
 | 
						|
{
 | 
						|
    XMLElement* ele = CreateUnlinkedNode<XMLElement>( _elementPool );
 | 
						|
    ele->SetName( name );
 | 
						|
    return ele;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
XMLComment* XMLDocument::NewComment( const wchar_t* str )
 | 
						|
{
 | 
						|
    XMLComment* comment = CreateUnlinkedNode<XMLComment>( _commentPool );
 | 
						|
    comment->SetValue( str );
 | 
						|
    return comment;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
XMLText* XMLDocument::NewText( const wchar_t* str )
 | 
						|
{
 | 
						|
    XMLText* text = CreateUnlinkedNode<XMLText>( _textPool );
 | 
						|
    text->SetValue( str );
 | 
						|
    return text;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
XMLDeclaration* XMLDocument::NewDeclaration( const wchar_t* str )
 | 
						|
{
 | 
						|
    XMLDeclaration* dec = CreateUnlinkedNode<XMLDeclaration>( _commentPool );
 | 
						|
    dec->SetValue( str ? str : TINYXML2_STR("xml version=\"1.0\" encoding=\"UTF-8\"") );
 | 
						|
    return dec;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
XMLUnknown* XMLDocument::NewUnknown( const wchar_t* str )
 | 
						|
{
 | 
						|
    XMLUnknown* unk = CreateUnlinkedNode<XMLUnknown>( _commentPool );
 | 
						|
    unk->SetValue( str );
 | 
						|
    return unk;
 | 
						|
}
 | 
						|
 | 
						|
static FILE* callfopen( const wchar_t* filepath, const wchar_t* mode )
 | 
						|
{
 | 
						|
    TIXMLASSERT( filepath );
 | 
						|
    TIXMLASSERT( mode );
 | 
						|
#if defined(_MSC_VER) && (_MSC_VER >= 1400 ) && (!defined WINCE)
 | 
						|
    FILE* fp = 0;
 | 
						|
    const errno_t err = fopen_s( &fp, filepath, mode );
 | 
						|
    if ( err ) {
 | 
						|
        return 0;
 | 
						|
    }
 | 
						|
#else
 | 
						|
    FILE* fp = fopen( filepath, mode );
 | 
						|
#endif
 | 
						|
    return fp;
 | 
						|
}
 | 
						|
 | 
						|
void XMLDocument::DeleteNode( XMLNode* node )	{
 | 
						|
    TIXMLASSERT( node );
 | 
						|
    TIXMLASSERT(node->_document == this );
 | 
						|
    if (node->_parent) {
 | 
						|
        node->_parent->DeleteChild( node );
 | 
						|
    }
 | 
						|
    else {
 | 
						|
        // Isn't in the tree.
 | 
						|
        // Use the parent delete.
 | 
						|
        // Also, we need to mark it tracked: we 'know'
 | 
						|
        // it was never used.
 | 
						|
        node->_memPool->SetTracked();
 | 
						|
        // Call the static XMLNode version:
 | 
						|
        XMLNode::DeleteNode(node);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
XMLError XMLDocument::LoadFile( const wchar_t* filename )
 | 
						|
{
 | 
						|
    if ( !filename ) {
 | 
						|
        TIXMLASSERT( false );
 | 
						|
        SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, 0, TINYXML2_STR("filename=<null>") );
 | 
						|
        return _errorID;
 | 
						|
    }
 | 
						|
 | 
						|
    Clear();
 | 
						|
    FILE* fp = callfopen( filename, TINYXML2_STR("rb") );
 | 
						|
    if ( !fp ) {
 | 
						|
        SetError( XML_ERROR_FILE_NOT_FOUND, 0, TINYXML2_STR("filename=%s"), filename );
 | 
						|
        return _errorID;
 | 
						|
    }
 | 
						|
    LoadFile( fp );
 | 
						|
    fclose( fp );
 | 
						|
    return _errorID;
 | 
						|
}
 | 
						|
 | 
						|
// This is likely overengineered template art to have a check that unsigned long value incremented
 | 
						|
// by one still fits into size_t. If size_t type is larger than unsigned long type
 | 
						|
// (x86_64-w64-mingw32 target) then the check is redundant and gcc and clang emit
 | 
						|
// -Wtype-limits warning. This piece makes the compiler select code with a check when a check
 | 
						|
// is useful and code with no check when a check is redundant depending on how size_t and unsigned long
 | 
						|
// types sizes relate to each other.
 | 
						|
template
 | 
						|
<bool = (sizeof(unsigned long) >= sizeof(size_t))>
 | 
						|
struct LongFitsIntoSizeTMinusOne {
 | 
						|
    static bool Fits( unsigned long value )
 | 
						|
    {
 | 
						|
        return value < (size_t)-1;
 | 
						|
    }
 | 
						|
};
 | 
						|
 | 
						|
template <>
 | 
						|
struct LongFitsIntoSizeTMinusOne<false> {
 | 
						|
    static bool Fits( unsigned long )
 | 
						|
    {
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
};
 | 
						|
 | 
						|
XMLError XMLDocument::LoadFile( FILE* fp )
 | 
						|
{
 | 
						|
    Clear();
 | 
						|
 | 
						|
    fseek( fp, 0, SEEK_SET );
 | 
						|
    if (fgetc( fp ) == EOF && ferror( fp ) != 0 ) {
 | 
						|
        SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
 | 
						|
        return _errorID;
 | 
						|
    }
 | 
						|
 | 
						|
    fseek( fp, 0, SEEK_END );
 | 
						|
    const long filelength = ftell( fp );
 | 
						|
    fseek( fp, 0, SEEK_SET );
 | 
						|
    if ( filelength == -1L ) {
 | 
						|
        SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
 | 
						|
        return _errorID;
 | 
						|
    }
 | 
						|
    TIXMLASSERT( filelength >= 0 );
 | 
						|
 | 
						|
    if ( !LongFitsIntoSizeTMinusOne<>::Fits( filelength ) ) {
 | 
						|
        // Cannot handle files which won't fit in buffer together with null terminator
 | 
						|
        SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
 | 
						|
        return _errorID;
 | 
						|
    }
 | 
						|
 | 
						|
    if ( filelength == 0 ) {
 | 
						|
        SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
 | 
						|
        return _errorID;
 | 
						|
    }
 | 
						|
 | 
						|
    const size_t size = filelength;
 | 
						|
    TIXMLASSERT( _wcharBuffer == 0 );
 | 
						|
	_wcharBuffer = new wchar_t[size + 1];
 | 
						|
    const size_t read = fread( _wcharBuffer, 1, size*sizeof(wchar_t), fp );
 | 
						|
    if ( read != size ) {
 | 
						|
        SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
 | 
						|
        return _errorID;
 | 
						|
    }
 | 
						|
 | 
						|
    _wcharBuffer[size] = 0;
 | 
						|
 | 
						|
    Parse();
 | 
						|
    return _errorID;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
XMLError XMLDocument::SaveFile( const wchar_t* filename, bool compact )
 | 
						|
{
 | 
						|
    if ( !filename ) {
 | 
						|
        TIXMLASSERT( false );
 | 
						|
        SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, 0, TINYXML2_STR("filename=<null>") );
 | 
						|
        return _errorID;
 | 
						|
    }
 | 
						|
 | 
						|
    FILE* fp = callfopen( filename, TINYXML2_STR("w") );
 | 
						|
    if ( !fp ) {
 | 
						|
        SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, 0, TINYXML2_STR("filename=%s"), filename );
 | 
						|
        return _errorID;
 | 
						|
    }
 | 
						|
    SaveFile(fp, compact);
 | 
						|
    fclose( fp );
 | 
						|
    return _errorID;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
XMLError XMLDocument::SaveFile( FILE* fp, bool compact )
 | 
						|
{
 | 
						|
    // Clear any error from the last save, otherwise it will get reported
 | 
						|
    // for *this* call.
 | 
						|
    ClearError();
 | 
						|
    XMLPrinter stream( fp, compact );
 | 
						|
    Print( &stream );
 | 
						|
    return _errorID;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
XMLError XMLDocument::Parse( const wchar_t* p, size_t len )
 | 
						|
{
 | 
						|
    Clear();
 | 
						|
 | 
						|
    if ( len == 0 || !p || !*p ) {
 | 
						|
        SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
 | 
						|
        return _errorID;
 | 
						|
    }
 | 
						|
    if ( len == (size_t)(-1) ) {
 | 
						|
        len = strlen( p );
 | 
						|
    }
 | 
						|
    TIXMLASSERT( _wcharBuffer == 0 );
 | 
						|
    _wcharBuffer = new wchar_t[ len+1 ];
 | 
						|
    memcpy( _wcharBuffer, p, len * sizeof(wchar_t));
 | 
						|
    _wcharBuffer[len] = 0;
 | 
						|
 | 
						|
    Parse();
 | 
						|
    if ( Error() ) {
 | 
						|
        // clean up now essentially dangling memory.
 | 
						|
        // and the parse fail can put objects in the
 | 
						|
        // pools that are dead and inaccessible.
 | 
						|
        DeleteChildren();
 | 
						|
        _elementPool.Clear();
 | 
						|
        _attributePool.Clear();
 | 
						|
        _textPool.Clear();
 | 
						|
        _commentPool.Clear();
 | 
						|
    }
 | 
						|
    return _errorID;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void XMLDocument::Print( XMLPrinter* streamer ) const
 | 
						|
{
 | 
						|
    if ( streamer ) {
 | 
						|
        Accept( streamer );
 | 
						|
    }
 | 
						|
    else {
 | 
						|
        XMLPrinter stdoutStreamer( stdout );
 | 
						|
        Accept( &stdoutStreamer );
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void XMLDocument::SetError( XMLError error, int lineNum, const wchar_t* format, ... )
 | 
						|
{
 | 
						|
    TIXMLASSERT( error >= 0 && error < XML_ERROR_COUNT );
 | 
						|
    _errorID = error;
 | 
						|
    _errorLineNum = lineNum;
 | 
						|
	_errorStr.Reset();
 | 
						|
 | 
						|
    const size_t BUFFER_SIZE = 1000;
 | 
						|
    wchar_t* buffer = new wchar_t[BUFFER_SIZE];
 | 
						|
 | 
						|
    TIXMLASSERT(sizeof(error) <= sizeof(int));
 | 
						|
    TIXML_SNPRINTF(buffer, BUFFER_SIZE, TINYXML2_STR("Error=%s ErrorID=%d (0x%x) Line number=%d"), ErrorIDToName(error), int(error), int(error), lineNum);
 | 
						|
 | 
						|
	if (format) {
 | 
						|
		size_t len = strlen(buffer);
 | 
						|
		TIXML_SNPRINTF(buffer + len, BUFFER_SIZE - len, TINYXML2_STR(": "));
 | 
						|
		len = strlen(buffer);
 | 
						|
 | 
						|
		va_list va;
 | 
						|
		va_start(va, format);
 | 
						|
		TIXML_VSNPRINTF(buffer + len, BUFFER_SIZE - len, format, va);
 | 
						|
		va_end(va);
 | 
						|
	}
 | 
						|
	_errorStr.SetStr(buffer);
 | 
						|
	delete[] buffer;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*static*/ const wchar_t* XMLDocument::ErrorIDToName(XMLError errorID)
 | 
						|
{
 | 
						|
	TIXMLASSERT( errorID >= 0 && errorID < XML_ERROR_COUNT );
 | 
						|
    const wchar_t* errorName = _errorNames[errorID];
 | 
						|
    TIXMLASSERT( errorName && errorName[0] );
 | 
						|
    return errorName;
 | 
						|
}
 | 
						|
 | 
						|
const wchar_t* XMLDocument::ErrorStr() const
 | 
						|
{
 | 
						|
	return _errorStr.Empty() ? TINYXML2_STR("") : _errorStr.GetStr();
 | 
						|
}
 | 
						|
 | 
						|
const wchar_t* XMLDocument::ErrorName() const
 | 
						|
{
 | 
						|
    return ErrorIDToName(_errorID);
 | 
						|
}
 | 
						|
 | 
						|
void XMLDocument::Parse()
 | 
						|
{
 | 
						|
    TIXMLASSERT( NoChildren() ); // Clear() must have been called previously
 | 
						|
    TIXMLASSERT( _wcharBuffer );
 | 
						|
    _parseCurLineNum = 1;
 | 
						|
    _parseLineNum = 1;
 | 
						|
    wchar_t* p = _wcharBuffer;
 | 
						|
    p = XMLUtil::SkipWhiteSpace( p, &_parseCurLineNum );
 | 
						|
    p = const_cast<wchar_t*>( XMLUtil::ReadBOM( p, &_writeBOM ) );
 | 
						|
    if ( !*p ) {
 | 
						|
        SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
 | 
						|
        return;
 | 
						|
    }
 | 
						|
    ParseDeep(p, 0, &_parseCurLineNum );
 | 
						|
}
 | 
						|
 | 
						|
void XMLDocument::PushDepth()
 | 
						|
{
 | 
						|
	_parsingDepth++;
 | 
						|
	if (_parsingDepth == TINYXML2_MAX_ELEMENT_DEPTH) {
 | 
						|
		SetError(XML_ELEMENT_DEPTH_EXCEEDED, _parseCurLineNum, TINYXML2_STR("Element nesting is too deep.") );
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void XMLDocument::PopDepth()
 | 
						|
{
 | 
						|
	TIXMLASSERT(_parsingDepth > 0);
 | 
						|
	--_parsingDepth;
 | 
						|
}
 | 
						|
 | 
						|
XMLPrinter::XMLPrinter( FILE* file, bool compact, int depth ) :
 | 
						|
    _elementJustOpened( false ),
 | 
						|
    _stack(),
 | 
						|
    _firstElement( true ),
 | 
						|
    _fp( file ),
 | 
						|
    _depth( depth ),
 | 
						|
    _textDepth( -1 ),
 | 
						|
    _processEntities( true ),
 | 
						|
    _compactMode( compact ),
 | 
						|
    _buffer(),
 | 
						|
    _entityFlag{}
 | 
						|
{
 | 
						|
    for( int i=0; i<ENTITY_RANGE; ++i ) {
 | 
						|
        _entityFlag[i] = false;
 | 
						|
        _restrictedEntityFlag[i] = false;
 | 
						|
    }
 | 
						|
    for( int i=0; i<NUM_ENTITIES; ++i ) {
 | 
						|
        const wchar_t entityValue = entities[i].value;
 | 
						|
        const TINYXML2_UNSIGNED_CHAR flagIndex = (TINYXML2_UNSIGNED_CHAR)entityValue;
 | 
						|
        TIXMLASSERT( flagIndex < ENTITY_RANGE );
 | 
						|
        _entityFlag[flagIndex] = true;
 | 
						|
    }
 | 
						|
    _restrictedEntityFlag[(TINYXML2_UNSIGNED_CHAR)TINYXML2_CHAR('&')] = true;
 | 
						|
    _restrictedEntityFlag[(TINYXML2_UNSIGNED_CHAR)TINYXML2_CHAR('<')] = true;
 | 
						|
    _restrictedEntityFlag[(TINYXML2_UNSIGNED_CHAR)TINYXML2_CHAR('>')] = true;	// not required, but consistency is nice
 | 
						|
    _buffer.Push( 0 );
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void XMLPrinter::Print( const wchar_t* format, ... )
 | 
						|
{
 | 
						|
    va_list     va;
 | 
						|
    va_start( va, format );
 | 
						|
 | 
						|
    if ( _fp ) {
 | 
						|
        vfprintf( _fp, format, va );
 | 
						|
    }
 | 
						|
    else {
 | 
						|
        const int len = TIXML_VSCPRINTF( format, va );
 | 
						|
        // Close out and re-start the va-args
 | 
						|
        va_end( va );
 | 
						|
        TIXMLASSERT( len >= 0 );
 | 
						|
        va_start( va, format );
 | 
						|
        TIXMLASSERT( _buffer.Size() > 0 && _buffer[_buffer.Size() - 1] == 0 );
 | 
						|
        wchar_t* p = _buffer.PushArr( len ) - 1;	// back up over the null terminator.
 | 
						|
		TIXML_VSNPRINTF( p, len+1, format, va );
 | 
						|
    }
 | 
						|
    va_end( va );
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void XMLPrinter::Write( const wchar_t* data, size_t size )
 | 
						|
{
 | 
						|
    if ( _fp ) {
 | 
						|
        fwrite ( data , sizeof(wchar_t), size, _fp);
 | 
						|
    }
 | 
						|
    else {
 | 
						|
        wchar_t* p = _buffer.PushArr( static_cast<int>(size) ) - 1;   // back up over the null terminator.
 | 
						|
        memcpy( p, data, size * sizeof(wchar_t));
 | 
						|
        p[size] = 0;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void XMLPrinter::Putc( wchar_t ch )
 | 
						|
{
 | 
						|
    if ( _fp ) {
 | 
						|
        fputc ( ch, _fp);
 | 
						|
    }
 | 
						|
    else {
 | 
						|
        wchar_t* p = _buffer.PushArr( sizeof(wchar_t) ) - 1;   // back up over the null terminator.
 | 
						|
        p[0] = ch;
 | 
						|
        p[1] = 0;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void XMLPrinter::PrintSpace( int depth )
 | 
						|
{
 | 
						|
    for( int i=0; i<depth; ++i ) {
 | 
						|
        Write( TINYXML2_STR("    ") );
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void XMLPrinter::PrintString( const wchar_t* p, bool restricted )
 | 
						|
{
 | 
						|
    // Look for runs of bytes between entities to print.
 | 
						|
    const wchar_t* q = p;
 | 
						|
 | 
						|
    if ( _processEntities ) {
 | 
						|
        const bool* flag = restricted ? _restrictedEntityFlag : _entityFlag;
 | 
						|
        while ( *q ) {
 | 
						|
            TIXMLASSERT( p <= q );
 | 
						|
            // Remember, wchar_t is sometimes signed. (How many times has that bitten me?)
 | 
						|
            if ( *q > 0 && *q < ENTITY_RANGE ) {
 | 
						|
                // Check for entities. If one is found, flush
 | 
						|
                // the stream up until the entity, write the
 | 
						|
                // entity, and keep looking.
 | 
						|
                if ( flag[(TINYXML2_UNSIGNED_CHAR)(*q)] ) {
 | 
						|
                    while ( p < q ) {
 | 
						|
                        const size_t delta = q - p;
 | 
						|
                        const int toPrint = ( INT_MAX < delta ) ? INT_MAX : (int)delta;
 | 
						|
                        Write( p, toPrint );
 | 
						|
                        p += toPrint;
 | 
						|
                    }
 | 
						|
                    bool entityPatternPrinted = false;
 | 
						|
                    for( int i=0; i<NUM_ENTITIES; ++i ) {
 | 
						|
                        if ( entities[i].value == *q ) {
 | 
						|
                            Putc( TINYXML2_CHAR('&') );
 | 
						|
                            Write( entities[i].pattern, entities[i].length );
 | 
						|
                            Putc( TINYXML2_CHAR(';') );
 | 
						|
                            entityPatternPrinted = true;
 | 
						|
                            break;
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                    if ( !entityPatternPrinted ) {
 | 
						|
                        // TIXMLASSERT( entityPatternPrinted ) causes gcc -Wunused-but-set-variable in release
 | 
						|
                        TIXMLASSERT( false );
 | 
						|
                    }
 | 
						|
                    ++p;
 | 
						|
                }
 | 
						|
            }
 | 
						|
            ++q;
 | 
						|
            TIXMLASSERT( p <= q );
 | 
						|
        }
 | 
						|
        // Flush the remaining string. This will be the entire
 | 
						|
        // string if an entity wasn't found.
 | 
						|
        if ( p < q ) {
 | 
						|
            const size_t delta = q - p;
 | 
						|
            const int toPrint = ( INT_MAX < delta ) ? INT_MAX : (int)delta;
 | 
						|
            Write( p, toPrint );
 | 
						|
        }
 | 
						|
    }
 | 
						|
    else {
 | 
						|
        Write( p );
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void XMLPrinter::PushHeader( bool writeBOM, bool writeDec )
 | 
						|
{
 | 
						|
    if ( writeBOM ) {
 | 
						|
        static const TINYXML2_UNSIGNED_CHAR bom[] = { TIXML_UTF_LEAD_0, TIXML_UTF_LEAD_1, TIXML_UTF_LEAD_2, 0 };
 | 
						|
        Write( reinterpret_cast< const wchar_t* >( bom ) );
 | 
						|
    }
 | 
						|
    if ( writeDec ) {
 | 
						|
        PushDeclaration( TINYXML2_STR("xml version=\"1.0\"") );
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void XMLPrinter::OpenElement( const wchar_t* name, bool compactMode )
 | 
						|
{
 | 
						|
    SealElementIfJustOpened();
 | 
						|
    _stack.Push( name );
 | 
						|
 | 
						|
    if ( _textDepth < 0 && !_firstElement && !compactMode ) {
 | 
						|
        Putc( TINYXML2_CHAR('\n') );
 | 
						|
    }
 | 
						|
    if ( !compactMode ) {
 | 
						|
        PrintSpace( _depth );
 | 
						|
    }
 | 
						|
 | 
						|
    Write ( TINYXML2_STR("<") );
 | 
						|
    Write ( name );
 | 
						|
 | 
						|
    _elementJustOpened = true;
 | 
						|
    _firstElement = false;
 | 
						|
    ++_depth;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void XMLPrinter::PushAttribute( const wchar_t* name, const wchar_t* value )
 | 
						|
{
 | 
						|
    TIXMLASSERT( _elementJustOpened );
 | 
						|
    Putc ( TINYXML2_CHAR(' ') );
 | 
						|
    Write( name );
 | 
						|
    Write( TINYXML2_STR("=\"") );
 | 
						|
    PrintString( value, false );
 | 
						|
    Putc ( TINYXML2_CHAR('\"') );
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void XMLPrinter::PushAttribute( const wchar_t* name, int v )
 | 
						|
{
 | 
						|
    wchar_t buf[BUF_SIZE];
 | 
						|
    XMLUtil::ToStr( v, buf, BUF_SIZE );
 | 
						|
    PushAttribute( name, buf );
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void XMLPrinter::PushAttribute( const wchar_t* name, unsigned v )
 | 
						|
{
 | 
						|
    wchar_t buf[BUF_SIZE];
 | 
						|
    XMLUtil::ToStr( v, buf, BUF_SIZE );
 | 
						|
    PushAttribute( name, buf );
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void XMLPrinter::PushAttribute(const wchar_t* name, int64_t v)
 | 
						|
{
 | 
						|
	wchar_t buf[BUF_SIZE];
 | 
						|
	XMLUtil::ToStr(v, buf, BUF_SIZE);
 | 
						|
	PushAttribute(name, buf);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void XMLPrinter::PushAttribute( const wchar_t* name, bool v )
 | 
						|
{
 | 
						|
    wchar_t buf[BUF_SIZE];
 | 
						|
    XMLUtil::ToStr( v, buf, BUF_SIZE );
 | 
						|
    PushAttribute( name, buf );
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void XMLPrinter::PushAttribute( const wchar_t* name, double v )
 | 
						|
{
 | 
						|
    wchar_t buf[BUF_SIZE];
 | 
						|
    XMLUtil::ToStr( v, buf, BUF_SIZE );
 | 
						|
    PushAttribute( name, buf );
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void XMLPrinter::CloseElement( bool compactMode )
 | 
						|
{
 | 
						|
    --_depth;
 | 
						|
    const wchar_t* name = _stack.Pop();
 | 
						|
 | 
						|
    if ( _elementJustOpened ) {
 | 
						|
        Write( TINYXML2_STR("/>") );
 | 
						|
    }
 | 
						|
    else {
 | 
						|
        if ( _textDepth < 0 && !compactMode) {
 | 
						|
            Putc( TINYXML2_CHAR('\n') );
 | 
						|
            PrintSpace( _depth );
 | 
						|
        }
 | 
						|
        Write ( TINYXML2_STR("</") );
 | 
						|
        Write ( name );
 | 
						|
        Write ( TINYXML2_STR(">") );
 | 
						|
    }
 | 
						|
 | 
						|
    if ( _textDepth == _depth ) {
 | 
						|
        _textDepth = -1;
 | 
						|
    }
 | 
						|
    if ( _depth == 0 && !compactMode) {
 | 
						|
        Putc( TINYXML2_CHAR('\n') );
 | 
						|
    }
 | 
						|
    _elementJustOpened = false;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void XMLPrinter::SealElementIfJustOpened()
 | 
						|
{
 | 
						|
    if ( !_elementJustOpened ) {
 | 
						|
        return;
 | 
						|
    }
 | 
						|
    _elementJustOpened = false;
 | 
						|
    Putc( TINYXML2_CHAR('>') );
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void XMLPrinter::PushText( const wchar_t* text, bool cdata )
 | 
						|
{
 | 
						|
    _textDepth = _depth-1;
 | 
						|
 | 
						|
    SealElementIfJustOpened();
 | 
						|
    if ( cdata ) {
 | 
						|
        Write( TINYXML2_STR("<![CDATA[") );
 | 
						|
        Write( text );
 | 
						|
        Write( TINYXML2_STR("]]>") );
 | 
						|
    }
 | 
						|
    else {
 | 
						|
        PrintString( text, true );
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void XMLPrinter::PushText( int64_t value )
 | 
						|
{
 | 
						|
    wchar_t buf[BUF_SIZE];
 | 
						|
    XMLUtil::ToStr( value, buf, BUF_SIZE );
 | 
						|
    PushText( buf, false );
 | 
						|
}
 | 
						|
 | 
						|
void XMLPrinter::PushText( int value )
 | 
						|
{
 | 
						|
    wchar_t buf[BUF_SIZE];
 | 
						|
    XMLUtil::ToStr( value, buf, BUF_SIZE );
 | 
						|
    PushText( buf, false );
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void XMLPrinter::PushText( unsigned value )
 | 
						|
{
 | 
						|
    wchar_t buf[BUF_SIZE];
 | 
						|
    XMLUtil::ToStr( value, buf, BUF_SIZE );
 | 
						|
    PushText( buf, false );
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void XMLPrinter::PushText( bool value )
 | 
						|
{
 | 
						|
    wchar_t buf[BUF_SIZE];
 | 
						|
    XMLUtil::ToStr( value, buf, BUF_SIZE );
 | 
						|
    PushText( buf, false );
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void XMLPrinter::PushText( float value )
 | 
						|
{
 | 
						|
    wchar_t buf[BUF_SIZE];
 | 
						|
    XMLUtil::ToStr( value, buf, BUF_SIZE );
 | 
						|
    PushText( buf, false );
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void XMLPrinter::PushText( double value )
 | 
						|
{
 | 
						|
    wchar_t buf[BUF_SIZE];
 | 
						|
    XMLUtil::ToStr( value, buf, BUF_SIZE );
 | 
						|
    PushText( buf, false );
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void XMLPrinter::PushComment( const wchar_t* comment )
 | 
						|
{
 | 
						|
    SealElementIfJustOpened();
 | 
						|
    if ( _textDepth < 0 && !_firstElement && !_compactMode) {
 | 
						|
        Putc( TINYXML2_CHAR('\n') );
 | 
						|
        PrintSpace( _depth );
 | 
						|
    }
 | 
						|
    _firstElement = false;
 | 
						|
 | 
						|
    Write( TINYXML2_STR("<!--") );
 | 
						|
    Write( comment );
 | 
						|
    Write( TINYXML2_STR("-->") );
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void XMLPrinter::PushDeclaration( const wchar_t* value )
 | 
						|
{
 | 
						|
    SealElementIfJustOpened();
 | 
						|
    if ( _textDepth < 0 && !_firstElement && !_compactMode) {
 | 
						|
        Putc( TINYXML2_CHAR('\n') );
 | 
						|
        PrintSpace( _depth );
 | 
						|
    }
 | 
						|
    _firstElement = false;
 | 
						|
 | 
						|
    Write( TINYXML2_STR("<?") );
 | 
						|
    Write( value );
 | 
						|
    Write( TINYXML2_STR("?>") );
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void XMLPrinter::PushUnknown( const wchar_t* value )
 | 
						|
{
 | 
						|
    SealElementIfJustOpened();
 | 
						|
    if ( _textDepth < 0 && !_firstElement && !_compactMode) {
 | 
						|
        Putc( TINYXML2_CHAR('\n') );
 | 
						|
        PrintSpace( _depth );
 | 
						|
    }
 | 
						|
    _firstElement = false;
 | 
						|
 | 
						|
    Write( TINYXML2_STR("<!") );
 | 
						|
    Write( value );
 | 
						|
    Putc( TINYXML2_CHAR('>') );
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool XMLPrinter::VisitEnter( const XMLDocument& doc )
 | 
						|
{
 | 
						|
    _processEntities = doc.ProcessEntities();
 | 
						|
    if ( doc.HasBOM() ) {
 | 
						|
        PushHeader( true, false );
 | 
						|
    }
 | 
						|
    return true;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool XMLPrinter::VisitEnter( const XMLElement& element, const XMLAttribute* attribute )
 | 
						|
{
 | 
						|
    const XMLElement* parentElem = 0;
 | 
						|
    if ( element.Parent() ) {
 | 
						|
        parentElem = element.Parent()->ToElement();
 | 
						|
    }
 | 
						|
    const bool compactMode = parentElem ? CompactMode( *parentElem ) : _compactMode;
 | 
						|
    OpenElement( element.Name(), compactMode );
 | 
						|
    while ( attribute ) {
 | 
						|
        PushAttribute( attribute->Name(), attribute->Value() );
 | 
						|
        attribute = attribute->Next();
 | 
						|
    }
 | 
						|
    return true;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool XMLPrinter::VisitExit( const XMLElement& element )
 | 
						|
{
 | 
						|
    CloseElement( CompactMode(element) );
 | 
						|
    return true;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool XMLPrinter::Visit( const XMLText& text )
 | 
						|
{
 | 
						|
    PushText( text.Value(), text.CData() );
 | 
						|
    return true;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool XMLPrinter::Visit( const XMLComment& comment )
 | 
						|
{
 | 
						|
    PushComment( comment.Value() );
 | 
						|
    return true;
 | 
						|
}
 | 
						|
 | 
						|
bool XMLPrinter::Visit( const XMLDeclaration& declaration )
 | 
						|
{
 | 
						|
    PushDeclaration( declaration.Value() );
 | 
						|
    return true;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool XMLPrinter::Visit( const XMLUnknown& unknown )
 | 
						|
{
 | 
						|
    PushUnknown( unknown.Value() );
 | 
						|
    return true;
 | 
						|
}
 | 
						|
 | 
						|
}   // namespace tinyxml2
 |