mirror of
https://github.com/RetroDECK/Supermodel.git
synced 2024-11-22 05:45:38 +00:00
Config nodes: added the ability to clear out node values or create empty leaf nodes
This commit is contained in:
parent
043f901c80
commit
83144f80b7
|
@ -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<T>()
|
||||
|
@ -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 <iostream>
|
||||
|
||||
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<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 and leave it empty
|
||||
node = std::make_shared<Node>(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<std::string *>(&m_key)->swap(*const_cast<std::string *>(&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<char>(std::cout), tab_stops * indent_level, ' ');
|
||||
std::cout << config.Key();
|
||||
if (config.Exists())
|
||||
{
|
||||
std::cout << " = " << config.ValueAs<std::string>();
|
||||
}
|
||||
std::cout << std::endl;
|
||||
for (const Node &child: config)
|
||||
{
|
||||
PrintConfigTree(child, indent_level + 1, tab_stops);
|
||||
}
|
||||
}
|
||||
} // Config
|
||||
} // Util
|
||||
|
|
|
@ -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<GenericValue> &value)
|
||||
{
|
||||
m_value = value;
|
||||
|
@ -222,32 +228,9 @@ namespace Util
|
|||
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 &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
|
||||
|
||||
|
|
Loading…
Reference in a new issue