Download NHK English news podcast automatically. XML Parser "spxml" is used. This application requires mpod mother board. See also http://mbed.org/users/geodenx/notebook/mpod/

Dependencies:   BlinkLed HTTPClient EthernetInterface FatFileSystemCpp MSCFileSystem spxml mbed-rtos mbed

Fork of mpod_nhk_english by Satoshi Togawa

Download NHK English news podcast automatically.
XML Parser "spxml" is used.
This application requires mpod mother board.
See also http://mbed.org/users/geodenx/notebook/mpod/

Files at this revision

API Documentation at this revision

Comitter:
togayan
Date:
Mon Aug 20 13:27:17 2012 +0000
Parent:
3:07562878d3c3
Child:
5:e87211ff9c23
Commit message:
1st revision

Changed in this revision

TinyXml2/tinyxml2.cpp Show diff for this revision Revisions of this file
TinyXml2/tinyxml2.h Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
spxml/spcanonxml.cpp Show annotated file Show diff for this revision Revisions of this file
spxml/spcanonxml.hpp Show annotated file Show diff for this revision Revisions of this file
spxml/spdomiterator.cpp Show annotated file Show diff for this revision Revisions of this file
spxml/spdomiterator.hpp Show annotated file Show diff for this revision Revisions of this file
spxml/spdomparser.cpp Show annotated file Show diff for this revision Revisions of this file
spxml/spdomparser.hpp Show annotated file Show diff for this revision Revisions of this file
spxml/spxmlcodec.cpp Show annotated file Show diff for this revision Revisions of this file
spxml/spxmlcodec.hpp Show annotated file Show diff for this revision Revisions of this file
spxml/spxmlevent.cpp Show annotated file Show diff for this revision Revisions of this file
spxml/spxmlevent.hpp Show annotated file Show diff for this revision Revisions of this file
spxml/spxmlhandle.cpp Show annotated file Show diff for this revision Revisions of this file
spxml/spxmlhandle.hpp Show annotated file Show diff for this revision Revisions of this file
spxml/spxmlnode.cpp Show annotated file Show diff for this revision Revisions of this file
spxml/spxmlnode.hpp Show annotated file Show diff for this revision Revisions of this file
spxml/spxmlparser.cpp Show annotated file Show diff for this revision Revisions of this file
spxml/spxmlparser.hpp Show annotated file Show diff for this revision Revisions of this file
spxml/spxmlreader.cpp Show annotated file Show diff for this revision Revisions of this file
spxml/spxmlreader.hpp Show annotated file Show diff for this revision Revisions of this file
spxml/spxmlstag.cpp Show annotated file Show diff for this revision Revisions of this file
spxml/spxmlstag.hpp Show annotated file Show diff for this revision Revisions of this file
spxml/spxmlutils.cpp Show annotated file Show diff for this revision Revisions of this file
spxml/spxmlutils.hpp Show annotated file Show diff for this revision Revisions of this file
spxml/strdup.cpp Show annotated file Show diff for this revision Revisions of this file
spxml/strdup.h Show annotated file Show diff for this revision Revisions of this file
--- a/TinyXml2/tinyxml2.cpp	Sun Aug 19 15:57:55 2012 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,2020 +0,0 @@
-/*
-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 <cstdio>
-#include <cstdlib>
-#include <new>
-#include <cstddef>
-
-using namespace tinyxml2;
-using namespace std;
-
-static const char LINE_FEED                = (char)0x0a;            // all line endings are normalized to LF
-static const char LF = LINE_FEED;
-static const char CARRIAGE_RETURN        = (char)0x0d;            // CR gets filtered out
-static const char CR = CARRIAGE_RETURN;
-static const char SINGLE_QUOTE            = '\'';
-static const char DOUBLE_QUOTE            = '\"';
-
-// Bunch of unicode info at:
-//        http://www.unicode.org/faq/utf_bom.html
-//    ef bb bf (Microsoft "lead bytes") - designates UTF-8
-
-static const unsigned char TIXML_UTF_LEAD_0 = 0xefU;
-static const unsigned char TIXML_UTF_LEAD_1 = 0xbbU;
-static const unsigned char TIXML_UTF_LEAD_2 = 0xbfU;
-
-
-#define DELETE_NODE( node )    {            \
-    if ( node ) {                        \
-        MemPool* pool = node->memPool;    \
-        node->~XMLNode();                \
-        pool->Free( node );                \
-    }                                    \
-}
-#define DELETE_ATTRIBUTE( attrib ) {        \
-    if ( attrib ) {                            \
-        MemPool* pool = attrib->memPool;    \
-        attrib->~XMLAttribute();            \
-        pool->Free( attrib );                \
-    }                                        \
-}
-
-struct Entity {
-    const char* pattern;
-    int length;
-    char value;
-};
-
-static const int NUM_ENTITIES = 5;
-static const Entity entities[NUM_ENTITIES] = 
-{
-    { "quot", 4,    DOUBLE_QUOTE },
-    { "amp", 3,        '&'  },
-    { "apos", 4,    SINGLE_QUOTE },
-    { "lt",    2,         '<'     },
-    { "gt",    2,        '>'     }
-};
-
-
-StrPair::~StrPair()
-{
-    Reset();
-}
-
-
-void StrPair::Reset()
-{
-    if ( flags & NEEDS_DELETE ) {
-        delete [] start;
-    }
-    flags = 0;
-    start = 0;
-    end = 0;
-}
-
-
-void StrPair::SetStr( const char* str, int flags )
-{
-    Reset();
-    size_t len = strlen( str );
-    start = new char[ len+1 ];
-    memcpy( start, str, len+1 );
-    end = start + len;
-    this->flags = flags | NEEDS_DELETE;
-}
-
-
-char* StrPair::ParseText( char* p, const char* endTag, int strFlags )
-{
-    TIXMLASSERT( endTag && *endTag );
-
-    char* start = p;    // fixme: hides a member
-    char  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;
-        }
-        ++p;
-    }    
-    return 0;
-}
-
-
-char* StrPair::ParseName( char* p )
-{
-    char* start = p;
-
-    if ( !start || !(*start) ) {
-        return 0;
-    }
-
-    if ( !XMLUtil::IsAlpha( *p ) ) {
-        return 0;
-    }
-
-    while( *p && (
-               XMLUtil::IsAlphaNum( (unsigned char) *p ) 
-            || *p == '_'
-            || *p == '-'
-            || *p == '.'
-            || *p == ':' ))
-    {
-        ++p;
-    }
-
-    if ( p > start ) {
-        Set( start, p, 0 );
-        return p;
-    }
-    return 0;
-}
-
-
-
-const char* StrPair::GetStr()
-{
-    if ( flags & NEEDS_FLUSH ) {
-        *end = 0;
-        flags ^= NEEDS_FLUSH;
-
-        if ( flags ) {
-            char* p = start;    // the read pointer
-            char* 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;
-                }
-                else if ( (flags & NEEDS_NEWLINE_NORMALIZATION) && *p == LF ) {
-                    if ( *(p+1) == CR ) {
-                        p += 2;
-                    }
-                    else {
-                        ++p;
-                    }
-                    *q++ = LF;
-                }
-                else if ( (flags & NEEDS_ENTITY_PROCESSING) && *p == '&' ) {
-                    // Entities handled by tinyXML2:
-                    // - special entities in the entity table [in/out]
-                    // - numeric character reference [in]
-                    //   &#20013; or &#x4e2d;
-
-                    if ( *(p+1) == '#' ) {
-                        char buf[10] = { 0 };
-                        int len;
-                        p = const_cast<char*>( XMLUtil::GetCharacterRef( p, buf, &len ) );
-                        for( int i=0; i<len; ++i ) {
-                            *q++ = buf[i];
-                        }
-                        TIXMLASSERT( q <= p );
-                    }
-                    else {
-                        int i=0;
-                        for(; i<NUM_ENTITIES; ++i ) {
-                            if (    strncmp( p+1, entities[i].pattern, entities[i].length ) == 0
-                                 && *(p+entities[i].length+1) == ';' ) 
-                            {
-                                // Found an entity convert;
-                                *q = entities[i].value;
-                                ++q;
-                                p += entities[i].length + 2;
-                                break;
-                            }
-                        }
-                        if ( i == NUM_ENTITIES ) {
-                            // fixme: treat as error?
-                            ++p;
-                            ++q;
-                        }
-                    }
-                }
-                else {
-                    *q = *p;
-                    ++p;
-                    ++q;
-                }
-            }
-            *q = 0;
-        }
-        flags = (flags & NEEDS_DELETE);
-    }
-    return start;
-}
-
-
-
-
-// --------- XMLUtil ----------- //
-
-const char* XMLUtil::ReadBOM( const char* p, bool* bom )
-{
-    *bom = false;
-    const unsigned char* pu = reinterpret_cast<const 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;
-    }
-    return p;
-}
-
-
-void XMLUtil::ConvertUTF32ToUTF8( unsigned long input, char* 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; return; }    // This code won't covert this correctly anyway.
-
-    output += *length;
-
-    // Scary scary fall throughs.
-    switch (*length) 
-    {
-        case 4:
-            --output; 
-            *output = (char)((input | BYTE_MARK) & BYTE_MASK); 
-            input >>= 6;
-        case 3:
-            --output; 
-            *output = (char)((input | BYTE_MARK) & BYTE_MASK); 
-            input >>= 6;
-        case 2:
-            --output; 
-            *output = (char)((input | BYTE_MARK) & BYTE_MASK); 
-            input >>= 6;
-        case 1:
-            --output; 
-            *output = (char)(input | FIRST_BYTE_MARK[*length]);
-    }
-}
-
-
-const char* XMLUtil::GetCharacterRef( const char* p, char* value, int* length )
-{
-    // Presume an entity, and pull it out.
-    *length = 0;
-
-    if ( *(p+1) == '#' && *(p+2) )
-    {
-        unsigned long ucs = 0;
-        ptrdiff_t delta = 0;
-        unsigned mult = 1;
-
-        if ( *(p+2) == 'x' )
-        {
-            // Hexadecimal.
-            if ( !*(p+3) ) return 0;
-
-            const char* q = p+3;
-            q = strchr( q, ';' );
-
-            if ( !q || !*q ) return 0;
-
-            delta = q-p;
-            --q;
-
-            while ( *q != 'x' )
-            {
-                if ( *q >= '0' && *q <= '9' )
-                    ucs += mult * (*q - '0');
-                else if ( *q >= 'a' && *q <= 'f' )
-                    ucs += mult * (*q - 'a' + 10);
-                else if ( *q >= 'A' && *q <= 'F' )
-                    ucs += mult * (*q - 'A' + 10 );
-                else 
-                    return 0;
-                mult *= 16;
-                --q;
-            }
-        }
-        else
-        {
-            // Decimal.
-            if ( !*(p+2) ) return 0;
-
-            const char* q = p+2;
-            q = strchr( q, ';' );
-
-            if ( !q || !*q ) return 0;
-
-            delta = q-p;
-            --q;
-
-            while ( *q != '#' )
-            {
-                if ( *q >= '0' && *q <= '9' )
-                    ucs += mult * (*q - '0');
-                else 
-                    return 0;
-                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, char* buffer, int bufferSize ) 
-{
-    TIXML_SNPRINTF( buffer, bufferSize, "%d", v );    
-}
-
-
-void XMLUtil::ToStr( unsigned v, char* buffer, int bufferSize )
-{
-    TIXML_SNPRINTF( buffer, bufferSize, "%u", v );    
-}
-
-
-void XMLUtil::ToStr( bool v, char* buffer, int bufferSize )
-{
-    TIXML_SNPRINTF( buffer, bufferSize, "%d", v ? 1 : 0 );    
-}
-
-
-void XMLUtil::ToStr( float v, char* buffer, int bufferSize )
-{
-    TIXML_SNPRINTF( buffer, bufferSize, "%f", v );    
-}
-
-
-void XMLUtil::ToStr( double v, char* buffer, int bufferSize )
-{
-    TIXML_SNPRINTF( buffer, bufferSize, "%f", v );    
-}
-
-
-bool XMLUtil::ToInt( const char* str, int* value )
-{
-    if ( TIXML_SSCANF( str, "%d", value ) == 1 )
-        return true;
-    return false;
-}
-
-bool XMLUtil::ToUnsigned( const char* str, unsigned *value )
-{
-    if ( TIXML_SSCANF( str, "%u", value ) == 1 )
-        return true;
-    return false;
-}
-
-bool XMLUtil::ToBool( const char* str, bool* value )
-{
-    int ival = 0;
-    if ( ToInt( str, &ival )) {
-        *value = (ival==0) ? false : true;
-        return true;
-    }
-    if ( StringEqual( str, "true" ) ) {
-        *value = true;
-        return true;
-    }
-    else if ( StringEqual( str, "false" ) ) {
-        *value = false;
-        return true;
-    }
-    return false;
-}
-
-
-bool XMLUtil::ToFloat( const char* str, float* value )
-{
-    if ( TIXML_SSCANF( str, "%f", value ) == 1 ) {
-        return true;
-    }
-    return false;
-}
-
-bool XMLUtil::ToDouble( const char* str, double* value )
-{
-    if ( TIXML_SSCANF( str, "%lf", value ) == 1 ) {
-        return true;
-    }
-    return false;
-}
-
-
-char* XMLDocument::Identify( char* p, XMLNode** node ) 
-{
-    XMLNode* returnNode = 0;
-    char* start = p;
-    p = XMLUtil::SkipWhiteSpace( p );
-    if( !p || !*p )
-    {
-        return p;
-    }
-
-    // What is this thing? 
-    // - Elements start with a letter or underscore, but xml is reserved.
-    // - Comments: <!--
-    // - Decleration: <?
-    // - Everthing else is unknown to tinyxml.
-    //
-
-    static const char* xmlHeader        = { "<?" };
-    static const char* commentHeader    = { "<!--" };
-    static const char* dtdHeader        = { "<!" };
-    static const char* cdataHeader        = { "<![CDATA[" };
-    static const char* elementHeader    = { "<" };    // and a header for everything else; check last.
-
-    static const int xmlHeaderLen        = 2;
-    static const int commentHeaderLen    = 4;
-    static const int dtdHeaderLen        = 2;
-    static const int cdataHeaderLen        = 9;
-    static const int elementHeaderLen    = 1;
-
-#if defined(_MSC_VER)
-#pragma warning ( push )
-#pragma warning ( disable : 4127 )
-#endif
-    TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLUnknown ) );        // use same memory pool
-    TIXMLASSERT( sizeof( XMLComment ) == sizeof( XMLDeclaration ) );    // use same memory pool
-#if defined(_MSC_VER)
-#pragma warning (pop)
-#endif
-    if ( XMLUtil::StringEqual( p, xmlHeader, xmlHeaderLen ) ) {
-        returnNode = new (commentPool.Alloc()) XMLDeclaration( this );
-        returnNode->memPool = &commentPool;
-        p += xmlHeaderLen;
-    }
-    else if ( XMLUtil::StringEqual( p, commentHeader, commentHeaderLen ) ) {
-        returnNode = new (commentPool.Alloc()) XMLComment( this );
-        returnNode->memPool = &commentPool;
-        p += commentHeaderLen;
-    }
-    else if ( XMLUtil::StringEqual( p, cdataHeader, cdataHeaderLen ) ) {
-        XMLText* text = new (textPool.Alloc()) XMLText( this );
-        returnNode = text;
-        returnNode->memPool = &textPool;
-        p += cdataHeaderLen;
-        text->SetCData( true );
-    }
-    else if ( XMLUtil::StringEqual( p, dtdHeader, dtdHeaderLen ) ) {
-        returnNode = new (commentPool.Alloc()) XMLUnknown( this );
-        returnNode->memPool = &commentPool;
-        p += dtdHeaderLen;
-    }
-    else if ( XMLUtil::StringEqual( p, elementHeader, elementHeaderLen ) ) {
-        returnNode = new (elementPool.Alloc()) XMLElement( this );
-        returnNode->memPool = &elementPool;
-        p += elementHeaderLen;
-    }
-    else {
-        returnNode = new (textPool.Alloc()) XMLText( this );
-        returnNode->memPool = &textPool;
-        p = start;    // Back it up, all the text counts.
-    }
-
-    *node = returnNode;
-    return p;
-}
-
-
-bool XMLDocument::Accept( XMLVisitor* visitor ) const
-{
-    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 ),
-    firstChild( 0 ), lastChild( 0 ),
-    prev( 0 ), next( 0 )
-{
-}
-
-
-XMLNode::~XMLNode()
-{
-    DeleteChildren();
-    if ( parent ) {
-        parent->Unlink( this );
-    }
-}
-
-
-void XMLNode::SetValue( const char* str, bool staticMem )
-{
-    if ( staticMem )
-        value.SetInternedStr( str );
-    else
-        value.SetStr( str );
-}
-
-
-void XMLNode::DeleteChildren()
-{
-    while( firstChild ) {
-        XMLNode* node = firstChild;
-        Unlink( node );
-        
-        DELETE_NODE( node );
-    }
-    firstChild = lastChild = 0;
-}
-
-
-void XMLNode::Unlink( XMLNode* child )
-{
-    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->parent = 0;
-}
-
-
-void XMLNode::DeleteChild( XMLNode* node )
-{
-    TIXMLASSERT( node->parent == this );
-    DELETE_NODE( node );
-}
-
-
-XMLNode* XMLNode::InsertEndChild( XMLNode* 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 )
-{
-    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( afterThis->parent == this );
-    if ( afterThis->parent != this )
-        return 0;
-
-    if ( afterThis->next == 0 ) {
-        // The last node or the only node.
-        return InsertEndChild( 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 char* value ) const
-{
-    for( XMLNode* node=firstChild; node; node=node->next ) {
-        XMLElement* element = node->ToElement();
-        if ( element ) {
-            if ( !value || XMLUtil::StringEqual( element->Name(), value ) ) {
-                return element;
-            }
-        }
-    }
-    return 0;
-}
-
-
-const XMLElement* XMLNode::LastChildElement( const char* value ) const
-{
-    for( XMLNode* node=lastChild; node; node=node->prev ) {
-        XMLElement* element = node->ToElement();
-        if ( element ) {
-            if ( !value || XMLUtil::StringEqual( element->Name(), value ) ) {
-                return element;
-            }
-        }
-    }
-    return 0;
-}
-
-
-const XMLElement* XMLNode::NextSiblingElement( const char* value ) const
-{
-    for( XMLNode* element=this->next; element; element = element->next ) {
-        if (    element->ToElement() 
-             && (!value || XMLUtil::StringEqual( value, element->Value() ))) 
-        {
-            return element->ToElement();
-        }
-    }
-    return 0;
-}
-
-
-const XMLElement* XMLNode::PreviousSiblingElement( const char* value ) const
-{
-    for( XMLNode* element=this->prev; element; element = element->prev ) {
-        if (    element->ToElement()
-             && (!value || XMLUtil::StringEqual( value, element->Value() ))) 
-        {
-            return element->ToElement();
-        }
-    }
-    return 0;
-}
-
-
-char* XMLNode::ParseDeep( char* p, StrPair* parentEnd )
-{
-    // This is a recursive method, but thinking about it "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.
-
-    while( p && *p ) {
-        XMLNode* node = 0;
-
-        p = document->Identify( p, &node );
-        if ( p == 0 || node == 0 ) {
-            break;
-        }
-
-        StrPair endTag;
-        p = node->ParseDeep( p, &endTag );
-        if ( !p ) {
-            DELETE_NODE( node );
-            node = 0;
-            if ( !document->Error() ) {
-                document->SetError( XML_ERROR_PARSING, 0, 0 );
-            }
-            break;
-        }
-
-        // We read the end tag. Return it to the parent.
-        if ( node->ToElement() && node->ToElement()->ClosingType() == XMLElement::CLOSING ) {
-            if ( parentEnd ) {
-                *parentEnd = static_cast<XMLElement*>(node)->value;
-            }
-            DELETE_NODE( node );
-            return p;
-        }
-
-        // Handle an end tag returned to this level.
-        // And handle a bunch of annoying errors.
-        XMLElement* ele = node->ToElement();
-        if ( ele ) {
-            if ( endTag.Empty() && ele->ClosingType() == XMLElement::OPEN ) {
-                document->SetError( XML_ERROR_MISMATCHED_ELEMENT, node->Value(), 0 );
-                p = 0;
-            }
-            else if ( !endTag.Empty() && ele->ClosingType() != XMLElement::OPEN ) {
-                document->SetError( XML_ERROR_MISMATCHED_ELEMENT, node->Value(), 0 );
-                p = 0;
-            }
-            else if ( !endTag.Empty() ) {
-                if ( !XMLUtil::StringEqual( endTag.GetStr(), node->Value() )) { 
-                    document->SetError( XML_ERROR_MISMATCHED_ELEMENT, node->Value(), 0 );
-                    p = 0;
-                }
-            }
-        }
-        if ( p == 0 ) {
-            DELETE_NODE( node );
-            node = 0;
-        }
-        if ( node ) {
-            this->InsertEndChild( node );
-        }
-    }
-    return 0;
-}
-
-// --------- XMLText ---------- //
-char* XMLText::ParseDeep( char* p, StrPair* )
-{
-    const char* start = p;
-    if ( this->CData() ) {
-        p = value.ParseText( p, "]]>", StrPair::NEEDS_NEWLINE_NORMALIZATION );
-        if ( !p ) {
-            document->SetError( XML_ERROR_PARSING_CDATA, start, 0 );
-        }
-        return p;
-    }
-    else {
-        p = value.ParseText( p, "<", document->ProcessEntities() ? StrPair::TEXT_ELEMENT : StrPair::TEXT_ELEMENT_LEAVE_ENTITIES );
-        if ( !p ) {
-            document->SetError( XML_ERROR_PARSING_TEXT, start, 0 );
-        }
-        if ( p && *p ) {
-            return p-1;
-        }
-    }
-    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
-{
-    return ( compare->ToText() && XMLUtil::StringEqual( compare->ToText()->Value(), Value() ));
-}
-
-
-bool XMLText::Accept( XMLVisitor* visitor ) const
-{
-    return visitor->Visit( *this );
-}
-
-
-// --------- XMLComment ---------- //
-
-XMLComment::XMLComment( XMLDocument* doc ) : XMLNode( doc )
-{
-}
-
-
-XMLComment::~XMLComment()
-{
-    //printf( "~XMLComment\n" );
-}
-
-
-char* XMLComment::ParseDeep( char* p, StrPair* )
-{
-    // Comment parses as text.
-    const char* start = p;
-    p = value.ParseText( p, "-->", StrPair::COMMENT );
-    if ( p == 0 ) {
-        document->SetError( XML_ERROR_PARSING_COMMENT, start, 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
-{
-    return ( compare->ToComment() && XMLUtil::StringEqual( compare->ToComment()->Value(), Value() ));
-}
-
-
-bool XMLComment::Accept( XMLVisitor* visitor ) const
-{
-    return visitor->Visit( *this );
-}
-
-
-// --------- XMLDeclaration ---------- //
-
-XMLDeclaration::XMLDeclaration( XMLDocument* doc ) : XMLNode( doc )
-{
-}
-
-
-XMLDeclaration::~XMLDeclaration()
-{
-    //printf( "~XMLDeclaration\n" );
-}
-
-
-char* XMLDeclaration::ParseDeep( char* p, StrPair* )
-{
-    // Declaration parses as text.
-    const char* start = p;
-    p = value.ParseText( p, "?>", StrPair::NEEDS_NEWLINE_NORMALIZATION );
-    if ( p == 0 ) {
-        document->SetError( XML_ERROR_PARSING_DECLARATION, start, 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
-{
-    return ( compare->ToDeclaration() && XMLUtil::StringEqual( compare->ToDeclaration()->Value(), Value() ));
-}
-
-
-
-bool XMLDeclaration::Accept( XMLVisitor* visitor ) const
-{
-    return visitor->Visit( *this );
-}
-
-// --------- XMLUnknown ---------- //
-
-XMLUnknown::XMLUnknown( XMLDocument* doc ) : XMLNode( doc )
-{
-}
-
-
-XMLUnknown::~XMLUnknown()
-{
-}
-
-
-char* XMLUnknown::ParseDeep( char* p, StrPair* )
-{
-    // Unknown parses as text.
-    const char* start = p;
-
-    p = value.ParseText( p, ">", StrPair::NEEDS_NEWLINE_NORMALIZATION );
-    if ( !p ) {
-        document->SetError( XML_ERROR_PARSING_UNKNOWN, start, 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
-{
-    return ( compare->ToUnknown() && XMLUtil::StringEqual( compare->ToUnknown()->Value(), Value() ));
-}
-
-
-bool XMLUnknown::Accept( XMLVisitor* visitor ) const
-{
-    return visitor->Visit( *this );
-}
-
-// --------- XMLAttribute ---------- //
-char* XMLAttribute::ParseDeep( char* p, bool processEntities )
-{
-    // 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 );
-    if ( !p || *p != '=' ) return 0;
-
-    ++p;    // move up to opening quote
-    p = XMLUtil::SkipWhiteSpace( p );
-    if ( *p != '\"' && *p != '\'' ) return 0;
-
-    char endTag[2] = { *p, 0 };
-    ++p;    // move past opening quote
-
-    p = value.ParseText( p, endTag, processEntities ? StrPair::ATTRIBUTE_VALUE : StrPair::ATTRIBUTE_VALUE_LEAVE_ENTITIES );
-    return p;
-}
-
-
-void XMLAttribute::SetName( const char* n )
-{
-    name.SetStr( n );
-}
-
-
-int XMLAttribute::QueryIntValue( int* value ) const
-{
-    if ( XMLUtil::ToInt( Value(), value ))
-        return XML_NO_ERROR;
-    return XML_WRONG_ATTRIBUTE_TYPE;
-}
-
-
-int XMLAttribute::QueryUnsignedValue( unsigned int* value ) const
-{
-    if ( XMLUtil::ToUnsigned( Value(), value ))
-        return XML_NO_ERROR;
-    return XML_WRONG_ATTRIBUTE_TYPE;
-}
-
-
-int XMLAttribute::QueryBoolValue( bool* value ) const
-{
-    if ( XMLUtil::ToBool( Value(), value )) {
-        return XML_NO_ERROR;
-    }
-    return XML_WRONG_ATTRIBUTE_TYPE;
-}
-
-
-int XMLAttribute::QueryFloatValue( float* value ) const
-{
-    if ( XMLUtil::ToFloat( Value(), value ))
-        return XML_NO_ERROR;
-    return XML_WRONG_ATTRIBUTE_TYPE;
-}
-
-
-int XMLAttribute::QueryDoubleValue( double* value ) const
-{
-    if ( XMLUtil::ToDouble( Value(), value ))
-        return XML_NO_ERROR;
-    return XML_WRONG_ATTRIBUTE_TYPE;
-}
-
-
-void XMLAttribute::SetAttribute( const char* v )
-{
-    value.SetStr( v );
-}
-
-
-void XMLAttribute::SetAttribute( int v )
-{
-    char buf[BUF_SIZE];
-    XMLUtil::ToStr( v, buf, BUF_SIZE );
-    value.SetStr( buf );
-}
-
-
-void XMLAttribute::SetAttribute( unsigned v )
-{
-    char buf[BUF_SIZE];
-    XMLUtil::ToStr( v, buf, BUF_SIZE );
-    value.SetStr( buf );
-}
-
-
-void XMLAttribute::SetAttribute( bool v )
-{
-    char buf[BUF_SIZE];
-    XMLUtil::ToStr( v, buf, BUF_SIZE );
-    value.SetStr( buf );
-}
-
-void XMLAttribute::SetAttribute( double v )
-{
-    char buf[BUF_SIZE];
-    XMLUtil::ToStr( v, buf, BUF_SIZE );
-    value.SetStr( buf );
-}
-
-void XMLAttribute::SetAttribute( float v )
-{
-    char buf[BUF_SIZE];
-    XMLUtil::ToStr( v, buf, BUF_SIZE );
-    value.SetStr( buf );
-}
-
-
-// --------- XMLElement ---------- //
-XMLElement::XMLElement( XMLDocument* doc ) : XMLNode( doc ),
-    closingType( 0 ),
-    rootAttribute( 0 )
-{
-}
-
-
-XMLElement::~XMLElement()
-{
-    while( rootAttribute ) {
-        XMLAttribute* next = rootAttribute->next;
-        DELETE_ATTRIBUTE( rootAttribute );
-        rootAttribute = next;
-    }
-}
-
-
-XMLAttribute* XMLElement::FindAttribute( const char* name )
-{
-    XMLAttribute* a = 0;
-    for( a=rootAttribute; a; a = a->next ) {
-        if ( XMLUtil::StringEqual( a->Name(), name ) )
-            return a;
-    }
-    return 0;
-}
-
-
-const XMLAttribute* XMLElement::FindAttribute( const char* name ) const
-{
-    XMLAttribute* a = 0;
-    for( a=rootAttribute; a; a = a->next ) {
-        if ( XMLUtil::StringEqual( a->Name(), name ) )
-            return a;
-    }
-    return 0;
-}
-
-
-const char* XMLElement::Attribute( const char* name, const char* value ) const
-{ 
-    const XMLAttribute* a = FindAttribute( name ); 
-    if ( !a ) 
-        return 0; 
-    if ( !value || XMLUtil::StringEqual( a->Value(), value ))
-        return a->Value();
-    return 0;
-}
-
-
-const char* XMLElement::GetText() const
-{
-    if ( FirstChild() && FirstChild()->ToText() ) {
-        return FirstChild()->ToText()->Value();
-    }
-    return 0;
-}
-
-
-int XMLElement::QueryIntText( int* _value ) const
-{
-    if ( FirstChild() && FirstChild()->ToText() ) {
-        const char* t = FirstChild()->ToText()->Value();
-        if ( XMLUtil::ToInt( t, _value ) ) {
-            return XML_SUCCESS;
-        }
-        return XML_CAN_NOT_CONVERT_TEXT;
-    }
-    return XML_NO_TEXT_NODE;
-}
-
-
-int XMLElement::QueryUnsignedText( unsigned* _value ) const
-{
-    if ( FirstChild() && FirstChild()->ToText() ) {
-        const char* t = FirstChild()->ToText()->Value();
-        if ( XMLUtil::ToUnsigned( t, _value ) ) {
-            return XML_SUCCESS;
-        }
-        return XML_CAN_NOT_CONVERT_TEXT;
-    }
-    return XML_NO_TEXT_NODE;
-}
-
-
-int XMLElement::QueryBoolText( bool* _value ) const
-{
-    if ( FirstChild() && FirstChild()->ToText() ) {
-        const char* t = FirstChild()->ToText()->Value();
-        if ( XMLUtil::ToBool( t, _value ) ) {
-            return XML_SUCCESS;
-        }
-        return XML_CAN_NOT_CONVERT_TEXT;
-    }
-    return XML_NO_TEXT_NODE;
-}
-
-
-int XMLElement::QueryDoubleText( double* _value ) const
-{
-    if ( FirstChild() && FirstChild()->ToText() ) {
-        const char* t = FirstChild()->ToText()->Value();
-        if ( XMLUtil::ToDouble( t, _value ) ) {
-            return XML_SUCCESS;
-        }
-        return XML_CAN_NOT_CONVERT_TEXT;
-    }
-    return XML_NO_TEXT_NODE;
-}
-
-
-int XMLElement::QueryFloatText( float* _value ) const
-{
-    if ( FirstChild() && FirstChild()->ToText() ) {
-        const char* t = FirstChild()->ToText()->Value();
-        if ( XMLUtil::ToFloat( t, _value ) ) {
-            return XML_SUCCESS;
-        }
-        return XML_CAN_NOT_CONVERT_TEXT;
-    }
-    return XML_NO_TEXT_NODE;
-}
-
-
-
-XMLAttribute* XMLElement::FindOrCreateAttribute( const char* 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 = new (document->attributePool.Alloc() ) XMLAttribute();
-        attrib->memPool = &document->attributePool;
-        if ( last ) {
-            last->next = attrib;
-        }
-        else {
-            rootAttribute = attrib;
-        }
-        attrib->SetName( name );
-    }
-    return attrib;
-}
-
-
-void XMLElement::DeleteAttribute( const char* 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;
-            }
-            DELETE_ATTRIBUTE( a );
-            break;
-        }
-        prev = a;
-    }
-}
-
-
-char* XMLElement::ParseAttributes( char* p )
-{
-    const char* start = p;
-    XMLAttribute* prevAttribute = 0;
-
-    // Read the attributes.
-    while( p ) {
-        p = XMLUtil::SkipWhiteSpace( p );
-        if ( !p || !(*p) ) {
-            document->SetError( XML_ERROR_PARSING_ELEMENT, start, Name() );
-            return 0;
-        }
-
-        // attribute.
-        if ( XMLUtil::IsAlpha( *p ) ) {
-            XMLAttribute* attrib = new (document->attributePool.Alloc() ) XMLAttribute();
-            attrib->memPool = &document->attributePool;
-
-            p = attrib->ParseDeep( p, document->ProcessEntities() );
-            if ( !p || Attribute( attrib->Name() ) ) {
-                DELETE_ATTRIBUTE( attrib );
-                document->SetError( XML_ERROR_PARSING_ATTRIBUTE, start, p );
-                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 ) { 
-                prevAttribute->next = attrib;
-            }
-            else {
-                rootAttribute = attrib;
-            }    
-            prevAttribute = attrib;
-        }
-        // end of the tag
-        else if ( *p == '/' && *(p+1) == '>' ) {
-            closingType = CLOSED;
-            return p+2;    // done; sealed element.
-        }
-        // end of the tag
-        else if ( *p == '>' ) {
-            ++p;
-            break;
-        }
-        else {
-            document->SetError( XML_ERROR_PARSING_ELEMENT, start, p );
-            return 0;
-        }
-    }
-    return p;
-}
-
-
-//
-//    <ele></ele>
-//    <ele>foo<b>bar</b></ele>
-//
-char* XMLElement::ParseDeep( char* p, StrPair* strPair )
-{
-    // Read the element name.
-    p = XMLUtil::SkipWhiteSpace( p );
-    if ( !p ) return 0;
-
-    // The closing element is the </element> form. It is
-    // parsed just like a regular element then deleted from
-    // the DOM.
-    if ( *p == '/' ) {
-        closingType = CLOSING;
-        ++p;
-    }
-
-    p = value.ParseName( p );
-    if ( value.Empty() ) return 0;
-
-    p = ParseAttributes( p );
-    if ( !p || !*p || closingType ) 
-        return p;
-
-    p = XMLNode::ParseDeep( p, strPair );
-    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
-{
-    const XMLElement* other = compare->ToElement();
-    if ( other && XMLUtil::StringEqual( other->Value(), Value() )) {
-
-        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
-{
-    if ( visitor->VisitEnter( *this, rootAttribute ) ) 
-    {
-        for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() )
-        {
-            if ( !node->Accept( visitor ) )
-                break;
-        }
-    }
-    return visitor->VisitExit( *this );
-}
-
-
-// --------- XMLDocument ----------- //
-XMLDocument::XMLDocument( bool _processEntities ) :
-    XMLNode( 0 ),
-    writeBOM( false ),
-    processEntities( _processEntities ),
-    errorID( 0 ),
-    errorStr1( 0 ),
-    errorStr2( 0 ),
-    charBuffer( 0 )
-{
-    document = this;    // avoid warning about 'this' in initializer list
-}
-
-
-XMLDocument::~XMLDocument()
-{
-    DeleteChildren();
-    delete [] charBuffer;
-
-#if 0
-    textPool.Trace( "text" );
-    elementPool.Trace( "element" );
-    commentPool.Trace( "comment" );
-    attributePool.Trace( "attribute" );
-#endif
-
-    TIXMLASSERT( textPool.CurrentAllocs() == 0 );
-    TIXMLASSERT( elementPool.CurrentAllocs() == 0 );
-    TIXMLASSERT( commentPool.CurrentAllocs() == 0 );
-    TIXMLASSERT( attributePool.CurrentAllocs() == 0 );
-}
-
-
-void XMLDocument::InitDocument()
-{
-    errorID = XML_NO_ERROR;
-    errorStr1 = 0;
-    errorStr2 = 0;
-
-    delete [] charBuffer;
-    charBuffer = 0;
-
-}
-
-
-XMLElement* XMLDocument::NewElement( const char* name )
-{
-    XMLElement* ele = new (elementPool.Alloc()) XMLElement( this );
-    ele->memPool = &elementPool;
-    ele->SetName( name );
-    return ele;
-}
-
-
-XMLComment* XMLDocument::NewComment( const char* str )
-{
-    XMLComment* comment = new (commentPool.Alloc()) XMLComment( this );
-    comment->memPool = &commentPool;
-    comment->SetValue( str );
-    return comment;
-}
-
-
-XMLText* XMLDocument::NewText( const char* str )
-{
-    XMLText* text = new (textPool.Alloc()) XMLText( this );
-    text->memPool = &textPool;
-    text->SetValue( str );
-    return text;
-}
-
-
-XMLDeclaration* XMLDocument::NewDeclaration( const char* str )
-{
-    XMLDeclaration* dec = new (commentPool.Alloc()) XMLDeclaration( this );
-    dec->memPool = &commentPool;
-    dec->SetValue( str ? str : "xml version=\"1.0\" encoding=\"UTF-8\"" );
-    return dec;
-}
-
-
-XMLUnknown* XMLDocument::NewUnknown( const char* str )
-{
-    XMLUnknown* unk = new (commentPool.Alloc()) XMLUnknown( this );
-    unk->memPool = &commentPool;
-    unk->SetValue( str );
-    return unk;
-}
-
-
-int XMLDocument::LoadFile( const char* filename )
-{
-    DeleteChildren();
-    InitDocument();
-
-#if defined(_MSC_VER)
-#pragma warning ( push )
-#pragma warning ( disable : 4996 )        // Fail to see a compelling reason why this should be deprecated.
-#endif
-    FILE* fp = fopen( filename, "rb" );
-#if defined(_MSC_VER)
-#pragma warning ( pop )
-#endif
-    if ( !fp ) {
-        SetError( XML_ERROR_FILE_NOT_FOUND, filename, 0 );
-        return errorID;
-    }
-    LoadFile( fp );
-    fclose( fp );
-    return errorID;
-}
-
-
-int XMLDocument::LoadFile( FILE* fp ) 
-{
-    DeleteChildren();
-    InitDocument();
-
-    fseek( fp, 0, SEEK_END );
-    unsigned size = ftell( fp );
-    fseek( fp, 0, SEEK_SET );
-
-    if ( size == 0 ) {
-        return errorID;
-    }
-
-    charBuffer = new char[size+1];
-    size_t read = fread( charBuffer, 1, size, fp );
-    if ( read != size ) {
-        SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 );
-        return errorID;
-    }
-    
-    charBuffer[size] = 0;
-
-    const char* p = charBuffer;
-    p = XMLUtil::SkipWhiteSpace( p );
-    p = XMLUtil::ReadBOM( p, &writeBOM );
-    if ( !p || !*p ) {
-        SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
-        return errorID;
-    }
-
-    ParseDeep( charBuffer + (p-charBuffer), 0 );
-    return errorID;
-}
-
-
-int XMLDocument::SaveFile( const char* filename )
-{
-#if defined(_MSC_VER)
-#pragma warning ( push )
-#pragma warning ( disable : 4996 )        // Fail to see a compelling reason why this should be deprecated.
-#endif
-    FILE* fp = fopen( filename, "w" );
-#if defined(_MSC_VER)
-#pragma warning ( pop )
-#endif
-    if ( !fp ) {
-        SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, filename, 0 );
-        return errorID;
-    }
-    SaveFile(fp);
-    fclose( fp );
-    return errorID;
-}
-
-
-int XMLDocument::SaveFile( FILE* fp )
-{
-    XMLPrinter stream( fp );
-    Print( &stream );
-    return errorID;
-}
-
-
-int XMLDocument::Parse( const char* p )
-{
-    DeleteChildren();
-    InitDocument();
-
-    if ( !p || !*p ) {
-        SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
-        return errorID;
-    }
-    p = XMLUtil::SkipWhiteSpace( p );
-    p = XMLUtil::ReadBOM( p, &writeBOM );
-    if ( !p || !*p ) {
-        SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 );
-        return errorID;
-    }
-
-    size_t len = strlen( p );
-    charBuffer = new char[ len+1 ];
-    memcpy( charBuffer, p, len+1 );
-
-    
-    ParseDeep( charBuffer, 0 );
-    return errorID;
-}
-
-
-void XMLDocument::Print( XMLPrinter* streamer ) 
-{
-    XMLPrinter stdStreamer( stdout );
-    if ( !streamer )
-        streamer = &stdStreamer;
-    Accept( streamer );
-}
-
-
-void XMLDocument::SetError( int error, const char* str1, const char* str2 )
-{
-    errorID = error;
-    errorStr1 = str1;
-    errorStr2 = str2;
-}
-
-
-void XMLDocument::PrintError() const 
-{
-    if ( errorID ) {
-        static const int LEN = 20;
-        char buf1[LEN] = { 0 };
-        char buf2[LEN] = { 0 };
-        
-        if ( errorStr1 ) {
-            TIXML_SNPRINTF( buf1, LEN, "%s", errorStr1 );
-        }
-        if ( errorStr2 ) {
-            TIXML_SNPRINTF( buf2, LEN, "%s", errorStr2 );
-        }
-
-        printf( "XMLDocument error id=%d str1=%s str2=%s\n",
-                errorID, buf1, buf2 );
-    }
-}
-
-
-XMLPrinter::XMLPrinter( FILE* file, bool compact ) : 
-    elementJustOpened( false ), 
-    firstElement( true ),
-    fp( file ), 
-    depth( 0 ), 
-    textDepth( -1 ),
-    processEntities( true ),
-    compactMode( compact )
-{
-    for( int i=0; i<ENTITY_RANGE; ++i ) {
-        entityFlag[i] = false;
-        restrictedEntityFlag[i] = false;
-    }
-    for( int i=0; i<NUM_ENTITIES; ++i ) {
-        TIXMLASSERT( entities[i].value < ENTITY_RANGE );
-        if ( entities[i].value < ENTITY_RANGE ) {
-            entityFlag[ (int)entities[i].value ] = true;
-        }
-    }
-    restrictedEntityFlag[(int)'&'] = true;
-    restrictedEntityFlag[(int)'<'] = true;
-    restrictedEntityFlag[(int)'>'] = true;    // not required, but consistency is nice
-    buffer.Push( 0 );
-}
-
-
-void XMLPrinter::Print( const char* format, ... )
-{
-    va_list     va;
-    va_start( va, format );
-
-    if ( fp ) {
-        vfprintf( fp, format, va );
-    }
-    else {
-        // This seems brutally complex. Haven't figured out a better
-        // way on windows.
-        #ifdef _MSC_VER
-            int len = -1;
-            int expand = 1000;
-            while ( len < 0 ) {
-                len = vsnprintf_s( accumulator.Mem(), accumulator.Capacity(), _TRUNCATE, format, va );
-                if ( len < 0 ) {
-                    expand *= 3/2;
-                    accumulator.PushArr( expand );
-                }
-            }
-            char* p = buffer.PushArr( len ) - 1;
-            memcpy( p, accumulator.Mem(), len+1 );
-        #else
-            int len = vsnprintf( 0, 0, format, va );
-            // Close out and re-start the va-args
-            va_end( va );
-            va_start( va, format );        
-            char* p = buffer.PushArr( len ) - 1;
-            vsnprintf( p, len+1, format, va );
-        #endif
-    }
-    va_end( va );
-}
-
-
-void XMLPrinter::PrintSpace( int depth )
-{
-    for( int i=0; i<depth; ++i ) {
-        Print( "    " );
-    }
-}
-
-
-void XMLPrinter::PrintString( const char* p, bool restricted )
-{
-    // Look for runs of bytes between entities to print.
-    const char* q = p;
-    const bool* flag = restricted ? restrictedEntityFlag : entityFlag;
-
-    if ( processEntities ) {
-        while ( *q ) {
-            // Remember, char 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[(unsigned)(*q)] ) {
-                    while ( p < q ) {
-                        Print( "%c", *p );
-                        ++p;
-                    }
-                    for( int i=0; i<NUM_ENTITIES; ++i ) {
-                        if ( entities[i].value == *q ) {
-                            Print( "&%s;", entities[i].pattern );
-                            break;
-                        }
-                    }
-                    ++p;
-                }
-            }
-            ++q;
-        }
-    }
-    // Flush the remaining string. This will be the entire
-    // string if an entity wasn't found.
-    if ( !processEntities || (q-p > 0) ) {
-        Print( "%s", p );
-    }
-}
-
-
-void XMLPrinter::PushHeader( bool writeBOM, bool writeDec )
-{
-    static const unsigned char bom[] = { TIXML_UTF_LEAD_0, TIXML_UTF_LEAD_1, TIXML_UTF_LEAD_2, 0 };
-    if ( writeBOM ) {
-        Print( "%s", bom );
-    }
-    if ( writeDec ) {
-        PushDeclaration( "xml version=\"1.0\"" );
-    }
-}
-
-
-void XMLPrinter::OpenElement( const char* name )
-{
-    if ( elementJustOpened ) {
-        SealElement();
-    }
-    stack.Push( name );
-
-    if ( textDepth < 0 && !firstElement && !compactMode ) {
-        Print( "\n" );
-        PrintSpace( depth );
-    }
-
-    Print( "<%s", name );
-    elementJustOpened = true;
-    firstElement = false;
-    ++depth;
-}
-
-
-void XMLPrinter::PushAttribute( const char* name, const char* value )
-{
-    TIXMLASSERT( elementJustOpened );
-    Print( " %s=\"", name );
-    PrintString( value, false );
-    Print( "\"" );
-}
-
-
-void XMLPrinter::PushAttribute( const char* name, int v )
-{
-    char buf[BUF_SIZE];
-    XMLUtil::ToStr( v, buf, BUF_SIZE );
-    PushAttribute( name, buf );
-}
-
-
-void XMLPrinter::PushAttribute( const char* name, unsigned v )
-{
-    char buf[BUF_SIZE];
-    XMLUtil::ToStr( v, buf, BUF_SIZE );
-    PushAttribute( name, buf );
-}
-
-
-void XMLPrinter::PushAttribute( const char* name, bool v )
-{
-    char buf[BUF_SIZE];
-    XMLUtil::ToStr( v, buf, BUF_SIZE );
-    PushAttribute( name, buf );
-}
-
-
-void XMLPrinter::PushAttribute( const char* name, double v )
-{
-    char buf[BUF_SIZE];
-    XMLUtil::ToStr( v, buf, BUF_SIZE );
-    PushAttribute( name, buf );
-}
-
-
-void XMLPrinter::CloseElement()
-{
-    --depth;
-    const char* name = stack.Pop();
-
-    if ( elementJustOpened ) {
-        Print( "/>" );
-    }
-    else {
-        if ( textDepth < 0 && !compactMode) {
-            Print( "\n" );
-            PrintSpace( depth );
-        }
-        Print( "</%s>", name );
-    }
-
-    if ( textDepth == depth )
-        textDepth = -1;
-    if ( depth == 0 && !compactMode)
-        Print( "\n" );
-    elementJustOpened = false;
-}
-
-
-void XMLPrinter::SealElement()
-{
-    elementJustOpened = false;
-    Print( ">" );
-}
-
-
-void XMLPrinter::PushText( const char* text, bool cdata )
-{
-    textDepth = depth-1;
-
-    if ( elementJustOpened ) {
-        SealElement();
-    }
-    if ( cdata ) {
-        Print( "<![CDATA[" );
-        Print( "%s", text );
-        Print( "]]>" );
-    }
-    else {
-        PrintString( text, true );
-    }
-}
-
-void XMLPrinter::PushText( int value ) 
-{
-    char buf[BUF_SIZE];
-    XMLUtil::ToStr( value, buf, BUF_SIZE );
-    PushText( buf, false );
-}
-
-
-void XMLPrinter::PushText( unsigned value ) 
-{
-    char buf[BUF_SIZE];
-    XMLUtil::ToStr( value, buf, BUF_SIZE );
-    PushText( buf, false );
-}
-
-
-void XMLPrinter::PushText( bool value ) 
-{
-    char buf[BUF_SIZE];
-    XMLUtil::ToStr( value, buf, BUF_SIZE );
-    PushText( buf, false );
-}
-
-
-void XMLPrinter::PushText( float value ) 
-{
-    char buf[BUF_SIZE];
-    XMLUtil::ToStr( value, buf, BUF_SIZE );
-    PushText( buf, false );
-}
-
-
-void XMLPrinter::PushText( double value ) 
-{
-    char buf[BUF_SIZE];
-    XMLUtil::ToStr( value, buf, BUF_SIZE );
-    PushText( buf, false );
-}
-
-
-void XMLPrinter::PushComment( const char* comment )
-{
-    if ( elementJustOpened ) {
-        SealElement();
-    }
-    if ( textDepth < 0 && !firstElement && !compactMode) {
-        Print( "\n" );
-        PrintSpace( depth );
-    }
-    firstElement = false;
-    Print( "<!--%s-->", comment );
-}
-
-
-void XMLPrinter::PushDeclaration( const char* value )
-{
-    if ( elementJustOpened ) {
-        SealElement();
-    }
-    if ( textDepth < 0 && !firstElement && !compactMode) {
-        Print( "\n" );
-        PrintSpace( depth );
-    }
-    firstElement = false;
-    Print( "<?%s?>", value );
-}
-
-
-void XMLPrinter::PushUnknown( const char* value )
-{
-    if ( elementJustOpened ) {
-        SealElement();
-    }
-    if ( textDepth < 0 && !firstElement && !compactMode) {
-        Print( "\n" );
-        PrintSpace( depth );
-    }
-    firstElement = false;
-    Print( "<!%s>", value );
-}
-
-
-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 )
-{
-    OpenElement( element.Name() );
-    while ( attribute ) {
-        PushAttribute( attribute->Name(), attribute->Value() );
-        attribute = attribute->Next();
-    }
-    return true;
-}
-
-
-bool XMLPrinter::VisitExit( const XMLElement& )
-{
-    CloseElement();
-    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;
-}
--- a/TinyXml2/tinyxml2.h	Sun Aug 19 15:57:55 2012 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1483 +0,0 @@
-/*
-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.
-*/
-
-#ifndef TINYXML2_INCLUDED
-#define TINYXML2_INCLUDED
-
-#include <cctype>
-#include <climits>
-#include <cstdio>
-#include <cstring>
-#include <cstdarg>
-#include "FATFileSystem.h"
-
-using namespace std;
-
-/* 
-   TODO: intern strings instead of allocation.
-*/
-/*
-    gcc: g++ -Wall tinyxml2.cpp xmltest.cpp -o gccxmltest.exe
-*/
-
-#if defined( _DEBUG ) || defined( DEBUG ) || defined (__DEBUG__)
-    #ifndef DEBUG
-        #define DEBUG
-    #endif
-#endif
-
-
-#if defined(DEBUG)
-        #if defined(_MSC_VER)
-                #define TIXMLASSERT( x )           if ( !(x)) { __debugbreak(); } //if ( !(x)) WinDebugBreak()
-        #elif defined (ANDROID_NDK)
-                #include <android/log.h>
-                #define TIXMLASSERT( x )           if ( !(x)) { __android_log_assert( "assert", "grinliz", "ASSERT in '%s' at %d.", __FILE__, __LINE__ ); }
-        #else
-                #include <assert.h>
-                #define TIXMLASSERT                assert
-        #endif
-#else
-        #define TIXMLASSERT( x )           {}
-#endif
-
-
-#if defined(_MSC_VER) && (_MSC_VER >= 1400 )
-    // Microsoft visual studio, version 2005 and higher.
-    /*int _snprintf_s(
-       char *buffer,
-       size_t sizeOfBuffer,
-       size_t count,    
-       const char *format [,
-          argument] ... 
-    );*/
-    inline int TIXML_SNPRINTF( char* buffer, size_t size, const char* format, ... ) {
-        va_list va;
-        va_start( va, format );
-        int result = vsnprintf_s( buffer, size, _TRUNCATE, format, va );
-        va_end( va );
-        return result;
-    }
-    #define TIXML_SSCANF   sscanf_s
-#else
-    // GCC version 3 and higher
-    //#warning( "Using sn* functions." )
-    #define TIXML_SNPRINTF snprintf
-    #define TIXML_SSCANF   sscanf
-#endif
-
-static const int TIXML2_MAJOR_VERSION = 1;
-static const int TIXML2_MINOR_VERSION = 0;
-static const int TIXML2_PATCH_VERSION = 6;
-
-namespace tinyxml2
-{
-class XMLDocument;
-class XMLElement;
-class XMLAttribute;
-class XMLComment;
-class XMLNode;
-class XMLText;
-class XMLDeclaration;
-class XMLUnknown;
-
-class XMLPrinter;
-
-/*
-    A class that wraps strings. Normally stores the start and end
-    pointers into the XML file itself, and will apply normalization
-    and entity translation if actually read. Can also store (and memory
-    manage) a traditional char[]
-*/
-class StrPair
-{
-public:
-    enum {
-        NEEDS_ENTITY_PROCESSING            = 0x01,
-        NEEDS_NEWLINE_NORMALIZATION        = 0x02,
-
-        TEXT_ELEMENT        = NEEDS_ENTITY_PROCESSING | NEEDS_NEWLINE_NORMALIZATION,
-        TEXT_ELEMENT_LEAVE_ENTITIES        = NEEDS_NEWLINE_NORMALIZATION,
-        ATTRIBUTE_NAME        = 0,
-        ATTRIBUTE_VALUE        = NEEDS_ENTITY_PROCESSING | NEEDS_NEWLINE_NORMALIZATION,
-        ATTRIBUTE_VALUE_LEAVE_ENTITIES        = NEEDS_NEWLINE_NORMALIZATION,
-        COMMENT                = NEEDS_NEWLINE_NORMALIZATION
-    };
-
-    StrPair() : flags( 0 ), start( 0 ), end( 0 ) {}
-    ~StrPair();
-
-    void Set( char* _start, char* _end, int _flags ) {
-        Reset();
-        this->start = _start; this->end = _end; this->flags = _flags | NEEDS_FLUSH;
-    }
-    const char* GetStr();
-    bool Empty() const { return start == end; }
-
-    void SetInternedStr( const char* str ) { Reset(); this->start = const_cast<char*>(str); }
-    void SetStr( const char* str, int flags=0 );
-
-    char* ParseText( char* in, const char* endTag, int strFlags );    
-    char* ParseName( char* in );
-
-
-private:
-    void Reset();
-
-    enum {
-        NEEDS_FLUSH = 0x100,
-        NEEDS_DELETE = 0x200
-    };
-
-    // After parsing, if *end != 0, it can be set to zero.
-    int flags;
-    char* start;    
-    char* end;
-};
-
-
-/*
-    A dynamic array of Plain Old Data. Doesn't support constructors, etc.
-    Has a small initial memory pool, so that low or no usage will not
-    cause a call to new/delete
-*/
-template <class T, int INIT>
-class DynArray
-{
-public:
-    DynArray< T, INIT >() 
-    {
-        mem = pool;
-        allocated = INIT;
-        size = 0;
-    }
-    ~DynArray()
-    {
-        if ( mem != pool ) {
-            delete [] mem;
-        }
-    }
-    void Push( T t )
-    {
-        EnsureCapacity( size+1 );
-        mem[size++] = t;
-    }
-
-    T* PushArr( int count )
-    {
-        EnsureCapacity( size+count );
-        T* ret = &mem[size];
-        size += count;
-        return ret;
-    }
-    T Pop() {
-        return mem[--size];
-    }
-    void PopArr( int count ) 
-    {
-        TIXMLASSERT( size >= count );
-        size -= count;
-    }
-
-    bool Empty() const                    { return size == 0; }
-    T& operator[](int i)                { TIXMLASSERT( i>= 0 && i < size ); return mem[i]; }
-    const T& operator[](int i) const    { TIXMLASSERT( i>= 0 && i < size ); return mem[i]; }
-    int Size() const                    { return size; }
-    int Capacity() const                { return allocated; }
-    const T* Mem() const                { return mem; }
-    T* Mem()                            { return mem; }
-
-
-private:
-    void EnsureCapacity( int cap ) {
-        if ( cap > allocated ) {
-            int newAllocated = cap * 2;
-            T* newMem = new T[newAllocated];
-            memcpy( newMem, mem, sizeof(T)*size );    // warning: not using constructors, only works for PODs
-            if ( mem != pool ) delete [] mem;
-            mem = newMem;
-            allocated = newAllocated;
-        }
-    }
-
-    T* mem;
-    T pool[INIT];
-    int allocated;        // objects allocated
-    int size;            // number objects in use
-};
-
-
-/*
-    Parent virtual class of a pool for fast allocation
-    and deallocation of objects.
-*/
-class MemPool
-{
-public:
-    MemPool() {}
-    virtual ~MemPool() {}
-
-    virtual int ItemSize() const = 0;
-    virtual void* Alloc() = 0;
-    virtual void Free( void* ) = 0; 
-};
-
-
-/*
-    Template child class to create pools of the correct type.
-*/
-template< int SIZE >
-class MemPoolT : public MemPool
-{
-public:
-    MemPoolT() : root(0), currentAllocs(0), nAllocs(0), maxAllocs(0)    {}
-    virtual ~MemPoolT() {
-        // Delete the blocks.
-        for( int i=0; i<blockPtrs.Size(); ++i ) {
-            delete blockPtrs[i];
-        }
-    }
-
-    virtual int ItemSize() const    { return SIZE; }
-    int CurrentAllocs() const        { return currentAllocs; }
-
-    virtual void* Alloc() {
-        if ( !root ) {
-            // Need a new block.
-            Block* block = new Block();
-            blockPtrs.Push( block );
-
-            for( int i=0; i<COUNT-1; ++i ) {
-                block->chunk[i].next = &block->chunk[i+1];
-            }
-            block->chunk[COUNT-1].next = 0;
-            root = block->chunk;
-        }
-        void* result = root;
-        root = root->next;
-
-        ++currentAllocs;
-        if ( currentAllocs > maxAllocs ) maxAllocs = currentAllocs;
-        nAllocs++;
-        return result;
-    }
-    virtual void Free( void* mem ) {
-        if ( !mem ) return;
-        --currentAllocs;
-        Chunk* chunk = (Chunk*)mem;
-        memset( chunk, 0xfe, sizeof(Chunk) );
-        chunk->next = root;
-        root = chunk;
-    }
-    void Trace( const char* name ) {
-        printf( "Mempool %s watermark=%d [%dk] current=%d size=%d nAlloc=%d blocks=%d\n",
-                 name, maxAllocs, maxAllocs*SIZE/1024, currentAllocs, SIZE, nAllocs, blockPtrs.Size() );
-    }
-
-private:
-    enum { COUNT = 1024/SIZE };
-    union Chunk {
-        Chunk* next;
-        char mem[SIZE];
-    };
-    struct Block {
-        Chunk chunk[COUNT];
-    };
-    DynArray< Block*, 10 > blockPtrs;
-    Chunk* root;
-
-    int currentAllocs;
-    int nAllocs;
-    int maxAllocs;
-};
-
-
-
-/**
-    Implements the interface to the "Visitor pattern" (see the Accept() method.)
-    If you call the Accept() method, it requires being passed a XMLVisitor
-    class to handle callbacks. For nodes that contain other nodes (Document, Element)
-    you will get called with a VisitEnter/VisitExit pair. Nodes that are always leafs
-    are simply called with Visit().
-
-    If you return 'true' from a Visit method, recursive parsing will continue. If you return
-    false, <b>no children of this node or its sibilings</b> will be visited.
-
-    All flavors of Visit methods have a default implementation that returns 'true' (continue 
-    visiting). You need to only override methods that are interesting to you.
-
-    Generally Accept() is called on the TiXmlDocument, although all nodes support visiting.
-
-    You should never change the document from a callback.
-
-    @sa XMLNode::Accept()
-*/
-class XMLVisitor
-{
-public:
-    virtual ~XMLVisitor() {}
-
-    /// Visit a document.
-    virtual bool VisitEnter( const XMLDocument& /*doc*/ )            { return true; }
-    /// Visit a document.
-    virtual bool VisitExit( const XMLDocument& /*doc*/ )            { return true; }
-
-    /// Visit an element.
-    virtual bool VisitEnter( const XMLElement& /*element*/, const XMLAttribute* /*firstAttribute*/ )    { return true; }
-    /// Visit an element.
-    virtual bool VisitExit( const XMLElement& /*element*/ )            { return true; }
-
-    /// Visit a declaration.
-    virtual bool Visit( const XMLDeclaration& /*declaration*/ )        { return true; }
-    /// Visit a text node.
-    virtual bool Visit( const XMLText& /*text*/ )                    { return true; }
-    /// Visit a comment node.
-    virtual bool Visit( const XMLComment& /*comment*/ )                { return true; }
-    /// Visit an unknown node.
-    virtual bool Visit( const XMLUnknown& /*unknown*/ )                { return true; }
-};
-
-
-/*
-    Utility functionality.
-*/
-class XMLUtil
-{
-public:
-    // Anything in the high order range of UTF-8 is assumed to not be whitespace. This isn't 
-    // correct, but simple, and usually works.
-    static const char* SkipWhiteSpace( const char* p )    { while( !IsUTF8Continuation(*p) && isspace( *reinterpret_cast<const unsigned char*>(p) ) ) { ++p; } return p; }
-    static char* SkipWhiteSpace( char* p )                { while( !IsUTF8Continuation(*p) && isspace( *reinterpret_cast<unsigned char*>(p) ) )        { ++p; } return p; }
-
-    inline static bool StringEqual( const char* p, const char* q, int nChar=INT_MAX )  {
-        int n = 0;
-        if ( p == q ) {
-            return true;
-        }
-        while( *p && *q && *p == *q && n<nChar ) {
-            ++p; ++q; ++n;
-        }
-        if ( (n == nChar) || ( *p == 0 && *q == 0 ) ) {
-            return true;
-        }
-        return false;
-    }
-    inline static int IsUTF8Continuation( const char p ) { return p & 0x80; }
-    inline static int IsAlphaNum( unsigned char anyByte )    { return ( anyByte < 128 ) ? isalnum( anyByte ) : 1; }
-    inline static int IsAlpha( unsigned char anyByte )        { return ( anyByte < 128 ) ? isalpha( anyByte ) : 1; }
-
-    static const char* ReadBOM( const char* p, bool* hasBOM );
-    // p is the starting location,
-    // the UTF-8 value of the entity will be placed in value, and length filled in.
-    static const char* GetCharacterRef( const char* p, char* value, int* length );
-    static void ConvertUTF32ToUTF8( unsigned long input, char* output, int* length );
-
-    // converts primitive types to strings
-    static void ToStr( int v, char* buffer, int bufferSize );
-    static void ToStr( unsigned v, char* buffer, int bufferSize );
-    static void ToStr( bool v, char* buffer, int bufferSize );
-    static void ToStr( float v, char* buffer, int bufferSize );
-    static void ToStr( double v, char* buffer, int bufferSize );
-
-    // converts strings to primitive types
-    static bool    ToInt( const char* str, int* value );
-    static bool ToUnsigned( const char* str, unsigned* value );
-    static bool    ToBool( const char* str, bool* value );
-    static bool    ToFloat( const char* str, float* value );
-    static bool ToDouble( const char* str, double* value );
-};
-
-
-/** XMLNode is a base class for every object that is in the
-    XML Document Object Model (DOM), except XMLAttributes.
-    Nodes have siblings, a parent, and children which can
-    be navigated. A node is always in a XMLDocument.
-    The type of a XMLNode can be queried, and it can 
-    be cast to its more defined type.
-
-    A XMLDocument allocates memory for all its Nodes.
-    When the XMLDocument gets deleted, all its Nodes
-    will also be deleted.
-
-    @verbatim
-    A Document can contain:    Element    (container or leaf)
-                            Comment (leaf)
-                            Unknown (leaf)
-                            Declaration( leaf )
-
-    An Element can contain:    Element (container or leaf)
-                            Text    (leaf)
-                            Attributes (not on tree)
-                            Comment (leaf)
-                            Unknown (leaf)
-
-    @endverbatim
-*/
-class XMLNode
-{
-    friend class XMLDocument;
-    friend class XMLElement;
-public:
-
-    /// Get the XMLDocument that owns this XMLNode.
-    const XMLDocument* GetDocument() const    { return document; }
-    /// Get the XMLDocument that owns this XMLNode.
-    XMLDocument* GetDocument()                { return document; }
-
-    virtual XMLElement*        ToElement()        { return 0; }    ///< Safely cast to an Element, or null.
-    virtual XMLText*        ToText()        { return 0; }    ///< Safely cast to Text, or null.
-    virtual XMLComment*        ToComment()        { return 0; }    ///< Safely cast to a Comment, or null.
-    virtual XMLDocument*    ToDocument()    { return 0; }    ///< Safely cast to a Document, or null.
-    virtual XMLDeclaration*    ToDeclaration()    { return 0; }    ///< Safely cast to a Declaration, or null.
-    virtual XMLUnknown*        ToUnknown()        { return 0; }    ///< Safely cast to an Unknown, or null.
-
-    virtual const XMLElement*        ToElement() const        { return 0; }
-    virtual const XMLText*            ToText() const            { return 0; }
-    virtual const XMLComment*        ToComment() const        { return 0; }
-    virtual const XMLDocument*        ToDocument() const        { return 0; }
-    virtual const XMLDeclaration*    ToDeclaration() const    { return 0; }
-    virtual const XMLUnknown*        ToUnknown() const        { return 0; }
-
-    /** The meaning of 'value' changes for the specific type.
-        @verbatim
-        Document:    empty
-        Element:    name of the element
-        Comment:    the comment text
-        Unknown:    the tag contents
-        Text:        the text string
-        @endverbatim
-    */
-    const char* Value() const            { return value.GetStr(); }
-    /** Set the Value of an XML node.
-        @sa Value()
-    */
-    void SetValue( const char* val, bool staticMem=false );
-
-    /// Get the parent of this node on the DOM.
-    const XMLNode*    Parent() const            { return parent; }
-    XMLNode* Parent()                        { return parent; }
-
-    /// Returns true if this node has no children.
-    bool NoChildren() const                    { return !firstChild; }
-
-    /// Get the first child node, or null if none exists.
-    const XMLNode*  FirstChild() const        { return firstChild; }
-    XMLNode*        FirstChild()            { return firstChild; }
-    /** Get the first child element, or optionally the first child
-        element with the specified name.
-    */
-    const XMLElement* FirstChildElement( const char* value=0 ) const;
-    XMLElement* FirstChildElement( const char* _value=0 )    { return const_cast<XMLElement*>(const_cast<const XMLNode*>(this)->FirstChildElement( _value )); }
-
-    /// Get the last child node, or null if none exists.
-    const XMLNode*    LastChild() const                        { return lastChild; }
-    XMLNode*        LastChild()                                { return const_cast<XMLNode*>(const_cast<const XMLNode*>(this)->LastChild() ); }
-
-    /** Get the last child element or optionally the last child
-        element with the specified name.
-    */
-    const XMLElement* LastChildElement( const char* value=0 ) const;
-    XMLElement* LastChildElement( const char* _value=0 )    { return const_cast<XMLElement*>(const_cast<const XMLNode*>(this)->LastChildElement(_value) ); }
-    
-    /// Get the previous (left) sibling node of this node.
-    const XMLNode*    PreviousSibling() const                    { return prev; }
-    XMLNode*    PreviousSibling()                            { return prev; }
-
-    /// Get the previous (left) sibling element of this node, with an opitionally supplied name.
-    const XMLElement*    PreviousSiblingElement( const char* value=0 ) const ;
-    XMLElement*    PreviousSiblingElement( const char* _value=0 ) { return const_cast<XMLElement*>(const_cast<const XMLNode*>(this)->PreviousSiblingElement( _value ) ); }
-    
-    /// Get the next (right) sibling node of this node.
-    const XMLNode*    NextSibling() const                        { return next; }
-    XMLNode*    NextSibling()                                { return next; }
-        
-    /// Get the next (right) sibling element of this node, with an opitionally supplied name.
-    const XMLElement*    NextSiblingElement( const char* value=0 ) const;
-     XMLElement*    NextSiblingElement( const char* _value=0 )    { return const_cast<XMLElement*>(const_cast<const XMLNode*>(this)->NextSiblingElement( _value ) ); }
-
-    /**
-        Add a child node as the last (right) child.
-    */
-    XMLNode* InsertEndChild( XMLNode* addThis );
-
-    XMLNode* LinkEndChild( XMLNode* addThis )    { return InsertEndChild( addThis ); }
-    /**
-        Add a child node as the first (left) child.
-    */
-    XMLNode* InsertFirstChild( XMLNode* addThis );
-    /**
-        Add a node after the specified child node.
-    */
-    XMLNode* InsertAfterChild( XMLNode* afterThis, XMLNode* addThis );
-    
-    /**
-        Delete all the children of this node.
-    */
-    void DeleteChildren();
-
-    /**
-        Delete a child of this node.
-    */
-    void DeleteChild( XMLNode* node );
-
-    /**
-        Make a copy of this node, but not its children.
-        You may pass in a Document pointer that will be
-        the owner of the new Node. If the 'document' is 
-        null, then the node returned will be allocated
-        from the current Document. (this->GetDocument())
-
-        Note: if called on a XMLDocument, this will return null.
-    */
-    virtual XMLNode* ShallowClone( XMLDocument* document ) const = 0;
-
-    /**
-        Test if 2 nodes are the same, but don't test children.
-        The 2 nodes do not need to be in the same Document.
-
-        Note: if called on a XMLDocument, this will return false.
-    */
-    virtual bool ShallowEqual( const XMLNode* compare ) const = 0;
-
-    /** Accept a hierarchical visit of the nodes in the TinyXML DOM. Every node in the 
-        XML tree will be conditionally visited and the host will be called back
-        via the TiXmlVisitor interface.
-
-        This is essentially a SAX interface for TinyXML. (Note however it doesn't re-parse
-        the XML for the callbacks, so the performance of TinyXML is unchanged by using this
-        interface versus any other.)
-
-        The interface has been based on ideas from:
-
-        - http://www.saxproject.org/
-        - http://c2.com/cgi/wiki?HierarchicalVisitorPattern 
-
-        Which are both good references for "visiting".
-
-        An example of using Accept():
-        @verbatim
-        TiXmlPrinter printer;
-        tinyxmlDoc.Accept( &printer );
-        const char* xmlcstr = printer.CStr();
-        @endverbatim
-    */
-    virtual bool Accept( XMLVisitor* visitor ) const = 0;
-
-    // internal
-    virtual char* ParseDeep( char*, StrPair* );
-
-protected:
-    XMLNode( XMLDocument* );
-    virtual ~XMLNode();
-    XMLNode( const XMLNode& );    // not supported
-    XMLNode& operator=( const XMLNode& );    // not supported
-    
-    XMLDocument*    document;
-    XMLNode*        parent;
-    mutable StrPair    value;
-
-    XMLNode*        firstChild;
-    XMLNode*        lastChild;
-
-    XMLNode*        prev;
-    XMLNode*        next;
-
-private:
-    MemPool*        memPool;
-    void Unlink( XMLNode* child );
-};
-
-
-/** XML text.
-
-    Note that a text node can have child element nodes, for example:
-    @verbatim
-    <root>This is <b>bold</b></root>
-    @endverbatim
-
-    A text node can have 2 ways to output the next. "normal" output 
-    and CDATA. It will default to the mode it was parsed from the XML file and
-    you generally want to leave it alone, but you can change the output mode with 
-    SetCDATA() and query it with CDATA().
-*/
-class XMLText : public XMLNode
-{
-    friend class XMLBase;
-    friend class XMLDocument;
-public:
-    virtual bool Accept( XMLVisitor* visitor ) const;
-
-    virtual XMLText*    ToText()            { return this; }
-    virtual const XMLText*    ToText() const    { return this; }
-
-    /// Declare whether this should be CDATA or standard text.
-    void SetCData( bool _isCData )            { this->isCData = _isCData; }
-    /// Returns true if this is a CDATA text element.
-    bool CData() const                        { return isCData; }
-
-    virtual char* ParseDeep( char*, StrPair* endTag );
-    virtual XMLNode* ShallowClone( XMLDocument* document ) const;
-    virtual bool ShallowEqual( const XMLNode* compare ) const;
-
-
-protected:
-    XMLText( XMLDocument* doc )    : XMLNode( doc ), isCData( false )    {}
-    virtual ~XMLText()                                                {}
-    XMLText( const XMLText& );    // not supported
-    XMLText& operator=( const XMLText& );    // not supported
-
-private:
-    bool isCData;
-};
-
-
-/** An XML Comment. */
-class XMLComment : public XMLNode
-{
-    friend class XMLDocument;
-public:
-    virtual XMLComment*    ToComment()                    { return this; }
-    virtual const XMLComment* ToComment() const        { return this; }
-
-    virtual bool Accept( XMLVisitor* visitor ) const;
-
-    virtual char* ParseDeep( char*, StrPair* endTag );
-    virtual XMLNode* ShallowClone( XMLDocument* document ) const;
-    virtual bool ShallowEqual( const XMLNode* compare ) const;
-
-protected:
-    XMLComment( XMLDocument* doc );
-    virtual ~XMLComment();
-    XMLComment( const XMLComment& );    // not supported
-    XMLComment& operator=( const XMLComment& );    // not supported
-
-private:
-};
-
-
-/** In correct XML the declaration is the first entry in the file.
-    @verbatim
-        <?xml version="1.0" standalone="yes"?>
-    @endverbatim
-
-    TinyXML2 will happily read or write files without a declaration,
-    however.
-
-    The text of the declaration isn't interpreted. It is parsed
-    and written as a string.
-*/
-class XMLDeclaration : public XMLNode
-{
-    friend class XMLDocument;
-public:
-    virtual XMLDeclaration*    ToDeclaration()                    { return this; }
-    virtual const XMLDeclaration* ToDeclaration() const        { return this; }
-
-    virtual bool Accept( XMLVisitor* visitor ) const;
-
-    virtual char* ParseDeep( char*, StrPair* endTag );
-    virtual XMLNode* ShallowClone( XMLDocument* document ) const;
-    virtual bool ShallowEqual( const XMLNode* compare ) const;
-
-protected:
-    XMLDeclaration( XMLDocument* doc );
-    virtual ~XMLDeclaration();
-    XMLDeclaration( const XMLDeclaration& );    // not supported
-    XMLDeclaration& operator=( const XMLDeclaration& );    // not supported
-};
-
-
-/** Any tag that tinyXml doesn't recognize is saved as an
-    unknown. It is a tag of text, but should not be modified.
-    It will be written back to the XML, unchanged, when the file
-    is saved.
-
-    DTD tags get thrown into TiXmlUnknowns.
-*/
-class XMLUnknown : public XMLNode
-{
-    friend class XMLDocument;
-public:
-    virtual XMLUnknown*    ToUnknown()                    { return this; }
-    virtual const XMLUnknown* ToUnknown() const        { return this; }
-
-    virtual bool Accept( XMLVisitor* visitor ) const;
-
-    virtual char* ParseDeep( char*, StrPair* endTag );
-    virtual XMLNode* ShallowClone( XMLDocument* document ) const;
-    virtual bool ShallowEqual( const XMLNode* compare ) const;
-
-protected:
-    XMLUnknown( XMLDocument* doc );
-    virtual ~XMLUnknown();
-    XMLUnknown( const XMLUnknown& );    // not supported
-    XMLUnknown& operator=( const XMLUnknown& );    // not supported
-};
-
-
-enum {
-    XML_NO_ERROR = 0,
-    XML_SUCCESS = 0,
-
-    XML_NO_ATTRIBUTE,
-    XML_WRONG_ATTRIBUTE_TYPE,
-
-    XML_ERROR_FILE_NOT_FOUND,
-    XML_ERROR_FILE_COULD_NOT_BE_OPENED,
-    XML_ERROR_FILE_READ_ERROR,
-    XML_ERROR_ELEMENT_MISMATCH,
-    XML_ERROR_PARSING_ELEMENT,
-    XML_ERROR_PARSING_ATTRIBUTE,
-    XML_ERROR_IDENTIFYING_TAG,
-    XML_ERROR_PARSING_TEXT,
-    XML_ERROR_PARSING_CDATA,
-    XML_ERROR_PARSING_COMMENT,
-    XML_ERROR_PARSING_DECLARATION,
-    XML_ERROR_PARSING_UNKNOWN,
-    XML_ERROR_EMPTY_DOCUMENT,
-    XML_ERROR_MISMATCHED_ELEMENT,
-    XML_ERROR_PARSING,
-
-    XML_CAN_NOT_CONVERT_TEXT,
-    XML_NO_TEXT_NODE
-};
-
-
-/** An attribute is a name-value pair. Elements have an arbitrary
-    number of attributes, each with a unique name.
-
-    @note The attributes are not XMLNodes. You may only query the
-    Next() attribute in a list.
-*/
-class XMLAttribute
-{
-    friend class XMLElement;
-public:
-    const char* Name() const { return name.GetStr(); }            ///< The name of the attribute.
-    const char* Value() const { return value.GetStr(); }        ///< The value of the attribute.
-    const XMLAttribute* Next() const { return next; }            ///< The next attribute in the list.
-
-    /** IntAttribute interprets the attribute as an integer, and returns the value.
-        If the value isn't an integer, 0 will be returned. There is no error checking;
-        use QueryIntAttribute() if you need error checking.
-    */
-    int         IntValue() const                { int i=0;        QueryIntValue( &i );        return i; }
-    /// Query as an unsigned integer. See IntAttribute()
-    unsigned UnsignedValue() const            { unsigned i=0; QueryUnsignedValue( &i );    return i; }
-    /// Query as a boolean. See IntAttribute()
-    bool     BoolValue() const                { bool b=false; QueryBoolValue( &b );        return b; }
-    /// Query as a double. See IntAttribute()
-    double      DoubleValue() const            { double d=0;    QueryDoubleValue( &d );        return d; }
-    /// Query as a float. See IntAttribute()
-    float     FloatValue() const                { float f=0;    QueryFloatValue( &f );        return f; }
-
-    /** QueryIntAttribute interprets the attribute as an integer, and returns the value
-        in the provided paremeter. The function will return XML_NO_ERROR on success,
-        and XML_WRONG_ATTRIBUTE_TYPE if the conversion is not successful.
-    */
-    int QueryIntValue( int* value ) const;
-    /// See QueryIntAttribute
-    int QueryUnsignedValue( unsigned int* value ) const;
-    /// See QueryIntAttribute
-    int QueryBoolValue( bool* value ) const;
-    /// See QueryIntAttribute
-    int QueryDoubleValue( double* value ) const;
-    /// See QueryIntAttribute
-    int QueryFloatValue( float* value ) const;
-
-    /// Set the attribute to a string value.
-    void SetAttribute( const char* value );
-    /// Set the attribute to value.
-    void SetAttribute( int value );
-    /// Set the attribute to value.
-    void SetAttribute( unsigned value );
-    /// Set the attribute to value.
-    void SetAttribute( bool value );
-    /// Set the attribute to value.
-    void SetAttribute( double value );
-    /// Set the attribute to value.
-    void SetAttribute( float value );
-
-private:
-    enum { BUF_SIZE = 200 };
-
-    XMLAttribute() : next( 0 ) {}
-    virtual ~XMLAttribute()    {}
-    XMLAttribute( const XMLAttribute& );    // not supported
-    void operator=( const XMLAttribute& );    // not supported
-    void SetName( const char* name );
-
-    char* ParseDeep( char* p, bool processEntities );
-
-    mutable StrPair name;
-    mutable StrPair value;
-    XMLAttribute* next;
-    MemPool* memPool;
-};
-
-
-/** The element is a container class. It has a value, the element name,
-    and can contain other elements, text, comments, and unknowns.
-    Elements also contain an arbitrary number of attributes.
-*/
-class XMLElement : public XMLNode
-{
-    friend class XMLBase;
-    friend class XMLDocument;
-public:
-    /// Get the name of an element (which is the Value() of the node.)
-    const char* Name() const        { return Value(); }
-    /// Set the name of the element.
-    void SetName( const char* str, bool staticMem=false )    { SetValue( str, staticMem ); }
-
-    virtual XMLElement* ToElement()                { return this; }
-    virtual const XMLElement* ToElement() const { return this; }
-    virtual bool Accept( XMLVisitor* visitor ) const;
-
-    /** Given an attribute name, Attribute() returns the value
-        for the attribute of that name, or null if none 
-        exists. For example:
-
-        @verbatim
-        const char* value = ele->Attribute( "foo" );
-        @endverbatim
-
-        The 'value' parameter is normally null. However, if specified, 
-        the attribute will only be returned if the 'name' and 'value' 
-        match. This allow you to write code:
-
-        @verbatim
-        if ( ele->Attribute( "foo", "bar" ) ) callFooIsBar();
-        @endverbatim
-
-        rather than:
-        @verbatim
-        if ( ele->Attribute( "foo" ) ) {
-            if ( strcmp( ele->Attribute( "foo" ), "bar" ) == 0 ) callFooIsBar();
-        }
-        @endverbatim
-    */
-    const char* Attribute( const char* name, const char* value=0 ) const;
-
-    /** Given an attribute name, IntAttribute() returns the value
-        of the attribute interpreted as an integer. 0 will be
-        returned if there is an error. For a method with error 
-        checking, see QueryIntAttribute()
-    */
-    int         IntAttribute( const char* name ) const        { int i=0;        QueryIntAttribute( name, &i );        return i; }
-    /// See IntAttribute()
-    unsigned UnsignedAttribute( const char* name ) const{ unsigned i=0; QueryUnsignedAttribute( name, &i ); return i; }
-    /// See IntAttribute()
-    bool     BoolAttribute( const char* name ) const    { bool b=false; QueryBoolAttribute( name, &b );        return b; }
-    /// See IntAttribute()
-    double      DoubleAttribute( const char* name ) const    { double d=0;    QueryDoubleAttribute( name, &d );    return d; }
-    /// See IntAttribute()
-    float     FloatAttribute( const char* name ) const    { float f=0;    QueryFloatAttribute( name, &f );    return f; }
-
-    /** Given an attribute name, QueryIntAttribute() returns 
-        XML_NO_ERROR, XML_WRONG_ATTRIBUTE_TYPE if the conversion
-        can't be performed, or XML_NO_ATTRIBUTE if the attribute
-        doesn't exist. If successful, the result of the conversion
-        will be written to 'value'. If not successful, nothing will
-        be written to 'value'. This allows you to provide default
-        value:
-
-        @verbatim
-        int value = 10;
-        QueryIntAttribute( "foo", &value );        // if "foo" isn't found, value will still be 10
-        @endverbatim
-    */
-    int QueryIntAttribute( const char* name, int* _value ) const                { const XMLAttribute* a = FindAttribute( name ); if ( !a ) return XML_NO_ATTRIBUTE; return a->QueryIntValue( _value ); } 
-    /// See QueryIntAttribute()
-    int QueryUnsignedAttribute( const char* name, unsigned int* _value ) const    { const XMLAttribute* a = FindAttribute( name ); if ( !a ) return XML_NO_ATTRIBUTE; return a->QueryUnsignedValue( _value ); }
-    /// See QueryIntAttribute()
-    int QueryBoolAttribute( const char* name, bool* _value ) const                { const XMLAttribute* a = FindAttribute( name ); if ( !a ) return XML_NO_ATTRIBUTE; return a->QueryBoolValue( _value ); }
-    /// See QueryIntAttribute()
-    int QueryDoubleAttribute( const char* name, double* _value ) const            { const XMLAttribute* a = FindAttribute( name ); if ( !a ) return XML_NO_ATTRIBUTE; return a->QueryDoubleValue( _value ); }
-    /// See QueryIntAttribute()
-    int QueryFloatAttribute( const char* name, float* _value ) const            { const XMLAttribute* a = FindAttribute( name ); if ( !a ) return XML_NO_ATTRIBUTE; return a->QueryFloatValue( _value ); }
-
-    /// Sets the named attribute to value.
-    void SetAttribute( const char* name, const char* _value )    { XMLAttribute* a = FindOrCreateAttribute( name ); a->SetAttribute( _value ); }
-    /// Sets the named attribute to value.
-    void SetAttribute( const char* name, int _value )            { XMLAttribute* a = FindOrCreateAttribute( name ); a->SetAttribute( _value ); }
-    /// Sets the named attribute to value.
-    void SetAttribute( const char* name, unsigned _value )        { XMLAttribute* a = FindOrCreateAttribute( name ); a->SetAttribute( _value ); }
-    /// Sets the named attribute to value.
-    void SetAttribute( const char* name, bool _value )            { XMLAttribute* a = FindOrCreateAttribute( name ); a->SetAttribute( _value ); }
-    /// Sets the named attribute to value.
-    void SetAttribute( const char* name, double _value )        { XMLAttribute* a = FindOrCreateAttribute( name ); a->SetAttribute( _value ); }
-
-    /**
-        Delete an attribute.
-    */
-    void DeleteAttribute( const char* name );
-
-    /// Return the first attribute in the list.
-    const XMLAttribute* FirstAttribute() const { return rootAttribute; }
-    /// Query a specific attribute in the list.
-    const XMLAttribute* FindAttribute( const char* name ) const;
-
-    /** Convenience function for easy access to the text inside an element. Although easy
-        and concise, GetText() is limited compared to getting the TiXmlText child
-        and accessing it directly.
-    
-        If the first child of 'this' is a TiXmlText, the GetText()
-        returns the character string of the Text node, else null is returned.
-
-        This is a convenient method for getting the text of simple contained text:
-        @verbatim
-        <foo>This is text</foo>
-            const char* str = fooElement->GetText();
-        @endverbatim
-
-        'str' will be a pointer to "This is text". 
-        
-        Note that this function can be misleading. If the element foo was created from
-        this XML:
-        @verbatim
-            <foo><b>This is text</b></foo> 
-        @endverbatim
-
-        then the value of str would be null. The first child node isn't a text node, it is
-        another element. From this XML:
-        @verbatim
-            <foo>This is <b>text</b></foo> 
-        @endverbatim
-        GetText() will return "This is ".
-    */
-    const char* GetText() const;
-
-    /** 
-        Convenience method to query the value of a child text node. This is probably best
-        shown by example. Given you have a document is this form:
-        @verbatim
-            <point>
-                <x>1</x>
-                <y>1.4</y>
-            </point>
-        @endverbatim
-
-        The QueryIntText() and similar functions provide a safe and easier way to get to the
-        "value" of x and y.
-
-        @verbatim
-            int x = 0;
-            float y = 0;    // types of x and y are contrived for example
-            const XMLElement* xElement = pointElement->FirstChildElement( "x" );
-            const XMLElement* yElement = pointElement->FirstChildElement( "y" );
-            xElement->QueryIntText( &x );
-            yElement->QueryFloatText( &y );
-        @endverbatim
-
-        @returns XML_SUCCESS (0) on success, XML_CAN_NOT_CONVERT_TEXT if the text cannot be converted
-                 to the requested type, and XML_NO_TEXT_NODE if there is no child text to query.
-            
-    */
-    int QueryIntText( int* _value ) const;
-    /// See QueryIntText()
-    int QueryUnsignedText( unsigned* _value ) const;
-    /// See QueryIntText()
-    int QueryBoolText( bool* _value ) const;
-    /// See QueryIntText()
-    int QueryDoubleText( double* _value ) const;
-    /// See QueryIntText()
-    int QueryFloatText( float* _value ) const;
-
-    // internal:
-    enum {
-        OPEN,        // <foo>
-        CLOSED,        // <foo/>
-        CLOSING        // </foo>
-    };
-    int ClosingType() const { return closingType; }
-    virtual char* ParseDeep( char* p, StrPair* endTag );
-    virtual XMLNode* ShallowClone( XMLDocument* document ) const;
-    virtual bool ShallowEqual( const XMLNode* compare ) const;
-
-private:
-    XMLElement( XMLDocument* doc );
-    virtual ~XMLElement();
-    XMLElement( const XMLElement& );    // not supported
-    void operator=( const XMLElement& );    // not supported
-
-    XMLAttribute* FindAttribute( const char* name );
-    XMLAttribute* FindOrCreateAttribute( const char* name );
-    //void LinkAttribute( XMLAttribute* attrib );
-    char* ParseAttributes( char* p );
-
-    int closingType;
-    // The attribute list is ordered; there is no 'lastAttribute'
-    // because the list needs to be scanned for dupes before adding
-    // a new attribute.
-    XMLAttribute* rootAttribute;
-};
-
-
-/** A Document binds together all the functionality. 
-    It can be saved, loaded, and printed to the screen.
-    All Nodes are connected and allocated to a Document.
-    If the Document is deleted, all its Nodes are also deleted.
-*/
-class XMLDocument : public XMLNode
-{
-    friend class XMLElement;
-public:
-    /// constructor
-    XMLDocument( bool processEntities = true ); 
-    virtual ~XMLDocument();
-
-    virtual XMLDocument* ToDocument()                { return this; }
-    virtual const XMLDocument* ToDocument() const    { return this; }
-
-    /**
-        Parse an XML file from a character string.
-        Returns XML_NO_ERROR (0) on success, or
-        an errorID.
-    */
-    int Parse( const char* xml );
-    
-    /**
-        Load an XML file from disk.
-        Returns XML_NO_ERROR (0) on success, or
-        an errorID.
-    */    
-    int LoadFile( const char* filename );
-    
-    /**
-        Load an XML file from disk. You are responsible
-        for providing and closing the FILE*.
-
-        Returns XML_NO_ERROR (0) on success, or
-        an errorID.
-    */    
-    int LoadFile( FILE* );
-    
-    /**
-        Save the XML file to disk.
-        Returns XML_NO_ERROR (0) on success, or
-        an errorID.
-    */
-    int SaveFile( const char* filename );
-
-    /**
-        Save the XML file to disk. You are responsible
-        for providing and closing the FILE*.
-
-        Returns XML_NO_ERROR (0) on success, or
-        an errorID.
-    */
-    int SaveFile( FILE* );
-
-    bool ProcessEntities() const                        { return processEntities; }
-
-    /**
-        Returns true if this document has a leading Byte Order Mark of UTF8.
-    */
-    bool HasBOM() const { return writeBOM; }
-    /** Sets whether to write the BOM when writing the file.
-    */
-    void SetBOM( bool useBOM ) { writeBOM = useBOM; }
-
-    /** Return the root element of DOM. Equivalent to FirstChildElement().
-        To get the first node, use FirstChild().
-    */
-    XMLElement* RootElement()                { return FirstChildElement(); }
-    const XMLElement* RootElement() const    { return FirstChildElement(); }
-
-    /** Print the Document. If the Printer is not provided, it will
-        print to stdout. If you provide Printer, this can print to a file:
-        @verbatim
-        XMLPrinter printer( fp );
-        doc.Print( &printer );
-        @endverbatim
-
-        Or you can use a printer to print to memory:
-        @verbatim
-        XMLPrinter printer;
-        doc->Print( &printer );
-        // printer.CStr() has a const char* to the XML
-        @endverbatim
-    */
-    void Print( XMLPrinter* streamer=0 );
-    virtual bool Accept( XMLVisitor* visitor ) const;
-
-    /**
-        Create a new Element associated with
-        this Document. The memory for the Element
-        is managed by the Document.
-    */
-    XMLElement* NewElement( const char* name );
-    /**
-        Create a new Comment associated with
-        this Document. The memory for the Comment
-        is managed by the Document.
-    */
-    XMLComment* NewComment( const char* comment );
-    /**
-        Create a new Text associated with
-        this Document. The memory for the Text
-        is managed by the Document.
-    */
-    XMLText* NewText( const char* text );
-    /**
-        Create a new Declaration associated with
-        this Document. The memory for the object
-        is managed by the Document.
-
-        If the 'text' param is null, the standard
-        declaration is used.:
-        @verbatim
-            <?xml version="1.0" encoding="UTF-8"?>
-        @endverbatim
-    */
-    XMLDeclaration* NewDeclaration( const char* text=0 );
-    /**
-        Create a new Unknown associated with
-        this Document. The memory for the object
-        is managed by the Document.
-    */
-    XMLUnknown* NewUnknown( const char* text );
-
-    /**
-        Delete a node associated with this document.
-        It will be unlinked from the DOM.
-    */
-    void DeleteNode( XMLNode* node )    { node->parent->DeleteChild( node ); }
-
-    void SetError( int error, const char* str1, const char* str2 );
-    
-    /// Return true if there was an error parsing the document.
-    bool Error() const { return errorID != XML_NO_ERROR; }
-    /// Return the errorID.
-    int  ErrorID() const { return errorID; }
-    /// Return a possibly helpful diagnostic location or string.
-    const char* GetErrorStr1() const { return errorStr1; }
-    /// Return a possibly helpful secondary diagnostic location or string.
-    const char* GetErrorStr2() const { return errorStr2; }
-    /// If there is an error, print it to stdout.
-    void PrintError() const;
-
-    // internal
-    char* Identify( char* p, XMLNode** node );
-
-    virtual XMLNode* ShallowClone( XMLDocument* /*document*/ ) const    { return 0; }
-    virtual bool ShallowEqual( const XMLNode* /*compare*/ ) const    { return false; }
-
-private:
-    XMLDocument( const XMLDocument& );    // not supported
-    void operator=( const XMLDocument& );    // not supported
-    void InitDocument();
-
-    bool writeBOM;
-    bool processEntities;
-    int errorID;
-    const char* errorStr1;
-    const char* errorStr2;
-    char* charBuffer;
-
-    MemPoolT< sizeof(XMLElement) >    elementPool;
-    MemPoolT< sizeof(XMLAttribute) > attributePool;
-    MemPoolT< sizeof(XMLText) >        textPool;
-    MemPoolT< sizeof(XMLComment) >    commentPool;
-};
-
-
-/**
-    A XMLHandle is a class that wraps a node pointer with null checks; this is
-    an incredibly useful thing. Note that XMLHandle is not part of the TinyXML
-    DOM structure. It is a separate utility class.
-
-    Take an example:
-    @verbatim
-    <Document>
-        <Element attributeA = "valueA">
-            <Child attributeB = "value1" />
-            <Child attributeB = "value2" />
-        </Element>
-    </Document>
-    @endverbatim
-
-    Assuming you want the value of "attributeB" in the 2nd "Child" element, it's very 
-    easy to write a *lot* of code that looks like:
-
-    @verbatim
-    XMLElement* root = document.FirstChildElement( "Document" );
-    if ( root )
-    {
-        XMLElement* element = root->FirstChildElement( "Element" );
-        if ( element )
-        {
-            XMLElement* child = element->FirstChildElement( "Child" );
-            if ( child )
-            {
-                XMLElement* child2 = child->NextSiblingElement( "Child" );
-                if ( child2 )
-                {
-                    // Finally do something useful.
-    @endverbatim
-
-    And that doesn't even cover "else" cases. XMLHandle addresses the verbosity
-    of such code. A XMLHandle checks for null pointers so it is perfectly safe 
-    and correct to use:
-
-    @verbatim
-    XMLHandle docHandle( &document );
-    XMLElement* child2 = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).FirstChild().NextSibling().ToElement();
-    if ( child2 )
-    {
-        // do something useful
-    @endverbatim
-
-    Which is MUCH more concise and useful.
-
-    It is also safe to copy handles - internally they are nothing more than node pointers.
-    @verbatim
-    XMLHandle handleCopy = handle;
-    @endverbatim
-
-    See also XMLConstHandle, which is the same as XMLHandle, but operates on const objects.
-*/
-class XMLHandle
-{
-public:
-    /// Create a handle from any node (at any depth of the tree.) This can be a null pointer.
-    XMLHandle( XMLNode* _node )                                                { node = _node; }
-    /// Create a handle from a node.
-    XMLHandle( XMLNode& _node )                                                { node = &_node; }
-    /// Copy constructor
-    XMLHandle( const XMLHandle& ref )                                        { node = ref.node; }
-    /// Assignment
-    XMLHandle& operator=( const XMLHandle& ref )                            { node = ref.node; return *this; }
-
-    /// Get the first child of this handle.
-    XMLHandle FirstChild()                                                     { return XMLHandle( node ? node->FirstChild() : 0 ); }
-    /// Get the first child element of this handle.
-    XMLHandle FirstChildElement( const char* value=0 )                        { return XMLHandle( node ? node->FirstChildElement( value ) : 0 ); }
-    /// Get the last child of this handle.
-    XMLHandle LastChild()                                                    { return XMLHandle( node ? node->LastChild() : 0 ); }
-    /// Get the last child element of this handle.
-    XMLHandle LastChildElement( const char* _value=0 )                        { return XMLHandle( node ? node->LastChildElement( _value ) : 0 ); }
-    /// Get the previous sibling of this handle.
-    XMLHandle PreviousSibling()                                                { return XMLHandle( node ? node->PreviousSibling() : 0 ); }
-    /// Get the previous sibling element of this handle.
-    XMLHandle PreviousSiblingElement( const char* _value=0 )                { return XMLHandle( node ? node->PreviousSiblingElement( _value ) : 0 ); }
-    /// Get the next sibling of this handle.
-    XMLHandle NextSibling()                                                    { return XMLHandle( node ? node->NextSibling() : 0 ); }        
-    /// Get the next sibling element of this handle.
-    XMLHandle NextSiblingElement( const char* _value=0 )                    { return XMLHandle( node ? node->NextSiblingElement( _value ) : 0 ); }
-
-    /// Safe cast to XMLNode. This can return null.
-    XMLNode* ToNode()                            { return node; } 
-    /// Safe cast to XMLElement. This can return null.
-    XMLElement* ToElement()                     { return ( ( node && node->ToElement() ) ? node->ToElement() : 0 ); }
-    /// Safe cast to XMLText. This can return null.
-    XMLText* ToText()                             { return ( ( node && node->ToText() ) ? node->ToText() : 0 ); }
-    /// Safe cast to XMLUnknown. This can return null.
-    XMLUnknown* ToUnknown()                     { return ( ( node && node->ToUnknown() ) ? node->ToUnknown() : 0 ); }
-    /// Safe cast to XMLDeclaration. This can return null.
-    XMLDeclaration* ToDeclaration()             { return ( ( node && node->ToDeclaration() ) ? node->ToDeclaration() : 0 ); }
-
-private:
-    XMLNode* node;
-};
-
-
-/**
-    A variant of the XMLHandle class for working with const XMLNodes and Documents. It is the
-    same in all regards, except for the 'const' qualifiers. See XMLHandle for API.
-*/
-class XMLConstHandle
-{
-public:
-    XMLConstHandle( const XMLNode* _node )                                            { node = _node; }
-    XMLConstHandle( const XMLNode& _node )                                            { node = &_node; }
-    XMLConstHandle( const XMLConstHandle& ref )                                        { node = ref.node; }
-
-    XMLConstHandle& operator=( const XMLConstHandle& ref )                            { node = ref.node; return *this; }
-
-    const XMLConstHandle FirstChild() const                                            { return XMLConstHandle( node ? node->FirstChild() : 0 ); }
-    const XMLConstHandle FirstChildElement( const char* value=0 ) const                { return XMLConstHandle( node ? node->FirstChildElement( value ) : 0 ); }
-    const XMLConstHandle LastChild()    const                                        { return XMLConstHandle( node ? node->LastChild() : 0 ); }
-    const XMLConstHandle LastChildElement( const char* _value=0 ) const                { return XMLConstHandle( node ? node->LastChildElement( _value ) : 0 ); }
-    const XMLConstHandle PreviousSibling() const                                    { return XMLConstHandle( node ? node->PreviousSibling() : 0 ); }
-    const XMLConstHandle PreviousSiblingElement( const char* _value=0 ) const        { return XMLConstHandle( node ? node->PreviousSiblingElement( _value ) : 0 ); }
-    const XMLConstHandle NextSibling() const                                        { return XMLConstHandle( node ? node->NextSibling() : 0 ); }
-    const XMLConstHandle NextSiblingElement( const char* _value=0 ) const            { return XMLConstHandle( node ? node->NextSiblingElement( _value ) : 0 ); }
-
-
-    const XMLNode* ToNode() const                { return node; } 
-    const XMLElement* ToElement() const            { return ( ( node && node->ToElement() ) ? node->ToElement() : 0 ); }
-    const XMLText* ToText() const                { return ( ( node && node->ToText() ) ? node->ToText() : 0 ); }
-    const XMLUnknown* ToUnknown() const            { return ( ( node && node->ToUnknown() ) ? node->ToUnknown() : 0 ); }
-    const XMLDeclaration* ToDeclaration() const    { return ( ( node && node->ToDeclaration() ) ? node->ToDeclaration() : 0 ); }
-
-private:
-    const XMLNode* node;
-};
-
-
-/**
-    Printing functionality. The XMLPrinter gives you more
-    options than the XMLDocument::Print() method.
-
-    It can:
-    -# Print to memory.
-    -# Print to a file you provide.
-    -# Print XML without a XMLDocument.
-
-    Print to Memory
-
-    @verbatim
-    XMLPrinter printer;
-    doc->Print( &printer );
-    SomeFunction( printer.CStr() );
-    @endverbatim
-
-    Print to a File
-    
-    You provide the file pointer.
-    @verbatim
-    XMLPrinter printer( fp );
-    doc.Print( &printer );
-    @endverbatim
-
-    Print without a XMLDocument
-
-    When loading, an XML parser is very useful. However, sometimes
-    when saving, it just gets in the way. The code is often set up
-    for streaming, and constructing the DOM is just overhead.
-
-    The Printer supports the streaming case. The following code
-    prints out a trivially simple XML file without ever creating
-    an XML document.
-
-    @verbatim
-    XMLPrinter printer( fp );
-    printer.OpenElement( "foo" );
-    printer.PushAttribute( "foo", "bar" );
-    printer.CloseElement();
-    @endverbatim
-*/
-class XMLPrinter : public XMLVisitor
-{
-public:
-    /** Construct the printer. If the FILE* is specified,
-        this will print to the FILE. Else it will print
-        to memory, and the result is available in CStr().
-        If 'compact' is set to true, then output is created
-        with only required whitespace and newlines.
-    */
-    XMLPrinter( FILE* file=0, bool compact = false );
-    virtual ~XMLPrinter()    {}
-
-    /** If streaming, write the BOM and declaration. */
-    void PushHeader( bool writeBOM, bool writeDeclaration );
-    /** If streaming, start writing an element.
-        The element must be closed with CloseElement()
-    */
-    void OpenElement( const char* name );
-    /// If streaming, add an attribute to an open element.
-    void PushAttribute( const char* name, const char* value );
-    void PushAttribute( const char* name, int value );
-    void PushAttribute( const char* name, unsigned value );
-    void PushAttribute( const char* name, bool value );
-    void PushAttribute( const char* name, double value );
-    /// If streaming, close the Element.
-    void CloseElement();
-
-    /// Add a text node.
-    void PushText( const char* text, bool cdata=false );
-    /// Add a text node from an integer.
-    void PushText( int value );
-    /// Add a text node from an unsigned.
-    void PushText( unsigned value );
-    /// Add a text node from a bool.
-    void PushText( bool value );
-    /// Add a text node from a float.
-    void PushText( float value );
-    /// Add a text node from a double.
-    void PushText( double value );
-
-    /// Add a comment
-    void PushComment( const char* comment );
-
-    void PushDeclaration( const char* value );
-    void PushUnknown( const char* value );
-
-    virtual bool VisitEnter( const XMLDocument& /*doc*/ );
-    virtual bool VisitExit( const XMLDocument& /*doc*/ )            { return true; }
-
-    virtual bool VisitEnter( const XMLElement& element, const XMLAttribute* attribute );
-    virtual bool VisitExit( const XMLElement& element );
-
-    virtual bool Visit( const XMLText& text );
-    virtual bool Visit( const XMLComment& comment );
-    virtual bool Visit( const XMLDeclaration& declaration );
-    virtual bool Visit( const XMLUnknown& unknown );
-
-    /**
-        If in print to memory mode, return a pointer to
-        the XML file in memory.
-    */
-    const char* CStr() const { return buffer.Mem(); }
-    /**
-           If in print to memory mode, return the size 
-        of the XML file in memory. (Note the size returned
-        includes the terminating null.)
-      */
-      int CStrSize() const { return buffer.Size(); }
-
-private:
-    void SealElement();
-    void PrintSpace( int depth );
-    void PrintString( const char*, bool restrictedEntitySet );    // prints out, after detecting entities.
-    void Print( const char* format, ... );
-
-    bool elementJustOpened;
-    bool firstElement;
-    FILE* fp;
-    int depth;
-    int textDepth;
-    bool processEntities;
-    bool compactMode;
-
-    enum {
-        ENTITY_RANGE = 64,
-        BUF_SIZE = 200
-    };
-    bool entityFlag[ENTITY_RANGE];
-    bool restrictedEntityFlag[ENTITY_RANGE];
-
-    DynArray< const char*, 10 > stack;
-    DynArray< char, 20 > buffer;
-#ifdef _MSC_VER
-    DynArray< char, 20 > accumulator;
-#endif
-};
-
-
-}    // tinyxml2
-
-
-#endif // TINYXML2_INCLUDED
--- a/main.cpp	Sun Aug 19 15:57:55 2012 +0000
+++ b/main.cpp	Mon Aug 20 13:27:17 2012 +0000
@@ -4,9 +4,9 @@
 #include "HTTPClient.h"
 #include "HTTPFile.h"
 #include "BlinkLed.h"
-#include "tinyxml2.h"
-
-using namespace tinyxml2;
+#include "spdomparser.hpp"
+#include "spxmlnode.hpp"
+#include "spxmlhandle.hpp"
 
 int GetFile(const char *path, const char *url);
 
@@ -40,31 +40,50 @@
     eth.connect();
     
     // Obtain original lastBuildDate
+    
     char lastBuildDateOriginal[128] = {0};
     {
-        XMLDocument docOriginal;
-        if(XML_SUCCESS != docOriginal.LoadFile(rssPath))
-        {
-            strcpy(lastBuildDateOriginal, "No original english.xml in USB memory");
-        }
-        else
+    FILE * fp = fopen ( rssPath, "r" );
+    if( NULL == fp ) {
+        printf( "cannot not open %s\n", rssPath );
+        sprintf(lastBuildDateOriginal, "cannot not open original %s", rssPath );
+    }
+    else
+    {
+        fseek(fp, 0, SEEK_END); // seek to end of file
+        unsigned int size = ftell(fp);
+        fseek(fp, 0, SEEK_SET); // seek to head of file
+        
+        char * source = NULL;
+        source = ( char * ) malloc ( size + 1 );
+        fread ( source, size, sizeof ( char ), fp );
+        fclose ( fp );
+        source[ size ] = 0;
+        
+        SP_XmlDomParser parser;
+        parser.append( source, strlen( source ) );
+        free( source );
+        
+        SP_XmlHandle rootHandle( parser.getDocument()->getRootElement() );
+        
+        SP_XmlCDataNode * lastBuildDateNode = rootHandle.getChild( "channel" ).getChild( "lastBuildDate" ).getChild(0).toCData();
+        if( NULL != lastBuildDateNode )
         {
-            XMLElement* lastBuildDateOriginalElement = docOriginal.FirstChildElement("rss")->FirstChildElement("channel")->FirstChildElement("lastBuildDate");
-            if(NULL == lastBuildDateOriginalElement)
-            {
-                strcpy(lastBuildDateOriginal, "No \"lastBuildDate\" element in original RSS");
-            }
-            else
-            {
-                strcpy(lastBuildDateOriginal, lastBuildDateOriginalElement->GetText());
-            }
+            printf( "Find /rss/channel/lastBuildDate/text()\n");
+            printf("text() of lastBuildDate: %s\n", lastBuildDateNode->getText());
+            strcpy(lastBuildDateOriginal, lastBuildDateNode->getText());
+        } else {
+            printf( "Cannot found /rss/channel/lastBuildDate/text()\n" );
+            sprintf(lastBuildDateOriginal, "Cannot found /rss/channel/lastBuildDate/text()");
         }
     }
+    }
     printf("\nlastBuildDate (original): %s\n", lastBuildDateOriginal);
     
     // Download RSS
     GetFile(rssPath, rssUrl);
     
+    #if 0
     // Obtain current lastBuildDate 
     char lastBuildDateCurrent[128] = {0};
     char mp3Url[256] = {0};
@@ -129,6 +148,7 @@
     {
         GetFile(mp3Path, mp3Url);
     }
+    #endif
     
     // Wait for the completion of writing to USB Mass Storage Device.
     wait(1);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/spxml/spcanonxml.cpp	Mon Aug 20 13:27:17 2012 +0000
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2007 Stephen Liu
+ * For license terms, see the file COPYING along with this library.
+ */
+
+#include <string.h>
+
+#include "spcanonxml.hpp"
+
+#include "spxmlnode.hpp"
+#include "spxmlutils.hpp"
+#include "spxmlcodec.hpp"
+
+SP_CanonXmlBuffer :: SP_CanonXmlBuffer( const SP_XmlNode * node )
+{
+	mBuffer = new SP_XmlStringBuffer();
+	dump( node, mBuffer );
+}
+
+SP_CanonXmlBuffer :: ~SP_CanonXmlBuffer()
+{
+	if( NULL != mBuffer ) delete mBuffer;
+	mBuffer = NULL;
+}
+
+const char * SP_CanonXmlBuffer :: getBuffer() const
+{
+	return mBuffer->getBuffer();
+}
+
+int SP_CanonXmlBuffer :: getSize() const
+{
+	return mBuffer->getSize();
+}
+
+void SP_CanonXmlBuffer :: canonEncode( const char * value,
+		SP_XmlStringBuffer * buffer )
+{
+	SP_XmlStringBuffer temp;
+	SP_XmlStringCodec::encode( "", value, &temp );
+
+	for( const char * pos = temp.getBuffer(); '\0' != *pos; pos++ ) {
+		if( '\r' == *pos ) {
+		} else if( '\n' == *pos ) {
+			buffer->append( "&#10;" );
+		} else {
+			buffer->append( *pos );
+		}
+	}
+}
+
+void SP_CanonXmlBuffer :: dump(
+		const SP_XmlNode * node, SP_XmlStringBuffer * buffer )
+{
+	if( NULL == node ) return;
+
+	if( SP_XmlNode::eXMLDOC == node->getType() ) {
+		SP_XmlDocument * document = static_cast<SP_XmlDocument*>((SP_XmlNode*)node);
+		const SP_XmlNodeList * children = document->getChildren();
+		for( int j = 0; j < children->getLength(); j++ ) {
+			dump( children->get( j ), buffer );
+		}
+	} else if( SP_XmlNode::eCDATA == node->getType() ) {
+		SP_XmlCDataNode * cdata = static_cast<SP_XmlCDataNode*>((SP_XmlNode*)node);
+
+		canonEncode( cdata->getText(), buffer );
+	} else if( SP_XmlNode::ePI == node->getType() ) {
+		SP_XmlPINode * piNode = static_cast<SP_XmlPINode*>((SP_XmlNode*)node);
+
+		buffer->append( "<?" );
+		buffer->append( piNode->getTarget() );
+		if( '\0' != *( piNode->getTarget() ) ) buffer->append( ' ' );
+		buffer->append( piNode->getData() );
+		buffer->append( "?>" );
+	} else if( SP_XmlNode::eCOMMENT == node->getType() ) {
+		// ignore
+	} else if( SP_XmlNode::eELEMENT == node->getType() ) {
+		dumpElement( node, buffer );
+	} else if( SP_XmlNode::eDOCDECL == node->getType() ) {
+		// ignore
+	} else if( SP_XmlNode::eDOCTYPE == node->getType() ) {
+		// ignore
+	} else {
+		// ignore
+	}
+}
+
+void SP_CanonXmlBuffer :: dumpElement(
+		const SP_XmlNode * node, SP_XmlStringBuffer * buffer )
+{
+	if( NULL == node ) return;
+
+	if( SP_XmlNode::eELEMENT == node->getType() ) {
+		SP_XmlElementNode * element = static_cast<SP_XmlElementNode*>((SP_XmlNode*)node);
+		buffer->append( "<" );
+		buffer->append( element->getName() );
+
+		int i = 0;
+
+		SP_XmlArrayList attrList;
+		for( i = 0; i < element->getAttrCount(); i++ ) {
+			attrList.append( (void*)element->getAttr( i, NULL ) );
+		}
+		attrList.sort( reinterpret_cast<int(*)(const void*, const void*)>(strcmp) );
+
+		const char * name = NULL, * value = NULL;
+		for( i = 0; i < attrList.getCount(); i++ ) {
+			name = (char*)attrList.getItem( i );
+			value = element->getAttrValue( name );
+			if( NULL != name && NULL != value ) {
+				buffer->append( ' ' );
+				buffer->append( name );
+				buffer->append( "=\"" );
+				canonEncode( value, buffer );
+				buffer->append( "\"" );
+			}
+		}
+
+		const SP_XmlNodeList * children = element->getChildren();
+
+		buffer->append( ">" );
+
+		for( int j = 0; j < children->getLength(); j++ ) {
+			dump( children->get( j ), buffer );
+		}
+
+		buffer->append( "</" );
+		buffer->append( element->getName() );
+		buffer->append( ">" );
+	} else {
+		dump( node, buffer );
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/spxml/spcanonxml.hpp	Mon Aug 20 13:27:17 2012 +0000
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2007 Stephen Liu
+ * For license terms, see the file COPYING along with this library.
+ */
+
+#ifndef __spcanonxml_hpp__
+#define __spcanonxml_hpp__
+
+class SP_XmlNode;
+class SP_XmlStringBuffer;
+class SP_XmlDocDeclNode;
+class SP_XmlDocTypeNode;
+
+/// XML Canonical, defined by James Clark.
+class SP_CanonXmlBuffer {
+public:
+	SP_CanonXmlBuffer( const SP_XmlNode * node );
+	~SP_CanonXmlBuffer();
+
+	const char * getBuffer() const;
+	int getSize() const;
+
+private:
+	SP_CanonXmlBuffer( SP_CanonXmlBuffer & );
+	SP_CanonXmlBuffer & operator=( SP_CanonXmlBuffer & );
+
+	static void dump( const SP_XmlNode * node,
+			SP_XmlStringBuffer * buffer );
+	static void dumpElement( const SP_XmlNode * node,
+			SP_XmlStringBuffer * buffer );
+
+	static void canonEncode( const char * value,
+			SP_XmlStringBuffer * buffer );
+
+	SP_XmlStringBuffer * mBuffer;
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/spxml/spdomiterator.cpp	Mon Aug 20 13:27:17 2012 +0000
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2007 Stephen Liu
+ * For license terms, see the file COPYING along with this library.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include "spdomiterator.hpp"
+#include "spxmlnode.hpp"
+
+SP_DomIterator :: SP_DomIterator( const SP_XmlNode * node )
+{
+	mRoot = node;
+	mCurrent = node;
+}
+
+SP_DomIterator :: ~SP_DomIterator()
+{
+}
+
+const SP_XmlNode * SP_DomIterator :: getNext()
+{
+	if( NULL == mCurrent ) return NULL;
+
+	const SP_XmlNode * retNode = mCurrent;
+
+	// find first left child
+	if( SP_XmlNode::eXMLDOC == mCurrent->getType() ) {
+		SP_XmlDocument * document = static_cast<SP_XmlDocument*>((SP_XmlNode*)mCurrent);
+
+		mCurrent = NULL;
+		retNode = document->getDocDecl();
+		if( NULL == retNode ) retNode = document->getDocType();
+		if( NULL == retNode ) {
+			retNode = document->getRootElement();
+			if( NULL != retNode ) mCurrent = document->getRootElement()->getChildren()->get( 0 );
+		}
+	} else if( SP_XmlNode::eELEMENT == mCurrent->getType() ) {
+		SP_XmlElementNode * element = static_cast<SP_XmlElementNode*>((SP_XmlNode*)mCurrent);
+		const SP_XmlNodeList * children = element->getChildren();
+		mCurrent = children->get( 0 );
+	} else {
+		mCurrent = NULL;
+	}
+
+	// find next sibling
+	if( NULL == mCurrent ) {
+		mCurrent = retNode;
+
+		const SP_XmlNode * parent = NULL;
+		if( NULL != mCurrent ) parent = mCurrent->getParent();
+
+		for( ; NULL != parent; ) {
+			if( SP_XmlNode::eXMLDOC == parent->getType() ) {
+				SP_XmlDocument * document = static_cast<SP_XmlDocument*>((SP_XmlNode*)parent);
+				if( mCurrent == document->getDocDecl() ) {
+					mCurrent = document->getDocType();
+					if( NULL == mCurrent ) mCurrent = document->getRootElement();
+				} else if( mCurrent == document->getDocType() ) {
+					mCurrent = document->getRootElement();
+				} else {
+					mCurrent = NULL;
+				}
+			} else if( SP_XmlNode::eELEMENT == parent->getType() ) {
+				SP_XmlElementNode * element = static_cast<SP_XmlElementNode*>((SP_XmlNode*)parent);
+				const SP_XmlNodeList * children = element->getChildren();
+
+				int index = -1;
+				for( int i = 0; i < children->getLength(); i++ ) {
+					if( mCurrent == children->get( i ) ) {
+						index = i;
+						break;
+					}
+				}
+
+				if( index >= 0 && index < ( children->getLength() - 1 ) ) {
+					mCurrent = children->get( index + 1 );
+				} else {
+					mCurrent = NULL;
+				}
+			} else {
+				mCurrent = NULL;
+				assert( 0 ); // should not occur
+			}
+
+			if( NULL == mCurrent ) {
+				mCurrent = parent;
+				parent = mCurrent->getParent();
+				if( NULL == parent ) mCurrent = NULL;
+				if( mRoot == mCurrent ) mCurrent = NULL;
+			} else {
+				break;
+			}
+		}
+	}
+
+	return retNode;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/spxml/spdomiterator.hpp	Mon Aug 20 13:27:17 2012 +0000
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2007 Stephen Liu
+ * For license terms, see the file COPYING along with this library.
+ */
+
+#ifndef __spdomiterator_hpp__
+#define __spdomiterator_hpp__
+
+class SP_XmlNode;
+
+/// DFS iterator -- Depth First Search
+class SP_DomIterator {
+public:
+	/// node as tree node, iterator this tree by DFS
+	SP_DomIterator( const SP_XmlNode * node );
+	~SP_DomIterator();
+
+	/// @return NULL : reach the end
+	const SP_XmlNode * getNext();
+
+private:
+
+	SP_DomIterator( SP_DomIterator & );
+	SP_DomIterator & operator=( SP_DomIterator & );
+
+	const SP_XmlNode * mRoot;
+	const SP_XmlNode * mCurrent;
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/spxml/spdomparser.cpp	Mon Aug 20 13:27:17 2012 +0000
@@ -0,0 +1,359 @@
+/*
+ * Copyright 2007 Stephen Liu
+ * For license terms, see the file COPYING along with this library.
+ */
+
+#include <assert.h>
+
+#include "spdomparser.hpp"
+#include "spxmlparser.hpp"
+#include "spxmlevent.hpp"
+#include "spxmlutils.hpp"
+#include "spxmlnode.hpp"
+#include "spxmlcodec.hpp"
+
+//=========================================================
+
+SP_XmlDomParser :: SP_XmlDomParser()
+{
+	mParser = new SP_XmlPullParser();
+	mDocument = new SP_XmlDocument();
+	mCurrent = NULL;
+}
+
+SP_XmlDomParser :: ~SP_XmlDomParser()
+{
+	if( NULL != mDocument ) delete mDocument;
+	mDocument = NULL;
+
+	if( NULL != mParser ) delete mParser;
+	mParser = NULL;
+}
+
+void SP_XmlDomParser :: setIgnoreWhitespace( int ignoreWhitespace )
+{
+	mParser->setIgnoreWhitespace( ignoreWhitespace );
+}
+
+int SP_XmlDomParser :: getIgnoreWhitespace()
+{
+	return mParser->getIgnoreWhitespace();
+}
+
+const char * SP_XmlDomParser :: getEncoding()
+{
+	return mParser->getEncoding();
+}
+
+int SP_XmlDomParser :: append( const char * source, int len )
+{
+	int ret = 0;
+
+	for( int pos = 0; pos < len; pos += 64 ) {
+		int realLen = ( len - pos ) > 64 ? 64 : ( len - pos );
+		ret += mParser->append( source + pos, realLen );
+		buildTree();
+	}
+
+	return ret;
+}
+
+void SP_XmlDomParser :: buildTree()
+{
+	for( SP_XmlPullEvent * event = mParser->getNext();
+			NULL != event; event = mParser->getNext() ) {
+
+		switch( event->getEventType() ) {
+			case SP_XmlPullEvent::eStartDocument:
+				// ignore
+				delete event;
+				break;
+			case SP_XmlPullEvent::eEndDocument:
+				// ignore
+				delete event;
+				break;
+			case SP_XmlPullEvent::eDocDecl:
+				{
+					mDocument->setDocDecl(
+							new SP_XmlDocDeclNode( (SP_XmlDocDeclEvent*)event ) );
+					break;
+				}
+			case SP_XmlPullEvent::eDocType:
+				{
+					mDocument->setDocType(
+							new SP_XmlDocTypeNode( (SP_XmlDocTypeEvent*)event ) );
+					break;
+				}
+			case SP_XmlPullEvent::eStartTag:
+				{
+					SP_XmlElementNode * element =
+							new SP_XmlElementNode( (SP_XmlStartTagEvent*)event );
+					if( NULL == mCurrent ) {
+						mCurrent = element;
+						mDocument->setRootElement( element );
+					} else {
+						mCurrent->addChild( element );
+						mCurrent = element;
+					}
+					break;
+				}
+			case SP_XmlPullEvent::eEndTag:
+				{
+					SP_XmlNode * parent = (SP_XmlNode*)mCurrent->getParent();
+					if( NULL != parent && SP_XmlNode::eELEMENT == parent->getType() ) {
+						mCurrent = static_cast<SP_XmlElementNode*>((SP_XmlNode*)parent);
+					} else {
+						mCurrent = NULL;
+					}
+
+					delete event;
+					break;
+				}
+			case SP_XmlPullEvent::eCData:
+				{
+					if( NULL != mCurrent ) {
+						mCurrent->addChild( new SP_XmlCDataNode( (SP_XmlCDataEvent*)event ) );
+					} else {
+						delete event;
+					}
+					break;
+				}
+			case SP_XmlPullEvent::eComment:
+				{
+					if( NULL != mCurrent ) {
+						mCurrent->addChild( new SP_XmlCommentNode( (SP_XmlCommentEvent*)event ) );
+					} else {
+						delete event;
+					}
+					break;
+				}
+			case SP_XmlPIEvent::ePI:
+				{
+					if( NULL != mCurrent ) {
+						mCurrent->addChild( new SP_XmlPINode( (SP_XmlPIEvent*)event ) );
+					} else {
+						mDocument->getChildren()->append(
+								new SP_XmlPINode( (SP_XmlPIEvent*)event ) );
+					}
+					break;
+				}
+			default:
+				{
+					assert( 0 );
+					break;
+				}
+		}
+	}
+}
+
+const char * SP_XmlDomParser :: getError()
+{
+	return mParser->getError();
+}
+
+const SP_XmlDocument * SP_XmlDomParser :: getDocument() const
+{
+	return mDocument;
+}
+
+//=========================================================
+
+SP_XmlDomBuffer :: SP_XmlDomBuffer( const SP_XmlNode * node, int indent )
+{
+	mBuffer = new SP_XmlStringBuffer();
+	dump( SP_XmlStringCodec::DEFAULT_ENCODING, node, mBuffer, indent ? 0 : -1 );
+}
+
+SP_XmlDomBuffer :: SP_XmlDomBuffer( const char * encoding, const SP_XmlNode * node, int indent )
+{
+	mBuffer = new SP_XmlStringBuffer();
+	dump( encoding, node, mBuffer, indent ? 0 : -1 );
+}
+
+SP_XmlDomBuffer :: ~SP_XmlDomBuffer()
+{
+	if( NULL != mBuffer ) delete mBuffer;
+	mBuffer = NULL;
+}
+
+const char * SP_XmlDomBuffer :: getBuffer() const
+{
+	return mBuffer->getBuffer();
+}
+
+int SP_XmlDomBuffer :: getSize() const
+{
+	return mBuffer->getSize();
+}
+
+void SP_XmlDomBuffer :: dump( const char * encoding,
+		const SP_XmlNode * node, SP_XmlStringBuffer * buffer, int level )
+{
+	if( SP_XmlNode::eXMLDOC == node->getType() ) {
+		SP_XmlDocument * document = static_cast<SP_XmlDocument*>((SP_XmlNode*)node);
+		dumpDocDecl( encoding, document->getDocDecl(), buffer, level );
+		dumpDocType( encoding, document->getDocType(), buffer, level );
+
+		const SP_XmlNodeList * children = document->getChildren();
+		for( int j = 0; j < children->getLength(); j++ ) {
+			dump( encoding, children->get( j ), buffer, level );
+		}
+	} else if( SP_XmlNode::eCDATA == node->getType() ) {
+		SP_XmlCDataNode * cdata = static_cast<SP_XmlCDataNode*>((SP_XmlNode*)node);
+		SP_XmlStringCodec::encode( encoding, cdata->getText(), buffer );
+	} else if( SP_XmlNode::eCOMMENT == node->getType() ) {
+		SP_XmlCommentNode * comment = static_cast<SP_XmlCommentNode*>((SP_XmlNode*)node);
+
+		if( level >= 0 ) {
+			buffer->append( '\n' );
+			for( int i = 0; i < level; i++ ) buffer->append( '\t' );
+			buffer->append( "<!--" );
+			buffer->append( comment->getText() );
+			buffer->append( "-->\n" );
+		} else {
+			buffer->append( "<!--" );
+			buffer->append( comment->getText() );
+			buffer->append( "-->" );
+		}
+	} else if( SP_XmlNode::eELEMENT == node->getType() ) {
+		dumpElement( encoding, node, buffer, level );
+	} else if( SP_XmlNode::eDOCDECL == node->getType() ) {
+		dumpDocDecl( encoding, (SP_XmlDocDeclNode*)node, buffer, level );
+	} else if( SP_XmlNode::eDOCTYPE == node->getType() ) {
+		dumpDocType( encoding, (SP_XmlDocTypeNode*)node, buffer, level );
+	} else if( SP_XmlNode::ePI == node->getType() ) {
+		SP_XmlPINode * piNode = static_cast<SP_XmlPINode*>((SP_XmlNode*)node);
+
+		if( level >= 0 ) {
+			for( int i = 0; i < level; i++ ) buffer->append( '\t' );
+			buffer->append( "<?" );
+			buffer->append( piNode->getTarget() );
+			buffer->append( ' ' );
+			buffer->append( piNode->getData() );
+			buffer->append( "?>\n" );
+		} else {
+			buffer->append( "<?" );
+			buffer->append( piNode->getTarget() );
+			if( '\0' != *( piNode->getTarget() ) ) buffer->append( ' ' );
+			buffer->append( piNode->getData() );
+			buffer->append( "?>" );
+		}
+	} else {
+		// ignore
+	}
+}
+
+void SP_XmlDomBuffer :: dumpDocDecl( const char * encoding,
+		const SP_XmlDocDeclNode * docDecl,
+		SP_XmlStringBuffer * buffer, int level )
+{
+	if( NULL == docDecl ) return;
+
+	buffer->append( "<?xml version=\"" );
+	if( '\0' != * ( docDecl->getVersion() ) ) {
+		buffer->append( docDecl->getVersion() );
+	} else {
+		buffer->append( "1.0" );
+	}
+	buffer->append( "\" " );
+
+	if( '\0' != * ( docDecl->getEncoding() ) ) {
+		buffer->append( "encoding=\"" );
+		buffer->append( docDecl->getEncoding() );
+		buffer->append( "\" " );
+	}
+
+	if( -1 != docDecl->getStandalone() ) {
+		char standalone[ 32 ];
+		snprintf( standalone, sizeof( standalone ), "standalone=\"%s\" ",
+				0 == docDecl->getStandalone() ? "no" : "yes" );
+		buffer->append( standalone );
+	}
+
+	buffer->append( level >= 0 ? "?>\n" : "?>" );
+}
+
+void SP_XmlDomBuffer :: dumpDocType( const char * encoding,
+		const SP_XmlDocTypeNode * docType,
+		SP_XmlStringBuffer * buffer, int level )
+{
+	if( NULL == docType ) return;
+
+	buffer->append( "<!DOCTYPE " );
+	buffer->append( docType->getName() );
+
+	if( '\0' != * ( docType->getPublicID() ) ) {
+		buffer->append( " PUBLIC " );
+		buffer->append( '"' );
+		buffer->append( docType->getPublicID() );
+		buffer->append( '"' );
+	}
+
+	if( '\0' != * ( docType->getSystemID() ) ) {
+		buffer->append( " SYSTEM " );
+		buffer->append( '"' );
+		buffer->append( docType->getSystemID() );
+		buffer->append( '"' );
+	}
+
+	if( '\0' != * ( docType->getDTD() ) ) {
+		buffer->append( " \"" );
+		buffer->append( docType->getDTD() );
+		buffer->append( '"' );
+	}
+
+	buffer->append( level >= 0 ? ">\n" : ">" );
+}
+
+void SP_XmlDomBuffer :: dumpElement( const char * encoding,
+		const SP_XmlNode * node, SP_XmlStringBuffer * buffer, int level )
+{
+	if( NULL == node ) return;
+
+	if( SP_XmlNode::eELEMENT == node->getType() ) {
+		int i = 0;
+
+		for( i = 0; i < level; i++ ) buffer->append( '\t' );
+
+		SP_XmlElementNode * element = static_cast<SP_XmlElementNode*>((SP_XmlNode*)node);
+		buffer->append( "<" );
+		buffer->append( element->getName() );
+
+		const char * name = NULL, * value = NULL;
+		for( i = 0; i < element->getAttrCount(); i++ ) {
+			name = element->getAttr( i, &value );
+			if( NULL != name && NULL != value ) {
+				buffer->append( ' ' );
+				buffer->append( name );
+				buffer->append( "=\"" );
+				SP_XmlStringCodec::encode( encoding, value, buffer );
+				buffer->append( "\"" );
+			}
+		}
+
+		const SP_XmlNodeList * children = element->getChildren();
+
+		if( children->getLength() > 0 ) {
+			if( SP_XmlNode::eCDATA != children->get( 0 )->getType() ) {
+				buffer->append( level >= 0 ? ">\n" : ">" );
+			} else {
+				buffer->append( ">" );
+			}
+
+			for( int j = 0; j < children->getLength(); j++ ) {
+				dump( encoding, children->get( j ), buffer, level >= 0 ? level + 1 : -1 );
+			}
+
+			if( SP_XmlNode::eCDATA != children->get( 0 )->getType() ) {
+				for( int i = 0; i < level; i++ ) buffer->append( '\t' );
+			}
+			buffer->append( "</" );
+			buffer->append( element->getName() );
+			buffer->append( level >= 0 ? ">\n" : ">" );
+		} else {
+			buffer->append( level >= 0 ? "/>\n" : ">" );
+		}
+	} else {
+		dump( encoding, node, buffer, level );
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/spxml/spdomparser.hpp	Mon Aug 20 13:27:17 2012 +0000
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2007 Stephen Liu
+ * For license terms, see the file COPYING along with this library.
+ */
+
+#ifndef __spdomparser_hpp__
+#define __spdomparser_hpp__
+
+class SP_XmlNode;
+class SP_XmlDocument;
+class SP_XmlElementNode;
+class SP_XmlDocDeclNode;
+class SP_XmlDocTypeNode;
+class SP_XmlPullParser;
+class SP_XmlStringBuffer;
+
+/// parse string to xml node tree
+class SP_XmlDomParser {
+public:
+	SP_XmlDomParser();
+	~SP_XmlDomParser();
+
+	/// append more input xml source
+	/// @return how much byte has been consumed
+	int append( const char * source, int len );
+
+	/// @return NOT NULL : the detail error message
+	/// @return NULL : no error
+	const char * getError();
+	
+	/// get the parse result
+	const SP_XmlDocument * getDocument() const;
+
+	void setIgnoreWhitespace( int ignoreWhitespace );
+
+	int getIgnoreWhitespace();
+
+	const char * getEncoding();
+
+private:
+	void buildTree();
+
+	SP_XmlDomParser( SP_XmlDomParser & );
+	SP_XmlDomParser & operator=( SP_XmlDomParser & );
+
+	SP_XmlPullParser * mParser;
+	SP_XmlDocument * mDocument;
+	SP_XmlElementNode * mCurrent;
+};
+
+/// serialize xml node tree to string
+class SP_XmlDomBuffer {
+public:
+	SP_XmlDomBuffer( const SP_XmlNode * node, int indent = 1 );
+	SP_XmlDomBuffer( const char * encoding, const SP_XmlNode * node, int indent = 1 );
+	~SP_XmlDomBuffer();
+
+	const char * getBuffer() const;
+	int getSize() const;
+
+private:
+	SP_XmlDomBuffer( SP_XmlDomBuffer & );
+	SP_XmlDomBuffer & operator=( SP_XmlDomBuffer & );
+
+	static void dumpDocDecl( const char * encoding,
+			const SP_XmlDocDeclNode * docDecl,
+			SP_XmlStringBuffer * buffer, int level );
+	static void dumpDocType( const char * encoding,
+			const SP_XmlDocTypeNode * docType,
+			SP_XmlStringBuffer * buffer, int level );
+	static void dump( const char * encoding,
+			const SP_XmlNode * node,
+			SP_XmlStringBuffer * buffer, int level );
+	static void dumpElement( const char * encoding,
+			const SP_XmlNode * node,
+			SP_XmlStringBuffer * buffer, int level );
+
+	SP_XmlStringBuffer * mBuffer;
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/spxml/spxmlcodec.cpp	Mon Aug 20 13:27:17 2012 +0000
@@ -0,0 +1,175 @@
+/*
+ * Copyright 2007 Stephen Liu
+ * For license terms, see the file COPYING along with this library.
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#include "spxmlcodec.hpp"
+#include "spxmlutils.hpp"
+
+const char * SP_XmlStringCodec :: DEFAULT_ENCODING = "utf-8";
+
+const char SP_XmlStringCodec :: XML_CHARS [] =
+		{ '<', '>', '&', '\'', '"' };
+const char * SP_XmlStringCodec :: ESC_CHARS [] =
+		{ "&lt;", "&gt;", "&amp;", "&apos;", "&quot;" };
+
+int SP_XmlStringCodec :: decode( const char * encoding, const char * encodeValue,
+		SP_XmlStringBuffer * outBuffer )
+{
+	int isUtf8 = ( 0 == strcasecmp( encoding, "utf-8" ) );
+
+	const char * pos = encodeValue;
+	for( ; '\0' != *pos; ) {
+		if( '&' == *pos ) {
+			int index = -1;
+			int len = 0;
+			for( int i = 0; i < (int)( sizeof( ESC_CHARS ) / sizeof( ESC_CHARS[0] ) ); i++ ) {
+				len = strlen( ESC_CHARS[ i ] );
+				if( 0 == strncmp( pos, ESC_CHARS[i], len ) ) {
+					index = i;
+					break;
+				}
+			}
+			if( index >= 0 ) {
+				outBuffer->append( XML_CHARS[ index ] );
+				pos += len;
+			} else {
+				char * next = "";
+				int ch = 0;
+				if( '#' == *( pos + 1 ) ) {
+					if( 'x' == *( pos + 2 ) ) {
+						ch = strtol( pos + 3, &next, 16 );
+					} else {
+						ch = strtol( pos + 2, &next, 10 );
+					}
+				}
+
+				// TODO: fully support xml entity, currently only support unicode entity
+				if( ';' == *next && 0 != ch ) {
+					if( isUtf8 ) {
+						SP_XmlUtf8Codec::uni2utf8( ch, outBuffer );
+					} else {
+						outBuffer->append( ch );
+					}
+					pos = next + 1;
+				} else {
+					outBuffer->append( *pos++ );
+				}
+			}
+		} else {
+			outBuffer->append( *pos++ );
+		}
+	}
+
+	return 0;
+}
+
+int SP_XmlStringCodec :: encode( const char * encoding, const char * decodeValue,
+		SP_XmlStringBuffer * outBuffer )
+{
+	int isUtf8 = ( 0 == strcasecmp( encoding, "utf-8" ) );
+
+	const unsigned char * pos = (unsigned char *)decodeValue;
+	for( ; '\0' != *pos; pos++ ) {
+		int index = -1;
+		for( int i = 0; i < (int)( sizeof( XML_CHARS ) / sizeof( XML_CHARS[0] ) ); i++ ) {
+			if( XML_CHARS[i] == *pos ) {
+				index = i;
+				break;
+			}
+		}
+		if( index >= 0 && '\'' != *pos ) {
+			outBuffer->append( ESC_CHARS[ index ] );
+		} else {
+			if( isUtf8 ) {
+				int ch = 0;
+				int len = SP_XmlUtf8Codec::utf82uni( (unsigned char*)pos, &ch );
+
+				if( len > 0 ) {
+					pos += len - 1;
+
+					char temp[ 32 ] = { 0 };
+					snprintf( temp, sizeof( temp ), "&#%d;", ch );
+					outBuffer->append( temp );
+				} else {
+					outBuffer->append( *pos );
+				}
+			} else {
+				if( *pos < 32 ) {
+					char temp[ 32 ] = { 0 };
+					snprintf( temp, sizeof( temp ), "&#%d;", *pos );
+					outBuffer->append( temp );
+				} else {
+					outBuffer->append( *pos );
+				}
+			}
+		}	
+	}
+
+	return 0;
+}
+
+int SP_XmlStringCodec :: isNameChar( const char * encoding, char c )
+{
+	if( 0 == strcasecmp( encoding, "utf-8" ) ) {
+		return 1;
+	} else {
+		return isalnum(c) || c == ':' || c == '-' || c == '.' || c == '_';
+	}
+}
+
+//=========================================================
+
+int SP_XmlUtf8Codec :: utf82uni( const unsigned char * utf8, int * ch )
+{
+	int len = 0;
+
+	unsigned char c1 = 0, c2 = 0, c3 = 0, c4 = 0;
+
+	if( *utf8 >= 0x80 ) {
+		c1 = *utf8++;
+
+		if( c1 < 0xE0 ) {         // 2 bytes
+			if( '\0' != ( c2 = *utf8 ) ) {
+				*ch = ((c1 & 0x1F) << 6) | (c2 & 0x3F);
+				len = 2;
+			}
+		} else if( c1 < 0xF0 ) {  // 3 bytes
+			if( '\0' != ( c2 = *utf8++ ) && '\0' != ( c3 = *utf8 ) ) {
+				*ch = ((c1 & 0x0F) << 12) | ((c2 & 0x3F) << 6)| (c3 & 0x3F);
+				len = 3;
+			}
+		} else {                     // 4 bytes
+			if( '\0' != ( c2 = *utf8++ ) && '\0' != ( c3 = *utf8++ )
+					&& '\0' != ( c4 = *utf8 ) ) {
+				*ch = ((c1 & 0x07) << 16) | ((c2 & 0x3F) << 12)
+						| ((c3 & 0x3F) << 6) | (c4 & 0x3F);
+				len = 4;
+			}
+		}
+	}
+
+	return len;
+}
+
+void SP_XmlUtf8Codec :: uni2utf8( int ch, SP_XmlStringBuffer * outBuffer )
+{
+	if( ch < 0x80 ) outBuffer->append( ch );
+	else if( ch < 0x800 ) {
+		outBuffer->append( 0xC0 | ( ch >> 6 ) );
+		outBuffer->append( 0x80 | ( ch & 0x3F ) );
+	} else if( ch < 0x10000 ) {
+		outBuffer->append( 0xE0 | ( ch >> 12 ) );
+		outBuffer->append( 0x80 | ( ( ch >> 6 ) & 0x3F ) );
+		outBuffer->append( 0x80 | ( ch & 0x3F ) );
+	} else if( ch < 0x200000 ) {
+		outBuffer->append( 0xF0 | ( ch >> 18 ) );
+		outBuffer->append( 0x80 | ( ( ch >> 12 ) & 0x3F ) );
+		outBuffer->append( 0x80 | ( ( ch >> 6 ) & 0x3F ) );
+		outBuffer->append( 0x80 | ( ch & 0x3F ) );
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/spxml/spxmlcodec.hpp	Mon Aug 20 13:27:17 2012 +0000
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2007 Stephen Liu
+ * For license terms, see the file COPYING along with this library.
+ */
+
+#ifndef __spxmlcodec_hpp__
+#define __spxmlcodec_hpp__
+
+class SP_XmlStringBuffer;
+
+class SP_XmlStringCodec {
+public:
+
+	static const char * DEFAULT_ENCODING;
+
+	static int decode( const char * encoding,
+			const char * encodeValue, SP_XmlStringBuffer * outBuffer );
+	static int encode( const char * encoding,
+			const char * decodeValue, SP_XmlStringBuffer * outBuffer );
+	static int isNameChar( const char * encoding, char c );
+
+private:
+	static const char XML_CHARS [];
+	static const char * ESC_CHARS [];
+
+	SP_XmlStringCodec();
+};
+
+class SP_XmlUtf8Codec {
+public:
+
+	// @return convert how many bytes
+	static int utf82uni( const unsigned char * utf8, int * ch );
+
+	static void uni2utf8( int ch, SP_XmlStringBuffer * outBuffer );
+
+private:
+	SP_XmlUtf8Codec();
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/spxml/spxmlevent.cpp	Mon Aug 20 13:27:17 2012 +0000
@@ -0,0 +1,369 @@
+/*
+ * Copyright 2007 Stephen Liu
+ * For license terms, see the file COPYING along with this library.
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include "strdup.h"
+
+#include "spxmlevent.hpp"
+#include "spxmlutils.hpp"
+
+SP_XmlPullEvent :: SP_XmlPullEvent( int eventType )
+    : mEventType( eventType )
+{
+}
+
+SP_XmlPullEvent :: ~SP_XmlPullEvent()
+{
+}
+
+int SP_XmlPullEvent :: getEventType()
+{
+    return mEventType;
+}
+
+//=========================================================
+
+SP_XmlPullEventQueue :: SP_XmlPullEventQueue()
+{
+    mQueue = new SP_XmlQueue();
+}
+
+SP_XmlPullEventQueue :: ~SP_XmlPullEventQueue()
+{
+    for( void * item = mQueue->pop(); NULL != item; item = mQueue->pop() ) {
+        delete (SP_XmlPullEvent*)item;
+    }
+
+    delete mQueue;
+}
+
+void SP_XmlPullEventQueue :: enqueue( SP_XmlPullEvent * event )
+{
+    mQueue->push( event );
+}
+
+SP_XmlPullEvent * SP_XmlPullEventQueue :: dequeue()
+{
+    SP_XmlPullEvent * event = (SP_XmlPullEvent*)mQueue->pop();
+    return event;
+}
+
+//=========================================================
+
+SP_XmlStartDocEvent :: SP_XmlStartDocEvent()
+    : SP_XmlPullEvent( eStartDocument )
+{
+}
+
+SP_XmlStartDocEvent :: ~SP_XmlStartDocEvent()
+{
+}
+
+//=========================================================
+
+SP_XmlEndDocEvent :: SP_XmlEndDocEvent()
+    : SP_XmlPullEvent( eEndDocument )
+{
+}
+
+SP_XmlEndDocEvent :: ~SP_XmlEndDocEvent()
+{
+}
+
+//=========================================================
+
+SP_XmlDocTypeEvent :: SP_XmlDocTypeEvent()
+    : SP_XmlPullEvent( eDocType )
+{
+    memset( mName, 0, sizeof( mName ) );
+    memset( mSystemID, 0, sizeof( mSystemID ) );
+    memset( mPublicID, 0, sizeof( mPublicID ) );
+    memset( mDTD, 0, sizeof( mDTD ) );
+}
+
+SP_XmlDocTypeEvent :: ~SP_XmlDocTypeEvent()
+{
+}
+
+void SP_XmlDocTypeEvent :: setName( const char * name )
+{
+    strncpy( mName, name, sizeof( mName ) - 1 );
+}
+
+const char * SP_XmlDocTypeEvent :: getName() const
+{
+    return mName;
+}
+
+void SP_XmlDocTypeEvent :: setSystemID( const char * systemID )
+{
+    strncpy( mSystemID, systemID, sizeof( mSystemID ) - 1 );
+}
+
+const char * SP_XmlDocTypeEvent :: getSystemID() const
+{
+    return mSystemID;
+}
+
+void SP_XmlDocTypeEvent :: setPublicID( const char * publicID )
+{
+    strncpy( mPublicID, publicID, sizeof( mPublicID ) - 1 );
+}
+
+const char * SP_XmlDocTypeEvent :: getPublicID() const
+{
+    return mPublicID;
+}
+
+void SP_XmlDocTypeEvent :: setDTD( const char * dtd )
+{
+    strncpy( mDTD, dtd, sizeof( mDTD ) - 1 );
+}
+
+const char * SP_XmlDocTypeEvent :: getDTD() const
+{
+    return mDTD;
+}
+
+//=========================================================
+
+SP_XmlPIEvent :: SP_XmlPIEvent()
+    : SP_XmlPullEvent( ePI )
+{
+    memset( mTarget, 0, sizeof( mTarget ) );
+    mData = NULL;
+}
+
+SP_XmlPIEvent :: ~SP_XmlPIEvent()
+{
+    if( NULL != mData ) free( mData );
+    mData = NULL;
+}
+
+void SP_XmlPIEvent :: setTarget( const char * target )
+{
+    snprintf( mTarget, sizeof( mTarget ), "%s", target );
+}
+
+const char * SP_XmlPIEvent :: getTarget()
+{
+    return mTarget;
+}
+
+void SP_XmlPIEvent :: setData( const char * data, int len )
+{
+    if( NULL != data ) {
+        if( NULL != mData ) free( mData );
+        mData = (char*)malloc( len + 1 );
+        memcpy( mData, data, len );
+        mData[ len ] = '\0';
+    }
+}
+
+const char * SP_XmlPIEvent :: getData()
+{
+    return mData;
+}
+
+//=========================================================
+
+SP_XmlDocDeclEvent :: SP_XmlDocDeclEvent()
+    : SP_XmlPullEvent( eDocDecl )
+{
+    memset( mVersion, 0, sizeof( mVersion ) );
+    memset( mEncoding, 0, sizeof( mEncoding ) );
+    mStandalone = -1;
+}
+
+SP_XmlDocDeclEvent :: ~SP_XmlDocDeclEvent()
+{
+}
+
+void SP_XmlDocDeclEvent :: setVersion( const char * version )
+{
+    strncpy( mVersion, version, sizeof( mVersion ) -1 );
+}
+
+const char * SP_XmlDocDeclEvent :: getVersion() const
+{
+    return mVersion;
+}
+
+void SP_XmlDocDeclEvent :: setEncoding( const char * encoding )
+{
+    strncpy( mEncoding, encoding, sizeof( mEncoding ) -1 );
+}
+
+const char * SP_XmlDocDeclEvent :: getEncoding() const
+{
+    return mEncoding;
+}
+
+void SP_XmlDocDeclEvent :: setStandalone( int standalone )
+{
+    mStandalone = standalone;
+}
+
+int SP_XmlDocDeclEvent :: getStandalone() const
+{
+    return mStandalone;
+}
+
+//=========================================================
+
+SP_XmlStartTagEvent :: SP_XmlStartTagEvent()
+    : SP_XmlPullEvent( eStartTag )
+{
+    mName = NULL;
+    mAttrNameList = new SP_XmlArrayList();
+    mAttrValueList = new SP_XmlArrayList();
+}
+
+SP_XmlStartTagEvent :: ~SP_XmlStartTagEvent()
+{
+    if( NULL != mName ) free( mName );
+    mName = NULL;
+
+    int i = 0;
+
+    for( i = 0; i < mAttrNameList->getCount(); i++ ) {
+        free( (char*)mAttrNameList->getItem( i ) );
+    }
+
+    delete mAttrNameList;
+    mAttrNameList = NULL;
+
+    for( i = 0; i < mAttrValueList->getCount(); i++ ) {
+        free( (char*)mAttrValueList->getItem( i ) );
+    }
+    delete mAttrValueList;
+    mAttrValueList = NULL;
+}
+
+void SP_XmlStartTagEvent :: setName( const char * name )
+{
+    if( NULL != name ) {
+        if( NULL != mName ) free( mName );
+        mName = strdup( name );
+    }
+}
+
+const char * SP_XmlStartTagEvent :: getName() const
+{
+    return mName;
+}
+
+void SP_XmlStartTagEvent :: addAttr( const char * name, const char * value )
+{
+    if( NULL != name ) mAttrNameList->append( strdup( name ) );
+    if( NULL != value ) mAttrValueList->append( strdup( value ) );
+}
+
+const char * SP_XmlStartTagEvent :: getAttrValue( const char * name ) const
+{
+    const char * ret = NULL;
+
+    for( int i = 0; i < mAttrNameList->getCount(); i++ ) {
+        if( 0 == strcmp( name, (char*)mAttrNameList->getItem( i ) ) ) {
+            ret = (char*)mAttrValueList->getItem( i );
+            break;
+        }
+    }
+
+    return ret;
+}
+
+int SP_XmlStartTagEvent :: getAttrCount() const
+{
+    return mAttrNameList->getCount();
+}
+
+const char * SP_XmlStartTagEvent :: getAttr( int index, const char ** value ) const
+{
+    const char * name = (char*)mAttrNameList->getItem( index );
+    if( NULL != name && NULL != value ) *value = (char*)mAttrValueList->getItem( index );
+
+    return name;
+}
+
+void SP_XmlStartTagEvent :: removeAttr( const char * name )
+{
+    int index = -1;
+
+    for( int i = 0; i < mAttrNameList->getCount(); i++ ) {
+        if( 0 == strcmp( name, (char*)mAttrNameList->getItem( i ) ) ) {
+            index = i;
+            break;
+        }
+    }
+
+    if( index >= 0 ) {
+        free( mAttrNameList->takeItem( index ) );
+        free( mAttrValueList->takeItem( index ) );
+    }
+}
+
+//=========================================================
+
+SP_XmlTextEvent :: SP_XmlTextEvent( int eventType )
+    : SP_XmlPullEvent( eventType )
+{
+    mText = NULL;
+}
+
+SP_XmlTextEvent :: ~SP_XmlTextEvent()
+{
+    if( NULL != mText ) free( mText );
+    mText = NULL;
+}
+
+void SP_XmlTextEvent :: setText( const char * text, int len )
+{
+    if( NULL != text ) {
+        if( NULL != mText ) free( mText );
+        mText = (char*)malloc( len + 1 );
+        memcpy( mText, text, len );
+        mText[ len ] = '\0';
+    }
+}
+
+const char * SP_XmlTextEvent :: getText() const
+{
+    return mText;
+}
+
+//=========================================================
+
+SP_XmlEndTagEvent :: SP_XmlEndTagEvent()
+    : SP_XmlTextEvent( eEndTag )
+{
+}
+
+SP_XmlEndTagEvent :: ~SP_XmlEndTagEvent()
+{
+}
+
+//=========================================================
+
+SP_XmlCDataEvent :: SP_XmlCDataEvent()
+    : SP_XmlTextEvent( eCData )
+{
+}
+
+SP_XmlCDataEvent :: ~SP_XmlCDataEvent()
+{
+}
+
+//=========================================================
+
+SP_XmlCommentEvent :: SP_XmlCommentEvent()
+    : SP_XmlTextEvent( eComment )
+{
+}
+
+SP_XmlCommentEvent :: ~SP_XmlCommentEvent()
+{
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/spxml/spxmlevent.hpp	Mon Aug 20 13:27:17 2012 +0000
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2007 Stephen Liu
+ * For license terms, see the file COPYING along with this library.
+ */
+
+#ifndef __spxmlevent_hpp__
+#define __spxmlevent_hpp__
+
+class SP_XmlArrayList;
+class SP_XmlQueue;
+
+class SP_XmlPullEvent {
+public:
+    enum EventType { eStartDocument, eEndDocument, ePI,
+            eDocDecl, eDocType, eStartTag, eEndTag, eCData, eComment };
+
+    SP_XmlPullEvent( int eventType );
+    virtual ~SP_XmlPullEvent();
+
+    int getEventType();
+
+private:
+    /// Private copy constructor and copy assignment ensure classes derived from
+    /// this cannot be copied.
+    SP_XmlPullEvent( SP_XmlPullEvent & );
+    SP_XmlPullEvent & operator=( SP_XmlPullEvent & );
+
+protected:
+    const int mEventType;
+};
+
+class SP_XmlPullEventQueue {
+public:
+    SP_XmlPullEventQueue();
+    ~SP_XmlPullEventQueue();
+
+    void enqueue( SP_XmlPullEvent * event );
+    SP_XmlPullEvent * dequeue();
+
+private:
+    SP_XmlPullEventQueue( SP_XmlPullEventQueue & );
+    SP_XmlPullEventQueue & operator=( SP_XmlPullEventQueue & );
+
+    SP_XmlQueue * mQueue;
+};
+
+class SP_XmlStartDocEvent : public SP_XmlPullEvent {
+public:
+    SP_XmlStartDocEvent();
+    virtual ~SP_XmlStartDocEvent();
+};
+
+class SP_XmlEndDocEvent : public SP_XmlPullEvent {
+public:
+    SP_XmlEndDocEvent();
+    virtual ~SP_XmlEndDocEvent();
+};
+
+class SP_XmlDocTypeEvent : public SP_XmlPullEvent {
+public:
+    SP_XmlDocTypeEvent();
+    virtual ~SP_XmlDocTypeEvent();
+
+    void setName( const char * name );
+    const char * getName() const;
+    void setSystemID( const char * systemID );
+    const char * getSystemID() const;
+    void setPublicID( const char * publicID );
+    const char * getPublicID() const;
+    void setDTD( const char * dtd );
+    const char * getDTD() const;
+
+private:
+    char mName[ 128 ];
+    char mSystemID[ 128 ];
+    char mPublicID[ 128 ];
+    char mDTD[ 256 ];    
+};
+
+class SP_XmlPIEvent : public SP_XmlPullEvent {
+public:
+    SP_XmlPIEvent();
+    virtual ~SP_XmlPIEvent();
+
+    void setTarget( const char * target );
+    const char * getTarget();
+
+    void setData( const char * data, int len );
+    const char * getData();
+
+private:
+    char mTarget[ 128 ];
+    char * mData;
+};
+
+class SP_XmlDocDeclEvent : public SP_XmlPullEvent {
+public:
+    SP_XmlDocDeclEvent();
+    virtual ~SP_XmlDocDeclEvent();
+
+    void setVersion( const char * version );
+    const char * getVersion() const;
+    void setEncoding( const char * encoding );
+    const char * getEncoding() const;
+    void setStandalone( int standalone );
+    int getStandalone() const;
+
+private:
+    char mVersion[ 8 ];
+    char mEncoding[ 32 ];
+    int mStandalone;
+};
+
+class SP_XmlStartTagEvent : public SP_XmlPullEvent {
+public:
+    SP_XmlStartTagEvent();
+    virtual ~SP_XmlStartTagEvent();
+
+    void setName( const char * name );
+    const char * getName() const;
+
+    void addAttr( const char * name, const char * value );
+    const char * getAttrValue( const char * name ) const;
+    int getAttrCount() const;
+
+    /// get attribute name and value by index, return attribute name
+    const char * getAttr( int index, const char ** value ) const;
+
+    void removeAttr( const char * name );
+
+private:
+    char * mName;
+    SP_XmlArrayList * mAttrNameList;
+    SP_XmlArrayList * mAttrValueList;
+};
+
+class SP_XmlTextEvent : public SP_XmlPullEvent {
+public:
+    SP_XmlTextEvent( int eventType );
+    virtual ~SP_XmlTextEvent();
+
+    void setText( const char * text, int len );
+    const char * getText() const;
+
+private:
+    char * mText;
+};
+
+class SP_XmlEndTagEvent : public SP_XmlTextEvent {
+public:
+    SP_XmlEndTagEvent();
+    virtual ~SP_XmlEndTagEvent();
+};
+
+class SP_XmlCDataEvent : public SP_XmlTextEvent {
+public:
+    SP_XmlCDataEvent();
+    virtual ~SP_XmlCDataEvent();
+};
+
+class SP_XmlCommentEvent : public SP_XmlTextEvent {
+public:
+    SP_XmlCommentEvent();
+    virtual ~SP_XmlCommentEvent();
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/spxml/spxmlhandle.cpp	Mon Aug 20 13:27:17 2012 +0000
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2008 Stephen Liu
+ * For license terms, see the file COPYING along with this library.
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "spxmlhandle.hpp"
+#include "spxmlnode.hpp"
+
+
+SP_XmlHandle :: SP_XmlHandle( SP_XmlNode * node )
+{
+	mNode = node;
+}
+
+SP_XmlHandle :: SP_XmlHandle( const SP_XmlHandle & ref )
+{
+	mNode = ref.mNode;
+}
+
+SP_XmlHandle & SP_XmlHandle :: operator=( const SP_XmlHandle & ref )
+{
+	mNode = ref.mNode;
+	return *this;
+}
+
+SP_XmlHandle :: ~SP_XmlHandle()
+{
+}
+
+SP_XmlHandle SP_XmlHandle :: getChild( const char * name, int index ) const
+{
+	SP_XmlNode * ret = NULL;
+
+	if( NULL != mNode ) {
+		if( SP_XmlNode::eELEMENT == mNode->getType() ) {
+			SP_XmlElementNode * element = (SP_XmlElementNode*)mNode;
+			const SP_XmlNodeList * children = element->getChildren();
+
+			int tmpIndex = index;
+			for( int i = 0; i < children->getLength(); i++ ) {
+				if( SP_XmlNode::eELEMENT == children->get(i)->getType() ) {
+					SP_XmlElementNode * iter = (SP_XmlElementNode*)children->get(i);
+					if( 0 == strcmp( name, iter->getName() ) ) {
+						if( 0 == tmpIndex ) {
+							ret = iter;
+							break;
+						}
+						tmpIndex--;
+					}
+				}
+			}
+		}
+	}
+
+	return SP_XmlHandle( ret );
+}
+
+SP_XmlHandle SP_XmlHandle :: getChild( int index ) const
+{
+	SP_XmlNode * ret = NULL;
+
+	if( NULL != mNode ) {
+		if( SP_XmlNode::eELEMENT == mNode->getType() ) {
+			SP_XmlElementNode * element = (SP_XmlElementNode*)mNode;
+			ret = (SP_XmlNode*)element->getChildren()->get( index );
+		}
+	}
+
+	return SP_XmlHandle( ret );
+}
+
+SP_XmlHandle SP_XmlHandle :: getElement( int index ) const
+{
+	SP_XmlNode * ret = NULL;
+
+	if( NULL != mNode ) {
+		if( SP_XmlNode::eELEMENT == mNode->getType() ) {
+			SP_XmlElementNode * element = (SP_XmlElementNode*)mNode;
+			const SP_XmlNodeList * children = element->getChildren();
+
+			int tmpIndex = index;
+			for( int i = 0; i < children->getLength(); i++ ) {
+				if( SP_XmlNode::eELEMENT == children->get(i)->getType() ) {
+					SP_XmlElementNode * iter = (SP_XmlElementNode*)children->get(i);
+
+					if( 0 == tmpIndex ) {
+						ret = iter;
+						break;
+					}
+					tmpIndex--;
+				}
+			}
+		}
+	}
+
+	return SP_XmlHandle( ret );
+}
+
+SP_XmlNode * SP_XmlHandle :: toNode()
+{
+	return mNode;
+}
+
+SP_XmlElementNode * SP_XmlHandle :: toElement()
+{
+	if( NULL != mNode && SP_XmlNode::eELEMENT == mNode->getType() ) {
+		return (SP_XmlElementNode*)mNode;
+	}
+
+	return NULL;
+}
+
+SP_XmlCDataNode * SP_XmlHandle :: toCData()
+{
+	if( NULL != mNode && SP_XmlNode::eCDATA == mNode->getType() ) {
+		return (SP_XmlCDataNode*)mNode;
+	}
+
+	return NULL;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/spxml/spxmlhandle.hpp	Mon Aug 20 13:27:17 2012 +0000
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2008 Stephen Liu
+ * For license terms, see the file COPYING along with this library.
+ */
+
+#ifndef __spxmlhandle_hpp__
+#define __spxmlhandle_hpp__
+
+class SP_XmlNode;
+
+class SP_XmlElementNode;
+class SP_XmlCDataNode;
+class SP_XmlCommentNode;
+
+/**
+ *  This class is a clone of TinyXML's TiXmlHandle class.
+ *
+ *
+ *  A SP_XmlHandle is a class that wraps a node pointer with null checks; this is
+ *  an incredibly useful thing. Note that SP_XmlHandle is not part of the SPXml
+ *  DOM structure. It is a separate utility class.
+ *
+
+	Take an example:
+	@verbatim
+	<Document>
+		<Element attributeA = "valueA">
+			<Child attributeB = "value1" />
+			<Child attributeB = "value2" />
+		</Element>
+	<Document>
+	@endverbatim
+
+	Assuming you want the value of "attributeB" in the 2nd "Child" element, a
+	SP_XmlHandle checks for null pointers so it is perfectly safe and correct to use:
+
+	@verbatim
+	SP_XmlHandle rootHandle( parser.getDocument()->getRootElement() );
+	SP_XmlElementNode * child2 = rootHandle.getChild( "Element" )
+			.getChild( "Child", 1 ).toElement();
+
+	if( child2 ) {
+		// do something
+	}
+	@endverbatim
+ *
+ */
+
+class SP_XmlHandle {
+public:
+	SP_XmlHandle( SP_XmlNode * node );
+	SP_XmlHandle( const SP_XmlHandle & ref );
+	SP_XmlHandle & operator=( const SP_XmlHandle & ref );
+
+	~SP_XmlHandle();
+
+	SP_XmlHandle getChild( const char * name, int index = 0 ) const;
+
+	SP_XmlHandle getChild( int index ) const;
+
+	SP_XmlHandle getElement( int index ) const;
+
+	SP_XmlNode * toNode();
+
+	SP_XmlElementNode * toElement();
+
+	SP_XmlCDataNode * toCData();
+
+private:
+	SP_XmlNode * mNode;
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/spxml/spxmlnode.cpp	Mon Aug 20 13:27:17 2012 +0000
@@ -0,0 +1,447 @@
+/*
+ * Copyright 2007 Stephen Liu
+ * For license terms, see the file COPYING along with this library.
+ */
+
+#include <string.h>
+#include <stdlib.h>
+
+#include "spxmlnode.hpp"
+#include "spxmlutils.hpp"
+#include "spxmlevent.hpp"
+
+//=========================================================
+
+SP_XmlNode :: SP_XmlNode( int type )
+	: mType( type )
+{
+	mParent = NULL;
+}
+
+SP_XmlNode :: ~SP_XmlNode()
+{
+	mParent = NULL;
+}
+
+void SP_XmlNode :: setParent( SP_XmlNode * parent )
+{
+	mParent = parent;
+}
+
+const SP_XmlNode * SP_XmlNode :: getParent() const
+{
+	return mParent;
+}
+
+int SP_XmlNode :: getType() const
+{
+	return mType;
+}
+
+//=========================================================
+
+SP_XmlNodeList :: SP_XmlNodeList()
+{
+	mList = new SP_XmlArrayList();
+}
+
+SP_XmlNodeList :: ~SP_XmlNodeList()
+{
+	for( int i = 0; i < mList->getCount(); i++ ) {
+		SP_XmlNode * node = (SP_XmlNode*)mList->getItem( i );
+		delete node;
+	}
+
+	delete mList;
+
+	mList = NULL;
+}
+
+int SP_XmlNodeList :: getLength() const
+{
+	return mList->getCount();
+}
+
+void SP_XmlNodeList :: append( SP_XmlNode * node )
+{
+	mList->append( node );
+}
+
+SP_XmlNode * SP_XmlNodeList :: get( int index ) const
+{
+	return (SP_XmlNode*)mList->getItem( index );
+}
+
+SP_XmlNode * SP_XmlNodeList :: take( int index ) const
+{
+	return (SP_XmlNode*)mList->takeItem( index );
+}
+
+//=========================================================
+
+SP_XmlDocument :: SP_XmlDocument()
+	: SP_XmlNode( eXMLDOC )
+{
+	mDocDecl = NULL;
+	mDocType = NULL;
+	mChildren = new SP_XmlNodeList();
+}
+
+SP_XmlDocument :: ~SP_XmlDocument()
+{
+	if( NULL != mDocDecl ) delete mDocDecl;
+	mDocDecl = NULL;
+
+	if( NULL != mDocType ) delete mDocType;
+	mDocType = NULL;
+
+	if( NULL != mChildren ) delete mChildren;
+	mChildren = NULL;
+}
+
+void SP_XmlDocument :: setDocDecl( SP_XmlDocDeclNode * docDecl )
+{
+	if( NULL != mDocDecl ) delete mDocDecl;
+	docDecl->setParent( this );
+	mDocDecl = docDecl;
+}
+
+SP_XmlDocDeclNode * SP_XmlDocument :: getDocDecl() const
+{
+	return mDocDecl;
+}
+
+void SP_XmlDocument :: setDocType( SP_XmlDocTypeNode * docType )
+{
+	if( NULL != mDocType ) delete mDocType;
+	docType->setParent( this );
+	mDocType = docType;
+}
+
+SP_XmlDocTypeNode * SP_XmlDocument :: getDocType() const
+{
+	return mDocType;
+}
+
+void SP_XmlDocument :: setRootElement( SP_XmlElementNode * rootElement )
+{
+	int index = -1;
+	for( int i = 0; i < mChildren->getLength(); i++ ) {
+		const SP_XmlNode * node = mChildren->get( i );
+
+		if( SP_XmlNode::eELEMENT == node->getType() ) {
+			index = i;
+			break;
+		}
+	}
+
+	if( index >= 0 ) {
+		SP_XmlNode * node = mChildren->take( index );
+		delete node;
+	}
+
+	mChildren->append( rootElement );
+	rootElement->setParent( this );
+}
+
+SP_XmlElementNode * SP_XmlDocument :: getRootElement() const
+{
+	SP_XmlElementNode * ret = NULL;
+
+	for( int i = 0; i < mChildren->getLength(); i++ ) {
+		const SP_XmlNode * node = mChildren->get( i );
+
+		if( SP_XmlNode::eELEMENT == node->getType() ) {
+			ret = (SP_XmlElementNode*)node;
+			break;
+		}
+	}
+
+	return ret;
+}
+
+SP_XmlNodeList * SP_XmlDocument :: getChildren() const
+{
+	return mChildren;
+}
+
+//=========================================================
+
+SP_XmlPINode :: SP_XmlPINode()
+	: SP_XmlNode( ePI )
+{
+	mEvent = new SP_XmlPIEvent();
+}
+
+SP_XmlPINode :: SP_XmlPINode( SP_XmlPIEvent * event )
+	: SP_XmlNode( ePI )
+{
+	mEvent = event;
+}
+
+SP_XmlPINode :: ~SP_XmlPINode()
+{
+	if( NULL != mEvent ) delete mEvent;
+	mEvent = NULL;
+}
+
+void SP_XmlPINode :: setTarget( const char * target )
+{
+	mEvent->setTarget( target );
+}
+
+const char * SP_XmlPINode :: getTarget()
+{
+	return mEvent->getTarget();
+}
+
+void SP_XmlPINode :: setData( const char * data )
+{
+	mEvent->setData( data, strlen( data ) );
+}
+
+const char * SP_XmlPINode :: getData()
+{
+	return mEvent->getData();
+}
+
+//=========================================================
+
+SP_XmlDocDeclNode :: SP_XmlDocDeclNode()
+	: SP_XmlNode( eDOCDECL )
+{
+	mEvent = new SP_XmlDocDeclEvent();
+}
+
+SP_XmlDocDeclNode :: SP_XmlDocDeclNode( SP_XmlDocDeclEvent * event )
+	: SP_XmlNode( eDOCDECL )
+{
+	mEvent = event;
+}
+
+SP_XmlDocDeclNode :: ~SP_XmlDocDeclNode()
+{
+	if( NULL != mEvent ) delete mEvent;
+	mEvent = NULL;
+}
+
+void SP_XmlDocDeclNode :: setVersion( const char * version )
+{
+	mEvent->setVersion( version );
+}
+
+const char * SP_XmlDocDeclNode :: getVersion() const
+{
+	return mEvent->getVersion();
+}
+
+void SP_XmlDocDeclNode :: setEncoding( const char * encoding )
+{
+	mEvent->setEncoding( encoding );
+}
+
+const char * SP_XmlDocDeclNode :: getEncoding() const
+{
+	return mEvent->getEncoding();
+}
+
+void SP_XmlDocDeclNode :: setStandalone( int standalone )
+{
+	mEvent->setStandalone( standalone );
+}
+
+int SP_XmlDocDeclNode :: getStandalone() const
+{
+	return mEvent->getStandalone();
+}
+
+//=========================================================
+
+SP_XmlDocTypeNode :: SP_XmlDocTypeNode()
+	: SP_XmlNode( eDOCTYPE )
+{
+	mEvent = new SP_XmlDocTypeEvent();
+}
+
+SP_XmlDocTypeNode :: SP_XmlDocTypeNode( SP_XmlDocTypeEvent * event )
+	: SP_XmlNode( eDOCTYPE )
+{
+	mEvent = event;
+}
+
+SP_XmlDocTypeNode :: ~SP_XmlDocTypeNode()
+{
+	if( NULL != mEvent ) delete mEvent;
+	mEvent = NULL;
+}
+
+void SP_XmlDocTypeNode :: setName( const char * name )
+{
+	mEvent->setName( name );
+}
+
+const char * SP_XmlDocTypeNode :: getName() const
+{
+	return mEvent->getName();
+}
+
+void SP_XmlDocTypeNode :: setSystemID( const char * systemID )
+{
+	mEvent->setSystemID( systemID );
+}
+
+const char * SP_XmlDocTypeNode :: getSystemID() const
+{
+	return mEvent->getSystemID();
+}
+
+void SP_XmlDocTypeNode :: setPublicID( const char * publicID )
+{
+	mEvent->setPublicID( publicID );
+}
+
+const char * SP_XmlDocTypeNode :: getPublicID() const
+{
+	return mEvent->getPublicID();
+}
+
+void SP_XmlDocTypeNode :: setDTD( const char * dtd )
+{
+	mEvent->setDTD( dtd );
+}
+
+const char * SP_XmlDocTypeNode :: getDTD() const
+{
+	return mEvent->getDTD();
+}
+
+//=========================================================
+
+SP_XmlElementNode :: SP_XmlElementNode()
+	: SP_XmlNode( eELEMENT )
+{
+	mEvent = new SP_XmlStartTagEvent();
+	mChildren = new SP_XmlNodeList();
+}
+
+SP_XmlElementNode :: SP_XmlElementNode( SP_XmlStartTagEvent * event )
+	: SP_XmlNode( eELEMENT )
+{
+	mEvent = event;
+	mChildren = new SP_XmlNodeList();
+}
+
+SP_XmlElementNode :: ~SP_XmlElementNode()
+{
+	if( NULL != mEvent ) delete mEvent;
+	mEvent = NULL;
+
+	if( NULL != mChildren ) delete mChildren;
+	mChildren = NULL;
+}
+
+void SP_XmlElementNode :: setName( const char * name )
+{
+	mEvent->setName( name );
+}
+
+const char * SP_XmlElementNode :: getName() const
+{
+	return mEvent->getName();
+}
+
+void SP_XmlElementNode :: addChild( SP_XmlNode * node )
+{
+	node->setParent( this );
+	mChildren->append( node );
+}
+
+const SP_XmlNodeList * SP_XmlElementNode :: getChildren() const
+{
+	return mChildren;
+}
+
+void SP_XmlElementNode :: addAttr( const char * name, const char * value )
+{
+	mEvent->addAttr( name, value );
+}
+
+const char * SP_XmlElementNode :: getAttrValue( const char * name ) const
+{
+	return mEvent->getAttrValue( name );
+}
+
+int SP_XmlElementNode :: getAttrCount() const
+{
+	return mEvent->getAttrCount();
+}
+
+const char * SP_XmlElementNode :: getAttr( int index, const char ** value ) const
+{
+	return mEvent->getAttr( index, value );
+}
+
+void SP_XmlElementNode :: removeAttr( const char * name )
+{
+	mEvent->removeAttr( name );
+}
+
+//=========================================================
+
+SP_XmlCDataNode :: SP_XmlCDataNode()
+	: SP_XmlNode( eCDATA )
+{
+	mEvent = new SP_XmlCDataEvent();
+}
+
+SP_XmlCDataNode :: SP_XmlCDataNode( SP_XmlCDataEvent * event )
+	: SP_XmlNode( eCDATA )
+{
+	mEvent = event;
+}
+
+SP_XmlCDataNode :: ~SP_XmlCDataNode()
+{
+	if( NULL != mEvent ) delete mEvent;
+	mEvent = NULL;
+}
+
+void SP_XmlCDataNode :: setText( const char * content )
+{
+	mEvent->setText( content, strlen( content ) );
+}
+
+const char * SP_XmlCDataNode :: getText() const
+{
+	return mEvent->getText();
+}
+
+//=========================================================
+
+SP_XmlCommentNode :: SP_XmlCommentNode()
+	: SP_XmlNode( eCOMMENT )
+{
+	mEvent = new SP_XmlCommentEvent();
+}
+
+SP_XmlCommentNode :: SP_XmlCommentNode( SP_XmlCommentEvent * event )
+	: SP_XmlNode( eCOMMENT )
+{
+	mEvent = event;
+}
+
+SP_XmlCommentNode :: ~SP_XmlCommentNode()
+{
+	if( NULL != mEvent ) delete mEvent;
+	mEvent = NULL;
+}
+
+void SP_XmlCommentNode :: setText( const char * comment )
+{
+	mEvent->setText( comment, strlen( comment ) );
+}
+
+const char * SP_XmlCommentNode :: getText() const
+{
+	return mEvent->getText();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/spxml/spxmlnode.hpp	Mon Aug 20 13:27:17 2012 +0000
@@ -0,0 +1,179 @@
+/*
+ * Copyright 2007 Stephen Liu
+ * For license terms, see the file COPYING along with this library.
+ */
+
+#ifndef __spxmlnode_hpp__
+#define __spxmlnode_hpp__
+
+class SP_XmlArrayList;
+
+class SP_XmlNode {
+public:
+	enum { eXMLDOC, eDOCDECL, ePI, eDOCTYPE, eELEMENT, eCDATA, eCOMMENT  };
+
+	SP_XmlNode( int type );
+	virtual ~SP_XmlNode();
+
+	void setParent( SP_XmlNode * parent );
+	const SP_XmlNode * getParent() const;
+	int getType() const;
+
+protected:
+	SP_XmlNode( SP_XmlNode & );
+	SP_XmlNode & operator=( SP_XmlNode & );
+
+private:
+	SP_XmlNode * mParent;
+	const int mType;
+};
+
+class SP_XmlNodeList {
+public:
+	SP_XmlNodeList();
+	~SP_XmlNodeList();
+
+	int getLength() const;
+	void append( SP_XmlNode * node );
+	SP_XmlNode * get( int index ) const;
+	SP_XmlNode * take( int index ) const;
+
+private:
+	SP_XmlNodeList( SP_XmlNodeList & );
+	SP_XmlNodeList & operator=( SP_XmlNodeList & );
+
+	SP_XmlArrayList * mList;
+};
+
+class SP_XmlPIEvent;
+class SP_XmlDocDeclEvent;
+class SP_XmlDocTypeEvent;
+class SP_XmlStartTagEvent;
+class SP_XmlCDataEvent;
+class SP_XmlCommentEvent;
+
+class SP_XmlElementNode;
+class SP_XmlDocDeclNode;
+class SP_XmlDocTypeNode;
+
+class SP_XmlDocument : public SP_XmlNode {
+public:
+	SP_XmlDocument();
+	virtual ~SP_XmlDocument();
+
+	void setDocDecl( SP_XmlDocDeclNode * docDecl );
+	SP_XmlDocDeclNode * getDocDecl() const;
+	void setDocType( SP_XmlDocTypeNode * docType );
+	SP_XmlDocTypeNode * getDocType() const;
+	void setRootElement( SP_XmlElementNode * rootElement );
+	SP_XmlElementNode * getRootElement() const;
+	SP_XmlNodeList * getChildren() const;
+
+private:
+	SP_XmlDocDeclNode * mDocDecl;
+	SP_XmlDocTypeNode * mDocType;
+	SP_XmlNodeList * mChildren;
+};
+
+class SP_XmlPINode : public SP_XmlNode {
+public:
+	SP_XmlPINode();
+	SP_XmlPINode( SP_XmlPIEvent * event );
+	virtual ~SP_XmlPINode();
+
+	void setTarget( const char * target );
+	const char * getTarget();
+
+	void setData( const char * data );
+	const char * getData();
+
+private:
+	SP_XmlPIEvent * mEvent;
+};
+
+class SP_XmlDocDeclNode : public SP_XmlNode {
+public:
+	SP_XmlDocDeclNode();
+	SP_XmlDocDeclNode( SP_XmlDocDeclEvent * event );
+	virtual ~SP_XmlDocDeclNode();
+
+	void setVersion( const char * version );
+	const char * getVersion() const;
+	void setEncoding( const char * encoding );
+	const char * getEncoding() const;
+	void setStandalone( int standalone );
+	int getStandalone() const;
+
+private:
+	SP_XmlDocDeclEvent * mEvent;
+};
+
+class SP_XmlDocTypeNode : public SP_XmlNode {
+public:
+	SP_XmlDocTypeNode();
+	SP_XmlDocTypeNode( SP_XmlDocTypeEvent * event );
+	virtual ~SP_XmlDocTypeNode();
+
+	void setName( const char * name );
+	const char * getName() const;
+	void setSystemID( const char * systemID );
+	const char * getSystemID() const;
+	void setPublicID( const char * publicID );
+	const char * getPublicID() const;
+	void setDTD( const char * dtd );
+	const char * getDTD() const;
+
+private:
+	SP_XmlDocTypeEvent * mEvent;
+};
+
+class SP_XmlElementNode : public SP_XmlNode {
+public:
+	SP_XmlElementNode();
+	SP_XmlElementNode( SP_XmlStartTagEvent * event );
+	virtual ~SP_XmlElementNode();
+
+	void setName( const char * name );
+	const char * getName() const;
+	void addChild( SP_XmlNode * node );
+	const SP_XmlNodeList * getChildren() const;
+
+	void addAttr( const char * name, const char * value );
+	const char * getAttrValue( const char * name ) const;
+	int getAttrCount() const;
+	const char * getAttr( int index, const char ** value ) const;
+
+	void removeAttr( const char * name );
+
+protected:
+	SP_XmlStartTagEvent * mEvent;
+	SP_XmlNodeList * mChildren;
+};
+
+class SP_XmlCDataNode : public SP_XmlNode {
+public:
+	SP_XmlCDataNode();
+	SP_XmlCDataNode( SP_XmlCDataEvent * event );
+	virtual ~SP_XmlCDataNode();
+
+	void setText( const char * content );
+	const char * getText() const;
+
+protected:
+	SP_XmlCDataEvent * mEvent;
+};
+
+class SP_XmlCommentNode : public SP_XmlNode {
+public:
+	SP_XmlCommentNode();
+	SP_XmlCommentNode( SP_XmlCommentEvent * event );
+	virtual ~SP_XmlCommentNode();
+
+	void setText( const char * comment );
+	const char * getText() const;
+
+protected:
+	SP_XmlCommentEvent * mEvent;
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/spxml/spxmlparser.cpp	Mon Aug 20 13:27:17 2012 +0000
@@ -0,0 +1,219 @@
+/*
+ * Copyright 2007 Stephen Liu
+ * For license terms, see the file COPYING along with this library.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <typeinfo>
+#include "strdup.h"
+
+#include "spxmlparser.hpp"
+#include "spxmlreader.hpp"
+#include "spxmlutils.hpp"
+#include "spxmlevent.hpp"
+#include "spxmlcodec.hpp"
+
+SP_XmlPullParser :: SP_XmlPullParser()
+{
+    mReaderPool = new SP_XmlReaderPool();
+    mReader = getReader( SP_XmlReader::eLBracket );
+    mEventQueue = new SP_XmlPullEventQueue();
+    mEventQueue->enqueue( new SP_XmlStartDocEvent() );
+
+    mRootTagState = eRootNone;
+    mTagNameStack = new SP_XmlArrayList();
+    mLevel = 0;
+
+    mIgnoreWhitespace = 1;
+
+    mError = NULL;
+
+    memset( mErrorSegment, 0, sizeof( mErrorSegment ) );
+    mErrorIndex = 0;
+    mRowIndex = mColIndex = 0;
+
+    memset( mEncoding, 0, sizeof( mEncoding ) );
+}
+
+SP_XmlPullParser :: ~SP_XmlPullParser()
+{
+    mReaderPool->save( mReader );
+
+    for( int i = 0; i < mTagNameStack->getCount(); i++ ) {
+        free( (char*)mTagNameStack->getItem( i ) );
+    }
+    delete mTagNameStack;
+
+    delete mEventQueue;
+
+    delete mReaderPool;
+
+    if( NULL != mError ) free( mError );    
+}
+
+const char * SP_XmlPullParser :: getEncoding()
+{
+    if( '\0' == mEncoding[0] ) {
+        return SP_XmlStringCodec::DEFAULT_ENCODING;
+    }
+
+    return mEncoding;
+}
+
+int SP_XmlPullParser :: append( const char * source, int len )
+{
+    if( NULL != mError ) return 0;
+
+    int consumed = 0;
+
+    for( int i = 0; i < len && NULL == mError; i++ ) {
+
+        consumed++;
+
+        char c = source[ i ];
+
+        mErrorSegment[ mErrorIndex++ % sizeof( mErrorSegment ) ] = c;
+        mReader->read( this, c );
+        if( '\n' == c ) {
+            mRowIndex++;
+            mColIndex = 0;
+        } else {
+            mColIndex++;
+        }
+    }
+
+    return consumed;
+}
+
+SP_XmlPullEvent * SP_XmlPullParser :: getNext()
+{
+    SP_XmlPullEvent * event = mEventQueue->dequeue();
+
+    if( NULL != event ) {
+        if( SP_XmlPullEvent::eStartTag == event->getEventType() ) mLevel++;
+        if( SP_XmlPullEvent::eEndTag == event->getEventType() ) mLevel--;
+    }
+
+    return event;
+}
+
+int SP_XmlPullParser :: getLevel()
+{
+    return mLevel;
+}
+
+void SP_XmlPullParser :: setIgnoreWhitespace( int ignoreWhitespace )
+{
+    mIgnoreWhitespace = ignoreWhitespace;
+}
+
+int SP_XmlPullParser :: getIgnoreWhitespace()
+{
+    return mIgnoreWhitespace;
+}
+
+const char * SP_XmlPullParser :: getError()
+{
+    return mError;
+}
+
+void SP_XmlPullParser :: changeReader( SP_XmlReader * reader )
+{
+    SP_XmlPullEvent * event = mReader->getEvent( this );
+    if( NULL != event ) {
+        if( SP_XmlPullEvent::eStartTag == event->getEventType() ) {
+            if( eRootNone == mRootTagState ) mRootTagState = eRootStart;
+            const char * name = ((SP_XmlStartTagEvent*)event)->getName();
+            mTagNameStack->append( strdup( name ) );
+        }
+        if( SP_XmlPullEvent::eEndTag == event->getEventType() ) {
+            char error[ 256 ] = { 0 };
+
+            const char * etag = ((SP_XmlEndTagEvent*)event)->getText();
+            char * stag = (char*)mTagNameStack->takeItem( SP_XmlArrayList::LAST_INDEX );
+            if( NULL != stag ) {
+                if( 0 != strcmp( stag, etag ) ) {
+                    snprintf( error, sizeof( error ),
+                            "mismatched tag, start-tag <%s>, end-tag <%s>", stag, etag );
+                }
+                free( stag );
+            } else {
+                snprintf( error, sizeof( error ),
+                        "mismatched tag, start-tag <NULL>, end-tag <%s>", etag );
+            }
+
+            if( '\0' != *error ) {
+                setError( error );
+                delete event;
+                event = NULL;
+            }
+        }
+
+        if( NULL != event ) {
+            if( SP_XmlPullEvent::eDocDecl == event->getEventType() ) {
+                snprintf( mEncoding, sizeof( mEncoding ), "%s",
+                    ((SP_XmlDocDeclEvent*)event)->getEncoding() );
+            }
+            mEventQueue->enqueue( event );
+            if( mTagNameStack->getCount() <= 0 && eRootStart == mRootTagState ) {
+                mRootTagState = eRootEnd;
+                mEventQueue->enqueue( new SP_XmlEndDocEvent() );
+            }
+        }
+    }
+
+    //printf( "\nchange: %s -> %s\n", typeid( *mReader ).name(), typeid( *reader ).name() );
+
+    mReaderPool->save( mReader );
+    mReader = reader;
+}
+
+SP_XmlReader * SP_XmlPullParser :: getReader( int type )
+{
+    return mReaderPool->borrow( type );
+}
+
+void SP_XmlPullParser :: setError( const char * error )
+{
+    if( NULL != error ) {
+        if( NULL != mError ) free( mError );
+
+        char segment[ 2 * sizeof( mErrorSegment ) + 1 ];
+        {
+            memset( segment, 0, sizeof( segment ) );
+
+            char temp[ sizeof( mErrorSegment ) + 1 ];
+            memset( temp, 0, sizeof( temp ) );
+            if( mErrorIndex < (int)sizeof( mErrorSegment ) ) {
+                strncpy( temp, mErrorSegment, mErrorIndex );
+            } else {
+                int offset = mErrorIndex % sizeof( mErrorSegment );
+                strncpy( temp, mErrorSegment + offset, sizeof( mErrorSegment ) - offset );
+                strncpy( temp + sizeof( mErrorSegment ) - offset, mErrorSegment, offset );
+            }
+
+            for( char * pos = temp, * dest = segment; '\0' != *pos; pos++ ) {
+                if( '\r' == *pos ) {
+                    *dest++ = '\\';
+                    *dest++ = 'r';
+                } else if( '\n' == *pos ) {
+                    *dest++ = '\\';
+                    *dest++ = 'n';
+                } else if( '\t' == *pos ) {
+                    *dest++ = '\\';
+                    *dest++ = 't';
+                } else {
+                    *dest++ = *pos;
+                }
+            }
+        }
+
+        char msg[ 512 ];
+        snprintf( msg, sizeof( msg), "%s ( occured at row(%d), col(%d) : %s )",
+                error, mRowIndex + 1, mColIndex + 1, segment );
+
+        mError = strdup( msg );
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/spxml/spxmlparser.hpp	Mon Aug 20 13:27:17 2012 +0000
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2007 Stephen Liu
+ * For license terms, see the file COPYING along with this library.
+ */
+
+#ifndef __xmlparser_hpp__
+#define __xmlparser_hpp__
+
+class SP_XmlPullEvent;
+class SP_XmlPullEventQueue;
+class SP_XmlReader;
+class SP_XmlReaderPool;
+class SP_XmlArrayList;
+
+class SP_XmlPullParser {
+public:
+	SP_XmlPullParser();
+	~SP_XmlPullParser();
+
+	/// append more input xml source
+	/// @return how much byte has been consumed
+	int append( const char * source, int len );
+
+	/// @return NOT NULL : the pull event
+	/// @return NULL : error or need more input
+	SP_XmlPullEvent * getNext();	
+
+	/// @return NOT NULL : the detail error message
+	/// @return NULL : no error
+	const char * getError();
+
+	int getLevel();
+
+	/// default ignoreWhitespace is true
+	void setIgnoreWhitespace( int ignoreWhitespace );
+
+	int getIgnoreWhitespace();
+
+	const char * getEncoding();
+
+protected:
+	void changeReader( SP_XmlReader * reader );
+
+	SP_XmlReader * getReader( int type );
+
+	void setError( const char * error );
+
+	friend class SP_XmlReader;
+
+private:
+	SP_XmlPullEventQueue * mEventQueue;
+	SP_XmlReader * mReader;
+	SP_XmlReaderPool * mReaderPool;
+	SP_XmlArrayList * mTagNameStack;
+
+	enum { eRootNone, eRootStart, eRootEnd };
+	int mRootTagState;
+
+	int mLevel;
+
+	int mIgnoreWhitespace;
+
+	char * mError;
+
+	char mErrorSegment[ 32 ];
+	int mErrorIndex;
+	int mColIndex, mRowIndex;
+
+	char mEncoding[ 32 ];
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/spxml/spxmlreader.cpp	Mon Aug 20 13:27:17 2012 +0000
@@ -0,0 +1,570 @@
+/*
+ * Copyright 2007 Stephen Liu
+ * For license terms, see the file COPYING along with this library.
+ */
+
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <typeinfo>
+
+#include "spxmlparser.hpp"
+#include "spxmlreader.hpp"
+#include "spxmlutils.hpp"
+#include "spxmlstag.hpp"
+#include "spxmlevent.hpp"
+#include "spxmlcodec.hpp"
+
+//=========================================================
+
+SP_XmlReader :: SP_XmlReader()
+{
+	mBuffer = new SP_XmlStringBuffer();
+}
+
+SP_XmlReader :: ~SP_XmlReader()
+{
+	delete mBuffer;
+}
+
+void SP_XmlReader :: changeReader(
+		SP_XmlPullParser * parser, SP_XmlReader * reader )
+{
+	parser->changeReader( reader );
+}
+
+SP_XmlReader * SP_XmlReader :: getReader( SP_XmlPullParser * parser, int type )
+{
+	return parser->getReader( type );
+}
+
+void SP_XmlReader :: setError( SP_XmlPullParser * parser, const char * error )
+{
+	parser->setError( error );
+}
+
+void SP_XmlReader :: reset()
+{
+	mBuffer->clean();
+}
+
+//=========================================================
+
+SP_XmlPIReader :: SP_XmlPIReader()
+{
+}
+
+SP_XmlPIReader :: ~SP_XmlPIReader()
+{
+}
+
+void SP_XmlPIReader :: read( SP_XmlPullParser * parser, char c )
+{
+	if( '>' == c ) {
+		changeReader( parser, getReader( parser, SP_XmlReader::ePCData ) );
+	} else {
+		mBuffer->append( c );
+	}
+}
+
+SP_XmlPullEvent * SP_XmlPIReader :: getEvent( SP_XmlPullParser * parser )
+{
+	SP_XmlPullEvent * retEvent = NULL;
+
+	if( mBuffer->getSize() > 0 ) {
+		char * begin = (char*)mBuffer->getBuffer();
+		for( ; isspace( *begin ); ) begin++;
+
+		char * end = begin;
+		for( ; '\0' != *end && '?' != *end && ( ! isspace( *end ) ); ) end++;
+
+		char savedChar = *end;
+		*end = '\0';
+
+		if( 0 == strcasecmp( begin, "xml" ) ) {
+			*end = savedChar;
+
+			retEvent = parseDocDeclEvent( parser, mBuffer );
+		} else {
+			SP_XmlPIEvent * piEvent = new SP_XmlPIEvent();
+			piEvent->setTarget( begin );
+
+			*end = savedChar;
+
+			begin = end;
+			for( ; isspace( *begin ); ) begin++;
+
+			end = begin;
+			for( ; '\0' != *end && '?' != *end; ) end++;
+
+			piEvent->setData( begin, end - begin );
+
+			retEvent = piEvent;
+		}
+	}
+
+	return retEvent;
+}
+
+SP_XmlPullEvent * SP_XmlPIReader :: parseDocDeclEvent( SP_XmlPullParser * parser,
+		SP_XmlStringBuffer * buffer )
+{
+	SP_XmlDocDeclEvent * retEvent = NULL;
+
+	SP_XmlSTagParser tagParser( parser->getEncoding() );
+
+	tagParser.append( buffer->getBuffer(), buffer->getSize() );
+	tagParser.append( " ", 1 );
+
+	if( NULL == tagParser.getError() ) {
+		SP_XmlStartTagEvent * event = tagParser.takeEvent();
+
+		const char * version = event->getAttrValue( "version" );
+		const char * encoding = event->getAttrValue( "encoding" );
+		const char * standalone = event->getAttrValue( "standalone" );
+
+		retEvent = new SP_XmlDocDeclEvent();
+		retEvent->setVersion( NULL == version ? "" : version );
+		retEvent->setEncoding( NULL == encoding ? "" : encoding );
+		if( NULL != standalone ) {
+			if( 0 == strcasecmp( "no", standalone ) ) {
+				retEvent->setStandalone( 0 );
+			} else {
+				retEvent->setStandalone( 1 );
+			}
+		}
+
+		delete event;
+	} else {
+		setError( parser, tagParser.getError() );
+	}
+
+	return retEvent;
+}
+
+//=========================================================
+
+SP_XmlStartTagReader :: SP_XmlStartTagReader()
+{
+	mIsQuot = 0;
+}
+
+SP_XmlStartTagReader :: ~SP_XmlStartTagReader()
+{
+}
+
+void SP_XmlStartTagReader :: read( SP_XmlPullParser * parser, char c )
+{
+	if( '>' == c && 0 == mIsQuot ) {
+		changeReader( parser, getReader( parser, SP_XmlReader::ePCData ) );
+	} else if( '/' == c && 0 == mIsQuot ) {
+		SP_XmlReader * reader = getReader( parser, SP_XmlReader::eETag );
+		const char * pos = mBuffer->getBuffer();
+		for( ; isspace( *pos ); ) pos++;
+		for( ; 0 == isspace( *pos ) && '\0' != *pos; pos++ ) {
+			reader->read( parser, *pos );
+		}
+		changeReader( parser, reader );
+	} else if( '<' == c && 0 == mIsQuot ) {
+		setError( parser, "illegal char" );
+	} else {
+		mBuffer->append( c );
+
+		if( 0 == mIsQuot ) {
+			if( '\'' == c ) mIsQuot = 1;
+			if( '"' == c ) mIsQuot = 2;
+		} else {
+			if( 1 == mIsQuot && '\'' == c ) mIsQuot = 0;
+			if( 2 == mIsQuot && '"' == c ) mIsQuot = 0;
+		}
+	}
+}
+
+SP_XmlPullEvent * SP_XmlStartTagReader :: getEvent( SP_XmlPullParser * parser )
+{
+	SP_XmlStartTagEvent * retEvent = NULL;
+
+	SP_XmlSTagParser tagParser( parser->getEncoding() );
+	tagParser.append( mBuffer->getBuffer(), mBuffer->getSize() );
+	tagParser.append( " ", 1 );
+
+	if( NULL == tagParser.getError() ) {
+		retEvent = tagParser.takeEvent();
+	} else {
+		setError( parser, tagParser.getError() );
+	}
+
+	return retEvent;
+}
+
+void SP_XmlStartTagReader :: reset()
+{
+	SP_XmlReader::reset();
+	mIsQuot = 0;
+}
+
+//=========================================================
+
+SP_XmlEndTagReader :: SP_XmlEndTagReader()
+{
+}
+
+SP_XmlEndTagReader :: ~SP_XmlEndTagReader()
+{
+}
+
+void SP_XmlEndTagReader :: read( SP_XmlPullParser * parser,	char c )
+{
+	if( '>' == c ) {
+		changeReader( parser, getReader( parser, SP_XmlReader::ePCData ) );
+	} else if( '/' == c ) {
+		setError( parser, "illegal name char" );
+	} else {
+		mBuffer->append( c );
+	}
+}
+
+SP_XmlPullEvent * SP_XmlEndTagReader :: getEvent( SP_XmlPullParser * parser )
+{
+	const char * end = mBuffer->getBuffer() + mBuffer->getSize() - 1;
+
+	for( ; end > mBuffer->getBuffer() && isspace( *end ); ) end--;
+
+	SP_XmlEndTagEvent * retEvent = new SP_XmlEndTagEvent();
+	retEvent->setText( mBuffer->getBuffer(), end - mBuffer->getBuffer() + 1 );
+
+	return retEvent;
+}
+
+//=========================================================
+
+SP_XmlPCDataReader :: SP_XmlPCDataReader()
+{
+}
+
+SP_XmlPCDataReader :: ~SP_XmlPCDataReader()
+{
+}
+
+void SP_XmlPCDataReader :: read( SP_XmlPullParser * parser, char c )
+{
+	if( '<' == c ) {
+		SP_XmlReader * reader = getReader( parser, SP_XmlReader::eLBracket );
+		reader->read( parser, c );
+		changeReader( parser, reader );
+	} else {
+		mBuffer->append( c );
+	}
+}
+
+SP_XmlPullEvent * SP_XmlPCDataReader :: getEvent( SP_XmlPullParser * parser )
+{
+	SP_XmlCDataEvent * retEvent = NULL;
+
+	int ignore = 0;
+
+	if( 0 != parser->getIgnoreWhitespace() ) {
+		ignore = 1;
+		for( const char * pos = mBuffer->getBuffer(); '\0' != *pos; pos++ ) {
+			if( !isspace( *pos ) ) {
+				ignore = 0;
+				break;
+			}
+		}
+	}
+
+	if( 0 == ignore && mBuffer->getSize() > 0 ) {
+		retEvent = new SP_XmlCDataEvent();
+		SP_XmlStringBuffer buffer;
+		SP_XmlStringCodec::decode( parser->getEncoding(), mBuffer->getBuffer(), &buffer );
+		retEvent->setText( buffer.getBuffer(), buffer.getSize() );
+	}
+
+	return retEvent;
+}
+
+//=========================================================
+
+SP_XmlCDataSectionReader :: SP_XmlCDataSectionReader()
+{
+}
+
+SP_XmlCDataSectionReader :: ~SP_XmlCDataSectionReader()
+{
+}
+
+void SP_XmlCDataSectionReader :: read( SP_XmlPullParser * parser, char c )
+{
+	if( '>' == c && mBuffer->getSize() > 2 ) {
+		char last1 = mBuffer->getBuffer()[ mBuffer->getSize() - 1 ];
+		char last2 = mBuffer->getBuffer()[ mBuffer->getSize() - 2 ];
+
+		if( ']' == last1 && ']' == last2 ) {
+			changeReader( parser, getReader( parser, SP_XmlReader::ePCData ) );
+		} else {
+			mBuffer->append( c );
+		}
+	} else {
+		mBuffer->append( c );
+	}
+}
+
+SP_XmlPullEvent * SP_XmlCDataSectionReader :: getEvent( SP_XmlPullParser * parser )
+{
+	SP_XmlCDataEvent * retEvent = NULL;
+
+	int len = mBuffer->getSize();
+	const char * data = mBuffer->getBuffer();
+	if( 0 == strncmp( data, "CDATA[", strlen( "CDATA[" ) ) ) {
+		data += strlen( "CDATA[" );
+		len -= strlen( "CDATA[" );
+	}
+
+	int ignore = 0;
+	if( 0 != parser->getIgnoreWhitespace() ) {
+		ignore = 1;
+		for( int i = 0; i < len - 2; i++ ) {
+			if( !isspace( data[i] ) ) {
+				ignore = 0;
+				break;
+			}
+		}
+	}
+
+	if( 0 == ignore && len > 2 ) {
+		retEvent = new SP_XmlCDataEvent();
+		retEvent->setText( data, len - 2 );
+	}
+
+	return retEvent;
+}
+
+//=========================================================
+
+SP_XmlCommentReader :: SP_XmlCommentReader()
+{
+}
+
+SP_XmlCommentReader :: ~SP_XmlCommentReader()
+{
+}
+
+void SP_XmlCommentReader :: read( SP_XmlPullParser * parser, char c )
+{
+	if( '>' == c && mBuffer->getSize() >= 2 ) {
+		int size = mBuffer->getSize();
+		if( '-' == mBuffer->getBuffer()[ size - 1 ]
+				&& '-' == mBuffer->getBuffer()[ size - 2 ] ) {
+			changeReader( parser, getReader( parser, SP_XmlReader::ePCData ) );
+		} else {
+			mBuffer->append( c );
+		}
+	} else {
+		mBuffer->append( c );
+	}
+}
+
+SP_XmlPullEvent * SP_XmlCommentReader :: getEvent( SP_XmlPullParser * parser )
+{
+	SP_XmlCommentEvent * retEvent = new SP_XmlCommentEvent();
+
+	retEvent->setText( mBuffer->getBuffer(), mBuffer->getSize() - 2 );
+
+	return retEvent;
+}
+
+//=========================================================
+
+SP_XmlDocTypeReader :: SP_XmlDocTypeReader()
+{
+}
+
+SP_XmlDocTypeReader :: ~SP_XmlDocTypeReader()
+{
+}
+
+void SP_XmlDocTypeReader :: read( SP_XmlPullParser * parser, char c )
+{
+	if( '>' == c ) {
+		if( NULL != strchr( mBuffer->getBuffer(), '[' ) ) {
+			char last = mBuffer->getBuffer()[ mBuffer->getSize() - 1 ];
+			if( ']' == last ) {
+				changeReader( parser, getReader( parser, SP_XmlReader::ePCData ) );
+			} else {
+				mBuffer->append( c );
+			}
+		} else {
+			changeReader( parser, getReader( parser, SP_XmlReader::ePCData ) );
+		}
+	} else {
+		mBuffer->append( c );
+	}
+}
+
+SP_XmlPullEvent * SP_XmlDocTypeReader :: getEvent( SP_XmlPullParser * parser )
+{
+	SP_XmlDocTypeEvent * retEvent = NULL;
+
+	SP_XmlSTagParser tagParser( parser->getEncoding() );
+
+	tagParser.append( "DOCTYPE ", strlen( "DOCTYPE " ) );
+	tagParser.append( mBuffer->getBuffer(), mBuffer->getSize() );
+	tagParser.append( " ", 1 );
+	if( NULL == tagParser.getError() ) {
+		SP_XmlStartTagEvent * event = tagParser.takeEvent();
+
+		retEvent = new SP_XmlDocTypeEvent();
+
+		for( int i = 0; i < event->getAttrCount(); i += 2 ) {
+			const char * name = event->getAttr( i, NULL );
+			if( 0 == strcmp( name, "DOCTYPE" ) ) {
+				name = event->getAttr( i + 1, NULL );
+				retEvent->setName( NULL == name ? "" : name );	
+			} else if( 0 == strcmp( name, "PUBLIC" ) ) {
+				name = event->getAttr( i + 1, NULL );
+				retEvent->setPublicID( NULL == name ? "" : name );
+			} else if( 0 == strcmp( name, "SYSTEM" ) ) {
+				name = event->getAttr( i + 1, NULL );
+				retEvent->setSystemID( NULL == name ? "" : name );
+			} else if( NULL != strstr( name, ".dtd" ) ) {
+				retEvent->setDTD( name );
+			}
+		}
+
+		delete event;
+	} else {
+		//setError( parser, tagParser.getError() );
+	}
+
+	return retEvent;
+}
+
+//=========================================================
+
+SP_XmlLeftBracketReader :: SP_XmlLeftBracketReader()
+{
+	mHasReadBracket = 0;
+}
+
+SP_XmlLeftBracketReader :: ~SP_XmlLeftBracketReader()
+{
+}
+
+void SP_XmlLeftBracketReader :: read( SP_XmlPullParser * parser, char c )
+{
+	if( 0 == mHasReadBracket ) {
+		if( isspace( c ) ) {
+			//skip
+		} else if( '<' == c ) {
+			mHasReadBracket = 1;
+		}
+	} else {
+		if( '?' == c ) {
+			changeReader( parser, getReader( parser, SP_XmlReader::ePI ) );
+		} else if( '/' == c ) {
+			changeReader( parser, getReader( parser, SP_XmlReader::eETag ) );
+		} else if( '!' == c ) {
+			changeReader( parser, getReader( parser, SP_XmlReader::eSign ) );
+		} else if( SP_XmlStringCodec::isNameChar( parser->getEncoding(), c ) ) {
+			SP_XmlReader * reader = getReader( parser, SP_XmlReader::eSTag );
+			reader->read( parser, c );
+			changeReader( parser, reader );
+		} else {
+			setError( parser, "not well-formed" );
+		}
+	}
+}
+
+SP_XmlPullEvent * SP_XmlLeftBracketReader :: getEvent( SP_XmlPullParser * parser )
+{
+	return NULL;
+}
+
+void SP_XmlLeftBracketReader :: reset()
+{
+	SP_XmlReader::reset();
+	mHasReadBracket = 0;
+}
+
+//=========================================================
+
+SP_XmlSignReader :: SP_XmlSignReader()
+{
+}
+
+SP_XmlSignReader :: ~SP_XmlSignReader()
+{
+}
+
+void SP_XmlSignReader :: read( SP_XmlPullParser * parser, char c )
+{
+	if( '[' == c ) {
+		changeReader( parser, getReader( parser, SP_XmlReader::eCDataSection ) );
+	} else if( '-' == c ) {
+		changeReader( parser, getReader( parser, SP_XmlReader::eComment ) );
+	} else if( isupper( c ) ) {
+		SP_XmlReader * reader = getReader( parser, SP_XmlReader::eDocType );
+		reader->read( parser, c );
+		changeReader( parser, reader );
+	} else {
+		setError( parser, "not well-formed" );
+	}
+}
+
+SP_XmlPullEvent * SP_XmlSignReader :: getEvent( SP_XmlPullParser * parser )
+{
+	return NULL;
+}
+
+//=========================================================
+
+SP_XmlReaderPool :: SP_XmlReaderPool()
+{
+	mReaderList = (SP_XmlReader**)malloc( sizeof( void * ) * SP_XmlReader::MAX_READER );
+	memset( mReaderList, 0, sizeof( void * ) * SP_XmlReader::MAX_READER );
+}
+
+SP_XmlReaderPool :: ~SP_XmlReaderPool()
+{
+	for( int i = 0; i < SP_XmlReader::MAX_READER; i++ ) {
+		if( NULL != mReaderList[i] ) {
+			delete mReaderList[i];
+		}
+	}
+	free( mReaderList );
+}
+
+SP_XmlReader * SP_XmlReaderPool :: borrow( int type )
+{
+	SP_XmlReader * reader = NULL;
+
+	if( type >= 0 && type < SP_XmlReader::MAX_READER ) {
+		reader = mReaderList[ type ];
+		if( NULL == reader ) {
+			switch( type ) {
+			case SP_XmlReader::ePI: reader = new SP_XmlPIReader(); break;
+			case SP_XmlReader::eSTag: reader = new SP_XmlStartTagReader(); break;
+			case SP_XmlReader::eETag: reader = new SP_XmlEndTagReader(); break;
+			case SP_XmlReader::ePCData: reader = new SP_XmlPCDataReader(); break;
+			case SP_XmlReader::eCDataSection: reader = new SP_XmlCDataSectionReader(); break;
+			case SP_XmlReader::eComment: reader = new SP_XmlCommentReader(); break;
+			case SP_XmlReader::eDocType: reader = new SP_XmlDocTypeReader(); break;
+			case SP_XmlReader::eLBracket: reader = new SP_XmlLeftBracketReader(); break;
+			case SP_XmlReader::eSign: reader = new SP_XmlSignReader(); break;
+			}
+			mReaderList[ type ] = reader;
+		}
+	}
+
+	//printf( "\nborrow change: %s\n", typeid( *reader ).name() );
+
+	return reader;
+}
+
+void SP_XmlReaderPool :: save( SP_XmlReader * reader )
+{
+	//printf( "\nreturn change: %s\n", typeid( *reader ).name() );
+	reader->reset();
+}
+
+//=========================================================
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/spxml/spxmlreader.hpp	Mon Aug 20 13:27:17 2012 +0000
@@ -0,0 +1,153 @@
+/*
+ * Copyright 2007 Stephen Liu
+ * For license terms, see the file COPYING along with this library.
+ */
+
+#ifndef __spxmlreader_hpp__
+#define __spxmlreader_hpp__
+
+class SP_XmlPullParser;
+class SP_XmlPullEvent;
+class SP_XmlStringBuffer;
+
+class SP_XmlReader {
+public:
+	enum { MAX_READER = 16 };
+	enum { ePI, eDocType, eSTag, eETag, ePCData,
+		eCDataSection, eComment, eLBracket, eSign };
+
+	/**
+	 * @param  parser : act as reader's context
+	 * @param  c : a char in xml stream
+	 */
+	virtual void read( SP_XmlPullParser * parser, char c ) = 0;
+
+	/**
+	 * reset reader state
+	 */
+	virtual void reset();
+
+	/**
+	 * convert internal xml string to event
+	 * @return NULL : this reader don't generate any event or error occured
+	 */
+	virtual SP_XmlPullEvent * getEvent( SP_XmlPullParser * parser ) = 0;
+
+protected:
+	SP_XmlStringBuffer * mBuffer;
+
+	friend class SP_XmlReaderPool;
+
+	SP_XmlReader();
+	virtual ~SP_XmlReader();
+
+	/// help to call parser->changeReader
+	void changeReader( SP_XmlPullParser * parser, SP_XmlReader * reader );
+
+	/// help to call parser->getReader
+	SP_XmlReader * getReader( SP_XmlPullParser * parser, int type );
+
+	/// help to call parser->setError
+	static void setError( SP_XmlPullParser * parser, const char * error );
+
+private:
+	SP_XmlReader( SP_XmlReader & );
+	SP_XmlReader & operator=( SP_XmlReader & );
+};
+
+class SP_XmlPIReader : public SP_XmlReader {
+public:
+	SP_XmlPIReader();
+	virtual ~SP_XmlPIReader();
+	virtual void read( SP_XmlPullParser * parser, char c );
+	virtual SP_XmlPullEvent * getEvent( SP_XmlPullParser * parser );
+
+private:
+	static SP_XmlPullEvent * parseDocDeclEvent( SP_XmlPullParser * parser,
+			SP_XmlStringBuffer * buffer );
+};
+
+class SP_XmlStartTagReader : public SP_XmlReader {
+public:
+	SP_XmlStartTagReader();
+	virtual ~SP_XmlStartTagReader();
+	virtual void read( SP_XmlPullParser * parser, char c );
+	virtual SP_XmlPullEvent * getEvent( SP_XmlPullParser * parser );
+	virtual void reset();
+
+private:
+	int mIsQuot;
+};
+
+class SP_XmlEndTagReader : public SP_XmlReader {
+public:
+	SP_XmlEndTagReader();
+	virtual ~SP_XmlEndTagReader();
+	virtual void read( SP_XmlPullParser * parser, char c );
+	virtual SP_XmlPullEvent * getEvent( SP_XmlPullParser * parser );
+};
+
+class SP_XmlPCDataReader : public SP_XmlReader {
+public:
+	SP_XmlPCDataReader();
+	virtual ~SP_XmlPCDataReader();
+	virtual void read( SP_XmlPullParser * parser, char c );
+	virtual SP_XmlPullEvent * getEvent( SP_XmlPullParser * parser );
+};
+
+class SP_XmlCDataSectionReader : public SP_XmlReader {
+public:
+	SP_XmlCDataSectionReader();
+	virtual ~SP_XmlCDataSectionReader();
+	virtual void read( SP_XmlPullParser * parser, char c );
+	virtual SP_XmlPullEvent * getEvent( SP_XmlPullParser * parser );
+};
+
+class SP_XmlCommentReader : public SP_XmlReader {
+public:
+	SP_XmlCommentReader();
+	virtual ~SP_XmlCommentReader();
+	virtual void read( SP_XmlPullParser * parser, char c );
+	virtual SP_XmlPullEvent * getEvent( SP_XmlPullParser * parser );
+};
+
+class SP_XmlDocTypeReader : public SP_XmlReader {
+public:
+	SP_XmlDocTypeReader();
+	virtual ~SP_XmlDocTypeReader();
+	virtual void read( SP_XmlPullParser * parser, char c );
+	virtual SP_XmlPullEvent * getEvent( SP_XmlPullParser * parser );
+};
+
+class SP_XmlLeftBracketReader : public SP_XmlReader {
+public:
+	SP_XmlLeftBracketReader();
+	virtual ~SP_XmlLeftBracketReader();
+	virtual void read( SP_XmlPullParser * parser, char c );
+	virtual SP_XmlPullEvent * getEvent( SP_XmlPullParser * parser );
+	virtual void reset();
+
+private:
+	int mHasReadBracket;
+};
+
+class SP_XmlSignReader : public SP_XmlReader {
+public:
+	SP_XmlSignReader();
+	virtual ~SP_XmlSignReader();
+	virtual void read( SP_XmlPullParser * parser, char c );
+	virtual SP_XmlPullEvent * getEvent( SP_XmlPullParser * parser );
+};
+
+class SP_XmlReaderPool {
+public:
+	SP_XmlReaderPool();
+	~SP_XmlReaderPool();
+	SP_XmlReader * borrow( int type );
+	void save( SP_XmlReader * reader );
+
+private:
+	SP_XmlReader ** mReaderList;
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/spxml/spxmlstag.cpp	Mon Aug 20 13:27:17 2012 +0000
@@ -0,0 +1,251 @@
+/*
+ * Copyright 2007 Stephen Liu
+ * For license terms, see the file COPYING along with this library.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <typeinfo>
+#include "strdup.h"
+
+#include "spxmlstag.hpp"
+#include "spxmlutils.hpp"
+#include "spxmlevent.hpp"
+#include "spxmlcodec.hpp"
+
+SP_XmlSTagParser :: SP_XmlSTagParser( const char * encoding )
+{
+    mEvent = new SP_XmlStartTagEvent();
+    mReader = new SP_XmlSTagNameReader();
+    mStartTagName = new SP_XmlStringBuffer();
+    mError = NULL;
+
+    snprintf( mEncoding, sizeof( mEncoding ), "%s", encoding );
+}
+
+SP_XmlSTagParser :: ~SP_XmlSTagParser()
+{
+    if( NULL != mEvent ) delete mEvent;
+    mEvent = NULL;
+
+    if( NULL != mReader ) delete mReader;
+    mReader = NULL;
+
+    if( NULL != mStartTagName ) delete mStartTagName;
+    mStartTagName = NULL;
+
+    if( NULL != mError ) free( mError );
+    mError = NULL;
+}
+
+const char * SP_XmlSTagParser :: getEncoding()
+{
+    return mEncoding;
+}
+
+SP_XmlStartTagEvent * SP_XmlSTagParser :: takeEvent()
+{
+    SP_XmlStartTagEvent * event = mEvent;
+
+    mEvent = NULL;
+
+    return event;
+}
+
+const char * SP_XmlSTagParser :: getError()
+{
+    return mError;
+}
+
+void SP_XmlSTagParser :: changeReader( SP_XmlSTagReader * reader )
+{
+    delete mReader;
+    mReader = reader;
+}
+
+void SP_XmlSTagParser :: setError( const char * error )
+{
+    if( NULL != error ) {
+        if( NULL != mError ) free( mError );
+        mError = strdup( error );
+    }
+}
+
+void SP_XmlSTagParser :: append( const char * source, int len )
+{
+    for( int i = 0; i < len && NULL == mError; i++ ) {
+        mReader->read( this, source[ i ] );
+    }
+}
+
+//=========================================================
+
+SP_XmlSTagReader :: SP_XmlSTagReader()
+{
+    mBuffer = new SP_XmlStringBuffer();
+}
+
+SP_XmlSTagReader :: ~SP_XmlSTagReader()
+{
+    delete mBuffer;
+    mBuffer = NULL;
+}
+
+void SP_XmlSTagReader :: changeReader( SP_XmlSTagParser * parser,
+        SP_XmlSTagReader * reader )
+{
+    //printf( "\nchange: %s\n", typeid( *reader ).name() );
+    parser->changeReader( reader );
+}
+
+void SP_XmlSTagReader :: setError( SP_XmlSTagParser * parser, const char * error )
+{
+    parser->setError( error );
+}
+
+void SP_XmlSTagReader :: setName( SP_XmlSTagParser * parser, const char * name )
+{
+    parser->mEvent->setName( name );
+}
+
+void SP_XmlSTagReader :: addAttrName( SP_XmlSTagParser * parser, const char * name )
+{
+    parser->mStartTagName->append( name );
+}
+
+void SP_XmlSTagReader :: addAttrValue( SP_XmlSTagParser * parser, const char * value )
+{
+    SP_XmlStringBuffer decodeValue;
+    SP_XmlStringCodec::decode( parser->getEncoding(), value, &decodeValue );
+
+    parser->mEvent->addAttr( parser->mStartTagName->getBuffer(), decodeValue.getBuffer() );
+    parser->mStartTagName->clean();
+}
+
+//=========================================================
+
+SP_XmlSTagNameReader :: SP_XmlSTagNameReader()
+{
+}
+
+SP_XmlSTagNameReader :: ~SP_XmlSTagNameReader()
+{
+}
+
+void SP_XmlSTagNameReader :: read( SP_XmlSTagParser * parser, char c )
+{
+    if( isspace( c ) ) {
+        if( 0 == mBuffer->getSize() ) {
+            //leading space, skip
+        } else {
+            setName( parser, mBuffer->getBuffer() );
+            changeReader( parser, new SP_XmlSTagAttrNameReader() );
+        }
+    } else {
+        mBuffer->append( c );
+    }
+}
+
+//=========================================================
+
+SP_XmlSTagAttrNameReader :: SP_XmlSTagAttrNameReader()
+{
+    mWait4Quot = 0;
+}
+
+SP_XmlSTagAttrNameReader :: ~SP_XmlSTagAttrNameReader()
+{
+}
+
+void SP_XmlSTagAttrNameReader :: read( SP_XmlSTagParser * parser, char c )
+{
+    if( 1 == mWait4Quot ) {
+        if( '"' == c ) {
+            addAttrName( parser, mBuffer->getBuffer() );
+            changeReader( parser, new SP_XmlSTagEqualMarkReader() );
+        } else {
+            mBuffer->append( c );
+        }
+    } else {
+        if( isspace( c ) ) {
+            if( 0 == mBuffer->getSize() ) {
+                //leading space, skip
+            } else {
+                addAttrName( parser, mBuffer->getBuffer() );
+                changeReader( parser, new SP_XmlSTagEqualMarkReader() );
+            }
+        } else {
+            if( '"' == c && 0 == mBuffer->getSize() ) {
+                mWait4Quot = 1;
+            } else if( '=' == c ) {
+                addAttrName( parser, mBuffer->getBuffer() );
+                SP_XmlSTagReader * reader = new SP_XmlSTagEqualMarkReader();
+                changeReader( parser, reader );
+                reader->read( parser, c );
+            } else {
+                mBuffer->append( c );
+            }
+        }
+    }
+}
+
+//=========================================================
+
+SP_XmlSTagEqualMarkReader :: SP_XmlSTagEqualMarkReader()
+{
+}
+
+SP_XmlSTagEqualMarkReader :: ~SP_XmlSTagEqualMarkReader()
+{
+}
+
+void SP_XmlSTagEqualMarkReader :: read( SP_XmlSTagParser * parser, char c )
+{
+    if( isspace( c ) ) {
+        //skip
+    } else if( '=' == c ) {
+        changeReader( parser, new SP_XmlSTagAttrValueReader() );
+    } else {
+        addAttrValue( parser, "" );
+        SP_XmlSTagReader * reader = new SP_XmlSTagAttrNameReader();
+        changeReader( parser, reader );
+        reader->read( parser, c );
+
+        //setError( parser, "miss '=' between name & value" );
+    }
+}
+
+//=========================================================
+
+SP_XmlSTagAttrValueReader :: SP_XmlSTagAttrValueReader()
+{
+    mHasReadQuot = 0;
+}
+
+SP_XmlSTagAttrValueReader :: ~SP_XmlSTagAttrValueReader()
+{
+}
+
+void SP_XmlSTagAttrValueReader :: read( SP_XmlSTagParser * parser, char c )
+{
+    if( 0 == mHasReadQuot ) {
+        if( isspace( c ) ) {
+            //skip  
+        } else if( '"' == c ) {
+            mHasReadQuot = 1;
+        } else if( '\'' == c ) {
+            mHasReadQuot = 2;
+        } else {
+            setError( parser, "unknown attribute value start" );
+        }
+    } else {
+        if( ( 1 == mHasReadQuot && '"' == c ) 
+                || ( 2 == mHasReadQuot && '\'' == c ) ) {
+            addAttrValue( parser, mBuffer->getBuffer() );
+            changeReader( parser, new SP_XmlSTagAttrNameReader() );
+        } else {
+            mBuffer->append( c );
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/spxml/spxmlstag.hpp	Mon Aug 20 13:27:17 2012 +0000
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2007 Stephen Liu
+ * For license terms, see the file COPYING along with this library.
+ */
+
+#ifndef __spxmlstag_hpp__
+#define __spxmlstag_hpp__
+
+class SP_XmlSTagReader;
+class SP_XmlStartTagEvent;
+class SP_XmlStringBuffer;
+
+class SP_XmlSTagParser {
+public:
+	SP_XmlSTagParser( const char * encoding );
+	~SP_XmlSTagParser();
+
+	void append( const char * source, int len );
+
+	SP_XmlStartTagEvent * takeEvent();
+	const char * getError();
+
+	const char * getEncoding();
+
+protected:
+	void changeReader( SP_XmlSTagReader * reader );
+	void setError( const char * error );
+
+	SP_XmlStartTagEvent * mEvent;
+
+	SP_XmlStringBuffer * mStartTagName;
+
+	friend class SP_XmlSTagReader;
+
+private:
+	SP_XmlSTagReader * mReader;
+	char * mError;
+	char mEncoding[ 32 ];
+};
+
+class SP_XmlSTagReader {
+public:
+	SP_XmlSTagReader();
+	virtual ~SP_XmlSTagReader();
+	virtual void read( SP_XmlSTagParser * parser, char c ) = 0;
+
+protected:
+
+	/// help to call parser->xxx
+	void changeReader( SP_XmlSTagParser * parser, SP_XmlSTagReader * reader );
+	void setError( SP_XmlSTagParser * parser, const char * error );
+
+	/// help to call parser->mEvent->xxx
+	void setName( SP_XmlSTagParser * parser, const char * name );
+	void addAttrName( SP_XmlSTagParser * parser, const char * name );
+	void addAttrValue( SP_XmlSTagParser * parser, const char * value );
+
+	SP_XmlStringBuffer * mBuffer;
+};
+
+class SP_XmlSTagNameReader : public SP_XmlSTagReader {
+public:
+	SP_XmlSTagNameReader();
+	virtual ~SP_XmlSTagNameReader();
+	virtual void read( SP_XmlSTagParser * parser, char c );
+};
+
+class SP_XmlSTagAttrNameReader : public SP_XmlSTagReader {
+public:
+	SP_XmlSTagAttrNameReader();
+	virtual ~SP_XmlSTagAttrNameReader();
+	virtual void read( SP_XmlSTagParser * parser, char c );
+
+private:
+	int mWait4Quot;
+};
+
+class SP_XmlSTagEqualMarkReader : public SP_XmlSTagReader {
+public:
+	SP_XmlSTagEqualMarkReader();
+	virtual ~SP_XmlSTagEqualMarkReader();
+	virtual void read( SP_XmlSTagParser * parser, char c );
+};
+
+class SP_XmlSTagAttrValueReader : public SP_XmlSTagReader {
+public:
+	SP_XmlSTagAttrValueReader();
+	virtual ~SP_XmlSTagAttrValueReader();
+	virtual void read( SP_XmlSTagParser * parser, char c );
+
+private:
+	int mHasReadQuot;
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/spxml/spxmlutils.cpp	Mon Aug 20 13:27:17 2012 +0000
@@ -0,0 +1,246 @@
+/*
+ * Copyright 2007 Stephen Liu
+ * For license terms, see the file COPYING along with this library.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <stdarg.h>
+#include <ctype.h>
+
+#include "spxmlutils.hpp"
+
+//=========================================================
+
+const int SP_XmlArrayList::LAST_INDEX = -1;
+
+SP_XmlArrayList :: SP_XmlArrayList( int initCount )
+{
+	mMaxCount = initCount <= 0 ? 2 : initCount;
+	mCount = 0;
+	mFirst = (void**)malloc( sizeof( void * ) * mMaxCount );
+}
+
+SP_XmlArrayList :: ~SP_XmlArrayList()
+{
+	free( mFirst );
+	mFirst = NULL;
+}
+
+int SP_XmlArrayList :: getCount() const
+{
+	return mCount;
+}
+
+int SP_XmlArrayList :: append( void * value )
+{
+	if( NULL == value ) return -1;
+
+	if( mCount >= mMaxCount ) {
+		mMaxCount = ( mMaxCount * 3 ) / 2 + 1;
+		mFirst = (void**)realloc( mFirst, sizeof( void * ) * mMaxCount );
+		assert( NULL != mFirst );
+		memset( mFirst + mCount, 0, ( mMaxCount - mCount ) * sizeof( void * ) );
+	}
+
+	mFirst[ mCount++ ] = value;
+
+	return 0;
+}
+
+void * SP_XmlArrayList :: takeItem( int index )
+{
+	void * ret = NULL;
+
+	if( LAST_INDEX == index ) index = mCount -1;
+	if( index < 0 || index >= mCount ) return ret;
+
+	ret = mFirst[ index ];
+
+	mCount--;
+
+	if( ( index + 1 ) < mMaxCount ) {
+		memmove( mFirst + index, mFirst + index + 1,
+			( mMaxCount - index - 1 ) * sizeof( void * ) );
+	} else {
+		mFirst[ index ] = NULL;
+	}
+
+	return ret;
+}
+
+const void * SP_XmlArrayList :: getItem( int index ) const
+{
+	const void * ret = NULL;
+
+	if( LAST_INDEX == index ) index = mCount - 1;
+	if( index < 0 || index >= mCount ) return ret;
+
+	ret = mFirst[ index ];
+
+	return ret;
+}
+
+void SP_XmlArrayList :: sort( int ( * cmpFunc )( const void *, const void * ) )
+{
+	for( int i = 0; i < mCount - 1; i++ ) {
+		int min = i;
+		for( int j = i + 1; j < mCount; j++ ) {
+			if( cmpFunc( mFirst[ min ], mFirst[ j ] ) > 0 ) {
+				min = j;
+			}
+		}
+
+		if( min != i ) {
+			void * temp = mFirst[ i ];
+			mFirst[ i ] = mFirst[ min ];
+			mFirst[ min ] = temp;
+		}
+	}
+}
+
+//=========================================================
+
+SP_XmlQueue :: SP_XmlQueue()
+{
+	mMaxCount = 8;
+	mEntries = (void**)malloc( sizeof( void * ) * mMaxCount );
+
+	mHead = mTail = mCount = 0;
+}
+
+SP_XmlQueue :: ~SP_XmlQueue()
+{
+	free( mEntries );
+	mEntries = NULL;
+}
+
+void SP_XmlQueue :: push( void * item )
+{
+	if( mCount >= mMaxCount ) {
+		mMaxCount = ( mMaxCount * 3 ) / 2 + 1;
+		void ** newEntries = (void**)malloc( sizeof( void * ) * mMaxCount );
+
+		unsigned int headLen = 0, tailLen = 0;
+		if( mHead < mTail ) {
+			headLen = mTail - mHead;
+		} else {
+			headLen = mCount - mTail;
+			tailLen = mTail;
+		}
+
+		memcpy( newEntries, &( mEntries[ mHead ] ), sizeof( void * ) * headLen );
+		if( tailLen ) {
+			memcpy( &( newEntries[ headLen ] ), mEntries, sizeof( void * ) * tailLen );
+		}
+
+		mHead = 0;
+		mTail = headLen + tailLen;
+
+		free( mEntries );
+		mEntries = newEntries;
+	}
+
+	mEntries[ mTail++ ] = item;
+	mTail = mTail % mMaxCount;
+	mCount++;
+}
+
+void * SP_XmlQueue :: pop()
+{
+	void * ret = NULL;
+
+	if( mCount > 0 ) {
+		ret = mEntries[ mHead++ ];
+		mHead = mHead % mMaxCount;
+		mCount--;
+	}
+
+	return ret;
+}
+
+void * SP_XmlQueue :: top()
+{
+	return mCount > 0 ? mEntries[ mHead ] : NULL;
+}
+
+//=========================================================
+
+SP_XmlStringBuffer :: SP_XmlStringBuffer()
+{
+	init();
+}
+
+void SP_XmlStringBuffer :: init()
+{
+	mSize = 0;
+	mMaxSize = 8;
+	mBuffer = (char*)malloc( mMaxSize );
+	memset( mBuffer, 0, mMaxSize );
+}
+
+SP_XmlStringBuffer :: ~SP_XmlStringBuffer()
+{
+	free( mBuffer );
+}
+
+int SP_XmlStringBuffer :: append( char c )
+{
+	if( mSize >= ( mMaxSize - 1 ) ) {
+		mMaxSize += ( mMaxSize * 3 ) / 2 + 1;
+		mBuffer = (char*)realloc( mBuffer, mMaxSize );
+		assert( NULL != mBuffer );
+		memset( mBuffer + mSize, 0, mMaxSize - mSize );
+	}
+	mBuffer[ mSize++ ] = c;
+
+	return 0;
+}
+
+int SP_XmlStringBuffer :: append( const char * value, int size )
+{
+	if( NULL == value ) return -1;
+
+	size = ( size <= 0 ? strlen( value ) : size );
+	if( size <= 0 ) return -1;
+
+	if( ( size + mSize ) > ( mMaxSize - 1 ) ) {
+		mMaxSize += size;
+		mBuffer = (char*)realloc( mBuffer, mMaxSize );
+		assert( NULL != mBuffer );
+		memset( mBuffer + mSize, 0, mMaxSize - mSize );
+	}
+
+	memcpy( mBuffer + mSize, value, size );
+	mSize += size;
+
+	return 0;
+}
+
+int SP_XmlStringBuffer :: getSize() const
+{
+	return mSize;
+}
+
+const char * SP_XmlStringBuffer :: getBuffer() const
+{
+	return mBuffer;
+}
+
+char * SP_XmlStringBuffer :: takeBuffer()
+{
+	char * ret = mBuffer;
+
+	mBuffer = NULL;
+	init();
+
+	return ret;
+}
+
+void SP_XmlStringBuffer :: clean()
+{
+	memset( mBuffer, 0, mMaxSize );
+	mSize = 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/spxml/spxmlutils.hpp	Mon Aug 20 13:27:17 2012 +0000
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2007 Stephen Liu
+ * For license terms, see the file COPYING along with this library.
+ */
+
+#ifndef __spxmlutils_hpp__
+#define __spxmlutils_hpp__
+
+#include <stdio.h>
+
+typedef struct tagSP_XmlArrayListNode SP_XmlArrayListNode_t;
+
+class SP_XmlArrayList {
+public:
+	static const int LAST_INDEX;
+
+	SP_XmlArrayList( int initCount = 2 );
+	virtual ~SP_XmlArrayList();
+
+	int getCount() const;
+	int append( void * value );
+	const void * getItem( int index ) const;
+	void * takeItem( int index );
+	void sort( int ( * cmpFunc )( const void *, const void * ) );
+
+private:
+	SP_XmlArrayList( SP_XmlArrayList & );
+	SP_XmlArrayList & operator=( SP_XmlArrayList & );
+
+	int mMaxCount;
+	int mCount;
+	void ** mFirst;
+};
+
+class SP_XmlQueue {
+public:
+	SP_XmlQueue();
+	virtual ~SP_XmlQueue();
+
+	void push( void * item );
+	void * pop();
+	void * top();
+
+private:
+	void ** mEntries;
+	unsigned int mHead;
+	unsigned int mTail;
+	unsigned int mCount;
+	unsigned int mMaxCount;
+};
+
+class SP_XmlStringBuffer {
+public:
+	SP_XmlStringBuffer();
+	virtual ~SP_XmlStringBuffer();
+	int append( char c );
+	int append( const char * value, int size = 0 );
+	int getSize() const;
+	const char * getBuffer() const;
+	char * takeBuffer();
+	void clean();
+
+private:
+	SP_XmlStringBuffer( SP_XmlStringBuffer & );
+	SP_XmlStringBuffer & operator=( SP_XmlStringBuffer & );
+
+	void init();
+
+	char * mBuffer;
+	int mMaxSize;
+	int mSize;	
+};
+
+#ifdef WIN32
+
+#define snprintf _snprintf
+#define strncasecmp strnicmp
+#define strcasecmp  stricmp
+#endif
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/spxml/strdup.cpp	Mon Aug 20 13:27:17 2012 +0000
@@ -0,0 +1,11 @@
+#include "strdup.h"
+#include <stdlib.h>
+#include <string.h>
+
+char *strdup(const char *s)
+{
+    char* dup = (char*)malloc(strlen(s) + 1);
+    if(dup != NULL)
+        strcpy(dup, s);
+    return dup; 
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/spxml/strdup.h	Mon Aug 20 13:27:17 2012 +0000
@@ -0,0 +1,6 @@
+#ifndef __strdup_h__
+#define __strdup_h__
+
+char *strdup(const char *s);
+
+#endif
\ No newline at end of file