#pragma once #include #include #include #include #include #include "commons.hpp" namespace xml { class Node; class Attribute; class Document; using xmlattribute = Attribute; using xmlelement = std::shared_ptr; using xmldocument = std::shared_ptr; using xmlelements_map = std::unordered_map; class Attribute { std::string name; std::string text; public: Attribute() = default; Attribute(std::string name, std::string text); const std::string& getName() const; const std::string& getText() const; int64_t asInt() const; double asFloat() const; bool asBool() const; glm::vec2 asVec2() const; glm::vec3 asVec3() const; glm::vec4 asVec4() const; glm::vec4 asColor() const; }; /// @brief XML element class. Text element has tag 'text' and attribute /// 'text' class Node { std::string tag; std::unordered_map attrs; std::vector elements; public: Node(std::string tag); /// @brief Add sub-element void add(const xmlelement& element); /// @brief Set attribute value. Creates attribute if does not exists /// @param name attribute name /// @param text attribute value void set(const std::string& name, const std::string& text); /// @brief Get element tag const std::string& getTag() const; inline bool isText() const { return getTag() == "#"; } inline const std::string& text() const { return attr("#").getText(); } /// @brief Get attribute by name /// @param name attribute name /// @throws std::runtime_error if element has no attribute /// @return xmlattribute - {name, value} const xmlattribute& attr(const std::string& name) const; /// @brief Get attribute by name /// @param name attribute name /// @param def default value will be returned wrapped in xmlattribute /// if element has no attribute /// @return xmlattribute - {name, value} or {name, def} if not found*/ xmlattribute attr(const std::string& name, const std::string& def) const; /// @brief Check if element has attribute /// @param name attribute name bool has(const std::string& name) const; /// @brief Get sub-element by index /// @param index sub-element index /// @throws std::out_of_range if an invalid index given /// @return sub-element xmlelement sub(size_t index); /// @brief Get number of sub-elements size_t size() const; const std::vector& getElements() const; const xmlelements_map& getAttributes() const; }; class Document { xmlelement root = nullptr; std::string version; std::string encoding; public: Document(std::string version, std::string encoding); void setRoot(const xmlelement& element); xmlelement getRoot() const; const std::string& getVersion() const; const std::string& getEncoding() const; }; class Parser : BasicParser { xmldocument document; xmlelement parseOpenTag(); xmlelement parseElement(); void parseDeclaration(); void parseComment(); std::string parseText(); std::string parseXMLName(); public: Parser(std::string_view filename, std::string_view source); xmldocument parse(); }; /// @brief Serialize XML Document to string /// @param document serializing document /// @param nice use human readable format (with indents and line-separators) /// @param indentStr indentation characters sequence (default - 4 spaces) /// @return XML string extern std::string stringify( const xmldocument& document, bool nice = true, const std::string& indentStr = " " ); /// @brief Read XML Document from string /// @param filename file name will be shown in error messages /// @param source xml source code string /// @return xml document extern xmldocument parse( const std::string& filename, const std::string& source ); }