diff --git a/Src/Util/NewConfig.cpp b/Src/Util/NewConfig.cpp index 015414a..37826d8 100644 --- a/Src/Util/NewConfig.cpp +++ b/Src/Util/NewConfig.cpp @@ -7,7 +7,7 @@ * * A hierarchical data structure supporting arbitrary nesting. Each node * (Config::Node) has a key and either a value or children (in fact, it may - * have both, but this rarely makes semantic sense and so the config tree + * have both, but this rarely makes semantic sense and so the config tree * builders take care not to allow it). * * Nesting is denoted with the '/' separator. For example: @@ -20,10 +20,10 @@ * [1] accesses the value of the node (more on this below). [2] accesses the * a child node with key "foo". [3] accesses the value of the child node "foo". * [4] accesses child "bar" of child "foo", and so forth. - * + * * Similar to map semantics, the operator [] never fails. If the node does not * exist, it creates a dummy "missing" node that is retained as a hidden child. - * This node will have an empty value, which cannot be accessed, except using + * This node will have an empty value, which cannot be accessed, except using * the ValueAsDefault<> method. This scheme exists to simplify lookup code for * keys known at compile time, the logic being that any "missing" key should * could just as well have been there in the first place, thus making the added @@ -39,7 +39,7 @@ * * Nodes at the same nesting level (siblings) are strung together in a * linked list. Parents also maintain pointers to the first and last of their - * children (for order-preserving iteration) as well as a map for direct + * children (for order-preserving iteration) as well as a map for direct * lookup by key. * * Keys may be reused at a given level. Key-value pairs will have their order @@ -80,7 +80,7 @@ * * Conversions as supported but are implemented via serialization and de- * serialization using sstream. Most "sane" conversions will work as expected. - * When a conversion to T is desired, or if the stored value type is not + * When a conversion to T is desired, or if the stored value type is not * precisely known, use: * * node.ValueAs() @@ -103,7 +103,7 @@ * - Section nodes have their key set to the section name and value empty. * They are the only nodes that can have children (i.e., IsLeaf() == false, * HasChildren() == true). - * - Top-level node in the tree is the global section, and its key is + * - Top-level node in the tree is the global section, and its key is * "Global". * - Only the global section (top-level section) may have child nodes that are * sections. The config tree can therefore only be up to 2 levels deep: the @@ -133,7 +133,7 @@ * [ Section4, , Section5 ] * SettingX = bar * - * In this example, SettingX will be set to "foo" in Section1, Section2, and + * In this example, SettingX will be set to "foo" in Section1, Section2, and * Section3. It will be set to "bar" in Section4, Section5, and the "Global" * section because of the unnamed element. * @@ -147,6 +147,7 @@ */ #include "Util/NewConfig.h" +#include namespace Util { @@ -251,8 +252,36 @@ namespace Util return os.str(); } + // Adds an empty node (no value and where Empty() will return true) + Node &Node::AddEmpty(const std::string &path) + { + 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 and leave it empty + node = std::make_shared(keys[i]); + // 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; + } + // Adds a newly-created node (which, among other things, implies no - // children) as a child + // children) as a child void Node::AddChild(Node &parent, ptr_t &node) { if (!parent.m_last_child) @@ -264,7 +293,7 @@ namespace Util { parent.m_last_child->m_next_sibling = node; parent.m_last_child = node; - } + } parent.m_children[node->m_key] = node; } @@ -290,7 +319,7 @@ namespace Util m_last_child.swap(rhs.m_last_child); m_children.swap(rhs.m_children); const_cast(&m_key)->swap(*const_cast(&rhs.m_key)); - m_value.swap(rhs.m_value); + m_value.swap(rhs.m_value); } Node &Node::operator=(const Node &rhs) @@ -340,5 +369,20 @@ namespace Util { //std::cout << ">>> Destroyed " << m_key << " (" << this << ")" << std::endl; } + + void PrintConfigTree(const Node &config, int indent_level, int tab_stops) + { + std::fill_n(std::ostream_iterator(std::cout), tab_stops * indent_level, ' '); + std::cout << config.Key(); + if (config.Exists()) + { + std::cout << " = " << config.ValueAs(); + } + std::cout << std::endl; + for (const Node &child: config) + { + PrintConfigTree(child, indent_level + 1, tab_stops); + } + } } // Config } // Util diff --git a/Src/Util/NewConfig.h b/Src/Util/NewConfig.h index 4ca138e..c28e8f8 100644 --- a/Src/Util/NewConfig.h +++ b/Src/Util/NewConfig.h @@ -36,6 +36,7 @@ namespace Util void CheckEmptyOrMissing() const; const Node &MissingNode(const std::string &key) const; + Node &AddEmpty(const std::string &path); void AddChild(Node &parent, ptr_t &node); void DeepCopy(const Node &that); void Swap(Node &rhs); @@ -168,6 +169,11 @@ namespace Util return m_value; } + inline void Clear() + { + m_value = nullptr; + } + inline void SetValue(const std::shared_ptr &value) { m_value = value; @@ -222,32 +228,9 @@ namespace Util 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 &new_leaf_node = AddEmpty(path); + new_leaf_node.SetValue(value); + return new_leaf_node; } Node &Add(const std::string &path) @@ -266,6 +249,19 @@ namespace Util Add(key, value); } + void SetEmpty(const std::string &key) + { + Node *node = TryGet(key); + if (node) + { + node->Clear(); + } + else + { + AddEmpty(key); + } + } + // True if value is empty (does not exist) inline bool Empty() const { @@ -312,6 +308,8 @@ namespace Util Node(Node&& that) noexcept; ~Node(); }; + + void PrintConfigTree(const Node &config, int indent_level = 0, int tab_stops = 2); } // Config } // Util