From 7e416ffa97424d9d0b76df1896cf0188fc2fecde Mon Sep 17 00:00:00 2001 From: Bartek Kryza Date: Fri, 9 Jun 2023 00:44:01 +0200 Subject: [PATCH] Added regex support to dependencies and dependants filter --- src/common/model/diagram_filter.cc | 126 +++++++++++---------- src/common/model/diagram_filter.h | 3 +- src/common/types.h | 8 ++ src/config/config.h | 4 +- src/package_diagram/model/diagram.h | 21 ++++ tests/test_config_data/filters.yml | 12 +- tests/test_filters.cc | 168 ++++++++++++++++++++++++++++ 7 files changed, 281 insertions(+), 61 deletions(-) diff --git a/src/common/model/diagram_filter.cc b/src/common/model/diagram_filter.cc index f747dad0..d4364fc6 100644 --- a/src/common/model/diagram_filter.cc +++ b/src/common/model/diagram_filter.cc @@ -640,6 +640,29 @@ bool diagram_filter::should_include( void diagram_filter::init_filters(const config::diagram &c) { + using specializations_filter_t = + edge_traversal_filter; + + using class_dependants_filter_t = + edge_traversal_filter; + using class_dependencies_filter_t = + edge_traversal_filter; + + using package_dependants_filter_t = + edge_traversal_filter; + using package_dependencies_filter_t = + edge_traversal_filter; + + using source_file_dependency_filter_t = + edge_traversal_filter; + // Process inclusive filters if (c.include) { add_inclusive_filter(std::make_unique( @@ -682,58 +705,55 @@ void diagram_filter::init_filters(const config::diagram &c) element_filters.emplace_back(std::make_unique( filter_t::kInclusive, c.include().parents)); - element_filters.emplace_back(std::make_unique< - edge_traversal_filter>( - filter_t::kInclusive, relationship_t::kInstantiation, - c.include().specializations)); + element_filters.emplace_back( + std::make_unique(filter_t::kInclusive, + relationship_t::kInstantiation, + c.include().specializations)); - element_filters.emplace_back(std::make_unique< - edge_traversal_filter>(filter_t::kInclusive, - relationship_t::kDependency, c.include().dependants)); + element_filters.emplace_back( + std::make_unique( + filter_t::kInclusive, relationship_t::kDependency, + c.include().dependants)); - element_filters.emplace_back(std::make_unique< - edge_traversal_filter>(filter_t::kInclusive, - relationship_t::kDependency, c.include().dependencies, true)); + element_filters.emplace_back( + std::make_unique( + filter_t::kInclusive, relationship_t::kDependency, + c.include().dependencies, true)); } else if (c.type() == diagram_t::kPackage) { - element_filters.emplace_back(std::make_unique>( - filter_t::kInclusive, relationship_t::kDependency, - c.include().dependants)); + element_filters.emplace_back( + std::make_unique( + filter_t::kInclusive, relationship_t::kDependency, + c.include().dependants)); - element_filters.emplace_back(std::make_unique>( - filter_t::kInclusive, relationship_t::kDependency, - c.include().dependencies, true)); + element_filters.emplace_back( + std::make_unique( + filter_t::kInclusive, relationship_t::kDependency, + c.include().dependencies, true)); } else if (c.type() == diagram_t::kInclude) { std::vector dependants; std::vector dependencies; for (auto &&path : c.include().dependants) { - const std::filesystem::path dep_path{path}; + const std::filesystem::path dep_path{*path.get()}; dependants.emplace_back(dep_path.lexically_normal().string()); } for (auto &&path : c.include().dependencies) { - const std::filesystem::path dep_path{path}; + const std::filesystem::path dep_path{*path.get()}; dependencies.emplace_back(dep_path.lexically_normal().string()); } - element_filters.emplace_back(std::make_unique>( - filter_t::kInclusive, relationship_t::kAssociation, - dependants)); + element_filters.emplace_back( + std::make_unique( + filter_t::kInclusive, relationship_t::kAssociation, + dependants)); - element_filters.emplace_back(std::make_unique>( - filter_t::kInclusive, relationship_t::kAssociation, - dependencies, true)); + element_filters.emplace_back( + std::make_unique( + filter_t::kInclusive, relationship_t::kAssociation, + dependencies, true)); } element_filters.emplace_back(std::make_unique( @@ -781,29 +801,23 @@ void diagram_filter::init_filters(const config::diagram &c) add_exclusive_filter(std::make_unique( filter_t::kExclusive, c.exclude().parents)); - add_exclusive_filter(std::make_unique< - edge_traversal_filter>( - filter_t::kExclusive, relationship_t::kInstantiation, - c.exclude().specializations)); + add_exclusive_filter( + std::make_unique(filter_t::kExclusive, + relationship_t::kInstantiation, c.exclude().specializations)); - add_exclusive_filter(std::make_unique>( - filter_t::kExclusive, relationship_t::kDependency, - c.exclude().dependants)); + add_exclusive_filter( + std::make_unique(filter_t::kExclusive, + relationship_t::kDependency, c.exclude().dependants)); - add_exclusive_filter(std::make_unique>( - filter_t::kExclusive, relationship_t::kDependency, - c.exclude().dependants)); + add_exclusive_filter( + std::make_unique(filter_t::kExclusive, + relationship_t::kDependency, c.exclude().dependants)); - add_exclusive_filter(std::make_unique>( - filter_t::kExclusive, relationship_t::kDependency, - c.exclude().dependencies, true)); + add_exclusive_filter( + std::make_unique(filter_t::kExclusive, + relationship_t::kDependency, c.exclude().dependencies, true)); - add_exclusive_filter(std::make_unique>( + add_exclusive_filter(std::make_unique( filter_t::kExclusive, relationship_t::kDependency, c.exclude().dependencies, true)); @@ -812,9 +826,9 @@ void diagram_filter::init_filters(const config::diagram &c) std::vector dependencies; for (auto &&path : c.exclude().dependants) { - std::filesystem::path dep_path{path}; + std::filesystem::path dep_path{*path.get()}; if (dep_path.is_relative()) { - dep_path = c.base_directory() / path; + dep_path = c.base_directory() / *path.get(); dep_path = relative(dep_path, c.relative_to()); } @@ -822,9 +836,9 @@ void diagram_filter::init_filters(const config::diagram &c) } for (auto &&path : c.exclude().dependencies) { - std::filesystem::path dep_path{path}; + std::filesystem::path dep_path{*path.get()}; if (dep_path.is_relative()) { - dep_path = c.base_directory() / path; + dep_path = c.base_directory() / *path.get(); dep_path = relative(dep_path, c.relative_to()); } diff --git a/src/common/model/diagram_filter.h b/src/common/model/diagram_filter.h index 67831ca6..597514f0 100644 --- a/src/common/model/diagram_filter.h +++ b/src/common/model/diagram_filter.h @@ -289,8 +289,7 @@ private: for (const auto &root_pattern : roots_) { if constexpr (std::is_same_v) { - auto root_refs = cd.template find( - root_pattern); + auto root_refs = cd.template find(root_pattern); for (auto &root : root_refs) { if (root.has_value()) diff --git a/src/common/types.h b/src/common/types.h index 23deba57..be383242 100644 --- a/src/common/types.h +++ b/src/common/types.h @@ -207,6 +207,14 @@ template struct or_regex { return std::get(value_) == v; } + template std::optional get() const + { + if (!std::holds_alternative(value_)) + return std::nullopt; + + return std::get(value_); + } + std::string to_string() const { if (std::holds_alternative(value_)) diff --git a/src/config/config.h b/src/config/config.h index f4829633..49130c65 100644 --- a/src/config/config.h +++ b/src/config/config.h @@ -103,9 +103,9 @@ struct filter { std::vector specializations; - std::vector dependants; + std::vector dependants; - std::vector dependencies; + std::vector dependencies; std::vector context; diff --git a/src/package_diagram/model/diagram.h b/src/package_diagram/model/diagram.h index 1a4aadd2..9f64ddb3 100644 --- a/src/package_diagram/model/diagram.h +++ b/src/package_diagram/model/diagram.h @@ -58,6 +58,10 @@ public: template opt_ref find(diagram_element::id_t id) const; + template + std::vector> find( + const clanguml::common::string_or_regex &pattern) const; + template bool add(const path &parent_path, std::unique_ptr &&e) { @@ -106,6 +110,23 @@ opt_ref diagram::find(diagram_element::id_t id) const return {}; } +template +std::vector> diagram::find( + const common::string_or_regex &pattern) const +{ + std::vector> result; + + for (const auto &element : element_view::view()) { + const auto full_name = element.get().full_name(false); + + if (pattern == full_name) { + result.emplace_back(element); + } + } + + return result; +} + template bool diagram::add_with_namespace_path(std::unique_ptr &&p) { diff --git a/tests/test_config_data/filters.yml b/tests/test_config_data/filters.yml index 118d48a9..6d605c24 100644 --- a/tests/test_config_data/filters.yml +++ b/tests/test_config_data/filters.yml @@ -68,4 +68,14 @@ diagrams: type: class include: context: - - r: '[A|B]' \ No newline at end of file + - r: '[A|B]' + regex_dependencies_test: + type: class + include: + dependencies: + - r: 'A21|B1' + regex_dependants_test: + type: class + include: + dependants: + - r: 'A|B' \ No newline at end of file diff --git a/tests/test_filters.cc b/tests/test_filters.cc index ded1bcc7..c1b9869e 100644 --- a/tests/test_filters.cc +++ b/tests/test_filters.cc @@ -541,6 +541,174 @@ TEST_CASE("Test context regexp filter", "[unit-test]") CHECK(!filter.should_include(*diagram.find("C1"))); } +TEST_CASE("Test dependencies regexp filter", "[unit-test]") +{ + using clanguml::class_diagram::model::class_; + using clanguml::class_diagram::model::class_method; + using clanguml::class_diagram::model::class_parent; + using clanguml::common::to_id; + using clanguml::common::model::access_t; + using clanguml::common::model::diagram_filter; + using clanguml::common::model::namespace_; + using clanguml::common::model::package; + using clanguml::common::model::relationship; + using clanguml::common::model::relationship_t; + using clanguml::common::model::source_file; + using clanguml::common::model::template_parameter; + using namespace std::string_literals; + using clanguml::class_diagram::model::class_; + + auto cfg = clanguml::config::load("./test_config_data/filters.yml"); + + auto &config = *cfg.diagrams["regex_dependencies_test"]; + clanguml::class_diagram::model::diagram diagram; + + auto c = std::make_unique(config.using_namespace()); + c->set_name("A"); + c->set_id(to_id("A"s)); + diagram.add(namespace_{}, std::move(c)); + + c = std::make_unique(config.using_namespace()); + c->set_name("A1"); + c->set_id(to_id("A1"s)); + c->add_relationship(relationship{relationship_t::kDependency, to_id("A"s)}); + diagram.add(namespace_{}, std::move(c)); + + c = std::make_unique(config.using_namespace()); + c->set_name("A2"); + c->set_id(to_id("A2"s)); + c->add_relationship(relationship{relationship_t::kDependency, to_id("A"s)}); + diagram.add(namespace_{}, std::move(c)); + + c = std::make_unique(config.using_namespace()); + c->set_name("A21"); + c->set_id(to_id("A21"s)); + c->add_relationship( + relationship{relationship_t::kDependency, to_id("A2"s)}); + diagram.add(namespace_{}, std::move(c)); + + c = std::make_unique(config.using_namespace()); + c->set_name("B"); + c->set_id(to_id("B"s)); + diagram.add(namespace_{}, std::move(c)); + + c = std::make_unique(config.using_namespace()); + c->set_name("B1"); + c->set_id(to_id("B1"s)); + c->add_relationship(relationship{relationship_t::kDependency, to_id("B"s)}); + diagram.add(namespace_{}, std::move(c)); + + c = std::make_unique(config.using_namespace()); + c->set_name("C"); + c->set_id(to_id("C"s)); + diagram.add(namespace_{}, std::move(c)); + + c = std::make_unique(config.using_namespace()); + c->set_name("C1"); + c->set_id(to_id("C1"s)); + c->add_relationship(relationship{relationship_t::kDependency, to_id("C"s)}); + diagram.add(namespace_{}, std::move(c)); + + diagram.set_complete(true); + + diagram_filter filter(diagram, config); + + CHECK(filter.should_include(*diagram.find("A"))); + CHECK(!filter.should_include(*diagram.find("A1"))); + CHECK(filter.should_include(*diagram.find("A2"))); + CHECK(filter.should_include(*diagram.find("A21"))); + + CHECK(filter.should_include(*diagram.find("B"))); + CHECK(filter.should_include(*diagram.find("B1"))); + + CHECK(!filter.should_include(*diagram.find("C"))); + CHECK(!filter.should_include(*diagram.find("C1"))); +} + +TEST_CASE("Test dependants regexp filter", "[unit-test]") +{ + using clanguml::class_diagram::model::class_; + using clanguml::class_diagram::model::class_method; + using clanguml::class_diagram::model::class_parent; + using clanguml::common::to_id; + using clanguml::common::model::access_t; + using clanguml::common::model::diagram_filter; + using clanguml::common::model::namespace_; + using clanguml::common::model::package; + using clanguml::common::model::relationship; + using clanguml::common::model::relationship_t; + using clanguml::common::model::source_file; + using clanguml::common::model::template_parameter; + using namespace std::string_literals; + using clanguml::class_diagram::model::class_; + + auto cfg = clanguml::config::load("./test_config_data/filters.yml"); + + auto &config = *cfg.diagrams["regex_dependants_test"]; + clanguml::class_diagram::model::diagram diagram; + + auto c = std::make_unique(config.using_namespace()); + c->set_name("A"); + c->set_id(to_id("A"s)); + diagram.add(namespace_{}, std::move(c)); + + c = std::make_unique(config.using_namespace()); + c->set_name("A1"); + c->set_id(to_id("A1"s)); + c->add_relationship(relationship{relationship_t::kDependency, to_id("A"s)}); + diagram.add(namespace_{}, std::move(c)); + + c = std::make_unique(config.using_namespace()); + c->set_name("A2"); + c->set_id(to_id("A2"s)); + c->add_relationship(relationship{relationship_t::kDependency, to_id("A"s)}); + diagram.add(namespace_{}, std::move(c)); + + c = std::make_unique(config.using_namespace()); + c->set_name("A21"); + c->set_id(to_id("A21"s)); + c->add_relationship( + relationship{relationship_t::kDependency, to_id("A2"s)}); + diagram.add(namespace_{}, std::move(c)); + + c = std::make_unique(config.using_namespace()); + c->set_name("B"); + c->set_id(to_id("B"s)); + diagram.add(namespace_{}, std::move(c)); + + c = std::make_unique(config.using_namespace()); + c->set_name("B1"); + c->set_id(to_id("B1"s)); + c->add_relationship(relationship{relationship_t::kDependency, to_id("B"s)}); + diagram.add(namespace_{}, std::move(c)); + + c = std::make_unique(config.using_namespace()); + c->set_name("C"); + c->set_id(to_id("C"s)); + diagram.add(namespace_{}, std::move(c)); + + c = std::make_unique(config.using_namespace()); + c->set_name("C1"); + c->set_id(to_id("C1"s)); + c->add_relationship(relationship{relationship_t::kDependency, to_id("C"s)}); + diagram.add(namespace_{}, std::move(c)); + + diagram.set_complete(true); + + diagram_filter filter(diagram, config); + + CHECK(filter.should_include(*diagram.find("A"))); + CHECK(filter.should_include(*diagram.find("A1"))); + CHECK(filter.should_include(*diagram.find("A2"))); + CHECK(filter.should_include(*diagram.find("A21"))); + + CHECK(filter.should_include(*diagram.find("B"))); + CHECK(filter.should_include(*diagram.find("B1"))); + + CHECK(!filter.should_include(*diagram.find("C"))); + CHECK(!filter.should_include(*diagram.find("C1"))); +} + /// /// Main test function ///