Added initial support for package diagrams
This commit is contained in:
6
.gitignore
vendored
6
.gitignore
vendored
@@ -18,3 +18,9 @@ bin/
|
|||||||
/debug/
|
/debug/
|
||||||
/release/
|
/release/
|
||||||
/.cache
|
/.cache
|
||||||
|
|
||||||
|
|
||||||
|
# CLion
|
||||||
|
|
||||||
|
.idea/
|
||||||
|
cmake-build-debug/
|
||||||
@@ -414,7 +414,7 @@ clanguml::class_diagram::model::diagram generate(
|
|||||||
cppast::libclang_compilation_database &db, const std::string &name,
|
cppast::libclang_compilation_database &db, const std::string &name,
|
||||||
clanguml::config::class_diagram &diagram)
|
clanguml::config::class_diagram &diagram)
|
||||||
{
|
{
|
||||||
spdlog::info("Generating diagram {}.puml", name);
|
LOG_DBG("Generating diagram {}.puml", name);
|
||||||
clanguml::class_diagram::model::diagram d;
|
clanguml::class_diagram::model::diagram d;
|
||||||
d.set_name(name);
|
d.set_name(name);
|
||||||
|
|
||||||
@@ -422,7 +422,7 @@ clanguml::class_diagram::model::diagram generate(
|
|||||||
// configuration
|
// configuration
|
||||||
std::vector<std::string> translation_units{};
|
std::vector<std::string> translation_units{};
|
||||||
for (const auto &g : diagram.glob) {
|
for (const auto &g : diagram.glob) {
|
||||||
spdlog::debug("Processing glob: {}", g);
|
LOG_DBG("Processing glob: {}", g);
|
||||||
const auto matches = glob::rglob(g);
|
const auto matches = glob::rglob(g);
|
||||||
std::copy(matches.begin(), matches.end(),
|
std::copy(matches.begin(), matches.end(),
|
||||||
std::back_inserter(translation_units));
|
std::back_inserter(translation_units));
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ bool diagram::should_include(const std::string &name_) const
|
|||||||
|
|
||||||
for (const auto &ex : exclude.namespaces) {
|
for (const auto &ex : exclude.namespaces) {
|
||||||
if (name.find(ex) == 0) {
|
if (name.find(ex) == 0) {
|
||||||
spdlog::debug("Skipping from diagram: {}", name);
|
LOG_DBG("Skipping from diagram: {}", name);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -132,6 +132,7 @@ using clanguml::common::model::scope_t;
|
|||||||
using clanguml::config::class_diagram;
|
using clanguml::config::class_diagram;
|
||||||
using clanguml::config::config;
|
using clanguml::config::config;
|
||||||
using clanguml::config::filter;
|
using clanguml::config::filter;
|
||||||
|
using clanguml::config::package_diagram;
|
||||||
using clanguml::config::plantuml;
|
using clanguml::config::plantuml;
|
||||||
using clanguml::config::sequence_diagram;
|
using clanguml::config::sequence_diagram;
|
||||||
using clanguml::config::source_location;
|
using clanguml::config::source_location;
|
||||||
@@ -282,6 +283,19 @@ template <> struct convert<sequence_diagram> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// class_diagram Yaml decoder
|
||||||
|
//
|
||||||
|
template <> struct convert<package_diagram> {
|
||||||
|
static bool decode(const Node &node, package_diagram &rhs)
|
||||||
|
{
|
||||||
|
if (!decode_diagram(node, rhs))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
//
|
//
|
||||||
// config Yaml decoder
|
// config Yaml decoder
|
||||||
//
|
//
|
||||||
@@ -313,8 +327,12 @@ template <> struct convert<config> {
|
|||||||
rhs.diagrams[name] = std::make_shared<sequence_diagram>(
|
rhs.diagrams[name] = std::make_shared<sequence_diagram>(
|
||||||
d.second.as<sequence_diagram>());
|
d.second.as<sequence_diagram>());
|
||||||
}
|
}
|
||||||
|
else if (diagram_type == "package") {
|
||||||
|
rhs.diagrams[name] = std::make_shared<package_diagram>(
|
||||||
|
d.second.as<package_diagram>());
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
spdlog::warn(
|
LOG_WARN(
|
||||||
"Diagrams of type {} are not supported at the moment... ",
|
"Diagrams of type {} are not supported at the moment... ",
|
||||||
diagram_type);
|
diagram_type);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -101,6 +101,10 @@ struct sequence_diagram : public diagram {
|
|||||||
std::vector<source_location> start_from;
|
std::vector<source_location> start_from;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct package_diagram : public diagram {
|
||||||
|
virtual ~package_diagram() = default;
|
||||||
|
};
|
||||||
|
|
||||||
struct config {
|
struct config {
|
||||||
// the glob list is additive and relative to the current
|
// the glob list is additive and relative to the current
|
||||||
// directory
|
// directory
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
#include "compilation_database.h"
|
#include "compilation_database.h"
|
||||||
|
#include "util/util.h"
|
||||||
|
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <spdlog/fmt/fmt.h>
|
#include <spdlog/fmt/fmt.h>
|
||||||
@@ -73,7 +74,7 @@ CXTranslationUnit compilation_database::parse_translation_unit(
|
|||||||
clang_CompileCommands_getCommand(compile_commands, 0);
|
clang_CompileCommands_getCommand(compile_commands, 0);
|
||||||
|
|
||||||
auto cc_filename = clang_CompileCommand_getFilename(compile_command);
|
auto cc_filename = clang_CompileCommand_getFilename(compile_command);
|
||||||
spdlog::debug(
|
LOG_DBG(
|
||||||
"Processing compile command file: {}", clang_getCString(cc_filename));
|
"Processing compile command file: {}", clang_getCString(cc_filename));
|
||||||
|
|
||||||
auto num_args = clang_CompileCommand_getNumArgs(compile_command);
|
auto num_args = clang_CompileCommand_getNumArgs(compile_command);
|
||||||
@@ -84,7 +85,7 @@ CXTranslationUnit compilation_database::parse_translation_unit(
|
|||||||
arguments = (char **)malloc(sizeof(char *) * num_args);
|
arguments = (char **)malloc(sizeof(char *) * num_args);
|
||||||
for (j = 0; j < num_args; ++j) {
|
for (j = 0; j < num_args; ++j) {
|
||||||
CXString arg = clang_CompileCommand_getArg(compile_command, j);
|
CXString arg = clang_CompileCommand_getArg(compile_command, j);
|
||||||
spdlog::debug("Processing argument: {}", clang_getCString(arg));
|
LOG_DBG("Processing argument: {}", clang_getCString(arg));
|
||||||
arguments[j] = strdup(clang_getCString(arg));
|
arguments[j] = strdup(clang_getCString(arg));
|
||||||
clang_disposeString(arg);
|
clang_disposeString(arg);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,7 +56,6 @@ std::string ns(const cppast::cpp_entity &e);
|
|||||||
std::string ns(const cppast::cpp_type &t, const cppast::cpp_entity_index &idx);
|
std::string ns(const cppast::cpp_type &t, const cppast::cpp_entity_index &idx);
|
||||||
|
|
||||||
bool is_inside_class(const cppast::cpp_entity &e);
|
bool is_inside_class(const cppast::cpp_entity &e);
|
||||||
|
|
||||||
} // namespace util
|
} // namespace util
|
||||||
} // namespace cx
|
} // namespace cx
|
||||||
} // namespace clanguml
|
} // namespace clanguml
|
||||||
|
|||||||
12
src/main.cc
12
src/main.cc
@@ -21,7 +21,9 @@
|
|||||||
#include "class_diagram/generators/plantuml/class_diagram_generator.h"
|
#include "class_diagram/generators/plantuml/class_diagram_generator.h"
|
||||||
#include "config/config.h"
|
#include "config/config.h"
|
||||||
#include "cx/compilation_database.h"
|
#include "cx/compilation_database.h"
|
||||||
|
#include "package_diagram/generators/plantuml/package_diagram_generator.h"
|
||||||
#include "sequence_diagram/generators/plantuml/sequence_diagram_generator.h"
|
#include "sequence_diagram/generators/plantuml/sequence_diagram_generator.h"
|
||||||
|
|
||||||
#include "util/util.h"
|
#include "util/util.h"
|
||||||
|
|
||||||
#include <cli11/CLI11.hpp>
|
#include <cli11/CLI11.hpp>
|
||||||
@@ -79,6 +81,7 @@ int main(int argc, const char *argv[])
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
using clanguml::config::class_diagram;
|
using clanguml::config::class_diagram;
|
||||||
|
using clanguml::config::package_diagram;
|
||||||
using clanguml::config::sequence_diagram;
|
using clanguml::config::sequence_diagram;
|
||||||
|
|
||||||
std::filesystem::path path{"puml/" + name + ".puml"};
|
std::filesystem::path path{"puml/" + name + ".puml"};
|
||||||
@@ -103,6 +106,15 @@ int main(int argc, const char *argv[])
|
|||||||
dynamic_cast<clanguml::config::sequence_diagram &>(*diagram),
|
dynamic_cast<clanguml::config::sequence_diagram &>(*diagram),
|
||||||
model);
|
model);
|
||||||
}
|
}
|
||||||
|
else if (std::dynamic_pointer_cast<package_diagram>(diagram)) {
|
||||||
|
auto model =
|
||||||
|
clanguml::package_diagram::generators::plantuml::generate(
|
||||||
|
db, name, dynamic_cast<package_diagram &>(*diagram));
|
||||||
|
|
||||||
|
ofs << clanguml::package_diagram::generators::plantuml::generator(
|
||||||
|
dynamic_cast<clanguml::config::package_diagram &>(*diagram),
|
||||||
|
model);
|
||||||
|
}
|
||||||
ofs.close();
|
ofs.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,199 @@
|
|||||||
|
/**
|
||||||
|
* src/class_diagram/generators/plantuml/class_diagram_generator.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 "package_diagram_generator.h"
|
||||||
|
|
||||||
|
#include "util/error.h"
|
||||||
|
|
||||||
|
namespace clanguml::package_diagram::generators::plantuml {
|
||||||
|
|
||||||
|
std::string relative_to(std::string n, std::string c)
|
||||||
|
{
|
||||||
|
if (c.rfind(n) == std::string::npos)
|
||||||
|
return c;
|
||||||
|
|
||||||
|
return c.substr(n.size() + 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
generator::generator(
|
||||||
|
clanguml::config::package_diagram &config, diagram_model &model)
|
||||||
|
: m_config(config)
|
||||||
|
, m_model(model)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string generator::to_string(relationship_t r, std::string style) const
|
||||||
|
{
|
||||||
|
switch (r) {
|
||||||
|
case relationship_t::kOwnership:
|
||||||
|
case relationship_t::kComposition:
|
||||||
|
return style.empty() ? "*--" : fmt::format("*-[{}]-", style);
|
||||||
|
case relationship_t::kAggregation:
|
||||||
|
return style.empty() ? "o--" : fmt::format("o-[{}]-", style);
|
||||||
|
case relationship_t::kContainment:
|
||||||
|
return style.empty() ? "--+" : fmt::format("-[{}]-+", style);
|
||||||
|
case relationship_t::kAssociation:
|
||||||
|
return style.empty() ? "-->" : fmt::format("-[{}]->", style);
|
||||||
|
case relationship_t::kInstantiation:
|
||||||
|
return style.empty() ? "..|>" : fmt::format(".[{}].|>", style);
|
||||||
|
case relationship_t::kFriendship:
|
||||||
|
return style.empty() ? "<.." : fmt::format("<.[{}].", style);
|
||||||
|
case relationship_t::kDependency:
|
||||||
|
return style.empty() ? "..>" : fmt::format(".[{}].>", style);
|
||||||
|
default:
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string generator::name(relationship_t r) const
|
||||||
|
{
|
||||||
|
switch (r) {
|
||||||
|
case relationship_t::kOwnership:
|
||||||
|
case relationship_t::kComposition:
|
||||||
|
return "composition";
|
||||||
|
case relationship_t::kAggregation:
|
||||||
|
return "aggregation";
|
||||||
|
case relationship_t::kContainment:
|
||||||
|
return "containment";
|
||||||
|
case relationship_t::kAssociation:
|
||||||
|
return "association";
|
||||||
|
case relationship_t::kDependency:
|
||||||
|
return "dependency";
|
||||||
|
default:
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void generator::generate(const package &p, std::ostream &ostr) const
|
||||||
|
{
|
||||||
|
const auto uns = m_config.using_namespace;
|
||||||
|
|
||||||
|
ostr << "component [" << p.name() << "] as " << p.alias();
|
||||||
|
|
||||||
|
if (!p.style().empty())
|
||||||
|
ostr << " " << p.style();
|
||||||
|
|
||||||
|
ostr << " {" << '\n';
|
||||||
|
|
||||||
|
// C++17 cannot figure out to use cbegin/cend in a for-range loop on const
|
||||||
|
// collection... ¯\_(ツ)_/¯
|
||||||
|
for (auto subpackage = p.cbegin(); subpackage != p.cend(); subpackage++) {
|
||||||
|
generate(**subpackage, ostr);
|
||||||
|
}
|
||||||
|
|
||||||
|
ostr << "}" << '\n';
|
||||||
|
|
||||||
|
//
|
||||||
|
// Process notes
|
||||||
|
//
|
||||||
|
for (auto decorator : p.decorators()) {
|
||||||
|
auto note = std::dynamic_pointer_cast<decorators::note>(decorator);
|
||||||
|
if (note && note->applies_to_diagram(m_config.name)) {
|
||||||
|
ostr << "note " << note->position << " of " << p.alias() << '\n'
|
||||||
|
<< note->text << '\n'
|
||||||
|
<< "end note\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//
|
||||||
|
// // Print relationships
|
||||||
|
// ostr << all_relations_str.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
void generator::generate(std::ostream &ostr) const
|
||||||
|
{
|
||||||
|
ostr << "@startuml" << '\n';
|
||||||
|
|
||||||
|
// Process aliases in any of the puml directives
|
||||||
|
for (const auto &b : m_config.puml.before) {
|
||||||
|
std::string note{b};
|
||||||
|
std::tuple<std::string, size_t, size_t> alias_match;
|
||||||
|
while (util::find_element_alias(note, alias_match)) {
|
||||||
|
auto alias = m_model.to_alias(ns_relative(
|
||||||
|
m_config.using_namespace, std::get<0>(alias_match)));
|
||||||
|
note.replace(
|
||||||
|
std::get<1>(alias_match), std::get<2>(alias_match), alias);
|
||||||
|
}
|
||||||
|
ostr << note << '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_config.should_include_entities("packages")) {
|
||||||
|
for (const auto &p : m_model) {
|
||||||
|
generate(*p, ostr);
|
||||||
|
ostr << '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process aliases in any of the puml directives
|
||||||
|
for (const auto &b : m_config.puml.after) {
|
||||||
|
std::string note{b};
|
||||||
|
std::tuple<std::string, size_t, size_t> alias_match;
|
||||||
|
while (util::find_element_alias(note, alias_match)) {
|
||||||
|
auto full_name =
|
||||||
|
fmt::format("{}::{}", fmt::join(m_config.using_namespace, "::"),
|
||||||
|
ns_relative(
|
||||||
|
m_config.using_namespace, std::get<0>(alias_match)));
|
||||||
|
|
||||||
|
auto alias = m_model.to_alias(full_name);
|
||||||
|
note.replace(
|
||||||
|
std::get<1>(alias_match), std::get<2>(alias_match), alias);
|
||||||
|
}
|
||||||
|
ostr << note << '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
ostr << "@enduml" << '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream &operator<<(std::ostream &os, const generator &g)
|
||||||
|
{
|
||||||
|
g.generate(os);
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
|
clanguml::package_diagram::model::diagram generate(
|
||||||
|
cppast::libclang_compilation_database &db, const std::string &name,
|
||||||
|
clanguml::config::package_diagram &diagram)
|
||||||
|
{
|
||||||
|
LOG_INFO("Generating package diagram {}.puml", name);
|
||||||
|
clanguml::package_diagram::model::diagram d;
|
||||||
|
d.set_name(name);
|
||||||
|
|
||||||
|
// Get all translation units matching the glob from diagram
|
||||||
|
// configuration
|
||||||
|
std::vector<std::string> translation_units{};
|
||||||
|
for (const auto &g : diagram.glob) {
|
||||||
|
LOG_DBG("Processing glob: {}", g);
|
||||||
|
const auto matches = glob::rglob(g);
|
||||||
|
std::copy(matches.begin(), matches.end(),
|
||||||
|
std::back_inserter(translation_units));
|
||||||
|
}
|
||||||
|
|
||||||
|
cppast::cpp_entity_index idx;
|
||||||
|
cppast::simple_file_parser<cppast::libclang_parser> parser{
|
||||||
|
type_safe::ref(idx)};
|
||||||
|
|
||||||
|
// Process all matching translation units
|
||||||
|
clanguml::package_diagram::visitor::translation_unit_visitor ctx(
|
||||||
|
idx, d, diagram);
|
||||||
|
cppast::parse_files(parser, translation_units, db);
|
||||||
|
for (auto &file : parser.files())
|
||||||
|
ctx(file);
|
||||||
|
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
/**
|
||||||
|
* src/package_diagram/generators/plantuml/package_diagram_generator.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 "package_diagram/model/package.h"
|
||||||
|
#include "package_diagram/visitor/translation_unit_visitor.h"
|
||||||
|
#include "common/model/relationship.h"
|
||||||
|
#include "config/config.h"
|
||||||
|
#include "cx/compilation_database.h"
|
||||||
|
#include "package_diagram/model/diagram.h"
|
||||||
|
#include "util/util.h"
|
||||||
|
|
||||||
|
#include <cppast/cpp_entity_index.hpp>
|
||||||
|
#include <cppast/libclang_parser.hpp>
|
||||||
|
#include <glob/glob.hpp>
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
namespace clanguml {
|
||||||
|
namespace package_diagram {
|
||||||
|
namespace generators {
|
||||||
|
namespace plantuml {
|
||||||
|
|
||||||
|
using diagram_config = clanguml::package_diagram::model::diagram;
|
||||||
|
using diagram_model = clanguml::package_diagram::model::diagram;
|
||||||
|
using clanguml::common::model::relationship_t;
|
||||||
|
using clanguml::common::model::scope_t;
|
||||||
|
using clanguml::package_diagram::model::package;
|
||||||
|
using namespace clanguml::util;
|
||||||
|
|
||||||
|
std::string relative_to(std::string n, std::string c);
|
||||||
|
|
||||||
|
class generator {
|
||||||
|
public:
|
||||||
|
generator(clanguml::config::package_diagram &config, diagram_model &model);
|
||||||
|
|
||||||
|
std::string to_string(relationship_t r, std::string style = "") const;
|
||||||
|
|
||||||
|
std::string name(relationship_t r) const;
|
||||||
|
|
||||||
|
void generate_alias(const package &c, std::ostream &ostr) const;
|
||||||
|
|
||||||
|
void generate(const package &e, std::ostream &ostr) const;
|
||||||
|
|
||||||
|
void generate(std::ostream &ostr) const;
|
||||||
|
|
||||||
|
friend std::ostream &operator<<(std::ostream &os, const generator &g);
|
||||||
|
|
||||||
|
private:
|
||||||
|
clanguml::config::package_diagram &m_config;
|
||||||
|
diagram_model &m_model;
|
||||||
|
};
|
||||||
|
|
||||||
|
clanguml::package_diagram::model::diagram generate(
|
||||||
|
cppast::libclang_compilation_database &db, const std::string &name,
|
||||||
|
clanguml::config::package_diagram &diagram);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
48
src/package_diagram/model/diagram.cc
Normal file
48
src/package_diagram/model/diagram.cc
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
/**
|
||||||
|
* src/package_diagram/model/diagram.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.h"
|
||||||
|
|
||||||
|
#include "util/error.h"
|
||||||
|
#include "util/util.h"
|
||||||
|
|
||||||
|
namespace clanguml::package_diagram::model {
|
||||||
|
|
||||||
|
std::string diagram::name() const { return name_; }
|
||||||
|
|
||||||
|
void diagram::set_name(const std::string &name) { name_ = name; }
|
||||||
|
|
||||||
|
std::string diagram::to_alias(const std::string &full_name) const
|
||||||
|
{
|
||||||
|
LOG_DBG("Looking for alias for {}", full_name);
|
||||||
|
|
||||||
|
auto fn = util::split(full_name, "::");
|
||||||
|
|
||||||
|
if (fn.empty())
|
||||||
|
throw error::uml_alias_missing(
|
||||||
|
fmt::format("Missing alias for '{}'", full_name));
|
||||||
|
|
||||||
|
auto package = get_package(fn);
|
||||||
|
|
||||||
|
if (!package)
|
||||||
|
throw error::uml_alias_missing(
|
||||||
|
fmt::format("Missing alias for '{}'", full_name));
|
||||||
|
|
||||||
|
return package.value().alias();
|
||||||
|
}
|
||||||
|
}
|
||||||
42
src/package_diagram/model/diagram.h
Normal file
42
src/package_diagram/model/diagram.h
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
/**
|
||||||
|
* src/package_diagram/model/diagram.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 "package.h"
|
||||||
|
|
||||||
|
#include <type_safe/optional_ref.hpp>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace clanguml::package_diagram::model {
|
||||||
|
|
||||||
|
class diagram : public detail::package_trait<package, std::vector> {
|
||||||
|
public:
|
||||||
|
std::string name() const;
|
||||||
|
|
||||||
|
void set_name(const std::string &name);
|
||||||
|
|
||||||
|
std::string to_alias(const std::string &full_name) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string name_;
|
||||||
|
|
||||||
|
std::vector<std::unique_ptr<package>> packages_;
|
||||||
|
};
|
||||||
|
}
|
||||||
50
src/package_diagram/model/package.cc
Normal file
50
src/package_diagram/model/package.cc
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
/**
|
||||||
|
* src/package_diagram/model/class.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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "package.h"
|
||||||
|
|
||||||
|
#include "util/util.h"
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
namespace clanguml::package_diagram::model {
|
||||||
|
package::package(const std::vector<std::string> &using_namespaces)
|
||||||
|
: element{using_namespaces}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string package::full_name(bool relative) const
|
||||||
|
{
|
||||||
|
auto fn = get_namespace();
|
||||||
|
auto ns = using_namespaces();
|
||||||
|
|
||||||
|
if (relative && (fn.size() >= ns.size())) {
|
||||||
|
if(starts_with(fn, ns))
|
||||||
|
fn = std::vector<std::string>(fn.begin() + ns.size(), fn.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn.push_back(name());
|
||||||
|
|
||||||
|
return fmt::format("{}", fmt::join(fn, "::"));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const package &l, const package &r)
|
||||||
|
{
|
||||||
|
return l.full_name(false) == r.full_name(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
121
src/package_diagram/model/package.h
Normal file
121
src/package_diagram/model/package.h
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
/**
|
||||||
|
* src/package_diagram/model/class.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 "common/model/element.h"
|
||||||
|
#include "common/model/stylable_element.h"
|
||||||
|
|
||||||
|
#include <spdlog/spdlog.h>
|
||||||
|
#include <type_safe/optional_ref.hpp>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace clanguml::package_diagram::model {
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
template <typename T, template <typename> class Container,
|
||||||
|
typename Ptr = std::unique_ptr<T>>
|
||||||
|
class package_trait {
|
||||||
|
public:
|
||||||
|
void add_package(std::unique_ptr<T> p)
|
||||||
|
{
|
||||||
|
packages_.emplace_back(std::move(p));
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_package(std::vector<std::string> path, std::unique_ptr<T> p)
|
||||||
|
{
|
||||||
|
assert(p);
|
||||||
|
|
||||||
|
LOG_DBG(
|
||||||
|
"Adding package {} at path '{}'", p->name(), fmt::join(path, "::"));
|
||||||
|
|
||||||
|
if (path.empty()) {
|
||||||
|
add_package(std::move(p));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto parent = get_package(path);
|
||||||
|
|
||||||
|
if (parent)
|
||||||
|
parent.value().add_package(std::move(p));
|
||||||
|
else
|
||||||
|
spdlog::error(
|
||||||
|
"No parent package found at: {}", fmt::join(path, "::"));
|
||||||
|
}
|
||||||
|
|
||||||
|
type_safe::optional_ref<T> get_package(std::vector<std::string> path) const
|
||||||
|
{
|
||||||
|
LOG_DBG("Getting package at path: {}", fmt::join(path, "::"));
|
||||||
|
|
||||||
|
if (path.empty() || !has_package(path.at(0)))
|
||||||
|
return {};
|
||||||
|
|
||||||
|
auto p = get_package(path.at(0));
|
||||||
|
if (path.size() == 1)
|
||||||
|
return p;
|
||||||
|
|
||||||
|
return p.value().get_package(
|
||||||
|
std::vector<std::string>(path.begin() + 1, path.end()));
|
||||||
|
}
|
||||||
|
|
||||||
|
type_safe::optional_ref<T> get_package(const std::string &name) const
|
||||||
|
{
|
||||||
|
auto it = std::find_if(packages_.cbegin(), packages_.cend(),
|
||||||
|
[&](const auto &p) { return name == p->name(); });
|
||||||
|
|
||||||
|
if (it == packages_.end())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
assert(it->get() != nullptr);
|
||||||
|
|
||||||
|
return type_safe::ref(*(it->get()));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool has_package(const std::string &name) const
|
||||||
|
{
|
||||||
|
return std::find_if(packages_.cbegin(), packages_.cend(),
|
||||||
|
[&](const auto &p) { return name == p->name(); }) !=
|
||||||
|
packages_.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef typename Container<Ptr>::iterator iterator;
|
||||||
|
typedef typename Container<Ptr>::const_iterator const_iterator;
|
||||||
|
|
||||||
|
inline iterator begin() noexcept { return packages_.begin(); }
|
||||||
|
inline const_iterator cbegin() const noexcept { return packages_.cbegin(); }
|
||||||
|
inline iterator end() noexcept { return packages_.end(); }
|
||||||
|
inline const_iterator cend() const noexcept { return packages_.cend(); }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Container<std::unique_ptr<T>> packages_;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
class package : public common::model::element,
|
||||||
|
public common::model::stylable_element,
|
||||||
|
public detail::package_trait<package, std::vector> {
|
||||||
|
public:
|
||||||
|
package(const std::vector<std::string> &using_namespaces);
|
||||||
|
|
||||||
|
std::string full_name(bool relative) const override;
|
||||||
|
|
||||||
|
friend bool operator==(const package &l, const package &r);
|
||||||
|
};
|
||||||
|
}
|
||||||
43
src/package_diagram/visitor/element_visitor_context.cc
Normal file
43
src/package_diagram/visitor/element_visitor_context.cc
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
/**
|
||||||
|
* src/package_diagram/model/visitor/element_visitor_context.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 "element_visitor_context.h"
|
||||||
|
|
||||||
|
#include "translation_unit_context.h"
|
||||||
|
|
||||||
|
namespace clanguml::package_diagram::visitor {
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
element_visitor_context<T>::element_visitor_context(
|
||||||
|
clanguml::package_diagram::model::diagram &diagram, T &element)
|
||||||
|
: element_{element}
|
||||||
|
, diagram_{diagram}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T> T &element_visitor_context<T>::element()
|
||||||
|
{
|
||||||
|
return element_;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
clanguml::package_diagram::model::diagram &element_visitor_context<T>::diagram()
|
||||||
|
{
|
||||||
|
return diagram_;
|
||||||
|
}
|
||||||
|
}
|
||||||
42
src/package_diagram/visitor/element_visitor_context.h
Normal file
42
src/package_diagram/visitor/element_visitor_context.h
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
/**
|
||||||
|
* src/package_diagram/model/visitor/element_visitor_context.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 "package_diagram/model/diagram.h"
|
||||||
|
|
||||||
|
namespace clanguml::package_diagram::visitor {
|
||||||
|
|
||||||
|
class translation_unit_context;
|
||||||
|
|
||||||
|
template <typename T> class element_visitor_context {
|
||||||
|
public:
|
||||||
|
element_visitor_context(
|
||||||
|
clanguml::package_diagram::model::diagram &diagram, T &element);
|
||||||
|
|
||||||
|
T &element();
|
||||||
|
|
||||||
|
clanguml::package_diagram::model::diagram &diagram();
|
||||||
|
|
||||||
|
private:
|
||||||
|
translation_unit_context *ctx_;
|
||||||
|
|
||||||
|
T &element_;
|
||||||
|
clanguml::package_diagram::model::diagram &diagram_;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
136
src/package_diagram/visitor/translation_unit_context.cc
Normal file
136
src/package_diagram/visitor/translation_unit_context.cc
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
/**
|
||||||
|
* src/package_diagram/visitor/translation_unit_context.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 "translation_unit_context.h"
|
||||||
|
|
||||||
|
#include "cx/util.h"
|
||||||
|
|
||||||
|
namespace clanguml::package_diagram::visitor {
|
||||||
|
|
||||||
|
translation_unit_context::translation_unit_context(
|
||||||
|
cppast::cpp_entity_index &idx,
|
||||||
|
clanguml::package_diagram::model::diagram &diagram,
|
||||||
|
const clanguml::config::package_diagram &config)
|
||||||
|
: entity_index_{idx}
|
||||||
|
, diagram_{diagram}
|
||||||
|
, config_{config}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool translation_unit_context::has_type_alias(
|
||||||
|
const std::string &full_name) const
|
||||||
|
{
|
||||||
|
bool res = alias_index_.find(full_name) != alias_index_.end();
|
||||||
|
|
||||||
|
LOG_DBG("Alias {} {} found in index", full_name, res ? "" : "not");
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void translation_unit_context::add_type_alias(const std::string &full_name,
|
||||||
|
type_safe::object_ref<const cppast::cpp_type> &&ref)
|
||||||
|
{
|
||||||
|
if (!has_type_alias(full_name)) {
|
||||||
|
LOG_DBG("Stored type alias: {} -> {} ", full_name,
|
||||||
|
cppast::to_string(ref.get()));
|
||||||
|
|
||||||
|
alias_index_.emplace(full_name, std::move(ref));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type_safe::object_ref<const cppast::cpp_type>
|
||||||
|
translation_unit_context::get_type_alias(const std::string &full_name) const
|
||||||
|
{
|
||||||
|
assert(has_type_alias(full_name));
|
||||||
|
|
||||||
|
return alias_index_.at(full_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
type_safe::object_ref<const cppast::cpp_type>
|
||||||
|
translation_unit_context::get_type_alias_final(const cppast::cpp_type &t) const
|
||||||
|
{
|
||||||
|
const auto type_full_name =
|
||||||
|
cx::util::full_name(cppast::remove_cv(t), entity_index_, false);
|
||||||
|
|
||||||
|
if (has_type_alias(type_full_name)) {
|
||||||
|
return get_type_alias_final(alias_index_.at(type_full_name).get());
|
||||||
|
}
|
||||||
|
|
||||||
|
return type_safe::ref(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool translation_unit_context::has_type_alias_template(
|
||||||
|
const std::string &full_name) const
|
||||||
|
{
|
||||||
|
bool res =
|
||||||
|
alias_template_index_.find(full_name) != alias_template_index_.end();
|
||||||
|
|
||||||
|
LOG_DBG("Alias template {} {} found in index", full_name, res ? "" : "not");
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void translation_unit_context::add_type_alias_template(
|
||||||
|
const std::string &full_name,
|
||||||
|
type_safe::object_ref<const cppast::cpp_type> &&ref)
|
||||||
|
{
|
||||||
|
if (!has_type_alias_template(full_name)) {
|
||||||
|
LOG_DBG("Stored type alias template for: {} ", full_name);
|
||||||
|
|
||||||
|
alias_template_index_.emplace(full_name, std::move(ref));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type_safe::object_ref<const cppast::cpp_type>
|
||||||
|
translation_unit_context::get_type_alias_template(
|
||||||
|
const std::string &full_name) const
|
||||||
|
{
|
||||||
|
assert(has_type_alias_template(full_name));
|
||||||
|
|
||||||
|
return alias_template_index_.at(full_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void translation_unit_context::push_namespace(const std::string &ns)
|
||||||
|
{
|
||||||
|
namespace_.push_back(ns);
|
||||||
|
}
|
||||||
|
|
||||||
|
void translation_unit_context::pop_namespace() { namespace_.pop_back(); }
|
||||||
|
|
||||||
|
const std::vector<std::string> &translation_unit_context::get_namespace() const
|
||||||
|
{
|
||||||
|
return namespace_;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cppast::cpp_entity_index &translation_unit_context::entity_index() const
|
||||||
|
{
|
||||||
|
return entity_index_;
|
||||||
|
}
|
||||||
|
|
||||||
|
const clanguml::config::package_diagram &
|
||||||
|
translation_unit_context::config() const
|
||||||
|
{
|
||||||
|
return config_;
|
||||||
|
}
|
||||||
|
|
||||||
|
clanguml::package_diagram::model::diagram &translation_unit_context::diagram()
|
||||||
|
{
|
||||||
|
return diagram_;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
88
src/package_diagram/visitor/translation_unit_context.h
Normal file
88
src/package_diagram/visitor/translation_unit_context.h
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
/**
|
||||||
|
* src/package_diagram/visitor/translation_unit_context.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 "config/config.h"
|
||||||
|
#include "package_diagram/model/diagram.h"
|
||||||
|
|
||||||
|
#include <cppast/cpp_entity_index.hpp>
|
||||||
|
#include <cppast/cpp_type.hpp>
|
||||||
|
#include <type_safe/reference.hpp>
|
||||||
|
|
||||||
|
namespace clanguml::package_diagram::visitor {
|
||||||
|
|
||||||
|
class translation_unit_context {
|
||||||
|
public:
|
||||||
|
translation_unit_context(cppast::cpp_entity_index &idx,
|
||||||
|
clanguml::package_diagram::model::diagram &diagram,
|
||||||
|
const clanguml::config::package_diagram &config);
|
||||||
|
|
||||||
|
bool has_type_alias(const std::string &full_name) const;
|
||||||
|
|
||||||
|
void add_type_alias(const std::string &full_name,
|
||||||
|
type_safe::object_ref<const cppast::cpp_type> &&ref);
|
||||||
|
|
||||||
|
type_safe::object_ref<const cppast::cpp_type> get_type_alias(
|
||||||
|
const std::string &full_name) const;
|
||||||
|
|
||||||
|
type_safe::object_ref<const cppast::cpp_type> get_type_alias_final(
|
||||||
|
const cppast::cpp_type &t) const;
|
||||||
|
|
||||||
|
bool has_type_alias_template(const std::string &full_name) const;
|
||||||
|
|
||||||
|
void add_type_alias_template(const std::string &full_name,
|
||||||
|
type_safe::object_ref<const cppast::cpp_type> &&ref);
|
||||||
|
|
||||||
|
type_safe::object_ref<const cppast::cpp_type> get_type_alias_template(
|
||||||
|
const std::string &full_name) const;
|
||||||
|
|
||||||
|
void push_namespace(const std::string &ns);
|
||||||
|
|
||||||
|
void pop_namespace();
|
||||||
|
|
||||||
|
const std::vector<std::string> &get_namespace() const;
|
||||||
|
|
||||||
|
const cppast::cpp_entity_index &entity_index() const;
|
||||||
|
|
||||||
|
const clanguml::config::package_diagram &config() const;
|
||||||
|
|
||||||
|
clanguml::package_diagram::model::diagram &diagram();
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Current visitor namespace
|
||||||
|
std::vector<std::string> namespace_;
|
||||||
|
|
||||||
|
// Reference to the cppast entity index
|
||||||
|
cppast::cpp_entity_index &entity_index_;
|
||||||
|
|
||||||
|
// Reference to the output diagram model
|
||||||
|
clanguml::package_diagram::model::diagram &diagram_;
|
||||||
|
|
||||||
|
// Reference to class diagram config
|
||||||
|
const clanguml::config::package_diagram &config_;
|
||||||
|
|
||||||
|
// Map of discovered aliases (declared with 'using' keyword)
|
||||||
|
std::map<std::string, type_safe::object_ref<const cppast::cpp_type>>
|
||||||
|
alias_index_;
|
||||||
|
|
||||||
|
// Map of discovered template aliases (declared with 'using' keyword)
|
||||||
|
std::map<std::string, type_safe::object_ref<const cppast::cpp_type>>
|
||||||
|
alias_template_index_;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
352
src/package_diagram/visitor/translation_unit_visitor.cc
Normal file
352
src/package_diagram/visitor/translation_unit_visitor.cc
Normal file
@@ -0,0 +1,352 @@
|
|||||||
|
/**
|
||||||
|
* src/package_diagram/visitor/translation_unit_visitor.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 "translation_unit_visitor.h"
|
||||||
|
|
||||||
|
#include <cppast/cpp_alias_template.hpp>
|
||||||
|
#include <cppast/cpp_array_type.hpp>
|
||||||
|
#include <cppast/cpp_class_template.hpp>
|
||||||
|
#include <cppast/cpp_entity_kind.hpp>
|
||||||
|
#include <cppast/cpp_enum.hpp>
|
||||||
|
#include <cppast/cpp_friend.hpp>
|
||||||
|
#include <cppast/cpp_member_function.hpp>
|
||||||
|
#include <cppast/cpp_member_variable.hpp>
|
||||||
|
#include <cppast/cpp_namespace.hpp>
|
||||||
|
#include <cppast/cpp_template.hpp>
|
||||||
|
#include <cppast/cpp_type_alias.hpp>
|
||||||
|
#include <cppast/cpp_variable.hpp>
|
||||||
|
#include <spdlog/spdlog.h>
|
||||||
|
|
||||||
|
#include <deque>
|
||||||
|
|
||||||
|
namespace clanguml::package_diagram::visitor {
|
||||||
|
|
||||||
|
using clanguml::class_diagram::model::type_alias;
|
||||||
|
using clanguml::common::model::access_t;
|
||||||
|
using clanguml::common::model::relationship;
|
||||||
|
using clanguml::common::model::relationship_t;
|
||||||
|
using clanguml::common::model::scope_t;
|
||||||
|
using clanguml::package_diagram::model::diagram;
|
||||||
|
using clanguml::package_diagram::model::package;
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
scope_t cpp_access_specifier_to_scope(
|
||||||
|
cppast::cpp_access_specifier_kind access_specifier)
|
||||||
|
{
|
||||||
|
scope_t scope = scope_t::kPublic;
|
||||||
|
switch (access_specifier) {
|
||||||
|
case cppast::cpp_access_specifier_kind::cpp_public:
|
||||||
|
scope = scope_t::kPublic;
|
||||||
|
break;
|
||||||
|
case cppast::cpp_access_specifier_kind::cpp_private:
|
||||||
|
scope = scope_t::kPrivate;
|
||||||
|
break;
|
||||||
|
case cppast::cpp_access_specifier_kind::cpp_protected:
|
||||||
|
scope = scope_t::kProtected;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return scope;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
translation_unit_visitor::translation_unit_visitor(
|
||||||
|
cppast::cpp_entity_index &idx,
|
||||||
|
clanguml::package_diagram::model::diagram &diagram,
|
||||||
|
const clanguml::config::package_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::namespace_t) {
|
||||||
|
if (info.event ==
|
||||||
|
cppast::visitor_info::container_entity_enter) {
|
||||||
|
LOG_DBG("========== Visiting '{}' - {}", e.name(),
|
||||||
|
cppast::to_string(e.kind()));
|
||||||
|
|
||||||
|
const auto &ns_declaration =
|
||||||
|
static_cast<const cppast::cpp_namespace &>(e);
|
||||||
|
if (!ns_declaration.is_anonymous() &&
|
||||||
|
!ns_declaration.is_inline()) {
|
||||||
|
|
||||||
|
auto p = std::make_unique<package>(
|
||||||
|
ctx.config().using_namespace);
|
||||||
|
p->set_name(e.name());
|
||||||
|
p->set_namespace(ctx.get_namespace());
|
||||||
|
ctx.diagram().add_package(
|
||||||
|
ctx.get_namespace(), std::move(p));
|
||||||
|
|
||||||
|
ctx.push_namespace(e.name());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
LOG_DBG("========== Leaving '{}' - {}", e.name(),
|
||||||
|
cppast::to_string(e.kind()));
|
||||||
|
|
||||||
|
const auto &ns_declaration =
|
||||||
|
static_cast<const cppast::cpp_namespace &>(e);
|
||||||
|
if (!ns_declaration.is_anonymous() &&
|
||||||
|
!ns_declaration.is_inline())
|
||||||
|
ctx.pop_namespace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (e.kind() ==
|
||||||
|
cppast::cpp_entity_kind::class_template_specialization_t) {
|
||||||
|
LOG_DBG("========== Visiting '{}' - {}",
|
||||||
|
cx::util::full_name(ctx.get_namespace(), e),
|
||||||
|
cppast::to_string(e.kind()));
|
||||||
|
|
||||||
|
auto &tspec = static_cast<
|
||||||
|
const cppast::cpp_class_template_specialization &>(e);
|
||||||
|
|
||||||
|
process_class_declaration(
|
||||||
|
tspec.class_(), type_safe::ref(tspec));
|
||||||
|
}
|
||||||
|
else if (e.kind() == cppast::cpp_entity_kind::class_t) {
|
||||||
|
LOG_DBG("========== Visiting '{}' - {}",
|
||||||
|
cx::util::full_name(ctx.get_namespace(), e),
|
||||||
|
cppast::to_string(e.kind()));
|
||||||
|
|
||||||
|
auto &cls = static_cast<const cppast::cpp_class &>(e);
|
||||||
|
if (cppast::get_definition(ctx.entity_index(), cls)) {
|
||||||
|
auto &clsdef = static_cast<const cppast::cpp_class &>(
|
||||||
|
cppast::get_definition(ctx.entity_index(), cls)
|
||||||
|
.value());
|
||||||
|
if (&cls != &clsdef) {
|
||||||
|
LOG_DBG("Forward declaration of class {} - skipping...",
|
||||||
|
cls.name());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx.config().should_include(
|
||||||
|
cx::util::fully_prefixed(ctx.get_namespace(), cls)))
|
||||||
|
process_class_declaration(cls);
|
||||||
|
}
|
||||||
|
// else if (e.kind() == cppast::cpp_entity_kind::enum_t)
|
||||||
|
// {
|
||||||
|
// LOG_DBG("========== Visiting '{}' - {}",
|
||||||
|
// cx::util::full_name(ctx.get_namespace(), e),
|
||||||
|
// cppast::to_string(e.kind()));
|
||||||
|
//
|
||||||
|
// auto &enm = static_cast<const cppast::cpp_enum
|
||||||
|
// &>(e);
|
||||||
|
//
|
||||||
|
// if (ctx.config().should_include(
|
||||||
|
// cx::util::fully_prefixed(ctx.get_namespace(),
|
||||||
|
// enm)))
|
||||||
|
// process_enum_declaration(enm);
|
||||||
|
// }
|
||||||
|
else if (e.kind() == cppast::cpp_entity_kind::type_alias_t) {
|
||||||
|
LOG_DBG("========== Visiting '{}' - {}",
|
||||||
|
cx::util::full_name(ctx.get_namespace(), e),
|
||||||
|
cppast::to_string(e.kind()));
|
||||||
|
|
||||||
|
auto &ta = static_cast<const cppast::cpp_type_alias &>(e);
|
||||||
|
type_alias t;
|
||||||
|
t.set_alias(cx::util::full_name(ctx.get_namespace(), ta));
|
||||||
|
t.set_underlying_type(cx::util::full_name(ta.underlying_type(),
|
||||||
|
ctx.entity_index(), cx::util::is_inside_class(e)));
|
||||||
|
|
||||||
|
ctx.add_type_alias(cx::util::full_name(ctx.get_namespace(), ta),
|
||||||
|
type_safe::ref(ta.underlying_type()));
|
||||||
|
|
||||||
|
// ctx.diagram().add_type_alias(std::move(t));
|
||||||
|
}
|
||||||
|
else if (e.kind() == cppast::cpp_entity_kind::alias_template_t) {
|
||||||
|
LOG_DBG("========== Visiting '{}' - {}",
|
||||||
|
cx::util::full_name(ctx.get_namespace(), e),
|
||||||
|
cppast::to_string(e.kind()));
|
||||||
|
|
||||||
|
auto &at = static_cast<const cppast::cpp_alias_template &>(e);
|
||||||
|
|
||||||
|
// if (at.type_alias().underlying_type().kind()
|
||||||
|
// ==
|
||||||
|
// cppast::cpp_type_kind::unexposed_t) {
|
||||||
|
// LOG_WARN("Template alias has unexposed
|
||||||
|
// underlying type: {}",
|
||||||
|
// static_cast<const
|
||||||
|
// cppast::cpp_unexposed_type &>(
|
||||||
|
// at.type_alias().underlying_type())
|
||||||
|
// .name());
|
||||||
|
// }
|
||||||
|
// else {
|
||||||
|
// class_ tinst =
|
||||||
|
// build_template_instantiation(static_cast<
|
||||||
|
// const
|
||||||
|
// cppast::cpp_template_instantiation_type
|
||||||
|
// &>(
|
||||||
|
// at.type_alias().underlying_type()));
|
||||||
|
//
|
||||||
|
// ctx.diagram().add_class(std::move(tinst));
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void translation_unit_visitor::process_class_declaration(
|
||||||
|
const cppast::cpp_class &cls,
|
||||||
|
type_safe::optional_ref<const cppast::cpp_template_specialization> tspec)
|
||||||
|
{
|
||||||
|
|
||||||
|
return;
|
||||||
|
/*
|
||||||
|
class_ c{ctx.config().using_namespace};
|
||||||
|
c.is_struct(cls.class_kind() == cppast::cpp_class_kind::struct_t);
|
||||||
|
c.set_name(cx::util::full_name(ctx.get_namespace(), cls));
|
||||||
|
|
||||||
|
if (cls.comment().has_value())
|
||||||
|
c.add_decorators(decorators::parse(cls.comment().value()));
|
||||||
|
|
||||||
|
cppast::cpp_access_specifier_kind last_access_specifier =
|
||||||
|
cppast::cpp_access_specifier_kind::cpp_private;
|
||||||
|
|
||||||
|
// Process class documentation comment
|
||||||
|
if (cppast::is_templated(cls)) {
|
||||||
|
if (cls.parent().value().comment().has_value())
|
||||||
|
c.add_decorators(
|
||||||
|
decorators::parse(cls.parent().value().comment().value()));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (cls.comment().has_value())
|
||||||
|
c.add_decorators(decorators::parse(cls.comment().value()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c.skip())
|
||||||
|
return;
|
||||||
|
|
||||||
|
c.set_style(c.style_spec());
|
||||||
|
|
||||||
|
// Process class child entities
|
||||||
|
if (c.is_struct())
|
||||||
|
last_access_specifier = cppast::cpp_access_specifier_kind::cpp_public;
|
||||||
|
|
||||||
|
for (auto &child : cls) {
|
||||||
|
if (child.kind() == cppast::cpp_entity_kind::access_specifier_t) {
|
||||||
|
auto &as = static_cast<const cppast::cpp_access_specifier &>(child);
|
||||||
|
last_access_specifier = as.access_specifier();
|
||||||
|
}
|
||||||
|
else if (child.kind() == cppast::cpp_entity_kind::member_variable_t) {
|
||||||
|
auto &mv = static_cast<const cppast::cpp_member_variable &>(child);
|
||||||
|
process_field(mv, c, last_access_specifier);
|
||||||
|
}
|
||||||
|
else if (child.kind() == cppast::cpp_entity_kind::variable_t) {
|
||||||
|
auto &mv = static_cast<const cppast::cpp_variable &>(child);
|
||||||
|
process_static_field(mv, c, last_access_specifier);
|
||||||
|
}
|
||||||
|
else if (child.kind() == cppast::cpp_entity_kind::member_function_t) {
|
||||||
|
auto &mf = static_cast<const cppast::cpp_member_function &>(child);
|
||||||
|
process_method(mf, c, last_access_specifier);
|
||||||
|
}
|
||||||
|
else if (child.kind() == cppast::cpp_entity_kind::function_t) {
|
||||||
|
auto &mf = static_cast<const cppast::cpp_function &>(child);
|
||||||
|
process_static_method(mf, c, last_access_specifier);
|
||||||
|
}
|
||||||
|
else if (child.kind() == cppast::cpp_entity_kind::function_template_t) {
|
||||||
|
auto &tm =
|
||||||
|
static_cast<const cppast::cpp_function_template &>(child);
|
||||||
|
process_template_method(tm, c, last_access_specifier);
|
||||||
|
}
|
||||||
|
else if (child.kind() == cppast::cpp_entity_kind::constructor_t) {
|
||||||
|
auto &mc = static_cast<const cppast::cpp_constructor &>(child);
|
||||||
|
process_constructor(mc, c, last_access_specifier);
|
||||||
|
}
|
||||||
|
else if (child.kind() == cppast::cpp_entity_kind::destructor_t) {
|
||||||
|
auto &mc = static_cast<const cppast::cpp_destructor &>(child);
|
||||||
|
process_destructor(mc, c, last_access_specifier);
|
||||||
|
}
|
||||||
|
else if (child.kind() == cppast::cpp_entity_kind::enum_t) {
|
||||||
|
auto &en = static_cast<const cppast::cpp_enum &>(child);
|
||||||
|
if (en.name().empty()) {
|
||||||
|
// Here we only want to handle anonymous enums, regular nested
|
||||||
|
// enums are handled in the file-level visitor
|
||||||
|
process_anonymous_enum(en, c, last_access_specifier);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (child.kind() == cppast::cpp_entity_kind::friend_t) {
|
||||||
|
auto &fr = static_cast<const cppast::cpp_friend &>(child);
|
||||||
|
|
||||||
|
LOG_DBG("Found friend declaration: {}, {}", child.name(),
|
||||||
|
child.scope_name() ? child.scope_name().value().name()
|
||||||
|
: "<no-scope>");
|
||||||
|
|
||||||
|
process_friend(fr, c);
|
||||||
|
}
|
||||||
|
else if (cppast::is_friended(child)) {
|
||||||
|
auto &fr =
|
||||||
|
static_cast<const cppast::cpp_friend &>(child.parent().value());
|
||||||
|
|
||||||
|
LOG_DBG("Found friend template: {}", child.name());
|
||||||
|
|
||||||
|
process_friend(fr, c);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
LOG_DBG("Found some other class child: {} ({})", child.name(),
|
||||||
|
cppast::to_string(child.kind()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process class bases
|
||||||
|
for (auto &base : cls.bases()) {
|
||||||
|
class_parent cp;
|
||||||
|
cp.set_name(
|
||||||
|
clanguml::cx::util::fully_prefixed(ctx.get_namespace(), base));
|
||||||
|
cp.is_virtual(base.is_virtual());
|
||||||
|
|
||||||
|
switch (base.access_specifier()) {
|
||||||
|
case cppast::cpp_access_specifier_kind::cpp_private:
|
||||||
|
cp.set_access(access_t::kPrivate);
|
||||||
|
break;
|
||||||
|
case cppast::cpp_access_specifier_kind::cpp_public:
|
||||||
|
cp.set_access(access_t::kPublic);
|
||||||
|
break;
|
||||||
|
case cppast::cpp_access_specifier_kind::cpp_protected:
|
||||||
|
cp.set_access(access_t::kProtected);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
cp.set_access(access_t::kPublic);
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_DBG("Found base class {} for class {}", cp.name(), c.name());
|
||||||
|
|
||||||
|
c.add_parent(std::move(cp));
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.diagram().add_class(std::move(c));
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
const cppast::cpp_type &translation_unit_visitor::resolve_alias(
|
||||||
|
const cppast::cpp_type &type)
|
||||||
|
{
|
||||||
|
const auto &raw_type = cppast::remove_cv(cx::util::unreferenced(type));
|
||||||
|
const auto type_full_name =
|
||||||
|
cx::util::full_name(raw_type, ctx.entity_index(), false);
|
||||||
|
if (ctx.has_type_alias(type_full_name)) {
|
||||||
|
return ctx.get_type_alias_final(raw_type).get();
|
||||||
|
}
|
||||||
|
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
}
|
||||||
66
src/package_diagram/visitor/translation_unit_visitor.h
Normal file
66
src/package_diagram/visitor/translation_unit_visitor.h
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
/**
|
||||||
|
* src/package_diagram/visitor/translation_unit_visitor.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 "package_diagram/model/diagram.h"
|
||||||
|
#include "package_diagram/visitor/translation_unit_context.h"
|
||||||
|
#include "config/config.h"
|
||||||
|
#include "cx/cursor.h"
|
||||||
|
|
||||||
|
#include <clang-c/CXCompilationDatabase.h>
|
||||||
|
#include <clang-c/Index.h>
|
||||||
|
#include <cppast/cpp_friend.hpp>
|
||||||
|
#include <cppast/cpp_function_template.hpp>
|
||||||
|
#include <cppast/cpp_member_function.hpp>
|
||||||
|
#include <cppast/cpp_member_variable.hpp>
|
||||||
|
#include <cppast/cpp_template.hpp>
|
||||||
|
#include <cppast/cpp_template_parameter.hpp>
|
||||||
|
#include <cppast/cpp_type.hpp>
|
||||||
|
#include <cppast/visitor.hpp>
|
||||||
|
#include <type_safe/reference.hpp>
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace clanguml::package_diagram::visitor {
|
||||||
|
|
||||||
|
class translation_unit_visitor {
|
||||||
|
public:
|
||||||
|
translation_unit_visitor(cppast::cpp_entity_index &idx,
|
||||||
|
clanguml::package_diagram::model::diagram &diagram,
|
||||||
|
const clanguml::config::package_diagram &config);
|
||||||
|
|
||||||
|
void operator()(const cppast::cpp_entity &file);
|
||||||
|
|
||||||
|
void process_class_declaration(const cppast::cpp_class &cls,
|
||||||
|
type_safe::optional_ref<const cppast::cpp_template_specialization>
|
||||||
|
tspec = nullptr);
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* Try to resolve a type instance into a type referenced through an alias.
|
||||||
|
* If t does not represent an alias, returns t.
|
||||||
|
*/
|
||||||
|
const cppast::cpp_type &resolve_alias(const cppast::cpp_type &t);
|
||||||
|
|
||||||
|
// ctx allows to track current visitor context, e.g. current namespace
|
||||||
|
translation_unit_context ctx;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -115,4 +115,34 @@ bool find_element_alias(
|
|||||||
bool replace_all(
|
bool replace_all(
|
||||||
std::string &input, std::string pattern, std::string replace_with);
|
std::string &input, std::string pattern, std::string replace_with);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Appends a vector to a vector.
|
||||||
|
*
|
||||||
|
* @tparam T
|
||||||
|
* @param l
|
||||||
|
* @param r
|
||||||
|
*/
|
||||||
|
template <typename T> void append(std::vector<T> &l, const std::vector<T> &r)
|
||||||
|
{
|
||||||
|
l.insert(l.end(), r.begin(), r.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Checks if vector starts with a prefix.
|
||||||
|
*
|
||||||
|
* @tparam T
|
||||||
|
* @param col
|
||||||
|
* @param prefix
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
bool starts_with(const std::vector<T> &col, const std::vector<T> &prefix)
|
||||||
|
{
|
||||||
|
if(prefix.size() > col.size())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return std::vector<std::string>(prefix.begin(), prefix.end()) ==
|
||||||
|
std::vector<std::string>(col.begin(), col.begin() + prefix.size());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
20
tests/t30001/.clang-uml
Normal file
20
tests/t30001/.clang-uml
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
compilation_database_dir: ..
|
||||||
|
output_directory: puml
|
||||||
|
diagrams:
|
||||||
|
t30001_package:
|
||||||
|
type: package
|
||||||
|
glob:
|
||||||
|
- ../../tests/t30001/t30001.cc
|
||||||
|
include:
|
||||||
|
namespaces:
|
||||||
|
- clanguml::t30001
|
||||||
|
exclude:
|
||||||
|
namespaces:
|
||||||
|
- clanguml::t30001::detail
|
||||||
|
using_namespace:
|
||||||
|
- clanguml::t30001
|
||||||
|
plantuml:
|
||||||
|
before:
|
||||||
|
- "' t30001 test package diagram"
|
||||||
|
after:
|
||||||
|
- "note left of @A(A::AA::AAA): A AAA note..."
|
||||||
25
tests/t30001/t30001.cc
Normal file
25
tests/t30001/t30001.cc
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
|
||||||
|
namespace clanguml {
|
||||||
|
namespace t30001 {
|
||||||
|
namespace A {
|
||||||
|
namespace AA {
|
||||||
|
namespace AAA {
|
||||||
|
} // namespace AAA
|
||||||
|
namespace BBB {
|
||||||
|
} // namespace BBB
|
||||||
|
} // namespace AA
|
||||||
|
namespace BB {
|
||||||
|
} // namespace BB
|
||||||
|
} // namespace A
|
||||||
|
namespace B {
|
||||||
|
namespace AA {
|
||||||
|
namespace AAA {
|
||||||
|
} // namespace AAA
|
||||||
|
namespace BBB {
|
||||||
|
} // namespace BBB
|
||||||
|
} // namespace AA
|
||||||
|
namespace BB {
|
||||||
|
} // namespace BB
|
||||||
|
} // namespace B
|
||||||
|
}
|
||||||
|
}
|
||||||
55
tests/t30001/test_case.h
Normal file
55
tests/t30001/test_case.h
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
/**
|
||||||
|
* tests/t30001/test_case.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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
TEST_CASE("t30001", "[test-case][package]")
|
||||||
|
{
|
||||||
|
auto [config, db] = load_config("t30001");
|
||||||
|
|
||||||
|
auto diagram = config.diagrams["t30001_package"];
|
||||||
|
|
||||||
|
REQUIRE(diagram->include.namespaces.size() == 1);
|
||||||
|
REQUIRE_THAT(diagram->include.namespaces,
|
||||||
|
VectorContains(std::string{"clanguml::t30001"}));
|
||||||
|
|
||||||
|
REQUIRE(diagram->exclude.namespaces.size() == 1);
|
||||||
|
REQUIRE_THAT(diagram->exclude.namespaces,
|
||||||
|
VectorContains(std::string{"clanguml::t30001::detail"}));
|
||||||
|
|
||||||
|
REQUIRE(diagram->should_include("clanguml::t30001::A"));
|
||||||
|
REQUIRE(!diagram->should_include("clanguml::t30001::detail::C"));
|
||||||
|
REQUIRE(!diagram->should_include("std::vector"));
|
||||||
|
|
||||||
|
REQUIRE(diagram->name == "t30001_package");
|
||||||
|
|
||||||
|
auto model = generate_package_diagram(db, diagram);
|
||||||
|
|
||||||
|
REQUIRE(model.name() == "t30001_package");
|
||||||
|
|
||||||
|
auto puml = generate_package_puml(diagram, model);
|
||||||
|
AliasMatcher _A(puml);
|
||||||
|
|
||||||
|
REQUIRE_THAT(puml, StartsWith("@startuml"));
|
||||||
|
REQUIRE_THAT(puml, EndsWith("@enduml\n"));
|
||||||
|
|
||||||
|
REQUIRE_THAT(puml, Contains("component [A]"));
|
||||||
|
REQUIRE_THAT(puml, Contains("component [AA]"));
|
||||||
|
REQUIRE_THAT(puml, Contains("component [AAA]"));
|
||||||
|
|
||||||
|
save_puml(
|
||||||
|
"./" + config.output_directory + "/" + diagram->name + ".puml", puml);
|
||||||
|
}
|
||||||
@@ -64,6 +64,18 @@ clanguml::class_diagram::model::diagram generate_class_diagram(
|
|||||||
return diagram_model;
|
return diagram_model;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clanguml::package_diagram::model::diagram generate_package_diagram(
|
||||||
|
cppast::libclang_compilation_database &db,
|
||||||
|
std::shared_ptr<clanguml::config::diagram> diagram)
|
||||||
|
{
|
||||||
|
auto diagram_model =
|
||||||
|
clanguml::package_diagram::generators::plantuml::generate(db,
|
||||||
|
diagram->name,
|
||||||
|
dynamic_cast<clanguml::config::package_diagram &>(*diagram));
|
||||||
|
|
||||||
|
return diagram_model;
|
||||||
|
}
|
||||||
|
|
||||||
std::string generate_sequence_puml(
|
std::string generate_sequence_puml(
|
||||||
std::shared_ptr<clanguml::config::diagram> config,
|
std::shared_ptr<clanguml::config::diagram> config,
|
||||||
clanguml::sequence_diagram::model::diagram &model)
|
clanguml::sequence_diagram::model::diagram &model)
|
||||||
@@ -92,6 +104,20 @@ std::string generate_class_puml(
|
|||||||
return ss.str();
|
return ss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string generate_package_puml(
|
||||||
|
std::shared_ptr<clanguml::config::diagram> config,
|
||||||
|
clanguml::package_diagram::model::diagram &model)
|
||||||
|
{
|
||||||
|
using namespace clanguml::package_diagram::generators::plantuml;
|
||||||
|
|
||||||
|
std::stringstream ss;
|
||||||
|
|
||||||
|
ss << generator(
|
||||||
|
dynamic_cast<clanguml::config::package_diagram &>(*config), model);
|
||||||
|
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
void save_puml(const std::string &path, const std::string &puml)
|
void save_puml(const std::string &path, const std::string &puml)
|
||||||
{
|
{
|
||||||
std::filesystem::path p{path};
|
std::filesystem::path p{path};
|
||||||
@@ -147,6 +173,11 @@ 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"
|
||||||
|
|
||||||
|
//
|
||||||
|
// Package diagram tests
|
||||||
|
//
|
||||||
|
#include "t30001/test_case.h"
|
||||||
|
|
||||||
//
|
//
|
||||||
// Other tests (e.g. configuration file)
|
// Other tests (e.g. configuration file)
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -24,6 +24,8 @@
|
|||||||
#include "class_diagram/visitor/translation_unit_visitor.h"
|
#include "class_diagram/visitor/translation_unit_visitor.h"
|
||||||
#include "config/config.h"
|
#include "config/config.h"
|
||||||
#include "cx/compilation_database.h"
|
#include "cx/compilation_database.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"
|
#include "sequence_diagram/generators/plantuml/sequence_diagram_generator.h"
|
||||||
#include "sequence_diagram/visitor/translation_unit_visitor.h"
|
#include "sequence_diagram/visitor/translation_unit_visitor.h"
|
||||||
#include "util/util.h"
|
#include "util/util.h"
|
||||||
@@ -32,6 +34,7 @@
|
|||||||
|
|
||||||
#include "catch.h"
|
#include "catch.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <complex>
|
#include <complex>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <string>
|
#include <string>
|
||||||
@@ -168,6 +171,8 @@ struct AliasMatcher {
|
|||||||
patterns.push_back("class \"" + name + "\" as ");
|
patterns.push_back("class \"" + name + "\" as ");
|
||||||
patterns.push_back("abstract \"" + name + "\" as ");
|
patterns.push_back("abstract \"" + name + "\" as ");
|
||||||
patterns.push_back("enum \"" + name + "\" as ");
|
patterns.push_back("enum \"" + name + "\" as ");
|
||||||
|
patterns.push_back("component \"" + name + "\" as ");
|
||||||
|
patterns.push_back("component [" + name + "] as ");
|
||||||
|
|
||||||
for (const auto &line : puml) {
|
for (const auto &line : puml) {
|
||||||
for (const auto &pattern : patterns) {
|
for (const auto &pattern : patterns) {
|
||||||
@@ -396,6 +401,13 @@ ContainsMatcher IsField(std::string const &name,
|
|||||||
return ContainsMatcher(
|
return ContainsMatcher(
|
||||||
CasedString(pattern + " : " + type, caseSensitivity));
|
CasedString(pattern + " : " + type, caseSensitivity));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ContainsMatcher IsPackage(std::string const &str,
|
||||||
|
CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes)
|
||||||
|
{
|
||||||
|
return ContainsMatcher(
|
||||||
|
CasedString("component [" + str + "]", caseSensitivity));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user