From b3b95efb653602dfae5f3de2724d1cdb33677114 Mon Sep 17 00:00:00 2001 From: Bartek Kryza Date: Thu, 8 Jun 2023 00:03:27 +0200 Subject: [PATCH] Added regex support to parents filter --- src/class_diagram/model/diagram.h | 23 +++++++ src/common/model/diagram_filter.cc | 27 ++++---- src/common/model/diagram_filter.h | 16 ++--- src/common/types.cc | 27 ++++++++ src/common/types.h | 80 +++++++++++++++++++++- src/config/config.cc | 4 -- src/config/config.h | 83 ++--------------------- src/config/yaml_decoders.cc | 4 +- src/config/yaml_emitters.cc | 70 ++++++++++--------- tests/test_config_data/filters.yml | 7 +- tests/test_filters.cc | 104 +++++++++++++++++++++++++++++ 11 files changed, 306 insertions(+), 139 deletions(-) create mode 100644 src/common/types.cc diff --git a/src/class_diagram/model/diagram.h b/src/class_diagram/model/diagram.h index 734ab2f5..8efd5969 100644 --- a/src/class_diagram/model/diagram.h +++ b/src/class_diagram/model/diagram.h @@ -24,8 +24,10 @@ #include "common/model/package.h" #include "common/types.h" #include "concept.h" +#include "config/config.h" #include "enum.h" +#include #include #include #include @@ -79,6 +81,10 @@ public: template opt_ref find(const std::string &name) const; + template + std::vector> find( + const clanguml::common::string_or_regex &pattern) const; + template opt_ref find(diagram_element::id_t id) const; @@ -217,6 +223,23 @@ opt_ref diagram::find(const std::string &name) const return {}; } +template +std::vector> diagram::find( + const common::string_or_regex &pattern) const +{ + std::vector> result; + + for (const auto &element : element_view::view()) { + const auto full_name = element.get().full_name(false); + + if (pattern == full_name) { + result.emplace_back(element); + } + } + + return result; +} + template opt_ref diagram::find(diagram_element::id_t id) const { diff --git a/src/common/model/diagram_filter.cc b/src/common/model/diagram_filter.cc index 96831ceb..d1c4da9d 100644 --- a/src/common/model/diagram_filter.cc +++ b/src/common/model/diagram_filter.cc @@ -173,7 +173,7 @@ tvl::value_t anyof_filter::match( } namespace_filter::namespace_filter( - filter_t type, std::vector namespaces) + filter_t type, std::vector namespaces) : filter_visitor{type} , namespaces_{std::move(namespaces)} { @@ -192,7 +192,7 @@ tvl::value_t namespace_filter::match( return ns.starts_with(ns_pattern) || ns == ns_pattern; } else { - const auto ®ex = std::get(nsit.value()); + const auto ®ex = std::get(nsit.value()); return regex == ns.to_string(); } }); @@ -226,7 +226,7 @@ tvl::value_t namespace_filter::match( return result; } else { - return std::get(nsit.value()) == + return std::get(nsit.value()) == e.full_name(false); } }); @@ -239,14 +239,14 @@ tvl::value_t namespace_filter::match( std::get(nsit.value())); } else { - return std::get(nsit.value()) == + return std::get(nsit.value()) == e.full_name(false); } }); } element_filter::element_filter( - filter_t type, std::vector elements) + filter_t type, std::vector elements) : filter_visitor{type} , elements_{std::move(elements)} { @@ -312,7 +312,7 @@ tvl::value_t method_type_filter::match( } subclass_filter::subclass_filter( - filter_t type, std::vector roots) + filter_t type, std::vector roots) : filter_visitor{type} , roots_{std::move(roots)} { @@ -361,7 +361,8 @@ tvl::value_t subclass_filter::match(const diagram &d, const element &e) const return false; } -parents_filter::parents_filter(filter_t type, std::vector children) +parents_filter::parents_filter( + filter_t type, std::vector children) : filter_visitor{type} , children_{std::move(children)} { @@ -383,11 +384,13 @@ tvl::value_t parents_filter::match(const diagram &d, const element &e) const // First get all parents of element e clanguml::common::reference_set parents; - for (const auto &child : children_) { - auto child_ref = cd.find(child); - if (!child_ref.has_value()) - continue; - parents.emplace(child_ref.value()); + for (const auto &child_pattern : children_) { + auto child_refs = cd.find(child_pattern); + + for (auto &child : child_refs) { + if (child.has_value()) + parents.emplace(child.value()); + } } cd.get_parents(parents); diff --git a/src/common/model/diagram_filter.h b/src/common/model/diagram_filter.h index a90cb1e5..25e2f998 100644 --- a/src/common/model/diagram_filter.h +++ b/src/common/model/diagram_filter.h @@ -113,7 +113,7 @@ private: struct namespace_filter : public filter_visitor { namespace_filter( - filter_t type, std::vector namespaces); + filter_t type, std::vector namespaces); ~namespace_filter() override = default; @@ -122,19 +122,19 @@ struct namespace_filter : public filter_visitor { tvl::value_t match(const diagram &d, const element &e) const override; private: - std::vector namespaces_; + std::vector namespaces_; }; struct element_filter : public filter_visitor { element_filter( - filter_t type, std::vector elements); + filter_t type, std::vector elements); ~element_filter() override = default; tvl::value_t match(const diagram &d, const element &e) const override; private: - std::vector elements_; + std::vector elements_; }; struct element_type_filter : public filter_visitor { @@ -162,25 +162,25 @@ private: }; struct subclass_filter : public filter_visitor { - subclass_filter(filter_t type, std::vector roots); + subclass_filter(filter_t type, std::vector roots); ~subclass_filter() override = default; tvl::value_t match(const diagram &d, const element &e) const override; private: - std::vector roots_; + std::vector roots_; }; struct parents_filter : public filter_visitor { - parents_filter(filter_t type, std::vector roots); + parents_filter(filter_t type, std::vector roots); ~parents_filter() override = default; tvl::value_t match(const diagram &d, const element &e) const override; private: - std::vector children_; + std::vector children_; }; template + * + * 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. + */ + +#include "types.h" + +namespace clanguml::common { + +std::string to_string(const std::string &s) { return s; } + +std::string to_string(string_or_regex sr) { return sr.to_string(); } + +}; diff --git a/src/common/types.h b/src/common/types.h index 749812f9..4fdb1f15 100644 --- a/src/common/types.h +++ b/src/common/types.h @@ -1,5 +1,5 @@ /** - * src/class_diagram/visitor/translation_unit_visitor.h + * src/common/types.h * * Copyright (c) 2021-2023 Bartek Kryza * @@ -20,15 +20,21 @@ #include #include #include +#include #include +#include #include +#include "model/namespace.h" + namespace clanguml::common { using id_t = int64_t; enum class generator_type_t { plantuml, json }; +std::string to_string(const std::string &s); + template class optional_ref { public: using optional_type = T; @@ -137,4 +143,76 @@ using reference_vector = std::vector>; template using reference_set = std::unordered_set>; +/** + * @brief Wrapper around std::regex, which contains original pattern + */ +struct regex { + regex(std::regex r, std::string p) + : regexp{std::move(r)} + , pattern{std::move(p)} + { + } + + [[nodiscard]] bool operator==(const std::string &v) const + { + return std::regex_match(v, regexp); + } + + std::regex regexp; + std::string pattern; +}; + +template struct or_regex { + or_regex() = default; + + or_regex(T v) + : value_{std::move(v)} + { + } + + or_regex(std::regex r, std::string p) + : value_{regex{std::move(r), std::move(p)}} + { + } + + or_regex &operator=(const T &v) + { + value_ = v; + return *this; + } + + or_regex &operator=(const regex &v) + { + value_ = v; + return *this; + } + + [[nodiscard]] bool operator==(const T &v) const + { + if (std::holds_alternative(value_)) + return std::regex_match(v, std::get(value_).regexp); + + return std::get(value_) == v; + } + + std::string to_string() const + { + if (std::holds_alternative(value_)) + return std::get(value_).pattern; + + return clanguml::common::to_string(std::get(value_)); + } + + const std::variant &value() const { return value_; } + +private: + std::variant value_; +}; + +using string_or_regex = or_regex; + +std::string to_string(string_or_regex sr); + +using namespace_or_regex = common::or_regex; + } // namespace clanguml::common \ No newline at end of file diff --git a/src/config/config.cc b/src/config/config.cc index 44ebafb0..95bd25bd 100644 --- a/src/config/config.cc +++ b/src/config/config.cc @@ -24,8 +24,6 @@ namespace clanguml::config { -std::string to_string(const std::string &s) { return s; } - std::string to_string(const hint_t t) { switch (t) { @@ -87,8 +85,6 @@ std::string to_string(method_type mt) } } -std::string to_string(string_or_regex sr) { return sr.to_string(); } - std::string to_string(const comment_parser_t cp) { switch (cp) { diff --git a/src/config/config.h b/src/config/config.h index 0355fe1e..ee1ec919 100644 --- a/src/config/config.h +++ b/src/config/config.h @@ -19,6 +19,7 @@ #include "class_diagram/model/diagram.h" #include "common/model/enums.h" +#include "common/types.h" #include "option.h" #include "util/util.h" @@ -36,80 +37,6 @@ namespace clanguml { namespace config { -std::string to_string(const std::string &s); - -/** - * @brief Wrapper around std::regex, which contains original pattern - */ -struct regex { - regex(std::regex r, std::string p) - : regexp{std::move(r)} - , pattern{std::move(p)} - { - } - - [[nodiscard]] bool operator==(const std::string &v) const - { - return std::regex_match(v, regexp); - } - - std::regex regexp; - std::string pattern; -}; - -template struct or_regex { - or_regex() = default; - - or_regex(T v) - : value_{std::move(v)} - { - } - - or_regex(std::regex r, std::string p) - : value_{regex{std::move(r), std::move(p)}} - { - } - - or_regex &operator=(const T &v) - { - value_ = v; - return *this; - } - - or_regex &operator=(const regex &v) - { - value_ = v; - return *this; - } - - [[nodiscard]] bool operator==(const T &v) const - { - if (std::holds_alternative(value_)) - return std::regex_match(v, std::get(value_).regexp); - - return std::get(value_) == v; - } - - std::string to_string() const - { - if (std::holds_alternative(value_)) - return std::get(value_).pattern; - - return clanguml::config::to_string(std::get(value_)); - } - - const std::variant &value() const { return value_; } - -private: - std::variant value_; -}; - -using string_or_regex = or_regex; - -std::string to_string(string_or_regex sr); - -using namespace_or_regex = or_regex; - enum class method_arguments { full, abbreviated, none }; enum class method_type { @@ -148,9 +75,9 @@ struct diagram_template { }; struct filter { - std::vector namespaces; + std::vector namespaces; - std::vector elements; + std::vector elements; // E.g.: // - class @@ -170,9 +97,9 @@ struct filter { // - private std::vector access; - std::vector subclasses; + std::vector subclasses; - std::vector parents; + std::vector parents; std::vector specializations; diff --git a/src/config/yaml_decoders.cc b/src/config/yaml_decoders.cc index b6701079..50e22376 100644 --- a/src/config/yaml_decoders.cc +++ b/src/config/yaml_decoders.cc @@ -20,6 +20,8 @@ #include "diagram_templates.h" namespace YAML { +using clanguml::common::namespace_or_regex; +using clanguml::common::string_or_regex; using clanguml::common::model::access_t; using clanguml::common::model::relationship_t; using clanguml::config::class_diagram; @@ -35,14 +37,12 @@ using clanguml::config::location_t; using clanguml::config::member_order_t; using clanguml::config::method_arguments; using clanguml::config::method_type; -using clanguml::config::namespace_or_regex; using clanguml::config::package_diagram; using clanguml::config::package_type_t; using clanguml::config::plantuml; using clanguml::config::relationship_hint_t; using clanguml::config::sequence_diagram; using clanguml::config::source_location; -using clanguml::config::string_or_regex; inline bool has_key(const YAML::Node &n, const std::string &key) { diff --git a/src/config/yaml_emitters.cc b/src/config/yaml_emitters.cc index 20f883c4..c30bf413 100644 --- a/src/config/yaml_emitters.cc +++ b/src/config/yaml_emitters.cc @@ -18,39 +18,7 @@ #include "config.h" -namespace clanguml::common::model { -YAML::Emitter &operator<<(YAML::Emitter &out, const namespace_ &n) -{ - out << n.to_string(); - return out; -} - -YAML::Emitter &operator<<(YAML::Emitter &out, const relationship_t &r) -{ - out << to_string(r); - return out; -} - -YAML::Emitter &operator<<(YAML::Emitter &out, const access_t &a) -{ - out << to_string(a); - return out; -} - -YAML::Emitter &operator<<(YAML::Emitter &out, const diagram_t &d) -{ - out << to_string(d); - return out; -} -} // namespace clanguml::common::model - -namespace clanguml::config { - -YAML::Emitter &operator<<(YAML::Emitter &out, const method_type &m) -{ - out << to_string(m); - return out; -} +namespace clanguml::common { YAML::Emitter &operator<<(YAML::Emitter &out, const string_or_regex &m) { @@ -78,6 +46,42 @@ YAML::Emitter &operator<<(YAML::Emitter &out, const namespace_or_regex &m) return out; } +namespace model { +YAML::Emitter &operator<<(YAML::Emitter &out, const namespace_ &n) +{ + out << n.to_string(); + return out; +} + +YAML::Emitter &operator<<(YAML::Emitter &out, const relationship_t &r) +{ + out << to_string(r); + return out; +} + +YAML::Emitter &operator<<(YAML::Emitter &out, const access_t &a) +{ + out << to_string(a); + return out; +} + +YAML::Emitter &operator<<(YAML::Emitter &out, const diagram_t &d) +{ + out << to_string(d); + return out; +} + +} // namespace model +} // namespace clanguml::common + +namespace clanguml::config { + +YAML::Emitter &operator<<(YAML::Emitter &out, const method_type &m) +{ + out << to_string(m); + return out; +} + YAML::Emitter &operator<<(YAML::Emitter &out, const filter &f) { out << YAML::BeginMap; diff --git a/tests/test_config_data/filters.yml b/tests/test_config_data/filters.yml index 30cb9cad..e46aeec1 100644 --- a/tests/test_config_data/filters.yml +++ b/tests/test_config_data/filters.yml @@ -53,4 +53,9 @@ diagrams: type: class include: subclasses: - - r: 'ns1::ns2::Base[A|B]' \ No newline at end of file + - r: 'ns1::ns2::Base[A|B]' + regex_parents_test: + type: class + include: + parents: + - r: 'ns1::ns2::.+[1|2]' \ No newline at end of file diff --git a/tests/test_filters.cc b/tests/test_filters.cc index 96757606..9d366bcb 100644 --- a/tests/test_filters.cc +++ b/tests/test_filters.cc @@ -312,6 +312,110 @@ TEST_CASE("Test subclasses regexp filter", "[unit-test]") CHECK(!filter.should_include(*c)); } +TEST_CASE("Test parents regexp filter", "[unit-test]") +{ + using clanguml::class_diagram::model::class_method; + using clanguml::class_diagram::model::class_parent; + using clanguml::common::to_id; + using clanguml::common::model::access_t; + using clanguml::common::model::diagram_filter; + using clanguml::common::model::namespace_; + using clanguml::common::model::package; + using clanguml::common::model::source_file; + using namespace std::string_literals; + + using clanguml::class_diagram::model::class_; + + auto cfg = clanguml::config::load("./test_config_data/filters.yml"); + + auto &config = *cfg.diagrams["regex_parents_test"]; + clanguml::class_diagram::model::diagram diagram; + + auto p = std::make_unique(config.using_namespace()); + p->set_namespace({}); + p->set_name("ns1"); + diagram.add({}, std::move(p)); + p = std::make_unique(config.using_namespace()); + p->set_namespace({"ns1"}); + p->set_name("ns2"); + diagram.add(namespace_{"ns1"}, std::move(p)); + + auto c = std::make_unique(config.using_namespace()); + c->set_namespace(namespace_{"ns1::ns2"}); + c->set_name("BaseA"); + c->set_id(to_id("ns1::ns2::BaseA"s)); + diagram.add(namespace_{"ns1::ns2"}, std::move(c)); + + c = std::make_unique(config.using_namespace()); + c->set_namespace(namespace_{"ns1::ns2"}); + c->set_name("A1"); + c->set_id(to_id("ns1::ns2::A1"s)); + c->add_parent({"ns1::ns2::BaseA"}); + diagram.add(namespace_{"ns1::ns2"}, std::move(c)); + + c = std::make_unique(config.using_namespace()); + c->set_namespace(namespace_{"ns1::ns2"}); + c->set_name("A2"); + c->set_id(to_id("ns1::ns2::A2"s)); + c->add_parent({"ns1::ns2::BaseA"}); + diagram.add(namespace_{"ns1::ns2"}, std::move(c)); + + c = std::make_unique(config.using_namespace()); + c->set_namespace(namespace_{"ns1::ns2"}); + c->set_name("BaseB"); + c->set_id(to_id("ns1::ns2::BaseB"s)); + diagram.add(namespace_{"ns1::ns2"}, std::move(c)); + + c = std::make_unique(config.using_namespace()); + c->set_namespace(namespace_{"ns1::ns2"}); + c->set_name("B1"); + c->set_id(to_id("ns1::ns2::B1"s)); + c->add_parent({"ns1::ns2::BaseB"}); + diagram.add(namespace_{"ns1::ns2"}, std::move(c)); + + c = std::make_unique(config.using_namespace()); + c->set_namespace(namespace_{"ns1::ns2"}); + c->set_name("B2"); + c->set_id(to_id("ns1::ns2::B2"s)); + c->add_parent({"ns1::ns2::BaseB"}); + diagram.add(namespace_{"ns1::ns2"}, std::move(c)); + + c = std::make_unique(config.using_namespace()); + c->set_namespace(namespace_{"ns1::ns2"}); + c->set_name("Common"); + c->set_id(to_id("ns1::ns2::Common"s)); + diagram.add(namespace_{"ns1::ns2"}, std::move(c)); + + c = std::make_unique(config.using_namespace()); + c->set_namespace(namespace_{"ns1::ns2"}); + c->set_name("C3"); + c->set_id(to_id("ns1::ns2::C3"s)); + c->add_parent({"ns1::ns2::Common"}); + diagram.add(namespace_{"ns1::ns2"}, std::move(c)); + + diagram.set_complete(true); + + diagram_filter filter(diagram, config); + + c = std::make_unique(config.using_namespace()); + c->set_namespace(namespace_{"ns1::ns2"}); + c->set_name("BaseA"); + c->set_id(to_id("ns1::ns2::BaseA"s)); + CHECK(filter.should_include(*c)); + + c = std::make_unique(config.using_namespace()); + c->set_namespace(namespace_{"ns1::ns2"}); + c->set_name("BaseB"); + c->set_id(to_id("ns1::ns2::BaseB"s)); + CHECK(filter.should_include(*c)); + + c = std::make_unique(config.using_namespace()); + c->set_namespace(namespace_{"ns1::ns2"}); + c->set_name("Common"); + c->set_id(to_id("ns1::ns2::Common"s)); + CHECK(!filter.should_include(*c)); +} + /// /// Main test function ///