0.6.2
C++ to UML diagram generator based on Clang
Loading...
Searching...
No Matches
Public Member Functions | Private Member Functions | Private Attributes | List of all members
clanguml::sequence_diagram::generators::plantuml::generator Class Reference

Sequence diagram PlantUML generator. More...

Detailed Description

Sequence diagram PlantUML generator.

Definition at line 50 of file sequence_diagram_generator.h.

#include <sequence_diagram_generator.h>

Public Member Functions

 generator (diagram_config &config, diagram_model &model)
 
void generate_diagram (std::ostream &ostr) const override
 Main generator method.
 
void generate_call (const clanguml::sequence_diagram::model::message &m, std::ostream &ostr) const
 Generate sequence diagram message.
 
void generate_return (const clanguml::sequence_diagram::model::message &m, std::ostream &ostr) const
 Generate sequence diagram return message.
 
void generate_participant (std::ostream &ostr, eid_t id, bool force=false) const
 Generate sequence diagram participant.
 
void generate_participant (std::ostream &ostr, const std::string &name) const
 Generate sequence diagram participant by name.
 
void generate_activity (eid_t activity_id, std::ostream &ostr, std::vector< eid_t > &visited) const
 Generate sequence diagram activity.
 
- Public Member Functions inherited from clanguml::common::generators::plantuml::generator< ConfigType, DiagramType >
 generator (ConfigType &config, DiagramType &model)
 Constructor.
 
 ~generator () override=default
 
void generate (std::ostream &ostr) const override
 Generate diagram.
 
virtual void generate_diagram (std::ostream &ostr) const =0
 Generate diagram specific part.
 
void generate_config_layout_hints (std::ostream &ostr) const
 Generate diagram layout hints.
 
void generate_plantuml_directives (std::ostream &ostr, const std::vector< std::string > &directives) const
 Generate PlantUML directives from config file.
 
void generate_notes (std::ostream &ostr, const model::element &element) const
 Generate diagram notes.
 
void generate_style (std::ostream &ostr, const std::string &element_type, const model::stylable_element &el) const
 Generate diagram element PlantUML style.
 
void generate_metadata (std::ostream &ostr) const
 Generate comment with diagram metadata.
 
void generate_title (std::ostream &ostr) const
 Generate diagram title.
 
template<typename E >
void generate_link (std::ostream &ostr, const E &e) const
 Generate hyper link to element.
 
void generate_link (std::ostream &ostr, const relationship &e) const
 generate_link specialization for relationship
 
void print_debug (const common::model::source_location &e, std::ostream &ostr) const
 Print debug information in diagram comments.
 
- Public Member Functions inherited from clanguml::common::generators::generator< ConfigType, DiagramType >
 generator (ConfigType &config, DiagramType &model)
 Constructor.
 
virtual ~generator ()=default
 
virtual void generate (std::ostream &ostr) const =0
 Generate diagram.
 
const ConfigType & config () const
 Get reference to diagram config.
 
const DiagramType & model () const
 Get reference to diagram model.
 
std::optional< std::pair< std::string, std::string > > get_link_pattern (const common::model::source_location &sl) const
 
std::optional< std::pair< std::string, std::string > > get_tooltip_pattern (const common::model::source_location &sl) const
 
std::optional< std::string > render_link (const common::model::diagram_element &e) const
 
std::optional< std::string > render_link (const common::model::relationship &e) const
 
std::optional< std::string > render_tooltip (const common::model::diagram_element &e) const
 
std::optional< std::string > render_tooltip (const common::model::relationship &e) const
 
void init_context ()
 Initialize diagram Jinja context.
 
void update_context () const
 Update diagram Jinja context.
 
void init_env ()
 
const inja::json & context () const
 
inja::Environment & env () const
 

Private Member Functions

bool is_participant_generated (eid_t id) const
 Check if specified participant has already been generated.
 
std::string generate_alias (const model::participant &participant) const
 Generate PlantUML alias for participant.
 
void generate_message_comment (std::ostream &ostr, const model::message &m) const
 Generate message call note.
 
void generate_from_to_sequences (std::ostream &ostr) const
 
void generate_to_sequences (std::ostream &ostr) const
 
void generate_from_sequences (std::ostream &ostr) const
 
std::vector< eid_tfind_from_activities () const
 
std::vector< model::message_chain_tfind_to_message_chains () const
 
std::string render_message_name (const std::string &m) const
 
model::function::message_render_mode select_method_arguments_render_mode () const
 Convert config to model message render mode.
 

Private Attributes

std::set< eid_tgenerated_participants_
 
std::set< unsigned int > generated_comment_ids_
 
std::vector< model::messagealready_generated_in_static_context_
 
std::set< eid_tgenerated_activities_
 

Additional Inherited Members

- Protected Attributes inherited from clanguml::common::generators::plantuml::generator< ConfigType, DiagramType >
std::set< std::string > m_generated_aliases
 
- Protected Attributes inherited from clanguml::common::generators::generator< ConfigType, DiagramType >
inja::json m_context
 
inja::Environment m_env
 

Constructor & Destructor Documentation

◆ generator()

clanguml::sequence_diagram::generators::plantuml::generator::generator ( diagram_config config,
diagram_model model 
)

Definition at line 38 of file sequence_diagram_generator.cc.

40 : common_generator<diagram_config, diagram_model>{config, model}
41{
42}

Member Function Documentation

◆ find_from_activities()

std::vector< eid_t > clanguml::sequence_diagram::generators::plantuml::generator::find_from_activities ( ) const
private

Definition at line 697 of file sequence_diagram_generator.cc.

698{
699 std::vector<eid_t> start_from;
700 for (const auto &sf : config().from()) {
701 if (sf.location_type == location_t::function) {
702 bool found{false};
703 for (const auto &[k, v] : model().sequences()) {
704 if (model().participants().count(v.from()) == 0)
705 continue;
706
707 const auto &caller = *model().participants().at(v.from());
708 std::string vfrom = caller.full_name(false);
709 if (sf.location == vfrom) {
710 LOG_DBG("Found sequence diagram start point: {}", k);
711 start_from.push_back(k);
712 found = true;
713 }
714 }
715
716 if (!found) {
717 model().handle_invalid_from_condition(sf);
718 }
719 }
720 }
721
722 return start_from;
723}

◆ find_to_message_chains()

std::vector< model::message_chain_t > clanguml::sequence_diagram::generators::plantuml::generator::find_to_message_chains ( ) const
private

Definition at line 725 of file sequence_diagram_generator.cc.

726{
727 std::vector<model::message_chain_t> result;
728
729 for (const auto &to_location : config().to()) {
730 auto to_activity_ids = model().get_to_activity_ids(to_location);
731
732 if (to_activity_ids.empty()) {
733 model().handle_invalid_to_condition(to_location);
734 }
735
736 for (const auto &to_activity_id : to_activity_ids) {
737 std::vector<model::message_chain_t> message_chains_unique =
738 model().get_all_from_to_message_chains(eid_t{}, to_activity_id);
739
740 result.insert(result.end(), message_chains_unique.begin(),
741 message_chains_unique.end());
742 }
743 }
744
745 return result;
746}

◆ generate_activity()

void clanguml::sequence_diagram::generators::plantuml::generator::generate_activity ( eid_t  activity_id,
std::ostream &  ostr,
std::vector< eid_t > &  visited 
) const

Generate sequence diagram activity.

Parameters
activity_idActivity id
ostrOutput stream
visitedList of already visited participants, this is necessary for breaking infinite recursion on recursive calls

Definition at line 203 of file sequence_diagram_generator.cc.

205{
206 const auto &a = model().get_activity(activity_id);
207
208 const auto [it, inserted] = generated_activities_.emplace(activity_id);
209
210 if (config().fold_repeated_activities() && !inserted &&
211 !a.messages().empty()) {
212 const auto &p =
213 model().get_participant<model::participant>(activity_id);
214
215 if (p.has_value()) {
216 ostr << "hnote over " << generate_alias(p.value()) << " : *\n";
217 // This is necessary to keep the hnote over the activity life line
218 ostr << generate_alias(p.value()) << "-[hidden]->"
219 << generate_alias(p.value()) << '\n';
220 }
221
222 return;
223 }
224
225 for (const auto &m : a.messages()) {
226 if (m.in_static_declaration_context()) {
228 continue;
229
231 }
232
233 if (m.type() == message_t::kCall || m.type() == message_t::kCoAwait) {
234 const auto &to =
235 model().get_participant<model::participant>(m.to());
236
237 if (!to.has_value()) {
238 LOG_DBG("Skipping activity {} due to missing target paricipant "
239 "in the diagram",
240 m.from());
241 continue;
242 }
243
244 visited.push_back(m.from());
245
246 LOG_DBG("Generating message [{}] --> [{}]", m.from(), m.to());
247
248 generate_call(m, ostr);
249
250 std::string to_alias = generate_alias(to.value());
251
252 ostr << "activate " << to_alias << '\n';
253
254 if (model().sequences().find(m.to()) != model().sequences().end()) {
255 if (std::find(visited.begin(), visited.end(), m.to()) ==
256 visited
257 .end()) { // break infinite recursion on recursive calls
258
259 LOG_DBG("Generating activity {} (called from {})", m.to(),
260 m.from());
261
262 generate_activity(m.to(), ostr, visited);
263 }
264 }
265 else
266 LOG_DBG("Skipping activity {} --> {} - missing sequence {}",
267 m.from(), m.to(), m.to());
268
269 ostr << "deactivate " << to_alias << '\n';
270
271 visited.pop_back();
272 }
273 else if (m.type() == message_t::kReturn) {
274 print_debug(m, ostr);
276 auto return_message = m;
277 if (!visited.empty()) {
278 return_message.set_to(visited.back());
279 }
280 generate_return(return_message, ostr);
281 }
282 else if (m.type() == message_t::kCoReturn) {
283 print_debug(m, ostr);
285 auto return_message = m;
286 if (!visited.empty()) {
287 return_message.set_to(visited.back());
288 }
289 generate_return(return_message, ostr);
290 }
291 else if (m.type() == message_t::kCoYield) {
292 print_debug(m, ostr);
294 auto return_message = m;
295 if (!visited.empty()) {
296 return_message.set_to(visited.back());
297 }
298 generate_return(return_message, ostr);
299 }
300 else if (m.type() == message_t::kIf) {
301 print_debug(m, ostr);
303 ostr << "alt";
304 if (const auto &text = m.condition_text(); text.has_value())
305 ostr << " " << text.value();
306 ostr << '\n';
307 }
308 else if (m.type() == message_t::kElseIf) {
309 print_debug(m, ostr);
310 ostr << "else";
311 if (const auto &text = m.condition_text(); text.has_value())
312 ostr << " " << text.value();
313 ostr << '\n';
314 }
315 else if (m.type() == message_t::kElse) {
316 print_debug(m, ostr);
317 ostr << "else\n";
318 }
319 else if (m.type() == message_t::kIfEnd) {
320 ostr << "end\n";
321 }
322 else if (m.type() == message_t::kWhile) {
323 print_debug(m, ostr);
325 ostr << "loop";
326 if (const auto &text = m.condition_text(); text.has_value())
327 ostr << " " << text.value();
328 ostr << '\n';
329 }
330 else if (m.type() == message_t::kWhileEnd) {
331 ostr << "end\n";
332 }
333 else if (m.type() == message_t::kFor) {
334 print_debug(m, ostr);
336 ostr << "loop";
337 if (const auto &text = m.condition_text(); text.has_value())
338 ostr << " " << text.value();
339 ostr << '\n';
340 }
341 else if (m.type() == message_t::kForEnd) {
342 ostr << "end\n";
343 }
344 else if (m.type() == message_t::kDo) {
345 print_debug(m, ostr);
347 ostr << "loop";
348 if (const auto &text = m.condition_text(); text.has_value())
349 ostr << " " << text.value();
350 ostr << '\n';
351 }
352 else if (m.type() == message_t::kDoEnd) {
353 ostr << "end\n";
354 }
355 else if (m.type() == message_t::kTry) {
356 print_debug(m, ostr);
358 ostr << "group try\n";
359 }
360 else if (m.type() == message_t::kCatch) {
361 print_debug(m, ostr);
362 ostr << "else " << render_message_name(m.message_name()) << '\n';
363 }
364 else if (m.type() == message_t::kTryEnd) {
365 print_debug(m, ostr);
366 ostr << "end\n";
367 }
368 else if (m.type() == message_t::kSwitch) {
369 print_debug(m, ostr);
371 ostr << "group switch\n";
372 }
373 else if (m.type() == message_t::kCase) {
374 print_debug(m, ostr);
375 ostr << "else " << render_message_name(m.message_name()) << '\n';
376 }
377 else if (m.type() == message_t::kSwitchEnd) {
378 ostr << "end\n";
379 }
380 else if (m.type() == message_t::kConditional) {
381 print_debug(m, ostr);
383 ostr << "alt";
384 if (const auto &text = m.condition_text(); text.has_value())
385 ostr << " " << text.value();
386 ostr << '\n';
387 }
388 else if (m.type() == message_t::kConditionalElse) {
389 print_debug(m, ostr);
390 ostr << "else\n";
391 }
392 else if (m.type() == message_t::kConditionalEnd) {
393 ostr << "end\n";
394 }
395 }
396}

◆ generate_alias()

std::string clanguml::sequence_diagram::generators::plantuml::generator::generate_alias ( const model::participant participant) const
private

Generate PlantUML alias for participant.

Parameters
participantSequence diagram participant model
Returns
Particpant alias

Definition at line 619 of file sequence_diagram_generator.cc.

621{
622 if ((participant.type_name() == "function" ||
623 participant.type_name() == "function_template") &&
624 config().combine_free_functions_into_file_participants()) {
625 const auto file_id = common::to_id(participant.file());
626
627 return fmt::format("C_{:022}", file_id.value());
628 }
629
630 return participant.alias();
631}

◆ generate_call()

void clanguml::sequence_diagram::generators::plantuml::generator::generate_call ( const clanguml::sequence_diagram::model::message m,
std::ostream &  ostr 
) const

Generate sequence diagram message.

Parameters
mMessage model
ostrOutput stream

Definition at line 44 of file sequence_diagram_generator.cc.

45{
46 const auto &from = model().get_participant<model::participant>(m.from());
47 const auto &to = model().get_participant<model::participant>(m.to());
48
49 if (!from || !to) {
50 LOG_DBG("Skipping empty call from '{}' to '{}'", m.from(), m.to());
51 return;
52 }
53
54 generate_participant(ostr, m.from());
55 generate_participant(ostr, m.to());
56
57 std::string message;
58
61
62 if (to.value().type_name() == "method") {
63 const auto &f = dynamic_cast<const model::method &>(to.value());
64 const std::string_view style = f.is_static() ? "__" : "";
65
66 if (m.type() == message_t::kCoAwait)
67 message = fmt::format("{}<< co_await >>\\n{}{}", style,
68 f.message_name(render_mode), style);
69 else
70 message = fmt::format(
71 "{}{}{}", style, f.message_name(render_mode), style);
72 }
73 else if (to.value().type_name() == "objc_method") {
74 const auto &f = dynamic_cast<const model::objc_method &>(to.value());
75 const std::string_view style = f.is_static() ? "__" : "";
76 message =
77 fmt::format("{}{}{}", style, f.message_name(render_mode), style);
78 }
79 else if (config().combine_free_functions_into_file_participants()) {
80 if (to.value().type_name() == "function") {
81 const auto &f = dynamic_cast<const model::function &>(to.value());
82 message = f.message_name(render_mode);
83
84 if (f.is_cuda_kernel())
85 message = fmt::format("<< CUDA Kernel >>\\n{}", message);
86 else if (f.is_cuda_device())
87 message = fmt::format("<< CUDA Device >>\\n{}", message);
88 else if (f.is_coroutine())
89 message = fmt::format("<< Coroutine >>\\n{}", message);
90 }
91 else if (to.value().type_name() == "function_template") {
92 const auto &f = dynamic_cast<const model::function &>(to.value());
93 message = f.message_name(render_mode);
94
95 if (f.is_cuda_kernel())
96 message = fmt::format("<< CUDA Kernel >>\\n{}", message);
97 else if (f.is_cuda_device())
98 message = fmt::format("<< CUDA Device >>\\n{}", message);
99 else if (f.is_coroutine())
100 message = fmt::format("<< Coroutine >>\\n{}", message);
101 }
102 }
103
104 message = config().simplify_template_type(message);
105
106 const std::string from_alias = generate_alias(from.value());
107 const std::string to_alias = generate_alias(to.value());
108
109 print_debug(m, ostr);
110
112
113 ostr << from_alias << " "
114 << common::generators::plantuml::to_plantuml(message_t::kCall) << " ";
115
116 ostr << to_alias;
117
118 if (config().generate_links) {
120 }
121
122 ostr << " : ";
123
125 ostr << "**[**";
126
127 ostr << render_message_name(message);
128
130 ostr << "**]**";
131
132 ostr << '\n';
133
134 LOG_DBG("Generated call '{}' from {} [{}] to {} [{}]", message,
135 from.value().full_name(false), m.from(), to.value().full_name(false),
136 m.to());
137}

◆ generate_diagram()

void clanguml::sequence_diagram::generators::plantuml::generator::generate_diagram ( std::ostream &  ostr) const
overridevirtual

Main generator method.

This method is called first and coordinates the entire diagram generation.

Parameters
ostrOutput stream.

Implements clanguml::common::generators::plantuml::generator< ConfigType, DiagramType >.

Definition at line 633 of file sequence_diagram_generator.cc.

634{
635 model().print();
636
637 if (config().participants_order.has_value) {
638 for (const auto &p : config().participants_order()) {
639 LOG_DBG("Pregenerating participant {}", p);
640 generate_participant(ostr, p);
641 }
642 }
643
645
647
649}

◆ generate_from_sequences()

void clanguml::sequence_diagram::generators::plantuml::generator::generate_from_sequences ( std::ostream &  ostr) const
private

Definition at line 651 of file sequence_diagram_generator.cc.

652{
653 std::vector<eid_t> start_from = find_from_activities();
654
655 // Use this to break out of recurrent loops
656 std::vector<eid_t> visited_participants;
657
658 for (const auto from_id : start_from) {
659 if (model().participants().count(from_id) == 0)
660 continue;
661
662 const auto &from = model().get_participant<model::function>(from_id);
663
664 if (!from.has_value()) {
665 LOG_WARN("Failed to find participant {} for 'from' "
666 "condition");
667 continue;
668 }
669
670 generate_participant(ostr, from_id);
671
672 std::string from_alias = generate_alias(from.value());
673
676
677 // For methods or functions in diagrams where they are
678 // combined into file participants, we need to add an
679 // 'entry' point call to know which method relates to the
680 // first activity for this 'start_from' condition
681 if (from.value().type_name() == "method" ||
682 from.value().type_name() == "objc_method" ||
683 config().combine_free_functions_into_file_participants()) {
684 ostr << "[->" << " " << from_alias << " : "
685 << render_message_name(from.value().message_name(render_mode))
686 << '\n';
687 }
688
689 ostr << "activate " << from_alias << '\n';
690
691 generate_activity(from_id, ostr, visited_participants);
692
693 ostr << "deactivate " << from_alias << '\n';
694 }
695}

◆ generate_from_to_sequences()

void clanguml::sequence_diagram::generators::plantuml::generator::generate_from_to_sequences ( std::ostream &  ostr) const
private

Definition at line 789 of file sequence_diagram_generator.cc.

790{
791 for (const auto &ft : config().from_to()) {
792 // First, find the sequence of activities from 'from' location
793 // to 'to' location
794 assert(ft.size() == 2);
795
796 const auto &from_location = ft.front();
797 const auto &to_location = ft.back();
798
799 const auto from_activity_ids =
800 model().get_from_activity_ids(from_location);
801
802 const auto to_activity_ids = model().get_to_activity_ids(to_location);
803
804 if (from_activity_ids.empty()) {
805 model().handle_invalid_from_condition(from_location);
806 }
807
808 if (from_activity_ids.empty() || to_activity_ids.empty()) {
809 model().handle_invalid_to_condition(to_location);
810 }
811
812 bool first_separator_skipped{false};
813
814 for (const auto from_activity_id : from_activity_ids) {
815 if (model().participants().count(from_activity_id) == 0)
816 continue;
817
818 for (const auto to_activity_id : to_activity_ids) {
819 if (model().participants().count(to_activity_id) == 0)
820 continue;
821
822 auto message_chains_unique =
823 model().get_all_from_to_message_chains(
824 from_activity_id, to_activity_id);
825
826 for (const auto &mc : message_chains_unique) {
827 if (!first_separator_skipped)
828 first_separator_skipped = true;
829 else
830 ostr << "====\n";
831
832 const auto &from = model().get_participant<model::function>(
833 from_activity_id);
834
835 if (from.value().type_name() == "method" ||
836 from.value().type_name() == "objc_method" ||
837 config()
838 .combine_free_functions_into_file_participants()) {
839 generate_participant(ostr, from_activity_id);
840 ostr << "[->" << " " << generate_alias(from.value())
841 << " : "
842 << render_message_name(from.value().message_name(
844 << '\n';
845 }
846
847 for (const auto &m : mc) {
848 generate_call(m, ostr);
849 }
850 }
851 }
852 }
853 }
854}

◆ generate_message_comment()

void clanguml::sequence_diagram::generators::plantuml::generator::generate_message_comment ( std::ostream &  ostr,
const model::message m 
) const
private

Generate message call note.

Parameters
ostrOutput stream
mMessage

Definition at line 398 of file sequence_diagram_generator.cc.

400{
401 const auto &from = model().get_participant<model::participant>(m.from());
402 if (!from)
403 return;
404
405 // First generate message comments from \note directives in comments
406 bool comment_generated_from_note_decorators{false};
407 for (const auto &decorator : m.decorators()) {
408 auto note = std::dynamic_pointer_cast<decorators::note>(decorator);
409 if (note && note->applies_to_diagram(config().name)) {
410 comment_generated_from_note_decorators = true;
411
412 ostr << "note over " << generate_alias(from.value()) << '\n';
413
415 note->text, config().message_comment_width())
416 << '\n';
417
418 ostr << "end note" << '\n';
419 }
420 }
421
422 if (comment_generated_from_note_decorators)
423 return;
424
425 if (!config().generate_message_comments())
426 return;
427
428 // Now generate message notes from raw comments if enabled
429 if (const auto &comment = m.comment(); comment &&
430 generated_comment_ids_.emplace(comment.value().at("id")).second) {
431
432 ostr << "note over " << generate_alias(from.value()) << '\n';
433
434 ostr << util::format_message_comment(comment.value().at("comment"),
435 config().message_comment_width())
436 << '\n';
437
438 ostr << "end note" << '\n';
439 }
440}

◆ generate_participant() [1/2]

void clanguml::sequence_diagram::generators::plantuml::generator::generate_participant ( std::ostream &  ostr,
const std::string &  name 
) const

Generate sequence diagram participant by name.

This is convienience wrapper over generate_participant() by id.

Parameters
ostrOutput stream
nameFull participant name

Definition at line 442 of file sequence_diagram_generator.cc.

444{
445 auto p = model().get(name);
446
447 if (!p.has_value()) {
448 LOG_WARN("Cannot find participant {} from `participants_order` "
449 "option",
450 name);
451 return;
452 }
453
454 generate_participant(ostr, p.value().id(), true);
455}

◆ generate_participant() [2/2]

void clanguml::sequence_diagram::generators::plantuml::generator::generate_participant ( std::ostream &  ostr,
eid_t  id,
bool  force = false 
) const

Generate sequence diagram participant.

Parameters
ostrOutput stream
idParticipant id
forceIf true, generate the participant even if its not in the set of active participants
Returns
Id of the generated participant

Definition at line 457 of file sequence_diagram_generator.cc.

459{
460 eid_t participant_id{};
461
462 if (!force) {
463 for (const auto pid : model().active_participants()) {
464 if (pid == id) {
465 participant_id = pid;
466 break;
467 }
468 }
469 }
470 else
471 participant_id = id;
472
473 if (participant_id == 0)
474 return;
475
476 if (is_participant_generated(participant_id))
477 return;
478
479 const auto &participant =
480 model().get_participant<model::participant>(participant_id).value();
481
482 if (participant.type_name() == "method") {
483 const auto class_id =
484 model()
485 .get_participant<model::method>(participant_id)
486 .value()
487 .class_id();
488
489 if (is_participant_generated(class_id))
490 return;
491
492 const auto &class_participant =
493 model().get_participant<model::participant>(class_id).value();
494
495 print_debug(class_participant, ostr);
496
497 auto participant_name = config().simplify_template_type(
498 display_name_adapter(class_participant).full_name(false));
499 participant_name =
500 config().using_namespace().relative(participant_name);
501
503
504 ostr << "participant \"" << participant_name << "\" as "
505 << class_participant.alias();
506
507 if (config().generate_links) {
509 ostr, class_participant);
510 }
511
512 ostr << '\n';
513
514 generated_participants_.emplace(class_id);
515 }
516 else if (participant.type_name() == "objc_method") {
517 const auto class_id =
518 model()
519 .get_participant<model::objc_method>(participant_id)
520 .value()
521 .class_id();
522
523 if (is_participant_generated(class_id))
524 return;
525
526 const auto &class_participant =
527 model().get_participant<model::participant>(class_id).value();
528
529 print_debug(class_participant, ostr);
530
531 auto participant_name = config().simplify_template_type(
532 display_name_adapter(class_participant).full_name(false));
533 participant_name =
534 config().using_namespace().relative(participant_name);
535
537
538 ostr << "participant \"" << participant_name << "\" as "
539 << class_participant.alias() << " <<ObjC Interface>>";
540
541 if (config().generate_links) {
543 ostr, class_participant);
544 }
545
546 ostr << '\n';
547
548 generated_participants_.emplace(class_id);
549 }
550 else if ((participant.type_name() == "function" ||
551 participant.type_name() == "function_template") &&
552 config().combine_free_functions_into_file_participants()) {
553 // Create a single participant for all functions declared in a
554 // single file
555 const auto &file_path =
556 model()
557 .get_participant<model::function>(participant_id)
558 .value()
559 .file();
560
561 assert(!file_path.empty());
562
563 const auto file_id = common::to_id(file_path);
564
565 if (is_participant_generated(file_id))
566 return;
567
568 auto participant_name = util::path_to_url(relative(
569 std::filesystem::path{file_path}, config().root_directory())
570 .string());
571
572 ostr << "participant \"" << participant_name << "\" as "
573 << fmt::format("C_{:022}", file_id.value());
574
575 ostr << '\n';
576
577 generated_participants_.emplace(file_id);
578 }
579 else {
580 print_debug(participant, ostr);
581
582 auto participant_name =
583 config().using_namespace().relative(config().simplify_template_type(
584 display_name_adapter(participant).full_name(false)));
586
587 ostr << "participant \"" << participant_name << "\" as "
588 << participant.alias();
589
590 if (const auto *function_ptr =
591 dynamic_cast<const model::function *>(&participant);
592 function_ptr) {
593 if (function_ptr->is_cuda_kernel())
594 ostr << " << CUDA Kernel >>";
595 else if (function_ptr->is_cuda_device())
596 ostr << " << CUDA Device >>";
597 else if (function_ptr->is_coroutine())
598 ostr << fmt::format("<< Coroutine >>");
599 }
600
601 if (config().generate_links) {
603 ostr, participant);
604 }
605
606 ostr << '\n';
607
608 generated_participants_.emplace(participant_id);
609 }
610}

◆ generate_return()

void clanguml::sequence_diagram::generators::plantuml::generator::generate_return ( const clanguml::sequence_diagram::model::message m,
std::ostream &  ostr 
) const

Generate sequence diagram return message.

Parameters
mMessage model
ostrOutput stream

Definition at line 139 of file sequence_diagram_generator.cc.

140{
141 // Add return activity only for messages between different actors
142 // and only if the return type is different than void
143 if (m.from() == m.to())
144 return;
145
146 std::string message_stereotype;
147 if (m.type() == message_t::kCoReturn) {
148 message_stereotype = "<< co_return >>";
149 }
150 else if (m.type() == message_t::kCoYield) {
151 message_stereotype = "<< co_yield >>";
152 }
153
154 std::string message_label;
155
156 const auto &from = model().get_participant<model::function>(m.from());
157 const auto &to = model().get_participant<model::participant>(m.to());
158 if (to.has_value() && from.has_value() && !from.value().is_void()) {
159 const std::string from_alias = generate_alias(from.value());
160
161 const std::string to_alias = generate_alias(to.value());
162
163 ostr << from_alias << " "
164 << common::generators::plantuml::to_plantuml(message_t::kReturn)
165 << " " << to_alias;
166
167 if (config().generate_return_types())
168 message_label = render_message_name(m.return_type());
169 else if (config().generate_return_values())
170 message_label = render_message_name(m.message_name());
171 }
172 else if (from.has_value() && !from.value().is_void() &&
173 (from.value().type_name() == "method" ||
174 from.value().type_name() == "objc_method" ||
175 config().combine_free_functions_into_file_participants())) {
176 const std::string from_alias = generate_alias(from.value());
177
178 ostr << "[<--" << " " << from_alias;
179 if (config().generate_return_types())
180 message_label = render_message_name(from.value().return_type());
181 else if (config().generate_return_values())
182 message_label = render_message_name(m.message_name());
183 }
184
185 if (!message_stereotype.empty()) {
186 if (message_label.empty())
187 message_label = fmt::format("//{}//", message_stereotype);
188 else
189 message_label = fmt::format(
190 "//{}//\\n//{}//", message_stereotype, message_label);
191 }
192 else {
193 if (!message_label.empty())
194 message_label = fmt::format("//{}//", message_label);
195 }
196
197 if (!message_label.empty())
198 ostr << " : " << message_label;
199
200 ostr << '\n';
201}

◆ generate_to_sequences()

void clanguml::sequence_diagram::generators::plantuml::generator::generate_to_sequences ( std::ostream &  ostr) const
private

Definition at line 753 of file sequence_diagram_generator.cc.

754{
755 std::vector<model::message_chain_t> message_chains =
757
758 bool first_separator_skipped{false};
759 for (const auto &mc : message_chains) {
760 if (!first_separator_skipped)
761 first_separator_skipped = true;
762 else
763 ostr << "====\n";
764
765 const auto from_activity_id = mc.front().from();
766
767 if (model().participants().count(from_activity_id) == 0)
768 continue;
769
770 const auto &from =
771 model().get_participant<model::function>(from_activity_id);
772
773 if (from.value().type_name() == "method" ||
774 from.value().type_name() == "objc_method" ||
775 config().combine_free_functions_into_file_participants()) {
776 generate_participant(ostr, from_activity_id);
777 ostr << "[->" << " " << generate_alias(from.value()) << " : "
778 << render_message_name(from.value().message_name(
780 << '\n';
781 }
782
783 for (const auto &m : mc) {
784 generate_call(m, ostr);
785 }
786 }
787}

◆ is_participant_generated()

bool clanguml::sequence_diagram::generators::plantuml::generator::is_participant_generated ( eid_t  id) const
private

Check if specified participant has already been generated.

Parameters
idParticipant id.
Returns
True, if participant has already been generated.

Definition at line 612 of file sequence_diagram_generator.cc.

613{
614 return std::find(generated_participants_.begin(),
616 id) != generated_participants_.end();
617}

◆ render_message_name()

std::string clanguml::sequence_diagram::generators::plantuml::generator::render_message_name ( const std::string &  m) const
private

Definition at line 748 of file sequence_diagram_generator.cc.

749{
750 return util::abbreviate(m, config().message_name_width());
751}

◆ select_method_arguments_render_mode()

model::function::message_render_mode clanguml::sequence_diagram::generators::plantuml::generator::select_method_arguments_render_mode ( ) const
private

Convert config to model message render mode.

Returns
Method render mode.

Definition at line 857 of file sequence_diagram_generator.cc.

858{
859 if (config().generate_method_arguments() ==
862
863 if (config().generate_method_arguments() == config::method_arguments::none)
865
867}

Member Data Documentation

◆ already_generated_in_static_context_

std::vector<model::message> clanguml::sequence_diagram::generators::plantuml::generator::already_generated_in_static_context_
mutableprivate

Definition at line 166 of file sequence_diagram_generator.h.

◆ generated_activities_

std::set<eid_t> clanguml::sequence_diagram::generators::plantuml::generator::generated_activities_
mutableprivate

Definition at line 167 of file sequence_diagram_generator.h.

◆ generated_comment_ids_

std::set<unsigned int> clanguml::sequence_diagram::generators::plantuml::generator::generated_comment_ids_
mutableprivate

Definition at line 165 of file sequence_diagram_generator.h.

◆ generated_participants_

std::set<eid_t> clanguml::sequence_diagram::generators::plantuml::generator::generated_participants_
mutableprivate

Definition at line 164 of file sequence_diagram_generator.h.


The documentation for this class was generated from the following files: