From ece02c09df5bedcd56785e0216caf783be2fbf35 Mon Sep 17 00:00:00 2001 From: Bartek Kryza Date: Sun, 27 Mar 2022 22:59:45 +0200 Subject: [PATCH] Added test case for subclass filter --- src/class_diagram/model/diagram.cc | 34 ++++++++++ src/class_diagram/model/diagram.h | 22 ++++++ src/common/model/diagram_filter.cc | 16 ++--- src/common/model/diagram_filter.h | 103 ++++++++++++++++++++++------- src/common/model/element.h | 2 +- src/common/model/namespace.cc | 1 - 6 files changed, 143 insertions(+), 35 deletions(-) diff --git a/src/class_diagram/model/diagram.cc b/src/class_diagram/model/diagram.cc index 1200e889..ea992712 100644 --- a/src/class_diagram/model/diagram.cc +++ b/src/class_diagram/model/diagram.cc @@ -48,6 +48,18 @@ bool diagram::has_enum(const enum_ &e) const [&e](const auto &ee) { return ee.get().full_name() == e.full_name(); }); } +type_safe::optional_ref diagram::get_class( + const std::string &name) const +{ + for (const auto &c : classes_) { + if (c.get().full_name(false) == name) { + return {c}; + } + } + + return type_safe::nullopt; +} + void diagram::add_type_alias(std::unique_ptr &&ta) { LOG_DBG( @@ -107,6 +119,28 @@ void diagram::add_enum(std::unique_ptr &&e) LOG_DBG("Enum {} already in the model", e->name()); } +void diagram::get_parents( + std::unordered_set> &parents) const +{ + bool found_new = false; + for (const auto &parent : parents) { + for (const auto &rel : parent.get().relationships()) { + if (rel.type() == common::model::relationship_t::kExtension) { + const auto p = get_class(rel.destination()); + if (p.has_value()) { + auto [it, found] = parents.emplace(p.value()); + if (found) + found_new = true; + } + } + } + } + + if (found_new) { + get_parents(parents); + } +} + std::string diagram::to_alias(const std::string &full_name) const { LOG_DBG("Looking for alias for {}", full_name); diff --git a/src/class_diagram/model/diagram.h b/src/class_diagram/model/diagram.h index 86f8acd7..7abce58d 100644 --- a/src/class_diagram/model/diagram.h +++ b/src/class_diagram/model/diagram.h @@ -25,6 +25,7 @@ #include "type_alias.h" #include +#include #include namespace clanguml::class_diagram::model { @@ -48,6 +49,9 @@ public: bool has_enum(const enum_ &e) const; + type_safe::optional_ref get_class( + const std::string &name) const; + void add_type_alias(std::unique_ptr &&ta); void add_class(std::unique_ptr &&c); @@ -58,6 +62,9 @@ public: std::string to_alias(const std::string &full_name) const; + void get_parents( + std::unordered_set> &parents) const; + friend void print_diagram_tree(const diagram &d, const int level); private: @@ -66,3 +73,18 @@ private: std::map> type_aliases_; }; } + +namespace std { + +template <> +struct hash< + type_safe::object_ref> { + std::size_t operator()(const type_safe::object_ref< + const clanguml::class_diagram::model::class_> &key) const + { + using clanguml::class_diagram::model::class_; + + return std::hash{}(key.get().full_name(false)); + } +}; +} diff --git a/src/common/model/diagram_filter.cc b/src/common/model/diagram_filter.cc index 5cd26c23..75a1b525 100644 --- a/src/common/model/diagram_filter.cc +++ b/src/common/model/diagram_filter.cc @@ -20,26 +20,26 @@ namespace clanguml::common::model { -bool filter_visitor::match(const diagram &d, const common::model::element &e) +std::optional filter_visitor::match(const diagram &d, const common::model::element &e) { - return false; + return {}; } -bool filter_visitor::match( +std::optional filter_visitor::match( const diagram &d, const common::model::relationship_t &r) { - return false; + return {}; } -bool filter_visitor::match(const diagram &d, const common::model::scope_t &r) +std::optional filter_visitor::match(const diagram &d, const common::model::scope_t &r) { - return false; + return {}; } -bool filter_visitor::match( +std::optional filter_visitor::match( const diagram &d, const common::model::namespace_ &ns) { - return false; + return {}; } template <> diff --git a/src/common/model/diagram_filter.h b/src/common/model/diagram_filter.h index 1f185d76..ccccf522 100644 --- a/src/common/model/diagram_filter.h +++ b/src/common/model/diagram_filter.h @@ -17,6 +17,7 @@ */ #pragma once +#include "class_diagram/model/diagram.h" #include "common/model/diagram.h" #include "common/model/element.h" #include "common/model/enums.h" @@ -36,11 +37,14 @@ public: { } - virtual bool match(const diagram &d, const common::model::element &e); - virtual bool match( + virtual std::optional match( + const diagram &d, const common::model::element &e); + virtual std::optional match( const diagram &d, const common::model::relationship_t &r); - virtual bool match(const diagram &d, const common::model::scope_t &r); - virtual bool match(const diagram &d, const common::model::namespace_ &ns); + virtual std::optional match( + const diagram &d, const common::model::scope_t &r); + virtual std::optional match( + const diagram &d, const common::model::namespace_ &ns); bool is_inclusive() const { return type_ == filter_t::kInclusive; } bool is_exclusive() const { return type_ == filter_t::kExclusive; } @@ -55,16 +59,16 @@ struct namespace_filter : public filter_visitor { { } - bool match(const diagram &d, const namespace_ &ns) override + std::optional match(const diagram &d, const namespace_ &ns) override { if (namespaces_.empty()) - return is_inclusive(); + return {}; return std::any_of(namespaces_.begin(), namespaces_.end(), [&ns](const auto &nsit) { return ns.starts_with(nsit); }); } - bool match(const diagram &d, const element &e) override + std::optional match(const diagram &d, const element &e) override { return std::any_of( namespaces_.begin(), namespaces_.end(), [&e](const auto &nsit) { @@ -82,8 +86,11 @@ struct element_filter : public filter_visitor { { } - bool match(const diagram &d, const element &e) override + std::optional match(const diagram &d, const element &e) override { + if (elements_.empty()) + return {}; + return std::any_of( elements_.begin(), elements_.end(), [&e](const auto &el) { auto fn = e.full_name(false); @@ -102,12 +109,38 @@ struct subclass_filter : public filter_visitor { { } - bool match(const diagram &d, const element &e) override + std::optional match(const diagram &d, const element &e) override { if (roots_.empty()) - return is_inclusive(); + return {}; - return true; + const auto &cd = dynamic_cast(d); + + // First get all parents of element e + std::unordered_set< + type_safe::object_ref> + parents; + + const auto &fn = e.full_name(false); + auto class_ref = cd.get_class(fn); + + if (!class_ref.has_value()) + return false; + + parents.emplace(class_ref.value()); + + cd.get_parents(parents); + + // Now check if any of the parents matches the roots specified in the + // filter config + for (const auto &root : roots_) { + for (const auto &parent : parents) { + if (root == parent.get().full_name(false)) + return true; + } + } + + return false; } std::vector roots_; @@ -120,10 +153,11 @@ struct relationship_filter : public filter_visitor { { } - bool match(const diagram &d, const relationship_t &r) override + std::optional match( + const diagram &d, const relationship_t &r) override { if (relationships_.empty()) - return is_inclusive(); + return {}; return std::any_of(relationships_.begin(), relationships_.end(), [&r](const auto &rel) { @@ -142,13 +176,13 @@ struct scope_filter : public filter_visitor { { } - bool match(const diagram &d, const scope_t &s) override + std::optional match(const diagram &d, const scope_t &s) override { if (scopes_.empty()) - return is_inclusive(); + return {}; - return std::any_of(scopes_.begin(), scopes_.end(), - [&s](const auto &rel) { + return std::any_of( + scopes_.begin(), scopes_.end(), [&s](const auto &rel) { bool res = to_string(s) == rel; return res; }); @@ -164,10 +198,10 @@ struct context_filter : public filter_visitor { { } - bool match(const diagram &d, const element &r) override + std::optional match(const diagram &d, const element &r) override { if (context_.empty()) - return is_inclusive(); + return {}; return std::any_of(context_.begin(), context_.end(), [&r](const auto &rel) { return true; }); @@ -209,12 +243,25 @@ public: template bool should_include(const T &e) { - if (std::any_of(exclusive_.begin(), exclusive_.end(), - [this, &e](const auto &ex) { return ex->match(diagram_, e); })) + bool exc = std::any_of( + exclusive_.begin(), exclusive_.end(), [this, &e](const auto &ex) { + auto m = ex->match(diagram_, e); + // Return a match if a filter is undefined for specific element + // or it's a match + return !m.has_value() || m.value(); + }); + if (exc) return false; - if (std::any_of(inclusive_.begin(), inclusive_.end(), - [this, &e](const auto &in) { return in->match(diagram_, e); })) + bool inc = std::all_of( + inclusive_.begin(), inclusive_.end(), [this, &e](const auto &in) { + auto m = in->match(diagram_, e); + // Return a match if a filter is undefined for specific element + // or it's a match + return !m.has_value() || m.value(); + }); + + if (inc) return true; return false; @@ -227,14 +274,18 @@ private: if (c.include) { inclusive_.emplace_back(std::make_unique( filter_t::kInclusive, c.include().namespaces)); - inclusive_.emplace_back(std::make_unique( - filter_t::kInclusive, c.include().elements)); + inclusive_.emplace_back(std::make_unique( filter_t::kInclusive, c.include().relationships)); inclusive_.emplace_back(std::make_unique( filter_t::kInclusive, c.include().scopes)); + + inclusive_.emplace_back(std::make_unique( + filter_t::kInclusive, c.include().elements)); + inclusive_.emplace_back(std::make_unique( filter_t::kInclusive, c.include().subclasses)); + inclusive_.emplace_back(std::make_unique( filter_t::kInclusive, c.include().context)); } @@ -257,6 +308,8 @@ private: } std::vector> inclusive_; + // std::vector> inclusive_and_; + std::vector> exclusive_; const common::model::diagram &diagram_; }; diff --git a/src/common/model/element.h b/src/common/model/element.h index 3adeec0b..74173a89 100644 --- a/src/common/model/element.h +++ b/src/common/model/element.h @@ -59,7 +59,7 @@ public: return ns_.relative_to(using_namespace_); } - virtual std::string full_name(bool relative) const { return name(); } + virtual std::string full_name(bool relative) const { return name_and_ns(); } void set_using_namespaces(const namespace_ &un); diff --git a/src/common/model/namespace.cc b/src/common/model/namespace.cc index ed25e50a..2e6c6012 100644 --- a/src/common/model/namespace.cc +++ b/src/common/model/namespace.cc @@ -140,7 +140,6 @@ const std::string &namespace_::operator[](const int index) const bool namespace_::starts_with(const namespace_ &right) const { - return util::starts_with(namespace_path_, right.namespace_path_); }