0.6.0
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-2025 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{
39 return filter().should_include(m);
40}
41
43{
44 return filter().should_include(m);
45}
46
48{
50}
51
53{
55}
56
58{
60}
61
63{
65}
66
68{
70}
71
73 const std::string &full_name) const
74{
76 find<class_>(full_name);
77
78 if (res.has_value())
79 return res;
80
81 res = find<enum_>(full_name);
82
83 if (res.has_value())
84 return res;
85
86 res = find<concept_>(full_name);
87
88 if (res.has_value())
89 return res;
90
91 res = find<objc_interface>(full_name);
92
93 return res;
94}
95
97 const eid_t id) const
98{
100
101 res = find<class_>(id);
102
103 if (res.has_value())
104 return res;
105
106 res = find<enum_>(id);
107
108 if (res.has_value())
109 return res;
110
111 res = find<concept_>(id);
112
113 if (res.has_value())
114 return res;
115
116 res = find<objc_interface>(id);
117
118 return res;
119}
120
121template <>
122bool diagram::add_with_namespace_path<common::model::package>(
123 std::unique_ptr<common::model::package> &&p)
124{
125 LOG_DBG("Adding namespace package: {}, {}", p->name(), p->full_name(true));
126
127 auto ns = p->get_relative_namespace();
128 if (common::model::needs_root_prefix(*p) && p->get_namespace().is_empty())
129 p->is_root(true);
130
131 return add_element(ns, std::move(p));
132}
133
134template <>
135bool diagram::add_with_filesystem_path<common::model::package>(
136 const common::model::path & /*parent_path*/,
137 std::unique_ptr<common::model::package> &&p)
138{
139 LOG_DBG("Adding filesystem package: {}, {}", p->name(), p->full_name(true));
140
141 auto ns = p->get_relative_namespace();
142
143 return add_element(ns, std::move(p));
144}
145
146template <>
147bool diagram::add_with_module_path<common::model::package>(
148 const common::model::path & /*parent_path*/,
149 std::unique_ptr<common::model::package> &&p)
150{
151 LOG_DBG("Adding module package: {}, {}", p->name(), p->full_name(true));
152
153 auto ns = p->get_relative_namespace();
154
155 return add_element(ns, std::move(p));
156}
157
160{
161 bool found_new{false};
162 for (const auto &parent : parents) {
163 for (const auto &rel : parent.get().relationships()) {
165 continue;
166
167 auto p = find<class_>(rel.destination());
168
169 if (p.has_value()) {
170 auto [it, found] = parents.emplace(std::ref(p.value()));
171 if (found)
172 found_new = true;
173 }
174 else {
175 LOG_WARN("Couldn't find class representing base class: {}",
176 rel.destination().value());
177 }
178 }
179 }
180
181 if (found_new) {
182 get_parents(parents);
183 }
184}
185
186bool diagram::has_element(eid_t id) const
187{
188 const auto has_class = std::any_of(classes().begin(), classes().end(),
189 [id](const auto &c) { return c.get().id() == id; });
190
191 if (has_class)
192 return true;
193
194 const auto has_concept = std::any_of(classes().begin(), classes().end(),
195 [id](const auto &c) { return c.get().id() == id; });
196
197 if (has_concept)
198 return true;
199
200 const auto has_enum = std::any_of(enums().begin(), enums().end(),
201 [id](const auto &c) { return c.get().id() == id; });
202
203 if (has_enum)
204 return true;
205
206 return std::any_of(objc_interfaces().begin(), objc_interfaces().end(),
207 [id](const auto &c) { return c.get().id() == id; });
208}
209
210std::string diagram::to_alias(eid_t id) const
211{
212 LOG_DBG("Looking for alias for {}", id);
213
214 for (const auto &c : classes()) {
215 if (c.get().id() == id) {
216 return c.get().alias();
217 }
218 }
219
220 for (const auto &e : enums()) {
221 if (e.get().id() == id)
222 return e.get().alias();
223 }
224
225 for (const auto &c : concepts()) {
226 if (c.get().id() == id)
227 return c.get().alias();
228 }
229
230 for (const auto &c : objc_interfaces()) {
231 if (c.get().id() == id)
232 return c.get().alias();
233 }
234
235 throw error::uml_alias_missing(fmt::format("Missing alias for {}", id));
236}
237
239{
240 using common::eid_t;
243
244 for_all_elements([&](auto &&elements_view) mutable {
245 for (const auto &el : elements_view) {
246 std::set<eid_t> dependency_relationships_to_remove;
247
248 for (auto &r : el.get().relationships()) {
249 if (r.type() != relationship_t::kDependency)
250 dependency_relationships_to_remove.emplace(r.destination());
251 }
252
253 util::erase_if(el.get().relationships(),
254 [&dependency_relationships_to_remove, &el](const auto &r) {
255 if (r.type() != relationship_t::kDependency)
256 return false;
257
258 auto has_another_relationship_to_destination =
259 dependency_relationships_to_remove.count(
260 r.destination()) > 0;
261 auto is_self_dependency = r.destination() == el.get().id();
262
263 return has_another_relationship_to_destination ||
264 is_self_dependency;
265 });
266 }
267 });
268}
269
271{
272 // First find all element ids which should be removed
273 std::set<eid_t> to_remove;
274
275 for_all_elements([&](auto &&elements_view) mutable {
276 for (const auto &el : elements_view)
277 if (!filter().should_include(el.get()))
278 to_remove.emplace(el.get().id());
279 });
280
285
286 nested_trait_ns::remove(to_remove);
287
288 for_all_elements([&](auto &&elements_view) mutable {
289 for (const auto &el : elements_view)
290 el.get().apply_filter(filter(), to_remove);
291 });
292}
293
295{
299}
300} // namespace clanguml::class_diagram::model
301
302namespace clanguml::common::model {
303template <>
304bool check_diagram_type<clanguml::class_diagram::model::diagram>(diagram_t t)
305{
306 return t == diagram_t::kClass;
307}
308}