From 1a6111e3f02b0a9cff0f81fb524b4dfb7d69854b Mon Sep 17 00:00:00 2001 From: Yuqian Yang Date: Tue, 4 Nov 2025 21:59:42 +0800 Subject: Move xml to base. --- include/cru/base/xml/XmlNode.h | 138 ++++++++++++++++ include/cru/base/xml/XmlParser.h | 43 +++++ include/cru/ui/ThemeResourceDictionary.h | 2 +- include/cru/ui/mapper/BorderStyleMapper.h | 2 +- include/cru/ui/mapper/BrushMapper.h | 2 +- include/cru/ui/mapper/ColorMapper.h | 2 +- include/cru/ui/mapper/CursorMapper.h | 2 +- include/cru/ui/mapper/Mapper.h | 2 +- include/cru/ui/mapper/ThicknessMapper.h | 2 +- include/cru/ui/mapper/style/BorderStylerMapper.h | 2 +- include/cru/ui/mapper/style/IConditionMapper.h | 2 +- include/cru/ui/mapper/style/IStylerMapper.h | 2 +- include/cru/ui/mapper/style/NoConditionMapper.h | 2 +- include/cru/ui/mapper/style/StyleRuleMapper.h | 2 +- include/cru/xml/Base.h | 11 -- include/cru/xml/XmlNode.h | 158 ------------------ include/cru/xml/XmlParser.h | 44 ----- src/CMakeLists.txt | 1 - src/base/CMakeLists.txt | 2 + src/base/xml/XmlNode.cpp | 75 +++++++++ src/base/xml/XmlParser.cpp | 197 +++++++++++++++++++++++ src/ui/CMakeLists.txt | 2 +- src/ui/ThemeResourceDictionary.cpp | 4 +- src/ui/mapper/BorderStyleMapper.cpp | 2 +- src/ui/mapper/BrushMapper.cpp | 2 +- src/ui/mapper/StringMapper.cpp | 2 +- src/ui/mapper/ThicknessMapper.cpp | 2 +- src/ui/mapper/style/AndConditionMapper.cpp | 2 +- src/ui/mapper/style/BorderStylerMapper.cpp | 2 +- src/ui/mapper/style/CheckedConditionMapper.cpp | 2 +- src/ui/mapper/style/ContentBrushStylerMapper.cpp | 2 +- src/ui/mapper/style/FocusConditionMapper.cpp | 2 +- src/ui/mapper/style/NoConditionMapper.cpp | 2 +- src/ui/mapper/style/OrConditionMapper.cpp | 2 +- src/xml/CMakeLists.txt | 6 - src/xml/XmlNode.cpp | 76 --------- src/xml/XmlParser.cpp | 196 ---------------------- test/CMakeLists.txt | 1 - test/base/CMakeLists.txt | 1 + test/base/xml/ParserTest.cpp | 116 +++++++++++++ test/xml/CMakeLists.txt | 6 - test/xml/ParserTest.cpp | 116 ------------- 42 files changed, 598 insertions(+), 641 deletions(-) create mode 100644 include/cru/base/xml/XmlNode.h create mode 100644 include/cru/base/xml/XmlParser.h delete mode 100644 include/cru/xml/Base.h delete mode 100644 include/cru/xml/XmlNode.h delete mode 100644 include/cru/xml/XmlParser.h create mode 100644 src/base/xml/XmlNode.cpp create mode 100644 src/base/xml/XmlParser.cpp delete mode 100644 src/xml/CMakeLists.txt delete mode 100644 src/xml/XmlNode.cpp delete mode 100644 src/xml/XmlParser.cpp create mode 100644 test/base/xml/ParserTest.cpp delete mode 100644 test/xml/CMakeLists.txt delete mode 100644 test/xml/ParserTest.cpp diff --git a/include/cru/base/xml/XmlNode.h b/include/cru/base/xml/XmlNode.h new file mode 100644 index 00000000..e9090b40 --- /dev/null +++ b/include/cru/base/xml/XmlNode.h @@ -0,0 +1,138 @@ +#pragma once + +#include "../Base.h" +#include "../StringUtil.h" + +namespace cru::xml { +class XmlElementNode; +class XmlTextNode; +class XmlCommentNode; + +class CRU_BASE_API XmlNode : public Object { + friend XmlElementNode; + + public: + enum class Type { Text, Element, Comment }; + + protected: + explicit XmlNode(Type type) : type_(type) {} + + public: + Type GetType() const { return type_; } + XmlElementNode* GetParent() const { return parent_; } + + virtual XmlNode* Clone() const = 0; + + bool IsTextNode() const { return type_ == Type::Text; } + bool IsElementNode() const { return type_ == Type::Element; } + bool IsCommentNode() const { return type_ == Type::Comment; } + + XmlElementNode* AsElement(); + XmlTextNode* AsText(); + XmlCommentNode* AsComment(); + const XmlElementNode* AsElement() const; + const XmlTextNode* AsText() const; + const XmlCommentNode* AsComment() const; + + private: + const Type type_; + XmlElementNode* parent_ = nullptr; +}; + +class CRU_BASE_API XmlTextNode : public XmlNode { + public: + XmlTextNode() : XmlNode(Type::Text) {} + explicit XmlTextNode(std::string text) + : XmlNode(Type::Text), text_(std::move(text)) {} + + public: + std::string GetText() const { return text_; } + void SetText(std::string text) { text_ = std::move(text); } + + XmlNode* Clone() const override { return new XmlTextNode(text_); } + + private: + std::string text_; +}; + +class CRU_BASE_API XmlElementNode : public XmlNode { + public: + XmlElementNode() : XmlNode(Type::Element) {} + explicit XmlElementNode( + std::string tag, + std::unordered_map attributes = {}) + : XmlNode(Type::Element), + tag_(std::move(tag)), + attributes_(std::move(attributes)) {} + + ~XmlElementNode() override; + + public: + std::string GetTag() const { return tag_; } + void SetTag(std::string tag) { tag_ = std::move(tag); } + const std::unordered_map& GetAttributes() const { + return attributes_; + } + void SetAttributes(std::unordered_map attributes) { + attributes_ = std::move(attributes); + } + const std::vector GetChildren() const { return children_; } + + Index GetChildCount() const { return children_.size(); } + std::string GetAttributeValue(const std::string& key) const { + return attributes_.at(key); + } + std::string GetAttributeValueCaseInsensitive(const std::string& key) const { + return *GetOptionalAttributeValueCaseInsensitive(key); + } + std::optional GetOptionalAttributeValue( + const std::string& key) const { + auto it = attributes_.find(key); + if (it == attributes_.end()) { + return std::nullopt; + } + + return it->second; + } + std::optional GetOptionalAttributeValueCaseInsensitive( + const std::string& key) const { + for (auto it = attributes_.begin(); it != attributes_.end(); ++it) { + if (cru::string::CaseInsensitiveCompare(it->first, key) == 0) { + return it->second; + } + } + + return std::nullopt; + } + + XmlNode* GetChildAt(Index index) const { return children_[index]; } + + void AddAttribute(std::string key, std::string value); + void AddChild(XmlNode* child); + + Index GetChildElementCount() const; + XmlElementNode* GetFirstChildElement() const; + + XmlNode* Clone() const override; + + private: + std::string tag_; + std::unordered_map attributes_; + std::vector children_; +}; + +class CRU_BASE_API XmlCommentNode : public XmlNode { + public: + XmlCommentNode() : XmlNode(Type::Comment) {} + explicit XmlCommentNode(std::string text) + : XmlNode(Type::Comment), text_(std::move(text)) {} + + std::string GetText() const { return text_; } + void SetText(std::string text) { text_ = std::move(text); } + + XmlNode* Clone() const override; + + private: + std::string text_; +}; +} // namespace cru::xml diff --git a/include/cru/base/xml/XmlParser.h b/include/cru/base/xml/XmlParser.h new file mode 100644 index 00000000..22b529d7 --- /dev/null +++ b/include/cru/base/xml/XmlParser.h @@ -0,0 +1,43 @@ +#pragma once + +#include "../Exception.h" +#include "XmlNode.h" + +namespace cru::xml { +class CRU_BASE_API XmlParsingException : public Exception { + public: + using Exception::Exception; +}; + +class CRU_BASE_API XmlParser { + public: + explicit XmlParser(std::string xml); + + CRU_DELETE_COPY(XmlParser) + CRU_DELETE_MOVE(XmlParser) + + ~XmlParser(); + + XmlElementNode* Parse(); + + private: + XmlElementNode* DoParse(); + + char16_t Read1(); + std::string ReadWithoutAdvance(int count = 1); + void ReadSpacesAndDiscard(); + std::string ReadSpaces(); + std::string ReadIdenitifier(); + std::string ReadAttributeString(); + + private: + std::string xml_; + + XmlElementNode* cache_ = nullptr; + + // Consider the while file enclosed by a single tag called $root. + XmlElementNode* pseudo_root_node_ = new XmlElementNode("$root"); + XmlElementNode* current_ = pseudo_root_node_; + int current_position_ = 0; +}; +} // namespace cru::xml diff --git a/include/cru/ui/ThemeResourceDictionary.h b/include/cru/ui/ThemeResourceDictionary.h index f28a61db..3995fe25 100644 --- a/include/cru/ui/ThemeResourceDictionary.h +++ b/include/cru/ui/ThemeResourceDictionary.h @@ -2,7 +2,7 @@ #include "Base.h" #include "cru/base/Base.h" -#include "cru/xml/XmlNode.h" +#include "cru/base/xml/XmlNode.h" #include "mapper/MapperRegistry.h" #include "style/StyleRuleSet.h" diff --git a/include/cru/ui/mapper/BorderStyleMapper.h b/include/cru/ui/mapper/BorderStyleMapper.h index d585639f..fd84b0b0 100644 --- a/include/cru/ui/mapper/BorderStyleMapper.h +++ b/include/cru/ui/mapper/BorderStyleMapper.h @@ -1,7 +1,7 @@ #pragma once #include "Mapper.h" #include "cru/ui/style/ApplyBorderStyleInfo.h" -#include "cru/xml/XmlNode.h" +#include "cru/base/xml/XmlNode.h" namespace cru::ui::mapper { class CRU_UI_API BorderStyleMapper diff --git a/include/cru/ui/mapper/BrushMapper.h b/include/cru/ui/mapper/BrushMapper.h index 3b918dc7..3972d6ff 100644 --- a/include/cru/ui/mapper/BrushMapper.h +++ b/include/cru/ui/mapper/BrushMapper.h @@ -2,7 +2,7 @@ #include "Mapper.h" #include "cru/base/Base.h" #include "cru/platform/graphics/Brush.h" -#include "cru/xml/XmlNode.h" +#include "cru/base/xml/XmlNode.h" namespace cru::ui::mapper { class BrushMapper : public BasicSharedPtrMapper { diff --git a/include/cru/ui/mapper/ColorMapper.h b/include/cru/ui/mapper/ColorMapper.h index e3846712..9555bf5a 100644 --- a/include/cru/ui/mapper/ColorMapper.h +++ b/include/cru/ui/mapper/ColorMapper.h @@ -1,6 +1,6 @@ #pragma once #include "Mapper.h" -#include "cru/xml/XmlNode.h" +#include "cru/base/xml/XmlNode.h" namespace cru::ui::mapper { class CRU_UI_API ColorMapper : public BasicMapper { diff --git a/include/cru/ui/mapper/CursorMapper.h b/include/cru/ui/mapper/CursorMapper.h index 951291c1..4d55f849 100644 --- a/include/cru/ui/mapper/CursorMapper.h +++ b/include/cru/ui/mapper/CursorMapper.h @@ -1,7 +1,7 @@ #pragma once #include "Mapper.h" #include "cru/platform/gui/Cursor.h" -#include "cru/xml/XmlNode.h" +#include "cru/base/xml/XmlNode.h" namespace cru::ui::mapper { class CRU_UI_API CursorMapper : public BasicSharedPtrMapper { diff --git a/include/cru/ui/mapper/Mapper.h b/include/cru/ui/mapper/Mapper.h index a4507fc9..e4a4c528 100644 --- a/include/cru/ui/mapper/Mapper.h +++ b/include/cru/ui/mapper/Mapper.h @@ -3,7 +3,7 @@ #include "cru/base/ClonablePtr.h" #include "cru/base/Exception.h" -#include "cru/xml/XmlNode.h" +#include "cru/base/xml/XmlNode.h" #include #include diff --git a/include/cru/ui/mapper/ThicknessMapper.h b/include/cru/ui/mapper/ThicknessMapper.h index 84b6b036..25885cbf 100644 --- a/include/cru/ui/mapper/ThicknessMapper.h +++ b/include/cru/ui/mapper/ThicknessMapper.h @@ -3,7 +3,7 @@ #include "../Base.h" #include "cru/base/Base.h" -#include "cru/xml/XmlNode.h" +#include "cru/base/xml/XmlNode.h" namespace cru::ui::mapper { class CRU_UI_API ThicknessMapper : public BasicMapper { diff --git a/include/cru/ui/mapper/style/BorderStylerMapper.h b/include/cru/ui/mapper/style/BorderStylerMapper.h index 57b5dce4..8cb4d392 100644 --- a/include/cru/ui/mapper/style/BorderStylerMapper.h +++ b/include/cru/ui/mapper/style/BorderStylerMapper.h @@ -3,7 +3,7 @@ #include "cru/base/ClonablePtr.h" #include "cru/ui/mapper/style/IStylerMapper.h" #include "cru/ui/style/Styler.h" -#include "cru/xml/XmlNode.h" +#include "cru/base/xml/XmlNode.h" namespace cru::ui::mapper::style { class CRU_UI_API BorderStylerMapper diff --git a/include/cru/ui/mapper/style/IConditionMapper.h b/include/cru/ui/mapper/style/IConditionMapper.h index bfe24224..7bfd1427 100644 --- a/include/cru/ui/mapper/style/IConditionMapper.h +++ b/include/cru/ui/mapper/style/IConditionMapper.h @@ -3,7 +3,7 @@ #include "cru/base/ClonablePtr.h" #include "cru/ui/mapper/Mapper.h" #include "cru/ui/style/Condition.h" -#include "cru/xml/XmlNode.h" +#include "cru/base/xml/XmlNode.h" namespace cru::ui::mapper::style { struct CRU_UI_API IConditionMapper : virtual Interface { diff --git a/include/cru/ui/mapper/style/IStylerMapper.h b/include/cru/ui/mapper/style/IStylerMapper.h index adf9b6fb..4aa43665 100644 --- a/include/cru/ui/mapper/style/IStylerMapper.h +++ b/include/cru/ui/mapper/style/IStylerMapper.h @@ -3,7 +3,7 @@ #include "cru/base/ClonablePtr.h" #include "cru/ui/mapper/Mapper.h" #include "cru/ui/style/Styler.h" -#include "cru/xml/XmlNode.h" +#include "cru/base/xml/XmlNode.h" namespace cru::ui::mapper::style { struct CRU_UI_API IStylerMapper : virtual Interface { diff --git a/include/cru/ui/mapper/style/NoConditionMapper.h b/include/cru/ui/mapper/style/NoConditionMapper.h index 06106514..bd2adf14 100644 --- a/include/cru/ui/mapper/style/NoConditionMapper.h +++ b/include/cru/ui/mapper/style/NoConditionMapper.h @@ -4,7 +4,7 @@ #include "cru/base/Base.h" #include "cru/base/ClonablePtr.h" #include "cru/ui/style/Condition.h" -#include "cru/xml/XmlNode.h" +#include "cru/base/xml/XmlNode.h" namespace cru::ui::mapper::style { class CRU_UI_API NoConditionMapper diff --git a/include/cru/ui/mapper/style/StyleRuleMapper.h b/include/cru/ui/mapper/style/StyleRuleMapper.h index 12fcb85b..b4cb95e7 100644 --- a/include/cru/ui/mapper/style/StyleRuleMapper.h +++ b/include/cru/ui/mapper/style/StyleRuleMapper.h @@ -3,7 +3,7 @@ #include "cru/base/Base.h" #include "cru/base/ClonablePtr.h" #include "cru/ui/style/StyleRule.h" -#include "cru/xml/XmlNode.h" +#include "cru/base/xml/XmlNode.h" namespace cru::ui::mapper::style { class CRU_UI_API StyleRuleMapper : public BasicClonablePtrMapper { diff --git a/include/cru/xml/Base.h b/include/cru/xml/Base.h deleted file mode 100644 index aa3aeaa2..00000000 --- a/include/cru/xml/Base.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#ifdef CRU_IS_DLL -#ifdef CRU_XML_EXPORT_API -#define CRU_XML_API __declspec(dllexport) -#else -#define CRU_XML_API __declspec(dllimport) -#endif -#else -#define CRU_XML_API -#endif diff --git a/include/cru/xml/XmlNode.h b/include/cru/xml/XmlNode.h deleted file mode 100644 index 1574f6fa..00000000 --- a/include/cru/xml/XmlNode.h +++ /dev/null @@ -1,158 +0,0 @@ -#pragma once - -#include "Base.h" - -#include -#include -#include -#include - -namespace cru::xml { -class XmlElementNode; -class XmlTextNode; -class XmlCommentNode; - -class CRU_XML_API XmlNode { - friend XmlElementNode; - - public: - enum class Type { Text, Element, Comment }; - - protected: - explicit XmlNode(Type type) : type_(type) {} - - public: - CRU_DELETE_COPY(XmlNode) - CRU_DELETE_MOVE(XmlNode) - - virtual ~XmlNode() = default; - - Type GetType() const { return type_; } - XmlElementNode* GetParent() const { return parent_; } - - virtual XmlNode* Clone() const = 0; - - bool IsTextNode() const { return type_ == Type::Text; } - bool IsElementNode() const { return type_ == Type::Element; } - bool IsCommentNode() const { return type_ == Type::Comment; } - - XmlElementNode* AsElement(); - XmlTextNode* AsText(); - XmlCommentNode* AsComment(); - const XmlElementNode* AsElement() const; - const XmlTextNode* AsText() const; - const XmlCommentNode* AsComment() const; - - private: - const Type type_; - XmlElementNode* parent_ = nullptr; -}; - -class CRU_XML_API XmlTextNode : public XmlNode { - public: - XmlTextNode() : XmlNode(Type::Text) {} - explicit XmlTextNode(std::string text) - : XmlNode(Type::Text), text_(std::move(text)) {} - - CRU_DELETE_COPY(XmlTextNode) - CRU_DELETE_MOVE(XmlTextNode) - - ~XmlTextNode() override = default; - - public: - std::string GetText() const { return text_; } - void SetText(std::string text) { text_ = std::move(text); } - - XmlNode* Clone() const override { return new XmlTextNode(text_); } - - private: - std::string text_; -}; - -class CRU_XML_API XmlElementNode : public XmlNode { - public: - XmlElementNode() : XmlNode(Type::Element) {} - explicit XmlElementNode(std::string tag, - std::unordered_map attributes = {}) - : XmlNode(Type::Element), - tag_(std::move(tag)), - attributes_(std::move(attributes)) {} - - CRU_DELETE_COPY(XmlElementNode) - CRU_DELETE_MOVE(XmlElementNode) - - ~XmlElementNode() override; - - public: - std::string GetTag() const { return tag_; } - void SetTag(std::string tag) { tag_ = std::move(tag); } - const std::unordered_map& GetAttributes() const { - return attributes_; - } - void SetAttributes(std::unordered_map attributes) { - attributes_ = std::move(attributes); - } - const std::vector GetChildren() const { return children_; } - - Index GetChildCount() const { return children_.size(); } - std::string GetAttributeValue(const std::string& key) const { - return attributes_.at(key); - } - std::string GetAttributeValueCaseInsensitive(const std::string& key) const { - return *GetOptionalAttributeValueCaseInsensitive(key); - } - std::optional GetOptionalAttributeValue(const std::string& key) const { - auto it = attributes_.find(key); - if (it == attributes_.end()) { - return std::nullopt; - } - - return it->second; - } - std::optional GetOptionalAttributeValueCaseInsensitive( - const std::string& key) const { - for (auto it = attributes_.begin(); it != attributes_.end(); ++it) { - if (cru::string::CaseInsensitiveCompare(it->first, key) == 0) { - return it->second; - } - } - - return std::nullopt; - } - - XmlNode* GetChildAt(Index index) const { return children_[index]; } - - void AddAttribute(std::string key, std::string value); - void AddChild(XmlNode* child); - - Index GetChildElementCount() const; - XmlElementNode* GetFirstChildElement() const; - - XmlNode* Clone() const override; - - private: - std::string tag_; - std::unordered_map attributes_; - std::vector children_; -}; - -class CRU_XML_API XmlCommentNode : public XmlNode { - public: - XmlCommentNode() : XmlNode(Type::Comment) {} - explicit XmlCommentNode(std::string text) - : XmlNode(Type::Comment), text_(std::move(text)) {} - - CRU_DELETE_COPY(XmlCommentNode) - CRU_DELETE_MOVE(XmlCommentNode) - - ~XmlCommentNode() override; - - std::string GetText() const { return text_; } - void SetText(std::string text) { text_ = std::move(text); } - - XmlNode* Clone() const override; - - private: - std::string text_; -}; -} // namespace cru::xml diff --git a/include/cru/xml/XmlParser.h b/include/cru/xml/XmlParser.h deleted file mode 100644 index b6589f1b..00000000 --- a/include/cru/xml/XmlParser.h +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once - -#include "XmlNode.h" - -#include "cru/base/Exception.h" - -namespace cru::xml { -class CRU_XML_API XmlParsingException : public Exception { - public: - using Exception::Exception; -}; - -class CRU_XML_API XmlParser { - public: - explicit XmlParser(std::string xml); - - CRU_DELETE_COPY(XmlParser) - CRU_DELETE_MOVE(XmlParser) - - ~XmlParser(); - - XmlElementNode* Parse(); - - private: - XmlElementNode* DoParse(); - - char16_t Read1(); - std::string ReadWithoutAdvance(int count = 1); - void ReadSpacesAndDiscard(); - std::string ReadSpaces(); - std::string ReadIdenitifier(); - std::string ReadAttributeString(); - - private: - std::string xml_; - - XmlElementNode* cache_ = nullptr; - - // Consider the while file enclosed by a single tag called $root. - XmlElementNode* pseudo_root_node_ = new XmlElementNode("$root"); - XmlElementNode* current_ = pseudo_root_node_; - int current_position_ = 0; -}; -} // namespace cru::xml diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 959f2dd1..9f9c205f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -3,6 +3,5 @@ add_subdirectory(platform) add_subdirectory(ui) add_subdirectory(parse) add_subdirectory(toml) -add_subdirectory(xml) add_subdirectory(ThemeBuilder) diff --git a/src/base/CMakeLists.txt b/src/base/CMakeLists.txt index be45d0e0..ef5afe3c 100644 --- a/src/base/CMakeLists.txt +++ b/src/base/CMakeLists.txt @@ -14,6 +14,8 @@ add_library(CruBase io/MemoryStream.cpp log/Logger.cpp log/StdioLogTarget.cpp + xml/XmlNode.cpp + xml/XmlParser.cpp ) target_compile_definitions(CruBase PRIVATE CRU_BASE_EXPORT_API) target_include_directories(CruBase PUBLIC ${CRU_INCLUDE_DIR}) diff --git a/src/base/xml/XmlNode.cpp b/src/base/xml/XmlNode.cpp new file mode 100644 index 00000000..d3e7369b --- /dev/null +++ b/src/base/xml/XmlNode.cpp @@ -0,0 +1,75 @@ +#include "cru/base/xml/XmlNode.h" + +#include + +namespace cru::xml { + +XmlElementNode* XmlNode::AsElement() { + return static_cast(this); +} + +XmlTextNode* XmlNode::AsText() { return static_cast(this); } + +XmlCommentNode* XmlNode::AsComment() { + return static_cast(this); +} + +const XmlElementNode* XmlNode::AsElement() const { + return static_cast(this); +} + +const XmlTextNode* XmlNode::AsText() const { + return static_cast(this); +} + +const XmlCommentNode* XmlNode::AsComment() const { + return static_cast(this); +} + +XmlElementNode::~XmlElementNode() { + for (auto child : children_) { + delete child; + } +} + +void XmlElementNode::AddAttribute(std::string key, std::string value) { + attributes_[std::move(key)] = std::move(value); +} + +void XmlElementNode::AddChild(XmlNode* child) { + Expects(child->GetParent() == nullptr); + children_.push_back(child); + child->parent_ = this; +} + +Index XmlElementNode::GetChildElementCount() const { + return std::count_if( + children_.cbegin(), children_.cend(), + [](xml::XmlNode* node) { return node->IsElementNode(); }); +} + +XmlElementNode* XmlElementNode::GetFirstChildElement() const { + for (auto child : children_) { + if (child->GetType() == XmlNode::Type::Element) { + return child->AsElement(); + } + } + return nullptr; +} + +XmlNode* XmlElementNode::Clone() const { + XmlElementNode* node = new XmlElementNode(tag_, attributes_); + + for (auto child : children_) { + node->AddChild(child->Clone()); + } + + return node; +} + +XmlNode* XmlCommentNode::Clone() const { + XmlCommentNode* node = new XmlCommentNode(text_); + + return node; +} +} // namespace cru::xml diff --git a/src/base/xml/XmlParser.cpp b/src/base/xml/XmlParser.cpp new file mode 100644 index 00000000..c35d7a7b --- /dev/null +++ b/src/base/xml/XmlParser.cpp @@ -0,0 +1,197 @@ +#include "cru/base/xml/XmlParser.h" +#include "cru/base/StringUtil.h" +#include "cru/base/xml/XmlNode.h" + +namespace cru::xml { +XmlParser::XmlParser(std::string xml) : xml_(std::move(xml)) {} + +XmlParser::~XmlParser() { delete pseudo_root_node_; } + +XmlElementNode* XmlParser::Parse() { + if (!cache_) { + cache_ = DoParse(); + } + return static_cast(cache_->Clone()); +} + +char16_t XmlParser::Read1() { + if (current_position_ >= xml_.size()) { + throw XmlParsingException("Unexpected end of xml"); + } + return xml_[current_position_++]; +} + +std::string XmlParser::ReadWithoutAdvance(int count) { + if (current_position_ + count > xml_.size()) { + count = xml_.size() - current_position_; + } + return xml_.substr(current_position_, count); +} + +void XmlParser::ReadSpacesAndDiscard() { + while (current_position_ < xml_.size() && + (xml_[current_position_] == ' ' || xml_[current_position_] == '\t' || + xml_[current_position_] == '\n' || xml_[current_position_] == '\r')) { + ++current_position_; + } +} + +std::string XmlParser::ReadSpaces() { + std::string spaces; + while (current_position_ < xml_.size() && + (xml_[current_position_] == ' ' || xml_[current_position_] == '\t' || + xml_[current_position_] == '\n' || xml_[current_position_] == '\r')) { + spaces += xml_[current_position_]; + ++current_position_; + } + return spaces; +} + +std::string XmlParser::ReadIdenitifier() { + std::string identifier; + while (current_position_ < xml_.size() && + (xml_[current_position_] >= 'a' && xml_[current_position_] <= 'z' || + xml_[current_position_] >= 'A' && xml_[current_position_] <= 'Z' || + xml_[current_position_] >= '0' && xml_[current_position_] <= '9' || + xml_[current_position_] == '_')) { + identifier += xml_[current_position_]; + ++current_position_; + } + return identifier; +} + +std::string XmlParser::ReadAttributeString() { + if (Read1() != '"') { + throw XmlParsingException("Expected \"."); + } + + std::string string; + + while (true) { + char16_t c = Read1(); + + if (c == '"') { + break; + } + + string += c; + } + + return string; +} + +XmlElementNode* XmlParser::DoParse() { + while (current_position_ < xml_.size()) { + ReadSpacesAndDiscard(); + + if (current_position_ == xml_.size()) { + break; + } + + if (ReadWithoutAdvance() == "<") { + current_position_ += 1; + + if (ReadWithoutAdvance() == "/") { + current_position_ += 1; + + ReadSpacesAndDiscard(); + + std::string tag = ReadIdenitifier(); + + if (tag != current_->GetTag()) { + throw XmlParsingException("Tag mismatch."); + } + + ReadSpacesAndDiscard(); + + if (Read1() != '>') { + throw XmlParsingException("Expected >."); + } + + current_ = current_->GetParent(); + } else if (ReadWithoutAdvance(3) == "!--") { + current_position_ += 3; + + std::string text; + while (true) { + auto str = ReadWithoutAdvance(3); + if (str == "-->") break; + if (str.empty()) throw XmlParsingException("Unexpected end of xml"); + text += Read1(); + } + + current_position_ += 3; + current_->AddChild(new XmlCommentNode(cru::string::Trim(text))); + } else { + ReadSpacesAndDiscard(); + + std::string tag = ReadIdenitifier(); + + XmlElementNode* node = new XmlElementNode(tag); + + bool is_self_closing = false; + + while (true) { + ReadSpacesAndDiscard(); + auto c = ReadWithoutAdvance(); + if (c == ">") { + current_position_ += 1; + break; + } else if (c == "/") { + current_position_ += 1; + + if (Read1() != '>') { + throw XmlParsingException("Expected >."); + } + + is_self_closing = true; + break; + } else { + std::string attribute_name = ReadIdenitifier(); + + ReadSpacesAndDiscard(); + + if (Read1() != '=') { + throw XmlParsingException("Expected '='"); + } + + ReadSpacesAndDiscard(); + + std::string attribute_value = ReadAttributeString(); + + node->AddAttribute(attribute_name, attribute_value); + } + } + + current_->AddChild(node); + + if (!is_self_closing) { + current_ = node; + } + } + + } else { + std::string text; + + while (ReadWithoutAdvance() != "<") { + char16_t c = Read1(); + + text += c; + } + + if (!text.empty()) + current_->AddChild(new XmlTextNode(cru::string::TrimEnd(text))); + } + } + + if (current_ != pseudo_root_node_) { + throw XmlParsingException("Unexpected end of xml"); + } + + if (pseudo_root_node_->GetChildren().size() != 1) { + throw XmlParsingException("Expected 1 node as root."); + } + + return static_cast(pseudo_root_node_->GetChildren()[0]); +} +} // namespace cru::xml diff --git a/src/ui/CMakeLists.txt b/src/ui/CMakeLists.txt index 93077388..4dfee0cb 100644 --- a/src/ui/CMakeLists.txt +++ b/src/ui/CMakeLists.txt @@ -79,4 +79,4 @@ add_library(CruUi style/StyleRuleSet.cpp ) target_compile_definitions(CruUi PRIVATE CRU_UI_EXPORT_API) -target_link_libraries(CruUi PUBLIC CruPlatformGui CruXml) +target_link_libraries(CruUi PUBLIC CruPlatformGui) diff --git a/src/ui/ThemeResourceDictionary.cpp b/src/ui/ThemeResourceDictionary.cpp index b9c7c284..9f734054 100644 --- a/src/ui/ThemeResourceDictionary.cpp +++ b/src/ui/ThemeResourceDictionary.cpp @@ -2,8 +2,8 @@ #include "cru/base/StringUtil.h" #include "cru/base/io/CFileStream.h" #include "cru/base/log/Logger.h" -#include "cru/xml/XmlNode.h" -#include "cru/xml/XmlParser.h" +#include "cru/base/xml/XmlNode.h" +#include "cru/base/xml/XmlParser.h" namespace cru::ui { diff --git a/src/ui/mapper/BorderStyleMapper.cpp b/src/ui/mapper/BorderStyleMapper.cpp index fa3f672d..d4889457 100644 --- a/src/ui/mapper/BorderStyleMapper.cpp +++ b/src/ui/mapper/BorderStyleMapper.cpp @@ -3,7 +3,7 @@ #include "cru/platform/graphics/Brush.h" #include "cru/ui/mapper/MapperRegistry.h" #include "cru/ui/style/ApplyBorderStyleInfo.h" -#include "cru/xml/XmlNode.h" +#include "cru/base/xml/XmlNode.h" namespace cru::ui::mapper { using namespace xml; diff --git a/src/ui/mapper/BrushMapper.cpp b/src/ui/mapper/BrushMapper.cpp index 81eb40a5..4f17bd12 100644 --- a/src/ui/mapper/BrushMapper.cpp +++ b/src/ui/mapper/BrushMapper.cpp @@ -5,7 +5,7 @@ #include "cru/platform/graphics/Brush.h" #include "cru/platform/graphics/Factory.h" #include "cru/ui/mapper/MapperRegistry.h" -#include "cru/xml/XmlNode.h" +#include "cru/base/xml/XmlNode.h" #include diff --git a/src/ui/mapper/StringMapper.cpp b/src/ui/mapper/StringMapper.cpp index 6f6b4546..24b79ff4 100644 --- a/src/ui/mapper/StringMapper.cpp +++ b/src/ui/mapper/StringMapper.cpp @@ -1,5 +1,5 @@ #include "cru/ui/mapper/StringMapper.h" -#include "cru/xml/XmlNode.h" +#include "cru/base/xml/XmlNode.h" namespace cru::ui::mapper { StringMapper::StringMapper() { SetAllowedTags({"String"}); } diff --git a/src/ui/mapper/ThicknessMapper.cpp b/src/ui/mapper/ThicknessMapper.cpp index e6557689..2dc6efdc 100644 --- a/src/ui/mapper/ThicknessMapper.cpp +++ b/src/ui/mapper/ThicknessMapper.cpp @@ -1,6 +1,6 @@ #include "cru/ui/mapper/ThicknessMapper.h" #include "cru/base/StringUtil.h" -#include "cru/xml/XmlNode.h" +#include "cru/base/xml/XmlNode.h" namespace cru::ui::mapper { bool ThicknessMapper::XmlElementIsOfThisType(xml::XmlElementNode* node) { diff --git a/src/ui/mapper/style/AndConditionMapper.cpp b/src/ui/mapper/style/AndConditionMapper.cpp index c1b7e5e5..0b1fc008 100644 --- a/src/ui/mapper/style/AndConditionMapper.cpp +++ b/src/ui/mapper/style/AndConditionMapper.cpp @@ -3,7 +3,7 @@ #include "cru/ui/mapper/MapperRegistry.h" #include "cru/ui/mapper/style/IConditionMapper.h" #include "cru/ui/style/Condition.h" -#include "cru/xml/XmlNode.h" +#include "cru/base/xml/XmlNode.h" namespace cru::ui::mapper::style { bool AndConditionMapper::XmlElementIsOfThisType(xml::XmlElementNode *node) { diff --git a/src/ui/mapper/style/BorderStylerMapper.cpp b/src/ui/mapper/style/BorderStylerMapper.cpp index e93a0af8..3deeed43 100644 --- a/src/ui/mapper/style/BorderStylerMapper.cpp +++ b/src/ui/mapper/style/BorderStylerMapper.cpp @@ -3,7 +3,7 @@ #include "cru/ui/mapper/MapperRegistry.h" #include "cru/ui/style/ApplyBorderStyleInfo.h" #include "cru/ui/style/Styler.h" -#include "cru/xml/XmlNode.h" +#include "cru/base/xml/XmlNode.h" namespace cru::ui::mapper::style { using cru::ui::style::ApplyBorderStyleInfo; diff --git a/src/ui/mapper/style/CheckedConditionMapper.cpp b/src/ui/mapper/style/CheckedConditionMapper.cpp index aafc97a4..2d01b4bf 100644 --- a/src/ui/mapper/style/CheckedConditionMapper.cpp +++ b/src/ui/mapper/style/CheckedConditionMapper.cpp @@ -2,7 +2,7 @@ #include "cru/base/ClonablePtr.h" #include "cru/base/StringUtil.h" #include "cru/ui/style/Condition.h" -#include "cru/xml/XmlNode.h" +#include "cru/base/xml/XmlNode.h" namespace cru::ui::mapper::style { bool CheckedConditionMapper::XmlElementIsOfThisType(xml::XmlElementNode* node) { diff --git a/src/ui/mapper/style/ContentBrushStylerMapper.cpp b/src/ui/mapper/style/ContentBrushStylerMapper.cpp index 027622fe..c5caab02 100644 --- a/src/ui/mapper/style/ContentBrushStylerMapper.cpp +++ b/src/ui/mapper/style/ContentBrushStylerMapper.cpp @@ -3,7 +3,7 @@ #include "cru/platform/graphics/Brush.h" #include "cru/ui/mapper/MapperRegistry.h" #include "cru/ui/style/Styler.h" -#include "cru/xml/XmlNode.h" +#include "cru/base/xml/XmlNode.h" namespace cru::ui::mapper::style { ContentBrushStylerMapper::ContentBrushStylerMapper() { diff --git a/src/ui/mapper/style/FocusConditionMapper.cpp b/src/ui/mapper/style/FocusConditionMapper.cpp index b21ac250..26a5780d 100644 --- a/src/ui/mapper/style/FocusConditionMapper.cpp +++ b/src/ui/mapper/style/FocusConditionMapper.cpp @@ -2,7 +2,7 @@ #include "cru/base/ClonablePtr.h" #include "cru/base/StringUtil.h" #include "cru/ui/style/Condition.h" -#include "cru/xml/XmlNode.h" +#include "cru/base/xml/XmlNode.h" namespace cru::ui::mapper::style { bool FocusConditionMapper::XmlElementIsOfThisType(xml::XmlElementNode* node) { diff --git a/src/ui/mapper/style/NoConditionMapper.cpp b/src/ui/mapper/style/NoConditionMapper.cpp index d38e187b..84cbf598 100644 --- a/src/ui/mapper/style/NoConditionMapper.cpp +++ b/src/ui/mapper/style/NoConditionMapper.cpp @@ -1,6 +1,6 @@ #include "cru/ui/mapper/style/NoConditionMapper.h" #include "cru/base/ClonablePtr.h" -#include "cru/xml/XmlNode.h" +#include "cru/base/xml/XmlNode.h" namespace cru::ui::mapper::style { bool NoConditionMapper::XmlElementIsOfThisType(xml::XmlElementNode* node) { diff --git a/src/ui/mapper/style/OrConditionMapper.cpp b/src/ui/mapper/style/OrConditionMapper.cpp index 9c983d83..98b4078d 100644 --- a/src/ui/mapper/style/OrConditionMapper.cpp +++ b/src/ui/mapper/style/OrConditionMapper.cpp @@ -3,7 +3,7 @@ #include "cru/ui/mapper/MapperRegistry.h" #include "cru/ui/mapper/style/IConditionMapper.h" #include "cru/ui/style/Condition.h" -#include "cru/xml/XmlNode.h" +#include "cru/base/xml/XmlNode.h" namespace cru::ui::mapper::style { bool OrConditionMapper::XmlElementIsOfThisType(xml::XmlElementNode *node) { diff --git a/src/xml/CMakeLists.txt b/src/xml/CMakeLists.txt deleted file mode 100644 index 5431d53d..00000000 --- a/src/xml/CMakeLists.txt +++ /dev/null @@ -1,6 +0,0 @@ -add_library(CruXml - XmlNode.cpp - XmlParser.cpp -) -target_compile_definitions(CruXml PRIVATE CRU_XML_EXPORT_API) -target_link_libraries(CruXml PUBLIC CruBase) diff --git a/src/xml/XmlNode.cpp b/src/xml/XmlNode.cpp deleted file mode 100644 index c9b73c50..00000000 --- a/src/xml/XmlNode.cpp +++ /dev/null @@ -1,76 +0,0 @@ -#include "cru/xml/XmlNode.h" -#include - -namespace cru::xml { - -XmlElementNode* XmlNode::AsElement() { - return static_cast(this); -} - -XmlTextNode* XmlNode::AsText() { return static_cast(this); } - -XmlCommentNode* XmlNode::AsComment() { - return static_cast(this); -} - -const XmlElementNode* XmlNode::AsElement() const { - return static_cast(this); -} - -const XmlTextNode* XmlNode::AsText() const { - return static_cast(this); -} - -const XmlCommentNode* XmlNode::AsComment() const { - return static_cast(this); -} - -XmlElementNode::~XmlElementNode() { - for (auto child : children_) { - delete child; - } -} - -void XmlElementNode::AddAttribute(std::string key, std::string value) { - attributes_[std::move(key)] = std::move(value); -} - -void XmlElementNode::AddChild(XmlNode* child) { - Expects(child->GetParent() == nullptr); - children_.push_back(child); - child->parent_ = this; -} - -Index XmlElementNode::GetChildElementCount() const { - return std::count_if( - children_.cbegin(), children_.cend(), - [](xml::XmlNode* node) { return node->IsElementNode(); }); -} - -XmlElementNode* XmlElementNode::GetFirstChildElement() const { - for (auto child : children_) { - if (child->GetType() == XmlNode::Type::Element) { - return child->AsElement(); - } - } - return nullptr; -} - -XmlNode* XmlElementNode::Clone() const { - XmlElementNode* node = new XmlElementNode(tag_, attributes_); - - for (auto child : children_) { - node->AddChild(child->Clone()); - } - - return node; -} - -XmlCommentNode::~XmlCommentNode() {} - -XmlNode* XmlCommentNode::Clone() const { - XmlCommentNode* node = new XmlCommentNode(text_); - - return node; -} -} // namespace cru::xml diff --git a/src/xml/XmlParser.cpp b/src/xml/XmlParser.cpp deleted file mode 100644 index 9bfd5441..00000000 --- a/src/xml/XmlParser.cpp +++ /dev/null @@ -1,196 +0,0 @@ -#include "cru/xml/XmlParser.h" -#include "cru/base/StringUtil.h" -#include "cru/xml/XmlNode.h" - -namespace cru::xml { -XmlParser::XmlParser(std::string xml) : xml_(std::move(xml)) {} - -XmlParser::~XmlParser() { delete pseudo_root_node_; } - -XmlElementNode* XmlParser::Parse() { - if (!cache_) { - cache_ = DoParse(); - } - return static_cast(cache_->Clone()); -} - -char16_t XmlParser::Read1() { - if (current_position_ >= xml_.size()) { - throw XmlParsingException("Unexpected end of xml"); - } - return xml_[current_position_++]; -} - -std::string XmlParser::ReadWithoutAdvance(int count) { - if (current_position_ + count > xml_.size()) { - count = xml_.size() - current_position_; - } - return xml_.substr(current_position_, count); -} - -void XmlParser::ReadSpacesAndDiscard() { - while (current_position_ < xml_.size() && - (xml_[current_position_] == ' ' || xml_[current_position_] == '\t' || - xml_[current_position_] == '\n' || xml_[current_position_] == '\r')) { - ++current_position_; - } -} - -std::string XmlParser::ReadSpaces() { - std::string spaces; - while (current_position_ < xml_.size() && - (xml_[current_position_] == ' ' || xml_[current_position_] == '\t' || - xml_[current_position_] == '\n' || xml_[current_position_] == '\r')) { - spaces += xml_[current_position_]; - ++current_position_; - } - return spaces; -} - -std::string XmlParser::ReadIdenitifier() { - std::string identifier; - while (current_position_ < xml_.size() && - (xml_[current_position_] >= 'a' && xml_[current_position_] <= 'z' || - xml_[current_position_] >= 'A' && xml_[current_position_] <= 'Z' || - xml_[current_position_] >= '0' && xml_[current_position_] <= '9' || - xml_[current_position_] == '_')) { - identifier += xml_[current_position_]; - ++current_position_; - } - return identifier; -} - -std::string XmlParser::ReadAttributeString() { - if (Read1() != '"') { - throw XmlParsingException("Expected \"."); - } - - std::string string; - - while (true) { - char16_t c = Read1(); - - if (c == '"') { - break; - } - - string += c; - } - - return string; -} - -XmlElementNode* XmlParser::DoParse() { - while (current_position_ < xml_.size()) { - ReadSpacesAndDiscard(); - - if (current_position_ == xml_.size()) { - break; - } - - if (ReadWithoutAdvance() == "<") { - current_position_ += 1; - - if (ReadWithoutAdvance() == "/") { - current_position_ += 1; - - ReadSpacesAndDiscard(); - - std::string tag = ReadIdenitifier(); - - if (tag != current_->GetTag()) { - throw XmlParsingException("Tag mismatch."); - } - - ReadSpacesAndDiscard(); - - if (Read1() != '>') { - throw XmlParsingException("Expected >."); - } - - current_ = current_->GetParent(); - } else if (ReadWithoutAdvance(3) == "!--") { - current_position_ += 3; - - std::string text; - while (true) { - auto str = ReadWithoutAdvance(3); - if (str == "-->") break; - if (str.empty()) throw XmlParsingException("Unexpected end of xml"); - text += Read1(); - } - - current_position_ += 3; - current_->AddChild(new XmlCommentNode(cru::string::Trim(text))); - } else { - ReadSpacesAndDiscard(); - - std::string tag = ReadIdenitifier(); - - XmlElementNode* node = new XmlElementNode(tag); - - bool is_self_closing = false; - - while (true) { - ReadSpacesAndDiscard(); - auto c = ReadWithoutAdvance(); - if (c == ">") { - current_position_ += 1; - break; - } else if (c == "/") { - current_position_ += 1; - - if (Read1() != '>') { - throw XmlParsingException("Expected >."); - } - - is_self_closing = true; - break; - } else { - std::string attribute_name = ReadIdenitifier(); - - ReadSpacesAndDiscard(); - - if (Read1() != '=') { - throw XmlParsingException("Expected '='"); - } - - ReadSpacesAndDiscard(); - - std::string attribute_value = ReadAttributeString(); - - node->AddAttribute(attribute_name, attribute_value); - } - } - - current_->AddChild(node); - - if (!is_self_closing) { - current_ = node; - } - } - - } else { - std::string text; - - while (ReadWithoutAdvance() != "<") { - char16_t c = Read1(); - - text += c; - } - - if (!text.empty()) current_->AddChild(new XmlTextNode(cru::string::TrimEnd(text))); - } - } - - if (current_ != pseudo_root_node_) { - throw XmlParsingException("Unexpected end of xml"); - } - - if (pseudo_root_node_->GetChildren().size() != 1) { - throw XmlParsingException("Expected 1 node as root."); - } - - return static_cast(pseudo_root_node_->GetChildren()[0]); -} -} // namespace cru::xml diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index cd51b2f3..22f66e40 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -17,6 +17,5 @@ endfunction() add_subdirectory(base) add_subdirectory(platform) -add_subdirectory(xml) add_subdirectory(toml) add_subdirectory(ui) diff --git a/test/base/CMakeLists.txt b/test/base/CMakeLists.txt index 39895c3e..b36bbd23 100644 --- a/test/base/CMakeLists.txt +++ b/test/base/CMakeLists.txt @@ -6,6 +6,7 @@ add_executable(CruBaseTest StringUtilTest.cpp SubProcessTest.cpp TimerTest.cpp + xml/ParserTest.cpp ) target_link_libraries(CruBaseTest PRIVATE CruBase CruTestBase) diff --git a/test/base/xml/ParserTest.cpp b/test/base/xml/ParserTest.cpp new file mode 100644 index 00000000..f7544f02 --- /dev/null +++ b/test/base/xml/ParserTest.cpp @@ -0,0 +1,116 @@ +#include "cru/base/xml/XmlNode.h" +#include "cru/base/xml/XmlParser.h" + +#include + +using namespace cru::xml; + +TEST_CASE("CruXmlParserTest Simple", "[xml]") { + XmlParser parser(""); + auto n = parser.Parse(); + REQUIRE(n->GetTag() == "root"); + REQUIRE(n->GetAttributes().empty() == true); + REQUIRE(n->GetChildCount() == 0); + delete n; +} + +TEST_CASE("CruXmlParserTest SimpleWithAttribute", "[xml]") { + XmlParser parser(""); + auto n = parser.Parse(); + REQUIRE(n->GetTag() == "root"); + REQUIRE(n->GetAttributeValue("a1") == "v1"); + REQUIRE(n->GetAttributeValue("a2") == "v2"); + REQUIRE(n->GetChildCount() == 0); + delete n; +} + +TEST_CASE("CruXmlParserTest SimpleSelfClosing", "[xml]") { + XmlParser parser(""); + auto n = parser.Parse(); + REQUIRE(n->GetTag() == "root"); + REQUIRE(n->GetAttributeValue("a1") == "v1"); + REQUIRE(n->GetAttributeValue("a2") == "v2"); + REQUIRE(n->GetChildCount() == 0); + delete n; +} + +TEST_CASE("CruXmlParserTest NestedElement", "[xml]") { + XmlParser parser( + ""); + auto n = parser.Parse(); + REQUIRE(n->GetChildren().size() == 2); + REQUIRE(n->GetChildAt(0)->AsElement()->GetTag() == "c1"); + REQUIRE(n->GetChildAt(1)->AsElement()->GetTag() == "c2"); + REQUIRE(n->GetChildAt(0)->AsElement()->GetChildCount() == 1); + REQUIRE(n->GetChildAt(0)->AsElement()->GetChildAt(0)->AsElement()->GetTag() == + "d1"); + REQUIRE(n->GetChildAt(1)->AsElement()->GetChildCount() == 2); + REQUIRE(n->GetChildAt(1)->AsElement()->GetChildAt(0)->AsElement()->GetTag() == + "d2"); + REQUIRE(n->GetChildAt(1)->AsElement()->GetChildAt(1)->AsElement()->GetTag() == + "d3"); + delete n; +} + +TEST_CASE("CruXmlParserTest SimpleText", "[xml]") { + XmlParser parser("text"); + auto n = parser.Parse(); + REQUIRE(n->GetChildCount() == 1); + REQUIRE(n->GetChildAt(0)->AsText()->GetText() == "text"); + delete n; +} + +TEST_CASE("CruXmlParserTest Whitespace", "[xml]") { + XmlParser parser("\t\t\n\t\t\ttext test\n\t\t\t\t"); + auto n = parser.Parse(); + REQUIRE(n->GetChildCount() == 1); + REQUIRE(n->GetChildAt(0)->AsText()->GetText() == "text test"); + delete n; +} + +TEST_CASE("CruXmlParserTest Comment", "[xml]") { + XmlParser parser(""); + auto n = parser.Parse(); + REQUIRE(n->IsCommentNode()); + REQUIRE(n->AsComment()->GetText() == "comment"); + delete n; +} + +TEST_CASE("CruXmlParserTest Complex", "[xml]") { + XmlParser parser( + R"( + + + + + + + t1 + t2 + text test + + t2 + + + + )"); + auto n = parser.Parse(); + REQUIRE(n->GetAttributeValue("a1") == "v1"); + REQUIRE(n->GetChildCount() == 3); + REQUIRE(n->GetChildAt(0)->AsElement()->GetTag() == "c1"); + REQUIRE(n->GetChildAt(0)->AsElement()->GetChildCount() == 1); + auto c2 = n->GetChildAt(1)->AsElement(); + REQUIRE(c2->GetTag() == "c2"); + REQUIRE(c2->GetAttributeValue("a2") == "v2"); + REQUIRE(c2->GetAttributeValue("a3") == "v3"); + REQUIRE(c2->GetChildAt(0)->AsText()->GetText() == "t1"); + auto d2 = c2->GetChildAt(1)->AsElement(); + REQUIRE(d2->GetTag() == "d2"); + REQUIRE(d2->GetAttributeValue("a4") == "v4"); + REQUIRE(c2->GetChildAt(2)->AsText()->GetText() == "text test"); + REQUIRE(c2->GetChildAt(3)->AsElement()->GetTag() == "d3"); + REQUIRE(c2->GetChildAt(4)->AsText()->GetText() == "t2"); + REQUIRE(n->GetChildAt(2)->IsCommentNode()); + REQUIRE(n->GetChildAt(2)->AsComment()->GetText() == "comment"); + delete n; +} diff --git a/test/xml/CMakeLists.txt b/test/xml/CMakeLists.txt deleted file mode 100644 index 07bfa52b..00000000 --- a/test/xml/CMakeLists.txt +++ /dev/null @@ -1,6 +0,0 @@ -add_executable(CruXmlTest - ParserTest.cpp -) -target_link_libraries(CruXmlTest PRIVATE CruXml CruTestBase) - -cru_catch_discover_tests(CruXmlTest) diff --git a/test/xml/ParserTest.cpp b/test/xml/ParserTest.cpp deleted file mode 100644 index 0d3ab1d7..00000000 --- a/test/xml/ParserTest.cpp +++ /dev/null @@ -1,116 +0,0 @@ -#include "cru/xml/XmlNode.h" -#include "cru/xml/XmlParser.h" - -#include - -using namespace cru::xml; - -TEST_CASE("CruXmlParserTest Simple", "[xml]") { - XmlParser parser(""); - auto n = parser.Parse(); - REQUIRE(n->GetTag() == "root"); - REQUIRE(n->GetAttributes().empty() == true); - REQUIRE(n->GetChildCount() == 0); - delete n; -} - -TEST_CASE("CruXmlParserTest SimpleWithAttribute", "[xml]") { - XmlParser parser(""); - auto n = parser.Parse(); - REQUIRE(n->GetTag() == "root"); - REQUIRE(n->GetAttributeValue("a1") == "v1"); - REQUIRE(n->GetAttributeValue("a2") == "v2"); - REQUIRE(n->GetChildCount() == 0); - delete n; -} - -TEST_CASE("CruXmlParserTest SimpleSelfClosing", "[xml]") { - XmlParser parser(""); - auto n = parser.Parse(); - REQUIRE(n->GetTag() == "root"); - REQUIRE(n->GetAttributeValue("a1") == "v1"); - REQUIRE(n->GetAttributeValue("a2") == "v2"); - REQUIRE(n->GetChildCount() == 0); - delete n; -} - -TEST_CASE("CruXmlParserTest NestedElement", "[xml]") { - XmlParser parser( - ""); - auto n = parser.Parse(); - REQUIRE(n->GetChildren().size() == 2); - REQUIRE(n->GetChildAt(0)->AsElement()->GetTag() == "c1"); - REQUIRE(n->GetChildAt(1)->AsElement()->GetTag() == "c2"); - REQUIRE(n->GetChildAt(0)->AsElement()->GetChildCount() == 1); - REQUIRE(n->GetChildAt(0)->AsElement()->GetChildAt(0)->AsElement()->GetTag() == - "d1"); - REQUIRE(n->GetChildAt(1)->AsElement()->GetChildCount() == 2); - REQUIRE(n->GetChildAt(1)->AsElement()->GetChildAt(0)->AsElement()->GetTag() == - "d2"); - REQUIRE(n->GetChildAt(1)->AsElement()->GetChildAt(1)->AsElement()->GetTag() == - "d3"); - delete n; -} - -TEST_CASE("CruXmlParserTest SimpleText", "[xml]") { - XmlParser parser("text"); - auto n = parser.Parse(); - REQUIRE(n->GetChildCount() == 1); - REQUIRE(n->GetChildAt(0)->AsText()->GetText() == "text"); - delete n; -} - -TEST_CASE("CruXmlParserTest Whitespace", "[xml]") { - XmlParser parser("\t\t\n\t\t\ttext test\n\t\t\t\t"); - auto n = parser.Parse(); - REQUIRE(n->GetChildCount() == 1); - REQUIRE(n->GetChildAt(0)->AsText()->GetText() == "text test"); - delete n; -} - -TEST_CASE("CruXmlParserTest Comment", "[xml]") { - XmlParser parser(""); - auto n = parser.Parse(); - REQUIRE(n->IsCommentNode()); - REQUIRE(n->AsComment()->GetText() == "comment"); - delete n; -} - -TEST_CASE("CruXmlParserTest Complex", "[xml]") { - XmlParser parser( - R"( - - - - - - - t1 - t2 - text test - - t2 - - - - )"); - auto n = parser.Parse(); - REQUIRE(n->GetAttributeValue("a1") == "v1"); - REQUIRE(n->GetChildCount() == 3); - REQUIRE(n->GetChildAt(0)->AsElement()->GetTag() == "c1"); - REQUIRE(n->GetChildAt(0)->AsElement()->GetChildCount() == 1); - auto c2 = n->GetChildAt(1)->AsElement(); - REQUIRE(c2->GetTag() == "c2"); - REQUIRE(c2->GetAttributeValue("a2") == "v2"); - REQUIRE(c2->GetAttributeValue("a3") == "v3"); - REQUIRE(c2->GetChildAt(0)->AsText()->GetText() == "t1"); - auto d2 = c2->GetChildAt(1)->AsElement(); - REQUIRE(d2->GetTag() == "d2"); - REQUIRE(d2->GetAttributeValue("a4") == "v4"); - REQUIRE(c2->GetChildAt(2)->AsText()->GetText() == "text test"); - REQUIRE(c2->GetChildAt(3)->AsElement()->GetTag() == "d3"); - REQUIRE(c2->GetChildAt(4)->AsText()->GetText() == "t2"); - REQUIRE(n->GetChildAt(2)->IsCommentNode()); - REQUIRE(n->GetChildAt(2)->AsComment()->GetText() == "comment"); - delete n; -} -- cgit v1.2.3