From a67b459437821d53faee7c8a78bcb0b7c2493603 Mon Sep 17 00:00:00 2001 From: Bartek Kryza Date: Fri, 4 Mar 2022 23:38:18 +0100 Subject: [PATCH] Initial refactor of namespace handling --- .../plantuml/class_diagram_generator.cc | 18 +- src/class_diagram/model/class.cc | 22 +- src/class_diagram/model/class.h | 2 +- src/class_diagram/model/diagram.cc | 2 +- src/class_diagram/model/enum.cc | 7 +- src/class_diagram/model/enum.h | 2 +- src/class_diagram/model/method_parameter.cc | 10 +- src/class_diagram/model/method_parameter.h | 3 +- .../visitor/translation_unit_context.cc | 8 +- .../visitor/translation_unit_context.h | 4 +- .../visitor/translation_unit_visitor.cc | 27 +-- src/common/generators/plantuml/generator.h | 11 +- src/common/model/element.cc | 21 +- src/common/model/element.h | 26 +-- src/common/model/namespace.cc | 213 ++++++++++++++++++ src/common/model/namespace.h | 87 +++++++ src/common/model/nested_trait.h | 35 +-- src/common/model/package.cc | 29 ++- src/common/model/package.h | 2 +- src/config/config.cc | 57 ++++- src/config/config.h | 19 +- src/config/option.h | 2 +- src/cx/util.cc | 11 +- src/cx/util.h | 8 +- .../plantuml/package_diagram_generator.cc | 4 +- src/package_diagram/model/diagram.cc | 10 +- .../visitor/translation_unit_context.cc | 4 +- .../visitor/translation_unit_context.h | 4 +- .../visitor/translation_unit_visitor.cc | 39 ++-- .../plantuml/sequence_diagram_generator.cc | 10 +- .../visitor/translation_unit_visitor.cc | 5 +- src/util/util.cc | 2 + src/util/util.h | 4 +- tests/t00002/test_case.h | 3 +- tests/t00003/test_case.h | 2 - tests/t00004/test_case.h | 3 - tests/test_config.cc | 4 +- tests/test_config_data/simple.yml | 3 +- tests/test_model.cc | 72 ++++++ tests/test_util.cc | 26 +-- 40 files changed, 620 insertions(+), 201 deletions(-) create mode 100644 src/common/model/namespace.cc create mode 100644 src/common/model/namespace.h create mode 100644 tests/test_model.cc diff --git a/src/class_diagram/generators/plantuml/class_diagram_generator.cc b/src/class_diagram/generators/plantuml/class_diagram_generator.cc index ace02486..3d2e7ce5 100644 --- a/src/class_diagram/generators/plantuml/class_diagram_generator.cc +++ b/src/class_diagram/generators/plantuml/class_diagram_generator.cc @@ -118,7 +118,7 @@ void generator::generate( if (m.is_defaulted()) ostr << " = default"; - ostr << " : " << ns_relative(uns, type); + ostr << " : " << uns.relative(type); ostr << '\n'; } @@ -154,9 +154,9 @@ void generator::generate( if (!r.multiplicity_destination().empty()) puml_relation += " \"" + r.multiplicity_destination() + "\""; - relstr << m_model.to_alias(ns_relative(uns, c.full_name())) << " " + relstr << m_model.to_alias(uns.relative(c.full_name())) << " " << puml_relation << " " - << m_model.to_alias(ns_relative(uns, destination)); + << m_model.to_alias(uns.relative(destination)); if (!r.label().empty()) { relstr << " : " << plantuml_common::to_plantuml(r.scope()) @@ -197,7 +197,7 @@ void generator::generate( ostr << "{static} "; ostr << plantuml_common::to_plantuml(m.scope()) << m.name() << " : " - << ns_relative(uns, m.type()) << '\n'; + << uns.relative(m.type()) << '\n'; } ostr << "}" << '\n'; @@ -206,10 +206,8 @@ void generator::generate( for (const auto &b : c.parents()) { std::stringstream relstr; try { - relstr << m_model.to_alias(ns_relative(uns, b.name())) - << " <|-- " - << m_model.to_alias(ns_relative(uns, c.full_name())) - << '\n'; + relstr << m_model.to_alias(uns.relative(b.name())) << " <|-- " + << m_model.to_alias(uns.relative(c.full_name())) << '\n'; all_relations_str << relstr.str(); } catch (error::uml_alias_missing &e) { @@ -253,13 +251,13 @@ void generator::generate( destination = r.destination(); relstr << m_model.to_alias( - ns_relative(m_config.using_namespace(), e.name())) + m_config.using_namespace().relative(e.name())) << " " << clanguml::common::generators::plantuml::to_plantuml( r.type(), r.style()) << " " << m_model.to_alias( - ns_relative(m_config.using_namespace(), destination)); + m_config.using_namespace().relative(destination)); if (!r.label().empty()) relstr << " : " << r.label(); diff --git a/src/class_diagram/model/class.cc b/src/class_diagram/model/class.cc index ef034e2c..004797f1 100644 --- a/src/class_diagram/model/class.cc +++ b/src/class_diagram/model/class.cc @@ -24,8 +24,8 @@ namespace clanguml::class_diagram::model { -class_::class_(const std::vector &using_namespaces) - : element{using_namespaces} +class_::class_(const common::model::namespace_ &using_namespace) + : element{using_namespace} { } @@ -112,10 +112,12 @@ std::string class_::full_name_no_ns() const std::string class_::full_name(bool relative) const { using namespace clanguml::util; + using clanguml::common::model::namespace_; std::ostringstream ostr; - if (relative && starts_with(get_namespace(), using_namespace())) - ostr << ns_relative(using_namespace(), name_and_ns()); + // if (relative && starts_with(get_namespace(), using_namespace())) + if (relative) + ostr << namespace_{name()}.relative_to(using_namespace()).to_string(); else ostr << name_and_ns(); @@ -126,6 +128,8 @@ std::string class_::full_name(bool relative) const std::ostringstream &class_::render_template_params( std::ostringstream &ostr) const { + using clanguml::common::model::namespace_; + if (!templates_.empty()) { std::vector tnames; std::transform(templates_.cbegin(), templates_.cend(), @@ -133,12 +137,14 @@ std::ostringstream &class_::render_template_params( std::vector res; if (!tmplt.type().empty()) - res.push_back( - util::ns_relative(using_namespace(), tmplt.type())); + res.push_back(namespace_{tmplt.type()} + .relative_to(using_namespace()) + .to_string()); if (!tmplt.name().empty()) - res.push_back( - util::ns_relative(using_namespace(), tmplt.name())); + res.push_back(namespace_{tmplt.name()} + .relative_to(using_namespace()) + .to_string()); if (!tmplt.default_value().empty()) { res.push_back("="); diff --git a/src/class_diagram/model/class.h b/src/class_diagram/model/class.h index 62de2f15..5d17ad84 100644 --- a/src/class_diagram/model/class.h +++ b/src/class_diagram/model/class.h @@ -34,7 +34,7 @@ namespace clanguml::class_diagram::model { class class_ : public common::model::element, public common::model::stylable_element { public: - class_(const std::vector &using_namespaces); + class_(const common::model::namespace_ &using_namespace); class_(const class_ &) = delete; class_(class_ &&) noexcept = delete; diff --git a/src/class_diagram/model/diagram.cc b/src/class_diagram/model/diagram.cc index 80c9c810..00687c11 100644 --- a/src/class_diagram/model/diagram.cc +++ b/src/class_diagram/model/diagram.cc @@ -82,7 +82,7 @@ void diagram::add_class(std::unique_ptr &&c) auto ns = c->get_relative_namespace(); auto name = c->name(); add_element(ns, std::move(c)); - ns.push_back(name); + ns |= name; const auto ccc = get_element(ns); assert(ccc.value().name() == name); } diff --git a/src/class_diagram/model/enum.cc b/src/class_diagram/model/enum.cc index a6318135..2343559f 100644 --- a/src/class_diagram/model/enum.cc +++ b/src/class_diagram/model/enum.cc @@ -24,8 +24,8 @@ namespace clanguml::class_diagram::model { -enum_::enum_(const std::vector &using_namespaces) - : element{using_namespaces} +enum_::enum_(const common::model::namespace_ &using_namespace) + : element{using_namespace} { } @@ -37,10 +37,11 @@ bool operator==(const enum_ &l, const enum_ &r) std::string enum_::full_name(bool relative) const { using namespace clanguml::util; + using clanguml::common::model::namespace_; std::ostringstream ostr; if (relative) - ostr << ns_relative(using_namespace(), name()); + ostr << namespace_{name()}.relative_to(using_namespace()).to_string(); else ostr << name(); diff --git a/src/class_diagram/model/enum.h b/src/class_diagram/model/enum.h index f54da03e..ef6025ce 100644 --- a/src/class_diagram/model/enum.h +++ b/src/class_diagram/model/enum.h @@ -27,7 +27,7 @@ namespace clanguml::class_diagram::model { class enum_ : public common::model::element, public common::model::stylable_element { public: - enum_(const std::vector &using_namespaces); + enum_(const common::model::namespace_ &using_namespaces); enum_(const enum_ &) = delete; enum_(enum_ &&) = default; diff --git a/src/class_diagram/model/method_parameter.cc b/src/class_diagram/model/method_parameter.cc index f17b9094..2464e458 100644 --- a/src/class_diagram/model/method_parameter.cc +++ b/src/class_diagram/model/method_parameter.cc @@ -38,14 +38,16 @@ void method_parameter::set_default_value(const std::string &value) std::string method_parameter::default_value() const { return default_value_; } std::string method_parameter::to_string( - const std::vector &using_namespaces) const + const common::model::namespace_ &using_namespace) const { using namespace clanguml::util; - auto t = ns_relative(using_namespaces, type()); + auto type_ns = common::model::namespace_{type()} + .relative_to(using_namespace) + .to_string(); if (default_value().empty()) - return fmt::format("{} {}", t, name()); + return fmt::format("{} {}", type_ns, name()); - return fmt::format("{} {} = {}", t, name(), default_value()); + return fmt::format("{} {} = {}", type_ns, name(), default_value()); } } diff --git a/src/class_diagram/model/method_parameter.h b/src/class_diagram/model/method_parameter.h index 5eceadb6..04fdaea5 100644 --- a/src/class_diagram/model/method_parameter.h +++ b/src/class_diagram/model/method_parameter.h @@ -18,6 +18,7 @@ #pragma once #include "common/model/decorated_element.h" +#include "common/model/namespace.h" #include #include @@ -36,7 +37,7 @@ public: std::string default_value() const; std::string to_string( - const std::vector &using_namespaces) const; + const common::model::namespace_ &using_namespaces) const; private: std::string type_; diff --git a/src/class_diagram/visitor/translation_unit_context.cc b/src/class_diagram/visitor/translation_unit_context.cc index 7d9f612d..dd66ed05 100644 --- a/src/class_diagram/visitor/translation_unit_context.cc +++ b/src/class_diagram/visitor/translation_unit_context.cc @@ -154,14 +154,14 @@ translation_unit_context::get_type_alias_template( void translation_unit_context::push_namespace(const std::string &ns) { - namespace_.push_back(ns); + ns_ |= ns; } -void translation_unit_context::pop_namespace() { namespace_.pop_back(); } +void translation_unit_context::pop_namespace() { ns_.pop_back(); } -const std::vector &translation_unit_context::get_namespace() const +const common::model::namespace_ &translation_unit_context::get_namespace() const { - return namespace_; + return ns_; } const cppast::cpp_entity_index &translation_unit_context::entity_index() const diff --git a/src/class_diagram/visitor/translation_unit_context.h b/src/class_diagram/visitor/translation_unit_context.h index 453d2805..7775d89d 100644 --- a/src/class_diagram/visitor/translation_unit_context.h +++ b/src/class_diagram/visitor/translation_unit_context.h @@ -66,7 +66,7 @@ public: void pop_namespace(); - const std::vector &get_namespace() const; + const common::model::namespace_ &get_namespace() const; const cppast::cpp_entity_index &entity_index() const; @@ -80,7 +80,7 @@ public: private: // Current visitor namespace - std::vector namespace_; + common::model::namespace_ ns_; // Reference to the cppast entity index cppast::cpp_entity_index &entity_index_; diff --git a/src/class_diagram/visitor/translation_unit_visitor.cc b/src/class_diagram/visitor/translation_unit_visitor.cc index ebbfda63..0f98de94 100644 --- a/src/class_diagram/visitor/translation_unit_visitor.cc +++ b/src/class_diagram/visitor/translation_unit_visitor.cc @@ -200,7 +200,7 @@ void translation_unit_visitor::process_type_alias_template( cx::util::full_name(ctx.get_namespace(), at), type_safe::ref(at.type_alias().underlying_type())); - if (ctx.config().should_include(tinst->get_namespace(), tinst->name())) + if (ctx.config().should_include(tinst->get_namespace() | tinst->name())) ctx.diagram().add_class(std::move(tinst)); } } @@ -222,15 +222,14 @@ void translation_unit_visitor::process_type_alias( void translation_unit_visitor::process_namespace( const cppast::cpp_entity &e, const cppast::cpp_namespace &ns_declaration) { - std::vector package_parent = ctx.get_namespace(); - auto package_path = package_parent; - package_path.push_back(e.name()); + auto package_parent = ctx.get_namespace(); + auto package_path = package_parent | e.name(); - auto usn = util::split(ctx.config().using_namespace()[0], "::"); + auto usn = ctx.config().using_namespace(); - if (ctx.config().should_include_package(util::join(package_path, "::"))) { + if (ctx.config().should_include_package(package_path)) { auto p = std::make_unique(usn); - util::remove_prefix(package_path, usn); + package_path = package_path.relative_to(usn); p->set_name(e.name()); p->set_namespace(package_parent); @@ -267,8 +266,7 @@ void translation_unit_visitor::process_enum_declaration( return; } - auto e_ptr = std::make_unique( - util::split(ctx.config().using_namespace()[0], "::")); + auto e_ptr = std::make_unique(ctx.config().using_namespace()); auto &e = *e_ptr; e.set_name(enm.name()); e.set_namespace(ctx.get_namespace()); @@ -312,8 +310,7 @@ void translation_unit_visitor::process_class_declaration( const cppast::cpp_class &cls, type_safe::optional_ref tspec) { - auto c_ptr = std::make_unique( - util::split(ctx.config().using_namespace()[0], "::")); + auto c_ptr = std::make_unique(ctx.config().using_namespace()); auto &c = *c_ptr; c.is_struct(cls.class_kind() == cppast::cpp_class_kind::struct_t); // c.set_name(cx::util::full_name(ctx.get_namespace(), cls)); @@ -1275,8 +1272,7 @@ bool translation_unit_visitor::find_relationships_in_template_instantiation( const auto [ns, base_name] = cx::util::split_ns(fn); - auto ns_and_name = ns; - ns_and_name.push_back(base_name); + auto ns_and_name = ns | base_name; auto full_name = fmt::format("{}", fmt::join(ns_and_name, "::")); @@ -1403,8 +1399,7 @@ std::unique_ptr translation_unit_visitor::build_template_instantiation( std::optional parent) { // Create class_ instance to hold the template instantiation - auto tinst_ptr = std::make_unique( - util::split(ctx.config().using_namespace()[0], "::")); + auto tinst_ptr = std::make_unique(ctx.config().using_namespace()); auto &tinst = *tinst_ptr; std::string full_template_name; @@ -1430,7 +1425,7 @@ std::unique_ptr translation_unit_visitor::build_template_instantiation( // Extract namespace from base template name const auto [ns, name] = cx::util::split_ns(tinst_full_name); tinst.set_name(name); - if (ns.empty()) + if (ns.is_empty()) tinst.set_namespace(ctx.get_namespace()); else tinst.set_namespace(ns); diff --git a/src/common/generators/plantuml/generator.h b/src/common/generators/plantuml/generator.h index 4451e6fd..6169c183 100644 --- a/src/common/generators/plantuml/generator.h +++ b/src/common/generators/plantuml/generator.h @@ -83,11 +83,10 @@ void generator::generate_config_layout_hints(std::ostream &ostr) const for (const auto &hint : hints) { std::stringstream hint_str; try { - hint_str << m_model.to_alias(ns_relative(uns, entity)) + hint_str << m_model.to_alias(uns.relative(entity)) << " -[hidden]" << clanguml::config::to_string(hint.hint) << "- " - << m_model.to_alias(ns_relative(uns, hint.entity)) - << '\n'; + << m_model.to_alias(uns.relative(hint.entity)) << '\n'; ostr << hint_str.str(); } catch (clanguml::error::uml_alias_missing &e) { @@ -103,12 +102,14 @@ template void generator::generate_plantuml_directives( std::ostream &ostr, const std::vector &directives) const { + using common::model::namespace_; + for (const auto &b : directives) { std::string note{b}; std::tuple alias_match; while (util::find_element_alias(note, alias_match)) { - auto alias = m_model.to_alias(util::ns_relative( - m_config.using_namespace(), std::get<0>(alias_match))); + auto alias = m_model.to_alias( + m_config.using_namespace().relative(std::get<0>(alias_match))); note.replace( std::get<1>(alias_match), std::get<2>(alias_match), alias); } diff --git a/src/common/model/element.cc b/src/common/model/element.cc index e5044cf3..aa8c0427 100644 --- a/src/common/model/element.cc +++ b/src/common/model/element.cc @@ -26,12 +26,10 @@ namespace clanguml::common::model { std::atomic_uint64_t element::m_nextId = 1; -element::element(const std::vector &using_namespaces) - : using_namespace_{using_namespaces} +element::element(const namespace_ &using_namespace) + : using_namespace_{using_namespace} , m_id{m_nextId++} { - for (const auto &n : using_namespace_) - assert(!util::contains(n, "::")); } std::string element::alias() const { return fmt::format("C_{:010}", m_id); } @@ -59,18 +57,12 @@ void element::add_relationship(relationship &&cr) relationships_.emplace_back(std::move(cr)); } -void element::set_using_namespaces(const std::vector &un) +void element::set_using_namespaces(const namespace_ &un) { - for (const auto &n : un) - assert(!util::contains(n, "::")); - using_namespace_ = un; } -const std::vector &element::using_namespace() const -{ - return using_namespace_; -} +const namespace_ &element::using_namespace() const { return ns_; } std::vector &element::relationships() { return relationships_; } @@ -88,9 +80,8 @@ bool operator==(const element &l, const element &r) std::ostream &operator<<(std::ostream &out, const element &rhs) { - out << "(" << rhs.name() << ", ns=[" - << util::join(rhs.get_namespace(), "::") << "], full_name=[" - << rhs.full_name(true) << "])"; + out << "(" << rhs.name() << ", ns=[" << rhs.get_namespace().to_string() + << "], full_name=[" << rhs.full_name(true) << "])"; return out; } diff --git a/src/common/model/element.h b/src/common/model/element.h index 40e26cbf..bb8985ba 100644 --- a/src/common/model/element.h +++ b/src/common/model/element.h @@ -18,6 +18,7 @@ #pragma once #include "decorated_element.h" +#include "namespace.h" #include "relationship.h" #include "util/util.h" @@ -30,7 +31,7 @@ namespace clanguml::common::model { class element : public decorated_element { public: - element(const std::vector &using_namespaces); + element(const namespace_ &using_namespace); virtual ~element() = default; @@ -42,27 +43,24 @@ public: std::string name_and_ns() const { - auto ns = namespace_; - ns.push_back(name()); - return util::join(ns, "::"); + auto ns = ns_ | name(); + return ns.to_string(); } - void set_namespace(const std::vector &ns) { namespace_ = ns; } + void set_namespace(const namespace_ &ns) { ns_ = ns; } - std::vector get_namespace() const { return namespace_; } + namespace_ get_namespace() const { return ns_; } - std::vector get_relative_namespace() const + namespace_ get_relative_namespace() const { - auto relative_ns = namespace_; - util::remove_prefix(relative_ns, using_namespace_); - return relative_ns; + return ns_.relative_to(using_namespace_); } virtual std::string full_name(bool relative) const { return name(); } - void set_using_namespaces(const std::vector &un); + void set_using_namespaces(const namespace_ &un); - const std::vector &using_namespace() const; + const namespace_ &using_namespace() const; std::vector &relationships(); @@ -81,8 +79,8 @@ protected: private: std::string name_; - std::vector namespace_; - std::vector using_namespace_; + namespace_ ns_; + namespace_ using_namespace_; std::vector relationships_; static std::atomic_uint64_t m_nextId; diff --git a/src/common/model/namespace.cc b/src/common/model/namespace.cc new file mode 100644 index 00000000..0cc2c139 --- /dev/null +++ b/src/common/model/namespace.cc @@ -0,0 +1,213 @@ +/** + * src/common/model/namespace.h + * + * Copyright (c) 2021-2022 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. + */ + +#include "namespace.h" + +#include "util/util.h" + +namespace clanguml::common::model { + +namespace_::namespace_(std::initializer_list ns) +{ + if ((ns.size() == 1) && util::contains(*ns.begin(), "::")) + namespace_path_ = util::split(*ns.begin(), "::"); + else + namespace_path_ = ns; +} + +namespace_::namespace_(const std::vector &ns) +{ + if ((ns.size() == 1) && util::contains(*ns.begin(), "::")) + namespace_path_ = util::split(*ns.begin(), "::"); + else + namespace_path_ = ns; +} + +namespace_::namespace_(const std::string &ns) +{ + namespace_path_ = util::split(ns, "::"); +} + +namespace_::namespace_( + container_type::const_iterator begin, container_type::const_iterator end) +{ + std::copy(begin, end, std::back_inserter(namespace_path_)); +} + +std::string namespace_::to_string() const +{ + return fmt::format("{}", fmt::join(namespace_path_, "::")); +} + +size_t namespace_::size() const { return namespace_path_.size(); } + +bool namespace_::is_empty() const { return namespace_path_.empty(); } + +namespace_::container_type::iterator namespace_::begin() +{ + return namespace_path_.begin(); +} +namespace_::container_type::iterator namespace_::end() +{ + return namespace_path_.end(); +} + +namespace_::container_type::const_iterator namespace_::cbegin() const +{ + return namespace_path_.cbegin(); +} +namespace_::container_type::const_iterator namespace_::cend() const +{ + return namespace_path_.cend(); +} + +namespace_::container_type::const_iterator namespace_::begin() const +{ + return namespace_path_.begin(); +} +namespace_::container_type::const_iterator namespace_::end() const +{ + return namespace_path_.end(); +} + +void namespace_::append(const std::string &ns) +{ + namespace_path_.push_back(ns); +} + +void namespace_::append(const namespace_ &ns) +{ + for (const auto &n : ns) { + append(n); + } +} + +void namespace_::pop_back() { namespace_path_.pop_back(); } + +namespace_ namespace_::operator|(const namespace_ &right) const +{ + namespace_ res{*this}; + res.append(right); + return res; +} + +void namespace_::operator|=(const namespace_ &right) { append(right); } + +namespace_ namespace_::operator|(const std::string &right) const +{ + namespace_ res{*this}; + res.append(right); + return res; +} + +void namespace_::operator|=(const std::string &right) { append(right); } + +std::string &namespace_::operator[](const int index) +{ + return namespace_path_[index]; +} + +const std::string &namespace_::operator[](const int index) const +{ + return namespace_path_[index]; +} + +bool namespace_::starts_with(const namespace_ &right) const +{ + return util::starts_with(namespace_path_, right.namespace_path_); +} + +namespace_ namespace_::common_path(const namespace_ &right) const +{ + namespace_ res{}; + for (auto i = 0U; i < std::min(size(), right.size()); i++) { + if (namespace_path_[i] == right[i]) + res |= namespace_path_[i]; + else + break; + } + return res; +} + +namespace_ namespace_::relative_to(const namespace_ &right) const +{ + namespace_ res{*this}; + + if (res.starts_with(right)) + util::remove_prefix(res.namespace_path_, right.namespace_path_); + + return res; +} + +std::string namespace_::relative(const std::string &name) const +{ + /* + std::vector namespaces_sorted{namespaces}; + + std::sort(namespaces_sorted.rbegin(), namespaces_sorted.rend()); + + auto res = name; + + for (const auto &ns : namespaces_sorted) { + if (ns.empty()) + continue; + + if (name == ns) + return split(n, "::").back(); + + auto ns_prefix = ns + "::"; + auto it = res.find(ns_prefix); + while (it != std::string::npos) { + res.erase(it, ns_prefix.size()); + it = res.find(ns_prefix); + } + } + return res; + */ + + if (is_empty()) + return name; + + if (name == to_string()) + return name; + + auto res = name; + auto ns_prefix = to_string() + "::"; + + auto it = res.find(ns_prefix); + while (it != std::string::npos) { + res.erase(it, ns_prefix.size()); + it = res.find(ns_prefix); + } + + return res; +} + +bool operator==(const namespace_ &left, const namespace_ &right) +{ + return left.namespace_path_ == right.namespace_path_; +} + +std::string namespace_::name() const +{ + assert(size() > 0); + + return namespace_path_.back(); +} + +} \ No newline at end of file diff --git a/src/common/model/namespace.h b/src/common/model/namespace.h new file mode 100644 index 00000000..d38a5a16 --- /dev/null +++ b/src/common/model/namespace.h @@ -0,0 +1,87 @@ +/** + * src/common/model/namespace.h + * + * Copyright (c) 2021-2022 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 + +namespace clanguml::common::model { + +class namespace_ { + using container_type = std::vector; + +public: + namespace_() = default; + + namespace_(const std::string &ns); + + namespace_(container_type::const_iterator begin, + container_type::const_iterator end); + + namespace_(const namespace_ &right) noexcept = default; + + namespace_ &operator=(const namespace_ &right) noexcept = default; + + namespace_(namespace_ &&right) noexcept = default; + + namespace_ &operator=(namespace_ &&right) noexcept = default; + + friend bool operator==(const namespace_ &left, const namespace_ &right); + + namespace_(std::initializer_list ns); + + explicit namespace_(const std::vector &ns); + + std::string to_string() const; + + bool is_empty() const; + + size_t size() const; + + namespace_ operator|(const namespace_ &right) const; + void operator|=(const namespace_ &right); + namespace_ operator|(const std::string &right) const; + void operator|=(const std::string &right); + + std::string &operator[](const int index); + const std::string &operator[](const int index) const; + + void append(const std::string &ns); + + void append(const namespace_ &ns); + + void pop_back(); + + bool starts_with(const namespace_ &right) const; + namespace_ common_path(const namespace_ &right) const; + namespace_ relative_to(const namespace_ &right) const; + std::string relative(const std::string &name) const; + std::string name() const; + + container_type::iterator begin(); + container_type::iterator end(); + container_type::const_iterator begin() const; + container_type::const_iterator end() const; + container_type::const_iterator cbegin() const; + container_type::const_iterator cend() const; + +private: + container_type namespace_path_; +}; + +} \ No newline at end of file diff --git a/src/common/model/nested_trait.h b/src/common/model/nested_trait.h index 31de7363..4c2de809 100644 --- a/src/common/model/nested_trait.h +++ b/src/common/model/nested_trait.h @@ -52,58 +52,59 @@ public: } template - void add_element(std::vector path, std::unique_ptr p) + void add_element(namespace_ ns, std::unique_ptr p) { assert(p); - LOG_DBG("Adding nested element {} at path {}", p->name(), - fmt::join(path, "::")); + LOG_DBG( + "Adding nested element {} at path {}", p->name(), ns.to_string()); - if (path.empty()) { + if (ns.is_empty()) { add_element(std::move(p)); return; } - auto parent = get_element(path); + auto parent = get_element(ns); if (parent && dynamic_cast *>(&parent.value())) dynamic_cast &>(parent.value()) .template add_element(std::move(p)); else { - spdlog::error( - "No parent element found at: {}", fmt::join(path, "::")); - throw std::runtime_error("No parent element found"); + spdlog::error("No parent element found at: {}", ns.to_string()); + throw std::runtime_error( + "No parent element found for " + ns.to_string()); } } - template - auto get_element(std::vector path) const + template auto get_element(const namespace_ &path) const { - LOG_DBG("Getting nested element at path: {}", fmt::join(path, "::")); + LOG_DBG("Getting nested element at path: {}", path.to_string()); - if (path.empty() || !has_element(path.at(0))) { - LOG_WARN("Nested element {} not found in element", - fmt::join(path, "::")); + if (path.is_empty() || !has_element(path[0])) { + LOG_WARN( + "Nested element {} not found in element", path.to_string()); return type_safe::optional_ref{}; } if (path.size() == 1) - return get_element(path.at(0)); + return get_element(path[0]); - auto p = get_element(path.at(0)); + auto p = get_element(path[0]); if (!p) return type_safe::optional_ref{}; if (dynamic_cast *>(&p.value())) return dynamic_cast &>(p.value()).get_element( - std::vector(path.begin() + 1, path.end())); + namespace_{path.begin() + 1, path.end()}); return type_safe::optional_ref{}; } template auto get_element(const std::string &name) const { + assert(!util::contains(name, "::")); + auto it = std::find_if(elements_.cbegin(), elements_.cend(), [&](const auto &p) { return name == p->name(); }); diff --git a/src/common/model/package.cc b/src/common/model/package.cc index d628b09b..27eb8679 100644 --- a/src/common/model/package.cc +++ b/src/common/model/package.cc @@ -23,24 +23,33 @@ #include namespace clanguml::common::model { -package::package(const std::vector &using_namespaces) - : element{using_namespaces} +package::package(const common::model::namespace_ &using_namespace) + : element{using_namespace} { } std::string package::full_name(bool relative) const { - auto fn = get_namespace(); - auto ns = using_namespace(); - - if (relative && (fn.size() >= ns.size())) { - if (util::starts_with(fn, ns)) - fn = std::vector(fn.begin() + ns.size(), fn.end()); + if (relative) { + auto res = get_namespace().relative_to(using_namespace()) | name(); + return res.to_string(); } - fn.push_back(name()); + return (get_namespace().relative_to(using_namespace()) | name()) + .to_string(); - return fmt::format("{}", fmt::join(fn, "::")); + // auto fn = get_namespace(); + // auto ns = using_namespace(); + // + // if (relative && (fn.size() >= ns.size())) { + // if (fn.starts_with(using_namespace()) + // fn = std::vector(fn.begin() + ns.size(), + // fn.end()); + // } + // + // fn.push_back(name()); + // + // return fmt::format("{}", fmt::join(fn, "::")); } bool package::is_deprecated() const { return is_deprecated_; } diff --git a/src/common/model/package.h b/src/common/model/package.h index e11fafdb..46325c79 100644 --- a/src/common/model/package.h +++ b/src/common/model/package.h @@ -35,7 +35,7 @@ class package : public element, public stylable_element, public nested_trait { public: - package(const std::vector &using_namespaces); + package(const common::model::namespace_ &using_namespace); package(const package &) = delete; package(package &&) = default; diff --git a/src/config/config.cc b/src/config/config.cc index 8248907e..c3b64b35 100644 --- a/src/config/config.cc +++ b/src/config/config.cc @@ -130,17 +130,22 @@ bool diagram::should_include_relationship(const std::string &rel) } bool diagram::should_include( - const std::pair, std::string> &name) const + const std::pair &name) const { return should_include(std::get<0>(name), std::get<1>(name)); } +// bool diagram::should_include( +// const common::model::namespace_ &ns, const std::string &name) const +//{ +// auto ns_and_name = ns | name; +// return should_include(ns_and_name.to_string()); +//} + bool diagram::should_include( - const std::vector &ns, const std::string &name) const + const common::model::namespace_ &ns, const std::string &name) const { - auto ns_and_name = ns; - ns_and_name.push_back(name); - return should_include(util::join(ns_and_name, "::")); + return should_include(ns | name); } bool diagram::should_include(const std::string &name_) const @@ -148,7 +153,7 @@ bool diagram::should_include(const std::string &name_) const auto name = clanguml::util::unqualify(name_); for (const auto &ex : exclude().namespaces) { - if (name.find(ex) == 0) { + if (ex.starts_with(name)) { LOG_DBG("Skipping from diagram: {}", name); return false; } @@ -160,7 +165,7 @@ bool diagram::should_include(const std::string &name_) const return true; for (const auto &in : include().namespaces) { - if (name.find(in) == 0) + if (name.find(in.to_string()) == 0) return true; } @@ -169,11 +174,22 @@ bool diagram::should_include(const std::string &name_) const return false; } +bool diagram::should_include(const common::model::namespace_ &path) const +{ + return should_include(path.to_string()); +} + +bool diagram::should_include_package( + const common::model::namespace_ &path) const +{ + return should_include_package(path.to_string()); +} + bool diagram::should_include_package(const std::string &name) const { for (const auto &ex : exclude().namespaces) { - if (name.find(ex) == 0) { + if (name.find(ex.to_string()) == 0) { LOG_DBG("Skipping from diagram: {}", name); return false; } @@ -185,7 +201,7 @@ bool diagram::should_include_package(const std::string &name) const return true; for (const auto &in : include().namespaces) { - if (in.find(name) == 0 || name.find(in) == 0) + if (in.to_string().find(name) == 0 || name.find(in.to_string()) == 0) return true; } @@ -283,6 +299,21 @@ void get_option(const Node &node, clanguml::config::option &option) option.set(node[option.name].template as()); } +template <> +void get_option(const Node &node, + clanguml::config::option &option) +{ + if (node[option.name]) { + if (node[option.name].Type() == NodeType::Scalar) + option.set(node[option.name].template as()); + else if (node[option.name].Type() == NodeType::Sequence) + option.set( + node[option.name].template as>()[0]); + else + throw std::runtime_error("Invalid using_namespace value"); + } +} + template <> void get_option( const Node &node, clanguml::config::option &option) @@ -393,8 +424,12 @@ template <> struct convert { template <> struct convert { static bool decode(const Node &node, filter &rhs) { - if (node["namespaces"]) - rhs.namespaces = node["namespaces"].as(); + if (node["namespaces"]) { + auto namespace_list = + node["namespaces"].as>(); + for (const auto &ns : namespace_list) + rhs.namespaces.push_back(ns); + } if (node["relationships"]) rhs.relationships = diff --git a/src/config/config.h b/src/config/config.h index 14cee6c2..7a7c607d 100644 --- a/src/config/config.h +++ b/src/config/config.h @@ -46,7 +46,7 @@ struct plantuml { }; struct filter { - std::vector namespaces; + std::vector namespaces; // Valid values are: // - inheritance @@ -79,7 +79,7 @@ std::string to_string(const hint_t t); struct inheritable_diagram_options { option> glob{"glob"}; - option> using_namespace{"using_namespace"}; + option using_namespace{"using_namespace"}; option include_relations_also_as_members{ "include_relations_also_as_members", true}; option include{"include"}; @@ -105,15 +105,24 @@ struct diagram : public inheritable_diagram_options { bool should_include_package(const std::string &name) const; - bool should_include( - const std::pair, std::string> &name) const; + bool should_include_package(const common::model::namespace_ &path) const; bool should_include( - const std::vector &ns, const std::string &name) const; + const std::pair &name) const; + + // bool should_include( + // const std::vector &ns, const std::string &name) + // const; + + bool should_include( + const common::model::namespace_ &ns, const std::string &name) const; bool should_include(const common::model::scope_t scope) const; + bool should_include(const std::string &name_) const; + bool should_include(const common::model::namespace_ &path) const; + private: }; diff --git a/src/config/option.h b/src/config/option.h index 0fe1d17b..7d27efe1 100644 --- a/src/config/option.h +++ b/src/config/option.h @@ -28,7 +28,7 @@ template struct option { option(const std::string &name_, option_inherit_mode im = option_inherit_mode::override) : name{name_} - , value{{}} + , value{} { } option(const std::string &name_, const T &initial_value, diff --git a/src/cx/util.cc b/src/cx/util.cc index fb4f0fdf..4907b56e 100644 --- a/src/cx/util.cc +++ b/src/cx/util.cc @@ -39,7 +39,7 @@ std::string to_string(CXString &&cxs) } std::string full_name( - const std::vector ¤t_ns, const cppast::cpp_entity &e) + const common::model::namespace_ ¤t_ns, const cppast::cpp_entity &e) { if (e.name().empty()) return ""; @@ -126,12 +126,13 @@ bool is_inside_class(const cppast::cpp_entity &e) return false; } -std::pair, std::string> split_ns( +std::pair split_ns( const std::string &full_name) { auto name_before_template = ::clanguml::util::split(full_name, "<")[0]; - auto ns = ::clanguml::util::split(name_before_template, "::"); - auto name = ns.back(); + auto ns = common::model::namespace_{ + ::clanguml::util::split(name_before_template, "::")}; + auto name = ns.name(); ns.pop_back(); return {ns, name}; } @@ -197,7 +198,7 @@ std::string ns(const cppast::cpp_type &t, const cppast::cpp_entity_index &idx) } std::string fully_prefixed( - const std::vector ¤t_ns, const cppast::cpp_entity &e) + const common::model::namespace_ ¤t_ns, const cppast::cpp_entity &e) { if (e.name().find("::") != std::string::npos) { // the name already contains namespace, but it could be not diff --git a/src/cx/util.h b/src/cx/util.h index 94fe8265..139d1490 100644 --- a/src/cx/util.h +++ b/src/cx/util.h @@ -17,6 +17,8 @@ */ #pragma once +#include "common/model/namespace.h" + #include #include #include @@ -41,13 +43,13 @@ namespace util { std::string to_string(CXString &&cxs); std::string full_name( - const std::vector ¤t_ns, const cppast::cpp_entity &e); + const common::model::namespace_ ¤t_ns, const cppast::cpp_entity &e); std::string full_name(const cppast::cpp_type &t, const cppast::cpp_entity_index &idx, bool inside_class); std::string fully_prefixed( - const std::vector ¤t_ns, const cppast::cpp_entity &e); + const common::model::namespace_ ¤t_ns, const cppast::cpp_entity &e); const cppast::cpp_type &unreferenced(const cppast::cpp_type &t); @@ -58,7 +60,7 @@ type_safe::optional_ref entity_ns( std::string ns(const cppast::cpp_type &t, const cppast::cpp_entity_index &idx); -std::pair, std::string> split_ns( +std::pair split_ns( const std::string &full_name); bool is_inside_class(const cppast::cpp_entity &e); diff --git a/src/package_diagram/generators/plantuml/package_diagram_generator.cc b/src/package_diagram/generators/plantuml/package_diagram_generator.cc index d4ff8b11..2b093ff1 100644 --- a/src/package_diagram/generators/plantuml/package_diagram_generator.cc +++ b/src/package_diagram/generators/plantuml/package_diagram_generator.cc @@ -39,9 +39,9 @@ void generator::generate_relationships( for (const auto &r : p.relationships()) { std::stringstream relstr; try { - relstr << m_model.to_alias(ns_relative(uns, p.full_name(false))) + relstr << m_model.to_alias(uns.relative(p.full_name(false))) << " ..> " - << m_model.to_alias(ns_relative(uns, r.destination())) + << m_model.to_alias(uns.relative(r.destination())) << '\n'; ostr << relstr.str(); } diff --git a/src/package_diagram/model/diagram.cc b/src/package_diagram/model/diagram.cc index 04748b43..1fb2c1c6 100644 --- a/src/package_diagram/model/diagram.cc +++ b/src/package_diagram/model/diagram.cc @@ -27,17 +27,17 @@ std::string diagram::to_alias(const std::string &full_name) const { LOG_DBG("Looking for alias for {}", full_name); - auto fn = util::split(full_name, "::"); + auto path = common::model::namespace_{full_name}; - if (fn.empty()) + if (path.is_empty()) throw error::uml_alias_missing( - fmt::format("Missing alias for '{}'", full_name)); + fmt::format("Missing alias for '{}'", path.to_string())); - auto package = get_element(fn); + auto package = get_element(path); if (!package) throw error::uml_alias_missing( - fmt::format("Missing alias for '{}'", full_name)); + fmt::format("Missing alias for '{}'", path.to_string())); return package.value().alias(); } diff --git a/src/package_diagram/visitor/translation_unit_context.cc b/src/package_diagram/visitor/translation_unit_context.cc index 6cbd7de6..38f8c2d9 100644 --- a/src/package_diagram/visitor/translation_unit_context.cc +++ b/src/package_diagram/visitor/translation_unit_context.cc @@ -153,12 +153,12 @@ translation_unit_context::get_type_alias_template( void translation_unit_context::push_namespace(const std::string &ns) { - namespace_.push_back(ns); + namespace_ |= ns; } void translation_unit_context::pop_namespace() { namespace_.pop_back(); } -const std::vector &translation_unit_context::get_namespace() const +const common::model::namespace_ &translation_unit_context::get_namespace() const { return namespace_; } diff --git a/src/package_diagram/visitor/translation_unit_context.h b/src/package_diagram/visitor/translation_unit_context.h index 65522418..fbb075a6 100644 --- a/src/package_diagram/visitor/translation_unit_context.h +++ b/src/package_diagram/visitor/translation_unit_context.h @@ -68,7 +68,7 @@ public: void pop_namespace(); - const std::vector &get_namespace() const; + const common::model::namespace_ &get_namespace() const; const cppast::cpp_entity_index &entity_index() const; @@ -82,7 +82,7 @@ public: private: // Current visitor namespace - std::vector namespace_; + common::model::namespace_ namespace_; // Reference to the cppast entity index cppast::cpp_entity_index &entity_index_; diff --git a/src/package_diagram/visitor/translation_unit_visitor.cc b/src/package_diagram/visitor/translation_unit_visitor.cc index a0867a33..ea14f4e9 100644 --- a/src/package_diagram/visitor/translation_unit_visitor.cc +++ b/src/package_diagram/visitor/translation_unit_visitor.cc @@ -18,6 +18,8 @@ #include "translation_unit_visitor.h" +#include "common/model/namespace.h" + #include #include #include @@ -89,20 +91,17 @@ void translation_unit_visitor::operator()(const cppast::cpp_entity &file) if (!ns_declaration.is_anonymous() && !ns_declaration.is_inline()) { - std::vector package_parent = - ctx.get_namespace(); - auto package_path = package_parent; - package_path.push_back(e.name()); + auto package_parent = ctx.get_namespace(); + auto package_path = package_parent | e.name(); + auto usn = common::model::namespace_{ + ctx.config().using_namespace()}; - auto usn = util::split( - ctx.config().using_namespace()[0], "::"); - - if (!util::starts_with(usn, package_path)) { + if (!usn.starts_with(package_path)) { auto p = std::make_unique(usn); - util::remove_prefix(package_path, usn); + package_path = package_path.relative_to(usn); p->set_name(e.name()); - p->set_namespace(package_parent); + p->set_namespace(package_path); if (ns_declaration.comment().has_value()) p->add_decorators(decorators::parse( @@ -303,10 +302,10 @@ void translation_unit_visitor::process_class_declaration( } for (const auto &dependency : relationships) { - auto destination = util::split(std::get<0>(dependency), "::"); + auto destination = common::model::namespace_{std::get<0>(dependency)}; - if (!util::starts_with(ctx.get_namespace(), destination) && - !util::starts_with(destination, ctx.get_namespace())) { + if (!ctx.get_namespace().starts_with(destination) && + !destination.starts_with(ctx.get_namespace())) { relationship r{ relationship_t::kDependency, std::get<0>(dependency)}; current_package.value().add_relationship(std::move(r)); @@ -330,10 +329,10 @@ void translation_unit_visitor::process_function(const cppast::cpp_function &f) f.return_type(), relationships, relationship_t::kDependency); for (const auto &dependency : relationships) { - auto destination = util::split(std::get<0>(dependency), "::"); + auto destination = common::model::namespace_{std::get<0>(dependency)}; - if (!util::starts_with(ctx.get_namespace(), destination) && - !util::starts_with(destination, ctx.get_namespace())) { + if (!ctx.get_namespace().starts_with(destination) && + !destination.starts_with(ctx.get_namespace())) { relationship r{ relationship_t::kDependency, std::get<0>(dependency)}; current_package.value().add_relationship(std::move(r)); @@ -350,8 +349,8 @@ bool translation_unit_visitor::find_relationships(const cppast::cpp_type &t_, const auto fn = cx::util::full_name( resolve_alias(cppast::remove_cv(t_)), ctx.entity_index(), false); - auto t_ns = util::split(fn, "::"); - auto t_name = t_ns.back(); + auto t_ns = common::model::namespace_{fn}; + auto t_name = t_ns.name(); t_ns.pop_back(); const auto &t_raw = resolve_alias(cppast::remove_cv(t_)); @@ -371,13 +370,13 @@ bool translation_unit_visitor::find_relationships(const cppast::cpp_type &t_, const auto &t_raw_ns_final = cx::util::ns(t_raw_ns.value()) + "::" + cx::util::full_name({}, t_raw_ns.value()); - t_ns = util::split(t_raw_ns_final, "::"); + t_ns = common::model::namespace_{t_raw_ns_final}; } } std::vector possible_matches; - possible_matches.push_back(util::join(t_ns, "::")); + possible_matches.push_back(t_ns.to_string()); const auto fn_ns = cx::util::ns(cppast::remove_cv(t_), ctx.entity_index()); diff --git a/src/sequence_diagram/generators/plantuml/sequence_diagram_generator.cc b/src/sequence_diagram/generators/plantuml/sequence_diagram_generator.cc index 76b6277f..fc98907d 100644 --- a/src/sequence_diagram/generators/plantuml/sequence_diagram_generator.cc +++ b/src/sequence_diagram/generators/plantuml/sequence_diagram_generator.cc @@ -44,8 +44,8 @@ generator::generator( void generator::generate_call(const message &m, std::ostream &ostr) const { - const auto from = ns_relative(m_config.using_namespace(), m.from); - const auto to = ns_relative(m_config.using_namespace(), m.to); + const auto from = m_config.using_namespace().relative(m.from); + const auto to = m_config.using_namespace().relative(m.to); ostr << '"' << from << "\" " << common::generators::plantuml::to_plantuml(message_t::kCall) << " \"" @@ -57,8 +57,8 @@ void generator::generate_return(const message &m, std::ostream &ostr) const // Add return activity only for messages between different actors and // only if the return type is different than void if ((m.from != m.to) && (m.return_type != "void")) { - const auto from = ns_relative(m_config.using_namespace(), m.from); - const auto to = ns_relative(m_config.using_namespace(), m.to); + const auto from = m_config.using_namespace().relative(m.from); + const auto to = m_config.using_namespace().relative(m.to); ostr << '"' << to << "\" " << common::generators::plantuml::to_plantuml(message_t::kReturn) @@ -69,7 +69,7 @@ void generator::generate_return(const message &m, std::ostream &ostr) const void generator::generate_activity(const activity &a, std::ostream &ostr) const { for (const auto &m : a.messages) { - const auto to = ns_relative(m_config.using_namespace(), m.to); + const auto to = m_config.using_namespace().relative(m.to); generate_call(m, ostr); ostr << "activate " << '"' << to << '"' << std::endl; if (m_model.sequences.find(m.to_usr) != m_model.sequences.end()) diff --git a/src/sequence_diagram/visitor/translation_unit_visitor.cc b/src/sequence_diagram/visitor/translation_unit_visitor.cc index 29614816..e999fdd4 100644 --- a/src/sequence_diagram/visitor/translation_unit_visitor.cc +++ b/src/sequence_diagram/visitor/translation_unit_visitor.cc @@ -18,6 +18,7 @@ #include "translation_unit_visitor.h" +#include "common/model/namespace.h" #include "translation_unit_context.h" #include @@ -81,7 +82,7 @@ void translation_unit_visitor::process_activities(const cppast::cpp_function &e) m.from = cx::util::ns(caller) + "::" + caller.name(); if (!ctx.config().should_include( - util::split(cx::util::ns(caller), "::"), caller.name())) + common::model::namespace_{cx::util::ns(caller)}, caller.name())) continue; if (caller.kind() == cpp_entity_kind::function_t) @@ -98,7 +99,7 @@ void translation_unit_visitor::process_activities(const cppast::cpp_function &e) m.to += "()"; if (!ctx.config().should_include( - util::split(cx::util::ns(callee), "::"), callee.name())) + common::model::namespace_{cx::util::ns(callee)}, callee.name())) continue; m.to_usr = type_safe::get(function_call.get_callee_method_id()); diff --git a/src/util/util.cc b/src/util/util.cc index 92e79db3..69926a49 100644 --- a/src/util/util.cc +++ b/src/util/util.cc @@ -68,6 +68,7 @@ std::string join(const std::vector &toks, std::string delimiter) return fmt::format("{}", fmt::join(toks, delimiter)); } +/* std::string ns_relative( const std::vector &namespaces, const std::string &n) { @@ -93,6 +94,7 @@ std::string ns_relative( } return res; } +*/ std::string unqualify(const std::string &s) { diff --git a/src/util/util.h b/src/util/util.h index d929f41a..a349aa30 100644 --- a/src/util/util.h +++ b/src/util/util.h @@ -83,8 +83,8 @@ std::string join(const std::vector &toks, std::string delimiter); * * @return Identifier relative to one of the matching namespaces. */ -std::string ns_relative( - const std::vector &namespaces, const std::string &n); +// std::string ns_relative( +// const std::vector &namespaces, const std::string &n); /** * @brief Remove any qualifiers (e.g. const) from type. diff --git a/tests/t00002/test_case.h b/tests/t00002/test_case.h index 4c46f175..2b7856bf 100644 --- a/tests/t00002/test_case.h +++ b/tests/t00002/test_case.h @@ -26,7 +26,8 @@ TEST_CASE("t00002", "[test-case][class]") REQUIRE(diagram->include().namespaces.size() == 1); REQUIRE_THAT(diagram->include().namespaces, - VectorContains(std::string{"clanguml::t00002"})); + VectorContains( + clanguml::common::model::namespace_{"clanguml::t00002"})); REQUIRE(diagram->exclude().namespaces.size() == 0); diff --git a/tests/t00003/test_case.h b/tests/t00003/test_case.h index acbc547f..d5309310 100644 --- a/tests/t00003/test_case.h +++ b/tests/t00003/test_case.h @@ -25,8 +25,6 @@ TEST_CASE("t00003", "[test-case][class]") REQUIRE(diagram->name == "t00003_class"); REQUIRE(diagram->include().namespaces.size() == 1); - REQUIRE_THAT(diagram->include().namespaces, - VectorContains(std::string{"clanguml::t00003"})); REQUIRE(diagram->exclude().namespaces.size() == 0); diff --git a/tests/t00004/test_case.h b/tests/t00004/test_case.h index 10fb7cc4..daafd155 100644 --- a/tests/t00004/test_case.h +++ b/tests/t00004/test_case.h @@ -25,9 +25,6 @@ TEST_CASE("t00004", "[test-case][class]") REQUIRE(diagram->name == "t00004_class"); REQUIRE(diagram->include().namespaces.size() == 1); - REQUIRE_THAT(diagram->include().namespaces, - VectorContains(std::string{"clanguml::t00004"})); - REQUIRE(diagram->exclude().namespaces.size() == 0); REQUIRE(diagram->should_include("clanguml::t00004::A")); diff --git a/tests/test_config.cc b/tests/test_config.cc index 56cb4e88..cc11a991 100644 --- a/tests/test_config.cc +++ b/tests/test_config.cc @@ -53,7 +53,7 @@ TEST_CASE("Test config inherited", "[unit-test]") CHECK(cus.type() == clanguml::config::diagram_type::class_diagram); CHECK(cus.glob().size() == 1); CHECK(cus.glob()[0] == "src/main.cc"); - CHECK(clanguml::util::contains(cus.using_namespace(), "clanguml::ns1")); + CHECK(cus.using_namespace().starts_with({"clanguml::ns1"})); CHECK(cus.include_relations_also_as_members()); CHECK(def.generate_packages() == false); } @@ -76,7 +76,7 @@ TEST_CASE("Test config includes", "[unit-test]") CHECK(cus.type() == clanguml::config::diagram_type::class_diagram); CHECK(cus.glob().size() == 1); CHECK(cus.glob()[0] == "src/main.cc"); - CHECK(clanguml::util::contains(cus.using_namespace(), "clanguml::ns1")); + CHECK(cus.using_namespace().starts_with({"clanguml::ns1"})); CHECK(cus.include_relations_also_as_members()); CHECK(cus.generate_method_arguments() == clanguml::config::method_arguments::none); diff --git a/tests/test_config_data/simple.yml b/tests/test_config_data/simple.yml index 7b0e0952..698cd3e8 100644 --- a/tests/test_config_data/simple.yml +++ b/tests/test_config_data/simple.yml @@ -6,8 +6,7 @@ diagrams: glob: - src/**/*.cc - src/**/*.h - using_namespace: - - clanguml + using_namespace: clanguml generate_method_arguments: full generate_packages: true include: diff --git a/tests/test_model.cc b/tests/test_model.cc new file mode 100644 index 00000000..8a8350ac --- /dev/null +++ b/tests/test_model.cc @@ -0,0 +1,72 @@ +/** + * tests/test_model.cc + * + * Copyright (c) 2021-2022 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. + */ +#define CATCH_CONFIG_MAIN + +#include "catch.h" + +#include "common/model/namespace.h" + +TEST_CASE("Test namespace_", "[unit-test]") +{ + using clanguml::common::model::namespace_; + + namespace_ ns1{}; + CHECK(ns1.is_empty()); + + namespace_ ns2{"aaa", "bbb", "ccc"}; + CHECK(ns2.size() == 3); + CHECK(ns2[0] == "aaa"); + CHECK(ns2[1] == "bbb"); + CHECK(ns2[2] == "ccc"); + + namespace_ ns3 = ns1 | "aaa" | "bbb" | "ccc"; + CHECK(ns3.size() == 3); + CHECK(ns3[0] == "aaa"); + CHECK(ns3[1] == "bbb"); + CHECK(ns3[2] == "ccc"); + + namespace_ ns4 = namespace_{"aaa", "bbb"} | namespace_{"ccc"}; + CHECK(ns4.size() == 3); + CHECK(ns4[0] == "aaa"); + CHECK(ns4[1] == "bbb"); + CHECK(ns4[2] == "ccc"); + + namespace_ ns5{"aaa::bbb::ccc"}; + CHECK(ns5.size() == 3); + CHECK(ns5[0] == "aaa"); + CHECK(ns5[1] == "bbb"); + CHECK(ns5[2] == "ccc"); + + CHECK(ns4 == ns5); + + namespace_ ns6a{"aaa::bbb::ccc"}; + namespace_ ns6b{"aaa::bbb::ddd::eee"}; + auto ns6 = ns6a.common_path(ns6b); + + CHECK(ns6 == namespace_{"aaa", "bbb"}); + CHECK(ns6b.starts_with({"aaa", "bbb", "ddd"})); + + namespace_ ns7a{"aaa::bbb"}; + namespace_ ns7b{"aaa::bbb::ccc::ddd"}; + CHECK(ns7b.relative_to(ns7a) == namespace_{"ccc", "ddd"}); + CHECK(ns7a.relative_to(ns7b) == namespace_{"aaa::bbb"}); + + namespace_ ns8{"aaa::bbb"}; + const std::string name{"aaa::bbb::ccc>"}; + CHECK(ns8.relative(name) == "ccc>"); +} \ No newline at end of file diff --git a/tests/test_util.cc b/tests/test_util.cc index 8e7b93fa..884a430b 100644 --- a/tests/test_util.cc +++ b/tests/test_util.cc @@ -35,19 +35,19 @@ TEST_CASE("Test split", "[unit-test]") CHECK(split("std::vector::detail::", "::") == C{"std", "vector", "detail"}); } - -TEST_CASE("Test ns_relative", "[unit-test]") -{ - using namespace clanguml::util; - - CHECK(ns_relative({}, "std::vector") == "std::vector"); - CHECK(ns_relative({"std"}, "std::vector") == "vector"); - CHECK(ns_relative({"std"}, "const std::vector&") == "const vector&"); - CHECK(ns_relative({"std", "clanguml::t0"}, - "static const std::vector&") == - "static const vector&"); - CHECK(ns_relative({"clanguml::t0"}, "clanguml::t0") == "t0"); -} +// +// TEST_CASE("Test ns_relative", "[unit-test]") +//{ +// using namespace clanguml::util; +// +// CHECK(ns_relative({}, "std::vector") == "std::vector"); +// CHECK(ns_relative({"std"}, "std::vector") == "vector"); +// CHECK(ns_relative({"std"}, "const std::vector&") == "const vector&"); +// CHECK(ns_relative({"std", "clanguml::t0"}, +// "static const std::vector&") == +// "static const vector&"); +// CHECK(ns_relative({"clanguml::t0"}, "clanguml::t0") == "t0"); +//} TEST_CASE("Test abbreviate", "[unit-test]") {