0.5.4
C++ to UML diagram generator based on Clang
Loading...
Searching...
No Matches
diagram.h
Go to the documentation of this file.
1/**
2 * @file src/package_diagram/model/diagram.h
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#pragma once
19
23
24#include <string>
25#include <vector>
26
28
34
38
39/**
40 * @brief Package diagram model.
41 */
44 public nested_trait_ns {
45public:
46 diagram() = default;
47
48 diagram(const diagram &) = delete;
49 diagram(diagram &&) = default;
50 diagram &operator=(const diagram &) = delete;
51 diagram &operator=(diagram &&) = default;
52
53 /**
54 * @brief Get the diagram model type - in this case package.
55 *
56 * @return Type of package diagram.
57 */
58 common::model::diagram_t type() const override;
59
60 /**
61 * @brief Get list of references to packages in the diagram model.
62 *
63 * @return List of references to packages in the diagram model.
64 */
66
67 /**
68 * @brief Search for element in the diagram by fully qualified name.
69 *
70 * @param full_name Fully qualified element name.
71 * @return Optional reference to a diagram element.
72 */
73 opt_ref<diagram_element> get(const std::string &full_name) const override;
74
75 /**
76 * @brief Search for element in the diagram by id.
77 *
78 * @param id Element id.
79 * @return Optional reference to a diagram element.
80 */
81 opt_ref<diagram_element> get(eid_t id) const override;
82
83 /**
84 * @brief Find an element in the diagram by name.
85 *
86 * This method allows for typed search, where the type of searched for
87 * element is determined from template specialization.
88 *
89 * @tparam ElementT Type of element (e.g. package)
90 * @param name Fully qualified name of the element
91 * @return Optional reference to a diagram element
92 */
93 template <typename ElementT>
94 opt_ref<ElementT> find(const std::string &name) const;
95
96 /**
97 * @brief Find an element in the diagram by id.
98 *
99 * This method allows for typed search, where the type of searched for
100 * element is determined from template specialization.
101 *
102 * @tparam ElementT Type of element (e.g. package)
103 * @param id Id of the element
104 * @return Optional reference to a diagram element
105 */
106 template <typename ElementT> opt_ref<ElementT> find(eid_t id) const;
107
108 /**
109 * @brief Find elements in the diagram by regex pattern.
110 *
111 * This method allows for typed search, where the type of searched for
112 * element is determined from template specialization.
113 *
114 * @tparam ElementT Type of element (e.g. class_)
115 * @param name String or regex pattern
116 * @return List of optional references to matched elements.
117 */
118 template <typename ElementT>
119 std::vector<opt_ref<ElementT>> find(
120 const clanguml::common::string_or_regex &pattern) const;
121
122 /**
123 * @brief Add diagram element at nested path
124 *
125 * This method handled both diagrams where packages are created from
126 * namespaces, as well as those were packages are created from project
127 * subdirectories.
128 *
129 * @tparam ElementT Type of diagram element to add
130 * @param parent_path Package nested path where the element should be added
131 * @param e Diagram element to add
132 * @return True, if the element was added.
133 */
134 template <typename ElementT>
135 bool add(const path &parent_path, std::unique_ptr<ElementT> &&e)
136 {
137 if (parent_path.type() == common::model::path_type::kNamespace) {
138 return add_with_namespace_path(std::move(e));
139 }
140
141 if (parent_path.type() == common::model::path_type::kModule) {
142 return add_with_module_path(parent_path, std::move(e));
143 }
144
145 return add_with_filesystem_path(parent_path, std::move(e));
146 }
147
148 /**
149 * @brief Get alias of existing diagram element
150 *
151 * @param id Id of a package in the diagram
152 * @return PlantUML alias of the element
153 */
154 std::string to_alias(eid_t id) const;
155
156 /**
157 * @brief Return the elements JSON context for inja templates.
158 *
159 * @return JSON node with elements context.
160 */
161 inja::json context() const override;
162
163 /**
164 * @brief Check whether the diagram is empty
165 *
166 * @return True, if diagram is empty
167 */
168 bool is_empty() const override;
169
170 void apply_filter() override;
171
172private:
173 /**
174 * @brief Add element using module as diagram path
175 *
176 * @tparam ElementT Element type
177 * @param e Element to add
178 * @return True, if the element was added
179 */
180 template <typename ElementT>
182 const common::model::path &parent_path, std::unique_ptr<ElementT> &&e);
183
184 /**
185 * @brief Add element using namespace as diagram path
186 *
187 * @tparam ElementT Element type
188 * @param e Element to add
189 * @return True, if the element was added
190 */
191 template <typename ElementT>
192 bool add_with_namespace_path(std::unique_ptr<ElementT> &&e);
193
194 /**
195 * @brief Add element using relative filesystem path as diagram path
196 *
197 * @tparam ElementT Element type
198 * @param parent_path Path to diagram elements parent package
199 * @param e Element to add
200 * @return True, if the element was added
201 */
202 template <typename ElementT>
204 const common::model::path &parent_path, std::unique_ptr<ElementT> &&e);
205};
206
207template <typename ElementT>
208opt_ref<ElementT> diagram::find(const std::string &name) const
209{
210 for (const auto &element : element_view<ElementT>::view()) {
211 const auto full_name = element.get().full_name(false);
212
213 if (full_name == name) {
214 return {element};
215 }
216 }
217
218 return {};
219}
220
221template <typename ElementT> opt_ref<ElementT> diagram::find(eid_t id) const
222{
223 for (const auto &element : element_view<ElementT>::view()) {
224 if (element.get().id() == id) {
225 return {element};
226 }
227 }
228
229 return {};
230}
231
232template <typename ElementT>
233std::vector<opt_ref<ElementT>> diagram::find(
234 const common::string_or_regex &pattern) const
235{
236 std::vector<opt_ref<ElementT>> result;
237
238 for (const auto &element : element_view<ElementT>::view()) {
239 const auto full_name = element.get().full_name(false);
240
241 if (pattern == full_name) {
242 result.emplace_back(element);
243 }
244 }
245
246 return result;
247}
248
249template <typename ElementT>
250bool diagram::add_with_namespace_path(std::unique_ptr<ElementT> &&p)
251{
252 LOG_DBG(
253 "Adding package: {}, {}, [{}]", p->name(), p->full_name(true), p->id());
254
255 auto ns = p->get_relative_namespace();
256 auto p_ref = std::ref(*p);
257
258 auto res = add_element(ns, std::move(p));
259 if (res)
260 element_view<ElementT>::add(p_ref);
261
262 return res;
263}
264
265template <typename ElementT>
267 const common::model::path &parent_path, std::unique_ptr<ElementT> &&p)
268{
269 LOG_DBG("Adding package: {}, {}, {}, [{}]", p->name(), p->full_name(false),
270 parent_path.to_string(), p->id());
271
272 // Make sure all parent modules are already packages in the
273 // model
274 auto module_relative_to = path{p->using_namespace()};
275
276 for (auto it = parent_path.begin(); it != parent_path.end(); it++) {
277 auto pkg = std::make_unique<common::model::package>(
278 p->using_namespace(), common::model::path_type::kModule);
279 pkg->set_name(*it);
280
281 auto module_relative_part = common::model::path(
282 parent_path.begin(), it, common::model::path_type::kModule);
283
284 auto module_absolute_path = module_relative_to | module_relative_part;
285 pkg->set_module(module_absolute_path.to_string());
286 pkg->set_namespace(module_absolute_path);
287
288 auto package_absolute_path = module_absolute_path | pkg->name();
289
290 pkg->set_id(common::to_id(package_absolute_path.to_string()));
291
292 auto p_ref = std::ref(*pkg);
293
294 auto res = add_element(module_relative_part, std::move(pkg));
295 if (res)
296 element_view<ElementT>::add(p_ref);
297 }
298
299 auto p_ref = std::ref(*p);
300
301 auto res = add_element(parent_path, std::move(p));
302 if (res)
303 element_view<ElementT>::add(p_ref);
304
305 return res;
306}
307
308template <typename ElementT>
310 const common::model::path &parent_path, std::unique_ptr<ElementT> &&p)
311{
312 LOG_DBG("Adding package: {}, {}", p->name(), p->full_name(true));
313
314 // Make sure all parent directories are already packages in the
315 // model
316 for (auto it = parent_path.begin(); it != parent_path.end(); it++) {
317 auto pkg =
318 std::make_unique<common::model::package>(p->using_namespace());
319 pkg->set_name(*it);
320 auto ns = common::model::path(parent_path.begin(), it);
321 pkg->set_namespace(ns);
322 pkg->set_id(common::to_id(pkg->full_name(false)));
323
324 add_with_filesystem_path(ns, std::move(pkg));
325 }
326
327 auto pp = std::ref(*p);
328 auto res = add_element(parent_path, std::move(p));
329 if (res)
330 element_view<ElementT>::add(pp);
331
332 return res;
333}
334
335} // namespace clanguml::package_diagram::model
336
337namespace clanguml::common::model {
338template <>
339bool check_diagram_type<clanguml::package_diagram::model::diagram>(diagram_t t);
340}