Added decorator diagram scope
This commit is contained in:
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 {
|
||||||
|
|
||||||
|
/// \clanguml{note[top] A class note.}
|
||||||
|
class A {
|
||||||
|
};
|
||||||
|
|
||||||
|
/// \clanguml{note[] B class note.}
|
||||||
|
class B {
|
||||||
|
};
|
||||||
|
|
||||||
|
///
|
||||||
|
/// @clanguml{note:t00028_class[bottom] C class note.}
|
||||||
|
/// This is class C.
|
||||||
|
class C {
|
||||||
|
};
|
||||||
|
|
||||||
|
/// \clanguml{note
|
||||||
|
/// D
|
||||||
|
/// class
|
||||||
|
/// note.}
|
||||||
|
class D {
|
||||||
|
};
|
||||||
|
|
||||||
|
/// \clanguml{note E template class note.}
|
||||||
|
template <typename T> class E {
|
||||||
|
T param;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// \clanguml{note:other_diagram[left] G class note.}
|
||||||
|
class G {
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @clanguml{note[ bottom ] F enum note.}
|
||||||
|
enum class F { one, two, three };
|
||||||
|
|
||||||
|
/// \clanguml{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 |
@@ -288,7 +288,7 @@ public:
|
|||||||
//
|
//
|
||||||
for (auto decorator : c.decorators) {
|
for (auto decorator : c.decorators) {
|
||||||
auto note = std::dynamic_pointer_cast<decorators::note>(decorator);
|
auto note = std::dynamic_pointer_cast<decorators::note>(decorator);
|
||||||
if (note) {
|
if (note && note->applies_to_diagram(m_config.name)) {
|
||||||
ostr << "note " << note->position << " of " << c.alias() << '\n'
|
ostr << "note " << note->position << " of " << c.alias() << '\n'
|
||||||
<< note->text << '\n'
|
<< note->text << '\n'
|
||||||
<< "end note\n";
|
<< "end note\n";
|
||||||
@@ -355,7 +355,7 @@ public:
|
|||||||
//
|
//
|
||||||
for (auto decorator : e.decorators) {
|
for (auto decorator : e.decorators) {
|
||||||
auto note = std::dynamic_pointer_cast<decorators::note>(decorator);
|
auto note = std::dynamic_pointer_cast<decorators::note>(decorator);
|
||||||
if (note) {
|
if (note && note->applies_to_diagram(m_config.name)) {
|
||||||
ostr << "note " << note->position << " of " << e.alias() << '\n'
|
ostr << "note " << note->position << " of " << e.alias() << '\n'
|
||||||
<< note->text << '\n'
|
<< note->text << '\n'
|
||||||
<< "end note\n";
|
<< "end note\n";
|
||||||
|
|||||||
@@ -52,66 +52,90 @@ std::shared_ptr<decorator> decorator::from_string(std::string_view c)
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<decorator> note::from_string(std::string_view c)
|
bool decorator::applies_to_diagram(std::string name)
|
||||||
{
|
{
|
||||||
auto res = std::make_shared<note>();
|
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();
|
auto it = c.begin();
|
||||||
std::advance(it, note::label.size());
|
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 == '[') {
|
if (*it == '[') {
|
||||||
std::advance(it, 1);
|
std::advance(it, 1);
|
||||||
|
|
||||||
auto pos = std::distance(c.begin(), it);
|
pos = std::distance(c.begin(), it);
|
||||||
auto note_position = c.substr(pos, c.find("]", pos) - pos);
|
res.param = c.substr(pos, c.find("]", pos) - pos);
|
||||||
if (!note_position.empty())
|
|
||||||
res->position = note_position;
|
|
||||||
|
|
||||||
std::advance(it, note_position.size() + 1);
|
std::advance(it, res.param.size() + 1);
|
||||||
}
|
}
|
||||||
else if (std::isspace(*it)) {
|
else if (std::isspace(*it)) {
|
||||||
std::advance(it, 1);
|
std::advance(it, 1);
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
LOG_WARN("Invalid note decorator: {}", c);
|
pos = std::distance(c.begin(), it);
|
||||||
return {};
|
res.text = c.substr(pos, c.find("}", pos) - pos);
|
||||||
|
res.text = util::trim(res.text);
|
||||||
|
res.param = util::trim(res.param);
|
||||||
|
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto pos = std::distance(c.begin(), it);
|
std::shared_ptr<decorator> note::from_string(std::string_view c)
|
||||||
res->text = c.substr(pos, c.find("}", pos) - pos);
|
{
|
||||||
|
auto res = std::make_shared<note>();
|
||||||
|
auto toks = res->tokenize(note::label, c);
|
||||||
|
|
||||||
res->position = util::trim(res->position);
|
res->diagrams = toks.diagrams;
|
||||||
res->text = util::trim(res->text);
|
|
||||||
|
if (!toks.param.empty())
|
||||||
|
res->position = toks.param;
|
||||||
|
|
||||||
|
res->text = toks.text;
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<decorator> skip::from_string(std::string_view c)
|
std::shared_ptr<decorator> skip::from_string(std::string_view c)
|
||||||
{
|
{
|
||||||
auto res = std::make_shared<skip>();
|
return std::make_shared<skip>();
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<decorator> skip_relationship::from_string(std::string_view c)
|
std::shared_ptr<decorator> skip_relationship::from_string(std::string_view c)
|
||||||
{
|
{
|
||||||
auto res = std::make_shared<skip_relationship>();
|
return std::make_shared<skip_relationship>();
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<decorator> style::from_string(std::string_view c)
|
std::shared_ptr<decorator> style::from_string(std::string_view c)
|
||||||
{
|
{
|
||||||
auto res = std::make_shared<style>();
|
auto res = std::make_shared<style>();
|
||||||
auto it = c.begin();
|
auto toks = res->tokenize(style::label, c);
|
||||||
std::advance(it, style::label.size());
|
|
||||||
|
|
||||||
if (*it != '[')
|
res->diagrams = toks.diagrams;
|
||||||
return {};
|
res->spec = toks.param;
|
||||||
|
|
||||||
std::advance(it, 1);
|
|
||||||
|
|
||||||
auto pos = std::distance(c.begin(), it);
|
|
||||||
res->spec = c.substr(pos, c.find("]", pos) - pos);
|
|
||||||
|
|
||||||
res->spec = util::trim(res->spec);
|
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@@ -119,18 +143,10 @@ std::shared_ptr<decorator> style::from_string(std::string_view c)
|
|||||||
std::shared_ptr<decorator> aggregation::from_string(std::string_view c)
|
std::shared_ptr<decorator> aggregation::from_string(std::string_view c)
|
||||||
{
|
{
|
||||||
auto res = std::make_shared<aggregation>();
|
auto res = std::make_shared<aggregation>();
|
||||||
auto it = c.begin();
|
auto toks = res->tokenize(aggregation::label, c);
|
||||||
std::advance(it, aggregation::label.size());
|
|
||||||
|
|
||||||
if (*it != '[')
|
res->diagrams = toks.diagrams;
|
||||||
return {};
|
res->multiplicity = toks.param;
|
||||||
|
|
||||||
std::advance(it, 1);
|
|
||||||
|
|
||||||
auto pos = std::distance(c.begin(), it);
|
|
||||||
res->multiplicity = c.substr(pos, c.find("]", pos) - pos);
|
|
||||||
|
|
||||||
res->multiplicity = util::trim(res->multiplicity);
|
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,10 +24,25 @@
|
|||||||
|
|
||||||
namespace clanguml {
|
namespace clanguml {
|
||||||
namespace decorators {
|
namespace decorators {
|
||||||
|
// \clanguml{label:diagram1,diagram2[param] text}
|
||||||
|
struct decorator_toks {
|
||||||
|
std::string label;
|
||||||
|
std::vector<std::string> diagrams;
|
||||||
|
std::string param;
|
||||||
|
std::string text;
|
||||||
|
};
|
||||||
|
|
||||||
struct decorator {
|
struct decorator {
|
||||||
|
std::vector<std::string> diagrams;
|
||||||
|
|
||||||
virtual ~decorator() = default;
|
virtual ~decorator() = default;
|
||||||
|
|
||||||
static std::shared_ptr<decorator> from_string(std::string_view c);
|
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 {
|
struct note : public decorator {
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ class B {
|
|||||||
};
|
};
|
||||||
|
|
||||||
///
|
///
|
||||||
/// @clanguml{note[bottom] C class note.}
|
/// @clanguml{note:t00028_class[bottom] C class note.}
|
||||||
/// This is class C.
|
/// This is class C.
|
||||||
class C {
|
class C {
|
||||||
};
|
};
|
||||||
@@ -30,6 +30,10 @@ template <typename T> class E {
|
|||||||
T param;
|
T param;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// \clanguml{note:other_diagram[left] G class note.}
|
||||||
|
class G {
|
||||||
|
};
|
||||||
|
|
||||||
/// @clanguml{note[ bottom ] F enum note.}
|
/// @clanguml{note[ bottom ] F enum note.}
|
||||||
enum class F { one, two, three };
|
enum class F { one, two, three };
|
||||||
|
|
||||||
@@ -44,6 +48,8 @@ class R {
|
|||||||
std::vector<std::shared_ptr<D>> ddd;
|
std::vector<std::shared_ptr<D>> ddd;
|
||||||
|
|
||||||
E<int> eee;
|
E<int> eee;
|
||||||
|
|
||||||
|
G **ggg;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace t00028
|
} // namespace t00028
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ note.)";
|
|||||||
REQUIRE_THAT(puml, HasNote(_A("D"), "left", d_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("E<T>"), "left", "E template class note."));
|
||||||
REQUIRE_THAT(puml, HasNote(_A("F"), "bottom", "F enum 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."));
|
REQUIRE_THAT(puml, HasNote(_A("R"), "right", "R class note."));
|
||||||
|
|
||||||
save_puml(
|
save_puml(
|
||||||
|
|||||||
@@ -157,3 +157,31 @@ TEST_CASE("Test decorator parser on skiprelationship", "[unit-test]")
|
|||||||
|
|
||||||
CHECK(n1);
|
CHECK(n1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Test decorator parser on diagram scope", "[unit-test]")
|
||||||
|
{
|
||||||
|
std::string comment = R"(
|
||||||
|
\clanguml{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"));
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user