Added regex support to parents filter

This commit is contained in:
Bartek Kryza 2023-06-08 00:03:27 +02:00
parent ad2fc3f8a6
commit b3b95efb65
No known key found for this signature in database
GPG Key ID: 6CDA4566635E93B1
11 changed files with 306 additions and 139 deletions

View File

@ -24,8 +24,10 @@
#include "common/model/package.h" #include "common/model/package.h"
#include "common/types.h" #include "common/types.h"
#include "concept.h" #include "concept.h"
#include "config/config.h"
#include "enum.h" #include "enum.h"
#include <regex>
#include <string> #include <string>
#include <unordered_set> #include <unordered_set>
#include <vector> #include <vector>
@ -79,6 +81,10 @@ public:
template <typename ElementT> template <typename ElementT>
opt_ref<ElementT> find(const std::string &name) const; opt_ref<ElementT> find(const std::string &name) const;
template <typename ElementT>
std::vector<opt_ref<ElementT>> find(
const clanguml::common::string_or_regex &pattern) const;
template <typename ElementT> template <typename ElementT>
opt_ref<ElementT> find(diagram_element::id_t id) const; opt_ref<ElementT> find(diagram_element::id_t id) const;
@ -217,6 +223,23 @@ opt_ref<ElementT> diagram::find(const std::string &name) const
return {}; return {};
} }
template <typename ElementT>
std::vector<opt_ref<ElementT>> diagram::find(
const common::string_or_regex &pattern) const
{
std::vector<opt_ref<ElementT>> result;
for (const auto &element : element_view<ElementT>::view()) {
const auto full_name = element.get().full_name(false);
if (pattern == full_name) {
result.emplace_back(element);
}
}
return result;
}
template <typename ElementT> template <typename ElementT>
opt_ref<ElementT> diagram::find(diagram_element::id_t id) const opt_ref<ElementT> diagram::find(diagram_element::id_t id) const
{ {

View File

@ -173,7 +173,7 @@ tvl::value_t anyof_filter::match(
} }
namespace_filter::namespace_filter( namespace_filter::namespace_filter(
filter_t type, std::vector<config::namespace_or_regex> namespaces) filter_t type, std::vector<common::namespace_or_regex> namespaces)
: filter_visitor{type} : filter_visitor{type}
, namespaces_{std::move(namespaces)} , namespaces_{std::move(namespaces)}
{ {
@ -192,7 +192,7 @@ tvl::value_t namespace_filter::match(
return ns.starts_with(ns_pattern) || ns == ns_pattern; return ns.starts_with(ns_pattern) || ns == ns_pattern;
} }
else { else {
const auto &regex = std::get<config::regex>(nsit.value()); const auto &regex = std::get<common::regex>(nsit.value());
return regex == ns.to_string(); return regex == ns.to_string();
} }
}); });
@ -226,7 +226,7 @@ tvl::value_t namespace_filter::match(
return result; return result;
} }
else { else {
return std::get<config::regex>(nsit.value()) == return std::get<common::regex>(nsit.value()) ==
e.full_name(false); e.full_name(false);
} }
}); });
@ -239,14 +239,14 @@ tvl::value_t namespace_filter::match(
std::get<namespace_>(nsit.value())); std::get<namespace_>(nsit.value()));
} }
else { else {
return std::get<config::regex>(nsit.value()) == return std::get<common::regex>(nsit.value()) ==
e.full_name(false); e.full_name(false);
} }
}); });
} }
element_filter::element_filter( element_filter::element_filter(
filter_t type, std::vector<config::string_or_regex> elements) filter_t type, std::vector<common::string_or_regex> elements)
: filter_visitor{type} : filter_visitor{type}
, elements_{std::move(elements)} , elements_{std::move(elements)}
{ {
@ -312,7 +312,7 @@ tvl::value_t method_type_filter::match(
} }
subclass_filter::subclass_filter( subclass_filter::subclass_filter(
filter_t type, std::vector<config::string_or_regex> roots) filter_t type, std::vector<common::string_or_regex> roots)
: filter_visitor{type} : filter_visitor{type}
, roots_{std::move(roots)} , roots_{std::move(roots)}
{ {
@ -361,7 +361,8 @@ tvl::value_t subclass_filter::match(const diagram &d, const element &e) const
return false; return false;
} }
parents_filter::parents_filter(filter_t type, std::vector<std::string> children) parents_filter::parents_filter(
filter_t type, std::vector<common::string_or_regex> children)
: filter_visitor{type} : filter_visitor{type}
, children_{std::move(children)} , 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 // First get all parents of element e
clanguml::common::reference_set<class_diagram::model::class_> parents; clanguml::common::reference_set<class_diagram::model::class_> parents;
for (const auto &child : children_) { for (const auto &child_pattern : children_) {
auto child_ref = cd.find<class_diagram::model::class_>(child); auto child_refs = cd.find<class_diagram::model::class_>(child_pattern);
if (!child_ref.has_value())
continue; for (auto &child : child_refs) {
parents.emplace(child_ref.value()); if (child.has_value())
parents.emplace(child.value());
}
} }
cd.get_parents(parents); cd.get_parents(parents);

View File

@ -113,7 +113,7 @@ private:
struct namespace_filter : public filter_visitor { struct namespace_filter : public filter_visitor {
namespace_filter( namespace_filter(
filter_t type, std::vector<config::namespace_or_regex> namespaces); filter_t type, std::vector<common::namespace_or_regex> namespaces);
~namespace_filter() override = default; ~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; tvl::value_t match(const diagram &d, const element &e) const override;
private: private:
std::vector<config::namespace_or_regex> namespaces_; std::vector<common::namespace_or_regex> namespaces_;
}; };
struct element_filter : public filter_visitor { struct element_filter : public filter_visitor {
element_filter( element_filter(
filter_t type, std::vector<config::string_or_regex> elements); filter_t type, std::vector<common::string_or_regex> elements);
~element_filter() override = default; ~element_filter() override = default;
tvl::value_t match(const diagram &d, const element &e) const override; tvl::value_t match(const diagram &d, const element &e) const override;
private: private:
std::vector<config::string_or_regex> elements_; std::vector<common::string_or_regex> elements_;
}; };
struct element_type_filter : public filter_visitor { struct element_type_filter : public filter_visitor {
@ -162,25 +162,25 @@ private:
}; };
struct subclass_filter : public filter_visitor { struct subclass_filter : public filter_visitor {
subclass_filter(filter_t type, std::vector<config::string_or_regex> roots); subclass_filter(filter_t type, std::vector<common::string_or_regex> roots);
~subclass_filter() override = default; ~subclass_filter() override = default;
tvl::value_t match(const diagram &d, const element &e) const override; tvl::value_t match(const diagram &d, const element &e) const override;
private: private:
std::vector<config::string_or_regex> roots_; std::vector<common::string_or_regex> roots_;
}; };
struct parents_filter : public filter_visitor { struct parents_filter : public filter_visitor {
parents_filter(filter_t type, std::vector<std::string> roots); parents_filter(filter_t type, std::vector<common::string_or_regex> roots);
~parents_filter() override = default; ~parents_filter() override = default;
tvl::value_t match(const diagram &d, const element &e) const override; tvl::value_t match(const diagram &d, const element &e) const override;
private: private:
std::vector<std::string> children_; std::vector<common::string_or_regex> children_;
}; };
template <typename DiagramT, typename ElementT, template <typename DiagramT, typename ElementT,

27
src/common/types.cc Normal file
View File

@ -0,0 +1,27 @@
/**
* src/common/types.cc
*
* Copyright (c) 2021-2023 Bartek Kryza <bkryza@gmail.com>
*
* 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(); }
};

View File

@ -1,5 +1,5 @@
/** /**
* src/class_diagram/visitor/translation_unit_visitor.h * src/common/types.h
* *
* Copyright (c) 2021-2023 Bartek Kryza <bkryza@gmail.com> * Copyright (c) 2021-2023 Bartek Kryza <bkryza@gmail.com>
* *
@ -20,15 +20,21 @@
#include <cassert> #include <cassert>
#include <cstdint> #include <cstdint>
#include <optional> #include <optional>
#include <regex>
#include <unordered_set> #include <unordered_set>
#include <variant>
#include <vector> #include <vector>
#include "model/namespace.h"
namespace clanguml::common { namespace clanguml::common {
using id_t = int64_t; using id_t = int64_t;
enum class generator_type_t { plantuml, json }; enum class generator_type_t { plantuml, json };
std::string to_string(const std::string &s);
template <typename T> class optional_ref { template <typename T> class optional_ref {
public: public:
using optional_type = T; using optional_type = T;
@ -137,4 +143,76 @@ using reference_vector = std::vector<std::reference_wrapper<T>>;
template <typename T> template <typename T>
using reference_set = std::unordered_set<std::reference_wrapper<T>>; using reference_set = std::unordered_set<std::reference_wrapper<T>>;
/**
* @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 <typename T> 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<regex>(value_))
return std::regex_match(v, std::get<regex>(value_).regexp);
return std::get<T>(value_) == v;
}
std::string to_string() const
{
if (std::holds_alternative<regex>(value_))
return std::get<regex>(value_).pattern;
return clanguml::common::to_string(std::get<T>(value_));
}
const std::variant<T, regex> &value() const { return value_; }
private:
std::variant<T, regex> value_;
};
using string_or_regex = or_regex<std::string>;
std::string to_string(string_or_regex sr);
using namespace_or_regex = common::or_regex<common::model::namespace_>;
} // namespace clanguml::common } // namespace clanguml::common

View File

@ -24,8 +24,6 @@
namespace clanguml::config { namespace clanguml::config {
std::string to_string(const std::string &s) { return s; }
std::string to_string(const hint_t t) std::string to_string(const hint_t t)
{ {
switch (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) std::string to_string(const comment_parser_t cp)
{ {
switch (cp) { switch (cp) {

View File

@ -19,6 +19,7 @@
#include "class_diagram/model/diagram.h" #include "class_diagram/model/diagram.h"
#include "common/model/enums.h" #include "common/model/enums.h"
#include "common/types.h"
#include "option.h" #include "option.h"
#include "util/util.h" #include "util/util.h"
@ -36,80 +37,6 @@
namespace clanguml { namespace clanguml {
namespace config { 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 <typename T> 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<regex>(value_))
return std::regex_match(v, std::get<regex>(value_).regexp);
return std::get<T>(value_) == v;
}
std::string to_string() const
{
if (std::holds_alternative<regex>(value_))
return std::get<regex>(value_).pattern;
return clanguml::config::to_string(std::get<T>(value_));
}
const std::variant<T, regex> &value() const { return value_; }
private:
std::variant<T, regex> value_;
};
using string_or_regex = or_regex<std::string>;
std::string to_string(string_or_regex sr);
using namespace_or_regex = or_regex<common::model::namespace_>;
enum class method_arguments { full, abbreviated, none }; enum class method_arguments { full, abbreviated, none };
enum class method_type { enum class method_type {
@ -148,9 +75,9 @@ struct diagram_template {
}; };
struct filter { struct filter {
std::vector<namespace_or_regex> namespaces; std::vector<common::namespace_or_regex> namespaces;
std::vector<string_or_regex> elements; std::vector<common::string_or_regex> elements;
// E.g.: // E.g.:
// - class // - class
@ -170,9 +97,9 @@ struct filter {
// - private // - private
std::vector<common::model::access_t> access; std::vector<common::model::access_t> access;
std::vector<string_or_regex> subclasses; std::vector<common::string_or_regex> subclasses;
std::vector<std::string> parents; std::vector<common::string_or_regex> parents;
std::vector<std::string> specializations; std::vector<std::string> specializations;

View File

@ -20,6 +20,8 @@
#include "diagram_templates.h" #include "diagram_templates.h"
namespace YAML { namespace YAML {
using clanguml::common::namespace_or_regex;
using clanguml::common::string_or_regex;
using clanguml::common::model::access_t; using clanguml::common::model::access_t;
using clanguml::common::model::relationship_t; using clanguml::common::model::relationship_t;
using clanguml::config::class_diagram; using clanguml::config::class_diagram;
@ -35,14 +37,12 @@ using clanguml::config::location_t;
using clanguml::config::member_order_t; using clanguml::config::member_order_t;
using clanguml::config::method_arguments; using clanguml::config::method_arguments;
using clanguml::config::method_type; using clanguml::config::method_type;
using clanguml::config::namespace_or_regex;
using clanguml::config::package_diagram; using clanguml::config::package_diagram;
using clanguml::config::package_type_t; using clanguml::config::package_type_t;
using clanguml::config::plantuml; using clanguml::config::plantuml;
using clanguml::config::relationship_hint_t; using clanguml::config::relationship_hint_t;
using clanguml::config::sequence_diagram; using clanguml::config::sequence_diagram;
using clanguml::config::source_location; using clanguml::config::source_location;
using clanguml::config::string_or_regex;
inline bool has_key(const YAML::Node &n, const std::string &key) inline bool has_key(const YAML::Node &n, const std::string &key)
{ {

View File

@ -18,39 +18,7 @@
#include "config.h" #include "config.h"
namespace clanguml::common::model { namespace clanguml::common {
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;
}
YAML::Emitter &operator<<(YAML::Emitter &out, const string_or_regex &m) 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; 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) YAML::Emitter &operator<<(YAML::Emitter &out, const filter &f)
{ {
out << YAML::BeginMap; out << YAML::BeginMap;

View File

@ -54,3 +54,8 @@ diagrams:
include: include:
subclasses: subclasses:
- r: 'ns1::ns2::Base[A|B]' - r: 'ns1::ns2::Base[A|B]'
regex_parents_test:
type: class
include:
parents:
- r: 'ns1::ns2::.+[1|2]'

View File

@ -312,6 +312,110 @@ TEST_CASE("Test subclasses regexp filter", "[unit-test]")
CHECK(!filter.should_include(*c)); 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<package>(config.using_namespace());
p->set_namespace({});
p->set_name("ns1");
diagram.add({}, std::move(p));
p = std::make_unique<package>(config.using_namespace());
p->set_namespace({"ns1"});
p->set_name("ns2");
diagram.add(namespace_{"ns1"}, std::move(p));
auto c = std::make_unique<class_>(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<class_>(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<class_>(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<class_>(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<class_>(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<class_>(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<class_>(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<class_>(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<class_>(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<class_>(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<class_>(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 /// Main test function
/// ///