23#include <inja/inja.hpp>
29 , together_group_stack_{!config.generate_packages()}
42 if (maybe_link_pattern) {
43 const auto &[link_prefix, link_pattern] = *maybe_link_pattern;
48 ostr <<
env().render(std::string_view{link_pattern},
context);
53 if (maybe_tooltip_pattern) {
54 const auto &[tooltip_prefix, tooltip_pattern] = *maybe_tooltip_pattern;
58 ostr <<
env().render(std::string_view{tooltip_pattern}, ec);
73 std::string class_type{
"class"};
75 class_type =
"abstract";
77 std::string full_name;
78 if (!
config().generate_fully_qualified_name())
83 assert(!full_name.empty());
87 ostr << class_type <<
" \""
90 ostr <<
"\" as " << c.
alias() <<
'\n';
100 if (!
config().generate_fully_qualified_name())
102 <<
" \"" << e.
name();
107 ostr <<
"\" as " << e.
alias() <<
'\n';
117 if (!
config().generate_fully_qualified_name())
124 ostr <<
"\" as " << c.
alias() <<
'\n';
132 std::string class_type{
"class"};
134 class_type =
"abstract";
136 ostr << class_type <<
" " << c.
alias();
142 if (
config().generate_links) {
148 ostr <<
" {" <<
'\n';
164 std::set<std::string> rendered_relations;
166 std::stringstream all_relations_str;
172 LOG_DBG(
"Skipping {} relation from {} to {} due "
174 to_string(r.type()), c.
full_name(), r.destination(), e.what());
181 std::vector<clanguml::class_diagram::model::class_member> members{
189 for (
const auto &m : members) {
190 if (!
config().include_relations_also_as_members() &&
191 rendered_relations.find(m.name()) != rendered_relations.end())
203 for (
const auto &member : c.
members())
206 for (
const auto &method : c.
methods())
213 bool is_first_non_empty_group{
true};
218 if (!is_first_non_empty_group)
220 is_first_non_empty_group =
false;
227 const std::vector<class_method> &methods, std::ostream &ostr)
const
229 auto sorted_methods = methods;
232 for (
const auto &m : sorted_methods) {
239 const std::vector<class_method> &methods)
const
241 std::map<std::string, std::vector<class_method>> result;
247 for (
const auto &m : methods) {
248 if (m.is_constructor() || m.is_destructor()) {
249 result[
"constructors"].push_back(m);
251 else if (m.is_copy_assignment() || m.is_move_assignment()) {
252 result[
"assignment"].push_back(m);
254 else if (m.is_operator()) {
255 result[
"operators"].push_back(m);
258 result[
"other"].push_back(m);
269 const auto &uns =
config().using_namespace();
271 constexpr auto kAbbreviatedMethodArgumentsLength{15};
276 ostr <<
"{abstract} ";
281 std::string type{uns.relative(
config().simplify_template_type(m.
type()))};
283 ostr << plantuml_common::to_plantuml(m.
access()) << m.
name();
290 if (
config().generate_method_arguments() !=
292 std::vector<std::string> params;
294 std::back_inserter(params), [
this](
const auto &mp) {
295 return config().simplify_template_type(
296 mp.to_string(config().using_namespace()));
298 auto args_string = fmt::format(
"{}", fmt::join(params,
", "));
299 if (
config().generate_method_arguments() ==
302 args_string, kAbbreviatedMethodArgumentsLength);
309 ostr <<
" constexpr";
311 ostr <<
" consteval";
325 ostr <<
" = default";
327 ostr <<
" = deleted";
330 ostr <<
" [coroutine]";
332 ostr <<
" : " << type;
334 if (
config().generate_links) {
343 const auto &uns =
config().using_namespace();
350 ostr << plantuml_common::to_plantuml(m.
access()) << m.
name() <<
" : "
352 uns.relative(
config().simplify_template_type(m.
type())));
354 if (
config().generate_links) {
361 std::string class_type{
"class"};
363 ostr << class_type <<
" " << c.
alias() <<
" <<concept>>";
365 if (
config().generate_links) {
371 ostr <<
" {" <<
'\n';
373 if (
config().generate_concept_requirements() &&
375 std::vector<std::string> parameters;
378 parameters.emplace_back(p.to_string(
config().using_namespace()));
381 ostr << fmt::format(
"({})\n", fmt::join(parameters,
","));
394 for (
const auto &decorator : member.
decorators()) {
395 auto note = std::dynamic_pointer_cast<decorators::note>(decorator);
396 if (note && note->applies_to_diagram(
config().name)) {
397 ostr <<
"note " << note->position <<
" of " << alias
398 <<
"::" << member.
name() <<
'\n'
399 << note->text <<
'\n'
407 for (
const auto &p :
model()) {
408 if (
auto *pkg =
dynamic_cast<package *
>(p.get()); pkg) {
411 else if (
auto *cls =
dynamic_cast<class_ *
>(p.get()); cls) {
414 else if (
auto *enm =
dynamic_cast<enum_ *
>(p.get()); enm) {
417 else if (
auto *cpt =
dynamic_cast<concept_ *
>(p.get()); cpt) {
424 const relationship &r, std::set<std::string> &rendered_relations)
const
428 LOG_DBG(
"Processing relationship {}", to_string(r.
type()));
430 std::string destination;
433 if (!target_element.has_value())
435 "Missing element in the model for ID: {}", r.
destination())};
437 destination = target_element.value().full_name(
false);
440 destination = destination.substr(2, destination.size());
442 std::string puml_relation;
446 puml_relation += plantuml_common::to_plantuml(r,
config());
451 if (!r.
label().empty()) {
452 rendered_relations.emplace(r.
label());
457 const class_ &c, std::ostream &ostr)
const
464 std::set<std::string> rendered_relations;
466 std::stringstream all_relations_str;
467 std::set<std::string> unique_relations;
470 LOG_DBG(
"== Processing relationship {}",
471 plantuml_common::to_plantuml(r,
config()));
473 std::stringstream relstr;
476 destination = r.destination();
478 std::string puml_relation;
479 if (!r.multiplicity_source().empty())
480 puml_relation +=
"\"" + r.multiplicity_source() +
"\" ";
482 puml_relation += plantuml_common::to_plantuml(r,
config());
484 if (!r.multiplicity_destination().empty())
485 puml_relation +=
" \"" + r.multiplicity_destination() +
"\"";
487 std::string target_alias;
489 target_alias =
model().to_alias(destination);
492 LOG_DBG(
"Failed to find alias to {}", destination);
500 relstr << c.
alias() <<
" " << puml_relation <<
" " << target_alias;
502 if (!r.label().empty()) {
503 relstr <<
" : " << plantuml_common::to_plantuml(r.access())
505 rendered_relations.emplace(r.label());
508 if (unique_relations.count(relstr.str()) == 0) {
509 unique_relations.emplace(relstr.str());
513 LOG_DBG(
"=== Adding relation {}", relstr.str());
515 all_relations_str << relstr.str();
519 LOG_DBG(
"=== Skipping {} relation from {} to {} due "
521 to_string(r.type()), c.
full_name(), destination, e.what());
525 if (
model().should_include(relationship_t::kExtension)) {
526 for (
const auto &b : c.
parents()) {
527 std::stringstream relstr;
529 auto target_alias =
model().to_alias(b.id());
535 relstr << target_alias <<
" <|-- " << c.
alias() <<
'\n';
536 all_relations_str << relstr.str();
539 LOG_DBG(
"=== Skipping inheritance relation from {} to {} due "
541 b.name(), c.
name(), e.what());
546 ostr << all_relations_str.str();
550 const concept_ &c, std::ostream &ostr)
const
557 std::set<std::string> rendered_relations;
559 std::stringstream all_relations_str;
560 std::set<std::string> unique_relations;
563 if (!
model().should_include(r.type()))
566 LOG_DBG(
"== Processing relationship {}", to_string(r.type()));
568 std::stringstream relstr;
571 destination = r.destination();
573 std::string puml_relation;
574 if (!r.multiplicity_source().empty())
575 puml_relation +=
"\"" + r.multiplicity_source() +
"\" ";
577 puml_relation += plantuml_common::to_plantuml(r,
config());
579 if (!r.multiplicity_destination().empty())
580 puml_relation +=
" \"" + r.multiplicity_destination() +
"\"";
582 std::string target_alias;
584 target_alias =
model().to_alias(destination);
587 LOG_DBG(
"Failed to find alias to {}", destination);
595 relstr << c.
alias() <<
" " << puml_relation <<
" " << target_alias;
597 if (!r.label().empty()) {
598 relstr <<
" : " << plantuml_common::to_plantuml(r.access())
600 rendered_relations.emplace(r.label());
603 if (unique_relations.count(relstr.str()) == 0) {
604 unique_relations.emplace(relstr.str());
608 LOG_DBG(
"=== Adding relation {}", relstr.str());
610 all_relations_str << relstr.str();
614 LOG_DBG(
"=== Skipping {} relation from {} to {} due "
616 to_string(r.type()), c.
full_name(), destination, e.what());
620 ostr << all_relations_str.str();
625 ostr <<
"enum " << e.
alias();
627 if (
config().generate_links) {
633 ostr <<
" {" <<
'\n';
635 for (
const auto &enum_constant : e.
constants()) {
636 ostr << enum_constant <<
'\n';
648 std::stringstream relstr;
650 destination = r.destination();
652 auto target_alias =
model().to_alias(destination);
658 relstr << e.
alias() <<
" "
661 <<
" " << target_alias;
663 if (!r.label().empty())
664 relstr <<
" : " << r.label();
668 ostr << relstr.str();
671 LOG_DBG(
"Skipping {} relation from {} to {} due "
682 const auto &uns =
config().using_namespace();
684 if (
config().generate_packages()) {
689 if (!uns.starts_with({p.full_name(false)})) {
691 ostr <<
"package [" << p.
name() <<
"] ";
692 ostr <<
"as " << p.
alias();
695 ostr <<
" <<deprecated>>";
699 ostr <<
" {" <<
'\n';
703 for (
const auto &subpackage : p) {
704 if (
dynamic_cast<package *
>(subpackage.get()) !=
nullptr) {
706 const auto &sp =
dynamic_cast<package &
>(*subpackage);
707 if (!sp.is_empty()) {
715 else if (
auto *cls =
dynamic_cast<class_ *
>(subpackage.get()); cls) {
716 if (
model().should_include(*subpackage)) {
717 auto together_group =
718 config().get_together_group(cls->full_name(
false));
719 if (together_group) {
721 together_group.value(), cls);
729 else if (
auto *enm =
dynamic_cast<enum_ *
>(subpackage.get()); enm) {
730 if (
model().should_include(*subpackage)) {
731 auto together_group =
732 config().get_together_group(subpackage->full_name(
false));
733 if (together_group) {
735 together_group.value(), enm);
743 else if (
auto *cpt =
dynamic_cast<concept_ *
>(subpackage.get()); cpt) {
744 if (
model().should_include(*subpackage)) {
745 auto together_group =
746 config().get_together_group(cpt->full_name(
false));
747 if (together_group) {
749 together_group.value(), cpt);
759 if (
config().generate_packages()) {
762 for (
const auto &[group_name, group_elements] :
764 ostr <<
"together {\n";
766 for (
auto *e : group_elements) {
767 if (
auto *cls =
dynamic_cast<class_ *
>(e); cls) {
771 if (
auto *enm =
dynamic_cast<enum_ *
>(e); enm) {
775 if (
auto *cpt =
dynamic_cast<concept_ *
>(e); cpt) {
786 if (!uns.starts_with({p.full_name(false)})) {
794 const package &p, std::ostream &ostr)
const
796 for (
const auto &subpackage : p) {
797 if (
dynamic_cast<package *
>(subpackage.get()) !=
nullptr) {
801 const auto &sp =
dynamic_cast<package &
>(*subpackage);
805 else if (
dynamic_cast<class_ *
>(subpackage.get()) !=
nullptr) {
806 if (
model().should_include(*subpackage)) {
808 dynamic_cast<class_ &
>(*subpackage), ostr);
811 else if (
dynamic_cast<enum_ *
>(subpackage.get()) !=
nullptr) {
812 if (
model().should_include(*subpackage)) {
814 dynamic_cast<enum_ &
>(*subpackage), ostr);
817 else if (
dynamic_cast<concept_ *
>(subpackage.get()) !=
nullptr) {
818 if (
model().should_include(*subpackage)) {
820 dynamic_cast<concept_ &
>(*subpackage), ostr);
839 for (
const auto &p :
model()) {
840 if (
auto *pkg =
dynamic_cast<package *
>(p.get()); pkg) {
841 if (!pkg->is_empty())
844 else if (
auto *cls =
dynamic_cast<class_ *
>(p.get()); cls) {
845 auto together_group =
846 config().get_together_group(cls->full_name(
false));
847 if (together_group) {
849 together_group.value(), cls);
856 else if (
auto *enm =
dynamic_cast<enum_ *
>(p.get()); enm) {
857 auto together_group =
858 config().get_together_group(enm->full_name(
false));
859 if (together_group) {
861 together_group.value(), enm);
868 else if (
auto *cpt =
dynamic_cast<concept_ *
>(p.get()); cpt) {
869 auto together_group =
870 config().get_together_group(cpt->full_name(
false));
871 if (together_group) {
873 together_group.value(), cpt);
885 for (
const auto &[group_name, group_elements] :
887 ostr <<
"together {\n";
889 for (
auto *e : group_elements) {
890 if (
auto *cls =
dynamic_cast<class_ *
>(e); cls) {
894 if (
auto *enm =
dynamic_cast<enum_ *
>(e); enm) {
898 if (
auto *cpt =
dynamic_cast<concept_ *
>(e); cpt) {