diff --git a/xml_converter/src/string_hierarchy.cpp b/xml_converter/src/string_hierarchy.cpp index 0c907c39..f14fe51b 100644 --- a/xml_converter/src/string_hierarchy.cpp +++ b/xml_converter/src/string_hierarchy.cpp @@ -2,6 +2,24 @@ #include +//////////////////////////////////////////////////////////////////////////////// +// in_hierarchy +// +// Returns if a particular node exists at the top level of the hirearchy. +//////////////////////////////////////////////////////////////////////////////// +bool StringHierarchy::in_hierarchy( + const std::string &node) const { + if (this->all_children_included) { + return true; + } + + auto iterator = this->children.find(node); + if (iterator == this->children.end()) { + return false; + } + return true; +} + //////////////////////////////////////////////////////////////////////////////// // in_hierarchy // @@ -12,6 +30,19 @@ bool StringHierarchy::in_hierarchy( return this->_in_hierarchy(path, 0); } +//////////////////////////////////////////////////////////////////////////////// +// in_hierarchy +// +// An explicit version of in_hierarchy that takes an initalizer list to prevent +// ambiguity between the vector and string overloads of the function. +//////////////////////////////////////////////////////////////////////////////// +bool StringHierarchy::in_hierarchy( + const std::initializer_list &input) const { + std::vector vec; + vec.insert(vec.end(), input.begin(), input.end()); + return this->in_hierarchy(vec); +} + //////////////////////////////////////////////////////////////////////////////// // _in_hirearchy // @@ -39,6 +70,57 @@ bool StringHierarchy::_in_hierarchy( return iterator->second._in_hierarchy(path, continue_index + 1); } +//////////////////////////////////////////////////////////////////////////////// +// sub_hierarchy +// +// A helper function to grab a sub hierarchy one level down from the top. +//////////////////////////////////////////////////////////////////////////////// +const StringHierarchy *StringHierarchy::sub_hierarchy( + const std::string &node) const { + if (this->all_children_included) { + return this; + } + + auto iterator = this->children.find(node); + if (iterator == this->children.end()) { + return nullptr; + } + return &(iterator->second); +} + +//////////////////////////////////////////////////////////////////////////////// +// sub_hierarchy +// +// An explicit version of sub_hierarchy that takes an initalizer list to +// prevent ambiguity between the vector and string overloads of the function. +//////////////////////////////////////////////////////////////////////////////// +const StringHierarchy *StringHierarchy::sub_hierarchy( + const std::initializer_list &input) const { + std::vector vec; + vec.insert(vec.end(), input.begin(), input.end()); + return this->sub_hierarchy(vec); +} + +//////////////////////////////////////////////////////////////////////////////// +// sub_hierarchy +// +// Get a subtree of the StringHierarchy in order to avoid needing to query the +// entire hierarchy in the case where the use case is traversing down a tree +// anyways and does not want to keep track of the parent's values. +//////////////////////////////////////////////////////////////////////////////// +const StringHierarchy *StringHierarchy::sub_hierarchy( + const std::vector &path) const { + const StringHierarchy *sub_hierarchy = this; + for (size_t i = 0; i < path.size(); i++) { + sub_hierarchy = sub_hierarchy->sub_hierarchy(path[i]); + // Escape before segfaulting. + if (sub_hierarchy == nullptr) { + return nullptr; + } + } + return sub_hierarchy; +} + //////////////////////////////////////////////////////////////////////////////// // add_path // diff --git a/xml_converter/src/string_hierarchy.hpp b/xml_converter/src/string_hierarchy.hpp index 9a5587db..594300ba 100644 --- a/xml_converter/src/string_hierarchy.hpp +++ b/xml_converter/src/string_hierarchy.hpp @@ -7,6 +7,21 @@ class StringHierarchy { public: bool in_hierarchy( + const std::string &node) const; + + bool in_hierarchy( + const std::vector &path) const; + + bool in_hierarchy( + const std::initializer_list &input) const; + + const StringHierarchy *sub_hierarchy( + const std::string &node) const; + + const StringHierarchy *sub_hierarchy( + const std::initializer_list &input) const; + + const StringHierarchy *sub_hierarchy( const std::vector &path) const; void add_path( diff --git a/xml_converter/tests/test_string_hierarchy.cpp b/xml_converter/tests/test_string_hierarchy.cpp index 7eed8a30..4e2d84c8 100644 --- a/xml_converter/tests/test_string_hierarchy.cpp +++ b/xml_converter/tests/test_string_hierarchy.cpp @@ -18,6 +18,11 @@ TEST_F(StringHierarchyTest, BasicPath) { EXPECT_TRUE(string_hierarchy.in_hierarchy({"root", "child1", "child2"})); } +TEST_F(StringHierarchyTest, BasicPathSingle) { + string_hierarchy.add_path({"root", "child1", "child2"}, false); + EXPECT_TRUE(string_hierarchy.in_hierarchy("root")); +} + TEST_F(StringHierarchyTest, ParentPath) { string_hierarchy.add_path({"root", "child1", "child2"}, false); EXPECT_TRUE(string_hierarchy.in_hierarchy({"root", "child1"})); @@ -34,6 +39,11 @@ TEST_F(StringHierarchyTest, InvalidRoot) { EXPECT_FALSE(string_hierarchy.in_hierarchy({"badroot"})); } +TEST_F(StringHierarchyTest, InvalidRootSingle) { + string_hierarchy.add_path({"root", "child1", "child2"}, false); + EXPECT_FALSE(string_hierarchy.in_hierarchy("badroot")); +} + TEST_F(StringHierarchyTest, NonExistantDepthNode) { string_hierarchy.add_path({"root", "child1", "child2"}, false); EXPECT_FALSE(string_hierarchy.in_hierarchy({"root", "child1", "child2", "child3"})); @@ -67,6 +77,41 @@ TEST_F(StringHierarchyTest, AllowAll) { EXPECT_TRUE(string_hierarchy.in_hierarchy({"literally", "anything"})); } +TEST_F(StringHierarchyTest, AllowAllSingle) { + string_hierarchy.add_path({}, true); + EXPECT_TRUE(string_hierarchy.in_hierarchy("everything")); +} + +TEST_F(StringHierarchyTest, InvalidHierarchy) { + string_hierarchy.add_path({"root", "child1", "child2"}, false); + const StringHierarchy* sub_hierarchy = string_hierarchy.sub_hierarchy({"invalid"}); + EXPECT_EQ(sub_hierarchy, nullptr); +} + +TEST_F(StringHierarchyTest, SubHierarchy) { + string_hierarchy.add_path({"root", "child1", "child2"}, false); + const StringHierarchy* sub_hierarchy = string_hierarchy.sub_hierarchy({"root", "child1"}); + EXPECT_NE(sub_hierarchy, nullptr); + EXPECT_FALSE(sub_hierarchy->in_hierarchy("root")); + EXPECT_FALSE(sub_hierarchy->in_hierarchy("child1")); + EXPECT_TRUE(sub_hierarchy->in_hierarchy("child2")); +} + +TEST_F(StringHierarchyTest, InvalidDeepHierarchy) { + string_hierarchy.add_path({"root", "child1", "child2"}, false); + const StringHierarchy* sub_hierarchy = string_hierarchy.sub_hierarchy({"invalid", "nonexistant"}); + EXPECT_EQ(sub_hierarchy, nullptr); +} + +TEST_F(StringHierarchyTest, SubHierarchySingle) { + string_hierarchy.add_path({"root", "child1", "child2"}, false); + const StringHierarchy* sub_hierarchy = string_hierarchy.sub_hierarchy("root"); + EXPECT_NE(sub_hierarchy, nullptr); + EXPECT_FALSE(sub_hierarchy->in_hierarchy("root")); + EXPECT_TRUE(sub_hierarchy->in_hierarchy("child1")); + EXPECT_TRUE(sub_hierarchy->in_hierarchy({"child1", "child2"})); +} + int main(int argc, char **argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS();