Extended context filter config schema to accept optional radius parameter

This commit is contained in:
Bartek Kryza
2023-11-09 23:09:17 +01:00
parent 237ef26389
commit 055897f11b
6 changed files with 95 additions and 35 deletions

View File

@@ -534,39 +534,35 @@ 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, context_filter::context_filter(
std::vector<common::string_or_regex> context, unsigned radius) filter_t type, std::vector<config::context_config> context, unsigned radius)
: filter_visitor{type} : filter_visitor{type}
, radius_{radius}
, context_{std::move(context)} , context_{std::move(context)}
{ {
} }
void context_filter::initialize(const diagram &d) const void context_filter::initialize_effective_context(
const diagram &d, unsigned idx) const
{ {
if (initialized_)
return;
initialized_ = true;
bool effective_context_extended{true}; bool effective_context_extended{true};
auto &effective_context = effective_contexts_[idx];
// First add to effective context all elements matching context_ // First add to effective context all elements matching context_
for (const auto &c : context_) { const auto &context = context_.at(idx);
const auto &context_matches = const auto &context_matches =
static_cast<const class_diagram::model::diagram &>(d) static_cast<const class_diagram::model::diagram &>(d)
.find<class_diagram::model::class_>(c); .find<class_diagram::model::class_>(context.pattern);
for (const auto &maybe_match : context_matches) { for (const auto &maybe_match : context_matches) {
if (maybe_match) if (maybe_match)
effective_context_.emplace(maybe_match.value().id()); effective_context.emplace(maybe_match.value().id());
}
} }
// Now repeat radius times - extend the effective context with elements // Now repeat radius times - extend the effective context with elements
// matching in direct relationship to what is in context // matching in direct relationship to what is in context
auto radius_counter = radius_; auto radius_counter = context.radius;
decltype(effective_context_) current_iteration_context; std::set<clanguml::common::id_t> current_iteration_context;
while (radius_counter > 0 && effective_context_extended) { while (radius_counter > 0 && effective_context_extended) {
// If at any iteration the effective context was not extended - we // If at any iteration the effective context was not extended - we
@@ -581,13 +577,13 @@ void context_filter::initialize(const diagram &d) const
// 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 effective context elements ... // relationship with any of the effective context elements ...
for (const relationship &rel : c.get().relationships()) { for (const relationship &rel : c.get().relationships()) {
for (const auto &ec : effective_context_) { for (const auto &ec : effective_context) {
if (d.should_include(rel.type()) && rel.destination() == ec) if (d.should_include(rel.type()) && rel.destination() == ec)
current_iteration_context.emplace(c.get().id()); current_iteration_context.emplace(c.get().id());
} }
} }
// ... or vice-versa // ... or vice-versa
for (const auto &ec : effective_context_) { for (const auto &ec : effective_context) {
const auto &maybe_class = const auto &maybe_class =
static_cast<const class_diagram::model::diagram &>(d) static_cast<const class_diagram::model::diagram &>(d)
.find<class_diagram::model::class_>(ec); .find<class_diagram::model::class_>(ec);
@@ -608,7 +604,7 @@ void context_filter::initialize(const diagram &d) const
// effective context... // effective context...
for (const class_diagram::model::class_parent &p : for (const class_diagram::model::class_parent &p :
c.get().parents()) { c.get().parents()) {
for (const auto &ec : effective_context_) { for (const auto &ec : effective_context) {
const auto &maybe_parent = const auto &maybe_parent =
static_cast<const class_diagram::model::diagram &>(d) static_cast<const class_diagram::model::diagram &>(d)
.find<class_diagram::model::class_>(ec); .find<class_diagram::model::class_>(ec);
@@ -622,7 +618,7 @@ void context_filter::initialize(const diagram &d) const
} }
// .. or vice-versa // .. or vice-versa
for (const auto &ec : effective_context_) { for (const auto &ec : effective_context) {
const auto &maybe_child = const auto &maybe_child =
static_cast<const class_diagram::model::diagram &>(d) static_cast<const class_diagram::model::diagram &>(d)
.find<class_diagram::model::class_>(ec); .find<class_diagram::model::class_>(ec);
@@ -637,15 +633,29 @@ void context_filter::initialize(const diagram &d) const
} }
for (auto id : current_iteration_context) { for (auto id : current_iteration_context) {
if (effective_context_.count(id) == 0) { if (effective_context.count(id) == 0) {
// Found new element to add to context // Found new element to add to context
effective_context_.emplace(id); effective_context.emplace(id);
effective_context_extended = true; effective_context_extended = true;
} }
} }
} }
} }
void context_filter::initialize(const diagram &d) const
{
if (initialized_)
return;
initialized_ = true;
// Prepare effective_contexts_
for (auto i = 0U; i < context_.size(); i++) {
effective_contexts_.push_back({});
initialize_effective_context(d, i);
}
}
tvl::value_t context_filter::match(const diagram &d, const element &e) const tvl::value_t context_filter::match(const diagram &d, const element &e) const
{ {
if (d.type() != diagram_t::kClass) if (d.type() != diagram_t::kClass)
@@ -658,11 +668,14 @@ tvl::value_t context_filter::match(const diagram &d, const element &e) const
initialize(d); initialize(d);
if (effective_context_.empty()) if (std::all_of(effective_contexts_.begin(), effective_contexts_.end(),
[](const auto &ec) { return ec.empty(); }))
return {}; return {};
if (effective_context_.count(e.id()) > 0) for (const auto &ec : effective_contexts_) {
if (ec.count(e.id()) > 0)
return true; return true;
}
return false; return false;
} }

View File

@@ -447,7 +447,7 @@ private:
* to any of the elements specified in context. * to any of the elements specified in context.
*/ */
struct context_filter : public filter_visitor { struct context_filter : public filter_visitor {
context_filter(filter_t type, std::vector<common::string_or_regex> context, context_filter(filter_t type, std::vector<config::context_config> context,
unsigned radius = 1); unsigned radius = 1);
~context_filter() override = default; ~context_filter() override = default;
@@ -457,15 +457,15 @@ struct context_filter : public filter_visitor {
private: private:
void initialize(const diagram &d) const; void initialize(const diagram &d) const;
const unsigned radius_; void initialize_effective_context(const diagram &d, unsigned idx) const;
std::vector<common::string_or_regex> context_; std::vector<config::context_config> context_;
/*! /*!
* Represents all elements which should belong to the diagram based * Represents all elements which should belong to the diagram based
* on this filter. It is populated by the initialize() method. * on this filter. It is populated by the initialize() method.
*/ */
mutable std::set<clanguml::common::id_t> effective_context_; mutable std::vector<std::set<clanguml::common::id_t>> effective_contexts_;
/*! Flag to mark whether the filter context has been computed */ /*! Flag to mark whether the filter context has been computed */
mutable bool initialized_{false}; mutable bool initialized_{false};

View File

@@ -141,6 +141,11 @@ struct mermaid {
void append(const mermaid &r); void append(const mermaid &r);
}; };
struct context_config {
common::string_or_regex pattern;
unsigned radius{0};
};
/** /**
* @brief Definition of diagram template * @brief Definition of diagram template
*/ */
@@ -316,9 +321,13 @@ struct filter {
* include: * include:
* context: * context:
* - ns1::ns2::ClassA * - ns1::ns2::ClassA
* - r: ns1::ns2::ClassB<.*>
* - match:
* pattern: ns1::ns2::ClassA
* radius: 3
* ``` * ```
*/ */
std::vector<common::string_or_regex> context; std::vector<context_config> context;
/*! @brief Paths filter /*! @brief Paths filter
* *

View File

@@ -112,6 +112,13 @@ types:
- function - function
- function_template - function_template
- lambda - lambda
context_filter_match_t:
match:
radius: int
pattern: regex_or_string_t
context_filter_t:
- regex_or_string_t
- context_filter_match_t
filter_t: filter_t:
namespaces: !optional [regex_or_string_t] namespaces: !optional [regex_or_string_t]
elements: !optional [regex_or_string_t] elements: !optional [regex_or_string_t]
@@ -123,7 +130,7 @@ types:
specializations: !optional [regex_or_string_t] specializations: !optional [regex_or_string_t]
dependants: !optional [regex_or_string_t] dependants: !optional [regex_or_string_t]
dependencies: !optional [regex_or_string_t] dependencies: !optional [regex_or_string_t]
context: !optional [regex_or_string_t] context: !optional [context_filter_t]
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]

View File

@@ -33,6 +33,7 @@ using clanguml::common::model::relationship_t;
using clanguml::config::callee_type; using clanguml::config::callee_type;
using clanguml::config::class_diagram; using clanguml::config::class_diagram;
using clanguml::config::config; using clanguml::config::config;
using clanguml::config::context_config;
using clanguml::config::diagram_template; using clanguml::config::diagram_template;
using clanguml::config::filter; using clanguml::config::filter;
using clanguml::config::generate_links_config; using clanguml::config::generate_links_config;
@@ -419,6 +420,23 @@ template <> struct convert<string_or_regex> {
} }
}; };
template <> struct convert<context_config> {
static bool decode(const Node &node, context_config &rhs)
{
using namespace std::string_literals;
if (node.IsMap() && has_key(node, "match")) {
rhs.radius = node["match"]["radius"].as<unsigned>();
rhs.pattern = node["match"]["radius"].as<string_or_regex>();
}
else {
rhs.radius = 1;
rhs.pattern = node.as<string_or_regex>();
}
return true;
}
};
template <> struct convert<namespace_or_regex> { template <> struct convert<namespace_or_regex> {
static bool decode(const Node &node, namespace_or_regex &rhs) static bool decode(const Node &node, namespace_or_regex &rhs)
{ {

View File

@@ -104,6 +104,19 @@ YAML::Emitter &operator<<(YAML::Emitter &out, const package_type_t &r)
return out; return out;
} }
YAML::Emitter &operator<<(YAML::Emitter &out, const context_config &c)
{
out << YAML::BeginMap;
out << YAML::Key << "match";
out << YAML::BeginMap;
out << YAML::Key << "radius" << YAML::Value << c.radius;
out << YAML::Key << "pattern" << YAML::Value << c.pattern;
out << YAML::EndMap;
out << YAML::EndMap;
return out;
}
YAML::Emitter &operator<<(YAML::Emitter &out, const filter &f) YAML::Emitter &operator<<(YAML::Emitter &out, const filter &f)
{ {
out << YAML::BeginMap; out << YAML::BeginMap;