0.5.4
C++ to UML diagram generator based on Clang
Loading...
Searching...
No Matches
class_diagram_generator.cc
Go to the documentation of this file.
1/**
2 * @file rc/class_diagram/generators/json/class_diagram_generator.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
20
21#include "util/error.h"
22
24using nlohmann::json;
25
26void set_module(nlohmann::json &j, const common::model::element &e)
27{
28 if (const auto &maybe_module = e.module(); maybe_module) {
29 j["module"]["name"] = *maybe_module;
30 j["module"]["is_private"] = e.module_private();
31 }
32}
33
34void to_json(nlohmann::json &j, const class_element &c)
35{
36 j["name"] = c.name();
39 j["access"] = to_string(c.access());
40 if (!c.file().empty())
41 j["source_location"] =
42 dynamic_cast<const common::model::source_location &>(c);
43 if (const auto &comment = c.comment(); comment)
44 j["comment"] = comment.value();
45}
46
47void to_json(nlohmann::json &j, const class_member &c)
48{
49 j = dynamic_cast<const class_element &>(c);
50 j["is_static"] = c.is_static();
51}
52
53void to_json(nlohmann::json &j, const method_parameter &c)
54{
55 j["name"] = c.name();
56 j["type"] = c.type();
57 if (!c.default_value().empty())
58 j["default_value"] = c.default_value();
59}
60
61void to_json(nlohmann::json &j, const class_method &c)
62{
63 j = dynamic_cast<const class_element &>(c);
64
65 j["is_pure_virtual"] = c.is_pure_virtual();
66 j["is_virtual"] = c.is_virtual();
67 j["is_const"] = c.is_const();
68 j["is_defaulted"] = c.is_defaulted();
69 j["is_deleted"] = c.is_deleted();
70 j["is_static"] = c.is_static();
71 j["is_noexcept"] = c.is_noexcept();
72 j["is_constexpr"] = c.is_constexpr();
73 j["is_consteval"] = c.is_consteval();
74 j["is_coroutine"] = c.is_coroutine();
75 j["is_constructor"] = c.is_constructor();
76 j["is_move_assignment"] = c.is_move_assignment();
77 j["is_copy_assignment"] = c.is_copy_assignment();
78 j["is_operator"] = c.is_operator();
79 j["template_parameters"] = c.template_params();
80 j["display_name"] = c.display_name();
81
82 j["parameters"] = c.parameters();
83}
84
85void to_json(nlohmann::json &j, const class_parent &c)
86{
87 j["is_virtual"] = c.is_virtual();
88 j["id"] = std::to_string(c.id().value());
90 j["access"] = to_string(c.access());
91 j["name"] = c.name();
92}
93
94void to_json(nlohmann::json &j, const class_ &c)
95{
96 j = dynamic_cast<const common::model::element &>(c);
97 j["is_struct"] = c.is_struct();
98 j["is_abstract"] = c.is_abstract();
99 j["is_union"] = c.is_union();
100 j["is_nested"] = c.is_nested();
101 j["is_template"] = c.is_template();
102
103 j["members"] = c.members();
104 j["methods"] = c.methods();
105 j["bases"] = c.parents();
106
107 set_module(j, c);
108
109 j["template_parameters"] = c.template_params();
110}
111
112void to_json(nlohmann::json &j, const enum_ &c)
113{
114 j = dynamic_cast<const common::model::element &>(c);
115 j["is_nested"] = c.is_nested();
116 j["constants"] = c.constants();
117
118 set_module(j, c);
119}
120
121void to_json(nlohmann::json &j, const concept_ &c)
122{
123 j = dynamic_cast<const common::model::element &>(c);
124 j["parameters"] = c.requires_parameters();
125 j["statements"] = c.requires_statements();
126
127 set_module(j, c);
128}
129
130} // namespace clanguml::class_diagram::model
131
133
136{
137}
138
139void generator::generate_diagram(nlohmann::json &parent) const
140{
141 if (config().using_namespace)
142 parent["using_namespace"] = config().using_namespace().to_string();
143
144 if (config().using_module)
145 parent["using_module"] = config().using_module();
146
147 if (config().generate_packages.has_value)
148 parent["package_type"] = to_string(config().package_type());
149 parent["elements"] = std::vector<nlohmann::json>{};
150 parent["relationships"] = std::vector<nlohmann::json>{};
151
153
155}
156
157void generator::generate_top_level_elements(nlohmann::json &parent) const
158{
159 for (const auto &p : model()) {
160 if (auto *pkg = dynamic_cast<package *>(p.get()); pkg) {
161 if (!pkg->is_empty())
162 generate(*pkg, parent);
163 }
164 else if (auto *cls = dynamic_cast<class_ *>(p.get()); cls) {
165 generate(*cls, parent);
166 }
167 else if (auto *enm = dynamic_cast<enum_ *>(p.get()); enm) {
168 generate(*enm, parent);
169 }
170 else if (auto *cpt = dynamic_cast<concept_ *>(p.get()); cpt) {
171 generate(*cpt, parent);
172 }
173 }
174}
175
176void generator::generate(const package &p, nlohmann::json &parent) const
177{
178 const auto &uns = config().using_namespace();
179
180 nlohmann::json package_object;
181
182 if (config().generate_packages()) {
183 // Don't generate packages from namespaces filtered out by
184 // using_namespace
185 if (!uns.starts_with({p.full_name(false)})) {
186 LOG_DBG("Generating package {}", p.name());
187
188 package_object["type"] = to_string(config().package_type());
189 package_object["name"] = p.name();
190 package_object["display_name"] = p.name();
191 }
192 }
193
194 for (const auto &subpackage : p) {
195 if (dynamic_cast<package *>(subpackage.get()) != nullptr) {
196 const auto &sp = dynamic_cast<package &>(*subpackage);
197 if (!sp.is_empty()) {
198 if (config().generate_packages())
199 generate(sp, package_object);
200 else
201 generate(sp, parent);
202 }
203 }
204 else if (auto *cls = dynamic_cast<class_ *>(subpackage.get()); cls) {
205 if (config().generate_packages())
206 generate(*cls, package_object);
207 else
208 generate(*cls, parent);
209 }
210 else if (auto *enm = dynamic_cast<enum_ *>(subpackage.get()); enm) {
211 if (config().generate_packages())
212 generate(*enm, package_object);
213 else
214 generate(*enm, parent);
215 }
216 else if (auto *cpt = dynamic_cast<concept_ *>(subpackage.get()); cpt) {
217 if (config().generate_packages())
218 generate(*cpt, package_object);
219 else
220 generate(*cpt, parent);
221 }
222 }
223
224 if (config().generate_packages() && !package_object.empty()) {
225 parent["elements"].push_back(std::move(package_object));
226 }
227}
228
229void generator::generate(const class_ &c, nlohmann::json &parent) const
230{
231 nlohmann::json object = c;
232
233 // Perform config dependent postprocessing on generated class
234 if (!config().generate_fully_qualified_name())
235 object["display_name"] =
237
238 object["display_name"] =
239 config().simplify_template_type(object["display_name"]);
240
241 for (auto &tp : object["template_parameters"]) {
242 if (tp.contains("type") && tp.at("type").is_string()) {
243 tp["type"] = config().using_namespace().relative(tp.at("type"));
244 }
245 }
246 for (auto &tp : object["members"]) {
247 if (tp.contains("type") && tp.at("type").is_string()) {
248 tp["type"] = config().using_namespace().relative(tp.at("type"));
249 }
250 }
251
252 std::string object_str = object.dump(2);
253
254 parent["elements"].push_back(std::move(object));
255}
256
257void generator::generate(const enum_ &e, nlohmann::json &parent) const
258{
259 nlohmann::json object = e;
260
261 if (!config().generate_fully_qualified_name())
262 object["display_name"] =
264
265 parent["elements"].push_back(std::move(object));
266}
267
268void generator::generate(const concept_ &c, nlohmann::json &parent) const
269{
270 nlohmann::json object = c;
271
272 if (!config().generate_fully_qualified_name())
273 object["display_name"] =
275
276 parent["elements"].push_back(std::move(object));
277}
278
279void generator::generate_relationships(nlohmann::json &parent) const
280{
281 for (const auto &p : model()) {
282 if (auto *pkg = dynamic_cast<package *>(p.get()); pkg) {
283 generate_relationships(*pkg, parent);
284 }
285 else if (auto *cls = dynamic_cast<class_ *>(p.get()); cls) {
286 generate_relationships(*cls, parent);
287 }
288 else if (auto *enm = dynamic_cast<enum_ *>(p.get()); enm) {
289 generate_relationships(*enm, parent);
290 }
291 else if (auto *cpt = dynamic_cast<concept_ *>(p.get()); cpt) {
292 generate_relationships(*cpt, parent);
293 }
294 }
295}
296
298 const class_ &c, nlohmann::json &parent) const
299{
300 for (const auto &r : c.relationships()) {
301 auto target_element = model().get(r.destination());
302 if (!target_element.has_value()) {
303 LOG_DBG("Skipping {} relation from {} to {} due "
304 "to unresolved destination id",
305 to_string(r.type()), c.full_name(), r.destination());
306 continue;
307 }
308
309 nlohmann::json rel = r;
310 rel["source"] = std::to_string(c.id().value());
311 parent["relationships"].push_back(rel);
312 }
313
314 if (model().should_include(relationship_t::kExtension)) {
315 for (const auto &b : c.parents()) {
317 relationship_t::kExtension, b.id(), b.access());
318 nlohmann::json rel = r;
319 rel["source"] = std::to_string(c.id().value());
320 parent["relationships"].push_back(rel);
321 }
322 }
323}
324
326 const enum_ &c, nlohmann::json &parent) const
327{
328 for (const auto &r : c.relationships()) {
329 auto target_element = model().get(r.destination());
330 if (!target_element.has_value()) {
331 LOG_DBG("Skipping {} relation from {} to {} due "
332 "to unresolved destination id",
333 to_string(r.type()), c.full_name(), r.destination());
334 continue;
335 }
336
337 nlohmann::json rel = r;
338 rel["source"] = std::to_string(c.id().value());
339 parent["relationships"].push_back(rel);
340 }
341}
342
344 const concept_ &c, nlohmann::json &parent) const
345{
346 for (const auto &r : c.relationships()) {
347 auto target_element = model().get(r.destination());
348 if (!target_element.has_value()) {
349 LOG_DBG("Skipping {} relation from {} to {} due "
350 "to unresolved destination id",
351 to_string(r.type()), c.full_name(), r.destination());
352 continue;
353 }
354
355 nlohmann::json rel = r;
356 rel["source"] = std::to_string(c.id().value());
357 parent["relationships"].push_back(rel);
358 }
359}
360
362 const package &p, nlohmann::json &parent) const
363{
364 for (const auto &subpackage : p) {
365 if (dynamic_cast<package *>(subpackage.get()) != nullptr) {
366 const auto &sp = dynamic_cast<package &>(*subpackage);
367 if (!sp.is_empty())
368 generate_relationships(sp, parent);
369 }
370 else if (dynamic_cast<class_ *>(subpackage.get()) != nullptr) {
371 if (model().should_include(*subpackage)) {
373 dynamic_cast<class_ &>(*subpackage), parent);
374 }
375 }
376 else if (dynamic_cast<enum_ *>(subpackage.get()) != nullptr) {
377 if (model().should_include(*subpackage)) {
379 dynamic_cast<enum_ &>(*subpackage), parent);
380 }
381 }
382 else if (dynamic_cast<concept_ *>(subpackage.get()) != nullptr) {
383 if (model().should_include(*subpackage)) {
385 dynamic_cast<concept_ &>(*subpackage), parent);
386 }
387 }
388 }
389}
390
391} // namespace clanguml::class_diagram::generators::json