diff --git a/src/class_diagram/model/class_template.h b/src/class_diagram/model/class_template.h index 54f2f8b6..149292d4 100644 --- a/src/class_diagram/model/class_template.h +++ b/src/class_diagram/model/class_template.h @@ -41,6 +41,8 @@ public: friend bool operator==(const class_template &l, const class_template &r); + std::vector template_params_; + private: std::string type_; std::string name_; diff --git a/src/cx/util.cc b/src/cx/util.cc index 67c7ddcc..1779c23a 100644 --- a/src/cx/util.cc +++ b/src/cx/util.cc @@ -25,6 +25,7 @@ #include #include +#include #include namespace clanguml { @@ -294,6 +295,81 @@ const cppast::cpp_type &unreferenced(const cppast::cpp_type &t) return t; } + +std::vector +parse_unexposed_template_params(const std::string ¶ms) +{ + using class_diagram::model::class_template; + + std::vector res; + + int nested_template_level{0}; + auto it = params.begin(); + + std::string type{}; + std::vector nested_params; + bool complete_class_template{false}; + + while (it != params.end()) { + if (*it == '<') { + int nested_level{0}; + auto bracket_match_begin = it + 1; + auto bracket_match_end = bracket_match_begin; + while (bracket_match_end != params.end()) { + if (*bracket_match_end == '<') { + nested_level++; + } + else if (*bracket_match_end == '>') { + if (nested_level > 0) + nested_level--; + else + break; + } + else { + } + bracket_match_end++; + } + std::string nested_params_str( + bracket_match_begin, bracket_match_end); + nested_params = parse_unexposed_template_params(nested_params_str); + if (nested_params.empty()) + nested_params.emplace_back(class_template{nested_params_str}); + it = bracket_match_end - 1; + } + else if (*it == '>') { + complete_class_template = true; + } + else if (*it == ',') { + complete_class_template = true; + } + else { + type += *it; + } + if(complete_class_template) { + class_template t; + t.set_type(clanguml::util::trim(type)); + type = ""; + t.template_params_ = std::move(nested_params); + + res.emplace_back(std::move(t)); + complete_class_template = false; + } + it++; + } + + if(!type.empty()) { + class_template t; + t.set_type(clanguml::util::trim(type)); + type = ""; + t.template_params_ = std::move(nested_params); + + res.emplace_back(std::move(t)); + complete_class_template = false; + } + + return res; +} + } // namespace util } // namespace cx } // namespace clanguml diff --git a/src/cx/util.h b/src/cx/util.h index 182c8486..f32122d6 100644 --- a/src/cx/util.h +++ b/src/cx/util.h @@ -24,6 +24,7 @@ #include #include +#include #include namespace clanguml { @@ -66,6 +67,10 @@ std::pair split_ns( const std::string &full_name); bool is_inside_class(const cppast::cpp_entity &e); + +std::vector +parse_unexposed_template_params(const std::string ¶ms); + } // namespace util } // namespace cx } // namespace clanguml diff --git a/tests/test_util.cc b/tests/test_util.cc index 884a430b..1df6d573 100644 --- a/tests/test_util.cc +++ b/tests/test_util.cc @@ -18,6 +18,7 @@ #define CATCH_CONFIG_MAIN #include "util/util.h" +#include #include "catch.h" @@ -35,19 +36,6 @@ TEST_CASE("Test split", "[unit-test]") CHECK(split("std::vector::detail::", "::") == C{"std", "vector", "detail"}); } -// -// TEST_CASE("Test ns_relative", "[unit-test]") -//{ -// using namespace clanguml::util; -// -// CHECK(ns_relative({}, "std::vector") == "std::vector"); -// CHECK(ns_relative({"std"}, "std::vector") == "vector"); -// CHECK(ns_relative({"std"}, "const std::vector&") == "const vector&"); -// CHECK(ns_relative({"std", "clanguml::t0"}, -// "static const std::vector&") == -// "static const vector&"); -// CHECK(ns_relative({"clanguml::t0"}, "clanguml::t0") == "t0"); -//} TEST_CASE("Test abbreviate", "[unit-test]") { @@ -80,3 +68,44 @@ TEST_CASE("Test replace_all", "[unit-test]") CHECK(text == orig); } + +TEST_CASE("Test parse_unexposed_template_params", "[unit-test]") +{ + using namespace clanguml::cx::util; + + const std::string int_template_str{"ns1::ns2::class1"}; + + auto int_template = parse_unexposed_template_params(int_template_str); + + CHECK(int_template.size() == 1); + CHECK(int_template[0].template_params_.size() == 1); + CHECK(int_template[0].type() == "ns1::ns2::class1"); + CHECK(int_template[0].template_params_[0].type() == "int"); + + const std::string int_int_template_str{"ns1::ns2::class1"}; + + auto int_int_template = + parse_unexposed_template_params(int_int_template_str); + + CHECK(int_int_template.size() == 1); + CHECK(int_int_template[0].template_params_.size() == 2); + CHECK(int_int_template[0].type() == "ns1::ns2::class1"); + CHECK(int_int_template[0].template_params_[0].type() == "int"); + CHECK(int_int_template[0].template_params_[1].type() == "int"); + + const std::string nested_template_str{ + "class1>>"}; + + auto nested_template = parse_unexposed_template_params(nested_template_str); + + CHECK(nested_template.size() == 1); + CHECK(nested_template[0].template_params_.size() == 2); + CHECK(nested_template[0].type() == "class1"); + CHECK(nested_template[0].template_params_[0].type() == "int"); + const auto &class2 = nested_template[0].template_params_[1]; + CHECK(class2.type() == "ns1::class2"); + CHECK(class2.template_params_[0].type() == "int"); + CHECK(class2.template_params_[1].type() == "std::vector"); + CHECK( + class2.template_params_[1].template_params_[0].type() == "std::string"); +}