0.6.0
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-2025 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 Check whether the diagram is empty
158 *
159 * @return True, if diagram is empty
160 */
161 bool is_empty() const override;
162
163 void apply_filter() override;
164
165 /**
166 * @brief Get reference to vector of elements of specific type
167 *
168 * @tparam ElementT Type of elements view
169 * @return Reference to elements vector
170 */
171 template <typename ElementT>
173
174private:
175 /**
176 * @brief Add element using module as diagram path
177 *
178 * @tparam ElementT Element type
179 * @param e Element to add
180 * @return True, if the element was added
181 */
182 template <typename ElementT>
184 const common::model::path &parent_path, std::unique_ptr<ElementT> &&e);
185
186 /**
187 * @brief Add element using namespace as diagram path
188 *
189 * @tparam ElementT Element type
190 * @param e Element to add
191 * @return True, if the element was added
192 */
193 template <typename ElementT>
194 bool add_with_namespace_path(std::unique_ptr<ElementT> &&e);
195
196 /**
197 * @brief Add element using relative filesystem path as diagram path
198 *
199 * @tparam ElementT Element type
200 * @param parent_path Path to diagram elements parent package
201 * @param e Element to add
202 * @return True, if the element was added
203 */
204 template <typename ElementT>
206 const common::model::path &parent_path, std::unique_ptr<ElementT> &&e);
207};
208
209template <typename ElementT>
210opt_ref<ElementT> diagram::find(const std::string &name) const
211{
212 for (const auto &element : element_view<ElementT>::view()) {
213 const auto full_name = element.get().full_name(false);
214
215 if (full_name == name) {
216 return {element};
217 }
218 }
219
220 return {};
221}
222
223template <typename ElementT> opt_ref<ElementT> diagram::find(eid_t id) const
224{
225 for (const auto &element : element_view<ElementT>::view()) {
226 if (element.get().id() == id) {
227 return {element};
228 }
229 }
230
231 return {};
232}
233
234template <typename ElementT>
235std::vector<opt_ref<ElementT>> diagram::find(
236 const common::string_or_regex &pattern) const
237{
238 std::vector<opt_ref<ElementT>> result;
239
240 for (const auto &element : element_view<ElementT>::view()) {
241 const auto full_name = element.get().full_name(false);
242
243 if (pattern == full_name) {
244 result.emplace_back(element);
245 }
246 }
247
248 return result;
249}
250
251template <typename ElementT>
252bool diagram::add_with_namespace_path(std::unique_ptr<ElementT> &&p)
253{
254 LOG_DBG(
255 "Adding package: {}, {}, [{}]", p->name(), p->full_name(true), p->id());
256
257 auto ns = p->get_relative_namespace();
258 auto p_ref = std::ref(*p);
259
260 auto res = add_element(ns, std::move(p));
261 if (res)
262 element_view<ElementT>::add(p_ref);
263
264 return res;
265}
266
267template <typename ElementT>
269 const common::model::path &parent_path, std::unique_ptr<ElementT> &&p)
270{
271 LOG_DBG("Adding package: {}, {}, {}, [{}]", p->name(), p->full_name(false),
272 parent_path.to_string(), p->id());
273
274 // Make sure all parent modules are already packages in the
275 // model
276 auto module_relative_to = path{p->using_namespace()};
277
278 for (auto it = parent_path.begin(); it != parent_path.end(); it++) {
279 auto pkg = std::make_unique<common::model::package>(
280 p->using_namespace(), common::model::path_type::kModule);
281 pkg->set_name(*it);
282
283 auto module_relative_part = common::model::path(
284 parent_path.begin(), it, common::model::path_type::kModule);
285
286 auto module_absolute_path = module_relative_to | module_relative_part;
287 pkg->set_module(module_absolute_path.to_string());
288 pkg->set_namespace(module_absolute_path);
289
290 auto package_absolute_path = module_absolute_path | pkg->name();
291
292 pkg->set_id(common::to_id(package_absolute_path.to_string()));
293
294 auto p_ref = std::ref(*pkg);
295
296 auto res = add_element(module_relative_part, std::move(pkg));
297 if (res)
298 element_view<ElementT>::add(p_ref);
299 }
300
301 auto p_ref = std::ref(*p);
302
303 auto res = add_element(parent_path, std::move(p));
304 if (res)
305 element_view<ElementT>::add(p_ref);
306
307 return res;
308}
309
310template <typename ElementT>
312 const common::model::path &parent_path, std::unique_ptr<ElementT> &&p)
313{
314 LOG_TRACE("Adding package: {}, {}", p->name(), p->full_name(true));
315
316 // Make sure all parent directories are already packages in the
317 // model
318 for (auto it = parent_path.begin(); it != parent_path.end(); it++) {
319 auto pkg = std::make_unique<common::model::package>(
320 p->using_namespace(), common::model::path_type::kFilesystem);
321 pkg->set_name(*it);
322 auto ns = common::model::path(
324 pkg->set_namespace(ns);
325 pkg->set_id(common::to_id(pkg->full_name(false)));
326
327 add_with_filesystem_path(ns, std::move(pkg));
328 }
329
330 auto pp = std::ref(*p);
331 auto res = add_element(parent_path, std::move(p));
332 if (res)
333 element_view<ElementT>::add(pp);
334
335 return res;
336}
337
338template <typename ElementT>
340{
341 return element_view<ElementT>::view();
342}
343
344} // namespace clanguml::package_diagram::model
345
346namespace clanguml::common::model {
347template <>
348bool check_diagram_type<clanguml::package_diagram::model::diagram>(diagram_t t);
349}