Added regex support to context filter

This commit is contained in:
Bartek Kryza
2023-06-08 18:55:10 +02:00
parent b0501d4bfb
commit 658bceee4b
6 changed files with 154 additions and 83 deletions

View File

@@ -430,7 +430,8 @@ tvl::value_t access_filter::match(
[&a](const auto &access) { return a == access; }); [&a](const auto &access) { return a == access; });
} }
context_filter::context_filter(filter_t type, std::vector<std::string> context) context_filter::context_filter(
filter_t type, std::vector<common::string_or_regex> context)
: filter_visitor{type} : filter_visitor{type}
, context_{std::move(context)} , context_{std::move(context)}
{ {
@@ -447,48 +448,50 @@ tvl::value_t context_filter::match(const diagram &d, const element &e) const
return {}; return {};
return tvl::any_of(context_.begin(), context_.end(), return tvl::any_of(context_.begin(), context_.end(),
[&e, &d](const auto &context_root_name) { [&e, &d](const auto &context_root_pattern) {
const auto &context_root = const auto &context_roots =
static_cast<const class_diagram::model::diagram &>(d) static_cast<const class_diagram::model::diagram &>(d)
.find<class_diagram::model::class_>(context_root_name); .find<class_diagram::model::class_>(context_root_pattern);
if (context_root.has_value()) { for (auto &context_root : context_roots) {
// This is a direct match to the context root if (context_root.has_value()) {
if (context_root.value().id() == e.id()) // This is a direct match to the context root
return true; if (context_root.value().id() == e.id())
return true;
// Return a positive match if the element e is in a direct // Return a positive match if the element e is in a direct
// relationship with any of the context_root's // relationship with any of the context_root's
for (const relationship &rel : for (const relationship &rel :
context_root.value().relationships()) { context_root.value().relationships()) {
if (d.should_include(rel.type()) && if (d.should_include(rel.type()) &&
rel.destination() == e.id()) rel.destination() == e.id())
return true;
}
for (const relationship &rel : e.relationships()) {
if (d.should_include(rel.type()) &&
rel.destination() == context_root.value().id())
return true;
}
// Return a positive match if the context_root is a parent
// of the element
for (const class_diagram::model::class_parent &p :
context_root.value().parents()) {
if (p.name() == e.full_name(false))
return true;
}
if (dynamic_cast<const class_diagram::model::class_ *>(&e) !=
nullptr) {
for (const class_diagram::model::class_parent &p :
static_cast<const class_diagram::model::class_ &>(e)
.parents()) {
if (p.name() == context_root.value().full_name(false))
return true; return true;
} }
for (const relationship &rel : e.relationships()) {
if (d.should_include(rel.type()) &&
rel.destination() == context_root.value().id())
return true;
}
// Return a positive match if the context_root is a parent
// of the element
for (const class_diagram::model::class_parent &p :
context_root.value().parents()) {
if (p.name() == e.full_name(false))
return true;
}
if (dynamic_cast<const class_diagram::model::class_ *>(
&e) != nullptr) {
for (const class_diagram::model::class_parent &p :
static_cast<const class_diagram::model::class_ &>(e)
.parents()) {
if (p.name() ==
context_root.value().full_name(false))
return true;
}
}
} }
} }
return false; return false;
}); });
} }

View File

@@ -363,14 +363,14 @@ private:
}; };
struct context_filter : public filter_visitor { struct context_filter : public filter_visitor {
context_filter(filter_t type, std::vector<std::string> context); context_filter(filter_t type, std::vector<common::string_or_regex> context);
~context_filter() override = default; ~context_filter() override = default;
tvl::value_t match(const diagram &d, const element &r) const override; tvl::value_t match(const diagram &d, const element &r) const override;
private: private:
std::vector<std::string> context_; std::vector<common::string_or_regex> context_;
}; };
struct paths_filter : public filter_visitor { struct paths_filter : public filter_visitor {

View File

@@ -127,6 +127,18 @@ public:
return *value_; return *value_;
} }
T &operator*()
{
assert(value_ != nullptr);
return *value_;
}
const T &operator*() const
{
assert(value_ != nullptr);
return *value_;
}
void reset() { value_ = nullptr; } void reset() { value_ = nullptr; }
T *get() const { return value_; } T *get() const { return value_; }

View File

@@ -107,7 +107,7 @@ struct filter {
std::vector<std::string> dependencies; std::vector<std::string> dependencies;
std::vector<std::string> context; std::vector<common::string_or_regex> context;
std::vector<std::filesystem::path> paths; std::vector<std::filesystem::path> paths;

View File

@@ -63,4 +63,9 @@ diagrams:
type: class type: class
include: include:
specializations: specializations:
- r: 'A<int,.+>' - r: 'A<int,.+>'
regex_context_test:
type: class
include:
context:
- r: '[A|B]'

View File

@@ -293,23 +293,9 @@ TEST_CASE("Test subclasses regexp filter", "[unit-test]")
diagram_filter filter(diagram, config); diagram_filter filter(diagram, config);
c = std::make_unique<class_>(config.using_namespace()); CHECK(filter.should_include(*diagram.find<class_>("ns1::ns2::A1")));
c->set_namespace(namespace_{"ns1::ns2"}); CHECK(filter.should_include(*diagram.find<class_>("ns1::ns2::B1")));
c->set_name("A1"); CHECK(!filter.should_include(*diagram.find<class_>("ns1::ns2::C1")));
c->set_id(to_id("ns1::ns2::A1"s));
CHECK(filter.should_include(*c));
c = std::make_unique<class_>(config.using_namespace());
c->set_namespace(namespace_{"ns1::ns2"});
c->set_name("B1");
c->set_id(to_id("ns1::ns2::B1"s));
CHECK(filter.should_include(*c));
c = std::make_unique<class_>(config.using_namespace());
c->set_namespace(namespace_{"ns1::ns2"});
c->set_name("C1");
c->set_id(to_id("ns1::ns2::C1"s));
CHECK(!filter.should_include(*c));
} }
TEST_CASE("Test parents regexp filter", "[unit-test]") TEST_CASE("Test parents regexp filter", "[unit-test]")
@@ -397,23 +383,9 @@ TEST_CASE("Test parents regexp filter", "[unit-test]")
diagram_filter filter(diagram, config); diagram_filter filter(diagram, config);
c = std::make_unique<class_>(config.using_namespace()); CHECK(filter.should_include(*diagram.find<class_>("ns1::ns2::BaseA")));
c->set_namespace(namespace_{"ns1::ns2"}); CHECK(filter.should_include(*diagram.find<class_>("ns1::ns2::BaseB")));
c->set_name("BaseA"); CHECK(!filter.should_include(*diagram.find<class_>("ns1::ns2::Common")));
c->set_id(to_id("ns1::ns2::BaseA"s));
CHECK(filter.should_include(*c));
c = std::make_unique<class_>(config.using_namespace());
c->set_namespace(namespace_{"ns1::ns2"});
c->set_name("BaseB");
c->set_id(to_id("ns1::ns2::BaseB"s));
CHECK(filter.should_include(*c));
c = std::make_unique<class_>(config.using_namespace());
c->set_namespace(namespace_{"ns1::ns2"});
c->set_name("Common");
c->set_id(to_id("ns1::ns2::Common"s));
CHECK(!filter.should_include(*c));
} }
TEST_CASE("Test specializations regexp filter", "[unit-test]") TEST_CASE("Test specializations regexp filter", "[unit-test]")
@@ -476,18 +448,97 @@ TEST_CASE("Test specializations regexp filter", "[unit-test]")
diagram_filter filter(diagram, config); diagram_filter filter(diagram, config);
c = std::make_unique<class_>(config.using_namespace()); CHECK(filter.should_include(*diagram.find<class_>("A<int,std::string>")));
CHECK(!filter.should_include(*diagram.find<class_>("A<double>")));
}
TEST_CASE("Test context 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_context_test"];
clanguml::class_diagram::model::diagram diagram;
auto c = std::make_unique<class_>(config.using_namespace());
c->set_name("A"); c->set_name("A");
c->add_template(template_parameter::make_argument("int")); c->set_id(to_id("A"s));
c->add_template(template_parameter::make_argument("std::string")); diagram.add(namespace_{}, std::move(c));
c->set_id(to_id("A<int,std::string>"s));
CHECK(filter.should_include(*c));
c = std::make_unique<class_>(config.using_namespace()); c = std::make_unique<class_>(config.using_namespace());
c->set_name("A"); c->set_name("A1");
c->add_template(template_parameter::make_argument("double")); c->set_id(to_id("A1"s));
c->set_id(to_id("A<double>"s)); c->add_relationship(
CHECK(!filter.should_include(*c)); relationship{relationship_t::kAssociation, 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::kAssociation, 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::kAssociation, 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")));
} }
/// ///