Added parents (base classes) diagram filter

This commit is contained in:
Bartek Kryza
2023-03-06 22:32:02 +01:00
parent 8ba0239283
commit 8f99e2fc2f
11 changed files with 158 additions and 0 deletions

View File

@@ -7,6 +7,7 @@
* [`context`](#context)
* [`relationships`](#relationships)
* [`subclasses`](#subclasses)
* [`parents`](#parents)
* [`specializations`](#specializations)
* [`dependants` and `dependencies`](#dependants-and-dependencies)
@@ -89,6 +90,10 @@ The following relationships can be used in this filter:
This filter allows to include or exclude all subclasses of a given class in the diagram.
## `parents`
This filter allows to include or exclude all parents (base classes) of a given class in the diagram.
## `specializations`
This filter allows to include or exclude specializations and instantiations of a specific template from the diagram.

View File

@@ -263,6 +263,45 @@ tvl::value_t subclass_filter::match(const diagram &d, const element &e) const
return false;
}
parents_filter::parents_filter(filter_t type, std::vector<std::string> children)
: filter_visitor{type}
, children_{std::move(children)}
{
}
tvl::value_t parents_filter::match(const diagram &d, const element &e) const
{
if (d.type() != diagram_t::kClass)
return {};
if (children_.empty())
return {};
if (!d.complete())
return {};
const auto &cd = dynamic_cast<const class_diagram::model::diagram &>(d);
// First get all parents of element e
clanguml::common::reference_set<class_diagram::model::class_> parents;
for (const auto &child : children_) {
auto child_ref = cd.get_class(child);
if (!child_ref.has_value())
continue;
parents.emplace(child_ref.value());
}
cd.get_parents(parents);
for (const auto &parent : parents) {
if (e == parent.get())
return true;
}
return false;
}
relationship_filter::relationship_filter(
filter_t type, std::vector<relationship_t> relationships)
: filter_visitor{type}
@@ -465,6 +504,9 @@ void diagram_filter::init_filters(const config::diagram &c)
element_filters.emplace_back(std::make_unique<subclass_filter>(
filter_t::kInclusive, c.include().subclasses));
element_filters.emplace_back(std::make_unique<parents_filter>(
filter_t::kInclusive, c.include().parents));
element_filters.emplace_back(std::make_unique<
edge_traversal_filter<class_diagram::model::diagram,
class_diagram::model::class_>>(filter_t::kInclusive,
@@ -545,6 +587,9 @@ void diagram_filter::init_filters(const config::diagram &c)
add_exclusive_filter(std::make_unique<subclass_filter>(
filter_t::kExclusive, c.exclude().subclasses));
add_exclusive_filter(std::make_unique<parents_filter>(
filter_t::kExclusive, c.exclude().parents));
add_exclusive_filter(std::make_unique<edge_traversal_filter<
class_diagram::model::diagram, class_diagram::model::class_>>(
filter_t::kExclusive, relationship_t::kInstantiation,

View File

@@ -135,6 +135,17 @@ private:
std::vector<std::string> roots_;
};
struct parents_filter : public filter_visitor {
parents_filter(filter_t type, std::vector<std::string> roots);
~parents_filter() override = default;
tvl::value_t match(const diagram &d, const element &e) const override;
private:
std::vector<std::string> children_;
};
template <typename DiagramT, typename ElementT,
typename MatchOverrideT = common::model::element>
struct edge_traversal_filter : public filter_visitor {

View File

@@ -69,6 +69,8 @@ struct filter {
std::vector<std::string> subclasses;
std::vector<std::string> parents;
std::vector<std::string> specializations;
std::vector<std::string> dependants;

View File

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

View File

@@ -69,6 +69,8 @@ YAML::Emitter &operator<<(YAML::Emitter &out, const filter &f)
<< f.specializations;
if (!f.subclasses.empty())
out << YAML::Key << "subclasses" << YAML::Value << f.subclasses;
if (!f.parents.empty())
out << YAML::Key << "parents" << YAML::Value << f.parents;
out << YAML::EndMap;
return out;

15
tests/t00060/.clang-uml Normal file
View File

@@ -0,0 +1,15 @@
compilation_database_dir: ..
output_directory: puml
diagrams:
t00060_class:
type: class
glob:
- ../../tests/t00060/t00060.cc
include:
namespaces:
- clanguml::t00060
parents:
- clanguml::t00060::D
- clanguml::t00060::H<T,P>
using_namespace:
- clanguml::t00060

20
tests/t00060/t00060.cc Normal file
View File

@@ -0,0 +1,20 @@
namespace clanguml {
namespace t00060 {
struct A { };
struct B : public A { };
struct C : public A { };
struct D : public B, public C { };
struct E : public C { };
struct F : public D { };
template <typename T> struct G {
T g;
};
template <typename T, typename P> struct H : public G<T> {
G<T> h;
P hh;
};
}
}

50
tests/t00060/test_case.h Normal file
View File

@@ -0,0 +1,50 @@
/**
* tests/t00060/test_case.h
*
* 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.
*/
TEST_CASE("t00060", "[test-case][class]")
{
auto [config, db] = load_config("t00060");
auto diagram = config.diagrams["t00060_class"];
REQUIRE(diagram->name == "t00060_class");
auto model = generate_class_diagram(*db, diagram);
REQUIRE(model->name() == "t00060_class");
auto puml = generate_class_puml(diagram, *model);
AliasMatcher _A(puml);
REQUIRE_THAT(puml, StartsWith("@startuml"));
REQUIRE_THAT(puml, EndsWith("@enduml\n"));
// Check if all classes exist
REQUIRE_THAT(puml, IsClass(_A("A")));
REQUIRE_THAT(puml, IsClass(_A("B")));
REQUIRE_THAT(puml, IsClass(_A("C")));
REQUIRE_THAT(puml, IsClass(_A("D")));
REQUIRE_THAT(puml, !IsClass(_A("E")));
REQUIRE_THAT(puml, !IsClass(_A("F")));
// Check if class templates exist
REQUIRE_THAT(puml, IsClassTemplate("G", "T"));
REQUIRE_THAT(puml, IsClassTemplate("H", "T,P"));
save_puml(config.output_directory() + "/" + diagram->name + ".puml", puml);
}

View File

@@ -263,6 +263,8 @@ using namespace clanguml::test::matchers;
#include "t00058/test_case.h"
#include "t00059/test_case.h"
#endif
#include "t00060/test_case.h"
///
/// Sequence diagram tests
///

View File

@@ -174,6 +174,9 @@ test_cases:
- name: t00059
title: Non-virtual abstract factory pattern using concepts test case
description:
- name: t00060
title: Parents (base classes) diagram filter test case
description:
Sequence diagrams:
- name: t20001
title: Basic sequence diagram test case