diff --git a/src/class_diagram/generators/mermaid/class_diagram_generator.cc b/src/class_diagram/generators/mermaid/class_diagram_generator.cc index 8b9b21f2..6503cc37 100644 --- a/src/class_diagram/generators/mermaid/class_diagram_generator.cc +++ b/src/class_diagram/generators/mermaid/class_diagram_generator.cc @@ -287,8 +287,8 @@ void generator::generate(const concept_ &c, std::ostream &ostr) const ostr << indent(1) << "class" << " " << c.alias(); - if (!c.style().empty()) - ostr << " " << c.style(); + if (!c.style()) + ostr << " " << c.style().value(); ostr << " {" << '\n'; ostr << indent(2) << "<>\n"; diff --git a/src/class_diagram/generators/plantuml/class_diagram_generator.cc b/src/class_diagram/generators/plantuml/class_diagram_generator.cc index 93d29bc1..c657c9a7 100644 --- a/src/class_diagram/generators/plantuml/class_diagram_generator.cc +++ b/src/class_diagram/generators/plantuml/class_diagram_generator.cc @@ -108,7 +108,7 @@ void generator::generate_alias(const concept_ &c, std::ostream &ostr) const if (config().generate_fully_qualified_name()) ostr << "class" - << " \"" << c.name(); + << " \"" << c.full_name_no_ns(); else ostr << "class" << " \"" << render_name(c.full_name()); @@ -137,8 +137,7 @@ void generator::generate(const class_ &c, std::ostream &ostr) const common_generator::generate_link(ostr, c); } - if (!c.style().empty()) - ostr << " " << c.style(); + generate_style(ostr, c.type_name(), c); ostr << " {" << '\n'; @@ -169,8 +168,7 @@ void generator::generate(const class_ &c, std::ostream &ostr) const catch (error::uml_alias_missing &e) { LOG_DBG("Skipping {} relation from {} to {} due " "to: {}", - plantuml_common::to_plantuml(r.type(), r.style()), - c.full_name(), r.destination(), e.what()); + to_string(r.type()), c.full_name(), r.destination(), e.what()); } } @@ -378,8 +376,7 @@ void generator::generate(const concept_ &c, std::ostream &ostr) const common_generator::generate_link(ostr, c); } - if (!c.style().empty()) - ostr << " " << c.style(); + generate_style(ostr, c.type_name(), c); ostr << " {" << '\n'; @@ -444,8 +441,7 @@ void generator::generate_relationship( { namespace plantuml_common = clanguml::common::generators::plantuml; - LOG_DBG("Processing relationship {}", - plantuml_common::to_plantuml(r.type(), r.style())); + LOG_DBG("Processing relationship {}", to_string(r.type())); std::string destination; @@ -463,7 +459,7 @@ void generator::generate_relationship( if (!r.multiplicity_source().empty()) puml_relation += "\"" + r.multiplicity_source() + "\" "; - puml_relation += plantuml_common::to_plantuml(r.type(), r.style()); + puml_relation += plantuml_common::to_plantuml(r, config()); if (!r.multiplicity_destination().empty()) puml_relation += " \"" + r.multiplicity_destination() + "\""; @@ -491,7 +487,7 @@ void generator::generate_relationships( continue; LOG_DBG("== Processing relationship {}", - plantuml_common::to_plantuml(r.type(), r.style())); + plantuml_common::to_plantuml(r, config())); std::stringstream relstr; clanguml::common::id_t destination{0}; @@ -502,7 +498,7 @@ void generator::generate_relationships( if (!r.multiplicity_source().empty()) puml_relation += "\"" + r.multiplicity_source() + "\" "; - puml_relation += plantuml_common::to_plantuml(r.type(), r.style()); + puml_relation += plantuml_common::to_plantuml(r, config()); if (!r.multiplicity_destination().empty()) puml_relation += " \"" + r.multiplicity_destination() + "\""; @@ -541,8 +537,7 @@ void generator::generate_relationships( catch (error::uml_alias_missing &e) { LOG_DBG("=== Skipping {} relation from {} to {} due " "to: {}", - plantuml_common::to_plantuml(r.type(), r.style()), - c.full_name(), destination, e.what()); + to_string(r.type()), c.full_name(), destination, e.what()); } } @@ -587,8 +582,7 @@ void generator::generate_relationships( if (!model().should_include(r.type())) continue; - LOG_DBG("== Processing relationship {}", - plantuml_common::to_plantuml(r.type(), r.style())); + LOG_DBG("== Processing relationship {}", to_string(r.type())); std::stringstream relstr; clanguml::common::id_t destination{0}; @@ -599,7 +593,7 @@ void generator::generate_relationships( if (!r.multiplicity_source().empty()) puml_relation += "\"" + r.multiplicity_source() + "\" "; - puml_relation += plantuml_common::to_plantuml(r.type(), r.style()); + puml_relation += plantuml_common::to_plantuml(r, config()); if (!r.multiplicity_destination().empty()) puml_relation += " \"" + r.multiplicity_destination() + "\""; @@ -638,8 +632,7 @@ void generator::generate_relationships( catch (error::uml_alias_missing &e) { LOG_DBG("=== Skipping {} relation from {} to {} due " "to: {}", - plantuml_common::to_plantuml(r.type(), r.style()), - c.full_name(), destination, e.what()); + to_string(r.type()), c.full_name(), destination, e.what()); } } @@ -654,8 +647,7 @@ void generator::generate(const enum_ &e, std::ostream &ostr) const common_generator::generate_link(ostr, e); } - if (!e.style().empty()) - ostr << " " << e.style(); + generate_style(ostr, e.type_name(), e); ostr << " {" << '\n'; @@ -687,7 +679,7 @@ void generator::generate_relationships(const enum_ &e, std::ostream &ostr) const relstr << e.alias() << " " << clanguml::common::generators::plantuml::to_plantuml( - r.type(), r.style()) + r, config()) << " " << target_alias; if (!r.label().empty()) @@ -701,7 +693,7 @@ void generator::generate_relationships(const enum_ &e, std::ostream &ostr) const LOG_DBG("Skipping {} relation from {} to {} due " "to: {}", clanguml::common::generators::plantuml::to_plantuml( - r.type(), r.style()), + r, config()), e.full_name(), destination, ex.what()); } } @@ -724,8 +716,7 @@ void generator::generate(const package &p, std::ostream &ostr) const if (p.is_deprecated()) ostr << " <>"; - if (!p.style().empty()) - ostr << " " << p.style(); + generate_style(ostr, p.type_name(), p); ostr << " {" << '\n'; } diff --git a/src/class_diagram/visitor/translation_unit_visitor.cc b/src/class_diagram/visitor/translation_unit_visitor.cc index 704abacf..23900ac8 100644 --- a/src/class_diagram/visitor/translation_unit_visitor.cc +++ b/src/class_diagram/visitor/translation_unit_visitor.cc @@ -462,12 +462,12 @@ void translation_unit_visitor::process_constraint_requirements( parm_var_decl) { parm_var_decl->getQualifiedNameAsString(); - auto param_name = parm_var_decl->getQualifiedNameAsString(); + auto param_name = parm_var_decl->getNameAsString(); auto param_type = common::to_string( parm_var_decl->getType(), cpt->getASTContext()); LOG_DBG("=== Processing parameter variable declaration: {}, {}", - param_name, param_type); + param_type, param_name); concept_model.add_parameter( {std::move(param_type), std::move(param_name)}); diff --git a/src/common/generators/mermaid/generator.cc b/src/common/generators/mermaid/generator.cc index d9b3a9c0..d9d1f749 100644 --- a/src/common/generators/mermaid/generator.cc +++ b/src/common/generators/mermaid/generator.cc @@ -19,7 +19,8 @@ namespace clanguml::common::generators::mermaid { -std::string to_mermaid(relationship_t r, const std::string & /*style*/) +std::string to_mermaid( + relationship_t r, const std::optional & /*style*/) { switch (r) { case relationship_t::kOwnership: diff --git a/src/common/generators/mermaid/generator.h b/src/common/generators/mermaid/generator.h index 93bedffc..a537bfd2 100644 --- a/src/common/generators/mermaid/generator.h +++ b/src/common/generators/mermaid/generator.h @@ -38,7 +38,8 @@ using clanguml::common::model::element; using clanguml::common::model::message_t; using clanguml::common::model::relationship_t; -std::string to_mermaid(relationship_t r, const std::string &style); +std::string to_mermaid( + relationship_t r, const std::optional &style); std::string to_mermaid(access_t scope); std::string to_mermaid(message_t r); diff --git a/src/common/generators/plantuml/generator.cc b/src/common/generators/plantuml/generator.cc index c05932d9..9b17a86e 100644 --- a/src/common/generators/plantuml/generator.cc +++ b/src/common/generators/plantuml/generator.cc @@ -19,28 +19,48 @@ namespace clanguml::common::generators::plantuml { -std::string to_plantuml(relationship_t r, const std::string &style) +std::string to_plantuml(const relationship &r, const config::diagram &cfg) { - switch (r) { + using common::model::relationship_t; + + std::string style; + + const auto &inline_style = r.style(); + + if (inline_style && !inline_style.value().empty()) { + if (inline_style && inline_style.value().back() == ']') + style = *inline_style; + else + style = fmt::format("[{}]", inline_style.value()); + } + + if (style.empty() && cfg.puml) { + if (auto config_style = cfg.puml().get_style(r.type()); + config_style.has_value()) { + style = config_style.value(); + } + } + + switch (r.type()) { case relationship_t::kOwnership: case relationship_t::kComposition: - return style.empty() ? "*--" : fmt::format("*-[{}]-", style); + return style.empty() ? "*--" : fmt::format("*-{}-", style); case relationship_t::kAggregation: - return style.empty() ? "o--" : fmt::format("o-[{}]-", style); + return style.empty() ? "o--" : fmt::format("o-{}-", style); case relationship_t::kContainment: - return style.empty() ? "--+" : fmt::format("-[{}]-+", style); + return style.empty() ? "--+" : fmt::format("-{}-+", style); case relationship_t::kAssociation: - return style.empty() ? "-->" : fmt::format("-[{}]->", style); + return style.empty() ? "-->" : fmt::format("-{}->", style); case relationship_t::kInstantiation: - return style.empty() ? "..|>" : fmt::format(".[{}].|>", style); + return style.empty() ? "..|>" : fmt::format(".{}.|>", style); case relationship_t::kFriendship: - return style.empty() ? "<.." : fmt::format("<.[{}].", style); + return style.empty() ? "<.." : fmt::format("<.{}.", style); case relationship_t::kDependency: - return style.empty() ? "..>" : fmt::format(".[{}].>", style); + return style.empty() ? "..>" : fmt::format(".{}.>", style); case relationship_t::kConstraint: - return style.empty() ? "..>" : fmt::format(".[{}].>", style); + return style.empty() ? "..>" : fmt::format(".{}.>", style); case relationship_t::kAlias: - return style.empty() ? ".." : fmt::format(".[{}].", style); + return style.empty() ? ".." : fmt::format(".{}.", style); default: return ""; } diff --git a/src/common/generators/plantuml/generator.h b/src/common/generators/plantuml/generator.h index 4a2446ee..6611336b 100644 --- a/src/common/generators/plantuml/generator.h +++ b/src/common/generators/plantuml/generator.h @@ -19,6 +19,7 @@ #include "common/generators/generator.h" #include "common/model/diagram_filter.h" +#include "common/model/relationship.h" #include "config/config.h" #include "util/error.h" #include "util/util.h" @@ -36,9 +37,9 @@ namespace clanguml::common::generators::plantuml { using clanguml::common::model::access_t; using clanguml::common::model::element; using clanguml::common::model::message_t; -using clanguml::common::model::relationship_t; +using clanguml::common::model::relationship; -std::string to_plantuml(relationship_t r, const std::string &style); +std::string to_plantuml(const relationship &r, const config::diagram &cfg); std::string to_plantuml(access_t scope); std::string to_plantuml(message_t r); @@ -124,6 +125,19 @@ public: void generate_notes( std::ostream &ostr, const model::element &element) const; + /** + * @brief Generate diagram element PlantUML style + * + * This method renders a style for a specific `el` element if specified + * in the config file or inline comment directive. + * + * @param ostr Output stream + * @param element_type Name of the element type (e.g. "class") + * @param el Reference to a stylable diagram element + */ + void generate_style(std::ostream &ostr, const std::string &element_type, + const model::stylable_element &el) const; + /** * @brief Generate comment with diagram metadata * @@ -437,6 +451,22 @@ void generator::generate_plantuml_directives( } } +template +void generator::generate_style(std::ostream &ostr, + const std::string &element_type, const model::stylable_element &el) const +{ + const auto &config = generators::generator::config(); + + if (el.style() && !el.style().value().empty()) + ostr << " " << *el.style(); + else if (config.puml) { + if (const auto config_style = config.puml().get_style(element_type); + config_style) { + ostr << " " << *config_style; + } + } +} + template void generator::generate_notes( std::ostream &ostr, const model::element &e) const diff --git a/src/common/model/diagram_element.h b/src/common/model/diagram_element.h index 8aa6264a..224181af 100644 --- a/src/common/model/diagram_element.h +++ b/src/common/model/diagram_element.h @@ -93,16 +93,16 @@ public: void set_name(const std::string &name) { name_ = name; } /** - * Return diagram's name. + * Return diagram element name. * - * @return Diagram's name. + * @return Diagram element name. */ std::string name() const { return name_; } /** - * Return the type name of the diagram. + * Return the type name of the diagram element. * - * @return Diagrams type name. + * @return Diagrams element type name. */ virtual std::string type_name() const { return "__undefined__"; }; diff --git a/src/common/model/stylable_element.cc b/src/common/model/stylable_element.cc index ea61d0ba..75517627 100644 --- a/src/common/model/stylable_element.cc +++ b/src/common/model/stylable_element.cc @@ -22,6 +22,6 @@ namespace clanguml::common::model { void stylable_element::set_style(const std::string &style) { style_ = style; } -std::string stylable_element::style() const { return style_; } +std::optional stylable_element::style() const { return style_; } } diff --git a/src/common/model/stylable_element.h b/src/common/model/stylable_element.h index 201da800..04399ea5 100644 --- a/src/common/model/stylable_element.h +++ b/src/common/model/stylable_element.h @@ -17,6 +17,7 @@ */ #pragma once +#include #include namespace clanguml::common::model { @@ -40,10 +41,10 @@ public: * * @return Style specification */ - std::string style() const; + std::optional style() const; private: - std::string style_; + std::optional style_; }; } // namespace clanguml::common::model diff --git a/src/config/config.cc b/src/config/config.cc index a12e10af..0a59d7d8 100644 --- a/src/config/config.cc +++ b/src/config/config.cc @@ -168,6 +168,24 @@ std::string to_string(member_order_t mo) } } +std::optional plantuml::get_style( + const common::model::relationship_t relationship_type) const +{ + if (style.count(to_string(relationship_type)) == 0) + return {}; + + return style.at(to_string(relationship_type)); +} + +std::optional plantuml::get_style( + const std::string &element_type) const +{ + if (style.count(element_type) == 0) + return {}; + + return style.at(element_type); +} + void plantuml::append(const plantuml &r) { before.insert(before.end(), r.before.begin(), r.before.end()); diff --git a/src/config/config.h b/src/config/config.h index 22c5ad65..dcdd58bd 100644 --- a/src/config/config.h +++ b/src/config/config.h @@ -109,6 +109,11 @@ enum class comment_parser_t { std::string to_string(comment_parser_t cp); +struct plantuml_keyword_mapping_t { + std::map> + relationships; +}; + /** * @brief PlantUML diagram config section * @@ -123,6 +128,13 @@ struct plantuml { std::vector after; /*! Command template to render diagram using PlantUML */ std::string cmd; + /*! Provide customized styles for various elements */ + std::map style; + + std::optional get_style( + const common::model::relationship_t relationship_type) const; + + std::optional get_style(const std::string &element_type) const; void append(const plantuml &r); }; diff --git a/src/config/schema.h b/src/config/schema.h index 1569a9ea..fa76104c 100644 --- a/src/config/schema.h +++ b/src/config/schema.h @@ -160,6 +160,7 @@ types: before: !optional [string] after: !optional [string] cmd: !optional string + style: !optional map_t mermaid: !optional before: !optional [string] after: !optional [string] diff --git a/src/config/yaml_decoders.cc b/src/config/yaml_decoders.cc index 3faf4347..bcf5f329 100644 --- a/src/config/yaml_decoders.cc +++ b/src/config/yaml_decoders.cc @@ -411,6 +411,9 @@ template <> struct convert { if (node["cmd"]) rhs.cmd = node["cmd"].as(); + if (node["style"]) + rhs.style = node["style"].as(); + return true; } }; diff --git a/src/include_diagram/generators/plantuml/include_diagram_generator.cc b/src/include_diagram/generators/plantuml/include_diagram_generator.cc index 9dd04e71..373607e8 100644 --- a/src/include_diagram/generators/plantuml/include_diagram_generator.cc +++ b/src/include_diagram/generators/plantuml/include_diagram_generator.cc @@ -53,7 +53,7 @@ void generator::generate_relationships( }, [&f, &ostr, this](const auto &r) { ostr << f.alias() << " " - << plantuml_common::to_plantuml(r.type(), r.style()) << " " + << plantuml_common::to_plantuml(r, config()) << " " << model().get(r.destination()).value().alias() << '\n'; }); } diff --git a/src/package_diagram/generators/plantuml/package_diagram_generator.cc b/src/package_diagram/generators/plantuml/package_diagram_generator.cc index a7e90722..998afb3f 100644 --- a/src/package_diagram/generators/plantuml/package_diagram_generator.cc +++ b/src/package_diagram/generators/plantuml/package_diagram_generator.cc @@ -89,8 +89,8 @@ void generator::generate(const package &p, std::ostream &ostr) const generate_link(ostr, p); } - if (!p.style().empty()) - ostr << " " << p.style(); + if (p.style()) + ostr << " " << p.style().value(); ostr << " {" << '\n'; } diff --git a/tests/t00009/.clang-uml b/tests/t00009/.clang-uml index 34653ebf..bb42e691 100644 --- a/tests/t00009/.clang-uml +++ b/tests/t00009/.clang-uml @@ -4,6 +4,11 @@ diagrams: glob: - t00009.cc using_namespace: clanguml::t00009 + plantuml: + style: + instantiation: up + association: up + aggregation: up include: namespaces: - clanguml::t00009 diff --git a/tests/t00009/test_case.h b/tests/t00009/test_case.h index c72a3a00..77885a25 100644 --- a/tests/t00009/test_case.h +++ b/tests/t00009/test_case.h @@ -43,15 +43,18 @@ TEST_CASE("t00009", "[test-case][class]") REQUIRE_THAT( src, (IsField("avector", "A> &"))); - REQUIRE_THAT(src, IsInstantiation(_A("A"), _A("A"))); - REQUIRE_THAT(src, IsInstantiation(_A("A"), _A("A"))); - - REQUIRE_THAT(src, IsAggregation(_A("B"), _A("A"), "+aint")); + REQUIRE_THAT(src, IsInstantiation(_A("A"), _A("A"), "up")); REQUIRE_THAT( - src, IsAssociation(_A("B"), _A("A"), "+astring")); + src, IsInstantiation(_A("A"), _A("A"), "up")); + + REQUIRE_THAT( + src, IsAggregation(_A("B"), _A("A"), "+aint", "", "", "up")); REQUIRE_THAT(src, IsAssociation( - _A("B"), _A("A>"), "+avector")); + _A("B"), _A("A"), "+astring", "", "", "up")); + REQUIRE_THAT(src, + IsAssociation(_A("B"), _A("A>"), + "+avector", "", "", "up")); save_puml(config.output_directory(), diagram->name + ".puml", src); } diff --git a/tests/t00016/.clang-uml b/tests/t00016/.clang-uml index 7b0d1b58..eb9b3051 100644 --- a/tests/t00016/.clang-uml +++ b/tests/t00016/.clang-uml @@ -4,6 +4,9 @@ diagrams: glob: - t00016.cc using_namespace: clanguml::t00016 + plantuml: + style: + instantiation: up include: namespaces: - clanguml::t00016 diff --git a/tests/t00016/test_case.h b/tests/t00016/test_case.h index 52b6bc3f..5efdbe20 100644 --- a/tests/t00016/test_case.h +++ b/tests/t00016/test_case.h @@ -41,16 +41,17 @@ TEST_CASE("t00016", "[test-case][class]") REQUIRE_THAT(src, IsClassTemplate("is_numeric", "float")); REQUIRE_THAT(src, - IsInstantiation(_A("is_numeric"), _A("is_numeric"))); + IsInstantiation( + _A("is_numeric"), _A("is_numeric"), "up")); REQUIRE_THAT(src, IsInstantiation( - _A("is_numeric"), _A("is_numeric"))); + _A("is_numeric"), _A("is_numeric"), "up")); REQUIRE_THAT(src, IsInstantiation( - _A("is_numeric"), _A("is_numeric"))); + _A("is_numeric"), _A("is_numeric"), "up")); REQUIRE_THAT(src, IsInstantiation( - _A("is_numeric"), _A("is_numeric"))); + _A("is_numeric"), _A("is_numeric"), "up")); save_puml(config.output_directory(), diagram->name + ".puml", src); } diff --git a/tests/t00019/.clang-uml b/tests/t00019/.clang-uml index 8cda1d78..6887aca0 100644 --- a/tests/t00019/.clang-uml +++ b/tests/t00019/.clang-uml @@ -5,6 +5,9 @@ diagrams: - "*.cc" generate_template_argument_dependencies: false using_namespace: clanguml::t00019 + plantuml: + style: + instantiation: up include: namespaces: - clanguml::t00019 diff --git a/tests/t00031/test_case.h b/tests/t00031/test_case.h index eb9d8c50..d4ab8482 100644 --- a/tests/t00031/test_case.h +++ b/tests/t00031/test_case.h @@ -41,18 +41,18 @@ TEST_CASE("t00031", "[test-case][class]") REQUIRE_THAT(src, IsClass(_A("D"))); REQUIRE_THAT(src, - IsAssociationWithStyle( - _A("R"), _A("A"), "+aaa", "#red,dashed,thickness=2")); + IsAssociation( + _A("R"), _A("A"), "+aaa", "", "", "[#red,dashed,thickness=2]")); REQUIRE_THAT(src, - IsCompositionWithStyle( - _A("R"), _A("B"), "+bbb", "#green,dashed,thickness=4")); + IsComposition(_A("R"), _A("B"), "+bbb", "", "", + "[#green,dashed,thickness=4]")); REQUIRE_THAT(src, IsDependency(_A("R"), _A("B"))); REQUIRE_THAT(src, - IsAggregationWithStyle( - _A("R"), _A("C"), "+ccc", "#blue,dotted,thickness=8")); + IsAggregation(_A("R"), _A("C"), "+ccc", "", "", + "[#blue,dotted,thickness=8]")); REQUIRE_THAT(src, - IsAssociationWithStyle( - _A("R"), _A("D"), "+ddd", "#blue,plain,thickness=16")); + IsAssociation(_A("R"), _A("D"), "+ddd", "", "", + "[#blue,plain,thickness=16]")); save_puml(config.output_directory(), diagram->name + ".puml", src); } diff --git a/tests/t00033/.clang-uml b/tests/t00033/.clang-uml index 27279195..5255fa07 100644 --- a/tests/t00033/.clang-uml +++ b/tests/t00033/.clang-uml @@ -4,6 +4,9 @@ diagrams: glob: - t00033.cc using_namespace: clanguml::t00033 + plantuml: + style: + instantiation: up include: namespaces: - clanguml::t00033 diff --git a/tests/t00033/test_case.h b/tests/t00033/test_case.h index 4e9a9c61..d5fdd3e7 100644 --- a/tests/t00033/test_case.h +++ b/tests/t00033/test_case.h @@ -48,11 +48,12 @@ TEST_CASE("t00033", "[test-case][class]") src, IsDependency(_A("B>>"), _A("C"))); REQUIRE_THAT(src, IsDependency(_A("C"), _A("D"))); - REQUIRE_THAT(src, IsInstantiation(_A("C"), _A("C"))); - REQUIRE_THAT( - src, IsInstantiation(_A("B"), _A("B>>"))); + REQUIRE_THAT(src, IsInstantiation(_A("C"), _A("C"), "up")); REQUIRE_THAT(src, - IsInstantiation(_A("A"), _A("A>>>"))); + IsInstantiation(_A("B"), _A("B>>"), "up")); + REQUIRE_THAT(src, + IsInstantiation( + _A("A"), _A("A>>>"), "up")); save_puml(config.output_directory(), diagram->name + ".puml", src); } diff --git a/tests/t00042/.clang-uml b/tests/t00042/.clang-uml index 8b0ee5f2..442348fe 100644 --- a/tests/t00042/.clang-uml +++ b/tests/t00042/.clang-uml @@ -5,6 +5,9 @@ diagrams: glob: - t00042.cc using_namespace: clanguml::t00042 + plantuml: + style: + instantiation: up include: specializations: - clanguml::t00042::A diff --git a/tests/t00047/.clang-uml b/tests/t00047/.clang-uml index 3dc96754..df387a8a 100644 --- a/tests/t00047/.clang-uml +++ b/tests/t00047/.clang-uml @@ -4,6 +4,9 @@ diagrams: glob: - t00047.cc using_namespace: clanguml::t00047 + plantuml: + style: + instantiation: up include: namespaces: - clanguml::t00047 \ No newline at end of file diff --git a/tests/t00059/.clang-uml b/tests/t00059/.clang-uml index 1d394b92..0c53bbc1 100644 --- a/tests/t00059/.clang-uml +++ b/tests/t00059/.clang-uml @@ -6,4 +6,11 @@ diagrams: include: namespaces: - clanguml::t00059 + plantuml: + style: + instantiation: up + association: up + aggregation: up + dependency: up + constraint: up using_namespace: clanguml::t00059 \ No newline at end of file diff --git a/tests/t00059/test_case.h b/tests/t00059/test_case.h index 3f72389b..851b1e87 100644 --- a/tests/t00059/test_case.h +++ b/tests/t00059/test_case.h @@ -40,9 +40,9 @@ TEST_CASE("t00059", "[test-case][class]") REQUIRE_THAT(src, IsConcept(_A("orange_c"))); REQUIRE_THAT( - src, IsConstraint(_A("apple_c"), _A("fruit_c"), "T")); + src, IsConstraint(_A("apple_c"), _A("fruit_c"), "T", "up")); REQUIRE_THAT( - src, IsConstraint(_A("orange_c"), _A("fruit_c"), "T")); + src, IsConstraint(_A("orange_c"), _A("fruit_c"), "T", "up")); REQUIRE_THAT( src, IsConceptRequirement(_A("apple_c"), "t.get_sweetness()")); @@ -60,31 +60,33 @@ TEST_CASE("t00059", "[test-case][class]") REQUIRE_THAT(src, IsDependency(_A("fruit_factory"), - _A("gala_apple"))); + _A("gala_apple"), "up")); REQUIRE_THAT(src, IsDependency(_A("fruit_factory"), - _A("valencia_orange"))); + _A("valencia_orange"), "up")); REQUIRE_THAT(src, IsDependency(_A("fruit_factory"), - _A("empire_apple"))); + _A("empire_apple"), "up")); REQUIRE_THAT(src, IsDependency(_A("fruit_factory"), - _A("lima_orange"))); + _A("lima_orange"), "up")); REQUIRE_THAT(src, IsAggregation(_A("R"), - _A("fruit_factory"), "+factory_1")); + _A("fruit_factory"), "+factory_1", + "", "", "up")); REQUIRE_THAT(src, IsAggregation(_A("R"), - _A("fruit_factory"), "+factory_2")); + _A("fruit_factory"), "+factory_2", "", + "", "up")); REQUIRE_THAT(src, IsInstantiation(_A("fruit_factory"), - _A("fruit_factory"))); + _A("fruit_factory"), "up")); REQUIRE_THAT(src, IsInstantiation(_A("fruit_factory"), - _A("fruit_factory"))); + _A("fruit_factory"), "up")); save_puml(config.output_directory(), diagram->name + ".puml", src); } diff --git a/tests/t00075/.clang-uml b/tests/t00075/.clang-uml new file mode 100644 index 00000000..cfd758ba --- /dev/null +++ b/tests/t00075/.clang-uml @@ -0,0 +1,17 @@ +diagrams: + t00075_class: + type: class + glob: + - t00075.cc + include: + namespaces: + - clanguml::t00075 + generate_packages: true + using_namespace: clanguml::t00075 + plantuml: + style: + concept: "#line:green;back:lightblue" + class: "#aliceblue;line:blue;line.dotted;text:blue" + package: "#back:grey" + enum: "#line.dotted" + constraint: "up[#green,dashed,thickness=2]" diff --git a/tests/t00075/t00075.cc b/tests/t00075/t00075.cc new file mode 100644 index 00000000..35bdc823 --- /dev/null +++ b/tests/t00075/t00075.cc @@ -0,0 +1,34 @@ +namespace clanguml { +namespace t00075 { +namespace ns1 { +namespace ns2 { + +template +concept C = requires(T t) { + T{}; + t.e(); + }; + +enum class E { k1, k2 }; + +struct A { + E e() const { return E::k1; }; +}; + +class B { +public: + E e() const { return E::k2; }; +}; + +template class ABE { + T a_or_b; +}; + +struct R { + ABE a; + ABE b; +}; +} // namespace ns2 +} // namespace ns1 +} +} \ No newline at end of file diff --git a/tests/t00075/test_case.h b/tests/t00075/test_case.h new file mode 100644 index 00000000..e1f4ac19 --- /dev/null +++ b/tests/t00075/test_case.h @@ -0,0 +1,92 @@ +/** + * tests/t00075/test_case.h + * + * Copyright (c) 2021-2024 Bartek Kryza + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +TEST_CASE("t00075", "[test-case][class]") +{ + auto [config, db] = load_config("t00075"); + + auto diagram = config.diagrams["t00075_class"]; + + REQUIRE(diagram->name == "t00075_class"); + + auto model = generate_class_diagram(*db, diagram); + + REQUIRE(model->name() == "t00075_class"); + + { + auto src = generate_class_puml(diagram, *model); + AliasMatcher _A(src); + + REQUIRE_THAT(src, StartsWith("@startuml")); + REQUIRE_THAT(src, EndsWith("@enduml\n")); + + // Check if all classes exist + REQUIRE_THAT(src, IsClass(_A("A"))); + REQUIRE_THAT(src, IsClass(_A("B"))); + REQUIRE_THAT(src, IsClass(_A("ABE"))); + REQUIRE_THAT(src, IsClass(_A("R"))); + + REQUIRE_THAT(src, IsEnum(_A("E"))); + + REQUIRE_THAT(src, IsConcept(_A("C"))); + + REQUIRE_THAT(src, IsConceptRequirement(_A("C"), "T{}")); + REQUIRE_THAT(src, IsConceptRequirement(_A("C"), "t.e()")); + REQUIRE_THAT(src, IsConceptRequirement(_A("C"), "(T t)")); + REQUIRE_THAT(src, !IsConceptRequirement(_A("C"), "(T ns1::ns2::t)")); + + REQUIRE_THAT(src, + IsConstraint(_A("ABE"), _A("C"), "T", + "up[#green,dashed,thickness=2]")); + + save_puml(config.output_directory(), diagram->name + ".puml", src); + } + + { + auto j = generate_class_json(diagram, *model); + + using namespace json; + + save_json(config.output_directory(), diagram->name + ".json", j); + } + + { + auto src = generate_class_mermaid(diagram, *model); + + mermaid::AliasMatcher _A(src); + using mermaid::IsClass; + using mermaid::IsConcept; + using mermaid::IsConceptRequirement; + using mermaid::IsEnum; + + // Check if all classes exist + REQUIRE_THAT(src, IsClass(_A("ns1::ns2::A"))); + REQUIRE_THAT(src, IsClass(_A("ns1::ns2::B"))); + REQUIRE_THAT(src, IsClass(_A("ns1::ns2::ABE"))); + REQUIRE_THAT(src, IsClass(_A("ns1::ns2::R"))); + + REQUIRE_THAT(src, IsEnum(_A("ns1::ns2::E"))); + + REQUIRE_THAT(src, IsConcept(_A("ns1::ns2::C"))); + + REQUIRE_THAT(src, IsConceptRequirement(_A("ns1::ns2::C"), "T{}")); + REQUIRE_THAT(src, IsConceptRequirement(_A("ns1::ns2::C"), "t.e()")); + + save_mermaid(config.output_directory(), diagram->name + ".mmd", src); + } +} \ No newline at end of file diff --git a/tests/test_cases.cc b/tests/test_cases.cc index 22bc5097..7f5f51b7 100644 --- a/tests/test_cases.cc +++ b/tests/test_cases.cc @@ -417,6 +417,7 @@ using namespace clanguml::test::matchers; #include "t00073/test_case.h" #if defined(ENABLE_CXX_STD_20_TEST_CASES) #include "t00074/test_case.h" +#include "t00075/test_case.h" #endif /// diff --git a/tests/test_cases.h b/tests/test_cases.h index 26883024..4b16e59c 100644 --- a/tests/test_cases.h +++ b/tests/test_cases.h @@ -640,14 +640,14 @@ ContainsMatcher IsInnerClass(std::string const &parent, ContainsMatcher IsAssociation(std::string const &from, std::string const &to, std::string const &label = "", std::string multiplicity_source = "", - std::string multiplicity_dest = "", + std::string multiplicity_dest = "", std::string style = "", CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes) { std::string format_string = "{}"; if (!multiplicity_source.empty()) format_string += " \"" + multiplicity_source + "\""; - format_string += " -->"; + format_string += fmt::format(" -{}->", style); if (!multiplicity_dest.empty()) format_string += " \"" + multiplicity_dest + "\""; @@ -668,14 +668,14 @@ ContainsMatcher IsAssociation(std::string const &from, std::string const &to, ContainsMatcher IsComposition(std::string const &from, std::string const &to, std::string const &label, std::string multiplicity_source = "", - std::string multiplicity_dest = "", + std::string multiplicity_dest = "", std::string style = "", CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes) { std::string format_string = "{}"; if (!multiplicity_source.empty()) format_string += " \"" + multiplicity_source + "\""; - format_string += " *--"; + format_string += fmt::format(" *-{}-", style); if (!multiplicity_dest.empty()) format_string += " \"" + multiplicity_dest + "\""; @@ -689,14 +689,14 @@ ContainsMatcher IsComposition(std::string const &from, std::string const &to, ContainsMatcher IsAggregation(std::string const &from, std::string const &to, std::string const &label, std::string multiplicity_source = "", - std::string multiplicity_dest = "", + std::string multiplicity_dest = "", std::string style = "", CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes) { std::string format_string = "{}"; if (!multiplicity_source.empty()) format_string += " \"" + multiplicity_source + "\""; - format_string += " o--"; + format_string += fmt::format(" o-{}-", style); if (!multiplicity_dest.empty()) format_string += " \"" + multiplicity_dest + "\""; @@ -708,45 +708,20 @@ ContainsMatcher IsAggregation(std::string const &from, std::string const &to, caseSensitivity)); } -ContainsMatcher IsAggregationWithStyle(std::string const &from, - std::string const &to, std::string const &label, std::string style, - CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes) -{ - return ContainsMatcher( - CasedString(fmt::format("{} o-[{}]- {} : {}", from, style, to, label), - caseSensitivity)); -} - -ContainsMatcher IsAssociationWithStyle(std::string const &from, - std::string const &to, std::string const &label, std::string style, - CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes) -{ - return ContainsMatcher( - CasedString(fmt::format("{} -[{}]-> {} : {}", from, style, to, label), - caseSensitivity)); -} - -ContainsMatcher IsCompositionWithStyle(std::string const &from, - std::string const &to, std::string const &label, std::string style, - CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes) -{ - return ContainsMatcher( - CasedString(fmt::format("{} *-[{}]- {} : {}", from, style, to, label), - caseSensitivity)); -} - ContainsMatcher IsInstantiation(std::string const &from, std::string const &to, + std::string style = "", CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes) { - return ContainsMatcher( - CasedString(fmt::format("{} ..|> {}", to, from), caseSensitivity)); + return ContainsMatcher(CasedString( + fmt::format("{} .{}.|> {}", to, style, from), caseSensitivity)); } ContainsMatcher IsDependency(std::string const &from, std::string const &to, + std::string style = "", CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes) { - return ContainsMatcher( - CasedString(fmt::format("{} ..> {}", from, to), caseSensitivity)); + return ContainsMatcher(CasedString( + fmt::format("{} .{}.> {}", from, style, to), caseSensitivity)); } namespace mermaid { @@ -767,15 +742,16 @@ ContainsMatcher IsIncludeDependency(std::string const &from, } ContainsMatcher IsConstraint(std::string const &from, std::string const &to, - std::string const &label = {}, + std::string const &label = {}, std::string style = "", CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes) { if (label.empty()) - return ContainsMatcher( - CasedString(fmt::format("{} ..> {}", from, to), caseSensitivity)); - else return ContainsMatcher(CasedString( - fmt::format("{} ..> {} : {}", from, to, label), caseSensitivity)); + fmt::format("{} .{}.> {}", from, style, to), caseSensitivity)); + else + return ContainsMatcher( + CasedString(fmt::format("{} .{}.> {} : {}", from, style, to, label), + caseSensitivity)); } ContainsMatcher IsConceptRequirement(std::string const &cpt, diff --git a/tests/test_cases.yaml b/tests/test_cases.yaml index 1b7f90b0..4a824bb8 100644 --- a/tests/test_cases.yaml +++ b/tests/test_cases.yaml @@ -219,6 +219,9 @@ test_cases: - name: t00074 title: Test case for rendering concepts without requirements description: + - name: t00075 + title: Test case for class diagram styles in config file + description: Sequence diagrams: - name: t20001 title: Basic sequence diagram test case