Browse Source

Add serializable for SectionPairSection class

Patrick-Christopher Mattulat 2 years ago
parent
commit
773c248738

+ 4 - 0
CMakeLists.txt

@@ -184,9 +184,11 @@ set(SOURCE_FILES_IO
         ${CMAKE_CURRENT_SOURCE_DIR}/source/ls-std/io/section-pair/model/SectionPairSection.cpp
         ${CMAKE_CURRENT_SOURCE_DIR}/source/ls-std/io/section-pair/validator/SectionPairIdentifierValidator.cpp
         ${CMAKE_CURRENT_SOURCE_DIR}/source/ls-std/io/section-pair/validator/SectionPairRowValueValidator.cpp
+        ${CMAKE_CURRENT_SOURCE_DIR}/source/ls-std/io/section-pair/validator/SectionPairSectionIdUnmarshalValidator.cpp
         ${CMAKE_CURRENT_SOURCE_DIR}/source/ls-std/io/section-pair/serialization/SerializableSectionPairRow.cpp
         ${CMAKE_CURRENT_SOURCE_DIR}/source/ls-std/io/section-pair/serialization/SerializableSectionPairRowListValue.cpp
         ${CMAKE_CURRENT_SOURCE_DIR}/source/ls-std/io/section-pair/serialization/SerializableSectionPairRowSingleValue.cpp
+        ${CMAKE_CURRENT_SOURCE_DIR}/source/ls-std/io/section-pair/serialization/SerializableSectionPairSection.cpp
         ${CMAKE_CURRENT_SOURCE_DIR}/source/ls-std/io/xml/XmlAttribute.cpp
         ${CMAKE_CURRENT_SOURCE_DIR}/source/ls-std/io/xml/XmlDeclaration.cpp
         ${CMAKE_CURRENT_SOURCE_DIR}/source/ls-std/io/xml/XmlDocument.cpp
@@ -277,6 +279,7 @@ if (${LS_STD_BUILD_WITH_TESTS})
             ${CMAKE_CURRENT_SOURCE_DIR}/test/cases/io/section-pair/evaluator/SectionPairRowValueArgumentEvaluatorTest.cpp
             ${CMAKE_CURRENT_SOURCE_DIR}/test/cases/io/section-pair/validator/SectionPairIdentifierValidatorTest.cpp
             ${CMAKE_CURRENT_SOURCE_DIR}/test/cases/io/section-pair/validator/SectionPairRowValueValidatorTest.cpp
+            ${CMAKE_CURRENT_SOURCE_DIR}/test/cases/io/section-pair/validator/SectionPairSectionIdUnmarshalValidatorTest.cpp
             ${CMAKE_CURRENT_SOURCE_DIR}/test/cases/io/section-pair/model/SectionPairRowListValueTest.cpp
             ${CMAKE_CURRENT_SOURCE_DIR}/test/cases/io/section-pair/model/SectionPairRowSingleValueTest.cpp
             ${CMAKE_CURRENT_SOURCE_DIR}/test/cases/io/section-pair/model/SectionPairRowTest.cpp
@@ -284,6 +287,7 @@ if (${LS_STD_BUILD_WITH_TESTS})
             ${CMAKE_CURRENT_SOURCE_DIR}/test/cases/io/section-pair/serialization/SerializableSectionPairRowListValueTest.cpp
             ${CMAKE_CURRENT_SOURCE_DIR}/test/cases/io/section-pair/serialization/SerializableSectionPairRowSingleValueTest.cpp
             ${CMAKE_CURRENT_SOURCE_DIR}/test/cases/io/section-pair/serialization/SerializableSectionPairRowTest.cpp
+            ${CMAKE_CURRENT_SOURCE_DIR}/test/cases/io/section-pair/serialization/SerializableSectionPairSectionTest.cpp
             ${CMAKE_CURRENT_SOURCE_DIR}/test/cases/io/xml/XmlAttributeTest.cpp
             ${CMAKE_CURRENT_SOURCE_DIR}/test/cases/io/xml/XmlDeclarationTest.cpp
             ${CMAKE_CURRENT_SOURCE_DIR}/test/cases/io/xml/XmlDocumentTest.cpp

+ 2 - 1
include/ls-std/io/section-pair/model/SectionPairSection.hpp

@@ -3,7 +3,7 @@
 * Company:         Lynar Studios
 * E-Mail:          webmaster@lynarstudios.com
 * Created:         2023-02-13
-* Changed:         2023-02-14
+* Changed:         2023-02-15
 *
 * */
 
@@ -27,6 +27,7 @@ namespace ls::std::io
 
       void add(const section_pair_row_list_element &_row);
       [[nodiscard]] section_pair_row_list_element get(size_t _index);
+      [[nodiscard]] ls::std::io::section_pair_row_list getList();
       [[nodiscard]] size_t getRowAmount();
       [[nodiscard]] ls::std::io::section_pair_identifier getSectionId();
       void setSectionId(const ls::std::io::section_pair_identifier &_sectionId);

+ 54 - 0
include/ls-std/io/section-pair/serialization/SerializableSectionPairSection.hpp

@@ -0,0 +1,54 @@
+/*
+* Author:          Patrick-Christopher Mattulat
+* Company:         Lynar Studios
+* E-Mail:          webmaster@lynarstudios.com
+* Created:         2023-02-14
+* Changed:         2023-02-15
+*
+* */
+
+#ifndef LS_STD_SERIALIZABLE_SECTION_PAIR_SECTION_HPP
+#define LS_STD_SERIALIZABLE_SECTION_PAIR_SECTION_HPP
+
+#include <ls-std/core/Class.hpp>
+#include <ls-std/core/interface/ISerializable.hpp>
+#include <ls-std/io/section-pair/SectionPairRowEnumType.hpp>
+#include <ls-std/os/dynamic-goal.hpp>
+
+namespace ls::std::io
+{
+  class LS_STD_DYNAMIC_GOAL SerializableSectionPairSection : public ls::std::core::interface_type::ISerializable
+  {
+    public:
+
+      explicit SerializableSectionPairSection(const ::std::shared_ptr<ls::std::core::Class> &_value);
+      ~SerializableSectionPairSection() override;
+
+      [[nodiscard]] ::std::shared_ptr<ls::std::core::Class> getValue();
+      [[nodiscard]] ls::std::core::type::byte_field marshal() override;
+      void unmarshal(const ls::std::core::type::byte_field &_data) override;
+
+    private:
+
+      ::std::shared_ptr<ls::std::core::Class> value{};
+
+      static void _checkSectionHeader(const ls::std::core::type::byte_field &_sectionHeader);
+      [[nodiscard]] static ls::std::core::type::byte_field _collectSectionRow(const ls::std::core::type::byte_field &_currentRows, ls::std::io::SectionPairRowEnumType &_type);
+      [[nodiscard]] static core::type::byte_field _collectSectionListValueRow(const core::type::byte_field &_currentRows, SectionPairRowEnumType &_type);
+      [[nodiscard]] static core::type::byte_field _collectSectionSingleValueRow(const ls::std::core::type::byte_field &_firstRow, SectionPairRowEnumType &type);
+      [[nodiscard]] static size_t _getNthSubStringPosition(const ls::std::core::type::byte_field &_text, const ls::std::core::type::byte_field &_subText);
+      [[nodiscard]] static ls::std::core::type::byte_field _getSectionHeader(const ls::std::core::type::byte_field &_data);
+      [[nodiscard]] static ls::std::core::type::byte_field _getSectionId(const ls::std::core::type::byte_field &_sectionHeader);
+      [[nodiscard]] static bool _isListValueRow(const ::std::string &_currentRow);
+      [[nodiscard]] static bool _isStartingValueRow(const ::std::string &_currentRow);
+      [[nodiscard]] static bool _isSingleValueRow(const ::std::string &_currentRow);
+      [[nodiscard]] ls::std::core::type::byte_field _marshalRows();
+      [[nodiscard]] ls::std::core::type::byte_field _marshalSectionId();
+      void _setValue(const ::std::shared_ptr<ls::std::core::Class> &_value);
+      void _unmarshalRow(const ::std::string &_sectionRow, ls::std::io::SectionPairRowEnumType _type);
+      void _unmarshalRows(const ls::std::core::type::byte_field &_serializedRows);
+      [[nodiscard]] size_t _unmarshalSectionHeader(const ls::std::core::type::byte_field &_data);
+  };
+}
+
+#endif

+ 38 - 0
include/ls-std/io/section-pair/validator/SectionPairSectionIdUnmarshalValidator.hpp

@@ -0,0 +1,38 @@
+/*
+* Author:          Patrick-Christopher Mattulat
+* Company:         Lynar Studios
+* E-Mail:          webmaster@lynarstudios.com
+* Created:         2023-02-14
+* Changed:         2023-02-15
+*
+* */
+
+#ifndef LS_STD_SECTION_PAIR_SECTION_ID_UNMARSHAL_VALIDATOR_HPP
+#define LS_STD_SECTION_PAIR_SECTION_ID_UNMARSHAL_VALIDATOR_HPP
+
+#include <ls-std/core/Class.hpp>
+#include <ls-std/core/interface/IValidator.hpp>
+#include <ls-std/core/type/Types.hpp>
+#include <ls-std/os/dynamic-goal.hpp>
+
+namespace ls::std::io
+{
+  class LS_STD_DYNAMIC_GOAL SectionPairSectionIdUnmarshalValidator : public ls::std::core::Class, public ls::std::core::interface_type::IValidator
+  {
+    public:
+
+      explicit SectionPairSectionIdUnmarshalValidator(ls::std::core::type::byte_field _data);
+      ~SectionPairSectionIdUnmarshalValidator() override;
+
+      [[nodiscard]] bool isValid() override;
+
+    private:
+
+      ls::std::core::type::byte_field data{};
+
+      [[nodiscard]] static bool _isValidSection(const ls::std::core::type::byte_field &_bracketsNotation);
+      [[nodiscard]] static ls::std::core::type::byte_field _trimStartAndEnd(const ls::std::core::type::byte_field &_trimmedWord, const ls::std::core::type::byte_field &_beginningTrimWord, const ls::std::core::type::byte_field &_endingTrimWord);
+  };
+}
+
+#endif

+ 3 - 1
include/ls-std/ls-std-io.hpp

@@ -3,7 +3,7 @@
  * Company:         Lynar Studios
  * E-Mail:          webmaster@lynarstudios.com
  * Created:         2023-02-11
- * Changed:         2023-02-13
+ * Changed:         2023-02-15
  *
  * */
 
@@ -32,9 +32,11 @@
 #include <ls-std/io/section-pair/serialization/SerializableSectionPairRow.hpp>
 #include <ls-std/io/section-pair/serialization/SerializableSectionPairRowListValue.hpp>
 #include <ls-std/io/section-pair/serialization/SerializableSectionPairRowSingleValue.hpp>
+#include <ls-std/io/section-pair/serialization/SerializableSectionPairSection.hpp>
 #include <ls-std/io/section-pair/type/SectionPairSectionTypes.hpp>
 #include <ls-std/io/section-pair/validator/SectionPairIdentifierValidator.hpp>
 #include <ls-std/io/section-pair/validator/SectionPairRowValueValidator.hpp>
+#include <ls-std/io/section-pair/validator/SectionPairSectionIdUnmarshalValidator.hpp>
 
 #include <ls-std/io/xml/XmlAttribute.hpp>
 #include <ls-std/io/xml/XmlDeclaration.hpp>

+ 9 - 2
source/ls-std/io/section-pair/model/SectionPairRowSingleValue.cpp

@@ -3,12 +3,13 @@
 * Company:         Lynar Studios
 * E-Mail:          webmaster@lynarstudios.com
 * Created:         2023-02-10
-* Changed:         2023-02-13
+* Changed:         2023-02-15
 *
 * */
 
 #include <ls-std/core/ConditionalFunctionExecutor.hpp>
 #include <ls-std/core/evaluator/EmptyStringArgumentEvaluator.hpp>
+#include <ls-std/io/NewLine.hpp>
 #include <ls-std/io/section-pair/evaluator/SectionPairRowValueArgumentEvaluator.hpp>
 #include <ls-std/io/section-pair/model/SectionPairRowSingleValue.hpp>
 #include <ls-std/io/section-pair/serialization/SerializableSectionPairRowSingleValue.hpp>
@@ -55,6 +56,12 @@ void ls::std::io::SectionPairRowSingleValue::_createSerializable()
 void ls::std::io::SectionPairRowSingleValue::_set(const ls::std::io::section_pair_row_value &_value)
 {
   ls::std::core::EmptyStringArgumentEvaluator{_value}.evaluate();
-  ls::std::io::SectionPairRowValueArgumentEvaluator(_value, "section pair single value \"" + _value + "\" contains invalid characters!").evaluate();
+  ls::std::io::SectionPairRowValueArgumentEvaluator(_value, this->getClassName() + ": section pair single value \"" + _value + "\" contains invalid characters!").evaluate();
+  ::std::string newLine = ls::std::io::NewLine::get();
   this->value = _value;
+
+  if (this->value.find(newLine) != ::std::string::npos)
+  {
+    this->value.replace(this->value.find(newLine), newLine.size(), "");
+  }
 }

+ 7 - 2
source/ls-std/io/section-pair/model/SectionPairSection.cpp

@@ -3,7 +3,7 @@
 * Company:         Lynar Studios
 * E-Mail:          webmaster@lynarstudios.com
 * Created:         2023-02-13
-* Changed:         2023-02-14
+* Changed:         2023-02-15
 *
 * */
 
@@ -48,6 +48,11 @@ ls::std::io::section_pair_row_list_element ls::std::io::SectionPairSection::get(
   return element;
 }
 
+ls::std::io::section_pair_row_list ls::std::io::SectionPairSection::getList()
+{
+  return this->rows;
+}
+
 size_t ls::std::io::SectionPairSection::getRowAmount()
 {
   return this->rows.size();
@@ -90,6 +95,6 @@ void ls::std::io::SectionPairSection::_rowExistenceCheck(const ls::std::io::sect
 void ls::std::io::SectionPairSection::_setSectionId(const ls::std::io::section_pair_identifier &_sectionId)
 {
   ls::std::core::EmptyStringArgumentEvaluator{_sectionId}.evaluate();
-  ls::std::io::SectionPairIdentifierArgumentEvaluator(_sectionId, this->getClassName() + "argument \"_sectionId\" contains invalid characters!").evaluate();
+  ls::std::io::SectionPairIdentifierArgumentEvaluator(_sectionId, this->getClassName() + ": argument \"_sectionId\" contains invalid characters!").evaluate();
   this->sectionId = _sectionId;
 }

+ 3 - 2
source/ls-std/io/section-pair/serialization/SerializableSectionPairRowSingleValue.cpp

@@ -3,12 +3,13 @@
 * Company:         Lynar Studios
 * E-Mail:          webmaster@lynarstudios.com
 * Created:         2023-02-11
-* Changed:         2023-02-13
+* Changed:         2023-02-15
 *
 * */
 
 #include <ls-std/core/evaluator/EmptyStringArgumentEvaluator.hpp>
 #include <ls-std/core/evaluator/NullPointerArgumentEvaluator.hpp>
+#include <ls-std/io/NewLine.hpp>
 #include <ls-std/io/section-pair/model/SectionPairRowSingleValue.hpp>
 #include <ls-std/io/section-pair/serialization/SerializableSectionPairRowSingleValue.hpp>
 
@@ -26,7 +27,7 @@ ls::std::io::SerializableSectionPairRowSingleValue::~SerializableSectionPairRowS
 
 ls::std::core::type::byte_field ls::std::io::SerializableSectionPairRowSingleValue::marshal()
 {
-  return ::std::dynamic_pointer_cast<ls::std::io::SectionPairRowSingleValue>(this->value)->get();
+  return ::std::dynamic_pointer_cast<ls::std::io::SectionPairRowSingleValue>(this->value)->get() + ls::std::io::NewLine::get();
 }
 
 void ls::std::io::SerializableSectionPairRowSingleValue::unmarshal(const ls::std::core::type::byte_field &_data)

+ 215 - 0
source/ls-std/io/section-pair/serialization/SerializableSectionPairSection.cpp

@@ -0,0 +1,215 @@
+/*
+* Author:          Patrick-Christopher Mattulat
+* Company:         Lynar Studios
+* E-Mail:          webmaster@lynarstudios.com
+* Created:         2023-02-14
+* Changed:         2023-02-15
+*
+* */
+
+#include <ls-std/core/evaluator/NullPointerArgumentEvaluator.hpp>
+#include <ls-std/core/exception/IllegalArgumentException.hpp>
+#include <ls-std/io/NewLine.hpp>
+#include <ls-std/io/section-pair/model/SectionPairRow.hpp>
+#include <ls-std/io/section-pair/model/SectionPairSection.hpp>
+#include <ls-std/io/section-pair/serialization/SerializableSectionPairSection.hpp>
+#include <ls-std/io/section-pair/validator/SectionPairSectionIdUnmarshalValidator.hpp>
+
+ls::std::io::SerializableSectionPairSection::SerializableSectionPairSection(const ::std::shared_ptr<ls::std::core::Class> &_value)
+{
+  this->_setValue(_value);
+}
+
+ls::std::io::SerializableSectionPairSection::~SerializableSectionPairSection() = default;
+
+::std::shared_ptr<ls::std::core::Class> ls::std::io::SerializableSectionPairSection::getValue()
+{
+  return this->value;
+}
+
+ls::std::core::type::byte_field ls::std::io::SerializableSectionPairSection::marshal()
+{
+  ls::std::core::type::byte_field serializedSection{};
+
+  serializedSection += this->_marshalSectionId();
+  serializedSection += this->_marshalRows();
+
+  return serializedSection;
+}
+
+void ls::std::io::SerializableSectionPairSection::unmarshal(const ls::std::core::type::byte_field &_data)
+{
+  size_t sectionHeaderSize = this->_unmarshalSectionHeader(_data);
+  this->_unmarshalRows(_data.substr(sectionHeaderSize));
+}
+
+void ls::std::io::SerializableSectionPairSection::_checkSectionHeader(const ls::std::core::type::byte_field &_sectionHeader)
+{
+  if (!ls::std::io::SectionPairSectionIdUnmarshalValidator{_sectionHeader}.isValid())
+  {
+    throw ls::std::core::IllegalArgumentException{"serialized section header is not valid!"};
+  }
+}
+
+ls::std::core::type::byte_field ls::std::io::SerializableSectionPairSection::_collectSectionRow(const ls::std::core::type::byte_field &_currentRows, ls::std::io::SectionPairRowEnumType &_type)
+{
+  ::std::string row{};
+  ::std::string newLine = ls::std::io::NewLine::get();
+  ::std::string firstRow = _currentRows.substr(0, _currentRows.find(newLine) + newLine.size());
+
+  if (ls::std::io::SerializableSectionPairSection::_isSingleValueRow(firstRow))
+  {
+    row = ls::std::io::SerializableSectionPairSection::_collectSectionSingleValueRow(firstRow, _type);
+  }
+
+  if (ls::std::io::SerializableSectionPairSection::_isListValueRow(firstRow))
+  {
+    row = ls::std::io::SerializableSectionPairSection::_collectSectionListValueRow(_currentRows, _type);
+  }
+
+  return row;
+}
+
+ls::std::core::type::byte_field ls::std::io::SerializableSectionPairSection::_collectSectionListValueRow(const ls::std::core::type::byte_field &_currentRows, ls::std::io::SectionPairRowEnumType &_type)
+{
+  ls::std::core::type::byte_field currentRows = _currentRows;
+  ls::std::core::type::byte_field currentRow{}, row{};
+  ::std::string newLine = ls::std::io::NewLine::get();
+  _type = SectionPairRowEnumType::SECTION_PAIR_ROW_LIST_VALUE;
+  size_t iterations{};
+  bool isStillListRow{};
+
+  do
+  {
+    ++iterations;
+    currentRow = currentRows.substr(0, currentRows.find(newLine) + newLine.size());
+    currentRows = currentRows.substr(currentRow.size());
+    isStillListRow = !ls::std::io::SerializableSectionPairSection::_isStartingValueRow(currentRow) || iterations == 1;
+
+    if (isStillListRow)
+    {
+      row += currentRow;
+    }
+  } while (isStillListRow);
+
+  return row;
+}
+
+ls::std::core::type::byte_field ls::std::io::SerializableSectionPairSection::_collectSectionSingleValueRow(const ls::std::core::type::byte_field &_firstRow, ls::std::io::SectionPairRowEnumType &_type)
+{
+  _type = SectionPairRowEnumType::SECTION_PAIR_ROW_SINGLE_VALUE;
+  return _firstRow;
+}
+
+size_t ls::std::io::SerializableSectionPairSection::_getNthSubStringPosition(const ls::std::core::type::byte_field &_text, const ls::std::core::type::byte_field &_subText)
+{
+  size_t position = -1;
+  size_t amount{};
+
+  for (int index = 0; index < (_text.size() - _subText.size()); index++)
+  {
+    if (_text.substr(index, _subText.size()) == _subText)
+    {
+      ++amount;
+    }
+
+    if (amount == 2)
+    {
+      position = index;
+      break;
+    }
+  }
+
+  return position;
+}
+
+ls::std::core::type::byte_field ls::std::io::SerializableSectionPairSection::_getSectionHeader(const ls::std::core::type::byte_field &_data)
+{
+  ls::std::core::type::byte_field sectionHeader{};
+  ::std::string newLine = ls::std::io::NewLine::get();
+  size_t position = ls::std::io::SerializableSectionPairSection::_getNthSubStringPosition(_data, newLine);
+
+  if (position != -1)
+  {
+    sectionHeader = _data.substr(0, position + newLine.size());
+  }
+
+  return sectionHeader;
+}
+
+ls::std::core::type::byte_field ls::std::io::SerializableSectionPairSection::_getSectionId(const ls::std::core::type::byte_field &_sectionHeader)
+{
+  ls::std::core::type::byte_field sectionId = _sectionHeader.substr(_sectionHeader.find('[') + 1);
+  return sectionId.substr(0, sectionId.find(']'));
+}
+
+bool ls::std::io::SerializableSectionPairSection::_isListValueRow(const ::std::string &_currentRow)
+{
+  return _currentRow.find(':') != ::std::string::npos;
+}
+
+bool ls::std::io::SerializableSectionPairSection::_isStartingValueRow(const ::std::string &_currentRow)
+{
+  bool isSingleValue = ls::std::io::SerializableSectionPairSection::_isSingleValueRow(_currentRow);
+  bool isListValue = ls::std::io::SerializableSectionPairSection::_isListValueRow(_currentRow);
+
+  return isSingleValue || isListValue;
+}
+
+bool ls::std::io::SerializableSectionPairSection::_isSingleValueRow(const ::std::string &_currentRow)
+{
+  return _currentRow.find('=') != ::std::string::npos;
+}
+
+ls::std::core::type::byte_field ls::std::io::SerializableSectionPairSection::_marshalRows()
+{
+  ls::std::core::type::byte_field serializedSectionRows{};
+
+  for (const auto &_row : ::std::dynamic_pointer_cast<ls::std::io::SectionPairSection>(this->value)->getList())
+  {
+    serializedSectionRows += _row->marshal();
+  }
+
+  return serializedSectionRows;
+}
+
+ls::std::core::type::byte_field ls::std::io::SerializableSectionPairSection::_marshalSectionId()
+{
+  return ls::std::io::NewLine::get() + "[" + ::std::dynamic_pointer_cast<ls::std::io::SectionPairSection>(this->value)->getSectionId() + "]" + ls::std::io::NewLine::get();
+}
+
+void ls::std::io::SerializableSectionPairSection::_setValue(const ::std::shared_ptr<ls::std::core::Class> &_value)
+{
+  ls::std::core::NullPointerArgumentEvaluator{_value, "model reference for SerializableSectionPairSection is null!"}.evaluate();
+  this->value = _value;
+}
+
+void ls::std::io::SerializableSectionPairSection::_unmarshalRow(const ::std::string &_sectionRow, ls::std::io::SectionPairRowEnumType _type)
+{
+  ls::std::io::section_pair_row_list_element row = ::std::make_shared<ls::std::io::SectionPairRow>("tmp-dir", _type);
+  row->unmarshal(_sectionRow);
+  ::std::dynamic_pointer_cast<ls::std::io::SectionPairSection>(this->value)->add(row);
+}
+
+void ls::std::io::SerializableSectionPairSection::_unmarshalRows(const ls::std::core::type::byte_field &_serializedRows)
+{
+  ::std::string newLine = ls::std::io::NewLine::get();
+  ::std::string currentRows = _serializedRows;
+  ls::std::io::SectionPairRowEnumType type{};
+
+  while (!currentRows.empty())
+  {
+    ::std::string sectionRow = ls::std::io::SerializableSectionPairSection::_collectSectionRow(currentRows, type);
+    this->_unmarshalRow(sectionRow, type);
+    currentRows = currentRows.substr(sectionRow.size());
+  }
+}
+
+size_t ls::std::io::SerializableSectionPairSection::_unmarshalSectionHeader(const ls::std::core::type::byte_field &_data)
+{
+  ls::std::core::type::byte_field sectionHeader = ls::std::io::SerializableSectionPairSection::_getSectionHeader(_data);
+  ls::std::io::SerializableSectionPairSection::_checkSectionHeader(sectionHeader);
+  ::std::dynamic_pointer_cast<ls::std::io::SectionPairSection>(this->value)->setSectionId(ls::std::io::SerializableSectionPairSection::_getSectionId(sectionHeader));
+
+  return sectionHeader.size();
+}

+ 2 - 2
source/ls-std/io/section-pair/validator/SectionPairRowValueValidator.cpp

@@ -3,7 +3,7 @@
 * Company:         Lynar Studios
 * E-Mail:          webmaster@lynarstudios.com
 * Created:         2023-02-09
-* Changed:         2023-02-11
+* Changed:         2023-02-15
 *
 * */
 
@@ -16,6 +16,6 @@ ls::std::io::SectionPairRowValueValidator::~SectionPairRowValueValidator() = def
 
 bool ls::std::io::SectionPairRowValueValidator::isValid()
 {
-  size_t foundPosition = this->value.find_first_not_of("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789_#![]{}()/$ۤ%?<>+:;., *\"");
+  size_t foundPosition = this->value.find_first_not_of("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789_#![]{}()/$ۤ%?<>+:;., *\"\n\r");
   return foundPosition == ls::std::io::section_pair_row_value::npos;
 }

+ 53 - 0
source/ls-std/io/section-pair/validator/SectionPairSectionIdUnmarshalValidator.cpp

@@ -0,0 +1,53 @@
+/*
+* Author:          Patrick-Christopher Mattulat
+* Company:         Lynar Studios
+* E-Mail:          webmaster@lynarstudios.com
+* Created:         2023-02-14
+* Changed:         2023-02-15
+*
+* */
+
+#include <ls-std/io/NewLine.hpp>
+#include <ls-std/io/section-pair/validator/SectionPairIdentifierValidator.hpp>
+#include <ls-std/io/section-pair/validator/SectionPairSectionIdUnmarshalValidator.hpp>
+
+ls::std::io::SectionPairSectionIdUnmarshalValidator::SectionPairSectionIdUnmarshalValidator(ls::std::core::type::byte_field _data) : ls::std::core::Class("SectionPairSectionIdUnmarshalValidator"), data(::std::move(_data))
+{}
+
+ls::std::io::SectionPairSectionIdUnmarshalValidator::~SectionPairSectionIdUnmarshalValidator() = default;
+
+bool ls::std::io::SectionPairSectionIdUnmarshalValidator::isValid()
+{
+  ::std::string newLine = ls::std::io::NewLine::get();
+  bool isValidSectionId = this->data.rfind(newLine, 0) == 0;
+  isValidSectionId = isValidSectionId && this->data.rfind(newLine) == (this->data.size() - newLine.size());
+
+  if (isValidSectionId)
+  {
+    isValidSectionId = ls::std::io::SectionPairSectionIdUnmarshalValidator::_isValidSection(ls::std::io::SectionPairSectionIdUnmarshalValidator::_trimStartAndEnd(this->data, newLine, newLine));
+  }
+
+  return isValidSectionId;
+}
+
+bool ls::std::io::SectionPairSectionIdUnmarshalValidator::_isValidSection(const ls::std::core::type::byte_field &_bracketsNotation)
+{
+  bool isValidBracketsNotation = _bracketsNotation.rfind('[', 0) == 0;
+  isValidBracketsNotation = isValidBracketsNotation && _bracketsNotation.rfind(']') == (_bracketsNotation.size() - 1);
+
+  if (isValidBracketsNotation)
+  {
+    ls::std::core::type::byte_field identifier = ls::std::io::SectionPairSectionIdUnmarshalValidator::_trimStartAndEnd(_bracketsNotation, "[", "]");
+    isValidBracketsNotation = ls::std::io::SectionPairIdentifierValidator{identifier}.isValid();
+  }
+
+  return isValidBracketsNotation;
+}
+
+ls::std::core::type::byte_field ls::std::io::SectionPairSectionIdUnmarshalValidator::_trimStartAndEnd(const ls::std::core::type::byte_field &_trimmedWord, const ls::std::core::type::byte_field &_beginningTrimWord, const ls::std::core::type::byte_field &_endingTrimWord)
+{
+  ls::std::core::type::byte_field trimmedWord = _trimmedWord.substr(_beginningTrimWord.size());
+  trimmedWord.erase(trimmedWord.end() - (uint32_t) _endingTrimWord.size(), trimmedWord.end());
+
+  return trimmedWord;
+}

+ 5 - 2
test/cases/io/section-pair/model/SectionPairRowSingleValueTest.cpp

@@ -3,7 +3,7 @@
 * Company:         Lynar Studios
 * E-Mail:          webmaster@lynarstudios.com
 * Created:         2023-02-10
-* Changed:         2023-02-13
+* Changed:         2023-02-15
 *
 * */
 
@@ -84,7 +84,10 @@ namespace
   TEST_F(SectionPairRowSingleValueTest, marshal)
   {
     shared_ptr<SectionPairRowSingleValue> value = make_shared<SectionPairRowSingleValue>("empty");
-    ASSERT_STREQ("empty", value->marshal().c_str());
+    byte_field expected = "empty" + NewLine::get();
+    byte_field actual = value->marshal();
+
+    ASSERT_STREQ(expected.c_str(), actual.c_str());
   }
 
   TEST_F(SectionPairRowSingleValueTest, set_empty_value)

+ 4 - 2
test/cases/io/section-pair/model/SectionPairRowTest.cpp

@@ -3,7 +3,7 @@
 * Company:         Lynar Studios
 * E-Mail:          webmaster@lynarstudios.com
 * Created:         2023-02-08
-* Changed:         2023-02-13
+* Changed:         2023-02-15
 *
 * */
 
@@ -101,8 +101,10 @@ namespace
     shared_ptr<SectionPairRow> row = make_shared<SectionPairRow>("favourite-color", SectionPairRowEnumType::SECTION_PAIR_ROW_SINGLE_VALUE);
     shared_ptr<SectionPairRowSingleValue> singleValue = dynamic_pointer_cast<SectionPairRowSingleValue>(row->getValue());
     singleValue->set("blue");
+    byte_field expected = "favourite-color=blue" + NewLine::get();
+    byte_field actual = row->marshal();
 
-    ASSERT_STREQ("favourite-color=blue", row->marshal().c_str());
+    ASSERT_STREQ(expected.c_str(), actual.c_str());
   }
 
   TEST_F(SectionPairRowTest, marshal_list_value)

+ 7 - 1
test/cases/io/section-pair/model/SectionPairSectionTest.cpp

@@ -3,7 +3,7 @@
 * Company:         Lynar Studios
 * E-Mail:          webmaster@lynarstudios.com
 * Created:         2023-02-13
-* Changed:         2023-02-14
+* Changed:         2023-02-15
 *
 * */
 
@@ -127,6 +127,12 @@ namespace
     ASSERT_EQ(0, section->getRowAmount());
   }
 
+  TEST_F(SectionPairSectionTest, getList)
+  {
+    shared_ptr<SectionPairSection> section = make_shared<SectionPairSection>("general");
+    ASSERT_TRUE(section->getList().empty());
+  }
+
   TEST_F(SectionPairSectionTest, getSectionId)
   {
     shared_ptr<SectionPairSection> section = make_shared<SectionPairSection>("general");

+ 6 - 2
test/cases/io/section-pair/serialization/SerializableSectionPairRowSingleValueTest.cpp

@@ -3,7 +3,7 @@
 * Company:         Lynar Studios
 * E-Mail:          webmaster@lynarstudios.com
 * Created:         2023-02-11
-* Changed:         2023-02-11
+* Changed:         2023-02-15
 *
 * */
 
@@ -13,6 +13,7 @@
 #include <memory>
 
 using namespace ls::std::core;
+using namespace ls::std::core::type;
 using namespace ls::std::io;
 using namespace ::std;
 
@@ -59,7 +60,10 @@ namespace
   TEST_F(SerializableSectionPairRowSingleValueTest, marshal)
   {
     SerializableSectionPairRowSingleValue serializable(make_shared<SectionPairRowSingleValue>("empty"));
-    ASSERT_STREQ("empty", serializable.marshal().c_str());
+    byte_field expected = "empty" + NewLine::get();
+    byte_field actual = serializable.marshal();
+
+    ASSERT_STREQ(expected.c_str(), actual.c_str());
   }
 
   TEST_F(SerializableSectionPairRowSingleValueTest, unmarshal)

+ 5 - 2
test/cases/io/section-pair/serialization/SerializableSectionPairRowTest.cpp

@@ -3,7 +3,7 @@
 * Company:         Lynar Studios
 * E-Mail:          webmaster@lynarstudios.com
 * Created:         2023-02-12
-* Changed:         2023-02-13
+* Changed:         2023-02-15
 *
 * */
 
@@ -13,6 +13,7 @@
 #include <memory>
 
 using namespace ls::std::core;
+using namespace ls::std::core::type;
 using namespace ls::std::io;
 using namespace ::std;
 
@@ -60,8 +61,10 @@ namespace
     shared_ptr<SectionPairRowSingleValue> singleValue = dynamic_pointer_cast<SectionPairRowSingleValue>(row->getValue());
     singleValue->set("blue");
     SerializableSectionPairRow serializable{row};
+    byte_field expected = "favourite-color=blue" + NewLine::get();
+    byte_field actual = serializable.marshal();
 
-    ASSERT_STREQ("favourite-color=blue", serializable.marshal().c_str());
+    ASSERT_STREQ(expected.c_str(), actual.c_str());
   }
 
   TEST_F(SerializableSectionPairRowTest, marshal_list_value)

+ 117 - 0
test/cases/io/section-pair/serialization/SerializableSectionPairSectionTest.cpp

@@ -0,0 +1,117 @@
+/*
+* Author:          Patrick-Christopher Mattulat
+* Company:         Lynar Studios
+* E-Mail:          webmaster@lynarstudios.com
+* Created:         2023-02-14
+* Changed:         2023-02-15
+*
+* */
+
+#include <gtest/gtest.h>
+#include <ls-std/ls-std-core.hpp>
+#include <ls-std/ls-std-io.hpp>
+#include <memory>
+
+using namespace ls::std::core;
+using namespace ls::std::core::type;
+using namespace ls::std::io;
+using namespace ::std;
+
+namespace
+{
+  class SerializableSectionPairSectionTest : public ::testing::Test
+  {
+    protected:
+
+      SerializableSectionPairSectionTest() = default;
+      ~SerializableSectionPairSectionTest() override = default;
+
+      void SetUp() override
+      {}
+
+      void TearDown() override
+      {}
+
+      static shared_ptr<SectionPairSection> createSection()
+      {
+        shared_ptr<SectionPairSection> section = make_shared<SectionPairSection>("general");
+
+        shared_ptr<SectionPairRow> name = make_shared<SectionPairRow>("name", SectionPairRowEnumType::SECTION_PAIR_ROW_SINGLE_VALUE);
+        dynamic_pointer_cast<SectionPairRowSingleValue>(name->getValue())->set("Tom");
+        section->add(name);
+
+        shared_ptr<SectionPairRow> jobs = make_shared<SectionPairRow>("jobs", SectionPairRowEnumType::SECTION_PAIR_ROW_LIST_VALUE);
+        shared_ptr<SectionPairRowListValue> jobList = dynamic_pointer_cast<SectionPairRowListValue>(jobs->getValue());
+        jobList->add("Farmer");
+        jobList->add("Bounty Hunter");
+        section->add(jobs);
+
+        shared_ptr<SectionPairRow> age = make_shared<SectionPairRow>("age", SectionPairRowEnumType::SECTION_PAIR_ROW_SINGLE_VALUE);
+        dynamic_pointer_cast<SectionPairRowSingleValue>(age->getValue())->set("33");
+        section->add(age);
+
+        return section;
+      }
+
+      static byte_field createSerializedSection()
+      {
+        byte_field serializedSection = NewLine::get() + "[general]" + NewLine::get();
+        byte_field serializedNameRow = "name=Tom" + NewLine::get();
+        byte_field serializedJobsRow = "jobs:" + NewLine::get() + "  Farmer" + NewLine::get() + "  Bounty Hunter" + NewLine::get();
+        byte_field serializedAgeRow = "age=33" + NewLine::get();
+
+        return serializedSection + serializedNameRow + serializedJobsRow + serializedAgeRow;
+      }
+  };
+
+  TEST_F(SerializableSectionPairSectionTest, constructor_no_reference)
+  {
+    EXPECT_THROW(
+        {
+          try
+          {
+            SerializableSectionPairSection serializable(nullptr);
+          }
+          catch (const IllegalArgumentException &_exception)
+          {
+            throw;
+          }
+        },
+        IllegalArgumentException);
+  }
+
+  TEST_F(SerializableSectionPairSectionTest, getValue)
+  {
+    SerializableSectionPairSection serializable{make_shared<SectionPairSection>("general")};
+    ASSERT_TRUE(serializable.getValue() != nullptr);
+  }
+
+  TEST_F(SerializableSectionPairSectionTest, marshal)
+  {
+    shared_ptr<SectionPairSection> section = SerializableSectionPairSectionTest::createSection();
+    SerializableSectionPairSection serializable{section};
+    byte_field expected = SerializableSectionPairSectionTest::createSerializedSection();
+    byte_field actual = serializable.marshal();
+
+    ASSERT_STREQ(expected.c_str(), actual.c_str());
+  }
+
+  TEST_F(SerializableSectionPairSectionTest, unmarshal)
+  {
+    shared_ptr<SectionPairSection> section = make_shared<SectionPairSection>("tmp-id");
+    SerializableSectionPairSection serializable{section};
+    serializable.unmarshal(SerializableSectionPairSectionTest::createSerializedSection());
+
+    ASSERT_STREQ("general", section->getSectionId().c_str());
+    ASSERT_EQ(3, section->getRowAmount());
+    ASSERT_STREQ("name", section->get(0)->getKey().c_str());
+    ASSERT_STREQ("Tom", dynamic_pointer_cast<SectionPairRowSingleValue>(section->get(0)->getValue())->get().c_str());
+    ASSERT_STREQ("jobs", section->get(1)->getKey().c_str());
+    shared_ptr<SectionPairRowListValue> listRow = dynamic_pointer_cast<SectionPairRowListValue>(section->get(1)->getValue());
+    ASSERT_EQ(2, listRow->getSize());
+    ASSERT_STREQ("Farmer", listRow->get(0).c_str());
+    ASSERT_STREQ("Bounty Hunter", listRow->get(1).c_str());
+    ASSERT_STREQ("age", section->get(2)->getKey().c_str());
+    ASSERT_STREQ("33", dynamic_pointer_cast<SectionPairRowSingleValue>(section->get(2)->getValue())->get().c_str());
+  }
+}

+ 58 - 0
test/cases/io/section-pair/validator/SectionPairSectionIdUnmarshalValidatorTest.cpp

@@ -0,0 +1,58 @@
+/*
+* Author:          Patrick-Christopher Mattulat
+* Company:         Lynar Studios
+* E-Mail:          webmaster@lynarstudios.com
+* Created:         2023-02-14
+* Changed:         2023-02-15
+*
+* */
+
+#include <gtest/gtest.h>
+#include <ls-std/ls-std-core.hpp>
+#include <ls-std/ls-std-io.hpp>
+
+using namespace ls::std::core;
+using namespace ls::std::core::type;
+using namespace ls::std::io;
+
+namespace
+{
+  class SectionPairSectionIdUnmarshalValidatorTest : public ::testing::Test
+  {
+    protected:
+
+      SectionPairSectionIdUnmarshalValidatorTest() = default;
+      ~SectionPairSectionIdUnmarshalValidatorTest() override = default;
+
+      void SetUp() override
+      {}
+
+      void TearDown() override
+      {}
+  };
+
+  TEST_F(SectionPairSectionIdUnmarshalValidatorTest, getClassName)
+  {
+    ASSERT_STREQ("SectionPairSectionIdUnmarshalValidator", SectionPairSectionIdUnmarshalValidator{"\n[general]\n"}.getClassName().c_str());
+  }
+
+  TEST_F(SectionPairSectionIdUnmarshalValidatorTest, isValid)
+  {
+    byte_field generalSection = NewLine::get() + "[general]" + NewLine::get();
+    byte_field localServerSection = NewLine::get() + "[local-server]" + NewLine::get();
+
+    ASSERT_TRUE(SectionPairSectionIdUnmarshalValidator{generalSection}.isValid());
+    ASSERT_TRUE(SectionPairSectionIdUnmarshalValidator{localServerSection}.isValid());
+  }
+
+  TEST_F(SectionPairSectionIdUnmarshalValidatorTest, isValid_not_valid)
+  {
+    byte_field generalSection = "[general]" + NewLine::get();
+    byte_field localServerSection = NewLine::get() + "[local-server]";
+    byte_field position = NewLine::get() + "position" + NewLine::get();
+
+    ASSERT_FALSE(SectionPairSectionIdUnmarshalValidator{generalSection}.isValid());
+    ASSERT_FALSE(SectionPairSectionIdUnmarshalValidator{localServerSection}.isValid());
+    ASSERT_FALSE(SectionPairSectionIdUnmarshalValidator{position}.isValid());
+  }
+}