VirtualBox

Changeset 21077 in vbox for trunk/src/VBox/Main/xml/xml.cpp


Ignore:
Timestamp:
Jun 30, 2009 3:19:12 PM (16 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
49335
Message:

back out r49329, r49331, will start over

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Main/xml/xml.cpp

    r21073 r21077  
     1/** @file
     2 * VirtualBox XML Manipulation API.
     3 */
     4
     5/*
     6 * Copyright (C) 2007-2009 Sun Microsystems, Inc.
     7 *
     8 * This file is part of VirtualBox Open Source Edition (OSE), as
     9 * available from http://www.215389.xyz. This file is free software;
     10 * you can redistribute it and/or modify it under the terms of the GNU
     11 * General Public License (GPL) as published by the Free Software
     12 * Foundation, in version 2 as it comes in the "COPYING" file of the
     13 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
     14 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
     15 *
     16 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
     17 * Clara, CA 95054 USA or visit http://www.sun.com if you need
     18 * additional information or have any questions.
     19 */
     20
     21#include "Logging.h"
     22
     23#include <iprt/cdefs.h>
     24#include <iprt/err.h>
     25#include <iprt/file.h>
     26#include <iprt/lock.h>
     27#include <iprt/string.h>
     28
     29#include <libxml/tree.h>
     30#include <libxml/parser.h>
     31#include <libxml/globals.h>
     32#include <libxml/xmlIO.h>
     33#include <libxml/xmlsave.h>
     34#include <libxml/uri.h>
     35
     36#include <libxml/xmlschemas.h>
     37
     38#include <list>
     39#include <map>
     40#include <boost/shared_ptr.hpp>
     41
     42#include "VBox/xml.h"
     43
     44////////////////////////////////////////////////////////////////////////////////
     45//
     46// globals
     47//
     48////////////////////////////////////////////////////////////////////////////////
     49
     50/**
     51 * Global module initialization structure. This is to wrap non-reentrant bits
     52 * of libxml, among other things.
     53 *
     54 * The constructor and destructor of this structure are used to perform global
     55 * module initiaizaton and cleanup. Thee must be only one global variable of
     56 * this structure.
     57 */
     58static
     59class Global
     60{
     61public:
     62
     63    Global()
     64    {
     65        /* Check the parser version. The docs say it will kill the app if
     66         * there is a serious version mismatch, but I couldn't find it in the
     67         * source code (it only prints the error/warning message to the console) so
     68         * let's leave it as is for informational purposes. */
     69        LIBXML_TEST_VERSION
     70
     71        /* Init libxml */
     72        xmlInitParser();
     73
     74        /* Save the default entity resolver before someone has replaced it */
     75        sxml.defaultEntityLoader = xmlGetExternalEntityLoader();
     76    }
     77
     78    ~Global()
     79    {
     80        /* Shutdown libxml */
     81        xmlCleanupParser();
     82    }
     83
     84    struct
     85    {
     86        xmlExternalEntityLoader defaultEntityLoader;
     87
     88        /** Used to provide some thread safety missing in libxml2 (see e.g.
     89         *  XmlTreeBackend::read()) */
     90        RTLockMtx lock;
     91    }
     92    sxml;  /* XXX naming this xml will break with gcc-3.3 */
     93}
     94gGlobal;
     95
     96
     97
     98namespace xml
     99{
     100
     101////////////////////////////////////////////////////////////////////////////////
     102//
     103// Exceptions
     104//
     105////////////////////////////////////////////////////////////////////////////////
     106
     107LogicError::LogicError(RT_SRC_POS_DECL)
     108    : Error(NULL)
     109{
     110    char *msg = NULL;
     111    RTStrAPrintf(&msg, "In '%s', '%s' at #%d",
     112                 pszFunction, pszFile, iLine);
     113    setWhat(msg);
     114    RTStrFree(msg);
     115}
     116
     117XmlError::XmlError(xmlErrorPtr aErr)
     118{
     119    if (!aErr)
     120        throw EInvalidArg(RT_SRC_POS);
     121
     122    char *msg = Format(aErr);
     123    setWhat(msg);
     124    RTStrFree(msg);
     125}
     126
     127/**
     128 * Composes a single message for the given error. The caller must free the
     129 * returned string using RTStrFree() when no more necessary.
     130 */
     131// static
     132char *XmlError::Format(xmlErrorPtr aErr)
     133{
     134    const char *msg = aErr->message ? aErr->message : "<none>";
     135    size_t msgLen = strlen(msg);
     136    /* strip spaces, trailing EOLs and dot-like char */
     137    while (msgLen && strchr(" \n.?!", msg [msgLen - 1]))
     138        --msgLen;
     139
     140    char *finalMsg = NULL;
     141    RTStrAPrintf(&finalMsg, "%.*s.\nLocation: '%s', line %d (%d), column %d",
     142                 msgLen, msg, aErr->file, aErr->line, aErr->int1, aErr->int2);
     143
     144    return finalMsg;
     145}
     146
     147EIPRTFailure::EIPRTFailure(int aRC)
     148    : RuntimeError(NULL),
     149      mRC(aRC)
     150{
     151    char *newMsg = NULL;
     152    RTStrAPrintf(&newMsg, "Runtime error: %d (%s)", aRC, RTErrGetShort(aRC));
     153    setWhat(newMsg);
     154    RTStrFree(newMsg);
     155}
     156
     157////////////////////////////////////////////////////////////////////////////////
     158//
     159// File Class
     160//
     161//////////////////////////////////////////////////////////////////////////////
     162
     163struct File::Data
     164{
     165    Data()
     166        : fileName (NULL), handle (NIL_RTFILE), opened (false) {}
     167
     168    char *fileName;
     169    RTFILE handle;
     170    bool opened : 1;
     171};
     172
     173File::File(Mode aMode, const char *aFileName)
     174    : m (new Data())
     175{
     176    m->fileName = RTStrDup (aFileName);
     177    if (m->fileName == NULL)
     178        throw ENoMemory();
     179
     180    unsigned flags = 0;
     181    switch (aMode)
     182    {
     183        case Mode_Read:
     184            flags = RTFILE_O_READ;
     185            break;
     186        case Mode_WriteCreate:      // fail if file exists
     187            flags = RTFILE_O_WRITE | RTFILE_O_CREATE;
     188            break;
     189        case Mode_Overwrite:        // overwrite if file exists
     190            flags = RTFILE_O_WRITE | RTFILE_O_CREATE_REPLACE;
     191            break;
     192        case Mode_ReadWrite:
     193            flags = RTFILE_O_READ | RTFILE_O_WRITE;
     194    }
     195
     196    int vrc = RTFileOpen (&m->handle, aFileName, flags);
     197    if (RT_FAILURE (vrc))
     198        throw EIPRTFailure (vrc);
     199
     200    m->opened = true;
     201}
     202
     203File::File (RTFILE aHandle, const char *aFileName /* = NULL */)
     204    : m (new Data())
     205{
     206    if (aHandle == NIL_RTFILE)
     207        throw EInvalidArg (RT_SRC_POS);
     208
     209    m->handle = aHandle;
     210
     211    if (aFileName)
     212    {
     213        m->fileName = RTStrDup (aFileName);
     214        if (m->fileName == NULL)
     215            throw ENoMemory();
     216    }
     217
     218    setPos (0);
     219}
     220
     221File::~File()
     222{
     223    if (m->opened)
     224        RTFileClose (m->handle);
     225
     226    RTStrFree (m->fileName);
     227}
     228
     229const char *File::uri() const
     230{
     231    return m->fileName;
     232}
     233
     234uint64_t File::pos() const
     235{
     236    uint64_t p = 0;
     237    int vrc = RTFileSeek (m->handle, 0, RTFILE_SEEK_CURRENT, &p);
     238    if (RT_SUCCESS (vrc))
     239        return p;
     240
     241    throw EIPRTFailure (vrc);
     242}
     243
     244void File::setPos (uint64_t aPos)
     245{
     246    uint64_t p = 0;
     247    unsigned method = RTFILE_SEEK_BEGIN;
     248    int vrc = VINF_SUCCESS;
     249
     250    /* check if we overflow int64_t and move to INT64_MAX first */
     251    if (((int64_t) aPos) < 0)
     252    {
     253        vrc = RTFileSeek (m->handle, INT64_MAX, method, &p);
     254        aPos -= (uint64_t) INT64_MAX;
     255        method = RTFILE_SEEK_CURRENT;
     256    }
     257    /* seek the rest */
     258    if (RT_SUCCESS (vrc))
     259        vrc = RTFileSeek (m->handle, (int64_t) aPos, method, &p);
     260    if (RT_SUCCESS (vrc))
     261        return;
     262
     263    throw EIPRTFailure (vrc);
     264}
     265
     266int File::read (char *aBuf, int aLen)
     267{
     268    size_t len = aLen;
     269    int vrc = RTFileRead (m->handle, aBuf, len, &len);
     270    if (RT_SUCCESS (vrc))
     271        return (int)len;
     272
     273    throw EIPRTFailure (vrc);
     274}
     275
     276int File::write (const char *aBuf, int aLen)
     277{
     278    size_t len = aLen;
     279    int vrc = RTFileWrite (m->handle, aBuf, len, &len);
     280    if (RT_SUCCESS (vrc))
     281        return (int)len;
     282
     283    throw EIPRTFailure (vrc);
     284
     285    return -1 /* failure */;
     286}
     287
     288void File::truncate()
     289{
     290    int vrc = RTFileSetSize (m->handle, pos());
     291    if (RT_SUCCESS (vrc))
     292        return;
     293
     294    throw EIPRTFailure (vrc);
     295}
     296
     297////////////////////////////////////////////////////////////////////////////////
     298//
     299// MemoryBuf Class
     300//
     301//////////////////////////////////////////////////////////////////////////////
     302
     303struct MemoryBuf::Data
     304{
     305    Data()
     306        : buf (NULL), len (0), uri (NULL), pos (0) {}
     307
     308    const char *buf;
     309    size_t len;
     310    char *uri;
     311
     312    size_t pos;
     313};
     314
     315MemoryBuf::MemoryBuf (const char *aBuf, size_t aLen, const char *aURI /* = NULL */)
     316    : m (new Data())
     317{
     318    if (aBuf == NULL)
     319        throw EInvalidArg (RT_SRC_POS);
     320
     321    m->buf = aBuf;
     322    m->len = aLen;
     323    m->uri = RTStrDup (aURI);
     324}
     325
     326MemoryBuf::~MemoryBuf()
     327{
     328    RTStrFree (m->uri);
     329}
     330
     331const char *MemoryBuf::uri() const
     332{
     333    return m->uri;
     334}
     335
     336uint64_t MemoryBuf::pos() const
     337{
     338    return m->pos;
     339}
     340
     341void MemoryBuf::setPos (uint64_t aPos)
     342{
     343    size_t pos = (size_t) aPos;
     344    if ((uint64_t) pos != aPos)
     345        throw EInvalidArg();
     346
     347    if (pos > m->len)
     348        throw EInvalidArg();
     349
     350    m->pos = pos;
     351}
     352
     353int MemoryBuf::read (char *aBuf, int aLen)
     354{
     355    if (m->pos >= m->len)
     356        return 0 /* nothing to read */;
     357
     358    size_t len = m->pos + aLen < m->len ? aLen : m->len - m->pos;
     359    memcpy (aBuf, m->buf + m->pos, len);
     360    m->pos += len;
     361
     362    return (int)len;
     363}
     364
     365////////////////////////////////////////////////////////////////////////////////
     366//
     367// GlobalLock class
     368//
     369////////////////////////////////////////////////////////////////////////////////
     370
     371struct GlobalLock::Data
     372{
     373    PFNEXTERNALENTITYLOADER pOldLoader;
     374    RTLock lock;
     375
     376    Data()
     377        : pOldLoader(NULL),
     378          lock(gGlobal.sxml.lock)
     379    {
     380    }
     381};
     382
     383GlobalLock::GlobalLock()
     384    : m(new Data())
     385{
     386}
     387
     388GlobalLock::~GlobalLock()
     389{
     390    if (m->pOldLoader)
     391        xmlSetExternalEntityLoader(m->pOldLoader);
     392    delete m;
     393    m = NULL;
     394}
     395
     396void GlobalLock::setExternalEntityLoader(PFNEXTERNALENTITYLOADER pLoader)
     397{
     398    m->pOldLoader = xmlGetExternalEntityLoader();
     399    xmlSetExternalEntityLoader(pLoader);
     400}
     401
     402// static
     403xmlParserInput* GlobalLock::callDefaultLoader(const char *aURI,
     404                                              const char *aID,
     405                                              xmlParserCtxt *aCtxt)
     406{
     407    return gGlobal.sxml.defaultEntityLoader(aURI, aID, aCtxt);
     408}
     409
     410////////////////////////////////////////////////////////////////////////////////
     411//
     412// Node class
     413//
     414////////////////////////////////////////////////////////////////////////////////
     415
     416struct Node::Data
     417{
     418    xmlNode     *plibNode;          // != NULL if this is an element or content node
     419    xmlAttr     *plibAttr;          // != NULL if this is an attribute node
     420
     421    Node        *pParent;           // NULL only for the root element
     422    const char  *pcszName;          // element or attribute name, points either into plibNode or plibAttr;
     423                                    // NULL if this is a content node
     424
     425    struct compare_const_char
     426    {
     427        bool operator()(const char* s1, const char* s2) const
     428        {
     429            return strcmp(s1, s2) < 0;
     430        }
     431    };
     432
     433    // attributes, if this is an element; can be empty
     434    typedef std::map<const char*, boost::shared_ptr<AttributeNode>, compare_const_char > AttributesMap;
     435    AttributesMap attribs;
     436
     437    // child elements, if this is an element; can be empty
     438    typedef std::list< boost::shared_ptr<Node> > InternalNodesList;
     439    InternalNodesList children;
     440};
     441
     442Node::Node(EnumType type)
     443    : mType(type),
     444      m(new Data)
     445{
     446    m->plibNode = NULL;
     447    m->plibAttr = NULL;
     448    m->pParent = NULL;
     449}
     450
     451Node::~Node()
     452{
     453    delete m;
     454}
     455
     456void Node::buildChildren()       // private
     457{
     458    // go thru this element's attributes
     459    xmlAttr *plibAttr = m->plibNode->properties;
     460    while (plibAttr)
     461    {
     462        const char *pcszAttribName = (const char*)plibAttr->name;
     463        boost::shared_ptr<AttributeNode> pNew(new AttributeNode);
     464        pNew->m->plibAttr = plibAttr;
     465        pNew->m->pcszName = (const char*)plibAttr->name;
     466        pNew->m->pParent = this;
     467        // store
     468        m->attribs[pcszAttribName] = pNew;
     469
     470        plibAttr = plibAttr->next;
     471    }
     472
     473    // go thru this element's child elements
     474    xmlNodePtr plibNode = m->plibNode->children;
     475    while (plibNode)
     476    {
     477        boost::shared_ptr<Node> pNew;
     478
     479        if (plibNode->name)
     480            pNew = boost::shared_ptr<Node>(new ElementNode);
     481        else
     482            pNew = boost::shared_ptr<Node>(new ContentNode);
     483
     484        pNew->m->plibNode = plibNode;
     485        pNew->m->pcszName = (const char*)plibNode->name;
     486        pNew->m->pParent = this;
     487        // store
     488        m->children.push_back(pNew);
     489
     490        // recurse for this child element to get its own children
     491        pNew->buildChildren();
     492
     493        plibNode = plibNode->next;
     494    }
     495}
     496
     497const char* Node::getName() const
     498{
     499    return m->pcszName;
     500}
     501
     502/**
     503 * Returns the value of a node. If this node is an attribute, returns
     504 * the attribute value; if this node is an element, then this returns
     505 * the element text content.
     506 * @return
     507 */
     508const char* Node::getValue() const
     509{
     510    if (    (m->plibAttr)
     511         && (m->plibAttr->children)
     512       )
     513        // libxml hides attribute values in another node created as a
     514        // single child of the attribute node, and it's in the content field
     515        return (const char*)m->plibAttr->children->content;
     516
     517    if (    (m->plibNode)
     518         && (m->plibNode->children)
     519       )
     520        return (const char*)m->plibNode->children->content;
     521
     522    return NULL;
     523}
     524
     525/**
     526 * Copies the value of a node into the given integer variable.
     527 * Returns TRUE only if a value was found and was actually an
     528 * integer of the given type.
     529 * @return
     530 */
     531bool Node::copyValue(int32_t &i) const
     532{
     533    const char *pcsz;
     534    if (    ((pcsz = getValue()))
     535         && (VINF_SUCCESS == RTStrToInt32Ex(pcsz, NULL, 10, &i))
     536       )
     537        return true;
     538
     539    return false;
     540}
     541
     542/**
     543 * Copies the value of a node into the given integer variable.
     544 * Returns TRUE only if a value was found and was actually an
     545 * integer of the given type.
     546 * @return
     547 */
     548bool Node::copyValue(uint32_t &i) const
     549{
     550    const char *pcsz;
     551    if (    ((pcsz = getValue()))
     552         && (VINF_SUCCESS == RTStrToUInt32Ex(pcsz, NULL, 10, &i))
     553       )
     554        return true;
     555
     556    return false;
     557}
     558
     559/**
     560 * Copies the value of a node into the given integer variable.
     561 * Returns TRUE only if a value was found and was actually an
     562 * integer of the given type.
     563 * @return
     564 */
     565bool Node::copyValue(int64_t &i) const
     566{
     567    const char *pcsz;
     568    if (    ((pcsz = getValue()))
     569         && (VINF_SUCCESS == RTStrToInt64Ex(pcsz, NULL, 10, &i))
     570       )
     571        return true;
     572
     573    return false;
     574}
     575
     576/**
     577 * Copies the value of a node into the given integer variable.
     578 * Returns TRUE only if a value was found and was actually an
     579 * integer of the given type.
     580 * @return
     581 */
     582bool Node::copyValue(uint64_t &i) const
     583{
     584    const char *pcsz;
     585    if (    ((pcsz = getValue()))
     586         && (VINF_SUCCESS == RTStrToUInt64Ex(pcsz, NULL, 10, &i))
     587       )
     588        return true;
     589
     590    return false;
     591}
     592
     593/**
     594 * Returns the line number of the current node in the source XML file.
     595 * Useful for error messages.
     596 * @return
     597 */
     598int Node::getLineNumber() const
     599{
     600    if (m->plibAttr)
     601        return m->pParent->m->plibNode->line;
     602
     603    return m->plibNode->line;
     604}
     605
     606ElementNode::ElementNode()
     607    : Node(IsElement)
     608{
     609}
     610
     611/**
     612 * Builds a list of direct child elements of the current element that
     613 * match the given string; if pcszMatch is NULL, all direct child
     614 * elements are returned.
     615 * @param children out: list of nodes to which children will be appended.
     616 * @param pcszMatch in: match string, or NULL to return all children.
     617 * @return Number of items appended to the list (0 if none).
     618 */
     619int ElementNode::getChildElements(ElementNodesList &children,
     620                                  const char *pcszMatch /*= NULL*/)
     621    const
     622{
     623    int i = 0;
     624    Data::InternalNodesList::const_iterator
     625        it,
     626        last = m->children.end();
     627    for (it = m->children.begin();
     628         it != last;
     629         ++it)
     630    {
     631        // export this child node if ...
     632        if (    (!pcszMatch)    // the caller wants all nodes or
     633             || (!strcmp(pcszMatch, (**it).getName())) // the element name matches
     634           )
     635        {
     636            Node *pNode = (*it).get();
     637            if (pNode->isElement())
     638                children.push_back(static_cast<ElementNode*>(pNode));
     639            ++i;
     640        }
     641    }
     642    return i;
     643}
     644
     645/**
     646 * Returns the first child element whose name matches pcszMatch.
     647 * @param pcszMatch
     648 * @return
     649 */
     650const ElementNode* ElementNode::findChildElement(const char *pcszMatch)
     651    const
     652{
     653    Data::InternalNodesList::const_iterator
     654        it,
     655        last = m->children.end();
     656    for (it = m->children.begin();
     657         it != last;
     658         ++it)
     659    {
     660        if ((**it).isElement())
     661        {
     662            ElementNode *pelm = static_cast<ElementNode*>((*it).get());
     663            if (!strcmp(pcszMatch, pelm->getName())) // the element name matches
     664                return pelm;
     665        }
     666    }
     667
     668    return NULL;
     669}
     670
     671/**
     672 * Returns the first child element whose "id" attribute matches pcszId.
     673 * @param pcszId identifier to look for.
     674 * @return child element or NULL if not found.
     675 */
     676const ElementNode* ElementNode::findChildElementFromId(const char *pcszId) const
     677{
     678    Data::InternalNodesList::const_iterator
     679        it,
     680        last = m->children.end();
     681    for (it = m->children.begin();
     682         it != last;
     683         ++it)
     684    {
     685        if ((**it).isElement())
     686        {
     687            ElementNode *pelm = static_cast<ElementNode*>((*it).get());
     688            const AttributeNode *pAttr;
     689            if (    ((pAttr = pelm->findAttribute("id")))
     690                 && (!strcmp(pAttr->getValue(), pcszId))
     691               )
     692                return pelm;
     693        }
     694    }
     695
     696    return NULL;
     697}
     698
     699/**
     700 *
     701 * @param pcszMatch
     702 * @return
     703 */
     704const AttributeNode* ElementNode::findAttribute(const char *pcszMatch) const
     705{
     706    Data::AttributesMap::const_iterator it;
     707
     708    it = m->attribs.find(pcszMatch);
     709    if (it != m->attribs.end())
     710        return it->second.get();
     711
     712    return NULL;
     713}
     714
     715/**
     716 * Convenience method which attempts to find the attribute with the given
     717 * name and returns its value as a string.
     718 *
     719 * @param pcszMatch name of attribute to find.
     720 * @param str out: attribute value
     721 * @return TRUE if attribute was found and str was thus updated.
     722 */
     723bool ElementNode::getAttributeValue(const char *pcszMatch, const char *&ppcsz) const
     724{
     725    const Node* pAttr;
     726    if ((pAttr = findAttribute(pcszMatch)))
     727    {
     728        ppcsz = pAttr->getValue();
     729        return true;
     730    }
     731
     732    return false;
     733}
     734
     735/**
     736 * Convenience method which attempts to find the attribute with the given
     737 * name and returns its value as a signed long integer. This calls
     738 * RTStrToInt64Ex internally and will only output the integer if that
     739 * function returns no error.
     740 *
     741 * @param pcszMatch name of attribute to find.
     742 * @param i out: attribute value
     743 * @return TRUE if attribute was found and str was thus updated.
     744 */
     745bool ElementNode::getAttributeValue(const char *pcszMatch, int64_t &i) const
     746{
     747    const char *pcsz;
     748    if (    (getAttributeValue(pcszMatch, pcsz))
     749         && (VINF_SUCCESS == RTStrToInt64Ex(pcsz, NULL, 10, &i))
     750       )
     751        return true;
     752
     753    return false;
     754}
     755
     756/**
     757 * Convenience method which attempts to find the attribute with the given
     758 * name and returns its value as an unsigned long integer.This calls
     759 * RTStrToUInt64Ex internally and will only output the integer if that
     760 * function returns no error.
     761 *
     762 * @param pcszMatch name of attribute to find.
     763 * @param i out: attribute value
     764 * @return TRUE if attribute was found and str was thus updated.
     765 */
     766bool ElementNode::getAttributeValue(const char *pcszMatch, uint64_t &i) const
     767{
     768    const char *pcsz;
     769    if (    (getAttributeValue(pcszMatch, pcsz))
     770         && (VINF_SUCCESS == RTStrToUInt64Ex(pcsz, NULL, 10, &i))
     771       )
     772        return true;
     773
     774    return false;
     775}
     776
     777/**
     778 * Creates a new child element node and appends it to the list
     779 * of children in "this".
     780 *
     781 * @param pcszElementName
     782 * @return
     783 */
     784ElementNode* ElementNode::createChild(const char *pcszElementName)
     785{
     786    // we must be an element, not an attribute
     787    if (!m->plibNode)
     788        throw ENodeIsNotElement(RT_SRC_POS);
     789
     790    // libxml side: create new node
     791    xmlNode *plibNode;
     792    if (!(plibNode = xmlNewNode(NULL,        // namespace
     793                                (const xmlChar*)pcszElementName)))
     794        throw ENoMemory();
     795    xmlAddChild(m->plibNode, plibNode);
     796
     797    // now wrap this in C++
     798    ElementNode *p = new ElementNode;
     799    boost::shared_ptr<ElementNode> pNew(p);
     800    pNew->m->plibNode = plibNode;
     801    pNew->m->pcszName = (const char*)plibNode->name;
     802
     803    m->children.push_back(pNew);
     804
     805    return p;
     806}
     807
     808
     809/**
     810 * Creates a content node and appends it to the list of children
     811 * in "this".
     812 *
     813 * @param pcszElementName
     814 * @return
     815 */
     816ContentNode* ElementNode::addContent(const char *pcszContent)
     817{
     818    // libxml side: create new node
     819    xmlNode *plibNode;
     820    if (!(plibNode = xmlNewText((const xmlChar*)pcszContent)))
     821        throw ENoMemory();
     822    xmlAddChild(m->plibNode, plibNode);
     823
     824    // now wrap this in C++
     825    ContentNode *p = new ContentNode;
     826    boost::shared_ptr<ContentNode> pNew(p);
     827    pNew->m->plibNode = plibNode;
     828    pNew->m->pcszName = NULL;
     829
     830    m->children.push_back(pNew);
     831
     832    return p;
     833}
     834
     835/**
     836 * Sets the given attribute.
     837 *
     838 * If an attribute with the given name exists, it is overwritten,
     839 * otherwise a new attribute is created. Returns the attribute node
     840 * that was either created or changed.
     841 *
     842 * @param pcszName
     843 * @param pcszValue
     844 * @return
     845 */
     846AttributeNode* ElementNode::setAttribute(const char *pcszName, const char *pcszValue)
     847{
     848    Data::AttributesMap::const_iterator it;
     849
     850    it = m->attribs.find(pcszName);
     851    if (it == m->attribs.end())
     852    {
     853        // libxml side: xmlNewProp creates an attribute
     854        xmlAttr *plibAttr = xmlNewProp(m->plibNode, (xmlChar*)pcszName, (xmlChar*)pcszValue);
     855        const char *pcszAttribName = (const char*)plibAttr->name;
     856
     857        // C++ side: create an attribute node around it
     858        boost::shared_ptr<AttributeNode> pNew(new AttributeNode);
     859        pNew->m->plibAttr = plibAttr;
     860        pNew->m->pcszName = (const char*)plibAttr->name;
     861        pNew->m->pParent = this;
     862        // store
     863        m->attribs[pcszAttribName] = pNew;
     864    }
     865    else
     866    {
     867        // @todo
     868        throw LogicError("Attribute exists");
     869    }
     870
     871    return NULL;
     872
     873}
     874
     875
     876AttributeNode::AttributeNode()
     877    : Node(IsAttribute)
     878{
     879}
     880
     881ContentNode::ContentNode()
     882    : Node(IsContent)
     883{
     884}
     885
     886/*
     887 * NodesLoop
     888 *
     889 */
     890
     891struct NodesLoop::Data
     892{
     893    ElementNodesList listElements;
     894    ElementNodesList::const_iterator it;
     895};
     896
     897NodesLoop::NodesLoop(const ElementNode &node, const char *pcszMatch /* = NULL */)
     898{
     899    m = new Data;
     900    node.getChildElements(m->listElements, pcszMatch);
     901    m->it = m->listElements.begin();
     902}
     903
     904NodesLoop::~NodesLoop()
     905{
     906    delete m;
     907}
     908
     909
     910/**
     911 * Handy convenience helper for looping over all child elements. Create an
     912 * instance of NodesLoop on the stack and call this method until it returns
     913 * NULL, like this:
     914 * <code>
     915 *      xml::Node node;         // should point to an element
     916 *      xml::NodesLoop loop(node, "child");  // find all "child" elements under node
     917 *      const xml::Node *pChild = NULL;
     918 *      while (pChild = loop.forAllNodes())
     919 *          ...;
     920 * </code>
     921 * @param node
     922 * @param pcszMatch
     923 * @return
     924 */
     925const ElementNode* NodesLoop::forAllNodes() const
     926{
     927    const ElementNode *pNode = NULL;
     928
     929    if (m->it != m->listElements.end())
     930    {
     931        pNode = *(m->it);
     932        ++(m->it);
     933    }
     934
     935    return pNode;
     936}
     937
     938////////////////////////////////////////////////////////////////////////////////
     939//
     940// Document class
     941//
     942////////////////////////////////////////////////////////////////////////////////
     943
     944struct Document::Data
     945{
     946    xmlDocPtr   plibDocument;
     947    ElementNode *pRootElement;
     948
     949    Data()
     950    {
     951        plibDocument = NULL;
     952        pRootElement = NULL;
     953    }
     954
     955    ~Data()
     956    {
     957        reset();
     958    }
     959
     960    void reset()
     961    {
     962        if (plibDocument)
     963        {
     964            xmlFreeDoc(plibDocument);
     965            plibDocument = NULL;
     966        }
     967        if (pRootElement)
     968        {
     969            delete pRootElement;
     970            pRootElement = NULL;
     971        }
     972    }
     973
     974    void copyFrom(const Document::Data *p)
     975    {
     976        if (p->plibDocument)
     977        {
     978            plibDocument = xmlCopyDoc(p->plibDocument,
     979                                      1);      // recursive == copy all
     980        }
     981    }
     982};
     983
     984Document::Document()
     985    : m(new Data)
     986{
     987}
     988
     989Document::Document(const Document &x)
     990    : m(new Data)
     991{
     992    m->copyFrom(x.m);
     993};
     994
     995Document& Document::operator=(const Document &x)
     996{
     997    m->reset();
     998    m->copyFrom(x.m);
     999    return *this;
     1000};
     1001
     1002Document::~Document()
     1003{
     1004    delete m;
     1005}
     1006
     1007/**
     1008 * private method to refresh all internal structures after the internal pDocument
     1009 * has changed. Called from XmlFileParser::read(). m->reset() must have been
     1010 * called before to make sure all members except the internal pDocument are clean.
     1011 */
     1012void Document::refreshInternals() // private
     1013{
     1014    m->pRootElement = new ElementNode();
     1015    m->pRootElement->m->plibNode = xmlDocGetRootElement(m->plibDocument);
     1016    m->pRootElement->m->pcszName = (const char*)m->pRootElement->m->plibNode->name;
     1017
     1018    m->pRootElement->buildChildren();
     1019}
     1020
     1021/**
     1022 * Returns the root element of the document, or NULL if the document is empty.
     1023 * @return
     1024 */
     1025const ElementNode* Document::getRootElement() const
     1026{
     1027    return m->pRootElement;
     1028}
     1029
     1030/**
     1031 * Creates a new element node and sets it as the root element. This will
     1032 * only work if the document is empty; otherwise EDocumentNotEmpty is thrown.
     1033 */
     1034ElementNode* Document::createRootElement(const char *pcszRootElementName)
     1035{
     1036    if (m->pRootElement || m->plibDocument)
     1037        throw EDocumentNotEmpty(RT_SRC_POS);
     1038
     1039    // libxml side: create document, create root node
     1040    m->plibDocument = xmlNewDoc((const xmlChar*)"1.0");
     1041    xmlNode *plibRootNode;
     1042    if (!(plibRootNode = xmlNewNode(NULL,        // namespace
     1043                                    (const xmlChar*)pcszRootElementName)))
     1044        throw ENoMemory();
     1045    xmlDocSetRootElement(m->plibDocument, plibRootNode);
     1046
     1047    // now wrap this in C++
     1048    m->pRootElement = new ElementNode();
     1049    m->pRootElement->m->plibNode = plibRootNode;
     1050    m->pRootElement->m->pcszName = (const char*)plibRootNode->name;
     1051
     1052    return m->pRootElement;
     1053}
     1054
     1055////////////////////////////////////////////////////////////////////////////////
     1056//
     1057// XmlParserBase class
     1058//
     1059////////////////////////////////////////////////////////////////////////////////
     1060
     1061XmlParserBase::XmlParserBase()
     1062{
     1063    m_ctxt = xmlNewParserCtxt();
     1064    if (m_ctxt == NULL)
     1065        throw ENoMemory();
     1066}
     1067
     1068XmlParserBase::~XmlParserBase()
     1069{
     1070    xmlFreeParserCtxt (m_ctxt);
     1071    m_ctxt = NULL;
     1072}
     1073
     1074////////////////////////////////////////////////////////////////////////////////
     1075//
     1076// XmlFileParser class
     1077//
     1078////////////////////////////////////////////////////////////////////////////////
     1079
     1080struct XmlFileParser::Data
     1081{
     1082    xmlParserCtxtPtr ctxt;
     1083    ministring strXmlFilename;
     1084
     1085    Data()
     1086    {
     1087        if (!(ctxt = xmlNewParserCtxt()))
     1088            throw xml::ENoMemory();
     1089    }
     1090
     1091    ~Data()
     1092    {
     1093        xmlFreeParserCtxt(ctxt);
     1094        ctxt = NULL;
     1095    }
     1096};
     1097
     1098XmlFileParser::XmlFileParser()
     1099    : XmlParserBase(),
     1100      m(new Data())
     1101{
     1102}
     1103
     1104XmlFileParser::~XmlFileParser()
     1105{
     1106    delete m;
     1107    m = NULL;
     1108}
     1109
     1110struct IOContext
     1111{
     1112    File file;
     1113    ministring error;
     1114
     1115    IOContext(const char *pcszFilename, File::Mode mode)
     1116        : file(mode, pcszFilename)
     1117    {
     1118    }
     1119
     1120    void setError(const xml::Error &x)
     1121    {
     1122        error = x.what();
     1123    }
     1124
     1125    void setError(const std::exception &x)
     1126    {
     1127        error = x.what();
     1128    }
     1129};
     1130
     1131struct ReadContext : IOContext
     1132{
     1133    ReadContext(const char *pcszFilename)
     1134        : IOContext(pcszFilename, File::Mode_Read)
     1135    {
     1136    }
     1137};
     1138
     1139struct WriteContext : IOContext
     1140{
     1141    WriteContext(const char *pcszFilename)
     1142        : IOContext(pcszFilename, File::Mode_Overwrite)
     1143    {
     1144    }
     1145};
     1146
     1147/**
     1148 * Reads the given file and fills the given Document object with its contents.
     1149 * Throws XmlError on parsing errors.
     1150 *
     1151 * The document that is passed in will be reset before being filled if not empty.
     1152 *
     1153 * @param pcszFilename in: name fo file to parse.
     1154 * @param doc out: document to be reset and filled with data according to file contents.
     1155 */
     1156void XmlFileParser::read(const char *pcszFilename,
     1157                         Document &doc)
     1158{
     1159    GlobalLock lock;
     1160//     global.setExternalEntityLoader(ExternalEntityLoader);
     1161
     1162    m->strXmlFilename = pcszFilename;
     1163
     1164    ReadContext context(pcszFilename);
     1165    doc.m->reset();
     1166    if (!(doc.m->plibDocument = xmlCtxtReadIO(m->ctxt,
     1167                                              ReadCallback,
     1168                                              CloseCallback,
     1169                                              &context,
     1170                                              pcszFilename,
     1171                                              NULL,       // encoding = auto
     1172                                              XML_PARSE_NOBLANKS)))
     1173        throw XmlError(xmlCtxtGetLastError(m->ctxt));
     1174
     1175    doc.refreshInternals();
     1176}
     1177
     1178// static
     1179int XmlFileParser::ReadCallback(void *aCtxt, char *aBuf, int aLen)
     1180{
     1181    ReadContext *pContext = static_cast<ReadContext*>(aCtxt);
     1182
     1183    /* To prevent throwing exceptions while inside libxml2 code, we catch
     1184     * them and forward to our level using a couple of variables. */
     1185
     1186    try
     1187    {
     1188        return pContext->file.read(aBuf, aLen);
     1189    }
     1190    catch (const xml::EIPRTFailure &err) { pContext->setError(err); }
     1191    catch (const xml::Error &err) { pContext->setError(err); }
     1192    catch (const std::exception &err) { pContext->setError(err); }
     1193    catch (...) { pContext->setError(xml::LogicError(RT_SRC_POS)); }
     1194
     1195    return -1 /* failure */;
     1196}
     1197
     1198int XmlFileParser::CloseCallback(void *aCtxt)
     1199{
     1200    /// @todo to be written
     1201
     1202    return -1;
     1203}
     1204
     1205////////////////////////////////////////////////////////////////////////////////
     1206//
     1207// XmlFileWriter class
     1208//
     1209////////////////////////////////////////////////////////////////////////////////
     1210
     1211struct XmlFileWriter::Data
     1212{
     1213    Document *pDoc;
     1214};
     1215
     1216XmlFileWriter::XmlFileWriter(Document &doc)
     1217{
     1218    m = new Data();
     1219    m->pDoc = &doc;
     1220}
     1221
     1222XmlFileWriter::~XmlFileWriter()
     1223{
     1224    delete m;
     1225}
     1226
     1227void XmlFileWriter::write(const char *pcszFilename)
     1228{
     1229    WriteContext context(pcszFilename);
     1230
     1231    GlobalLock lock;
     1232
     1233    /* serialize to the stream */
     1234    xmlIndentTreeOutput = 1;
     1235    xmlTreeIndentString = "  ";
     1236    xmlSaveNoEmptyTags = 0;
     1237
     1238    xmlSaveCtxtPtr saveCtxt;
     1239    if (!(saveCtxt = xmlSaveToIO(WriteCallback,
     1240                                 CloseCallback,
     1241                                 &context,
     1242                                 NULL,
     1243                                 XML_SAVE_FORMAT)))
     1244        throw xml::LogicError(RT_SRC_POS);
     1245
     1246    long rc = xmlSaveDoc(saveCtxt, m->pDoc->m->plibDocument);
     1247    if (rc == -1)
     1248    {
     1249        /* look if there was a forwared exception from the lower level */
     1250//         if (m->trappedErr.get() != NULL)
     1251//             m->trappedErr->rethrow();
     1252
     1253        /* there must be an exception from the Output implementation,
     1254         * otherwise the save operation must always succeed. */
     1255        throw xml::LogicError(RT_SRC_POS);
     1256    }
     1257
     1258    xmlSaveClose(saveCtxt);
     1259}
     1260
     1261int XmlFileWriter::WriteCallback(void *aCtxt, const char *aBuf, int aLen)
     1262{
     1263    WriteContext *pContext = static_cast<WriteContext*>(aCtxt);
     1264
     1265    /* To prevent throwing exceptions while inside libxml2 code, we catch
     1266     * them and forward to our level using a couple of variables. */
     1267    try
     1268    {
     1269        return pContext->file.write(aBuf, aLen);
     1270    }
     1271    catch (const xml::EIPRTFailure &err) { pContext->setError(err); }
     1272    catch (const xml::Error &err) { pContext->setError(err); }
     1273    catch (const std::exception &err) { pContext->setError(err); }
     1274    catch (...) { pContext->setError(xml::LogicError(RT_SRC_POS)); }
     1275
     1276    return -1 /* failure */;
     1277}
     1278
     1279int XmlFileWriter::CloseCallback(void *aCtxt)
     1280{
     1281    /// @todo to be written
     1282
     1283    return -1;
     1284}
     1285
     1286
     1287} // end namespace xml
     1288
     1289
Note: See TracChangeset for help on using the changeset viewer.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette