Procházet zdrojové kódy

Added XMLNode class (incomplete)

- added XMLNode class to represent a XML node
inside a XML file
- added tests for XMLNode class
Patrick-Laptop před 4 roky
rodič
revize
2cb4fc6ce5

+ 4 - 2
CMakeLists.txt

@@ -113,7 +113,8 @@ set(SOURCE_FILES
         ${CMAKE_CURRENT_SOURCE_DIR}/source/serialization/xml/logic/SerializableXMLState.cpp
         ${CMAKE_CURRENT_SOURCE_DIR}/source/io/xml/XMLAttribute.hpp
         ${CMAKE_CURRENT_SOURCE_DIR}/source/io/xml/XMLAttribute.cpp
-        )
+        ${CMAKE_CURRENT_SOURCE_DIR}/source/io/xml/XMLNode.hpp
+        ${CMAKE_CURRENT_SOURCE_DIR}/source/io/xml/XMLNode.cpp)
 
 set(LIBRARY_SOURCE_FILES
         ${CMAKE_CURRENT_SOURCE_DIR}/source/lib/tiny_xml_2/include/tinyxml2.h
@@ -156,7 +157,8 @@ set(TEST_FILES
         ${CMAKE_CURRENT_SOURCE_DIR}/test/cases/serialization/xml/TinyXMLTest.cpp
         ${CMAKE_CURRENT_SOURCE_DIR}/test/cases/serialization/xml/logic/SerializableXMLStateConnectionTest.cpp
         ${CMAKE_CURRENT_SOURCE_DIR}/test/cases/serialization/xml/logic/SerializableXMLStateTest.cpp
-        ${CMAKE_CURRENT_SOURCE_DIR}/test/cases/io/xml/XMLAttributeTest.cpp)
+        ${CMAKE_CURRENT_SOURCE_DIR}/test/cases/io/xml/XMLAttributeTest.cpp
+        ${CMAKE_CURRENT_SOURCE_DIR}/test/cases/io/xml/XMLNodeTest.cpp)
 
 ##########################################################
 # Build

+ 205 - 0
source/io/xml/XMLNode.cpp

@@ -0,0 +1,205 @@
+/*
+ * Author:          Patrick-Christopher Mattulat
+ * Company:         Lynar Studios
+ * E-Mail:          webmaster@lynarstudios.com
+ * Created:         2020-09-24
+ * Changed:         2020-09-24
+ *
+ * */
+
+#include "XMLNode.hpp"
+#include "../../utils/STLUtils.hpp"
+
+ls_std::XMLNode::XMLNode(std::string _name) : Class("XMLNode"),
+name(std::move(_name))
+{}
+
+bool ls_std::XMLNode::addAttributeToBeginning(const std::shared_ptr<ls_std::XMLAttribute> &_attribute)
+{
+  bool added {};
+
+  if(_attribute != nullptr && !_hasAttribute(_attribute->getName())) {
+    this->attributes.push_front(_attribute);
+    added = true;
+  }
+
+  return added;
+}
+
+bool ls_std::XMLNode::addAttributeToEnd(const std::shared_ptr<ls_std::XMLAttribute> &_attribute)
+{
+  bool added {};
+
+  if(_attribute != nullptr && !_hasAttribute(_attribute->getName())) {
+    this->attributes.push_back(_attribute);
+    added = true;
+  }
+
+  return added;
+}
+
+bool ls_std::XMLNode::addChildAfter(const std::shared_ptr<ls_std::XMLNode>& _child, const std::shared_ptr<ls_std::XMLNode>& _search)
+{
+  bool added {};
+  auto iterator = this->children.begin();
+
+  if(_child != nullptr && !this->_hasChild(_child)) {
+    while(iterator != this->children.end()) {
+      if(*iterator == _search) {
+        iterator++;
+        this->children.insert(iterator, _child);
+        added = true;
+        break;
+      }
+
+      iterator++;
+    }
+  }
+
+  return added;
+}
+
+bool ls_std::XMLNode::addChildBefore(const std::shared_ptr<ls_std::XMLNode>& _child, const std::shared_ptr<ls_std::XMLNode>& _search)
+{
+  bool added {};
+  auto iterator = this->children.begin();
+
+  if(_child != nullptr && !this->_hasChild(_child)) {
+    while(iterator != this->children.end()) {
+      if(*iterator == _search) {
+        this->children.insert(iterator, _child);
+        added = true;
+        break;
+      }
+
+      iterator++;
+    }
+  }
+
+  return added;
+}
+
+bool ls_std::XMLNode::addChildToBeginning(const std::shared_ptr<XMLNode> &_child)
+{
+  bool added {};
+
+  if(_child != nullptr && !this->_hasChild(_child)) {
+    this->children.push_front(_child);
+    added = true;
+  }
+
+  return added;
+}
+
+bool ls_std::XMLNode::addChildToEnd(const std::shared_ptr<XMLNode>& _child)
+{
+  bool added {};
+
+  if(_child != nullptr && !this->_hasChild(_child)) {
+    this->children.push_back(_child);
+    added = true;
+  }
+
+  return added;
+}
+
+std::list<std::shared_ptr<ls_std::XMLAttribute>> ls_std::XMLNode::getAttributes()
+{
+  return this->attributes;
+}
+
+std::list<std::shared_ptr<ls_std::XMLNode>> ls_std::XMLNode::getChildren()
+{
+  return this->children;
+}
+
+std::list<std::shared_ptr<ls_std::XMLNode>> ls_std::XMLNode::getChildren(const std::string &_name)
+{
+  std::list<std::shared_ptr<ls_std::XMLNode>> childrenWithName {};
+
+  for(const auto& child : this->children) {
+    if(child->getName() == _name) {
+      childrenWithName.push_back(child);
+    }
+  }
+
+  return childrenWithName;
+}
+
+std::string ls_std::XMLNode::getName()
+{
+  return this->name;
+}
+
+bool ls_std::XMLNode::hasAttribute(const std::string &_name)
+{
+  return this->_hasAttribute(_name);
+}
+
+bool ls_std::XMLNode::hasChild(const std::string &_name)
+{
+  return this->_hasChild(_name);
+}
+
+bool ls_std::XMLNode::hasChild(const std::shared_ptr<XMLNode> &_child)
+{
+  return this->_hasChild(_child);
+}
+
+void ls_std::XMLNode::removeFirstAttribute()
+{
+  this->attributes.pop_front();
+}
+
+void ls_std::XMLNode::removeLastAttribute()
+{
+  this->attributes.pop_back();
+}
+
+void ls_std::XMLNode::removeFirstChild()
+{
+  this->children.pop_front();
+}
+
+void ls_std::XMLNode::removeLastChild()
+{
+  this->children.pop_back();
+}
+
+void ls_std::XMLNode::setName(std::string _name)
+{
+  this->name = std::move(_name);
+}
+
+bool ls_std::XMLNode::_hasAttribute(const std::string &_name)
+{
+  bool exists {};
+
+  for(const auto& attribute : this->attributes) {
+    if(attribute->getName() == _name) {
+      exists = true;
+      break;
+    }
+  }
+
+  return exists;
+}
+
+bool ls_std::XMLNode::_hasChild(const std::shared_ptr<ls_std::XMLNode> &_child)
+{
+  return ls_std::STLUtils<std::list<std::shared_ptr<ls_std::XMLNode>>, std::shared_ptr<ls_std::XMLNode>>::contains(this->children, _child);
+}
+
+bool ls_std::XMLNode::_hasChild(const std::string &_name)
+{
+  bool exists {};
+
+  for(const auto& attribute : this->children) {
+    if(attribute->getName() == _name) {
+      exists = true;
+      break;
+    }
+  }
+
+  return exists;
+}

+ 59 - 0
source/io/xml/XMLNode.hpp

@@ -0,0 +1,59 @@
+/*
+ * Author:          Patrick-Christopher Mattulat
+ * Company:         Lynar Studios
+ * E-Mail:          webmaster@lynarstudios.com
+ * Created:         2020-09-24
+ * Changed:         2020-09-25
+ *
+ * */
+
+#ifndef LS_STD_XML_NODE_HPP
+#define LS_STD_XML_NODE_HPP
+
+#include <list>
+#include <memory>
+#include "../../base/Class.hpp"
+#include "XMLAttribute.hpp"
+
+namespace ls_std {
+  class XMLNode : public Class {
+    public:
+
+      explicit XMLNode(std::string _name);
+      ~XMLNode() = default;
+
+      bool addAttributeAfter(const std::shared_ptr<ls_std::XMLAttribute>& _attribute, const std::string& _name);
+      bool addAttributeBefore(const std::shared_ptr<ls_std::XMLAttribute>& _attribute, const std::string& _name);
+      bool addAttributeToBeginning(const std::shared_ptr<ls_std::XMLAttribute>& _attribute);
+      bool addAttributeToEnd(const std::shared_ptr<ls_std::XMLAttribute>& _attribute);
+      bool addChildAfter(const std::shared_ptr<XMLNode>& _child, const std::shared_ptr<XMLNode>& _search);
+      bool addChildBefore(const std::shared_ptr<XMLNode>& _child, const std::shared_ptr<XMLNode>& _search);
+      bool addChildToBeginning(const std::shared_ptr<XMLNode>& _child);
+      bool addChildToEnd(const std::shared_ptr<XMLNode>& _child);
+      std::list<std::shared_ptr<XMLAttribute>> getAttributes();
+      std::list<std::shared_ptr<XMLNode>> getChildren();
+      std::list<std::shared_ptr<XMLNode>> getChildren(const std::string& _name);
+      std::string getName();
+      bool hasAttribute(const std::string& _name);
+      bool hasChild(const std::string& _name);
+      bool hasChild(const std::shared_ptr<XMLNode>& _child);
+      void removeFirstAttribute();
+      void removeLastAttribute();
+      void removeFirstChild();
+      void removeLastChild();
+      void setName(std::string _name);
+      std::string toXML();
+
+    private:
+
+      std::list<std::shared_ptr<XMLAttribute>> attributes {};
+      std::list<std::shared_ptr<XMLNode>> children {};
+      std::string name {};
+
+      bool _hasAttribute(const std::string& _name);
+      bool _hasChild(const std::shared_ptr<XMLNode>& _child);
+      bool _hasChild(const std::string& _name);
+  };
+}
+
+#endif

+ 239 - 0
test/cases/io/xml/XMLNodeTest.cpp

@@ -0,0 +1,239 @@
+/*
+ * Author:          Patrick-Christopher Mattulat
+ * Company:         Lynar Studios
+ * E-Mail:          webmaster@lynarstudios.com
+ * Created:         2020-09-25
+ * Changed:         2020-09-25
+ *
+ * */
+
+#include <gtest/gtest.h>
+#include "../../../../source/io/xml/XMLNode.hpp"
+
+namespace {
+  class XMLNodeTest : public ::testing::Test {
+    protected:
+
+      XMLNodeTest() = default;
+      ~XMLNodeTest() override = default;
+
+      void SetUp() override {}
+      void TearDown() override {}
+  };
+
+  TEST_F(XMLNodeTest, addChildAfter)
+  {
+    ls_std::XMLNode dialogsNode {"dialogs"};
+    std::shared_ptr<ls_std::XMLNode> currentNode {};
+    std::shared_ptr<ls_std::XMLNode> dialogNodeA = std::make_shared<ls_std::XMLNode>("dialogNodeA");
+    std::shared_ptr<ls_std::XMLNode> dialogNodeB = std::make_shared<ls_std::XMLNode>("dialogNodeB");
+    std::shared_ptr<ls_std::XMLNode> dialogNodeC = std::make_shared<ls_std::XMLNode>("dialogNodeC");
+    std::shared_ptr<ls_std::XMLNode> dialogNodeD = std::make_shared<ls_std::XMLNode>("dialogNodeD");
+
+    ASSERT_TRUE(dialogsNode.addChildToEnd(dialogNodeB));
+    ASSERT_TRUE(dialogsNode.addChildToEnd(dialogNodeC));
+    ASSERT_EQ(2, dialogsNode.getChildren().size());
+    currentNode = *std::next(dialogsNode.getChildren().begin(), 0);
+    ASSERT_STREQ("dialogNodeB", currentNode->getName().c_str());
+    currentNode = *std::next(dialogsNode.getChildren().begin(), 1);
+    ASSERT_STREQ("dialogNodeC", currentNode->getName().c_str());
+
+    // add A after B
+
+    ASSERT_TRUE(dialogsNode.addChildAfter(dialogNodeA, dialogNodeB));
+
+    currentNode = *std::next(dialogsNode.getChildren().begin(), 0);
+    ASSERT_STREQ("dialogNodeB", currentNode->getName().c_str());
+    currentNode = *std::next(dialogsNode.getChildren().begin(), 1);
+    ASSERT_STREQ("dialogNodeA", currentNode->getName().c_str());
+    currentNode = *std::next(dialogsNode.getChildren().begin(), 2);
+    ASSERT_STREQ("dialogNodeC", currentNode->getName().c_str());
+
+    // add D after C
+
+    ASSERT_TRUE(dialogsNode.addChildAfter(dialogNodeD, dialogNodeC));
+
+    currentNode = *std::next(dialogsNode.getChildren().begin(), 0);
+    ASSERT_STREQ("dialogNodeB", currentNode->getName().c_str());
+    currentNode = *std::next(dialogsNode.getChildren().begin(), 1);
+    ASSERT_STREQ("dialogNodeA", currentNode->getName().c_str());
+    currentNode = *std::next(dialogsNode.getChildren().begin(), 2);
+    ASSERT_STREQ("dialogNodeC", currentNode->getName().c_str());
+    currentNode = *std::next(dialogsNode.getChildren().begin(), 3);
+    ASSERT_STREQ("dialogNodeD", currentNode->getName().c_str());
+  }
+
+  TEST_F(XMLNodeTest, addChildBefore)
+  {
+    ls_std::XMLNode dialogsNode {"dialogs"};
+    std::shared_ptr<ls_std::XMLNode> currentNode {};
+    std::shared_ptr<ls_std::XMLNode> dialogNodeA = std::make_shared<ls_std::XMLNode>("dialogNodeA");
+    std::shared_ptr<ls_std::XMLNode> dialogNodeB = std::make_shared<ls_std::XMLNode>("dialogNodeB");
+    std::shared_ptr<ls_std::XMLNode> dialogNodeC = std::make_shared<ls_std::XMLNode>("dialogNodeC");
+    std::shared_ptr<ls_std::XMLNode> dialogNodeD = std::make_shared<ls_std::XMLNode>("dialogNodeD");
+
+    ASSERT_TRUE(dialogsNode.addChildToEnd(dialogNodeB));
+    ASSERT_TRUE(dialogsNode.addChildToEnd(dialogNodeC));
+    ASSERT_EQ(2, dialogsNode.getChildren().size());
+    currentNode = *std::next(dialogsNode.getChildren().begin(), 0);
+    ASSERT_STREQ("dialogNodeB", currentNode->getName().c_str());
+    currentNode = *std::next(dialogsNode.getChildren().begin(), 1);
+    ASSERT_STREQ("dialogNodeC", currentNode->getName().c_str());
+
+    // add A before C
+
+    ASSERT_TRUE(dialogsNode.addChildBefore(dialogNodeA, dialogNodeC));
+
+    currentNode = *std::next(dialogsNode.getChildren().begin(), 0);
+    ASSERT_STREQ("dialogNodeB", currentNode->getName().c_str());
+    currentNode = *std::next(dialogsNode.getChildren().begin(), 1);
+    ASSERT_STREQ("dialogNodeA", currentNode->getName().c_str());
+    currentNode = *std::next(dialogsNode.getChildren().begin(), 2);
+    ASSERT_STREQ("dialogNodeC", currentNode->getName().c_str());
+
+    // add D before B
+
+    ASSERT_TRUE(dialogsNode.addChildBefore(dialogNodeD, dialogNodeB));
+
+    currentNode = *std::next(dialogsNode.getChildren().begin(), 0);
+    ASSERT_STREQ("dialogNodeD", currentNode->getName().c_str());
+    currentNode = *std::next(dialogsNode.getChildren().begin(), 1);
+    ASSERT_STREQ("dialogNodeB", currentNode->getName().c_str());
+    currentNode = *std::next(dialogsNode.getChildren().begin(), 2);
+    ASSERT_STREQ("dialogNodeA", currentNode->getName().c_str());
+    currentNode = *std::next(dialogsNode.getChildren().begin(), 3);
+    ASSERT_STREQ("dialogNodeC", currentNode->getName().c_str());
+  }
+
+  TEST_F(XMLNodeTest, addChildToBeginning)
+  {
+    ls_std::XMLNode dialogsNode {"dialogs"};
+    std::shared_ptr<ls_std::XMLNode> currentNode {};
+
+    ls_std::XMLNode dialogNodeA {"dialogNodeA"};
+    ls_std::XMLNode dialogNodeB {"dialogNodeB"};
+    ls_std::XMLNode dialogNodeC {"dialogNodeC"};
+
+    ASSERT_TRUE(dialogsNode.getChildren().empty());
+    ASSERT_EQ(0, dialogsNode.getChildren().size());
+
+    // adding C
+
+    ASSERT_TRUE(dialogsNode.addChildToBeginning(std::make_shared<ls_std::XMLNode>(dialogNodeC)));
+    ASSERT_TRUE(!dialogsNode.getChildren().empty());
+    ASSERT_EQ(1, dialogsNode.getChildren().size());
+
+    currentNode = *std::next(dialogsNode.getChildren().begin(), 0);
+    ASSERT_STREQ("dialogNodeC", currentNode->getName().c_str());
+
+    // adding B
+
+    ASSERT_TRUE(dialogsNode.addChildToBeginning(std::make_shared<ls_std::XMLNode>(dialogNodeB)));
+    ASSERT_EQ(2, dialogsNode.getChildren().size());
+    currentNode = *std::next(dialogsNode.getChildren().begin(), 0);
+    ASSERT_STREQ("dialogNodeB", currentNode->getName().c_str());
+    currentNode = *std::next(dialogsNode.getChildren().begin(), 1);
+    ASSERT_STREQ("dialogNodeC", currentNode->getName().c_str());
+
+    // adding A
+
+    ASSERT_TRUE(dialogsNode.addChildToBeginning(std::make_shared<ls_std::XMLNode>(dialogNodeA)));
+    ASSERT_EQ(3, dialogsNode.getChildren().size());
+    currentNode = *std::next(dialogsNode.getChildren().begin(), 0);
+    ASSERT_STREQ("dialogNodeA", currentNode->getName().c_str());
+    currentNode = *std::next(dialogsNode.getChildren().begin(), 1);
+    ASSERT_STREQ("dialogNodeB", currentNode->getName().c_str());
+    currentNode = *std::next(dialogsNode.getChildren().begin(), 2);
+    ASSERT_STREQ("dialogNodeC", currentNode->getName().c_str());
+  }
+
+  TEST_F(XMLNodeTest, addChildToEnd)
+  {
+    ls_std::XMLNode dialogsNode {"dialogs"};
+    std::shared_ptr<ls_std::XMLNode> currentNode {};
+
+    ls_std::XMLNode dialogNodeA {"dialogNodeA"};
+    ls_std::XMLNode dialogNodeB {"dialogNodeB"};
+    ls_std::XMLNode dialogNodeC {"dialogNodeC"};
+
+    ASSERT_TRUE(dialogsNode.getChildren().empty());
+    ASSERT_EQ(0, dialogsNode.getChildren().size());
+
+    // adding C
+
+    ASSERT_TRUE(dialogsNode.addChildToEnd(std::make_shared<ls_std::XMLNode>(dialogNodeC)));
+    ASSERT_TRUE(!dialogsNode.getChildren().empty());
+    ASSERT_EQ(1, dialogsNode.getChildren().size());
+
+    currentNode = *std::next(dialogsNode.getChildren().begin(), 0);
+    ASSERT_STREQ("dialogNodeC", currentNode->getName().c_str());
+
+    // adding B
+
+    ASSERT_TRUE(dialogsNode.addChildToEnd(std::make_shared<ls_std::XMLNode>(dialogNodeB)));
+    ASSERT_EQ(2, dialogsNode.getChildren().size());
+    currentNode = *std::next(dialogsNode.getChildren().begin(), 0);
+    ASSERT_STREQ("dialogNodeC", currentNode->getName().c_str());
+    currentNode = *std::next(dialogsNode.getChildren().begin(), 1);
+    ASSERT_STREQ("dialogNodeB", currentNode->getName().c_str());
+
+    // adding A
+
+    ASSERT_TRUE(dialogsNode.addChildToEnd(std::make_shared<ls_std::XMLNode>(dialogNodeA)));
+    ASSERT_EQ(3, dialogsNode.getChildren().size());
+    currentNode = *std::next(dialogsNode.getChildren().begin(), 0);
+    ASSERT_STREQ("dialogNodeC", currentNode->getName().c_str());
+    currentNode = *std::next(dialogsNode.getChildren().begin(), 1);
+    ASSERT_STREQ("dialogNodeB", currentNode->getName().c_str());
+    currentNode = *std::next(dialogsNode.getChildren().begin(), 2);
+    ASSERT_STREQ("dialogNodeA", currentNode->getName().c_str());
+  }
+
+  TEST_F(XMLNodeTest, getAttributes)
+  {
+    ls_std::XMLNode dialogNode {"dialog"};
+    ASSERT_TRUE(dialogNode.getAttributes().empty());
+  }
+
+  TEST_F(XMLNodeTest, getChildren)
+  {
+    ls_std::XMLNode dialogNode {"dialog"};
+    ASSERT_TRUE(dialogNode.getChildren().empty());
+  }
+
+  TEST_F(XMLNodeTest, getChildrenV2)
+  {
+    ls_std::XMLNode dialogsNode {"dialogs"};
+    ls_std::XMLNode dialogNode {"dialog"};
+    ls_std::XMLNode otherNode {"something"};
+
+    // preparation
+
+    ASSERT_TRUE(dialogsNode.addChildToEnd(std::make_shared<ls_std::XMLNode>(dialogNode)));
+    ASSERT_TRUE(dialogsNode.addChildToEnd(std::make_shared<ls_std::XMLNode>(otherNode)));
+    ASSERT_TRUE(dialogsNode.addChildToEnd(std::make_shared<ls_std::XMLNode>(otherNode)));
+    ASSERT_TRUE(dialogsNode.addChildToEnd(std::make_shared<ls_std::XMLNode>(dialogNode)));
+    ASSERT_TRUE(dialogsNode.addChildToEnd(std::make_shared<ls_std::XMLNode>(otherNode)));
+
+    // check
+
+    ASSERT_TRUE(!dialogsNode.getChildren().empty());
+    ASSERT_EQ(5, dialogsNode.getChildren().size());
+    ASSERT_EQ(2, dialogsNode.getChildren("dialog").size());
+    ASSERT_EQ(3, dialogsNode.getChildren("something").size());
+  }
+
+  TEST_F(XMLNodeTest, getName)
+  {
+    ls_std::XMLNode dialogNode {"dialog"};
+    ASSERT_STREQ("dialog", dialogNode.getName().c_str());
+  }
+
+  TEST_F(XMLNodeTest, setName)
+  {
+    ls_std::XMLNode dialogNode {"dialog"};
+    dialogNode.setName("dialog2");
+
+    ASSERT_STREQ("dialog2", dialogNode.getName().c_str());
+  }
+}