Added initial support for package diagrams
This commit is contained in:
6
.gitignore
vendored
6
.gitignore
vendored
@@ -18,3 +18,9 @@ bin/
|
||||
/debug/
|
||||
/release/
|
||||
/.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,
|
||||
clanguml::config::class_diagram &diagram)
|
||||
{
|
||||
spdlog::info("Generating diagram {}.puml", name);
|
||||
LOG_DBG("Generating diagram {}.puml", name);
|
||||
clanguml::class_diagram::model::diagram d;
|
||||
d.set_name(name);
|
||||
|
||||
@@ -422,7 +422,7 @@ clanguml::class_diagram::model::diagram generate(
|
||||
// configuration
|
||||
std::vector<std::string> translation_units{};
|
||||
for (const auto &g : diagram.glob) {
|
||||
spdlog::debug("Processing glob: {}", g);
|
||||
LOG_DBG("Processing glob: {}", g);
|
||||
const auto matches = glob::rglob(g);
|
||||
std::copy(matches.begin(), matches.end(),
|
||||
std::back_inserter(translation_units));
|
||||
|
||||
@@ -69,7 +69,7 @@ bool diagram::should_include(const std::string &name_) const
|
||||
|
||||
for (const auto &ex : exclude.namespaces) {
|
||||
if (name.find(ex) == 0) {
|
||||
spdlog::debug("Skipping from diagram: {}", name);
|
||||
LOG_DBG("Skipping from diagram: {}", name);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -132,6 +132,7 @@ using clanguml::common::model::scope_t;
|
||||
using clanguml::config::class_diagram;
|
||||
using clanguml::config::config;
|
||||
using clanguml::config::filter;
|
||||
using clanguml::config::package_diagram;
|
||||
using clanguml::config::plantuml;
|
||||
using clanguml::config::sequence_diagram;
|
||||
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
|
||||
//
|
||||
@@ -313,8 +327,12 @@ template <> struct convert<config> {
|
||||
rhs.diagrams[name] = std::make_shared<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 {
|
||||
spdlog::warn(
|
||||
LOG_WARN(
|
||||
"Diagrams of type {} are not supported at the moment... ",
|
||||
diagram_type);
|
||||
}
|
||||
|
||||
@@ -101,6 +101,10 @@ struct sequence_diagram : public diagram {
|
||||
std::vector<source_location> start_from;
|
||||
};
|
||||
|
||||
struct package_diagram : public diagram {
|
||||
virtual ~package_diagram() = default;
|
||||
};
|
||||
|
||||
struct config {
|
||||
// the glob list is additive and relative to the current
|
||||
// directory
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
#include "compilation_database.h"
|
||||
#include "util/util.h"
|
||||
|
||||
#include <filesystem>
|
||||
#include <spdlog/fmt/fmt.h>
|
||||
@@ -73,7 +74,7 @@ CXTranslationUnit compilation_database::parse_translation_unit(
|
||||
clang_CompileCommands_getCommand(compile_commands, 0);
|
||||
|
||||
auto cc_filename = clang_CompileCommand_getFilename(compile_command);
|
||||
spdlog::debug(
|
||||
LOG_DBG(
|
||||
"Processing compile command file: {}", clang_getCString(cc_filename));
|
||||
|
||||
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);
|
||||
for (j = 0; j < num_args; ++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));
|
||||
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);
|
||||
|
||||
bool is_inside_class(const cppast::cpp_entity &e);
|
||||
|
||||
} // namespace util
|
||||
} // namespace cx
|
||||
} // namespace clanguml
|
||||
|
||||
12
src/main.cc
12
src/main.cc
@@ -21,7 +21,9 @@
|
||||
#include "class_diagram/generators/plantuml/class_diagram_generator.h"
|
||||
#include "config/config.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 "util/util.h"
|
||||
|
||||
#include <cli11/CLI11.hpp>
|
||||
@@ -79,6 +81,7 @@ int main(int argc, const char *argv[])
|
||||
continue;
|
||||
|
||||
using clanguml::config::class_diagram;
|
||||
using clanguml::config::package_diagram;
|
||||
using clanguml::config::sequence_diagram;
|
||||
|
||||
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),
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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(
|
||||
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;
|
||||
}
|
||||
|
||||
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::shared_ptr<clanguml::config::diagram> config,
|
||||
clanguml::sequence_diagram::model::diagram &model)
|
||||
@@ -92,6 +104,20 @@ std::string generate_class_puml(
|
||||
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)
|
||||
{
|
||||
std::filesystem::path p{path};
|
||||
@@ -147,6 +173,11 @@ using namespace clanguml::test::matchers;
|
||||
#include "t20001/test_case.h"
|
||||
#include "t20002/test_case.h"
|
||||
|
||||
//
|
||||
// Package diagram tests
|
||||
//
|
||||
#include "t30001/test_case.h"
|
||||
|
||||
//
|
||||
// Other tests (e.g. configuration file)
|
||||
//
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
#include "class_diagram/visitor/translation_unit_visitor.h"
|
||||
#include "config/config.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/visitor/translation_unit_visitor.h"
|
||||
#include "util/util.h"
|
||||
@@ -32,6 +34,7 @@
|
||||
|
||||
#include "catch.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <complex>
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
@@ -168,6 +171,8 @@ struct AliasMatcher {
|
||||
patterns.push_back("class \"" + name + "\" as ");
|
||||
patterns.push_back("abstract \"" + 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 &pattern : patterns) {
|
||||
@@ -396,6 +401,13 @@ ContainsMatcher IsField(std::string const &name,
|
||||
return ContainsMatcher(
|
||||
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