0.6.0
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-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
21#include "util/error.h"
22
25using nlohmann::json;
26
27void set_module(nlohmann::json &j, const common::model::element &e)
28{
29 if (const auto &maybe_module = e.module(); maybe_module) {
30 j["module"]["name"] = *maybe_module;
31 j["module"]["is_private"] = e.module_private();
32 }
33}
34
35void to_json(nlohmann::json &j, const class_element &c)
36{
37 j["name"] = display_name_adapter(c).name();
38 j["type"] = display_name_adapter(c).type();
40 j["access"] = to_string(c.access());
41 if (!c.file().empty())
42 j["source_location"] =
43 dynamic_cast<const common::model::source_location &>(c);
44 if (const auto &comment = c.comment(); comment)
45 j["comment"] = comment.value();
46}
47
48void to_json(nlohmann::json &j, const class_member &c)
49{
50 j = dynamic_cast<const class_element &>(c);
51 j["is_static"] = c.is_static();
52}
53
54void to_json(nlohmann::json &j, const method_parameter &c)
55{
56 j["name"] = c.name();
57 j["type"] = c.type();
58 if (!c.default_value().empty())
59 j["default_value"] = c.default_value();
60}
61
62void to_json(nlohmann::json &j, const class_method &c)
63{
64 j = dynamic_cast<const class_element &>(c);
65
66 j["is_pure_virtual"] = c.is_pure_virtual();
67 j["is_virtual"] = c.is_virtual();
68 j["is_const"] = c.is_const();
69 j["is_defaulted"] = c.is_defaulted();
70 j["is_deleted"] = c.is_deleted();
71 j["is_static"] = c.is_static();
72 j["is_noexcept"] = c.is_noexcept();
73 j["is_constexpr"] = c.is_constexpr();
74 j["is_consteval"] = c.is_consteval();
75 j["is_coroutine"] = c.is_coroutine();
76 j["is_constructor"] = c.is_constructor();
77 j["is_move_assignment"] = c.is_move_assignment();
78 j["is_copy_assignment"] = c.is_copy_assignment();
79 j["is_operator"] = c.is_operator();
80 j["template_parameters"] = c.template_params();
81 j["display_name"] = c.display_name();
82
83 j["parameters"] = c.parameters();
84}
85
86void to_json(nlohmann::json &j, const objc_method &c)
87{
88 j = dynamic_cast<const class_element &>(c);
89
90 j["is_optional"] = c.is_optional();
91 j["display_name"] = c.display_name();
92
93 j["parameters"] = c.parameters();
94}
95
96void to_json(nlohmann::json &j, const class_ &c)
97{
98 j = dynamic_cast<const common::model::element &>(c);
99 j["is_struct"] = c.is_struct();
100 j["is_abstract"] = c.is_abstract();
101 j["is_union"] = c.is_union();
102 j["is_nested"] = c.is_nested();
103 j["is_template"] = c.is_template();
104
105 j["members"] = c.members();
106 j["methods"] = c.methods();
107 auto bases = nlohmann::json::array();
108
109 for (const auto &rel : c.relationships()) {
111 continue;
112
113 auto base = nlohmann::json::object();
114 base["is_virtual"] = rel.is_virtual();
115 base["id"] = std::to_string(rel.destination().value());
116 if (rel.access() != common::model::access_t::kNone)
117 base["access"] = to_string(rel.access());
118 bases.push_back(std::move(base));
119 }
120 j["bases"] = std::move(bases);
121
122 set_module(j, c);
123
124 j["template_parameters"] = c.template_params();
125}
126
127void to_json(nlohmann::json &j, const objc_interface &c)
128{
129 j = dynamic_cast<const common::model::element &>(c);
130 j["is_protocol"] = c.is_protocol();
131 j["is_category"] = c.is_category();
132
133 j["members"] = c.members();
134 j["methods"] = c.methods();
135 auto bases = nlohmann::json::array();
136 auto protocols = nlohmann::json::array();
137
138 for (const auto &rel : c.relationships()) {
140 auto base = nlohmann::json::object();
141 base["id"] = std::to_string(rel.destination().value());
142 if (rel.access() != common::model::access_t::kNone)
143 base["access"] = to_string(rel.access());
144 bases.push_back(std::move(base));
145 }
146 else if (rel.type() == common::model::relationship_t::kInstantiation) {
147 auto protocol = nlohmann::json::object();
148 protocol["id"] = std::to_string(rel.destination().value());
149 protocols.push_back(std::move(protocol));
150 }
151 }
152 j["bases"] = std::move(bases);
153 j["protocols"] = std::move(protocols);
154}
155
156void to_json(nlohmann::json &j, const enum_ &c)
157{
158 j = dynamic_cast<const common::model::element &>(c);
159 j["is_nested"] = c.is_nested();
160 j["constants"] = c.constants();
161
162 set_module(j, c);
163}
164
165void to_json(nlohmann::json &j, const concept_ &c)
166{
167 j = dynamic_cast<const common::model::element &>(c);
168 j["parameters"] = c.requires_parameters();
169 j["statements"] = c.requires_statements();
170
171 set_module(j, c);
172}
173
174} // namespace clanguml::class_diagram::model
175
178
181{
182}
183
184void generator::generate_diagram(nlohmann::json &parent) const
185{
186 if (config().using_namespace)
187 parent["using_namespace"] = config().using_namespace().to_string();
188
189 if (config().using_module)
190 parent["using_module"] = config().using_module();
191
192 if (config().generate_packages.has_value)
193 parent["package_type"] = to_string(config().package_type());
194 parent["elements"] = std::vector<nlohmann::json>{};
195 parent["relationships"] = std::vector<nlohmann::json>{};
196
198
200}
201
202void generator::generate_top_level_elements(nlohmann::json &parent) const
203{
204 for (const auto &p : model()) {
205 if (auto *pkg = dynamic_cast<package *>(p.get()); pkg) {
206 if (!pkg->is_empty())
207 generate(*pkg, parent);
208 }
209 else {
210 model().dynamic_apply(
211 p.get(), [&](auto *el) { generate(*el, parent); });
212 }
213 }
214}
215
216void generator::generate(const package &p, nlohmann::json &parent) const
217{
218 const auto &uns = config().using_namespace();
219
220 nlohmann::json package_object;
221
222 if (config().generate_packages()) {
223 // Don't generate packages from namespaces filtered out by
224 // using_namespace
225 if (!uns.starts_with({p.full_name(false)})) {
226 LOG_DBG("Generating package {}", p.name());
227
228 package_object["type"] = to_string(config().package_type());
229 package_object["name"] = p.name();
230 package_object["display_name"] =
232 }
233 }
234
235 for (const auto &subpackage : p) {
236 if (dynamic_cast<package *>(subpackage.get()) != nullptr) {
237 const auto &sp = dynamic_cast<package &>(*subpackage);
238 if (!sp.is_empty()) {
239 if (config().generate_packages())
240 generate(sp, package_object);
241 else
242 generate(sp, parent);
243 }
244 }
245 else {
246 model().dynamic_apply(subpackage.get(), [&](auto *el) {
247 if (config().generate_packages())
248 generate(*el, package_object);
249 else
250 generate(*el, parent);
251 });
252 }
253 }
254
255 if (config().generate_packages() && !package_object.empty()) {
256 parent["elements"].push_back(std::move(package_object));
257 }
258}
259
260void generator::generate(const class_ &c, nlohmann::json &parent) const
261{
262 nlohmann::json object = c;
263
264 // Perform config dependent postprocessing on generated class
265 if (!config().generate_fully_qualified_name())
266 object["display_name"] =
267 display_name_adapter(c).with_packages().full_name_no_ns();
268
269 object["display_name"] =
270 config().simplify_template_type(object["display_name"]);
271
272 for (auto &tp : object["template_parameters"]) {
273 if (tp.contains("type") && tp.at("type").is_string()) {
274 tp["type"] = config().using_namespace().relative(tp.at("type"));
275 }
276 }
277 for (auto &tp : object["members"]) {
278 if (tp.contains("type") && tp.at("type").is_string()) {
279 tp["type"] = config().using_namespace().relative(tp.at("type"));
280 }
281 }
282
283 parent["elements"].push_back(std::move(object));
284}
285
286void generator::generate(const enum_ &e, nlohmann::json &parent) const
287{
288 nlohmann::json object = e;
289
290 if (!config().generate_fully_qualified_name())
291 object["display_name"] = display_name_adapter(e).full_name_no_ns();
292
293 parent["elements"].push_back(std::move(object));
294}
295
296void generator::generate(const concept_ &c, nlohmann::json &parent) const
297{
298 nlohmann::json object = c;
299
300 if (!config().generate_fully_qualified_name())
301 object["display_name"] = display_name_adapter(c).full_name_no_ns();
302
303 parent["elements"].push_back(std::move(object));
304}
305
306void generator::generate(const objc_interface &c, nlohmann::json &parent) const
307{
308 nlohmann::json object = c;
309
310 // Perform config dependent postprocessing on generated class
311 if (!config().generate_fully_qualified_name())
312 object["display_name"] = display_name_adapter(c).full_name_no_ns();
313
314 object["display_name"] =
315 config().simplify_template_type(object["display_name"]);
316
317 parent["elements"].push_back(std::move(object));
318}
319
320void generator::generate_relationships(nlohmann::json &parent) const
321{
322 for (const auto &p : model()) {
323 if (auto *pkg = dynamic_cast<package *>(p.get()); pkg) {
324 generate_relationships(*pkg, parent);
325 }
326 else {
327 model().dynamic_apply(p.get(),
328 [&](auto *el) { generate_relationships(*el, parent); });
329 }
330 }
331}
332
333template <>
334void generator::generate_relationships<package>(
335 const package &p, nlohmann::json &parent) const
336{
337 for (const auto &subpackage : p) {
338 if (dynamic_cast<package *>(subpackage.get()) != nullptr) {
339 const auto &sp = dynamic_cast<package &>(*subpackage);
340 if (!sp.is_empty())
341 generate_relationships(sp, parent);
342 }
343 else {
344 model().dynamic_apply(subpackage.get(), [&](auto *el) {
345 if (model().should_include(*el)) {
346 generate_relationships(*el, parent);
347 }
348 });
349 }
350 }
351}
352
353} // namespace clanguml::class_diagram::generators::json