Added initial version of advanced diagram filter config (#289)

This commit is contained in:
Bartek Kryza
2024-06-25 17:26:14 +02:00
parent 19bb8ae1ca
commit 40851b395e
11 changed files with 585 additions and 79 deletions

View File

@@ -169,22 +169,116 @@ anyof_filter::anyof_filter(
tvl::value_t anyof_filter::match( tvl::value_t anyof_filter::match(
const diagram &d, const common::model::element &e) const const diagram &d, const common::model::element &e) const
{ {
return tvl::any_of(filters_.begin(), filters_.end(), return match_anyof(d, e);
[&d, &e](const auto &f) { return f->match(d, e); }); }
tvl::value_t anyof_filter::match(
const diagram &d, const common::model::relationship_t &r) const
{
return match_anyof(d, r);
}
tvl::value_t anyof_filter::match(
const diagram &d, const common::model::access_t &a) const
{
return match_anyof(d, a);
}
tvl::value_t anyof_filter::match(
const diagram &d, const common::model::namespace_ &ns) const
{
return match_anyof(d, ns);
}
tvl::value_t anyof_filter::match(
const diagram &d, const common::model::source_file &f) const
{
return match_anyof(d, f);
}
tvl::value_t anyof_filter::match(
const diagram &d, const common::model::source_location &f) const
{
return match_anyof(d, f);
}
tvl::value_t anyof_filter::match(
const diagram &d, const class_diagram::model::class_method &m) const
{
return match_anyof(d, m);
}
tvl::value_t anyof_filter::match(
const diagram &d, const class_diagram::model::class_member &m) const
{
return match_anyof(d, m);
} }
tvl::value_t anyof_filter::match( tvl::value_t anyof_filter::match(
const diagram &d, const sequence_diagram::model::participant &p) const const diagram &d, const sequence_diagram::model::participant &p) const
{ {
return tvl::any_of(filters_.begin(), filters_.end(), return match_anyof(d, p);
[&d, &p](const auto &f) { return f->match(d, p); });
} }
tvl::value_t anyof_filter::match( allof_filter::allof_filter(
const diagram &d, const common::model::source_file &e) const filter_t type, std::vector<std::unique_ptr<filter_visitor>> filters)
: filter_visitor{type}
, filters_{std::move(filters)}
{ {
return tvl::any_of(filters_.begin(), filters_.end(), }
[&d, &e](const auto &f) { return f->match(d, e); });
tvl::value_t allof_filter::match(
const diagram &d, const common::model::element &e) const
{
return match_allof(d, e);
}
tvl::value_t allof_filter::match(
const diagram &d, const common::model::relationship_t &r) const
{
return match_allof(d, r);
}
tvl::value_t allof_filter::match(
const diagram &d, const common::model::access_t &a) const
{
return match_allof(d, a);
}
tvl::value_t allof_filter::match(
const diagram &d, const common::model::namespace_ &ns) const
{
return match_allof(d, ns);
}
tvl::value_t allof_filter::match(
const diagram &d, const common::model::source_file &f) const
{
return match_allof(d, f);
}
tvl::value_t allof_filter::match(
const diagram &d, const common::model::source_location &f) const
{
return match_allof(d, f);
}
tvl::value_t allof_filter::match(
const diagram &d, const class_diagram::model::class_method &m) const
{
return match_allof(d, m);
}
tvl::value_t allof_filter::match(
const diagram &d, const class_diagram::model::class_member &m) const
{
return match_allof(d, m);
}
tvl::value_t allof_filter::match(
const diagram &d, const sequence_diagram::model::participant &p) const
{
return match_allof(d, p);
} }
namespace_filter::namespace_filter( namespace_filter::namespace_filter(
@@ -978,6 +1072,15 @@ diagram_filter::diagram_filter(const common::model::diagram &d,
{ {
} }
void diagram_filter::add_filter(
filter_t filter_type, std::unique_ptr<filter_visitor> fv)
{
if (filter_type == filter_t::kInclusive)
add_inclusive_filter(std::move(fv));
else
add_exclusive_filter(std::move(fv));
}
void diagram_filter::add_inclusive_filter(std::unique_ptr<filter_visitor> fv) void diagram_filter::add_inclusive_filter(std::unique_ptr<filter_visitor> fv)
{ {
inclusive_.emplace_back(std::move(fv)); inclusive_.emplace_back(std::move(fv));

View File

@@ -128,12 +128,81 @@ struct anyof_filter : public filter_visitor {
const diagram &d, const common::model::element &e) const override; const diagram &d, const common::model::element &e) const override;
tvl::value_t match(const diagram &d, tvl::value_t match(const diagram &d,
const sequence_diagram::model::participant &p) const override; const common::model::relationship_t &r) const override;
tvl::value_t match( tvl::value_t match(
const diagram &d, const common::model::source_file &e) const override; const diagram &d, const common::model::access_t &a) const override;
tvl::value_t match(
const diagram &d, const common::model::namespace_ &ns) const override;
tvl::value_t match(
const diagram &d, const common::model::source_file &f) const override;
tvl::value_t match(const diagram &d,
const common::model::source_location &f) const override;
tvl::value_t match(const diagram &d,
const class_diagram::model::class_method &m) const override;
tvl::value_t match(const diagram &d,
const class_diagram::model::class_member &m) const override;
tvl::value_t match(const diagram &d,
const sequence_diagram::model::participant &p) const override;
private: private:
template <typename E>
tvl::value_t match_anyof(const diagram &d, const E &element) const
{
return tvl::any_of(filters_.begin(), filters_.end(),
[&d, &element](const auto &f) { return f->match(d, element); });
}
std::vector<std::unique_ptr<filter_visitor>> filters_;
};
struct allof_filter : public filter_visitor {
allof_filter(
filter_t type, std::vector<std::unique_ptr<filter_visitor>> filters);
~allof_filter() override = default;
tvl::value_t match(
const diagram &d, const common::model::element &e) const override;
tvl::value_t match(const diagram &d,
const common::model::relationship_t &r) const override;
tvl::value_t match(
const diagram &d, const common::model::access_t &a) const override;
tvl::value_t match(
const diagram &d, const common::model::namespace_ &ns) const override;
tvl::value_t match(
const diagram &d, const common::model::source_file &f) const override;
tvl::value_t match(const diagram &d,
const common::model::source_location &f) const override;
tvl::value_t match(const diagram &d,
const class_diagram::model::class_method &m) const override;
tvl::value_t match(const diagram &d,
const class_diagram::model::class_member &m) const override;
tvl::value_t match(const diagram &d,
const sequence_diagram::model::participant &p) const override;
private:
template <typename E>
tvl::value_t match_allof(const diagram &d, const E &element) const
{
return tvl::all_of(filters_.begin(), filters_.end(),
[&d, &element](const auto &f) { return f->match(d, element); });
}
std::vector<std::unique_ptr<filter_visitor>> filters_; std::vector<std::unique_ptr<filter_visitor>> filters_;
}; };
@@ -681,6 +750,8 @@ public:
diagram_filter(const common::model::diagram &d, const config::diagram &c, diagram_filter(const common::model::diagram &d, const config::diagram &c,
private_constructor_tag_t unused); private_constructor_tag_t unused);
void add_filter(filter_t filter_type, std::unique_ptr<filter_visitor> fv);
/** /**
* Add inclusive filter. * Add inclusive filter.
* *

View File

@@ -44,92 +44,92 @@ using source_file_dependency_filter_t =
common::model::source_file, std::string, common::model::source_file>; common::model::source_file, std::string, common::model::source_file>;
} }
void basic_diagram_filter_initializer::initialize( void basic_diagram_filter_initializer::initialize()
const config::diagram &c, diagram_filter &df)
{ {
// Process inclusive filters // Process inclusive filters
if (c.include) { if (diagram_config.include) {
df.add_inclusive_filter(std::make_unique<namespace_filter>( df.add_inclusive_filter(std::make_unique<namespace_filter>(
filter_t::kInclusive, c.include().namespaces)); filter_t::kInclusive, diagram_config.include().namespaces));
df.add_inclusive_filter(std::make_unique<modules_filter>( df.add_inclusive_filter(std::make_unique<modules_filter>(
filter_t::kInclusive, c.include().modules)); filter_t::kInclusive, diagram_config.include().modules));
df.add_inclusive_filter(std::make_unique<module_access_filter>( df.add_inclusive_filter(std::make_unique<module_access_filter>(
filter_t::kInclusive, c.include().module_access)); filter_t::kInclusive, diagram_config.include().module_access));
df.add_inclusive_filter(std::make_unique<relationship_filter>( df.add_inclusive_filter(std::make_unique<relationship_filter>(
filter_t::kInclusive, c.include().relationships)); filter_t::kInclusive, diagram_config.include().relationships));
df.add_inclusive_filter(std::make_unique<access_filter>( df.add_inclusive_filter(std::make_unique<access_filter>(
filter_t::kInclusive, c.include().access)); filter_t::kInclusive, diagram_config.include().access));
df.add_inclusive_filter(std::make_unique<paths_filter>( df.add_inclusive_filter(std::make_unique<paths_filter>(
filter_t::kInclusive, c.root_directory(), c.include().paths)); filter_t::kInclusive, diagram_config.root_directory(),
diagram_config.include().paths));
df.add_inclusive_filter( df.add_inclusive_filter(
std::make_unique<class_method_filter>(filter_t::kInclusive, std::make_unique<class_method_filter>(filter_t::kInclusive,
std::make_unique<access_filter>( std::make_unique<access_filter>(
filter_t::kInclusive, c.include().access), filter_t::kInclusive, diagram_config.include().access),
std::make_unique<method_type_filter>( std::make_unique<method_type_filter>(filter_t::kInclusive,
filter_t::kInclusive, c.include().method_types))); diagram_config.include().method_types)));
df.add_inclusive_filter( df.add_inclusive_filter(
std::make_unique<class_member_filter>(filter_t::kInclusive, std::make_unique<class_member_filter>(filter_t::kInclusive,
std::make_unique<access_filter>( std::make_unique<access_filter>(
filter_t::kInclusive, c.include().access))); filter_t::kInclusive, diagram_config.include().access)));
// Include any of these matches even if one them does not match // Include any of these matches even if one them does not match
std::vector<std::unique_ptr<filter_visitor>> element_filters; std::vector<std::unique_ptr<filter_visitor>> element_filters;
element_filters.emplace_back(std::make_unique<element_filter>( element_filters.emplace_back(std::make_unique<element_filter>(
filter_t::kInclusive, c.include().elements)); filter_t::kInclusive, diagram_config.include().elements));
element_filters.emplace_back(std::make_unique<element_type_filter>( element_filters.emplace_back(std::make_unique<element_type_filter>(
filter_t::kInclusive, c.include().element_types)); filter_t::kInclusive, diagram_config.include().element_types));
if (c.type() == diagram_t::kClass) { if (diagram_config.type() == diagram_t::kClass) {
element_filters.emplace_back(std::make_unique<subclass_filter>( element_filters.emplace_back(std::make_unique<subclass_filter>(
filter_t::kInclusive, c.include().subclasses)); filter_t::kInclusive, diagram_config.include().subclasses));
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, diagram_config.include().parents));
element_filters.emplace_back( element_filters.emplace_back(
std::make_unique<specializations_filter_t>(filter_t::kInclusive, std::make_unique<specializations_filter_t>(filter_t::kInclusive,
relationship_t::kInstantiation, relationship_t::kInstantiation,
c.include().specializations)); diagram_config.include().specializations));
element_filters.emplace_back( element_filters.emplace_back(
std::make_unique<class_dependants_filter_t>( std::make_unique<class_dependants_filter_t>(
filter_t::kInclusive, relationship_t::kDependency, filter_t::kInclusive, relationship_t::kDependency,
c.include().dependants)); diagram_config.include().dependants));
element_filters.emplace_back( element_filters.emplace_back(
std::make_unique<class_dependencies_filter_t>( std::make_unique<class_dependencies_filter_t>(
filter_t::kInclusive, relationship_t::kDependency, filter_t::kInclusive, relationship_t::kDependency,
c.include().dependencies, true)); diagram_config.include().dependencies, true));
} }
else if (c.type() == diagram_t::kSequence) { else if (diagram_config.type() == diagram_t::kSequence) {
element_filters.emplace_back(std::make_unique<callee_filter>( element_filters.emplace_back(std::make_unique<callee_filter>(
filter_t::kInclusive, c.include().callee_types)); filter_t::kInclusive, diagram_config.include().callee_types));
} }
else if (c.type() == diagram_t::kPackage) { else if (diagram_config.type() == diagram_t::kPackage) {
element_filters.emplace_back( element_filters.emplace_back(
std::make_unique<package_dependants_filter_t>( std::make_unique<package_dependants_filter_t>(
filter_t::kInclusive, relationship_t::kDependency, filter_t::kInclusive, relationship_t::kDependency,
c.include().dependants)); diagram_config.include().dependants));
element_filters.emplace_back( element_filters.emplace_back(
std::make_unique<package_dependencies_filter_t>( std::make_unique<package_dependencies_filter_t>(
filter_t::kInclusive, relationship_t::kDependency, filter_t::kInclusive, relationship_t::kDependency,
c.include().dependencies, true)); diagram_config.include().dependencies, true));
} }
else if (c.type() == diagram_t::kInclude) { else if (diagram_config.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 : diagram_config.include().dependants) {
if (auto p = path.get<std::string>(); p.has_value()) { if (auto p = path.get<std::string>(); p.has_value()) {
const std::filesystem::path dep_path{*p}; const std::filesystem::path dep_path{*p};
dependants.emplace_back( dependants.emplace_back(
@@ -137,7 +137,7 @@ void basic_diagram_filter_initializer::initialize(
} }
} }
for (auto &&path : c.include().dependencies) { for (auto &&path : diagram_config.include().dependencies) {
if (auto p = path.get<std::string>(); p.has_value()) { if (auto p = path.get<std::string>(); p.has_value()) {
const std::filesystem::path dep_path{*p}; const std::filesystem::path dep_path{*p};
dependencies.emplace_back( dependencies.emplace_back(
@@ -157,90 +157,91 @@ void basic_diagram_filter_initializer::initialize(
} }
element_filters.emplace_back(std::make_unique<context_filter>( element_filters.emplace_back(std::make_unique<context_filter>(
filter_t::kInclusive, c.include().context)); filter_t::kInclusive, diagram_config.include().context));
df.add_inclusive_filter(std::make_unique<anyof_filter>( df.add_inclusive_filter(std::make_unique<anyof_filter>(
filter_t::kInclusive, std::move(element_filters))); filter_t::kInclusive, std::move(element_filters)));
} }
// Process exclusive filters // Process exclusive filters
if (c.exclude) { if (diagram_config.exclude) {
df.add_exclusive_filter(std::make_unique<namespace_filter>( df.add_exclusive_filter(std::make_unique<namespace_filter>(
filter_t::kExclusive, c.exclude().namespaces)); filter_t::kExclusive, diagram_config.exclude().namespaces));
df.add_exclusive_filter(std::make_unique<modules_filter>( df.add_exclusive_filter(std::make_unique<modules_filter>(
filter_t::kExclusive, c.exclude().modules)); filter_t::kExclusive, diagram_config.exclude().modules));
df.add_exclusive_filter(std::make_unique<module_access_filter>( df.add_exclusive_filter(std::make_unique<module_access_filter>(
filter_t::kExclusive, c.exclude().module_access)); filter_t::kExclusive, diagram_config.exclude().module_access));
df.add_exclusive_filter(std::make_unique<paths_filter>( df.add_exclusive_filter(std::make_unique<paths_filter>(
filter_t::kExclusive, c.root_directory(), c.exclude().paths)); filter_t::kExclusive, diagram_config.root_directory(),
diagram_config.exclude().paths));
df.add_exclusive_filter(std::make_unique<element_filter>( df.add_exclusive_filter(std::make_unique<element_filter>(
filter_t::kExclusive, c.exclude().elements)); filter_t::kExclusive, diagram_config.exclude().elements));
df.add_exclusive_filter(std::make_unique<element_type_filter>( df.add_exclusive_filter(std::make_unique<element_type_filter>(
filter_t::kExclusive, c.exclude().element_types)); filter_t::kExclusive, diagram_config.exclude().element_types));
df.add_exclusive_filter(std::make_unique<relationship_filter>( df.add_exclusive_filter(std::make_unique<relationship_filter>(
filter_t::kExclusive, c.exclude().relationships)); filter_t::kExclusive, diagram_config.exclude().relationships));
df.add_exclusive_filter(std::make_unique<access_filter>( df.add_exclusive_filter(std::make_unique<access_filter>(
filter_t::kExclusive, c.exclude().access)); filter_t::kExclusive, diagram_config.exclude().access));
df.add_exclusive_filter( df.add_exclusive_filter(
std::make_unique<class_method_filter>(filter_t::kExclusive, std::make_unique<class_method_filter>(filter_t::kExclusive,
std::make_unique<access_filter>( std::make_unique<access_filter>(
filter_t::kExclusive, c.exclude().access), filter_t::kExclusive, diagram_config.exclude().access),
std::make_unique<method_type_filter>( std::make_unique<method_type_filter>(filter_t::kExclusive,
filter_t::kExclusive, c.exclude().method_types))); diagram_config.exclude().method_types)));
df.add_exclusive_filter( df.add_exclusive_filter(
std::make_unique<class_member_filter>(filter_t::kExclusive, std::make_unique<class_member_filter>(filter_t::kExclusive,
std::make_unique<access_filter>( std::make_unique<access_filter>(
filter_t::kExclusive, c.exclude().access))); filter_t::kExclusive, diagram_config.exclude().access)));
df.add_exclusive_filter(std::make_unique<subclass_filter>( df.add_exclusive_filter(std::make_unique<subclass_filter>(
filter_t::kExclusive, c.exclude().subclasses)); filter_t::kExclusive, diagram_config.exclude().subclasses));
df.add_exclusive_filter(std::make_unique<parents_filter>( df.add_exclusive_filter(std::make_unique<parents_filter>(
filter_t::kExclusive, c.exclude().parents)); filter_t::kExclusive, diagram_config.exclude().parents));
df.add_exclusive_filter( df.add_exclusive_filter(std::make_unique<specializations_filter_t>(
std::make_unique<specializations_filter_t>(filter_t::kExclusive, filter_t::kExclusive, relationship_t::kInstantiation,
relationship_t::kInstantiation, c.exclude().specializations)); diagram_config.exclude().specializations));
if (c.type() == diagram_t::kClass) { if (diagram_config.type() == diagram_t::kClass) {
df.add_exclusive_filter(std::make_unique<class_dependants_filter_t>( df.add_exclusive_filter(std::make_unique<class_dependants_filter_t>(
filter_t::kExclusive, relationship_t::kDependency, filter_t::kExclusive, relationship_t::kDependency,
c.exclude().dependants)); diagram_config.exclude().dependants));
df.add_exclusive_filter( df.add_exclusive_filter(
std::make_unique<class_dependencies_filter_t>( std::make_unique<class_dependencies_filter_t>(
filter_t::kExclusive, relationship_t::kDependency, filter_t::kExclusive, relationship_t::kDependency,
c.exclude().dependencies, true)); diagram_config.exclude().dependencies, true));
} }
else if (c.type() == diagram_t::kSequence) { else if (diagram_config.type() == diagram_t::kSequence) {
df.add_exclusive_filter(std::make_unique<callee_filter>( df.add_exclusive_filter(std::make_unique<callee_filter>(
filter_t::kExclusive, c.exclude().callee_types)); filter_t::kExclusive, diagram_config.exclude().callee_types));
} }
else if (c.type() == diagram_t::kPackage) { else if (diagram_config.type() == diagram_t::kPackage) {
df.add_exclusive_filter( df.add_exclusive_filter(
std::make_unique<package_dependencies_filter_t>( std::make_unique<package_dependencies_filter_t>(
filter_t::kExclusive, relationship_t::kDependency, filter_t::kExclusive, relationship_t::kDependency,
c.exclude().dependencies, true)); diagram_config.exclude().dependencies, true));
df.add_exclusive_filter( df.add_exclusive_filter(
std::make_unique<package_dependants_filter_t>( std::make_unique<package_dependants_filter_t>(
filter_t::kExclusive, relationship_t::kDependency, filter_t::kExclusive, relationship_t::kDependency,
c.exclude().dependants)); diagram_config.exclude().dependants));
} }
else if (c.type() == diagram_t::kInclude) { else if (diagram_config.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.exclude().dependants) { for (auto &&path : diagram_config.exclude().dependants) {
if (auto p = path.get<std::string>(); p.has_value()) { if (auto p = path.get<std::string>(); p.has_value()) {
std::filesystem::path dep_path{*p}; std::filesystem::path dep_path{*p};
dependants.emplace_back( dependants.emplace_back(
@@ -248,7 +249,7 @@ void basic_diagram_filter_initializer::initialize(
} }
} }
for (auto &&path : c.exclude().dependencies) { for (auto &&path : diagram_config.exclude().dependencies) {
if (auto p = path.get<std::string>(); p.has_value()) { if (auto p = path.get<std::string>(); p.has_value()) {
std::filesystem::path dep_path{*p}; std::filesystem::path dep_path{*p};
dependencies.emplace_back( dependencies.emplace_back(
@@ -268,13 +269,129 @@ void basic_diagram_filter_initializer::initialize(
} }
df.add_exclusive_filter(std::make_unique<context_filter>( df.add_exclusive_filter(std::make_unique<context_filter>(
filter_t::kExclusive, c.exclude().context)); filter_t::kExclusive, diagram_config.exclude().context));
} }
} }
void advanced_diagram_filter_initializer::initialize( std::vector<std::unique_ptr<filter_visitor>>
const config::diagram &c, diagram_filter &df) advanced_diagram_filter_initializer::build(
filter_t filter_type, const config::filter &filter_config)
{ {
std::vector<std::unique_ptr<filter_visitor>> result;
// At any level, only allof, anyof, or a set of other non-operator
// filters can be present
if (filter_config.allof) {
std::vector<std::unique_ptr<filter_visitor>> allof_filters =
build(filter_type, *filter_config.allof);
auto allof_filter = std::make_unique<common::model::allof_filter>(
filter_type, std::move(allof_filters));
result.emplace_back(std::move(allof_filter));
}
if (filter_config.anyof) {
std::vector<std::unique_ptr<filter_visitor>> anyof_filters =
build(filter_type, *filter_config.anyof);
auto anyof_filter = std::make_unique<common::model::anyof_filter>(
filter_type, std::move(anyof_filters));
result.emplace_back(std::move(anyof_filter));
}
add_filter<namespace_filter>(filter_type, filter_config.namespaces, result);
add_filter<modules_filter>(filter_type, filter_config.modules, result);
add_filter<module_access_filter>(
filter_type, filter_config.module_access, result);
add_filter<relationship_filter>(
filter_type, filter_config.relationships, result);
add_filter<access_filter>(filter_type, filter_config.access, result);
if (!filter_config.method_types.empty()) {
result.emplace_back(std::make_unique<class_method_filter>(filter_type,
std::make_unique<access_filter>(
filter_t::kInclusive, filter_config.access),
std::make_unique<method_type_filter>(
filter_t::kInclusive, filter_config.method_types)));
}
if (!filter_config.paths.empty()) {
result.emplace_back(std::make_unique<paths_filter>(
filter_type, diagram_config.root_directory(), filter_config.paths));
}
if (!filter_config.access.empty())
result.emplace_back(std::make_unique<class_member_filter>(filter_type,
std::make_unique<access_filter>(
filter_type, filter_config.access)));
add_filter<element_filter>(filter_type, filter_config.elements, result);
add_filter<element_type_filter>(
filter_type, filter_config.element_types, result);
add_filter<subclass_filter>(filter_type, filter_config.subclasses, result);
add_filter<parents_filter>(filter_type, filter_config.parents, result);
add_edge_filter<specializations_filter_t>(filter_type,
filter_config.specializations, relationship_t::kInstantiation, false,
result);
add_edge_filter<class_dependants_filter_t>(filter_type,
filter_config.dependants, relationship_t::kDependency, false, result);
add_edge_filter<class_dependencies_filter_t>(filter_type,
filter_config.dependencies, relationship_t::kDependency, true, result);
add_filter<callee_filter>(filter_type, filter_config.callee_types, result);
add_edge_filter<package_dependants_filter_t>(filter_type,
filter_config.dependants, relationship_t::kDependency, false, result);
add_edge_filter<package_dependencies_filter_t>(filter_type,
filter_config.dependencies, relationship_t::kDependency, true, result);
add_filter<context_filter>(filter_type, filter_config.context, result);
if (diagram_config.type() == diagram_t::kInclude) {
std::vector<std::string> dependants;
std::vector<std::string> dependencies;
for (auto &&path : filter_config.dependants) {
if (auto p = path.get<std::string>(); p.has_value()) {
const std::filesystem::path dep_path{*p};
dependants.emplace_back(dep_path.lexically_normal().string());
}
}
for (auto &&path : filter_config.dependencies) {
if (auto p = path.get<std::string>(); p.has_value()) {
const std::filesystem::path dep_path{*p};
dependencies.emplace_back(dep_path.lexically_normal().string());
}
}
add_edge_filter<source_file_dependency_filter_t>(filter_type,
dependants, relationship_t::kAssociation, false, result);
add_edge_filter<source_file_dependency_filter_t>(filter_type,
dependencies, relationship_t::kAssociation, true, result);
}
return result;
}
void advanced_diagram_filter_initializer::initialize()
{
if (diagram_config.include) {
auto inclusive_filter =
build(filter_t::kInclusive, diagram_config.include());
for (auto &f : inclusive_filter)
df.add_filter(filter_t::kInclusive, std::move(f));
}
if (diagram_config.exclude) {
auto exclusive_filter =
build(filter_t::kExclusive, diagram_config.exclude());
for (auto &f : exclusive_filter)
df.add_filter(filter_t::kExclusive, std::move(f));
}
} }
} // namespace clanguml::common::model } // namespace clanguml::common::model

View File

@@ -22,12 +22,57 @@
namespace clanguml::common::model { namespace clanguml::common::model {
struct basic_diagram_filter_initializer { class diagram_filter_initializer {
void initialize(const config::diagram &c, diagram_filter &df); public:
diagram_filter_initializer(const config::diagram &c, diagram_filter &filter)
: diagram_config{c}
, df{filter}
{
}
virtual void initialize() = 0;
protected:
const config::diagram &diagram_config;
diagram_filter &df;
}; };
struct advanced_diagram_filter_initializer { class basic_diagram_filter_initializer : public diagram_filter_initializer {
void initialize(const config::diagram &c, diagram_filter &df); public:
using diagram_filter_initializer::diagram_filter_initializer;
void initialize() override;
};
class advanced_diagram_filter_initializer : public diagram_filter_initializer {
public:
using diagram_filter_initializer::diagram_filter_initializer;
void initialize() override;
private:
std::vector<std::unique_ptr<filter_visitor>> build(
filter_t filter_type, const config::filter &filter_config);
template <typename FT, typename T>
void add_filter(const filter_t &filter_type,
const std::vector<T> &filter_config,
std::vector<std::unique_ptr<filter_visitor>> &result)
{
if (!filter_config.empty())
result.emplace_back(
std::make_unique<FT>(filter_type, filter_config));
}
template <typename FT, typename T>
void add_edge_filter(const filter_t &filter_type,
const std::vector<T> &filter_config, relationship_t rt, bool direction,
std::vector<std::unique_ptr<filter_visitor>> &result)
{
if (!filter_config.empty())
result.emplace_back(std::make_unique<FT>(
filter_type, rt, filter_config, direction));
}
}; };
class diagram_filter_factory { class diagram_filter_factory {
@@ -38,9 +83,14 @@ public:
auto filter = std::make_unique<diagram_filter>( auto filter = std::make_unique<diagram_filter>(
d, c, diagram_filter::private_constructor_tag_t{}); d, c, diagram_filter::private_constructor_tag_t{});
basic_diagram_filter_initializer init; if (c.filter_mode() == config::filter_mode_t::basic) {
basic_diagram_filter_initializer init{c, *filter};
init.initialize(c, *filter); init.initialize();
}
else {
advanced_diagram_filter_initializer init{c, *filter};
init.initialize();
}
return filter; return filter;
} }

View File

@@ -186,6 +186,19 @@ std::string to_string(context_direction_t cd)
} }
} }
std::string to_string(filter_mode_t fm)
{
switch (fm) {
case filter_mode_t::basic:
return "basic";
case filter_mode_t::advanced:
return "advanced";
default:
assert(false);
return "";
}
}
std::optional<std::string> plantuml::get_style( std::optional<std::string> plantuml::get_style(
const common::model::relationship_t relationship_type) const const common::model::relationship_t relationship_type) const
{ {
@@ -228,6 +241,7 @@ void inheritable_diagram_options::inherit(
using_module.override(parent.using_module); using_module.override(parent.using_module);
include_relations_also_as_members.override( include_relations_also_as_members.override(
parent.include_relations_also_as_members); parent.include_relations_also_as_members);
filter_mode.override(parent.filter_mode);
include.override(parent.include); include.override(parent.include);
exclude.override(parent.exclude); exclude.override(parent.exclude);
puml.override(parent.puml); puml.override(parent.puml);

View File

@@ -116,6 +116,13 @@ struct plantuml_keyword_mapping_t {
relationships; relationships;
}; };
enum class filter_mode_t {
basic, /*!< Default filter structure without logical operators */
advanced /*!< Advanced filter config with logical operators */
};
std::string to_string(filter_mode_t cp);
/** /**
* @brief PlantUML diagram config section * @brief PlantUML diagram config section
* *
@@ -187,6 +194,9 @@ struct diagram_template {
}; };
struct filter { struct filter {
std::shared_ptr<filter> anyof;
std::shared_ptr<filter> allof;
/*! @brief Namespaces filter /*! @brief Namespaces filter
* *
* Example: * Example:
@@ -544,6 +554,7 @@ struct inheritable_diagram_options {
option<std::string> using_module{"using_module"}; option<std::string> using_module{"using_module"};
option<bool> include_relations_also_as_members{ option<bool> include_relations_also_as_members{
"include_relations_also_as_members", true}; "include_relations_also_as_members", true};
option<filter_mode_t> filter_mode{"filter_mode", filter_mode_t::basic};
option<filter> include{"include"}; option<filter> include{"include"};
option<filter> exclude{"exclude"}; option<filter> exclude{"exclude"};
option<plantuml> puml{"plantuml", option_inherit_mode::kAppend}; option<plantuml> puml{"plantuml", option_inherit_mode::kAppend};

View File

@@ -156,6 +156,8 @@ types:
paths: !optional [string] paths: !optional [string]
method_types: !optional [method_type_filter_t] method_types: !optional [method_type_filter_t]
callee_types: !optional [callee_type_filter_t] callee_types: !optional [callee_type_filter_t]
anyof: !optional filter_t
allof: !optional filter_t
function_location_t: function_location_t:
function: string function: string
marker_location_t: marker_location_t:
@@ -163,6 +165,9 @@ types:
source_location_t: source_location_t:
- function_location_t - function_location_t
- marker_location_t - marker_location_t
filter_mode_t: !variant
- basic
- advanced
class_diagram_t: class_diagram_t:
type: !variant [class] type: !variant [class]
# #
@@ -171,6 +176,7 @@ types:
__parent_path: !optional string __parent_path: !optional string
comment_parser: !optional comment_parser_t comment_parser: !optional comment_parser_t
debug_mode: !optional bool debug_mode: !optional bool
filter_mode: !optional filter_mode_t
exclude: !optional filter_t exclude: !optional filter_t
generate_links: !optional generate_links_t generate_links: !optional generate_links_t
git: !optional git_t git: !optional git_t

View File

@@ -158,6 +158,21 @@ void get_option<clanguml::config::comment_parser_t>(const Node &node,
} }
} }
template <>
void get_option<clanguml::config::filter_mode_t>(const Node &node,
clanguml::config::option<clanguml::config::filter_mode_t> &option)
{
if (node[option.name]) {
const auto &val = node[option.name].as<std::string>();
if (val == "basic")
option.set(clanguml::config::filter_mode_t::basic);
else if (val == "advanced")
option.set(clanguml::config::filter_mode_t::advanced);
else
throw std::runtime_error("Invalid comment_parser value: " + val);
}
}
template <> template <>
void get_option<std::map<std::string, clanguml::config::diagram_template>>( void get_option<std::map<std::string, clanguml::config::diagram_template>>(
const Node &node, const Node &node,
@@ -521,6 +536,14 @@ template <> struct convert<namespace_or_regex> {
template <> struct convert<filter> { template <> struct convert<filter> {
static bool decode(const Node &node, filter &rhs) static bool decode(const Node &node, filter &rhs)
{ {
if (node["anyof"]) {
rhs.anyof = std::make_unique<filter>(node["anyof"].as<filter>());
}
if (node["allof"]) {
rhs.anyof = std::make_unique<filter>(node["anyof"].as<filter>());
}
if (node["namespaces"]) { if (node["namespaces"]) {
auto namespace_list = auto namespace_list =
node["namespaces"].as<decltype(rhs.namespaces)>(); node["namespaces"].as<decltype(rhs.namespaces)>();
@@ -631,6 +654,7 @@ template <typename T> bool decode_diagram(const Node &node, T &rhs)
get_option(node, rhs.glob); get_option(node, rhs.glob);
get_option(node, rhs.using_namespace); get_option(node, rhs.using_namespace);
get_option(node, rhs.using_module); get_option(node, rhs.using_module);
get_option(node, rhs.filter_mode);
get_option(node, rhs.include); get_option(node, rhs.include);
get_option(node, rhs.exclude); get_option(node, rhs.exclude);
get_option(node, rhs.puml); get_option(node, rhs.puml);
@@ -849,6 +873,7 @@ template <> struct convert<config> {
get_option(node, rhs.glob); get_option(node, rhs.glob);
get_option(node, rhs.using_namespace); get_option(node, rhs.using_namespace);
get_option(node, rhs.using_module); get_option(node, rhs.using_module);
get_option(node, rhs.filter_mode);
get_option(node, rhs.output_directory); get_option(node, rhs.output_directory);
get_option(node, rhs.query_driver); get_option(node, rhs.query_driver);
get_option(node, rhs.allow_empty_diagrams); get_option(node, rhs.allow_empty_diagrams);

View File

@@ -93,6 +93,7 @@ set(TEST_NAMES
test_config test_config
test_cli_handler test_cli_handler
test_filters test_filters
test_filters_advanced
test_thread_pool_executor test_thread_pool_executor
test_query_driver_output_extractor test_query_driver_output_extractor
test_progress_indicator) test_progress_indicator)

View File

@@ -0,0 +1,20 @@
compilation_database_dir: debug
output_directory: output
diagrams:
anyof_test:
type: class
relative_to: ../../../src
glob:
- src/**/*.cc
- src/**/*.h
filter_mode: advanced
include:
anyof:
namespaces:
- ns1::ns2
elements:
- std::thread
exclude:
anyof:
namespaces:
- ns1::ns2::detail

View File

@@ -0,0 +1,88 @@
/**
* @file tests/test_filters_advanced.cc
*
* Copyright (c) 2021-2024 Bartek Kryza <bkryza@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define DOCTEST_CONFIG_IMPLEMENT
#include "doctest/doctest.h"
#include "class_diagram/model/class.h"
#include "cli/cli_handler.h"
#include "common/model/filters/diagram_filter_factory.h"
#include "common/model/source_file.h"
#include "config/config.h"
#include "include_diagram/model/diagram.h"
#include "sequence_diagram/model/diagram.h"
#include <filesystem>
TEST_CASE("Test advanced diagram filter anyof")
{
using clanguml::common::model::diagram_filter;
using clanguml::common::model::diagram_filter_factory;
using clanguml::common::model::namespace_;
using clanguml::common::model::source_file;
using clanguml::config::filter_mode_t;
auto cfg =
clanguml::config::load("./test_config_data/filters_advanced.yml");
auto &config = *cfg.diagrams["anyof_test"];
clanguml::include_diagram::model::diagram diagram;
auto filter_ptr = diagram_filter_factory::create(diagram, config);
diagram_filter &filter = *filter_ptr;
CHECK(config.filter_mode() == filter_mode_t::advanced);
CHECK(filter.should_include(namespace_{"ns1::ns2"}));
CHECK_FALSE(filter.should_include(namespace_{"std::string"}));
clanguml::common::model::element std_thread{{}};
std_thread.set_namespace(namespace_{"std"});
std_thread.set_name("thread");
CHECK(filter.should_include(std_thread));
std_thread.set_name("jthread");
CHECK_FALSE(filter.should_include(std_thread));
CHECK_FALSE(filter.should_include(namespace_{"ns1::ns2::detail"}));
}
///
/// Main test function
///
int main(int argc, char *argv[])
{
doctest::Context context;
context.applyCommandLine(argc, argv);
clanguml::cli::cli_handler clih;
std::vector<const char *> argvv = {
"clang-uml", "--config", "./test_config_data/simple.yml"};
argvv.push_back("-q");
clih.handle_options(argvv.size(), argvv.data());
int res = context.run();
if (context.shouldExit())
return res;
return res;
}