Skip to content

Commit

Permalink
Merge pull request #168 from AsherGlick/string_hirearchy_utils
Browse files Browse the repository at this point in the history
String Hierarchy Utility Functions
  • Loading branch information
AsherGlick authored Oct 4, 2023
2 parents 32ccac8 + aede4e4 commit 99440c1
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 0 deletions.
82 changes: 82 additions & 0 deletions xml_converter/src/string_hierarchy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,24 @@

#include <iostream>

////////////////////////////////////////////////////////////////////////////////
// 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
//
Expand All @@ -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<std::string> &input) const {
std::vector<std::string> vec;
vec.insert(vec.end(), input.begin(), input.end());
return this->in_hierarchy(vec);
}

////////////////////////////////////////////////////////////////////////////////
// _in_hirearchy
//
Expand Down Expand Up @@ -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<std::string> &input) const {
std::vector<std::string> 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<std::string> &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
//
Expand Down
15 changes: 15 additions & 0 deletions xml_converter/src/string_hierarchy.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,21 @@
class StringHierarchy {
public:
bool in_hierarchy(
const std::string &node) const;

bool in_hierarchy(
const std::vector<std::string> &path) const;

bool in_hierarchy(
const std::initializer_list<std::string> &input) const;

const StringHierarchy *sub_hierarchy(
const std::string &node) const;

const StringHierarchy *sub_hierarchy(
const std::initializer_list<std::string> &input) const;

const StringHierarchy *sub_hierarchy(
const std::vector<std::string> &path) const;

void add_path(
Expand Down
45 changes: 45 additions & 0 deletions xml_converter/tests/test_string_hierarchy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"}));
Expand All @@ -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"}));
Expand Down Expand Up @@ -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();
Expand Down

0 comments on commit 99440c1

Please sign in to comment.