Merge pathToAbsoluteSchemaString and pathToSchemaString

These functions did almost the same thing, the only difference was that
the former inserted module prefixes everyhwere and the other didn't.
The new function takes an argument specifying whether to insert prefixes
everyhwere.

pathToSchemaString also checks whether a path is absolute or relative
and prepends a slash. This means, that it's no longer possible to
convert and absolute path to a string with no leading slash. The
StaticSchema class had to be changed accordingly, because it stored
absolute paths in strings without a leading slash (because it was
designed before absolute paths existed).

The fact that this was split into two functions helped find an error in
a later patch.

Change-Id: I6112cb5982919b51b61152f355771a52dd467c6e
diff --git a/src/ast_path.cpp b/src/ast_path.cpp
index 3a486a5..bdb3447 100644
--- a/src/ast_path.cpp
+++ b/src/ast_path.cpp
@@ -158,58 +158,42 @@
     return boost::apply_visitor(nodeToSchemaStringVisitor(), node.m_suffix);
 }
 
-std::string pathToDataString(const dataPath_& path)
+std::string pathToDataString(const dataPath_& path, Prefixes prefixes)
 {
     std::string res;
     if (path.m_scope == Scope::Absolute) {
         res = "/";
     }
-    for (const auto it : path.m_nodes)
+
+    for (const auto it : path.m_nodes) {
         if (it.m_prefix)
             res = joinPaths(res, it.m_prefix.value().m_name + ":" + boost::apply_visitor(nodeToDataStringVisitor(), it.m_suffix));
         else
-            res = joinPaths(res, boost::apply_visitor(nodeToDataStringVisitor(), it.m_suffix));
+            res = joinPaths(res, (prefixes == Prefixes::Always ? path.m_nodes.at(0).m_prefix.value().m_name + ":" : "") + boost::apply_visitor(nodeToDataStringVisitor(), it.m_suffix));
+    }
 
     return res;
 }
 
-std::string pathToAbsoluteSchemaString(const dataPath_& path)
-{
-    return pathToAbsoluteSchemaString(dataPathToSchemaPath(path));
-}
-
-std::string pathToAbsoluteSchemaString(const schemaPath_& path)
+std::string pathToSchemaString(const schemaPath_& path, Prefixes prefixes)
 {
     std::string res;
-    if (path.m_nodes.empty()) {
-        return "";
+    if (path.m_scope == Scope::Absolute) {
+        res = "/";
     }
 
-    auto topLevelModule = path.m_nodes.at(0).m_prefix.value();
     for (const auto it : path.m_nodes) {
         if (it.m_prefix)
             res = joinPaths(res, it.m_prefix.value().m_name + ":" + boost::apply_visitor(nodeToSchemaStringVisitor(), it.m_suffix));
         else
-            res = joinPaths(res, topLevelModule.m_name + ":" + boost::apply_visitor(nodeToSchemaStringVisitor(), it.m_suffix));
+            res = joinPaths(res, (prefixes == Prefixes::Always ? path.m_nodes.at(0).m_prefix.value().m_name + ":" : "") + boost::apply_visitor(nodeToSchemaStringVisitor(), it.m_suffix));
     }
     return res;
 }
 
-std::string pathToSchemaString(const schemaPath_& path)
+std::string pathToSchemaString(const dataPath_& path, Prefixes prefixes)
 {
-    std::string res;
-    for (const auto it : path.m_nodes) {
-        if (it.m_prefix)
-            res = joinPaths(res, it.m_prefix.value().m_name + ":" + boost::apply_visitor(nodeToSchemaStringVisitor(), it.m_suffix));
-        else
-            res = joinPaths(res, boost::apply_visitor(nodeToSchemaStringVisitor(), it.m_suffix));
-    }
-    return res;
-}
-
-std::string pathToSchemaString(const dataPath_& path)
-{
-    return pathToSchemaString(dataPathToSchemaPath(path));
+    return pathToSchemaString(dataPathToSchemaPath(path), prefixes);
 }
 
 struct dataSuffixToSchemaSuffix : boost::static_visitor<decltype(schemaNode_::m_suffix)> {
diff --git a/src/ast_path.hpp b/src/ast_path.hpp
index c15351f..338c766 100644
--- a/src/ast_path.hpp
+++ b/src/ast_path.hpp
@@ -16,6 +16,11 @@
 
 #include "ast_values.hpp"
 
+enum class Prefixes {
+    Always,
+    WhenNeeded
+};
+
 struct nodeup_ {
     bool operator==(const nodeup_&) const
     {
@@ -106,10 +111,9 @@
 
 std::string nodeToSchemaString(decltype(dataPath_::m_nodes)::value_type node);
 
-std::string pathToAbsoluteSchemaString(const dataPath_& path);
-std::string pathToAbsoluteSchemaString(const schemaPath_& path);
-std::string pathToDataString(const dataPath_& path);
-std::string pathToSchemaString(const schemaPath_& path);
+std::string pathToDataString(const dataPath_& path, Prefixes prefixes);
+std::string pathToSchemaString(const schemaPath_& path, Prefixes prefixes);
+std::string pathToSchemaString(const dataPath_& path, Prefixes prefixes);
 schemaNode_ dataNodeToSchemaNode(const dataNode_& node);
 schemaPath_ dataPathToSchemaPath(const dataPath_& path);
 std::string escapeListKeyString(const std::string& what);
diff --git a/src/interpreter.cpp b/src/interpreter.cpp
index 61604bb..f112b52 100644
--- a/src/interpreter.cpp
+++ b/src/interpreter.cpp
@@ -109,19 +109,19 @@
 std::string Interpreter::absolutePathFromCommand(const T& command) const
 {
     if (command.m_path.m_scope == Scope::Absolute)
-        return pathToDataString(command.m_path);
+        return pathToDataString(command.m_path, Prefixes::WhenNeeded);
     else
-        return joinPaths(m_parser.currentNode(), pathToDataString(command.m_path));
+        return joinPaths(m_parser.currentNode(), pathToDataString(command.m_path, Prefixes::WhenNeeded));
 }
 
 struct pathToStringVisitor : boost::static_visitor<std::string> {
     std::string operator()(const schemaPath_& path) const
     {
-        return pathToSchemaString(path);
+        return pathToSchemaString(path, Prefixes::WhenNeeded);
     }
     std::string operator()(const dataPath_& path) const
     {
-        return pathToDataString(path);
+        return pathToDataString(path, Prefixes::WhenNeeded);
     }
 };
 
diff --git a/src/parser.cpp b/src/parser.cpp
index c7644e2..ce6ea01 100644
--- a/src/parser.cpp
+++ b/src/parser.cpp
@@ -80,7 +80,7 @@
 
 std::string Parser::currentNode() const
 {
-    return pathToDataString(m_curDir);
+    return pathToDataString(m_curDir, Prefixes::WhenNeeded);
 }
 
 struct getSchemaPathVisitor : boost::static_visitor<schemaPath_> {
diff --git a/src/schema.hpp b/src/schema.hpp
index b8d74a8..8b7031a 100644
--- a/src/schema.hpp
+++ b/src/schema.hpp
@@ -42,11 +42,6 @@
     Recursive
 };
 
-enum class Prefixes {
-    Always,
-    WhenNeeded
-};
-
 
 class InvalidNodeException : public std::invalid_argument {
 public:
diff --git a/src/static_schema.cpp b/src/static_schema.cpp
index ad2025c..47e0884 100644
--- a/src/static_schema.cpp
+++ b/src/static_schema.cpp
@@ -14,7 +14,7 @@
 
 StaticSchema::StaticSchema()
 {
-    m_nodes.emplace("", std::unordered_map<std::string, NodeType>());
+    m_nodes.emplace("/", std::unordered_map<std::string, NodeType>());
 }
 
 const std::unordered_map<std::string, NodeType>& StaticSchema::children(const std::string& name) const
@@ -38,7 +38,7 @@
 
 bool StaticSchema::isContainer(const schemaPath_& location, const ModuleNodePair& node) const
 {
-    std::string locationString = pathToAbsoluteSchemaString(location);
+    std::string locationString = pathToSchemaString(location, Prefixes::Always);
     auto fullName = fullNodeName(location, node);
     if (!nodeExists(locationString, fullName))
         return false;
@@ -57,7 +57,7 @@
 
 bool StaticSchema::listHasKey(const schemaPath_& location, const ModuleNodePair& node, const std::string& key) const
 {
-    std::string locationString = pathToAbsoluteSchemaString(location);
+    std::string locationString = pathToSchemaString(location, Prefixes::Always);
     assert(isList(location, node));
 
     const auto& child = children(locationString).at(fullNodeName(location, node));
@@ -67,7 +67,7 @@
 
 const std::set<std::string> StaticSchema::listKeys(const schemaPath_& location, const ModuleNodePair& node) const
 {
-    std::string locationString = pathToAbsoluteSchemaString(location);
+    std::string locationString = pathToSchemaString(location, Prefixes::Always);
     assert(isList(location, node));
 
     const auto& child = children(locationString).at(fullNodeName(location, node));
@@ -77,7 +77,7 @@
 
 bool StaticSchema::isList(const schemaPath_& location, const ModuleNodePair& node) const
 {
-    std::string locationString = pathToAbsoluteSchemaString(location);
+    std::string locationString = pathToSchemaString(location, Prefixes::Always);
     auto fullName = fullNodeName(location, node);
     if (!nodeExists(locationString, fullName))
         return false;
@@ -100,7 +100,7 @@
 {
     if (!isContainer(location, node))
         return false;
-    std::string locationString = pathToAbsoluteSchemaString(location);
+    std::string locationString = pathToSchemaString(location, Prefixes::Always);
     return boost::get<yang::container>(children(locationString).at(fullNodeName(location, node))).m_presence == yang::ContainerTraits::Presence;
 }
 
@@ -172,7 +172,7 @@
 
 const std::set<std::string> StaticSchema::validIdentities(const schemaPath_& location, const ModuleNodePair& node, const Prefixes prefixes) const
 {
-    std::string locationString = pathToAbsoluteSchemaString(location);
+    std::string locationString = pathToSchemaString(location, Prefixes::Always);
     assert(isLeaf(location, node));
 
     const auto& child = children(locationString).at(fullNodeName(location, node));
@@ -207,7 +207,7 @@
 
 bool StaticSchema::isLeaf(const schemaPath_& location, const ModuleNodePair& node) const
 {
-    std::string locationString = pathToAbsoluteSchemaString(location);
+    std::string locationString = pathToSchemaString(location, Prefixes::Always);
     auto fullName = fullNodeName(location, node);
     if (!nodeExists(locationString, fullName))
         return false;
@@ -219,14 +219,22 @@
 {
     std::string res = path;
     auto pos = res.find_last_of('/');
-    if (pos != res.npos)
+    if (pos == 0) { // path had only one path fragment - "/something:something"
+        res.erase(0, 1);
+        return res;
+    }
+    if (pos != res.npos) { // path had more fragments
         res.erase(0, pos);
+        return res;
+    }
+
+    // path was empty
     return res;
 }
 
 yang::LeafDataTypes StaticSchema::leafrefBase(const schemaPath_& location, const ModuleNodePair& node) const
 {
-    std::string locationString = pathToAbsoluteSchemaString(location);
+    std::string locationString = pathToSchemaString(location, Prefixes::Always);
     auto leaf{boost::get<yang::leaf>(children(locationString).at(fullNodeName(location, node)))};
     auto locationOfSource = stripLastNodeFromPath(leaf.m_leafRefSource);
     auto nameOfSource = lastNodeOfSchemaPath(leaf.m_leafRefSource);
@@ -235,13 +243,13 @@
 
 yang::LeafDataTypes StaticSchema::leafType(const schemaPath_& location, const ModuleNodePair& node) const
 {
-    std::string locationString = pathToAbsoluteSchemaString(location);
+    std::string locationString = pathToSchemaString(location, Prefixes::Always);
     return boost::get<yang::leaf>(children(locationString).at(fullNodeName(location, node))).m_type;
 }
 
 const std::set<std::string> StaticSchema::enumValues(const schemaPath_& location, const ModuleNodePair& node) const
 {
-    std::string locationString = pathToAbsoluteSchemaString(location);
+    std::string locationString = pathToSchemaString(location, Prefixes::Always);
     assert(isLeaf(location, node));
 
     const auto& child = children(locationString).at(fullNodeName(location, node));
@@ -253,7 +261,7 @@
 // for this class.
 std::set<std::string> StaticSchema::childNodes(const schemaPath_& path, const Recursion) const
 {
-    std::string locationString = pathToAbsoluteSchemaString(path);
+    std::string locationString = pathToSchemaString(path, Prefixes::Always);
     std::set<std::string> res;
 
     auto childrenRef = children(locationString);
diff --git a/src/utils.cpp b/src/utils.cpp
index eb4f432..cd0858f 100644
--- a/src/utils.cpp
+++ b/src/utils.cpp
@@ -21,10 +21,13 @@
 {
     std::string res = path;
     auto pos = res.find_last_of('/');
-    if (pos == res.npos)
+    if (pos == res.npos) { // path has no backslash - it's either empty, or is a relative path with one fragment
         res.clear();
-    else
+    } else if (pos == 0) { // path has one backslash at the start - it's either "/" or "/one-path-fragment"
+        return "/";
+    } else {
         res.erase(pos);
+    }
     return res;
 }
 
diff --git a/src/yang_schema.cpp b/src/yang_schema.cpp
index 5de3b9e..06259b9 100644
--- a/src/yang_schema.cpp
+++ b/src/yang_schema.cpp
@@ -220,8 +220,7 @@
 
 libyang::S_Schema_Node YangSchema::getSchemaNode(const schemaPath_& location, const ModuleNodePair& node) const
 {
-    std::string absPath = location.m_nodes.empty() ? "" : "/";
-    absPath += pathToAbsoluteSchemaString(location) + "/" + fullNodeName(location, node);
+    std::string absPath = joinPaths(pathToSchemaString(location, Prefixes::Always), fullNodeName(location, node));
 
     return impl_getSchemaNode(absPath);
 }
@@ -321,8 +320,8 @@
     if (path.m_nodes.empty()) {
         nodes = m_context->data_instantiables(0);
     } else {
-        const auto absolutePath = "/" + pathToAbsoluteSchemaString(path);
-        const auto node = getSchemaNode(absolutePath);
+        const auto pathString = pathToSchemaString(path, Prefixes::Always);
+        const auto node = getSchemaNode(pathString);
         nodes = node->child_instantiables(0);
     }