From 237ef26389ab66579b38b13383d3fb68a6731840 Mon Sep 17 00:00:00 2001 From: Bartek Kryza Date: Wed, 8 Nov 2023 23:42:22 +0100 Subject: [PATCH] Initial implementation of radius parameter in context filter --- src/common/model/diagram_filter.cc | 171 ++++++++++++++++++++--------- src/common/model/diagram_filter.h | 20 +++- 2 files changed, 137 insertions(+), 54 deletions(-) diff --git a/src/common/model/diagram_filter.cc b/src/common/model/diagram_filter.cc index ac6ddf4ba..d0baab32e 100644 --- a/src/common/model/diagram_filter.cc +++ b/src/common/model/diagram_filter.cc @@ -534,13 +534,118 @@ tvl::value_t access_filter::match( [&a](const auto &access) { return a == access; }); } -context_filter::context_filter( - filter_t type, std::vector context) +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 +{ + 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); + + 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; + + while (radius_counter > 0 && effective_context_extended) { + // If at any iteration the effective context was not extended - we + // don't to need to continue + radius_counter--; + effective_context_extended = false; + current_iteration_context.clear(); + + // For each class in the model + for (const auto &c : + static_cast(d).classes()) { + // 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_) { + 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_) { + const auto &maybe_class = + static_cast(d) + .find(ec); + + if (!maybe_class) + continue; + + for (const relationship &rel : + maybe_class.value().relationships()) { + + if (d.should_include(rel.type()) && + rel.destination() == c.get().id()) + current_iteration_context.emplace(c.get().id()); + } + } + + // Check if any of the elements parents are already in the + // effective context... + for (const class_diagram::model::class_parent &p : + c.get().parents()) { + for (const auto &ec : effective_context_) { + const auto &maybe_parent = + static_cast(d) + .find(ec); + if (!maybe_parent) + continue; + + if (d.should_include(relationship_t::kExtension) && + maybe_parent.value().full_name(false) == p.name()) + current_iteration_context.emplace(c.get().id()); + } + } + + // .. or vice-versa + for (const auto &ec : effective_context_) { + const auto &maybe_child = + static_cast(d) + .find(ec); + + for (const class_diagram::model::class_parent &p : + maybe_child.value().parents()) { + if (p.name() == c.get().full_name(false)) { + current_iteration_context.emplace(c.get().id()); + } + } + } + } + + for (auto id : current_iteration_context) { + if (effective_context_.count(id) == 0) { + // Found new element to add to context + effective_context_.emplace(id); + effective_context_extended = true; + } + } + } +} + tvl::value_t context_filter::match(const diagram &d, const element &e) const { if (d.type() != diagram_t::kClass) @@ -551,53 +656,15 @@ tvl::value_t context_filter::match(const diagram &d, const element &e) const if (!d.complete()) return {}; - return tvl::any_of(context_.begin(), context_.end(), - [&e, &d](const auto &context_root_pattern) { - const auto &context_roots = - static_cast(d) - .find(context_root_pattern); - - for (auto &context_root : context_roots) { - if (context_root.has_value()) { - // This is a direct match to the context root - if (context_root.value().id() == e.id()) - return true; - - // Return a positive match if the element e is in a direct - // relationship with any of the context_root's - for (const relationship &rel : - context_root.value().relationships()) { - if (d.should_include(rel.type()) && - 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; - } + initialize(d); - // 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( - &e) != nullptr) { - for (const class_diagram::model::class_parent &p : - static_cast(e) - .parents()) { - if (p.name() == - context_root.value().full_name(false)) - return true; - } - } - } - } - return false; - }); + if (effective_context_.empty()) + return {}; + + if (effective_context_.count(e.id()) > 0) + return true; + + return false; } paths_filter::paths_filter(filter_t type, const std::filesystem::path &root, @@ -630,15 +697,17 @@ paths_filter::paths_filter(filter_t type, const std::filesystem::path &root, match_successful = true; } catch (std::filesystem::filesystem_error &e) { - LOG_WARN("Cannot add non-existent path {} to paths filter", + LOG_WARN("Cannot add non-existent path {} to " + "paths filter", absolute_path.string()); continue; } } if (!match_successful) - LOG_WARN( - "Paths filter pattern '{}' did not match any files...", path); + LOG_WARN("Paths filter pattern '{}' did not match " + "any files...", + path); } } diff --git a/src/common/model/diagram_filter.h b/src/common/model/diagram_filter.h index c713eaa94..888d7cc04 100644 --- a/src/common/model/diagram_filter.h +++ b/src/common/model/diagram_filter.h @@ -443,18 +443,32 @@ struct access_filter : public filter_visitor { }; /** - * Match diagram elements which are in direct relationship to any of the - * elements specified in context. + * Match diagram elements which are in within a 'radius' distance relationship + * 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; tvl::value_t match(const diagram &d, const element &r) const override; private: + void initialize(const diagram &d) const; + + const unsigned radius_; + 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_; + + /*! Flag to mark whether the filter context has been computed */ + mutable bool initialized_{false}; }; /**