diff --git a/src/class_diagram/model/diagram.cc b/src/class_diagram/model/diagram.cc index 1f56f3a9..700e294e 100644 --- a/src/class_diagram/model/diagram.cc +++ b/src/class_diagram/model/diagram.cc @@ -41,10 +41,10 @@ common::model::diagram_t diagram::type() const return common::model::diagram_t::kClass; } -type_safe::optional_ref diagram::get( - const std::string &full_name) const +type_safe::optional_ref +diagram::get(const std::string &full_name) const { - type_safe::optional_ref res; + type_safe::optional_ref res; res = get_class(full_name); diff --git a/src/class_diagram/model/diagram.h b/src/class_diagram/model/diagram.h index 85fbe2b6..d27ad37b 100644 --- a/src/class_diagram/model/diagram.h +++ b/src/class_diagram/model/diagram.h @@ -32,7 +32,8 @@ namespace clanguml::class_diagram::model { class diagram : public clanguml::common::model::diagram, public clanguml::common::model::nested_trait< - clanguml::common::model::element> { + clanguml::common::model::element, + clanguml::common::model::namespace_> { public: diagram() = default; @@ -43,7 +44,7 @@ public: common::model::diagram_t type() const override; - type_safe::optional_ref get( + type_safe::optional_ref get( const std::string &full_name) const override; const std::vector> classes() const; diff --git a/src/common/model/diagram.h b/src/common/model/diagram.h index 3d71e2fd..dbcd9b52 100644 --- a/src/common/model/diagram.h +++ b/src/common/model/diagram.h @@ -17,7 +17,9 @@ */ #pragma once +#include "diagram_element.h" #include "enums.h" +#include "namespace.h" #include @@ -27,7 +29,6 @@ namespace clanguml::common::model { class diagram_filter; -class namespace_; class element; class relationship; @@ -38,7 +39,7 @@ public: virtual diagram_t type() const = 0; - virtual type_safe::optional_ref get( + virtual type_safe::optional_ref get( const std::string &full_name) const = 0; diagram(const diagram &) = delete; diff --git a/src/common/model/diagram_element.cc b/src/common/model/diagram_element.cc new file mode 100644 index 00000000..bd3179f3 --- /dev/null +++ b/src/common/model/diagram_element.cc @@ -0,0 +1,99 @@ +/** + * src/common/model/diagram_element.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. + */ + +#include "diagram_element.h" + +#include "util/util.h" + +#include + +namespace clanguml::common::model { + +std::atomic_uint64_t diagram_element::m_nextId = 1; + +diagram_element::diagram_element() + : m_id{m_nextId++} +{ +} + +std::string diagram_element::alias() const +{ + return fmt::format("C_{:010}", m_id); +} + +void diagram_element::add_relationship(relationship &&cr) +{ + if (cr.destination().empty()) { + LOG_DBG("Skipping relationship '{}' - {} - '{}' due empty " + "destination", + cr.destination(), to_string(cr.type()), full_name(true)); + return; + } + + if ((cr.type() == relationship_t::kInstantiation) && + (cr.destination() == full_name(true))) { + LOG_DBG("Skipping self instantiation relationship for {}", + cr.destination()); + return; + } + + LOG_DBG("Adding relationship: '{}' - {} - '{}'", cr.destination(), + to_string(cr.type()), full_name(true)); + + if (!util::contains(relationships_, cr)) + relationships_.emplace_back(std::move(cr)); +} + +std::vector &diagram_element::relationships() +{ + return relationships_; +} + +const std::vector &diagram_element::relationships() const +{ + return relationships_; +} + +void diagram_element::append(const decorated_element &e) +{ + decorated_element::append(e); +} + +inja::json diagram_element::context() const +{ + inja::json ctx; + ctx["name"] = name(); + ctx["alias"] = alias(); + ctx["full_name"] = full_name(false); + + return ctx; +} + +bool operator==(const diagram_element &l, const diagram_element &r) +{ + return l.full_name(false) == r.full_name(false); +} + +std::ostream &operator<<(std::ostream &out, const diagram_element &rhs) +{ + out << "(" << rhs.name() << ", full_name=[" << rhs.full_name(false) << "])"; + + return out; +} + +} diff --git a/src/common/model/diagram_element.h b/src/common/model/diagram_element.h new file mode 100644 index 00000000..023cd90c --- /dev/null +++ b/src/common/model/diagram_element.h @@ -0,0 +1,71 @@ +/** + * src/common/model/diagram_element.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 "decorated_element.h" +#include "relationship.h" +#include "util/util.h" + +#include + +#include +#include +#include +#include + +namespace clanguml::common::model { + +class diagram_element : public decorated_element { +public: + diagram_element(); + + virtual ~diagram_element() = default; + + std::string alias() const; + + void set_name(const std::string &name) { name_ = name; } + + std::string name() const { return name_; } + + virtual std::string full_name(bool relative) const { return name(); } + + std::vector &relationships(); + + const std::vector &relationships() const; + + void add_relationship(relationship &&cr); + + void append(const decorated_element &e); + + friend bool operator==(const diagram_element &l, const diagram_element &r); + + friend std::ostream &operator<<( + std::ostream &out, const diagram_element &rhs); + + virtual inja::json context() const; + +protected: + const uint64_t m_id{0}; + +private: + std::string name_; + std::vector relationships_; + + static std::atomic_uint64_t m_nextId; +}; +} diff --git a/src/common/model/element.cc b/src/common/model/element.cc index 164d9292..dd789d92 100644 --- a/src/common/model/element.cc +++ b/src/common/model/element.cc @@ -24,39 +24,11 @@ namespace clanguml::common::model { -std::atomic_uint64_t element::m_nextId = 1; - element::element(const namespace_ &using_namespace) : using_namespace_{using_namespace} - , m_id{m_nextId++} { } -std::string element::alias() const { return fmt::format("C_{:010}", m_id); } - -void element::add_relationship(relationship &&cr) -{ - if (cr.destination().empty()) { - LOG_DBG("Skipping relationship '{}' - {} - '{}' due empty " - "destination", - cr.destination(), to_string(cr.type()), full_name(true)); - return; - } - - if ((cr.type() == relationship_t::kInstantiation) && - (cr.destination() == full_name(true))) { - LOG_DBG("Skipping self instantiation relationship for {}", - cr.destination()); - return; - } - - LOG_DBG("Adding relationship: '{}' - {} - '{}'", cr.destination(), - to_string(cr.type()), full_name(true)); - - if (!util::contains(relationships_, cr)) - relationships_.emplace_back(std::move(cr)); -} - void element::set_using_namespaces(const namespace_ &un) { using_namespace_ = un; @@ -64,15 +36,6 @@ void element::set_using_namespaces(const namespace_ &un) const namespace_ &element::using_namespace() const { return using_namespace_; } -std::vector &element::relationships() { return relationships_; } - -const std::vector &element::relationships() const -{ - return relationships_; -} - -void element::append(const element &e) { decorated_element::append(e); } - inja::json element::context() const { inja::json ctx; diff --git a/src/common/model/element.h b/src/common/model/element.h index 74173a89..881460bd 100644 --- a/src/common/model/element.h +++ b/src/common/model/element.h @@ -17,7 +17,7 @@ */ #pragma once -#include "decorated_element.h" +#include "diagram_element.h" #include "namespace.h" #include "relationship.h" #include "source_location.h" @@ -32,18 +32,12 @@ namespace clanguml::common::model { -class element : public decorated_element, public source_location { +class element : public diagram_element, public source_location { public: element(const namespace_ &using_namespace); virtual ~element() = default; - std::string alias() const; - - void set_name(const std::string &name) { name_ = name; } - - std::string name() const { return name_; } - std::string name_and_ns() const { auto ns = ns_ | name(); @@ -59,36 +53,26 @@ public: return ns_.relative_to(using_namespace_); } - virtual std::string full_name(bool relative) const { return name_and_ns(); } + const namespace_ &path() const { return ns_; } + + std::string full_name(bool relative) const override + { + return name_and_ns(); + } void set_using_namespaces(const namespace_ &un); const namespace_ &using_namespace() const; - std::vector &relationships(); - - const std::vector &relationships() const; - - void add_relationship(relationship &&cr); - - void append(const element &e); - friend bool operator==(const element &l, const element &r); friend std::ostream &operator<<(std::ostream &out, const element &rhs); - virtual inja::json context() const; - -protected: - const uint64_t m_id{0}; + inja::json context() const override; private: - std::string name_; namespace_ ns_; namespace_ using_namespace_; - std::vector relationships_; - type_safe::optional location_; - - static std::atomic_uint64_t m_nextId; + // type_safe::optional location_; }; } diff --git a/src/common/model/enums.h b/src/common/model/enums.h index e102a67c..fbbeb21f 100644 --- a/src/common/model/enums.h +++ b/src/common/model/enums.h @@ -35,7 +35,8 @@ enum class relationship_t { kAssociation, kInstantiation, kFriendship, - kDependency + kDependency, + kInclusion }; enum class message_t { kCall, kReturn }; diff --git a/src/common/model/namespace.cc b/src/common/model/namespace.cc index 2e6c6012..3039adb3 100644 --- a/src/common/model/namespace.cc +++ b/src/common/model/namespace.cc @@ -17,194 +17,3 @@ */ #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(); } - -type_safe::optional namespace_::parent() const -{ - if (size() <= 1) { - return {}; - } - - namespace_ res{*this}; - res.pop_back(); - return {std::move(res)}; -} - -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_); -} - -bool namespace_::ends_with(const namespace_ &right) const -{ - return util::ends_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 -{ - 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_; -} - -bool operator<(const namespace_ &left, const namespace_ &right) -{ - return std::hash{}(left) < std::hash{}(right); -} - -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 index 03d42bc9..9ba3faad 100644 --- a/src/common/model/namespace.h +++ b/src/common/model/namespace.h @@ -17,78 +17,20 @@ */ #pragma once +#include "path.h" + #include #include #include namespace clanguml::common::model { -class namespace_ { -public: - using container_type = std::vector; - - 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); - 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(); - - type_safe::optional parent() const; - - bool starts_with(const namespace_ &right) const; - bool ends_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_; +struct ns_path_separator { + static constexpr std::string_view value = "::"; }; +using namespace_ = path; + } namespace std { @@ -96,7 +38,7 @@ namespace std { template <> struct hash { std::size_t operator()(const clanguml::common::model::namespace_ &key) const { - using clanguml::common::model::namespace_; + using clanguml::common::model::path; std::size_t seed = key.size(); for (const auto &ns : key) { @@ -108,4 +50,4 @@ template <> struct hash { } }; -} \ No newline at end of file +} diff --git a/src/common/model/nested_trait.h b/src/common/model/nested_trait.h index 91515d1f..d79d04b3 100644 --- a/src/common/model/nested_trait.h +++ b/src/common/model/nested_trait.h @@ -1,5 +1,5 @@ /** - * src/common/model/element.h + * src/common/model/nested_trait.h * * Copyright (c) 2021-2022 Bartek Kryza * @@ -26,7 +26,7 @@ #include namespace clanguml::common::model { -template class nested_trait { +template class nested_trait { public: nested_trait() = default; @@ -52,31 +52,31 @@ public: } template - void add_element(namespace_ ns, std::unique_ptr p) + void add_element(const Path &path, std::unique_ptr p) { assert(p); LOG_DBG( - "Adding nested element {} at path {}", p->name(), ns.to_string()); + "Adding nested element {} at path {}", p->name(), path.to_string()); - if (ns.is_empty()) { + if (path.is_empty()) { add_element(std::move(p)); return; } - auto parent = get_element(ns); + auto parent = get_element(path); - if (parent && dynamic_cast *>(&parent.value())) - dynamic_cast &>(parent.value()) + if (parent && dynamic_cast *>(&parent.value())) + dynamic_cast &>(parent.value()) .template add_element(std::move(p)); else { - spdlog::error("No parent element found at: {}", ns.to_string()); + spdlog::error("No parent element found at: {}", path.to_string()); throw std::runtime_error( - "No parent element found for " + ns.to_string()); + "No parent element found for " + path.to_string()); } } - template auto get_element(const namespace_ &path) const + template auto get_element(const Path &path) const { LOG_DBG("Getting nested element at path: {}", path.to_string()); @@ -93,9 +93,9 @@ public: if (!p) return type_safe::optional_ref{}; - if (dynamic_cast *>(&p.value())) - return dynamic_cast &>(p.value()).get_element( - namespace_{path.begin() + 1, path.end()}); + if (dynamic_cast *>(&p.value())) + return dynamic_cast &>(p.value()) + .get_element(Path{path.begin() + 1, path.end()}); return type_safe::optional_ref{}; } @@ -145,9 +145,10 @@ public: std::cout << "--- Printing tree:\n"; } for (const auto &e : d) { - if (dynamic_cast *>(e.get())) { + if (dynamic_cast *>(e.get())) { std::cout << std::string(level, ' ') << "[" << *e << "]\n"; - dynamic_cast *>(e.get())->print_tree(level + 1); + dynamic_cast *>(e.get())->print_tree( + level + 1); } else { std::cout << std::string(level, ' ') << "- " << *e << "]\n"; diff --git a/src/common/model/package.h b/src/common/model/package.h index 9b28893e..1fdac159 100644 --- a/src/common/model/package.h +++ b/src/common/model/package.h @@ -33,7 +33,7 @@ namespace clanguml::common::model { class package : public element, public stylable_element, - public nested_trait { + public nested_trait { public: package(const common::model::namespace_ &using_namespace); diff --git a/src/common/model/path.h b/src/common/model/path.h new file mode 100644 index 00000000..555b5085 --- /dev/null +++ b/src/common/model/path.h @@ -0,0 +1,207 @@ +/** + * src/common/model/path.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 "util/util.h" + +#include +#include +#include + +namespace clanguml::common::model { + +template class path { +public: + using container_type = std::vector; + + path() = default; + + path(const std::string &ns) { path_ = util::split(ns, Sep::value); } + + path(container_type::const_iterator begin, + container_type::const_iterator end) + { + std::copy(begin, end, std::back_inserter(path_)); + } + + path(const path &right) noexcept = default; + + path &operator=(const path &right) noexcept = default; + + path(path &&right) noexcept = default; + + path &operator=(path &&right) noexcept = default; + + path(std::initializer_list ns) + { + if ((ns.size() == 1) && util::contains(*ns.begin(), Sep::value)) + path_ = util::split(*ns.begin(), Sep::value); + else + path_ = ns; + } + + explicit path(const std::vector &ns) + { + if ((ns.size() == 1) && util::contains(*ns.begin(), Sep::value)) + path_ = util::split(*ns.begin(), Sep::value); + else + path_ = ns; + } + + friend bool operator==(const path &left, const path &right) + { + return left.path_ == right.path_; + } + + friend bool operator<(const path &left, const path &right) + { + return std::hash>{}(left) < std::hash>{}(right); + } + + std::string to_string() const + { + return fmt::format("{}", fmt::join(path_, Sep::value)); + } + + bool is_empty() const { return path_.empty(); } + + size_t size() const { return path_.size(); } + + path operator|(const path &right) const + { + path res{*this}; + res.append(right); + return res; + } + + void operator|=(const path &right) { append(right); } + + path operator|(const std::string &right) const + { + path res{*this}; + res.append(right); + return res; + } + + void operator|=(const std::string &right) { append(right); } + + std::string &operator[](const int index) { return path_[index]; } + + const std::string &operator[](const int index) const + { + return path_[index]; + } + + void append(const std::string &ns) { path_.push_back(ns); } + + void append(const path &ns) + { + for (const auto &n : ns) { + append(n); + } + } + + void pop_back() { path_.pop_back(); } + + type_safe::optional parent() const + { + if (size() <= 1) { + return {}; + } + + path res{*this}; + res.pop_back(); + return {std::move(res)}; + } + + bool starts_with(const path &right) const + { + return util::starts_with(path_, right.path_); + } + + bool ends_with(const path &right) const + { + return util::ends_with(path_, right.path_); + } + + path common_path(const path &right) const + { + path res{}; + for (auto i = 0U; i < std::min(size(), right.size()); i++) { + if (path_[i] == right[i]) + res |= path_[i]; + else + break; + } + return res; + } + + path relative_to(const path &right) const + { + path res{*this}; + + if (res.starts_with(right)) + util::remove_prefix(res.path_, right.path_); + + return res; + } + + std::string relative(const std::string &name) const + { + if (is_empty()) + return name; + + if (name == to_string()) + return name; + + auto res = name; + auto ns_prefix = to_string() + std::string{Sep::value}; + + auto it = res.find(ns_prefix); + while (it != std::string::npos) { + res.erase(it, ns_prefix.size()); + it = res.find(ns_prefix); + } + + return res; + } + + std::string name() const + { + assert(size() > 0); + + return path_.back(); + } + + path::container_type::iterator begin() { return path_.begin(); } + path::container_type::iterator end() { return path_.end(); } + + path::container_type::const_iterator cbegin() const + { + return path_.cbegin(); + } + path::container_type::const_iterator cend() const { return path_.cend(); } + + path::container_type::const_iterator begin() const { return path_.begin(); } + path::container_type::const_iterator end() const { return path_.end(); } + +private: + container_type path_; +}; + +} \ No newline at end of file diff --git a/src/config/config.cc b/src/config/config.cc index 6084be63..3138cc00 100644 --- a/src/config/config.cc +++ b/src/config/config.cc @@ -66,6 +66,8 @@ std::string to_string(const diagram_type t) return "sequence"; case diagram_type::package_diagram: return "package"; + case diagram_type::include_diagram: + return "include"; default: assert(false); } @@ -136,6 +138,11 @@ diagram_type package_diagram::type() const return diagram_type::package_diagram; } +diagram_type include_diagram::type() const +{ + return diagram_type::include_diagram; +} + template <> void append_value>( std::vector &l, const std::vector &r) @@ -159,6 +166,7 @@ using clanguml::config::filter; using clanguml::config::generate_links_config; using clanguml::config::git_config; using clanguml::config::hint_t; +using clanguml::config::include_diagram; using clanguml::config::layout_hint; using clanguml::config::method_arguments; using clanguml::config::package_diagram; @@ -228,6 +236,9 @@ std::shared_ptr parse_diagram_config(const Node &d) else if (diagram_type == "package") { return std::make_shared(d.as()); } + else if (diagram_type == "include") { + return std::make_shared(d.as()); + } LOG_ERROR("Diagrams of type {} are not supported... ", diagram_type); @@ -470,7 +481,7 @@ template <> struct convert { }; // -// class_diagram Yaml decoder +// package_diagram Yaml decoder // template <> struct convert { static bool decode(const Node &node, package_diagram &rhs) @@ -484,6 +495,21 @@ template <> struct convert { } }; +// +// include_diagram Yaml decoder +// +template <> struct convert { + static bool decode(const Node &node, include_diagram &rhs) + { + if (!decode_diagram(node, rhs)) + return false; + + get_option(node, rhs.layout); + + return true; + } +}; + // // layout_hint Yaml decoder // diff --git a/src/config/config.h b/src/config/config.h index 11db75fe..c598d774 100644 --- a/src/config/config.h +++ b/src/config/config.h @@ -35,7 +35,13 @@ namespace clanguml { namespace config { -enum class diagram_type { class_diagram, sequence_diagram, package_diagram }; +enum class diagram_type { + class_diagram, + sequence_diagram, + package_diagram, + include_diagram +}; + enum class method_arguments { full, abbreviated, none }; struct plantuml { @@ -149,6 +155,14 @@ struct package_diagram : public diagram { option layout{"layout"}; }; +struct include_diagram : public diagram { + virtual ~include_diagram() = default; + + diagram_type type() const override; + + option layout{"layout"}; +}; + struct config : public inheritable_diagram_options { // the glob list is additive and relative to the current // directory diff --git a/src/include_diagram/generators/plantuml/include_diagram_generator.cc b/src/include_diagram/generators/plantuml/include_diagram_generator.cc new file mode 100644 index 00000000..58ed09e4 --- /dev/null +++ b/src/include_diagram/generators/plantuml/include_diagram_generator.cc @@ -0,0 +1,64 @@ +/** + * src/package_diagram/generators/plantuml/package_diagram_generator.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. + */ + +#include "include_diagram_generator.h" + +#include "util/error.h" + +namespace clanguml::include_diagram::generators::plantuml { + +generator::generator(diagram_config &config, diagram_model &model) + : common_generator{config, model} +{ +} + +void generator::generate_relationships( + const source_file &f, std::ostream &ostr) const +{ + LOG_DBG("Generating relationships for file {}", f.full_name(true)); +} + +void generator::generate(const source_file &f, std::ostream &ostr) const +{ + LOG_DBG("Generating source_file {}", f.name()); +} + +void generator::generate(std::ostream &ostr) const +{ + ostr << "@startuml" << '\n'; + + generate_plantuml_directives(ostr, m_config.puml().before); + + for (const auto &p : m_model) { + generate(dynamic_cast(*p), ostr); + ostr << '\n'; + } + + // Process package relationships + for (const auto &p : m_model) { + generate_relationships(dynamic_cast(*p), ostr); + ostr << '\n'; + } + + generate_config_layout_hints(ostr); + + generate_plantuml_directives(ostr, m_config.puml().after); + + ostr << "@enduml" << '\n'; +} +} diff --git a/src/include_diagram/generators/plantuml/include_diagram_generator.h b/src/include_diagram/generators/plantuml/include_diagram_generator.h new file mode 100644 index 00000000..8b2967c3 --- /dev/null +++ b/src/include_diagram/generators/plantuml/include_diagram_generator.h @@ -0,0 +1,70 @@ +/** + * src/include_diagram/generators/plantuml/include_diagram_generator.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 "common/generators/plantuml/generator.h" +#include "common/model/package.h" +#include "common/model/relationship.h" +#include "config/config.h" +#include "cx/compilation_database.h" +#include "include_diagram/model/diagram.h" +#include "include_diagram/model/source_file.h" +#include "include_diagram/visitor/translation_unit_visitor.h" +#include "util/util.h" + +#include +#include + +#include +#include +#include +#include + +namespace clanguml { +namespace include_diagram { +namespace generators { +namespace plantuml { + +using diagram_config = clanguml::config::include_diagram; +using diagram_model = clanguml::include_diagram::model::diagram; + +template +using common_generator = + clanguml::common::generators::plantuml::generator; + +using clanguml::common::model::access_t; +using clanguml::common::model::package; +using clanguml::common::model::relationship_t; +using clanguml::include_diagram::model::source_file; +using namespace clanguml::util; + +class generator : public common_generator { +public: + generator(diagram_config &config, diagram_model &model); + + void generate_relationships(const source_file &p, std::ostream &ostr) const; + + void generate(const source_file &e, std::ostream &ostr) const; + + void generate(std::ostream &ostr) const; +}; + +} +} +} +} diff --git a/src/include_diagram/model/diagram.cc b/src/include_diagram/model/diagram.cc new file mode 100644 index 00000000..3ad181f0 --- /dev/null +++ b/src/include_diagram/model/diagram.cc @@ -0,0 +1,65 @@ +/** + * src/include_diagram/model/diagram.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. + */ + +#include "diagram.h" + +#include "util/error.h" +#include "util/util.h" + +namespace clanguml::include_diagram::model { + +common::model::diagram_t diagram::type() const +{ + return common::model::diagram_t::kPackage; +} + +type_safe::optional_ref diagram::get( + const std::string &full_name) const +{ + return get_file(full_name); +} + +void diagram::add_file(std::unique_ptr &&f) +{ + LOG_DBG("Adding source file: {}, {}", f->name(), f->full_name(true)); + + files_.emplace_back(*f); + + add_element(f->path(), std::move(f)); +} + +type_safe::optional_ref +diagram::get_file(const std::string &name) const +{ + for (const auto &p : files_) { + if (p.get().full_name(false) == name) { + return {p}; + } + } + + return type_safe::nullopt; +} + +std::string diagram::to_alias(const std::string &full_name) const +{ + LOG_DBG("Looking for alias for {}", full_name); + + return full_name; +} + +} diff --git a/src/include_diagram/model/diagram.h b/src/include_diagram/model/diagram.h new file mode 100644 index 00000000..8c71bcd7 --- /dev/null +++ b/src/include_diagram/model/diagram.h @@ -0,0 +1,59 @@ +/** + * src/include_diagram/model/diagram.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 "common/model/diagram.h" +#include "common/model/package.h" +#include "source_file.h" + +#include + +#include +#include + +namespace clanguml::include_diagram::model { + +class diagram : public clanguml::common::model::diagram, + public clanguml::common::model::nested_trait { +public: + diagram() = default; + + diagram(const diagram &) = delete; + diagram(diagram &&) = default; + diagram &operator=(const diagram &) = delete; + diagram &operator=(diagram &&) = default; + + common::model::diagram_t type() const override; + + type_safe::optional_ref get( + const std::string &full_name) const; + + void add_file(std::unique_ptr &&f); + + type_safe::optional_ref get_file( + const std::string &name) const; + + std::string to_alias(const std::string &full_name) const; + +private: + std::vector< + type_safe::object_ref> + files_; +}; +} diff --git a/src/include_diagram/model/source_file.cc b/src/include_diagram/model/source_file.cc new file mode 100644 index 00000000..3b596231 --- /dev/null +++ b/src/include_diagram/model/source_file.cc @@ -0,0 +1,19 @@ +/** + * src/include_diagram/model/source_file.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. + */ + +#include "source_file.h" diff --git a/src/include_diagram/model/source_file.h b/src/include_diagram/model/source_file.h new file mode 100644 index 00000000..0e16835e --- /dev/null +++ b/src/include_diagram/model/source_file.h @@ -0,0 +1,95 @@ +/** + * src/include_diagram/model/source_file.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 "common/model/diagram_element.h" +#include "common/model/nested_trait.h" +#include "common/model/path.h" +#include "common/model/stylable_element.h" +#include "util/util.h" + +#include +#include + +#include +#include +#include + +namespace clanguml::include_diagram::model { + +enum class source_file_type { kDirectory, kHeader, kImplementation }; + +struct fs_path_sep { + static constexpr std::string_view value = "/"; +}; + +using filesystem_path = common::model::path; + +class source_file + : public common::model::diagram_element, + public common::model::stylable_element, + public common::model::nested_trait { +public: + source_file() = default; + + void set_path(const filesystem_path &p) { path_ = p; } + + source_file(const source_file &) = delete; + source_file(source_file &&) = default; + source_file &operator=(const source_file &) = delete; + source_file &operator=(source_file &&) = delete; + + const filesystem_path &path() const { return path_; } + + std::string full_name(bool relative) const override + { + return (path_ | name()).to_string(); + } + + void add_file(std::unique_ptr &&f) + { + LOG_DBG("Adding source file: {}, {}", f->name(), f->full_name(true)); + + add_element(f->path(), std::move(f)); + } + +private: + filesystem_path path_; +}; +} + +namespace std { + +template <> struct hash { + std::size_t operator()( + const clanguml::include_diagram::model::filesystem_path &key) const + { + using clanguml::common::model::path; + + std::size_t seed = key.size(); + for (const auto &ns : key) { + seed ^= std::hash{}(ns) + 0x6a3712b5 + (seed << 6) + + (seed >> 2); + } + + return seed; + } +}; + +} diff --git a/src/include_diagram/visitor/element_visitor_context.cc b/src/include_diagram/visitor/element_visitor_context.cc new file mode 100644 index 00000000..05118ab7 --- /dev/null +++ b/src/include_diagram/visitor/element_visitor_context.cc @@ -0,0 +1,43 @@ +/** + * src/include_diagram/model/visitor/element_visitor_context.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. + */ + +#include "element_visitor_context.h" + +#include "translation_unit_context.h" + +namespace clanguml::include_diagram::visitor { + +template +element_visitor_context::element_visitor_context( + clanguml::include_diagram::model::diagram &diagram, T &element) + : element_{element} + , diagram_{diagram} +{ +} + +template T &element_visitor_context::element() +{ + return element_; +} + +template +clanguml::include_diagram::model::diagram &element_visitor_context::diagram() +{ + return diagram_; +} +} diff --git a/src/include_diagram/visitor/element_visitor_context.h b/src/include_diagram/visitor/element_visitor_context.h new file mode 100644 index 00000000..7dfc7e7a --- /dev/null +++ b/src/include_diagram/visitor/element_visitor_context.h @@ -0,0 +1,42 @@ +/** + * src/include_diagram/model/visitor/element_visitor_context.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_diagram/model/diagram.h" + +namespace clanguml::include_diagram::visitor { + +class translation_unit_context; + +template class element_visitor_context { +public: + element_visitor_context( + clanguml::include_diagram::model::diagram &diagram, T &element); + + T &element(); + + clanguml::include_diagram::model::diagram &diagram(); + +private: + translation_unit_context *ctx_; + + T &element_; + clanguml::include_diagram::model::diagram &diagram_; +}; + +} diff --git a/src/include_diagram/visitor/translation_unit_context.cc b/src/include_diagram/visitor/translation_unit_context.cc new file mode 100644 index 00000000..a77e46aa --- /dev/null +++ b/src/include_diagram/visitor/translation_unit_context.cc @@ -0,0 +1,63 @@ +/** + * src/include_diagram/visitor/translation_unit_context.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. + */ + +#include "translation_unit_context.h" + +#include "cx/util.h" + +namespace clanguml::include_diagram::visitor { + +translation_unit_context::translation_unit_context( + cppast::cpp_entity_index &idx, + clanguml::include_diagram::model::diagram &diagram, + const clanguml::config::include_diagram &config) + : entity_index_{idx} + , diagram_{diagram} + , config_{config} +{ +} + +const cppast::cpp_entity_index &translation_unit_context::entity_index() const +{ + return entity_index_; +} + +const clanguml::config::include_diagram & +translation_unit_context::config() const +{ + return config_; +} + +clanguml::include_diagram::model::diagram &translation_unit_context::diagram() +{ + return diagram_; +} + +void translation_unit_context::set_current_file( + type_safe::optional_ref f) +{ + current_file_ = f; +} + +type_safe::optional_ref +translation_unit_context::get_current_file() const +{ + return current_file_; +} + +} diff --git a/src/include_diagram/visitor/translation_unit_context.h b/src/include_diagram/visitor/translation_unit_context.h new file mode 100644 index 00000000..fe7cac41 --- /dev/null +++ b/src/include_diagram/visitor/translation_unit_context.h @@ -0,0 +1,62 @@ +/** + * src/include_diagram/visitor/translation_unit_context.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 "common/model/package.h" +#include "config/config.h" +#include "include_diagram/model/diagram.h" + +#include +#include +#include +#include + +namespace clanguml::include_diagram::visitor { + +class translation_unit_context { +public: + translation_unit_context(cppast::cpp_entity_index &idx, + clanguml::include_diagram::model::diagram &diagram, + const clanguml::config::include_diagram &config); + + const cppast::cpp_entity_index &entity_index() const; + + const clanguml::config::include_diagram &config() const; + + clanguml::include_diagram::model::diagram &diagram(); + + void set_current_file( + type_safe::optional_ref p); + + type_safe::optional_ref + get_current_file() const; + +private: + // Reference to the cppast entity index + cppast::cpp_entity_index &entity_index_; + + // Reference to the output diagram model + clanguml::include_diagram::model::diagram &diagram_; + + // Reference to class diagram config + const clanguml::config::include_diagram &config_; + + type_safe::optional_ref current_file_; +}; + +} diff --git a/src/include_diagram/visitor/translation_unit_visitor.cc b/src/include_diagram/visitor/translation_unit_visitor.cc new file mode 100644 index 00000000..d8c9ff2f --- /dev/null +++ b/src/include_diagram/visitor/translation_unit_visitor.cc @@ -0,0 +1,53 @@ +/** + * src/include_diagram/visitor/translation_unit_visitor.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. + */ + +#include "translation_unit_visitor.h" + +#include +#include + +namespace clanguml::include_diagram::visitor { + +using clanguml::include_diagram::model::diagram; + +translation_unit_visitor::translation_unit_visitor( + cppast::cpp_entity_index &idx, + clanguml::include_diagram::model::diagram &diagram, + const clanguml::config::include_diagram &config) + : ctx{idx, diagram, config} +{ +} + +void translation_unit_visitor::operator()(const cppast::cpp_entity &file) +{ + cppast::visit(file, + [&, this](const cppast::cpp_entity &e, cppast::visitor_info info) { + if (e.kind() == cppast::cpp_entity_kind::include_directive_t) { + + const auto &inc = + static_cast(e); + + auto file_name = std::filesystem::path(inc.full_path()); + + auto f = std::make_unique(); + f->set_path(file_name.parent_path().string()); + f->set_name(file_name.filename().string()); + } + }); +} +} diff --git a/src/include_diagram/visitor/translation_unit_visitor.h b/src/include_diagram/visitor/translation_unit_visitor.h new file mode 100644 index 00000000..f0b1318f --- /dev/null +++ b/src/include_diagram/visitor/translation_unit_visitor.h @@ -0,0 +1,51 @@ +/** + * src/include_diagram/visitor/translation_unit_visitor.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 "config/config.h" +#include "cx/cursor.h" +#include "include_diagram/model/diagram.h" +#include "include_diagram/visitor/translation_unit_context.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace clanguml::include_diagram::visitor { + +class translation_unit_visitor { +public: + translation_unit_visitor(cppast::cpp_entity_index &idx, + clanguml::include_diagram::model::diagram &diagram, + const clanguml::config::include_diagram &config); + + void operator()(const cppast::cpp_entity &file); + +private: + // ctx allows to track current visitor context, e.g. current namespace + translation_unit_context ctx; +}; +} diff --git a/src/package_diagram/model/diagram.cc b/src/package_diagram/model/diagram.cc index 903609f4..21d469c9 100644 --- a/src/package_diagram/model/diagram.cc +++ b/src/package_diagram/model/diagram.cc @@ -51,7 +51,7 @@ type_safe::optional_ref diagram::get_package( return type_safe::nullopt; } -type_safe::optional_ref diagram::get( +type_safe::optional_ref diagram::get( const std::string &full_name) const { return get_package(full_name); diff --git a/src/package_diagram/model/diagram.h b/src/package_diagram/model/diagram.h index f8c92553..2a32826f 100644 --- a/src/package_diagram/model/diagram.h +++ b/src/package_diagram/model/diagram.h @@ -29,7 +29,8 @@ namespace clanguml::package_diagram::model { class diagram : public clanguml::common::model::diagram, public clanguml::common::model::nested_trait< - clanguml::common::model::element> { + clanguml::common::model::element, + clanguml::common::model::namespace_> { public: diagram() = default; @@ -40,7 +41,7 @@ public: common::model::diagram_t type() const override; - type_safe::optional_ref get( + type_safe::optional_ref get( const std::string &full_name) const; void add_package(std::unique_ptr &&p); diff --git a/src/sequence_diagram/model/diagram.cc b/src/sequence_diagram/model/diagram.cc index 27695aa8..0a7f1a88 100644 --- a/src/sequence_diagram/model/diagram.cc +++ b/src/sequence_diagram/model/diagram.cc @@ -28,7 +28,7 @@ common::model::diagram_t diagram::type() const return common::model::diagram_t::kSequence; } -type_safe::optional_ref diagram::get( +type_safe::optional_ref diagram::get( const std::string &full_name) const { return {}; diff --git a/src/sequence_diagram/model/diagram.h b/src/sequence_diagram/model/diagram.h index 0c917960..97c7f4f1 100644 --- a/src/sequence_diagram/model/diagram.h +++ b/src/sequence_diagram/model/diagram.h @@ -36,7 +36,7 @@ public: common::model::diagram_t type() const override; - type_safe::optional_ref get( + type_safe::optional_ref get( const std::string &full_name) const; std::string to_alias(const std::string &full_name) const; diff --git a/src/util/util.cc b/src/util/util.cc index 847155c8..55991c13 100644 --- a/src/util/util.cc +++ b/src/util/util.cc @@ -101,7 +101,7 @@ std::string rtrim(const std::string &s) std::string trim(const std::string &s) { return rtrim(ltrim(s)); } -std::vector split(std::string str, std::string delimiter) +std::vector split(std::string str, std::string_view delimiter) { std::vector result; @@ -124,7 +124,8 @@ std::vector split(std::string str, std::string delimiter) return result; } -std::string join(const std::vector &toks, std::string delimiter) +std::string join( + const std::vector &toks, std::string_view delimiter) { return fmt::format("{}", fmt::join(toks, delimiter)); } diff --git a/src/util/util.h b/src/util/util.h index 26ad5506..4208c5c4 100644 --- a/src/util/util.h +++ b/src/util/util.h @@ -83,9 +83,10 @@ std::string get_git_toplevel_dir(); * * @return Vector of string tokens. */ -std::vector split(std::string str, std::string delimiter); +std::vector split(std::string str, std::string_view delimiter); -std::string join(const std::vector &toks, std::string delimiter); +std::string join( + const std::vector &toks, std::string_view delimiter); /** * @brief Remove any qualifiers (e.g. const) from type. diff --git a/tests/t40001/.clang-uml b/tests/t40001/.clang-uml new file mode 100644 index 00000000..2ccf4457 --- /dev/null +++ b/tests/t40001/.clang-uml @@ -0,0 +1,10 @@ +compilation_database_dir: .. +output_directory: puml +diagrams: + t40001_include: + type: include + glob: + - ../../tests/t40001/t40001.cc + plantuml: + before: + - "' t40001 test include diagram" \ No newline at end of file diff --git a/tests/t40001/t40001.cc b/tests/t40001/t40001.cc new file mode 100644 index 00000000..fe07ee65 --- /dev/null +++ b/tests/t40001/t40001.cc @@ -0,0 +1,10 @@ +#include +#include + +#include "t40001_include1.h" + +namespace clanguml { +namespace t40001 { + +} // namespace t40001 +} // namespace clanguml diff --git a/tests/t40001/t40001_include1.h b/tests/t40001/t40001_include1.h new file mode 100644 index 00000000..94dcfa4a --- /dev/null +++ b/tests/t40001/t40001_include1.h @@ -0,0 +1,7 @@ +#pragma once + +namespace clanguml::t40001 { + +int foo() { return 0; } + +} \ No newline at end of file diff --git a/tests/t40001/test_case.h b/tests/t40001/test_case.h new file mode 100644 index 00000000..64fcaa7e --- /dev/null +++ b/tests/t40001/test_case.h @@ -0,0 +1,40 @@ +/** + * tests/t40001/test_case.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. + */ + +TEST_CASE("t40001", "[test-case][package]") +{ + auto [config, db] = load_config("t40001"); + + auto diagram = config.diagrams["t40001_include"]; + + REQUIRE(diagram->name == "t40001_include"); + + auto model = generate_include_diagram(db, diagram); + + REQUIRE(model->name() == "t40001_include"); + + auto puml = generate_include_puml(diagram, *model); + + AliasMatcher _A(puml); + + REQUIRE_THAT(puml, StartsWith("@startuml")); + REQUIRE_THAT(puml, EndsWith("@enduml\n")); + + save_puml( + "./" + config.output_directory() + "/" + diagram->name + ".puml", puml); +} diff --git a/tests/test_cases.cc b/tests/test_cases.cc index 3487f740..a782978c 100644 --- a/tests/test_cases.cc +++ b/tests/test_cases.cc @@ -103,6 +103,22 @@ generate_package_diagram(cppast::libclang_compilation_database &db, db, diagram->name, dynamic_cast(*diagram)); } +std::unique_ptr +generate_include_diagram(cppast::libclang_compilation_database &db, + std::shared_ptr diagram) +{ + using diagram_config = clanguml::config::include_diagram; + using diagram_model = clanguml::include_diagram::model::diagram; + using diagram_visitor = + clanguml::include_diagram::visitor::translation_unit_visitor; + + inject_diagram_options(diagram); + + return clanguml::common::generators::plantuml::generate( + db, diagram->name, dynamic_cast(*diagram)); +} + std::string generate_sequence_puml( std::shared_ptr config, clanguml::sequence_diagram::model::diagram &model) @@ -145,6 +161,20 @@ std::string generate_package_puml( return ss.str(); } +std::string generate_include_puml( + std::shared_ptr config, + clanguml::include_diagram::model::diagram &model) +{ + using namespace clanguml::include_diagram::generators::plantuml; + + std::stringstream ss; + + ss << generator( + dynamic_cast(*config), model); + + return ss.str(); +} + void save_puml(const std::string &path, const std::string &puml) { std::filesystem::path p{path}; @@ -218,6 +248,11 @@ using namespace clanguml::test::matchers; #include "t30006/test_case.h" #include "t30007/test_case.h" +// +// Include diagram tests +// +#include "t40001/test_case.h" + // // Other tests (e.g. configuration file) // diff --git a/tests/test_cases.h b/tests/test_cases.h index 030f9849..6d9a4a70 100644 --- a/tests/test_cases.h +++ b/tests/test_cases.h @@ -24,6 +24,8 @@ #include "class_diagram/visitor/translation_unit_visitor.h" #include "config/config.h" #include "cx/compilation_database.h" +#include "include_diagram/generators/plantuml/include_diagram_generator.h" +#include "include_diagram/visitor/translation_unit_visitor.h" #include "package_diagram/generators/plantuml/package_diagram_generator.h" #include "package_diagram/visitor/translation_unit_visitor.h" #include "sequence_diagram/generators/plantuml/sequence_diagram_generator.h"