Merge pull request #5 from bkryza/add-first-version-of-inline-directive-support
Add first version of inline directive support
This commit is contained in:
@@ -26,6 +26,10 @@
|
|||||||
* [t00025](./test_cases/t00025.md) - Template proxy pattern
|
* [t00025](./test_cases/t00025.md) - Template proxy pattern
|
||||||
* [t00026](./test_cases/t00026.md) - Template memento pattern
|
* [t00026](./test_cases/t00026.md) - Template memento pattern
|
||||||
* [t00027](./test_cases/t00027.md) - Template decorator pattern
|
* [t00027](./test_cases/t00027.md) - Template decorator pattern
|
||||||
|
* [t00028](./test_cases/t00028.md) - PlantUML note decorator test case
|
||||||
|
* [t00029](./test_cases/t00029.md) - PlantUML skip decorator test case
|
||||||
|
* [t00030](./test_cases/t00030.md) - PlantUML relationship decorators test case
|
||||||
|
* [t00031](./test_cases/t00031.md) - PlantUML style decorator test case
|
||||||
## Sequence diagrams
|
## Sequence diagrams
|
||||||
* [t20001](./test_cases/t20001.md) - Basic sequence diagram
|
* [t20001](./test_cases/t20001.md) - Basic sequence diagram
|
||||||
## Configuration diagrams
|
## Configuration diagrams
|
||||||
|
|||||||
80
docs/test_cases/t00028.md
Normal file
80
docs/test_cases/t00028.md
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
# t00028 - PlantUML note decorator test case
|
||||||
|
## Config
|
||||||
|
```yaml
|
||||||
|
compilation_database_dir: ..
|
||||||
|
output_directory: puml
|
||||||
|
diagrams:
|
||||||
|
t00028_class:
|
||||||
|
type: class
|
||||||
|
glob:
|
||||||
|
- ../../tests/t00028/t00028.cc
|
||||||
|
using_namespace:
|
||||||
|
- clanguml::t00028
|
||||||
|
include:
|
||||||
|
namespaces:
|
||||||
|
- clanguml::t00028
|
||||||
|
|
||||||
|
```
|
||||||
|
## Source code
|
||||||
|
File t00028.cc
|
||||||
|
```cpp
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace clanguml {
|
||||||
|
namespace t00028 {
|
||||||
|
|
||||||
|
/// \uml{note[top] A class note.}
|
||||||
|
class A {
|
||||||
|
};
|
||||||
|
|
||||||
|
/// \uml{note[] B class note.}
|
||||||
|
class B {
|
||||||
|
};
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @uml{note:t00028_class[bottom] C class note.}
|
||||||
|
/// This is class C.
|
||||||
|
class C {
|
||||||
|
};
|
||||||
|
|
||||||
|
/// \uml{note
|
||||||
|
/// D
|
||||||
|
/// class
|
||||||
|
/// note.}
|
||||||
|
class D {
|
||||||
|
};
|
||||||
|
|
||||||
|
/// \uml{note E template class note.}
|
||||||
|
template <typename T> class E {
|
||||||
|
T param;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// \uml{note:other_diagram[left] G class note.}
|
||||||
|
class G {
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @uml{note[ bottom ] F enum note.}
|
||||||
|
enum class F { one, two, three };
|
||||||
|
|
||||||
|
/// \uml{note[right] R class note.}
|
||||||
|
class R {
|
||||||
|
A aaa;
|
||||||
|
|
||||||
|
B *bbb;
|
||||||
|
|
||||||
|
C &ccc;
|
||||||
|
|
||||||
|
std::vector<std::shared_ptr<D>> ddd;
|
||||||
|
|
||||||
|
E<int> eee;
|
||||||
|
|
||||||
|
G **ggg;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace t00028
|
||||||
|
} // namespace clanguml
|
||||||
|
|
||||||
|
```
|
||||||
|
## Generated UML diagrams
|
||||||
|

|
||||||
BIN
docs/test_cases/t00028_class.png
Normal file
BIN
docs/test_cases/t00028_class.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 41 KiB |
77
docs/test_cases/t00029.md
Normal file
77
docs/test_cases/t00029.md
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
# t00029 - PlantUML skip decorator test case
|
||||||
|
## Config
|
||||||
|
```yaml
|
||||||
|
compilation_database_dir: ..
|
||||||
|
output_directory: puml
|
||||||
|
diagrams:
|
||||||
|
t00029_class:
|
||||||
|
type: class
|
||||||
|
glob:
|
||||||
|
- ../../tests/t00029/t00029.cc
|
||||||
|
using_namespace:
|
||||||
|
- clanguml::t00029
|
||||||
|
include:
|
||||||
|
namespaces:
|
||||||
|
- clanguml::t00029
|
||||||
|
|
||||||
|
```
|
||||||
|
## Source code
|
||||||
|
File t00029.cc
|
||||||
|
```cpp
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace clanguml {
|
||||||
|
namespace t00029 {
|
||||||
|
|
||||||
|
class A {
|
||||||
|
};
|
||||||
|
|
||||||
|
/// \uml{skip}
|
||||||
|
class B {
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T> class C {
|
||||||
|
T param;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @uml{skip:t00029_class}
|
||||||
|
template <typename T> class D {
|
||||||
|
T param;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class E { one, two, three };
|
||||||
|
|
||||||
|
/// \uml{skip}
|
||||||
|
enum class F { red, green, blue };
|
||||||
|
|
||||||
|
class G1 {
|
||||||
|
};
|
||||||
|
|
||||||
|
class G2 {
|
||||||
|
};
|
||||||
|
|
||||||
|
class G3 {
|
||||||
|
};
|
||||||
|
|
||||||
|
class G4 {
|
||||||
|
};
|
||||||
|
|
||||||
|
struct R {
|
||||||
|
G1 g1;
|
||||||
|
|
||||||
|
/// \uml{skip}
|
||||||
|
G2 g2;
|
||||||
|
|
||||||
|
/// \uml{skiprelationship}
|
||||||
|
G3 &g3;
|
||||||
|
|
||||||
|
std::shared_ptr<G4> g4;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace t00029
|
||||||
|
} // namespace clanguml
|
||||||
|
|
||||||
|
```
|
||||||
|
## Generated UML diagrams
|
||||||
|

|
||||||
BIN
docs/test_cases/t00029_class.png
Normal file
BIN
docs/test_cases/t00029_class.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
58
docs/test_cases/t00030.md
Normal file
58
docs/test_cases/t00030.md
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
# t00030 - PlantUML relationship decorators test case
|
||||||
|
## Config
|
||||||
|
```yaml
|
||||||
|
compilation_database_dir: ..
|
||||||
|
output_directory: puml
|
||||||
|
diagrams:
|
||||||
|
t00030_class:
|
||||||
|
type: class
|
||||||
|
glob:
|
||||||
|
- ../../tests/t00030/t00030.cc
|
||||||
|
using_namespace:
|
||||||
|
- clanguml::t00030
|
||||||
|
include:
|
||||||
|
namespaces:
|
||||||
|
- clanguml::t00030
|
||||||
|
|
||||||
|
```
|
||||||
|
## Source code
|
||||||
|
File t00030.cc
|
||||||
|
```cpp
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace clanguml {
|
||||||
|
namespace t00030 {
|
||||||
|
|
||||||
|
class A {
|
||||||
|
};
|
||||||
|
|
||||||
|
class B {
|
||||||
|
};
|
||||||
|
|
||||||
|
class C {
|
||||||
|
};
|
||||||
|
|
||||||
|
class D {
|
||||||
|
};
|
||||||
|
|
||||||
|
struct R {
|
||||||
|
/// @uml{association[]}
|
||||||
|
A aaa;
|
||||||
|
|
||||||
|
/// @uml{composition[0..1:1..*]}
|
||||||
|
std::vector<B> bbb;
|
||||||
|
|
||||||
|
/// @uml{aggregation[0..1:1..5]}
|
||||||
|
std::vector<C> ccc;
|
||||||
|
|
||||||
|
/// @uml{association[:1]}
|
||||||
|
D ddd;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace t00030
|
||||||
|
} // namespace clanguml
|
||||||
|
|
||||||
|
```
|
||||||
|
## Generated UML diagrams
|
||||||
|

|
||||||
BIN
docs/test_cases/t00030_class.png
Normal file
BIN
docs/test_cases/t00030_class.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
62
docs/test_cases/t00031.md
Normal file
62
docs/test_cases/t00031.md
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
# t00031 - PlantUML style decorator test case
|
||||||
|
## Config
|
||||||
|
```yaml
|
||||||
|
compilation_database_dir: ..
|
||||||
|
output_directory: puml
|
||||||
|
diagrams:
|
||||||
|
t00031_class:
|
||||||
|
type: class
|
||||||
|
glob:
|
||||||
|
- ../../tests/t00031/t00031.cc
|
||||||
|
using_namespace:
|
||||||
|
- clanguml::t00031
|
||||||
|
include:
|
||||||
|
namespaces:
|
||||||
|
- clanguml::t00031
|
||||||
|
|
||||||
|
```
|
||||||
|
## Source code
|
||||||
|
File t00031.cc
|
||||||
|
```cpp
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace clanguml {
|
||||||
|
namespace t00031 {
|
||||||
|
|
||||||
|
/// @uml{style[#back:lightgreen|yellow;header:blue/red]}
|
||||||
|
class A {
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @uml{style[#line.dotted:blue]}
|
||||||
|
enum B { one, two, three };
|
||||||
|
|
||||||
|
/// @uml{style[#pink;line:red;line.bold;text:red]}
|
||||||
|
template <typename T> class C {
|
||||||
|
T ttt;
|
||||||
|
};
|
||||||
|
|
||||||
|
class D {
|
||||||
|
};
|
||||||
|
|
||||||
|
struct R {
|
||||||
|
/// @uml{style[#red,dashed,thickness=2]}
|
||||||
|
A *aaa;
|
||||||
|
|
||||||
|
/// @uml{composition}
|
||||||
|
/// @uml{style[#green,dashed,thickness=4]}
|
||||||
|
std::vector<B> bbb;
|
||||||
|
|
||||||
|
/// @uml{style[#blue,dotted,thickness=8]}
|
||||||
|
C<int> ccc;
|
||||||
|
|
||||||
|
/// @uml{style[#blue,plain,thickness=16]}
|
||||||
|
D *ddd;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace t00031
|
||||||
|
} // namespace clanguml
|
||||||
|
|
||||||
|
```
|
||||||
|
## Generated UML diagrams
|
||||||
|

|
||||||
BIN
docs/test_cases/t00031_class.png
Normal file
BIN
docs/test_cases/t00031_class.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 19 KiB |
@@ -75,24 +75,24 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string to_string(relationship_t r) const
|
std::string to_string(relationship_t r, std::string style = "") const
|
||||||
{
|
{
|
||||||
switch (r) {
|
switch (r) {
|
||||||
case relationship_t::kOwnership:
|
case relationship_t::kOwnership:
|
||||||
case relationship_t::kComposition:
|
case relationship_t::kComposition:
|
||||||
return "*--";
|
return style.empty() ? "*--" : fmt::format("*-[{}]-", style);
|
||||||
case relationship_t::kAggregation:
|
case relationship_t::kAggregation:
|
||||||
return "o--";
|
return style.empty() ? "o--" : fmt::format("o-[{}]-", style);
|
||||||
case relationship_t::kContainment:
|
case relationship_t::kContainment:
|
||||||
return "--+";
|
return style.empty() ? "--+" : fmt::format("-[{}]-+", style);
|
||||||
case relationship_t::kAssociation:
|
case relationship_t::kAssociation:
|
||||||
return "-->";
|
return style.empty() ? "-->" : fmt::format("-[{}]->", style);
|
||||||
case relationship_t::kInstantiation:
|
case relationship_t::kInstantiation:
|
||||||
return "..|>";
|
return style.empty() ? "..|>" : fmt::format(".[{}].|>", style);
|
||||||
case relationship_t::kFriendship:
|
case relationship_t::kFriendship:
|
||||||
return "<..";
|
return style.empty() ? "<.." : fmt::format("<.[{}].", style);
|
||||||
case relationship_t::kDependency:
|
case relationship_t::kDependency:
|
||||||
return "..>";
|
return style.empty() ? "..>" : fmt::format(".[{}].>", style);
|
||||||
default:
|
default:
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
@@ -129,7 +129,7 @@ public:
|
|||||||
|
|
||||||
ostr << class_type << " \"" << c.full_name(m_config.using_namespace);
|
ostr << class_type << " \"" << c.full_name(m_config.using_namespace);
|
||||||
|
|
||||||
ostr << "\" as " << c.alias() << std::endl;
|
ostr << "\" as " << c.alias() << '\n';
|
||||||
}
|
}
|
||||||
|
|
||||||
void generate_alias(const enum_ &e, std::ostream &ostr) const
|
void generate_alias(const enum_ &e, std::ostream &ostr) const
|
||||||
@@ -137,7 +137,7 @@ public:
|
|||||||
ostr << "enum"
|
ostr << "enum"
|
||||||
<< " \"" << e.full_name(m_config.using_namespace);
|
<< " \"" << e.full_name(m_config.using_namespace);
|
||||||
|
|
||||||
ostr << "\" as " << e.alias() << std::endl;
|
ostr << "\" as " << e.alias() << '\n';
|
||||||
}
|
}
|
||||||
|
|
||||||
void generate(const class_ &c, std::ostream &ostr) const
|
void generate(const class_ &c, std::ostream &ostr) const
|
||||||
@@ -149,7 +149,12 @@ public:
|
|||||||
if (c.is_abstract())
|
if (c.is_abstract())
|
||||||
class_type = "abstract";
|
class_type = "abstract";
|
||||||
|
|
||||||
ostr << class_type << " " << c.alias() << " {" << std::endl;
|
ostr << class_type << " " << c.alias();
|
||||||
|
|
||||||
|
if (!c.style.empty())
|
||||||
|
ostr << " " << c.style;
|
||||||
|
|
||||||
|
ostr << " {" << '\n';
|
||||||
|
|
||||||
//
|
//
|
||||||
// Process methods
|
// Process methods
|
||||||
@@ -192,7 +197,7 @@ public:
|
|||||||
|
|
||||||
ostr << " : " << ns_relative(uns, type);
|
ostr << " : " << ns_relative(uns, type);
|
||||||
|
|
||||||
ostr << std::endl;
|
ostr << '\n';
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -224,9 +229,18 @@ public:
|
|||||||
destination = r.destination;
|
destination = r.destination;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string puml_relation;
|
||||||
|
if (!r.multiplicity_source.empty())
|
||||||
|
puml_relation += "\"" + r.multiplicity_source + "\" ";
|
||||||
|
|
||||||
|
puml_relation += to_string(r.type, r.style);
|
||||||
|
|
||||||
|
if (!r.multiplicity_destination.empty())
|
||||||
|
puml_relation += " \"" + r.multiplicity_destination + "\"";
|
||||||
|
|
||||||
relstr << m_model.to_alias(
|
relstr << m_model.to_alias(
|
||||||
uns, ns_relative(uns, c.full_name(uns)))
|
uns, ns_relative(uns, c.full_name(uns)))
|
||||||
<< " " << to_string(r.type) << " "
|
<< " " << puml_relation << " "
|
||||||
<< m_model.to_alias(uns, ns_relative(uns, destination));
|
<< m_model.to_alias(uns, ns_relative(uns, destination));
|
||||||
|
|
||||||
if (!r.label.empty()) {
|
if (!r.label.empty()) {
|
||||||
@@ -234,7 +248,7 @@ public:
|
|||||||
rendered_relations.emplace(r.label);
|
rendered_relations.emplace(r.label);
|
||||||
}
|
}
|
||||||
|
|
||||||
relstr << std::endl;
|
relstr << '\n';
|
||||||
|
|
||||||
all_relations_str << relstr.str();
|
all_relations_str << relstr.str();
|
||||||
}
|
}
|
||||||
@@ -260,10 +274,10 @@ public:
|
|||||||
ostr << "{static} ";
|
ostr << "{static} ";
|
||||||
|
|
||||||
ostr << to_string(m.scope) << m.name << " : "
|
ostr << to_string(m.scope) << m.name << " : "
|
||||||
<< ns_relative(uns, m.type) << std::endl;
|
<< ns_relative(uns, m.type) << '\n';
|
||||||
}
|
}
|
||||||
|
|
||||||
ostr << "}" << std::endl;
|
ostr << "}" << '\n';
|
||||||
|
|
||||||
if (m_config.should_include_relationship("inheritance"))
|
if (m_config.should_include_relationship("inheritance"))
|
||||||
for (const auto &b : c.bases) {
|
for (const auto &b : c.bases) {
|
||||||
@@ -273,7 +287,7 @@ public:
|
|||||||
<< " <|-- "
|
<< " <|-- "
|
||||||
<< m_model.to_alias(
|
<< m_model.to_alias(
|
||||||
uns, ns_relative(uns, c.full_name(uns)))
|
uns, ns_relative(uns, c.full_name(uns)))
|
||||||
<< std::endl;
|
<< '\n';
|
||||||
ostr << relstr.str();
|
ostr << relstr.str();
|
||||||
}
|
}
|
||||||
catch (error::uml_alias_missing &e) {
|
catch (error::uml_alias_missing &e) {
|
||||||
@@ -283,19 +297,36 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// 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";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Print relationships
|
// Print relationships
|
||||||
ostr << all_relations_str.str();
|
ostr << all_relations_str.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
void generate(const enum_ &e, std::ostream &ostr) const
|
void generate(const enum_ &e, std::ostream &ostr) const
|
||||||
{
|
{
|
||||||
ostr << "enum " << e.alias() << " {" << std::endl;
|
ostr << "enum " << e.alias();
|
||||||
|
|
||||||
|
if (!e.style.empty())
|
||||||
|
ostr << " " << e.style;
|
||||||
|
|
||||||
|
ostr << " {" << '\n';
|
||||||
|
|
||||||
for (const auto &enum_constant : e.constants) {
|
for (const auto &enum_constant : e.constants) {
|
||||||
ostr << enum_constant << std::endl;
|
ostr << enum_constant << '\n';
|
||||||
}
|
}
|
||||||
|
|
||||||
ostr << "}" << std::endl;
|
ostr << "}" << '\n';
|
||||||
|
|
||||||
for (const auto &r : e.relationships) {
|
for (const auto &r : e.relationships) {
|
||||||
if (!m_config.should_include_relationship(name(r.type)))
|
if (!m_config.should_include_relationship(name(r.type)))
|
||||||
@@ -327,7 +358,8 @@ public:
|
|||||||
if (!r.label.empty())
|
if (!r.label.empty())
|
||||||
relstr << " : " << r.label;
|
relstr << " : " << r.label;
|
||||||
|
|
||||||
relstr << std::endl;
|
relstr << '\n';
|
||||||
|
|
||||||
ostr << relstr.str();
|
ostr << relstr.str();
|
||||||
}
|
}
|
||||||
catch (error::uml_alias_missing &ex) {
|
catch (error::uml_alias_missing &ex) {
|
||||||
@@ -336,11 +368,23 @@ public:
|
|||||||
to_string(r.type), e.name, destination, ex.what());
|
to_string(r.type), e.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";
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void generate(std::ostream &ostr) const
|
void generate(std::ostream &ostr) const
|
||||||
{
|
{
|
||||||
ostr << "@startuml" << std::endl;
|
ostr << "@startuml" << '\n';
|
||||||
|
|
||||||
for (const auto &b : m_config.puml.before) {
|
for (const auto &b : m_config.puml.before) {
|
||||||
std::string note{b};
|
std::string note{b};
|
||||||
@@ -352,7 +396,7 @@ public:
|
|||||||
note.replace(
|
note.replace(
|
||||||
std::get<1>(alias_match), std::get<2>(alias_match), alias);
|
std::get<1>(alias_match), std::get<2>(alias_match), alias);
|
||||||
}
|
}
|
||||||
ostr << note << std::endl;
|
ostr << note << '\n';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_config.should_include_entities("classes")) {
|
if (m_config.should_include_entities("classes")) {
|
||||||
@@ -361,14 +405,14 @@ public:
|
|||||||
!m_config.should_include(c.name))
|
!m_config.should_include(c.name))
|
||||||
continue;
|
continue;
|
||||||
generate_alias(c, ostr);
|
generate_alias(c, ostr);
|
||||||
ostr << std::endl;
|
ostr << '\n';
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto &e : m_model.enums) {
|
for (const auto &e : m_model.enums) {
|
||||||
if (!m_config.should_include(e.name))
|
if (!m_config.should_include(e.name))
|
||||||
continue;
|
continue;
|
||||||
generate_alias(e, ostr);
|
generate_alias(e, ostr);
|
||||||
ostr << std::endl;
|
ostr << '\n';
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto &c : m_model.classes) {
|
for (const auto &c : m_model.classes) {
|
||||||
@@ -376,14 +420,14 @@ public:
|
|||||||
!m_config.should_include(c.name))
|
!m_config.should_include(c.name))
|
||||||
continue;
|
continue;
|
||||||
generate(c, ostr);
|
generate(c, ostr);
|
||||||
ostr << std::endl;
|
ostr << '\n';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_config.should_include_entities("enums"))
|
if (m_config.should_include_entities("enums"))
|
||||||
for (const auto &e : m_model.enums) {
|
for (const auto &e : m_model.enums) {
|
||||||
generate(e, ostr);
|
generate(e, ostr);
|
||||||
ostr << std::endl;
|
ostr << '\n';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process aliases in any of the puml directives
|
// Process aliases in any of the puml directives
|
||||||
@@ -397,10 +441,10 @@ public:
|
|||||||
note.replace(
|
note.replace(
|
||||||
std::get<1>(alias_match), std::get<2>(alias_match), alias);
|
std::get<1>(alias_match), std::get<2>(alias_match), alias);
|
||||||
}
|
}
|
||||||
ostr << note << std::endl;
|
ostr << note << '\n';
|
||||||
}
|
}
|
||||||
|
|
||||||
ostr << "@enduml" << std::endl;
|
ostr << "@enduml" << '\n';
|
||||||
}
|
}
|
||||||
|
|
||||||
friend std::ostream &operator<<(std::ostream &os, const generator &g);
|
friend std::ostream &operator<<(std::ostream &os, const generator &g);
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "decorators.h"
|
||||||
#include "util/error.h"
|
#include "util/error.h"
|
||||||
#include "util/util.h"
|
#include "util/util.h"
|
||||||
|
|
||||||
@@ -53,7 +54,61 @@ enum class relationship_t {
|
|||||||
|
|
||||||
std::string to_string(relationship_t r);
|
std::string to_string(relationship_t r);
|
||||||
|
|
||||||
class element {
|
struct stylable_element {
|
||||||
|
std::string style;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct decorated_element {
|
||||||
|
std::vector<std::shared_ptr<decorators::decorator>> decorators;
|
||||||
|
|
||||||
|
bool skip() const
|
||||||
|
{
|
||||||
|
for (auto d : decorators)
|
||||||
|
if (std::dynamic_pointer_cast<decorators::skip>(d))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool skip_relationship() const
|
||||||
|
{
|
||||||
|
for (auto d : decorators)
|
||||||
|
if (std::dynamic_pointer_cast<decorators::skip_relationship>(d))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<relationship_t, std::string> relationship() const
|
||||||
|
{
|
||||||
|
for (auto &d : decorators)
|
||||||
|
if (std::dynamic_pointer_cast<decorators::association>(d))
|
||||||
|
return {relationship_t::kAssociation,
|
||||||
|
std::dynamic_pointer_cast<decorators::relationship>(d)
|
||||||
|
->multiplicity};
|
||||||
|
else if (std::dynamic_pointer_cast<decorators::aggregation>(d))
|
||||||
|
return {relationship_t::kAggregation,
|
||||||
|
std::dynamic_pointer_cast<decorators::relationship>(d)
|
||||||
|
->multiplicity};
|
||||||
|
else if (std::dynamic_pointer_cast<decorators::composition>(d))
|
||||||
|
return {relationship_t::kComposition,
|
||||||
|
std::dynamic_pointer_cast<decorators::relationship>(d)
|
||||||
|
->multiplicity};
|
||||||
|
|
||||||
|
return {relationship_t::kNone, ""};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string style_spec()
|
||||||
|
{
|
||||||
|
for (auto d : decorators)
|
||||||
|
if (std::dynamic_pointer_cast<decorators::style>(d))
|
||||||
|
return std::dynamic_pointer_cast<decorators::style>(d)->spec;
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class element : public decorated_element {
|
||||||
public:
|
public:
|
||||||
element()
|
element()
|
||||||
: m_id{m_nextId++}
|
: m_id{m_nextId++}
|
||||||
@@ -71,7 +126,7 @@ private:
|
|||||||
static std::atomic_uint64_t m_nextId;
|
static std::atomic_uint64_t m_nextId;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct class_element {
|
struct class_element : public decorated_element {
|
||||||
scope_t scope;
|
scope_t scope;
|
||||||
std::string name;
|
std::string name;
|
||||||
std::string type;
|
std::string type;
|
||||||
@@ -82,7 +137,7 @@ struct class_member : public class_element {
|
|||||||
bool is_static{false};
|
bool is_static{false};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct method_parameter {
|
struct method_parameter : public decorated_element {
|
||||||
std::string type;
|
std::string type;
|
||||||
std::string name;
|
std::string name;
|
||||||
std::string default_value;
|
std::string default_value;
|
||||||
@@ -115,11 +170,11 @@ struct class_parent {
|
|||||||
access_t access;
|
access_t access;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct class_relationship {
|
struct class_relationship : public decorated_element, public stylable_element {
|
||||||
relationship_t type{relationship_t::kAssociation};
|
relationship_t type{relationship_t::kAssociation};
|
||||||
std::string destination;
|
std::string destination;
|
||||||
std::string cardinality_source;
|
std::string multiplicity_source;
|
||||||
std::string cardinality_destination;
|
std::string multiplicity_destination;
|
||||||
std::string label;
|
std::string label;
|
||||||
scope_t scope{scope_t::kNone};
|
scope_t scope{scope_t::kNone};
|
||||||
|
|
||||||
@@ -148,7 +203,7 @@ struct type_alias {
|
|||||||
std::string underlying_type;
|
std::string underlying_type;
|
||||||
};
|
};
|
||||||
|
|
||||||
class class_ : public element {
|
class class_ : public element, public stylable_element {
|
||||||
public:
|
public:
|
||||||
std::string usr;
|
std::string usr;
|
||||||
bool is_struct{false};
|
bool is_struct{false};
|
||||||
@@ -233,7 +288,7 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct enum_ : public element {
|
struct enum_ : public element, public stylable_element {
|
||||||
std::vector<std::string> constants;
|
std::vector<std::string> constants;
|
||||||
std::vector<class_relationship> relationships;
|
std::vector<class_relationship> relationships;
|
||||||
|
|
||||||
|
|||||||
@@ -193,6 +193,18 @@ void tu_visitor::process_enum_declaration(const cppast::cpp_enum &enm)
|
|||||||
enum_ e;
|
enum_ e;
|
||||||
e.name = cx::util::full_name(ctx.namespace_, enm);
|
e.name = cx::util::full_name(ctx.namespace_, enm);
|
||||||
|
|
||||||
|
if (enm.comment().has_value())
|
||||||
|
e.decorators = decorators::parse(enm.comment().value());
|
||||||
|
|
||||||
|
if (e.skip())
|
||||||
|
return;
|
||||||
|
|
||||||
|
e.style = e.style_spec();
|
||||||
|
|
||||||
|
// Process enum documentation comment
|
||||||
|
if (enm.comment().has_value())
|
||||||
|
e.decorators = decorators::parse(enm.comment().value());
|
||||||
|
|
||||||
for (const auto &ev : enm) {
|
for (const auto &ev : enm) {
|
||||||
if (ev.kind() == cppast::cpp_entity_kind::enum_value_t) {
|
if (ev.kind() == cppast::cpp_entity_kind::enum_value_t) {
|
||||||
e.constants.push_back(ev.name());
|
e.constants.push_back(ev.name());
|
||||||
@@ -226,9 +238,28 @@ void tu_visitor::process_class_declaration(const cppast::cpp_class &cls,
|
|||||||
c.is_struct = cls.class_kind() == cppast::cpp_class_kind::struct_t;
|
c.is_struct = cls.class_kind() == cppast::cpp_class_kind::struct_t;
|
||||||
c.name = cx::util::full_name(ctx.namespace_, cls);
|
c.name = cx::util::full_name(ctx.namespace_, cls);
|
||||||
|
|
||||||
|
if (cls.comment().has_value())
|
||||||
|
c.decorators = decorators::parse(cls.comment().value());
|
||||||
|
|
||||||
cppast::cpp_access_specifier_kind last_access_specifier =
|
cppast::cpp_access_specifier_kind last_access_specifier =
|
||||||
cppast::cpp_access_specifier_kind::cpp_private;
|
cppast::cpp_access_specifier_kind::cpp_private;
|
||||||
|
|
||||||
|
// Process class documentation comment
|
||||||
|
if (cppast::is_templated(cls)) {
|
||||||
|
if (cls.parent().value().comment().has_value())
|
||||||
|
c.decorators =
|
||||||
|
decorators::parse(cls.parent().value().comment().value());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (cls.comment().has_value())
|
||||||
|
c.decorators = decorators::parse(cls.comment().value());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c.skip())
|
||||||
|
return;
|
||||||
|
|
||||||
|
c.style = c.style_spec();
|
||||||
|
|
||||||
// Process class child entities
|
// Process class child entities
|
||||||
if (c.is_struct)
|
if (c.is_struct)
|
||||||
last_access_specifier = cppast::cpp_access_specifier_kind::cpp_public;
|
last_access_specifier = cppast::cpp_access_specifier_kind::cpp_public;
|
||||||
@@ -240,7 +271,6 @@ void tu_visitor::process_class_declaration(const cppast::cpp_class &cls,
|
|||||||
}
|
}
|
||||||
else if (child.kind() == cppast::cpp_entity_kind::member_variable_t) {
|
else if (child.kind() == cppast::cpp_entity_kind::member_variable_t) {
|
||||||
auto &mv = static_cast<const cppast::cpp_member_variable &>(child);
|
auto &mv = static_cast<const cppast::cpp_member_variable &>(child);
|
||||||
LOG_DBG("Found member variable {}", mv.name());
|
|
||||||
process_field(mv, c, last_access_specifier);
|
process_field(mv, c, last_access_specifier);
|
||||||
}
|
}
|
||||||
else if (child.kind() == cppast::cpp_entity_kind::variable_t) {
|
else if (child.kind() == cppast::cpp_entity_kind::variable_t) {
|
||||||
@@ -489,7 +519,7 @@ void tu_visitor::process_class_declaration(const cppast::cpp_class &cls,
|
|||||||
|
|
||||||
bool tu_visitor::process_field_with_template_instantiation(
|
bool tu_visitor::process_field_with_template_instantiation(
|
||||||
const cppast::cpp_member_variable &mv, const cppast::cpp_type &tr,
|
const cppast::cpp_member_variable &mv, const cppast::cpp_type &tr,
|
||||||
class_ &c, cppast::cpp_access_specifier_kind as)
|
class_ &c, class_member &m, cppast::cpp_access_specifier_kind as)
|
||||||
{
|
{
|
||||||
LOG_DBG("Processing field with template instatiation type {}",
|
LOG_DBG("Processing field with template instatiation type {}",
|
||||||
cppast::to_string(tr));
|
cppast::to_string(tr));
|
||||||
@@ -546,6 +576,17 @@ bool tu_visitor::process_field_with_template_instantiation(
|
|||||||
rr.type = relationship_t::kAggregation;
|
rr.type = relationship_t::kAggregation;
|
||||||
rr.label = mv.name();
|
rr.label = mv.name();
|
||||||
rr.scope = detail::cpp_access_specifier_to_scope(as);
|
rr.scope = detail::cpp_access_specifier_to_scope(as);
|
||||||
|
rr.style = m.style_spec();
|
||||||
|
|
||||||
|
auto [decorator_rtype, decorator_rmult] = m.relationship();
|
||||||
|
if (decorator_rtype != relationship_t::kNone) {
|
||||||
|
rr.type = decorator_rtype;
|
||||||
|
auto mult = util::split(decorator_rmult, ":");
|
||||||
|
if (mult.size() == 2) {
|
||||||
|
rr.multiplicity_source = mult[0];
|
||||||
|
rr.multiplicity_destination = mult[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
LOG_DBG("Adding field instantiation relationship {} {} {} : {}",
|
LOG_DBG("Adding field instantiation relationship {} {} {} : {}",
|
||||||
rr.destination, model::class_diagram::to_string(rr.type), c.usr,
|
rr.destination, model::class_diagram::to_string(rr.type), c.usr,
|
||||||
@@ -576,6 +617,12 @@ void tu_visitor::process_field(const cppast::cpp_member_variable &mv, class_ &c,
|
|||||||
m.scope = detail::cpp_access_specifier_to_scope(as);
|
m.scope = detail::cpp_access_specifier_to_scope(as);
|
||||||
m.is_static = false;
|
m.is_static = false;
|
||||||
|
|
||||||
|
if (mv.comment().has_value())
|
||||||
|
m.decorators = decorators::parse(mv.comment().value());
|
||||||
|
|
||||||
|
if (m.skip())
|
||||||
|
return;
|
||||||
|
|
||||||
auto &tr = cx::util::unreferenced(cppast::remove_cv(mv.type()));
|
auto &tr = cx::util::unreferenced(cppast::remove_cv(mv.type()));
|
||||||
|
|
||||||
LOG_DBG("Processing field {} with unreferenced type of kind {}", mv.name(),
|
LOG_DBG("Processing field {} with unreferenced type of kind {}", mv.name(),
|
||||||
@@ -591,7 +638,7 @@ void tu_visitor::process_field(const cppast::cpp_member_variable &mv, class_ &c,
|
|||||||
else if (tr.kind() == cppast::cpp_type_kind::template_instantiation_t) {
|
else if (tr.kind() == cppast::cpp_type_kind::template_instantiation_t) {
|
||||||
template_instantiation_added_as_aggregation =
|
template_instantiation_added_as_aggregation =
|
||||||
process_field_with_template_instantiation(
|
process_field_with_template_instantiation(
|
||||||
mv, resolve_alias(tr), c, as);
|
mv, resolve_alias(tr), c, m, as);
|
||||||
}
|
}
|
||||||
else if (tr.kind() == cppast::cpp_type_kind::unexposed_t) {
|
else if (tr.kind() == cppast::cpp_type_kind::unexposed_t) {
|
||||||
LOG_DBG(
|
LOG_DBG(
|
||||||
@@ -599,7 +646,8 @@ void tu_visitor::process_field(const cppast::cpp_member_variable &mv, class_ &c,
|
|||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!template_instantiation_added_as_aggregation &&
|
if (!m.skip_relationship() &&
|
||||||
|
!template_instantiation_added_as_aggregation &&
|
||||||
(tr.kind() != cppast::cpp_type_kind::builtin_t) &&
|
(tr.kind() != cppast::cpp_type_kind::builtin_t) &&
|
||||||
(tr.kind() != cppast::cpp_type_kind::template_parameter_t)) {
|
(tr.kind() != cppast::cpp_type_kind::template_parameter_t)) {
|
||||||
const auto &ttt = resolve_alias(mv.type());
|
const auto &ttt = resolve_alias(mv.type());
|
||||||
@@ -613,6 +661,17 @@ void tu_visitor::process_field(const cppast::cpp_member_variable &mv, class_ &c,
|
|||||||
r.type = relationship_type;
|
r.type = relationship_type;
|
||||||
r.label = m.name;
|
r.label = m.name;
|
||||||
r.scope = m.scope;
|
r.scope = m.scope;
|
||||||
|
r.style = m.style_spec();
|
||||||
|
|
||||||
|
auto [decorator_rtype, decorator_rmult] = m.relationship();
|
||||||
|
if (decorator_rtype != relationship_t::kNone) {
|
||||||
|
r.type = decorator_rtype;
|
||||||
|
auto mult = util::split(decorator_rmult, ":");
|
||||||
|
if (mult.size() == 2) {
|
||||||
|
r.multiplicity_source = mult[0];
|
||||||
|
r.multiplicity_destination = mult[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
LOG_DBG("Adding field relationship {} {} {} : {}",
|
LOG_DBG("Adding field relationship {} {} {} : {}",
|
||||||
r.destination, model::class_diagram::to_string(r.type),
|
r.destination, model::class_diagram::to_string(r.type),
|
||||||
@@ -650,6 +709,12 @@ void tu_visitor::process_static_field(const cppast::cpp_variable &mv, class_ &c,
|
|||||||
m.scope = detail::cpp_access_specifier_to_scope(as);
|
m.scope = detail::cpp_access_specifier_to_scope(as);
|
||||||
m.is_static = true;
|
m.is_static = true;
|
||||||
|
|
||||||
|
if (mv.comment().has_value())
|
||||||
|
m.decorators = decorators::parse(mv.comment().value());
|
||||||
|
|
||||||
|
if (m.skip())
|
||||||
|
return;
|
||||||
|
|
||||||
c.members.emplace_back(std::move(m));
|
c.members.emplace_back(std::move(m));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -666,6 +731,12 @@ void tu_visitor::process_method(const cppast::cpp_member_function &mf,
|
|||||||
m.is_static = false;
|
m.is_static = false;
|
||||||
m.scope = detail::cpp_access_specifier_to_scope(as);
|
m.scope = detail::cpp_access_specifier_to_scope(as);
|
||||||
|
|
||||||
|
if (mf.comment().has_value())
|
||||||
|
m.decorators = decorators::parse(mf.comment().value());
|
||||||
|
|
||||||
|
if (m.skip())
|
||||||
|
return;
|
||||||
|
|
||||||
for (auto ¶m : mf.parameters())
|
for (auto ¶m : mf.parameters())
|
||||||
process_function_parameter(param, m, c);
|
process_function_parameter(param, m, c);
|
||||||
|
|
||||||
@@ -695,6 +766,12 @@ void tu_visitor::process_template_method(
|
|||||||
m.is_static = false;
|
m.is_static = false;
|
||||||
m.scope = detail::cpp_access_specifier_to_scope(as);
|
m.scope = detail::cpp_access_specifier_to_scope(as);
|
||||||
|
|
||||||
|
if (mf.comment().has_value())
|
||||||
|
m.decorators = decorators::parse(mf.comment().value());
|
||||||
|
|
||||||
|
if (m.skip())
|
||||||
|
return;
|
||||||
|
|
||||||
for (auto ¶m : mf.function().parameters())
|
for (auto ¶m : mf.function().parameters())
|
||||||
process_function_parameter(param, m, c);
|
process_function_parameter(param, m, c);
|
||||||
|
|
||||||
@@ -716,6 +793,12 @@ void tu_visitor::process_static_method(const cppast::cpp_function &mf,
|
|||||||
m.is_static = true;
|
m.is_static = true;
|
||||||
m.scope = detail::cpp_access_specifier_to_scope(as);
|
m.scope = detail::cpp_access_specifier_to_scope(as);
|
||||||
|
|
||||||
|
if (mf.comment().has_value())
|
||||||
|
m.decorators = decorators::parse(mf.comment().value());
|
||||||
|
|
||||||
|
if (m.skip())
|
||||||
|
return;
|
||||||
|
|
||||||
for (auto ¶m : mf.parameters())
|
for (auto ¶m : mf.parameters())
|
||||||
process_function_parameter(param, m, c);
|
process_function_parameter(param, m, c);
|
||||||
|
|
||||||
@@ -737,6 +820,12 @@ void tu_visitor::process_constructor(const cppast::cpp_constructor &mf,
|
|||||||
m.is_static = false;
|
m.is_static = false;
|
||||||
m.scope = detail::cpp_access_specifier_to_scope(as);
|
m.scope = detail::cpp_access_specifier_to_scope(as);
|
||||||
|
|
||||||
|
if (mf.comment().has_value())
|
||||||
|
m.decorators = decorators::parse(mf.comment().value());
|
||||||
|
|
||||||
|
if (m.skip())
|
||||||
|
return;
|
||||||
|
|
||||||
for (auto ¶m : mf.parameters())
|
for (auto ¶m : mf.parameters())
|
||||||
process_function_parameter(param, m, c);
|
process_function_parameter(param, m, c);
|
||||||
|
|
||||||
@@ -764,6 +853,13 @@ void tu_visitor::process_function_parameter(
|
|||||||
{
|
{
|
||||||
method_parameter mp;
|
method_parameter mp;
|
||||||
mp.name = param.name();
|
mp.name = param.name();
|
||||||
|
|
||||||
|
if (param.comment().has_value())
|
||||||
|
m.decorators = decorators::parse(param.comment().value());
|
||||||
|
|
||||||
|
if (mp.skip())
|
||||||
|
return;
|
||||||
|
|
||||||
const auto ¶m_type =
|
const auto ¶m_type =
|
||||||
cppast::remove_cv(cx::util::unreferenced(param.type()));
|
cppast::remove_cv(cx::util::unreferenced(param.type()));
|
||||||
if (param_type.kind() == cppast::cpp_type_kind::template_instantiation_t) {
|
if (param_type.kind() == cppast::cpp_type_kind::template_instantiation_t) {
|
||||||
@@ -777,7 +873,7 @@ void tu_visitor::process_function_parameter(
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto dv = param.default_value();
|
auto dv = param.default_value();
|
||||||
if (dv)
|
if (dv) {
|
||||||
switch (dv.value().kind()) {
|
switch (dv.value().kind()) {
|
||||||
case cppast::cpp_expression_kind::literal_t:
|
case cppast::cpp_expression_kind::literal_t:
|
||||||
mp.default_value =
|
mp.default_value =
|
||||||
@@ -794,66 +890,73 @@ void tu_visitor::process_function_parameter(
|
|||||||
default:
|
default:
|
||||||
mp.default_value = "{}";
|
mp.default_value = "{}";
|
||||||
}
|
}
|
||||||
|
|
||||||
// find relationship for the type
|
|
||||||
std::vector<std::pair<std::string, relationship_t>> relationships;
|
|
||||||
find_relationships(cppast::remove_cv(param.type()), relationships,
|
|
||||||
relationship_t::kDependency);
|
|
||||||
for (const auto &[type, relationship_type] : relationships) {
|
|
||||||
if ((relationship_type != relationship_t::kNone) && (type != c.name)) {
|
|
||||||
class_relationship r;
|
|
||||||
r.destination = type;
|
|
||||||
r.type = relationship_t::kDependency;
|
|
||||||
r.label = "";
|
|
||||||
|
|
||||||
LOG_DBG("Adding field relationship {} {} {} : {}", r.destination,
|
|
||||||
model::class_diagram::to_string(r.type), c.usr, r.label);
|
|
||||||
|
|
||||||
c.add_relationship(std::move(r));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Also consider the container itself if it is user defined type
|
if (!mp.skip_relationship()) {
|
||||||
const auto &t = cppast::remove_cv(cx::util::unreferenced(param.type()));
|
// find relationship for the type
|
||||||
if (t.kind() == cppast::cpp_type_kind::template_instantiation_t) {
|
std::vector<std::pair<std::string, relationship_t>> relationships;
|
||||||
auto &template_instantiation_type =
|
find_relationships(cppast::remove_cv(param.type()), relationships,
|
||||||
static_cast<const cppast::cpp_template_instantiation_type &>(t);
|
relationship_t::kDependency);
|
||||||
if (template_instantiation_type.primary_template()
|
for (const auto &[type, relationship_type] : relationships) {
|
||||||
.get(ctx.entity_index)
|
if ((relationship_type != relationship_t::kNone) &&
|
||||||
.size()) {
|
(type != c.name)) {
|
||||||
// Here we need the name of the primary template with full
|
|
||||||
// namespace prefix to apply config inclusion filters
|
|
||||||
auto primary_template_name = cx::util::full_name(ctx.namespace_,
|
|
||||||
template_instantiation_type.primary_template()
|
|
||||||
.get(ctx.entity_index)[0]
|
|
||||||
.get());
|
|
||||||
|
|
||||||
LOG_DBG(
|
|
||||||
"Maybe building instantiation for: {}", primary_template_name);
|
|
||||||
|
|
||||||
if (ctx.config.should_include(primary_template_name)) {
|
|
||||||
class_ tinst =
|
|
||||||
build_template_instantiation(template_instantiation_type);
|
|
||||||
|
|
||||||
LOG_DBG("Created template instantiation: {}, {}", tinst.name,
|
|
||||||
tinst.usr);
|
|
||||||
|
|
||||||
class_relationship r;
|
class_relationship r;
|
||||||
r.destination = tinst.base_template_usr;
|
r.destination = type;
|
||||||
r.type = relationship_t::kInstantiation;
|
r.type = relationship_t::kDependency;
|
||||||
r.label = "";
|
r.label = "";
|
||||||
tinst.add_relationship(std::move(r));
|
|
||||||
|
|
||||||
class_relationship rr;
|
LOG_DBG("Adding field relationship {} {} {} : {}",
|
||||||
rr.destination = tinst.usr;
|
r.destination, model::class_diagram::to_string(r.type),
|
||||||
rr.type = relationship_t::kDependency;
|
c.usr, r.label);
|
||||||
rr.label = "";
|
|
||||||
LOG_DBG("Adding field dependency relationship {} {} {} : {}",
|
|
||||||
rr.destination, model::class_diagram::to_string(rr.type),
|
|
||||||
c.usr, rr.label);
|
|
||||||
c.add_relationship(std::move(rr));
|
|
||||||
|
|
||||||
ctx.d.add_class(std::move(tinst));
|
c.add_relationship(std::move(r));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Also consider the container itself if it is user defined type
|
||||||
|
const auto &t = cppast::remove_cv(cx::util::unreferenced(param.type()));
|
||||||
|
if (t.kind() == cppast::cpp_type_kind::template_instantiation_t) {
|
||||||
|
auto &template_instantiation_type =
|
||||||
|
static_cast<const cppast::cpp_template_instantiation_type &>(t);
|
||||||
|
if (template_instantiation_type.primary_template()
|
||||||
|
.get(ctx.entity_index)
|
||||||
|
.size()) {
|
||||||
|
// Here we need the name of the primary template with full
|
||||||
|
// namespace prefix to apply config inclusion filters
|
||||||
|
auto primary_template_name = cx::util::full_name(ctx.namespace_,
|
||||||
|
template_instantiation_type.primary_template()
|
||||||
|
.get(ctx.entity_index)[0]
|
||||||
|
.get());
|
||||||
|
|
||||||
|
LOG_DBG("Maybe building instantiation for: {}",
|
||||||
|
primary_template_name);
|
||||||
|
|
||||||
|
if (ctx.config.should_include(primary_template_name)) {
|
||||||
|
class_ tinst = build_template_instantiation(
|
||||||
|
template_instantiation_type);
|
||||||
|
|
||||||
|
LOG_DBG("Created template instantiation: {}, {}",
|
||||||
|
tinst.name, tinst.usr);
|
||||||
|
|
||||||
|
class_relationship r;
|
||||||
|
r.destination = tinst.base_template_usr;
|
||||||
|
r.type = relationship_t::kInstantiation;
|
||||||
|
r.label = "";
|
||||||
|
tinst.add_relationship(std::move(r));
|
||||||
|
|
||||||
|
class_relationship rr;
|
||||||
|
rr.destination = tinst.usr;
|
||||||
|
rr.type = relationship_t::kDependency;
|
||||||
|
rr.label = "";
|
||||||
|
LOG_DBG(
|
||||||
|
"Adding field dependency relationship {} {} {} : {}",
|
||||||
|
rr.destination,
|
||||||
|
model::class_diagram::to_string(rr.type), c.usr,
|
||||||
|
rr.label);
|
||||||
|
c.add_relationship(std::move(rr));
|
||||||
|
|
||||||
|
ctx.d.add_class(std::move(tinst));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -910,6 +1013,12 @@ void tu_visitor::process_friend(const cppast::cpp_friend &f, class_ &parent)
|
|||||||
r.type = relationship_t::kFriendship;
|
r.type = relationship_t::kFriendship;
|
||||||
r.label = "<<friend>>";
|
r.label = "<<friend>>";
|
||||||
|
|
||||||
|
if (f.comment().has_value())
|
||||||
|
r.decorators = decorators::parse(f.comment().value());
|
||||||
|
|
||||||
|
if (r.skip() || r.skip_relationship())
|
||||||
|
return;
|
||||||
|
|
||||||
if (f.type()) {
|
if (f.type()) {
|
||||||
auto name = cppast::to_string(f.type().value());
|
auto name = cppast::to_string(f.type().value());
|
||||||
|
|
||||||
|
|||||||
@@ -166,6 +166,7 @@ public:
|
|||||||
bool process_field_with_template_instantiation(
|
bool process_field_with_template_instantiation(
|
||||||
const cppast::cpp_member_variable &mv, const cppast::cpp_type &tr,
|
const cppast::cpp_member_variable &mv, const cppast::cpp_type &tr,
|
||||||
clanguml::model::class_diagram::class_ &c,
|
clanguml::model::class_diagram::class_ &c,
|
||||||
|
clanguml::model::class_diagram::class_member &m,
|
||||||
cppast::cpp_access_specifier_kind as);
|
cppast::cpp_access_specifier_kind as);
|
||||||
|
|
||||||
void process_static_field(const cppast::cpp_variable &mv,
|
void process_static_field(const cppast::cpp_variable &mv,
|
||||||
|
|||||||
219
src/uml/decorators.cc
Normal file
219
src/uml/decorators.cc
Normal file
@@ -0,0 +1,219 @@
|
|||||||
|
/**
|
||||||
|
* src/uml/decorators.cc
|
||||||
|
*
|
||||||
|
* Copyright (c) 2021 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 "decorators.h"
|
||||||
|
|
||||||
|
#include "util/util.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
|
namespace clanguml {
|
||||||
|
namespace decorators {
|
||||||
|
|
||||||
|
const std::string note::label = "note";
|
||||||
|
const std::string skip::label = "skip";
|
||||||
|
const std::string skip_relationship::label = "skiprelationship";
|
||||||
|
const std::string style::label = "style";
|
||||||
|
const std::string aggregation::label = "aggregation";
|
||||||
|
const std::string composition::label = "composition";
|
||||||
|
const std::string association::label = "association";
|
||||||
|
|
||||||
|
std::shared_ptr<decorator> decorator::from_string(std::string_view c)
|
||||||
|
{
|
||||||
|
if (c.find(note::label) == 0) {
|
||||||
|
return note::from_string(c);
|
||||||
|
}
|
||||||
|
else if (c.find(skip_relationship::label) == 0) {
|
||||||
|
return skip_relationship::from_string(c);
|
||||||
|
}
|
||||||
|
else if (c.find(skip::label) == 0) {
|
||||||
|
return skip::from_string(c);
|
||||||
|
}
|
||||||
|
else if (c.find(style::label) == 0) {
|
||||||
|
return style::from_string(c);
|
||||||
|
}
|
||||||
|
else if (c.find(aggregation::label) == 0) {
|
||||||
|
return aggregation::from_string(c);
|
||||||
|
}
|
||||||
|
else if (c.find(composition::label) == 0) {
|
||||||
|
return composition::from_string(c);
|
||||||
|
}
|
||||||
|
else if (c.find(association::label) == 0) {
|
||||||
|
return association::from_string(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool decorator::applies_to_diagram(std::string name)
|
||||||
|
{
|
||||||
|
return diagrams.empty() ||
|
||||||
|
(std::find(diagrams.begin(), diagrams.end(), name) != diagrams.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
decorator_toks decorator::tokenize(const std::string &label, std::string_view c)
|
||||||
|
{
|
||||||
|
decorator_toks res;
|
||||||
|
res.label = label;
|
||||||
|
size_t pos{};
|
||||||
|
auto it = c.begin();
|
||||||
|
std::advance(it, label.size());
|
||||||
|
|
||||||
|
if (*it == ':') {
|
||||||
|
std::advance(it, 1);
|
||||||
|
|
||||||
|
pos = std::distance(c.begin(), it);
|
||||||
|
// If the diagram list is provided after ':', [] is mandatory
|
||||||
|
// even if empty
|
||||||
|
auto d = c.substr(pos, c.find("[", pos) - pos);
|
||||||
|
if (!d.empty()) {
|
||||||
|
std::string d_str{d};
|
||||||
|
d_str.erase(std::remove_if(d_str.begin(), d_str.end(),
|
||||||
|
(int (*)(int))std::isspace),
|
||||||
|
d_str.end());
|
||||||
|
res.diagrams = util::split(d_str, ",");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::advance(it, d.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*it == '[') {
|
||||||
|
std::advance(it, 1);
|
||||||
|
|
||||||
|
pos = std::distance(c.begin(), it);
|
||||||
|
res.param = c.substr(pos, c.find("]", pos) - pos);
|
||||||
|
|
||||||
|
std::advance(it, res.param.size() + 1);
|
||||||
|
}
|
||||||
|
else if (std::isspace(*it)) {
|
||||||
|
std::advance(it, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
pos = std::distance(c.begin(), it);
|
||||||
|
res.text = c.substr(pos, c.find("}", pos) - pos);
|
||||||
|
res.text = util::trim(res.text);
|
||||||
|
res.param = util::trim(res.param);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<decorator> note::from_string(std::string_view c)
|
||||||
|
{
|
||||||
|
auto res = std::make_shared<note>();
|
||||||
|
auto toks = res->tokenize(note::label, c);
|
||||||
|
|
||||||
|
res->diagrams = toks.diagrams;
|
||||||
|
|
||||||
|
if (!toks.param.empty())
|
||||||
|
res->position = toks.param;
|
||||||
|
|
||||||
|
res->text = toks.text;
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<decorator> skip::from_string(std::string_view c)
|
||||||
|
{
|
||||||
|
return std::make_shared<skip>();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<decorator> skip_relationship::from_string(std::string_view c)
|
||||||
|
{
|
||||||
|
return std::make_shared<skip_relationship>();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<decorator> style::from_string(std::string_view c)
|
||||||
|
{
|
||||||
|
auto res = std::make_shared<style>();
|
||||||
|
auto toks = res->tokenize(style::label, c);
|
||||||
|
|
||||||
|
res->diagrams = toks.diagrams;
|
||||||
|
res->spec = toks.param;
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<decorator> aggregation::from_string(std::string_view c)
|
||||||
|
{
|
||||||
|
auto res = std::make_shared<aggregation>();
|
||||||
|
auto toks = res->tokenize(aggregation::label, c);
|
||||||
|
|
||||||
|
res->diagrams = toks.diagrams;
|
||||||
|
res->multiplicity = toks.param;
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<decorator> composition::from_string(std::string_view c)
|
||||||
|
{
|
||||||
|
auto res = std::make_shared<composition>();
|
||||||
|
auto toks = res->tokenize(composition::label, c);
|
||||||
|
|
||||||
|
res->diagrams = toks.diagrams;
|
||||||
|
res->multiplicity = toks.param;
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<decorator> association::from_string(std::string_view c)
|
||||||
|
{
|
||||||
|
auto res = std::make_shared<association>();
|
||||||
|
auto toks = res->tokenize(association::label, c);
|
||||||
|
|
||||||
|
res->diagrams = toks.diagrams;
|
||||||
|
res->multiplicity = toks.param;
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::shared_ptr<decorator>> parse(
|
||||||
|
std::string documentation_block, std::string clanguml_tag)
|
||||||
|
{
|
||||||
|
std::vector<std::shared_ptr<decorator>> res;
|
||||||
|
const std::string begin_tag{"@" + clanguml_tag};
|
||||||
|
const auto begin_tag_size = begin_tag.size();
|
||||||
|
|
||||||
|
// First replace all \uml occurences with @uml
|
||||||
|
util::replace_all(
|
||||||
|
documentation_block, "\\" + clanguml_tag, "@" + clanguml_tag);
|
||||||
|
documentation_block = util::trim(documentation_block);
|
||||||
|
|
||||||
|
std::string_view block_view{documentation_block};
|
||||||
|
|
||||||
|
auto pos = block_view.find("@" + clanguml_tag + "{");
|
||||||
|
while (pos < documentation_block.size()) {
|
||||||
|
auto c_begin = pos + begin_tag_size;
|
||||||
|
auto c_end = documentation_block.find("}", c_begin);
|
||||||
|
|
||||||
|
if (c_end == std::string::npos)
|
||||||
|
return res;
|
||||||
|
|
||||||
|
auto com =
|
||||||
|
decorator::from_string(block_view.substr(c_begin + 1, c_end - 2));
|
||||||
|
|
||||||
|
if (com)
|
||||||
|
res.emplace_back(std::move(com));
|
||||||
|
|
||||||
|
pos = block_view.find("@" + clanguml_tag + "{", c_end);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace decorators
|
||||||
|
} // namespace clanguml
|
||||||
101
src/uml/decorators.h
Normal file
101
src/uml/decorators.h
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
/**
|
||||||
|
* src/uml/decorators.h
|
||||||
|
*
|
||||||
|
* Copyright (c) 2021 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 <functional>
|
||||||
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace clanguml {
|
||||||
|
namespace decorators {
|
||||||
|
struct decorator_toks {
|
||||||
|
std::string label;
|
||||||
|
std::vector<std::string> diagrams;
|
||||||
|
std::string param;
|
||||||
|
std::string text;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct decorator {
|
||||||
|
std::vector<std::string> diagrams;
|
||||||
|
|
||||||
|
virtual ~decorator() = default;
|
||||||
|
|
||||||
|
static std::shared_ptr<decorator> from_string(std::string_view c);
|
||||||
|
|
||||||
|
bool applies_to_diagram(std::string name);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
decorator_toks tokenize(const std::string &label, std::string_view c);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct note : public decorator {
|
||||||
|
static const std::string label;
|
||||||
|
|
||||||
|
std::string position{"left"};
|
||||||
|
std::string text;
|
||||||
|
|
||||||
|
static std::shared_ptr<decorator> from_string(std::string_view c);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct skip : public decorator {
|
||||||
|
static const std::string label;
|
||||||
|
|
||||||
|
static std::shared_ptr<decorator> from_string(std::string_view c);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct skip_relationship : public decorator {
|
||||||
|
static const std::string label;
|
||||||
|
|
||||||
|
static std::shared_ptr<decorator> from_string(std::string_view c);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct style : public decorator {
|
||||||
|
static const std::string label;
|
||||||
|
|
||||||
|
std::string spec;
|
||||||
|
static std::shared_ptr<decorator> from_string(std::string_view c);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct relationship : public decorator {
|
||||||
|
std::string multiplicity;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct aggregation : public relationship {
|
||||||
|
static const std::string label;
|
||||||
|
|
||||||
|
static std::shared_ptr<decorator> from_string(std::string_view c);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct composition : public relationship {
|
||||||
|
static const std::string label;
|
||||||
|
|
||||||
|
static std::shared_ptr<decorator> from_string(std::string_view c);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct association : public relationship {
|
||||||
|
static const std::string label;
|
||||||
|
|
||||||
|
static std::shared_ptr<decorator> from_string(std::string_view c);
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<std::shared_ptr<decorator>> parse(
|
||||||
|
std::string documentation_block, std::string clanguml_tag = "uml");
|
||||||
|
|
||||||
|
} // namespace decorators
|
||||||
|
} // namespace clanguml
|
||||||
@@ -126,5 +126,20 @@ bool find_element_alias(
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool replace_all(
|
||||||
|
std::string &input, std::string pattern, std::string replace_with)
|
||||||
|
{
|
||||||
|
bool replaced{false};
|
||||||
|
|
||||||
|
auto pos = input.find(pattern);
|
||||||
|
while (pos < input.size()) {
|
||||||
|
input.replace(pos, pattern.size(), replace_with);
|
||||||
|
pos = input.find(pattern, pos + replace_with.size());
|
||||||
|
replaced = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return replaced;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -104,5 +104,15 @@ std::string unqualify(const std::string &s);
|
|||||||
*/
|
*/
|
||||||
bool find_element_alias(
|
bool find_element_alias(
|
||||||
const std::string &input, std::tuple<std::string, size_t, size_t> &result);
|
const std::string &input, std::tuple<std::string, size_t, size_t> &result);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Find and replace in string
|
||||||
|
*
|
||||||
|
* Replaces all occurences of pattern with replace_with in input string.
|
||||||
|
*
|
||||||
|
* @return True if at least on replacement was made
|
||||||
|
*/
|
||||||
|
bool replace_all(
|
||||||
|
std::string &input, std::string pattern, std::string replace_with);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,14 @@ set(CLANG_UML_TEST_CASES_HEADER
|
|||||||
catch.h
|
catch.h
|
||||||
)
|
)
|
||||||
|
|
||||||
|
set(CLANG_UML_TEST_DECORATOR_PARSER_SRC
|
||||||
|
test_decorator_parser.cc
|
||||||
|
${TEST_UTIL_SOURCES}
|
||||||
|
)
|
||||||
|
set(CLANG_UML_TEST_DECORATOR_PARSER_HEADER
|
||||||
|
catch.h
|
||||||
|
)
|
||||||
|
|
||||||
add_executable(test_util
|
add_executable(test_util
|
||||||
${CLANG_UML_TEST_UTIL_SRC}
|
${CLANG_UML_TEST_UTIL_SRC}
|
||||||
${CLANG_UML_TEST_UTIL_HEADER})
|
${CLANG_UML_TEST_UTIL_HEADER})
|
||||||
@@ -33,6 +41,15 @@ target_link_libraries(test_util
|
|||||||
${YAML_CPP_LIBRARIES}
|
${YAML_CPP_LIBRARIES}
|
||||||
spdlog::spdlog clang-umllib cppast)
|
spdlog::spdlog clang-umllib cppast)
|
||||||
|
|
||||||
|
add_executable(test_decorator_parser
|
||||||
|
${CLANG_UML_TEST_DECORATOR_PARSER_SRC}
|
||||||
|
${CLANG_UML_TEST_DECORATOR_PARSER_HEADER})
|
||||||
|
|
||||||
|
target_link_libraries(test_decorator_parser
|
||||||
|
PRIVATE
|
||||||
|
${LIBCLANG_LIBRARIES}
|
||||||
|
${YAML_CPP_LIBRARIES}
|
||||||
|
spdlog::spdlog clang-umllib cppast)
|
||||||
|
|
||||||
add_executable(test_cases
|
add_executable(test_cases
|
||||||
${CLANG_UML_TEST_CASES_SRC}
|
${CLANG_UML_TEST_CASES_SRC}
|
||||||
@@ -57,4 +74,5 @@ foreach(TEST_CASE_CONFIG ${TEST_CASE_CONFIGS})
|
|||||||
endforeach()
|
endforeach()
|
||||||
|
|
||||||
add_test(NAME test_util COMMAND test_util)
|
add_test(NAME test_util COMMAND test_util)
|
||||||
|
add_test(NAME test_decorator_parser COMMAND test_decorator_parser)
|
||||||
add_test(NAME test_cases COMMAND test_cases)
|
add_test(NAME test_cases COMMAND test_cases)
|
||||||
|
|||||||
12
tests/t00028/.clang-uml
Normal file
12
tests/t00028/.clang-uml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
compilation_database_dir: ..
|
||||||
|
output_directory: puml
|
||||||
|
diagrams:
|
||||||
|
t00028_class:
|
||||||
|
type: class
|
||||||
|
glob:
|
||||||
|
- ../../tests/t00028/t00028.cc
|
||||||
|
using_namespace:
|
||||||
|
- clanguml::t00028
|
||||||
|
include:
|
||||||
|
namespaces:
|
||||||
|
- clanguml::t00028
|
||||||
56
tests/t00028/t00028.cc
Normal file
56
tests/t00028/t00028.cc
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace clanguml {
|
||||||
|
namespace t00028 {
|
||||||
|
|
||||||
|
/// \uml{note[top] A class note.}
|
||||||
|
class A {
|
||||||
|
};
|
||||||
|
|
||||||
|
/// \uml{note[] B class note.}
|
||||||
|
class B {
|
||||||
|
};
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @uml{note:t00028_class[bottom] C class note.}
|
||||||
|
/// This is class C.
|
||||||
|
class C {
|
||||||
|
};
|
||||||
|
|
||||||
|
/// \uml{note
|
||||||
|
/// D
|
||||||
|
/// class
|
||||||
|
/// note.}
|
||||||
|
class D {
|
||||||
|
};
|
||||||
|
|
||||||
|
/// \uml{note E template class note.}
|
||||||
|
template <typename T> class E {
|
||||||
|
T param;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// \uml{note:other_diagram[left] G class note.}
|
||||||
|
class G {
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @uml{note[ bottom ] F enum note.}
|
||||||
|
enum class F { one, two, three };
|
||||||
|
|
||||||
|
/// \uml{note[right] R class note.}
|
||||||
|
class R {
|
||||||
|
A aaa;
|
||||||
|
|
||||||
|
B *bbb;
|
||||||
|
|
||||||
|
C &ccc;
|
||||||
|
|
||||||
|
std::vector<std::shared_ptr<D>> ddd;
|
||||||
|
|
||||||
|
E<int> eee;
|
||||||
|
|
||||||
|
G **ggg;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace t00028
|
||||||
|
} // namespace clanguml
|
||||||
67
tests/t00028/test_case.h
Normal file
67
tests/t00028/test_case.h
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
/**
|
||||||
|
* tests/t00028/test_case.cc
|
||||||
|
*
|
||||||
|
* Copyright (c) 2021 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("t00028", "[test-case][class]")
|
||||||
|
{
|
||||||
|
auto [config, db] = load_config("t00028");
|
||||||
|
|
||||||
|
auto diagram = config.diagrams["t00028_class"];
|
||||||
|
|
||||||
|
REQUIRE(diagram->name == "t00028_class");
|
||||||
|
|
||||||
|
REQUIRE(diagram->include.namespaces.size() == 1);
|
||||||
|
REQUIRE_THAT(diagram->include.namespaces,
|
||||||
|
VectorContains(std::string{"clanguml::t00028"}));
|
||||||
|
|
||||||
|
REQUIRE(diagram->exclude.namespaces.size() == 0);
|
||||||
|
|
||||||
|
REQUIRE(diagram->should_include("clanguml::t00028::A"));
|
||||||
|
|
||||||
|
auto model = generate_class_diagram(db, diagram);
|
||||||
|
|
||||||
|
REQUIRE(model.name == "t00028_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("A")));
|
||||||
|
REQUIRE_THAT(puml, IsClass(_A("B")));
|
||||||
|
REQUIRE_THAT(puml, IsClass(_A("C")));
|
||||||
|
REQUIRE_THAT(puml, IsClass(_A("D")));
|
||||||
|
REQUIRE_THAT(puml, IsClassTemplate("E", "T"));
|
||||||
|
REQUIRE_THAT(puml, IsEnum(_A("F")));
|
||||||
|
REQUIRE_THAT(puml, IsClass(_A("R")));
|
||||||
|
REQUIRE_THAT(puml, HasNote(_A("A"), "top", "A class note."));
|
||||||
|
REQUIRE_THAT(puml, HasNote(_A("B"), "left", "B class note."));
|
||||||
|
REQUIRE_THAT(puml, HasNote(_A("C"), "bottom", "C class note."));
|
||||||
|
const auto d_note = R"(
|
||||||
|
D
|
||||||
|
class
|
||||||
|
note.)";
|
||||||
|
REQUIRE_THAT(puml, HasNote(_A("D"), "left", d_note));
|
||||||
|
REQUIRE_THAT(puml, HasNote(_A("E<T>"), "left", "E template class note."));
|
||||||
|
REQUIRE_THAT(puml, HasNote(_A("F"), "bottom", "F enum note."));
|
||||||
|
REQUIRE_THAT(puml, !HasNote(_A("G"), "left", "G class note."));
|
||||||
|
REQUIRE_THAT(puml, HasNote(_A("R"), "right", "R class note."));
|
||||||
|
|
||||||
|
save_puml(
|
||||||
|
"./" + config.output_directory + "/" + diagram->name + ".puml", puml);
|
||||||
|
}
|
||||||
12
tests/t00029/.clang-uml
Normal file
12
tests/t00029/.clang-uml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
compilation_database_dir: ..
|
||||||
|
output_directory: puml
|
||||||
|
diagrams:
|
||||||
|
t00029_class:
|
||||||
|
type: class
|
||||||
|
glob:
|
||||||
|
- ../../tests/t00029/t00029.cc
|
||||||
|
using_namespace:
|
||||||
|
- clanguml::t00029
|
||||||
|
include:
|
||||||
|
namespaces:
|
||||||
|
- clanguml::t00029
|
||||||
53
tests/t00029/t00029.cc
Normal file
53
tests/t00029/t00029.cc
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace clanguml {
|
||||||
|
namespace t00029 {
|
||||||
|
|
||||||
|
class A {
|
||||||
|
};
|
||||||
|
|
||||||
|
/// \uml{skip}
|
||||||
|
class B {
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T> class C {
|
||||||
|
T param;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @uml{skip:t00029_class}
|
||||||
|
template <typename T> class D {
|
||||||
|
T param;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class E { one, two, three };
|
||||||
|
|
||||||
|
/// \uml{skip}
|
||||||
|
enum class F { red, green, blue };
|
||||||
|
|
||||||
|
class G1 {
|
||||||
|
};
|
||||||
|
|
||||||
|
class G2 {
|
||||||
|
};
|
||||||
|
|
||||||
|
class G3 {
|
||||||
|
};
|
||||||
|
|
||||||
|
class G4 {
|
||||||
|
};
|
||||||
|
|
||||||
|
struct R {
|
||||||
|
G1 g1;
|
||||||
|
|
||||||
|
/// \uml{skip}
|
||||||
|
G2 g2;
|
||||||
|
|
||||||
|
/// \uml{skiprelationship}
|
||||||
|
G3 &g3;
|
||||||
|
|
||||||
|
std::shared_ptr<G4> g4;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace t00029
|
||||||
|
} // namespace clanguml
|
||||||
65
tests/t00029/test_case.h
Normal file
65
tests/t00029/test_case.h
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
/**
|
||||||
|
* tests/t00029/test_case.cc
|
||||||
|
*
|
||||||
|
* Copyright (c) 2021 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("t00029", "[test-case][class]")
|
||||||
|
{
|
||||||
|
auto [config, db] = load_config("t00029");
|
||||||
|
|
||||||
|
auto diagram = config.diagrams["t00029_class"];
|
||||||
|
|
||||||
|
REQUIRE(diagram->name == "t00029_class");
|
||||||
|
|
||||||
|
REQUIRE(diagram->include.namespaces.size() == 1);
|
||||||
|
REQUIRE_THAT(diagram->include.namespaces,
|
||||||
|
VectorContains(std::string{"clanguml::t00029"}));
|
||||||
|
|
||||||
|
REQUIRE(diagram->exclude.namespaces.size() == 0);
|
||||||
|
|
||||||
|
REQUIRE(diagram->should_include("clanguml::t00029::A"));
|
||||||
|
|
||||||
|
auto model = generate_class_diagram(db, diagram);
|
||||||
|
|
||||||
|
REQUIRE(model.name == "t00029_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("A")));
|
||||||
|
REQUIRE_THAT(puml, !IsClass(_A("B")));
|
||||||
|
REQUIRE_THAT(puml, IsClassTemplate("C", "T"));
|
||||||
|
REQUIRE_THAT(puml, !IsClassTemplate("D", "T"));
|
||||||
|
REQUIRE_THAT(puml, IsEnum(_A("E")));
|
||||||
|
REQUIRE_THAT(puml, !IsEnum(_A("F")));
|
||||||
|
REQUIRE_THAT(puml, IsClass(_A("G1")));
|
||||||
|
REQUIRE_THAT(puml, IsClass(_A("G2")));
|
||||||
|
REQUIRE_THAT(puml, IsClass(_A("G3")));
|
||||||
|
REQUIRE_THAT(puml, IsClass(_A("G4")));
|
||||||
|
|
||||||
|
REQUIRE_THAT(puml, IsClass(_A("R")));
|
||||||
|
|
||||||
|
REQUIRE_THAT(puml, IsAggregation(_A("R"), _A("G1"), "+g1"));
|
||||||
|
REQUIRE_THAT(puml, !IsAggregation(_A("R"), _A("G2"), "+g2"));
|
||||||
|
REQUIRE_THAT(puml, !IsAggregation(_A("R"), _A("G3"), "+g3"));
|
||||||
|
REQUIRE_THAT(puml, IsAssociation(_A("R"), _A("G4"), "+g4"));
|
||||||
|
|
||||||
|
save_puml(
|
||||||
|
"./" + config.output_directory + "/" + diagram->name + ".puml", puml);
|
||||||
|
}
|
||||||
12
tests/t00030/.clang-uml
Normal file
12
tests/t00030/.clang-uml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
compilation_database_dir: ..
|
||||||
|
output_directory: puml
|
||||||
|
diagrams:
|
||||||
|
t00030_class:
|
||||||
|
type: class
|
||||||
|
glob:
|
||||||
|
- ../../tests/t00030/t00030.cc
|
||||||
|
using_namespace:
|
||||||
|
- clanguml::t00030
|
||||||
|
include:
|
||||||
|
namespaces:
|
||||||
|
- clanguml::t00030
|
||||||
34
tests/t00030/t00030.cc
Normal file
34
tests/t00030/t00030.cc
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace clanguml {
|
||||||
|
namespace t00030 {
|
||||||
|
|
||||||
|
class A {
|
||||||
|
};
|
||||||
|
|
||||||
|
class B {
|
||||||
|
};
|
||||||
|
|
||||||
|
class C {
|
||||||
|
};
|
||||||
|
|
||||||
|
class D {
|
||||||
|
};
|
||||||
|
|
||||||
|
struct R {
|
||||||
|
/// @uml{association[]}
|
||||||
|
A aaa;
|
||||||
|
|
||||||
|
/// @uml{composition[0..1:1..*]}
|
||||||
|
std::vector<B> bbb;
|
||||||
|
|
||||||
|
/// @uml{aggregation[0..1:1..5]}
|
||||||
|
std::vector<C> ccc;
|
||||||
|
|
||||||
|
/// @uml{association[:1]}
|
||||||
|
D ddd;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace t00030
|
||||||
|
} // namespace clanguml
|
||||||
57
tests/t00030/test_case.h
Normal file
57
tests/t00030/test_case.h
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
/**
|
||||||
|
* tests/t00030/test_case.cc
|
||||||
|
*
|
||||||
|
* Copyright (c) 2021 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("t00030", "[test-case][class]")
|
||||||
|
{
|
||||||
|
auto [config, db] = load_config("t00030");
|
||||||
|
|
||||||
|
auto diagram = config.diagrams["t00030_class"];
|
||||||
|
|
||||||
|
REQUIRE(diagram->name == "t00030_class");
|
||||||
|
|
||||||
|
REQUIRE(diagram->include.namespaces.size() == 1);
|
||||||
|
REQUIRE_THAT(diagram->include.namespaces,
|
||||||
|
VectorContains(std::string{"clanguml::t00030"}));
|
||||||
|
|
||||||
|
REQUIRE(diagram->exclude.namespaces.size() == 0);
|
||||||
|
|
||||||
|
REQUIRE(diagram->should_include("clanguml::t00030::A"));
|
||||||
|
|
||||||
|
auto model = generate_class_diagram(db, diagram);
|
||||||
|
|
||||||
|
REQUIRE(model.name == "t00030_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("A")));
|
||||||
|
REQUIRE_THAT(puml, IsClass(_A("B")));
|
||||||
|
REQUIRE_THAT(puml, IsClass(_A("C")));
|
||||||
|
REQUIRE_THAT(puml, IsClass(_A("D")));
|
||||||
|
|
||||||
|
REQUIRE_THAT(puml, IsAssociation(_A("R"), _A("A"), "+aaa"));
|
||||||
|
REQUIRE_THAT(puml, IsComposition(_A("R"), _A("B"), "+bbb", "0..1", "1..*"));
|
||||||
|
REQUIRE_THAT(puml, IsAggregation(_A("R"), _A("C"), "+ccc", "0..1", "1..5"));
|
||||||
|
REQUIRE_THAT(puml, IsAssociation(_A("R"), _A("D"), "+ddd", "", "1"));
|
||||||
|
|
||||||
|
save_puml(
|
||||||
|
"./" + config.output_directory + "/" + diagram->name + ".puml", puml);
|
||||||
|
}
|
||||||
12
tests/t00031/.clang-uml
Normal file
12
tests/t00031/.clang-uml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
compilation_database_dir: ..
|
||||||
|
output_directory: puml
|
||||||
|
diagrams:
|
||||||
|
t00031_class:
|
||||||
|
type: class
|
||||||
|
glob:
|
||||||
|
- ../../tests/t00031/t00031.cc
|
||||||
|
using_namespace:
|
||||||
|
- clanguml::t00031
|
||||||
|
include:
|
||||||
|
namespaces:
|
||||||
|
- clanguml::t00031
|
||||||
38
tests/t00031/t00031.cc
Normal file
38
tests/t00031/t00031.cc
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace clanguml {
|
||||||
|
namespace t00031 {
|
||||||
|
|
||||||
|
/// @uml{style[#back:lightgreen|yellow;header:blue/red]}
|
||||||
|
class A {
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @uml{style[#line.dotted:blue]}
|
||||||
|
enum B { one, two, three };
|
||||||
|
|
||||||
|
/// @uml{style[#pink;line:red;line.bold;text:red]}
|
||||||
|
template <typename T> class C {
|
||||||
|
T ttt;
|
||||||
|
};
|
||||||
|
|
||||||
|
class D {
|
||||||
|
};
|
||||||
|
|
||||||
|
struct R {
|
||||||
|
/// @uml{style[#red,dashed,thickness=2]}
|
||||||
|
A *aaa;
|
||||||
|
|
||||||
|
/// @uml{composition}
|
||||||
|
/// @uml{style[#green,dashed,thickness=4]}
|
||||||
|
std::vector<B> bbb;
|
||||||
|
|
||||||
|
/// @uml{style[#blue,dotted,thickness=8]}
|
||||||
|
C<int> ccc;
|
||||||
|
|
||||||
|
/// @uml{style[#blue,plain,thickness=16]}
|
||||||
|
D *ddd;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace t00031
|
||||||
|
} // namespace clanguml
|
||||||
65
tests/t00031/test_case.h
Normal file
65
tests/t00031/test_case.h
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
/**
|
||||||
|
* tests/t00031/test_case.cc
|
||||||
|
*
|
||||||
|
* Copyright (c) 2021 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("t00031", "[test-case][class]")
|
||||||
|
{
|
||||||
|
auto [config, db] = load_config("t00031");
|
||||||
|
|
||||||
|
auto diagram = config.diagrams["t00031_class"];
|
||||||
|
|
||||||
|
REQUIRE(diagram->name == "t00031_class");
|
||||||
|
|
||||||
|
REQUIRE(diagram->include.namespaces.size() == 1);
|
||||||
|
REQUIRE_THAT(diagram->include.namespaces,
|
||||||
|
VectorContains(std::string{"clanguml::t00031"}));
|
||||||
|
|
||||||
|
REQUIRE(diagram->exclude.namespaces.size() == 0);
|
||||||
|
|
||||||
|
REQUIRE(diagram->should_include("clanguml::t00031::A"));
|
||||||
|
|
||||||
|
auto model = generate_class_diagram(db, diagram);
|
||||||
|
|
||||||
|
REQUIRE(model.name == "t00031_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("A")));
|
||||||
|
REQUIRE_THAT(puml, IsEnum(_A("B")));
|
||||||
|
REQUIRE_THAT(puml, IsClassTemplate("C", "T"));
|
||||||
|
REQUIRE_THAT(puml, IsClass(_A("D")));
|
||||||
|
|
||||||
|
REQUIRE_THAT(puml,
|
||||||
|
IsAssociationWithStyle(
|
||||||
|
_A("R"), _A("A"), "+aaa", "#red,dashed,thickness=2"));
|
||||||
|
REQUIRE_THAT(puml,
|
||||||
|
IsCompositionWithStyle(
|
||||||
|
_A("R"), _A("B"), "+bbb", "#green,dashed,thickness=4"));
|
||||||
|
REQUIRE_THAT(puml,
|
||||||
|
IsAggregationWithStyle(
|
||||||
|
_A("R"), _A("C<int>"), "+ccc", "#blue,dotted,thickness=8"));
|
||||||
|
REQUIRE_THAT(puml,
|
||||||
|
IsAssociationWithStyle(
|
||||||
|
_A("R"), _A("D"), "+ddd", "#blue,plain,thickness=16"));
|
||||||
|
|
||||||
|
save_puml(
|
||||||
|
"./" + config.output_directory + "/" + diagram->name + ".puml", puml);
|
||||||
|
}
|
||||||
@@ -131,6 +131,10 @@ using namespace clanguml::test::matchers;
|
|||||||
#include "t00025/test_case.h"
|
#include "t00025/test_case.h"
|
||||||
#include "t00026/test_case.h"
|
#include "t00026/test_case.h"
|
||||||
#include "t00027/test_case.h"
|
#include "t00027/test_case.h"
|
||||||
|
#include "t00028/test_case.h"
|
||||||
|
#include "t00029/test_case.h"
|
||||||
|
#include "t00030/test_case.h"
|
||||||
|
#include "t00031/test_case.h"
|
||||||
|
|
||||||
//
|
//
|
||||||
// Sequence diagram tests
|
// Sequence diagram tests
|
||||||
|
|||||||
@@ -170,8 +170,7 @@ struct AliasMatcher {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
throw std::runtime_error(fmt::format(
|
return "__INVALID__ALIAS__";
|
||||||
"Cannot find alias {} in {}", name, fmt::join(puml, "\n")));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::vector<std::string> puml;
|
const std::vector<std::string> puml;
|
||||||
@@ -218,11 +217,23 @@ ContainsMatcher IsInnerClass(std::string const &parent,
|
|||||||
}
|
}
|
||||||
|
|
||||||
ContainsMatcher IsAssociation(std::string const &from, std::string const &to,
|
ContainsMatcher IsAssociation(std::string const &from, std::string const &to,
|
||||||
std::string const &label,
|
std::string const &label, std::string multiplicity_source = "",
|
||||||
|
std::string multiplicity_dest = "",
|
||||||
CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes)
|
CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes)
|
||||||
{
|
{
|
||||||
|
std::string format_string = "{}";
|
||||||
|
if (!multiplicity_source.empty())
|
||||||
|
format_string += " \"" + multiplicity_source + "\"";
|
||||||
|
|
||||||
|
format_string += " -->";
|
||||||
|
|
||||||
|
if (!multiplicity_dest.empty())
|
||||||
|
format_string += " \"" + multiplicity_dest + "\"";
|
||||||
|
|
||||||
|
format_string += " {} : {}";
|
||||||
|
|
||||||
return ContainsMatcher(CasedString(
|
return ContainsMatcher(CasedString(
|
||||||
fmt::format("{} --> {} : {}", from, to, label), caseSensitivity));
|
fmt::format(format_string, from, to, label), caseSensitivity));
|
||||||
}
|
}
|
||||||
|
|
||||||
ContainsMatcher IsFriend(std::string const &from, std::string const &to,
|
ContainsMatcher IsFriend(std::string const &from, std::string const &to,
|
||||||
@@ -233,19 +244,70 @@ ContainsMatcher IsFriend(std::string const &from, std::string const &to,
|
|||||||
}
|
}
|
||||||
|
|
||||||
ContainsMatcher IsComposition(std::string const &from, std::string const &to,
|
ContainsMatcher IsComposition(std::string const &from, std::string const &to,
|
||||||
std::string const &label,
|
std::string const &label, std::string multiplicity_source = "",
|
||||||
|
std::string multiplicity_dest = "",
|
||||||
CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes)
|
CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes)
|
||||||
{
|
{
|
||||||
|
std::string format_string = "{}";
|
||||||
|
if (!multiplicity_source.empty())
|
||||||
|
format_string += " \"" + multiplicity_source + "\"";
|
||||||
|
|
||||||
|
format_string += " *--";
|
||||||
|
|
||||||
|
if (!multiplicity_dest.empty())
|
||||||
|
format_string += " \"" + multiplicity_dest + "\"";
|
||||||
|
|
||||||
|
format_string += " {} : {}";
|
||||||
|
|
||||||
return ContainsMatcher(CasedString(
|
return ContainsMatcher(CasedString(
|
||||||
fmt::format("{} *-- {} : {}", from, to, label), caseSensitivity));
|
fmt::format(format_string, from, to, label), caseSensitivity));
|
||||||
}
|
}
|
||||||
|
|
||||||
ContainsMatcher IsAggregation(std::string const &from, std::string const &to,
|
ContainsMatcher IsAggregation(std::string const &from, std::string const &to,
|
||||||
std::string const &label,
|
std::string const &label, std::string multiplicity_source = "",
|
||||||
|
std::string multiplicity_dest = "",
|
||||||
CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes)
|
CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes)
|
||||||
{
|
{
|
||||||
|
std::string format_string = "{}";
|
||||||
|
if (!multiplicity_source.empty())
|
||||||
|
format_string += " \"" + multiplicity_source + "\"";
|
||||||
|
|
||||||
|
format_string += " o--";
|
||||||
|
|
||||||
|
if (!multiplicity_dest.empty())
|
||||||
|
format_string += " \"" + multiplicity_dest + "\"";
|
||||||
|
|
||||||
|
format_string += " {} : {}";
|
||||||
|
|
||||||
return ContainsMatcher(CasedString(
|
return ContainsMatcher(CasedString(
|
||||||
fmt::format("{} o-- {} : {}", from, to, label), caseSensitivity));
|
fmt::format(format_string, from, to, label), caseSensitivity));
|
||||||
|
}
|
||||||
|
|
||||||
|
ContainsMatcher IsAggregationWithStyle(std::string const &from,
|
||||||
|
std::string const &to, std::string const &label, std::string style,
|
||||||
|
CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes)
|
||||||
|
{
|
||||||
|
return ContainsMatcher(
|
||||||
|
CasedString(fmt::format("{} o-[{}]- {} : {}", from, style, to, label),
|
||||||
|
caseSensitivity));
|
||||||
|
}
|
||||||
|
|
||||||
|
ContainsMatcher IsAssociationWithStyle(std::string const &from,
|
||||||
|
std::string const &to, std::string const &label, std::string style,
|
||||||
|
CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes)
|
||||||
|
{
|
||||||
|
return ContainsMatcher(
|
||||||
|
CasedString(fmt::format("{} -[{}]-> {} : {}", from, style, to, label),
|
||||||
|
caseSensitivity));
|
||||||
|
}
|
||||||
|
|
||||||
|
ContainsMatcher IsCompositionWithStyle(std::string const &from,
|
||||||
|
std::string const &to, std::string const &label, std::string style,
|
||||||
|
CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes)
|
||||||
|
{
|
||||||
|
return ContainsMatcher(
|
||||||
|
CasedString(fmt::format("{} *-[{}]- {} : {}", from, style, to, label),
|
||||||
|
caseSensitivity));
|
||||||
}
|
}
|
||||||
|
|
||||||
ContainsMatcher IsInstantiation(std::string const &from, std::string const &to,
|
ContainsMatcher IsInstantiation(std::string const &from, std::string const &to,
|
||||||
@@ -262,6 +324,14 @@ ContainsMatcher IsDependency(std::string const &from, std::string const &to,
|
|||||||
CasedString(fmt::format("{} ..> {}", from, to), caseSensitivity));
|
CasedString(fmt::format("{} ..> {}", from, to), caseSensitivity));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ContainsMatcher HasNote(std::string const &cls, std::string const &position,
|
||||||
|
std::string const ¬e,
|
||||||
|
CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes)
|
||||||
|
{
|
||||||
|
return ContainsMatcher(CasedString(
|
||||||
|
fmt::format("note {} of {}", position, cls), caseSensitivity));
|
||||||
|
}
|
||||||
|
|
||||||
template <typename... Ts>
|
template <typename... Ts>
|
||||||
ContainsMatcher IsMethod(std::string const &name,
|
ContainsMatcher IsMethod(std::string const &name,
|
||||||
std::string const &type = "void",
|
std::string const &type = "void",
|
||||||
|
|||||||
@@ -78,6 +78,18 @@ test_cases:
|
|||||||
- name: t00027
|
- name: t00027
|
||||||
title: Template decorator pattern
|
title: Template decorator pattern
|
||||||
description:
|
description:
|
||||||
|
- name: t00028
|
||||||
|
title: PlantUML note decorator test case
|
||||||
|
description:
|
||||||
|
- name: t00029
|
||||||
|
title: PlantUML skip decorator test case
|
||||||
|
description:
|
||||||
|
- name: t00030
|
||||||
|
title: PlantUML relationship decorators test case
|
||||||
|
description:
|
||||||
|
- name: t00031
|
||||||
|
title: PlantUML style decorator test case
|
||||||
|
description:
|
||||||
Sequence diagrams:
|
Sequence diagrams:
|
||||||
- name: t20001
|
- name: t20001
|
||||||
title: Basic sequence diagram
|
title: Basic sequence diagram
|
||||||
|
|||||||
235
tests/test_decorator_parser.cc
Normal file
235
tests/test_decorator_parser.cc
Normal file
@@ -0,0 +1,235 @@
|
|||||||
|
/**
|
||||||
|
* tests/test_decorator_parser.cc
|
||||||
|
*
|
||||||
|
* Copyright (c) 2021 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.
|
||||||
|
*/
|
||||||
|
#define CATCH_CONFIG_MAIN
|
||||||
|
|
||||||
|
#include "uml/decorators.h"
|
||||||
|
|
||||||
|
#include "catch.h"
|
||||||
|
|
||||||
|
TEST_CASE("Test decorator parser on regular comment", "[unit-test]")
|
||||||
|
{
|
||||||
|
std::string comment = R"(
|
||||||
|
\brief This is a comment.
|
||||||
|
|
||||||
|
This is a longer comment.
|
||||||
|
|
||||||
|
\param a int an int
|
||||||
|
\param b float a float
|
||||||
|
|
||||||
|
)";
|
||||||
|
|
||||||
|
using namespace clanguml::decorators;
|
||||||
|
|
||||||
|
auto decorators = parse(comment);
|
||||||
|
|
||||||
|
CHECK(decorators.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Test decorator parser on note", "[unit-test]")
|
||||||
|
{
|
||||||
|
std::string comment = R"(
|
||||||
|
\brief This is a comment.
|
||||||
|
|
||||||
|
This is a longer comment.
|
||||||
|
|
||||||
|
\
|
||||||
|
|
||||||
|
\param a int an int
|
||||||
|
\param b float a float
|
||||||
|
|
||||||
|
\uml{note[left] This is a comment}
|
||||||
|
@uml{note[ top ]
|
||||||
|
|
||||||
|
This is a comment }
|
||||||
|
\uml{note This is a comment}
|
||||||
|
\uml{note[] This is a comment}
|
||||||
|
)";
|
||||||
|
|
||||||
|
using namespace clanguml::decorators;
|
||||||
|
|
||||||
|
auto decorators = parse(comment);
|
||||||
|
|
||||||
|
CHECK(decorators.size() == 4);
|
||||||
|
|
||||||
|
auto n1 = std::dynamic_pointer_cast<note>(decorators.at(0));
|
||||||
|
auto n2 = std::dynamic_pointer_cast<note>(decorators.at(1));
|
||||||
|
auto n3 = std::dynamic_pointer_cast<note>(decorators.at(2));
|
||||||
|
auto n4 = std::dynamic_pointer_cast<note>(decorators.at(3));
|
||||||
|
|
||||||
|
CHECK(n1);
|
||||||
|
CHECK(n1->position == "left");
|
||||||
|
CHECK(n1->text == "This is a comment");
|
||||||
|
|
||||||
|
CHECK(n2);
|
||||||
|
CHECK(n2->position == "top");
|
||||||
|
CHECK(n2->text == "This is a comment");
|
||||||
|
|
||||||
|
CHECK(n3);
|
||||||
|
CHECK(n3->position == "left");
|
||||||
|
CHECK(n3->text == "This is a comment");
|
||||||
|
|
||||||
|
CHECK(n4);
|
||||||
|
CHECK(n4->position == "left");
|
||||||
|
CHECK(n4->text == "This is a comment");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Test decorator parser on note with custom tag", "[unit-test]")
|
||||||
|
{
|
||||||
|
std::string comment = R"(
|
||||||
|
\brief This is a comment.
|
||||||
|
|
||||||
|
This is a longer comment.
|
||||||
|
|
||||||
|
\
|
||||||
|
|
||||||
|
\param a int an int
|
||||||
|
\param b float a float
|
||||||
|
|
||||||
|
\clanguml{note[left] This is a comment}
|
||||||
|
@clanguml{note[ top ]
|
||||||
|
|
||||||
|
This is a comment }
|
||||||
|
\clanguml{note This is a comment}
|
||||||
|
\clanguml{note[] This is a comment}
|
||||||
|
)";
|
||||||
|
|
||||||
|
using namespace clanguml::decorators;
|
||||||
|
|
||||||
|
auto decorators = parse(comment, "clanguml");
|
||||||
|
|
||||||
|
CHECK(decorators.size() == 4);
|
||||||
|
|
||||||
|
auto n1 = std::dynamic_pointer_cast<note>(decorators.at(0));
|
||||||
|
auto n2 = std::dynamic_pointer_cast<note>(decorators.at(1));
|
||||||
|
auto n3 = std::dynamic_pointer_cast<note>(decorators.at(2));
|
||||||
|
auto n4 = std::dynamic_pointer_cast<note>(decorators.at(3));
|
||||||
|
|
||||||
|
CHECK(n1);
|
||||||
|
CHECK(n1->position == "left");
|
||||||
|
CHECK(n1->text == "This is a comment");
|
||||||
|
|
||||||
|
CHECK(n2);
|
||||||
|
CHECK(n2->position == "top");
|
||||||
|
CHECK(n2->text == "This is a comment");
|
||||||
|
|
||||||
|
CHECK(n3);
|
||||||
|
CHECK(n3->position == "left");
|
||||||
|
CHECK(n3->text == "This is a comment");
|
||||||
|
|
||||||
|
CHECK(n4);
|
||||||
|
CHECK(n4->position == "left");
|
||||||
|
CHECK(n4->text == "This is a comment");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Test decorator parser on style", "[unit-test]")
|
||||||
|
{
|
||||||
|
std::string comment = R"(
|
||||||
|
\uml{style[#green,dashed,thickness=4]}
|
||||||
|
)";
|
||||||
|
|
||||||
|
using namespace clanguml::decorators;
|
||||||
|
|
||||||
|
auto decorators = parse(comment);
|
||||||
|
|
||||||
|
CHECK(decorators.size() == 1);
|
||||||
|
|
||||||
|
auto n1 = std::dynamic_pointer_cast<style>(decorators.at(0));
|
||||||
|
|
||||||
|
CHECK(n1);
|
||||||
|
CHECK(n1->spec == "#green,dashed,thickness=4");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Test decorator parser on aggregation", "[unit-test]")
|
||||||
|
{
|
||||||
|
std::string comment = R"(
|
||||||
|
\uml{aggregation[0..1:0..*]}
|
||||||
|
)";
|
||||||
|
|
||||||
|
using namespace clanguml::decorators;
|
||||||
|
|
||||||
|
auto decorators = parse(comment);
|
||||||
|
|
||||||
|
CHECK(decorators.size() == 1);
|
||||||
|
|
||||||
|
auto n1 = std::dynamic_pointer_cast<aggregation>(decorators.at(0));
|
||||||
|
|
||||||
|
CHECK(n1);
|
||||||
|
CHECK(n1->multiplicity == "0..1:0..*");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Test decorator parser on skip", "[unit-test]")
|
||||||
|
{
|
||||||
|
std::string comment = R"(
|
||||||
|
\uml{skip}
|
||||||
|
)";
|
||||||
|
|
||||||
|
using namespace clanguml::decorators;
|
||||||
|
|
||||||
|
auto decorators = parse(comment);
|
||||||
|
|
||||||
|
CHECK(decorators.size() == 1);
|
||||||
|
|
||||||
|
auto n1 = std::dynamic_pointer_cast<skip>(decorators.at(0));
|
||||||
|
|
||||||
|
CHECK(n1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Test decorator parser on skiprelationship", "[unit-test]")
|
||||||
|
{
|
||||||
|
std::string comment = R"(
|
||||||
|
\uml{skiprelationship}
|
||||||
|
)";
|
||||||
|
|
||||||
|
using namespace clanguml::decorators;
|
||||||
|
|
||||||
|
auto decorators = parse(comment);
|
||||||
|
|
||||||
|
CHECK(decorators.size() == 1);
|
||||||
|
|
||||||
|
auto n1 = std::dynamic_pointer_cast<skip_relationship>(decorators.at(0));
|
||||||
|
|
||||||
|
CHECK(n1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Test decorator parser on diagram scope", "[unit-test]")
|
||||||
|
{
|
||||||
|
std::string comment = R"(
|
||||||
|
\uml{note:diagram1, diagram2,
|
||||||
|
diagram3[left] Note only for diagrams 1, 2 and 3.}
|
||||||
|
)";
|
||||||
|
|
||||||
|
using namespace clanguml::decorators;
|
||||||
|
|
||||||
|
auto decorators = parse(comment);
|
||||||
|
|
||||||
|
CHECK(decorators.size() == 1);
|
||||||
|
|
||||||
|
auto n1 = std::dynamic_pointer_cast<note>(decorators.at(0));
|
||||||
|
|
||||||
|
CHECK(n1);
|
||||||
|
CHECK(n1->diagrams.size() == 3);
|
||||||
|
CHECK(n1->diagrams[0] == "diagram1");
|
||||||
|
CHECK(n1->diagrams[1] == "diagram2");
|
||||||
|
CHECK(n1->diagrams[2] == "diagram3");
|
||||||
|
|
||||||
|
CHECK(n1->position == "left");
|
||||||
|
CHECK(n1->text == "Note only for diagrams 1, 2 and 3.");
|
||||||
|
|
||||||
|
CHECK(n1->applies_to_diagram("diagram2"));
|
||||||
|
CHECK(!n1->applies_to_diagram("diagram4"));
|
||||||
|
}
|
||||||
@@ -46,3 +46,25 @@ TEST_CASE("Test ns_relative", "[unit-test]")
|
|||||||
"static const vector<a>&");
|
"static const vector<a>&");
|
||||||
CHECK(ns_relative({"clanguml::t0"}, "clanguml::t0") == "t0");
|
CHECK(ns_relative({"clanguml::t0"}, "clanguml::t0") == "t0");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Test replace_all", "[unit-test]")
|
||||||
|
{
|
||||||
|
using namespace clanguml::util;
|
||||||
|
|
||||||
|
const std::string orig = R"(
|
||||||
|
Lorem ipsum \clanguml{note} something something...
|
||||||
|
|
||||||
|
\clanguml{style}
|
||||||
|
|
||||||
|
)";
|
||||||
|
std::string text{orig};
|
||||||
|
|
||||||
|
CHECK(replace_all(text, "NOTHERE", "HERE") == false);
|
||||||
|
CHECK(replace_all(text, "\\clanguml", "@clanguml") == true);
|
||||||
|
CHECK(replace_all(text, "something", "nothing") == true);
|
||||||
|
CHECK(replace_all(text, "something", "nothing") == false);
|
||||||
|
CHECK(replace_all(text, "nothing", "something") == true);
|
||||||
|
CHECK(replace_all(text, "@clanguml", "\\clanguml") == true);
|
||||||
|
|
||||||
|
CHECK(text == orig);
|
||||||
|
}
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ with open(r'tests/test_cases.yaml') as f:
|
|||||||
tc.write("{}\n".format(test_case['description']))
|
tc.write("{}\n".format(test_case['description']))
|
||||||
|
|
||||||
# Write test config file
|
# Write test config file
|
||||||
config = open('tests/{0}/.clanguml'.format(name), 'r').read()
|
config = open('tests/{0}/.clang-uml'.format(name), 'r').read()
|
||||||
tc.write("## Config\n")
|
tc.write("## Config\n")
|
||||||
tc.write("```yaml\n")
|
tc.write("```yaml\n")
|
||||||
tc.write(config)
|
tc.write(config)
|
||||||
|
|||||||
Reference in New Issue
Block a user