Initial refactoring of sequence diagram visitor to include participants
This commit is contained in:
@@ -36,6 +36,10 @@ public:
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
optional_ref(T *value) { value_ = value; }
|
||||||
|
|
||||||
|
optional_ref(const T *value) { value_ = value; }
|
||||||
|
|
||||||
optional_ref(T &value) { value_ = &value; }
|
optional_ref(T &value) { value_ = &value; }
|
||||||
|
|
||||||
optional_ref(const T &value) { value_ = &value; }
|
optional_ref(const T &value) { value_ = &value; }
|
||||||
|
|||||||
@@ -38,15 +38,15 @@ generator::generator(
|
|||||||
|
|
||||||
void generator::generate_call(const message &m, std::ostream &ostr) const
|
void generator::generate_call(const message &m, std::ostream &ostr) const
|
||||||
{
|
{
|
||||||
const auto from = m_config.using_namespace().relative(m.from);
|
const auto from = m_config.using_namespace().relative(m.from_name);
|
||||||
const auto to = m_config.using_namespace().relative(m.to);
|
const auto to = m_config.using_namespace().relative(m.to_name);
|
||||||
|
|
||||||
if (from.empty() || to.empty()) {
|
if (from.empty() || to.empty()) {
|
||||||
LOG_DBG("Skipping empty call from '{}' to '{}'", from, to);
|
LOG_DBG("Skipping empty call from '{}' to '{}'", from, to);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto message = m.message;
|
auto message = m.message_name;
|
||||||
if (!message.empty()) {
|
if (!message.empty()) {
|
||||||
message = m_config.using_namespace().relative(message);
|
message = m_config.using_namespace().relative(message);
|
||||||
message += "()";
|
message += "()";
|
||||||
@@ -57,7 +57,7 @@ void generator::generate_call(const message &m, std::ostream &ostr) const
|
|||||||
<< to << "\" : " << message << std::endl;
|
<< to << "\" : " << message << std::endl;
|
||||||
|
|
||||||
LOG_DBG("Generated call '{}' from {} [{}] to {} [{}]", message, from,
|
LOG_DBG("Generated call '{}' from {} [{}] to {} [{}]", message, from,
|
||||||
m.from_usr, to, m.to_usr);
|
m.from_name, to, m.to_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
void generator::generate_return(const message &m, std::ostream &ostr) const
|
void generator::generate_return(const message &m, std::ostream &ostr) const
|
||||||
@@ -65,8 +65,8 @@ void generator::generate_return(const message &m, std::ostream &ostr) const
|
|||||||
// Add return activity only for messages between different actors and
|
// Add return activity only for messages between different actors and
|
||||||
// only if the return type is different than void
|
// only if the return type is different than void
|
||||||
if ((m.from != m.to) && (m.return_type != "void")) {
|
if ((m.from != m.to) && (m.return_type != "void")) {
|
||||||
const auto from = m_config.using_namespace().relative(m.from);
|
const auto from = m_config.using_namespace().relative(m.from_name);
|
||||||
const auto to = m_config.using_namespace().relative(m.to);
|
const auto to = m_config.using_namespace().relative(m.to_name);
|
||||||
|
|
||||||
ostr << '"' << to << "\" "
|
ostr << '"' << to << "\" "
|
||||||
<< common::generators::plantuml::to_plantuml(message_t::kReturn)
|
<< common::generators::plantuml::to_plantuml(message_t::kReturn)
|
||||||
@@ -77,17 +77,25 @@ void generator::generate_return(const message &m, std::ostream &ostr) const
|
|||||||
void generator::generate_activity(const activity &a, std::ostream &ostr) const
|
void generator::generate_activity(const activity &a, std::ostream &ostr) const
|
||||||
{
|
{
|
||||||
for (const auto &m : a.messages) {
|
for (const auto &m : a.messages) {
|
||||||
const auto to = m_config.using_namespace().relative(m.to);
|
const auto to = m_config.using_namespace().relative(m.to_name);
|
||||||
|
|
||||||
if (to.empty())
|
if (to.empty())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
LOG_DBG("Generating message {} --> {}",
|
||||||
|
m.from_name, m.to_name);
|
||||||
generate_call(m, ostr);
|
generate_call(m, ostr);
|
||||||
|
|
||||||
ostr << "activate " << '"' << to << '"' << std::endl;
|
ostr << "activate " << '"' << to << '"' << std::endl;
|
||||||
|
|
||||||
if (m_model.sequences.find(m.to_usr) != m_model.sequences.end())
|
if (m_model.sequences.find(m.to) != m_model.sequences.end()) {
|
||||||
generate_activity(m_model.sequences[m.to_usr], ostr);
|
LOG_DBG("Creating activity {} --> {} - missing sequence {}",
|
||||||
|
m.from_name, m.to_name, m.to);
|
||||||
|
generate_activity(m_model.sequences[m.to], ostr);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
LOG_DBG("Skipping activity {} --> {} - missing sequence {}",
|
||||||
|
m.from_name, m.to_name, m.to);
|
||||||
|
|
||||||
generate_return(m, ostr);
|
generate_return(m, ostr);
|
||||||
|
|
||||||
@@ -97,6 +105,8 @@ void generator::generate_activity(const activity &a, std::ostream &ostr) const
|
|||||||
|
|
||||||
void generator::generate(std::ostream &ostr) const
|
void generator::generate(std::ostream &ostr) const
|
||||||
{
|
{
|
||||||
|
m_model.print();
|
||||||
|
|
||||||
ostr << "@startuml" << std::endl;
|
ostr << "@startuml" << std::endl;
|
||||||
|
|
||||||
generate_plantuml_directives(ostr, m_config.puml().before);
|
generate_plantuml_directives(ostr, m_config.puml().before);
|
||||||
@@ -107,6 +117,7 @@ void generator::generate(std::ostream &ostr) const
|
|||||||
for (const auto &[k, v] : m_model.sequences) {
|
for (const auto &[k, v] : m_model.sequences) {
|
||||||
std::string vfrom = v.from;
|
std::string vfrom = v.from;
|
||||||
if (vfrom == sf.location) {
|
if (vfrom == sf.location) {
|
||||||
|
LOG_DBG("Found sequence diagram start point: {}", k);
|
||||||
start_from = k;
|
start_from = k;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,6 +58,20 @@ inja::json diagram::context() const
|
|||||||
return ctx;
|
return ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void diagram::print() const
|
||||||
|
{
|
||||||
|
for (const auto &[from_id, act] : sequences) {
|
||||||
|
LOG_DBG("Sequence id={}:", from_id);
|
||||||
|
LOG_DBG(" Activity id={}, from={}:", act.usr, act.from);
|
||||||
|
for (const auto &message : act.messages) {
|
||||||
|
LOG_DBG(
|
||||||
|
" Message from={}, from_id={}, to={}, to_id={}, name={}",
|
||||||
|
message.from_name, message.from, message.to_name, message.to,
|
||||||
|
message.message_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace clanguml::common::model {
|
namespace clanguml::common::model {
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
#include "activity.h"
|
#include "activity.h"
|
||||||
#include "common/model/diagram.h"
|
#include "common/model/diagram.h"
|
||||||
#include "common/types.h"
|
#include "common/types.h"
|
||||||
|
#include "participant.h"
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
@@ -47,9 +48,41 @@ public:
|
|||||||
|
|
||||||
inja::json context() const override;
|
inja::json context() const override;
|
||||||
|
|
||||||
|
void print() const;
|
||||||
|
|
||||||
bool started{false};
|
bool started{false};
|
||||||
|
|
||||||
std::map<int64_t, activity> sequences;
|
template <typename T>
|
||||||
|
common::optional_ref<T> get_participant(
|
||||||
|
common::model::diagram_element::id_t id)
|
||||||
|
{
|
||||||
|
if (participants.find(id) == participants.end()) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return common::optional_ref<T>(
|
||||||
|
static_cast<T *>(participants.at(id).get()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_participant(std::unique_ptr<participant> p)
|
||||||
|
{
|
||||||
|
LOG_DBG("Adding {} participant: {}, {} [{}]", p->type_name(),
|
||||||
|
p->full_name(false), p->id(),
|
||||||
|
p->type_name() == "method"
|
||||||
|
? dynamic_cast<method *>(p.get())->method_name()
|
||||||
|
: "");
|
||||||
|
|
||||||
|
const auto pid = p->id();
|
||||||
|
|
||||||
|
if (participants.find(pid) == participants.end()) {
|
||||||
|
participants.emplace(pid, std::move(p));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::map<common::model::diagram_element::id_t, activity> sequences;
|
||||||
|
|
||||||
|
std::map<common::model::diagram_element::id_t, std::unique_ptr<participant>>
|
||||||
|
participants;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,13 +25,34 @@
|
|||||||
namespace clanguml::sequence_diagram::model {
|
namespace clanguml::sequence_diagram::model {
|
||||||
|
|
||||||
struct message {
|
struct message {
|
||||||
|
message()
|
||||||
|
: from{}
|
||||||
|
, from_name{}
|
||||||
|
, to{}
|
||||||
|
, to_name{}
|
||||||
|
, message_name{}
|
||||||
|
, return_type{}
|
||||||
|
, line{}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
common::model::message_t type;
|
common::model::message_t type;
|
||||||
std::string from;
|
|
||||||
std::uint_least64_t from_usr{};
|
/// Source participant id
|
||||||
std::string to;
|
std::uint_least64_t from;
|
||||||
std::int64_t to_usr{};
|
std::string from_name;
|
||||||
std::string message;
|
// std::uint_least64_t from_usr{};
|
||||||
|
|
||||||
|
/// Target participant id
|
||||||
|
std::uint_least64_t to;
|
||||||
|
std::string to_name;
|
||||||
|
|
||||||
|
// std::int64_t to_usr{};
|
||||||
|
|
||||||
|
std::string message_name;
|
||||||
|
|
||||||
std::string return_type;
|
std::string return_type;
|
||||||
|
|
||||||
unsigned int line;
|
unsigned int line;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
131
src/sequence_diagram/model/participant.cc
Normal file
131
src/sequence_diagram/model/participant.cc
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
/**
|
||||||
|
* src/sequence_diagram/model/participant.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 "participant.h"
|
||||||
|
|
||||||
|
namespace clanguml::sequence_diagram::model {
|
||||||
|
class_::class_(const common::model::namespace_ &using_namespace)
|
||||||
|
: participant{using_namespace}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool class_::is_struct() const { return is_struct_; }
|
||||||
|
|
||||||
|
void class_::is_struct(bool is_struct) { is_struct_ = is_struct; }
|
||||||
|
|
||||||
|
bool class_::is_template() const { return is_template_; }
|
||||||
|
|
||||||
|
void class_::is_template(bool is_template) { is_template_ = is_template; }
|
||||||
|
|
||||||
|
bool class_::is_template_instantiation() const
|
||||||
|
{
|
||||||
|
return is_template_instantiation_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void class_::is_template_instantiation(bool is_template_instantiation)
|
||||||
|
{
|
||||||
|
is_template_instantiation_ = is_template_instantiation;
|
||||||
|
}
|
||||||
|
|
||||||
|
void class_::add_template(class_diagram::model::template_parameter tmplt)
|
||||||
|
{
|
||||||
|
templates_.emplace_back(std::move(tmplt));
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<class_diagram::model::template_parameter> &
|
||||||
|
class_::templates() const
|
||||||
|
{
|
||||||
|
return templates_;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string class_::full_name_no_ns() const
|
||||||
|
{
|
||||||
|
using namespace clanguml::util;
|
||||||
|
|
||||||
|
std::ostringstream ostr;
|
||||||
|
|
||||||
|
ostr << name();
|
||||||
|
|
||||||
|
render_template_params(ostr, false);
|
||||||
|
|
||||||
|
return ostr.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string class_::full_name(bool relative) const
|
||||||
|
{
|
||||||
|
using namespace clanguml::util;
|
||||||
|
using clanguml::common::model::namespace_;
|
||||||
|
|
||||||
|
std::ostringstream ostr;
|
||||||
|
|
||||||
|
ostr << name_and_ns();
|
||||||
|
render_template_params(ostr, relative);
|
||||||
|
|
||||||
|
std::string res;
|
||||||
|
|
||||||
|
if (relative)
|
||||||
|
res = using_namespace().relative(ostr.str());
|
||||||
|
else
|
||||||
|
res = ostr.str();
|
||||||
|
|
||||||
|
if (res.empty())
|
||||||
|
return "<<anonymous>>";
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostringstream &class_::render_template_params(
|
||||||
|
std::ostringstream &ostr, 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
function::function(const common::model::namespace_ &using_namespace)
|
||||||
|
: participant{using_namespace}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string function::full_name(bool relative) const
|
||||||
|
{
|
||||||
|
return element::full_name(relative) + "()";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string function::full_name_no_ns() const
|
||||||
|
{
|
||||||
|
return element::full_name_no_ns() + "()";
|
||||||
|
}
|
||||||
|
|
||||||
|
method::method(const common::model::namespace_ &using_namespace)
|
||||||
|
: participant{using_namespace}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
177
src/sequence_diagram/model/participant.h
Normal file
177
src/sequence_diagram/model/participant.h
Normal file
@@ -0,0 +1,177 @@
|
|||||||
|
/**
|
||||||
|
* src/sequence_diagram/model/participant.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 "class_diagram/model/template_parameter.h"
|
||||||
|
#include "class_diagram/model/type_alias.h"
|
||||||
|
#include "common/model/element.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace clanguml::sequence_diagram::model {
|
||||||
|
|
||||||
|
struct participant : public common::model::element,
|
||||||
|
public common::model::stylable_element {
|
||||||
|
enum class stereotype_t {
|
||||||
|
participant = 0,
|
||||||
|
actor,
|
||||||
|
boundary,
|
||||||
|
control,
|
||||||
|
entity,
|
||||||
|
database,
|
||||||
|
collections,
|
||||||
|
queue
|
||||||
|
};
|
||||||
|
|
||||||
|
using common::model::element::element;
|
||||||
|
|
||||||
|
participant(const participant &) = delete;
|
||||||
|
participant(participant &&) noexcept = delete;
|
||||||
|
participant &operator=(const participant &) = delete;
|
||||||
|
participant &operator=(participant &&) = delete;
|
||||||
|
|
||||||
|
std::string type_name() const override { return "participant"; }
|
||||||
|
|
||||||
|
stereotype_t stereotype_{stereotype_t::participant};
|
||||||
|
};
|
||||||
|
//
|
||||||
|
// struct template_trait {
|
||||||
|
// void set_base_template(const std::string &full_name)
|
||||||
|
// {
|
||||||
|
// base_template_full_name_ = full_name;
|
||||||
|
// }
|
||||||
|
// std::string base_template() const { return base_template_full_name_; }
|
||||||
|
//
|
||||||
|
// void add_template(class_diagram::model::template_parameter tmplt)
|
||||||
|
// {
|
||||||
|
// templates_.push_back(std::move(tmplt));
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// const std::vector<class_diagram::model::template_parameter> &
|
||||||
|
// templates() const
|
||||||
|
// {
|
||||||
|
// return templates_;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// std::vector<class_diagram::model::template_parameter> templates_;
|
||||||
|
// std::string base_template_full_name_;
|
||||||
|
//};
|
||||||
|
|
||||||
|
struct class_ : public participant {
|
||||||
|
public:
|
||||||
|
class_(const common::model::namespace_ &using_namespace);
|
||||||
|
|
||||||
|
class_(const class_ &) = delete;
|
||||||
|
class_(class_ &&) noexcept = delete;
|
||||||
|
class_ &operator=(const class_ &) = delete;
|
||||||
|
class_ &operator=(class_ &&) = delete;
|
||||||
|
|
||||||
|
std::string type_name() const override { return "class"; }
|
||||||
|
|
||||||
|
bool is_struct() const;
|
||||||
|
void is_struct(bool is_struct);
|
||||||
|
|
||||||
|
bool is_template() const;
|
||||||
|
void is_template(bool is_template);
|
||||||
|
|
||||||
|
bool is_template_instantiation() const;
|
||||||
|
void is_template_instantiation(bool is_template_instantiation);
|
||||||
|
|
||||||
|
void add_template(class_diagram::model::template_parameter tmplt);
|
||||||
|
|
||||||
|
const std::vector<class_diagram::model::template_parameter> &
|
||||||
|
templates() const;
|
||||||
|
|
||||||
|
void set_base_template(const std::string &full_name);
|
||||||
|
std::string base_template() const;
|
||||||
|
|
||||||
|
friend bool operator==(const class_ &l, const class_ &r);
|
||||||
|
|
||||||
|
std::string full_name(bool relative = true) const override;
|
||||||
|
|
||||||
|
std::string full_name_no_ns() const override;
|
||||||
|
|
||||||
|
bool is_abstract() const;
|
||||||
|
|
||||||
|
bool is_alias() const { return is_alias_; }
|
||||||
|
|
||||||
|
void is_alias(bool alias) { is_alias_ = alias; }
|
||||||
|
|
||||||
|
int calculate_template_specialization_match(
|
||||||
|
const class_ &other, const std::string &full_name) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::ostringstream &render_template_params(
|
||||||
|
std::ostringstream &ostr, bool relative) const;
|
||||||
|
|
||||||
|
bool is_struct_{false};
|
||||||
|
bool is_template_{false};
|
||||||
|
bool is_template_instantiation_{false};
|
||||||
|
bool is_alias_{false};
|
||||||
|
std::vector<class_diagram::model::template_parameter> templates_;
|
||||||
|
std::string base_template_full_name_;
|
||||||
|
std::map<std::string, clanguml::class_diagram::model::type_alias>
|
||||||
|
type_aliases_;
|
||||||
|
|
||||||
|
std::string full_name_;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct function : public participant {
|
||||||
|
function(const common::model::namespace_ &using_namespace);
|
||||||
|
|
||||||
|
function(const function &) = delete;
|
||||||
|
function(function &&) noexcept = delete;
|
||||||
|
function &operator=(const function &) = delete;
|
||||||
|
function &operator=(function &&) = delete;
|
||||||
|
|
||||||
|
std::string type_name() const override { return "function"; }
|
||||||
|
|
||||||
|
std::string full_name(bool relative = true) const override;
|
||||||
|
|
||||||
|
std::string full_name_no_ns() const override;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct method : public participant {
|
||||||
|
method(const common::model::namespace_ &using_namespace);
|
||||||
|
|
||||||
|
method(const function &) = delete;
|
||||||
|
method(method &&) noexcept = delete;
|
||||||
|
method &operator=(const method &) = delete;
|
||||||
|
method &operator=(method &&) = delete;
|
||||||
|
|
||||||
|
std::string type_name() const override { return "method"; }
|
||||||
|
|
||||||
|
const std::string method_name() const {
|
||||||
|
return method_name_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_method_name(const std::string& name) { method_name_ = name; }
|
||||||
|
|
||||||
|
void set_class_id(diagram_element::id_t id) { class_id_ = id; }
|
||||||
|
|
||||||
|
diagram_element::id_t class_id() const { return class_id_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
diagram_element::id_t class_id_;
|
||||||
|
std::string method_name_;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct function_template : public participant { };
|
||||||
|
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -17,6 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "common/visitor/translation_unit_visitor.h"
|
||||||
#include "config/config.h"
|
#include "config/config.h"
|
||||||
#include "sequence_diagram/model/diagram.h"
|
#include "sequence_diagram/model/diagram.h"
|
||||||
|
|
||||||
@@ -26,8 +27,182 @@
|
|||||||
|
|
||||||
namespace clanguml::sequence_diagram::visitor {
|
namespace clanguml::sequence_diagram::visitor {
|
||||||
|
|
||||||
|
std::string to_string(const clang::FunctionTemplateDecl *decl);
|
||||||
|
|
||||||
|
struct call_expression_context {
|
||||||
|
call_expression_context()
|
||||||
|
: current_class_decl_{nullptr}
|
||||||
|
, current_method_decl_{nullptr}
|
||||||
|
, current_function_decl_{nullptr}
|
||||||
|
, current_function_template_decl_{nullptr}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset()
|
||||||
|
{
|
||||||
|
current_class_decl_ = nullptr;
|
||||||
|
current_method_decl_ = nullptr;
|
||||||
|
current_function_decl_ = nullptr;
|
||||||
|
current_function_template_decl_ = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool valid() const
|
||||||
|
{
|
||||||
|
return (current_class_decl_ != nullptr) ||
|
||||||
|
(current_method_decl_ != nullptr) ||
|
||||||
|
(current_function_decl_ != nullptr) ||
|
||||||
|
(current_function_template_decl_ != nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void update(clang::CXXRecordDecl *cls) { current_class_decl_ = cls; }
|
||||||
|
|
||||||
|
void update(clang::ClassTemplateDecl *clst)
|
||||||
|
{
|
||||||
|
current_class_template_decl_ = clst;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto &get_ast_context()
|
||||||
|
{
|
||||||
|
return current_class_decl_ ? current_class_decl_->getASTContext()
|
||||||
|
: current_function_decl_->getASTContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
void update(clang::CXXMethodDecl *method) { current_method_decl_ = method; }
|
||||||
|
|
||||||
|
void update(clang::FunctionDecl *function)
|
||||||
|
{
|
||||||
|
if (!function->isCXXClassMember())
|
||||||
|
reset();
|
||||||
|
|
||||||
|
current_function_decl_ = function;
|
||||||
|
|
||||||
|
// Check if this function is a part of template function declaration,
|
||||||
|
// If no - reset the current_function_template_decl_
|
||||||
|
if (current_function_template_decl_ &&
|
||||||
|
current_function_template_decl_->getQualifiedNameAsString() !=
|
||||||
|
function->getQualifiedNameAsString()) {
|
||||||
|
current_function_template_decl_ = nullptr;
|
||||||
|
}
|
||||||
|
// else {
|
||||||
|
// call_expression_context_.current_class_method_ =
|
||||||
|
// process_class_method(method);
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
void update(clang::FunctionTemplateDecl *function_template)
|
||||||
|
{
|
||||||
|
current_function_template_decl_ = function_template;
|
||||||
|
|
||||||
|
if (!function_template->isCXXClassMember())
|
||||||
|
current_class_decl_ = nullptr;
|
||||||
|
|
||||||
|
current_function_template_decl_ = function_template;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool in_class_method() const { return current_class_decl_ != nullptr; }
|
||||||
|
|
||||||
|
bool in_function() const
|
||||||
|
{
|
||||||
|
return current_class_decl_ == nullptr &&
|
||||||
|
current_function_decl_ != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool in_function_template() const
|
||||||
|
{
|
||||||
|
return current_function_decl_ != nullptr &&
|
||||||
|
current_function_template_decl_ != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// std::string caller_name() const
|
||||||
|
// {
|
||||||
|
// if (in_class_method())
|
||||||
|
// return current_class_decl_->getQualifiedNameAsString();
|
||||||
|
// else if (in_function_template()) {
|
||||||
|
// return to_string(current_function_template_decl_);
|
||||||
|
// }
|
||||||
|
// else if (in_function()) {
|
||||||
|
// const auto function_name =
|
||||||
|
// current_function_decl_->getQualifiedNameAsString();
|
||||||
|
// LOG_DBG("Processing function {}", function_name);
|
||||||
|
// // Handle call expression within free function
|
||||||
|
// if
|
||||||
|
// (current_function_decl_->isFunctionTemplateSpecialization()) {
|
||||||
|
// /*
|
||||||
|
// /// This template specialization was formed from a
|
||||||
|
// template-id but
|
||||||
|
// /// has not yet been declared, defined, or
|
||||||
|
// instantiated. TSK_Undeclared = 0,
|
||||||
|
// /// This template specialization was implicitly
|
||||||
|
// instantiated
|
||||||
|
// from a
|
||||||
|
// /// template. (C++ [temp.inst]).
|
||||||
|
// TSK_ImplicitInstantiation,
|
||||||
|
// /// This template specialization was declared or
|
||||||
|
// defined by
|
||||||
|
// an
|
||||||
|
// /// explicit specialization (C++ [temp.expl.spec]) or
|
||||||
|
// partial
|
||||||
|
// /// specialization (C++ [temp.class.spec]).
|
||||||
|
// TSK_ExplicitSpecialization,
|
||||||
|
// /// This template specialization was instantiated from
|
||||||
|
// a
|
||||||
|
// template
|
||||||
|
// /// due to an explicit instantiation declaration
|
||||||
|
// request
|
||||||
|
// /// (C++11 [temp.explicit]).
|
||||||
|
// TSK_ExplicitInstantiationDeclaration,
|
||||||
|
// /// This template specialization was instantiated from
|
||||||
|
// a
|
||||||
|
// template
|
||||||
|
// /// due to an explicit instantiation definition
|
||||||
|
// request
|
||||||
|
// /// (C++ [temp.explicit]).
|
||||||
|
// TSK_ExplicitInstantiationDefinition
|
||||||
|
// */
|
||||||
|
// [[maybe_unused]] const auto specialization_kind =
|
||||||
|
// current_function_decl_->getTemplateSpecializationKind();
|
||||||
|
// [[maybe_unused]] const auto *primary_template =
|
||||||
|
// current_function_decl_->getPrimaryTemplate();
|
||||||
|
//
|
||||||
|
// for (const auto &arg :
|
||||||
|
// current_function_decl_->getTemplateSpecializationArgs()
|
||||||
|
// ->asArray()) {
|
||||||
|
// LOG_DBG("TEMPLATE SPECIALIZATION ARG:");
|
||||||
|
// arg.dump();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// LOG_DBG("--------------");
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return function_name + "()";
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// return "";
|
||||||
|
// }
|
||||||
|
|
||||||
|
std::int64_t caller_id() const
|
||||||
|
{
|
||||||
|
return current_caller_id_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_caller_id(std::int64_t id) {
|
||||||
|
LOG_DBG("Setting current caller id to {}", id);
|
||||||
|
current_caller_id_ = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
clang::CXXRecordDecl *current_class_decl_;
|
||||||
|
clang::ClassTemplateDecl *current_class_template_decl_;
|
||||||
|
clang::CXXMethodDecl *current_method_decl_;
|
||||||
|
clang::FunctionDecl *current_function_decl_;
|
||||||
|
clang::FunctionTemplateDecl *current_function_template_decl_;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::int64_t current_caller_id_;
|
||||||
|
};
|
||||||
|
|
||||||
class translation_unit_visitor
|
class translation_unit_visitor
|
||||||
: public clang::RecursiveASTVisitor<translation_unit_visitor> {
|
: public clang::RecursiveASTVisitor<translation_unit_visitor>,
|
||||||
|
public common::visitor::translation_unit_visitor {
|
||||||
public:
|
public:
|
||||||
translation_unit_visitor(clang::SourceManager &sm,
|
translation_unit_visitor(clang::SourceManager &sm,
|
||||||
clanguml::sequence_diagram::model::diagram &diagram,
|
clanguml::sequence_diagram::model::diagram &diagram,
|
||||||
@@ -39,6 +214,8 @@ public:
|
|||||||
|
|
||||||
virtual bool VisitCXXRecordDecl(clang::CXXRecordDecl *cls);
|
virtual bool VisitCXXRecordDecl(clang::CXXRecordDecl *cls);
|
||||||
|
|
||||||
|
bool VisitClassTemplateDecl(clang::ClassTemplateDecl *cls);
|
||||||
|
|
||||||
virtual bool VisitFunctionDecl(clang::FunctionDecl *function_declaration);
|
virtual bool VisitFunctionDecl(clang::FunctionDecl *function_declaration);
|
||||||
|
|
||||||
bool VisitFunctionTemplateDecl(
|
bool VisitFunctionTemplateDecl(
|
||||||
@@ -50,8 +227,22 @@ public:
|
|||||||
|
|
||||||
void finalize() { }
|
void finalize() { }
|
||||||
|
|
||||||
|
/// Store the mapping from local clang entity id (obtained using
|
||||||
|
/// getID()) method to clang-uml global id
|
||||||
|
void set_ast_local_id(
|
||||||
|
int64_t local_id, common::model::diagram_element::id_t global_id);
|
||||||
|
|
||||||
|
/// Retrieve the global clang-uml entity id based on the clang local id
|
||||||
|
std::optional<common::model::diagram_element::id_t> get_ast_local_id(
|
||||||
|
int64_t local_id) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
clang::SourceManager &source_manager_;
|
std::unique_ptr<clanguml::sequence_diagram::model::class_>
|
||||||
|
create_class_declaration(clang::CXXRecordDecl *cls);
|
||||||
|
|
||||||
|
bool process_template_parameters(
|
||||||
|
const clang::ClassTemplateDecl &template_declaration,
|
||||||
|
sequence_diagram::model::class_ &c);
|
||||||
|
|
||||||
// Reference to the output diagram model
|
// Reference to the output diagram model
|
||||||
clanguml::sequence_diagram::model::diagram &diagram_;
|
clanguml::sequence_diagram::model::diagram &diagram_;
|
||||||
@@ -59,10 +250,18 @@ private:
|
|||||||
// Reference to class diagram config
|
// Reference to class diagram config
|
||||||
const clanguml::config::sequence_diagram &config_;
|
const clanguml::config::sequence_diagram &config_;
|
||||||
|
|
||||||
clang::CXXRecordDecl *current_class_decl_;
|
call_expression_context call_expression_context_;
|
||||||
clang::CXXMethodDecl *current_method_decl_;
|
|
||||||
clang::FunctionDecl *current_function_decl_;
|
std::map<common::model::diagram_element::id_t,
|
||||||
clang::FunctionTemplateDecl *current_function_template_decl_;
|
std::unique_ptr<clanguml::sequence_diagram::model::class_>>
|
||||||
|
forward_declarations_;
|
||||||
|
|
||||||
|
std::map<int64_t, common::model::diagram_element::id_t> local_ast_id_map_;
|
||||||
|
|
||||||
|
std::map<int64_t /* local anonymous struct id */,
|
||||||
|
std::tuple<std::string /* field name */, common::model::relationship_t,
|
||||||
|
common::model::access_t>>
|
||||||
|
anonymous_struct_relationships_;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
14
tests/t20004/.clang-uml
Normal file
14
tests/t20004/.clang-uml
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
compilation_database_dir: ..
|
||||||
|
output_directory: puml
|
||||||
|
diagrams:
|
||||||
|
t20004_sequence:
|
||||||
|
type: sequence
|
||||||
|
glob:
|
||||||
|
- ../../tests/t20004/t20004.cc
|
||||||
|
include:
|
||||||
|
namespaces:
|
||||||
|
- clanguml::t20004
|
||||||
|
using_namespace:
|
||||||
|
- clanguml::t20004
|
||||||
|
start_from:
|
||||||
|
- function: "clanguml::t20004::main()"
|
||||||
18
tests/t20004/t20004.cc
Normal file
18
tests/t20004/t20004.cc
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
namespace clanguml {
|
||||||
|
namespace t20004 {
|
||||||
|
|
||||||
|
template <typename T> T m4(T p);
|
||||||
|
|
||||||
|
template <typename T> T m3(T p) { return m4<T>(p); }
|
||||||
|
|
||||||
|
template <typename T> T m2(T p) { return m3<T>(p); }
|
||||||
|
|
||||||
|
template <typename T> T m1(T p) { return m2<T>(p); }
|
||||||
|
|
||||||
|
template<> [[maybe_unused]] int m4<int>(int p) { return p+2; }
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
return m1<int>(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
47
tests/t20004/test_case.h
Normal file
47
tests/t20004/test_case.h
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
/**
|
||||||
|
* tests/t20004/test_case.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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
TEST_CASE("t20004", "[test-case][sequence]")
|
||||||
|
{
|
||||||
|
auto [config, db] = load_config("t20004");
|
||||||
|
|
||||||
|
auto diagram = config.diagrams["t20004_sequence"];
|
||||||
|
|
||||||
|
REQUIRE(diagram->name == "t20004_sequence");
|
||||||
|
|
||||||
|
auto model = generate_sequence_diagram(*db, diagram);
|
||||||
|
|
||||||
|
REQUIRE(model->name() == "t20004_sequence");
|
||||||
|
|
||||||
|
auto puml = generate_sequence_puml(diagram, *model);
|
||||||
|
AliasMatcher _A(puml);
|
||||||
|
|
||||||
|
REQUIRE_THAT(puml, StartsWith("@startuml"));
|
||||||
|
REQUIRE_THAT(puml, HasFunctionCall("main()", "m1<int>()"));
|
||||||
|
REQUIRE_THAT(puml, HasFunctionCall("m2<T>", "m3<T>"));
|
||||||
|
REQUIRE_THAT(puml, HasFunctionCall("m3<T>", "m4<T>"));
|
||||||
|
REQUIRE_THAT(puml, EndsWith("@enduml\n"));
|
||||||
|
|
||||||
|
|
||||||
|
// Check if all calls exist
|
||||||
|
REQUIRE_THAT(puml, HasCall("A", "log_result"));
|
||||||
|
//REQUIRE_THAT(puml, HasCallWithResponse("B", "A", "add3"));
|
||||||
|
|
||||||
|
save_puml(
|
||||||
|
"./" + config.output_directory() + "/" + diagram->name + ".puml", puml);
|
||||||
|
}
|
||||||
@@ -250,6 +250,7 @@ using namespace clanguml::test::matchers;
|
|||||||
#include "t20001/test_case.h"
|
#include "t20001/test_case.h"
|
||||||
#include "t20002/test_case.h"
|
#include "t20002/test_case.h"
|
||||||
#include "t20003/test_case.h"
|
#include "t20003/test_case.h"
|
||||||
|
#include "t20004/test_case.h"
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Package diagram tests
|
/// Package diagram tests
|
||||||
|
|||||||
Reference in New Issue
Block a user