Mbed port of the Simple Plain Xml parser. See http://code.google.com/p/spxml/ for more details. This library uses less memory and is much better suited to streaming data than TinyXML (doesn\'t use as much C++ features, and especially works without streams). See http://mbed.org/users/hlipka/notebook/xml-parsing/ for usage examples.

Dependents:   spxmltest_weather VFD_fontx2_weather weather_LCD_display News_LCD_display ... more

spdomparser.cpp

Committer:
hlipka
Date:
2010-11-24
Revision:
0:3fa97f2c0505

File content as of revision 0:3fa97f2c0505:

/*
 * Copyright 2007 Stephen Liu
 * LGPL, see http://code.google.com/p/spxml/
 * 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 );
    }
}