From 055897f11baa2c1641f02908ec2923ac9ada6d62 Mon Sep 17 00:00:00 2001 From: Bartek Kryza Date: Thu, 9 Nov 2023 23:09:17 +0100 Subject: [PATCH] Extended context filter config schema to accept optional radius parameter --- src/common/model/diagram_filter.cc | 71 ++++++++++++++++++------------ src/common/model/diagram_filter.h | 8 ++-- src/config/config.h | 11 ++++- src/config/schema.h | 9 +++- src/config/yaml_decoders.cc | 18 ++++++++ src/config/yaml_emitters.cc | 13 ++++++ 6 files changed, 95 insertions(+), 35 deletions(-) diff --git a/src/common/model/diagram_filter.cc b/src/common/model/diagram_filter.cc index d0baab32..03563e88 100644 --- a/src/common/model/diagram_filter.cc +++ b/src/common/model/diagram_filter.cc @@ -534,39 +534,35 @@ tvl::value_t access_filter::match( [&a](const auto &access) { return a == access; }); } -context_filter::context_filter(filter_t type, - std::vector context, unsigned radius) +context_filter::context_filter( + filter_t type, std::vector context, unsigned radius) : filter_visitor{type} - , radius_{radius} , 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}; - // First add to effective context all elements matching context_ - for (const auto &c : context_) { - const auto &context_matches = - static_cast(d) - .find(c); + auto &effective_context = effective_contexts_[idx]; - for (const auto &maybe_match : context_matches) { - if (maybe_match) - effective_context_.emplace(maybe_match.value().id()); - } + // First add to effective context all elements matching context_ + const auto &context = context_.at(idx); + const auto &context_matches = + static_cast(d) + .find(context.pattern); + + for (const auto &maybe_match : context_matches) { + if (maybe_match) + effective_context.emplace(maybe_match.value().id()); } // Now repeat radius times - extend the effective context with elements // matching in direct relationship to what is in context - auto radius_counter = radius_; - decltype(effective_context_) current_iteration_context; + auto radius_counter = context.radius; + std::set current_iteration_context; while (radius_counter > 0 && effective_context_extended) { // 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 // relationship with any of the effective context elements ... 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) current_iteration_context.emplace(c.get().id()); } } // ... or vice-versa - for (const auto &ec : effective_context_) { + for (const auto &ec : effective_context) { const auto &maybe_class = static_cast(d) .find(ec); @@ -608,7 +604,7 @@ void context_filter::initialize(const diagram &d) const // effective context... for (const class_diagram::model::class_parent &p : c.get().parents()) { - for (const auto &ec : effective_context_) { + for (const auto &ec : effective_context) { const auto &maybe_parent = static_cast(d) .find(ec); @@ -622,7 +618,7 @@ void context_filter::initialize(const diagram &d) const } // .. or vice-versa - for (const auto &ec : effective_context_) { + for (const auto &ec : effective_context) { const auto &maybe_child = static_cast(d) .find(ec); @@ -637,15 +633,29 @@ void context_filter::initialize(const diagram &d) const } 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 - effective_context_.emplace(id); + effective_context.emplace(id); 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 { 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); - if (effective_context_.empty()) + if (std::all_of(effective_contexts_.begin(), effective_contexts_.end(), + [](const auto &ec) { return ec.empty(); })) return {}; - if (effective_context_.count(e.id()) > 0) - return true; + for (const auto &ec : effective_contexts_) { + if (ec.count(e.id()) > 0) + return true; + } return false; } diff --git a/src/common/model/diagram_filter.h b/src/common/model/diagram_filter.h index 888d7cc0..589fddf8 100644 --- a/src/common/model/diagram_filter.h +++ b/src/common/model/diagram_filter.h @@ -447,7 +447,7 @@ private: * to any of the elements specified in context. */ struct context_filter : public filter_visitor { - context_filter(filter_t type, std::vector context, + context_filter(filter_t type, std::vector context, unsigned radius = 1); ~context_filter() override = default; @@ -457,15 +457,15 @@ struct context_filter : public filter_visitor { private: void initialize(const diagram &d) const; - const unsigned radius_; + void initialize_effective_context(const diagram &d, unsigned idx) const; - std::vector context_; + std::vector context_; /*! * Represents all elements which should belong to the diagram based * on this filter. It is populated by the initialize() method. */ - mutable std::set effective_context_; + mutable std::vector> effective_contexts_; /*! Flag to mark whether the filter context has been computed */ mutable bool initialized_{false}; diff --git a/src/config/config.h b/src/config/config.h index f4560c87..5fd9c5d0 100644 --- a/src/config/config.h +++ b/src/config/config.h @@ -141,6 +141,11 @@ struct mermaid { void append(const mermaid &r); }; +struct context_config { + common::string_or_regex pattern; + unsigned radius{0}; +}; + /** * @brief Definition of diagram template */ @@ -316,9 +321,13 @@ struct filter { * include: * context: * - ns1::ns2::ClassA + * - r: ns1::ns2::ClassB<.*> + * - match: + * pattern: ns1::ns2::ClassA + * radius: 3 * ``` */ - std::vector context; + std::vector context; /*! @brief Paths filter * diff --git a/src/config/schema.h b/src/config/schema.h index fc02f46d..b8310477 100644 --- a/src/config/schema.h +++ b/src/config/schema.h @@ -112,6 +112,13 @@ types: - function - function_template - 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: namespaces: !optional [regex_or_string_t] elements: !optional [regex_or_string_t] @@ -123,7 +130,7 @@ types: specializations: !optional [regex_or_string_t] dependants: !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] method_types: !optional [method_type_filter_t] callee_types: !optional [callee_type_filter_t] diff --git a/src/config/yaml_decoders.cc b/src/config/yaml_decoders.cc index a2ca40de..50227526 100644 --- a/src/config/yaml_decoders.cc +++ b/src/config/yaml_decoders.cc @@ -33,6 +33,7 @@ using clanguml::common::model::relationship_t; using clanguml::config::callee_type; using clanguml::config::class_diagram; using clanguml::config::config; +using clanguml::config::context_config; using clanguml::config::diagram_template; using clanguml::config::filter; using clanguml::config::generate_links_config; @@ -419,6 +420,23 @@ template <> struct convert { } }; +template <> struct convert { + 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(); + rhs.pattern = node["match"]["radius"].as(); + } + else { + rhs.radius = 1; + rhs.pattern = node.as(); + } + + return true; + } +}; + template <> struct convert { static bool decode(const Node &node, namespace_or_regex &rhs) { diff --git a/src/config/yaml_emitters.cc b/src/config/yaml_emitters.cc index d717ec43..200e1df4 100644 --- a/src/config/yaml_emitters.cc +++ b/src/config/yaml_emitters.cc @@ -104,6 +104,19 @@ YAML::Emitter &operator<<(YAML::Emitter &out, const package_type_t &r) 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) { out << YAML::BeginMap;