#pragma once // ------------------------------------------------------------------------------------------------ #include "Base/Shared.hpp" // ------------------------------------------------------------------------------------------------ #include "Library/Numeric/LongInt.hpp" // ------------------------------------------------------------------------------------------------ #include #include // ------------------------------------------------------------------------------------------------ namespace SqMod { // ------------------------------------------------------------------------------------------------ using namespace pugi; // ------------------------------------------------------------------------------------------------ class XmlNode; class XmlText; class XmlDocument; class XmlAttribute; class XmlParseResult; /* ------------------------------------------------------------------------------------------------ * Manages a reference counted xml document instance. */ class DocumentRef { // -------------------------------------------------------------------------------------------- friend class XmlDocument; public: // -------------------------------------------------------------------------------------------- typedef xml_document Type; /* The managed type. */ // -------------------------------------------------------------------------------------------- typedef Type* Pointer; /* Pointer to the managed type. */ typedef const Type* ConstPtr; /* Constant pointer to the managed type. */ // -------------------------------------------------------------------------------------------- typedef Type& Reference; /* Reference to the managed type. */ typedef const Type& ConstRef; /* Constant reference to the managed type. */ // -------------------------------------------------------------------------------------------- typedef unsigned int Counter; /* Reference counter type. */ /* -------------------------------------------------------------------------------------------- * Validate the managed handle and throw exception if invalid. */ void Validate() const { if (!m_Ptr) { STHROWF("Invalid XML document reference"); } } private: // -------------------------------------------------------------------------------------------- Pointer m_Ptr; /* The document reader, writer and manager instance. */ Counter* m_Ref; /* Reference count to the managed instance. */ /* -------------------------------------------------------------------------------------------- * Grab a strong reference to a document instance. */ void Grab() { if (m_Ptr) { ++(*m_Ref); } } /* -------------------------------------------------------------------------------------------- * Drop a strong reference to a document instance. */ void Drop() { if (m_Ptr && --(*m_Ref) == 0) { delete m_Ptr; delete m_Ref; m_Ptr = nullptr; m_Ref = nullptr; } } public: /* -------------------------------------------------------------------------------------------- * Default constructor (null). */ DocumentRef() : m_Ptr(nullptr), m_Ref(nullptr) { /* ... */ } /* -------------------------------------------------------------------------------------------- * Base constructor. */ explicit DocumentRef(std::nullptr_t SQ_UNUSED_ARG(p)) //NOLINT (yes, I am using this constructor) : m_Ptr(new Type()) , m_Ref(new Counter(1)) { /* ... */ } /* -------------------------------------------------------------------------------------------- * Copy constructor. */ DocumentRef(const DocumentRef & o) : m_Ptr(o.m_Ptr), m_Ref(o.m_Ref) { Grab(); } /* -------------------------------------------------------------------------------------------- * Move constructor. */ DocumentRef(DocumentRef && o) noexcept : m_Ptr(o.m_Ptr), m_Ref(o.m_Ref) { o.m_Ptr = nullptr; o.m_Ref = nullptr; } /* -------------------------------------------------------------------------------------------- * Destructor. */ ~DocumentRef() { Drop(); } /* -------------------------------------------------------------------------------------------- * Copy assignment operator. */ DocumentRef & operator = (const DocumentRef & o) //NOLINT (yes, I am checking for self assignment!) { if (m_Ptr != o.m_Ptr) { Drop(); m_Ptr = o.m_Ptr; m_Ref = o.m_Ref; Grab(); } return *this; } /* -------------------------------------------------------------------------------------------- * Move assignment operator. */ DocumentRef & operator = (DocumentRef && o) noexcept { if (m_Ptr != o.m_Ptr) { m_Ptr = o.m_Ptr; m_Ref = o.m_Ref; o.m_Ptr = nullptr; o.m_Ref = nullptr; } return *this; } /* -------------------------------------------------------------------------------------------- * Perform an equality comparison between two document instances. */ bool operator == (const DocumentRef & o) const { return (m_Ptr == o.m_Ptr); } /* -------------------------------------------------------------------------------------------- * Perform an inequality comparison between two document instances. */ bool operator != (const DocumentRef & o) const { return (m_Ptr != o.m_Ptr); } /* -------------------------------------------------------------------------------------------- * Implicit conversion to boolean for use in boolean operations. */ operator bool () const //NOLINT (intentionally implicit) { return m_Ptr; } /* -------------------------------------------------------------------------------------------- * Implicit conversion to the managed instance pointer. */ operator Pointer () //NOLINT (intentionally implicit) { return m_Ptr; } /* -------------------------------------------------------------------------------------------- * Implicit conversion to the managed instance pointer. */ operator ConstPtr () const //NOLINT (intentionally implicit) { return m_Ptr; } /* -------------------------------------------------------------------------------------------- * Implicit conversion to the managed instance reference. */ operator Reference () //NOLINT (intentionally implicit) { assert(m_Ptr); return *m_Ptr; } /* -------------------------------------------------------------------------------------------- * Implicit conversion to the managed instance reference. */ operator ConstRef () const //NOLINT (intentionally implicit) { assert(m_Ptr); return *m_Ptr; } /* -------------------------------------------------------------------------------------------- * Member operator for dereferencing the managed pointer. */ Pointer operator -> () const { assert(m_Ptr); return m_Ptr; } /* -------------------------------------------------------------------------------------------- * Indirection operator for obtaining a reference of the managed pointer. */ Reference operator * () const { assert(m_Ptr); return *m_Ptr; } /* -------------------------------------------------------------------------------------------- * Retrieve the number of active references to the managed instance. */ Counter Count() const { return (m_Ptr && m_Ref) ? (*m_Ref) : 0; } }; /* ------------------------------------------------------------------------------------------------ * Allows the user to inspect the result of certain operations and act accordingly. */ class XmlParseResult { // -------------------------------------------------------------------------------------------- friend class XmlDocument; friend class XmlNode; protected: // -------------------------------------------------------------------------------------------- typedef xml_parse_result Result; /* -------------------------------------------------------------------------------------------- * Explicit constructor. */ XmlParseResult(DocumentRef doc, const Result & result) : m_Doc(std::move(doc)), m_Result(result) { /* ... */ } /* -------------------------------------------------------------------------------------------- * Validate the document reference and throw an error if invalid. */ void Validate() const { // Is the documen handle valid? if (!m_Doc) { STHROWF("Invalid XML document reference"); } } private: // --------------------------------------------------------------------------------------------- DocumentRef m_Doc{}; /* The main xml document instance. */ Result m_Result{}; /* The managed parse result. */ public: /* -------------------------------------------------------------------------------------------- * Default constructor. */ XmlParseResult() = default; /* -------------------------------------------------------------------------------------------- * Copy constructor. (disabled) */ XmlParseResult(const XmlParseResult & o) = default; /* -------------------------------------------------------------------------------------------- * Copy assignment operator. (disabled) */ XmlParseResult & operator = (const XmlParseResult & o) = default; /* -------------------------------------------------------------------------------------------- * Used by the script engine to compare two instances of this type. */ Int32 Cmp(const XmlParseResult & o) { if (m_Result.status == o.m_Result.status) { return 0; } else if (m_Result.status > o.m_Result.status) { return 1; } else { return -1; } } /* -------------------------------------------------------------------------------------------- * Used by the script engine to convert an instance of this type to a string. */ CSStr ToString() const { return m_Result.description(); } /* -------------------------------------------------------------------------------------------- * See whether this instance references a valid xml document. */ bool IsValid() const { return m_Doc; } /* -------------------------------------------------------------------------------------------- * Return the number of active references to this document instance. */ Uint32 GetRefCount() const { return m_Doc.Count(); } /* -------------------------------------------------------------------------------------------- * Cast to bool operator. */ bool IsOk() const { return m_Result; } /* -------------------------------------------------------------------------------------------- * Parsing status code. */ Int32 GetStatus() const { return (Int32)m_Result.status; } /* -------------------------------------------------------------------------------------------- * Last parsed offset. (in char_t units from start of input data) */ SQInteger GetOffset() const { return (SQInteger)m_Result.offset; } /* -------------------------------------------------------------------------------------------- * Source document encoding. */ Int32 GetEncoding() const { return m_Result.encoding; } /* -------------------------------------------------------------------------------------------- * Retrieve error description as a string. */ CSStr GetDescription() const { return m_Result.description(); } /* -------------------------------------------------------------------------------------------- * Check the parse result and throw the necessary errors. */ void Check() const { if (m_Result.status != status_ok) { STHROWF("XML parse error [%s]", m_Result.description()); } } }; /* ------------------------------------------------------------------------------------------------ * Class that can read/write and alter the contents of XML files. */ class XmlDocument { protected: // -------------------------------------------------------------------------------------------- typedef xml_document Type; /* -------------------------------------------------------------------------------------------- * See if the document is allowed to overwrite its contents and throw an error if not. */ void CanLoad() const { // Is the document even valid? m_Doc.Validate(); // Are there any other references? if (m_Doc.Count() > 1) { // To load new values now, would mean to cause undefined behavior in existing references STHROWF("Loading is disabled while document is referenced"); } } private: // --------------------------------------------------------------------------------------------- DocumentRef m_Doc; // The main xml document instance. public: /* -------------------------------------------------------------------------------------------- * Default constructor. */ XmlDocument() : m_Doc(nullptr) { /*...*/ } /* -------------------------------------------------------------------------------------------- * Copy constructor. (disabled) */ XmlDocument(const XmlDocument & o) = delete; /* -------------------------------------------------------------------------------------------- * Copy assignment operator. (disabled) */ XmlDocument & operator = (const XmlDocument & o) = delete; /* -------------------------------------------------------------------------------------------- * Used by the script engine to compare two instances of this type. */ Int32 Cmp(const XmlDocument & o) const { if (m_Doc && !o.m_Doc) { return 1; } else if (!m_Doc && o.m_Doc) { return -1; } else if (!m_Doc && !o.m_Doc) { return 0; } if (*m_Doc == *o.m_Doc) { return 0; } else if (*m_Doc > *o.m_Doc) { return 1; } else { return -1; } } /* -------------------------------------------------------------------------------------------- * Used by the script engine to convert an instance of this type to a string. */ CSStr ToString() const { // Do we manage a valid document? if (m_Doc) { return m_Doc->name(); } // Default to an empty name return _SC(""); } /* -------------------------------------------------------------------------------------------- * See whether this instance references a valid xml document. */ bool IsValid() const { return m_Doc; } /* -------------------------------------------------------------------------------------------- * Return the number of active references to this document instance. */ Uint32 GetRefCount() const { return m_Doc.Count(); } /* -------------------------------------------------------------------------------------------- * Removes all nodes, leaving the empty document. */ void Reset() { // Validate the document handle m_Doc.Validate(); // Perform the requested operation m_Doc->reset(); } /* -------------------------------------------------------------------------------------------- * Removes all nodes, then copies the entire contents of the specified document. */ void Reset(const XmlDocument & doc) { // Validate the document handles m_Doc.Validate(); doc.m_Doc.Validate(); // Perform the requested operation m_Doc->reset(*(doc.m_Doc)); } /* -------------------------------------------------------------------------------------------- * Load document from zero-terminated string. (LoadString collides with the windows api) */ XmlParseResult LoadData(CSStr source) { // Make sure that we are allowed to load in data CanLoad(); // Perform the requested operation and return the result return XmlParseResult(m_Doc, m_Doc->load_string(source)); } /* -------------------------------------------------------------------------------------------- * Load document from zero-terminated string. (LoadString collides with the windows api) */ XmlParseResult LoadData(CSStr source, Uint32 options) { // Make sure that we are allowed to load in data CanLoad(); // Perform the requested operation and return the result return XmlParseResult(m_Doc, m_Doc->load_string(source, options)); } /* -------------------------------------------------------------------------------------------- * Load document from file on disk. */ XmlParseResult LoadFile(CSStr filepath) { // Make sure that we are allowed to load in data CanLoad(); // Perform the requested operation and return the result return XmlParseResult(m_Doc, m_Doc->load_file(filepath)); } /* -------------------------------------------------------------------------------------------- * Load document from file on disk. */ XmlParseResult LoadFile(CSStr filepath, Uint32 options) { // Make sure that we are allowed to load in data CanLoad(); // Perform the requested operation and return the result return XmlParseResult(m_Doc, m_Doc->load_file(filepath, options)); } /* -------------------------------------------------------------------------------------------- * Load document from file on disk. */ XmlParseResult LoadFile(CSStr filepath, Uint32 options, Int32 encoding) { // Make sure that we are allowed to load in data CanLoad(); // Perform the requested operation and return the result return XmlParseResult(m_Doc, m_Doc->load_file(filepath, options, static_cast< xml_encoding >(encoding))); } /* -------------------------------------------------------------------------------------------- * Save XML to file on disk. */ void SaveFile(CSStr filepath) { // Validate the document handle m_Doc.Validate(); // Perform the requested operation m_Doc->save_file(filepath); } /* -------------------------------------------------------------------------------------------- * Save XML to file on disk. */ void SaveFile(CSStr filepath, CSStr indent) { // Validate the document handle m_Doc.Validate(); // Perform the requested operation m_Doc->save_file(filepath, indent); } /* -------------------------------------------------------------------------------------------- * Save XML to file on disk. */ void SaveFile(CSStr filepath, CSStr indent, Uint32 format) { // Validate the document handle m_Doc.Validate(); // Perform the requested operation m_Doc->save_file(filepath, indent, format); } /* -------------------------------------------------------------------------------------------- * Save XML to file on disk. */ void SaveFile(CSStr filepath, CSStr indent, Uint32 format, Int32 encoding) { // Validate the document handle m_Doc.Validate(); // Perform the requested operation m_Doc->save_file(filepath, indent, format, static_cast< xml_encoding >(encoding)); } /* -------------------------------------------------------------------------------------------- * Retrieve the document root node. */ XmlNode GetNode() const; }; /* ------------------------------------------------------------------------------------------------ * A light-weight handle for manipulating nodes in DOM tree. */ class XmlNode { // -------------------------------------------------------------------------------------------- friend class XmlDocument; friend class XmlText; protected: // -------------------------------------------------------------------------------------------- typedef xml_node Type; /* -------------------------------------------------------------------------------------------- * Explicit constructor. */ XmlNode(DocumentRef doc, const Type & node) : m_Doc(std::move(doc)), m_Node(node) { /* ... */ } private: // --------------------------------------------------------------------------------------------- DocumentRef m_Doc{}; // The main xml document instance. Type m_Node{}; // The managed document node. public: /* -------------------------------------------------------------------------------------------- * Default constructor. */ XmlNode() = default; /* -------------------------------------------------------------------------------------------- * Copy constructor. (disabled) */ XmlNode(const XmlNode & o) = default; /* -------------------------------------------------------------------------------------------- * Copy assignment operator. (disabled) */ XmlNode & operator = (const XmlNode & o) = default; /* -------------------------------------------------------------------------------------------- * Used by the script engine to compare two instances of this type. */ Int32 Cmp(const XmlNode & o) { if (m_Node == o.m_Node) { return 0; } else if (m_Node > o.m_Node) { return 1; } else { return -1; } } /* -------------------------------------------------------------------------------------------- * Used by the script engine to convert an instance of this type to a string. */ CSStr ToString() const { return m_Node.value(); } /* -------------------------------------------------------------------------------------------- * See whether this instance references a valid xml document. */ bool IsValid() const { return m_Doc; } /* -------------------------------------------------------------------------------------------- * Return the number of active references to this document instance. */ Uint32 GetRefCount() const { return m_Doc.Count(); } /* -------------------------------------------------------------------------------------------- * See whether the node is empty. */ bool IsEmpty() const { return m_Node.empty(); } /* -------------------------------------------------------------------------------------------- * Get hash value (unique for handles to the same object). */ SQInteger GetHashValue() const { return static_cast< SQInteger >(m_Node.hash_value()); } /* -------------------------------------------------------------------------------------------- * Get node offset in parsed file/string (in char_t units) for debugging purposes. */ SQInteger GetOffsetDebug() const { return static_cast< SQInteger >(m_Node.offset_debug()); } /* -------------------------------------------------------------------------------------------- * Retrieve node type. */ Int32 GetType() const { return static_cast< Int32 >(m_Node.type()); } /* -------------------------------------------------------------------------------------------- * Retrieve node name. */ CSStr GetName() const { return m_Node.name(); } /* -------------------------------------------------------------------------------------------- * Retrieve node name. */ void SetName(CSStr name) { if (!m_Node.set_name(name)) { STHROWF("Unable to set XML node name"); } } /* -------------------------------------------------------------------------------------------- * Modify the node name. */ bool ApplyName(CSStr name) { return m_Node.set_name(name); } /* -------------------------------------------------------------------------------------------- * Retrieve node value. */ CSStr GetValue() const { return m_Node.value(); } /* -------------------------------------------------------------------------------------------- * Retrieve node value. */ void SetValue(CSStr name) { if (!m_Node.set_value(name)) { STHROWF("Unable to set XML node value"); } } /* -------------------------------------------------------------------------------------------- * Modify the node value. */ bool ApplyValue(CSStr value) { return m_Node.set_value(value); } /* -------------------------------------------------------------------------------------------- * Parses buffer as an XML document fragment and appends all nodes as children of this node. */ XmlParseResult AppendBuffer(CSStr source) { // Is the specified source buffer even valid? if (source) { return XmlParseResult(m_Doc, m_Node.append_buffer(source, std::char_traits< SQChar >::length(source) * sizeof(SQChar))); } // Return the default result return XmlParseResult(); } /* -------------------------------------------------------------------------------------------- * Parses buffer as an XML document fragment and appends all nodes as children of this node. */ XmlParseResult AppendBuffer(CSStr source, Uint32 options) { // Is the specified source buffer even valid? if (source) { return XmlParseResult(m_Doc, m_Node.append_buffer(source, std::char_traits< SQChar >::length(source) * sizeof(SQChar), options)); } // Return the default result return XmlParseResult(); } /* -------------------------------------------------------------------------------------------- * Parses buffer as an XML document fragment and appends all nodes as children of this node. */ XmlParseResult AppendBuffer(CSStr source, Uint32 options, Int32 encoding) { // Is the specified source buffer even valid? if (source) { return XmlParseResult(m_Doc, m_Node.append_buffer(source, std::char_traits< SQChar >::length(source) * sizeof(SQChar), options, (xml_encoding)encoding)); } // Return the default result return XmlParseResult(); } /* -------------------------------------------------------------------------------------------- * Retrieve the first child attribute. */ XmlAttribute GetFirstAttr() const; /* -------------------------------------------------------------------------------------------- * Retrieve the last child attribute. */ XmlAttribute GetLastAttr() const; /* -------------------------------------------------------------------------------------------- * Retrieve the first child node. */ XmlNode GetFirstChild() const { return XmlNode(m_Doc, m_Node.first_child()); } /* -------------------------------------------------------------------------------------------- * Retrieve the last child node. */ XmlNode GetLastChild() const { return XmlNode(m_Doc, m_Node.last_child()); } /* -------------------------------------------------------------------------------------------- * Get next sibling in the children list of the parent node. */ XmlNode GetNextSibling() const { return XmlNode(m_Doc, m_Node.next_sibling()); } /* -------------------------------------------------------------------------------------------- * Get previous sibling in the children list of the parent node */ XmlNode GetPrevSibling() const { return XmlNode(m_Doc, m_Node.previous_sibling()); } /* -------------------------------------------------------------------------------------------- * Retrieve the parent node. */ XmlNode GetParent() const { return XmlNode(m_Doc, m_Node.parent()); } /* -------------------------------------------------------------------------------------------- * Retrieve the root node. */ XmlNode GetRoot() const { return XmlNode(m_Doc, m_Node.root()); } /* -------------------------------------------------------------------------------------------- * Retrieve the text object for the current node. */ XmlText GetText() const; /* -------------------------------------------------------------------------------------------- * Retrieve child node with the specified name. */ XmlNode Child(CSStr name) const { return XmlNode(m_Doc, m_Node.child(name)); } /* -------------------------------------------------------------------------------------------- * Retrieve child attribute with the specified name. */ XmlAttribute GetAttribute(CSStr name) const; /* -------------------------------------------------------------------------------------------- * Retrieve next sibling with the specified name. */ XmlNode NextSibling(CSStr name) const { return XmlNode(m_Doc, m_Node.next_sibling(name)); } /* -------------------------------------------------------------------------------------------- * Retrieve previous sibling with the specified name. */ XmlNode PrevSibling(CSStr name) const { return XmlNode(m_Doc, m_Node.previous_sibling(name)); } /* -------------------------------------------------------------------------------------------- * Retrieve child attribute, starting the search from a hint. */ XmlAttribute AttributeFrom(CSStr name, XmlAttribute & attr) const; /* -------------------------------------------------------------------------------------------- * Get child value of current node; that is, value of the first child node of type PCDATA/CDATA. */ CSStr GetChildValue() const { return m_Node.child_value(); } /* -------------------------------------------------------------------------------------------- * Retrieve child value of child with specified name. */ CSStr ChildValue(CSStr name) const { return m_Node.child_value(name); } /* -------------------------------------------------------------------------------------------- * Append a child attribute with the specified name. */ XmlAttribute AppendAttr(CSStr name); /* -------------------------------------------------------------------------------------------- * Prepend a child attribute with the specified name. */ XmlAttribute PrependAttr(CSStr name); /* -------------------------------------------------------------------------------------------- * Insert a child attribute with the specified name, after the specified node. */ XmlAttribute InsertAttrAfter(CSStr name, const XmlAttribute & attr); /* -------------------------------------------------------------------------------------------- * Insert a child attribute with the specified name, before the specified node. */ XmlAttribute InsertAttrBefore(CSStr name, const XmlAttribute & attr); /* -------------------------------------------------------------------------------------------- * Append a copy of the specified attribute as a child. */ XmlAttribute AppendAttrCopy(const XmlAttribute & proto); /* -------------------------------------------------------------------------------------------- * Prepend a copy of the specified attribute as a child. */ XmlAttribute PrependAttrCopy(const XmlAttribute & proto); /* -------------------------------------------------------------------------------------------- * Insert a copy of the specified attribute as a child after the specified attribute. */ XmlAttribute InsertAttrCopyAfter(const XmlAttribute & proto, const XmlAttribute & attr); /* -------------------------------------------------------------------------------------------- * Insert a copy of the specified attribute as a child before the specified attribute. */ XmlAttribute InsertAttrCopyBefore(const XmlAttribute & proto, const XmlAttribute & attr); /* -------------------------------------------------------------------------------------------- * Append a basic child node with the specified name. */ XmlNode AppendChild(CSStr name) { return XmlNode(m_Doc, m_Node.append_child(name)); } /* -------------------------------------------------------------------------------------------- * Prepend a basic child node with the specified name. */ XmlNode PrependChild(CSStr name) { return XmlNode(m_Doc, m_Node.prepend_child(name)); } /* -------------------------------------------------------------------------------------------- * Append a basic child node. */ XmlNode AppendChildNode() { return XmlNode(m_Doc, m_Node.append_child()); } /* -------------------------------------------------------------------------------------------- * Prepend a basic child node. */ XmlNode PrependChildNode() { return XmlNode(m_Doc, m_Node.prepend_child()); } /* -------------------------------------------------------------------------------------------- * Append a basic child node with the specified type. */ XmlNode AppendChildType(Int32 type) { return XmlNode(m_Doc, m_Node.append_child(static_cast< xml_node_type >(type))); } /* -------------------------------------------------------------------------------------------- * Prepend a basic child node with the specified type. */ XmlNode PrependChildType(Int32 type) { return XmlNode(m_Doc, m_Node.prepend_child(static_cast< xml_node_type >(type))); } /* -------------------------------------------------------------------------------------------- * Insert a basic child node with the specified name, after the specified node. */ XmlNode InsertChildAfter(CSStr name, const XmlNode & node) { return XmlNode(m_Doc, m_Node.insert_child_after(name, node.m_Node)); } /* -------------------------------------------------------------------------------------------- * Insert a basic child node with the specified name, before the specified node. */ XmlNode InsertChildBefore(CSStr name, const XmlNode & node) { return XmlNode(m_Doc, m_Node.insert_child_before(name, node.m_Node)); } /* -------------------------------------------------------------------------------------------- * Insert a basic child node with the specified type, after the specified node. */ XmlNode InsertChildTypeAfter(Int32 type, const XmlNode & node) { return XmlNode(m_Doc, m_Node.insert_child_after(static_cast< xml_node_type >(type), node.m_Node)); } /* -------------------------------------------------------------------------------------------- * Insert a basic child node with the specified type, before the specified node. */ XmlNode InsertChildTypeBefore(Int32 type, const XmlNode & node) { return XmlNode(m_Doc, m_Node.insert_child_before(static_cast< xml_node_type >(type), node.m_Node)); } /* -------------------------------------------------------------------------------------------- * Append a copy of the specified node as a child. */ XmlNode AppendCopy(const XmlNode & proto) { return XmlNode(m_Doc, m_Node.append_copy(proto.m_Node)); } /* -------------------------------------------------------------------------------------------- * Prepend a copy of the specified node as a child. */ XmlNode PrependCopy(const XmlNode & proto) { return XmlNode(m_Doc, m_Node.prepend_copy(proto.m_Node)); } /* -------------------------------------------------------------------------------------------- * Insert a copy of the specified node as a child after the specified node. */ XmlNode InsertCopyAfter(const XmlNode & proto, const XmlNode & node) { return XmlNode(m_Doc, m_Node.insert_copy_after(proto.m_Node, node.m_Node)); } /* -------------------------------------------------------------------------------------------- * Insert a copy of the specified node as a child before the specified node. */ XmlNode InsertCopyBefore(const XmlNode & proto, const XmlNode & node) { return XmlNode(m_Doc, m_Node.insert_copy_before(proto.m_Node, node.m_Node)); } /* -------------------------------------------------------------------------------------------- * Append the specified node as a child and take ownership of it. */ XmlNode AppendMove(const XmlNode & proto) { return XmlNode(m_Doc, m_Node.append_copy(proto.m_Node)); } /* -------------------------------------------------------------------------------------------- * Prepend the specified node as a child and take ownership of it. */ XmlNode PrependMove(const XmlNode & proto) { return XmlNode(m_Doc, m_Node.prepend_copy(proto.m_Node)); } /* -------------------------------------------------------------------------------------------- * Insert the specified node as a child after the specified node and take ownership of it. */ XmlNode InsertMoveAfter(const XmlNode & proto, const XmlNode & node) { return XmlNode(m_Doc, m_Node.insert_copy_after(proto.m_Node, node.m_Node)); } /* -------------------------------------------------------------------------------------------- * Insert the specified node as a child before the specified node and take ownership of it. */ XmlNode InsertMoveBefore(const XmlNode & proto, const XmlNode & node) { return XmlNode(m_Doc, m_Node.insert_copy_before(proto.m_Node, node.m_Node)); } /* -------------------------------------------------------------------------------------------- * Remove the child attribute matching the specified name. */ bool RemoveAttr(CSStr name) { return m_Node.remove_attribute(name); } /* -------------------------------------------------------------------------------------------- * Remove the specified attribute. */ bool RemoveAttrInst(const XmlAttribute & attr); /* -------------------------------------------------------------------------------------------- * Remove the child node matching the specified name. */ bool RemoveChild(CSStr name) { return m_Node.remove_child(name); } /* -------------------------------------------------------------------------------------------- * Remove the specified node. */ bool RemoveChildInst(const XmlNode & node) { return m_Node.remove_child(node.m_Node); } /* -------------------------------------------------------------------------------------------- * Find child node by attribute name/value. */ XmlNode FindChildByAttr(CSStr attr_name, CSStr attr_value) const { return XmlNode(m_Doc, m_Node.find_child_by_attribute(attr_name, attr_value)); } /* -------------------------------------------------------------------------------------------- * Find child node by attribute name/value. */ XmlNode FindChildByAttr(CSStr name, CSStr attr_name, CSStr attr_value) const { return XmlNode(m_Doc, m_Node.find_child_by_attribute(name, attr_name, attr_value)); } /* -------------------------------------------------------------------------------------------- * Search for a node by path consisting of node names and . or .. elements. */ XmlNode FindElemByPath(CSStr path, SQChar delimiter) const { return XmlNode(m_Doc, m_Node.first_element_by_path(path, delimiter)); } }; /* ------------------------------------------------------------------------------------------------ * A light-weight handle for manipulating attributes in DOM tree. */ class XmlAttribute { // -------------------------------------------------------------------------------------------- friend class XmlNode; protected: // -------------------------------------------------------------------------------------------- typedef xml_attribute Type; /* -------------------------------------------------------------------------------------------- * Explicit constructor. */ XmlAttribute(DocumentRef doc, const Type & attr) : m_Doc(std::move(doc)), m_Attr(attr) { /* ... */ } private: // --------------------------------------------------------------------------------------------- DocumentRef m_Doc{}; // The main xml document instance. Type m_Attr{}; // The managed node attribute. public: /* -------------------------------------------------------------------------------------------- * Default constructor. */ XmlAttribute() = default; /* -------------------------------------------------------------------------------------------- * Copy constructor. (disabled) */ XmlAttribute(const XmlAttribute & o) = default; /* -------------------------------------------------------------------------------------------- * Copy assignment operator. (disabled) */ XmlAttribute & operator = (const XmlAttribute & o) = default; /* -------------------------------------------------------------------------------------------- * Used by the script engine to compare two instances of this type. */ Int32 Cmp(const XmlAttribute & o) { if (m_Attr == o.m_Attr) { return 0; } else if (m_Attr > o.m_Attr) { return 1; } else { return -1; } } /* -------------------------------------------------------------------------------------------- * Used by the script engine to convert an instance of this type to a string. */ CSStr ToString() const { return m_Attr.value(); } /* -------------------------------------------------------------------------------------------- * See whether this instance references a valid xml document. */ bool IsValid() const { return m_Doc; } /* -------------------------------------------------------------------------------------------- * Return the number of active references to this document instance. */ Uint32 GetRefCount() const { return m_Doc.Count(); } /* -------------------------------------------------------------------------------------------- * See whether the attribute is empty. */ bool IsEmpty() const { return m_Attr.empty(); } /* -------------------------------------------------------------------------------------------- * Get hash value (unique for handles to the same object). */ SQInteger GetHashValue() const { return (SQInteger)m_Attr.hash_value(); } /* -------------------------------------------------------------------------------------------- * Retrieve attribute name. */ CSStr GetName() const { return m_Attr.name(); } /* -------------------------------------------------------------------------------------------- * Retrieve attribute name. */ void SetName(CSStr name) { if (!m_Attr.set_name(name)) { STHROWF("Unable to set XML attribute name"); } } /* -------------------------------------------------------------------------------------------- * Modify the attribute name. */ bool ApplyName(CSStr name) { return m_Attr.set_name(name); } /* -------------------------------------------------------------------------------------------- * Retrieve attribute value. */ CSStr GetValue() const { return m_Attr.value(); } /* -------------------------------------------------------------------------------------------- * Retrieve attribute value. */ void SetValue(CSStr name) { if (!m_Attr.set_value(name)) { STHROWF("Unable to set XML attribute value"); } } /* -------------------------------------------------------------------------------------------- * Modify the attribute value. */ bool ApplyValue(CSStr value) { return m_Attr.set_value(value); } /* -------------------------------------------------------------------------------------------- * Retrieve the value as a string or the specified default value if empty. */ CSStr AsString(CSStr def) const { return m_Attr.as_string(def); } /* -------------------------------------------------------------------------------------------- * Retrieve the value as a integer or the specified default value if empty. */ Int32 AsInt(Int32 def) const { return m_Attr.as_int(def); } /* -------------------------------------------------------------------------------------------- * Retrieve the value as a unsigned integer or the specified default value if empty. */ Uint32 AsUint(Uint32 def) const { return m_Attr.as_uint(def); } /* -------------------------------------------------------------------------------------------- * Retrieve the value as a floating point or the specified default value if empty. */ SQFloat AsFloat(SQFloat def) const { return (SQFloat)m_Attr.as_float(def); } /* -------------------------------------------------------------------------------------------- * Retrieve the value as a double floating point or the specified default value if empty. */ SQFloat AsDouble(SQFloat def) const { return (SQFloat)m_Attr.as_double(def); } /* -------------------------------------------------------------------------------------------- * Retrieve the value as a long integer or the specified default value if empty. */ LightObj AsLong(const SLongInt & def) const; /* -------------------------------------------------------------------------------------------- * Retrieve the value as a unsigned long integer or the specified default value if empty. */ LightObj AsUlong(const ULongInt & def) const; /* -------------------------------------------------------------------------------------------- * Retrieve the value as a boolean or the specified default value if empty. */ bool AsBool(bool def) const { return m_Attr.as_bool(def); } /* -------------------------------------------------------------------------------------------- * Modify the value as a string. */ bool ApplyString(CSStr value) { return m_Attr.set_value(value); } /* -------------------------------------------------------------------------------------------- * Modify the value as a integer. */ bool ApplyInt(Int32 value) { return m_Attr.set_value(value); } /* -------------------------------------------------------------------------------------------- * Modify the value as a unsigned integer. */ bool ApplyUint(Uint32 value) { return m_Attr.set_value(value); } /* -------------------------------------------------------------------------------------------- * Modify the value as a floating point. */ bool ApplyFloat(SQFloat value) { return m_Attr.set_value(value); } /* -------------------------------------------------------------------------------------------- * Modify the value as a double floating point. */ bool ApplyDouble(SQFloat value) { return m_Attr.set_value(value); } /* -------------------------------------------------------------------------------------------- * Modify the value as a long integer. */ bool ApplyLong(const SLongInt & value); /* -------------------------------------------------------------------------------------------- * Modify the value as a unsigned long integer. */ bool ApplyUlong(const ULongInt & value); /* -------------------------------------------------------------------------------------------- * Modify the value as a boolean. */ bool ApplyBool(bool value) { return m_Attr.set_value(value); } /* -------------------------------------------------------------------------------------------- * Retrieve the value as a string. */ CSStr GetString() const { return m_Attr.as_string(); } /* -------------------------------------------------------------------------------------------- * Modify the value as a string. */ void SetString(CSStr value) { m_Attr = value; } /* -------------------------------------------------------------------------------------------- * Retrieve the value as a integer. */ Int32 GetInt() const { return m_Attr.as_int(); } /* -------------------------------------------------------------------------------------------- * Modify the value as a integer. */ void SetInt(Int32 value) { m_Attr = value; } /* -------------------------------------------------------------------------------------------- * Retrieve the value as a unsigned integer. */ Uint32 GetUint() const { return m_Attr.as_uint(); } /* -------------------------------------------------------------------------------------------- * Modify the value as a unsigned integer. */ void SetUint(Uint32 value) { m_Attr = value; } /* -------------------------------------------------------------------------------------------- * Retrieve the value as a floating point. */ SQFloat GetFloat() const { return static_cast< SQFloat >(m_Attr.as_float()); } /* -------------------------------------------------------------------------------------------- * Modify the value as a floating point. */ void SetFloat(SQFloat value) { m_Attr = value; } /* -------------------------------------------------------------------------------------------- * Retrieve the value as a double floating point. */ SQFloat GetDouble() const { return static_cast< SQFloat >(m_Attr.as_double()); } /* -------------------------------------------------------------------------------------------- * Modify the value as a double floating point. */ void SetDouble(SQFloat value) { m_Attr = value; } /* -------------------------------------------------------------------------------------------- * Retrieve the value as a long integer. */ LightObj GetLong() const; /* -------------------------------------------------------------------------------------------- * Modify the value as a long integer. */ void SetLong(const SLongInt & value); /* -------------------------------------------------------------------------------------------- * Retrieve the value as a unsigned long integer. */ LightObj GetUlong() const; /* -------------------------------------------------------------------------------------------- * Modify the value as a unsigned long integer. */ void SetUlong(const ULongInt & value); /* -------------------------------------------------------------------------------------------- * Retrieve the value as a boolean. */ bool GetBool() const { return m_Attr.as_bool(); } /* -------------------------------------------------------------------------------------------- * Modify the value as a boolean. */ void SetBool(bool value) { m_Attr = value; } /* -------------------------------------------------------------------------------------------- * Retrieve next attribute in the attribute list of the parent node. */ XmlAttribute NextAttribute() const { return XmlAttribute(m_Doc, m_Attr.next_attribute()); } /* -------------------------------------------------------------------------------------------- * Retrieve previous attribute in the attribute list of the parent node. */ XmlAttribute PrevAttribute() const { return XmlAttribute(m_Doc, m_Attr.previous_attribute()); } }; /* ------------------------------------------------------------------------------------------------ * A helper for working with text inside PCDATA nodes. */ class XmlText { // -------------------------------------------------------------------------------------------- friend class XmlNode; protected: // -------------------------------------------------------------------------------------------- typedef xml_text Type; /* -------------------------------------------------------------------------------------------- * Explicit constructor. */ XmlText(DocumentRef doc, const Type & text) : m_Doc(std::move(doc)), m_Text(text) { /* ... */ } private: // --------------------------------------------------------------------------------------------- DocumentRef m_Doc{}; // The main xml document instance. Type m_Text{}; // The managed document node. public: /* -------------------------------------------------------------------------------------------- * Default constructor. */ XmlText() = default; /* -------------------------------------------------------------------------------------------- * Copy constructor. (disabled) */ XmlText(const XmlText & o) = default; /* -------------------------------------------------------------------------------------------- * Copy assignment operator. (disabled) */ XmlText & operator = (const XmlText & o) = default; /* -------------------------------------------------------------------------------------------- * Used by the script engine to compare two instances of this type. */ Int32 Cmp(const XmlText & o) { if (strcmp(m_Text.get(), o.m_Text.get()) == 0) { return 0; } else if (strlen(m_Text.get()) > strlen(o.m_Text.get())) { return 1; } else { return -1; } } /* -------------------------------------------------------------------------------------------- * Used by the script engine to convert an instance of this type to a string. */ CSStr ToString() const { return m_Text.get(); } /* -------------------------------------------------------------------------------------------- * See whether this instance references a valid xml document. */ bool IsValid() const { return m_Doc; } /* -------------------------------------------------------------------------------------------- * Return the number of active references to this document instance. */ Uint32 GetRefCount() const { return m_Doc.Count(); } /* -------------------------------------------------------------------------------------------- * See whether the text is empty. */ bool IsEmpty() const { return m_Text.empty(); } /* -------------------------------------------------------------------------------------------- * Retrieve the text value. */ CSStr GetValue() const { return m_Text.get(); } /* -------------------------------------------------------------------------------------------- * Retrieve the value as a string or the specified default value if empty. */ CSStr AsString(CSStr def) const { return m_Text.as_string(def); } /* -------------------------------------------------------------------------------------------- * Retrieve the value as a integer or the specified default value if empty. */ Int32 AsInt(Int32 def) const { return m_Text.as_int(def); } /* -------------------------------------------------------------------------------------------- * Retrieve the value as a unsigned integer or the specified default value if empty. */ Uint32 AsUint(Uint32 def) const { return m_Text.as_uint(def); } /* -------------------------------------------------------------------------------------------- * Retrieve the value as a floating point or the specified default value if empty. */ SQFloat AsFloat(SQFloat def) const { return static_cast< SQFloat >(m_Text.as_float(static_cast< Float32 >(def))); } /* -------------------------------------------------------------------------------------------- * Retrieve the value as a double floating point or the specified default value if empty. */ SQFloat AsDouble(SQFloat def) const { return static_cast< SQFloat >(m_Text.as_double(static_cast< Float64 >(def))); } /* -------------------------------------------------------------------------------------------- * Retrieve the value as a long integer or the specified default value if empty. */ LightObj AsLong(const SLongInt & def) const; /* -------------------------------------------------------------------------------------------- * Retrieve the value as a unsigned long integer or the specified default value if empty. */ LightObj AsUlong(const ULongInt & def) const; /* -------------------------------------------------------------------------------------------- * Retrieve the value as a boolean or the specified default value if empty. */ bool AsBool(bool def) const { return m_Text.as_bool(def); } /* -------------------------------------------------------------------------------------------- * Modify the value as a string. */ bool ApplyString(CSStr value) { return m_Text.set(value); } /* -------------------------------------------------------------------------------------------- * Modify the value as a integer. */ bool ApplyInt(Int32 value) { return m_Text.set(value); } /* -------------------------------------------------------------------------------------------- * Modify the value as a unsigned integer. */ bool ApplyUint(Uint32 value) { return m_Text.set(value); } /* -------------------------------------------------------------------------------------------- * Modify the value as a floating point. */ bool ApplyFloat(SQFloat value) { return m_Text.set(value); } /* -------------------------------------------------------------------------------------------- * Modify the value as a double floating point. */ bool ApplyDouble(SQFloat value) { return m_Text.set(value); } /* -------------------------------------------------------------------------------------------- * Modify the value as a long integer. */ bool ApplyLong(const SLongInt & value); /* -------------------------------------------------------------------------------------------- * Modify the value as a unsigned long integer. */ bool ApplyUlong(const ULongInt & value); /* -------------------------------------------------------------------------------------------- * Modify the value as a boolean. */ bool ApplyBool(bool value) { return m_Text.set(value); } /* -------------------------------------------------------------------------------------------- * Retrieve the value as a string. */ CSStr GetString() const { return m_Text.as_string(); } /* -------------------------------------------------------------------------------------------- * Modify the value as a string. */ void SetString(CSStr value) { m_Text = value; } /* -------------------------------------------------------------------------------------------- * Retrieve the value as a integer. */ Int32 GetInt() const { return m_Text.as_int(); } /* -------------------------------------------------------------------------------------------- * Modify the value as a integer. */ void SetInt(Int32 value) { m_Text = value; } /* -------------------------------------------------------------------------------------------- * Retrieve the value as a unsigned integer. */ Uint32 GetUint() const { return m_Text.as_uint(); } /* -------------------------------------------------------------------------------------------- * Modify the value as a unsigned integer. */ void SetUint(Uint32 value) { m_Text = value; } /* -------------------------------------------------------------------------------------------- * Retrieve the value as a floating point. */ SQFloat GetFloat() const { return static_cast< SQFloat >(m_Text.as_float()); } /* -------------------------------------------------------------------------------------------- * Modify the value as a floating point. */ void SetFloat(SQFloat value) { m_Text = value; } /* -------------------------------------------------------------------------------------------- * Retrieve the value as a double floating point. */ SQFloat GetDouble() const { return static_cast< SQFloat >(m_Text.as_double()); } /* -------------------------------------------------------------------------------------------- * Modify the value as a double floating point. */ void SetDouble(SQFloat value) { m_Text = value; } /* -------------------------------------------------------------------------------------------- * Retrieve the value as a long integer. */ LightObj GetLong() const; /* -------------------------------------------------------------------------------------------- * Modify the value as a long integer. */ void SetLong(const SLongInt & value); /* -------------------------------------------------------------------------------------------- * Retrieve the value as a unsigned long integer. */ LightObj GetUlong() const; /* -------------------------------------------------------------------------------------------- * Modify the value as a unsigned long integer. */ void SetUlong(const ULongInt & value); /* -------------------------------------------------------------------------------------------- * Retrieve the value as a boolean. */ bool GetBool() const { return m_Text.as_bool(); } /* -------------------------------------------------------------------------------------------- * Modify the value as a boolean. */ void SetBool(bool value) { m_Text = value; } /* -------------------------------------------------------------------------------------------- * Retrieve the data node (node_pcdata or node_cdata) for this object. */ XmlNode GetData() const; }; } // Namespace:: SqMod