Supermodel/Src/Util/NewConfig.h

319 lines
8.8 KiB
C++

#ifndef INCLUDED_UTIL_CONFIG_H
#define INCLUDED_UTIL_CONFIG_H
#include "Util/GenericValue.h"
#include <map>
#include <memory>
#include <iterator>
#include <exception>
namespace Util
{
namespace Config
{
class Node
{
private:
typedef std::shared_ptr<Node> ptr_t;
typedef std::shared_ptr<const Node> const_ptr_t;
const std::string m_key; // this cannot be changed (maps depend on it)
std::shared_ptr<GenericValue> m_value; // null pointer marks this node as an invalid, empty node
ptr_t m_next_sibling;
ptr_t m_first_child;
ptr_t m_last_child;
std::map<std::string, ptr_t> m_children;
mutable std::map<std::string, Node> m_missing_nodes; // missing nodes from failed queries (must also be empty)
bool m_missing = false;
void Destroy()
{
m_value.reset();
m_next_sibling.reset();
m_first_child.reset();
m_last_child.reset();
m_children.clear();
}
void CheckEmptyOrMissing() const;
const Node &MissingNode(const std::string &key) const;
void AddChild(Node &parent, ptr_t &node);
void DeepCopy(const Node &that);
void Swap(Node &rhs);
Node(); // prohibit accidental/unintentional creation of empty nodes
public:
class const_iterator;
class iterator: public std::iterator<std::forward_iterator_tag, Node>
{
private:
ptr_t m_node;
friend class const_iterator;
public:
inline iterator(ptr_t node = ptr_t())
: m_node(node)
{}
inline iterator(const iterator &it)
: m_node(it.m_node)
{}
inline iterator operator++()
{
// Prefix increment
m_node = m_node->m_next_sibling;
return *this;
}
inline iterator operator++(int)
{
// Postfix increment
iterator current(*this);
m_node = m_node->m_next_sibling;
return *this;
}
inline Node &operator*() const
{
return *m_node;
}
inline ptr_t operator->() const
{
return m_node;
}
inline bool operator==(const iterator &rhs) const
{
return m_node == rhs.m_node;
}
inline bool operator!=(const iterator &rhs) const
{
return m_node != rhs.m_node;
}
};
class const_iterator: public std::iterator<std::forward_iterator_tag, const Node>
{
private:
const_ptr_t m_node;
public:
inline const_iterator()
: m_node(const_ptr_t())
{}
inline const_iterator(const_ptr_t node)
: m_node(node)
{}
inline const_iterator(ptr_t node)
: m_node(std::const_pointer_cast<const Node>(node))
{}
inline const_iterator(const const_iterator &it)
: m_node(it.m_node)
{}
inline const_iterator(const Node::iterator &it)
: m_node(it.m_node)
{}
inline const_iterator operator++()
{
// Prefix increment
m_node = m_node->m_next_sibling;
return *this;
}
inline const_iterator operator++(int)
{
// Postfix increment
//iterator current(*this); //unreferenced local variable
m_node = m_node->m_next_sibling;
return *this;
}
inline const Node &operator*() const
{
return *m_node;
}
inline const_ptr_t operator->() const
{
return m_node;
}
inline bool operator==(const const_iterator &rhs) const
{
return m_node == rhs.m_node;
}
inline bool operator!=(const const_iterator &rhs) const
{
return m_node != rhs.m_node;
}
};
inline iterator begin()
{
return iterator(m_first_child);
}
inline iterator end()
{
return iterator();
}
inline const_iterator begin() const
{
return iterator(m_first_child);
}
inline const_iterator end() const
{
return iterator();
}
const inline std::string &Key() const
{
return m_key;
}
inline std::shared_ptr<GenericValue> GetValue() const
{
return m_value;
}
inline void SetValue(const std::shared_ptr<GenericValue> &value)
{
m_value = value;
}
template <typename T>
const T &Value() const
{
CheckEmptyOrMissing();
return m_value->Value<T>();
}
template <typename T>
T ValueAs() const
{
CheckEmptyOrMissing();
return m_value->ValueAs<T>();
}
template <typename T>
T ValueAsDefault(const T &default_value) const
{
if (Empty())
return default_value;
return m_value->ValueAs<T>();
}
// Set value of this node
template <typename T>
inline void SetValue(const T &value)
{
if (!m_missing)
{
if (!Empty() && m_value->Is<T>())
m_value->Set(value);
else
m_value = std::make_shared<ValueInstance<T>>(value);
}
else
throw std::range_error(Util::Format() << "Node \"" << m_key << "\" does not exist");
}
// char* is a troublesome case because we want to convert it to std::string
inline void SetValue(const char *value)
{
SetValue<std::string>(value);
}
// Add a child node. Multiple nodes of the same key may be added but only
// when specified as leaves. For example, adding "foo/bar" twice will
// result in one "foo" with two "bar" children.
template <typename T>
Node &Add(const std::string &path, const T &value)
{
std::vector<std::string> keys = Util::Format(path).Split('/');
Node *parent = this;
ptr_t node;
for (size_t i = 0; i < keys.size(); i++)
{
bool leaf = i == keys.size() - 1;
auto it = parent->m_children.find(keys[i]);
if (leaf || it == parent->m_children.end())
{
// Create node at this level
node = std::make_shared<Node>(keys[i]);
// The leaf node gets the value
if (leaf)
node->SetValue(value);
// Attach node to parent and move down to next nesting level: last
// created node is new parent
AddChild(*parent, node);
parent = node.get();
}
else
{
// Descend deeper...
parent = it->second.get();
}
}
return *node;
}
Node &Add(const std::string &path)
{
return Add(path, std::string());
}
// Set the value of the matching child node if it exists, else add new
template <typename T>
void Set(const std::string &key, const T &value)
{
Node *node = TryGet(key);
if (node)
node->SetValue(value);
else
Add(key, value);
}
// True if value is empty (does not exist)
inline bool Empty() const
{
return !m_value;
}
// True if value exists (is not empty)
inline bool Exists() const
{
return !Empty();
}
// True if no keys under this node
inline bool IsLeaf() const
{
return m_children.empty();
}
// True if this node has keys
inline bool HasChildren() const
{
return !IsLeaf();
}
// Always succeeds -- failed lookups permanently create an empty node.
// Use with caution. Intended for hard-coded lookups.
const Node &operator[](const std::string &path) const;
// These throw if the node is missing
Node &Get(const std::string &path);
const Node &Get(const std::string &path) const;
// This returns nullptr if node is missing
Node *TryGet(const std::string &path);
const Node *TryGet(const std::string &path) const;
void Serialize(std::ostream *os, size_t indent_level = 0) const;
std::string ToString(size_t indent_level = 0) const;
Node &operator=(const Node &rhs);
Node &operator=(Node &&rhs);
Node(const std::string &key);
Node(const std::string &key, const std::string &value);
Node(const Node &that);
Node(Node &&that);
~Node();
};
} // Config
} // Util
#endif // INCLUDED_UTIL_CONFIG_H