Merge pull request #15 from bkryza/add-diagram-layout-hints
Add diagram layout hints
@@ -7,20 +7,72 @@
|
||||
the key of the diagram YAML node
|
||||
|
||||
### Diagram options
|
||||
* `type` - type of diagram, one of [`class`, `sequence`]
|
||||
* `type` - type of diagram, one of [`class`, `sequence`, `package`]
|
||||
* `glob` - list of glob patterns to match source code files for analysis
|
||||
* `include_relations_also_as_members` - when set to `false`, class members for relationships are rendered in UML are skipped from class definition (default: `true`)
|
||||
* `generate_method_arguments` - determines whether the class diagrams methods contain full arguments (`full`), are abbreviated (`abbreviated`) or skipped (`none`)
|
||||
* `using_namespace` - similar to C++ `using namespace`, a `A::B` value here will render a class `A::B::C::MyClass` in the diagram as `C::MyClass`
|
||||
* `include` - definition of inclusion patterns:
|
||||
* `namespaces` - list of namespaces to include
|
||||
* `relationships` - list of relationships to include
|
||||
* `entity_types` - list of entity types to include (e.g. `class`, `enum`)
|
||||
* `scopes` - list of visibility scopes to include (e.g. `private`)
|
||||
* `exclude` - definition of exclusion patterns:
|
||||
* `exclude` - definition of excqlusion patterns:
|
||||
* `namespaces` - list of namespaces to exclude
|
||||
* `relationships` - list of relationships to exclude
|
||||
* `entity_types` - list of entity types to exclude (e.g. `class`, `enum`)
|
||||
* `scopes` - list of visibility scopes to exclude (e.g. `private`)
|
||||
* `layout` - add layout hints for entities (classes, packages)
|
||||
* `plantuml` - verbatim PlantUML directives which should be added to a diagram
|
||||
* `before` - list of directives which will be added before the generated diagram
|
||||
* `after` - list of directives which will be added after the generated diagram
|
||||
|
||||
|
||||
## Example complete config
|
||||
|
||||
```yaml
|
||||
# Directory containing the compile_commands.json file
|
||||
compilation_database_dir: debug
|
||||
# The directory where *.puml files will be generated
|
||||
output_directory: docs/diagrams
|
||||
# Set this as default for all diagrams
|
||||
generate_method_arguments: none
|
||||
# The map of diagrams - keys are also diagram file names
|
||||
diagrams:
|
||||
main_package:
|
||||
# Include this diagram definition from a separate file
|
||||
include!: uml/main_package_diagram.yml
|
||||
config_class:
|
||||
type: class
|
||||
# Do not include rendered relations in the class box
|
||||
include_relations_also_as_members: false
|
||||
# Limiting the number of files to include can significantly
|
||||
# improve the generation time
|
||||
glob:
|
||||
- src/common/model/*.h
|
||||
- src/common/model/*.cc
|
||||
- src/class_diagram/model/*.h
|
||||
- src/class_diagram/model/*.cc
|
||||
include:
|
||||
# Only include entities from the following namespaces
|
||||
namespaces:
|
||||
- clanguml::common::model
|
||||
- clanguml::class_diagram::model
|
||||
exclude:
|
||||
# Do not include private members and methods in the diagram
|
||||
scopes:
|
||||
- private
|
||||
layout:
|
||||
# Add layout hints for PlantUML
|
||||
ClassA:
|
||||
- up: ClassB
|
||||
- left: ClassC
|
||||
# Entities from this namespace will be shortened
|
||||
# (can only contain one element at the moment)
|
||||
using_namespace:
|
||||
- clanguml::class_diagram::model
|
||||
plantuml:
|
||||
# Add this line to the beginning of the resulting puml file
|
||||
before:
|
||||
- 'title clang-uml class diagram model'
|
||||
```
|
||||
@@ -33,6 +33,7 @@
|
||||
* [t00032](./test_cases/t00032.md) - Class template with template base classes test case
|
||||
* [t00033](./test_cases/t00033.md) - Nested template instantiation dependency test case
|
||||
* [t00034](./test_cases/t00034.md) - Template metaprogramming type function test case
|
||||
* [t00035](./test_cases/t00035.md) - PlantUML class diagram layout hints test case
|
||||
## Sequence diagrams
|
||||
* [t20001](./test_cases/t20001.md) - Basic sequence diagram test case
|
||||
* [t20002](./test_cases/t20002.md) - Free function sequence diagram test case
|
||||
@@ -43,5 +44,6 @@
|
||||
* [t30004](./test_cases/t30004.md) - PlantUML package decorators test case
|
||||
* [t30005](./test_cases/t30005.md) - Package namespace alias test case
|
||||
* [t30006](./test_cases/t30006.md) - Package split namespace test case
|
||||
* [t30007](./test_cases/t30007.md) - Package diagram layout hints test case
|
||||
## Configuration diagrams
|
||||
* [t90000](./test_cases/t90000.md) - Basic config test
|
||||
|
||||
@@ -18,6 +18,7 @@ diagrams:
|
||||
## Source code
|
||||
File t00006.cc
|
||||
```cpp
|
||||
#include <array>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
|
||||
@@ -19,6 +19,8 @@ diagrams:
|
||||
## Source code
|
||||
File t00017.cc
|
||||
```cpp
|
||||
#include <utility>
|
||||
|
||||
namespace clanguml {
|
||||
namespace t00017 {
|
||||
class A {
|
||||
@@ -55,6 +57,15 @@ class K {
|
||||
};
|
||||
|
||||
class R {
|
||||
explicit R(int &some_int, C &cc, const E &ee, F &&ff, I *&ii)
|
||||
: some_int_reference{some_int}
|
||||
, c{cc}
|
||||
, e{ee}
|
||||
, f{std::move(ff)}
|
||||
, i{ii}
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
int some_int;
|
||||
int *some_int_pointer;
|
||||
@@ -64,7 +75,7 @@ private:
|
||||
B *b;
|
||||
C &c;
|
||||
const D *d;
|
||||
const E &e{};
|
||||
const E &e;
|
||||
F &&f;
|
||||
G **g;
|
||||
H ***h;
|
||||
|
||||
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 28 KiB |
@@ -59,6 +59,11 @@ enum class F { one, two, three };
|
||||
|
||||
/// \uml{note[right] R class note.}
|
||||
class R {
|
||||
explicit R(C &c)
|
||||
: ccc(c)
|
||||
{
|
||||
}
|
||||
|
||||
A aaa;
|
||||
|
||||
B *bbb;
|
||||
|
||||
|
Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 62 KiB |
50
docs/test_cases/t00035.md
Normal file
@@ -0,0 +1,50 @@
|
||||
# t00035 - PlantUML class diagram layout hints test case
|
||||
## Config
|
||||
```yaml
|
||||
compilation_database_dir: ..
|
||||
output_directory: puml
|
||||
diagrams:
|
||||
t00035_class:
|
||||
type: class
|
||||
glob:
|
||||
- ../../tests/t00035/t00035.cc
|
||||
using_namespace:
|
||||
- clanguml::t00035
|
||||
include:
|
||||
namespaces:
|
||||
- clanguml::t00035
|
||||
layout:
|
||||
Center:
|
||||
- up: Top
|
||||
- down: Bottom
|
||||
- left: Left
|
||||
- right: Right
|
||||
|
||||
```
|
||||
## Source code
|
||||
File t00035.cc
|
||||
```cpp
|
||||
namespace clanguml {
|
||||
namespace t00035 {
|
||||
|
||||
struct Top {
|
||||
};
|
||||
|
||||
struct Left {
|
||||
};
|
||||
|
||||
struct Center {
|
||||
};
|
||||
|
||||
struct Bottom {
|
||||
};
|
||||
|
||||
struct Right {
|
||||
};
|
||||
|
||||
} // namespace t00035
|
||||
} // namespace clanguml
|
||||
|
||||
```
|
||||
## Generated UML diagrams
|
||||

|
||||
BIN
docs/test_cases/t00035_class.png
Normal file
|
After Width: | Height: | Size: 7.2 KiB |
|
Before Width: | Height: | Size: 9.9 KiB After Width: | Height: | Size: 9.9 KiB |
|
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 7.0 KiB After Width: | Height: | Size: 7.0 KiB |
62
docs/test_cases/t30007.md
Normal file
@@ -0,0 +1,62 @@
|
||||
# t30007 - Package diagram layout hints test case
|
||||
## Config
|
||||
```yaml
|
||||
compilation_database_dir: ..
|
||||
output_directory: puml
|
||||
diagrams:
|
||||
t30007_package:
|
||||
type: package
|
||||
glob:
|
||||
- ../../tests/t30007/t30007.cc
|
||||
include:
|
||||
namespaces:
|
||||
- clanguml::t30007
|
||||
using_namespace:
|
||||
- clanguml::t30007
|
||||
layout:
|
||||
C:
|
||||
- up: 'A::AA'
|
||||
- left: B
|
||||
plantuml:
|
||||
before:
|
||||
- "' t30007 test package diagram"
|
||||
```
|
||||
## Source code
|
||||
File t30007.cc
|
||||
```cpp
|
||||
namespace clanguml {
|
||||
namespace t30007 {
|
||||
|
||||
namespace B {
|
||||
struct BB {
|
||||
};
|
||||
}
|
||||
|
||||
/// \uml{note[top] Compare layout with t30006.}
|
||||
namespace A {
|
||||
namespace AA {
|
||||
struct A1 {
|
||||
B::BB *b;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
namespace C {
|
||||
struct CC {
|
||||
};
|
||||
}
|
||||
|
||||
/// \uml{note[bottom] Bottom A note.}
|
||||
namespace A {
|
||||
namespace AA {
|
||||
struct A2 {
|
||||
C::CC *c;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
## Generated UML diagrams
|
||||

|
||||
BIN
docs/test_cases/t30007_package.png
Normal file
|
After Width: | Height: | Size: 8.9 KiB |
@@ -22,79 +22,9 @@
|
||||
|
||||
namespace clanguml::class_diagram::generators::plantuml {
|
||||
|
||||
std::string relative_to(std::string n, std::string c)
|
||||
generator::generator(diagram_config &config, diagram_model &model)
|
||||
: common_generator<diagram_config, diagram_model>{config, model}
|
||||
{
|
||||
if (c.rfind(n) == std::string::npos)
|
||||
return c;
|
||||
|
||||
return c.substr(n.size() + 2);
|
||||
}
|
||||
|
||||
generator::generator(
|
||||
clanguml::config::class_diagram &config, diagram_model &model)
|
||||
: m_config(config)
|
||||
, m_model(model)
|
||||
{
|
||||
}
|
||||
|
||||
std::string generator::to_string(scope_t scope) const
|
||||
{
|
||||
switch (scope) {
|
||||
case scope_t::kPublic:
|
||||
return "+";
|
||||
case scope_t::kProtected:
|
||||
return "#";
|
||||
case scope_t::kPrivate:
|
||||
return "-";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
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::kInstantiation:
|
||||
return "instantiation";
|
||||
case relationship_t::kFriendship:
|
||||
return "friendship";
|
||||
case relationship_t::kDependency:
|
||||
return "dependency";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
void generator::generate_alias(const class_ &c, std::ostream &ostr) const
|
||||
@@ -118,6 +48,7 @@ void generator::generate_alias(const enum_ &e, std::ostream &ostr) const
|
||||
|
||||
void generator::generate(const class_ &c, std::ostream &ostr) const
|
||||
{
|
||||
namespace plantuml_common = clanguml::common::generators::plantuml;
|
||||
|
||||
const auto &uns = m_config.using_namespace();
|
||||
|
||||
@@ -147,7 +78,7 @@ void generator::generate(const class_ &c, std::ostream &ostr) const
|
||||
|
||||
std::string type{m.type()};
|
||||
|
||||
ostr << to_string(m.scope()) << m.name();
|
||||
ostr << plantuml_common::to_plantuml(m.scope()) << m.name();
|
||||
|
||||
ostr << "(";
|
||||
if (m_config.generate_method_arguments() !=
|
||||
@@ -190,10 +121,12 @@ void generator::generate(const class_ &c, std::ostream &ostr) const
|
||||
std::stringstream all_relations_str;
|
||||
std::set<std::string> unique_relations;
|
||||
for (const auto &r : c.relationships()) {
|
||||
if (!m_config.should_include_relationship(name(r.type())))
|
||||
if (!m_config.should_include_relationship(
|
||||
common::model::to_string(r.type())))
|
||||
continue;
|
||||
|
||||
LOG_DBG("== Processing relationship {}", to_string(r.type()));
|
||||
LOG_DBG("== Processing relationship {}",
|
||||
plantuml_common::to_plantuml(r.type(), r.style()));
|
||||
|
||||
std::stringstream relstr;
|
||||
std::string destination;
|
||||
@@ -206,7 +139,7 @@ void generator::generate(const class_ &c, std::ostream &ostr) const
|
||||
if (!r.multiplicity_source().empty())
|
||||
puml_relation += "\"" + r.multiplicity_source() + "\" ";
|
||||
|
||||
puml_relation += to_string(r.type(), r.style());
|
||||
puml_relation += plantuml_common::to_plantuml(r.type(), r.style());
|
||||
|
||||
if (!r.multiplicity_destination().empty())
|
||||
puml_relation += " \"" + r.multiplicity_destination() + "\"";
|
||||
@@ -216,7 +149,8 @@ void generator::generate(const class_ &c, std::ostream &ostr) const
|
||||
<< m_model.to_alias(ns_relative(uns, destination));
|
||||
|
||||
if (!r.label().empty()) {
|
||||
relstr << " : " << to_string(r.scope()) << r.label();
|
||||
relstr << " : " << plantuml_common::to_plantuml(r.scope())
|
||||
<< r.label();
|
||||
rendered_relations.emplace(r.label());
|
||||
}
|
||||
|
||||
@@ -233,7 +167,8 @@ void generator::generate(const class_ &c, std::ostream &ostr) const
|
||||
catch (error::uml_alias_missing &e) {
|
||||
LOG_ERROR("=== Skipping {} relation from {} to {} due "
|
||||
"to: {}",
|
||||
to_string(r.type()), c.full_name(), destination, e.what());
|
||||
plantuml_common::to_plantuml(r.type(), r.style()),
|
||||
c.full_name(), destination, e.what());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -251,13 +186,13 @@ void generator::generate(const class_ &c, std::ostream &ostr) const
|
||||
if (m.is_static())
|
||||
ostr << "{static} ";
|
||||
|
||||
ostr << to_string(m.scope()) << m.name() << " : "
|
||||
ostr << plantuml_common::to_plantuml(m.scope()) << m.name() << " : "
|
||||
<< ns_relative(uns, m.type()) << '\n';
|
||||
}
|
||||
|
||||
ostr << "}" << '\n';
|
||||
|
||||
if (m_config.should_include_relationship("inheritance"))
|
||||
if (m_config.should_include_relationship("inheritance")) {
|
||||
for (const auto &b : c.parents()) {
|
||||
std::stringstream relstr;
|
||||
try {
|
||||
@@ -273,19 +208,10 @@ void generator::generate(const class_ &c, std::ostream &ostr) const
|
||||
b.name(), c.name(), e.what());
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Process notes
|
||||
//
|
||||
for (auto decorator : c.decorators()) {
|
||||
auto note = std::dynamic_pointer_cast<decorators::note>(decorator);
|
||||
if (note && note->applies_to_diagram(m_config.name)) {
|
||||
ostr << "note " << note->position << " of " << c.alias() << '\n'
|
||||
<< note->text << '\n'
|
||||
<< "end note\n";
|
||||
}
|
||||
}
|
||||
|
||||
generate_notes(ostr, c);
|
||||
|
||||
// Print relationships
|
||||
ostr << all_relations_str.str();
|
||||
}
|
||||
@@ -306,18 +232,21 @@ void generator::generate(const enum_ &e, std::ostream &ostr) const
|
||||
ostr << "}" << '\n';
|
||||
|
||||
for (const auto &r : e.relationships()) {
|
||||
if (!m_config.should_include_relationship(name(r.type())))
|
||||
if (!m_config.should_include_relationship(
|
||||
common::model::to_string(r.type())))
|
||||
continue;
|
||||
|
||||
std::string destination;
|
||||
std::stringstream relstr;
|
||||
try {
|
||||
|
||||
destination = r.destination();
|
||||
|
||||
relstr << m_model.to_alias(
|
||||
ns_relative(m_config.using_namespace(), e.name()))
|
||||
<< " " << to_string(r.type()) << " "
|
||||
<< " "
|
||||
<< clanguml::common::generators::plantuml::to_plantuml(
|
||||
r.type(), r.style())
|
||||
<< " "
|
||||
<< m_model.to_alias(
|
||||
ns_relative(m_config.using_namespace(), destination));
|
||||
|
||||
@@ -331,38 +260,20 @@ void generator::generate(const enum_ &e, std::ostream &ostr) const
|
||||
catch (error::uml_alias_missing &ex) {
|
||||
LOG_ERROR("Skipping {} relation from {} to {} due "
|
||||
"to: {}",
|
||||
to_string(r.type()), e.full_name(), destination, ex.what());
|
||||
clanguml::common::generators::plantuml::to_plantuml(
|
||||
r.type(), r.style()),
|
||||
e.full_name(), destination, ex.what());
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Process notes
|
||||
//
|
||||
for (auto decorator : e.decorators()) {
|
||||
auto note = std::dynamic_pointer_cast<decorators::note>(decorator);
|
||||
if (note && note->applies_to_diagram(m_config.name)) {
|
||||
ostr << "note " << note->position << " of " << e.alias() << '\n'
|
||||
<< note->text << '\n'
|
||||
<< "end note\n";
|
||||
}
|
||||
}
|
||||
generate_notes(ostr, e);
|
||||
}
|
||||
|
||||
void generator::generate(std::ostream &ostr) const
|
||||
{
|
||||
ostr << "@startuml" << '\n';
|
||||
|
||||
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';
|
||||
}
|
||||
generate_plantuml_directives(ostr, m_config.puml().before);
|
||||
|
||||
if (m_config.should_include_entities("classes")) {
|
||||
for (const auto &c : m_model.classes()) {
|
||||
@@ -393,57 +304,11 @@ void generator::generate(std::ostream &ostr) const
|
||||
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 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';
|
||||
}
|
||||
generate_config_layout_hints(ostr);
|
||||
|
||||
generate_plantuml_directives(ostr, m_config.puml().after);
|
||||
|
||||
ostr << "@enduml" << '\n';
|
||||
}
|
||||
std::ostream &operator<<(std::ostream &os, const generator &g)
|
||||
{
|
||||
g.generate(os);
|
||||
return os;
|
||||
}
|
||||
|
||||
clanguml::class_diagram::model::diagram generate(
|
||||
cppast::libclang_compilation_database &db, const std::string &name,
|
||||
clanguml::config::class_diagram &diagram)
|
||||
{
|
||||
LOG_DBG("Generating diagram {}.puml", name);
|
||||
clanguml::class_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::class_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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "class_diagram/model/diagram.h"
|
||||
#include "class_diagram/model/enum.h"
|
||||
#include "class_diagram/visitor/translation_unit_visitor.h"
|
||||
#include "common/generators/plantuml/generator.h"
|
||||
#include "common/model/relationship.h"
|
||||
#include "config/config.h"
|
||||
#include "cx/compilation_database.h"
|
||||
@@ -40,25 +41,22 @@ namespace class_diagram {
|
||||
namespace generators {
|
||||
namespace plantuml {
|
||||
|
||||
using diagram_config = clanguml::class_diagram::model::diagram;
|
||||
using diagram_config = clanguml::config::class_diagram;
|
||||
using diagram_model = clanguml::class_diagram::model::diagram;
|
||||
template <typename C, typename D>
|
||||
using common_generator =
|
||||
clanguml::common::generators::plantuml::generator<C, D>;
|
||||
|
||||
using clanguml::class_diagram::model::class_;
|
||||
using clanguml::class_diagram::model::enum_;
|
||||
using clanguml::common::model::relationship_t;
|
||||
using clanguml::common::model::scope_t;
|
||||
|
||||
using namespace clanguml::util;
|
||||
|
||||
std::string relative_to(std::string n, std::string c);
|
||||
|
||||
class generator {
|
||||
class generator : public common_generator<diagram_config, diagram_model> {
|
||||
public:
|
||||
generator(clanguml::config::class_diagram &config, diagram_model &model);
|
||||
|
||||
std::string to_string(scope_t scope) const;
|
||||
|
||||
std::string to_string(relationship_t r, std::string style = "") const;
|
||||
|
||||
std::string name(relationship_t r) const;
|
||||
generator(diagram_config &config, diagram_model &model);
|
||||
|
||||
void generate_alias(const class_ &c, std::ostream &ostr) const;
|
||||
|
||||
@@ -68,19 +66,9 @@ public:
|
||||
|
||||
void generate(const enum_ &e, std::ostream &ostr) const;
|
||||
|
||||
void generate(std::ostream &ostr) const;
|
||||
|
||||
friend std::ostream &operator<<(std::ostream &os, const generator &g);
|
||||
|
||||
private:
|
||||
clanguml::config::class_diagram &m_config;
|
||||
diagram_model &m_model;
|
||||
void generate(std::ostream &ostr) const override;
|
||||
};
|
||||
|
||||
clanguml::class_diagram::model::diagram generate(
|
||||
cppast::libclang_compilation_database &db, const std::string &name,
|
||||
clanguml::config::class_diagram &diagram);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,10 +23,6 @@
|
||||
|
||||
namespace clanguml::class_diagram::model {
|
||||
|
||||
std::string diagram::name() const { return name_; }
|
||||
|
||||
void diagram::set_name(const std::string &name) { name_ = name; }
|
||||
|
||||
const std::vector<class_> diagram::classes() const { return classes_; }
|
||||
|
||||
const std::vector<enum_> diagram::enums() const { return enums_; }
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "class.h"
|
||||
#include "common/model/diagram.h"
|
||||
#include "enum.h"
|
||||
#include "type_alias.h"
|
||||
|
||||
@@ -26,12 +27,8 @@
|
||||
|
||||
namespace clanguml::class_diagram::model {
|
||||
|
||||
class diagram {
|
||||
class diagram : public clanguml::common::model::diagram {
|
||||
public:
|
||||
std::string name() const;
|
||||
|
||||
void set_name(const std::string &name);
|
||||
|
||||
const std::vector<class_> classes() const;
|
||||
|
||||
const std::vector<enum_> enums() const;
|
||||
@@ -47,7 +44,6 @@ public:
|
||||
std::string to_alias(const std::string &full_name) const;
|
||||
|
||||
private:
|
||||
std::string name_;
|
||||
std::vector<class_> classes_;
|
||||
std::vector<enum_> enums_;
|
||||
std::map<std::string, type_alias> type_aliases_;
|
||||
|
||||
71
src/common/generators/plantuml/generator.cc
Normal file
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
* src/common/generators/plantuml/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.
|
||||
*/
|
||||
#include "generator.h"
|
||||
|
||||
namespace clanguml::common::generators::plantuml {
|
||||
|
||||
std::string to_plantuml(relationship_t r, std::string style)
|
||||
{
|
||||
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 to_plantuml(scope_t scope)
|
||||
{
|
||||
switch (scope) {
|
||||
case scope_t::kPublic:
|
||||
return "+";
|
||||
case scope_t::kProtected:
|
||||
return "#";
|
||||
case scope_t::kPrivate:
|
||||
return "-";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
std::string to_plantuml(message_t r)
|
||||
{
|
||||
switch (r) {
|
||||
case message_t::kCall:
|
||||
return "->";
|
||||
case message_t::kReturn:
|
||||
return "-->";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
165
src/common/generators/plantuml/generator.h
Normal file
@@ -0,0 +1,165 @@
|
||||
/**
|
||||
* src/common/generators/plantuml/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 "config/config.h"
|
||||
#include "util/error.h"
|
||||
#include "util/util.h"
|
||||
|
||||
#include <cppast/libclang_parser.hpp>
|
||||
#include <glob/glob.hpp>
|
||||
|
||||
#include <ostream>
|
||||
|
||||
namespace clanguml::common::generators::plantuml {
|
||||
|
||||
using clanguml::common::model::message_t;
|
||||
using clanguml::common::model::relationship_t;
|
||||
using clanguml::common::model::scope_t;
|
||||
|
||||
std::string to_plantuml(relationship_t r, std::string style);
|
||||
std::string to_plantuml(scope_t scope);
|
||||
std::string to_plantuml(message_t r);
|
||||
|
||||
template <typename ConfigType, typename DiagramType> class generator {
|
||||
public:
|
||||
generator(ConfigType &config, DiagramType &model)
|
||||
: m_config{config}
|
||||
, m_model{model}
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~generator() = default;
|
||||
|
||||
virtual void generate(std::ostream &ostr) const = 0;
|
||||
|
||||
template <typename C, typename D>
|
||||
friend std::ostream &operator<<(std::ostream &os, const generator<C, D> &g);
|
||||
|
||||
void generate_config_layout_hints(std::ostream &ostr) const;
|
||||
|
||||
void generate_plantuml_directives(
|
||||
std::ostream &ostr, const std::vector<std::string> &directives) const;
|
||||
|
||||
void generate_notes(
|
||||
std::ostream &ostr, const model::element &decorators) const;
|
||||
|
||||
protected:
|
||||
ConfigType &m_config;
|
||||
DiagramType &m_model;
|
||||
};
|
||||
|
||||
template <typename C, typename D>
|
||||
std::ostream &operator<<(std::ostream &os, const generator<C, D> &g)
|
||||
{
|
||||
g.generate(os);
|
||||
return os;
|
||||
}
|
||||
|
||||
template <typename C, typename D>
|
||||
void generator<C, D>::generate_config_layout_hints(std::ostream &ostr) const
|
||||
{
|
||||
using namespace clanguml::util;
|
||||
|
||||
const auto &uns = m_config.using_namespace();
|
||||
|
||||
// Generate layout hints
|
||||
for (const auto &[entity, hints] : m_config.layout()) {
|
||||
for (const auto &hint : hints) {
|
||||
std::stringstream hint_str;
|
||||
try {
|
||||
hint_str << m_model.to_alias(ns_relative(uns, entity))
|
||||
<< " -[hidden]"
|
||||
<< clanguml::config::to_string(hint.hint) << "- "
|
||||
<< m_model.to_alias(ns_relative(uns, hint.entity))
|
||||
<< '\n';
|
||||
ostr << hint_str.str();
|
||||
}
|
||||
catch (clanguml::error::uml_alias_missing &e) {
|
||||
LOG_ERROR("=== Skipping layout hint from {} to {} due "
|
||||
"to: {}",
|
||||
entity, hint.entity, e.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename C, typename D>
|
||||
void generator<C, D>::generate_plantuml_directives(
|
||||
std::ostream &ostr, const std::vector<std::string> &directives) const
|
||||
{
|
||||
for (const auto &b : directives) {
|
||||
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(util::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';
|
||||
}
|
||||
}
|
||||
|
||||
template <typename C, typename D>
|
||||
void generator<C, D>::generate_notes(
|
||||
std::ostream &ostr, const model::element &e) const
|
||||
{
|
||||
for (auto decorator : e.decorators()) {
|
||||
auto note = std::dynamic_pointer_cast<decorators::note>(decorator);
|
||||
if (note && note->applies_to_diagram(m_config.name)) {
|
||||
ostr << "note " << note->position << " of " << e.alias() << '\n'
|
||||
<< note->text << '\n'
|
||||
<< "end note\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename DiagramModel, typename DiagramConfig,
|
||||
typename DiagramVisitor>
|
||||
DiagramModel generate(cppast::libclang_compilation_database &db,
|
||||
const std::string &name, DiagramConfig &diagram)
|
||||
{
|
||||
LOG_INFO("Generating diagram {}.puml", name);
|
||||
DiagramModel 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
|
||||
DiagramVisitor ctx(idx, d, diagram);
|
||||
cppast::parse_files(parser, translation_units, db);
|
||||
for (auto &file : parser.files())
|
||||
ctx(file);
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* src/sequence_diagram/model/enums.h
|
||||
* src/common/model/diagram.cc
|
||||
*
|
||||
* Copyright (c) 2021-2022 Bartek Kryza <bkryza@gmail.com>
|
||||
*
|
||||
@@ -15,10 +15,13 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
namespace clanguml::sequence_diagram::model {
|
||||
#include "diagram.h"
|
||||
|
||||
enum class message_t { kCall, kReturn };
|
||||
namespace clanguml::common::model {
|
||||
|
||||
}
|
||||
std::string diagram::name() const { return name_; }
|
||||
|
||||
void diagram::set_name(const std::string &name) { name_ = name; }
|
||||
|
||||
}
|
||||
34
src/common/model/diagram.h
Normal file
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* src/common/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 <string>
|
||||
|
||||
namespace clanguml::common::model {
|
||||
|
||||
class diagram {
|
||||
public:
|
||||
std::string name() const;
|
||||
|
||||
void set_name(const std::string &name);
|
||||
|
||||
private:
|
||||
std::string name_;
|
||||
};
|
||||
|
||||
}
|
||||
93
src/common/model/enums.cc
Normal file
@@ -0,0 +1,93 @@
|
||||
/**
|
||||
* src/class_diagram/model/enums.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 "enums.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
namespace clanguml::common::model {
|
||||
|
||||
std::string to_string(relationship_t r)
|
||||
{
|
||||
switch (r) {
|
||||
case relationship_t::kNone:
|
||||
return "none";
|
||||
case relationship_t::kExtension:
|
||||
return "extension";
|
||||
case relationship_t::kComposition:
|
||||
return "composition";
|
||||
case relationship_t::kAggregation:
|
||||
return "aggregation";
|
||||
case relationship_t::kContainment:
|
||||
return "containment";
|
||||
case relationship_t::kOwnership:
|
||||
return "ownership";
|
||||
case relationship_t::kAssociation:
|
||||
return "association";
|
||||
case relationship_t::kInstantiation:
|
||||
return "instantiation";
|
||||
case relationship_t::kFriendship:
|
||||
return "friendship";
|
||||
case relationship_t::kDependency:
|
||||
return "dependency";
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
std::string to_string(scope_t s)
|
||||
{
|
||||
switch (s) {
|
||||
case scope_t::kPublic:
|
||||
return "public";
|
||||
case scope_t::kProtected:
|
||||
return "protected";
|
||||
case scope_t::kPrivate:
|
||||
return "private";
|
||||
case scope_t::kNone:
|
||||
return "none";
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
std::string to_string(access_t a)
|
||||
{
|
||||
switch (a) {
|
||||
case access_t::kPublic:
|
||||
return "public";
|
||||
case access_t::kProtected:
|
||||
return "protected";
|
||||
case access_t::kPrivate:
|
||||
return "private";
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
std::string to_string(message_t r)
|
||||
{
|
||||
switch (r) {
|
||||
case message_t::kCall:
|
||||
return "call";
|
||||
case message_t::kReturn:
|
||||
return "return";
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,8 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace clanguml::common::model {
|
||||
|
||||
enum class access_t { kPublic, kProtected, kPrivate };
|
||||
@@ -36,4 +38,14 @@ enum class relationship_t {
|
||||
kDependency
|
||||
};
|
||||
|
||||
enum class message_t { kCall, kReturn };
|
||||
|
||||
std::string to_string(relationship_t r);
|
||||
|
||||
std::string to_string(scope_t r);
|
||||
|
||||
std::string to_string(access_t r);
|
||||
|
||||
std::string to_string(message_t r);
|
||||
|
||||
}
|
||||
|
||||
@@ -20,34 +20,6 @@
|
||||
|
||||
namespace clanguml::common::model {
|
||||
|
||||
std::string to_string(relationship_t r)
|
||||
{
|
||||
switch (r) {
|
||||
case relationship_t::kNone:
|
||||
return "none";
|
||||
case relationship_t::kExtension:
|
||||
return "extension";
|
||||
case relationship_t::kComposition:
|
||||
return "composition";
|
||||
case relationship_t::kAggregation:
|
||||
return "aggregation";
|
||||
case relationship_t::kContainment:
|
||||
return "containment";
|
||||
case relationship_t::kOwnership:
|
||||
return "ownership";
|
||||
case relationship_t::kAssociation:
|
||||
return "association";
|
||||
case relationship_t::kInstantiation:
|
||||
return "instantiation";
|
||||
case relationship_t::kFriendship:
|
||||
return "frendship";
|
||||
case relationship_t::kDependency:
|
||||
return "dependency";
|
||||
default:
|
||||
return "invalid";
|
||||
}
|
||||
}
|
||||
|
||||
relationship::relationship(relationship_t type, const std::string &destination,
|
||||
scope_t scope, const std::string &label,
|
||||
const std::string &multiplicity_source,
|
||||
|
||||
@@ -24,8 +24,6 @@
|
||||
|
||||
namespace clanguml::common::model {
|
||||
|
||||
std::string to_string(relationship_t r);
|
||||
|
||||
class relationship : public common::model::decorated_element,
|
||||
public common::model::stylable_element {
|
||||
public:
|
||||
|
||||
@@ -58,6 +58,22 @@ std::string to_string(const diagram_type t)
|
||||
}
|
||||
}
|
||||
|
||||
std::string to_string(const hint_t t)
|
||||
{
|
||||
switch (t) {
|
||||
case hint_t::up:
|
||||
return "up";
|
||||
case hint_t::down:
|
||||
return "down";
|
||||
case hint_t::left:
|
||||
return "left";
|
||||
case hint_t::right:
|
||||
return "right";
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
void plantuml::append(const plantuml &r)
|
||||
{
|
||||
before.insert(before.end(), r.before.begin(), r.before.end());
|
||||
@@ -204,6 +220,8 @@ using clanguml::common::model::scope_t;
|
||||
using clanguml::config::class_diagram;
|
||||
using clanguml::config::config;
|
||||
using clanguml::config::filter;
|
||||
using clanguml::config::hint_t;
|
||||
using clanguml::config::layout_hint;
|
||||
using clanguml::config::method_arguments;
|
||||
using clanguml::config::package_diagram;
|
||||
using clanguml::config::plantuml;
|
||||
@@ -375,6 +393,7 @@ template <> struct convert<class_diagram> {
|
||||
return false;
|
||||
|
||||
get_option(node, rhs.classes);
|
||||
get_option(node, rhs.layout);
|
||||
get_option(node, rhs.include_relations_also_as_members);
|
||||
get_option(node, rhs.generate_method_arguments);
|
||||
|
||||
@@ -406,6 +425,39 @@ template <> struct convert<package_diagram> {
|
||||
if (!decode_diagram(node, rhs))
|
||||
return false;
|
||||
|
||||
get_option(node, rhs.layout);
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// layout_hint Yaml decoder
|
||||
//
|
||||
template <> struct convert<layout_hint> {
|
||||
static bool decode(const Node &node, layout_hint &rhs)
|
||||
{
|
||||
assert(node.Type() == NodeType::Map);
|
||||
|
||||
if (node["up"]) {
|
||||
rhs.hint = hint_t::up;
|
||||
rhs.entity = node["up"].as<std::string>();
|
||||
}
|
||||
else if (node["down"]) {
|
||||
rhs.hint = hint_t::down;
|
||||
rhs.entity = node["down"].as<std::string>();
|
||||
}
|
||||
else if (node["left"]) {
|
||||
rhs.hint = hint_t::left;
|
||||
rhs.entity = node["left"].as<std::string>();
|
||||
}
|
||||
else if (node["right"]) {
|
||||
rhs.hint = hint_t::right;
|
||||
rhs.entity = node["right"].as<std::string>();
|
||||
}
|
||||
else
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -65,7 +65,17 @@ struct filter {
|
||||
std::vector<common::model::scope_t> scopes;
|
||||
};
|
||||
|
||||
enum class hint_t { up, down, left, right };
|
||||
|
||||
struct layout_hint {
|
||||
hint_t hint;
|
||||
std::string entity;
|
||||
};
|
||||
|
||||
using layout_hints = std::map<std::string, std::vector<layout_hint>>;
|
||||
|
||||
std::string to_string(const diagram_type t);
|
||||
std::string to_string(const hint_t t);
|
||||
|
||||
struct inheritable_diagram_options {
|
||||
option<std::vector<std::string>> glob{"glob"};
|
||||
@@ -109,6 +119,7 @@ struct class_diagram : public diagram {
|
||||
diagram_type type() const override;
|
||||
|
||||
option<std::vector<std::string>> classes{"classes"};
|
||||
option<layout_hints> layout{"layout"};
|
||||
|
||||
bool has_class(std::string clazz);
|
||||
};
|
||||
@@ -125,6 +136,8 @@ struct package_diagram : public diagram {
|
||||
virtual ~package_diagram() = default;
|
||||
|
||||
diagram_type type() const override;
|
||||
|
||||
option<layout_hints> layout{"layout"};
|
||||
};
|
||||
|
||||
struct config : public inheritable_diagram_options {
|
||||
|
||||
36
src/main.cc
@@ -113,31 +113,47 @@ int main(int argc, const char *argv[])
|
||||
ofs.open(path, std::ofstream::out | std::ofstream::trunc);
|
||||
|
||||
if (diagram->type() == diagram_type::class_diagram) {
|
||||
using diagram_config = clanguml::config::class_diagram;
|
||||
using diagram_model = clanguml::class_diagram::model::diagram;
|
||||
using diagram_visitor =
|
||||
clanguml::class_diagram::visitor::translation_unit_visitor;
|
||||
|
||||
auto model =
|
||||
clanguml::class_diagram::generators::plantuml::generate(
|
||||
db, name, dynamic_cast<class_diagram &>(*diagram));
|
||||
clanguml::common::generators::plantuml::generate<diagram_model,
|
||||
diagram_config, diagram_visitor>(db, diagram->name,
|
||||
dynamic_cast<diagram_config &>(*diagram));
|
||||
|
||||
ofs << clanguml::class_diagram::generators::plantuml::generator(
|
||||
dynamic_cast<clanguml::config::class_diagram &>(*diagram),
|
||||
model);
|
||||
dynamic_cast<diagram_config &>(*diagram), model);
|
||||
}
|
||||
else if (diagram->type() == diagram_type::sequence_diagram) {
|
||||
using diagram_config = clanguml::config::sequence_diagram;
|
||||
using diagram_model = clanguml::sequence_diagram::model::diagram;
|
||||
using diagram_visitor =
|
||||
clanguml::sequence_diagram::visitor::translation_unit_visitor;
|
||||
|
||||
auto model =
|
||||
clanguml::sequence_diagram::generators::plantuml::generate(
|
||||
db, name, dynamic_cast<sequence_diagram &>(*diagram));
|
||||
clanguml::common::generators::plantuml::generate<diagram_model,
|
||||
diagram_config, diagram_visitor>(db, diagram->name,
|
||||
dynamic_cast<diagram_config &>(*diagram));
|
||||
|
||||
ofs << clanguml::sequence_diagram::generators::plantuml::generator(
|
||||
dynamic_cast<clanguml::config::sequence_diagram &>(*diagram),
|
||||
model);
|
||||
}
|
||||
else if (diagram->type() == diagram_type::package_diagram) {
|
||||
using diagram_config = clanguml::config::package_diagram;
|
||||
using diagram_model = clanguml::package_diagram::model::diagram;
|
||||
using diagram_visitor =
|
||||
clanguml::package_diagram::visitor::translation_unit_visitor;
|
||||
|
||||
auto model =
|
||||
clanguml::package_diagram::generators::plantuml::generate(
|
||||
db, name, dynamic_cast<package_diagram &>(*diagram));
|
||||
clanguml::common::generators::plantuml::generate<diagram_model,
|
||||
diagram_config, diagram_visitor>(db, diagram->name,
|
||||
dynamic_cast<diagram_config &>(*diagram));
|
||||
|
||||
ofs << clanguml::package_diagram::generators::plantuml::generator(
|
||||
dynamic_cast<clanguml::config::package_diagram &>(*diagram),
|
||||
model);
|
||||
dynamic_cast<diagram_config &>(*diagram), model);
|
||||
}
|
||||
|
||||
LOG_INFO("Written {} diagram to {}", name, path.string());
|
||||
|
||||
@@ -22,61 +22,9 @@
|
||||
|
||||
namespace clanguml::package_diagram::generators::plantuml {
|
||||
|
||||
std::string relative_to(std::string n, std::string c)
|
||||
generator::generator(diagram_config &config, diagram_model &model)
|
||||
: common_generator<diagram_config, diagram_model>{config, model}
|
||||
{
|
||||
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_relationships(
|
||||
@@ -132,35 +80,14 @@ void generator::generate(const package &p, std::ostream &ostr) const
|
||||
|
||||
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";
|
||||
}
|
||||
}
|
||||
generate_notes(ostr, p);
|
||||
}
|
||||
|
||||
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';
|
||||
}
|
||||
generate_plantuml_directives(ostr, m_config.puml().before);
|
||||
|
||||
if (m_config.should_include_entities("packages")) {
|
||||
for (const auto &p : m_model) {
|
||||
@@ -175,58 +102,11 @@ void generator::generate(std::ostream &ostr) const
|
||||
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 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';
|
||||
}
|
||||
generate_config_layout_hints(ostr);
|
||||
|
||||
generate_plantuml_directives(ostr, m_config.puml().after);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "common/generators/plantuml/generator.h"
|
||||
#include "common/model/relationship.h"
|
||||
#include "config/config.h"
|
||||
#include "cx/compilation_database.h"
|
||||
@@ -27,7 +28,6 @@
|
||||
|
||||
#include <cppast/cpp_entity_index.hpp>
|
||||
#include <cppast/libclang_parser.hpp>
|
||||
#include <glob/glob.hpp>
|
||||
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
@@ -39,22 +39,21 @@ namespace package_diagram {
|
||||
namespace generators {
|
||||
namespace plantuml {
|
||||
|
||||
using diagram_config = clanguml::package_diagram::model::diagram;
|
||||
using diagram_config = clanguml::config::package_diagram;
|
||||
using diagram_model = clanguml::package_diagram::model::diagram;
|
||||
|
||||
template <typename C, typename D>
|
||||
using common_generator =
|
||||
clanguml::common::generators::plantuml::generator<C, D>;
|
||||
|
||||
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 {
|
||||
class generator : public common_generator<diagram_config, diagram_model> {
|
||||
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;
|
||||
generator(diagram_config &config, diagram_model &model);
|
||||
|
||||
void generate_alias(const package &c, std::ostream &ostr) const;
|
||||
|
||||
@@ -63,18 +62,8 @@ public:
|
||||
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);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,10 +23,6 @@
|
||||
|
||||
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);
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "common/model/diagram.h"
|
||||
#include "package.h"
|
||||
|
||||
#include <type_safe/optional_ref.hpp>
|
||||
@@ -26,17 +27,12 @@
|
||||
|
||||
namespace clanguml::package_diagram::model {
|
||||
|
||||
class diagram : public detail::package_trait<package, std::vector> {
|
||||
class diagram : public clanguml::common::model::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_;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -25,12 +25,10 @@
|
||||
|
||||
namespace clanguml::sequence_diagram::generators::plantuml {
|
||||
|
||||
using diagram_model = clanguml::sequence_diagram::model::diagram;
|
||||
using diagram_config = clanguml::config::sequence_diagram::diagram;
|
||||
using clanguml::common::model::message_t;
|
||||
using clanguml::config::source_location;
|
||||
using clanguml::sequence_diagram::model::activity;
|
||||
using clanguml::sequence_diagram::model::message;
|
||||
using clanguml::sequence_diagram::model::message_t;
|
||||
using clanguml::sequence_diagram::visitor::translation_unit_context;
|
||||
using namespace clanguml::util;
|
||||
|
||||
@@ -40,31 +38,18 @@ using namespace clanguml::util;
|
||||
|
||||
generator::generator(
|
||||
clanguml::config::sequence_diagram &config, diagram_model &model)
|
||||
: m_config(config)
|
||||
, m_model(model)
|
||||
: common_generator<diagram_config, diagram_model>{config, model}
|
||||
{
|
||||
}
|
||||
|
||||
std::string generator::to_string(message_t r) const
|
||||
{
|
||||
switch (r) {
|
||||
case message_t::kCall:
|
||||
return "->";
|
||||
case message_t::kReturn:
|
||||
return "<--";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
void generator::generate_call(const message &m, std::ostream &ostr) const
|
||||
{
|
||||
const auto from = ns_relative(m_config.using_namespace(), m.from);
|
||||
const auto to = ns_relative(m_config.using_namespace(), m.to);
|
||||
|
||||
ostr << '"' << from << "\" "
|
||||
<< "->"
|
||||
<< " \"" << to << "\" : " << m.message << "()" << std::endl;
|
||||
<< common::generators::plantuml::to_plantuml(message_t::kCall) << " \""
|
||||
<< to << "\" : " << m.message << "()" << std::endl;
|
||||
}
|
||||
|
||||
void generator::generate_return(const message &m, std::ostream &ostr) const
|
||||
@@ -76,7 +61,7 @@ void generator::generate_return(const message &m, std::ostream &ostr) const
|
||||
const auto to = ns_relative(m_config.using_namespace(), m.to);
|
||||
|
||||
ostr << '"' << to << "\" "
|
||||
<< "-->"
|
||||
<< common::generators::plantuml::to_plantuml(message_t::kReturn)
|
||||
<< " \"" << from << "\"" << std::endl;
|
||||
}
|
||||
}
|
||||
@@ -98,8 +83,7 @@ void generator::generate(std::ostream &ostr) const
|
||||
{
|
||||
ostr << "@startuml" << std::endl;
|
||||
|
||||
for (const auto &b : m_config.puml().before)
|
||||
ostr << b << std::endl;
|
||||
generate_plantuml_directives(ostr, m_config.puml().before);
|
||||
|
||||
for (const auto &sf : m_config.start_from()) {
|
||||
if (sf.location_type == source_location::location_t::function) {
|
||||
@@ -117,47 +101,10 @@ void generator::generate(std::ostream &ostr) const
|
||||
continue;
|
||||
}
|
||||
}
|
||||
for (const auto &a : m_config.puml().after)
|
||||
ostr << a << std::endl;
|
||||
|
||||
generate_plantuml_directives(ostr, m_config.puml().after);
|
||||
|
||||
ostr << "@enduml" << std::endl;
|
||||
}
|
||||
|
||||
std::ostream &operator<<(std::ostream &os, const generator &g)
|
||||
{
|
||||
g.generate(os);
|
||||
return os;
|
||||
}
|
||||
|
||||
clanguml::sequence_diagram::model::diagram generate(
|
||||
cppast::libclang_compilation_database &db, const std::string &name,
|
||||
clanguml::config::sequence_diagram &diagram)
|
||||
{
|
||||
spdlog::info("Generating diagram {}.puml", name);
|
||||
clanguml::sequence_diagram::model::diagram d;
|
||||
d.name = name;
|
||||
|
||||
cppast::cpp_entity_index idx;
|
||||
cppast::simple_file_parser<cppast::libclang_parser> parser{
|
||||
type_safe::ref(idx)};
|
||||
|
||||
clanguml::sequence_diagram::visitor::translation_unit_visitor visitor(
|
||||
idx, d, diagram);
|
||||
|
||||
// Get all translation units matching the glob from diagram
|
||||
// configuration
|
||||
std::vector<std::string> translation_units{};
|
||||
for (const auto &g : diagram.glob()) {
|
||||
spdlog::debug("Processing glob: {}", g);
|
||||
const auto matches = glob::rglob(g);
|
||||
std::copy(matches.begin(), matches.end(),
|
||||
std::back_inserter(translation_units));
|
||||
}
|
||||
cppast::parse_files(parser, translation_units, db);
|
||||
|
||||
for (auto &file : parser.files())
|
||||
visitor(file);
|
||||
|
||||
return d;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "common/generators/plantuml/generator.h"
|
||||
#include "config/config.h"
|
||||
#include "cx/compilation_database.h"
|
||||
#include "sequence_diagram/model/diagram.h"
|
||||
@@ -36,13 +37,16 @@ namespace sequence_diagram {
|
||||
namespace generators {
|
||||
namespace plantuml {
|
||||
|
||||
using diagram_config = clanguml::config::sequence_diagram;
|
||||
using diagram_model = clanguml::sequence_diagram::model::diagram;
|
||||
|
||||
class generator {
|
||||
public:
|
||||
generator(clanguml::config::sequence_diagram &config, diagram_model &model);
|
||||
template <typename C, typename D>
|
||||
using common_generator =
|
||||
clanguml::common::generators::plantuml::generator<C, D>;
|
||||
|
||||
std::string to_string(clanguml::sequence_diagram::model::message_t r) const;
|
||||
class generator : public common_generator<diagram_config, diagram_model> {
|
||||
public:
|
||||
generator(diagram_config &config, diagram_model &model);
|
||||
|
||||
void generate_call(const clanguml::sequence_diagram::model::message &m,
|
||||
std::ostream &ostr) const;
|
||||
@@ -54,18 +58,8 @@ public:
|
||||
std::ostream &ostr) const;
|
||||
|
||||
void generate(std::ostream &ostr) const;
|
||||
|
||||
friend std::ostream &operator<<(std::ostream &os, const generator &g);
|
||||
|
||||
private:
|
||||
clanguml::config::sequence_diagram &m_config;
|
||||
clanguml::sequence_diagram::model::diagram &m_model;
|
||||
};
|
||||
|
||||
clanguml::sequence_diagram::model::diagram generate(
|
||||
cppast::libclang_compilation_database &db, const std::string &name,
|
||||
clanguml::config::sequence_diagram &diagram);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,4 +29,9 @@
|
||||
|
||||
namespace clanguml::sequence_diagram::model {
|
||||
|
||||
std::string diagram::to_alias(const std::string &full_name) const
|
||||
{
|
||||
return full_name;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,15 +18,18 @@
|
||||
#pragma once
|
||||
|
||||
#include "activity.h"
|
||||
#include "common/model/diagram.h"
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
namespace clanguml::sequence_diagram::model {
|
||||
|
||||
struct diagram {
|
||||
class diagram : public clanguml::common::model::diagram {
|
||||
public:
|
||||
std::string to_alias(const std::string &full_name) const;
|
||||
|
||||
bool started{false};
|
||||
std::string name;
|
||||
|
||||
std::map<std::uint_least64_t, activity> sequences;
|
||||
};
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "enums.h"
|
||||
#include "common/model/enums.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@@ -25,7 +25,7 @@
|
||||
namespace clanguml::sequence_diagram::model {
|
||||
|
||||
struct message {
|
||||
message_t type;
|
||||
common::model::message_t type;
|
||||
std::string from;
|
||||
std::uint_least64_t from_usr;
|
||||
std::string to;
|
||||
|
||||
@@ -36,10 +36,10 @@ translation_unit_visitor::translation_unit_visitor(
|
||||
|
||||
void translation_unit_visitor::process_activities(const cppast::cpp_function &e)
|
||||
{
|
||||
using clanguml::common::model::message_t;
|
||||
using clanguml::sequence_diagram::model::activity;
|
||||
using clanguml::sequence_diagram::model::diagram;
|
||||
using clanguml::sequence_diagram::model::message;
|
||||
using clanguml::sequence_diagram::model::message_t;
|
||||
using cppast::cpp_entity;
|
||||
using cppast::cpp_entity_kind;
|
||||
using cppast::cpp_function;
|
||||
|
||||
18
tests/t00035/.clang-uml
Normal file
@@ -0,0 +1,18 @@
|
||||
compilation_database_dir: ..
|
||||
output_directory: puml
|
||||
diagrams:
|
||||
t00035_class:
|
||||
type: class
|
||||
glob:
|
||||
- ../../tests/t00035/t00035.cc
|
||||
using_namespace:
|
||||
- clanguml::t00035
|
||||
include:
|
||||
namespaces:
|
||||
- clanguml::t00035
|
||||
layout:
|
||||
Center:
|
||||
- up: Top
|
||||
- down: Bottom
|
||||
- left: Left
|
||||
- right: Right
|
||||
20
tests/t00035/t00035.cc
Normal file
@@ -0,0 +1,20 @@
|
||||
namespace clanguml {
|
||||
namespace t00035 {
|
||||
|
||||
struct Top {
|
||||
};
|
||||
|
||||
struct Left {
|
||||
};
|
||||
|
||||
struct Center {
|
||||
};
|
||||
|
||||
struct Bottom {
|
||||
};
|
||||
|
||||
struct Right {
|
||||
};
|
||||
|
||||
} // namespace t00035
|
||||
} // namespace clanguml
|
||||
52
tests/t00035/test_case.h
Normal file
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* tests/t00035/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("t00035", "[test-case][class]")
|
||||
{
|
||||
auto [config, db] = load_config("t00035");
|
||||
|
||||
auto diagram = config.diagrams["t00035_class"];
|
||||
|
||||
REQUIRE(diagram->name == "t00035_class");
|
||||
|
||||
REQUIRE(diagram->should_include("clanguml::t00035::A"));
|
||||
|
||||
auto model = generate_class_diagram(db, diagram);
|
||||
|
||||
REQUIRE(model.name() == "t00035_class");
|
||||
|
||||
auto puml = generate_class_puml(diagram, model);
|
||||
AliasMatcher _A(puml);
|
||||
|
||||
REQUIRE_THAT(puml, StartsWith("@startuml"));
|
||||
REQUIRE_THAT(puml, EndsWith("@enduml\n"));
|
||||
|
||||
REQUIRE_THAT(puml, IsClass(_A("Top")));
|
||||
REQUIRE_THAT(puml, IsClass(_A("Bottom")));
|
||||
REQUIRE_THAT(puml, IsClass(_A("Center")));
|
||||
REQUIRE_THAT(puml, IsClass(_A("Left")));
|
||||
REQUIRE_THAT(puml, IsClass(_A("Right")));
|
||||
|
||||
REQUIRE_THAT(puml, IsLayoutHint(_A("Center"), "up", _A("Top")));
|
||||
REQUIRE_THAT(puml, IsLayoutHint(_A("Center"), "left", _A("Left")));
|
||||
REQUIRE_THAT(puml, IsLayoutHint(_A("Center"), "right", _A("Right")));
|
||||
REQUIRE_THAT(puml, IsLayoutHint(_A("Center"), "down", _A("Bottom")));
|
||||
|
||||
save_puml(
|
||||
"./" + config.output_directory() + "/" + diagram->name + ".puml", puml);
|
||||
}
|
||||
@@ -30,7 +30,7 @@ TEST_CASE("t20001", "[test-case][sequence]")
|
||||
|
||||
auto model = generate_sequence_diagram(db, diagram);
|
||||
|
||||
REQUIRE(model.name == "t20001_sequence");
|
||||
REQUIRE(model.name() == "t20001_sequence");
|
||||
|
||||
auto puml = generate_sequence_puml(diagram, model);
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ TEST_CASE("t20002", "[test-case][sequence]")
|
||||
|
||||
auto model = generate_sequence_diagram(db, diagram);
|
||||
|
||||
REQUIRE(model.name == "t20002_sequence");
|
||||
REQUIRE(model.name() == "t20002_sequence");
|
||||
|
||||
auto puml = generate_sequence_puml(diagram, model);
|
||||
|
||||
|
||||
19
tests/t30007/.clang-uml
Normal file
@@ -0,0 +1,19 @@
|
||||
compilation_database_dir: ..
|
||||
output_directory: puml
|
||||
diagrams:
|
||||
t30007_package:
|
||||
type: package
|
||||
glob:
|
||||
- ../../tests/t30007/t30007.cc
|
||||
include:
|
||||
namespaces:
|
||||
- clanguml::t30007
|
||||
using_namespace:
|
||||
- clanguml::t30007
|
||||
layout:
|
||||
C:
|
||||
- up: 'A::AA'
|
||||
- left: B
|
||||
plantuml:
|
||||
before:
|
||||
- "' t30007 test package diagram"
|
||||
33
tests/t30007/t30007.cc
Normal file
@@ -0,0 +1,33 @@
|
||||
namespace clanguml {
|
||||
namespace t30007 {
|
||||
|
||||
namespace B {
|
||||
struct BB {
|
||||
};
|
||||
}
|
||||
|
||||
/// \uml{note[top] Compare layout with t30006.}
|
||||
namespace A {
|
||||
namespace AA {
|
||||
struct A1 {
|
||||
B::BB *b;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
namespace C {
|
||||
struct CC {
|
||||
};
|
||||
}
|
||||
|
||||
/// \uml{note[bottom] Bottom A note.}
|
||||
namespace A {
|
||||
namespace AA {
|
||||
struct A2 {
|
||||
C::CC *c;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
53
tests/t30007/test_case.h
Normal file
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* tests/t30007/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("t30007", "[test-case][package]")
|
||||
{
|
||||
auto [config, db] = load_config("t30007");
|
||||
|
||||
auto diagram = config.diagrams["t30007_package"];
|
||||
|
||||
REQUIRE(diagram->should_include("clanguml::t30007::A"));
|
||||
REQUIRE(diagram->should_include("clanguml::t30007::C"));
|
||||
REQUIRE(!diagram->should_include("std::vector"));
|
||||
|
||||
REQUIRE(diagram->name == "t30007_package");
|
||||
|
||||
auto model = generate_package_diagram(db, diagram);
|
||||
|
||||
REQUIRE(model.name() == "t30007_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, IsPackage("A"));
|
||||
REQUIRE_THAT(puml, IsPackage("B"));
|
||||
REQUIRE_THAT(puml, IsPackage("C"));
|
||||
|
||||
REQUIRE_THAT(puml, IsDependency(_A("AA"), _A("B")));
|
||||
REQUIRE_THAT(puml, IsDependency(_A("AA"), _A("C")));
|
||||
|
||||
REQUIRE_THAT(puml, IsLayoutHint(_A("C"), "up", _A("AA")));
|
||||
REQUIRE_THAT(puml, IsLayoutHint(_A("C"), "left", _A("B")));
|
||||
|
||||
save_puml(
|
||||
"./" + config.output_directory() + "/" + diagram->name + ".puml", puml);
|
||||
}
|
||||
@@ -16,6 +16,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
#include "test_cases.h"
|
||||
#include "common/generators/plantuml/generator.h"
|
||||
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
@@ -44,36 +45,48 @@ clanguml::sequence_diagram::model::diagram generate_sequence_diagram(
|
||||
cppast::libclang_compilation_database &db,
|
||||
std::shared_ptr<clanguml::config::diagram> diagram)
|
||||
{
|
||||
auto diagram_model =
|
||||
clanguml::sequence_diagram::generators::plantuml::generate(db,
|
||||
diagram->name,
|
||||
dynamic_cast<clanguml::config::sequence_diagram &>(*diagram));
|
||||
using diagram_config = clanguml::config::sequence_diagram;
|
||||
using diagram_model = clanguml::sequence_diagram::model::diagram;
|
||||
using diagram_visitor =
|
||||
clanguml::sequence_diagram::visitor::translation_unit_visitor;
|
||||
|
||||
return diagram_model;
|
||||
auto model = clanguml::common::generators::plantuml::generate<diagram_model,
|
||||
diagram_config, diagram_visitor>(db, diagram->name,
|
||||
dynamic_cast<clanguml::config::sequence_diagram &>(*diagram));
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
clanguml::class_diagram::model::diagram generate_class_diagram(
|
||||
cppast::libclang_compilation_database &db,
|
||||
std::shared_ptr<clanguml::config::diagram> diagram)
|
||||
{
|
||||
auto diagram_model =
|
||||
clanguml::class_diagram::generators::plantuml::generate(db,
|
||||
diagram->name,
|
||||
dynamic_cast<clanguml::config::class_diagram &>(*diagram));
|
||||
using diagram_config = clanguml::config::class_diagram;
|
||||
using diagram_model = clanguml::class_diagram::model::diagram;
|
||||
using diagram_visitor =
|
||||
clanguml::class_diagram::visitor::translation_unit_visitor;
|
||||
|
||||
return diagram_model;
|
||||
auto model = clanguml::common::generators::plantuml::generate<diagram_model,
|
||||
diagram_config, diagram_visitor>(
|
||||
db, diagram->name, dynamic_cast<diagram_config &>(*diagram));
|
||||
|
||||
return 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));
|
||||
using diagram_config = clanguml::config::package_diagram;
|
||||
using diagram_model = clanguml::package_diagram::model::diagram;
|
||||
using diagram_visitor =
|
||||
clanguml::package_diagram::visitor::translation_unit_visitor;
|
||||
|
||||
return diagram_model;
|
||||
auto model = clanguml::common::generators::plantuml::generate<diagram_model,
|
||||
diagram_config, diagram_visitor>(
|
||||
db, diagram->name, dynamic_cast<diagram_config &>(*diagram));
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
std::string generate_sequence_puml(
|
||||
@@ -166,6 +179,7 @@ using namespace clanguml::test::matchers;
|
||||
#include "t00032/test_case.h"
|
||||
#include "t00033/test_case.h"
|
||||
#include "t00034/test_case.h"
|
||||
#include "t00035/test_case.h"
|
||||
|
||||
//
|
||||
// Sequence diagram tests
|
||||
@@ -182,6 +196,7 @@ using namespace clanguml::test::matchers;
|
||||
#include "t30004/test_case.h"
|
||||
#include "t30005/test_case.h"
|
||||
#include "t30006/test_case.h"
|
||||
#include "t30007/test_case.h"
|
||||
|
||||
//
|
||||
// Other tests (e.g. configuration file)
|
||||
|
||||
@@ -351,6 +351,14 @@ ContainsMatcher IsDependency(std::string const &from, std::string const &to,
|
||||
CasedString(fmt::format("{} ..> {}", from, to), caseSensitivity));
|
||||
}
|
||||
|
||||
ContainsMatcher IsLayoutHint(std::string const &from, std::string const &hint,
|
||||
std::string const &to,
|
||||
CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes)
|
||||
{
|
||||
return ContainsMatcher(CasedString(
|
||||
fmt::format("{} -[hidden]{}- {}", from, hint, to), caseSensitivity));
|
||||
}
|
||||
|
||||
ContainsMatcher HasNote(std::string const &cls, std::string const &position,
|
||||
std::string const ¬e,
|
||||
CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes)
|
||||
|
||||
@@ -99,6 +99,9 @@ test_cases:
|
||||
- name: t00034
|
||||
title: Template metaprogramming type function test case
|
||||
description:
|
||||
- name: t00035
|
||||
title: PlantUML class diagram layout hints test case
|
||||
description:
|
||||
Sequence diagrams:
|
||||
- name: t20001
|
||||
title: Basic sequence diagram test case
|
||||
@@ -125,6 +128,9 @@ test_cases:
|
||||
- name: t30006
|
||||
title: Package split namespace test case
|
||||
description:
|
||||
- name: t30007
|
||||
title: Package diagram layout hints test case
|
||||
description:
|
||||
Configuration diagrams:
|
||||
- name: t90000
|
||||
title: Basic config test
|
||||
|
||||
@@ -77,4 +77,45 @@ TEST_CASE("Test config includes", "[unit-test]")
|
||||
CHECK(cus.include_relations_also_as_members());
|
||||
CHECK(cus.generate_method_arguments() ==
|
||||
clanguml::config::method_arguments::none);
|
||||
}
|
||||
|
||||
TEST_CASE("Test config layout", "[unit-test]")
|
||||
{
|
||||
auto cfg = clanguml::config::load("./test_config_data/layout.yml");
|
||||
|
||||
CHECK(cfg.diagrams.size() == 2);
|
||||
|
||||
auto &def = static_cast<clanguml::config::class_diagram &>(
|
||||
*cfg.diagrams["class_main"]);
|
||||
|
||||
auto check_layout = [](const auto &diagram,
|
||||
const clanguml::config::diagram_type type) {
|
||||
CHECK(diagram.type() == type);
|
||||
|
||||
CHECK(diagram.layout().at("ABCD").size() == 2);
|
||||
CHECK(diagram.layout().at("ABCD")[0].hint ==
|
||||
clanguml::config::hint_t::up);
|
||||
CHECK(diagram.layout().at("ABCD")[0].entity == "ABCD_SUBCLASS");
|
||||
CHECK(diagram.layout().at("ABCD")[1].hint ==
|
||||
clanguml::config::hint_t::left);
|
||||
CHECK(diagram.layout().at("ABCD")[1].entity == "ABCD_SIBLING");
|
||||
|
||||
CHECK(diagram.layout().at("ABCD_SIBLING").size() == 2);
|
||||
CHECK(diagram.layout().at("ABCD_SIBLING")[0].hint ==
|
||||
clanguml::config::hint_t::right);
|
||||
CHECK(diagram.layout().at("ABCD_SIBLING")[0].entity ==
|
||||
"ABCD_OTHER_SIBLING");
|
||||
CHECK(diagram.layout().at("ABCD_SIBLING")[1].hint ==
|
||||
clanguml::config::hint_t::down);
|
||||
CHECK(diagram.layout().at("ABCD_SIBLING")[1].entity ==
|
||||
"ABCD_SIBLING_SIBLING");
|
||||
};
|
||||
|
||||
check_layout(static_cast<clanguml::config::class_diagram &>(
|
||||
*cfg.diagrams["class_main"]),
|
||||
clanguml::config::diagram_type::class_diagram);
|
||||
|
||||
check_layout(static_cast<clanguml::config::package_diagram &>(
|
||||
*cfg.diagrams["package_main"]),
|
||||
clanguml::config::diagram_type::package_diagram);
|
||||
}
|
||||
37
tests/test_config_data/layout.yml
Normal file
@@ -0,0 +1,37 @@
|
||||
compilation_database_dir: debug
|
||||
output_directory: output
|
||||
diagrams:
|
||||
class_main:
|
||||
type: class
|
||||
glob:
|
||||
- src/**/*.cc
|
||||
- src/**/*.h
|
||||
using_namespace:
|
||||
- clanguml
|
||||
generate_method_arguments: full
|
||||
layout:
|
||||
ABCD:
|
||||
- up: ABCD_SUBCLASS
|
||||
- left: ABCD_SIBLING
|
||||
ABCD_SIBLING:
|
||||
- right: ABCD_OTHER_SIBLING
|
||||
- down: ABCD_SIBLING_SIBLING
|
||||
include:
|
||||
namespaces:
|
||||
- clanguml
|
||||
- ABCD
|
||||
package_main:
|
||||
type: package
|
||||
glob:
|
||||
- src/**/*.cc
|
||||
- src/**/*.h
|
||||
using_namespace:
|
||||
- clanguml
|
||||
generate_method_arguments: full
|
||||
layout:
|
||||
ABCD:
|
||||
- up: ABCD_SUBCLASS
|
||||
- left: ABCD_SIBLING
|
||||
ABCD_SIBLING:
|
||||
- right: ABCD_OTHER_SIBLING
|
||||
- down: ABCD_SIBLING_SIBLING
|
||||
@@ -1,10 +1,14 @@
|
||||
type: class
|
||||
include_relations_also_as_members: false
|
||||
generate_method_arguments: none
|
||||
glob:
|
||||
- src/common/model/*.h
|
||||
- src/common/model/*.cc
|
||||
- src/class_diagram/model/*.h
|
||||
- src/class_diagram/model/*.cc
|
||||
include:
|
||||
namespaces:
|
||||
- clanguml::common::model
|
||||
- clanguml::class_diagram::model
|
||||
using_namespace:
|
||||
- clanguml::class_diagram::model
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
type: class
|
||||
include_relations_also_as_members: false
|
||||
generate_method_arguments: none
|
||||
glob:
|
||||
- src/common/model/*.h
|
||||
- src/common/model/*.cc
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
type: class
|
||||
include_relations_also_as_members: false
|
||||
generate_method_arguments: none
|
||||
glob:
|
||||
- src/common/model/*.h
|
||||
- src/common/model/*.cc
|
||||
- src/package_diagram/model/*.h
|
||||
- src/package_diagram/model/*.cc
|
||||
include:
|
||||
namespaces:
|
||||
- clanguml::common::model
|
||||
- clanguml::package_diagram::model
|
||||
using_namespace:
|
||||
- clanguml::package_diagram::model
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
type: class
|
||||
include_relations_also_as_members: false
|
||||
generate_method_arguments: none
|
||||
glob:
|
||||
- src/common/model/*.h
|
||||
- src/common/model/*.cc
|
||||
- src/sequence_diagram/model/*.h
|
||||
- src/sequence_diagram/model/*.cc
|
||||
include:
|
||||
namespaces:
|
||||
- clanguml::common::model
|
||||
- clanguml::sequence_diagram::model
|
||||
using_namespace:
|
||||
- clanguml::sequence_diagram::model
|
||||
|
||||