diff options
Diffstat (limited to 'src/VBox/Runtime/r3/xml.cpp')
-rw-r--r-- | src/VBox/Runtime/r3/xml.cpp | 975 |
1 files changed, 606 insertions, 369 deletions
diff --git a/src/VBox/Runtime/r3/xml.cpp b/src/VBox/Runtime/r3/xml.cpp index a753f34c..708e41fb 100644 --- a/src/VBox/Runtime/r3/xml.cpp +++ b/src/VBox/Runtime/r3/xml.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2007-2011 Oracle Corporation + * Copyright (C) 2007-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -24,6 +24,10 @@ * terms and conditions of either the GPL or the CDDL or both. */ + +/******************************************************************************* +* Header Files * +*******************************************************************************/ #include <iprt/dir.h> #include <iprt/file.h> #include <iprt/err.h> @@ -42,14 +46,11 @@ #include <libxml/xmlschemas.h> #include <map> -#include <boost/shared_ptr.hpp> -//////////////////////////////////////////////////////////////////////////////// -// -// globals -// -//////////////////////////////////////////////////////////////////////////////// +/******************************************************************************* +* Global Variables * +*******************************************************************************/ /** * Global module initialization structure. This is to wrap non-reentrant bits * of libxml, among other things. @@ -58,8 +59,7 @@ * module initialization and cleanup. There must be only one global variable of * this structure. */ -static -class Global +static class Global { public: @@ -93,8 +93,7 @@ public: RTCLockMtx lock; } sxml; /* XXX naming this xml will break with gcc-3.3 */ -} -gGlobal; +} gGlobal; @@ -131,8 +130,7 @@ XmlError::XmlError(xmlErrorPtr aErr) * Composes a single message for the given error. The caller must free the * returned string using RTStrFree() when no more necessary. */ -// static -char *XmlError::Format(xmlErrorPtr aErr) +/* static */ char *XmlError::Format(xmlErrorPtr aErr) { const char *msg = aErr->message ? aErr->message : "<none>"; size_t msgLen = strlen(msg); @@ -156,7 +154,7 @@ EIPRTFailure::EIPRTFailure(int aRC, const char *pcszContext, ...) va_start(args, pcszContext); RTStrAPrintfV(&pszContext2, pcszContext, args); char *newMsg; - RTStrAPrintf(&newMsg, "%s: %d (%s)", pszContext2, aRC, RTErrGetShort(aRC)); + RTStrAPrintf(&newMsg, "%s: %d(%s)", pszContext2, aRC, RTErrGetShort(aRC)); setWhat(newMsg); RTStrFree(newMsg); RTStrFree(pszContext2); @@ -241,7 +239,7 @@ File::~File() delete m; } -const char* File::uri() const +const char *File::uri() const { return m->strFileName.c_str(); } @@ -291,19 +289,17 @@ int File::read(char *aBuf, int aLen) int File::write(const char *aBuf, int aLen) { size_t len = aLen; - int vrc = RTFileWrite (m->handle, aBuf, len, &len); - if (RT_SUCCESS (vrc)) + int vrc = RTFileWrite(m->handle, aBuf, len, &len); + if (RT_SUCCESS(vrc)) return (int)len; throw EIPRTFailure(vrc, "Runtime error writing to file '%s'", m->strFileName.c_str()); - - return -1 /* failure */; } void File::truncate() { - int vrc = RTFileSetSize (m->handle, pos()); - if (RT_SUCCESS (vrc)) + int vrc = RTFileSetSize(m->handle, pos()); + if (RT_SUCCESS(vrc)) return; throw EIPRTFailure(vrc, "Runtime error truncating file '%s'", m->strFileName.c_str()); @@ -318,7 +314,7 @@ void File::truncate() struct MemoryBuf::Data { Data() - : buf (NULL), len (0), uri (NULL), pos (0) {} + : buf(NULL), len(0), uri(NULL), pos(0) {} const char *buf; size_t len; @@ -327,20 +323,20 @@ struct MemoryBuf::Data size_t pos; }; -MemoryBuf::MemoryBuf (const char *aBuf, size_t aLen, const char *aURI /* = NULL */) - : m (new Data()) +MemoryBuf::MemoryBuf(const char *aBuf, size_t aLen, const char *aURI /* = NULL */) + : m(new Data()) { if (aBuf == NULL) - throw EInvalidArg (RT_SRC_POS); + throw EInvalidArg(RT_SRC_POS); m->buf = aBuf; m->len = aLen; - m->uri = RTStrDup (aURI); + m->uri = RTStrDup(aURI); } MemoryBuf::~MemoryBuf() { - RTStrFree (m->uri); + RTStrFree(m->uri); } const char *MemoryBuf::uri() const @@ -353,9 +349,9 @@ uint64_t MemoryBuf::pos() const return m->pos; } -void MemoryBuf::setPos (uint64_t aPos) +void MemoryBuf::setPos(uint64_t aPos) { - size_t off = (size_t) aPos; + size_t off = (size_t)aPos; if ((uint64_t) off != aPos) throw EInvalidArg(); @@ -365,13 +361,13 @@ void MemoryBuf::setPos (uint64_t aPos) m->pos = off; } -int MemoryBuf::read (char *aBuf, int aLen) +int MemoryBuf::read(char *aBuf, int aLen) { if (m->pos >= m->len) return 0 /* nothing to read */; size_t len = m->pos + aLen < m->len ? aLen : m->len - m->pos; - memcpy (aBuf, m->buf + m->pos, len); + memcpy(aBuf, m->buf + m->pos, len); m->pos += len; return (int)len; @@ -422,90 +418,43 @@ xmlParserInput* GlobalLock::callDefaultLoader(const char *aURI, return gGlobal.sxml.defaultEntityLoader(aURI, aID, aCtxt); } + + //////////////////////////////////////////////////////////////////////////////// // // Node class // //////////////////////////////////////////////////////////////////////////////// -struct Node::Data -{ - struct compare_const_char - { - bool operator()(const char* s1, const char* s2) const - { - return strcmp(s1, s2) < 0; - } - }; - - // attributes, if this is an element; can be empty - typedef std::map<const char*, boost::shared_ptr<AttributeNode>, compare_const_char > AttributesMap; - AttributesMap attribs; - - // child elements, if this is an element; can be empty - typedef std::list< boost::shared_ptr<Node> > InternalNodesList; - InternalNodesList children; -}; - Node::Node(EnumType type, Node *pParent, - xmlNode *plibNode, - xmlAttr *plibAttr) - : m_Type(type), - m_pParent(pParent), - m_plibNode(plibNode), - m_plibAttr(plibAttr), - m_pcszNamespacePrefix(NULL), - m_pcszNamespaceHref(NULL), - m_pcszName(NULL), - m(new Data) + PRTLISTANCHOR pListAnchor, + xmlNode *pLibNode, + xmlAttr *pLibAttr) + : m_Type(type) + , m_pParent(pParent) + , m_pLibNode(pLibNode) + , m_pLibAttr(pLibAttr) + , m_pcszNamespacePrefix(NULL) + , m_pcszNamespaceHref(NULL) + , m_pcszName(NULL) + , m_pParentListAnchor(pListAnchor) { + RTListInit(&m_listEntry); } Node::~Node() { - delete m; } /** - * Private implementation. - * @param elmRoot + * Returns the name of the node, which is either the element name or + * the attribute name. For other node types it probably returns NULL. + * @return */ -void Node::buildChildren(const ElementNode &elmRoot) // private +const char *Node::getName() const { - // go thru this element's attributes - xmlAttr *plibAttr = m_plibNode->properties; - while (plibAttr) - { - const char *pcszKey; - boost::shared_ptr<AttributeNode> pNew(new AttributeNode(elmRoot, this, plibAttr, &pcszKey)); - // store - m->attribs[pcszKey] = pNew; - - plibAttr = plibAttr->next; - } - - // go thru this element's child elements - xmlNodePtr plibNode = m_plibNode->children; - while (plibNode) - { - boost::shared_ptr<Node> pNew; - - if (plibNode->type == XML_ELEMENT_NODE) - pNew = boost::shared_ptr<Node>(new ElementNode(&elmRoot, this, plibNode)); - else if (plibNode->type == XML_TEXT_NODE) - pNew = boost::shared_ptr<Node>(new ContentNode(this, plibNode)); - if (pNew) - { - // store - m->children.push_back(pNew); - - // recurse for this child element to get its own children - pNew->buildChildren(elmRoot); - } - - plibNode = plibNode->next; - } + return m_pcszName; } /** @@ -513,9 +462,19 @@ void Node::buildChildren(const ElementNode &elmRoot) // private * the attribute name. For other node types it probably returns NULL. * @return */ -const char* Node::getName() const +const char *Node::getPrefix() const { - return m_pcszName; + return m_pcszNamespacePrefix; +} + +/** + * Returns the XML namespace URI, which is the attribute name. For other node types it probably + * returns NULL. + * @return + */ +const char *Node::getNamespaceURI() const +{ + return m_pcszNamespaceHref; } /** @@ -524,7 +483,7 @@ const char* Node::getName() const * @param pcsz * @return */ -bool Node::nameEquals(const char *pcszNamespace, const char *pcsz) const +bool Node::nameEqualsNS(const char *pcszNamespace, const char *pcsz) const { if (m_pcszName == pcsz) return true; @@ -546,24 +505,52 @@ bool Node::nameEquals(const char *pcszNamespace, const char *pcsz) const } /** + * Variant of nameEquals that checks the namespace as well. + * + * @returns true if equal, false if not. + * @param pcsz The element name. + * @param cchMax The maximum number of character from @a pcsz to + * match. + * @param pcszNamespace The name space prefix or NULL (default). + */ +bool Node::nameEqualsN(const char *pcsz, size_t cchMax, const char *pcszNamespace /* = NULL*/) const +{ + /* Match the name. */ + if (!m_pcszName) + return false; + if (!pcsz || cchMax == 0) + return false; + if (strncmp(m_pcszName, pcsz, cchMax)) + return false; + if (strlen(m_pcszName) > cchMax) + return false; + + /* Match name space. */ + if (!pcszNamespace) + return true; /* NULL, anything goes. */ + if (!m_pcszNamespacePrefix) + return false; /* Element has no namespace. */ + return !strcmp(m_pcszNamespacePrefix, pcszNamespace); +} + +/** * Returns the value of a node. If this node is an attribute, returns * the attribute value; if this node is an element, then this returns * the element text content. * @return */ -const char* Node::getValue() const +const char *Node::getValue() const { - if ( (m_plibAttr) - && (m_plibAttr->children) - ) + if ( m_pLibAttr + && m_pLibAttr->children + ) // libxml hides attribute values in another node created as a // single child of the attribute node, and it's in the content field - return (const char*)m_plibAttr->children->content; + return (const char *)m_pLibAttr->children->content; - if ( (m_plibNode) - && (m_plibNode->children) - ) - return (const char*)m_plibNode->children->content; + if ( m_pLibNode + && m_pLibNode->children) + return (const char *)m_pLibNode->children->content; return NULL; } @@ -643,39 +630,132 @@ bool Node::copyValue(uint64_t &i) const */ int Node::getLineNumber() const { - if (m_plibAttr) - return m_pParent->m_plibNode->line; + if (m_pLibAttr) + return m_pParent->m_pLibNode->line; - return m_plibNode->line; + return m_pLibNode->line; } /** * Private element constructor. - * @param pelmRoot - * @param pParent - * @param plibNode + * + * @param pElmRoot Pointer to the root element. + * @param pParent Pointer to the parent element (always an ElementNode, + * despite the type). NULL for the root node. + * @param pListAnchor Pointer to the m_children member of the parent. NULL + * for the root node. + * @param pLibNode Pointer to the libxml2 node structure. */ -ElementNode::ElementNode(const ElementNode *pelmRoot, +ElementNode::ElementNode(const ElementNode *pElmRoot, Node *pParent, - xmlNode *plibNode) + PRTLISTANCHOR pListAnchor, + xmlNode *pLibNode) : Node(IsElement, pParent, - plibNode, + pListAnchor, + pLibNode, NULL) { - if (!(m_pelmRoot = pelmRoot)) - // NULL passed, then this is the root element - m_pelmRoot = this; + m_pElmRoot = pElmRoot ? pElmRoot : this; // If NULL is passed, then this is the root element. + m_pcszName = (const char *)pLibNode->name; - m_pcszName = (const char*)plibNode->name; + if (pLibNode->ns) + { + m_pcszNamespacePrefix = (const char *)m_pLibNode->ns->prefix; + m_pcszNamespaceHref = (const char *)m_pLibNode->ns->href; + } - if (plibNode->ns) + RTListInit(&m_children); + RTListInit(&m_attributes); +} + +ElementNode::~ElementNode() +{ + Node *pCur, *pNext; + RTListForEachSafeCpp(&m_children, pCur, pNext, Node, m_listEntry) { - m_pcszNamespacePrefix = (const char*)m_plibNode->ns->prefix; - m_pcszNamespaceHref = (const char*)m_plibNode->ns->href; + delete pCur; } + RTListInit(&m_children); + + RTListForEachSafeCpp(&m_attributes, pCur, pNext, Node, m_listEntry) + { + delete pCur; + } + RTListInit(&m_attributes); } + +/** + * Gets the next tree element in a full tree enumeration. + * + * @returns Pointer to the next element in the tree, NULL if we're done. + * @param pElmRoot The root of the tree we're enumerating. NULL if + * it's the entire tree. + */ +ElementNode const *ElementNode::getNextTreeElement(ElementNode const *pElmRoot /*= NULL */) const +{ + /* + * Consider children first. + */ + ElementNode const *pChild = getFirstChildElement(); + if (pChild) + return pChild; + + /* + * Then siblings, aunts and uncles. + */ + ElementNode const *pCur = this; + do + { + ElementNode const *pSibling = pCur->getNextSibilingElement(); + if (pSibling != NULL) + return pSibling; + + pCur = static_cast<const xml::ElementNode *>(pCur->m_pParent); + Assert(pCur || pCur == pElmRoot); + } while (pCur != pElmRoot); + + return NULL; +} + + +/** + * Private implementation. + * + * @param pElmRoot The root element. + */ +/*static*/ void ElementNode::buildChildren(ElementNode *pElmRoot) // protected +{ + for (ElementNode *pCur = pElmRoot; pCur; pCur = pCur->getNextTreeElement(pElmRoot)) + { + /* + * Go thru this element's attributes creating AttributeNodes for them. + */ + for (xmlAttr *pLibAttr = pCur->m_pLibNode->properties; pLibAttr; pLibAttr = pLibAttr->next) + { + AttributeNode *pNew = new AttributeNode(pElmRoot, pCur, &pCur->m_attributes, pLibAttr); + RTListAppend(&pCur->m_attributes, &pNew->m_listEntry); + } + + /* + * Go thru this element's child elements (element and text nodes). + */ + for (xmlNodePtr pLibNode = pCur->m_pLibNode->children; pLibNode; pLibNode = pLibNode->next) + { + Node *pNew; + if (pLibNode->type == XML_ELEMENT_NODE) + pNew = new ElementNode(pElmRoot, pCur, &pCur->m_children, pLibNode); + else if (pLibNode->type == XML_TEXT_NODE) + pNew = new ContentNode(pCur, &pCur->m_children, pLibNode); + else + continue; + RTListAppend(&pCur->m_children, &pNew->m_listEntry); + } + } +} + + /** * Builds a list of direct child elements of the current element that * match the given string; if pcszMatch is NULL, all direct child @@ -689,18 +769,16 @@ int ElementNode::getChildElements(ElementNodesList &children, const { int i = 0; - for (Data::InternalNodesList::iterator it = m->children.begin(); - it != m->children.end(); - ++it) + Node *p; + RTListForEachCpp(&m_children, p, Node, m_listEntry) { // export this child node if ... - Node *p = it->get(); if (p->isElement()) - if ( (!pcszMatch) // the caller wants all nodes or - || (!strcmp(pcszMatch, p->getName())) // the element name matches + if ( !pcszMatch // ... the caller wants all nodes or ... + || !strcmp(pcszMatch, p->getName()) // ... the element name matches. ) { - children.push_back(static_cast<ElementNode*>(p)); + children.push_back(static_cast<ElementNode *>(p)); ++i; } } @@ -714,25 +792,18 @@ int ElementNode::getChildElements(ElementNodesList &children, * @param pcszMatch Element name to match. * @return */ -const ElementNode* ElementNode::findChildElement(const char *pcszNamespace, - const char *pcszMatch) - const +const ElementNode *ElementNode::findChildElementNS(const char *pcszNamespace, const char *pcszMatch) const { - Data::InternalNodesList::const_iterator - it, - last = m->children.end(); - for (it = m->children.begin(); - it != last; - ++it) + Node *p; + RTListForEachCpp(&m_children, p, Node, m_listEntry) { - if ((**it).isElement()) + if (p->isElement()) { - ElementNode *pelm = static_cast<ElementNode*>((*it).get()); - if (pelm->nameEquals(pcszNamespace, pcszMatch)) + ElementNode *pelm = static_cast<ElementNode*>(p); + if (pelm->nameEqualsNS(pcszNamespace, pcszMatch)) return pelm; } } - return NULL; } @@ -741,58 +812,154 @@ const ElementNode* ElementNode::findChildElement(const char *pcszNamespace, * @param pcszId identifier to look for. * @return child element or NULL if not found. */ -const ElementNode* ElementNode::findChildElementFromId(const char *pcszId) const -{ - Data::InternalNodesList::const_iterator - it, - last = m->children.end(); - for (it = m->children.begin(); - it != last; - ++it) +const ElementNode *ElementNode::findChildElementFromId(const char *pcszId) const +{ + const Node *p; + RTListForEachCpp(&m_children, p, Node, m_listEntry) { - if ((**it).isElement()) + if (p->isElement()) { - ElementNode *pelm = static_cast<ElementNode*>((*it).get()); - const AttributeNode *pAttr; - if ( ((pAttr = pelm->findAttribute("id"))) - && (!strcmp(pAttr->getValue(), pcszId)) - ) - return pelm; + const ElementNode *pElm = static_cast<const ElementNode *>(p); + const AttributeNode *pAttr = pElm->findAttribute("id"); + if (pAttr && !strcmp(pAttr->getValue(), pcszId)) + return pElm; } } + return NULL; +} + +const ElementNode *ElementNode::findChildElementP(const char *pcszPath, const char *pcszNamespace /*= NULL*/) const +{ + size_t cchThis = strchr(pcszPath, '/') - pcszPath; + if (cchThis == (size_t)((const char *)0 - pcszPath)) + return findChildElementNS(pcszNamespace, pcszPath); + + /** @todo Can be done without recursion as we have both sibling lists and parent + * pointers in this variant. */ + const Node *p; + RTListForEachCpp(&m_children, p, Node, m_listEntry) + { + if (p->isElement()) + { + const ElementNode *pElm = static_cast<const ElementNode *>(p); + if (pElm->nameEqualsN(pcszPath, cchThis, pcszNamespace)) + { + pElm = findChildElementP(pcszPath + cchThis, pcszNamespace); + if (pElm) + return pElm; + } + } + } + + return NULL; +} + +const ElementNode *ElementNode::getFirstChildElement() const +{ + const Node *p; + RTListForEachCpp(&m_children, p, Node, m_listEntry) + { + if (p->isElement()) + return static_cast<const ElementNode *>(p); + } + return NULL; +} + +const ElementNode *ElementNode::getLastChildElement() const +{ + const Node *p; + RTListForEachReverseCpp(&m_children, p, Node, m_listEntry) + { + if (p->isElement()) + return static_cast<const ElementNode *>(p); + } return NULL; } +const ElementNode *ElementNode::getPrevSibilingElement() const +{ + if (!m_pParent) + return NULL; + const Node *pSibling = this; + for (;;) + { + pSibling = RTListGetPrevCpp(m_pParentListAnchor, pSibling, const Node, m_listEntry); + if (!pSibling) + return NULL; + if (pSibling->isElement()) + return static_cast<const ElementNode *>(pSibling); + } +} + +const ElementNode *ElementNode::getNextSibilingElement() const +{ + if (!m_pParent) + return NULL; + const Node *pSibling = this; + for (;;) + { + pSibling = RTListGetNextCpp(m_pParentListAnchor, pSibling, const Node, m_listEntry); + if (!pSibling) + return NULL; + if (pSibling->isElement()) + return static_cast<const ElementNode *>(pSibling); + } +} + +const ElementNode *ElementNode::findPrevSibilingElement(const char *pcszMatch, const char *pcszNamespace /*= NULL*/) const +{ + if (!m_pParent) + return NULL; + const Node *pSibling = this; + for (;;) + { + pSibling = RTListGetPrevCpp(m_pParentListAnchor, pSibling, const Node, m_listEntry); + if (!pSibling) + return NULL; + if (pSibling->isElement()) + { + const ElementNode *pElem = static_cast<const ElementNode *>(pSibling); + if (pElem->nameEqualsNS(pcszNamespace, pcszMatch)) + return pElem; + } + } +} + +const ElementNode *ElementNode::findNextSibilingElement(const char *pcszMatch, const char *pcszNamespace /*= NULL*/) const +{ + if (!m_pParent) + return NULL; + const Node *pSibling = this; + for (;;) + { + pSibling = RTListGetNextCpp(m_pParentListAnchor, pSibling, const Node, m_listEntry); + if (!pSibling) + return NULL; + if (pSibling->isElement()) + { + const ElementNode *pElem = static_cast<const ElementNode *>(pSibling); + if (pElem->nameEqualsNS(pcszNamespace, pcszMatch)) + return pElem; + } + } +} + + /** * Looks up the given attribute node in this element's attribute map. * - * With respect to namespaces, the internal attributes map stores namespace - * prefixes with attribute names only if the attribute uses a non-default - * namespace. As a result, the following rules apply: - * - * -- To find attributes from a non-default namespace, pcszMatch must not - * be prefixed with a namespace. - * - * -- To find attributes from the default namespace (or if the document does - * not use namespaces), pcszMatch must be prefixed with the namespace - * prefix and a colon. - * - * For example, if the document uses the "vbox:" namespace by default, you - * must omit "vbox:" from pcszMatch to find such attributes, whether they - * are specifed in the xml or not. - * - * @param pcszMatch - * @return + * @param pcszMatch The name of the attribute to find. + * @param pcszNamespace The attribute name space prefix or NULL. */ -const AttributeNode* ElementNode::findAttribute(const char *pcszMatch) const +const AttributeNode *ElementNode::findAttribute(const char *pcszMatch, const char *pcszNamespace /*= NULL*/) const { - Data::AttributesMap::const_iterator it; - - it = m->attribs.find(pcszMatch); - if (it != m->attribs.end()) - return it->second.get(); - + AttributeNode *p; + RTListForEachCpp(&m_attributes, p, AttributeNode, m_listEntry) + { + if (p->nameEqualsNS(pcszNamespace, pcszMatch)) + return p; + } return NULL; } @@ -800,19 +967,19 @@ const AttributeNode* ElementNode::findAttribute(const char *pcszMatch) const * Convenience method which attempts to find the attribute with the given * name and returns its value as a string. * - * @param pcszMatch name of attribute to find (see findAttribute() for namespace remarks) - * @param ppcsz out: attribute value - * @return TRUE if attribute was found and str was thus updated. + * @param pcszMatch Name of attribute to find. + * @param ppcsz Where to return the attribute. + * @param pcszNamespace The attribute name space prefix or NULL. + * @returns Boolean success indicator. */ -bool ElementNode::getAttributeValue(const char *pcszMatch, const char *&ppcsz) const +bool ElementNode::getAttributeValue(const char *pcszMatch, const char **ppcsz, const char *pcszNamespace /*= NULL*/) const { - const Node* pAttr; - if ((pAttr = findAttribute(pcszMatch))) + const AttributeNode *pAttr = findAttribute(pcszMatch, pcszNamespace); + if (pAttr) { - ppcsz = pAttr->getValue(); + *ppcsz = pAttr->getValue(); return true; } - return false; } @@ -820,16 +987,20 @@ bool ElementNode::getAttributeValue(const char *pcszMatch, const char *&ppcsz) c * Convenience method which attempts to find the attribute with the given * name and returns its value as a string. * - * @param pcszMatch name of attribute to find (see findAttribute() for namespace remarks) - * @param str out: attribute value; overwritten only if attribute was found - * @return TRUE if attribute was found and str was thus updated. + * @param pcszMatch Name of attribute to find. + * @param pStr Pointer to the string object that should receive the + * attribute value. + * @param pcszNamespace The attribute name space prefix or NULL. + * @returns Boolean success indicator. + * + * @throws Whatever the string class may throw on assignment. */ -bool ElementNode::getAttributeValue(const char *pcszMatch, RTCString &str) const +bool ElementNode::getAttributeValue(const char *pcszMatch, RTCString *pStr, const char *pcszNamespace /*= NULL*/) const { - const Node* pAttr; - if ((pAttr = findAttribute(pcszMatch))) + const AttributeNode *pAttr = findAttribute(pcszMatch, pcszNamespace); + if (pAttr) { - str = pAttr->getValue(); + *pStr = pAttr->getValue(); return true; } @@ -839,15 +1010,18 @@ bool ElementNode::getAttributeValue(const char *pcszMatch, RTCString &str) const /** * Like getAttributeValue (ministring variant), but makes sure that all backslashes * are converted to forward slashes. - * @param pcszMatch - * @param str - * @return + * + * @param pcszMatch Name of attribute to find. + * @param pStr Pointer to the string object that should + * receive the attribute path value. + * @param pcszNamespace The attribute name space prefix or NULL. + * @returns Boolean success indicator. */ -bool ElementNode::getAttributeValuePath(const char *pcszMatch, RTCString &str) const +bool ElementNode::getAttributeValuePath(const char *pcszMatch, RTCString *pStr, const char *pcszNamespace /*= NULL*/) const { - if (getAttributeValue(pcszMatch, str)) + if (getAttributeValue(pcszMatch, pStr, pcszNamespace)) { - str.findReplace('\\', '/'); + pStr->findReplace('\\', '/'); return true; } @@ -856,85 +1030,85 @@ bool ElementNode::getAttributeValuePath(const char *pcszMatch, RTCString &str) c /** * Convenience method which attempts to find the attribute with the given - * name and returns its value as a signed integer. This calls - * RTStrToInt32Ex internally and will only output the integer if that - * function returns no error. + * name and returns its value as a signed 32-bit integer. * - * @param pcszMatch name of attribute to find (see findAttribute() for namespace remarks) - * @param i out: attribute value; overwritten only if attribute was found - * @return TRUE if attribute was found and str was thus updated. + * @param pcszMatch Name of attribute to find. + * @param piValue Where to return the value. + * @param pcszNamespace The attribute name space prefix or NULL. + * @returns Boolean success indicator. */ -bool ElementNode::getAttributeValue(const char *pcszMatch, int32_t &i) const +bool ElementNode::getAttributeValue(const char *pcszMatch, int32_t *piValue, const char *pcszNamespace /*= NULL*/) const { - const char *pcsz; - if ( (getAttributeValue(pcszMatch, pcsz)) - && (VINF_SUCCESS == RTStrToInt32Ex(pcsz, NULL, 0, &i)) - ) - return true; - + const char *pcsz = findAttributeValue(pcszMatch, pcszNamespace); + if (pcsz) + { + int rc = RTStrToInt32Ex(pcsz, NULL, 0, piValue); + if (rc == VINF_SUCCESS) + return true; + } return false; } /** * Convenience method which attempts to find the attribute with the given - * name and returns its value as an unsigned integer.This calls - * RTStrToUInt32Ex internally and will only output the integer if that - * function returns no error. + * name and returns its value as an unsigned 32-bit integer. * - * @param pcszMatch name of attribute to find (see findAttribute() for namespace remarks) - * @param i out: attribute value; overwritten only if attribute was found - * @return TRUE if attribute was found and str was thus updated. + * @param pcszMatch Name of attribute to find. + * @param puValue Where to return the value. + * @param pcszNamespace The attribute name space prefix or NULL. + * @returns Boolean success indicator. */ -bool ElementNode::getAttributeValue(const char *pcszMatch, uint32_t &i) const +bool ElementNode::getAttributeValue(const char *pcszMatch, uint32_t *puValue, const char *pcszNamespace /*= NULL*/) const { - const char *pcsz; - if ( (getAttributeValue(pcszMatch, pcsz)) - && (VINF_SUCCESS == RTStrToUInt32Ex(pcsz, NULL, 0, &i)) - ) - return true; - + const char *pcsz = findAttributeValue(pcszMatch, pcszNamespace); + if (pcsz) + { + int rc = RTStrToUInt32Ex(pcsz, NULL, 0, puValue); + if (rc == VINF_SUCCESS) + return true; + } return false; } /** * Convenience method which attempts to find the attribute with the given - * name and returns its value as a signed long integer. This calls - * RTStrToInt64Ex internally and will only output the integer if that - * function returns no error. + * name and returns its value as a signed 64-bit integer. * - * @param pcszMatch name of attribute to find (see findAttribute() for namespace remarks) - * @param i out: attribute value - * @return TRUE if attribute was found and str was thus updated. + * @param pcszMatch Name of attribute to find. + * @param piValue Where to return the value. + * @param pcszNamespace The attribute name space prefix or NULL. + * @returns Boolean success indicator. */ -bool ElementNode::getAttributeValue(const char *pcszMatch, int64_t &i) const +bool ElementNode::getAttributeValue(const char *pcszMatch, int64_t *piValue, const char *pcszNamespace /*= NULL*/) const { - const char *pcsz; - if ( (getAttributeValue(pcszMatch, pcsz)) - && (VINF_SUCCESS == RTStrToInt64Ex(pcsz, NULL, 0, &i)) - ) - return true; - + const char *pcsz = findAttributeValue(pcszMatch, pcszNamespace); + if (pcsz) + { + int rc = RTStrToInt64Ex(pcsz, NULL, 0, piValue); + if (rc == VINF_SUCCESS) + return true; + } return false; } /** * Convenience method which attempts to find the attribute with the given - * name and returns its value as an unsigned long integer.This calls - * RTStrToUInt64Ex internally and will only output the integer if that - * function returns no error. + * name and returns its value as an unsigned 64-bit integer. * - * @param pcszMatch name of attribute to find (see findAttribute() for namespace remarks) - * @param i out: attribute value; overwritten only if attribute was found - * @return TRUE if attribute was found and str was thus updated. + * @param pcszMatch Name of attribute to find. + * @param puValue Where to return the value. + * @param pcszNamespace The attribute name space prefix or NULL. + * @returns Boolean success indicator. */ -bool ElementNode::getAttributeValue(const char *pcszMatch, uint64_t &i) const +bool ElementNode::getAttributeValue(const char *pcszMatch, uint64_t *puValue, const char *pcszNamespace /*= NULL*/) const { - const char *pcsz; - if ( (getAttributeValue(pcszMatch, pcsz)) - && (VINF_SUCCESS == RTStrToUInt64Ex(pcsz, NULL, 0, &i)) - ) - return true; - + const char *pcsz = findAttributeValue(pcszMatch, pcszNamespace); + if (pcsz) + { + int rc = RTStrToUInt64Ex(pcsz, NULL, 0, puValue); + if (rc == VINF_SUCCESS) + return true; + } return false; } @@ -943,29 +1117,30 @@ bool ElementNode::getAttributeValue(const char *pcszMatch, uint64_t &i) const * name and returns its value as a boolean. This accepts "true", "false", * "yes", "no", "1" or "0" as valid values. * - * @param pcszMatch name of attribute to find (see findAttribute() for namespace remarks) - * @param f out: attribute value; overwritten only if attribute was found - * @return TRUE if attribute was found and str was thus updated. + * @param pcszMatch Name of attribute to find. + * @param pfValue Where to return the value. + * @param pcszNamespace The attribute name space prefix or NULL. + * @returns Boolean success indicator. */ -bool ElementNode::getAttributeValue(const char *pcszMatch, bool &f) const +bool ElementNode::getAttributeValue(const char *pcszMatch, bool *pfValue, const char *pcszNamespace /*= NULL*/) const { - const char *pcsz; - if (getAttributeValue(pcszMatch, pcsz)) + const char *pcsz = findAttributeValue(pcszMatch, pcszNamespace); + if (pcsz) { - if ( (!strcmp(pcsz, "true")) - || (!strcmp(pcsz, "yes")) - || (!strcmp(pcsz, "1")) + if ( !strcmp(pcsz, "true") + || !strcmp(pcsz, "yes") + || !strcmp(pcsz, "1") ) { - f = true; + *pfValue = true; return true; } - if ( (!strcmp(pcsz, "false")) - || (!strcmp(pcsz, "no")) - || (!strcmp(pcsz, "0")) + if ( !strcmp(pcsz, "false") + || !strcmp(pcsz, "no") + || !strcmp(pcsz, "0") ) { - f = false; + *pfValue = false; return true; } } @@ -973,6 +1148,81 @@ bool ElementNode::getAttributeValue(const char *pcszMatch, bool &f) const return false; } + +bool ElementNode::getElementValue(int32_t *piValue) const +{ + const char *pszValue = getValue(); + if (pszValue) + { + int rc = RTStrToInt32Ex(pszValue, NULL, 0, piValue); + if (rc == VINF_SUCCESS) + return true; + } + return false; +} + +bool ElementNode::getElementValue(uint32_t *puValue) const +{ + const char *pszValue = getValue(); + if (pszValue) + { + int rc = RTStrToUInt32Ex(pszValue, NULL, 0, puValue); + if (rc == VINF_SUCCESS) + return true; + } + return false; +} + +bool ElementNode::getElementValue(int64_t *piValue) const +{ + const char *pszValue = getValue(); + if (pszValue) + { + int rc = RTStrToInt64Ex(pszValue, NULL, 0, piValue); + if (rc == VINF_SUCCESS) + return true; + } + return false; +} + +bool ElementNode::getElementValue(uint64_t *puValue) const +{ + const char *pszValue = getValue(); + if (pszValue) + { + int rc = RTStrToUInt64Ex(pszValue, NULL, 0, puValue); + if (rc == VINF_SUCCESS) + return true; + } + return false; +} + +bool ElementNode::getElementValue(bool *pfValue) const +{ + const char *pszValue = getValue(); + if (pszValue) + { + if ( !strcmp(pszValue, "true") + || !strcmp(pszValue, "yes") + || !strcmp(pszValue, "1") + ) + { + *pfValue = true; + return true; + } + if ( !strcmp(pszValue, "false") + || !strcmp(pszValue, "no") + || !strcmp(pszValue, "0") + ) + { + *pfValue = true; + return true; + } + } + return false; +} + + /** * Creates a new child element node and appends it to the list * of children in "this". @@ -980,23 +1230,22 @@ bool ElementNode::getAttributeValue(const char *pcszMatch, bool &f) const * @param pcszElementName * @return */ -ElementNode* ElementNode::createChild(const char *pcszElementName) +ElementNode *ElementNode::createChild(const char *pcszElementName) { // we must be an element, not an attribute - if (!m_plibNode) + if (!m_pLibNode) throw ENodeIsNotElement(RT_SRC_POS); // libxml side: create new node - xmlNode *plibNode; - if (!(plibNode = xmlNewNode(NULL, // namespace + xmlNode *pLibNode; + if (!(pLibNode = xmlNewNode(NULL, // namespace (const xmlChar*)pcszElementName))) throw std::bad_alloc(); - xmlAddChild(m_plibNode, plibNode); + xmlAddChild(m_pLibNode, pLibNode); // now wrap this in C++ - ElementNode *p = new ElementNode(m_pelmRoot, this, plibNode); - boost::shared_ptr<ElementNode> pNew(p); - m->children.push_back(pNew); + ElementNode *p = new ElementNode(m_pElmRoot, this, &m_children, pLibNode); + RTListAppend(&m_children, &p->m_listEntry); return p; } @@ -1009,18 +1258,17 @@ ElementNode* ElementNode::createChild(const char *pcszElementName) * @param pcszContent * @return */ -ContentNode* ElementNode::addContent(const char *pcszContent) +ContentNode *ElementNode::addContent(const char *pcszContent) { // libxml side: create new node - xmlNode *plibNode; - if (!(plibNode = xmlNewText((const xmlChar*)pcszContent))) + xmlNode *pLibNode = xmlNewText((const xmlChar*)pcszContent); + if (!pLibNode) throw std::bad_alloc(); - xmlAddChild(m_plibNode, plibNode); + xmlAddChild(m_pLibNode, pLibNode); // now wrap this in C++ - ContentNode *p = new ContentNode(this, plibNode); - boost::shared_ptr<ContentNode> pNew(p); - m->children.push_back(pNew); + ContentNode *p = new ContentNode(this, &m_children, pLibNode); + RTListAppend(&m_children, &p->m_listEntry); return p; } @@ -1032,42 +1280,40 @@ ContentNode* ElementNode::addContent(const char *pcszContent) * otherwise a new attribute is created. Returns the attribute node * that was either created or changed. * - * @param pcszName - * @param pcszValue - * @return + * @param pcszName The attribute name. + * @param pcszValue The attribute value. + * @return Pointer to the attribute node that was created or modified. */ -AttributeNode* ElementNode::setAttribute(const char *pcszName, const char *pcszValue) +AttributeNode *ElementNode::setAttribute(const char *pcszName, const char *pcszValue) { - AttributeNode *pattrReturn; - Data::AttributesMap::const_iterator it; - - it = m->attribs.find(pcszName); - if (it == m->attribs.end()) - { - // libxml side: xmlNewProp creates an attribute - xmlAttr *plibAttr = xmlNewProp(m_plibNode, (xmlChar*)pcszName, (xmlChar*)pcszValue); - - // C++ side: create an attribute node around it - const char *pcszKey; - boost::shared_ptr<AttributeNode> pNew(new AttributeNode(*m_pelmRoot, this, plibAttr, &pcszKey)); - // store - m->attribs[pcszKey] = pNew; - pattrReturn = pNew.get(); - } - else + /* + * Do we already have an attribute and should we just update it? + */ + AttributeNode *pAttr; + RTListForEachCpp(&m_attributes, pAttr, AttributeNode, m_listEntry) { - // overwrite existing libxml attribute node - xmlAttrPtr plibAttr = xmlSetProp(m_plibNode, (xmlChar*)pcszName, (xmlChar*)pcszValue); - - // and fix our existing C++ side around it - boost::shared_ptr<AttributeNode> pattr = it->second; - pattr->m_plibAttr = plibAttr; // in case the xmlAttrPtr is different, I'm not sure + if (pAttr->nameEquals(pcszName)) + { + /* Overwrite existing libxml attribute node ... */ + xmlAttrPtr pLibAttr = xmlSetProp(m_pLibNode, (xmlChar *)pcszName, (xmlChar *)pcszValue); - pattrReturn = pattr.get(); + /* ... and update our C++ wrapper in case the attrib pointer changed. */ + pAttr->m_pLibAttr = pLibAttr; + return pAttr; + } } - return pattrReturn; + /* + * No existing attribute, create a new one. + */ + /* libxml side: xmlNewProp creates an attribute. */ + xmlAttr *pLibAttr = xmlNewProp(m_pLibNode, (xmlChar *)pcszName, (xmlChar *)pcszValue); + + /* C++ side: Create an attribute node around it. */ + pAttr = new AttributeNode(m_pElmRoot, this, &m_attributes, pLibAttr); + RTListAppend(&m_attributes, &pAttr->m_listEntry); + return pAttr; } /** @@ -1196,53 +1442,41 @@ AttributeNode* ElementNode::setAttribute(const char *pcszName, bool f) } /** - * Private constructor for a new attribute node. This one is special: - * in ppcszKey, it returns a pointer to a string buffer that should be - * used to index the attribute correctly with namespaces. + * Private constructor for a new attribute node. * - * @param pParent - * @param elmRoot - * @param plibAttr - * @param ppcszKey + * @param pElmRoot Pointer to the root element. Needed for getting the + * default name space. + * @param pParent Pointer to the parent element (always an ElementNode, + * despite the type). NULL for the root node. + * @param pListAnchor Pointer to the m_children member of the parent. NULL + * for the root node. + * @param pLibNode Pointer to the libxml2 node structure. */ -AttributeNode::AttributeNode(const ElementNode &elmRoot, +AttributeNode::AttributeNode(const ElementNode *pElmRoot, Node *pParent, - xmlAttr *plibAttr, - const char **ppcszKey) + PRTLISTANCHOR pListAnchor, + xmlAttr *pLibAttr) : Node(IsAttribute, pParent, + pListAnchor, NULL, - plibAttr) + pLibAttr) { - m_pcszName = (const char*)plibAttr->name; + m_pcszName = (const char *)pLibAttr->name; - *ppcszKey = m_pcszName; - - if ( plibAttr->ns - && plibAttr->ns->prefix - ) + if ( pLibAttr->ns + && pLibAttr->ns->prefix) { - m_pcszNamespacePrefix = (const char*)plibAttr->ns->prefix; - m_pcszNamespaceHref = (const char*)plibAttr->ns->href; - - if ( !elmRoot.m_pcszNamespaceHref - || (strcmp(m_pcszNamespaceHref, elmRoot.m_pcszNamespaceHref)) - ) - { - // not default namespace: - m_strKey = m_pcszNamespacePrefix; - m_strKey.append(':'); - m_strKey.append(m_pcszName); - - *ppcszKey = m_strKey.c_str(); - } + m_pcszNamespacePrefix = (const char *)pLibAttr->ns->prefix; + m_pcszNamespaceHref = (const char *)pLibAttr->ns->href; } } -ContentNode::ContentNode(Node *pParent, xmlNode *plibNode) +ContentNode::ContentNode(Node *pParent, PRTLISTANCHOR pListAnchor, xmlNode *pLibNode) : Node(IsContent, pParent, - plibNode, + pListAnchor, + pLibNode, NULL) { } @@ -1380,9 +1614,9 @@ Document::~Document() */ void Document::refreshInternals() // private { - m->pRootElement = new ElementNode(NULL, NULL, xmlDocGetRootElement(m->plibDocument)); + m->pRootElement = new ElementNode(NULL, NULL, NULL, xmlDocGetRootElement(m->plibDocument)); - m->pRootElement->buildChildren(*m->pRootElement); + ElementNode::buildChildren(m->pRootElement); } /** @@ -1390,7 +1624,7 @@ void Document::refreshInternals() // private * Const variant. * @return */ -const ElementNode* Document::getRootElement() const +const ElementNode *Document::getRootElement() const { return m->pRootElement; } @@ -1400,41 +1634,43 @@ const ElementNode* Document::getRootElement() const * Non-const variant. * @return */ -ElementNode* Document::getRootElement() +ElementNode *Document::getRootElement() { return m->pRootElement; } /** - * Creates a new element node and sets it as the root element. This will - * only work if the document is empty; otherwise EDocumentNotEmpty is thrown. + * Creates a new element node and sets it as the root element. + * + * This will only work if the document is empty; otherwise EDocumentNotEmpty is + * thrown. */ -ElementNode* Document::createRootElement(const char *pcszRootElementName, +ElementNode *Document::createRootElement(const char *pcszRootElementName, const char *pcszComment /* = NULL */) { if (m->pRootElement || m->plibDocument) throw EDocumentNotEmpty(RT_SRC_POS); // libxml side: create document, create root node - m->plibDocument = xmlNewDoc((const xmlChar*)"1.0"); - xmlNode *plibRootNode; - if (!(plibRootNode = xmlNewNode(NULL, // namespace - (const xmlChar*)pcszRootElementName))) + m->plibDocument = xmlNewDoc((const xmlChar *)"1.0"); + xmlNode *plibRootNode = xmlNewNode(NULL /*namespace*/ , (const xmlChar *)pcszRootElementName); + if (!plibRootNode) throw std::bad_alloc(); xmlDocSetRootElement(m->plibDocument, plibRootNode); + // now wrap this in C++ - m->pRootElement = new ElementNode(NULL, NULL, plibRootNode); + m->pRootElement = new ElementNode(NULL, NULL, NULL, plibRootNode); // add document global comment if specified if (pcszComment != NULL) { - xmlNode *pComment; - if (!(pComment = xmlNewDocComment(m->plibDocument, - (const xmlChar *)pcszComment))) + xmlNode *pComment = xmlNewDocComment(m->plibDocument, (const xmlChar *)pcszComment); + if (!pComment) throw std::bad_alloc(); xmlAddPrevSibling(plibRootNode, pComment); + // now wrap this in C++ - m->pComment = new ElementNode(NULL, NULL, pComment); + m->pComment = new ElementNode(NULL, NULL, NULL, pComment); } return m->pRootElement; @@ -1480,12 +1716,13 @@ XmlMemParser::~XmlMemParser() * * The document that is passed in will be reset before being filled if not empty. * - * @param pvBuf in: memory buffer to parse. - * @param cbSize in: size of the memory buffer. - * @param strFilename in: name fo file to parse. - * @param doc out: document to be reset and filled with data according to file contents. + * @param pvBuf Memory buffer to parse. + * @param cbSize Size of the memory buffer. + * @param strFilename Refernece to the name of the file we're parsing. + * @param doc Reference to the output document. This will be reset + * and filled with data according to file contents. */ -void XmlMemParser::read(const void* pvBuf, size_t cbSize, +void XmlMemParser::read(const void *pvBuf, size_t cbSize, const RTCString &strFilename, Document &doc) { |