0.5.4
C++ to UML diagram generator based on Clang
Loading...
Searching...
No Matches
diagram.cc
Go to the documentation of this file.
1/**
2 * @file src/class_diagram/model/diagram.cc
3 *
4 * Copyright (c) 2021-2024 Bartek Kryza <bkryza@gmail.com>
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18
19#include "diagram.h"
20
22#include "util/error.h"
23#include "util/util.h"
24
26
28{
29 return filter().should_include(m);
30}
31
33{
34 return filter().should_include(m);
35}
36
38{
40}
41
43{
45}
46
48{
50}
51
53{
55}
56
58 const std::string &full_name) const
59{
61 find<class_>(full_name);
62
63 if (res.has_value())
64 return res;
65
66 res = find<enum_>(full_name);
67
68 if (res.has_value())
69 return res;
70
71 res = find<concept_>(full_name);
72
73 return res;
74}
75
77 const eid_t id) const
78{
80
81 res = find<class_>(id);
82
83 if (res.has_value())
84 return res;
85
86 res = find<enum_>(id);
87
88 if (res.has_value())
89 return res;
90
91 res = find<concept_>(id);
92
93 return res;
94}
95
96template <>
97bool diagram::add_with_namespace_path<common::model::package>(
98 std::unique_ptr<common::model::package> &&p)
99{
100 LOG_DBG("Adding namespace package: {}, {}", p->name(), p->full_name(true));
101
102 auto ns = p->get_relative_namespace();
103
104 return add_element(ns, std::move(p));
105}
106
107template <>
108bool diagram::add_with_filesystem_path<common::model::package>(
109 const common::model::path & /*parent_path*/,
110 std::unique_ptr<common::model::package> &&p)
111{
112 LOG_DBG("Adding filesystem package: {}, {}", p->name(), p->full_name(true));
113
114 auto ns = p->get_relative_namespace();
115
116 return add_element(ns, std::move(p));
117}
118
119template <>
120bool diagram::add_with_module_path<common::model::package>(
121 const common::model::path & /*parent_path*/,
122 std::unique_ptr<common::model::package> &&p)
123{
124 LOG_DBG("Adding module package: {}, {}", p->name(), p->full_name(true));
125
126 auto ns = p->get_relative_namespace();
127
128 return add_element(ns, std::move(p));
129}
130
133{
134 bool found_new{false};
135 for (const auto &parent : parents) {
136 for (const auto &pp : parent.get().parents()) {
137 auto p = find<class_>(pp.id());
138
139 if (p.has_value()) {
140 auto [it, found] = parents.emplace(std::ref(p.value()));
141 if (found)
142 found_new = true;
143 }
144 else {
145 LOG_WARN("Couldn't find class representing base class: {} [{}]",
146 pp.name(), pp.id());
147 }
148 }
149 }
150
151 if (found_new) {
152 get_parents(parents);
153 }
154}
155
157{
158 const auto has_class = std::any_of(classes().begin(), classes().end(),
159 [id](const auto &c) { return c.get().id() == id; });
160
161 if (has_class)
162 return true;
163
164 const auto has_concept = std::any_of(classes().begin(), classes().end(),
165 [id](const auto &c) { return c.get().id() == id; });
166
167 if (has_concept)
168 return true;
169
170 return std::any_of(enums().begin(), enums().end(),
171 [id](const auto &c) { return c.get().id() == id; });
172}
173
174std::string diagram::to_alias(eid_t id) const
175{
176 LOG_DBG("Looking for alias for {}", id);
177
178 for (const auto &c : classes()) {
179 if (c.get().id() == id) {
180 return c.get().alias();
181 }
182 }
183
184 for (const auto &e : enums()) {
185 if (e.get().id() == id)
186 return e.get().alias();
187 }
188
189 for (const auto &c : concepts()) {
190 if (c.get().id() == id)
191 return c.get().alias();
192 }
193
194 throw error::uml_alias_missing(fmt::format("Missing alias for {}", id));
195}
196
197inja::json diagram::context() const
198{
199 inja::json ctx;
200 ctx["name"] = name();
201 ctx["type"] = "class";
202
203 inja::json::array_t elements{};
204
205 // Add classes
206 for (const auto &c : classes()) {
207 elements.emplace_back(c.get().context());
208 }
209
210 // Add enums
211 for (const auto &e : enums()) {
212 elements.emplace_back(e.get().context());
213 }
214
215 // Add enums
216 for (const auto &c : concepts()) {
217 elements.emplace_back(c.get().context());
218 }
219
220 ctx["elements"] = elements;
221
222 return ctx;
223}
224
226{
227 using common::eid_t;
230
231 for (auto &c : element_view<class_>::view()) {
232 std::set<eid_t> dependency_relationships_to_remove;
233
234 for (auto &r : c.get().relationships()) {
235 if (r.type() != relationship_t::kDependency)
236 dependency_relationships_to_remove.emplace(r.destination());
237 }
238
239 for (const auto &base : c.get().parents()) {
240 dependency_relationships_to_remove.emplace(base.id());
241 }
242
243 util::erase_if(c.get().relationships(),
244 [&dependency_relationships_to_remove, &c](const auto &r) {
245 if (r.type() != relationship_t::kDependency)
246 return false;
247
248 auto has_another_relationship_to_destination =
249 dependency_relationships_to_remove.count(r.destination()) >
250 0;
251 auto is_self_dependency = r.destination() == c.get().id();
252
253 return has_another_relationship_to_destination ||
254 is_self_dependency;
255 });
256 }
257}
258
260{
261 // First find all element ids which should be removed
262 std::set<eid_t> to_remove;
263
264 for (const auto &c : element_view<class_>::view())
265 if (!filter().should_include(c.get()))
266 to_remove.emplace(c.get().id());
267
268 for (const auto &e : element_view<enum_>::view())
269 if (!filter().should_include(e.get()))
270 to_remove.emplace(e.get().id());
271
272 for (const auto &c : element_view<concept_>::view())
273 if (!filter().should_include(c.get()))
274 to_remove.emplace(c.get().id());
275
279
280 nested_trait_ns::remove(to_remove);
281
282 for (auto &c : element_view<class_>::view())
283 c.get().apply_filter(filter(), to_remove);
284
285 for (auto &e : element_view<enum_>::view())
286 e.get().apply_filter(filter(), to_remove);
287
288 for (auto &c : element_view<concept_>::view())
289 c.get().apply_filter(filter(), to_remove);
290}
291
293{
296}
297} // namespace clanguml::class_diagram::model
298
299namespace clanguml::common::model {
300template <>
301bool check_diagram_type<clanguml::class_diagram::model::diagram>(diagram_t t)
302{
303 return t == diagram_t::kClass;
304}
305}