diff --git a/src/class_diagram/generators/json/class_diagram_generator.h b/src/class_diagram/generators/json/class_diagram_generator.h index aaf5ede8b..0005ee109 100644 --- a/src/class_diagram/generators/json/class_diagram_generator.h +++ b/src/class_diagram/generators/json/class_diagram_generator.h @@ -156,7 +156,8 @@ void generator::generate_relationships(const T &c, nlohmann::json &parent) const if (!target_element.has_value()) { LOG_DBG("Skipping {} relation from '{}' to '{}' due " "to unresolved destination id", - to_string(r.type()), c.full_name(), r.destination().value()); + to_string(r.type()), c.full_name(true), + r.destination().value()); continue; } diff --git a/src/class_diagram/generators/mermaid/class_diagram_generator.cc b/src/class_diagram/generators/mermaid/class_diagram_generator.cc index db5769233..115fc94df 100644 --- a/src/class_diagram/generators/mermaid/class_diagram_generator.cc +++ b/src/class_diagram/generators/mermaid/class_diagram_generator.cc @@ -96,7 +96,8 @@ void generator::generate(const class_ &c, std::ostream &ostr) const catch (error::uml_alias_missing &e) { LOG_DBG("Skipping {} relation from {} to {} due " "to: {}", - to_string(r.type()), c.full_name(), r.destination(), e.what()); + to_string(r.type()), c.full_name(false), r.destination(), + e.what()); } } @@ -466,7 +467,7 @@ void generator::generate_relationships( catch (error::uml_alias_missing &e) { LOG_DBG("=== Skipping {} relation from {} to {} due " "to: {}", - to_string(r.type()), c.full_name(), destination, e.what()); + to_string(r.type()), c.full_name(true), destination, e.what()); } } @@ -548,7 +549,7 @@ void generator::generate_relationships( catch (error::uml_alias_missing &e) { LOG_DBG("=== Skipping {} relation from {} to {} due " "to: {}", - to_string(r.type()), c.full_name(), destination, e.what()); + to_string(r.type()), c.full_name(true), destination, e.what()); } } @@ -596,7 +597,7 @@ void generator::generate_relationships(const enum_ &e, std::ostream &ostr) const LOG_DBG("Skipping {} relation from {} to {} due " "to: {}", clanguml::common::generators::mermaid::to_mermaid(r.type()), - e.full_name(), destination, ex.what()); + e.full_name(true), destination, ex.what()); } } } @@ -656,7 +657,8 @@ void generator::generate(const objc_interface &c, std::ostream &ostr) const catch (error::uml_alias_missing &e) { LOG_DBG("Skipping {} relation from {} to {} due " "to: {}", - to_string(r.type()), c.full_name(), r.destination(), e.what()); + to_string(r.type()), c.full_name(true), r.destination(), + e.what()); } } @@ -768,7 +770,7 @@ void generator::generate_relationships( catch (error::uml_alias_missing &e) { LOG_DBG("=== Skipping {} relation from {} to {} due " "to: {}", - to_string(r.type()), c.full_name(), destination, e.what()); + to_string(r.type()), c.full_name(true), destination, e.what()); } } diff --git a/src/class_diagram/generators/plantuml/class_diagram_generator.cc b/src/class_diagram/generators/plantuml/class_diagram_generator.cc index 1e750411c..a48f7297b 100644 --- a/src/class_diagram/generators/plantuml/class_diagram_generator.cc +++ b/src/class_diagram/generators/plantuml/class_diagram_generator.cc @@ -79,7 +79,7 @@ void generator::generate_alias(const class_ &c, std::ostream &ostr) const if (!config().generate_fully_qualified_name()) full_name = c.full_name_no_ns(); else - full_name = c.full_name(); + full_name = c.full_name(true); assert(!full_name.empty()); @@ -101,7 +101,7 @@ void generator::generate_alias(const enum_ &e, std::ostream &ostr) const if (!config().generate_fully_qualified_name()) ostr << "enum" << " \"" << e.name(); else - ostr << "enum" << " \"" << render_name(e.full_name()); + ostr << "enum" << " \"" << render_name(e.full_name(true)); ostr << "\" as " << e.alias() << '\n'; @@ -116,7 +116,7 @@ void generator::generate_alias(const concept_ &c, std::ostream &ostr) const if (!config().generate_fully_qualified_name()) ostr << "class" << " \"" << c.full_name_no_ns(); else - ostr << "class" << " \"" << render_name(c.full_name()); + ostr << "class" << " \"" << render_name(c.full_name(true)); ostr << "\" as " << c.alias() << '\n'; @@ -134,7 +134,7 @@ void generator::generate_alias( else ostr << "protocol"; - ostr << " \"" << render_name(e.full_name()); + ostr << " \"" << render_name(e.full_name(true)); ostr << "\" as " << e.alias() << '\n'; @@ -185,7 +185,8 @@ void generator::generate(const class_ &c, std::ostream &ostr) const catch (error::uml_alias_missing &e) { LOG_DBG("Skipping {} relation from {} to {} due " "to: {}", - to_string(r.type()), c.full_name(), r.destination(), e.what()); + to_string(r.type()), c.full_name(true), r.destination(), + e.what()); } } @@ -416,7 +417,8 @@ void generator::generate(const objc_interface &c, std::ostream &ostr) const catch (error::uml_alias_missing &e) { LOG_DBG("Skipping {} relation from {} to {} due " "to: {}", - to_string(r.type()), c.full_name(), r.destination(), e.what()); + to_string(r.type()), c.full_name(true), r.destination(), + e.what()); } } @@ -631,7 +633,7 @@ void generator::generate_relationships( catch (error::uml_alias_missing &e) { LOG_DBG("=== Skipping {} relation from {} to {} due " "to: {}", - to_string(r.type()), c.full_name(), destination, e.what()); + to_string(r.type()), c.full_name(true), destination, e.what()); } } @@ -705,7 +707,7 @@ void generator::generate_relationships( catch (error::uml_alias_missing &e) { LOG_DBG("=== Skipping {} relation from {} to {} due " "to: {}", - to_string(r.type()), c.full_name(), destination, e.what()); + to_string(r.type()), c.full_name(true), destination, e.what()); } } @@ -764,7 +766,7 @@ void generator::generate_relationships(const enum_ &e, std::ostream &ostr) const "to: {}", clanguml::common::generators::plantuml::to_plantuml( r, config()), - e.full_name(), destination, ex.what()); + e.full_name(true), destination, ex.what()); } } } @@ -838,7 +840,7 @@ void generator::generate_relationships( catch (error::uml_alias_missing &e) { LOG_DBG("=== Skipping {} relation from {} to {} due " "to: {}", - to_string(r.type()), c.full_name(), destination, e.what()); + to_string(r.type()), c.full_name(true), destination, e.what()); } } diff --git a/src/class_diagram/model/class.cc b/src/class_diagram/model/class.cc index 3e64448cb..bf4cfd30a 100644 --- a/src/class_diagram/model/class.cc +++ b/src/class_diagram/model/class.cc @@ -67,11 +67,8 @@ std::string class_::full_name_no_ns() const return ostr.str(); } -std::string class_::full_name(bool relative) const +std::string class_::full_name_impl(bool relative) const { - if (relative == false && complete() && full_name_cache()) - return *full_name_cache(); - using namespace clanguml::util; using clanguml::common::model::namespace_; @@ -91,9 +88,6 @@ std::string class_::full_name(bool relative) const if (res.empty()) return "<>"; - if (relative == false && complete()) - cache_full_name(res); - return res; } diff --git a/src/class_diagram/model/class.h b/src/class_diagram/model/class.h index fbe651d9f..d9fd7f7a6 100644 --- a/src/class_diagram/model/class.h +++ b/src/class_diagram/model/class.h @@ -113,18 +113,6 @@ class class_ : public common::model::template_element, */ const std::vector &methods() const; - /** - * @brief Get class full name. - * - * This method renders the entire class name including all template - * parameters and/or arguments. - * - * @param relative Whether the class name should be relative to - * using_namespace - * @return Full class name. - */ - std::string full_name(bool relative = true) const override; - /** * @brief Get unqualified class ful name. * @@ -155,6 +143,19 @@ class class_ : public common::model::template_element, void apply_filter(const common::model::diagram_filter &filter, const std::set &removed) override; +protected: + /** + * @brief Get class full name. + * + * This method renders the entire class name including all template + * parameters and/or arguments. + * + * @param relative Whether the class name should be relative to + * using_namespace + * @return Full class name. + */ + std::string full_name_impl(bool relative = true) const override; + private: bool is_struct_{false}; bool is_union_{false}; diff --git a/src/class_diagram/model/concept.cc b/src/class_diagram/model/concept.cc index 4adaf5ab4..e7969d6c7 100644 --- a/src/class_diagram/model/concept.cc +++ b/src/class_diagram/model/concept.cc @@ -46,14 +46,11 @@ std::string concept_::full_name_no_ns() const return ostr.str(); } -std::string concept_::full_name(bool relative) const +std::string concept_::full_name_impl(bool relative) const { using namespace clanguml::util; using clanguml::common::model::namespace_; - if (relative == false && complete() && full_name_cache()) - return *full_name_cache(); - std::ostringstream ostr; ostr << name_and_ns(); @@ -70,9 +67,6 @@ std::string concept_::full_name(bool relative) const if (res.empty()) return "<>"; - if (relative == false && complete()) - cache_full_name(res); - return res; } diff --git a/src/class_diagram/model/concept.h b/src/class_diagram/model/concept.h index f40b1c1bb..5c854ac50 100644 --- a/src/class_diagram/model/concept.h +++ b/src/class_diagram/model/concept.h @@ -52,8 +52,6 @@ class concept_ : public common::model::element, friend bool operator==(const concept_ &l, const concept_ &r); - std::string full_name(bool relative = true) const override; - std::string full_name_no_ns() const override; /** @@ -87,6 +85,9 @@ class concept_ : public common::model::element, */ const std::vector &requires_statements() const; +protected: + std::string full_name_impl(bool relative = true) const override; + private: std::vector requires_expression_; diff --git a/src/class_diagram/model/enum.cc b/src/class_diagram/model/enum.cc index 0799bcdb5..5c143c6af 100644 --- a/src/class_diagram/model/enum.cc +++ b/src/class_diagram/model/enum.cc @@ -34,14 +34,11 @@ bool operator==(const enum_ &l, const enum_ &r) return (l.get_namespace() == r.get_namespace()) && (l.name() == r.name()); } -std::string enum_::full_name(bool relative) const +std::string enum_::full_name_impl(bool relative) const { using namespace clanguml::util; using clanguml::common::model::namespace_; - if (relative == false && complete() && full_name_cache()) - return *full_name_cache(); - std::ostringstream ostr; if (relative) ostr << namespace_{name_and_ns()} @@ -52,9 +49,6 @@ std::string enum_::full_name(bool relative) const std::string res{ostr.str()}; - if (relative == false && complete()) - cache_full_name(res); - return res; } diff --git a/src/class_diagram/model/enum.h b/src/class_diagram/model/enum.h index 9d0f1bec9..bb280df62 100644 --- a/src/class_diagram/model/enum.h +++ b/src/class_diagram/model/enum.h @@ -41,8 +41,6 @@ class enum_ : public common::model::element, friend bool operator==(const enum_ &l, const enum_ &r); - std::string full_name(bool relative = true) const override; - /** * @brief Get the enums constants. * @@ -64,6 +62,9 @@ class enum_ : public common::model::element, */ std::optional doxygen_link() const override; +protected: + std::string full_name_impl(bool relative = true) const override; + private: std::vector constants_; }; diff --git a/src/class_diagram/model/objc_interface.cc b/src/class_diagram/model/objc_interface.cc index c2a40a60c..a44813e92 100644 --- a/src/class_diagram/model/objc_interface.cc +++ b/src/class_diagram/model/objc_interface.cc @@ -35,7 +35,7 @@ bool operator==(const objc_interface &l, const objc_interface &r) return l.id() == r.id(); } -std::string objc_interface::full_name(bool /*relative*/) const +std::string objc_interface::full_name_impl(bool /*relative*/) const { return name(); } diff --git a/src/class_diagram/model/objc_interface.h b/src/class_diagram/model/objc_interface.h index 7d2ce1aeb..8ea1c1ce9 100644 --- a/src/class_diagram/model/objc_interface.h +++ b/src/class_diagram/model/objc_interface.h @@ -53,8 +53,6 @@ class objc_interface : public common::model::element, friend bool operator==(const objc_interface &l, const objc_interface &r); - std::string full_name(bool relative = true) const override; - void add_member(objc_member &&member); void add_method(objc_method &&method); @@ -78,6 +76,9 @@ class objc_interface : public common::model::element, */ std::optional doxygen_link() const override; +protected: + std::string full_name_impl(bool relative = true) const override; + private: std::vector members_; std::vector methods_; diff --git a/src/class_diagram/visitor/translation_unit_visitor.cc b/src/class_diagram/visitor/translation_unit_visitor.cc index 4faee3ded..3652baa1f 100644 --- a/src/class_diagram/visitor/translation_unit_visitor.cc +++ b/src/class_diagram/visitor/translation_unit_visitor.cc @@ -285,7 +285,7 @@ bool translation_unit_visitor::VisitTypeAliasTemplateDecl( *template_specialization_ptr, cls, *template_type_specialization_ptr); if (diagram().should_include(*template_specialization_ptr)) { - const auto name = template_specialization_ptr->full_name(); + const auto name = template_specialization_ptr->full_name(true); const auto id = template_specialization_ptr->id(); LOG_DBG("Adding class {} with id {}", name, id); @@ -346,7 +346,7 @@ bool translation_unit_visitor::VisitClassTemplateDecl( forward_declarations_.erase(id); if (diagram().should_include(*c_ptr)) { - const auto name = c_ptr->full_name(); + const auto name = c_ptr->full_name(true); LOG_DBG("Adding class template {} with id {}", name, id); add_class(std::move(c_ptr)); @@ -401,8 +401,8 @@ bool translation_unit_visitor::VisitRecordDecl(clang::RecordDecl *rec) add_class(std::move(record_ptr)); } else { - LOG_DBG("Skipping struct/union {} with id {}", record_model.full_name(), - record_model.id()); + LOG_DBG("Skipping struct/union {} with id {}", + record_model.full_name(true), record_model.id()); } return true; @@ -442,7 +442,7 @@ bool translation_unit_visitor::VisitObjCCategoryDecl( } else { LOG_DBG("Skipping ObjC category {} with id {}", - category_model.full_name(), category_model.id()); + category_model.full_name(true), category_model.id()); } return true; @@ -482,7 +482,7 @@ bool translation_unit_visitor::VisitObjCProtocolDecl( } else { LOG_DBG("Skipping ObjC protocol {} with id {}", - protocol_model.full_name(), protocol_model.id()); + protocol_model.full_name(true), protocol_model.id()); } return true; @@ -523,7 +523,7 @@ bool translation_unit_visitor::VisitObjCInterfaceDecl( } else { LOG_DBG("Skipping ObjC interface {} with id {}", - interface_model.full_name(), interface_model.id()); + interface_model.full_name(true), interface_model.id()); } return true; @@ -574,8 +574,8 @@ bool translation_unit_visitor::TraverseConceptDecl(clang::ConceptDecl *cpt) add_concept(std::move(concept_model)); } else { - LOG_DBG("Skipping concept {} with id {}", concept_model->full_name(), - concept_model->id()); + LOG_DBG("Skipping concept {} with id {}", + concept_model->full_name(true), concept_model->id()); } return true; @@ -883,7 +883,7 @@ bool translation_unit_visitor::VisitCXXRecordDecl(clang::CXXRecordDecl *cls) add_class(std::move(c_ptr)); } else { - LOG_DBG("Skipping class {} with id {}", class_model.full_name(), + LOG_DBG("Skipping class {} with id {}", class_model.full_name(true), class_model.id()); } @@ -1577,7 +1577,7 @@ void translation_unit_visitor::process_method( LOG_DBG("Adding method return type relationship from {}::{} to " "{}: {}", - c.full_name(), mf.getNameAsString(), + c.full_name(true), mf.getNameAsString(), clanguml::common::model::to_string(r.type()), r.label()); c.add_relationship(std::move(r)); @@ -1647,7 +1647,7 @@ void translation_unit_visitor::process_objc_method( LOG_DBG("Adding method return type relationship from {}::{} to " "{}: {}", - c.full_name(), mf.getNameAsString(), + c.full_name(true), mf.getNameAsString(), clanguml::common::model::to_string(r.type()), r.label()); c.add_relationship(std::move(r)); @@ -1984,8 +1984,8 @@ void translation_unit_visitor::process_objc_method_parameter( LOG_DBG("Adding ObjC method parameter relationship from {} to " "{}: {}", - c.full_name(), clanguml::common::model::to_string(r.type()), - r.label()); + c.full_name(true), + clanguml::common::model::to_string(r.type()), r.label()); c.add_relationship(std::move(r)); } @@ -2060,8 +2060,8 @@ void translation_unit_visitor::process_function_parameter( LOG_DBG("Adding function parameter relationship from {} to " "{}: {}", - c.full_name(), clanguml::common::model::to_string(r.type()), - r.label()); + c.full_name(true), + clanguml::common::model::to_string(r.type()), r.label()); c.add_relationship(std::move(r)); } diff --git a/src/common/model/diagram_element.h b/src/common/model/diagram_element.h index a401ad2d8..40e82ad48 100644 --- a/src/common/model/diagram_element.h +++ b/src/common/model/diagram_element.h @@ -20,6 +20,7 @@ #include "decorated_element.h" #include "relationship.h" #include "source_location.h" +#include "util/memoized.h" #include "util/util.h" #include @@ -40,7 +41,12 @@ class diagram_filter; * This is a base cass of any standalone elements such as classes, structs, * concepts, packages and so on participants and so on. */ -class diagram_element : public decorated_element, public source_location { +struct full_name_tag_t { }; + +class diagram_element + : public decorated_element, + public source_location, + public util::memoized { public: diagram_element(); @@ -117,7 +123,13 @@ class diagram_element : public decorated_element, public source_location { * * @return Full elements name. */ - virtual std::string full_name(bool /*relative*/) const { return name(); } + std::string full_name(bool relative) const + { + return memoize( + complete(), + [this](bool relative) { return full_name_impl(relative); }, + relative); + } /** * Return all relationships outgoing from this element. @@ -190,13 +202,12 @@ class diagram_element : public decorated_element, public source_location { virtual void apply_filter( const diagram_filter &filter, const std::set &removed); - const std::optional &full_name_cache() const +protected: + virtual std::string full_name_impl(bool /*relative*/) const { - return full_name_cache_; + return name(); } - void cache_full_name(const std::string &fn) const { full_name_cache_ = fn; } - private: eid_t id_{}; std::optional parent_element_id_{}; @@ -204,7 +215,5 @@ class diagram_element : public decorated_element, public source_location { std::vector relationships_; bool nested_{false}; bool complete_{false}; - - mutable std::optional full_name_cache_; }; } // namespace clanguml::common::model diff --git a/src/common/model/element.h b/src/common/model/element.h index 34f1915c3..3f3f98dd1 100644 --- a/src/common/model/element.h +++ b/src/common/model/element.h @@ -118,19 +118,6 @@ class element : public diagram_element { */ bool module_private() const { return module_private_; } - /** - * Return elements full name. - * - * @return Fully qualified elements name. - */ - std::string full_name(bool relative) const override - { - if (relative) - return name(); - - return name_and_ns(); - } - /** * Return elements full name but without namespace. * @@ -151,6 +138,20 @@ class element : public diagram_element { inja::json context() const override; +protected: + /** + * Return elements full name. + * + * @return Fully qualified elements name. + */ + std::string full_name_impl(bool relative) const override + { + if (relative) + return name(); + + return name_and_ns(); + } + private: namespace_ ns_; namespace_ using_namespace_; diff --git a/src/common/model/package.cc b/src/common/model/package.cc index 244f28827..99c8a3644 100644 --- a/src/common/model/package.cc +++ b/src/common/model/package.cc @@ -26,7 +26,7 @@ package::package(const common::model::namespace_ &using_namespace, path_type pt) { } -std::string package::full_name(bool relative) const +std::string package::full_name_impl(bool relative) const { if (relative) { auto res = get_namespace().relative_to(using_namespace()) | name(); diff --git a/src/common/model/package.h b/src/common/model/package.h index 98fffd41b..aaa28a64b 100644 --- a/src/common/model/package.h +++ b/src/common/model/package.h @@ -51,8 +51,6 @@ class package : public element, std::string type_name() const override { return "package"; } - std::string full_name(bool relative) const override; - /** * Returns whether the namespace is deprecated. * @@ -77,6 +75,9 @@ class package : public element, */ std::optional doxygen_link() const override; +protected: + std::string full_name_impl(bool relative) const override; + private: bool is_deprecated_{false}; }; diff --git a/src/common/model/source_file.h b/src/common/model/source_file.h index fc1b51763..c38964d29 100644 --- a/src/common/model/source_file.h +++ b/src/common/model/source_file.h @@ -138,16 +138,6 @@ class source_file */ const filesystem_path &path() const { return path_; } - /** - * Return the full path string, i.e. parent path and elements name. - * - * @return Full source file path as string. - */ - std::string full_name(bool /*relative*/) const override - { - return (path_ | name()).to_string(); - } - /** * Return full path, i.e. parent path and elements name. * @@ -195,6 +185,17 @@ class source_file return ctx; } +protected: + /** + * Return the full path string, i.e. parent path and elements name. + * + * @return Full source file path as string. + */ + std::string full_name_impl(bool /*relative*/) const override + { + return (path_ | name()).to_string(); + } + private: filesystem_path path_{path_type::kFilesystem}; source_file_t type_{source_file_t::kDirectory}; diff --git a/src/sequence_diagram/model/participant.cc b/src/sequence_diagram/model/participant.cc index 3bf0ea10c..631e32f81 100644 --- a/src/sequence_diagram/model/participant.cc +++ b/src/sequence_diagram/model/participant.cc @@ -62,14 +62,11 @@ std::string class_::full_name_no_ns() const return ostr.str(); } -std::string class_::full_name(bool relative) const +std::string class_::full_name_impl(bool relative) const { using namespace clanguml::util; using clanguml::common::model::namespace_; - if (relative == false && complete() && full_name_cache()) - return *full_name_cache(); - std::ostringstream ostr; if (relative) @@ -88,9 +85,6 @@ std::string class_::full_name(bool relative) const if (res.empty()) return "<>"; - if (relative == false && complete()) - cache_full_name(res); - return res; } @@ -123,9 +117,9 @@ function::function(const common::model::namespace_ &using_namespace) { } -std::string function::full_name(bool relative) const +std::string function::full_name_impl(bool relative) const { - return fmt::format("{}({}){}", participant::full_name(relative), + return fmt::format("{}({}){}", participant::full_name_impl(relative), fmt::join(parameters_, ","), is_const() ? " const" : ""); } @@ -217,7 +211,7 @@ void objc_method::set_class_full_name(const std::string &name) const auto &objc_method::class_full_name() const { return class_full_name_; } -std::string objc_method::full_name(bool relative) const +std::string objc_method::full_name_impl(bool relative) const { if (relative) return fmt::format("{}({}){}", method_name(), @@ -294,7 +288,7 @@ void method::set_class_full_name(const std::string &name) const auto &method::class_full_name() const { return class_full_name_; } -std::string method::full_name(bool relative) const +std::string method::full_name_impl(bool relative) const { if (relative) return fmt::format("{}({}){}", method_name(), @@ -340,7 +334,7 @@ function_template::function_template( { } -std::string function_template::full_name(bool relative) const +std::string function_template::full_name_impl(bool relative) const { using namespace clanguml::util; using clanguml::common::model::namespace_; diff --git a/src/sequence_diagram/model/participant.h b/src/sequence_diagram/model/participant.h index f43bc52dd..a85c3dd16 100644 --- a/src/sequence_diagram/model/participant.h +++ b/src/sequence_diagram/model/participant.h @@ -146,13 +146,6 @@ struct class_ : public participant { friend bool operator==(const class_ &l, const class_ &r); - /** - * Return elements full name. - * - * @return Fully qualified elements name. - */ - std::string full_name(bool relative = true) const override; - /** * Return elements full name but without namespace. * @@ -206,6 +199,14 @@ struct class_ : public participant { eid_t lambda_operator_id() const { return lambda_operator_id_; } +protected: + /** + * Return elements full name. + * + * @return Fully qualified elements name. + */ + std::string full_name_impl(bool relative = true) const override; + private: bool is_struct_{false}; bool is_template_{false}; @@ -239,13 +240,6 @@ struct function : public participant { */ std::string type_name() const override { return "function"; } - /** - * Return elements full name. - * - * @return Fully qualified elements name. - */ - std::string full_name(bool relative = true) const override; - /** * Return elements full name but without namespace. * @@ -376,6 +370,14 @@ struct function : public participant { */ const std::vector ¶meters() const; +protected: + /** + * Return elements full name. + * + * @return Fully qualified elements name. + */ + std::string full_name_impl(bool relative = true) const override; + private: bool is_const_{false}; bool is_void_{false}; @@ -450,13 +452,6 @@ struct method : public function { */ const auto &class_full_name() const; - /** - * Return elements full name. - * - * @return Fully qualified elements name. - */ - std::string full_name(bool relative) const override; - std::string message_name(message_render_mode mode) const override; /** @@ -515,6 +510,14 @@ struct method : public function { */ void is_assignment(bool a); +protected: + /** + * Return elements full name. + * + * @return Fully qualified elements name. + */ + std::string full_name_impl(bool relative) const override; + private: eid_t class_id_{}; std::string method_name_; @@ -584,13 +587,6 @@ struct objc_method : public function { */ const auto &class_full_name() const; - /** - * Return elements full name. - * - * @return Fully qualified elements name. - */ - std::string full_name(bool relative) const override; - std::string message_name(message_render_mode mode) const override; /** @@ -607,6 +603,14 @@ struct objc_method : public function { */ std::string to_string() const override; +protected: + /** + * Return elements full name. + * + * @return Fully qualified elements name. + */ + std::string full_name_impl(bool relative) const override; + private: eid_t class_id_{}; std::string method_name_; @@ -631,13 +635,6 @@ struct function_template : public function { */ std::string type_name() const override { return "function_template"; } - /** - * Return elements full name. - * - * @return Fully qualified elements name. - */ - std::string full_name(bool relative = true) const override; - /** * Return elements full name but without namespace. * @@ -652,5 +649,13 @@ struct function_template : public function { * @return Message label */ std::string message_name(message_render_mode mode) const override; + +protected: + /** + * Return elements full name. + * + * @return Fully qualified elements name. + */ + std::string full_name_impl(bool relative = true) const override; }; } // namespace clanguml::sequence_diagram::model diff --git a/src/sequence_diagram/visitor/translation_unit_visitor.cc b/src/sequence_diagram/visitor/translation_unit_visitor.cc index b7660ff4b..7ca8b5598 100644 --- a/src/sequence_diagram/visitor/translation_unit_visitor.cc +++ b/src/sequence_diagram/visitor/translation_unit_visitor.cc @@ -96,8 +96,8 @@ bool translation_unit_visitor::VisitObjCProtocolDecl( diagram().add_participant(std::move(objc_protocol_model_ptr)); } else { - LOG_DBG("Skipping ObjC protocol {} with id {}", class_model.full_name(), - class_id); + LOG_DBG("Skipping ObjC protocol {} with id {}", + class_model.full_name(true), class_id); } return true; @@ -147,7 +147,7 @@ bool translation_unit_visitor::VisitObjCInterfaceDecl( } else { LOG_DBG("Skipping ObjC interface {} with id {}", - class_model.full_name(), class_id); + class_model.full_name(true), class_id); } return true; @@ -214,8 +214,8 @@ bool translation_unit_visitor::VisitCXXRecordDecl( diagram().add_participant(std::move(class_model_ptr)); } else { - LOG_DBG( - "Skipping class {} with id {}", class_model.full_name(), class_id); + LOG_DBG("Skipping class {} with id {}", class_model.full_name(true), + class_id); } return true; diff --git a/src/util/memoized.h b/src/util/memoized.h new file mode 100644 index 000000000..9e235f44d --- /dev/null +++ b/src/util/memoized.h @@ -0,0 +1,80 @@ +/** + * src/util/memoized.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. + */ +#pragma once + +#include +#include +#include + +namespace clanguml::util { +/** + * @brief Simple memoization implementation for expensive methods. + * + * @tparam T Tag type to allow multiple memoizations per class + * @tparam Ret Return type of the memoized method F + * @tparam Args Arguments the memoized method F + */ +template class memoized { +public: + using key_t = std::tuple; + using value_t = Ret; + + template + auto memoize(bool is_complete, F f, Args... args) const + { + if (!is_complete) + return f(args...); + + const auto key = key_t{args...}; + if (cache_.find(key) == cache_.end()) + cache_[key] = f(args...); + + return cache_.at(key); + } + +private: + mutable std::map cache_; +}; + +template class memoized { +public: + using key_t = bool; + using value_t = Ret; + + template auto memoize(bool is_complete, F f, bool arg) const + { + if (!is_complete) + return f(arg); + + const auto key = arg; + if (key && !true_value_) { + true_value_ = f(arg); + } + + if (!key && !false_value_) { + false_value_ = f(arg); + } + + return key ? *true_value_ : *false_value_; // NOLINT + } + +private: + mutable std::optional true_value_; + mutable std::optional false_value_; +}; +} // namespace clanguml::util \ No newline at end of file diff --git a/tests/test_cases.h b/tests/test_cases.h index 406a112af..fec45be9f 100644 --- a/tests/test_cases.h +++ b/tests/test_cases.h @@ -142,7 +142,9 @@ template struct diagram_source_t { bool generate_packages{false}; }; -struct plantuml_t : public diagram_source_t { +struct plantuml_t : public diagram_source_t, + util::memoized { + using diagram_source_t::diagram_source_t; using source_type = std::string; using generator_tag = clanguml::common::generators::plantuml_generator_tag; @@ -150,6 +152,13 @@ struct plantuml_t : public diagram_source_t { inline static const std::string diagram_type_name{"PlantUML"}; std::string get_alias(std::string name) const override + { + return memoize( + true, [this, name](std::string x) { return get_alias_impl(x); }, + name); + } + + std::string get_alias_impl(std::string name) const { std::vector patterns; @@ -162,24 +171,36 @@ struct plantuml_t : public diagram_source_t { util::replace_all(name, "[", "\\["); util::replace_all(name, "]", "\\]"); - patterns.push_back( - std::regex{"class\\s\"" + name + "\"\\sas\\s" + alias_regex}); - patterns.push_back( - std::regex{"abstract\\s\"" + name + "\"\\sas\\s" + alias_regex}); - patterns.push_back( - std::regex{"enum\\s\"" + name + "\"\\sas\\s" + alias_regex}); - patterns.push_back( - std::regex{"package\\s\"" + name + "\"\\sas\\s" + alias_regex}); - patterns.push_back( - std::regex{"package\\s\\[" + name + "\\]\\sas\\s" + alias_regex}); - patterns.push_back( - std::regex{"file\\s\"" + name + "\"\\sas\\s" + alias_regex}); - patterns.push_back( - std::regex{"folder\\s\"" + name + "\"\\sas\\s" + alias_regex}); - patterns.push_back( - std::regex{"participant\\s\"" + name + "\"\\sas\\s" + alias_regex}); - patterns.push_back( - std::regex{"protocol\\s\"" + name + "\"\\sas\\s" + alias_regex}); + if (diagram_type == common::model::diagram_t::kClass) { + patterns.push_back( + std::regex{"class\\s\"" + name + "\"\\sas\\s" + alias_regex}); + patterns.push_back(std::regex{ + "abstract\\s\"" + name + "\"\\sas\\s" + alias_regex}); + patterns.push_back( + std::regex{"enum\\s\"" + name + "\"\\sas\\s" + alias_regex}); + patterns.push_back( + std::regex{"package\\s\"" + name + "\"\\sas\\s" + alias_regex}); + patterns.push_back(std::regex{ + "package\\s\\[" + name + "\\]\\sas\\s" + alias_regex}); + patterns.push_back(std::regex{ + "protocol\\s\"" + name + "\"\\sas\\s" + alias_regex}); + } + else if (diagram_type == common::model::diagram_t::kSequence) { + patterns.push_back(std::regex{ + "participant\\s\"" + name + "\"\\sas\\s" + alias_regex}); + } + else if (diagram_type == common::model::diagram_t::kPackage) { + patterns.push_back( + std::regex{"package\\s\"" + name + "\"\\sas\\s" + alias_regex}); + patterns.push_back(std::regex{ + "package\\s\\[" + name + "\\]\\sas\\s" + alias_regex}); + } + else if (diagram_type == common::model::diagram_t::kInclude) { + patterns.push_back( + std::regex{"file\\s\"" + name + "\"\\sas\\s" + alias_regex}); + patterns.push_back( + std::regex{"folder\\s\"" + name + "\"\\sas\\s" + alias_regex}); + } std::smatch base_match; @@ -196,14 +217,22 @@ struct plantuml_t : public diagram_source_t { } }; -struct mermaid_t : public diagram_source_t { +struct mermaid_t : public diagram_source_t, + util::memoized { using diagram_source_t::diagram_source_t; using source_type = std::string; using generator_tag = clanguml::common::generators::mermaid_generator_tag; inline static const std::string diagram_type_name{"MermaidJS"}; - std::string get_alias_impl(std::string name) const + std::string get_alias(std::string name) const override + { + return memoize( + true, [this, name](std::string x) { return get_alias_impl(x); }, + name); + } + + std::string get_alias_class_diagram_impl(std::string name) const { std::vector patterns; @@ -277,12 +306,12 @@ struct mermaid_t : public diagram_source_t { return fmt::format("__INVALID__ALIAS__({})", name); } - std::string get_alias(std::string name) const override + std::string get_alias_impl(std::string name) const { if (diagram_type == common::model::diagram_t::kSequence) return get_alias_sequence_diagram_impl(name); - return get_alias_impl(name); + return get_alias_class_diagram_impl(name); } };