Create an abstract class for schemas

Change-Id: I0845e8fa11db68add75bed40d44779f67c0a33aa
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 4ec7140..9b3e0a1 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -55,6 +55,7 @@
 add_definitions(-DBOOST_SPIRIT_X3_NO_FILESYSTEM)
 
 set(parser_SRCS
+    src/static_schema.cpp
     src/schema.cpp
     src/parser.cpp
     src/ast_commands.cpp
diff --git a/src/main.cpp b/src/main.cpp
index 2835600..0c02ecf 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -13,7 +13,7 @@
 #include "NETCONF_CLI_VERSION.h"
 #include "interpreter.hpp"
 #include "parser.hpp"
-#include "schema.hpp"
+#include "static_schema.hpp"
 
 
 static const char usage[] =
@@ -46,7 +46,7 @@
                                true);
     std::cout << "Welcome to netconf-cli" << std::endl;
 
-    auto schema = std::make_shared<Schema>();
+    auto schema = std::make_shared<StaticSchema>();
     schema->addContainer("", "a", yang::ContainerTraits::Presence);
     schema->addContainer("", "b");
     schema->addLeaf("", "leafString", yang::LeafDataTypes::String);
diff --git a/src/schema.cpp b/src/schema.cpp
index 066dc3b..c390e31 100644
--- a/src/schema.cpp
+++ b/src/schema.cpp
@@ -7,126 +7,5 @@
 */
 
 #include "schema.hpp"
-#include "utils.hpp"
 
-InvalidNodeException::~InvalidNodeException() = default;
-
-Schema::Schema()
-{
-    m_nodes.emplace("", std::unordered_map<std::string, NodeType>());
-}
-
-const std::unordered_map<std::string, NodeType>& Schema::children(const std::string& name) const
-{
-    return m_nodes.at(name);
-}
-
-bool Schema::nodeExists(const std::string& location, const std::string& name) const
-{
-    if (name.empty())
-        return true;
-    const auto& childrenRef = children(location);
-
-    return childrenRef.find(name) != childrenRef.end();
-}
-
-bool Schema::isContainer(const path_& location, const std::string& name) const
-{
-    std::string locationString = pathToSchemaString(location);
-    if (!nodeExists(locationString, name))
-        return false;
-
-    return children(locationString).at(name).type() == typeid(yang::container);
-}
-
-void Schema::addContainer(const std::string& location, const std::string& name, yang::ContainerTraits isPresence)
-{
-    m_nodes.at(location).emplace(name, yang::container{isPresence});
-
-    //create a new set of children for the new node
-    std::string key = joinPaths(location, name);
-    m_nodes.emplace(key, std::unordered_map<std::string, NodeType>());
-}
-
-
-bool Schema::listHasKey(const path_& location, const std::string& name, const std::string& key) const
-{
-    std::string locationString = pathToSchemaString(location);
-    assert(isList(location, name));
-
-    const auto& child = children(locationString).at(name);
-    const auto& list = boost::get<yang::list>(child);
-    return list.m_keys.find(key) != list.m_keys.end();
-}
-
-const std::set<std::string>& Schema::listKeys(const path_& location, const std::string& name) const
-{
-    std::string locationString = pathToSchemaString(location);
-    assert(isList(location, name));
-
-    const auto& child = children(locationString).at(name);
-    const auto& list = boost::get<yang::list>(child);
-    return list.m_keys;
-}
-
-bool Schema::isList(const path_& location, const std::string& name) const
-{
-    std::string locationString = pathToSchemaString(location);
-    if (!nodeExists(locationString, name))
-        return false;
-    const auto& child = children(locationString).at(name);
-    if (child.type() != typeid(yang::list))
-        return false;
-
-    return true;
-}
-
-void Schema::addList(const std::string& location, const std::string& name, const std::set<std::string>& keys)
-{
-    m_nodes.at(location).emplace(name, yang::list{keys});
-
-    m_nodes.emplace(name, std::unordered_map<std::string, NodeType>());
-}
-
-bool Schema::isPresenceContainer(const path_& location, const std::string& name) const
-{
-    if (!isContainer(location, name))
-        return false;
-    std::string locationString = pathToSchemaString(location);
-    return boost::get<yang::container>(children(locationString).at(name)).m_presence == yang::ContainerTraits::Presence;
-}
-
-void Schema::addLeaf(const std::string& location, const std::string& name, const yang::LeafDataTypes& type)
-{
-    m_nodes.at(location).emplace(name, yang::leaf{type, {}});
-}
-
-void Schema::addLeafEnum(const std::string& location, const std::string& name, std::set<std::string> enumValues)
-{
-    m_nodes.at(location).emplace(name, yang::leaf{yang::LeafDataTypes::Enum, enumValues});
-}
-
-bool Schema::leafEnumHasValue(const path_& location, const std::string& name, const std::string& value) const
-{
-    std::string locationString = pathToSchemaString(location);
-    assert(isLeaf(location, name));
-
-    const auto& child = children(locationString).at(name);
-    const auto& list = boost::get<yang::leaf>(child);
-    return list.m_enumValues.find(value) != list.m_enumValues.end();
-}
-
-bool Schema::isLeaf(const path_& location, const std::string& name) const
-{
-    std::string locationString = pathToSchemaString(location);
-    if (!nodeExists(locationString, name))
-        return false;
-
-    return children(locationString).at(name).type() == typeid(yang::leaf);
-}
-
-yang::LeafDataTypes Schema::leafType(const path_& location, const std::string& name) const
-{
-    std::string locationString = pathToSchemaString(location);
-    return boost::get<yang::leaf>(children(locationString).at(name)).m_type;
-}
+Schema::~Schema() = default;
diff --git a/src/schema.hpp b/src/schema.hpp
index e87619b..7c05d17 100644
--- a/src/schema.hpp
+++ b/src/schema.hpp
@@ -1,3 +1,4 @@
+
 /*
  * Copyright (C) 2018 CESNET, https://photonics.cesnet.cz/
  * Copyright (C) 2018 FIT CVUT, https://fit.cvut.cz/
@@ -52,25 +53,21 @@
 };
 
 /*! \class Schema
- *     \brief The class representing the schema, that the user traverses.
+ *     \brief A base schema class for schemas
  *         */
 class Schema {
 public:
-    Schema();
-    bool nodeExists(const std::string& location, const std::string& name) const;
+    virtual ~Schema();
 
-    bool isContainer(const path_& location, const std::string& name) const;
-    void addContainer(const std::string& location, const std::string& name, yang::ContainerTraits isPresence = yang::ContainerTraits::None);
-    const std::set<std::string>& listKeys(const path_& location, const std::string& name) const;
-    bool listHasKey(const path_& location, const std::string& name, const std::string& key) const;
-    bool isList(const path_& location, const std::string& name) const;
-    void addList(const std::string& location, const std::string& name, const std::set<std::string>& keys);
-    bool isPresenceContainer(const path_& location, const std::string& name) const;
-    void addLeaf(const std::string& location, const std::string& name, const yang::LeafDataTypes& type);
-    void addLeafEnum(const std::string& location, const std::string& name, std::set<std::string> enumValues);
-    bool leafEnumHasValue(const path_& location, const std::string& name, const std::string& value) const;
-    bool isLeaf(const path_& location, const std::string& name) const;
-    yang::LeafDataTypes leafType(const path_& location, const std::string& name) const;
+    virtual bool isContainer(const path_& location, const std::string& name) const = 0;
+    virtual bool isLeaf(const path_& location, const std::string& name) const = 0;
+    virtual bool isList(const path_& location, const std::string& name) const = 0;
+    virtual bool isPresenceContainer(const path_& location, const std::string& name) const = 0;
+    virtual bool leafEnumHasValue(const path_& location, const std::string& name, const std::string& value) const = 0;
+    virtual bool listHasKey(const path_& location, const std::string& name, const std::string& key) const = 0;
+    virtual bool nodeExists(const std::string& location, const std::string& name) const = 0;
+    virtual const std::set<std::string>& listKeys(const path_& location, const std::string& name) const = 0;
+    virtual yang::LeafDataTypes leafType(const path_& location, const std::string& name) const = 0;
 
 private:
     const std::unordered_map<std::string, NodeType>& children(const std::string& name) const;
diff --git a/src/static_schema.cpp b/src/static_schema.cpp
new file mode 100644
index 0000000..65debf0
--- /dev/null
+++ b/src/static_schema.cpp
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2018 CESNET, https://photonics.cesnet.cz/
+ * Copyright (C) 2018 FIT CVUT, https://fit.cvut.cz/
+ *
+ * Written by Václav Kubernát <kubervac@fit.cvut.cz>
+ *
+*/
+
+#include "static_schema.hpp"
+#include "utils.hpp"
+
+InvalidNodeException::~InvalidNodeException() = default;
+
+StaticSchema::StaticSchema()
+{
+    m_nodes.emplace("", std::unordered_map<std::string, NodeType>());
+}
+
+const std::unordered_map<std::string, NodeType>& StaticSchema::children(const std::string& name) const
+{
+    return m_nodes.at(name);
+}
+
+bool StaticSchema::nodeExists(const std::string& location, const std::string& name) const
+{
+    if (name.empty())
+        return true;
+    const auto& childrenRef = children(location);
+
+    return childrenRef.find(name) != childrenRef.end();
+}
+
+bool StaticSchema::isContainer(const path_& location, const std::string& name) const
+{
+    std::string locationString = pathToSchemaString(location);
+    if (!nodeExists(locationString, name))
+        return false;
+
+    return children(locationString).at(name).type() == typeid(yang::container);
+}
+
+void StaticSchema::addContainer(const std::string& location, const std::string& name, yang::ContainerTraits isPresence)
+{
+    m_nodes.at(location).emplace(name, yang::container{isPresence});
+
+    //create a new set of children for the new node
+    std::string key = joinPaths(location, name);
+    m_nodes.emplace(key, std::unordered_map<std::string, NodeType>());
+}
+
+
+bool StaticSchema::listHasKey(const path_& location, const std::string& name, const std::string& key) const
+{
+    std::string locationString = pathToSchemaString(location);
+    assert(isList(location, name));
+
+    const auto& child = children(locationString).at(name);
+    const auto& list = boost::get<yang::list>(child);
+    return list.m_keys.find(key) != list.m_keys.end();
+}
+
+const std::set<std::string>& StaticSchema::listKeys(const path_& location, const std::string& name) const
+{
+    std::string locationString = pathToSchemaString(location);
+    assert(isList(location, name));
+
+    const auto& child = children(locationString).at(name);
+    const auto& list = boost::get<yang::list>(child);
+    return list.m_keys;
+}
+
+bool StaticSchema::isList(const path_& location, const std::string& name) const
+{
+    std::string locationString = pathToSchemaString(location);
+    if (!nodeExists(locationString, name))
+        return false;
+    const auto& child = children(locationString).at(name);
+    if (child.type() != typeid(yang::list))
+        return false;
+
+    return true;
+}
+
+void StaticSchema::addList(const std::string& location, const std::string& name, const std::set<std::string>& keys)
+{
+    m_nodes.at(location).emplace(name, yang::list{keys});
+
+    m_nodes.emplace(name, std::unordered_map<std::string, NodeType>());
+}
+
+bool StaticSchema::isPresenceContainer(const path_& location, const std::string& name) const
+{
+    if (!isContainer(location, name))
+        return false;
+    std::string locationString = pathToSchemaString(location);
+    return boost::get<yang::container>(children(locationString).at(name)).m_presence == yang::ContainerTraits::Presence;
+}
+
+void StaticSchema::addLeaf(const std::string& location, const std::string& name, const yang::LeafDataTypes& type)
+{
+    m_nodes.at(location).emplace(name, yang::leaf{type, {}});
+}
+
+void StaticSchema::addLeafEnum(const std::string& location, const std::string& name, std::set<std::string> enumValues)
+{
+    m_nodes.at(location).emplace(name, yang::leaf{yang::LeafDataTypes::Enum, enumValues});
+}
+
+bool StaticSchema::leafEnumHasValue(const path_& location, const std::string& name, const std::string& value) const
+{
+    std::string locationString = pathToSchemaString(location);
+    assert(isLeaf(location, name));
+
+    const auto& child = children(locationString).at(name);
+    const auto& list = boost::get<yang::leaf>(child);
+    return list.m_enumValues.find(value) != list.m_enumValues.end();
+}
+
+bool StaticSchema::isLeaf(const path_& location, const std::string& name) const
+{
+    std::string locationString = pathToSchemaString(location);
+    if (!nodeExists(locationString, name))
+        return false;
+
+    return children(locationString).at(name).type() == typeid(yang::leaf);
+}
+
+yang::LeafDataTypes StaticSchema::leafType(const path_& location, const std::string& name) const
+{
+    std::string locationString = pathToSchemaString(location);
+    return boost::get<yang::leaf>(children(locationString).at(name)).m_type;
+}
diff --git a/src/static_schema.hpp b/src/static_schema.hpp
new file mode 100644
index 0000000..bab8bde
--- /dev/null
+++ b/src/static_schema.hpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2018 CESNET, https://photonics.cesnet.cz/
+ * Copyright (C) 2018 FIT CVUT, https://fit.cvut.cz/
+ *
+ * Written by Václav Kubernát <kubervac@fit.cvut.cz>
+ *
+*/
+
+#pragma once
+
+#include <boost/variant.hpp>
+#include <set>
+#include <stdexcept>
+#include <unordered_map>
+#include "ast_path.hpp"
+#include "schema.hpp"
+
+
+/*! \class StaticSchema
+ *     \brief Static schema, used mainly for testing
+ *         */
+
+class StaticSchema : public Schema {
+public:
+    StaticSchema();
+
+    bool isContainer(const path_& location, const std::string& name) const override;
+    bool isLeaf(const path_& location, const std::string& name) const override;
+    bool isList(const path_& location, const std::string& name) const override;
+    bool isPresenceContainer(const path_& location, const std::string& name) const override;
+    bool leafEnumHasValue(const path_& location, const std::string& name, const std::string& value) const override;
+    bool listHasKey(const path_& location, const std::string& name, const std::string& key) const override;
+    bool nodeExists(const std::string& location, const std::string& name) const override;
+    const std::set<std::string>& listKeys(const path_& location, const std::string& name) const override;
+    yang::LeafDataTypes leafType(const path_& location, const std::string& name) const override;
+
+    void addContainer(const std::string& location, const std::string& name, yang::ContainerTraits isPresence = yang::ContainerTraits::None);
+    void addLeaf(const std::string& location, const std::string& name, const yang::LeafDataTypes& type);
+    void addLeafEnum(const std::string& location, const std::string& name, std::set<std::string> enumValues);
+    void addList(const std::string& location, const std::string& name, const std::set<std::string>& keys);
+
+private:
+    const std::unordered_map<std::string, NodeType>& children(const std::string& name) const;
+
+    std::unordered_map<std::string, std::unordered_map<std::string, NodeType>> m_nodes;
+};
diff --git a/tests/cd.cpp b/tests/cd.cpp
index 3ba1e2f..29187ff 100644
--- a/tests/cd.cpp
+++ b/tests/cd.cpp
@@ -9,11 +9,11 @@
 #include "trompeloeil_catch.h"
 #include "ast_commands.hpp"
 #include "parser.hpp"
-#include "schema.hpp"
+#include "static_schema.hpp"
 
 TEST_CASE("cd")
 {
-    auto schema = std::make_shared<Schema>();
+    auto schema = std::make_shared<StaticSchema>();
     schema->addContainer("", "a");
     schema->addContainer("", "b");
     schema->addContainer("a", "a2");
diff --git a/tests/leaf_editing.cpp b/tests/leaf_editing.cpp
index 9ae2d97..b8c5591 100644
--- a/tests/leaf_editing.cpp
+++ b/tests/leaf_editing.cpp
@@ -9,11 +9,11 @@
 #include "trompeloeil_catch.h"
 #include "ast_commands.hpp"
 #include "parser.hpp"
-#include "schema.hpp"
+#include "static_schema.hpp"
 
 TEST_CASE("leaf editing")
 {
-    auto schema = std::make_shared<Schema>();
+    auto schema = std::make_shared<StaticSchema>();
     schema->addContainer("", "contA");
     schema->addLeaf("", "leafString", yang::LeafDataTypes::String);
     schema->addLeaf("", "leafDecimal", yang::LeafDataTypes::Decimal);
diff --git a/tests/presence_containers.cpp b/tests/presence_containers.cpp
index 0a678fb..87957cb 100644
--- a/tests/presence_containers.cpp
+++ b/tests/presence_containers.cpp
@@ -10,11 +10,11 @@
 #include "trompeloeil_catch.h"
 #include "ast_commands.hpp"
 #include "parser.hpp"
-#include "schema.hpp"
+#include "static_schema.hpp"
 
 TEST_CASE("presence containers")
 {
-    auto schema = std::make_shared<Schema>();
+    auto schema = std::make_shared<StaticSchema>();
     schema->addContainer("", "a", yang::ContainerTraits::Presence);
     schema->addContainer("", "b");
     schema->addContainer("a", "a2");