0.6.0
C++ to UML diagram generator based on Clang
Loading...
Searching...
No Matches
text_diagram_strategy.h
Go to the documentation of this file.
1/**
2 * @file src/common/generators/text_based_generator.h
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#pragma once
19
24#include "config/config.h"
25#include "util/error.h"
26#include "util/util.h"
27
28#include <optional>
29#include <ostream>
30#include <regex>
31#include <string>
32
34
37
39
40/**
41 * @brief Common methods for PlantUML and MermaidJS class diagrams
42 */
43template <typename G> class text_diagram_strategy {
44public:
46 std::map<std::string, std::vector<model::class_method>>;
47
48 text_diagram_strategy(G &generator, bool generate_packages)
49 : generator_{generator}
50 , together_group_stack_{generate_packages}
51 {
52 }
53
54 void generate(const package &p, std::ostream &ostr) const
55 {
56 generator_.start_package(p, ostr);
57
58 for (const auto &subpackage : p) {
59 if (dynamic_cast<package *>(subpackage.get()) != nullptr) {
60 // TODO: add option - generate_empty_packages
61 const auto &sp = dynamic_cast<package &>(*subpackage);
62 if (!sp.is_empty()) {
64
65 generate(sp, ostr);
66
68 }
69 }
70 else {
71 generator_.model().dynamic_apply(
72 subpackage.get(), [&](auto *el) {
73 auto together_group =
74 generator_.config().get_together_group(
75 el->full_name(false));
76 if (together_group) {
77 together_group_stack_.group_together(
78 together_group.value(), el);
79 }
80 else {
81 generator_.generate_alias(*el, ostr);
82 generator_.generate(*el, ostr);
83 }
84 });
85 }
86
87 if (!together_group_stack_.is_flat()) {
88 generate_groups(ostr);
89 }
90 }
91
92 generator_.end_package(p, ostr);
93 }
94
95 void generate_top_level_elements(std::ostream &ostr) const
96 {
97 for (const auto &p : generator_.model()) {
98 if (auto *pkg = dynamic_cast<package *>(p.get()); pkg) {
99 if (!pkg->is_empty())
100 generate(*pkg, ostr);
101 }
102 else {
103 generator_.model().dynamic_apply(p.get(), [&](auto *el) {
104 auto together_group =
105 generator_.config().get_together_group(
106 el->full_name(false));
107 if (together_group) {
108 together_group_stack_.group_together(
109 together_group.value(), el);
110 }
111 else {
112 generator_.generate_alias(*el, ostr);
113 generator_.generate(*el, ostr);
114 }
115 });
116 }
117 }
118 }
119
120 void generate_relationships(const package &p, std::ostream &ostr) const
121 {
122 for (const auto &subpackage : p) {
123 if (dynamic_cast<package *>(subpackage.get()) != nullptr) {
124 // TODO: add option - generate_empty_packages, currently
125 // packages which do not contain anything but other
126 // packages are skipped
127 const auto &sp = dynamic_cast<package &>(*subpackage);
128 if (!sp.is_empty())
129 generate_relationships(sp, ostr);
130 }
131 else {
132 generator_.model().dynamic_apply(
133 subpackage.get(), [&](auto *el) {
134 if (generator_.model().should_include(*el)) {
135 generator_.generate_relationships(*el, ostr);
136 }
137 });
138 }
139 }
140 }
141
142 void generate_relationships(std::ostream &ostr) const
143 {
144 for (const auto &p : generator_.model()) {
145 if (auto *pkg = dynamic_cast<package *>(p.get()); pkg) {
146 generate_relationships(*pkg, ostr);
147 }
148 else {
149 generator_.model().dynamic_apply(p.get(), [&](auto *el) {
150 generator_.generate_relationships(*el, ostr);
151 });
152 }
153 }
154 }
155
156 void generate_groups(std::ostream &ostr) const
157 {
158 for (const auto &[group_name, group_elements] :
159 together_group_stack_.get_current_groups()) {
160
161 generator_.start_together_group(group_name, ostr);
162
163 for (auto *e : group_elements) {
164 generator_.model().dynamic_apply(e, [&](auto *el) {
165 generator_.generate_alias(*el, ostr);
166 generator_.generate(*el, ostr);
167 });
168 }
169
170 generator_.end_together_group(group_name, ostr);
171 }
172 }
173
174 template <typename T>
175 void sort_class_elements(std::vector<T> &elements) const
176 {
177 if (generator_.config().member_order() ==
179 std::sort(elements.begin(), elements.end(),
180 [](const auto &m1, const auto &m2) {
181 return m1.name() < m2.name();
182 });
183 }
184 }
185
187 const method_groups_t &methods, std::ostream &ostr) const
188 {
189 bool is_first_non_empty_group{true};
190
191 for (const auto &group : method_groups_) {
192 const auto &group_methods = methods.at(group);
193 if (!group_methods.empty()) {
194 if (!is_first_non_empty_group)
195 generator_.start_method_group(ostr);
196 is_first_non_empty_group = false;
197 generator_.generate_methods(group_methods, ostr);
198 }
199 }
200 }
201
202 /**
203 * @brief Group class methods based on method type.
204 *
205 * @param methods List of class methods.
206 *
207 * @return Map of method groups.
208 */
210 const std::vector<model::class_method> &methods) const
211 {
212 std::map<std::string, std::vector<model::class_method>> result;
213
214 for (const auto &g : method_groups_) {
215 result[g] = {};
216 }
217
218 for (const auto &m : methods) {
219 if (m.is_constructor() || m.is_destructor()) {
220 result["constructors"].push_back(m);
221 }
222 else if (m.is_copy_assignment() || m.is_move_assignment()) {
223 result["assignment"].push_back(m);
224 }
225 else if (m.is_operator()) {
226 result["operators"].push_back(m);
227 }
228 else {
229 result["other"].push_back(m);
230 }
231 }
232
233 return result;
234 }
235
236private:
237 const std::vector<std::string> method_groups_{
238 "constructors", "assignment", "operators", "other"};
239
243};
244
245} // namespace clanguml::class_diagram::generators