0.6.0
C++ to UML diagram generator based on Clang
Loading...
Searching...
No Matches
sequence_diagram_generator.cc
Go to the documentation of this file.
1/**
2 * @file src/sequence_diagram/generators/json/sequence_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
20
22
24
25void to_json(nlohmann::json &j, const participant &c)
26{
27 to_json(j, dynamic_cast<const participant::element &>(c));
28 j["type"] = c.type_name();
29
30 if (c.type_name() == "method") {
31 j["name"] = dynamic_cast<const method &>(c).method_name();
32 }
33 else if (c.type_name() == "objc_method") {
34 j["name"] = dynamic_cast<const objc_method &>(c).method_name();
35 }
36
37 j["full_name"] = display_name_adapter(c).full_name(false);
38
39 if (c.type_name() == "function" || c.type_name() == "function_template") {
40 const auto &f = dynamic_cast<const function &>(c);
41 if (f.is_cuda_kernel())
42 j["is_cuda_kernel"] = true;
43 if (f.is_cuda_device())
44 j["is_cuda_device"] = true;
45 }
46}
47
48void to_json(nlohmann::json &j, const activity &c)
49{
50 j["participant_id"] = std::to_string(c.from().value());
51}
52
53} // namespace clanguml::sequence_diagram::model
54
56
62
63using namespace clanguml::util;
64
65//
66// generator
67//
68
72{
73}
74
75void generator::generate_call(const message &m, nlohmann::json &parent) const
76{
77 const auto &from = model().get_participant<model::participant>(m.from());
78 const auto &to = model().get_participant<model::participant>(m.to());
79
80 if (!from || !to) {
81 LOG_DBG("Skipping empty call from '{}' to '{}'", m.from(), m.to());
82 return;
83 }
84
87
88 std::string message;
89
92
93 if (to.value().type_name() == "method") {
94 message = dynamic_cast<const model::method &>(to.value())
95 .message_name(render_mode);
96 }
97 else if (to.value().type_name() == "objc_method") {
98 message = dynamic_cast<const model::objc_method &>(to.value())
99 .message_name(render_mode);
100 }
101 else if (config().combine_free_functions_into_file_participants()) {
102 if (to.value().type_name() == "function") {
103 message = dynamic_cast<const model::function &>(to.value())
104 .message_name(render_mode);
105 }
106 else if (to.value().type_name() == "function_template") {
107 message = dynamic_cast<const model::function_template &>(to.value())
108 .message_name(render_mode);
109 }
110 }
111
112 message = config().simplify_template_type(message);
113
114 nlohmann::json msg;
115
116 msg["name"] = message;
117 msg["type"] = "message";
118 msg["from"]["activity_id"] = std::to_string(from.value().id().value());
119 msg["to"]["activity_id"] = std::to_string(to.value().id().value());
120 if (const auto &cmt = m.comment(); cmt.has_value())
121 msg["comment"] = cmt.value().at("comment");
122
123 if (from.value().type_name() == "method") {
124 const auto &class_participant =
125 model().get_participant<model::method>(from.value().id()).value();
126
127 msg["from"]["participant_id"] =
128 std::to_string(class_participant.class_id().value());
129 }
130 else if (from.value().type_name() == "objc_method") {
131 const auto &class_participant =
132 model()
133 .get_participant<model::objc_method>(from.value().id())
134 .value();
135
136 msg["from"]["participant_id"] =
137 std::to_string(class_participant.class_id().value());
138 }
139 else if (from.value().type_name() == "function" ||
140 from.value().type_name() == "function_template") {
141 if (config().combine_free_functions_into_file_participants()) {
142 const auto &file_participant =
143 model()
144 .get_participant<model::function>(from.value().id())
145 .value();
146 msg["from"]["participant_id"] = std::to_string(
147 common::to_id(file_participant.file_relative()).value());
148 }
149 else {
150 msg["from"]["participant_id"] =
151 std::to_string(from.value().id().value());
152 }
153 }
154 else if (from.value().type_name() == "lambda") {
155 msg["from"]["participant_id"] =
156 std::to_string(from.value().id().value());
157 }
158
159 if (to.value().type_name() == "method") {
160 const auto &class_participant =
161 model().get_participant<model::method>(to.value().id()).value();
162
163 msg["to"]["participant_id"] =
164 std::to_string(class_participant.class_id().value());
165 }
166 else if (to.value().type_name() == "objc_method") {
167 const auto &class_participant =
168 model()
169 .get_participant<model::objc_method>(to.value().id())
170 .value();
171
172 msg["to"]["participant_id"] =
173 std::to_string(class_participant.class_id().value());
174 }
175 else if (to.value().type_name() == "function" ||
176 to.value().type_name() == "function_template") {
177 if (config().combine_free_functions_into_file_participants()) {
178 const auto &file_participant =
179 model()
180 .get_participant<model::function>(to.value().id())
181 .value();
182 msg["to"]["participant_id"] = std::to_string(
183 common::to_id(file_participant.file_relative()).value());
184 }
185 else {
186 msg["to"]["participant_id"] =
187 std::to_string(to.value().id().value());
188 }
189 }
190 else if (to.value().type_name() == "lambda") {
191 msg["to"]["participant_id"] = std::to_string(to.value().id().value());
192 }
193
194 msg["source_location"] =
195 dynamic_cast<const clanguml::common::model::source_location &>(m);
196
197 msg["scope"] = to_string(m.message_scope());
198 msg["return_type"] = config().simplify_template_type(m.return_type());
199
200 parent["messages"].push_back(std::move(msg));
201
202 LOG_DBG("Generated call '{}' from {} [{}] to {} [{}]", message,
203 from.value().full_name(false), m.from(), to.value().full_name(false),
204 m.to());
205}
206
208 const activity &a, std::vector<eid_t> &visited) const
209{
210 // Generate calls from this activity to other activities
211 for (const auto &m : a.messages()) {
212 switch (m.type()) {
213 case message_t::kCall:
214 process_call_message(m, visited);
215 break;
216 case message_t::kIf:
218 break;
219 case message_t::kElseIf:
220 case message_t::kElse:
222 break;
223 case message_t::kIfEnd:
225 break;
226 case message_t::kWhile:
228 break;
229 case message_t::kWhileEnd:
231 break;
232 case message_t::kFor:
234 break;
235 case message_t::kForEnd:
237 break;
238 case message_t::kDo:
240 break;
241 case message_t::kDoEnd:
243 break;
244 case message_t::kTry:
246 break;
247 case message_t::kCatch:
249 break;
250 case message_t::kTryEnd:
252 break;
253 case message_t::kSwitch:
255 break;
256 case message_t::kCase:
258 break;
259 case message_t::kSwitchEnd:
261 break;
262 case message_t::kConditional:
264 break;
265 case message_t::kConditionalElse:
267 break;
268 case message_t::kConditionalEnd:
270 break;
271 case message_t::kNone:
272 case message_t::kReturn:; // noop
273 }
274 }
275}
276
278{
279 assert(!block_statements_stack_.empty());
280
281 return block_statements_stack_.back().get();
282}
283
285 const model::message &m, std::vector<eid_t> &visited) const
286{
287 visited.push_back(m.from());
288
291 visited.pop_back();
292 return;
293 }
294
296 }
297
298 LOG_DBG("Generating message {} --> {}", m.from(), m.to());
299
301
302 if (model().sequences().find(m.to()) != model().sequences().end()) {
303 if (std::find(visited.begin(), visited.end(), m.to()) ==
304 visited.end()) { // break infinite recursion on recursive calls
305
306 LOG_DBG("Creating activity {} --> {} - missing sequence {}",
307 m.from(), m.to(), m.to());
308
309 generate_activity(model().get_activity(m.to()), visited);
310 }
311 }
312 else
313 LOG_DBG("Skipping activity {} --> {} - missing sequence {}", m.from(),
314 m.to(), m.to());
315
316 visited.pop_back();
317}
318
320{
321 nlohmann::json while_block;
322 while_block["type"] = "loop";
323 while_block["name"] = "while";
324 while_block["activity_id"] = std::to_string(m.from().value());
325 if (auto text = m.condition_text(); text.has_value())
326 while_block["condition_text"] = *text;
327
328 current_block_statement()["messages"].push_back(std::move(while_block));
329
330 block_statements_stack_.push_back(
331 std::ref(current_block_statement()["messages"].back()));
332}
333
335{
336 // Remove the while statement block from the stack
337 block_statements_stack_.pop_back();
338}
339
341{
342 nlohmann::json for_block;
343 for_block["type"] = "loop";
344 for_block["name"] = "for";
345 for_block["activity_id"] = std::to_string(m.from().value());
346 if (auto text = m.condition_text(); text.has_value())
347 for_block["condition_text"] = *text;
348
349 current_block_statement()["messages"].push_back(std::move(for_block));
350
351 block_statements_stack_.push_back(
352 std::ref(current_block_statement()["messages"].back()));
353}
354
356{
357 // Remove the while statement block from the stack
358 block_statements_stack_.pop_back();
359}
360
362{
363 nlohmann::json do_block;
364 do_block["type"] = "loop";
365 do_block["name"] = "do";
366 do_block["activity_id"] = std::to_string(m.from().value());
367 if (auto text = m.condition_text(); text.has_value())
368 do_block["condition_text"] = *text;
369
370 current_block_statement()["messages"].push_back(std::move(do_block));
371
372 block_statements_stack_.push_back(
373 std::ref(current_block_statement()["messages"].back()));
374}
375
377{
378 // Remove the do statement block from the stack
379 block_statements_stack_.pop_back();
380}
381
383{
384 nlohmann::json try_block;
385 try_block["type"] = "break";
386 try_block["name"] = "try";
387 try_block["activity_id"] = std::to_string(m.from().value());
388
389 current_block_statement()["messages"].push_back(std::move(try_block));
390
391 block_statements_stack_.push_back(
392 std::ref(current_block_statement()["messages"].back()));
393
394 nlohmann::json branch;
395 branch["type"] = "main";
396 current_block_statement()["branches"].push_back(std::move(branch));
397
398 block_statements_stack_.push_back(
399 std::ref(current_block_statement()["branches"].back()));
400}
401
403{
404 // remove previous block from the stack
405 block_statements_stack_.pop_back();
406
407 nlohmann::json branch;
408 branch["type"] = "catch";
409 current_block_statement()["branches"].push_back(std::move(branch));
410
411 block_statements_stack_.push_back(
412 std::ref(current_block_statement()["branches"].back()));
413}
414
416{
417 // Remove last if block from the stack
418 block_statements_stack_.pop_back();
419
420 // Remove the try statement block from the stack
421 block_statements_stack_.pop_back();
422}
423
425{
426 nlohmann::json if_block;
427 if_block["type"] = "alt";
428 if_block["name"] = "switch";
429 if_block["activity_id"] = std::to_string(m.from().value());
430
431 current_block_statement()["messages"].push_back(std::move(if_block));
432
433 block_statements_stack_.push_back(
434 std::ref(current_block_statement()["messages"].back()));
435}
436
438{
439 if (current_block_statement()["type"] == "case")
440 block_statements_stack_.pop_back();
441
442 nlohmann::json case_block;
443 case_block["type"] = "case";
444 case_block["name"] = m.message_name();
445 current_block_statement()["branches"].push_back(std::move(case_block));
446
447 block_statements_stack_.push_back(
448 std::ref(current_block_statement()["branches"].back()));
449}
450
452{ // Remove last case block from the stack
453 block_statements_stack_.pop_back();
454
455 // Remove the switch statement block from the stack
456 block_statements_stack_.pop_back();
457}
458
460{
461 nlohmann::json if_block;
462 if_block["type"] = "alt";
463 if_block["name"] = "conditional";
464 if_block["activity_id"] = std::to_string(m.from().value());
465 if (auto text = m.condition_text(); text.has_value())
466 if_block["condition_text"] = *text;
467
468 current_block_statement()["messages"].push_back(std::move(if_block));
469
470 block_statements_stack_.push_back(
471 std::ref(current_block_statement()["messages"].back()));
472
473 nlohmann::json branch;
474 branch["type"] = "consequent";
475 current_block_statement()["branches"].push_back(std::move(branch));
476
477 block_statements_stack_.push_back(
478 std::ref(current_block_statement()["branches"].back()));
479}
480
482{
483 // remove previous branch from the stack
484 block_statements_stack_.pop_back();
485
486 nlohmann::json branch;
487 branch["type"] = "alternative";
488 if (auto text = m.condition_text(); text.has_value())
489 branch["condition_text"] = *text;
490 current_block_statement()["branches"].push_back(std::move(branch));
491
492 block_statements_stack_.push_back(
493 std::ref(current_block_statement()["branches"].back()));
494}
495
497{
498 // Remove last if branch from the stack
499 block_statements_stack_.pop_back();
500
501 // Remove the if statement block from the stack
502 block_statements_stack_.pop_back();
503}
504
506{
507 // Remove last if branch from the stack
508 block_statements_stack_.pop_back();
509
510 // Remove the if statement block from the stack
511 block_statements_stack_.pop_back();
512}
513
515{
516 // remove previous branch from the stack
517 block_statements_stack_.pop_back();
518
519 nlohmann::json branch;
520 branch["type"] = "alternative";
521 current_block_statement()["branches"].push_back(std::move(branch));
522
523 block_statements_stack_.push_back(
524 std::ref(current_block_statement()["branches"].back()));
525}
526
528{
529 nlohmann::json if_block;
530 if_block["type"] = "alt";
531 if_block["name"] = "if";
532 if_block["activity_id"] = std::to_string(m.from().value());
533 if (auto text = m.condition_text(); text.has_value())
534 if_block["condition_text"] = *text;
535
536 current_block_statement()["messages"].push_back(std::move(if_block));
537
538 block_statements_stack_.push_back(
539 std::ref(current_block_statement()["messages"].back()));
540
541 nlohmann::json branch;
542 branch["type"] = "consequent";
543 current_block_statement()["branches"].push_back(std::move(branch));
544
545 block_statements_stack_.push_back(
546 std::ref(current_block_statement()["branches"].back()));
547}
548
550 nlohmann::json &parent, const std::string &name) const
551{
552 auto p = model().get(name);
553
554 if (!p.has_value()) {
555 LOG_WARN("Cannot find participant {} from `participants_order` option",
556 name);
557 return;
558 }
559
560 generate_participant(parent, p.value().id(), true);
561}
562
564 nlohmann::json & /*parent*/, eid_t id, bool force) const
565{
566 std::optional<eid_t> participant_id{};
567
568 if (!force) {
569 for (const auto pid : model().active_participants()) {
570 if (pid == id) {
571 participant_id = pid;
572 break;
573 }
574 }
575 }
576 else
577 participant_id = id;
578
579 if (!participant_id.has_value())
580 return participant_id;
581
582 if (is_participant_generated(*participant_id))
583 return participant_id;
584
585 const auto &participant =
586 model().get_participant<model::participant>(*participant_id).value();
587
588 const auto participant_type = participant.type_name();
589
590 if (participant_type == "method") {
591 auto class_participant_id =
592 model()
593 .get_participant<model::method>(*participant_id)
594 .value()
595 .class_id();
596
597 LOG_DBG("Generating JSON method participant: {}",
598 model()
599 .get_participant<model::method>(*participant_id)
600 .value()
601 .full_name(false));
602
603 if (!is_participant_generated(class_participant_id)) {
604 const auto &class_participant =
605 model()
606 .get_participant<model::participant>(class_participant_id)
607 .value();
608
609 generated_participants_.emplace(*participant_id);
610 generated_participants_.emplace(class_participant_id);
611
612 json_["participants"].push_back(class_participant);
613 json_["participants"].back()["activities"].push_back(participant);
614
615 // Perform config dependent postprocessing on generated class
616 const auto class_participant_full_name =
617 display_name_adapter(class_participant).full_name(false);
618
619 json_["participants"].back().at("display_name") =
620 make_display_name(class_participant_full_name);
621
622 return class_participant_id;
623 }
624
625 if (!is_participant_generated(*participant_id)) {
626 for (auto &p : json_["participants"]) {
627 if (p.at("id") ==
628 std::to_string(class_participant_id.value())) {
629 generated_participants_.emplace(*participant_id);
630 p["activities"].push_back(participant);
631 return class_participant_id;
632 }
633 }
634 }
635 }
636 if (participant_type == "objc_method") {
637 auto class_participant_id =
638 model()
639 .get_participant<model::objc_method>(*participant_id)
640 .value()
641 .class_id();
642
643 LOG_DBG("Generating JSON ObjC method participant: {}",
644 model()
645 .get_participant<model::objc_method>(*participant_id)
646 .value()
647 .full_name(false));
648
649 if (!is_participant_generated(class_participant_id)) {
650 const auto &class_participant =
651 model()
652 .get_participant<model::participant>(class_participant_id)
653 .value();
654
655 generated_participants_.emplace(*participant_id);
656 generated_participants_.emplace(class_participant_id);
657
658 json_["participants"].push_back(class_participant);
659 json_["participants"].back()["activities"].push_back(participant);
660
661 // Perform config dependent postprocessing on generated class
662 const auto class_participant_full_name =
663 display_name_adapter(class_participant).full_name(false);
664
665 json_["participants"].back().at("display_name") =
666 make_display_name(class_participant_full_name);
667
668 return class_participant_id;
669 }
670
671 if (!is_participant_generated(*participant_id)) {
672 for (auto &p : json_["participants"]) {
673 if (p.at("id") ==
674 std::to_string(class_participant_id.value())) {
675 generated_participants_.emplace(*participant_id);
676 p["activities"].push_back(participant);
677 return class_participant_id;
678 }
679 }
680 }
681 }
682 else if ((participant_type == "function" ||
683 participant_type == "function_template") &&
684 config().combine_free_functions_into_file_participants()) {
685 // Create a single participant for all functions declared in a
686 // single file
687 // participant_id will become activity_id within a file participant
688 const auto &function_participant =
689 model().get_participant<model::function>(*participant_id).value();
690
691 const auto file_participant_id =
692 common::to_id(function_participant.file_relative());
693
694 if (!is_participant_generated(file_participant_id)) {
695 nlohmann::json p = function_participant;
696
697 const auto file_path =
698 config().make_path_relative(function_participant.file());
699
700 p["display_name"] = util::path_to_url(file_path.string());
701 p["name"] = file_path.filename();
702
703 if (is_participant_generated(file_participant_id))
704 return participant_id;
705
706 p["id"] = std::to_string(file_participant_id.value());
707 p["type"] = "file";
708 p.erase("source_location");
709
710 generated_participants_.emplace(participant_id.value());
711
712 p["activities"].push_back(participant);
713 json_["participants"].push_back(p);
714
715 generated_participants_.emplace(file_participant_id);
716
717 return file_participant_id;
718 }
719
720 if (!is_participant_generated(*participant_id)) {
721 for (auto &p : json_["participants"]) {
722 if (p.at("id") == std::to_string(file_participant_id.value())) {
723 generated_participants_.emplace(*participant_id);
724 p["activities"].push_back(participant);
725 }
726 }
727 }
728
729 return file_participant_id;
730 }
731 else {
732 json_["participants"].push_back(participant);
733 const auto function_participant_full_name =
734 display_name_adapter(participant).full_name(false);
735
736 json_["participants"].back().at("display_name") =
737 make_display_name(function_participant_full_name);
738 }
739
740 generated_participants_.emplace(*participant_id);
741
742 return participant_id;
743}
744
746{
747 return std::find(generated_participants_.begin(),
749 id) != generated_participants_.end();
750}
751
752void generator::generate_diagram(nlohmann::json &parent) const
753{
754 model().print();
755
756 if (config().using_namespace)
757 parent["using_namespace"] = config().using_namespace().to_string();
758
759 if (config().participants_order.has_value) {
760 for (const auto &p : config().participants_order()) {
761 LOG_DBG("Pregenerating participant {}", p);
763 }
764 }
765
767
768 generate_to_sequences(parent);
769
771
772 // Perform config dependent postprocessing on generated participants
773 for (auto &p : json_["participants"]) {
774 if (p.contains("display_name")) {
775 p["display_name"] = make_display_name(p["display_name"]);
776 }
777 }
778
779 parent["participants"] = json_["participants"];
780}
781
782void generator::generate_from_sequences(nlohmann::json &parent) const
783{
784 std::vector<eid_t> start_from = find_from_activities();
785
786 // Use this to break out of recurrent loops
787 std::vector<eid_t> visited_participants;
788 for (const auto from_id : start_from) {
789
790 const auto &from = model().get_participant<model::function>(from_id);
791
792 if (!from.has_value()) {
793 LOG_WARN("Failed to find participant {} for 'from' "
794 "condition");
795 continue;
796 }
797
798 generate_participant(json_, from_id);
799
800 [[maybe_unused]] model::function::message_render_mode render_mode =
802
803 nlohmann::json sequence;
804 sequence["from"]["location"] = from.value().full_name(false);
805 sequence["from"]["id"] = std::to_string(from_id.value());
806
807 block_statements_stack_.push_back(std::ref(sequence));
808
809 generate_activity(model().get_activity(from_id), visited_participants);
810
811 block_statements_stack_.pop_back();
812
813 if (from.value().type_name() == "method" ||
814 config().combine_free_functions_into_file_participants()) {
815
816 sequence["return_type"] =
817 make_display_name(from.value().return_type());
818 }
819
820 parent["sequences"].push_back(std::move(sequence));
821 }
822}
823
824void generator::generate_to_sequences(nlohmann::json &parent) const
825{
826 for (const auto &to_location : config().to()) {
827 auto to_activity_ids = model().get_to_activity_ids(to_location);
828
829 if (to_activity_ids.empty()) {
830 LOG_WARN("Failed to find participant matching '{}' for "
831 "'to' condition: ",
832 to_location.location.to_string());
833 }
834
835 for (const auto to_activity_id : to_activity_ids) {
836 const auto &to =
837 model().get_participant<model::function>(to_activity_id);
838
839 nlohmann::json sequence;
840 sequence["to"]["location"] = to.value().full_name(false);
841 sequence["to"]["id"] = std::to_string(to_activity_id.value());
842 sequence["message_chains"] = nlohmann::json::array();
843
844 block_statements_stack_.push_back(std::ref(sequence));
845
846 std::vector<model::message_chain_t> message_chains =
847 model().get_all_from_to_message_chains(eid_t{}, to_activity_id);
848
849 for (const auto &mc : message_chains) {
850 const auto from_activity_id = mc.front().from();
851
852 if (model().participants().count(from_activity_id) == 0)
853 continue;
854
855 nlohmann::json message_chain;
856
857 block_statements_stack_.push_back(std::ref(message_chain));
858
859 for (const auto &m : mc) {
861 }
862
863 block_statements_stack_.pop_back();
864
865 sequence["message_chains"].push_back(std::move(message_chain));
866 }
867
868 block_statements_stack_.pop_back();
869
870 parent["sequences"].push_back(std::move(sequence));
871 }
872 }
873}
874
875void generator::generate_from_to_sequences(nlohmann::json &parent) const
876{
877 for (const auto &ft : config().from_to()) {
878 // First, find the sequence of activities from 'from' location
879 // to 'to' location
880 assert(ft.size() == 2);
881
882 const auto &from_location = ft.front();
883 const auto &to_location = ft.back();
884
885 auto from_activity_ids = model().get_from_activity_ids(from_location);
886 auto to_activity_ids = model().get_to_activity_ids(to_location);
887
888 if (from_activity_ids.empty()) {
890 model().name(),
891 fmt::format("Failed to find participant matching '{}' for "
892 "'from' condition: ",
893 from_location.location.to_string()));
894 }
895
896 if (from_activity_ids.empty() || to_activity_ids.empty()) {
898 model().name(),
899 fmt::format("Failed to find participant matching '{}' for "
900 "'to' condition: ",
901 to_location.location.to_string()));
902 }
903
904 for (const auto from_activity_id : from_activity_ids) {
905 if (model().participants().count(from_activity_id) == 0)
906 continue;
907
908 const auto &from =
909 model().get_participant<model::function>(from_activity_id);
910
911 for (const auto to_activity_id : to_activity_ids) {
912 if (model().participants().count(to_activity_id) == 0)
913 continue;
914
915 const auto &to =
916 model().get_participant<model::function>(to_activity_id);
917
918 auto message_chains_unique =
919 model().get_all_from_to_message_chains(
920 from_activity_id, to_activity_id);
921
922 nlohmann::json sequence;
923 sequence["from_to"]["from"]["location"] =
924 from.value().full_name(false);
925 sequence["from_to"]["from"]["id"] =
926 std::to_string(from_activity_id.value());
927 sequence["from_to"]["to"]["location"] =
928 to.value().full_name(false);
929 sequence["from_to"]["to"]["id"] =
930 std::to_string(to_activity_id.value());
931
932 block_statements_stack_.push_back(std::ref(sequence));
933
934 sequence["message_chains"] = nlohmann::json::array();
935
936 for (const auto &mc : message_chains_unique) {
937 nlohmann::json message_chain;
938
939 block_statements_stack_.push_back(std::ref(message_chain));
940
941 for (const auto &m : mc) {
943 }
944
945 block_statements_stack_.pop_back();
946
947 sequence["message_chains"].push_back(
948 std::move(message_chain));
949 }
950
951 block_statements_stack_.pop_back();
952
953 parent["sequences"].push_back(std::move(sequence));
954 }
955 }
956 }
957}
958
959std::vector<eid_t> generator::find_from_activities() const
960{
961 std::vector<eid_t> start_from;
962 for (const auto &sf : config().from()) {
963 if (sf.location_type == location_t::function) {
964 bool found{false};
965 for (const auto &[k, v] : model().sequences()) {
966 if (model().participants().count(v.from()) == 0)
967 continue;
968
969 const auto &caller = *model().participants().at(v.from());
970 std::string vfrom = caller.full_name(false);
971 if (sf.location == vfrom) {
972 LOG_DBG("Found sequence diagram start point: {}", k);
973 start_from.push_back(k);
974 found = true;
975 }
976 }
977
978 if (!found)
980 model().name(),
981 fmt::format("Failed to find participant matching '{}' for "
982 "'from' condition: ",
983 sf.location.to_string()));
984 }
985 }
986
987 return start_from;
988}
989
990std::string generator::make_display_name(const std::string &full_name) const
991{
992 auto result = config().simplify_template_type(full_name);
993 result = config().using_namespace().relative(result);
995
996 return result;
997}
998
999} // namespace clanguml::sequence_diagram::generators::json