Fixed dependants and dependencies include diagram handling

This commit is contained in:
Bartek Kryza
2022-04-23 16:31:01 +02:00
parent d7781794ec
commit cc22494c79
14 changed files with 295 additions and 97 deletions

View File

@@ -20,6 +20,7 @@
#include "class_diagram/model/class.h"
#include "common/model/package.h"
#include "include_diagram/model/diagram.h"
#include "package_diagram/model/diagram.h"
namespace clanguml::common::model {
@@ -47,6 +48,13 @@ const std::vector<type_safe::object_ref<const common::model::package>> &view(
return d.packages();
}
template <>
const std::vector<type_safe::object_ref<const common::model::source_file>> &
view(const include_diagram::model::diagram &d)
{
return d.files();
}
template <>
const type_safe::optional_ref<const class_diagram::model::class_> get(
const class_diagram::model::diagram &d, const std::string &full_name)
@@ -61,8 +69,15 @@ const type_safe::optional_ref<const common::model::package> get(
return d.get_package(full_name);
}
template <>
const type_safe::optional_ref<const common::model::source_file> get(
const include_diagram::model::diagram &d, const std::string &full_name)
{
return d.get_file(full_name);
}
} // namespace detail
filter_visitor::filter_visitor(filter_t type)
: type_{type}
{
@@ -124,6 +139,13 @@ tvl::value_t anyof_filter::match(
[&d, &e](const auto &f) { return f->match(d, e); });
}
tvl::value_t anyof_filter::match(
const diagram &d, const common::model::source_file &e) const
{
return tvl::any_of(filters_.begin(), filters_.end(),
[&d, &e](const auto &f) { return f->match(d, e); });
}
namespace_filter::namespace_filter(
filter_t type, std::vector<namespace_> namespaces)
: filter_visitor{type}
@@ -389,33 +411,72 @@ void diagram_filter::init_filters(const config::diagram &c)
element_filters.emplace_back(std::make_unique<element_filter>(
filter_t::kInclusive, c.include().elements));
element_filters.emplace_back(std::make_unique<subclass_filter>(
filter_t::kInclusive, c.include().subclasses));
if (c.type() == diagram_t::kClass) {
element_filters.emplace_back(std::make_unique<subclass_filter>(
filter_t::kInclusive, c.include().subclasses));
element_filters.emplace_back(
std::make_unique<tree_element_filter<class_diagram::model::diagram,
class_diagram::model::class_>>(filter_t::kInclusive,
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,
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<tree_element_filter<
package_diagram::model::diagram, common::model::package>>(
filter_t::kInclusive, relationship_t::kDependency,
c.include().dependants));
element_filters.emplace_back(
std::make_unique<tree_element_filter<class_diagram::model::diagram,
class_diagram::model::class_>>(filter_t::kInclusive,
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().dependencies, true));
}
else if (c.type() == diagram_t::kPackage) {
element_filters.emplace_back(std::make_unique<tree_element_filter<
package_diagram::model::diagram, common::model::package>>(
filter_t::kInclusive, relationship_t::kDependency,
c.include().dependants));
element_filters.emplace_back(std::make_unique<tree_element_filter<
package_diagram::model::diagram, common::model::package>>(
filter_t::kInclusive, relationship_t::kDependency,
c.include().dependencies, true));
element_filters.emplace_back(std::make_unique<tree_element_filter<
package_diagram::model::diagram, common::model::package>>(
filter_t::kInclusive, relationship_t::kDependency,
c.include().dependencies, true));
}
else if (c.type() == diagram_t::kInclude) {
std::vector<std::string> dependants;
std::vector<std::string> dependencies;
for (auto &&path : c.include().dependants) {
std::filesystem::path dep_path{path};
if (dep_path.is_relative()) {
dep_path = c.base_directory() / path;
dep_path = relative(dep_path, c.relative_to());
}
dependants.emplace_back(dep_path.lexically_normal().string());
}
for (auto &&path : c.include().dependencies) {
std::filesystem::path dep_path{path};
if (dep_path.is_relative()) {
dep_path = c.base_directory() / path;
dep_path = relative(dep_path, c.relative_to());
}
dependencies.emplace_back(dep_path.lexically_normal().string());
}
element_filters.emplace_back(std::make_unique<
tree_element_filter<include_diagram::model::diagram,
common::model::source_file, common::model::source_file>>(
filter_t::kInclusive, relationship_t::kAssociation,
dependants));
element_filters.emplace_back(std::make_unique<
tree_element_filter<include_diagram::model::diagram,
common::model::source_file, common::model::source_file>>(
filter_t::kInclusive, relationship_t::kAssociation,
dependencies, true));
}
element_filters.emplace_back(std::make_unique<context_filter>(
filter_t::kInclusive, c.include().context));
@@ -469,6 +530,41 @@ void diagram_filter::init_filters(const config::diagram &c)
filter_t::kExclusive, relationship_t::kDependency,
c.exclude().dependencies, true));
if (c.type() == diagram_t::kInclude) {
std::vector<std::string> dependants;
std::vector<std::string> dependencies;
for (auto &&path : c.exclude().dependants) {
std::filesystem::path dep_path{path};
if (dep_path.is_relative()) {
dep_path = c.base_directory() / path;
dep_path = relative(dep_path, c.relative_to());
}
dependants.emplace_back(dep_path.lexically_normal().string());
}
for (auto &&path : c.exclude().dependencies) {
std::filesystem::path dep_path{path};
if (dep_path.is_relative()) {
dep_path = c.base_directory() / path;
dep_path = relative(dep_path, c.relative_to());
}
dependencies.emplace_back(dep_path.lexically_normal().string());
}
add_exclusive_filter(std::make_unique<
tree_element_filter<include_diagram::model::diagram,
common::model::source_file>>(filter_t::kExclusive,
relationship_t::kAssociation, dependencies, true));
add_exclusive_filter(std::make_unique<
tree_element_filter<include_diagram::model::diagram,
common::model::source_file>>(filter_t::kExclusive,
relationship_t::kAssociation, dependants));
}
add_exclusive_filter(std::make_unique<context_filter>(
filter_t::kExclusive, c.exclude().context));
}

View File

@@ -25,6 +25,7 @@
#include "config/config.h"
#include "cx/util.h"
#include "diagram.h"
#include "include_diagram/model/diagram.h"
#include "source_file.h"
#include "tvl.h"
@@ -79,6 +80,9 @@ struct anyof_filter : public filter_visitor {
tvl::value_t match(
const diagram &d, const common::model::element &e) const override;
tvl::value_t match(
const diagram &d, const common::model::source_file &e) const override;
private:
std::vector<std::unique_ptr<filter_visitor>> filters_;
};
@@ -112,7 +116,8 @@ private:
std::vector<std::string> roots_;
};
template <typename DiagramT, typename ElementT>
template <typename DiagramT, typename ElementT,
typename MatchOverrideT = common::model::element>
struct tree_element_filter : public filter_visitor {
tree_element_filter(filter_t type, relationship_t relationship,
std::vector<std::string> roots, bool forward = false)
@@ -123,7 +128,7 @@ struct tree_element_filter : public filter_visitor {
{
}
tvl::value_t match(const diagram &d, const element &e) const override
tvl::value_t match(const diagram &d, const MatchOverrideT &e) const override
{
// This filter should only be run on the completely generated diagram
// model by visitor
@@ -168,41 +173,92 @@ private:
matching_elements_.emplace(template_ref.value());
}
auto match_tree_rel = [&, this](const auto &from, const auto &to) {
bool added_new_element{false};
assert(!matching_elements_.empty());
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) {
if (rel.destination() == to_el->full_name(false)) {
const auto &to_add = forward_ ? to_el : from_el;
if (matching_elements_.insert(to_add).second)
added_new_element = true;
if constexpr (std::is_same_v<ElementT, common::model::source_file>) {
auto match_tree_rel = [&, this](const auto &from, const auto &to) {
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()) {
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;
};
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;
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 {
if (match_tree_rel(
detail::view<ElementT>(cd), matching_elements_))
keep_looking = true;
}
else {
auto match_tree_rel = [&, this](const auto &from, const auto &to) {
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()) {
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;
}
}
}

View File

@@ -129,4 +129,15 @@ template <> struct hash<clanguml::common::model::filesystem_path> {
}
};
template <>
struct hash<type_safe::object_ref<const clanguml::common::model::source_file>> {
std::size_t operator()(
const type_safe::object_ref<const clanguml::common::model::source_file>
&key) const
{
using clanguml::common::model::source_file;
return std::hash<std::string>{}(key.get().full_name(false));
}
};
}

View File

@@ -42,7 +42,11 @@ void generator::generate_relationships(
}
else {
for (const auto &r : f.relationships()) {
if (m_model.should_include(r.type())) {
if (m_model.should_include(r.type()) &&
// make sure we only generate relationships for elements
// included in the diagram
util::contains(m_generated_aliases, r.destination()) &&
util::contains(m_generated_aliases, f.alias())) {
ostr << f.alias() << " "
<< plantuml_common::to_plantuml(r.type(), r.style()) << " "
<< r.destination() << '\n';
@@ -63,15 +67,21 @@ void generator::generate(const source_file &f, std::ostream &ostr) const
generate(dynamic_cast<const source_file &>(*file), ostr);
}
ostr << "}" << '\n';
m_generated_aliases.emplace(f.alias());
}
else {
ostr << "file \"" << f.name() << "\" as " << f.alias();
if (m_model.should_include(f)) {
ostr << "file \"" << f.name() << "\" as " << f.alias();
if (m_config.generate_links) {
generate_link(ostr, f);
if (m_config.generate_links) {
generate_link(ostr, f);
}
ostr << '\n';
m_generated_aliases.emplace(f.alias());
}
ostr << '\n';
}
}
@@ -83,7 +93,9 @@ void generator::generate(std::ostream &ostr) const
// Generate files and folders
for (const auto &p : m_model) {
generate(dynamic_cast<source_file &>(*p), ostr);
if (p->type() == common::model::source_file_t::kDirectory ||
m_model.should_include(*p))
generate(dynamic_cast<source_file &>(*p), ostr);
}
// Process file include relationships

View File

@@ -25,7 +25,7 @@ namespace clanguml::include_diagram::model {
common::model::diagram_t diagram::type() const
{
return common::model::diagram_t::kPackage;
return common::model::diagram_t::kInclude;
}
type_safe::optional_ref<const common::model::diagram_element> diagram::get(
@@ -94,11 +94,18 @@ std::string diagram::to_alias(const std::string &full_name) const
return source_file.value().alias();
}
const std::vector<
type_safe::object_ref<const common::model::source_file, false>> &
diagram::files() const
{
return files_;
}
}
namespace clanguml::common::model {
template <>
bool check_diagram_type<clanguml::include_diagram::model::diagram>(diagram_t t)
bool check_diagram_type<include_diagram::model::diagram>(diagram_t t)
{
return t == diagram_t::kInclude;
}

View File

@@ -52,6 +52,10 @@ public:
std::string to_alias(const std::string &full_name) const;
const std::vector<
type_safe::object_ref<const common::model::source_file, false>> &
files() const;
private:
std::vector<type_safe::object_ref<const common::model::source_file, false>>
files_;