blob: 9c77f351e931cc23b3e6a834cba210ec2739c5ba [file] [log] [blame]
/*
* 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 <experimental/iterator>
#include <sstream>
#include "ast_path.hpp"
#include "utils.hpp"
container_::container_(const std::string& name)
: m_name(name)
{
}
bool container_::operator==(const container_& b) const
{
return this->m_name == b.m_name;
}
leaf_::leaf_(const std::string& name)
: m_name(name)
{
}
bool leafListElement_::operator==(const leafListElement_& b) const
{
return this->m_name == b.m_name && this->m_value == b.m_value;
}
leafList_::leafList_(const std::string& name)
: m_name(name)
{
}
bool leafList_::operator==(const leafList_& b) const
{
return this->m_name == b.m_name;
}
bool module_::operator==(const module_& b) const
{
return this->m_name == b.m_name;
}
dataNode_::dataNode_() = default;
dataNode_::dataNode_(decltype(m_suffix) node)
: m_suffix(node)
{
}
dataNode_::dataNode_(module_ module, decltype(m_suffix) node)
: m_prefix(module)
, m_suffix(node)
{
}
dataNode_::dataNode_(boost::optional<module_> module, decltype(m_suffix) node)
: m_prefix(module)
, m_suffix(node)
{
}
schemaNode_::schemaNode_(decltype(m_suffix) node)
: m_suffix(node)
{
}
schemaNode_::schemaNode_(module_ module, decltype(m_suffix) node)
: m_prefix(module)
, m_suffix(node)
{
}
schemaNode_::schemaNode_() = default;
bool schemaNode_::operator==(const schemaNode_& b) const
{
return this->m_suffix == b.m_suffix && this->m_prefix == b.m_prefix;
}
bool dataNode_::operator==(const dataNode_& b) const
{
return this->m_suffix == b.m_suffix && this->m_prefix == b.m_prefix;
}
bool leaf_::operator==(const leaf_& b) const
{
return this->m_name == b.m_name;
}
listElement_::listElement_(const std::string& listName, const ListInstance& keys)
: m_name(listName)
, m_keys(keys)
{
}
bool listElement_::operator==(const listElement_& b) const
{
return (this->m_name == b.m_name && this->m_keys == b.m_keys);
}
bool list_::operator==(const list_& b) const
{
return (this->m_name == b.m_name);
}
bool rpcNode_::operator==(const rpcNode_& other) const
{
return this->m_name == other.m_name;
}
bool actionNode_::operator==(const actionNode_& other) const
{
return this->m_name == other.m_name;
}
list_::list_(const std::string& listName)
: m_name(listName)
{
}
namespace {
template <typename T, typename U>
auto findFirstOf(const std::vector<U>& nodes)
{
return std::find_if(nodes.begin(), nodes.end(), [](const auto& e) {
return std::holds_alternative<T>(e.m_suffix);
});
}
template <typename T>
void validatePathNodes(const std::vector<T>& nodes)
{
static_assert(std::is_same<T, dataNode_>() || std::is_same<T, schemaNode_>());
if (nodes.empty()) {
// there are default ctors, so it makes sense to specify the same thing via explicit args and not fail
return;
}
if (auto firstLeaf = findFirstOf<leaf_>(nodes);
firstLeaf != nodes.end() && firstLeaf != nodes.end() - 1) {
throw std::logic_error{"Cannot put any extra nodes after a leaf"};
}
if (auto firstLeafList = findFirstOf<leafList_>(nodes);
firstLeafList != nodes.end() && firstLeafList != nodes.end() - 1) {
throw std::logic_error{"Cannot put any extra nodes after a leaf-list"};
}
if constexpr (std::is_same<T, dataNode_>()) {
if (auto firstLeafListElements = findFirstOf<leafListElement_>(nodes);
firstLeafListElements != nodes.end() && firstLeafListElements != nodes.end() - 1) {
throw std::logic_error{"Cannot put any extra nodes after a leaf-list with element specification"};
}
if (auto firstList = findFirstOf<list_>(nodes);
firstList != nodes.end() && firstList != nodes.end() - 1) {
throw std::logic_error{
"A list with no key specification can be present only as a last item in a dataPath. Did you mean to use a schemaPath?"
};
}
}
}
}
schemaPath_::schemaPath_() = default;
schemaPath_::schemaPath_(const Scope scope, const std::vector<schemaNode_>& nodes, const TrailingSlash trailingSlash)
: m_scope(scope)
, m_nodes(nodes)
, m_trailingSlash(trailingSlash)
{
validatePathNodes(m_nodes);
}
bool schemaPath_::operator==(const schemaPath_& b) const
{
if (this->m_nodes.size() != b.m_nodes.size()) {
return false;
}
return this->m_nodes == b.m_nodes;
}
dataPath_::dataPath_() = default;
dataPath_::dataPath_(const Scope scope, const std::vector<dataNode_>& nodes, const TrailingSlash trailingSlash)
: m_scope(scope)
, m_nodes(nodes)
, m_trailingSlash(trailingSlash)
{
validatePathNodes(m_nodes);
}
bool dataPath_::operator==(const dataPath_& b) const
{
if (this->m_nodes.size() != b.m_nodes.size()) {
return false;
}
return this->m_nodes == b.m_nodes;
}
struct nodeToSchemaStringVisitor {
std::string operator()(const nodeup_&) const
{
return "..";
}
template <class T>
std::string operator()(const T& node) const
{
return node.m_name;
}
};
std::string escapeListKeyString(const std::string& what)
{
// If we have both single and double quote, then we're screwed, but that "shouldn't happen"
// in <= YANG 1.1 due to limitations in XPath 1.0.
if (what.find('\'') != std::string::npos) {
return '\"' + what + '\"';
} else {
return '\'' + what + '\'';
}
}
struct nodeToDataStringVisitor {
std::string operator()(const listElement_& node) const
{
std::ostringstream res;
res << node.m_name + "[";
std::transform(node.m_keys.begin(), node.m_keys.end(),
std::experimental::make_ostream_joiner(res, "]["),
[] (const auto& it) { return it.first + "=" + escapeListKeyString(leafDataToString(it.second)); });
res << "]";
return res.str();
}
std::string operator()(const leafListElement_& node) const
{
return node.m_name + "[.=" + escapeListKeyString(leafDataToString(node.m_value)) + "]";
}
std::string operator()(const nodeup_&) const
{
return "..";
}
template <class T>
std::string operator()(const T& node) const
{
return node.m_name;
}
};
std::string nodeToSchemaString(decltype(dataPath_::m_nodes)::value_type node)
{
return std::visit(nodeToSchemaStringVisitor(), node.m_suffix);
}
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) {
if (it.m_prefix) {
res = joinPaths(res, it.m_prefix.value().m_name + ":" + std::visit(nodeToDataStringVisitor(), it.m_suffix));
} else {
res = joinPaths(res, (prefixes == Prefixes::Always ? path.m_nodes.at(0).m_prefix.value().m_name + ":" : "") + std::visit(nodeToDataStringVisitor(), it.m_suffix));
}
}
if (path.m_trailingSlash == TrailingSlash::Present) {
res += "/";
}
return res;
}
std::string pathToSchemaString(const schemaPath_& path, Prefixes prefixes)
{
std::string res;
if (path.m_scope == Scope::Absolute) {
res = "/";
}
for (const auto& it : path.m_nodes) {
if (it.m_prefix) {
res = joinPaths(res, it.m_prefix.value().m_name + ":" + std::visit(nodeToSchemaStringVisitor(), it.m_suffix));
} else {
res = joinPaths(res, (prefixes == Prefixes::Always ? path.m_nodes.at(0).m_prefix.value().m_name + ":" : "") + std::visit(nodeToSchemaStringVisitor(), it.m_suffix));
}
}
return res;
}
std::string pathToSchemaString(const dataPath_& path, Prefixes prefixes)
{
return pathToSchemaString(dataPathToSchemaPath(path), prefixes);
}
struct dataSuffixToSchemaSuffix {
using ReturnType = decltype(schemaNode_::m_suffix);
ReturnType operator()(const listElement_& listElement) const
{
return list_{listElement.m_name};
}
ReturnType operator()(const leafListElement_& leafListElement) const
{
return leafList_{leafListElement.m_name};
}
template <typename T>
ReturnType operator()(const T& suffix) const
{
return suffix;
}
};
schemaNode_ dataNodeToSchemaNode(const dataNode_& node)
{
schemaNode_ res;
res.m_prefix = node.m_prefix;
res.m_suffix = std::visit(dataSuffixToSchemaSuffix(), node.m_suffix);
return res;
}
schemaPath_ dataPathToSchemaPath(const dataPath_& path)
{
schemaPath_ res{path.m_scope, {}};
std::transform(path.m_nodes.begin(), path.m_nodes.end(),
std::back_inserter(res.m_nodes),
[](const dataNode_& node) { return dataNodeToSchemaNode(node); });
return res;
}
namespace {
template <typename NodeType>
void impl_pushFragment(std::vector<NodeType>& where, const NodeType& what)
{
if (std::holds_alternative<nodeup_>(what.m_suffix)) {
if (!where.empty()) { // Allow going up, when already at root
where.pop_back();
}
} else {
where.emplace_back(what);
}
}
}
void schemaPath_::pushFragment(const schemaNode_& fragment)
{
impl_pushFragment(m_nodes, fragment);
validatePathNodes(m_nodes);
}
void dataPath_::pushFragment(const dataNode_& fragment)
{
impl_pushFragment(m_nodes, fragment);
validatePathNodes(m_nodes);
}