summaryrefslogtreecommitdiffstats
path: root/include/iprt/cpp/xml.h
diff options
context:
space:
mode:
Diffstat (limited to 'include/iprt/cpp/xml.h')
-rw-r--r--include/iprt/cpp/xml.h1247
1 files changed, 1247 insertions, 0 deletions
diff --git a/include/iprt/cpp/xml.h b/include/iprt/cpp/xml.h
new file mode 100644
index 00000000..9459f9d3
--- /dev/null
+++ b/include/iprt/cpp/xml.h
@@ -0,0 +1,1247 @@
+/** @file
+ * IPRT - XML Helper APIs.
+ */
+
+/*
+ * Copyright (C) 2007-2022 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef IPRT_INCLUDED_cpp_xml_h
+#define IPRT_INCLUDED_cpp_xml_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#ifndef IN_RING3
+# error "There are no XML APIs available in Ring-0 Context!"
+#endif
+#ifdef IPRT_NO_CRT
+# error "Not available in no-CRT mode because it depends on exceptions, std::list, std::map and stdio.h."
+#endif
+
+#include <iprt/list.h>
+#include <iprt/cpp/exception.h>
+#include <iprt/cpp/utils.h>
+
+#include <list>
+#include <memory>
+
+
+/** @defgroup grp_rt_cpp_xml C++ XML support
+ * @ingroup grp_rt_cpp
+ * @{
+ */
+
+/* Forwards */
+typedef struct _xmlParserInput xmlParserInput;
+typedef xmlParserInput *xmlParserInputPtr;
+typedef struct _xmlParserCtxt xmlParserCtxt;
+typedef xmlParserCtxt *xmlParserCtxtPtr;
+typedef struct _xmlError xmlError;
+typedef xmlError *xmlErrorPtr;
+
+typedef struct _xmlAttr xmlAttr;
+typedef struct _xmlNode xmlNode;
+
+#define RT_XML_CONTENT_SMALL _8K
+#define RT_XML_CONTENT_LARGE _128K
+#define RT_XML_ATTR_TINY 64
+#define RT_XML_ATTR_SMALL _1K
+#define RT_XML_ATTR_MEDIUM _8K
+#define RT_XML_ATTR_LARGE _64K
+
+/** @} */
+
+namespace xml
+{
+
+/**
+ * @addtogroup grp_rt_cpp_xml
+ * @{
+ */
+
+// Exceptions
+//////////////////////////////////////////////////////////////////////////////
+
+class RT_DECL_CLASS LogicError : public RTCError
+{
+public:
+
+ LogicError(const char *aMsg = NULL)
+ : RTCError(aMsg)
+ {}
+
+ LogicError(RT_SRC_POS_DECL);
+};
+
+class RT_DECL_CLASS RuntimeError : public RTCError
+{
+public:
+
+ RuntimeError(const char *aMsg = NULL)
+ : RTCError(aMsg)
+ {}
+};
+
+class RT_DECL_CLASS XmlError : public RuntimeError
+{
+public:
+ XmlError(xmlErrorPtr aErr);
+
+ static char* Format(xmlErrorPtr aErr);
+};
+
+// Logical errors
+//////////////////////////////////////////////////////////////////////////////
+
+class RT_DECL_CLASS ENotImplemented : public LogicError
+{
+public:
+ ENotImplemented(const char *aMsg = NULL) : LogicError(aMsg) {}
+ ENotImplemented(RT_SRC_POS_DECL) : LogicError(RT_SRC_POS_ARGS) {}
+};
+
+class RT_DECL_CLASS EInvalidArg : public LogicError
+{
+public:
+ EInvalidArg(const char *aMsg = NULL) : LogicError(aMsg) {}
+ EInvalidArg(RT_SRC_POS_DECL) : LogicError(RT_SRC_POS_ARGS) {}
+};
+
+class RT_DECL_CLASS EDocumentNotEmpty : public LogicError
+{
+public:
+ EDocumentNotEmpty(const char *aMsg = NULL) : LogicError(aMsg) {}
+ EDocumentNotEmpty(RT_SRC_POS_DECL) : LogicError(RT_SRC_POS_ARGS) {}
+};
+
+class RT_DECL_CLASS ENodeIsNotElement : public LogicError
+{
+public:
+ ENodeIsNotElement(const char *aMsg = NULL) : LogicError(aMsg) {}
+ ENodeIsNotElement(RT_SRC_POS_DECL) : LogicError(RT_SRC_POS_ARGS) {}
+};
+
+// Runtime errors
+//////////////////////////////////////////////////////////////////////////////
+
+class RT_DECL_CLASS EIPRTFailure : public RuntimeError
+{
+public:
+
+ EIPRTFailure(int aRC, const char *pszContextFmt, ...);
+
+ int rc() const
+ {
+ return mRC;
+ }
+
+private:
+ int mRC;
+};
+
+/**
+ * The Stream class is a base class for I/O streams.
+ */
+class RT_DECL_CLASS Stream
+{
+public:
+
+ virtual ~Stream() {}
+
+ virtual const char *uri() const = 0;
+
+ /**
+ * Returns the current read/write position in the stream. The returned
+ * position is a zero-based byte offset from the beginning of the file.
+ *
+ * Throws ENotImplemented if this operation is not implemented for the
+ * given stream.
+ */
+ virtual uint64_t pos() const = 0;
+
+ /**
+ * Sets the current read/write position in the stream.
+ *
+ * @param aPos Zero-based byte offset from the beginning of the stream.
+ *
+ * Throws ENotImplemented if this operation is not implemented for the
+ * given stream.
+ */
+ virtual void setPos (uint64_t aPos) = 0;
+};
+
+/**
+ * The Input class represents an input stream.
+ *
+ * This input stream is used to read the settings tree from.
+ * This is an abstract class that must be subclassed in order to fill it with
+ * useful functionality.
+ */
+class RT_DECL_CLASS Input : virtual public Stream
+{
+public:
+
+ /**
+ * Reads from the stream to the supplied buffer.
+ *
+ * @param aBuf Buffer to store read data to.
+ * @param aLen Buffer length.
+ *
+ * @return Number of bytes read.
+ */
+ virtual int read (char *aBuf, int aLen) = 0;
+};
+
+/**
+ *
+ */
+class RT_DECL_CLASS Output : virtual public Stream
+{
+public:
+
+ /**
+ * Writes to the stream from the supplied buffer.
+ *
+ * @param aBuf Buffer to write data from.
+ * @param aLen Buffer length.
+ *
+ * @return Number of bytes written.
+ */
+ virtual int write (const char *aBuf, int aLen) = 0;
+
+ /**
+ * Truncates the stream from the current position and upto the end.
+ * The new file size will become exactly #pos() bytes.
+ *
+ * Throws ENotImplemented if this operation is not implemented for the
+ * given stream.
+ */
+ virtual void truncate() = 0;
+};
+
+
+//////////////////////////////////////////////////////////////////////////////
+
+/**
+ * The File class is a stream implementation that reads from and writes to
+ * regular files.
+ *
+ * The File class uses IPRT File API for file operations. Note that IPRT File
+ * API is not thread-safe. This means that if you pass the same RTFILE handle to
+ * different File instances that may be simultaneously used on different
+ * threads, you should care about serialization; otherwise you will get garbage
+ * when reading from or writing to such File instances.
+ */
+class RT_DECL_CLASS File : public Input, public Output
+{
+public:
+
+ /**
+ * Possible file access modes.
+ */
+ enum Mode { Mode_Read, Mode_WriteCreate, Mode_Overwrite, Mode_ReadWrite };
+
+ /**
+ * Opens a file with the given name in the given mode. If @a aMode is Read
+ * or ReadWrite, the file must exist. If @a aMode is Write, the file must
+ * not exist. Otherwise, an EIPRTFailure exception will be thrown.
+ *
+ * @param aMode File mode.
+ * @param aFileName File name.
+ * @param aFlushIt Whether to flush a writable file before closing it.
+ */
+ File(Mode aMode, const char *aFileName, bool aFlushIt = false);
+
+ /**
+ * Uses the given file handle to perform file operations. This file
+ * handle must be already open in necessary mode (read, or write, or mixed).
+ *
+ * The read/write position of the given handle will be reset to the
+ * beginning of the file on success.
+ *
+ * Note that the given file handle will not be automatically closed upon
+ * this object destruction.
+ *
+ * @note It you pass the same RTFILE handle to more than one File instance,
+ * please make sure you have provided serialization in case if these
+ * instasnces are to be simultaneously used by different threads.
+ * Otherwise you may get garbage when reading or writing.
+ *
+ * @param aHandle Open file handle.
+ * @param aFileName File name (for reference).
+ * @param aFlushIt Whether to flush a writable file before closing it.
+ */
+ File(RTFILE aHandle, const char *aFileName = NULL, bool aFlushIt = false);
+
+ /**
+ * Destroys the File object. If the object was created from a file name
+ * the corresponding file will be automatically closed. If the object was
+ * created from a file handle, it will remain open.
+ */
+ virtual ~File();
+
+ const char *uri() const;
+
+ uint64_t pos() const;
+ void setPos(uint64_t aPos);
+
+ /**
+ * See Input::read(). If this method is called in wrong file mode,
+ * LogicError will be thrown.
+ */
+ int read(char *aBuf, int aLen);
+
+ /**
+ * See Output::write(). If this method is called in wrong file mode,
+ * LogicError will be thrown.
+ */
+ int write(const char *aBuf, int aLen);
+
+ /**
+ * See Output::truncate(). If this method is called in wrong file mode,
+ * LogicError will be thrown.
+ */
+ void truncate();
+
+private:
+
+ /* Obscure class data */
+ struct Data;
+ Data *m;
+
+ /* auto_ptr data doesn't have proper copy semantics */
+ DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(File);
+};
+
+/**
+ * The MemoryBuf class represents a stream implementation that reads from the
+ * memory buffer.
+ */
+class RT_DECL_CLASS MemoryBuf : public Input
+{
+public:
+
+ MemoryBuf (const char *aBuf, size_t aLen, const char *aURI = NULL);
+
+ virtual ~MemoryBuf();
+
+ const char *uri() const;
+
+ int read(char *aBuf, int aLen);
+ uint64_t pos() const;
+ void setPos(uint64_t aPos);
+
+private:
+ /* Obscure class data */
+ struct Data;
+ Data *m;
+
+ /* auto_ptr data doesn't have proper copy semantics */
+ DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(MemoryBuf);
+};
+
+
+/*
+ * GlobalLock
+ *
+ *
+ */
+
+
+typedef DECLCALLBACKTYPE_EX(xmlParserInput *, RT_NOTHING, FNEXTERNALENTITYLOADER,(const char *aURI, const char *aID,
+ xmlParserCtxt *aCtxt));
+typedef FNEXTERNALENTITYLOADER *PFNEXTERNALENTITYLOADER; /**< xmlExternalEntityLoader w/ noexcept. */
+
+class RT_DECL_CLASS GlobalLock
+{
+public:
+ GlobalLock();
+ ~GlobalLock();
+
+ void setExternalEntityLoader(PFNEXTERNALENTITYLOADER pFunc);
+
+ static xmlParserInput* callDefaultLoader(const char *aURI,
+ const char *aID,
+ xmlParserCtxt *aCtxt);
+
+private:
+ /* Obscure class data. */
+ struct Data;
+ struct Data *m;
+};
+
+class ElementNode;
+typedef std::list<const ElementNode*> ElementNodesList;
+
+class AttributeNode;
+
+class ContentNode;
+
+/**
+ * Node base class.
+ *
+ * Cannot be used directly, but ElementNode, ContentNode and AttributeNode
+ * derive from this. This does implement useful public methods though.
+ *
+ *
+ */
+class RT_DECL_CLASS Node
+{
+public:
+ virtual ~Node();
+
+ const char *getName() const;
+ const char *getPrefix() const;
+ const char *getNamespaceURI() const;
+ bool nameEqualsNS(const char *pcszNamespace, const char *pcsz) const;
+ bool nameEquals(const char *pcsz) const
+ {
+ return nameEqualsNS(NULL, pcsz);
+ }
+ bool nameEqualsN(const char *pcsz, size_t cchMax, const char *pcszNamespace = NULL) const;
+
+ const char *getValue() const;
+ const char *getValueN(size_t cchValueLimit) const;
+ bool copyValue(int32_t &i) const;
+ bool copyValue(uint32_t &i) const;
+ bool copyValue(int64_t &i) const;
+ bool copyValue(uint64_t &i) const;
+
+ /** @name Introspection.
+ * @{ */
+ /** Is this an ElementNode instance.
+ * @returns true / false */
+ bool isElement() const
+ {
+ return m_Type == IsElement;
+ }
+
+ /** Is this an ContentNode instance.
+ * @returns true / false */
+ bool isContent() const
+ {
+ return m_Type == IsContent;
+ }
+
+ /** Is this an AttributeNode instance.
+ * @returns true / false */
+ bool isAttribute() const
+ {
+ return m_Type == IsAttribute;
+ }
+
+ int getLineNumber() const;
+ /** @} */
+
+ /** @name General tree enumeration.
+ *
+ * Use the introspection methods isElement() and isContent() before doing static
+ * casting. Parents are always or ElementNode type, but siblings and children
+ * can be of both ContentNode and ElementNode types.
+ *
+ * @remarks Attribute node are in the attributes list, while both content and
+ * element nodes are in the list of children. See ElementNode.
+ *
+ * @remarks Careful mixing tree walking with node removal!
+ * @{
+ */
+ /** Get the parent node
+ * @returns Pointer to the parent node, or NULL if root. */
+ const Node *getParent() const
+ {
+ return m_pParent;
+ }
+
+ /** Get the previous sibling.
+ * @returns Pointer to the previous sibling node, NULL if first child.
+ */
+ const Node *getPrevSibiling() const
+ {
+ if (!m_pParentListAnchor)
+ return NULL;
+ return RTListGetPrevCpp(m_pParentListAnchor, this, const Node, m_listEntry);
+ }
+
+ /** Get the next sibling.
+ * @returns Pointer to the next sibling node, NULL if last child. */
+ const Node *getNextSibiling() const
+ {
+ if (!m_pParentListAnchor)
+ return NULL;
+ return RTListGetNextCpp(m_pParentListAnchor, this, const Node, m_listEntry);
+ }
+ /** @} */
+
+protected:
+ /** Node types. */
+ typedef enum { IsElement, IsAttribute, IsContent } EnumType;
+
+ /** The type of node this is an instance of. */
+ EnumType m_Type;
+ /** The parent node (always an element), NULL if root. */
+ Node *m_pParent;
+
+ xmlNode *m_pLibNode; ///< != NULL if this is an element or content node
+ xmlAttr *m_pLibAttr; ///< != NULL if this is an attribute node
+ const char *m_pcszNamespacePrefix; ///< not always set
+ const char *m_pcszNamespaceHref; ///< full http:// spec
+ const char *m_pcszName; ///< element or attribute name, points either into pLibNode or pLibAttr;
+ ///< NULL if this is a content node
+
+ /** Child list entry of this node. (List head m_pParent->m_children or
+ * m_pParent->m_attribute depending on the type.) */
+ RTLISTNODE m_listEntry;
+ /** Pointer to the parent list anchor.
+ * This allows us to use m_listEntry both for children and attributes. */
+ PRTLISTANCHOR m_pParentListAnchor;
+
+ // hide the default constructor so people use only our factory methods
+ Node(EnumType type,
+ Node *pParent,
+ PRTLISTANCHOR pListAnchor,
+ xmlNode *pLibNode,
+ xmlAttr *pLibAttr);
+ Node(const Node &x); // no copying
+
+ friend class AttributeNode;
+ friend class ElementNode; /* C list hack. */
+};
+
+/**
+ * Node subclass that represents an attribute of an element.
+ *
+ * For attributes, Node::getName() returns the attribute name, and Node::getValue()
+ * returns the attribute value, if any.
+ *
+ * Since the Node constructor is private, one can create new attribute nodes
+ * only through the following factory methods:
+ *
+ * -- ElementNode::setAttribute()
+ */
+class RT_DECL_CLASS AttributeNode : public Node
+{
+public:
+
+protected:
+ // hide the default constructor so people use only our factory methods
+ AttributeNode(const ElementNode *pElmRoot,
+ Node *pParent,
+ PRTLISTANCHOR pListAnchor,
+ xmlAttr *pLibAttr);
+ AttributeNode(const AttributeNode &x); // no copying
+
+ friend class Node;
+ friend class ElementNode;
+};
+
+/**
+ * Node subclass that represents an element.
+ *
+ * For elements, Node::getName() returns the element name, and Node::getValue()
+ * returns the text contents, if any.
+ *
+ * Since the Node constructor is private, one can create element nodes
+ * only through the following factory methods:
+ *
+ * -- Document::createRootElement()
+ * -- ElementNode::createChild()
+ */
+class RT_DECL_CLASS ElementNode : public Node
+{
+public:
+ int getChildElements(ElementNodesList &children, const char *pcszMatch = NULL) const;
+
+ const ElementNode *findChildElementNS(const char *pcszNamespace, const char *pcszMatch) const;
+ const ElementNode *findChildElement(const char *pcszMatch) const
+ {
+ return findChildElementNS(NULL, pcszMatch);
+ }
+ const ElementNode *findChildElementFromId(const char *pcszId) const;
+
+ /** Finds the first decendant matching the name at the end of @a pcszPath and
+ * optionally namespace.
+ *
+ * @returns Pointer to the child string value, NULL if not found or no value.
+ * @param pcszPath Path to the child element. Slashes can be used to
+ * make a simple path to any decendant.
+ * @param pcszNamespace The namespace to match, NULL (default) match any
+ * namespace. When using a path, this matches all
+ * elements along the way.
+ * @see findChildElement, findChildElementP
+ */
+ const ElementNode *findChildElementP(const char *pcszPath, const char *pcszNamespace = NULL) const;
+
+ /** Finds the first child with matching the give name and optionally namspace,
+ * returning its value.
+ *
+ * @returns Pointer to the child string value, NULL if not found or no value.
+ * @param pcszPath Path to the child element. Slashes can be used to
+ * make a simple path to any decendant.
+ * @param pcszNamespace The namespace to match, NULL (default) match any
+ * namespace. When using a path, this matches all
+ * elements along the way.
+ * @see findChildElement, findChildElementP
+ */
+ const char *findChildElementValueP(const char *pcszPath, const char *pcszNamespace = NULL) const
+ {
+ const ElementNode *pElem = findChildElementP(pcszPath, pcszNamespace);
+ if (pElem)
+ return pElem->getValue();
+ return NULL;
+ }
+
+ /** Finds the first child with matching the give name and optionally namspace,
+ * returning its value. Checks the length against the limit.
+ *
+ * @returns Pointer to the child string value, NULL if not found or no value.
+ * @param pcszPath Path to the child element. Slashes can be used to
+ * make a simple path to any decendant.
+ * @param cchValueLimit If the length of the returned value exceeds this
+ * limit a EIPRTFailure exception will be thrown.
+ * @param pcszNamespace The namespace to match, NULL (default) match any
+ * namespace. When using a path, this matches all
+ * elements along the way.
+ * @see findChildElement, findChildElementP
+ */
+ const char *findChildElementValuePN(const char *pcszPath, size_t cchValueLimit, const char *pcszNamespace = NULL) const
+ {
+ const ElementNode *pElem = findChildElementP(pcszPath, pcszNamespace);
+ if (pElem)
+ return pElem->getValueN(cchValueLimit);
+ return NULL;
+ }
+
+ /** Combines findChildElementNS and findAttributeValue.
+ *
+ * @returns Pointer to attribute string value, NULL if either the element or
+ * the attribute was not found.
+ * @param pcszChild The child element name.
+ * @param pcszAttribute The attribute name.
+ * @param pcszChildNamespace The namespace to match @a pcszChild with, NULL
+ * (default) match any namespace.
+ * @param pcszAttributeNamespace The namespace prefix to apply to the
+ * attribute, NULL (default) match any namespace.
+ * @see findChildElementNS and findAttributeValue
+ * @note The findChildElementAttributeValueP() method would do the same thing
+ * given the same inputs, but it would be slightly slower, thus the
+ * separate method.
+ */
+ const char *findChildElementAttributeValue(const char *pcszChild, const char *pcszAttribute,
+ const char *pcszChildNamespace = NULL,
+ const char *pcszAttributeNamespace = NULL) const
+ {
+ const ElementNode *pElem = findChildElementNS(pcszChildNamespace, pcszChild);
+ if (pElem)
+ return pElem->findAttributeValue(pcszAttribute, pcszAttributeNamespace);
+ return NULL;
+ }
+
+ /** Combines findChildElementP and findAttributeValue.
+ *
+ * @returns Pointer to attribute string value, NULL if either the element or
+ * the attribute was not found.
+ * @param pcszPath Path to the child element. Slashes can be used
+ * to make a simple path to any decendant.
+ * @param pcszAttribute The attribute name.
+ * @param pcszPathNamespace The namespace to match @a pcszPath with, NULL
+ * (default) match any namespace. When using a
+ * path, this matches all elements along the way.
+ * @param pcszAttributeNamespace The namespace prefix to apply to the
+ * attribute, NULL (default) match any namespace.
+ * @see findChildElementP and findAttributeValue
+ */
+ const char *findChildElementAttributeValueP(const char *pcszPath, const char *pcszAttribute,
+ const char *pcszPathNamespace = NULL,
+ const char *pcszAttributeNamespace = NULL) const
+ {
+ const ElementNode *pElem = findChildElementP(pcszPath, pcszPathNamespace);
+ if (pElem)
+ return pElem->findAttributeValue(pcszAttribute, pcszAttributeNamespace);
+ return NULL;
+ }
+
+ /** Combines findChildElementP and findAttributeValueN.
+ *
+ * @returns Pointer to attribute string value, NULL if either the element or
+ * the attribute was not found.
+ * @param pcszPath The attribute name. Slashes can be used to make a
+ * simple path to any decendant.
+ * @param pcszAttribute The attribute name.
+ * @param cchValueLimit If the length of the returned value exceeds this
+ * limit a EIPRTFailure exception will be thrown.
+ * @param pcszPathNamespace The namespace to match @a pcszPath with, NULL
+ * (default) match any namespace. When using a
+ * path, this matches all elements along the way.
+ * @param pcszAttributeNamespace The namespace prefix to apply to the
+ * attribute, NULL (default) match any namespace.
+ * @see findChildElementP and findAttributeValue
+ */
+ const char *findChildElementAttributeValuePN(const char *pcszPath, const char *pcszAttribute,
+ size_t cchValueLimit,
+ const char *pcszPathNamespace = NULL,
+ const char *pcszAttributeNamespace = NULL) const
+ {
+ const ElementNode *pElem = findChildElementP(pcszPath, pcszPathNamespace);
+ if (pElem)
+ return pElem->findAttributeValueN(pcszAttribute, cchValueLimit, pcszAttributeNamespace);
+ return NULL;
+ }
+
+
+ /** @name Tree enumeration.
+ * @{ */
+
+ /** Get the next tree element in a full tree enumeration.
+ *
+ * By starting with the root node, this can be used to enumerate the entire tree
+ * (or sub-tree if @a pElmRoot is used).
+ *
+ * @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 *getNextTreeElement(ElementNode const *pElmRoot = NULL) const;
+ RT_CPP_GETTER_UNCONST_RET(ElementNode *, ElementNode, getNextTreeElement, (const ElementNode *pElmRoot = NULL), (pElmRoot))
+
+ /** Get the first child node.
+ * @returns Pointer to the first child node, NULL if no children. */
+ const Node *getFirstChild() const
+ {
+ return RTListGetFirstCpp(&m_children, const Node, m_listEntry);
+ }
+ RT_CPP_GETTER_UNCONST_RET(Node *, ElementNode, getFirstChild,(),())
+
+ /** Get the last child node.
+ * @returns Pointer to the last child node, NULL if no children. */
+ const Node *getLastChild() const
+ {
+ return RTListGetLastCpp(&m_children, const Node, m_listEntry);
+ }
+
+ /** Get the first child node.
+ * @returns Pointer to the first child node, NULL if no children. */
+ const ElementNode *getFirstChildElement() const;
+
+ /** Get the last child node.
+ * @returns Pointer to the last child node, NULL if no children. */
+ const ElementNode *getLastChildElement() const;
+
+ /** Get the previous sibling element.
+ * @returns Pointer to the previous sibling element, NULL if first child
+ * element.
+ * @see getNextSibilingElement, getPrevSibling
+ */
+ const ElementNode *getPrevSibilingElement() const;
+
+ /** Get the next sibling element.
+ * @returns Pointer to the next sibling element, NULL if last child element.
+ * @see getPrevSibilingElement, getNextSibling
+ */
+ const ElementNode *getNextSibilingElement() const;
+
+ /** Find the previous element matching the given name and namespace (optionally).
+ * @returns Pointer to the previous sibling element, NULL if first child
+ * element.
+ * @param pcszName The element name to match.
+ * @param pcszNamespace The namespace name, default is NULL which means
+ * anything goes.
+ * @note Changed the order of the arguments.
+ */
+ const ElementNode *findPrevSibilingElement(const char *pcszName, const char *pcszNamespace = NULL) const;
+
+ /** Find the next element matching the given name and namespace (optionally).
+ * @returns Pointer to the previous sibling element, NULL if first child
+ * element.
+ * @param pcszName The element name to match.
+ * @param pcszNamespace The namespace name, default is NULL which means
+ * anything goes.
+ * @note Changed the order of the arguments.
+ */
+ const ElementNode *findNextSibilingElement(const char *pcszName, const char *pcszNamespace = NULL) const;
+ /** @} */
+
+ /** @name Attribute enumeration
+ * @{ */
+
+ /** Get the first attribute node.
+ * @returns Pointer to the first child node, NULL if no attributes. */
+ const AttributeNode *getFirstAttribute() const
+ {
+ return RTListGetFirstCpp(&m_attributes, const AttributeNode, m_listEntry);
+ }
+
+ /** Get the last attribute node.
+ * @returns Pointer to the last child node, NULL if no attributes. */
+ const AttributeNode *getLastAttribute() const
+ {
+ return RTListGetLastCpp(&m_attributes, const AttributeNode, m_listEntry);
+ }
+
+ /** @} */
+
+ const AttributeNode *findAttribute(const char *pcszMatch, const char *pcszNamespace = NULL) const;
+ /** Find the first attribute with the given name, returning its value string.
+ * @returns Pointer to the attribute string value.
+ * @param pcszName The attribute name.
+ * @param pcszNamespace The namespace name, default is NULL which means
+ * anything goes.
+ * @see getAttributeValue
+ */
+ const char *findAttributeValue(const char *pcszName, const char *pcszNamespace = NULL) const
+ {
+ const AttributeNode *pAttr = findAttribute(pcszName, pcszNamespace);
+ if (pAttr)
+ return pAttr->getValue();
+ return NULL;
+ }
+ /** Find the first attribute with the given name, returning its value string.
+ * @returns Pointer to the attribute string value.
+ * @param pcszName The attribute name.
+ * @param cchValueLimit If the length of the returned value exceeds this
+ * limit a EIPRTFailure exception will be thrown.
+ * @param pcszNamespace The namespace name, default is NULL which means
+ * anything goes.
+ * @see getAttributeValue
+ */
+ const char *findAttributeValueN(const char *pcszName, size_t cchValueLimit, const char *pcszNamespace = NULL) const
+ {
+ const AttributeNode *pAttr = findAttribute(pcszName, pcszNamespace);
+ if (pAttr)
+ return pAttr->getValueN(cchValueLimit);
+ return NULL;
+ }
+
+ bool getAttributeValue(const char *pcszMatch, const char *&pcsz, const char *pcszNamespace = NULL) const
+ { return getAttributeValue(pcszMatch, &pcsz, pcszNamespace); }
+ bool getAttributeValue(const char *pcszMatch, RTCString &str, const char *pcszNamespace = NULL) const
+ { return getAttributeValue(pcszMatch, &str, pcszNamespace); }
+ bool getAttributeValuePath(const char *pcszMatch, RTCString &str, const char *pcszNamespace = NULL) const
+ { return getAttributeValue(pcszMatch, &str, pcszNamespace); }
+ bool getAttributeValue(const char *pcszMatch, int32_t &i, const char *pcszNamespace = NULL) const
+ { return getAttributeValue(pcszMatch, &i, pcszNamespace); }
+ bool getAttributeValue(const char *pcszMatch, uint32_t &i, const char *pcszNamespace = NULL) const
+ { return getAttributeValue(pcszMatch, &i, pcszNamespace); }
+ bool getAttributeValue(const char *pcszMatch, int64_t &i, const char *pcszNamespace = NULL) const
+ { return getAttributeValue(pcszMatch, &i, pcszNamespace); }
+ bool getAttributeValue(const char *pcszMatch, uint64_t &u, const char *pcszNamespace = NULL) const
+ { return getAttributeValue(pcszMatch, &u, pcszNamespace); }
+ bool getAttributeValue(const char *pcszMatch, bool &f, const char *pcszNamespace = NULL) const
+ { return getAttributeValue(pcszMatch, &f, pcszNamespace); }
+ bool getAttributeValueN(const char *pcszMatch, const char *&pcsz, size_t cchValueLimit, const char *pcszNamespace = NULL) const
+ { return getAttributeValueN(pcszMatch, &pcsz, cchValueLimit, pcszNamespace); }
+ bool getAttributeValueN(const char *pcszMatch, RTCString &str, size_t cchValueLimit, const char *pcszNamespace = NULL) const
+ { return getAttributeValueN(pcszMatch, &str, cchValueLimit, pcszNamespace); }
+ bool getAttributeValuePathN(const char *pcszMatch, RTCString &str, size_t cchValueLimit, const char *pcszNamespace = NULL) const
+ { return getAttributeValueN(pcszMatch, &str, cchValueLimit, pcszNamespace); }
+
+ /** @name Variants that for clarity does not use references for output params.
+ * @{ */
+ bool getAttributeValue(const char *pcszMatch, const char **ppcsz, const char *pcszNamespace = NULL) const;
+ bool getAttributeValue(const char *pcszMatch, RTCString *pStr, const char *pcszNamespace = NULL) const;
+ bool getAttributeValuePath(const char *pcszMatch, RTCString *pStr, const char *pcszNamespace = NULL) const;
+ bool getAttributeValue(const char *pcszMatch, int32_t *pi, const char *pcszNamespace = NULL) const;
+ bool getAttributeValue(const char *pcszMatch, uint32_t *pu, const char *pcszNamespace = NULL) const;
+ bool getAttributeValue(const char *pcszMatch, int64_t *piValue, const char *pcszNamespace = NULL) const;
+ bool getAttributeValue(const char *pcszMatch, uint64_t *pu, const char *pcszNamespace = NULL) const;
+ bool getAttributeValue(const char *pcszMatch, bool *pf, const char *pcszNamespace = NULL) const;
+ bool getAttributeValueN(const char *pcszMatch, const char **ppcsz, size_t cchValueLimit, const char *pcszNamespace = NULL) const;
+ bool getAttributeValueN(const char *pcszMatch, RTCString *pStr, size_t cchValueLimit, const char *pcszNamespace = NULL) const;
+ bool getAttributeValuePathN(const char *pcszMatch, RTCString *pStr, size_t cchValueLimit, const char *pcszNamespace = NULL) const;
+ /** @} */
+
+ /** @name Convenience methods for convering the element value.
+ * @{ */
+ bool getElementValue(int32_t *piValue) const;
+ bool getElementValue(uint32_t *puValue) const;
+ bool getElementValue(int64_t *piValue) const;
+ bool getElementValue(uint64_t *puValue) const;
+ bool getElementValue(bool *pfValue) const;
+ /** @} */
+
+ /** @name Convenience findChildElementValueP and getElementValue.
+ * @{ */
+ bool getChildElementValueP(const char *pcszPath, int32_t *piValue, const char *pcszNamespace = NULL) const
+ {
+ const ElementNode *pElem = findChildElementP(pcszPath, pcszNamespace);
+ return pElem && pElem->getElementValue(piValue);
+ }
+ bool getChildElementValueP(const char *pcszPath, uint32_t *puValue, const char *pcszNamespace = NULL) const
+ {
+ const ElementNode *pElem = findChildElementP(pcszPath, pcszNamespace);
+ return pElem && pElem->getElementValue(puValue);
+ }
+ bool getChildElementValueP(const char *pcszPath, int64_t *piValue, const char *pcszNamespace = NULL) const
+ {
+ const ElementNode *pElem = findChildElementP(pcszPath, pcszNamespace);
+ return pElem && pElem->getElementValue(piValue);
+ }
+ bool getChildElementValueP(const char *pcszPath, uint64_t *puValue, const char *pcszNamespace = NULL) const
+ {
+ const ElementNode *pElem = findChildElementP(pcszPath, pcszNamespace);
+ return pElem && pElem->getElementValue(puValue);
+ }
+ bool getChildElementValueP(const char *pcszPath, bool *pfValue, const char *pcszNamespace = NULL) const
+ {
+ const ElementNode *pElem = findChildElementP(pcszPath, pcszNamespace);
+ return pElem && pElem->getElementValue(pfValue);
+ }
+
+ /** @} */
+
+ /** @name Convenience findChildElementValueP and getElementValue with a
+ * default value being return if the child element isn't present.
+ *
+ * @remarks These will return false on conversion errors.
+ * @{ */
+ bool getChildElementValueDefP(const char *pcszPath, int32_t iDefault, int32_t *piValue, const char *pcszNamespace = NULL) const
+ {
+ const ElementNode *pElem = findChildElementP(pcszPath, pcszNamespace);
+ if (pElem)
+ return pElem->getElementValue(piValue);
+ *piValue = iDefault;
+ return true;
+ }
+ bool getChildElementValueDefP(const char *pcszPath, uint32_t uDefault, uint32_t *puValue, const char *pcszNamespace = NULL) const
+ {
+ const ElementNode *pElem = findChildElementP(pcszPath, pcszNamespace);
+ if (pElem)
+ return pElem->getElementValue(puValue);
+ *puValue = uDefault;
+ return true;
+ }
+ bool getChildElementValueDefP(const char *pcszPath, int64_t iDefault, int64_t *piValue, const char *pcszNamespace = NULL) const
+ {
+ const ElementNode *pElem = findChildElementP(pcszPath, pcszNamespace);
+ if (pElem)
+ return pElem->getElementValue(piValue);
+ *piValue = iDefault;
+ return true;
+ }
+ bool getChildElementValueDefP(const char *pcszPath, uint64_t uDefault, uint64_t *puValue, const char *pcszNamespace = NULL) const
+ {
+ const ElementNode *pElem = findChildElementP(pcszPath, pcszNamespace);
+ if (pElem)
+ return pElem->getElementValue(puValue);
+ *puValue = uDefault;
+ return true;
+ }
+ bool getChildElementValueDefP(const char *pcszPath, bool fDefault, bool *pfValue, const char *pcszNamespace = NULL) const
+ {
+ const ElementNode *pElem = findChildElementP(pcszPath, pcszNamespace);
+ if (pElem)
+ return pElem->getElementValue(pfValue);
+ *pfValue = fDefault;
+ return true;
+ }
+ /** @} */
+
+ ElementNode *createChild(const char *pcszElementName);
+
+ ContentNode *addContent(const char *pcszContent);
+ ContentNode *addContent(const RTCString &strContent)
+ {
+ return addContent(strContent.c_str());
+ }
+
+ ContentNode *setContent(const char *pcszContent);
+ ContentNode *setContent(const RTCString &strContent)
+ {
+ return setContent(strContent.c_str());
+ }
+
+ AttributeNode *setAttribute(const char *pcszName, const char *pcszValue);
+ AttributeNode *setAttribute(const char *pcszName, const RTCString &strValue)
+ {
+ return setAttribute(pcszName, strValue.c_str());
+ }
+ AttributeNode *setAttributePath(const char *pcszName, const RTCString &strValue);
+ AttributeNode *setAttribute(const char *pcszName, int32_t i);
+ AttributeNode *setAttribute(const char *pcszName, uint32_t i);
+ AttributeNode *setAttribute(const char *pcszName, int64_t i);
+ AttributeNode *setAttribute(const char *pcszName, uint64_t i);
+ AttributeNode *setAttributeHex(const char *pcszName, uint32_t i);
+ AttributeNode *setAttribute(const char *pcszName, bool f);
+
+ virtual ~ElementNode();
+
+protected:
+ // hide the default constructor so people use only our factory methods
+ ElementNode(const ElementNode *pElmRoot, Node *pParent, PRTLISTANCHOR pListAnchor, xmlNode *pLibNode);
+ ElementNode(const ElementNode &x); // no copying
+
+ /** We keep a pointer to the root element for attribute namespace handling. */
+ const ElementNode *m_pElmRoot;
+
+ /** List of child elements and content nodes. */
+ RTLISTANCHOR m_children;
+ /** List of attributes nodes. */
+ RTLISTANCHOR m_attributes;
+
+ static void buildChildren(ElementNode *pElmRoot);
+
+ friend class Node;
+ friend class Document;
+ friend class XmlFileParser;
+};
+
+/**
+ * Node subclass that represents content (non-element text).
+ *
+ * Since the Node constructor is private, one can create new content nodes
+ * only through the following factory methods:
+ *
+ * -- ElementNode::addContent()
+ */
+class RT_DECL_CLASS ContentNode : public Node
+{
+public:
+
+protected:
+ // hide the default constructor so people use only our factory methods
+ ContentNode(Node *pParent, PRTLISTANCHOR pListAnchor, xmlNode *pLibNode);
+ ContentNode(const ContentNode &x); // no copying
+
+ friend class Node;
+ friend class ElementNode;
+};
+
+
+/**
+ * Handy helper class with which one can loop through all or some children
+ * of a particular element. See NodesLoop::forAllNodes() for details.
+ */
+class RT_DECL_CLASS NodesLoop
+{
+public:
+ NodesLoop(const ElementNode &node, const char *pcszMatch = NULL);
+ ~NodesLoop();
+ const ElementNode* forAllNodes() const;
+
+private:
+ /* Obscure class data */
+ struct Data;
+ Data *m;
+};
+
+/**
+ * The XML document class. An instance of this needs to be created by a user
+ * of the XML classes and then passed to
+ *
+ * -- XmlMemParser or XmlFileParser to read an XML document; those classes then
+ * fill the caller's Document with ElementNode, ContentNode and AttributeNode
+ * instances. The typical sequence then is:
+ * @code
+ Document doc;
+ XmlFileParser parser;
+ parser.read("file.xml", doc);
+ Element *pElmRoot = doc.getRootElement();
+ @endcode
+ *
+ * -- XmlMemWriter or XmlFileWriter to write out an XML document after it has
+ * been created and filled. Example:
+ *
+ * @code
+ Document doc;
+ Element *pElmRoot = doc.createRootElement();
+ // add children
+ xml::XmlFileWriter writer(doc);
+ writer.write("file.xml", true);
+ @endcode
+ */
+class RT_DECL_CLASS Document
+{
+public:
+ Document();
+ ~Document();
+
+ Document(const Document &x);
+ Document& operator=(const Document &x);
+
+ const ElementNode* getRootElement() const;
+ ElementNode* getRootElement();
+
+ ElementNode* createRootElement(const char *pcszRootElementName,
+ const char *pcszComment = NULL);
+
+private:
+ friend class XmlMemParser;
+ friend class XmlFileParser;
+ friend class XmlMemWriter;
+ friend class XmlStringWriter;
+ friend class XmlFileWriter;
+
+ void refreshInternals();
+
+ /* Obscure class data */
+ struct Data;
+ Data *m;
+};
+
+/*
+ * XmlParserBase
+ *
+ */
+
+class RT_DECL_CLASS XmlParserBase
+{
+protected:
+ XmlParserBase();
+ ~XmlParserBase();
+
+ xmlParserCtxtPtr m_ctxt;
+};
+
+/*
+ * XmlMemParser
+ *
+ */
+
+class RT_DECL_CLASS XmlMemParser : public XmlParserBase
+{
+public:
+ XmlMemParser();
+ ~XmlMemParser();
+
+ void read(const void* pvBuf, size_t cbSize, const RTCString &strFilename, Document &doc);
+};
+
+/*
+ * XmlFileParser
+ *
+ */
+
+class RT_DECL_CLASS XmlFileParser : public XmlParserBase
+{
+public:
+ XmlFileParser();
+ ~XmlFileParser();
+
+ void read(const RTCString &strFilename, Document &doc);
+
+private:
+ /* Obscure class data */
+ struct Data;
+ struct Data *m;
+
+ static int ReadCallback(void *aCtxt, char *aBuf, int aLen) RT_NOTHROW_PROTO;
+ static int CloseCallback(void *aCtxt) RT_NOTHROW_PROTO;
+};
+
+/**
+ * XmlMemWriter
+ */
+class RT_DECL_CLASS XmlMemWriter
+{
+public:
+ XmlMemWriter();
+ ~XmlMemWriter();
+
+ void write(const Document &doc, void** ppvBuf, size_t *pcbSize);
+
+private:
+ void* m_pBuf;
+};
+
+
+/**
+ * XmlStringWriter - writes the XML to an RTCString instance.
+ */
+class RT_DECL_CLASS XmlStringWriter
+{
+public:
+ XmlStringWriter();
+
+ int write(const Document &rDoc, RTCString *pStrDst);
+
+private:
+ static int WriteCallbackForSize(void *pvUser, const char *pachBuf, int cbToWrite) RT_NOTHROW_PROTO;
+ static int WriteCallbackForReal(void *pvUser, const char *pachBuf, int cbToWrite) RT_NOTHROW_PROTO;
+ static int CloseCallback(void *pvUser) RT_NOTHROW_PROTO;
+
+ /** Pointer to the destination string while we're in the write() call. */
+ RTCString *m_pStrDst;
+ /** Set by WriteCallback if we cannot grow the destination string. */
+ bool m_fOutOfMemory;
+};
+
+
+/**
+ * XmlFileWriter
+ */
+class RT_DECL_CLASS XmlFileWriter
+{
+public:
+ XmlFileWriter(Document &doc);
+ ~XmlFileWriter();
+
+ /**
+ * Writes the XML document to the specified file.
+ *
+ * @param pcszFilename The name of the output file.
+ * @param fSafe If @c true, some extra safety precautions will be
+ * taken when writing the file:
+ * -# The file is written with a '-tmp' suffix.
+ * -# It is flushed to disk after writing.
+ * -# Any original file is renamed to '-prev'.
+ * -# The '-tmp' file is then renamed to the
+ * specified name.
+ * -# The directory changes are flushed to disk.
+ * The suffixes are available via s_pszTmpSuff and
+ * s_pszPrevSuff.
+ */
+ void write(const char *pcszFilename, bool fSafe);
+
+ static int WriteCallback(void *aCtxt, const char *aBuf, int aLen) RT_NOTHROW_PROTO;
+ static int CloseCallback(void *aCtxt) RT_NOTHROW_PROTO;
+
+ /** The suffix used by XmlFileWriter::write() for the temporary file. */
+ static const char * const s_pszTmpSuff;
+ /** The suffix used by XmlFileWriter::write() for the previous (backup) file. */
+ static const char * const s_pszPrevSuff;
+
+private:
+ void writeInternal(const char *pcszFilename, bool fSafe);
+
+ /* Obscure class data */
+ struct Data;
+ Data *m;
+};
+
+#if defined(_MSC_VER)
+#pragma warning (default:4251)
+#endif
+
+/** @} */
+
+} // end namespace xml
+
+#endif /* !IPRT_INCLUDED_cpp_xml_h */
+