diff --git a/Src/Util/NewConfig.cpp b/Src/Util/NewConfig.cpp index 65e9685..dec300d 100644 --- a/Src/Util/NewConfig.cpp +++ b/Src/Util/NewConfig.cpp @@ -95,6 +95,7 @@ namespace Util Node &Node::Get(const std::string &path) { + // This is probably dangerous and we should just have a non-const [] return const_cast(operator[](path)); } @@ -103,33 +104,49 @@ namespace Util return operator[](path); } - void Node::Print(size_t indent_level) const + std::string Node::ToString(size_t indent_level) const { - std::fill_n(std::ostream_iterator(std::cout), 2 * indent_level, ' '); - std::cout << m_key; + std::ostringstream os; + std::fill_n(std::ostream_iterator(os), 2 * indent_level, ' '); + os << m_key; if (m_value.length()) - std::cout << '=' << m_value; - std::cout << " children={"; + os << '=' << m_value; + os << " children={"; for (auto v: m_children) - std::cout << ' ' << v.first; - std::cout << " }" << std::endl; + os << ' ' << v.first; + os << " }" << std::endl; for (Ptr_t child = m_first_child; child; child = child->m_next_sibling) - child->Print(indent_level + 1); + os << child->ToString(indent_level + 1); + return os.str(); } - Node &Node::Create(const std::string &key) + void Node::Print(size_t indent_level) const + { + std::cout << ToString(indent_level); + } + + Node &Node::Add(const std::string &key) { Ptr_t node = std::make_shared(key); AddChild(node); return *node; } - Node &Node::Create(const std::string &key, const std::string &value) + Node &Node::Add(const std::string &key, const std::string &value) { Ptr_t node = std::make_shared(key, value); AddChild(node); return *node; } + + void Node::Set(const std::string &key, const std::string &value) + { + Node &node = Get(key); + if (node.Empty()) + Add(key, value); + else + node.SetValue(value); + } // Adds a newly-created node (which, among other things, implies no // children) as a child @@ -218,11 +235,11 @@ namespace Util q.pop(); // Create a config entry for this XML element - Util::Config::Node *node = &parent_node->Create(element->Name(), element->GetText() ? std::string(element->GetText()) : std::string()); + Util::Config::Node *node = &parent_node->Add(element->Name(), element->GetText() ? std::string(element->GetText()) : std::string()); // Create entries for each attribute for (const XMLAttribute *a = element->FirstAttribute(); a != 0; a = a->Next()) - node->Create(a->Name(), a->Value()); + node->Add(a->Name(), a->Value()); // Push all child elements for (const XMLElement *e = element->FirstChildElement(); e != 0; e = e->NextSiblingElement()) @@ -304,7 +321,7 @@ namespace Util // In INI files, we do not allow multiple settings with the same key. If // a setting is specified multiple times, previous ones are overwritten. if (current_section[lvalue].Empty()) - current_section.Create(lvalue, rvalue); + current_section.Add(lvalue, rvalue); else current_section.Get(lvalue).SetValue(rvalue); } @@ -347,7 +364,7 @@ namespace Util current_section = &global; else if (global[section].Empty()) { - Node &new_section = global.Create(section); + Node &new_section = global.Add(section); current_section = &new_section; } else @@ -388,10 +405,13 @@ namespace Util if (it->IsLeaf()) { // INI semantics: take care to only create a single setting per key + merged.Set(key, value); + /* if (merged[key].Empty()) - merged.Create(key, value); + merged.Add(key, value); else merged.Get(key).SetValue(value); + */ } } // Merge in settings from section y @@ -401,10 +421,13 @@ namespace Util auto &value = it->Value(); if (it->IsLeaf()) { + merged.Set(key, value); + /* if (merged[key].Empty()) - merged.Create(key, value); + merged.Add(key, value); else merged.Get(key).SetValue(value); + */ } } return merged_ptr; diff --git a/Src/Util/NewConfig.h b/Src/Util/NewConfig.h index c4324bb..2488f3f 100644 --- a/Src/Util/NewConfig.h +++ b/Src/Util/NewConfig.h @@ -186,11 +186,11 @@ namespace Util const Node &operator[](const std::string &path) const; const Node &Get(const std::string &path) const; void Print(size_t indent_level = 0) const; - //TODO: this API is confusing. Create() -> Add() and add a Set() which - // modifies existing settings if they exist (INI semantics should - // use this). - Node &Create(const std::string &key); - Node &Create(const std::string &key, const std::string &value); + std::string ToString(size_t indent_level = 0) const; + //TODO: Add() needs to accept and correctly handle nested keys (e.g., "foo/bar/baz") + Node &Add(const std::string &key); + Node &Add(const std::string &key, const std::string &value); + void Set(const std::string &key, const std::string &value); Node(const std::string &key); Node(const std::string &key, const std::string &value); Node(const Node &that); diff --git a/Src/Util/Test_Config.cpp b/Src/Util/Test_Config.cpp index 5ee7752..8f40361 100644 --- a/Src/Util/Test_Config.cpp +++ b/Src/Util/Test_Config.cpp @@ -1,8 +1,18 @@ #include "Util/NewConfig.h" #include +static void PrintTestResults(std::vector> results) +{ + std::cout << "TEST RESULTS" << std::endl; + std::cout << "------------" << std::endl; + for (auto v: results) + std::cout << v.first << ": " << (v.second ? "passed" : "FAILED") << std::endl; +} + int main() { + std::vector> test_results; + /* * Creates a config tree corresponding to the following: * @@ -21,41 +31,44 @@ int main() * */ Util::Config::Node::Ptr_t root = std::make_shared("global"); - auto &game = root->Create("game"); - game.Create("name", "scud"); - auto &roms = game.Create("roms"); - auto &crom1 = roms.Create("crom"); - crom1.Create("name", "foo.bin"); - crom1.Create("offset", "0"); - auto &crom2 = roms.Create("crom"); - crom2.Create("name", "bar.bin"); - crom2.Create("offset", "2"); + auto &game = root->Add("game"); + game.Add("name", "scud"); + auto &roms = game.Add("roms"); + auto &crom1 = roms.Add("crom"); + crom1.Add("name", "foo.bin"); + crom1.Add("offset", "0"); + auto &crom2 = roms.Add("crom"); + crom2.Add("name", "bar.bin"); + crom2.Add("offset", "2"); const char *expected_output = - "global children={ game } \n" - " game children={ name roms } \n" - " name=scud children={ } \n" - " roms children={ crom } \n" - " crom children={ name offset } \n" - " name=foo.bin children={ } \n" - " offset=0 children={ } \n" - " crom children={ name offset } \n" - " name=bar.bin children={ } \n" - " offset=2 children={ } \n"; + "global children={ game }\n" + " game children={ name roms }\n" + " name=scud children={ }\n" + " roms children={ crom }\n" + " crom children={ name offset }\n" + " name=foo.bin children={ }\n" + " offset=0 children={ }\n" + " crom children={ name offset }\n" + " name=bar.bin children={ }\n" + " offset=2 children={ }\n"; std::cout << "Expected output:" << std::endl << std::endl << expected_output << std::endl; std::cout << "Actual config tree:" << std::endl << std::endl; root->Print(); std::cout << std::endl; + test_results.push_back({ "Manual Creation", root->ToString() == expected_output }); // Expect second crom: bar.bin auto &global = *root; std::cout << "game/roms/crom/name=" << global["game/roms/crom/name"].Value() << " (expected: bar.bin)" << std::endl << std::endl; + test_results.push_back({ "Lookup", global["game/roms/crom/name"].Value() == "bar.bin" }); // Make a copy std::cout << "Copy:" << std::endl << std::endl; Util::Config::Node::Ptr_t copy = std::make_shared(*root); copy->Print(); std::cout << std::endl; + test_results.push_back({ "Copy", copy->ToString() == root->ToString() }); // Parse from XML const char *xml = @@ -76,55 +89,58 @@ int main() " \n" " \n"; const char *expected_xml_config_tree = - "xml children={ game } \n" - " game children={ name roms } \n" - " name=scud children={ } \n" - " roms children={ region } \n" - " region children={ byte_swap file name stride } \n" - " name=crom children={ } \n" - " stride=8 children={ } \n" - " byte_swap=true children={ } \n" - " file children={ crc32 name offset } \n" - " offset=0 children={ } \n" - " name=epr-19734.20 children={ } \n" - " crc32=0xBE897336 children={ } \n" - " file children={ crc32 name offset } \n" - " offset=2 children={ } \n" - " name=epr-19733.19 children={ } \n" - " crc32=0x6565E29A children={ } \n" - " file children={ crc32 name offset } \n" - " offset=4 children={ } \n" - " name=epr-19732.18 children={ } \n" - " crc32=0x23E864BB children={ } \n" - " file children={ crc32 name offset } \n" - " offset=6 children={ } \n" - " name=epr-19731.17 children={ } \n" - " crc32=0x3EE6447E children={ } \n" - " region children={ byte_swap file name stride } \n" - " name=banked_crom children={ } \n" - " stride=8 children={ } \n" - " byte_swap=true children={ } \n" - " file children={ crc32 name offset } \n" - " offset=0x0000000 children={ } \n" - " name=mpr-20364.4 children={ } \n" - " crc32=0xA2A68EF2 children={ } \n" - " file children={ crc32 name offset } \n" - " offset=0x0000002 children={ } \n" - " name=mpr-20363.3 children={ } \n" - " crc32=0x3E3CC6FF children={ } \n" - " file children={ crc32 name offset } \n" - " offset=0x0000004 children={ } \n" - " name=mpr-20362.2 children={ } \n" - " crc32=0xF7E60DFD children={ } \n" - " file children={ crc32 name offset } \n" - " offset=0x0000006 children={ } \n" - " name=mpr-20361.1 children={ } \n" - " crc32=0xDDB66C2F children={ } \n"; + "xml children={ game }\n" + " game children={ name roms }\n" + " name=scud children={ }\n" + " roms children={ region }\n" + " region children={ byte_swap file name stride }\n" + " name=crom children={ }\n" + " stride=8 children={ }\n" + " byte_swap=true children={ }\n" + " file children={ crc32 name offset }\n" + " offset=0 children={ }\n" + " name=epr-19734.20 children={ }\n" + " crc32=0xBE897336 children={ }\n" + " file children={ crc32 name offset }\n" + " offset=2 children={ }\n" + " name=epr-19733.19 children={ }\n" + " crc32=0x6565E29A children={ }\n" + " file children={ crc32 name offset }\n" + " offset=4 children={ }\n" + " name=epr-19732.18 children={ }\n" + " crc32=0x23E864BB children={ }\n" + " file children={ crc32 name offset }\n" + " offset=6 children={ }\n" + " name=epr-19731.17 children={ }\n" + " crc32=0x3EE6447E children={ }\n" + " region children={ byte_swap file name stride }\n" + " name=banked_crom children={ }\n" + " stride=8 children={ }\n" + " byte_swap=true children={ }\n" + " file children={ crc32 name offset }\n" + " offset=0x0000000 children={ }\n" + " name=mpr-20364.4 children={ }\n" + " crc32=0xA2A68EF2 children={ }\n" + " file children={ crc32 name offset }\n" + " offset=0x0000002 children={ }\n" + " name=mpr-20363.3 children={ }\n" + " crc32=0x3E3CC6FF children={ }\n" + " file children={ crc32 name offset }\n" + " offset=0x0000004 children={ }\n" + " name=mpr-20362.2 children={ }\n" + " crc32=0xF7E60DFD children={ }\n" + " file children={ crc32 name offset }\n" + " offset=0x0000006 children={ }\n" + " name=mpr-20361.1 children={ }\n" + " crc32=0xDDB66C2F children={ }\n"; std::cout << "Expected output:" << std::endl << std::endl << expected_xml_config_tree << std::endl; std::cout << "Actual config tree:" << std::endl << std::endl; Util::Config::Node::Ptr_t xml_config = Util::Config::FromXML(xml); xml_config->Print(); std::cout << std::endl; + test_results.push_back({ "XML", xml_config->ToString() == expected_xml_config_tree }); + + PrintTestResults(test_results); return 0; } \ No newline at end of file