From dec4b7bc3dbd4d193350c79403e9ff973c65100b Mon Sep 17 00:00:00 2001 From: Bartek Kryza Date: Mon, 8 May 2023 22:53:21 +0200 Subject: [PATCH] Added element_types diagram filter (#131) --- docs/configuration_file.md | 2 ++ docs/diagram_filters.md | 12 +++++++ src/common/model/diagram_filter.cc | 22 ++++++++++++ src/common/model/diagram_filter.h | 11 ++++++ src/config/config.h | 6 ++++ src/config/yaml_decoders.cc | 4 +++ src/config/yaml_emitters.cc | 2 ++ tests/t00063/.clang-uml | 15 ++++++++ tests/t00063/t00063.cc | 9 +++++ tests/t00063/test_case.h | 57 ++++++++++++++++++++++++++++++ tests/test_cases.cc | 1 + 11 files changed, 141 insertions(+) create mode 100644 tests/t00063/.clang-uml create mode 100644 tests/t00063/t00063.cc create mode 100644 tests/t00063/test_case.h diff --git a/docs/configuration_file.md b/docs/configuration_file.md index 646f6eaf..2ba15f19 100644 --- a/docs/configuration_file.md +++ b/docs/configuration_file.md @@ -30,6 +30,7 @@ * `namespaces` - list of namespaces to include * `relationships` - list of relationships to include * `elements` - list of elements, i.e. specific classes, enums, templates to include + * `element_types` - list of element types e.g. `enum`, `class`, `concept` * `access` - list of visibility scopes to include (e.g. `private`) * `subclasses` - include only subclasses of specified classes (and themselves) * `specializations` - include all specializations or instantiations of a given template @@ -40,6 +41,7 @@ * `namespaces` - list of namespaces to exclude * `relationships` - list of relationships to exclude * `elements` - list of elements, i.e. specific classes, enums, templates to exclude + * `element_types` - list of element types e.g. `enum`, `class`, `concept` * `access` - list of visibility scopes to exclude (e.g. `private`) * `subclasses` - exclude subclasses of specified classes (and themselves) * `specializations` - exclude all specializations or instantiations of a given template diff --git a/docs/diagram_filters.md b/docs/diagram_filters.md index 8af8d4f3..860888f4 100644 --- a/docs/diagram_filters.md +++ b/docs/diagram_filters.md @@ -4,6 +4,7 @@ * [`namespaces`](#namespaces) * [`elements`](#elements) +* [`element_types`](#element_types) * [`paths`](#paths) * [`context`](#context) * [`relationships`](#relationships) @@ -53,6 +54,17 @@ from an included namespace: - ns1::ns2::MyClass ``` +## `element_types` + +Allows to include or exclude elements of specific type from the diagram, for instance +to remove all enums from a diagram add the following: + +```yaml + exclude: + element_types: + - enum +``` + ## `paths` This filter allows to include or exclude from the diagram elements declared diff --git a/src/common/model/diagram_filter.cc b/src/common/model/diagram_filter.cc index 7ee323f4..1743bcfc 100644 --- a/src/common/model/diagram_filter.cc +++ b/src/common/model/diagram_filter.cc @@ -220,6 +220,22 @@ tvl::value_t element_filter::match( }); } +element_type_filter::element_type_filter( + filter_t type, std::vector element_types) + : filter_visitor{type} + , element_types_{std::move(element_types)} +{ +} + +tvl::value_t element_type_filter::match( + const diagram &d, const element &e) const +{ + return tvl::any_of(element_types_.begin(), element_types_.end(), + [&e](const auto &element_type) { + return e.type_name() == element_type; + }); +} + subclass_filter::subclass_filter(filter_t type, std::vector roots) : filter_visitor{type} , roots_{std::move(roots)} @@ -531,6 +547,9 @@ void diagram_filter::init_filters(const config::diagram &c) element_filters.emplace_back(std::make_unique( filter_t::kInclusive, c.include().elements)); + element_filters.emplace_back(std::make_unique( + filter_t::kInclusive, c.include().element_types)); + if (c.type() == diagram_t::kClass) { element_filters.emplace_back(std::make_unique( filter_t::kInclusive, c.include().subclasses)); @@ -609,6 +628,9 @@ void diagram_filter::init_filters(const config::diagram &c) add_exclusive_filter(std::make_unique( filter_t::kExclusive, c.exclude().elements)); + add_exclusive_filter(std::make_unique( + filter_t::kExclusive, c.exclude().element_types)); + add_exclusive_filter(std::make_unique( filter_t::kExclusive, c.exclude().relationships)); diff --git a/src/common/model/diagram_filter.h b/src/common/model/diagram_filter.h index e3648ce5..fae444a6 100644 --- a/src/common/model/diagram_filter.h +++ b/src/common/model/diagram_filter.h @@ -127,6 +127,17 @@ private: std::vector elements_; }; +struct element_type_filter : public filter_visitor { + element_type_filter(filter_t type, std::vector element_types); + + ~element_type_filter() override = default; + + tvl::value_t match(const diagram &d, const element &e) const override; + +private: + std::vector element_types_; +}; + struct subclass_filter : public filter_visitor { subclass_filter(filter_t type, std::vector roots); diff --git a/src/config/config.h b/src/config/config.h index f9f7f80e..746332be 100644 --- a/src/config/config.h +++ b/src/config/config.h @@ -61,6 +61,12 @@ struct filter { std::vector elements; + // E.g.: + // - class + // - enum + // - concept + std::vector element_types; + // E.g.: // - inheritance/extension // - dependency diff --git a/src/config/yaml_decoders.cc b/src/config/yaml_decoders.cc index 5c0479ee..fee09d3d 100644 --- a/src/config/yaml_decoders.cc +++ b/src/config/yaml_decoders.cc @@ -301,6 +301,10 @@ template <> struct convert { if (node["elements"]) rhs.elements = node["elements"].as(); + if (node["element_types"]) + rhs.element_types = + node["element_types"].as(); + if (node["access"]) rhs.access = node["access"].as(); diff --git a/src/config/yaml_emitters.cc b/src/config/yaml_emitters.cc index a69589d0..64ed826e 100644 --- a/src/config/yaml_emitters.cc +++ b/src/config/yaml_emitters.cc @@ -60,6 +60,8 @@ YAML::Emitter &operator<<(YAML::Emitter &out, const filter &f) out << YAML::Key << "dependencies" << YAML::Value << f.dependencies; if (!f.elements.empty()) out << YAML::Key << "elements" << YAML::Value << f.elements; + if (!f.element_types.empty()) + out << YAML::Key << "element_types" << YAML::Value << f.element_types; if (!f.paths.empty()) out << YAML::Key << "paths" << YAML::Value << f.paths; if (!f.relationships.empty()) diff --git a/tests/t00063/.clang-uml b/tests/t00063/.clang-uml new file mode 100644 index 00000000..e3704069 --- /dev/null +++ b/tests/t00063/.clang-uml @@ -0,0 +1,15 @@ +compilation_database_dir: .. +output_directory: puml +diagrams: + t00063_class: + type: class + glob: + - ../../tests/t00063/t00063.cc + include: + namespaces: + - clanguml::t00063 + exclude: + element_types: + - enum + using_namespace: + - clanguml::t00063 \ No newline at end of file diff --git a/tests/t00063/t00063.cc b/tests/t00063/t00063.cc new file mode 100644 index 00000000..f4fc47c3 --- /dev/null +++ b/tests/t00063/t00063.cc @@ -0,0 +1,9 @@ +namespace clanguml { +namespace t00063 { +class A { }; + +enum B { b1, b2, b3 }; + +enum class C { c1, c2, c3 }; +} +} \ No newline at end of file diff --git a/tests/t00063/test_case.h b/tests/t00063/test_case.h new file mode 100644 index 00000000..258f89b9 --- /dev/null +++ b/tests/t00063/test_case.h @@ -0,0 +1,57 @@ +/** + * tests/t00063/test_case.h + * + * Copyright (c) 2021-2023 Bartek Kryza + * + * 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("t00063", "[test-case][class]") +{ + auto [config, db] = load_config("t00063"); + + auto diagram = config.diagrams["t00063_class"]; + + REQUIRE(diagram->name == "t00063_class"); + + auto model = generate_class_diagram(*db, diagram); + + REQUIRE(model->name() == "t00063_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, IsClass(_A("A"))); + REQUIRE_THAT(puml, !IsEnum(_A("B"))); + REQUIRE_THAT(puml, !IsEnum(_A("C"))); + + save_puml( + config.output_directory() + "/" + diagram->name + ".puml", puml); + } + + { + auto j = generate_class_json(diagram, *model); + + using namespace json; + + REQUIRE(IsClass(j, "A")); + REQUIRE(!IsEnum(j, "B")); + REQUIRE(!IsEnum(j, "C")); + + save_json(config.output_directory() + "/" + diagram->name + ".json", j); + } +} \ No newline at end of file diff --git a/tests/test_cases.cc b/tests/test_cases.cc index ed337623..68ec4684 100644 --- a/tests/test_cases.cc +++ b/tests/test_cases.cc @@ -299,6 +299,7 @@ using namespace clanguml::test::matchers; #include "t00060/test_case.h" #include "t00061/test_case.h" #include "t00062/test_case.h" +#include "t00063/test_case.h" /// /// Sequence diagram tests