0.5.4
C++ to UML diagram generator based on Clang
Loading...
Searching...
No Matches
decorators.cc
Go to the documentation of this file.
1/**
2 * @file src/decorators/decorators.cc
3 *
4 * Copyright (c) 2021-2024 Bartek Kryza <bkryza@gmail.com>
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18#include "decorators.h"
19
20#include "util/util.h"
21
22#include <string>
23#include <string_view>
24
26
27std::shared_ptr<decorator> decorator::from_string(std::string_view c)
28{
29 if (c.find(note::label) == 0) {
30 return note::from_string(c);
31 }
32 if (c.find(skip_relationship::label) == 0) {
34 }
35 if (c.find(skip::label) == 0) {
36 return skip::from_string(c);
37 }
38 if (c.find(style::label) == 0) {
39 return style::from_string(c);
40 }
41 if (c.find(aggregation::label) == 0) {
43 }
44 if (c.find(composition::label) == 0) {
46 }
47 if (c.find(association::label) == 0) {
49 }
50 if (c.find(call::label) == 0) {
51 return call::from_string(c);
52 }
53
54 return {};
55}
56
57bool decorator::applies_to_diagram(const std::string &name)
58{
59 return diagrams.empty() ||
60 (std::find(diagrams.begin(), diagrams.end(), name) != diagrams.end());
61}
62
63decorator_toks decorator::tokenize(const std::string &label, std::string_view c)
64{
66 res.label = label;
67 size_t pos{};
68 std::string_view::const_iterator it = c.begin();
69 std::advance(it, label.size());
70
71 if (*it == ':') {
72 std::advance(it, 1);
73
74 pos = std::distance(c.begin(), it);
75 // If the diagram list is provided after ':', [] is mandatory
76 // even if empty
77 auto d = c.substr(pos, c.find('[', pos) - pos);
78 if (!d.empty()) {
79 std::string d_str{d};
80 d_str.erase(std::remove_if(d_str.begin(), d_str.end(),
81 static_cast<int (*)(int)>(std::isspace)),
82 d_str.end());
83 res.diagrams = util::split(d_str, ",");
84 }
85
86 std::advance(it, d.size());
87 }
88
89 if (*it == '[') {
90 std::advance(it, 1);
91
92 pos = std::distance(c.begin(), it);
93 res.param = c.substr(pos, c.find(']', pos) - pos);
94
95 std::advance(it, res.param.size() + 1);
96 }
97 else if (std::isspace(*it) != 0) {
98 std::advance(it, 1);
99 }
100
101 pos = std::distance(c.begin(), it);
102 res.text = c.substr(pos, c.find('}', pos) - pos);
103 res.text = util::trim(res.text);
104 res.param = util::trim(res.param);
105
106 return res;
107}
108
109std::shared_ptr<decorator> note::from_string(std::string_view c)
110{
111 auto res = std::make_shared<note>();
112 auto toks = res->tokenize(note::label, c);
113
114 res->diagrams = toks.diagrams;
115
116 if (!toks.param.empty())
117 res->position = toks.param;
118
119 res->text = toks.text;
120
121 return res;
122}
123
124std::shared_ptr<decorator> skip::from_string(std::string_view /*c*/)
125{
126 return std::make_shared<skip>();
127}
128
129std::shared_ptr<decorator> skip_relationship::from_string(
130 std::string_view /*c*/)
131{
132 return std::make_shared<skip_relationship>();
133}
134
135std::shared_ptr<decorator> style::from_string(std::string_view c)
136{
137 auto res = std::make_shared<style>();
138 auto toks = res->tokenize(style::label, c);
139
140 res->diagrams = toks.diagrams;
141 res->spec = toks.param;
142
143 return res;
144}
145
146std::shared_ptr<decorator> aggregation::from_string(std::string_view c)
147{
148 auto res = std::make_shared<aggregation>();
149 auto toks = res->tokenize(aggregation::label, c);
150
151 res->diagrams = toks.diagrams;
152 res->multiplicity = toks.param;
153
154 return res;
155}
156
157std::shared_ptr<decorator> composition::from_string(std::string_view c)
158{
159 auto res = std::make_shared<composition>();
160 auto toks = res->tokenize(composition::label, c);
161
162 res->diagrams = toks.diagrams;
163 res->multiplicity = toks.param;
164
165 return res;
166}
167
168std::shared_ptr<decorator> association::from_string(std::string_view c)
169{
170 auto res = std::make_shared<association>();
171 auto toks = res->tokenize(association::label, c);
172
173 res->diagrams = toks.diagrams;
174 res->multiplicity = toks.param;
175
176 return res;
177}
178
179std::shared_ptr<decorator> call::from_string(std::string_view c)
180{
181 auto res = std::make_shared<call>();
182 auto toks = res->tokenize(call::label, c);
183
184 res->diagrams = toks.diagrams;
185 res->callee = util::trim(toks.text);
186
187 return res;
188}
189
190std::pair<std::vector<std::shared_ptr<decorator>>, std::string> parse(
191 std::string documentation_block, const std::string &clanguml_tag)
192{
193 std::vector<std::shared_ptr<decorator>> res;
194 std::string stripped_comment;
195
196 const std::string begin_tag{"@" + clanguml_tag};
197 const auto begin_tag_size = begin_tag.size();
198
199 // First replace all \uml occurences with @uml
201 documentation_block, "\\" + clanguml_tag, "@" + clanguml_tag);
202 documentation_block = util::trim(documentation_block);
203
204 const std::string_view block_view{documentation_block};
205
206 auto pos = block_view.find("@" + clanguml_tag + "{");
207
208 if (pos == std::string::npos) {
209 // This comment had no uml directives
210 return {{}, util::trim(documentation_block)};
211 }
212
213 size_t last_end_pos{0};
214 while (pos < documentation_block.size()) {
215 auto c_begin = pos + begin_tag_size;
216 auto c_end = block_view.find('}', c_begin);
217
218 if (c_end == std::string::npos) {
219 return {res, util::trim(stripped_comment)};
220 }
221
222 auto com =
223 decorator::from_string(block_view.substr(c_begin + 1, c_end - 2));
224
225 if (com)
226 res.emplace_back(std::move(com));
227
228 const auto in_between_length = pos - last_end_pos;
229 stripped_comment += block_view.substr(last_end_pos, in_between_length);
230
231 last_end_pos = pos + (c_end - c_begin + begin_tag_size + 1);
232
233 pos = block_view.find("@" + clanguml_tag + "{", c_end);
234 }
235
236 return {res, util::trim(stripped_comment)};
237};
238
239} // namespace clanguml::decorators