Merge "Build with libyang"
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 1ba791a..cae063a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -58,6 +58,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 e498db0..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,23 +46,23 @@
                                true);
     std::cout << "Welcome to netconf-cli" << std::endl;
 
-    Schema schema;
-    schema.addContainer("", "a", yang::ContainerTraits::Presence);
-    schema.addContainer("", "b");
-    schema.addLeaf("", "leafString", yang::LeafDataTypes::String);
-    schema.addLeaf("", "leafDecimal", yang::LeafDataTypes::Decimal);
-    schema.addLeaf("", "leafBool", yang::LeafDataTypes::Bool);
-    schema.addLeaf("", "leafInt", yang::LeafDataTypes::Int);
-    schema.addLeaf("", "leafUint", yang::LeafDataTypes::Uint);
-    schema.addLeafEnum("", "leafEnum", {"lol", "data", "coze"});
-    schema.addContainer("a", "a2");
-    schema.addLeaf("a", "leafa", yang::LeafDataTypes::String);
-    schema.addContainer("b", "b2", yang::ContainerTraits::Presence);
-    schema.addContainer("a/a2", "a3", yang::ContainerTraits::Presence);
-    schema.addContainer("b/b2", "b3");
-    schema.addList("", "list", {"number"});
-    schema.addContainer("list", "contInList", yang::ContainerTraits::Presence);
-    schema.addList("", "twoKeyList", {"number", "name"});
+    auto schema = std::make_shared<StaticSchema>();
+    schema->addContainer("", "a", yang::ContainerTraits::Presence);
+    schema->addContainer("", "b");
+    schema->addLeaf("", "leafString", yang::LeafDataTypes::String);
+    schema->addLeaf("", "leafDecimal", yang::LeafDataTypes::Decimal);
+    schema->addLeaf("", "leafBool", yang::LeafDataTypes::Bool);
+    schema->addLeaf("", "leafInt", yang::LeafDataTypes::Int);
+    schema->addLeaf("", "leafUint", yang::LeafDataTypes::Uint);
+    schema->addLeafEnum("", "leafEnum", {"lol", "data", "coze"});
+    schema->addContainer("a", "a2");
+    schema->addLeaf("a", "leafa", yang::LeafDataTypes::String);
+    schema->addContainer("b", "b2", yang::ContainerTraits::Presence);
+    schema->addContainer("a/a2", "a3", yang::ContainerTraits::Presence);
+    schema->addContainer("b/b2", "b3");
+    schema->addList("", "list", {"number"});
+    schema->addContainer("list", "contInList", yang::ContainerTraits::Presence);
+    schema->addList("", "twoKeyList", {"number", "name"});
     Parser parser(schema);
 
     while (true) {
@@ -74,7 +74,7 @@
 
         try {
             command_ cmd = parser.parseCommand(input, std::cout);
-            boost::apply_visitor(Interpreter(parser, schema), cmd);
+            boost::apply_visitor(Interpreter(parser, *schema), cmd);
         } catch (InvalidCommandException& ex) {
             std::cerr << ex.what() << std::endl;
         }
diff --git a/src/parser.cpp b/src/parser.cpp
index e2d0330..0c62358 100644
--- a/src/parser.cpp
+++ b/src/parser.cpp
@@ -13,7 +13,7 @@
 InvalidCommandException::~InvalidCommandException() = default;
 
 
-Parser::Parser(const Schema& schema)
+Parser::Parser(const std::shared_ptr<const Schema> schema)
     : m_schema(schema)
 {
 }
@@ -21,7 +21,7 @@
 command_ Parser::parseCommand(const std::string& line, std::ostream& errorStream)
 {
     command_ parsedCommand;
-    ParserContext ctx(m_schema, m_curDir);
+    ParserContext ctx(*m_schema, m_curDir);
     auto it = line.begin();
 
     boost::spirit::x3::error_handler<std::string::const_iterator> errorHandler(it, line.end(), errorStream);
diff --git a/src/parser.hpp b/src/parser.hpp
index 0a253c1..05e636e 100644
--- a/src/parser.hpp
+++ b/src/parser.hpp
@@ -26,12 +26,12 @@
 
 class Parser {
 public:
-    Parser(const Schema& schema);
+    Parser(const std::shared_ptr<const Schema> schema);
     command_ parseCommand(const std::string& line, std::ostream& errorStream);
     void changeNode(const path_& name);
     std::string currentNode() const;
 
 private:
-    const Schema& m_schema;
+    const std::shared_ptr<const Schema> m_schema;
     path_ m_curDir;
 };
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 8fe30ad..29187ff 100644
--- a/tests/cd.cpp
+++ b/tests/cd.cpp
@@ -9,20 +9,20 @@
 #include "trompeloeil_catch.h"
 #include "ast_commands.hpp"
 #include "parser.hpp"
-#include "schema.hpp"
+#include "static_schema.hpp"
 
 TEST_CASE("cd")
 {
-    Schema schema;
-    schema.addContainer("", "a");
-    schema.addContainer("", "b");
-    schema.addContainer("a", "a2");
-    schema.addContainer("b", "b2");
-    schema.addContainer("a/a2", "a3");
-    schema.addContainer("b/b2", "b3");
-    schema.addList("", "list", {"number"});
-    schema.addContainer("list", "contInList");
-    schema.addList("", "twoKeyList", {"number", "name"});
+    auto schema = std::make_shared<StaticSchema>();
+    schema->addContainer("", "a");
+    schema->addContainer("", "b");
+    schema->addContainer("a", "a2");
+    schema->addContainer("b", "b2");
+    schema->addContainer("a/a2", "a3");
+    schema->addContainer("b/b2", "b3");
+    schema->addList("", "list", {"number"});
+    schema->addContainer("list", "contInList");
+    schema->addList("", "twoKeyList", {"number", "name"});
     Parser parser(schema);
     std::string input;
     std::ostringstream errorStream;
diff --git a/tests/leaf_editing.cpp b/tests/leaf_editing.cpp
index 09e8504..b8c5591 100644
--- a/tests/leaf_editing.cpp
+++ b/tests/leaf_editing.cpp
@@ -9,21 +9,21 @@
 #include "trompeloeil_catch.h"
 #include "ast_commands.hpp"
 #include "parser.hpp"
-#include "schema.hpp"
+#include "static_schema.hpp"
 
 TEST_CASE("leaf editing")
 {
-    Schema schema;
-    schema.addContainer("", "contA");
-    schema.addLeaf("", "leafString", yang::LeafDataTypes::String);
-    schema.addLeaf("", "leafDecimal", yang::LeafDataTypes::Decimal);
-    schema.addLeaf("", "leafBool", yang::LeafDataTypes::Bool);
-    schema.addLeaf("", "leafInt", yang::LeafDataTypes::Int);
-    schema.addLeaf("", "leafUint", yang::LeafDataTypes::Uint);
-    schema.addLeafEnum("", "leafEnum", {"lol", "data", "coze"});
-    schema.addLeaf("contA", "leafInCont", yang::LeafDataTypes::String);
-    schema.addList("", "list", {"number"});
-    schema.addLeaf("list", "leafInList", yang::LeafDataTypes::String);
+    auto schema = std::make_shared<StaticSchema>();
+    schema->addContainer("", "contA");
+    schema->addLeaf("", "leafString", yang::LeafDataTypes::String);
+    schema->addLeaf("", "leafDecimal", yang::LeafDataTypes::Decimal);
+    schema->addLeaf("", "leafBool", yang::LeafDataTypes::Bool);
+    schema->addLeaf("", "leafInt", yang::LeafDataTypes::Int);
+    schema->addLeaf("", "leafUint", yang::LeafDataTypes::Uint);
+    schema->addLeafEnum("", "leafEnum", {"lol", "data", "coze"});
+    schema->addLeaf("contA", "leafInCont", yang::LeafDataTypes::String);
+    schema->addList("", "list", {"number"});
+    schema->addLeaf("list", "leafInList", yang::LeafDataTypes::String);
     Parser parser(schema);
     std::string input;
     std::ostringstream errorStream;
diff --git a/tests/presence_containers.cpp b/tests/presence_containers.cpp
index 8958627..87957cb 100644
--- a/tests/presence_containers.cpp
+++ b/tests/presence_containers.cpp
@@ -10,18 +10,18 @@
 #include "trompeloeil_catch.h"
 #include "ast_commands.hpp"
 #include "parser.hpp"
-#include "schema.hpp"
+#include "static_schema.hpp"
 
 TEST_CASE("presence containers")
 {
-    Schema schema;
-    schema.addContainer("", "a", yang::ContainerTraits::Presence);
-    schema.addContainer("", "b");
-    schema.addContainer("a", "a2");
-    schema.addContainer("a/a2", "a3", yang::ContainerTraits::Presence);
-    schema.addContainer("b", "b2", yang::ContainerTraits::Presence);
-    schema.addList("", "list", {"quote"});
-    schema.addContainer("list", "contInList", yang::ContainerTraits::Presence);
+    auto schema = std::make_shared<StaticSchema>();
+    schema->addContainer("", "a", yang::ContainerTraits::Presence);
+    schema->addContainer("", "b");
+    schema->addContainer("a", "a2");
+    schema->addContainer("a/a2", "a3", yang::ContainerTraits::Presence);
+    schema->addContainer("b", "b2", yang::ContainerTraits::Presence);
+    schema->addList("", "list", {"quote"});
+    schema->addContainer("list", "contInList", yang::ContainerTraits::Presence);
     Parser parser(schema);
     std::string input;
     std::ostringstream errorStream;