Added regex support to dependencies and dependants filter

This commit is contained in:
Bartek Kryza
2023-06-09 00:44:01 +02:00
parent 658bceee4b
commit 7e416ffa97
7 changed files with 281 additions and 61 deletions

View File

@@ -640,6 +640,29 @@ bool diagram_filter::should_include(
void diagram_filter::init_filters(const config::diagram &c) void diagram_filter::init_filters(const config::diagram &c)
{ {
using specializations_filter_t =
edge_traversal_filter<class_diagram::model::diagram,
class_diagram::model::class_, common::string_or_regex>;
using class_dependants_filter_t =
edge_traversal_filter<class_diagram::model::diagram,
class_diagram::model::class_, common::string_or_regex>;
using class_dependencies_filter_t =
edge_traversal_filter<class_diagram::model::diagram,
class_diagram::model::class_, common::string_or_regex>;
using package_dependants_filter_t =
edge_traversal_filter<package_diagram::model::diagram,
common::model::package, common::string_or_regex>;
using package_dependencies_filter_t =
edge_traversal_filter<package_diagram::model::diagram,
common::model::package, common::string_or_regex>;
using source_file_dependency_filter_t =
edge_traversal_filter<include_diagram::model::diagram,
common::model::source_file, std::string,
common::model::source_file>;
// Process inclusive filters // Process inclusive filters
if (c.include) { if (c.include) {
add_inclusive_filter(std::make_unique<namespace_filter>( add_inclusive_filter(std::make_unique<namespace_filter>(
@@ -682,58 +705,55 @@ void diagram_filter::init_filters(const config::diagram &c)
element_filters.emplace_back(std::make_unique<parents_filter>( element_filters.emplace_back(std::make_unique<parents_filter>(
filter_t::kInclusive, c.include().parents)); filter_t::kInclusive, c.include().parents));
element_filters.emplace_back(std::make_unique< element_filters.emplace_back(
edge_traversal_filter<class_diagram::model::diagram, std::make_unique<specializations_filter_t>(filter_t::kInclusive,
class_diagram::model::class_, common::string_or_regex>>( relationship_t::kInstantiation,
filter_t::kInclusive, relationship_t::kInstantiation, c.include().specializations));
c.include().specializations));
element_filters.emplace_back(std::make_unique< element_filters.emplace_back(
edge_traversal_filter<class_diagram::model::diagram, std::make_unique<class_dependants_filter_t>(
class_diagram::model::class_>>(filter_t::kInclusive, filter_t::kInclusive, relationship_t::kDependency,
relationship_t::kDependency, c.include().dependants)); c.include().dependants));
element_filters.emplace_back(std::make_unique< element_filters.emplace_back(
edge_traversal_filter<class_diagram::model::diagram, std::make_unique<class_dependencies_filter_t>(
class_diagram::model::class_>>(filter_t::kInclusive, filter_t::kInclusive, relationship_t::kDependency,
relationship_t::kDependency, c.include().dependencies, true)); c.include().dependencies, true));
} }
else if (c.type() == diagram_t::kPackage) { else if (c.type() == diagram_t::kPackage) {
element_filters.emplace_back(std::make_unique<edge_traversal_filter< element_filters.emplace_back(
package_diagram::model::diagram, common::model::package>>( std::make_unique<package_dependants_filter_t>(
filter_t::kInclusive, relationship_t::kDependency, filter_t::kInclusive, relationship_t::kDependency,
c.include().dependants)); c.include().dependants));
element_filters.emplace_back(std::make_unique<edge_traversal_filter< element_filters.emplace_back(
package_diagram::model::diagram, common::model::package>>( std::make_unique<package_dependencies_filter_t>(
filter_t::kInclusive, relationship_t::kDependency, filter_t::kInclusive, relationship_t::kDependency,
c.include().dependencies, true)); c.include().dependencies, true));
} }
else if (c.type() == diagram_t::kInclude) { else if (c.type() == diagram_t::kInclude) {
std::vector<std::string> dependants; std::vector<std::string> dependants;
std::vector<std::string> dependencies; std::vector<std::string> dependencies;
for (auto &&path : c.include().dependants) { for (auto &&path : c.include().dependants) {
const std::filesystem::path dep_path{path}; const std::filesystem::path dep_path{*path.get<std::string>()};
dependants.emplace_back(dep_path.lexically_normal().string()); dependants.emplace_back(dep_path.lexically_normal().string());
} }
for (auto &&path : c.include().dependencies) { for (auto &&path : c.include().dependencies) {
const std::filesystem::path dep_path{path}; const std::filesystem::path dep_path{*path.get<std::string>()};
dependencies.emplace_back(dep_path.lexically_normal().string()); dependencies.emplace_back(dep_path.lexically_normal().string());
} }
element_filters.emplace_back(std::make_unique<edge_traversal_filter< element_filters.emplace_back(
include_diagram::model::diagram, common::model::source_file, std::make_unique<source_file_dependency_filter_t>(
std::string, common::model::source_file>>( filter_t::kInclusive, relationship_t::kAssociation,
filter_t::kInclusive, relationship_t::kAssociation, dependants));
dependants));
element_filters.emplace_back(std::make_unique<edge_traversal_filter< element_filters.emplace_back(
include_diagram::model::diagram, common::model::source_file, std::make_unique<source_file_dependency_filter_t>(
std::string, common::model::source_file>>( filter_t::kInclusive, relationship_t::kAssociation,
filter_t::kInclusive, relationship_t::kAssociation, dependencies, true));
dependencies, true));
} }
element_filters.emplace_back(std::make_unique<context_filter>( element_filters.emplace_back(std::make_unique<context_filter>(
@@ -781,29 +801,23 @@ void diagram_filter::init_filters(const config::diagram &c)
add_exclusive_filter(std::make_unique<parents_filter>( add_exclusive_filter(std::make_unique<parents_filter>(
filter_t::kExclusive, c.exclude().parents)); filter_t::kExclusive, c.exclude().parents));
add_exclusive_filter(std::make_unique< add_exclusive_filter(
edge_traversal_filter<class_diagram::model::diagram, std::make_unique<specializations_filter_t>(filter_t::kExclusive,
class_diagram::model::class_, common::string_or_regex>>( relationship_t::kInstantiation, c.exclude().specializations));
filter_t::kExclusive, relationship_t::kInstantiation,
c.exclude().specializations));
add_exclusive_filter(std::make_unique<edge_traversal_filter< add_exclusive_filter(
class_diagram::model::diagram, class_diagram::model::class_>>( std::make_unique<class_dependants_filter_t>(filter_t::kExclusive,
filter_t::kExclusive, relationship_t::kDependency, relationship_t::kDependency, c.exclude().dependants));
c.exclude().dependants));
add_exclusive_filter(std::make_unique<edge_traversal_filter< add_exclusive_filter(
package_diagram::model::diagram, common::model::package>>( std::make_unique<package_dependants_filter_t>(filter_t::kExclusive,
filter_t::kExclusive, relationship_t::kDependency, relationship_t::kDependency, c.exclude().dependants));
c.exclude().dependants));
add_exclusive_filter(std::make_unique<edge_traversal_filter< add_exclusive_filter(
class_diagram::model::diagram, class_diagram::model::class_>>( std::make_unique<class_dependencies_filter_t>(filter_t::kExclusive,
filter_t::kExclusive, relationship_t::kDependency, relationship_t::kDependency, c.exclude().dependencies, true));
c.exclude().dependencies, true));
add_exclusive_filter(std::make_unique<edge_traversal_filter< add_exclusive_filter(std::make_unique<package_dependencies_filter_t>(
package_diagram::model::diagram, common::model::package>>(
filter_t::kExclusive, relationship_t::kDependency, filter_t::kExclusive, relationship_t::kDependency,
c.exclude().dependencies, true)); c.exclude().dependencies, true));
@@ -812,9 +826,9 @@ void diagram_filter::init_filters(const config::diagram &c)
std::vector<std::string> dependencies; std::vector<std::string> dependencies;
for (auto &&path : c.exclude().dependants) { for (auto &&path : c.exclude().dependants) {
std::filesystem::path dep_path{path}; std::filesystem::path dep_path{*path.get<std::string>()};
if (dep_path.is_relative()) { if (dep_path.is_relative()) {
dep_path = c.base_directory() / path; dep_path = c.base_directory() / *path.get<std::string>();
dep_path = relative(dep_path, c.relative_to()); 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) { for (auto &&path : c.exclude().dependencies) {
std::filesystem::path dep_path{path}; std::filesystem::path dep_path{*path.get<std::string>()};
if (dep_path.is_relative()) { if (dep_path.is_relative()) {
dep_path = c.base_directory() / path; dep_path = c.base_directory() / *path.get<std::string>();
dep_path = relative(dep_path, c.relative_to()); dep_path = relative(dep_path, c.relative_to());
} }

View File

@@ -289,8 +289,7 @@ private:
for (const auto &root_pattern : roots_) { for (const auto &root_pattern : roots_) {
if constexpr (std::is_same_v<ConfigEntryT, if constexpr (std::is_same_v<ConfigEntryT,
common::string_or_regex>) { common::string_or_regex>) {
auto root_refs = cd.template find<class_diagram::model::class_>( auto root_refs = cd.template find<ElementT>(root_pattern);
root_pattern);
for (auto &root : root_refs) { for (auto &root : root_refs) {
if (root.has_value()) if (root.has_value())

View File

@@ -207,6 +207,14 @@ template <typename T> struct or_regex {
return std::get<T>(value_) == v; return std::get<T>(value_) == v;
} }
template <typename Ret> std::optional<Ret> get() const
{
if (!std::holds_alternative<Ret>(value_))
return std::nullopt;
return std::get<Ret>(value_);
}
std::string to_string() const std::string to_string() const
{ {
if (std::holds_alternative<regex>(value_)) if (std::holds_alternative<regex>(value_))

View File

@@ -103,9 +103,9 @@ struct filter {
std::vector<common::string_or_regex> specializations; std::vector<common::string_or_regex> specializations;
std::vector<std::string> dependants; std::vector<common::string_or_regex> dependants;
std::vector<std::string> dependencies; std::vector<common::string_or_regex> dependencies;
std::vector<common::string_or_regex> context; std::vector<common::string_or_regex> context;

View File

@@ -58,6 +58,10 @@ public:
template <typename ElementT> template <typename ElementT>
opt_ref<ElementT> find(diagram_element::id_t id) const; opt_ref<ElementT> find(diagram_element::id_t id) const;
template <typename ElementT>
std::vector<opt_ref<ElementT>> find(
const clanguml::common::string_or_regex &pattern) const;
template <typename ElementT> template <typename ElementT>
bool add(const path &parent_path, std::unique_ptr<ElementT> &&e) bool add(const path &parent_path, std::unique_ptr<ElementT> &&e)
{ {
@@ -106,6 +110,23 @@ opt_ref<ElementT> diagram::find(diagram_element::id_t id) const
return {}; return {};
} }
template <typename ElementT>
std::vector<opt_ref<ElementT>> diagram::find(
const common::string_or_regex &pattern) const
{
std::vector<opt_ref<ElementT>> result;
for (const auto &element : element_view<ElementT>::view()) {
const auto full_name = element.get().full_name(false);
if (pattern == full_name) {
result.emplace_back(element);
}
}
return result;
}
template <typename ElementT> template <typename ElementT>
bool diagram::add_with_namespace_path(std::unique_ptr<ElementT> &&p) bool diagram::add_with_namespace_path(std::unique_ptr<ElementT> &&p)
{ {

View File

@@ -68,4 +68,14 @@ diagrams:
type: class type: class
include: include:
context: context:
- r: '[A|B]' - r: '[A|B]'
regex_dependencies_test:
type: class
include:
dependencies:
- r: 'A21|B1'
regex_dependants_test:
type: class
include:
dependants:
- r: 'A|B'

View File

@@ -541,6 +541,174 @@ TEST_CASE("Test context regexp filter", "[unit-test]")
CHECK(!filter.should_include(*diagram.find<class_>("C1"))); CHECK(!filter.should_include(*diagram.find<class_>("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<class_>(config.using_namespace());
c->set_name("A");
c->set_id(to_id("A"s));
diagram.add(namespace_{}, std::move(c));
c = std::make_unique<class_>(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<class_>(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<class_>(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<class_>(config.using_namespace());
c->set_name("B");
c->set_id(to_id("B"s));
diagram.add(namespace_{}, std::move(c));
c = std::make_unique<class_>(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<class_>(config.using_namespace());
c->set_name("C");
c->set_id(to_id("C"s));
diagram.add(namespace_{}, std::move(c));
c = std::make_unique<class_>(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<class_>("A")));
CHECK(!filter.should_include(*diagram.find<class_>("A1")));
CHECK(filter.should_include(*diagram.find<class_>("A2")));
CHECK(filter.should_include(*diagram.find<class_>("A21")));
CHECK(filter.should_include(*diagram.find<class_>("B")));
CHECK(filter.should_include(*diagram.find<class_>("B1")));
CHECK(!filter.should_include(*diagram.find<class_>("C")));
CHECK(!filter.should_include(*diagram.find<class_>("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<class_>(config.using_namespace());
c->set_name("A");
c->set_id(to_id("A"s));
diagram.add(namespace_{}, std::move(c));
c = std::make_unique<class_>(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<class_>(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<class_>(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<class_>(config.using_namespace());
c->set_name("B");
c->set_id(to_id("B"s));
diagram.add(namespace_{}, std::move(c));
c = std::make_unique<class_>(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<class_>(config.using_namespace());
c->set_name("C");
c->set_id(to_id("C"s));
diagram.add(namespace_{}, std::move(c));
c = std::make_unique<class_>(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<class_>("A")));
CHECK(filter.should_include(*diagram.find<class_>("A1")));
CHECK(filter.should_include(*diagram.find<class_>("A2")));
CHECK(filter.should_include(*diagram.find<class_>("A21")));
CHECK(filter.should_include(*diagram.find<class_>("B")));
CHECK(filter.should_include(*diagram.find<class_>("B1")));
CHECK(!filter.should_include(*diagram.find<class_>("C")));
CHECK(!filter.should_include(*diagram.find<class_>("C1")));
}
/// ///
/// Main test function /// Main test function
/// ///