Added relationship direction flag to context diagram filter (#274)
This commit is contained in:
@@ -659,10 +659,10 @@ void context_filter::initialize_effective_context(
|
||||
auto &effective_context = effective_contexts_[idx];
|
||||
|
||||
// First add to effective context all elements matching context_ patterns
|
||||
const auto &context = context_.at(idx);
|
||||
const auto &context_cfg = context_.at(idx);
|
||||
const auto &context_matches =
|
||||
dynamic_cast<const class_diagram::model::diagram &>(d)
|
||||
.find<class_diagram::model::class_>(context.pattern);
|
||||
.find<class_diagram::model::class_>(context_cfg.pattern);
|
||||
|
||||
for (const auto &maybe_match : context_matches) {
|
||||
if (maybe_match)
|
||||
@@ -671,7 +671,7 @@ void context_filter::initialize_effective_context(
|
||||
|
||||
const auto &context_enum_matches =
|
||||
dynamic_cast<const class_diagram::model::diagram &>(d)
|
||||
.find<class_diagram::model::enum_>(context.pattern);
|
||||
.find<class_diagram::model::enum_>(context_cfg.pattern);
|
||||
|
||||
for (const auto &maybe_match : context_enum_matches) {
|
||||
if (maybe_match)
|
||||
@@ -680,7 +680,7 @@ void context_filter::initialize_effective_context(
|
||||
|
||||
const auto &context_concept_matches =
|
||||
dynamic_cast<const class_diagram::model::diagram &>(d)
|
||||
.find<class_diagram::model::concept_>(context.pattern);
|
||||
.find<class_diagram::model::concept_>(context_cfg.pattern);
|
||||
|
||||
for (const auto &maybe_match : context_concept_matches) {
|
||||
if (maybe_match)
|
||||
@@ -689,7 +689,7 @@ void context_filter::initialize_effective_context(
|
||||
|
||||
// Now repeat radius times - extend the effective context with elements
|
||||
// matching in direct relationship to what is in context
|
||||
auto radius_counter = context.radius;
|
||||
auto radius_counter = context_cfg.radius;
|
||||
std::set<eid_t> current_iteration_context;
|
||||
|
||||
while (radius_counter > 0 && effective_context_extended) {
|
||||
@@ -701,18 +701,18 @@ void context_filter::initialize_effective_context(
|
||||
|
||||
// For each class in the model
|
||||
find_elements_in_direct_relationship<class_diagram::model::class_>(
|
||||
d, effective_context, current_iteration_context);
|
||||
d, context_cfg, effective_context, current_iteration_context);
|
||||
|
||||
find_elements_inheritance_relationship(
|
||||
d, effective_context, current_iteration_context);
|
||||
d, context_cfg, effective_context, current_iteration_context);
|
||||
|
||||
// For each concept in the model
|
||||
find_elements_in_direct_relationship<class_diagram::model::concept_>(
|
||||
d, effective_context, current_iteration_context);
|
||||
d, context_cfg, effective_context, current_iteration_context);
|
||||
|
||||
// For each enum in the model
|
||||
find_elements_in_direct_relationship<class_diagram::model::enum_>(
|
||||
d, effective_context, current_iteration_context);
|
||||
d, context_cfg, effective_context, current_iteration_context);
|
||||
|
||||
for (auto id : current_iteration_context) {
|
||||
if (effective_context.count(id) == 0) {
|
||||
@@ -725,6 +725,7 @@ void context_filter::initialize_effective_context(
|
||||
}
|
||||
|
||||
void context_filter::find_elements_inheritance_relationship(const diagram &d,
|
||||
const config::context_config &context_cfg,
|
||||
std::set<eid_t> &effective_context,
|
||||
std::set<eid_t> ¤t_iteration_context) const
|
||||
{
|
||||
@@ -733,20 +734,23 @@ void context_filter::find_elements_inheritance_relationship(const diagram &d,
|
||||
for (const auto &c : cd.classes()) {
|
||||
// Check if any of the elements parents are already in the
|
||||
// effective context...
|
||||
for (const class_diagram::model::class_parent &p : c.get().parents()) {
|
||||
for (const auto &ec : effective_context) {
|
||||
const auto &maybe_parent =
|
||||
cd.find<class_diagram::model::class_>(ec);
|
||||
if (!maybe_parent)
|
||||
continue;
|
||||
|
||||
if (d.should_include(relationship_t::kExtension) &&
|
||||
maybe_parent.value().full_name(false) == p.name())
|
||||
current_iteration_context.emplace(c.get().id());
|
||||
}
|
||||
}
|
||||
if (context_cfg.direction != config::context_direction_t::outward)
|
||||
find_elements_base_classes(
|
||||
d, effective_context, current_iteration_context, cd, c);
|
||||
|
||||
// .. or vice-versa
|
||||
if (context_cfg.direction != config::context_direction_t::inward)
|
||||
find_elements_sub_classes(
|
||||
effective_context, current_iteration_context, cd, c);
|
||||
}
|
||||
}
|
||||
|
||||
void context_filter::find_elements_sub_classes(
|
||||
std::set<eid_t> &effective_context,
|
||||
std::set<eid_t> ¤t_iteration_context,
|
||||
const class_diagram::model::diagram &cd,
|
||||
const std::reference_wrapper<class_diagram::model::class_> &c) const
|
||||
{
|
||||
for (const auto &ec : effective_context) {
|
||||
const auto &maybe_child = cd.find<class_diagram::model::class_>(ec);
|
||||
|
||||
@@ -761,6 +765,24 @@ void context_filter::find_elements_inheritance_relationship(const diagram &d,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
void context_filter::find_elements_base_classes(const diagram &d,
|
||||
std::set<eid_t> &effective_context,
|
||||
std::set<eid_t> ¤t_iteration_context,
|
||||
const class_diagram::model::diagram &cd,
|
||||
const std::reference_wrapper<class_diagram::model::class_> &c) const
|
||||
{
|
||||
for (const class_diagram::model::class_parent &p : c.get().parents()) {
|
||||
for (const auto &ec : effective_context) {
|
||||
const auto &maybe_parent =
|
||||
cd.find<class_diagram::model::class_>(ec);
|
||||
if (!maybe_parent)
|
||||
continue;
|
||||
|
||||
if (d.should_include(relationship_t::kExtension) &&
|
||||
maybe_parent.value().full_name(false) == p.name())
|
||||
current_iteration_context.emplace(c.get().id());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -802,6 +824,16 @@ tvl::value_t context_filter::match(const diagram &d, const element &e) const
|
||||
return false;
|
||||
}
|
||||
|
||||
bool context_filter::is_inward(relationship_t r) const
|
||||
{
|
||||
return r == relationship_t::kAssociation;
|
||||
}
|
||||
|
||||
bool context_filter::is_outward(relationship_t r) const
|
||||
{
|
||||
return r != relationship_t::kAssociation;
|
||||
}
|
||||
|
||||
paths_filter::paths_filter(filter_t type, const std::filesystem::path &root,
|
||||
const std::vector<std::string> &p)
|
||||
: filter_visitor{type}
|
||||
|
||||
@@ -494,8 +494,13 @@ private:
|
||||
|
||||
void initialize_effective_context(const diagram &d, unsigned idx) const;
|
||||
|
||||
bool is_inward(relationship_t r) const;
|
||||
|
||||
bool is_outward(relationship_t r) const;
|
||||
|
||||
template <typename ElementT>
|
||||
void find_elements_in_direct_relationship(const diagram &d,
|
||||
const config::context_config &context_cfg,
|
||||
std::set<eid_t> &effective_context,
|
||||
std::set<eid_t> ¤t_iteration_context) const
|
||||
{
|
||||
@@ -511,6 +516,12 @@ private:
|
||||
// which have a relationship to any of the effective_context
|
||||
// elements
|
||||
for (const relationship &rel : el.get().relationships()) {
|
||||
if (context_cfg.direction ==
|
||||
config::context_direction_t::outward/* &&
|
||||
!is_inward(rel.type())*/) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const auto &element_id : effective_context) {
|
||||
if (d.should_include(rel.type()) &&
|
||||
rel.destination() == element_id)
|
||||
@@ -519,7 +530,9 @@ private:
|
||||
}
|
||||
|
||||
// Now search current effective_context elements and add any
|
||||
// elements of any type in the diagram which to that element
|
||||
// elements of any type in the diagram which have a relationship
|
||||
// to that element
|
||||
|
||||
for (const auto element_id : effective_context) {
|
||||
const auto &maybe_element = cd.get(element_id);
|
||||
|
||||
@@ -528,6 +541,12 @@ private:
|
||||
|
||||
for (const relationship &rel :
|
||||
maybe_element.value().relationships()) {
|
||||
if ((context_cfg.direction ==
|
||||
config::context_direction_t::inward) &&
|
||||
(rel.type() != relationship_t::kAggregation &&
|
||||
rel.type() != relationship_t::kComposition)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (d.should_include(rel.type()) &&
|
||||
rel.destination() == el.get().id())
|
||||
@@ -538,9 +557,21 @@ private:
|
||||
}
|
||||
|
||||
void find_elements_inheritance_relationship(const diagram &d,
|
||||
const config::context_config &context_cfg,
|
||||
std::set<eid_t> &effective_context,
|
||||
std::set<eid_t> ¤t_iteration_context) const;
|
||||
|
||||
void find_elements_base_classes(const diagram &d,
|
||||
std::set<eid_t> &effective_context,
|
||||
std::set<eid_t> ¤t_iteration_context,
|
||||
const class_diagram::model::diagram &cd,
|
||||
const std::reference_wrapper<class_diagram::model::class_> &c) const;
|
||||
|
||||
void find_elements_sub_classes(std::set<eid_t> &effective_context,
|
||||
std::set<eid_t> ¤t_iteration_context,
|
||||
const class_diagram::model::diagram &cd,
|
||||
const std::reference_wrapper<class_diagram::model::class_> &c) const;
|
||||
|
||||
std::vector<config::context_config> context_;
|
||||
|
||||
/*!
|
||||
|
||||
@@ -171,6 +171,20 @@ std::string to_string(member_order_t mo)
|
||||
return "";
|
||||
}
|
||||
}
|
||||
std::string to_string(context_direction_t cd)
|
||||
{
|
||||
switch (cd) {
|
||||
case context_direction_t::inward:
|
||||
return "inward";
|
||||
case context_direction_t::outward:
|
||||
return "outward";
|
||||
case context_direction_t::any:
|
||||
return "any";
|
||||
default:
|
||||
assert(false);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<std::string> plantuml::get_style(
|
||||
const common::model::relationship_t relationship_type) const
|
||||
|
||||
@@ -159,9 +159,14 @@ struct mermaid {
|
||||
void append(const mermaid &r);
|
||||
};
|
||||
|
||||
enum class context_direction_t { inward, outward, any };
|
||||
|
||||
std::string to_string(context_direction_t cd);
|
||||
|
||||
struct context_config {
|
||||
common::string_or_regex pattern;
|
||||
unsigned radius{0};
|
||||
context_direction_t direction{context_direction_t::any};
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -114,10 +114,15 @@ types:
|
||||
- lambda
|
||||
- cuda_kernel
|
||||
- cuda_device
|
||||
direction_t: !variant
|
||||
- inward
|
||||
- outward
|
||||
- any
|
||||
context_filter_match_t:
|
||||
match:
|
||||
radius: int
|
||||
pattern: regex_or_string_t
|
||||
direction: !optional direction_t
|
||||
context_filter_t:
|
||||
- regex_or_string_t
|
||||
- context_filter_match_t
|
||||
|
||||
@@ -35,6 +35,7 @@ using clanguml::config::callee_type;
|
||||
using clanguml::config::class_diagram;
|
||||
using clanguml::config::config;
|
||||
using clanguml::config::context_config;
|
||||
using clanguml::config::context_direction_t;
|
||||
using clanguml::config::diagram_template;
|
||||
using clanguml::config::filter;
|
||||
using clanguml::config::generate_links_config;
|
||||
@@ -259,6 +260,25 @@ template <> struct convert<module_access_t> {
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// config context_direction_t decoder
|
||||
//
|
||||
template <> struct convert<context_direction_t> {
|
||||
static bool decode(const Node &node, context_direction_t &rhs)
|
||||
{
|
||||
if (node.as<std::string>() == "inward")
|
||||
rhs = context_direction_t::inward;
|
||||
else if (node.as<std::string>() == "outward")
|
||||
rhs = context_direction_t::outward;
|
||||
else if (node.as<std::string>() == "any")
|
||||
rhs = context_direction_t::any;
|
||||
else
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// config method_type decoder
|
||||
//
|
||||
@@ -460,8 +480,11 @@ template <> struct convert<context_config> {
|
||||
{
|
||||
using namespace std::string_literals;
|
||||
if (node.IsMap() && has_key(node, "match")) {
|
||||
rhs.radius = node["match"]["radius"].as<unsigned>();
|
||||
rhs.pattern = node["match"]["pattern"].as<string_or_regex>();
|
||||
const auto &match = node["match"];
|
||||
rhs.radius = match["radius"].as<unsigned>();
|
||||
rhs.pattern = match["pattern"].as<string_or_regex>();
|
||||
if (has_key(match, "direction"))
|
||||
rhs.direction = match["direction"].as<context_direction_t>();
|
||||
}
|
||||
else {
|
||||
rhs.radius = 1;
|
||||
|
||||
16
tests/t00076/.clang-uml
Normal file
16
tests/t00076/.clang-uml
Normal file
@@ -0,0 +1,16 @@
|
||||
diagrams:
|
||||
t00076_class:
|
||||
type: class
|
||||
glob:
|
||||
- t00076.cc
|
||||
include:
|
||||
namespaces:
|
||||
- clanguml::t00076
|
||||
context:
|
||||
- match:
|
||||
radius: 1
|
||||
pattern: clanguml::t00076::B
|
||||
direction: inward
|
||||
#relationships:
|
||||
# - any
|
||||
using_namespace: clanguml::t00076
|
||||
36
tests/t00076/t00076.cc
Normal file
36
tests/t00076/t00076.cc
Normal file
@@ -0,0 +1,36 @@
|
||||
namespace clanguml {
|
||||
namespace t00076 {
|
||||
|
||||
enum Color { red, green, blue };
|
||||
|
||||
struct F;
|
||||
struct G { };
|
||||
struct H { };
|
||||
struct J { };
|
||||
|
||||
struct A { };
|
||||
|
||||
struct B : public A {
|
||||
F *f;
|
||||
Color c;
|
||||
G g;
|
||||
/// @uml{composition[0..1:1..*]}
|
||||
J j;
|
||||
|
||||
void a(H *h) { (void)h; }
|
||||
};
|
||||
|
||||
struct C : public B { };
|
||||
|
||||
struct D : public C { };
|
||||
|
||||
struct E {
|
||||
B *b;
|
||||
};
|
||||
struct F { };
|
||||
|
||||
struct I {
|
||||
void i(B *b) { (void)b; }
|
||||
};
|
||||
}
|
||||
}
|
||||
40
tests/t00076/test_case.h
Normal file
40
tests/t00076/test_case.h
Normal file
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* tests/t00076/test_case.h
|
||||
*
|
||||
* Copyright (c) 2021-2024 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("t00076")
|
||||
{
|
||||
using namespace clanguml::test;
|
||||
using namespace std::string_literals;
|
||||
|
||||
auto [config, db, diagram, model] =
|
||||
CHECK_CLASS_MODEL("t00076", "t00076_class");
|
||||
|
||||
CHECK_CLASS_DIAGRAM(*config, diagram, *model, [](const auto &src) {
|
||||
REQUIRE(IsClass(src, "B"));
|
||||
REQUIRE(IsClass(src, "C"));
|
||||
REQUIRE(IsClass(src, "E"));
|
||||
REQUIRE(IsClass(src, "G"));
|
||||
REQUIRE(IsClass(src, "I"));
|
||||
REQUIRE(IsClass(src, "J"));
|
||||
REQUIRE(IsEnum(src, "Color"));
|
||||
REQUIRE(!IsClass(src, "A"));
|
||||
REQUIRE(!IsClass(src, "D"));
|
||||
REQUIRE(!IsClass(src, "F"));
|
||||
REQUIRE(!IsClass(src, "H"));
|
||||
});
|
||||
}
|
||||
@@ -549,6 +549,7 @@ void CHECK_INCLUDE_DIAGRAM(const clanguml::config::config &config,
|
||||
#include "t00074/test_case.h"
|
||||
#include "t00075/test_case.h"
|
||||
#endif
|
||||
#include "t00076/test_case.h"
|
||||
|
||||
///
|
||||
/// Sequence diagram tests
|
||||
@@ -664,7 +665,7 @@ int main(int argc, char *argv[])
|
||||
std::vector<const char *> argvv = {
|
||||
"clang-uml", "--config", "./test_config_data/simple.yml"};
|
||||
|
||||
argvv.push_back("-q");
|
||||
argvv.push_back("-vvv");
|
||||
|
||||
clih.handle_options(argvv.size(), argvv.data());
|
||||
|
||||
|
||||
@@ -222,6 +222,8 @@ test_cases:
|
||||
- name: t00075
|
||||
title: Test case for class diagram styles in config file
|
||||
description:
|
||||
- name: t00076
|
||||
title: Test case for context diagram with inward direction flag
|
||||
Sequence diagrams:
|
||||
- name: t20001
|
||||
title: Basic sequence diagram test case
|
||||
|
||||
Reference in New Issue
Block a user