Added dependants diagram filter

This commit is contained in:
Bartek Kryza
2022-04-18 11:59:56 +02:00
parent 74add47464
commit 11dccf1496
10 changed files with 255 additions and 95 deletions

View File

@@ -22,16 +22,15 @@
#include "util/util.h"
#include <cassert>
#include <iostream>
namespace clanguml::class_diagram::model {
const std::vector<type_safe::object_ref<const class_>> diagram::classes() const
const std::vector<type_safe::object_ref<const class_>> &diagram::classes() const
{
return classes_;
}
const std::vector<type_safe::object_ref<const enum_>> diagram::enums() const
const std::vector<type_safe::object_ref<const enum_>> &diagram::enums() const
{
return enums_;
}

View File

@@ -47,9 +47,9 @@ public:
type_safe::optional_ref<const clanguml::common::model::diagram_element> get(
const std::string &full_name) const override;
const std::vector<type_safe::object_ref<const class_>> classes() const;
const std::vector<type_safe::object_ref<const class_>> &classes() const;
const std::vector<type_safe::object_ref<const enum_>> enums() const;
const std::vector<type_safe::object_ref<const enum_>> &enums() const;
bool has_class(const class_ &c) const;

View File

@@ -18,8 +18,35 @@
#include "diagram_filter.h"
#include "class_diagram/model/class.h"
namespace clanguml::common::model {
namespace detail {
template <>
const std::vector<type_safe::object_ref<const class_diagram::model::class_>> &
view(const class_diagram::model::diagram &d)
{
return d.classes();
}
template <>
const std::vector<type_safe::object_ref<const class_diagram::model::enum_>> &
view(const class_diagram::model::diagram &d)
{
return d.enums();
}
template <>
const type_safe::optional_ref<const class_diagram::model::class_> get(
const class_diagram::model::diagram &d, const std::string &full_name)
{
return d.get_class(full_name);
}
}
filter_visitor::filter_visitor(filter_t type)
: type_{type}
{
@@ -174,85 +201,6 @@ tvl::value_t subclass_filter::match(const diagram &d, const element &e) const
return false;
}
specialization_filter::specialization_filter(
filter_t type, std::vector<std::string> roots)
: filter_visitor{type}
, roots_{roots}
{
}
void specialization_filter::init(const class_diagram::model::diagram &cd) const
{
if (initialized_)
return;
// First get all templates specified in the configuration
for (const auto &template_root : roots_) {
auto template_ref = cd.get_class(template_root);
if (template_ref.has_value())
templates_.emplace(template_ref.value());
}
// Iterate over the templates set, until no new template instantiations or
// specializations are found
bool found_new_template{true};
while (found_new_template) {
found_new_template = false;
for (const auto &t : cd.classes()) {
auto tfn = t->full_name(false);
auto tfn_relative = t->full_name(true);
for (const auto &r : t->relationships()) {
if (r.type() == relationship_t::kInstantiation) {
auto r_dest = r.destination();
for (const auto &t_dest : templates_) {
auto t_dest_full = t_dest->full_name(true);
if (r_dest == t_dest_full) {
auto inserted = templates_.insert(t);
if (inserted.second)
found_new_template = true;
}
}
}
}
}
}
initialized_ = true;
}
tvl::value_t specialization_filter::match(
const diagram &d, const element &e) const
{
if (d.type() != diagram_t::kClass)
return {};
if (roots_.empty())
return {};
if (!d.complete())
return {};
const auto &cd = dynamic_cast<const class_diagram::model::diagram &>(d);
init(cd);
const auto &fn = e.full_name(false);
auto class_ref = cd.get_class(fn);
if (!class_ref.has_value())
// Couldn't find the element in the diagram
return false;
// Now check if the e element is contained in the calculated set
const auto &e_full_name = e.full_name(true);
bool res = std::find_if(templates_.begin(), templates_.end(),
[&e_full_name](const auto &te) {
return te->full_name(true) == e_full_name;
}) != templates_.end();
return res;
}
relationship_filter::relationship_filter(
filter_t type, std::vector<relationship_t> relationships)
: filter_visitor{type}
@@ -422,8 +370,17 @@ void diagram_filter::init_filters(const config::diagram &c)
filter_t::kInclusive, c.include().elements));
element_filters.emplace_back(std::make_unique<subclass_filter>(
filter_t::kInclusive, c.include().subclasses));
element_filters.emplace_back(std::make_unique<specialization_filter>(
filter_t::kInclusive, c.include().specializations));
element_filters.emplace_back(
std::make_unique<tree_element_filter<class_diagram::model::diagram,
class_diagram::model::class_>>(filter_t::kInclusive,
relationship_t::kInstantiation, c.include().specializations));
element_filters.emplace_back(
std::make_unique<tree_element_filter<class_diagram::model::diagram,
class_diagram::model::class_>>(filter_t::kInclusive,
relationship_t::kDependency, c.include().dependants));
element_filters.emplace_back(std::make_unique<context_filter>(
filter_t::kInclusive, c.include().context));
@@ -445,8 +402,14 @@ void diagram_filter::init_filters(const config::diagram &c)
filter_t::kExclusive, c.exclude().access));
add_exclusive_filter(std::make_unique<subclass_filter>(
filter_t::kExclusive, c.exclude().subclasses));
add_exclusive_filter(std::make_unique<specialization_filter>(
filter_t::kExclusive, c.exclude().specializations));
add_exclusive_filter(
std::make_unique<tree_element_filter<class_diagram::model::diagram,
class_diagram::model::class_>>(filter_t::kExclusive,
relationship_t::kInstantiation, c.exclude().specializations));
add_exclusive_filter(
std::make_unique<tree_element_filter<class_diagram::model::diagram,
class_diagram::model::class_>>(filter_t::kExclusive,
relationship_t::kDependency, c.exclude().dependants));
add_exclusive_filter(std::make_unique<context_filter>(
filter_t::kExclusive, c.exclude().context));
}

View File

@@ -34,6 +34,16 @@ namespace clanguml::common::model {
enum filter_t { kInclusive, kExclusive };
namespace detail {
template <typename ElementT, typename DiagramT>
const std::vector<type_safe::object_ref<const ElementT>> &view(
const DiagramT &d);
template <typename ElementT, typename DiagramT>
const type_safe::optional_ref<const ElementT> get(
const DiagramT &d, const std::string &full_name);
} // namespace detail
class filter_visitor {
public:
filter_visitor(filter_t type);
@@ -102,19 +112,101 @@ private:
std::vector<std::string> roots_;
};
struct specialization_filter : public filter_visitor {
specialization_filter(filter_t type, std::vector<std::string> roots);
template <typename DiagramT, typename ElementT>
struct tree_element_filter : public filter_visitor {
tree_element_filter(filter_t type, relationship_t relationship,
std::vector<std::string> roots)
: filter_visitor{type}
, relationship_{relationship}
, roots_{roots}
{
}
tvl::value_t match(const diagram &d, const element &e) const override;
tvl::value_t match(const diagram &d, const element &e) const override
{
if (roots_.empty())
return {};
// This filter should only be run on the completely generated diagram
// model by visitor
if (!d.complete())
return {};
const auto &cd = dynamic_cast<const DiagramT &>(d);
// Calculate the set of matching elements
init(cd);
const auto &fn = e.full_name(false);
auto element_ref = detail::get<ElementT>(cd, fn);
if (!element_ref.has_value())
// Couldn't find the element in the diagram
return false;
// Now check if the e element is contained in the calculated set
const auto &e_full_name = e.full_name(false);
bool res =
std::find_if(matching_elements_.begin(), matching_elements_.end(),
[&e_full_name](const auto &te) {
return te->full_name(false) == e_full_name;
}) != matching_elements_.end();
return res;
}
private:
void init(const class_diagram::model::diagram &d) const;
void init(const DiagramT &cd) const
{
if (initialized_)
return;
// First get all elements specified in the filter configuration
for (const auto &template_root : roots_) {
auto template_ref = detail::get<ElementT>(cd, template_root);
if (template_ref.has_value())
matching_elements_.emplace(template_ref.value());
}
// Iterate over the templates set, until no new template instantiations
// or specializations are found
bool found_new_template{true};
auto reverse_relationship_match = [&found_new_template, this](
const relationship &rel,
const auto &el) {
if (rel.type() == relationship_) {
for (const auto &already_matching : matching_elements_) {
if (rel.destination() ==
already_matching->full_name(false)) {
auto inserted = matching_elements_.insert(el);
if (inserted.second)
found_new_template = true;
}
}
}
};
while (found_new_template) {
found_new_template = false;
// For each element of type ElementT in the diagram
for (const auto &el : detail::view<ElementT>(cd)) {
// Check if any of its relationships of type relationship_
// points to an element already in the matching_elements_ set
for (const auto &rel : el->relationships()) {
reverse_relationship_match(rel, el);
}
}
}
initialized_ = true;
}
std::vector<std::string> roots_;
relationship_t relationship_;
mutable bool initialized_{false};
mutable std::unordered_set<
type_safe::object_ref<const class_diagram::model::class_, false>>
templates_;
mutable std::unordered_set<type_safe::object_ref<const ElementT, false>>
matching_elements_;
};
struct relationship_filter : public filter_visitor {

View File

@@ -412,6 +412,9 @@ template <> struct convert<filter> {
rhs.specializations =
node["specializations"].as<decltype(rhs.specializations)>();
if (node["dependants"])
rhs.dependants = node["dependants"].as<decltype(rhs.dependants)>();
if (node["context"])
rhs.context = node["context"].as<decltype(rhs.context)>();

View File

@@ -72,6 +72,8 @@ struct filter {
std::vector<std::string> specializations;
std::vector<std::string> dependants;
std::vector<std::string> context;
std::vector<std::filesystem::path> paths;