Added initial structure for include diagram generation

This commit is contained in:
Bartek Kryza
2022-04-04 23:55:12 +02:00
parent 0301173a52
commit 46e8885c41
39 changed files with 1268 additions and 356 deletions

View File

@@ -17,7 +17,9 @@
*/
#pragma once
#include "diagram_element.h"
#include "enums.h"
#include "namespace.h"
#include <type_safe/optional_ref.hpp>
@@ -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<const element> get(
virtual type_safe::optional_ref<const diagram_element> get(
const std::string &full_name) const = 0;
diagram(const diagram &) = delete;

View File

@@ -0,0 +1,99 @@
/**
* src/common/model/diagram_element.cc
*
* Copyright (c) 2021-2022 Bartek Kryza <bkryza@gmail.com>
*
* 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 <ostream>
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<relationship> &diagram_element::relationships()
{
return relationships_;
}
const std::vector<relationship> &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;
}
}

View File

@@ -0,0 +1,71 @@
/**
* src/common/model/diagram_element.h
*
* Copyright (c) 2021-2022 Bartek Kryza <bkryza@gmail.com>
*
* 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 <inja/inja.hpp>
#include <atomic>
#include <exception>
#include <string>
#include <vector>
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<relationship> &relationships();
const std::vector<relationship> &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<relationship> relationships_;
static std::atomic_uint64_t m_nextId;
};
}

View File

@@ -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<relationship> &element::relationships() { return relationships_; }
const std::vector<relationship> &element::relationships() const
{
return relationships_;
}
void element::append(const element &e) { decorated_element::append(e); }
inja::json element::context() const
{
inja::json ctx;

View File

@@ -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<relationship> &relationships();
const std::vector<relationship> &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<relationship> relationships_;
type_safe::optional<source_location> location_;
static std::atomic_uint64_t m_nextId;
// type_safe::optional<source_location> location_;
};
}

View File

@@ -35,7 +35,8 @@ enum class relationship_t {
kAssociation,
kInstantiation,
kFriendship,
kDependency
kDependency,
kInclusion
};
enum class message_t { kCall, kReturn };

View File

@@ -17,194 +17,3 @@
*/
#include "namespace.h"
#include "util/util.h"
namespace clanguml::common::model {
namespace_::namespace_(std::initializer_list<std::string> ns)
{
if ((ns.size() == 1) && util::contains(*ns.begin(), "::"))
namespace_path_ = util::split(*ns.begin(), "::");
else
namespace_path_ = ns;
}
namespace_::namespace_(const std::vector<std::string> &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_> 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<namespace_>{}(left) < std::hash<namespace_>{}(right);
}
std::string namespace_::name() const
{
assert(size() > 0);
return namespace_path_.back();
}
}

View File

@@ -17,78 +17,20 @@
*/
#pragma once
#include "path.h"
#include <string>
#include <type_safe/optional.hpp>
#include <vector>
namespace clanguml::common::model {
class namespace_ {
public:
using container_type = std::vector<std::string>;
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<std::string> ns);
explicit namespace_(const std::vector<std::string> &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<namespace_> 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<ns_path_separator>;
}
namespace std {
@@ -96,7 +38,7 @@ namespace std {
template <> struct hash<clanguml::common::model::namespace_> {
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<clanguml::common::model::namespace_> {
}
};
}
}

View File

@@ -1,5 +1,5 @@
/**
* src/common/model/element.h
* src/common/model/nested_trait.h
*
* Copyright (c) 2021-2022 Bartek Kryza <bkryza@gmail.com>
*
@@ -26,7 +26,7 @@
#include <vector>
namespace clanguml::common::model {
template <typename T> class nested_trait {
template <typename T, typename Path> class nested_trait {
public:
nested_trait() = default;
@@ -52,31 +52,31 @@ public:
}
template <typename V = T>
void add_element(namespace_ ns, std::unique_ptr<V> p)
void add_element(const Path &path, std::unique_ptr<V> 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<nested_trait<T> *>(&parent.value()))
dynamic_cast<nested_trait<T> &>(parent.value())
if (parent && dynamic_cast<nested_trait<T, Path> *>(&parent.value()))
dynamic_cast<nested_trait<T, Path> &>(parent.value())
.template add_element<V>(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 <typename V = T> auto get_element(const namespace_ &path) const
template <typename V = T> 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<V>{};
if (dynamic_cast<nested_trait<T> *>(&p.value()))
return dynamic_cast<nested_trait<T> &>(p.value()).get_element<V>(
namespace_{path.begin() + 1, path.end()});
if (dynamic_cast<nested_trait<T, Path> *>(&p.value()))
return dynamic_cast<nested_trait<T, Path> &>(p.value())
.get_element<V>(Path{path.begin() + 1, path.end()});
return type_safe::optional_ref<V>{};
}
@@ -145,9 +145,10 @@ public:
std::cout << "--- Printing tree:\n";
}
for (const auto &e : d) {
if (dynamic_cast<nested_trait<T> *>(e.get())) {
if (dynamic_cast<nested_trait<T, Path> *>(e.get())) {
std::cout << std::string(level, ' ') << "[" << *e << "]\n";
dynamic_cast<nested_trait<T> *>(e.get())->print_tree(level + 1);
dynamic_cast<nested_trait<T, Path> *>(e.get())->print_tree(
level + 1);
}
else {
std::cout << std::string(level, ' ') << "- " << *e << "]\n";

View File

@@ -33,7 +33,7 @@ namespace clanguml::common::model {
class package : public element,
public stylable_element,
public nested_trait<element> {
public nested_trait<element, namespace_> {
public:
package(const common::model::namespace_ &using_namespace);

207
src/common/model/path.h Normal file
View File

@@ -0,0 +1,207 @@
/**
* src/common/model/path.h
*
* Copyright (c) 2021-2022 Bartek Kryza <bkryza@gmail.com>
*
* 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 <string>
#include <type_safe/optional.hpp>
#include <vector>
namespace clanguml::common::model {
template <typename Sep> class path {
public:
using container_type = std::vector<std::string>;
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<std::string> 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<std::string> &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<Sep> &left, const path<Sep> &right)
{
return left.path_ == right.path_;
}
friend bool operator<(const path<Sep> &left, const path<Sep> &right)
{
return std::hash<path<Sep>>{}(left) < std::hash<path<Sep>>{}(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<path> 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_;
};
}