0.6.1
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 auto ns = p->get_relative_namespace();
140
141 LOG_DBG("Adding filesystem package: {}, {}, {}", p->name(),
142 p->full_name(true), ns.to_string());
143
144 return add_element(ns, std::move(p));
145}
146
147template <>
148bool diagram::add_with_module_path<common::model::package>(
149 const common::model::path & /*parent_path*/,
150 std::unique_ptr<common::model::package> &&p)
151{
152 LOG_DBG("Adding module package: {}, {}", p->name(), p->full_name(true));
153
154 auto ns = p->get_relative_namespace();
155
156 return add_element(ns, std::move(p));
157}
158
161{
162 bool found_new{false};
163 for (const auto &parent : parents) {
164 for (const auto &rel : parent.get().relationships()) {
166 continue;
167
168 auto p = find<class_>(rel.destination());
169
170 if (p.has_value()) {
171 auto [it, found] = parents.emplace(std::ref(p.value()));
172 if (found)
173 found_new = true;
174 }
175 else {
176 LOG_WARN("Couldn't find class representing base class: {}",
177 rel.destination().value());
178 }
179 }
180 }
181
182 if (found_new) {
183 get_parents(parents);
184 }
185}
186
187bool diagram::has_element(eid_t id) const
188{
189 const auto has_class = std::any_of(classes().begin(), classes().end(),
190 [id](const auto &c) { return c.get().id() == id; });
191
192 if (has_class)
193 return true;
194
195 const auto has_concept = std::any_of(classes().begin(), classes().end(),
196 [id](const auto &c) { return c.get().id() == id; });
197
198 if (has_concept)
199 return true;
200
201 const auto has_enum = std::any_of(enums().begin(), enums().end(),
202 [id](const auto &c) { return c.get().id() == id; });
203
204 if (has_enum)
205 return true;
206
207 return std::any_of(objc_interfaces().begin(), objc_interfaces().end(),
208 [id](const auto &c) { return c.get().id() == id; });
209}
210
211std::string diagram::to_alias(eid_t id) const
212{
213 LOG_TRACE("Looking for alias for {}", id);
214
215 for (const auto &c : classes()) {
216 if (c.get().id() == id) {
217 return c.get().alias();
218 }
219 }
220
221 for (const auto &e : enums()) {
222 if (e.get().id() == id)
223 return e.get().alias();
224 }
225
226 for (const auto &c : concepts()) {
227 if (c.get().id() == id)
228 return c.get().alias();
229 }
230
231 for (const auto &c : objc_interfaces()) {
232 if (c.get().id() == id)
233 return c.get().alias();
234 }
235
236 throw error::uml_alias_missing(fmt::format("Missing alias for {}", id));
237}
238
240{
241 using common::eid_t;
244
245 for_all_elements([&](auto &&elements_view) mutable {
246 for (const auto &el : elements_view) {
247 std::set<eid_t> dependency_relationships_to_remove;
248
249 for (auto &r : el.get().relationships()) {
250 if (r.type() != relationship_t::kDependency)
251 dependency_relationships_to_remove.emplace(r.destination());
252 }
253
254 util::erase_if(el.get().relationships(),
255 [&dependency_relationships_to_remove, &el](const auto &r) {
256 if (r.type() != relationship_t::kDependency)
257 return false;
258
259 auto has_another_relationship_to_destination =
260 dependency_relationships_to_remove.count(
261 r.destination()) > 0;
262 auto is_self_dependency = r.destination() == el.get().id();
263
264 return has_another_relationship_to_destination ||
265 is_self_dependency;
266 });
267 }
268 });
269}
270
272{
273 // First find all element ids which should be removed
274 std::set<eid_t> to_remove;
275
276 while (true) {
277 for_all_elements([&](auto &&elements_view) mutable {
278 for (const auto &el : elements_view)
279 if (!filter().should_include(el.get()))
280 to_remove.emplace(el.get().id());
281 });
282
283 if (to_remove.empty())
284 break;
285
290
291 nested_trait_ns::remove(to_remove);
292
293 to_remove.clear();
294
295 filter().reset();
296 }
297
298 for_all_elements([&](auto &&elements_view) mutable {
299 for (const auto &el : elements_view)
300 el.get().apply_filter(filter(), to_remove);
301 });
302}
303
305{
309}
310} // namespace clanguml::class_diagram::model
311
312namespace clanguml::common::model {
313template <>
314bool check_diagram_type<clanguml::class_diagram::model::diagram>(diagram_t t)
315{
316 return t == diagram_t::kClass;
317}
318}