From e6fa19ff39b01d8773af52172936023d53d12677 Mon Sep 17 00:00:00 2001 From: Bartek Kryza Date: Sat, 27 May 2023 22:31:46 +0200 Subject: [PATCH] Refactored nested diagrams with element_view template --- src/class_diagram/model/diagram.cc | 407 ++---------------- src/class_diagram/model/diagram.h | 203 +++++++-- .../visitor/translation_unit_visitor.cc | 30 +- src/common/model/diagram_filter.cc | 14 +- src/common/model/element_view.h | 70 +++ src/include_diagram/model/diagram.cc | 38 +- src/include_diagram/model/diagram.h | 62 ++- src/package_diagram/model/diagram.cc | 75 +--- src/package_diagram/model/diagram.h | 118 ++++- .../visitor/translation_unit_visitor.cc | 4 +- 10 files changed, 445 insertions(+), 576 deletions(-) create mode 100644 src/common/model/element_view.h diff --git a/src/class_diagram/model/diagram.cc b/src/class_diagram/model/diagram.cc index 473d2e10..b250ca86 100644 --- a/src/class_diagram/model/diagram.cc +++ b/src/class_diagram/model/diagram.cc @@ -21,20 +21,21 @@ #include "util/error.h" #include "util/util.h" -#include - namespace clanguml::class_diagram::model { const common::reference_vector &diagram::classes() const { - return classes_; + return element_view::view(); } -const common::reference_vector &diagram::enums() const { return enums_; } +const common::reference_vector &diagram::enums() const +{ + return element_view::view(); +} const common::reference_vector &diagram::concepts() const { - return concepts_; + return element_view::view(); } common::model::diagram_t diagram::type() const @@ -46,17 +47,17 @@ common::optional_ref diagram::get( const std::string &full_name) const { common::optional_ref res = - get_class(full_name); + find(full_name); if (res.has_value()) return res; - res = get_enum(full_name); + res = find(full_name); if (res.has_value()) return res; - res = get_concept(full_name); + res = find(full_name); return res; } @@ -66,163 +67,24 @@ common::optional_ref diagram::get( { common::optional_ref res; - res = get_class(id); + res = find(id); if (res.has_value()) return res; - res = get_enum(id); + res = find(id); if (res.has_value()) return res; - res = get_concept(id); + res = find(id); return res; } -bool diagram::has_class(const class_ &c) const -{ - return std::any_of(classes_.cbegin(), classes_.cend(), - [&c](const auto &cc) { return cc.get() == c; }); -} - -bool diagram::has_enum(const enum_ &e) const -{ - return std::any_of(enums_.cbegin(), enums_.cend(), - [&e](const auto &ee) { return ee.get().full_name() == e.full_name(); }); -} - -bool diagram::has_concept(const concept_ &c) const -{ - return std::any_of(concepts_.cbegin(), concepts_.cend(), - [&c](const auto &cc) { return cc.get() == c; }); -} - -common::optional_ref diagram::get_class(const std::string &name) const -{ - for (const auto &c : classes_) { - const auto full_name = c.get().full_name(false); - - if (full_name == name) { - return {c}; - } - } - - return {}; -} - -common::optional_ref diagram::get_class( - clanguml::common::model::diagram_element::id_t id) const -{ - for (const auto &c : classes_) { - if (c.get().id() == id) { - return {c}; - } - } - - return {}; -} - -common::optional_ref diagram::get_enum(const std::string &name) const -{ - for (const auto &e : enums_) { - if (e.get().full_name(false) == name) { - return {e}; - } - } - - return {}; -} - -common::optional_ref diagram::get_enum( - clanguml::common::model::diagram_element::id_t id) const -{ - for (const auto &e : enums_) { - if (e.get().id() == id) { - return {e}; - } - } - - return {}; -} - -common::optional_ref diagram::get_concept( - const std::string &name) const -{ - for (const auto &c : concepts_) { - const auto full_name = c.get().full_name(false); - - if (full_name == name) { - return {c}; - } - } - - return {}; -} - -common::optional_ref diagram::get_concept( - clanguml::common::model::diagram_element::id_t id) const -{ - for (const auto &c : concepts_) { - if (c.get().id() == id) { - return {c}; - } - } - - return {}; -} - -bool diagram::add_class( - const common::model::path &parent_path, std::unique_ptr &&c) -{ - if (parent_path.type() == common::model::path_type::kNamespace) { - return add_class_ns(std::move(c)); - } - - return add_class_fs(parent_path, std::move(c)); -} - -bool diagram::add_enum(const path &parent_path, std::unique_ptr &&e) -{ - if (parent_path.type() == common::model::path_type::kNamespace) { - return add_enum_ns(std::move(e)); - } - - return add_enum_fs(parent_path, std::move(e)); -} - -bool diagram::add_concept( - const path &parent_path, std::unique_ptr &&c) -{ - if (parent_path.type() == common::model::path_type::kNamespace) { - return add_concept_ns(std::move(c)); - } - - return add_concept_fs(parent_path, std::move(c)); -} - -bool diagram::add_package( - const path &parent_path, std::unique_ptr &&p) -{ - if (parent_path.type() == common::model::path_type::kNamespace) { - return add_package_ns(std::move(p)); - } - - return add_package_fs(parent_path, std::move(p)); -} - -bool diagram::add_package_fs( - const path &parent_path, std::unique_ptr &&p) -{ - LOG_DBG("Adding filesystem package: {}, {}", p->name(), p->full_name(true)); - - auto ns = p->get_relative_namespace(); - - return add_element(ns, std::move(p)); -} - -bool diagram::add_package_ns(std::unique_ptr &&p) +template <> +bool diagram::add_with_namespace_path( + std::unique_ptr &&p) { LOG_DBG("Adding namespace package: {}, {}", p->name(), p->full_name(true)); @@ -231,223 +93,16 @@ bool diagram::add_package_ns(std::unique_ptr &&p) return add_element(ns, std::move(p)); } -bool diagram::add_class_fs( - const common::model::path &parent_path, std::unique_ptr &&c) +template <> +bool diagram::add_with_filesystem_path( + const common::model::path &parent_path, + std::unique_ptr &&p) { - // Make sure all parent directories are already packages in the - // model - for (auto it = parent_path.begin(); it != parent_path.end(); it++) { - auto pkg = - std::make_unique(c->using_namespace()); - pkg->set_name(*it); - auto ns = common::model::path(parent_path.begin(), it); - // ns.pop_back(); - pkg->set_namespace(ns); - pkg->set_id(common::to_id(pkg->full_name(false))); + LOG_DBG("Adding filesystem package: {}, {}", p->name(), p->full_name(true)); - add_package_fs(ns, std::move(pkg)); - } + auto ns = p->get_relative_namespace(); - const auto base_name = c->name(); - const auto full_name = c->full_name(false); - const auto id = c->id(); - auto cc = std::ref(*c); - - if (add_element(parent_path, std::move(c))) { - classes_.push_back(cc); - return true; - } - else { - LOG_WARN("Cannot add class {} with id {} due to: {}", base_name, id); - } - - return false; -} - -bool diagram::add_class_ns(std::unique_ptr &&c) -{ - const auto base_name = c->name(); - const auto full_name = c->full_name(false); - - LOG_DBG("Adding class: {}::{}, {}", c->get_namespace().to_string(), - base_name, full_name); - - if (util::contains(base_name, "::")) - throw std::runtime_error("Name cannot contain namespace: " + base_name); - - if (util::contains(base_name, "*")) - throw std::runtime_error("Name cannot contain *: " + base_name); - - const auto ns = c->get_relative_namespace(); - auto name = base_name; - auto name_with_ns = c->name_and_ns(); - auto name_and_ns = ns | name; - auto &cc = *c; - auto id = cc.id(); - - try { - if (!has_class(cc)) { - if (add_element(ns, std::move(c))) - classes_.push_back(std::ref(cc)); - - const auto &el = get_element(name_and_ns).value(); - - if ((el.name() != name) || !(el.get_relative_namespace() == ns)) - throw std::runtime_error( - "Invalid element stored in the diagram tree"); - - LOG_DBG("Added class {} ({} - [{}])", base_name, full_name, id); - - return true; - } - } - catch (const std::runtime_error &e) { - LOG_WARN( - "Cannot add class {} with id {} due to: {}", name, id, e.what()); - return false; - } - - LOG_DBG( - "Class {} ({} - [{}]) already in the model", base_name, full_name, id); - - return false; -} - -bool diagram::add_enum_ns(std::unique_ptr &&e) -{ - const auto full_name = e->name(); - - LOG_DBG("Adding enum: {}", full_name); - - assert(!util::contains(e->name(), "::")); - - auto e_ref = std::ref(*e); - auto ns = e->get_relative_namespace(); - - if (!has_enum(*e)) { - if (add_element(ns, std::move(e))) { - enums_.emplace_back(e_ref); - return true; - } - } - - LOG_DBG("Enum {} already in the model", full_name); - - return false; -} - -bool diagram::add_enum_fs( - const common::model::path &parent_path, std::unique_ptr &&e) -{ - // Make sure all parent directories are already packages in the - // model - for (auto it = parent_path.begin(); it != parent_path.end(); it++) { - auto pkg = - std::make_unique(e->using_namespace()); - pkg->set_name(*it); - auto ns = common::model::path(parent_path.begin(), it); - // ns.pop_back(); - pkg->set_namespace(ns); - pkg->set_id(common::to_id(pkg->full_name(false))); - - add_package_fs(ns, std::move(pkg)); - } - - const auto base_name = e->name(); - const auto full_name = e->full_name(false); - const auto id = e->id(); - auto &cc = *e; - - if (add_element(parent_path, std::move(e))) { - enums_.push_back(std::ref(cc)); - return true; - } - else { - LOG_WARN("Cannot add class {} with id {} due to: {}", base_name, id); - } - - return false; -} - -bool diagram::add_concept_ns(std::unique_ptr &&c) -{ - const auto base_name = c->name(); - const auto full_name = c->full_name(false); - - LOG_DBG("Adding concept: {}::{}, {}", c->get_namespace().to_string(), - base_name, full_name); - - if (util::contains(base_name, "::")) - throw std::runtime_error("Name cannot contain namespace: " + base_name); - - if (util::contains(base_name, "*")) - throw std::runtime_error("Name cannot contain *: " + base_name); - - const auto ns = c->get_relative_namespace(); - auto name = base_name; - auto name_with_ns = c->name_and_ns(); - auto name_and_ns = ns | name; - auto &cc = *c; - auto id = cc.id(); - - try { - if (!has_concept(cc)) { - if (add_element(ns, std::move(c))) - concepts_.push_back(std::ref(cc)); - - const auto &el = get_element(name_and_ns).value(); - - if ((el.name() != name) || !(el.get_relative_namespace() == ns)) - throw std::runtime_error( - "Invalid element stored in the diagram tree"); - - LOG_DBG("Added concept {} ({} - [{}])", base_name, full_name, id); - - return true; - } - } - catch (const std::runtime_error &e) { - LOG_WARN( - "Cannot add concept {} with id {} due to: {}", name, id, e.what()); - return false; - } - - LOG_DBG("Concept {} ({} - [{}]) already in the model", base_name, full_name, - id); - - return false; -} - -bool diagram::add_concept_fs( - const common::model::path &parent_path, std::unique_ptr &&c) -{ - // Make sure all parent directories are already packages in the - // model - for (auto it = parent_path.begin(); it != parent_path.end(); it++) { - auto pkg = - std::make_unique(c->using_namespace()); - pkg->set_name(*it); - auto ns = common::model::path(parent_path.begin(), it); - // ns.pop_back(); - pkg->set_namespace(ns); - pkg->set_id(common::to_id(pkg->full_name(false))); - - add_package_fs(ns, std::move(pkg)); - } - - const auto base_name = c->name(); - const auto full_name = c->full_name(false); - const auto id = c->id(); - auto &cc = *c; - - if (add_element(parent_path, std::move(c))) { - concepts_.push_back(std::ref(cc)); - return true; - } - - LOG_WARN("Cannot add class {} with id {} due to: {}", base_name, id); - - return false; + return add_element(ns, std::move(p)); } void diagram::get_parents( @@ -456,7 +111,7 @@ void diagram::get_parents( bool found_new{false}; for (const auto &parent : parents) { for (const auto &pp : parent.get().parents()) { - auto p = get_class(pp.id()); + auto p = find(pp.id()); if (p.has_value()) { auto [it, found] = parents.emplace(std::ref(p.value())); @@ -474,13 +129,19 @@ void diagram::get_parents( bool diagram::has_element( clanguml::common::model::diagram_element::id_t id) const { - const auto has_class = std::any_of(classes_.begin(), classes_.end(), + const auto has_class = std::any_of(classes().begin(), classes().end(), [id](const auto &c) { return c.get().id() == id; }); if (has_class) return true; - return std::any_of(enums_.begin(), enums_.end(), + const auto has_concept = std::any_of(classes().begin(), classes().end(), + [id](const auto &c) { return c.get().id() == id; }); + + if (has_concept) + return true; + + return std::any_of(enums().begin(), enums().end(), [id](const auto &c) { return c.get().id() == id; }); } @@ -489,18 +150,18 @@ std::string diagram::to_alias( { LOG_DBG("Looking for alias for {}", id); - for (const auto &c : classes_) { + for (const auto &c : classes()) { if (c.get().id() == id) { return c.get().alias(); } } - for (const auto &e : enums_) { + for (const auto &e : enums()) { if (e.get().id() == id) return e.get().alias(); } - for (const auto &c : concepts_) { + for (const auto &c : concepts()) { if (c.get().id() == id) return c.get().alias(); } diff --git a/src/class_diagram/model/diagram.h b/src/class_diagram/model/diagram.h index 1d566ac7..47b42602 100644 --- a/src/class_diagram/model/diagram.h +++ b/src/class_diagram/model/diagram.h @@ -19,6 +19,7 @@ #include "class.h" #include "common/model/diagram.h" +#include "common/model/element_view.h" #include "common/model/nested_trait.h" #include "common/model/package.h" #include "common/types.h" @@ -34,6 +35,7 @@ namespace clanguml::class_diagram::model { using common::opt_ref; using common::model::diagram_element; using common::model::diagram_t; +using common::model::element_view; using common::model::path; using common::model::path_type; @@ -41,7 +43,11 @@ using nested_trait_ns = clanguml::common::model::nested_trait; -class diagram : public common::model::diagram::diagram, public nested_trait_ns { +class diagram : public common::model::diagram::diagram, + public element_view, + public element_view, + public element_view, + public nested_trait_ns { public: diagram() = default; @@ -62,32 +68,23 @@ public: const common::reference_vector &concepts() const; - bool has_class(const class_ &c) const; + template bool contains(const ElementT &e); - bool has_enum(const enum_ &e) const; + template + opt_ref find(const std::string &name) const; - bool has_concept(const concept_ &e) const; + template + opt_ref find(diagram_element::id_t id) const; - opt_ref get_class(const std::string &name) const; + template + bool add(const path &parent_path, std::unique_ptr &&e) + { + if (parent_path.type() == common::model::path_type::kNamespace) { + return add_with_namespace_path(std::move(e)); + } - opt_ref get_class(diagram_element::id_t id) const; - - opt_ref get_enum(const std::string &name) const; - - opt_ref get_enum(diagram_element::id_t id) const; - - opt_ref get_concept(const std::string &name) const; - - opt_ref get_concept(diagram_element::id_t id) const; - - bool add_class(const path &parent_path, std::unique_ptr &&c); - - bool add_enum(const path &parent_path, std::unique_ptr &&e); - - bool add_concept(const path &parent_path, std::unique_ptr &&e); - - bool add_package( - const path &parent_path, std::unique_ptr &&p); + return add_with_filesystem_path(parent_path, std::move(e)); + } std::string to_alias(diagram_element::id_t id) const; @@ -100,28 +97,148 @@ public: inja::json context() const override; private: - bool add_class_ns(std::unique_ptr &&c); - bool add_class_fs( - const common::model::path &parent_path, std::unique_ptr &&c); + template + bool add_with_namespace_path(std::unique_ptr &&e); - bool add_enum_ns(std::unique_ptr &&c); - bool add_enum_fs( - const common::model::path &parent_path, std::unique_ptr &&c); - - bool add_package_ns(std::unique_ptr &&p); - bool add_package_fs(const common::model::path &parent_path, - std::unique_ptr &&p); - - bool add_concept_ns(std::unique_ptr &&p); - bool add_concept_fs( - const common::model::path &parent_path, std::unique_ptr &&p); - - common::reference_vector classes_; - - common::reference_vector enums_; - - common::reference_vector concepts_; + template + bool add_with_filesystem_path( + const common::model::path &parent_path, std::unique_ptr &&e); }; + +template bool diagram::contains(const ElementT &element) +{ + return std::any_of(element_view::view().cbegin(), + element_view::view().cend(), + [&element]( + const auto &element_opt) { return element_opt.get() == element; }); +} + +template +bool diagram::add_with_namespace_path(std::unique_ptr &&e) +{ + const auto base_name = e->name(); + const auto full_name = e->full_name(false); + const auto element_type = e->type_name(); + + LOG_DBG("Adding {}: {}::{}, {}", element_type, + e->get_namespace().to_string(), base_name, full_name); + + if (util::contains(base_name, "::")) + throw std::runtime_error("Name cannot contain namespace: " + base_name); + + if (util::contains(base_name, "*")) + throw std::runtime_error("Name cannot contain *: " + base_name); + + const auto ns = e->get_relative_namespace(); + auto name = base_name; + auto name_with_ns = e->name_and_ns(); + auto name_and_ns = ns | name; + auto &e_ref = *e; + auto id = e_ref.id(); + + try { + if (!contains(e_ref)) { + if (add_element(ns, std::move(e))) + element_view::add(std::ref(e_ref)); + + const auto &el = get_element(name_and_ns).value(); + + if ((el.name() != name) || !(el.get_relative_namespace() == ns)) + throw std::runtime_error( + "Invalid element stored in the diagram tree"); + + LOG_DBG("Added {} {} ({} - [{}])", element_type, base_name, + full_name, id); + + return true; + } + } + catch (const std::runtime_error &e) { + LOG_WARN("Cannot add {} {} with id {} due to: {}", element_type, name, + id, e.what()); + return false; + } + + LOG_DBG("{} {} ({} - [{}]) already in the model", element_type, base_name, + full_name, id); + + return false; +} + +template +bool diagram::add_with_filesystem_path( + const common::model::path &parent_path, std::unique_ptr &&e) +{ + const auto element_type = e->type_name(); + + // Make sure all parent directories are already packages in the + // model + for (auto it = parent_path.begin(); it != parent_path.end(); it++) { + auto pkg = + std::make_unique(e->using_namespace()); + pkg->set_name(*it); + auto ns = common::model::path(parent_path.begin(), it); + // ns.pop_back(); + pkg->set_namespace(ns); + pkg->set_id(common::to_id(pkg->full_name(false))); + + add(ns, std::move(pkg)); + } + + const auto base_name = e->name(); + const auto full_name = e->full_name(false); + const auto id = e->id(); + auto &e_ref = *e; + + if (add_element(parent_path, std::move(e))) { + element_view::add(std::ref(e_ref)); + return true; + } + + LOG_WARN( + "Cannot add {} {} with id {} due to: {}", element_type, base_name, id); + + return false; +} + +template +opt_ref diagram::find(const std::string &name) const +{ + for (const auto &element : element_view::view()) { + const auto full_name = element.get().full_name(false); + + if (full_name == name) { + return {element}; + } + } + + return {}; +} + +template +opt_ref diagram::find(diagram_element::id_t id) const +{ + for (const auto &element : element_view::view()) { + if (element.get().id() == id) { + return {element}; + } + } + + return {}; +} + +// +// Template method specialization pre-declarations... +// +template <> +bool diagram::add_with_namespace_path( + std::unique_ptr &&p); + +template <> +bool diagram::add_with_filesystem_path( + const common::model::path &parent_path, + std::unique_ptr &&p); + } // namespace clanguml::class_diagram::model namespace clanguml::common::model { diff --git a/src/class_diagram/visitor/translation_unit_visitor.cc b/src/class_diagram/visitor/translation_unit_visitor.cc index 39d66b85..05c89c08 100644 --- a/src/class_diagram/visitor/translation_unit_visitor.cc +++ b/src/class_diagram/visitor/translation_unit_visitor.cc @@ -84,7 +84,7 @@ bool translation_unit_visitor::VisitNamespaceDecl(clang::NamespaceDecl *ns) } if (!p->skip()) { - diagram().add_package(package_path, std::move(p)); + diagram().add(package_path, std::move(p)); } } @@ -140,8 +140,8 @@ bool translation_unit_visitor::VisitEnumDecl(clang::EnumDecl *enm) } } - if (id_opt && diagram_.get_class(*id_opt)) { - auto parent_class = diagram_.get_class(*id_opt); + if (id_opt && diagram().find(*id_opt)) { + auto parent_class = diagram().find(*id_opt); e.set_namespace(ns); e.set_name(parent_class.value().name() + "##" + enm->getNameAsString()); @@ -364,8 +364,8 @@ bool translation_unit_visitor::VisitRecordDecl(clang::RecordDecl *rec) id_mapper().add(rec->getID(), rec_id); - auto &record_model = diagram().get_class(rec_id).has_value() - ? *diagram().get_class(rec_id).get() + auto &record_model = diagram().find(rec_id).has_value() + ? *diagram().find(rec_id).get() : *record_ptr; if (rec->isCompleteDefinition() && !record_model.complete()) { @@ -730,8 +730,8 @@ bool translation_unit_visitor::VisitCXXRecordDecl(clang::CXXRecordDecl *cls) id_mapper().add(cls->getID(), cls_id); - auto &class_model = diagram().get_class(cls_id).has_value() - ? *diagram().get_class(cls_id).get() + auto &class_model = diagram().find(cls_id).has_value() + ? *diagram().find(cls_id).get() : *c_ptr; if (cls->isCompleteDefinition() && !class_model.complete()) @@ -899,11 +899,11 @@ void translation_unit_visitor::process_record_parent( } } - if (id_opt && diagram_.get_class(*id_opt)) { + if (id_opt && diagram_.find(*id_opt)) { // Here we have 2 options, either: // - the parent is a regular C++ class/struct // - the parent is a class template declaration/specialization - auto parent_class = diagram_.get_class(*id_opt); + auto parent_class = diagram_.find(*id_opt); c.set_namespace(parent_ns); const auto cls_name = cls->getNameAsString(); @@ -2105,10 +2105,10 @@ void translation_unit_visitor::add_class(std::unique_ptr &&c) common::model::path p{file, common::model::path_type::kFilesystem}; p.pop_back(); - diagram().add_class(p, std::move(c)); + diagram().add(p, std::move(c)); } else { - diagram().add_class(c->path(), std::move(c)); + diagram().add(c->path(), std::move(c)); } } @@ -2123,10 +2123,10 @@ void translation_unit_visitor::add_enum(std::unique_ptr &&e) common::model::path p{file, common::model::path_type::kFilesystem}; p.pop_back(); - diagram().add_enum(p, std::move(e)); + diagram().add(p, std::move(e)); } else { - diagram().add_enum(e->path(), std::move(e)); + diagram().add(e->path(), std::move(e)); } } @@ -2141,10 +2141,10 @@ void translation_unit_visitor::add_concept(std::unique_ptr &&c) common::model::path p{file, common::model::path_type::kFilesystem}; p.pop_back(); - diagram().add_concept(p, std::move(c)); + diagram().add(p, std::move(c)); } else { - diagram().add_concept(c->path(), std::move(c)); + diagram().add(c->path(), std::move(c)); } } diff --git a/src/common/model/diagram_filter.cc b/src/common/model/diagram_filter.cc index 3ab7080f..27e5b026 100644 --- a/src/common/model/diagram_filter.cc +++ b/src/common/model/diagram_filter.cc @@ -61,21 +61,21 @@ template <> const clanguml::common::optional_ref get( const class_diagram::model::diagram &d, const std::string &full_name) { - return d.get_class(full_name); + return d.find(full_name); } template <> const clanguml::common::optional_ref get( const package_diagram::model::diagram &d, const std::string &full_name) { - return d.get_package(full_name); + return d.find(full_name); } template <> const clanguml::common::optional_ref get( const include_diagram::model::diagram &d, const std::string &full_name) { - return d.get_file(full_name); + return d.find(full_name); } template <> @@ -259,7 +259,7 @@ tvl::value_t subclass_filter::match(const diagram &d, const element &e) const clanguml::common::reference_set parents; const auto &fn = e.full_name(false); - auto class_ref = cd.get_class(fn); + auto class_ref = cd.find(fn); if (!class_ref.has_value()) return false; @@ -308,7 +308,7 @@ tvl::value_t parents_filter::match(const diagram &d, const element &e) const clanguml::common::reference_set parents; for (const auto &child : children_) { - auto child_ref = cd.get_class(child); + auto child_ref = cd.find(child); if (!child_ref.has_value()) continue; parents.emplace(child_ref.value()); @@ -370,8 +370,8 @@ tvl::value_t context_filter::match(const diagram &d, const element &e) const return tvl::any_of(context_.begin(), context_.end(), [&e, &d](const auto &context_root_name) { const auto &context_root = - static_cast(d).get_class( - context_root_name); + static_cast(d) + .find(context_root_name); if (context_root.has_value()) { // This is a direct match to the context root diff --git a/src/common/model/element_view.h b/src/common/model/element_view.h new file mode 100644 index 00000000..3e124341 --- /dev/null +++ b/src/common/model/element_view.h @@ -0,0 +1,70 @@ +/** + * src/common/model/element_view.h + * + * Copyright (c) 2021-2023 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 "common/types.h" + +namespace clanguml::common::model { + +/** + * Provides type based views over elements in a diagram. + * + * @tparam T Type of diagram element + */ +template class element_view { +public: + /** + * @brief Add reference to diagram element + * + * @param element Reference to diagram element of specific type + */ + void add(std::reference_wrapper element) + { + elements_.emplace_back(std::move(element)); + } + + /** + * @brief Get collection of reference to diagram elements + * + * @return + */ + const reference_vector &view() const { return elements_; } + + /** + * @brief Get typed diagram element by id + * @param id Global id of a diagram element + * + * @return + */ + common::optional_ref get( + clanguml::common::model::diagram_element::id_t id) const + { + for (const auto &e : elements_) { + if (e.get().id() == id) { + return {e}; + } + } + + return {}; + } + +private: + reference_vector elements_; +}; + +} // namespace clanguml::common::model \ No newline at end of file diff --git a/src/include_diagram/model/diagram.cc b/src/include_diagram/model/diagram.cc index cd2e71b5..3a723898 100644 --- a/src/include_diagram/model/diagram.cc +++ b/src/include_diagram/model/diagram.cc @@ -31,19 +31,19 @@ common::model::diagram_t diagram::type() const common::optional_ref diagram::get( const std::string &full_name) const { - return get_file(full_name); + return find(full_name); } common::optional_ref diagram::get( const common::model::diagram_element::id_t id) const { - return get_file(id); + return find(id); } void diagram::add_file(std::unique_ptr &&f) { // Don't add the same file more than once - if (get_file(f->id())) + if (find(f->id())) return; LOG_DBG("Adding source file: {}, {}", f->name(), f->fs_path().string()); @@ -53,7 +53,7 @@ void diagram::add_file(std::unique_ptr &&f) assert(!ff.name().empty()); assert(ff.id() != 0); - files_.emplace_back(ff); + element_view::add(ff); auto p = ff.path(); @@ -89,34 +89,6 @@ void diagram::add_file(std::unique_ptr &&f) add_element(p, std::move(f)); } -common::optional_ref diagram::get_file( - const std::string &name) const -{ - // Convert the name to the OS preferred path - std::filesystem::path namePath{name}; - namePath.make_preferred(); - - for (const auto &p : files_) { - if (p.get().full_name(false) == namePath.string()) { - return {p}; - } - } - - return {}; -} - -common::optional_ref diagram::get_file( - const common::model::diagram_element::id_t id) const -{ - for (const auto &p : files_) { - if (p.get().id() == id) { - return {p}; - } - } - - return {}; -} - std::string diagram::to_alias(const std::string &full_name) const { LOG_DBG("Looking for alias for {}", full_name); @@ -139,7 +111,7 @@ std::string diagram::to_alias(const std::string &full_name) const const common::reference_vector & diagram::files() const { - return files_; + return element_view::view(); } common::optional_ref diff --git a/src/include_diagram/model/diagram.h b/src/include_diagram/model/diagram.h index 6aea9cc9..82d606f4 100644 --- a/src/include_diagram/model/diagram.h +++ b/src/include_diagram/model/diagram.h @@ -18,6 +18,7 @@ #pragma once #include "common/model/diagram.h" +#include "common/model/element_view.h" #include "common/model/package.h" #include "common/model/source_file.h" #include "common/types.h" @@ -27,9 +28,13 @@ namespace clanguml::include_diagram::model { +using clanguml::common::opt_ref; +using clanguml::common::model::diagram_element; +using clanguml::common::model::source_file; + class diagram : public clanguml::common::model::diagram, - public clanguml::common::model::nested_trait< - clanguml::common::model::source_file, + public clanguml::common::model::element_view, + public clanguml::common::model::nested_trait { public: diagram() = default; @@ -41,33 +46,58 @@ public: common::model::diagram_t type() const override; - common::optional_ref get( - const std::string &full_name) const override; + opt_ref get(const std::string &full_name) const override; - common::optional_ref get( - common::model::diagram_element::id_t id) const override; + opt_ref get(diagram_element::id_t id) const override; void add_file(std::unique_ptr &&f); - common::optional_ref get_file( - const std::string &name) const; + template + opt_ref find(const std::string &name) const; - common::optional_ref get_file( - common::model::diagram_element::id_t id) const; + template + opt_ref find(diagram_element::id_t id) const; std::string to_alias(const std::string &full_name) const; - const common::reference_vector &files() const; + const common::reference_vector &files() const; - common::optional_ref - get_with_namespace(const std::string &name, + opt_ref get_with_namespace(const std::string &name, const common::model::namespace_ &ns) const override; inja::json context() const override; - -private: - common::reference_vector files_; }; + +template +opt_ref diagram::find(const std::string &name) const +{ + // Convert the name to the OS preferred path + std::filesystem::path namePath{name}; + namePath.make_preferred(); + + for (const auto &element : element_view::view()) { + const auto full_name = element.get().full_name(false); + + if (full_name == namePath.string()) { + return {element}; + } + } + + return {}; +} + +template +opt_ref diagram::find(diagram_element::id_t id) const +{ + for (const auto &element : element_view::view()) { + if (element.get().id() == id) { + return {element}; + } + } + + return {}; +} + } // namespace clanguml::include_diagram::model namespace clanguml::common::model { diff --git a/src/package_diagram/model/diagram.cc b/src/package_diagram/model/diagram.cc index dfe64537..b8f25258 100644 --- a/src/package_diagram/model/diagram.cc +++ b/src/package_diagram/model/diagram.cc @@ -31,75 +31,13 @@ common::model::diagram_t diagram::type() const const common::reference_vector & diagram::packages() const { - return packages_; -} - -void diagram::add_package(std::unique_ptr &&p) -{ - LOG_DBG( - "Adding package: {}, {}, [{}]", p->name(), p->full_name(true), p->id()); - - auto ns = p->get_relative_namespace(); - - packages_.emplace_back(*p); - - add_element(ns, std::move(p)); -} - -void diagram::add_package_fs(const common::model::path &parent_path, - std::unique_ptr &&p) -{ - LOG_DBG("Adding package: {}, {}", p->name(), p->full_name(true)); - - // Make sure all parent directories are already packages in the - // model - for (auto it = parent_path.begin(); it != parent_path.end(); it++) { - auto pkg = - std::make_unique(p->using_namespace()); - pkg->set_name(*it); - auto ns = common::model::path(parent_path.begin(), it); - // ns.pop_back(); - pkg->set_namespace(ns); - pkg->set_id(common::to_id(pkg->full_name(false))); - - add_package_fs(ns, std::move(pkg)); - } - - auto pp = std::ref(*p); - if (add_element(parent_path, std::move(p))) { - packages_.emplace_back(pp); - } -} - -common::optional_ref diagram::get_package( - const std::string &name) const -{ - for (const auto &p : packages_) { - auto p_full_name = p.get().full_name(false); - if (p_full_name == name) { - return {p}; - } - } - - return {}; -} - -common::optional_ref diagram::get_package( - const clanguml::common::model::diagram_element::id_t id) const -{ - for (const auto &p : packages_) { - if (p.get().id() == id) { - return {p}; - } - } - - return {}; + return element_view::view(); } common::optional_ref diagram::get( const std::string &full_name) const { - return get_package(full_name); + return find(full_name); } common::optional_ref diagram::get( @@ -107,7 +45,7 @@ common::optional_ref diagram::get( { LOG_DBG("Looking for package with id {}", id); - return get_package(id); + return find(id); } std::string diagram::to_alias( @@ -115,10 +53,9 @@ std::string diagram::to_alias( { LOG_DBG("Looking for alias for {}", id); - for (const auto &p : packages_) { - if (p.get().id() == id) - return p.get().alias(); - } + auto p = find(id); + if (p.has_value() && p.value().id() == id) + return p.value().alias(); return {}; } diff --git a/src/package_diagram/model/diagram.h b/src/package_diagram/model/diagram.h index 4c7cf528..1a4aadd2 100644 --- a/src/package_diagram/model/diagram.h +++ b/src/package_diagram/model/diagram.h @@ -18,6 +18,7 @@ #pragma once #include "common/model/diagram.h" +#include "common/model/element_view.h" #include "common/model/package.h" #include @@ -25,7 +26,13 @@ namespace clanguml::package_diagram::model { +using clanguml::common::opt_ref; +using clanguml::common::model::diagram_element; +using clanguml::common::model::package; +using clanguml::common::model::path; + class diagram : public clanguml::common::model::diagram, + public clanguml::common::model::element_view, public clanguml::common::model::nested_trait< clanguml::common::model::element, clanguml::common::model::namespace_> { @@ -39,34 +46,109 @@ public: common::model::diagram_t type() const override; - const common::reference_vector & - packages() const; + const common::reference_vector &packages() const; - common::optional_ref get( - const std::string &full_name) const override; + opt_ref get(const std::string &full_name) const override; - common::optional_ref get( - clanguml::common::model::diagram_element::id_t id) const override; + opt_ref get(diagram_element::id_t id) const override; - common::optional_ref get_package( - const std::string &name) const; + template + opt_ref find(const std::string &name) const; - common::optional_ref get_package( - clanguml::common::model::diagram_element::id_t id) const; + template + opt_ref find(diagram_element::id_t id) const; - void add_package(std::unique_ptr &&p); + template + bool add(const path &parent_path, std::unique_ptr &&e) + { + if (parent_path.type() == common::model::path_type::kNamespace) { + return add_with_namespace_path(std::move(e)); + } - void add_package_fs(const common::model::path &parent_path, - std::unique_ptr &&p); + return add_with_filesystem_path(parent_path, std::move(e)); + } - std::string to_alias( - clanguml::common::model::diagram_element::id_t /*id*/) const; + template + bool add_with_namespace_path(std::unique_ptr &&e); + + template + bool add_with_filesystem_path( + const common::model::path &parent_path, std::unique_ptr &&e); + + std::string to_alias(diagram_element::id_t /*id*/) const; inja::json context() const override; - -private: - common::reference_vector packages_; }; + +template +opt_ref diagram::find(const std::string &name) const +{ + for (const auto &element : element_view::view()) { + const auto full_name = element.get().full_name(false); + + if (full_name == name) { + return {element}; + } + } + + return {}; +} + +template +opt_ref diagram::find(diagram_element::id_t id) const +{ + for (const auto &element : element_view::view()) { + if (element.get().id() == id) { + return {element}; + } + } + + return {}; +} + +template +bool diagram::add_with_namespace_path(std::unique_ptr &&p) +{ + LOG_DBG( + "Adding package: {}, {}, [{}]", p->name(), p->full_name(true), p->id()); + + auto ns = p->get_relative_namespace(); + auto p_ref = std::ref(*p); + + auto res = add_element(ns, std::move(p)); + if (res) + element_view::add(p_ref); + + return res; +} + +template +bool diagram::add_with_filesystem_path( + const common::model::path &parent_path, std::unique_ptr &&p) +{ + LOG_DBG("Adding package: {}, {}", p->name(), p->full_name(true)); + + // Make sure all parent directories are already packages in the + // model + for (auto it = parent_path.begin(); it != parent_path.end(); it++) { + auto pkg = + std::make_unique(p->using_namespace()); + pkg->set_name(*it); + auto ns = common::model::path(parent_path.begin(), it); + pkg->set_namespace(ns); + pkg->set_id(common::to_id(pkg->full_name(false))); + + add_with_filesystem_path(ns, std::move(pkg)); + } + + auto pp = std::ref(*p); + auto res = add_element(parent_path, std::move(p)); + if (res) + element_view::add(pp); + + return res; +} + } // namespace clanguml::package_diagram::model namespace clanguml::common::model { diff --git a/src/package_diagram/visitor/translation_unit_visitor.cc b/src/package_diagram/visitor/translation_unit_visitor.cc index 5456cbe6..ae706c92 100644 --- a/src/package_diagram/visitor/translation_unit_visitor.cc +++ b/src/package_diagram/visitor/translation_unit_visitor.cc @@ -93,7 +93,7 @@ bool translation_unit_visitor::VisitNamespaceDecl(clang::NamespaceDecl *ns) } if (!p->skip()) { - diagram().add_package(std::move(p)); + diagram().add(p->path(), std::move(p)); } } @@ -222,7 +222,7 @@ void translation_unit_visitor::add_relationships( pkg->set_name(pkg_name); pkg->set_id(get_package_id(cls)); - diagram().add_package_fs(parent_path, std::move(pkg)); + diagram().add(parent_path, std::move(pkg)); } auto current_package_id = get_package_id(cls);