0.6.0
C++ to UML diagram generator based on Clang
Loading...
Searching...
No Matches
diagram.h
Go to the documentation of this file.
1/**
2 * @file src/sequence_diagram/model/diagram.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
20#include "activity.h"
22#include "common/types.h"
23#include "config/config.h"
24#include "participant.h"
25
26#include <map>
27#include <string>
28
30
31using message_chain_t = std::vector<sequence_diagram::model::message>;
32
33/**
34 * This structure models reverse call graph for building sequence diagrams
35 * with `to` and `from_to` conditions.
36 */
39
40 // `callers` are message pointing towards an activity
41 std::vector<reverse_call_graph_activity_node> callers;
42};
43
44/**
45 * @brief Convert reverse call graph into a list of activity id chains
46 * @param root Root of the reverse call graph tree
47 * @return
48 */
49std::vector<std::vector<eid_t>> find_reverse_message_chains(
51
52/**
53 * @brief Model of a sequence diagram
54 *
55 * @embed{sequence_model_class.svg}
56 */
58public:
59 diagram() = default;
60
61 diagram(const diagram &) = delete;
62 diagram(diagram &&) = default;
63 diagram &operator=(const diagram &) = delete;
64 diagram &operator=(diagram &&) = default;
65
66 /**
67 * @brief Get the diagram model type - in this case sequence.
68 *
69 * @return Type of sequence diagram.
70 */
71 common::model::diagram_t type() const override;
72
73 /**
74 * @brief Search for element in the diagram by fully qualified name.
75 *
76 * @param full_name Fully qualified element name.
77 * @return Optional reference to a diagram element.
78 */
80 const std::string &full_name) const override;
81
82 /**
83 * @brief Search for element in the diagram by id.
84 *
85 * @param id Element id.
86 * @return Optional reference to a diagram element.
87 */
89 eid_t id) const override;
90
91 /**
92 * @brief Get participant by id
93 *
94 * @param id Participant id.
95 * @return Optional reference to a diagram element.
96 */
97 template <typename T>
99 {
100 if (participants_.find(id) == participants_.end()) {
101 return {};
102 }
103
105 dynamic_cast<T *>(participants_.at(id).get()));
106 }
107
108 /**
109 * @brief Add sequence diagram participant
110 *
111 * @param p Sequence diagram participant model
112 */
113 void add_participant(std::unique_ptr<participant> p);
114
115 /**
116 * @brief Set participant with `id` as active
117 *
118 * @param id Id of participant to activate
119 */
121
122 /**
123 * @brief Check if diagram has activity identified by caller id
124 *
125 * @param id Caller id representing the activity
126 * @return True, if an activity already exists
127 */
128 bool has_activity(eid_t id) const;
129
130 /**
131 * @brief Get reference to current activity of a participant
132 *
133 * @param id Participant id
134 * @return
135 */
136 const activity &get_activity(eid_t id) const;
137
138 /**
139 * @brief Get reference to current activity of a participant
140 *
141 * @param id Participant id
142 * @return
143 */
145
146 /**
147 * @brief Add message to current activity
148 *
149 * @param message Message model
150 */
152
153 /**
154 * @brief Add block message to the current activity
155 *
156 * Block messages represent sequence diagram blocks such as `alt`
157 * or `loop`.
158 *
159 * The block messages can be stacked.
160 *
161 * @param message Message model
162 */
164
165 /**
166 * @brief End current block message
167 *
168 * @param message Message model
169 * @param start_type Type of block statement.
170 */
173
174 /**
175 * @brief Add `switch` block `case` statement
176 * @param m Message model
177 */
179
180 /**
181 * @brief Get all sequences in the diagram
182 *
183 * @return Map of sequences in the diagram
184 */
185 std::map<eid_t, activity> &sequences();
186
187 /**
188 * @brief Get all sequences in the diagram
189 *
190 * @return Map of sequences in the diagram
191 */
192 const std::map<eid_t, activity> &sequences() const;
193
194 /**
195 * @brief Get map of all participants in the diagram
196 *
197 * @return Map of participants in the diagram
198 */
199 std::map<eid_t, std::unique_ptr<participant>> &participants();
200
201 /**
202 * @brief Get map of all participants in the diagram
203 *
204 * @return Map of participants in the diagram
205 */
206 const std::map<eid_t, std::unique_ptr<participant>> &participants() const;
207
208 /**
209 * @brief Get all active participants in the diagram
210 *
211 * @return Set of all active participant ids
212 */
213 std::set<eid_t> &active_participants();
214
215 /**
216 * @brief Get all active participants in the diagram
217 *
218 * @return Set of all active participant ids
219 */
220 const std::set<eid_t> &active_participants() const;
221
222 /**
223 * @brief Convert element full name to PlantUML alias.
224 *
225 * @todo This method does not belong here - refactor to PlantUML specific
226 * code.
227 *
228 * @param full_name Full name of the diagram element.
229 * @return PlantUML alias.
230 */
231 std::string to_alias(const std::string &full_name) const;
232
233 /**
234 * @brief Debug method for printing entire diagram to console.
235 */
236 void print() const;
237
238 // Implicitly import should_include overloads from base class
240
241 /**
242 * @brief Convenience `should_include` overload for participant
243 * @param p Participant model
244 * @return True, if the participant should be included in the diagram
245 */
247
248 /**
249 * @brief Get list of all possible 'from' values in the model
250 *
251 * @return List of all possible 'from' values
252 */
253 std::vector<std::string> list_from_values() const;
254
255 /**
256 * @brief Get list of all possible 'to' values in the model
257 *
258 * @return List of all possible 'to' values
259 */
260 std::vector<std::string> list_to_values() const;
261
262 /**
263 * @brief Generate a list of message chains matching a from_to constraint
264 *
265 * If 'from_activity' is 0, this method will return all message chains
266 * ending in 'to_activity'.
267 *
268 * @param from_activity Source activity for from_to message chain
269 * @param to_activity Target activity for from_to message chain
270 * @return List of message chains
271 */
272 std::vector<message_chain_t> get_all_from_to_message_chains(
273 eid_t from_activity, eid_t to_activity) const;
274
275 /**
276 * @brief Build reverse call graph
277 *
278 * This method builds a reverse call graph tree based on `callers()` list
279 * stored in each activity.
280 *
281 * @param node The current reverse call graph node
282 * @param visited_callers This is necessary for breaking recursive calls
283 */
285 std::set<eid_t> visited_callers = {}) const;
286
287 /**
288 * @brief Get ids of activities matching 'to'
289 *
290 * @param to_location Target activity
291 * @return Activity id
292 */
293 std::vector<eid_t> get_to_activity_ids(
294 const config::source_location &to_location) const;
295
296 /**
297 * @brief Get ids of activities matching 'from'
298 *
299 * @param from_location Source activity
300 * @return Activity id
301 */
302 std::vector<eid_t> get_from_activity_ids(
303 const config::source_location &from_location) const;
304
305 /**
306 * @brief Once the diagram is complete, run any final processing.
307 *
308 * This method should be overriden by specific diagram models to do some
309 * final tasks like cleaning up the model (e.g. some filters only work
310 * on completed diagrams).
311 */
312 void finalize() override;
313
314 /**
315 * @brief Check whether the diagram is empty
316 *
317 * @return True, if diagram is empty
318 */
319 bool is_empty() const override;
320
321 /**
322 * If option to inline lambda calls is enabled, we need to modify the
323 * sequences to skip the lambda calls. In case lambda call does not lead
324 * to a non-lambda call, omit it entirely
325 */
327
328private:
329 /**
330 * This method checks the last messages in sequence (current_messages),
331 * if they represent a block sequence identified by statement_begin
332 * (e.g. if/else) and there are no actual call expressions within this block
333 * statement the entire block statement is removed from the end of the
334 * sequence.
335 *
336 * Otherwise the block statement is ended with a proper statement
337 * (e.g. endif)
338 *
339 * @param m Message to add to the sequence
340 * @param statement_begin Type of message which begins this type of block
341 * statement (e.g. message_t::kIf)
342 * @param current_messages Reference to the sequence messages which should
343 * be amended
344 */
345 void fold_or_end_block_statement(message &&m,
346 common::model::message_t statement_begin,
347 std::vector<message> &current_messages) const;
348
350 {
352 static std::set<message_t> block_begin_types{message_t::kIf,
353 message_t::kWhile, message_t::kDo, message_t::kFor, message_t::kTry,
354 message_t::kSwitch, message_t::kConditional};
355
356 return block_begin_types.count(mt) > 0;
357 };
358
360 {
362 static std::set<message_t> block_end_types{message_t::kIfEnd,
363 message_t::kWhileEnd, message_t::kDoEnd, message_t::kForEnd,
364 message_t::kTryEnd, message_t::kSwitchEnd,
365 message_t::kConditionalEnd};
366
367 return block_end_types.count(mt) > 0;
368 };
369
371 eid_t id, model::activity &new_activity, const model::message &m);
372
373 std::map<eid_t, activity> activities_;
374
375 std::map<eid_t, std::unique_ptr<participant>> participants_;
376
377 std::set<eid_t> active_participants_;
378};
379
380} // namespace clanguml::sequence_diagram::model
381
382namespace clanguml::common::model {
383template <>
385 diagram_t t);
386}