Browse Source

Merge branch 'dev' of repo-admin/ls-standard-library into master

repo-admin 3 years ago
parent
commit
8a28c2fafa

+ 57 - 20
CMakeLists.txt

@@ -3,10 +3,16 @@
 ##########################################################
 
 cmake_minimum_required(VERSION 3.17)
-set(PROJECT_NAME ls_std)
-set(RELEASE_VERSION 2020.3.1)
-set(BUILD_WITH_TESTS FALSE)
-project(${PROJECT_NAME})
+project(ls_std VERSION 2021.1.0)
+
+##########################################################
+# Options
+##########################################################
+
+option(LS_STD_BUILD_WITH_TESTS "Build project with tests..." OFF)
+option(LS_STD_BUILD_STATIC "Build ls_std static library..." ON)
+option(LS_STD_BUILD_SHARED "Build ls_std shared library..." OFF)
+option(LS_STD_BUILD_MODULE "Build ls_std module library..." OFF)
 
 ##########################################################
 # Compiler Settings
@@ -23,7 +29,7 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
 
 message("${PROJECT_NAME}: Adding include directories...")
 
-if (${BUILD_WITH_TESTS})
+if (${LS_STD_BUILD_WITH_TESTS})
     include_directories(${CMAKE_CURRENT_SOURCE_DIR}/test)
     include_directories(${CMAKE_CURRENT_LIST_DIR}/test/lib/googletest-1.8.1/googletest/include)
 endif ()
@@ -36,7 +42,7 @@ include_directories(${CMAKE_CURRENT_LIST_DIR}/include)
 
 message("${PROJECT_NAME}: Adding additional cmake dependencies...")
 
-if (${BUILD_WITH_TESTS})
+if (${LS_STD_BUILD_WITH_TESTS})
     add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/test/lib/googletest-1.8.1)
 endif ()
 
@@ -85,9 +91,14 @@ set(SOURCE_FILES
         ${CMAKE_CURRENT_SOURCE_DIR}/source/ls_std/io/xml/XMLParser.cpp
         ${CMAKE_CURRENT_SOURCE_DIR}/source/ls_std/event/Event.cpp
         ${CMAKE_CURRENT_SOURCE_DIR}/source/ls_std/event/EventHandler.cpp
-        ${CMAKE_CURRENT_SOURCE_DIR}/source/ls_std/event/EventManager.cpp)
-
-if (${BUILD_WITH_TESTS})
+        ${CMAKE_CURRENT_SOURCE_DIR}/source/ls_std/event/EventManager.cpp
+        ${CMAKE_CURRENT_SOURCE_DIR}/source/ls_std/serialization/json/event/SerializableJSONEvent.cpp
+        ${CMAKE_CURRENT_SOURCE_DIR}/source/ls_std/io/kv/KVPair.cpp
+        ${CMAKE_CURRENT_SOURCE_DIR}/source/ls_std/io/kv/KVDocument.cpp
+        ${CMAKE_CURRENT_SOURCE_DIR}/source/ls_std/io/kv/KVParser.cpp
+        ${CMAKE_CURRENT_SOURCE_DIR}/source/ls_std/io/kv/KVReader.cpp)
+
+if (${LS_STD_BUILD_WITH_TESTS})
     set(TEST_FILES
             ${CMAKE_CURRENT_SOURCE_DIR}/test/cases/boxing/IntegerTest.cpp
             ${CMAKE_CURRENT_SOURCE_DIR}/test/cases/boxing/BooleanTest.cpp
@@ -139,14 +150,19 @@ if (${BUILD_WITH_TESTS})
             ${CMAKE_CURRENT_SOURCE_DIR}/test/classes/event/GossipNewsEvent.cpp
             ${CMAKE_CURRENT_SOURCE_DIR}/test/classes/event/GossipNewsAgency.cpp
             ${CMAKE_CURRENT_SOURCE_DIR}/test/cases/event/EventHandlerTest.cpp
-            ${CMAKE_CURRENT_SOURCE_DIR}/test/cases/event/EventManagerTest.cpp)
+            ${CMAKE_CURRENT_SOURCE_DIR}/test/cases/event/EventManagerTest.cpp
+            ${CMAKE_CURRENT_SOURCE_DIR}/test/cases/serialization/json/event/SerializableJSONEventTest.cpp
+            ${CMAKE_CURRENT_SOURCE_DIR}/test/cases/io/kv/KVPairTest.cpp
+            ${CMAKE_CURRENT_SOURCE_DIR}/test/cases/io/kv/KVDocumentTest.cpp
+            ${CMAKE_CURRENT_SOURCE_DIR}/test/cases/io/kv/KVParserTest.cpp
+            ${CMAKE_CURRENT_SOURCE_DIR}/test/cases/io/kv/KVReaderTest.cpp)
 endif ()
 
 ##########################################################
 # Build Tests
 ##########################################################
 
-if (${BUILD_WITH_TESTS})
+if (${LS_STD_BUILD_WITH_TESTS})
     message("${PROJECT_NAME}: Building tests...")
     add_executable(${PROJECT_NAME}_test ${TEST_FILES})
 endif ()
@@ -155,22 +171,43 @@ endif ()
 # Build Library
 ##########################################################
 
-message("${PROJECT_NAME}: Building library version ${RELEASE_VERSION}...")
+message("${PROJECT_NAME}: Building library version ${PROJECT_VERSION}...")
 
-add_library("${PROJECT_NAME}_${RELEASE_VERSION}_static" STATIC ${SOURCE_FILES})
-set_target_properties("${PROJECT_NAME}_${RELEASE_VERSION}_static" PROPERTIES DEBUG_POSTFIX "_d")
+if (${LS_STD_BUILD_STATIC})
+    add_library("${PROJECT_NAME}_${PROJECT_VERSION}_static" STATIC ${SOURCE_FILES})
+    set_target_properties("${PROJECT_NAME}_${PROJECT_VERSION}_static" PROPERTIES DEBUG_POSTFIX "_d")
+endif ()
 
-add_library("${PROJECT_NAME}_${RELEASE_VERSION}_shared" SHARED ${SOURCE_FILES})
-set_target_properties("${PROJECT_NAME}_${RELEASE_VERSION}_shared" PROPERTIES DEBUG_POSTFIX "_d")
+if (${LS_STD_BUILD_SHARED})
+    add_library("${PROJECT_NAME}_${PROJECT_VERSION}_shared" SHARED ${SOURCE_FILES})
+    set_target_properties("${PROJECT_NAME}_${PROJECT_VERSION}_shared" PROPERTIES DEBUG_POSTFIX "_d")
+endif ()
 
-add_library("${PROJECT_NAME}_${RELEASE_VERSION}_module" MODULE ${SOURCE_FILES})
-set_target_properties("${PROJECT_NAME}_${RELEASE_VERSION}_module" PROPERTIES DEBUG_POSTFIX "_d")
+if (${LS_STD_BUILD_MODULE})
+    add_library("${PROJECT_NAME}_${PROJECT_VERSION}_module" MODULE ${SOURCE_FILES})
+    set_target_properties("${PROJECT_NAME}_${PROJECT_VERSION}_module" PROPERTIES DEBUG_POSTFIX "_d")
+endif ()
 
 ##########################################################
 # Linking
 ##########################################################
 
-if (${BUILD_WITH_TESTS})
+if (${LS_STD_BUILD_WITH_TESTS})
     message("${PROJECT_NAME}: Linking libraries for test application...")
-    target_link_libraries(${PROJECT_NAME}_test gtest gtest_main "${PROJECT_NAME}_${RELEASE_VERSION}_static")
+
+    if (WIN32)
+        SET(CMAKE_FIND_LIBRARY_PREFIXES "")
+        SET(CMAKE_FIND_LIBRARY_SUFFIXES ".lib" ".dll")
+
+        find_library(WSOCK32_LIBRARY wsock32)
+        find_library(WS2_32_LIBRARY ws2_32)
+        message("${PROJECT_NAME}: library search - ${WSOCK32_LIBRARY}...")
+        message("${PROJECT_NAME}: library search - ${WS2_32_LIBRARY}...")
+
+        target_link_libraries(${PROJECT_NAME}_test gtest gtest_main "${WSOCK32_LIBRARY}" "${WS2_32_LIBRARY}" "${PROJECT_NAME}_${PROJECT_VERSION}_static")
+    endif ()
+
+    if (UNIX)
+        target_link_libraries(${PROJECT_NAME}_test gtest gtest_main "${PROJECT_NAME}_${PROJECT_VERSION}_static")
+    endif ()
 endif ()

+ 48 - 8
README.md

@@ -1,23 +1,63 @@
-# Lynar Studios - Standard Library 2020.3.1 #
+# Lynar Studios - Standard Library 2021.1.0 #
 
 This is a cross-platform standard library written in C++ which provides basic functionality and implementation, like:
  
-- Boxing Classes
-- File Operations
-- Minimal Reflection
-- Date Class
-- State Machine Class
+- Boxing  
+  - Double
+  - Float
+  - String
+  - Integer 
+  - Long
+  - Boolean  
+- File
+  - IReader (interface)
+  - IWriter (interface)  
+  - IStorable (interface)  
+  - Model
+  - Reader
+  - Writer
+- Minimal Reflection  
+  - Class (base)
+- Time
+  - Date
+- Logic
+  - State Machine (Model, State, Connection)
+  - IListener (Interface)
+  - Narrator  
 - Serialization
+  - ISerializable (Interface)
+  - JSON Boxing (Double, Float, String, Integer, Long, Boolean)
+  - JSON State Machine (Model, State, Connection)  
+  - JSON Event
 - JSON
+  - Marshalling
+  - Unmarshalling
 - XML
-- Logger Class
+  - Model (Document, Declaration, Node, Attribute)
+  - Reader
+  - Writer
+  - Parser  
+- Logger
+  - Model
+  - Log Level
 - Event Handling
+  - IEventSubscriber (Interface)
+  - Model
+  - Event Handler
+  - Event Manager
+- Exception
+- KV
+  - Document
+  - KV Pair
+  - Parser
+  - Reader  
 
 It has been tested on __Windows__, __Linux__ and __MacOS__ systems.
 
 ### Changelog ###
 
-- improved CMakeLists.txt file by adding functionality to build project with or without tests
+- Made Event class serializable
+- Added Key Value File Functionality
 
 ### Documentation ###
 

+ 0 - 0
test/resources/event_handling_uml.png → doc/event_handling_uml.png


+ 13 - 0
doc/kv_file_definition.md

@@ -0,0 +1,13 @@
+# Key Value File - Definition #
+
+This document defines the standard of a key value file (__.kv__).
+
+1. A Key Value File needs to have the file extension "__.kv__".
+2. A Key Value Pair, which is defined in such a file, has the following format:
+   ```
+   port=8080;
+   ```
+   The key is defined at the beginning of a line followed by an "__=__" sign for assignment.  
+   The value (which is the assigment) comes write after the "__=__" sign. Please note that no quotes are required.
+   To mark the end of a pair definition a semicolon "__;__" is required.
+3. Every line in a __.kv__ file, which does not contain a "__=__" and a "__;__" sign, will be considered as a comment. Also, adding any text to a line after a semicolon has been set - to mark the end of a pair definition - will be ignored.

+ 0 - 0
test/resources/state_machine_test.png → doc/state_machine_test.png


+ 17 - 3
include/ls_std/event/Event.hpp

@@ -3,7 +3,7 @@
  * Company:         Lynar Studios
  * E-Mail:          webmaster@lynarstudios.com
  * Created:         2020-11-26
- * Changed:         2020-11-29
+ * Changed:         2020-12-23
  *
  * */
 
@@ -11,25 +11,39 @@
 #define LS_STD_EVENT_HPP
 
 #include <ls_std/base/Class.hpp>
+#include <ls_std/serialization/ISerializable.hpp>
+#include <memory>
 #include "EventTypes.hpp"
 
 namespace ls_std {
-  class Event : public ls_std::Class {
+  class Event : public ls_std::Class, public ISerializable {
     public:
 
-      explicit Event(ls_std::event_id  _id);
+      explicit Event(const ls_std::event_id&  _id);
       ~Event() override = default;
 
+      // implementation
+
+      ls_std::byte_field marshal() override;
+      void unmarshal(const ls_std::byte_field& _data) override;
+
+      // additional functionality
+
       void addParameter(const ls_std::event_parameter& _eventParameter);
+      void clearParameterList();
       ls_std::event_id getId();
       ls_std::event_parameter_list getParameterList();
       void removeParameter(const ls_std::event_parameter_id& _id);
+      void setId(const ls_std::event_id&  _id);
+      void setSerializable(std::shared_ptr<ISerializable> _serializable);
 
     private:
 
       ls_std::event_id id {};
       ls_std::event_parameter_list parameterList {};
+      std::shared_ptr<ISerializable> serializable {};
 
+      void _assignId(const ls_std::event_id& _id);
       bool _hasParameter(const ls_std::event_id&  _id);
   };
 }

+ 39 - 0
include/ls_std/io/kv/KVDocument.hpp

@@ -0,0 +1,39 @@
+/*
+ * Author:          Patrick-Christopher Mattulat
+ * Company:         Lynar Studios
+ * E-Mail:          webmaster@lynarstudios.com
+ * Created:         2020-12-25
+ * Changed:         2020-12-25
+ *
+ * */
+
+#ifndef LS_STD_KV_DOCUMENT_HPP
+#define LS_STD_KV_DOCUMENT_HPP
+
+#include <ls_std/base/Class.hpp>
+#include "KVPair.hpp"
+#include "KVTypes.hpp"
+#include <map>
+
+namespace ls_std {
+  class KVDocument : public ls_std::Class {
+    public:
+
+      KVDocument();
+      ~KVDocument() override = default;
+
+      bool addPair(ls_std::KVPair _pair);
+      void clear();
+      std::map<ls_std::kv_key, ls_std::KVPair> getPairs();
+      bool hasPair(const ls_std::kv_key& _key);
+      void removePair(const ls_std::kv_key& _key);
+
+    private:
+
+      std::map<ls_std::kv_key, ls_std::KVPair> pairs {};
+
+      bool _hasPair(const ls_std::kv_key& _key);
+  };
+}
+
+#endif

+ 36 - 0
include/ls_std/io/kv/KVPair.hpp

@@ -0,0 +1,36 @@
+/*
+ * Author:          Patrick-Christopher Mattulat
+ * Company:         Lynar Studios
+ * E-Mail:          webmaster@lynarstudios.com
+ * Created:         2020-12-25
+ * Changed:         2020-12-25
+ *
+ * */
+
+#ifndef LS_STD_KV_PAIR_HPP
+#define LS_STD_KV_PAIR_HPP
+
+#include <ls_std/base/Class.hpp>
+#include "KVTypes.hpp"
+
+namespace ls_std {
+  class KVPair : public ls_std::Class {
+    public:
+
+      explicit KVPair(const ls_std::kv_key& _key, ls_std::kv_value _value);
+      ~KVPair() override = default;
+
+      ls_std::kv_key getKey();
+      ls_std::kv_value getValue();
+      void setValue(const ls_std::kv_value& _value);
+
+    private:
+
+      ls_std::kv_key key {};
+      ls_std::kv_value value {};
+
+      void _assignKey(const ls_std::kv_key& _key);
+  };
+}
+
+#endif

+ 23 - 0
include/ls_std/io/kv/KVParseData.hpp

@@ -0,0 +1,23 @@
+/*
+ * Author:          Patrick-Christopher Mattulat
+ * Company:         Lynar Studios
+ * E-Mail:          webmaster@lynarstudios.com
+ * Created:         2020-12-25
+ * Changed:         2020-12-25
+ *
+ * */
+
+#ifndef LS_STD_KV_PARSE_DATA_HPP
+#define LS_STD_KV_PARSE_DATA_HPP
+
+#include <string>
+#include <ls_std/boxing/String.hpp>
+
+namespace ls_std {
+  struct KVParseData {
+    std::string::size_type index {};
+    ls_std::String line {};
+  };
+}
+
+#endif

+ 44 - 0
include/ls_std/io/kv/KVParser.hpp

@@ -0,0 +1,44 @@
+/*
+ * Author:          Patrick-Christopher Mattulat
+ * Company:         Lynar Studios
+ * E-Mail:          webmaster@lynarstudios.com
+ * Created:         2020-12-25
+ * Changed:         2020-12-25
+ *
+ * */
+
+#ifndef LS_STD_KV_PARSER_HPP
+#define LS_STD_KV_PARSER_HPP
+
+#include <ls_std/base/Class.hpp>
+#include <ls_std/base/Types.hpp>
+#include "KVDocument.hpp"
+#include "KVParseData.hpp"
+#include <memory>
+
+namespace ls_std {
+  class KVParser : public ls_std::Class {
+    public:
+
+      explicit KVParser(const std::shared_ptr<ls_std::KVDocument>& _document);
+      ~KVParser() override = default;
+
+      std::shared_ptr<ls_std::KVDocument> getDocument();
+      void parse(const ls_std::byte_field& _data);
+      void setDocument(const std::shared_ptr<ls_std::KVDocument>& _document);
+
+    private:
+
+      std::shared_ptr<ls_std::KVDocument> document {};
+
+      void _assignDocument(const std::shared_ptr<ls_std::KVDocument>& _document);
+      static bool _lineHasPair(ls_std::KVParseData _parseData);
+      void _parse(const ls_std::byte_field& _data);
+      void _parsePair(ls_std::KVParseData _parseData);
+      static ls_std::KVParseData _readLine(const ls_std::byte_field& _data, std::string::size_type _index);
+      static void _readLineWithUnixLineBreak(ls_std::KVParseData& _parseData);
+      static void _readLineWithWindowsLineBreak(ls_std::KVParseData& _parseData);
+  };
+}
+
+#endif

+ 46 - 0
include/ls_std/io/kv/KVReader.hpp

@@ -0,0 +1,46 @@
+/*
+ * Author:          Patrick-Christopher Mattulat
+ * Company:         Lynar Studios
+ * E-Mail:          webmaster@lynarstudios.com
+ * Created:         2020-12-25
+ * Changed:         2020-12-25
+ *
+ * */
+
+#ifndef LS_STD_KV_READER_HPP
+#define LS_STD_KV_READER_HPP
+
+#include <ls_std/base/Class.hpp>
+#include <ls_std/io/IReader.hpp>
+#include <ls_std/io/kv/KVDocument.hpp>
+#include <ls_std/io/File.hpp>
+#include <memory>
+
+namespace ls_std {
+  class KVReader : public ls_std::Class, public ls_std::IReader {
+    public:
+
+      explicit KVReader(const std::shared_ptr<ls_std::KVDocument>& _document, const std::string& _absolutePath);
+      ~KVReader() override = default;
+
+      // implementation
+
+      ls_std::byte_field read() override;
+
+      // additional functionality
+
+      std::shared_ptr<ls_std::KVDocument> getDocument();
+      void setDocument(const std::shared_ptr<ls_std::KVDocument>& _document);
+      void setFile(const ls_std::File& _kvFile);
+
+    private:
+
+      std::shared_ptr<ls_std::KVDocument> document {};
+      ls_std::File kvFile;
+
+      void _assignDocument(const std::shared_ptr<ls_std::KVDocument>& _document);
+      void _assignFile(ls_std::File _kvFile);
+  };
+}
+
+#endif

+ 20 - 0
include/ls_std/io/kv/KVTypes.hpp

@@ -0,0 +1,20 @@
+/*
+ * Author:          Patrick-Christopher Mattulat
+ * Company:         Lynar Studios
+ * E-Mail:          webmaster@lynarstudios.com
+ * Created:         2020-12-25
+ * Changed:         2020-12-25
+ *
+ * */
+
+#ifndef LS_STD_KV_TYPES_HPP
+#define LS_STD_KV_TYPES_HPP
+
+#include <string>
+
+namespace ls_std {
+  using kv_key    = std::string;
+  using kv_value  = std::string;
+}
+
+#endif

+ 13 - 1
include/ls_std/ls_std.hpp

@@ -3,13 +3,17 @@
  * Company:         Lynar Studios
  * E-Mail:          webmaster@lynarstudios.com
  * Created:         2020-10-29
- * Changed:         2020-11-27
+ * Changed:         2020-12-25
  *
  * */
 
 #ifndef LS_STD_LS_STD_HPP
 #define LS_STD_LS_STD_HPP
 
+#if _WIN32
+#include <winsock2.h>
+#endif
+
 #include "base/Class.hpp"
 #include "base/Types.hpp"
 #include "base/Version.hpp"
@@ -49,6 +53,10 @@
 #include "io/IWriter.hpp"
 #include "io/NewLine.hpp"
 #include "io/StorableFile.hpp"
+#include "io/kv/KVPair.hpp"
+#include "io/kv/KVDocument.hpp"
+#include "io/kv/KVParser.hpp"
+#include "io/kv/KVReader.hpp"
 
 #include "logic/State.hpp"
 #include "logic/StateConnection.hpp"
@@ -64,12 +72,16 @@
 #include "serialization/logic/SerializableJSONState.hpp"
 #include "serialization/logic/SerializableJSONStateConnection.hpp"
 #include "serialization/logic/SerializableJSONStateMachine.hpp"
+#include "serialization/event/SerializableJSONEvent.hpp"
 #include "serialization/ISerializable.hpp"
 
 #include "time/Date.hpp"
 
 #include "utils/RegexUtils.hpp"
 #include "utils/STLUtils.hpp"
+#if _WIN32
+#include "utils/WindowsUtils.hpp"
+#endif
 
 #include "event/Event.hpp"
 #include "event/EventTypes.hpp"

+ 48 - 0
include/ls_std/serialization/event/SerializableJSONEvent.hpp

@@ -0,0 +1,48 @@
+/*
+ * Author:          Patrick-Christopher Mattulat
+ * Company:         Lynar Studios
+ * E-Mail:          webmaster@lynarstudios.com
+ * Created:         2020-12-07
+ * Changed:         2020-12-20
+ *
+ * */
+
+#ifndef LS_STD_SERIALIZABLE_JSON_EVENT_HPP
+#define LS_STD_SERIALIZABLE_JSON_EVENT_HPP
+
+#include <ls_std/serialization/ISerializable.hpp>
+#include <ls_std/base/Class.hpp>
+#include <ls_std/event/Event.hpp>
+#include <memory>
+#include <ls_std/lib/nlohmann_json/include/nlohmann/json.hpp>
+
+namespace ls_std {
+  class SerializableJSONEvent : public ls_std::Class, public ls_std::ISerializable {
+    public:
+
+      explicit SerializableJSONEvent(const std::shared_ptr<ls_std::Event>& _value);
+      ~SerializableJSONEvent() override = default;
+
+      // implementation
+
+      ls_std::byte_field marshal() override;
+      void unmarshal(const ls_std::byte_field& _data) override;
+
+      // additional functionality
+
+      std::shared_ptr<ls_std::Event> getValue();
+      void setValue(const std::shared_ptr<ls_std::Event>& _value);
+
+    private:
+
+      nlohmann::json jsonObject {};
+      std::shared_ptr<ls_std::Event> value {};
+
+      void _assignValue(const std::shared_ptr<ls_std::Event>& _value);
+      void _unmarshalParameterList();
+      void _update();
+      void _updateEventParameterList();
+  };
+}
+
+#endif

+ 40 - 0
include/ls_std/utils/WindowsUtils.hpp

@@ -0,0 +1,40 @@
+/*
+ * Author:          Patrick-Christopher Mattulat
+ * Company:         Lynar Studios
+ * E-Mail:          webmaster@lynarstudios.com
+ * Created:         2020-12-06
+ * Changed:         2020-12-06
+ *
+ * */
+
+#ifndef LS_STD_WINDOWS_UTILS_HPP
+#define LS_STD_WINDOWS_UTILS_HPP
+
+#include <ls_std/base/Types.hpp>
+#include <string>
+#include <windows.h>
+
+namespace ls_std {
+  class WindowsUtils {
+    public:
+
+      WindowsUtils() = default;
+      ~WindowsUtils() = default;
+
+      static std::string getMessageFromErrorCode(const int& _errorCode) {
+        ls_std::byte messageBuffer[256 + 1];
+
+        FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+                       nullptr,
+                       _errorCode,
+                       MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
+                       messageBuffer,
+                       sizeof (messageBuffer),
+                       nullptr);
+
+        return std::string {messageBuffer};
+      }
+  };
+}
+
+#endif

+ 48 - 4
source/ls_std/event/Event.cpp

@@ -3,15 +3,35 @@
  * Company:         Lynar Studios
  * E-Mail:          webmaster@lynarstudios.com
  * Created:         2020-11-26
- * Changed:         2020-11-29
+ * Changed:         2020-12-23
  *
  * */
 
 #include <ls_std/event/Event.hpp>
+#include <ls_std/exception/IllegalArgumentException.hpp>
 
-ls_std::Event::Event(ls_std::event_id _id) : ls_std::Class("Event"),
-id(std::move(_id))
-{}
+ls_std::Event::Event(const ls_std::event_id& _id) : ls_std::Class("Event")
+{
+  this->_assignId(_id);
+}
+
+ls_std::byte_field ls_std::Event::marshal()
+{
+  ls_std::byte_field data {};
+
+  if(this->serializable != nullptr) {
+    data = this->serializable->marshal();
+  }
+
+  return data;
+}
+
+void ls_std::Event::unmarshal(const ls_std::byte_field &_data)
+{
+  if(this->serializable != nullptr) {
+    this->serializable->unmarshal(_data);
+  }
+}
 
 void ls_std::Event::addParameter(const ls_std::event_parameter &_eventParameter)
 {
@@ -20,6 +40,11 @@ void ls_std::Event::addParameter(const ls_std::event_parameter &_eventParameter)
   }
 }
 
+void ls_std::Event::clearParameterList()
+{
+  this->parameterList.clear();
+}
+
 ls_std::event_id ls_std::Event::getId()
 {
   return this->id;
@@ -37,7 +62,26 @@ void ls_std::Event::removeParameter(const ls_std::event_parameter_id &_id)
   }
 }
 
+void ls_std::Event::setId(const ls_std::event_id&  _id)
+{
+  this->_assignId(_id);
+}
+
+void ls_std::Event::_assignId(const ls_std::event_id& _id)
+{
+  if(_id.empty()) {
+    throw ls_std::IllegalArgumentException {};
+  }
+
+  this->id = _id;
+}
+
 bool ls_std::Event::_hasParameter(const ls_std::event_id &_id)
 {
   return this->parameterList.find(_id) != this->parameterList.end();
 }
+
+void ls_std::Event::setSerializable(std::shared_ptr<ISerializable> _serializable)
+{
+  this->serializable = std::move(_serializable);
+}

+ 52 - 0
source/ls_std/io/kv/KVDocument.cpp

@@ -0,0 +1,52 @@
+/*
+ * Author:          Patrick-Christopher Mattulat
+ * Company:         Lynar Studios
+ * E-Mail:          webmaster@lynarstudios.com
+ * Created:         2020-12-25
+ * Changed:         2020-12-25
+ *
+ * */
+
+#include <ls_std/io/kv/KVDocument.hpp>
+
+ls_std::KVDocument::KVDocument() : ls_std::Class("KVDocument")
+{}
+
+bool ls_std::KVDocument::addPair(ls_std::KVPair _pair)
+{
+  bool added {};
+
+  if(!this->_hasPair(_pair.getKey())) {
+    std::pair<ls_std::kv_key, ls_std::KVPair> pair = std::make_pair(_pair.getKey(), _pair);
+    added = this->pairs.insert(pair).second;
+  }
+
+  return added;
+}
+
+void ls_std::KVDocument::clear()
+{
+  this->pairs.clear();
+}
+
+std::map<ls_std::kv_key, ls_std::KVPair> ls_std::KVDocument::getPairs()
+{
+  return this->pairs;
+}
+
+bool ls_std::KVDocument::hasPair(const ls_std::kv_key &_key)
+{
+  return this->_hasPair(_key);
+}
+
+void ls_std::KVDocument::removePair(const ls_std::kv_key &_key)
+{
+  if(_hasPair(_key)) {
+    this->pairs.erase(_key);
+  }
+}
+
+bool ls_std::KVDocument::_hasPair(const ls_std::kv_key& _key)
+{
+  return this->pairs.find(_key) != this->pairs.end();
+}

+ 41 - 0
source/ls_std/io/kv/KVPair.cpp

@@ -0,0 +1,41 @@
+/*
+ * Author:          Patrick-Christopher Mattulat
+ * Company:         Lynar Studios
+ * E-Mail:          webmaster@lynarstudios.com
+ * Created:         2020-12-25
+ * Changed:         2020-12-25
+ *
+ * */
+
+#include <ls_std/io/kv/KVPair.hpp>
+#include <ls_std/exception/IllegalArgumentException.hpp>
+
+ls_std::KVPair::KVPair(const ls_std::kv_key& _key, ls_std::kv_value _value) : ls_std::Class("KVPair"),
+value(std::move(_value))
+{
+  this->_assignKey(_key);
+}
+
+ls_std::kv_key ls_std::KVPair::getKey()
+{
+  return this->key;
+}
+
+ls_std::kv_value ls_std::KVPair::getValue()
+{
+  return this->value;
+}
+
+void ls_std::KVPair::setValue(const ls_std::kv_value &_value)
+{
+  this->value = _value;
+}
+
+void ls_std::KVPair::_assignKey(const ls_std::kv_key &_key)
+{
+  if(_key.empty()) {
+    throw ls_std::IllegalArgumentException {};
+  }
+
+  this->key = _key;
+}

+ 96 - 0
source/ls_std/io/kv/KVParser.cpp

@@ -0,0 +1,96 @@
+/*
+ * Author:          Patrick-Christopher Mattulat
+ * Company:         Lynar Studios
+ * E-Mail:          webmaster@lynarstudios.com
+ * Created:         2020-12-25
+ * Changed:         2020-12-25
+ *
+ * */
+
+#include <ls_std/io/kv/KVParser.hpp>
+#include <ls_std/exception/NullPointerException.hpp>
+#include <ls_std/io/NewLine.hpp>
+
+ls_std::KVParser::KVParser(const std::shared_ptr<ls_std::KVDocument> &_document) : ls_std::Class("KVParser")
+{
+  this->_assignDocument(_document);
+}
+
+std::shared_ptr<ls_std::KVDocument> ls_std::KVParser::getDocument()
+{
+  return this->document;
+}
+
+void ls_std::KVParser::parse(const ls_std::byte_field &_data)
+{
+  this->_parse(_data);
+}
+
+void ls_std::KVParser::setDocument(const std::shared_ptr<ls_std::KVDocument> &_document)
+{
+  this->_assignDocument(_document);
+}
+
+void ls_std::KVParser::_assignDocument(const std::shared_ptr<ls_std::KVDocument> &_document)
+{
+  if(_document == nullptr) {
+    throw ls_std::NullPointerException {};
+  }
+
+  this->document = _document;
+}
+
+bool ls_std::KVParser::_lineHasPair(ls_std::KVParseData _parseData)
+{
+  return _parseData.line.contains("=") && _parseData.line.contains(";");
+}
+
+void ls_std::KVParser::_parse(const ls_std::byte_field &_data)
+{
+  for(std::string::size_type index = 0 ; index < _data.size() ; index++) {
+    ls_std::KVParseData parseData = ls_std::KVParser::_readLine(_data, index);
+    this->_parsePair(parseData);
+    index = parseData.index;
+  }
+}
+
+void ls_std::KVParser::_parsePair(ls_std::KVParseData _parseData)
+{
+  if(ls_std::KVParser::_lineHasPair(_parseData)) {
+    size_t equalSignPosition = _parseData.line.toString().find('=');
+    ls_std::kv_key key = _parseData.line.toString().substr(0, equalSignPosition);
+    ls_std::kv_value value = _parseData.line.toString().substr(equalSignPosition + 1);
+    value = value.substr(0, value.find(';'));
+
+    this->document->addPair(ls_std::KVPair {key, value});
+  }
+}
+
+ls_std::KVParseData ls_std::KVParser::_readLine(const ls_std::byte_field &_data, std::string::size_type _index)
+{
+  ls_std::KVParseData parseData {};
+  parseData.line = _data.substr(_index);
+
+  if(parseData.line.contains(ls_std::NewLine::getWindowsNewLine())) {
+    ls_std::KVParser::_readLineWithWindowsLineBreak(parseData);
+  } else {
+    if(parseData.line.contains(ls_std::NewLine::getUnixNewLine())) {
+      ls_std::KVParser::_readLineWithUnixLineBreak(parseData);
+    }
+  }
+
+  parseData.index = _index + parseData.line.toString().size();
+  return parseData;
+}
+
+void ls_std::KVParser::_readLineWithUnixLineBreak(ls_std::KVParseData& _parseData)
+{
+  size_t newLinePosition = _parseData.line.toString().find(ls_std::NewLine::getUnixNewLine());
+  _parseData.line = _parseData.line.toString().substr(0, newLinePosition);
+}
+
+void ls_std::KVParser::_readLineWithWindowsLineBreak(ls_std::KVParseData &_parseData)
+{
+  size_t newLinePosition = _parseData.line.toString().find(ls_std::NewLine::getWindowsNewLine());
+  _parseData.line = _parseData.line.toString().substr(0, newLinePosition + 1);
+}

+ 61 - 0
source/ls_std/io/kv/KVReader.cpp

@@ -0,0 +1,61 @@
+/*
+ * Author:          Patrick-Christopher Mattulat
+ * Company:         Lynar Studios
+ * E-Mail:          webmaster@lynarstudios.com
+ * Created:         2020-12-25
+ * Changed:         2020-12-25
+ *
+ * */
+
+#include <ls_std/io/kv/KVReader.hpp>
+#include <ls_std/exception/IllegalArgumentException.hpp>
+#include <ls_std/io/FileReader.hpp>
+#include <ls_std/io/kv/KVParser.hpp>
+
+ls_std::KVReader::KVReader(const std::shared_ptr<ls_std::KVDocument> &_document, const std::string &_absolutePath) : ls_std::Class("KVReader"),
+kvFile(ls_std::File {""})
+{
+  this->_assignDocument(_document);
+  this->_assignFile(ls_std::File {_absolutePath});
+}
+
+ls_std::byte_field ls_std::KVReader::read()
+{
+  ls_std::byte_field data = ls_std::FileReader {this->kvFile}.read();
+  ls_std::KVParser {this->document}.parse(data);
+
+  return data;
+}
+
+std::shared_ptr<ls_std::KVDocument> ls_std::KVReader::getDocument()
+{
+  return this->document;
+}
+
+void ls_std::KVReader::setDocument(const std::shared_ptr<ls_std::KVDocument> &_document)
+{
+  this->_assignDocument(_document);
+}
+
+void ls_std::KVReader::setFile(const ls_std::File &_kvFile)
+{
+  this->_assignFile(_kvFile);
+}
+
+void ls_std::KVReader::_assignDocument(const std::shared_ptr<ls_std::KVDocument> &_document)
+{
+  if(_document == nullptr) {
+    throw ls_std::IllegalArgumentException {};
+  }
+
+  this->document = _document;
+}
+
+void ls_std::KVReader::_assignFile(ls_std::File _kvFile)
+{
+  if(!_kvFile.exists()) {
+    throw ls_std::IllegalArgumentException {};
+  }
+
+  this->kvFile = _kvFile;
+}

+ 2 - 3
source/ls_std/serialization/json/boxing/SerializableJSONString.cpp

@@ -3,7 +3,7 @@
  * Company:         Lynar Studios
  * E-Mail:          webmaster@lynarstudios.com
  * Created:         2020-08-30
- * Changed:         2020-11-26
+ * Changed:         2020-12-20
  *
  * */
 
@@ -24,8 +24,7 @@ ls_std::byte_field ls_std::SerializableJSONString::marshal()
 
 void ls_std::SerializableJSONString::unmarshal(const ls_std::byte_field& _data)
 {
-  std::string jsonString = std::string(_data);
-  this->jsonObject = nlohmann::json::parse(jsonString);
+  this->jsonObject = nlohmann::json::parse(_data);
 
   if(this->jsonObject.contains("value")) {
     *this->value = this->jsonObject["value"];

+ 79 - 0
source/ls_std/serialization/json/event/SerializableJSONEvent.cpp

@@ -0,0 +1,79 @@
+/*
+ * Author:          Patrick-Christopher Mattulat
+ * Company:         Lynar Studios
+ * E-Mail:          webmaster@lynarstudios.com
+ * Created:         2020-12-07
+ * Changed:         2020-12-20
+ *
+ * */
+
+#include <ls_std/serialization/event/SerializableJSONEvent.hpp>
+#include <ls_std/exception/NullPointerException.hpp>
+
+ls_std::SerializableJSONEvent::SerializableJSONEvent(const std::shared_ptr<ls_std::Event>& _value) :
+ls_std::Class("SerializableJSONEvent")
+{
+  this->_assignValue(_value);
+}
+
+ls_std::byte_field ls_std::SerializableJSONEvent::marshal()
+{
+  this->_update();
+  return this->jsonObject.dump();
+}
+
+void ls_std::SerializableJSONEvent::unmarshal(const ls_std::byte_field &_data)
+{
+  this->jsonObject = nlohmann::json::parse(_data);
+
+  this->value->setId(this->jsonObject["id"]);
+  this->_unmarshalParameterList();
+}
+
+std::shared_ptr<ls_std::Event> ls_std::SerializableJSONEvent::getValue()
+{
+  return this->value;
+}
+
+void ls_std::SerializableJSONEvent::setValue(const std::shared_ptr<ls_std::Event>& _value)
+{
+  this->_assignValue(_value);
+}
+
+void ls_std::SerializableJSONEvent::_assignValue(const std::shared_ptr<ls_std::Event> &_value)
+{
+  if(_value == nullptr) {
+    throw ls_std::NullPointerException {};
+  }
+
+  this->value = _value;
+}
+
+void ls_std::SerializableJSONEvent::_unmarshalParameterList()
+{
+  this->value->clearParameterList();
+
+  for(const auto& parameterJSON : this->jsonObject["parameterList"]) {
+    ls_std::event_parameter parameter = {parameterJSON.at(0), parameterJSON.at(1)};
+    this->value->addParameter(parameter);
+  }
+}
+
+void ls_std::SerializableJSONEvent::_update()
+{
+  this->jsonObject = {
+      {"id", this->value->getId()}
+  };
+
+  this->_updateEventParameterList();
+}
+
+void ls_std::SerializableJSONEvent::_updateEventParameterList()
+{
+  std::string jsonString {};
+
+  for(const auto& eventParameter : this->value->getParameterList()) {
+    nlohmann::json parameterJson = {eventParameter.first, eventParameter.second};
+    this->jsonObject["parameterList"][eventParameter.first] = parameterJson;
+  }
+}

+ 58 - 1
test/cases/event/EventTest.cpp

@@ -3,7 +3,7 @@
  * Company:         Lynar Studios
  * E-Mail:          webmaster@lynarstudios.com
  * Created:         2020-11-26
- * Changed:         2020-11-26
+ * Changed:         2020-12-23
  *
  * */
 
@@ -24,6 +24,42 @@ namespace {
       {}
   };
 
+  // implementation
+
+  TEST_F(EventTest, marshal)
+  {
+    ls_std::Event event {"OPEN_DOOR_EVENT"};
+    event.addParameter(ls_std::event_parameter{"key_available", "true"});
+    event.addParameter(ls_std::event_parameter{"door_id", "16675"});
+
+    std::shared_ptr<ls_std::SerializableJSONEvent> serializable = std::make_shared<ls_std::SerializableJSONEvent>(std::make_shared<ls_std::Event>(event));
+    event.setSerializable(serializable);
+
+    ls_std::byte_field data = event.marshal();
+    ASSERT_FALSE(data.empty());
+    std::string expectedString = R"({"id":"OPEN_DOOR_EVENT","parameterList":{"door_id":["door_id","16675"],"key_available":["key_available","true"]}})";
+    ASSERT_STREQ(expectedString.c_str(), data.c_str());
+  }
+
+  TEST_F(EventTest, unmarshal)
+  {
+    std::shared_ptr<ls_std::Event> event =  std::make_shared<ls_std::Event>("TMP_EVENT");
+    std::shared_ptr<ls_std::SerializableJSONEvent> serializable = std::make_shared<ls_std::SerializableJSONEvent>(event);
+    event->setSerializable(serializable);
+
+    std::string jsonString = R"({"id":"OPEN_DOOR_EVENT","parameterList":{"door_id":["door_id","16675"],"key_available":["key_available","true"]}})";
+    event->unmarshal(jsonString);
+    ASSERT_STREQ("OPEN_DOOR_EVENT", event->getId().c_str());
+    ls_std::event_parameter_list parameterList = event->getParameterList();
+
+    ASSERT_FALSE(parameterList.empty());
+    ASSERT_EQ(2, parameterList.size());
+    ASSERT_STREQ("16675", parameterList.at("door_id").c_str());
+    ASSERT_STREQ("true", parameterList.at("key_available").c_str());
+  }
+
+  // additional functionality
+
   TEST_F(EventTest, getClassName)
   {
     ls_std::Event event {"OPEN_DOOR_EVENT"};
@@ -45,6 +81,18 @@ namespace {
     ASSERT_STREQ("yes", parameterList.at("facing_door").c_str());
   }
 
+  TEST_F(EventTest, clearParameterList)
+  {
+    ls_std::Event event {"OPEN_DOOR_EVENT"};
+    event.addParameter(ls_std::event_parameter("key", "yes"));
+    event.addParameter(ls_std::event_parameter("facing_door", "yes"));
+    ASSERT_EQ(2, event.getParameterList().size());
+
+    event.clearParameterList();
+    ASSERT_TRUE(event.getParameterList().empty());
+    ASSERT_EQ(0, event.getParameterList().size());
+  }
+
   TEST_F(EventTest, getId)
   {
     ls_std::Event event {"OPEN_DOOR_EVENT"};
@@ -73,4 +121,13 @@ namespace {
     ASSERT_EQ(0, event.getParameterList().size());
     ASSERT_TRUE(event.getParameterList().empty());
   }
+
+  TEST_F(EventTest, setId)
+  {
+    ls_std::Event event {"OPEN_DOOR_EVENT"};
+    ASSERT_STREQ("OPEN_DOOR_EVENT", event.getId().c_str());
+
+    event.setId("ANOTHER_EVENT");
+    ASSERT_STREQ("ANOTHER_EVENT", event.getId().c_str());
+  }
 }

+ 95 - 0
test/cases/io/kv/KVDocumentTest.cpp

@@ -0,0 +1,95 @@
+/*
+ * Author:          Patrick-Christopher Mattulat
+ * Company:         Lynar Studios
+ * E-Mail:          webmaster@lynarstudios.com
+ * Created:         2020-12-25
+ * Changed:         2020-12-25
+ *
+ * */
+
+#include <gtest/gtest.h>
+#include <ls_std/ls_std.hpp>
+
+namespace {
+  class KVDocumentTest : public ::testing::Test {
+    protected:
+
+      KVDocumentTest() = default;
+      ~KVDocumentTest() override = default;
+
+      void SetUp() override {}
+      void TearDown() override {}
+  };
+
+  TEST_F(KVDocumentTest, addPair)
+  {
+    ls_std::KVDocument document {};
+    ls_std::KVPair pair {"port", "13088"};
+
+    ASSERT_TRUE(document.getPairs().empty());
+    ASSERT_TRUE(document.addPair(pair));
+    ASSERT_EQ(1, document.getPairs().size());
+  }
+
+  TEST_F(KVDocumentTest, addPairNegative)
+  {
+    ls_std::KVDocument document {};
+    ls_std::KVPair pair {"port", "13088"};
+
+    ASSERT_TRUE(document.getPairs().empty());
+    ASSERT_TRUE(document.addPair(pair));
+    ASSERT_EQ(1, document.getPairs().size());
+
+    ASSERT_FALSE(document.addPair(pair));
+  }
+
+  TEST_F(KVDocumentTest, clear)
+  {
+    ls_std::KVDocument document {};
+    ls_std::KVPair pair {"port", "13088"};
+
+    ASSERT_TRUE(document.getPairs().empty());
+    ASSERT_TRUE(document.addPair(pair));
+    ASSERT_FALSE(document.getPairs().empty());
+    ASSERT_EQ(1, document.getPairs().size());
+
+    document.clear();
+    ASSERT_TRUE(document.getPairs().empty());
+    ASSERT_EQ(0, document.getPairs().size());
+  }
+
+  TEST_F(KVDocumentTest, getPairs)
+  {
+    ls_std::KVDocument document {};
+    ASSERT_TRUE(document.getPairs().empty());
+  }
+
+  TEST_F(KVDocumentTest, hasPair)
+  {
+    ls_std::KVDocument document {};
+    ASSERT_TRUE(document.getPairs().empty());
+    ASSERT_TRUE(document.addPair(ls_std::KVPair {"port", "80"}));
+    ASSERT_TRUE(document.hasPair("port"));
+  }
+
+  TEST_F(KVDocumentTest, hasPairNegative)
+  {
+    ls_std::KVDocument document {};
+    ASSERT_TRUE(document.getPairs().empty());
+    ASSERT_FALSE(document.hasPair("port"));
+  }
+
+  TEST_F(KVDocumentTest, removePair)
+  {
+    ls_std::KVDocument document {};
+    ASSERT_TRUE(document.addPair(ls_std::KVPair {"port", "80"}));
+    ASSERT_TRUE(document.addPair(ls_std::KVPair {"host", "localhost"}));
+    ASSERT_TRUE(document.addPair(ls_std::KVPair {"protocol", "TCP"}));
+    ASSERT_EQ(3, document.getPairs().size());
+
+    document.removePair("protocol");
+    ASSERT_EQ(2, document.getPairs().size());
+    ASSERT_TRUE(document.hasPair("port"));
+    ASSERT_TRUE(document.hasPair("host"));
+  }
+}

+ 44 - 0
test/cases/io/kv/KVPairTest.cpp

@@ -0,0 +1,44 @@
+/*
+ * Author:          Patrick-Christopher Mattulat
+ * Company:         Lynar Studios
+ * E-Mail:          webmaster@lynarstudios.com
+ * Created:         2020-12-25
+ * Changed:         2020-12-25
+ *
+ * */
+
+#include <gtest/gtest.h>
+#include <ls_std/ls_std.hpp>
+
+namespace {
+  class KVPairTest : public ::testing::Test {
+    protected:
+
+      KVPairTest() = default;
+      ~KVPairTest() override = default;
+
+      void SetUp() override {}
+      void TearDown() override {}
+  };
+
+  TEST_F(KVPairTest, getKey)
+  {
+    ls_std::KVPair pair {"port", "13088"};
+    ASSERT_STREQ("port", pair.getKey().c_str());
+  }
+
+  TEST_F(KVPairTest, getValue)
+  {
+    ls_std::KVPair pair {"port", "13088"};
+    ASSERT_STREQ("13088", pair.getValue().c_str());
+  }
+
+  TEST_F(KVPairTest, setValue)
+  {
+    ls_std::KVPair pair {"port", "13088"};
+    ASSERT_STREQ("13088", pair.getValue().c_str());
+
+    pair.setValue("8080");
+    ASSERT_STREQ("8080", pair.getValue().c_str());
+  }
+}

+ 58 - 0
test/cases/io/kv/KVParserTest.cpp

@@ -0,0 +1,58 @@
+/*
+ * Author:          Patrick-Christopher Mattulat
+ * Company:         Lynar Studios
+ * E-Mail:          webmaster@lynarstudios.com
+ * Created:         2020-12-25
+ * Changed:         2020-12-25
+ *
+ * */
+
+#include <gtest/gtest.h>
+#include <ls_std/ls_std.hpp>
+
+namespace {
+  class KVParserTest : public ::testing::Test {
+    protected:
+
+      KVParserTest() = default;
+      ~KVParserTest() override = default;
+
+      void SetUp() override {}
+      void TearDown() override {}
+  };
+
+  TEST_F(KVParserTest, getDocument)
+  {
+    ls_std::KVParser parser {std::make_shared<ls_std::KVDocument>()};
+    ASSERT_TRUE(parser.getDocument() != nullptr);
+  }
+
+  TEST_F(KVParserTest, parse)
+  {
+    std::shared_ptr<ls_std::KVDocument> document = std::make_shared<ls_std::KVDocument>();
+    ls_std::KVParser parser {document};
+    ls_std::byte_field data = "# starting comment\n\nport=8080; # some comment\nhost=localhost;\nservice-name=deamon;";
+    parser.parse(data);
+
+    ASSERT_EQ(3, document->getPairs().size());
+    ASSERT_TRUE(document->hasPair("port"));
+    ASSERT_TRUE(document->hasPair("host"));
+    ASSERT_TRUE(document->hasPair("service-name"));
+
+    ASSERT_STREQ("8080", document->getPairs().at("port").getValue().c_str());
+    ASSERT_STREQ("localhost", document->getPairs().at("host").getValue().c_str());
+    ASSERT_STREQ("deamon", document->getPairs().at("service-name").getValue().c_str());
+  }
+
+  TEST_F(KVParserTest, setDocument)
+  {
+    std::shared_ptr<ls_std::KVDocument> document1 = std::make_shared<ls_std::KVDocument>();
+    std::shared_ptr<ls_std::KVDocument> document2 = std::make_shared<ls_std::KVDocument>();
+
+    ls_std::KVParser parser {document1};
+    ASSERT_TRUE(parser.getDocument() == document1);
+
+    parser.setDocument(document2);
+    ASSERT_TRUE(parser.getDocument() == document2);
+  }
+}

+ 62 - 0
test/cases/io/kv/KVReaderTest.cpp

@@ -0,0 +1,62 @@
+/*
+ * Author:          Patrick-Christopher Mattulat
+ * Company:         Lynar Studios
+ * E-Mail:          webmaster@lynarstudios.com
+ * Created:         2020-12-25
+ * Changed:         2020-12-25
+ *
+ * */
+
+#include <gtest/gtest.h>
+#include <ls_std/ls_std.hpp>
+#include <TestHelper.hpp>
+
+namespace {
+  class KVReaderTest : public ::testing::Test {
+    protected:
+
+      KVReaderTest() = default;
+      ~KVReaderTest() override = default;
+
+      void SetUp() override {}
+      void TearDown() override {}
+  };
+
+  TEST_F(KVReaderTest, getDocument)
+  {
+    std::string kvPath = TestHelper::getResourcesFolderLocation() + "server_settings.kv";
+    ls_std::KVReader reader {std::make_shared<ls_std::KVDocument>(), kvPath};
+
+    ASSERT_TRUE(reader.getDocument() != nullptr);
+  }
+
+  TEST_F(KVReaderTest, read)
+  {
+    std::string kvPath = TestHelper::getResourcesFolderLocation() + "server_settings.kv";
+    std::shared_ptr<ls_std::KVDocument> document = std::make_shared<ls_std::KVDocument>();
+    ls_std::KVReader reader {document, kvPath};
+
+    reader.read();
+    ASSERT_EQ(3, document->getPairs().size());
+    ASSERT_TRUE(document->hasPair("port"));
+    ASSERT_TRUE(document->hasPair("host"));
+    ASSERT_TRUE(document->hasPair("service-name"));
+
+    ASSERT_STREQ("8080", document->getPairs().at("port").getValue().c_str());
+    ASSERT_STREQ("localhost", document->getPairs().at("host").getValue().c_str());
+    ASSERT_STREQ("deamon", document->getPairs().at("service-name").getValue().c_str());
+  }
+
+  TEST_F(KVReaderTest, setDocument)
+  {
+    std::string kvPath = TestHelper::getResourcesFolderLocation() + "server_settings.kv";
+    std::shared_ptr<ls_std::KVDocument> document1 = std::make_shared<ls_std::KVDocument>();
+    std::shared_ptr<ls_std::KVDocument> document2 = std::make_shared<ls_std::KVDocument>();
+
+    ls_std::KVReader reader {document1, kvPath};
+    ASSERT_TRUE(reader.getDocument() == document1);
+
+    reader.setDocument(document2);
+    ASSERT_TRUE(reader.getDocument() == document2);
+  }
+}

+ 55 - 0
test/cases/serialization/json/event/SerializableJSONEventTest.cpp

@@ -0,0 +1,55 @@
+/*
+ * Author:          Patrick-Christopher Mattulat
+ * Company:         Lynar Studios
+ * E-Mail:          webmaster@lynarstudios.com
+ * Created:         2020-12-20
+ * Changed:         2020-12-20
+ *
+ * */
+
+#include <gtest/gtest.h>
+#include <ls_std/ls_std.hpp>
+
+namespace
+{
+  class SerializableJSONEventTest : public ::testing::Test
+  {
+    protected:
+
+      SerializableJSONEventTest() = default;
+      ~SerializableJSONEventTest() override = default;
+
+      void SetUp() override {}
+      void TearDown() override {}
+  };
+
+  TEST_F(SerializableJSONEventTest, marshal)
+  {
+    ls_std::Event event {"OPEN_DOOR_EVENT"};
+    event.addParameter(ls_std::event_parameter{"key_available", "true"});
+    event.addParameter(ls_std::event_parameter{"door_id", "16675"});
+
+    ls_std::SerializableJSONEvent serializable {std::make_shared<ls_std::Event>(event)};
+
+    ls_std::byte_field data = serializable.marshal();
+    ASSERT_FALSE(data.empty());
+    std::string expectedString = R"({"id":"OPEN_DOOR_EVENT","parameterList":{"door_id":["door_id","16675"],"key_available":["key_available","true"]}})";
+    ASSERT_STREQ(expectedString.c_str(), data.c_str());
+  }
+
+  TEST_F(SerializableJSONEventTest, unmarshal)
+  {
+    ls_std::Event event {"TMP_EVENT"};
+    ls_std::SerializableJSONEvent serializable {std::make_shared<ls_std::Event>(event)};
+    std::string jsonString = R"({"id":"OPEN_DOOR_EVENT","parameterList":{"door_id":["door_id","16675"],"key_available":["key_available","true"]}})";
+
+    serializable.unmarshal(jsonString);
+    ASSERT_STREQ("OPEN_DOOR_EVENT", serializable.getValue()->getId().c_str());
+    ls_std::event_parameter_list parameterList = serializable.getValue()->getParameterList();
+
+    ASSERT_FALSE(parameterList.empty());
+    ASSERT_EQ(2, parameterList.size());
+    ASSERT_STREQ("16675", parameterList.at("door_id").c_str());
+    ASSERT_STREQ("true", parameterList.at("key_available").c_str());
+  }
+}

+ 5 - 0
test/resources/server_settings.kv

@@ -0,0 +1,5 @@
+# This is an example key value file for server settings
+
+port=8080;
+host=localhost;
+service-name=deamon;