Browse Source

Remove parsing functionality from XMLReader class

- add XMLParser class (partially taken from XMLReader
class)
- add tests for XMLParser class
- remove parsing functionality from XMLReader class to
reduce complexity
- renamed XMLReaderMock class to XMLParserMock class
- adjust test for XMLParserMock class
- extend tests for XMLReader class
Patrick-Christopher Mattulat 3 years ago
parent
commit
f22a7d9d76

+ 6 - 4
CMakeLists.txt

@@ -70,10 +70,11 @@ set(SOURCE_FILES
         ${CMAKE_CURRENT_SOURCE_DIR}/source/ls_std/io/xml/XMLDeclaration.cpp
         ${CMAKE_CURRENT_SOURCE_DIR}/source/ls_std/io/xml/XMLDocument.cpp
         ${CMAKE_CURRENT_SOURCE_DIR}/source/ls_std/io/xml/XMLReader.cpp
-        ${CMAKE_CURRENT_SOURCE_DIR}/source/ls_std/io/xml/XMLReaderMock.cpp
+        ${CMAKE_CURRENT_SOURCE_DIR}/source/ls_std/io/xml/XMLParserMock.cpp
         ${CMAKE_CURRENT_SOURCE_DIR}/source/ls_std/io/StandardOutputWriter.cpp
         ${CMAKE_CURRENT_SOURCE_DIR}/source/ls_std/logic/Narrator.cpp
-        ${CMAKE_CURRENT_SOURCE_DIR}/source/ls_std/logic/IListener.cpp)
+        ${CMAKE_CURRENT_SOURCE_DIR}/source/ls_std/logic/IListener.cpp
+        ${CMAKE_CURRENT_SOURCE_DIR}/source/ls_std/io/xml/XMLParser.cpp)
 
 set(TEST_FILES
         ${CMAKE_CURRENT_SOURCE_DIR}/test/cases/boxing/IntegerTest.cpp
@@ -114,10 +115,11 @@ set(TEST_FILES
         ${CMAKE_CURRENT_SOURCE_DIR}/test/cases/io/xml/XMLDeclarationTest.cpp
         ${CMAKE_CURRENT_SOURCE_DIR}/test/cases/io/xml/XMLDocumentTest.cpp
         ${CMAKE_CURRENT_SOURCE_DIR}/test/cases/io/xml/XMLReaderTest.cpp
-        ${CMAKE_CURRENT_SOURCE_DIR}/test/cases/io/xml/XMLReaderMockTest.cpp
+        ${CMAKE_CURRENT_SOURCE_DIR}/test/cases/io/xml/XMLParserMockTest.cpp
         ${CMAKE_CURRENT_SOURCE_DIR}/test/classes/observer/TestDataCar.cpp
         ${CMAKE_CURRENT_SOURCE_DIR}/test/classes/observer/TestDataMercedesCar.cpp
-        ${CMAKE_CURRENT_SOURCE_DIR}/test/cases/logic/NarratorTest.cpp)
+        ${CMAKE_CURRENT_SOURCE_DIR}/test/cases/logic/NarratorTest.cpp
+        ${CMAKE_CURRENT_SOURCE_DIR}/test/cases/io/xml/XMLParserTest.cpp)
 
 ##########################################################
 # Build

+ 71 - 0
include/ls_std/io/xml/XMLParser.hpp

@@ -0,0 +1,71 @@
+/*
+ * Author:          Patrick-Christopher Mattulat
+ * Company:         Lynar Studios
+ * E-Mail:          webmaster@lynarstudios.com
+ * Created:         2020-11-25
+ * Changed:         2020-11-26
+ *
+ * */
+
+#ifndef LS_STD_XML_PARSER_HPP
+#define LS_STD_XML_PARSER_HPP
+
+#include <ls_std/base/Class.hpp>
+#include <ls_std/base/Types.hpp>
+#include "XMLDocument.hpp"
+#include "XMLParseMode.hpp"
+#include "XMLParseData.hpp"
+#include <list>
+
+namespace ls_std {
+  class XMLParser : public Class {
+    public:
+
+      explicit XMLParser(const std::shared_ptr<ls_std::XMLDocument>& _document);
+      ~XMLParser() override = default;
+
+      std::shared_ptr<ls_std::XMLDocument> getDocument();
+      void parse(const ls_std::byte_field& _data);
+      void setDocument(const std::shared_ptr<ls_std::XMLDocument>& _document);
+
+    protected:
+
+      static std::pair<std::string, std::string> _readAttribute_(const ls_std::byte_field& _data);
+      static std::list<std::pair<std::string, std::string>> _readAttributes_(ls_std::byte_field _data);
+
+    private:
+
+      uint8_t currentLevel {};
+      std::shared_ptr<ls_std::XMLDocument> document {};
+      uint8_t maxLevel {};
+      ls_std::XMLParseMode mode {};
+      std::list<ls_std::XMLParseData> parseData {};
+
+      void _analyze(const ls_std::byte_field &_data, std::string::size_type _index);
+      void _assignDocument(const std::shared_ptr<ls_std::XMLDocument>& _document);
+      static std::shared_ptr<ls_std::XMLDeclaration> _createDeclaration(const std::list<std::pair<std::string, std::string>>& _attributes);
+      static std::shared_ptr<ls_std::XMLNode> _createNode(const std::list<std::pair<std::string, std::string>>& _attributes, const std::string& _name);
+      static std::pair<std::string, std::string> _findAttribute(const std::list<std::pair<std::string, std::string>>& _attributes, const std::string& _name);
+      static size_t _findAttributeEndPosition(const ls_std::byte_field& _data);
+      static ls_std::byte_field _getNextTagString(const ls_std::byte_field& _data, std::string::size_type _index);
+      void _isClosingTag(const ls_std::byte_field& _data, std::string::size_type _index);
+      void _isDeclaration(const ls_std::byte_field& _data, std::string::size_type _index);
+      void _isOpeningTag(const ls_std::byte_field& _data, std::string::size_type _index);
+      void _isValue(const ls_std::byte_field& _data, std::string::size_type _index);
+      void _mergeNodes();
+      void _mergeChildrenToParentNode(const std::shared_ptr<ls_std::XMLNode>& _parent, std::list<ls_std::XMLParseData>::iterator& _iterator, uint8_t _parentLevel);
+      void _mergeNodesOnCurrentLevel();
+      void _parse(const ls_std::byte_field& _data);
+      static std::pair<std::string, std::string> _parseAttribute(const ls_std::byte_field& _data);
+      static std::list<std::pair<std::string, std::string>> _parseAttributes(ls_std::byte_field _data);
+      size_t _parseClosingTag(const ls_std::byte_field& _data, std::string::size_type _index);
+      size_t _parseDeclaration(const ls_std::byte_field& _data, std::string::size_type _index);
+      size_t _parseOpeningTag(const ls_std::byte_field& _data, std::string::size_type _index);
+      static ls_std::byte_field _parseTagName(const ls_std::byte_field& _data);
+      size_t _parseValue(const ls_std::byte_field& _data, std::string::size_type _index);
+      void _reset();
+      void _setMaxLevel();
+  };
+}
+
+#endif

+ 7 - 7
include/ls_std/io/xml/XMLReaderMock.hpp → include/ls_std/io/xml/XMLParserMock.hpp

@@ -3,21 +3,21 @@
  * Company:         Lynar Studios
  * E-Mail:          webmaster@lynarstudios.com
  * Created:         2020-10-18
- * Changed:         2020-11-14
+ * Changed:         2020-11-26
  *
  * */
 
-#ifndef LS_STD_XML_READER_MOCK_HPP
-#define LS_STD_XML_READER_MOCK_HPP
+#ifndef LS_STD_XML_PARSER_MOCK_HPP
+#define LS_STD_XML_PARSER_MOCK_HPP
 
-#include "XMLReader.hpp"
+#include "XMLParser.hpp"
 
 namespace ls_std {
-  class XMLReaderMock : public XMLReader {
+  class XMLParserMock : public XMLParser {
     public:
 
-      XMLReaderMock();
-      ~XMLReaderMock() override = default;
+      XMLParserMock();
+      ~XMLParserMock() override = default;
 
       static std::pair<std::string, std::string> readAttribute(const ls_std::byte_field &_data);
       static std::list<std::pair<std::string, std::string>> readAttributes(ls_std::byte_field _data);

+ 6 - 37
include/ls_std/io/xml/XMLReader.hpp

@@ -3,7 +3,7 @@
  * Company:         Lynar Studios
  * E-Mail:          webmaster@lynarstudios.com
  * Created:         2020-10-08
- * Changed:         2020-11-20
+ * Changed:         2020-11-26
  *
  * */
 
@@ -19,11 +19,11 @@
 #include <list>
 
 namespace ls_std {
-  class XMLReader : public Class, IReader {
+  class XMLReader : public Class, public IReader {
     public:
 
       explicit XMLReader(const std::shared_ptr<ls_std::XMLDocument>& _document, const std::string& _absolutePath);
-      ~XMLReader() = default;
+      ~XMLReader() override = default;
 
       // implementation
 
@@ -32,47 +32,16 @@ namespace ls_std {
       // additional functionality
 
       std::shared_ptr<ls_std::XMLDocument> getDocument();
+      void setDocument(const std::shared_ptr<ls_std::XMLDocument>& _document);
       void setFile(const ls_std::File& _xmlFile);
 
-    protected:
-
-      static std::pair<std::string, std::string> _readAttribute_(const ls_std::byte_field& _data);
-      static std::list<std::pair<std::string, std::string>> _readAttributes_(ls_std::byte_field _data);
-
     private:
 
-      uint8_t currentLevel {};
       std::shared_ptr<ls_std::XMLDocument> document {};
-      uint8_t maxLevel {};
-      ls_std::XMLParseMode mode {};
-      std::list<ls_std::XMLParseData> parseData {};
       ls_std::File xmlFile;
 
-      void _analyze(const ls_std::byte_field &_data, std::string::size_type _index);
-      static void _checkDocumentExistence(const std::shared_ptr<ls_std::XMLDocument>& _document);
-      static void _checkFileExistence(ls_std::File _xmlFile);
-      static std::shared_ptr<ls_std::XMLDeclaration> _createDeclaration(const std::list<std::pair<std::string, std::string>>& _attributes);
-      static std::shared_ptr<ls_std::XMLNode> _createNode(const std::list<std::pair<std::string, std::string>>& _attributes, const std::string& _name);
-      static std::pair<std::string, std::string> _findAttribute(const std::list<std::pair<std::string, std::string>>& _attributes, const std::string& _name);
-      static size_t _findAttributeEndPosition(const ls_std::byte_field& _data);
-      static ls_std::byte_field _getNextTagString(const ls_std::byte_field& _data, std::string::size_type _index);
-      void _isClosingTag(const ls_std::byte_field& _data, std::string::size_type _index);
-      void _isDeclaration(const ls_std::byte_field& _data, std::string::size_type _index);
-      void _isOpeningTag(const ls_std::byte_field& _data, std::string::size_type _index);
-      void _isValue(const ls_std::byte_field& _data, std::string::size_type _index);
-      void _mergeNodes();
-      void _mergeChildrenToParentNode(const std::shared_ptr<ls_std::XMLNode>& _parent, std::list<ls_std::XMLParseData>::iterator& _iterator, uint8_t _parentLevel);
-      void _mergeNodesOnCurrentLevel();
-      void _parse(const ls_std::byte_field& _data);
-      static std::pair<std::string, std::string> _parseAttribute(const ls_std::byte_field& _data);
-      static std::list<std::pair<std::string, std::string>> _parseAttributes(ls_std::byte_field _data);
-      size_t _parseClosingTag(const ls_std::byte_field& _data, std::string::size_type _index);
-      size_t _parseDeclaration(const ls_std::byte_field& _data, std::string::size_type _index);
-      size_t _parseOpeningTag(const ls_std::byte_field& _data, std::string::size_type _index);
-      static ls_std::byte_field _parseTagName(const ls_std::byte_field& _data);
-      size_t _parseValue(const ls_std::byte_field& _data, std::string::size_type _index);
-      void _reset();
-      void _setMaxLevel();
+      void _assignDocument(const std::shared_ptr<ls_std::XMLDocument>& _document);
+      void _assignFile(ls_std::File _xmlFile);
   };
 }
 

+ 2 - 2
include/ls_std/ls_std.hpp

@@ -3,7 +3,7 @@
  * Company:         Lynar Studios
  * E-Mail:          webmaster@lynarstudios.com
  * Created:         2020-10-29
- * Changed:         2020-11-06
+ * Changed:         2020-11-26
  *
  * */
 
@@ -30,7 +30,6 @@
 #include "io/logging/LogLevel.hpp"
 #include "io/logging/LogLevelValue.hpp"
 #include "io/logging/Logger.hpp"
-#include "io/xml/XMLReaderMock.hpp"
 #include "io/xml/XMLReader.hpp"
 #include "io/xml/XMLParseMode.hpp"
 #include "io/xml/XMLParseData.hpp"
@@ -38,6 +37,7 @@
 #include "io/xml/XMLDocument.hpp"
 #include "io/xml/XMLDeclaration.hpp"
 #include "io/xml/XMLAttribute.hpp"
+#include "io/xml/XMLParser.hpp"
 #include "io/File.hpp"
 #include "io/FileOutputStream.hpp"
 #include "io/FilePathSeparator.hpp"

+ 371 - 0
source/ls_std/io/xml/XMLParser.cpp

@@ -0,0 +1,371 @@
+/*
+ * Author:          Patrick-Christopher Mattulat
+ * Company:         Lynar Studios
+ * E-Mail:          webmaster@lynarstudios.com
+ * Created:         2020-11-26
+ * Changed:         2020-11-26
+ *
+ * */
+
+#include <ls_std/io/xml/XMLParser.hpp>
+#include <ls_std/exception/IllegalArgumentException.hpp>
+#include <ls_std/boxing/String.hpp>
+
+ls_std::XMLParser::XMLParser(const std::shared_ptr<ls_std::XMLDocument> &_document) : ls_std::Class("XMLParser")
+{
+  this->_assignDocument(_document);
+  this->_reset();
+}
+
+std::shared_ptr<ls_std::XMLDocument> ls_std::XMLParser::getDocument()
+{
+  return this->document;
+}
+
+void ls_std::XMLParser::parse(const ls_std::byte_field &_data)
+{
+  this->_parse(_data);
+  this->_mergeNodes();
+  this->_reset();
+}
+
+void ls_std::XMLParser::setDocument(const std::shared_ptr<ls_std::XMLDocument> &_document)
+{
+  this->_assignDocument(_document);
+}
+
+std::pair<std::string, std::string> ls_std::XMLParser::_readAttribute_(const ls_std::byte_field &_data)
+{
+  return ls_std::XMLParser::_parseAttribute(_data);
+}
+
+std::list<std::pair<std::string, std::string>> ls_std::XMLParser::_readAttributes_(ls_std::byte_field _data)
+{
+  return ls_std::XMLParser::_parseAttributes(std::move(_data));
+}
+
+void ls_std::XMLParser::_analyze(const ls_std::byte_field &_data, std::string::size_type _index)
+{
+  this->_isDeclaration(_data, _index);
+  this->_isClosingTag(_data, _index);
+  this->_isOpeningTag(_data, _index);
+  this->_isValue(_data, _index);
+}
+
+void ls_std::XMLParser::_assignDocument(const std::shared_ptr<ls_std::XMLDocument> &_document)
+{
+  if(_document == nullptr) {
+    throw ls_std::IllegalArgumentException {};
+  }
+
+  this->document = _document;
+}
+
+std::shared_ptr<ls_std::XMLDeclaration> ls_std::XMLParser::_createDeclaration(const std::list<std::pair<std::string, std::string>> &_attributes)
+{
+  std::shared_ptr<ls_std::XMLDeclaration> declaration = std::make_shared<ls_std::XMLDeclaration>("1.0");
+  std::pair<std::string, std::string> attribute = ls_std::XMLParser::_findAttribute(_attributes, "version");
+
+  if(!attribute.first.empty()) {
+    declaration->setVersion(attribute.second);
+  }
+
+  attribute = ls_std::XMLParser::_findAttribute(_attributes, "encoding");
+
+  if(!attribute.first.empty()) {
+    declaration->setEncoding(attribute.second);
+  }
+
+  attribute = ls_std::XMLParser::_findAttribute(_attributes, "standalone");
+
+  if(!attribute.first.empty()) {
+    declaration->setStandalone(attribute.second);
+  }
+
+  return declaration;
+}
+
+std::shared_ptr<ls_std::XMLNode> ls_std::XMLParser::_createNode(const std::list<std::pair<std::string, std::string>> &_attributes, const std::string &_name)
+{
+  std::shared_ptr<ls_std::XMLNode> node = std::make_shared<ls_std::XMLNode>(_name);
+  std::shared_ptr<ls_std::XMLAttribute> attribute {};
+
+  for(const auto& parsedAttribute : _attributes) {
+    attribute = std::make_shared<ls_std::XMLAttribute>(parsedAttribute.first);
+    attribute->setValue(parsedAttribute.second);
+    node->addAttributeToEnd(attribute);
+  }
+
+  return node;
+}
+
+std::pair<std::string, std::string> ls_std::XMLParser::_findAttribute(const std::list<std::pair<std::string, std::string>> &_attributes, const std::string &_name)
+{
+  std::pair<std::string, std::string> attribute {};
+
+  for(const auto& currentAttribute : _attributes) {
+    if(currentAttribute.first == _name) {
+      attribute = currentAttribute;
+      break;
+    }
+  }
+
+  return attribute;
+}
+
+size_t ls_std::XMLParser::_findAttributeEndPosition(const ls_std::byte_field &_data)
+{
+  std::string::size_type position = std::string::npos;
+  std::string::size_type counter {};
+
+  for(char letter : _data) {
+    if(letter == '"') {
+      counter++;
+    }
+
+    if(counter == 2) {
+      break;
+    }
+
+    position++;
+  }
+
+  return position;
+}
+
+ls_std::byte_field ls_std::XMLParser::_getNextTagString(const ls_std::byte_field &_data, std::string::size_type _index)
+{
+  ls_std::byte_field tag {};
+  size_t closingCharacterPosition = _index + _data.substr(_index).find('>');
+
+  if(closingCharacterPosition != std::string::npos) {
+    tag = _data.substr(_index, (closingCharacterPosition - _index) + 1);
+  }
+
+  return tag;
+}
+
+void ls_std::XMLParser::_isClosingTag(const ls_std::byte_field &_data, std::string::size_type _index)
+{
+  if(this->mode == XML_PARSE_MODE_ANALYZE && _data.substr(_index, 2) == "</") {
+    this->mode = XML_PARSE_MODE_CLOSING_TAG;
+  }
+}
+
+void ls_std::XMLParser::_isDeclaration(const ls_std::byte_field &_data, std::string::size_type _index)
+{
+  if(_data.substr(_index, 5) == "<?xml") {
+    this->mode = XML_PARSE_MODE_DECLARATION;
+  }
+}
+
+void ls_std::XMLParser::_isOpeningTag(const ls_std::byte_field &_data, std::string::size_type _index)
+{
+  if(this->mode == XML_PARSE_MODE_ANALYZE && _data.substr(_index, 1) == "<") {
+    this->mode = XML_PARSE_MODE_OPENING_TAG;
+  }
+}
+
+void ls_std::XMLParser::_isValue(const ls_std::byte_field &_data, std::string::size_type _index)
+{
+  if(this->mode == XML_PARSE_MODE_ANALYZE) {
+    std::string::size_type end = _data.substr(_index).find('<');
+    bool isValue = _data[_index - 1] == '>' && end != std::string::npos && end > 0;
+
+    if(isValue) {
+      ls_std::String value {_data.substr(_index, end)};
+
+      if(!value.contains("\n") && !value.contains("\r\n") ) {
+        this->mode = XML_PARSE_MODE_VALUE;
+      }
+    }
+  }
+}
+
+void ls_std::XMLParser::_mergeNodes()
+{
+  while(this->maxLevel > 1) {
+    this->_mergeNodesOnCurrentLevel();
+    this->maxLevel -= 1;
+  }
+
+  this->document->setRootElement(this->parseData.front().node);
+}
+
+void ls_std::XMLParser::_mergeChildrenToParentNode(const std::shared_ptr<ls_std::XMLNode> &_parent, std::list<ls_std::XMLParseData>::iterator &_iterator, uint8_t _parentLevel)
+{
+  do {
+    _iterator++;
+
+    if(_iterator == this->parseData.end()) {
+      break;
+    }
+    else {
+      if(_iterator->level == this->maxLevel) {
+        _parent->addChildToEnd(_iterator->node);
+      }
+    }
+  }
+  while(_iterator->level > _parentLevel);
+}
+
+void ls_std::XMLParser::_mergeNodesOnCurrentLevel()
+{
+  auto iterator = this->parseData.begin();
+  uint8_t parentLevel = this->maxLevel - 1;
+
+  while(iterator != this->parseData.end()) {
+    if(iterator->level == parentLevel) {
+      this->_mergeChildrenToParentNode(iterator->node, iterator, parentLevel);
+    }
+    else {
+      iterator++;
+    }
+  }
+}
+
+void ls_std::XMLParser::_parse(const ls_std::byte_field &_data)
+{
+  for(std::string::size_type index = 0 ; index < _data.size() ; index++) {
+    switch(this->mode) {
+      case XML_PARSE_MODE_ANALYZE:
+      {
+        this->_analyze(_data, index);
+      } break;
+      case XML_PARSE_MODE_DECLARATION:
+      {
+        --index;
+        index = this->_parseDeclaration(_data, index);
+        this->mode = XML_PARSE_MODE_ANALYZE;
+      } break;
+      case XML_PARSE_MODE_OPENING_TAG:
+      {
+        --index;
+        index = ls_std::XMLParser::_parseOpeningTag(_data, index);
+        this->mode = XML_PARSE_MODE_ANALYZE;
+      } break;
+      case XML_PARSE_MODE_VALUE:
+      {
+        --index;
+        index = ls_std::XMLParser::_parseValue(_data, index);
+        this->mode = XML_PARSE_MODE_ANALYZE;
+      } break;
+      case XML_PARSE_MODE_CLOSING_TAG:
+      {
+        --index;
+        index = ls_std::XMLParser::_parseClosingTag(_data, index);
+        this->mode = XML_PARSE_MODE_ANALYZE;
+      } break;
+    }
+  }
+}
+
+std::pair<std::string, std::string> ls_std::XMLParser::_parseAttribute(const ls_std::byte_field &_data)
+{
+  std::pair<std::string, std::string> parsedAttribute {};
+  parsedAttribute.first = _data.substr(0, _data.find('='));
+  parsedAttribute.second  = _data.substr(_data.find('"') + 1);
+  parsedAttribute.second.pop_back();
+
+  return parsedAttribute;
+}
+
+std::list<std::pair<std::string, std::string>> ls_std::XMLParser::_parseAttributes(ls_std::byte_field _data)
+{
+  std::list<std::pair<std::string, std::string>> attributes {};
+  size_t position = _data.find(' ');
+  _data = position == std::string::npos ? "" : _data.substr(position);
+
+  while(!_data.empty()) {
+    do {
+      position = _data.find(' ') + 1;
+    }
+    while(_data[position] == ' ');
+
+    if(_data.size() <= 3 && ls_std::String {_data}.endsWith(">")) {
+      break;
+    }
+
+    std::string attributeString = _data.substr(position, ls_std::XMLParser::_findAttributeEndPosition(_data) + 1);
+    attributes.push_back(ls_std::XMLParser::_parseAttribute(attributeString));
+    _data = _data.substr(position + attributeString.size());
+  }
+
+  return attributes;
+}
+
+size_t ls_std::XMLParser::_parseClosingTag(const ls_std::byte_field &_data, std::string::size_type _index)
+{
+  std::string tagString = ls_std::XMLParser::_getNextTagString(_data, _index);
+  this->currentLevel -= 1;
+  return tagString.empty() ? _index : _index + (tagString.size() - 1);
+}
+
+size_t ls_std::XMLParser::_parseDeclaration(const ls_std::byte_field &_data, std::string::size_type _index)
+{
+  std::string tagString = ls_std::XMLParser::_getNextTagString(_data, _index);
+  bool isValidTagString = !tagString.empty();
+
+  if(isValidTagString) {
+    std::shared_ptr<ls_std::XMLDeclaration> declaration = this->_createDeclaration(ls_std::XMLParser::_parseAttributes(tagString));
+    this->document->setDeclaration(declaration);
+  }
+
+  return !isValidTagString ? _index : _index + (tagString.size() - 1);
+}
+
+size_t ls_std::XMLParser::_parseOpeningTag(const ls_std::byte_field &_data, std::string::size_type _index)
+{
+  ls_std::String tagString {ls_std::XMLParser::_getNextTagString(_data, _index)};
+  bool isValidTagString = !tagString.toString().empty();
+  ls_std::XMLParseData singleParseData {};
+
+  if(isValidTagString) {
+    std::shared_ptr<ls_std::XMLNode> node = ls_std::XMLParser::_createNode(ls_std::XMLParser::_parseAttributes(tagString), ls_std::XMLParser::_parseTagName(tagString));
+
+    singleParseData.level = this->currentLevel;
+    singleParseData.node = node;
+    this->parseData.push_back(singleParseData);
+
+    if(!tagString.endsWith("/>")) {
+      this->currentLevel += 1;
+      this->_setMaxLevel();
+    }
+  }
+
+  return !isValidTagString ? _index : _index + (tagString.toString().size() - 1);
+}
+
+ls_std::byte_field ls_std::XMLParser::_parseTagName(const ls_std::byte_field &_data)
+{
+  std::string::size_type position = _data.find(' ');
+
+  if(position == std::string::npos) {
+    position = _data.find('>');
+  }
+
+  return _data.substr(1, position - 1);
+}
+
+size_t ls_std::XMLParser::_parseValue(const ls_std::byte_field &_data, std::string::size_type _index)
+{
+  ls_std::byte_field value = _data.substr(_index, _data.substr(_index).find('<'));
+  this->parseData.back().node->setValue(value);
+
+  return _index + (value.size() - 1);
+}
+
+void ls_std::XMLParser::_reset()
+{
+  this->currentLevel = 1;
+  this->maxLevel = 1;
+  this->mode = XML_PARSE_MODE_ANALYZE;
+  this->parseData.clear();
+}
+
+void ls_std::XMLParser::_setMaxLevel()
+{
+  if(this->currentLevel > this->maxLevel) {
+    this->maxLevel = this->currentLevel;
+  }
+}

+ 23 - 0
source/ls_std/io/xml/XMLParserMock.cpp

@@ -0,0 +1,23 @@
+/*
+ * Author:          Patrick-Christopher Mattulat
+ * Company:         Lynar Studios
+ * E-Mail:          webmaster@lynarstudios.com
+ * Created:         2020-10-18
+ * Changed:         2020-11-26
+ *
+ * */
+
+#include <ls_std/io/xml/XMLParserMock.hpp>
+
+ls_std::XMLParserMock::XMLParserMock() : ls_std::XMLParser(nullptr)
+{}
+
+std::pair<std::string, std::string> ls_std::XMLParserMock::readAttribute(const ls_std::byte_field &_data)
+{
+  return ls_std::XMLParser::_readAttribute_(_data);
+}
+
+std::list<std::pair<std::string, std::string>> ls_std::XMLParserMock::readAttributes(ls_std::byte_field _data)
+{
+  return ls_std::XMLParser::_readAttributes_(std::move(_data));
+}

+ 16 - 338
source/ls_std/io/xml/XMLReader.cpp

@@ -3,7 +3,7 @@
  * Company:         Lynar Studios
  * E-Mail:          webmaster@lynarstudios.com
  * Created:         2020-10-10
- * Changed:         2020-11-25
+ * Changed:         2020-11-26
  *
  * */
 
@@ -11,23 +11,19 @@
 #include <ls_std/exception/IllegalArgumentException.hpp>
 #include <ls_std/io/FileReader.hpp>
 #include <ls_std/boxing/String.hpp>
+#include <ls_std/io/xml/XMLParser.hpp>
 
-ls_std::XMLReader::XMLReader(const std::shared_ptr<ls_std::XMLDocument>& _document, const std::string& _absolutePath) :
-Class("XMLReader"),
-xmlFile(ls_std::File {_absolutePath}),
-document(_document)
+ls_std::XMLReader::XMLReader(const std::shared_ptr<ls_std::XMLDocument>& _document, const std::string& _absolutePath) : ls_std::Class("XMLReader"),
+xmlFile(ls_std::File {""})
 {
-  XMLReader::_checkDocumentExistence(_document);
-  XMLReader::_checkFileExistence(this->xmlFile);
-  this->_reset();
+  this->_assignDocument(_document);
+  this->_assignFile(ls_std::File {_absolutePath});
 }
 
 ls_std::byte_field ls_std::XMLReader::read()
 {
   ls_std::byte_field data = ls_std::FileReader {this->xmlFile}.read();
-  this->_parse(data);
-  this->_mergeNodes();
-  this->_reset();
+  ls_std::XMLParser {this->document}.parse(data);
 
   return data;
 }
@@ -37,348 +33,30 @@ std::shared_ptr<ls_std::XMLDocument> ls_std::XMLReader::getDocument()
   return this->document;
 }
 
-void ls_std::XMLReader::setFile(const ls_std::File &_xmlFile)
-{
-  XMLReader::_checkFileExistence(_xmlFile);
-  this->xmlFile = _xmlFile;
-}
-
-std::pair<std::string, std::string> ls_std::XMLReader::_readAttribute_(const ls_std::byte_field &_data)
+void ls_std::XMLReader::setDocument(const std::shared_ptr<ls_std::XMLDocument> &_document)
 {
-  return ls_std::XMLReader::_parseAttribute(_data);
+  this->_assignDocument(_document);
 }
 
-std::list<std::pair<std::string, std::string>> ls_std::XMLReader::_readAttributes_(ls_std::byte_field _data)
-{
-  return ls_std::XMLReader::_parseAttributes(std::move(_data));
-}
-
-void ls_std::XMLReader::_analyze(const ls_std::byte_field &_data, std::string::size_type _index)
+void ls_std::XMLReader::setFile(const ls_std::File &_xmlFile)
 {
-  this->_isDeclaration(_data, _index);
-  this->_isClosingTag(_data, _index);
-  this->_isOpeningTag(_data, _index);
-  this->_isValue(_data, _index);
+  this->_assignFile(_xmlFile);
 }
 
-void ls_std::XMLReader::_checkDocumentExistence(const std::shared_ptr<ls_std::XMLDocument>& _document)
+void ls_std::XMLReader::_assignDocument(const std::shared_ptr<ls_std::XMLDocument> &_document)
 {
   if(_document == nullptr) {
     throw ls_std::IllegalArgumentException {};
   }
+
+  this->document = _document;
 }
 
-void ls_std::XMLReader::_checkFileExistence(ls_std::File _xmlFile)
+void ls_std::XMLReader::_assignFile(ls_std::File _xmlFile)
 {
   if(!_xmlFile.exists()) {
     throw ls_std::IllegalArgumentException {};
   }
-}
-
-std::shared_ptr<ls_std::XMLDeclaration> ls_std::XMLReader::_createDeclaration(const std::list<std::pair<std::string, std::string>>& _attributes)
-{
-  std::shared_ptr<ls_std::XMLDeclaration> declaration = std::make_shared<ls_std::XMLDeclaration>("1.0");
-  std::pair<std::string, std::string> attribute = ls_std::XMLReader::_findAttribute(_attributes, "version");
-
-  if(!attribute.first.empty()) {
-    declaration->setVersion(attribute.second);
-  }
-
-  attribute = ls_std::XMLReader::_findAttribute(_attributes, "encoding");
-
-  if(!attribute.first.empty()) {
-    declaration->setEncoding(attribute.second);
-  }
-
-  attribute = ls_std::XMLReader::_findAttribute(_attributes, "standalone");
-
-  if(!attribute.first.empty()) {
-    declaration->setStandalone(attribute.second);
-  }
-
-  return declaration;
-}
-
-std::shared_ptr<ls_std::XMLNode> ls_std::XMLReader::_createNode(const std::list<std::pair<std::string, std::string>> &_attributes, const std::string& _name)
-{
-  std::shared_ptr<ls_std::XMLNode> node = std::make_shared<ls_std::XMLNode>(_name);
-  std::shared_ptr<ls_std::XMLAttribute> attribute {};
-
-  for(const auto& parsedAttribute : _attributes) {
-    attribute = std::make_shared<ls_std::XMLAttribute>(parsedAttribute.first);
-    attribute->setValue(parsedAttribute.second);
-    node->addAttributeToEnd(attribute);
-  }
-
-  return node;
-}
-
-std::pair<std::string, std::string> ls_std::XMLReader::_findAttribute(const std::list<std::pair<std::string, std::string>> &_attributes, const std::string &_name)
-{
-  std::pair<std::string, std::string> attribute {};
-
-  for(const auto& currentAttribute : _attributes) {
-    if(currentAttribute.first == _name) {
-      attribute = currentAttribute;
-      break;
-    }
-  }
-
-  return attribute;
-}
-
-size_t ls_std::XMLReader::_findAttributeEndPosition(const ls_std::byte_field &_data)
-{
-  std::string::size_type position = std::string::npos;
-  std::string::size_type counter {};
-
-  for(char letter : _data) {
-    if(letter == '"') {
-      counter++;
-    }
-
-    if(counter == 2) {
-      break;
-    }
-
-    position++;
-  }
-
-  return position;
-}
-
-ls_std::byte_field ls_std::XMLReader::_getNextTagString(const ls_std::byte_field &_data, std::string::size_type _index)
-{
-  ls_std::byte_field tag {};
-  size_t closingCharacterPosition = _index + _data.substr(_index).find('>');
-
-  if(closingCharacterPosition != std::string::npos) {
-    tag = _data.substr(_index, (closingCharacterPosition - _index) + 1);
-  }
-
-  return tag;
-}
-
-void ls_std::XMLReader::_isClosingTag(const ls_std::byte_field &_data, std::string::size_type _index)
-{
-  if(this->mode == XML_PARSE_MODE_ANALYZE && _data.substr(_index, 2) == "</") {
-    this->mode = XML_PARSE_MODE_CLOSING_TAG;
-  }
-}
-
-void ls_std::XMLReader::_isDeclaration(const ls_std::byte_field& _data, std::string::size_type _index)
-{
-  if(_data.substr(_index, 5) == "<?xml") {
-    this->mode = XML_PARSE_MODE_DECLARATION;
-  }
-}
-
-void ls_std::XMLReader::_isOpeningTag(const ls_std::byte_field &_data, std::string::size_type _index)
-{
-  if(this->mode == XML_PARSE_MODE_ANALYZE && _data.substr(_index, 1) == "<") {
-    this->mode = XML_PARSE_MODE_OPENING_TAG;
-  }
-}
-
-void ls_std::XMLReader::_isValue(const ls_std::byte_field &_data, std::string::size_type _index)
-{
-  if(this->mode == XML_PARSE_MODE_ANALYZE) {
-    std::string::size_type end = _data.substr(_index).find('<');
-    bool isValue = _data[_index - 1] == '>' && end != std::string::npos && end > 0;
-
-    if(isValue) {
-      ls_std::String value {_data.substr(_index, end)};
-
-      if(!value.contains("\n") && !value.contains("\r\n") ) {
-        this->mode = XML_PARSE_MODE_VALUE;
-      }
-    }
-  }
-}
-
-void ls_std::XMLReader::_mergeNodes()
-{
-  while(this->maxLevel > 1) {
-    this->_mergeNodesOnCurrentLevel();
-    this->maxLevel -= 1;
-  }
-
-  this->document->setRootElement(this->parseData.front().node);
-}
-
-void ls_std::XMLReader::_mergeChildrenToParentNode(const std::shared_ptr<ls_std::XMLNode>& _parent, std::list<ls_std::XMLParseData>::iterator& _iterator, uint8_t _parentLevel)
-{
-  do {
-    _iterator++;
 
-    if(_iterator == this->parseData.end()) {
-      break;
-    }
-    else {
-      if(_iterator->level == this->maxLevel) {
-        _parent->addChildToEnd(_iterator->node);
-      }
-    }
-  }
-  while(_iterator->level > _parentLevel);
-}
-
-void ls_std::XMLReader::_mergeNodesOnCurrentLevel() {
-  auto iterator = this->parseData.begin();
-  uint8_t parentLevel = this->maxLevel - 1;
-
-  while(iterator != this->parseData.end()) {
-    if(iterator->level == parentLevel) {
-      this->_mergeChildrenToParentNode(iterator->node, iterator, parentLevel);
-    }
-    else {
-      iterator++;
-    }
-  }
-}
-
-void ls_std::XMLReader::_parse(const ls_std::byte_field &_data)
-{
-  for(std::string::size_type index = 0 ; index < _data.size() ; index++) {
-    switch(this->mode) {
-      case XML_PARSE_MODE_ANALYZE:
-      {
-        this->_analyze(_data, index);
-      } break;
-      case XML_PARSE_MODE_DECLARATION:
-      {
-        --index;
-        index = this->_parseDeclaration(_data, index);
-        this->mode = XML_PARSE_MODE_ANALYZE;
-      } break;
-      case XML_PARSE_MODE_OPENING_TAG:
-      {
-        --index;
-        index = ls_std::XMLReader::_parseOpeningTag(_data, index);
-        this->mode = XML_PARSE_MODE_ANALYZE;
-      } break;
-      case XML_PARSE_MODE_VALUE:
-      {
-        --index;
-        index = ls_std::XMLReader::_parseValue(_data, index);
-        this->mode = XML_PARSE_MODE_ANALYZE;
-      } break;
-      case XML_PARSE_MODE_CLOSING_TAG:
-      {
-        --index;
-        index = ls_std::XMLReader::_parseClosingTag(_data, index);
-        this->mode = XML_PARSE_MODE_ANALYZE;
-      } break;
-    }
-  }
-}
-
-std::pair<std::string, std::string> ls_std::XMLReader::_parseAttribute(const ls_std::byte_field &_data)
-{
-  std::pair<std::string, std::string> parsedAttribute {};
-  parsedAttribute.first = _data.substr(0, _data.find('='));
-  parsedAttribute.second  = _data.substr(_data.find('"') + 1);
-  parsedAttribute.second.pop_back();
-
-  return parsedAttribute;
-}
-
-std::list<std::pair<std::string, std::string>> ls_std::XMLReader::_parseAttributes(ls_std::byte_field _data)
-{
-  std::list<std::pair<std::string, std::string>> attributes {};
-  size_t position = _data.find(' ');
-  _data = position == std::string::npos ? "" : _data.substr(position);
-
-  while(!_data.empty()) {
-    do {
-      position = _data.find(' ') + 1;
-    }
-    while(_data[position] == ' ');
-
-    if(_data.size() <= 3 && ls_std::String {_data}.endsWith(">")) {
-      break;
-    }
-
-    std::string attributeString = _data.substr(position, ls_std::XMLReader::_findAttributeEndPosition(_data) + 1);
-    attributes.push_back(ls_std::XMLReader::_parseAttribute(attributeString));
-    _data = _data.substr(position + attributeString.size());
-  }
-
-  return attributes;
-}
-
-size_t ls_std::XMLReader::_parseClosingTag(const ls_std::byte_field &_data, std::string::size_type _index)
-{
-  std::string tagString = ls_std::XMLReader::_getNextTagString(_data, _index);
-  this->currentLevel -= 1;
-  return tagString.empty() ? _index : _index + (tagString.size() - 1);
-}
-
-size_t ls_std::XMLReader::_parseDeclaration(const ls_std::byte_field &_data, std::string::size_type _index)
-{
-  std::string tagString = ls_std::XMLReader::_getNextTagString(_data, _index);
-  bool isValidTagString = !tagString.empty();
-
-  if(isValidTagString) {
-    std::shared_ptr<ls_std::XMLDeclaration> declaration = this->_createDeclaration(ls_std::XMLReader::_parseAttributes(tagString));
-    this->document->setDeclaration(declaration);
-  }
-
-  return !isValidTagString ? _index : _index + (tagString.size() - 1);
-}
-
-size_t ls_std::XMLReader::_parseOpeningTag(const ls_std::byte_field &_data, std::string::size_type _index)
-{
-  ls_std::String tagString {ls_std::XMLReader::_getNextTagString(_data, _index)};
-  bool isValidTagString = !tagString.toString().empty();
-  ls_std::XMLParseData singleParseData {};
-
-  if(isValidTagString) {
-    std::shared_ptr<ls_std::XMLNode> node = ls_std::XMLReader::_createNode(ls_std::XMLReader::_parseAttributes(tagString), ls_std::XMLReader::_parseTagName(tagString));
-
-    singleParseData.level = this->currentLevel;
-    singleParseData.node = node;
-    this->parseData.push_back(singleParseData);
-
-    if(!tagString.endsWith("/>")) {
-      this->currentLevel += 1;
-      this->_setMaxLevel();
-    }
-  }
-
-  return !isValidTagString ? _index : _index + (tagString.toString().size() - 1);
-}
-
-ls_std::byte_field ls_std::XMLReader::_parseTagName(const ls_std::byte_field &_data)
-{
-  std::string::size_type position = _data.find(' ');
-
-  if(position == std::string::npos) {
-    position = _data.find('>');
-  }
-
-  return _data.substr(1, position - 1);
-}
-
-size_t ls_std::XMLReader::_parseValue(const ls_std::byte_field &_data, std::string::size_type _index)
-{
-  ls_std::byte_field value = _data.substr(_index, _data.substr(_index).find('<'));
-  this->parseData.back().node->setValue(value);
-
-  return _index + (value.size() - 1);
-}
-
-void ls_std::XMLReader::_reset()
-{
-  this->currentLevel = 1;
-  this->maxLevel = 1;
-  this->mode = XML_PARSE_MODE_ANALYZE;
-  this->parseData.clear();
-}
-
-void ls_std::XMLReader::_setMaxLevel()
-{
-  if(this->currentLevel > this->maxLevel) {
-    this->maxLevel = this->currentLevel;
-  }
+  this->xmlFile = _xmlFile;
 }

+ 0 - 23
source/ls_std/io/xml/XMLReaderMock.cpp

@@ -1,23 +0,0 @@
-/*
- * Author:          Patrick-Christopher Mattulat
- * Company:         Lynar Studios
- * E-Mail:          webmaster@lynarstudios.com
- * Created:         2020-10-18
- * Changed:         2020-11-25
- *
- * */
-
-#include <ls_std/io/xml/XMLReaderMock.hpp>
-
-ls_std::XMLReaderMock::XMLReaderMock() : XMLReader(nullptr, "")
-{}
-
-std::pair<std::string, std::string> ls_std::XMLReaderMock::readAttribute(const ls_std::byte_field &_data)
-{
-  return ls_std::XMLReader::_readAttribute_(_data);
-}
-
-std::list<std::pair<std::string, std::string>> ls_std::XMLReaderMock::readAttributes(ls_std::byte_field _data)
-{
-  return ls_std::XMLReader::_readAttributes_(std::move(_data));
-}

+ 11 - 11
test/cases/io/xml/XMLReaderMockTest.cpp → test/cases/io/xml/XMLParserMockTest.cpp

@@ -3,41 +3,41 @@
  * Company:         Lynar Studios
  * E-Mail:          webmaster@lynarstudios.com
  * Created:         2020-10-18
- * Changed:         2020-10-18
+ * Changed:         2020-11-26
  *
  * */
 
 #include <gtest/gtest.h>
-#include <ls_std/ls_std.hpp>
+#include <ls_std/io/xml/XMLParserMock.hpp>
 
 namespace {
-  class XMLReaderMockTest : public ::testing::Test {
+  class XMLParserMockTest : public ::testing::Test {
     protected:
 
-      XMLReaderMockTest() = default;
-      ~XMLReaderMockTest() override = default;
+      XMLParserMockTest() = default;
+      ~XMLParserMockTest() override = default;
 
       void SetUp() override {}
       void TearDown() override {}
   };
 
-  TEST_F(XMLReaderMockTest, readAttribute)
+  TEST_F(XMLParserMockTest, readAttribute)
   {
-    std::pair<std::string, std::string> attribute = ls_std::XMLReaderMock::readAttribute(R"(name="tim")");
+    std::pair<std::string, std::string> attribute = ls_std::XMLParserMock::readAttribute(R"(name="tim")");
     ASSERT_TRUE(attribute.first == "name");
     ASSERT_TRUE(attribute.second == "tim");
 
-    attribute = ls_std::XMLReaderMock::readAttribute(R"(id="dialog_001")");
+    attribute = ls_std::XMLParserMock::readAttribute(R"(id="dialog_001")");
     ASSERT_TRUE(attribute.first == "id");
     ASSERT_TRUE(attribute.second == "dialog_001");
   }
 
-  TEST_F(XMLReaderMockTest, readAttributes)
+  TEST_F(XMLParserMockTest, readAttributes)
   {
     // first case
 
     std::string tag = R"(<?xml version="1.0" encoding="UTF-8" ?>)";
-    std::list<std::pair<std::string, std::string>> attributes = ls_std::XMLReaderMock::readAttributes(tag);
+    std::list<std::pair<std::string, std::string>> attributes = ls_std::XMLParserMock::readAttributes(tag);
 
     ASSERT_EQ(2, attributes.size());
 
@@ -52,7 +52,7 @@ namespace {
     // second case
 
     tag = R"(<stateMachine name="test_machine">)";
-    attributes = ls_std::XMLReaderMock::readAttributes(tag);
+    attributes = ls_std::XMLParserMock::readAttributes(tag);
 
     ASSERT_EQ(1, attributes.size());
 

+ 219 - 0
test/cases/io/xml/XMLParserTest.cpp

@@ -0,0 +1,219 @@
+/*
+ * Author:          Patrick-Christopher Mattulat
+ * Company:         Lynar Studios
+ * E-Mail:          webmaster@lynarstudios.com
+ * Created:         2020-11-26
+ * Changed:         2020-11-26
+ *
+ * */
+
+#include <gtest/gtest.h>
+#include <ls_std/ls_std.hpp>
+#include "../../../TestHelper.hpp"
+
+namespace {
+  class XMLParserTest : public ::testing::Test {
+    protected:
+
+      XMLParserTest() = default;
+      ~XMLParserTest() override = default;
+
+      static ls_std::byte_field readXMLStateMachine() {
+        std::string xmlPath = TestHelper::getResourcesFolderLocation() + "state_machine_test.xml";
+        ls_std::File file {xmlPath};
+        ls_std::byte_field data = ls_std::FileReader {file}.read();
+
+        return data;
+      }
+
+      void SetUp() override {}
+      void TearDown() override {}
+  };
+
+  TEST_F(XMLParserTest, read)
+  {
+    ls_std::XMLParser xmlParser {std::make_shared<ls_std::XMLDocument>()};
+    std::list<std::shared_ptr<ls_std::XMLNode>> children, statesChildren, memoryChildren, connectionChildren {};
+    std::list<std::shared_ptr<ls_std::XMLAttribute>> attributes {};
+
+    ls_std::byte_field data = readXMLStateMachine();
+    xmlParser.parse(data);
+
+    // check declaration
+
+    ASSERT_STREQ("UTF-8", xmlParser.getDocument()->getDeclaration()->getEncoding().c_str());
+    ASSERT_STREQ("1.0", xmlParser.getDocument()->getDeclaration()->getVersion().c_str());
+    ASSERT_TRUE(xmlParser.getDocument()->getDeclaration()->getStandalone().empty());
+
+    // check root element
+
+    std::shared_ptr<ls_std::XMLNode> root = xmlParser.getDocument()->getRootElement();
+    ASSERT_STREQ("stateMachine", root->getName().c_str());
+    ASSERT_STREQ("name", root->getAttributes().front()->getName().c_str());
+    ASSERT_EQ(1, root->getAttributes().size());
+    ASSERT_STREQ("test_machine", root->getAttributes().front()->getValue().c_str());
+
+    // root children
+
+    children = root->getChildren();
+    ASSERT_EQ(3, children.size());
+    ASSERT_STREQ("states", ls_std::STLUtils::getListElementAt(children, 0)->getName().c_str());
+    ASSERT_TRUE(ls_std::STLUtils::getListElementAt(children, 0)->getAttributes().empty());
+    ASSERT_STREQ("currentState", ls_std::STLUtils::getListElementAt(children, 1)->getName().c_str());
+    ASSERT_TRUE(ls_std::STLUtils::getListElementAt(children, 1)->getAttributes().empty());
+    ASSERT_STREQ("memory", ls_std::STLUtils::getListElementAt(children, 2)->getName().c_str());
+    ASSERT_TRUE(ls_std::STLUtils::getListElementAt(children, 2)->getAttributes().empty());
+
+    // states
+
+    statesChildren = ls_std::STLUtils::getListElementAt(children, 0)->getChildren();
+    ASSERT_EQ(5, statesChildren.size());
+
+    ASSERT_STREQ("state", ls_std::STLUtils::getListElementAt(statesChildren, 0)->getName().c_str());
+    ASSERT_EQ(1, ls_std::STLUtils::getListElementAt(statesChildren, 0)->getAttributes().size());
+    ASSERT_STREQ("id", ls_std::STLUtils::getListElementAt(statesChildren, 0)->getAttributes().front()->getName().c_str());
+    ASSERT_STREQ("A", ls_std::STLUtils::getListElementAt(statesChildren, 0)->getAttributes().front()->getValue().c_str());
+    ASSERT_TRUE(ls_std::STLUtils::getListElementAt(statesChildren, 0)->getValue().empty());
+    ASSERT_EQ(1, ls_std::STLUtils::getListElementAt(statesChildren, 0)->getChildren().size());
+    ASSERT_STREQ("connections", ls_std::STLUtils::getListElementAt(statesChildren, 0)->getChildren().front()->getName().c_str());
+    ASSERT_TRUE(ls_std::STLUtils::getListElementAt(statesChildren, 0)->getChildren().front()->getValue().empty());
+    ASSERT_TRUE(ls_std::STLUtils::getListElementAt(statesChildren, 0)->getChildren().front()->getAttributes().empty());
+    connectionChildren = ls_std::STLUtils::getListElementAt(statesChildren, 0)->getChildren().front()->getChildren();
+    ASSERT_EQ(1, connectionChildren.size());
+    ASSERT_STREQ("connection", ls_std::STLUtils::getListElementAt(connectionChildren, 0)->getName().c_str());
+    ASSERT_TRUE(ls_std::STLUtils::getListElementAt(connectionChildren, 0)->getValue().empty());
+    attributes = ls_std::STLUtils::getListElementAt(connectionChildren, 0)->getAttributes();
+    ASSERT_EQ(3, attributes.size());
+    ASSERT_STREQ("connectionId", ls_std::STLUtils::getListElementAt(attributes, 0)->getName().c_str());
+    ASSERT_STREQ("AB", ls_std::STLUtils::getListElementAt(attributes, 0)->getValue().c_str());
+    ASSERT_STREQ("condition", ls_std::STLUtils::getListElementAt(attributes, 1)->getName().c_str());
+    ASSERT_STREQ("false", ls_std::STLUtils::getListElementAt(attributes, 1)->getValue().c_str());
+    ASSERT_STREQ("id", ls_std::STLUtils::getListElementAt(attributes, 2)->getName().c_str());
+    ASSERT_STREQ("B", ls_std::STLUtils::getListElementAt(attributes, 2)->getValue().c_str());
+
+    ASSERT_STREQ("state", ls_std::STLUtils::getListElementAt(statesChildren, 1)->getName().c_str());
+    ASSERT_EQ(1, ls_std::STLUtils::getListElementAt(statesChildren, 1)->getAttributes().size());
+    ASSERT_STREQ("id", ls_std::STLUtils::getListElementAt(statesChildren, 1)->getAttributes().front()->getName().c_str());
+    ASSERT_STREQ("B", ls_std::STLUtils::getListElementAt(statesChildren, 1)->getAttributes().front()->getValue().c_str());
+    ASSERT_TRUE(ls_std::STLUtils::getListElementAt(statesChildren, 1)->getValue().empty());
+    ASSERT_EQ(1, ls_std::STLUtils::getListElementAt(statesChildren, 1)->getChildren().size());
+    ASSERT_STREQ("connections", ls_std::STLUtils::getListElementAt(statesChildren, 1)->getChildren().front()->getName().c_str());
+    ASSERT_TRUE(ls_std::STLUtils::getListElementAt(statesChildren, 1)->getChildren().front()->getValue().empty());
+    ASSERT_TRUE(ls_std::STLUtils::getListElementAt(statesChildren, 1)->getChildren().front()->getAttributes().empty());
+    connectionChildren = ls_std::STLUtils::getListElementAt(statesChildren, 1)->getChildren().front()->getChildren();
+    ASSERT_EQ(2, connectionChildren.size());
+    ASSERT_STREQ("connection", ls_std::STLUtils::getListElementAt(connectionChildren, 0)->getName().c_str());
+    ASSERT_TRUE(ls_std::STLUtils::getListElementAt(connectionChildren, 0)->getValue().empty());
+    attributes = ls_std::STLUtils::getListElementAt(connectionChildren, 0)->getAttributes();
+    ASSERT_EQ(3, attributes.size());
+    ASSERT_STREQ("connectionId", ls_std::STLUtils::getListElementAt(attributes, 0)->getName().c_str());
+    ASSERT_STREQ("BC", ls_std::STLUtils::getListElementAt(attributes, 0)->getValue().c_str());
+    ASSERT_STREQ("condition", ls_std::STLUtils::getListElementAt(attributes, 1)->getName().c_str());
+    ASSERT_STREQ("false", ls_std::STLUtils::getListElementAt(attributes, 1)->getValue().c_str());
+    ASSERT_STREQ("id", ls_std::STLUtils::getListElementAt(attributes, 2)->getName().c_str());
+    ASSERT_STREQ("C", ls_std::STLUtils::getListElementAt(attributes, 2)->getValue().c_str());
+    ASSERT_STREQ("connection", ls_std::STLUtils::getListElementAt(connectionChildren, 1)->getName().c_str());
+    ASSERT_TRUE(ls_std::STLUtils::getListElementAt(connectionChildren, 1)->getValue().empty());
+    attributes = ls_std::STLUtils::getListElementAt(connectionChildren, 1)->getAttributes();
+    ASSERT_EQ(3, attributes.size());
+    ASSERT_STREQ("connectionId", ls_std::STLUtils::getListElementAt(attributes, 0)->getName().c_str());
+    ASSERT_STREQ("BD", ls_std::STLUtils::getListElementAt(attributes, 0)->getValue().c_str());
+    ASSERT_STREQ("condition", ls_std::STLUtils::getListElementAt(attributes, 1)->getName().c_str());
+    ASSERT_STREQ("false", ls_std::STLUtils::getListElementAt(attributes, 1)->getValue().c_str());
+    ASSERT_STREQ("id", ls_std::STLUtils::getListElementAt(attributes, 2)->getName().c_str());
+    ASSERT_STREQ("D", ls_std::STLUtils::getListElementAt(attributes, 2)->getValue().c_str());
+
+    ASSERT_STREQ("state", ls_std::STLUtils::getListElementAt(statesChildren, 2)->getName().c_str());
+    ASSERT_EQ(1, ls_std::STLUtils::getListElementAt(statesChildren, 2)->getAttributes().size());
+    ASSERT_STREQ("id", ls_std::STLUtils::getListElementAt(statesChildren, 2)->getAttributes().front()->getName().c_str());
+    ASSERT_STREQ("C", ls_std::STLUtils::getListElementAt(statesChildren, 2)->getAttributes().front()->getValue().c_str());
+    ASSERT_TRUE(ls_std::STLUtils::getListElementAt(statesChildren, 2)->getValue().empty());
+    ASSERT_EQ(1, ls_std::STLUtils::getListElementAt(statesChildren, 2)->getChildren().size());
+    ASSERT_STREQ("connections", ls_std::STLUtils::getListElementAt(statesChildren, 2)->getChildren().front()->getName().c_str());
+    ASSERT_TRUE(ls_std::STLUtils::getListElementAt(statesChildren, 2)->getChildren().front()->getValue().empty());
+    ASSERT_TRUE(ls_std::STLUtils::getListElementAt(statesChildren, 2)->getChildren().front()->getAttributes().empty());
+    connectionChildren = ls_std::STLUtils::getListElementAt(statesChildren, 2)->getChildren().front()->getChildren();
+    ASSERT_EQ(1, connectionChildren.size());
+    ASSERT_STREQ("connection", ls_std::STLUtils::getListElementAt(connectionChildren, 0)->getName().c_str());
+    ASSERT_TRUE(ls_std::STLUtils::getListElementAt(connectionChildren, 0)->getValue().empty());
+    attributes = ls_std::STLUtils::getListElementAt(connectionChildren, 0)->getAttributes();
+    ASSERT_EQ(3, attributes.size());
+    ASSERT_STREQ("connectionId", ls_std::STLUtils::getListElementAt(attributes, 0)->getName().c_str());
+    ASSERT_STREQ("CE", ls_std::STLUtils::getListElementAt(attributes, 0)->getValue().c_str());
+    ASSERT_STREQ("condition", ls_std::STLUtils::getListElementAt(attributes, 1)->getName().c_str());
+    ASSERT_STREQ("false", ls_std::STLUtils::getListElementAt(attributes, 1)->getValue().c_str());
+    ASSERT_STREQ("id", ls_std::STLUtils::getListElementAt(attributes, 2)->getName().c_str());
+    ASSERT_STREQ("E", ls_std::STLUtils::getListElementAt(attributes, 2)->getValue().c_str());
+
+    ASSERT_STREQ("state", ls_std::STLUtils::getListElementAt(statesChildren, 3)->getName().c_str());
+    ASSERT_EQ(1, ls_std::STLUtils::getListElementAt(statesChildren, 3)->getAttributes().size());
+    ASSERT_STREQ("id", ls_std::STLUtils::getListElementAt(statesChildren, 3)->getAttributes().front()->getName().c_str());
+    ASSERT_STREQ("D", ls_std::STLUtils::getListElementAt(statesChildren, 3)->getAttributes().front()->getValue().c_str());
+    ASSERT_TRUE(ls_std::STLUtils::getListElementAt(statesChildren, 3)->getChildren().front()->getAttributes().empty());
+    connectionChildren = ls_std::STLUtils::getListElementAt(statesChildren, 3)->getChildren().front()->getChildren();
+    ASSERT_EQ(1, connectionChildren.size());
+    ASSERT_STREQ("connection", ls_std::STLUtils::getListElementAt(connectionChildren, 0)->getName().c_str());
+    ASSERT_TRUE(ls_std::STLUtils::getListElementAt(connectionChildren, 0)->getValue().empty());
+    attributes = ls_std::STLUtils::getListElementAt(connectionChildren, 0)->getAttributes();
+    ASSERT_EQ(3, attributes.size());
+    ASSERT_STREQ("connectionId", ls_std::STLUtils::getListElementAt(attributes, 0)->getName().c_str());
+    ASSERT_STREQ("DE", ls_std::STLUtils::getListElementAt(attributes, 0)->getValue().c_str());
+    ASSERT_STREQ("condition", ls_std::STLUtils::getListElementAt(attributes, 1)->getName().c_str());
+    ASSERT_STREQ("false", ls_std::STLUtils::getListElementAt(attributes, 1)->getValue().c_str());
+    ASSERT_STREQ("id", ls_std::STLUtils::getListElementAt(attributes, 2)->getName().c_str());
+    ASSERT_STREQ("E", ls_std::STLUtils::getListElementAt(attributes, 2)->getValue().c_str());
+
+    ASSERT_STREQ("state", ls_std::STLUtils::getListElementAt(statesChildren, 4)->getName().c_str());
+    ASSERT_EQ(1, ls_std::STLUtils::getListElementAt(statesChildren, 4)->getAttributes().size());
+    ASSERT_STREQ("id", ls_std::STLUtils::getListElementAt(statesChildren, 4)->getAttributes().front()->getName().c_str());
+    ASSERT_STREQ("E", ls_std::STLUtils::getListElementAt(statesChildren, 4)->getAttributes().front()->getValue().c_str());
+    ASSERT_TRUE(ls_std::STLUtils::getListElementAt(statesChildren, 4)->getChildren().empty());
+
+    // current state
+
+    ASSERT_STREQ("currentState", ls_std::STLUtils::getListElementAt(children, 1)->getName().c_str());
+    ASSERT_STREQ("A", ls_std::STLUtils::getListElementAt(children, 1)->getValue().c_str());
+    ASSERT_TRUE(ls_std::STLUtils::getListElementAt(children, 1)->getChildren().empty());
+    ASSERT_TRUE(ls_std::STLUtils::getListElementAt(children, 1)->getAttributes().empty());
+
+    // memory
+
+    memoryChildren = ls_std::STLUtils::getListElementAt(children, 2)->getChildren();
+    ASSERT_EQ(3, memoryChildren.size());
+
+    ASSERT_STREQ("location", ls_std::STLUtils::getListElementAt(memoryChildren, 0)->getName().c_str());
+    ASSERT_STREQ("A", ls_std::STLUtils::getListElementAt(memoryChildren, 0)->getValue().c_str());
+    ASSERT_TRUE(ls_std::STLUtils::getListElementAt(memoryChildren, 0)->getChildren().empty());
+    ASSERT_TRUE(ls_std::STLUtils::getListElementAt(memoryChildren, 0)->getAttributes().empty());
+
+    ASSERT_STREQ("location", ls_std::STLUtils::getListElementAt(memoryChildren, 1)->getName().c_str());
+    ASSERT_STREQ("B", ls_std::STLUtils::getListElementAt(memoryChildren, 1)->getValue().c_str());
+    ASSERT_TRUE(ls_std::STLUtils::getListElementAt(memoryChildren, 1)->getChildren().empty());
+    ASSERT_TRUE(ls_std::STLUtils::getListElementAt(memoryChildren, 1)->getAttributes().empty());
+
+    ASSERT_STREQ("location", ls_std::STLUtils::getListElementAt(memoryChildren, 2)->getName().c_str());
+    ASSERT_STREQ("C", ls_std::STLUtils::getListElementAt(memoryChildren, 2)->getValue().c_str());
+    ASSERT_TRUE(ls_std::STLUtils::getListElementAt(memoryChildren, 2)->getChildren().empty());
+    ASSERT_TRUE(ls_std::STLUtils::getListElementAt(memoryChildren, 2)->getAttributes().empty());
+  }
+
+  TEST_F(XMLParserTest, getDocument)
+  {
+    std::string xmlPath = TestHelper::getResourcesFolderLocation() + "state_machine_test.xml";
+    ls_std::XMLParser xmlParser {std::make_shared<ls_std::XMLDocument>()};
+
+    ASSERT_TRUE(xmlParser.getDocument() != nullptr);
+  }
+
+  TEST_F(XMLParserTest, setDocument)
+  {
+    std::string xmlPath = TestHelper::getResourcesFolderLocation() + "state_machine_test.xml";
+    std::shared_ptr<ls_std::XMLDocument> document = std::make_shared<ls_std::XMLDocument>();
+    ls_std::XMLParser xmlParser {document};
+    ASSERT_TRUE(xmlParser.getDocument() == document);
+
+    document =std::make_shared<ls_std::XMLDocument>();
+    xmlParser.setDocument(document);
+    ASSERT_TRUE(xmlParser.getDocument() == document);
+  }
+}

+ 13 - 1
test/cases/io/xml/XMLReaderTest.cpp

@@ -3,7 +3,7 @@
  * Company:         Lynar Studios
  * E-Mail:          webmaster@lynarstudios.com
  * Created:         2020-10-10
- * Changed:         2020-10-22
+ * Changed:         2020-11-26
  *
  * */
 
@@ -197,6 +197,18 @@ namespace {
     ASSERT_TRUE(xmlReader.getDocument() != nullptr);
   }
 
+  TEST_F(XMLReaderTest, setDocument)
+  {
+    std::string xmlPath = TestHelper::getResourcesFolderLocation() + "state_machine_test.xml";
+    std::shared_ptr<ls_std::XMLDocument> document = std::make_shared<ls_std::XMLDocument>();
+    ls_std::XMLReader xmlReader {document, xmlPath};
+    ASSERT_TRUE(xmlReader.getDocument() == document);
+
+    document = std::make_shared<ls_std::XMLDocument>();
+    xmlReader.setDocument(document);
+    ASSERT_TRUE(xmlReader.getDocument() == document);
+  }
+
   TEST_F(XMLReaderTest, setFile)
   {
     std::string xmlPath = TestHelper::getResourcesFolderLocation() + "state_machine_test.xml";