#ifndef INCLUDED_UTIL_CONFIG_H #define INCLUDED_UTIL_CONFIG_H #include "Util/GenericValue.h" #include #include #include #include namespace Util { namespace Config { class Node { private: typedef std::shared_ptr ptr_t; typedef std::shared_ptr const_ptr_t; const std::string m_key; // this cannot be changed (maps depend on it) std::shared_ptr 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 m_children; mutable std::map 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 { 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 { 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(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 GetValue() const { return m_value; } inline void SetValue(const std::shared_ptr &value) { m_value = value; } template const T &Value() const { CheckEmptyOrMissing(); return m_value->Value(); } template T ValueAs() const { CheckEmptyOrMissing(); return m_value->ValueAs(); } template T ValueAsDefault(const T &default_value) const { if (Empty()) return default_value; return m_value->ValueAs(); } // Set value of this node template inline void SetValue(const T &value) { if (!m_missing) { if (!Empty() && m_value->Is()) m_value->Set(value); else m_value = std::make_shared>(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(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 Node &Add(const std::string &path, const T &value) { std::vector 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(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 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) noexcept; Node(const std::string &key); Node(const std::string &key, const std::string &value); Node(const Node &that); Node(Node&& that) noexcept; ~Node(); }; } // Config } // Util #endif // INCLUDED_UTIL_CONFIG_H