Improved rendering of template methods in class diagrams

This commit is contained in:
Bartek Kryza
2023-01-25 00:56:33 +01:00
parent 0e6532f66c
commit 9a7d66f93f
24 changed files with 335 additions and 226 deletions

View File

@@ -0,0 +1,219 @@
/**
* src/common/model/template_parameter.cc
*
* Copyright (c) 2021-2023 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 "template_parameter.h"
#include "common/model/enums.h"
#include <common/model/namespace.h>
#include <utility>
namespace clanguml::common::model {
template_parameter::template_parameter(const std::string &type,
const std::string &name, std::string default_value, bool is_variadic)
: default_value_{std::move(default_value)}
, is_variadic_{is_variadic}
{
set_name(name);
set_type(type);
}
void template_parameter::set_type(const std::string &type)
{
if (util::ends_with(type, std::string{"..."})) {
type_ = type.substr(0, type.size() - 3);
is_variadic_ = true;
}
else
type_ = type;
}
std::string template_parameter::type() const
{
if (is_variadic_ && !type_.empty())
return type_ + "...";
return type_;
}
void template_parameter::set_name(const std::string &name)
{
if (util::ends_with(name, std::string{"..."})) {
name_ = name.substr(0, name.size() - 3);
is_variadic_ = true;
}
else
name_ = name;
}
std::string template_parameter::name() const
{
if (is_variadic_ && type_.empty())
return name_ + "...";
return name_;
}
void template_parameter::set_default_value(const std::string &value)
{
default_value_ = value;
}
std::string template_parameter::default_value() const { return default_value_; }
void template_parameter::is_variadic(bool is_variadic) noexcept
{
is_variadic_ = is_variadic;
}
bool template_parameter::is_variadic() const noexcept { return is_variadic_; }
bool template_parameter::is_specialization_of(
const template_parameter &ct) const
{
return (ct.is_template_parameter() ||
ct.is_template_template_parameter()) &&
!is_template_parameter();
}
void template_parameter::add_template_param(template_parameter &&ct)
{
template_params_.emplace_back(std::move(ct));
}
void template_parameter::add_template_param(const template_parameter &ct)
{
template_params_.push_back(ct);
}
const std::vector<template_parameter> &
template_parameter::template_params() const
{
return template_params_;
}
bool operator==(const template_parameter &l, const template_parameter &r)
{
bool res{false};
if (l.is_template_parameter() != r.is_template_parameter())
return res;
if (l.is_template_parameter()) {
// If this is a template parameter (e.g. 'typename T' or 'typename U'
// we don't actually care what it is called
res = (l.is_variadic() == r.is_variadic()) &&
(l.default_value() == r.default_value());
}
else
res = (l.name() == r.name()) && (l.type() == r.type()) &&
(l.default_value() == r.default_value());
return res && (l.template_params_ == r.template_params_);
}
bool operator!=(const template_parameter &l, const template_parameter &r)
{
return !(l == r);
}
std::string template_parameter::to_string(
const clanguml::common::model::namespace_ &using_namespace,
bool relative) const
{
using clanguml::common::model::namespace_;
std::string res;
if (!type().empty()) {
if (!relative)
res += namespace_{type()}.to_string();
else
res += namespace_{type()}.relative_to(using_namespace).to_string();
}
if (!name().empty()) {
if (!type().empty())
res += " ";
if (!relative)
res += namespace_{name()}.to_string();
else
res += namespace_{name()}.relative_to(using_namespace).to_string();
}
// Render nested template params
if (!template_params_.empty()) {
std::vector<std::string> params;
for (const auto &template_param : template_params_) {
params.push_back(
template_param.to_string(using_namespace, relative));
}
res += fmt::format("<{}>", fmt::join(params, ","));
}
if (!default_value().empty()) {
res += "=";
res += default_value();
}
return res;
}
bool template_parameter::find_nested_relationships(
std::vector<std::pair<int64_t, common::model::relationship_t>>
&nested_relationships,
common::model::relationship_t hint,
const std::function<bool(const std::string &full_name)> &should_include)
const
{
bool added_aggregation_relationship{false};
// If this type argument should be included in the relationship
// just add it and skip recursion (e.g. this is a user defined type)
if (should_include(name())) {
if (id()) {
nested_relationships.emplace_back(id().value(), hint);
added_aggregation_relationship =
(hint == common::model::relationship_t::kAggregation);
}
}
// Otherwise (e.g. this is a std::shared_ptr) and we're actually
// interested what is stored inside it
else {
for (const auto &template_argument : template_params()) {
if (should_include(template_argument.name()) &&
template_argument.id()) {
nested_relationships.emplace_back(
template_argument.id().value(), hint);
added_aggregation_relationship =
(hint == common::model::relationship_t::kAggregation);
}
else {
added_aggregation_relationship =
template_argument.find_nested_relationships(
nested_relationships, hint, should_include);
}
}
}
return added_aggregation_relationship;
}
} // namespace clanguml::common::model

View File

@@ -0,0 +1,133 @@
/**
* src/common/model/template_parameter.h
*
* Copyright (c) 2021-2023 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 "common/model/enums.h"
#include "common/model/namespace.h"
#include <optional>
#include <string>
#include <vector>
namespace clanguml::common::model {
/// @brief Represents template parameter or template argument
///
/// This class can represent both template parameter and template arguments,
/// including variadic parameters and instantiations with
/// nested templates
class template_parameter {
public:
template_parameter(const std::string &type = "",
const std::string &name = "", std::string default_value = "",
bool is_variadic = false);
template_parameter(const template_parameter &right) = default;
void set_type(const std::string &type);
std::string type() const;
void set_id(const int64_t id) { id_ = id; }
std::optional<int64_t> id() const { return id_; }
void set_name(const std::string &name);
std::string name() const;
void set_default_value(const std::string &value);
std::string default_value() const;
void is_variadic(bool is_variadic) noexcept;
bool is_variadic() const noexcept;
void is_pack(bool is_pack) noexcept;
bool is_pack() const noexcept;
bool is_specialization_of(const template_parameter &ct) const;
friend bool operator==(
const template_parameter &l, const template_parameter &r);
friend bool operator!=(
const template_parameter &l, const template_parameter &r);
bool is_template_parameter() const { return is_template_parameter_; }
void is_template_parameter(bool is_template_parameter)
{
is_template_parameter_ = is_template_parameter;
}
bool is_template_template_parameter() const
{
return is_template_template_parameter_;
}
void is_template_template_parameter(bool is_template_template_parameter)
{
is_template_template_parameter_ = is_template_template_parameter;
}
std::string to_string(
const clanguml::common::model::namespace_ &using_namespace,
bool relative) const;
void add_template_param(template_parameter &&ct);
void add_template_param(const template_parameter &ct);
const std::vector<template_parameter> &template_params() const;
void clear_params() { template_params_.clear(); }
bool find_nested_relationships(
std::vector<std::pair<int64_t, common::model::relationship_t>>
&nested_relationships,
common::model::relationship_t hint,
const std::function<bool(const std::string &full_name)> &should_include)
const;
private:
/// Represents the type of non-type template parameters
/// e.g. 'int'
std::string type_;
/// The name of the parameter (e.g. 'T' or 'N')
std::string name_;
/// Default value of the template parameter
std::string default_value_;
/// Whether the template parameter is a regular template parameter
/// When false, it is a non-type template parameter
bool is_template_parameter_{false};
/// Whether the template parameter is a template template parameter
/// Can only be true when is_template_parameter_ is true
bool is_template_template_parameter_{false};
/// Whether the template parameter is variadic
bool is_variadic_{false};
/// Whether the argument specializes argument pack from parent template
bool is_pack_{false};
// Nested template parameters
std::vector<template_parameter> template_params_;
std::optional<int64_t> id_;
};
} // namespace clanguml::common::model

View File

@@ -0,0 +1,97 @@
/**
* src/common/model/template_trait.cc
*
* Copyright (c) 2021-2023 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 "common/model/template_trait.h"
namespace clanguml::common::model {
std::ostream &template_trait::render_template_params(std::ostream &ostr,
const common::model::namespace_ &using_namespace, bool relative) const
{
using clanguml::common::model::namespace_;
if (!templates_.empty()) {
std::vector<std::string> tnames;
std::vector<std::string> tnames_simplified;
std::transform(templates_.cbegin(), templates_.cend(),
std::back_inserter(tnames),
[ns = using_namespace, relative](
const auto &tmplt) { return tmplt.to_string(ns, relative); });
ostr << fmt::format("<{}>", fmt::join(tnames, ","));
}
return ostr;
}
void template_trait::set_base_template(const std::string &full_name)
{
base_template_full_name_ = full_name;
}
std::string template_trait::base_template() const
{
return base_template_full_name_;
}
void template_trait::add_template(template_parameter &&tmplt)
{
templates_.push_back(std::move(tmplt));
}
bool template_trait::is_implicit() const { return is_implicit_; }
void template_trait::set_implicit(bool implicit) { is_implicit_ = implicit; }
const std::vector<template_parameter> &template_trait::templates() const
{
return templates_;
}
int template_trait::calculate_template_specialization_match(
const template_trait &other, const std::string & /*full_name*/) const
{
int res{};
// TODO: handle variadic templates
if (templates().size() != other.templates().size()) {
return res;
}
// Iterate over all template arguments
for (auto i = 0U; i < other.templates().size(); i++) {
const auto &template_arg = templates().at(i);
const auto &other_template_arg = other.templates().at(i);
if (template_arg == other_template_arg) {
res++;
}
else if (other_template_arg.is_specialization_of(template_arg)) {
continue;
}
else {
res = 0;
break;
}
}
return res;
}
} // namespace clanguml::common::model

View File

@@ -0,0 +1,54 @@
/**
* src/common/model/template_trait.h
*
* Copyright (c) 2021-2023 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 "common/model/element.h"
#include "common/model/template_parameter.h"
#include <string>
#include <vector>
namespace clanguml::common::model {
class template_trait {
public:
std::ostream &render_template_params(std::ostream &ostr,
const common::model::namespace_ &using_namespace, bool relative) const;
void set_base_template(const std::string &full_name);
std::string base_template() const;
void add_template(template_parameter &&tmplt);
const std::vector<template_parameter> &templates() const;
int calculate_template_specialization_match(
const template_trait &other, const std::string &full_name) const;
bool is_implicit() const;
void set_implicit(bool implicit);
private:
std::vector<template_parameter> templates_;
std::string base_template_full_name_;
bool is_implicit_{false};
};
} // namespace clanguml::common::model