Added specialization diagram filter
This commit is contained in:
@@ -498,7 +498,6 @@ void generator::generate(std::ostream &ostr) const
|
||||
generate(dynamic_cast<enum_ &>(*p), ostr);
|
||||
}
|
||||
}
|
||||
ostr << '\n';
|
||||
}
|
||||
|
||||
for (const auto &p : m_model) {
|
||||
@@ -515,7 +514,6 @@ void generator::generate(std::ostream &ostr) const
|
||||
generate_relationships(dynamic_cast<enum_ &>(*p), ostr);
|
||||
}
|
||||
}
|
||||
ostr << '\n';
|
||||
}
|
||||
|
||||
generate_config_layout_hints(ostr);
|
||||
|
||||
@@ -174,6 +174,85 @@ 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}
|
||||
@@ -280,7 +359,6 @@ paths_filter::paths_filter(filter_t type, const std::filesystem::path &root,
|
||||
tvl::value_t paths_filter::match(
|
||||
const diagram &d, const common::model::source_file &p) const
|
||||
{
|
||||
|
||||
if (paths_.empty()) {
|
||||
return {};
|
||||
}
|
||||
@@ -344,6 +422,8 @@ 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<context_filter>(
|
||||
filter_t::kInclusive, c.include().context));
|
||||
|
||||
@@ -365,6 +445,8 @@ 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<context_filter>(
|
||||
filter_t::kExclusive, c.exclude().context));
|
||||
}
|
||||
@@ -377,5 +459,4 @@ bool diagram_filter::should_include<std::string>(const std::string &name) const
|
||||
|
||||
return should_include(ns, n);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -102,6 +102,21 @@ private:
|
||||
std::vector<std::string> roots_;
|
||||
};
|
||||
|
||||
struct specialization_filter : public filter_visitor {
|
||||
specialization_filter(filter_t type, std::vector<std::string> roots);
|
||||
|
||||
tvl::value_t match(const diagram &d, const element &e) const override;
|
||||
|
||||
private:
|
||||
void init(const class_diagram::model::diagram &d) const;
|
||||
|
||||
std::vector<std::string> roots_;
|
||||
mutable bool initialized_{false};
|
||||
mutable std::unordered_set<
|
||||
type_safe::object_ref<const class_diagram::model::class_, false>>
|
||||
templates_;
|
||||
};
|
||||
|
||||
struct relationship_filter : public filter_visitor {
|
||||
relationship_filter(
|
||||
filter_t type, std::vector<relationship_t> relationships);
|
||||
|
||||
@@ -408,6 +408,10 @@ template <> struct convert<filter> {
|
||||
if (node["subclasses"])
|
||||
rhs.subclasses = node["subclasses"].as<decltype(rhs.subclasses)>();
|
||||
|
||||
if (node["specializations"])
|
||||
rhs.specializations =
|
||||
node["specializations"].as<decltype(rhs.specializations)>();
|
||||
|
||||
if (node["context"])
|
||||
rhs.context = node["context"].as<decltype(rhs.context)>();
|
||||
|
||||
|
||||
@@ -70,6 +70,8 @@ struct filter {
|
||||
|
||||
std::vector<std::string> subclasses;
|
||||
|
||||
std::vector<std::string> specializations;
|
||||
|
||||
std::vector<std::string> context;
|
||||
|
||||
std::vector<std::filesystem::path> paths;
|
||||
|
||||
19
tests/t00042/.clang-uml
Normal file
19
tests/t00042/.clang-uml
Normal file
@@ -0,0 +1,19 @@
|
||||
compilation_database_dir: ..
|
||||
output_directory: puml
|
||||
diagrams:
|
||||
t00042_class:
|
||||
type: class
|
||||
generate_packages: false
|
||||
glob:
|
||||
- ../../tests/t00042/t00042.cc
|
||||
using_namespace:
|
||||
- clanguml::t00042
|
||||
include:
|
||||
specializations:
|
||||
- clanguml::t00042::A<T>
|
||||
- clanguml::t00042::B<T,K>
|
||||
relationships:
|
||||
- instantiation
|
||||
exclude:
|
||||
specializations:
|
||||
- clanguml::t00042::C<T>
|
||||
31
tests/t00042/t00042.cc
Normal file
31
tests/t00042/t00042.cc
Normal file
@@ -0,0 +1,31 @@
|
||||
#include <string>
|
||||
|
||||
namespace clanguml::t00042 {
|
||||
|
||||
template <typename T> struct A {
|
||||
T a;
|
||||
};
|
||||
|
||||
template <> struct A<void> {
|
||||
void *a;
|
||||
};
|
||||
|
||||
template <typename T, typename K> struct B {
|
||||
T b;
|
||||
K bb;
|
||||
};
|
||||
|
||||
template <typename T> struct C {
|
||||
T c;
|
||||
};
|
||||
|
||||
struct R {
|
||||
A<double> a_double;
|
||||
A<std::string> a_string;
|
||||
|
||||
B<int, float> b_int_float;
|
||||
|
||||
C<int> c_int;
|
||||
};
|
||||
|
||||
} // namespace clanguml::t00042
|
||||
44
tests/t00042/test_case.h
Normal file
44
tests/t00042/test_case.h
Normal file
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* tests/t00042/test_case.cc
|
||||
*
|
||||
* Copyright (c) 2021-2022 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.
|
||||
*/
|
||||
|
||||
TEST_CASE("t00042", "[test-case][class]")
|
||||
{
|
||||
auto [config, db] = load_config("t00042");
|
||||
|
||||
auto diagram = config.diagrams["t00042_class"];
|
||||
|
||||
REQUIRE(diagram->name == "t00042_class");
|
||||
REQUIRE(diagram->generate_packages() == false);
|
||||
|
||||
auto model = generate_class_diagram(db, diagram);
|
||||
|
||||
REQUIRE(model->name() == "t00042_class");
|
||||
|
||||
auto puml = generate_class_puml(diagram, *model);
|
||||
AliasMatcher _A(puml);
|
||||
|
||||
REQUIRE_THAT(puml, StartsWith("@startuml"));
|
||||
REQUIRE_THAT(puml, EndsWith("@enduml\n"));
|
||||
|
||||
REQUIRE_THAT(puml, IsClassTemplate("A", "T"));
|
||||
REQUIRE_THAT(puml, IsClassTemplate("B", "T,K"));
|
||||
REQUIRE_THAT(puml, !IsClassTemplate("C", "T"));
|
||||
|
||||
save_puml(
|
||||
"./" + config.output_directory() + "/" + diagram->name + ".puml", puml);
|
||||
}
|
||||
@@ -223,6 +223,7 @@ using namespace clanguml::test::matchers;
|
||||
#include "t00039/test_case.h"
|
||||
#include "t00040/test_case.h"
|
||||
#include "t00041/test_case.h"
|
||||
#include "t00042/test_case.h"
|
||||
|
||||
//
|
||||
// Sequence diagram tests
|
||||
|
||||
@@ -120,6 +120,8 @@ test_cases:
|
||||
- name: t00041
|
||||
title: Context diagram filter test
|
||||
description:
|
||||
- name: t00042
|
||||
title: Specialization class template diagram filter test
|
||||
Sequence diagrams:
|
||||
- name: t20001
|
||||
title: Basic sequence diagram test case
|
||||
|
||||
Reference in New Issue
Block a user