Refactored edge traversal diagram filter
This commit is contained in:
@@ -76,6 +76,12 @@ const type_safe::optional_ref<const common::model::source_file> get(
|
|||||||
return d.get_file(full_name);
|
return d.get_file(full_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
std::string destination_comparator<common::model::source_file>(
|
||||||
|
const common::model::source_file &f)
|
||||||
|
{
|
||||||
|
return f.alias();
|
||||||
|
}
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
filter_visitor::filter_visitor(filter_t type)
|
filter_visitor::filter_visitor(filter_t type)
|
||||||
@@ -416,27 +422,27 @@ void diagram_filter::init_filters(const config::diagram &c)
|
|||||||
filter_t::kInclusive, c.include().subclasses));
|
filter_t::kInclusive, c.include().subclasses));
|
||||||
|
|
||||||
element_filters.emplace_back(std::make_unique<
|
element_filters.emplace_back(std::make_unique<
|
||||||
tree_element_filter<class_diagram::model::diagram,
|
edge_traversal_filter<class_diagram::model::diagram,
|
||||||
class_diagram::model::class_>>(filter_t::kInclusive,
|
class_diagram::model::class_>>(filter_t::kInclusive,
|
||||||
relationship_t::kInstantiation, c.include().specializations));
|
relationship_t::kInstantiation, c.include().specializations));
|
||||||
|
|
||||||
element_filters.emplace_back(std::make_unique<
|
element_filters.emplace_back(std::make_unique<
|
||||||
tree_element_filter<class_diagram::model::diagram,
|
edge_traversal_filter<class_diagram::model::diagram,
|
||||||
class_diagram::model::class_>>(filter_t::kInclusive,
|
class_diagram::model::class_>>(filter_t::kInclusive,
|
||||||
relationship_t::kDependency, c.include().dependants));
|
relationship_t::kDependency, c.include().dependants));
|
||||||
|
|
||||||
element_filters.emplace_back(std::make_unique<
|
element_filters.emplace_back(std::make_unique<
|
||||||
tree_element_filter<class_diagram::model::diagram,
|
edge_traversal_filter<class_diagram::model::diagram,
|
||||||
class_diagram::model::class_>>(filter_t::kInclusive,
|
class_diagram::model::class_>>(filter_t::kInclusive,
|
||||||
relationship_t::kDependency, c.include().dependencies, true));
|
relationship_t::kDependency, c.include().dependencies, true));
|
||||||
}
|
}
|
||||||
else if (c.type() == diagram_t::kPackage) {
|
else if (c.type() == diagram_t::kPackage) {
|
||||||
element_filters.emplace_back(std::make_unique<tree_element_filter<
|
element_filters.emplace_back(std::make_unique<edge_traversal_filter<
|
||||||
package_diagram::model::diagram, common::model::package>>(
|
package_diagram::model::diagram, common::model::package>>(
|
||||||
filter_t::kInclusive, relationship_t::kDependency,
|
filter_t::kInclusive, relationship_t::kDependency,
|
||||||
c.include().dependants));
|
c.include().dependants));
|
||||||
|
|
||||||
element_filters.emplace_back(std::make_unique<tree_element_filter<
|
element_filters.emplace_back(std::make_unique<edge_traversal_filter<
|
||||||
package_diagram::model::diagram, common::model::package>>(
|
package_diagram::model::diagram, common::model::package>>(
|
||||||
filter_t::kInclusive, relationship_t::kDependency,
|
filter_t::kInclusive, relationship_t::kDependency,
|
||||||
c.include().dependencies, true));
|
c.include().dependencies, true));
|
||||||
@@ -466,13 +472,13 @@ void diagram_filter::init_filters(const config::diagram &c)
|
|||||||
}
|
}
|
||||||
|
|
||||||
element_filters.emplace_back(std::make_unique<
|
element_filters.emplace_back(std::make_unique<
|
||||||
tree_element_filter<include_diagram::model::diagram,
|
edge_traversal_filter<include_diagram::model::diagram,
|
||||||
common::model::source_file, common::model::source_file>>(
|
common::model::source_file, common::model::source_file>>(
|
||||||
filter_t::kInclusive, relationship_t::kAssociation,
|
filter_t::kInclusive, relationship_t::kAssociation,
|
||||||
dependants));
|
dependants));
|
||||||
|
|
||||||
element_filters.emplace_back(std::make_unique<
|
element_filters.emplace_back(std::make_unique<
|
||||||
tree_element_filter<include_diagram::model::diagram,
|
edge_traversal_filter<include_diagram::model::diagram,
|
||||||
common::model::source_file, common::model::source_file>>(
|
common::model::source_file, common::model::source_file>>(
|
||||||
filter_t::kInclusive, relationship_t::kAssociation,
|
filter_t::kInclusive, relationship_t::kAssociation,
|
||||||
dependencies, true));
|
dependencies, true));
|
||||||
@@ -505,27 +511,27 @@ void diagram_filter::init_filters(const config::diagram &c)
|
|||||||
add_exclusive_filter(std::make_unique<subclass_filter>(
|
add_exclusive_filter(std::make_unique<subclass_filter>(
|
||||||
filter_t::kExclusive, c.exclude().subclasses));
|
filter_t::kExclusive, c.exclude().subclasses));
|
||||||
|
|
||||||
add_exclusive_filter(
|
add_exclusive_filter(std::make_unique<edge_traversal_filter<
|
||||||
std::make_unique<tree_element_filter<class_diagram::model::diagram,
|
class_diagram::model::diagram, class_diagram::model::class_>>(
|
||||||
class_diagram::model::class_>>(filter_t::kExclusive,
|
filter_t::kExclusive, relationship_t::kInstantiation,
|
||||||
relationship_t::kInstantiation, c.exclude().specializations));
|
c.exclude().specializations));
|
||||||
|
|
||||||
add_exclusive_filter(
|
add_exclusive_filter(std::make_unique<edge_traversal_filter<
|
||||||
std::make_unique<tree_element_filter<class_diagram::model::diagram,
|
class_diagram::model::diagram, class_diagram::model::class_>>(
|
||||||
class_diagram::model::class_>>(filter_t::kExclusive,
|
filter_t::kExclusive, relationship_t::kDependency,
|
||||||
relationship_t::kDependency, c.exclude().dependants));
|
c.exclude().dependants));
|
||||||
|
|
||||||
add_exclusive_filter(std::make_unique<tree_element_filter<
|
add_exclusive_filter(std::make_unique<edge_traversal_filter<
|
||||||
package_diagram::model::diagram, common::model::package>>(
|
package_diagram::model::diagram, common::model::package>>(
|
||||||
filter_t::kExclusive, relationship_t::kDependency,
|
filter_t::kExclusive, relationship_t::kDependency,
|
||||||
c.exclude().dependants));
|
c.exclude().dependants));
|
||||||
|
|
||||||
add_exclusive_filter(
|
add_exclusive_filter(std::make_unique<edge_traversal_filter<
|
||||||
std::make_unique<tree_element_filter<class_diagram::model::diagram,
|
class_diagram::model::diagram, class_diagram::model::class_>>(
|
||||||
class_diagram::model::class_>>(filter_t::kExclusive,
|
filter_t::kExclusive, relationship_t::kDependency,
|
||||||
relationship_t::kDependency, c.exclude().dependencies, true));
|
c.exclude().dependencies, true));
|
||||||
|
|
||||||
add_exclusive_filter(std::make_unique<tree_element_filter<
|
add_exclusive_filter(std::make_unique<edge_traversal_filter<
|
||||||
package_diagram::model::diagram, common::model::package>>(
|
package_diagram::model::diagram, common::model::package>>(
|
||||||
filter_t::kExclusive, relationship_t::kDependency,
|
filter_t::kExclusive, relationship_t::kDependency,
|
||||||
c.exclude().dependencies, true));
|
c.exclude().dependencies, true));
|
||||||
@@ -555,12 +561,12 @@ void diagram_filter::init_filters(const config::diagram &c)
|
|||||||
}
|
}
|
||||||
|
|
||||||
add_exclusive_filter(std::make_unique<
|
add_exclusive_filter(std::make_unique<
|
||||||
tree_element_filter<include_diagram::model::diagram,
|
edge_traversal_filter<include_diagram::model::diagram,
|
||||||
common::model::source_file>>(filter_t::kExclusive,
|
common::model::source_file>>(filter_t::kExclusive,
|
||||||
relationship_t::kAssociation, dependencies, true));
|
relationship_t::kAssociation, dependencies, true));
|
||||||
|
|
||||||
add_exclusive_filter(std::make_unique<
|
add_exclusive_filter(std::make_unique<
|
||||||
tree_element_filter<include_diagram::model::diagram,
|
edge_traversal_filter<include_diagram::model::diagram,
|
||||||
common::model::source_file>>(filter_t::kExclusive,
|
common::model::source_file>>(filter_t::kExclusive,
|
||||||
relationship_t::kAssociation, dependants));
|
relationship_t::kAssociation, dependants));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,6 +43,15 @@ const std::vector<type_safe::object_ref<const ElementT>> &view(
|
|||||||
template <typename ElementT, typename DiagramT>
|
template <typename ElementT, typename DiagramT>
|
||||||
const type_safe::optional_ref<const ElementT> get(
|
const type_safe::optional_ref<const ElementT> get(
|
||||||
const DiagramT &d, const std::string &full_name);
|
const DiagramT &d, const std::string &full_name);
|
||||||
|
|
||||||
|
template <typename ElementT>
|
||||||
|
std::string destination_comparator(const ElementT &e)
|
||||||
|
{
|
||||||
|
return e.full_name(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
std::string destination_comparator(const common::model::source_file &f);
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
class filter_visitor {
|
class filter_visitor {
|
||||||
@@ -118,8 +127,8 @@ private:
|
|||||||
|
|
||||||
template <typename DiagramT, typename ElementT,
|
template <typename DiagramT, typename ElementT,
|
||||||
typename MatchOverrideT = common::model::element>
|
typename MatchOverrideT = common::model::element>
|
||||||
struct tree_element_filter : public filter_visitor {
|
struct edge_traversal_filter : public filter_visitor {
|
||||||
tree_element_filter(filter_t type, relationship_t relationship,
|
edge_traversal_filter(filter_t type, relationship_t relationship,
|
||||||
std::vector<std::string> roots, bool forward = false)
|
std::vector<std::string> roots, bool forward = false)
|
||||||
: filter_visitor{type}
|
: filter_visitor{type}
|
||||||
, relationship_{relationship}
|
, relationship_{relationship}
|
||||||
@@ -161,126 +170,88 @@ struct tree_element_filter : public filter_visitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
template <typename C, typename D>
|
||||||
|
bool add_adjacent(const C &from, const D &to,
|
||||||
|
const std::vector<relationship_t> &relationships) const
|
||||||
|
{
|
||||||
|
bool added_new_element{false};
|
||||||
|
|
||||||
|
for (const auto &from_el : from) {
|
||||||
|
// Check if any of its relationships of type relationship_
|
||||||
|
// points to an element already in the matching_elements_
|
||||||
|
// set
|
||||||
|
for (const auto &rel : from_el->relationships()) {
|
||||||
|
// Consider only if connected by one of specified relationships
|
||||||
|
if (util::contains(relationships, rel.type())) {
|
||||||
|
for (const auto &to_el : to) {
|
||||||
|
if (rel.destination() ==
|
||||||
|
detail::destination_comparator(*to_el)) {
|
||||||
|
const auto &to_add = forward_ ? to_el : from_el;
|
||||||
|
if (matching_elements_.insert(to_add).second)
|
||||||
|
added_new_element = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return added_new_element;
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_parents(const DiagramT &cd) const
|
||||||
|
{
|
||||||
|
decltype(matching_elements_) parents;
|
||||||
|
|
||||||
|
util::for_each(
|
||||||
|
matching_elements_, [this, &cd, &parents](const auto &element) {
|
||||||
|
auto parent = detail::get<ElementT, DiagramT>(
|
||||||
|
cd, element.get().path().to_string());
|
||||||
|
|
||||||
|
while (parent.has_value()) {
|
||||||
|
parents.emplace(type_safe::ref(parent.value()));
|
||||||
|
parent = detail::get<ElementT, DiagramT>(
|
||||||
|
cd, parent.value().path().to_string());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
matching_elements_.insert(std::begin(parents), std::end(parents));
|
||||||
|
}
|
||||||
|
|
||||||
void init(const DiagramT &cd) const
|
void init(const DiagramT &cd) const
|
||||||
{
|
{
|
||||||
if (initialized_)
|
if (initialized_)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// First get all elements specified in the filter configuration
|
// First get all elements specified in the filter configuration
|
||||||
|
// which will serve as starting points for the search
|
||||||
|
// of matching elements
|
||||||
for (const auto &template_root : roots_) {
|
for (const auto &template_root : roots_) {
|
||||||
auto template_ref = detail::get<ElementT>(cd, template_root);
|
auto template_ref = detail::get<ElementT>(cd, template_root);
|
||||||
if (template_ref.has_value())
|
if (template_ref.has_value())
|
||||||
matching_elements_.emplace(template_ref.value());
|
matching_elements_.emplace(template_ref.value());
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(!matching_elements_.empty());
|
assert(roots_.empty() == matching_elements_.empty());
|
||||||
|
|
||||||
if constexpr (std::is_same_v<ElementT, common::model::source_file>) {
|
bool keep_looking{true};
|
||||||
auto match_tree_rel = [&, this](const auto &from, const auto &to) {
|
while (keep_looking) {
|
||||||
bool added_new_element{false};
|
keep_looking = false;
|
||||||
|
if (forward_) {
|
||||||
for (const auto &from_el : from) {
|
if (add_adjacent(matching_elements_, detail::view<ElementT>(cd),
|
||||||
// Check if any of its relationships of type relationship_
|
{relationship_}))
|
||||||
// points to an element already in the matching_elements_
|
keep_looking = true;
|
||||||
// set
|
|
||||||
for (const auto &rel : from_el->relationships()) {
|
|
||||||
if (rel.type() == relationship_) {
|
|
||||||
for (const auto &to_el : to) {
|
|
||||||
auto dest = rel.destination();
|
|
||||||
auto alias = to_el->alias();
|
|
||||||
if (dest == alias) {
|
|
||||||
const auto &to_add =
|
|
||||||
forward_ ? to_el : from_el;
|
|
||||||
if (matching_elements_.insert(to_add)
|
|
||||||
.second)
|
|
||||||
added_new_element = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return added_new_element;
|
|
||||||
};
|
|
||||||
|
|
||||||
bool keep_looking{true};
|
|
||||||
while (keep_looking) {
|
|
||||||
keep_looking = false;
|
|
||||||
if (forward_) {
|
|
||||||
if (match_tree_rel(
|
|
||||||
matching_elements_, detail::view<ElementT>(cd)))
|
|
||||||
keep_looking = true;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (match_tree_rel(
|
|
||||||
detail::view<ElementT>(cd), matching_elements_))
|
|
||||||
keep_looking = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
else {
|
||||||
else {
|
if (add_adjacent(detail::view<ElementT>(cd), matching_elements_,
|
||||||
auto match_tree_rel = [&, this](const auto &from, const auto &to) {
|
{relationship_}))
|
||||||
bool added_new_element{false};
|
keep_looking = true;
|
||||||
|
|
||||||
for (const auto &from_el : from) {
|
|
||||||
// Check if any of its relationships of type relationship_
|
|
||||||
// points to an element already in the matching_elements_
|
|
||||||
// set
|
|
||||||
for (const auto &rel : from_el->relationships()) {
|
|
||||||
if (rel.type() == relationship_) {
|
|
||||||
for (const auto &to_el : to) {
|
|
||||||
auto dest = rel.destination();
|
|
||||||
auto to_el_fn = to_el->full_name(false);
|
|
||||||
if (dest == to_el_fn) {
|
|
||||||
const auto &to_add =
|
|
||||||
forward_ ? to_el : from_el;
|
|
||||||
if (matching_elements_.insert(to_add)
|
|
||||||
.second)
|
|
||||||
added_new_element = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return added_new_element;
|
|
||||||
};
|
|
||||||
|
|
||||||
bool keep_looking{true};
|
|
||||||
while (keep_looking) {
|
|
||||||
keep_looking = false;
|
|
||||||
if (forward_) {
|
|
||||||
if (match_tree_rel(
|
|
||||||
matching_elements_, detail::view<ElementT>(cd)))
|
|
||||||
keep_looking = true;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (match_tree_rel(
|
|
||||||
detail::view<ElementT>(cd), matching_elements_))
|
|
||||||
keep_looking = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if constexpr (std::is_same_v<ElementT, common::model::package>) {
|
// For nested diagrams, include also parent elements
|
||||||
if (type() == filter_t::kInclusive) {
|
if ((type() == filter_t::kInclusive) &&
|
||||||
// For package diagrams, add also all parents of matching
|
(cd.type() == common::model::diagram_t::kPackage)) {
|
||||||
// packages
|
add_parents(cd);
|
||||||
decltype(matching_elements_) parents;
|
|
||||||
util::for_each(matching_elements_,
|
|
||||||
[this, &cd, &parents](const auto &package) {
|
|
||||||
auto parent_path = package.get().path();
|
|
||||||
auto parent = cd.get_package(parent_path.to_string());
|
|
||||||
while (parent.has_value()) {
|
|
||||||
parents.emplace(type_safe::ref(parent.value()));
|
|
||||||
parent = cd.get_package(
|
|
||||||
parent.value().path().to_string());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
matching_elements_.insert(
|
|
||||||
std::begin(parents), std::end(parents));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
initialized_ = true;
|
initialized_ = true;
|
||||||
|
|||||||
@@ -85,8 +85,9 @@ public:
|
|||||||
return type_safe::optional_ref<V>{};
|
return type_safe::optional_ref<V>{};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (path.size() == 1)
|
if (path.size() == 1) {
|
||||||
return get_element<V>(path[0]);
|
return get_element<V>(path[0]);
|
||||||
|
}
|
||||||
|
|
||||||
auto p = get_element<T>(path[0]);
|
auto p = get_element<T>(path[0]);
|
||||||
|
|
||||||
@@ -100,6 +101,18 @@ public:
|
|||||||
return type_safe::optional_ref<V>{};
|
return type_safe::optional_ref<V>{};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename V = T> auto get_element_parent(const T &element) const
|
||||||
|
{
|
||||||
|
auto path = element.path();
|
||||||
|
auto parent = get_element(path);
|
||||||
|
|
||||||
|
if (parent.has_value())
|
||||||
|
return type_safe::optional_ref<V>{
|
||||||
|
type_safe::ref<V>(dynamic_cast<V &>(parent.value()))};
|
||||||
|
|
||||||
|
return type_safe::optional_ref<V>{};
|
||||||
|
}
|
||||||
|
|
||||||
template <typename V = T> auto get_element(const std::string &name) const
|
template <typename V = T> auto get_element(const std::string &name) const
|
||||||
{
|
{
|
||||||
assert(!util::contains(name, "::"));
|
assert(!util::contains(name, "::"));
|
||||||
|
|||||||
Reference in New Issue
Block a user