0.6.1
C++ to UML diagram generator based on Clang
Loading...
Searching...
No Matches
diagram_filter.h
Go to the documentation of this file.
1/**
2 * @file src/common/model/filters/diagram_filter.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#include "common/clang_utils.h"
26#include "common/model/enums.h"
29#include "common/model/tvl.h"
30#include "config/config.h"
33#include "util/memoized.h"
34
35#include <filesystem>
36#include <utility>
37
39
40class diagram_filter_factory;
41
44
45/**
46 * Diagram filters can be add in 2 modes:
47 * - inclusive - the elements that match are included in the diagram
48 * - exclusive - the elements that match are excluded from the diagram
49 */
50enum class filter_t {
51 kInclusive, /*!< Filter is inclusive */
52 kExclusive /*!< Filter is exclusve */
53};
54
55namespace detail {
56template <typename ElementT, typename DiagramT>
58
59template <typename ElementT, typename DiagramT>
61 const DiagramT &d, const std::string &full_name);
62
63template <typename ElementT> eid_t destination_comparator(const ElementT &e)
64{
65 return e.id();
66}
67
69} // namespace detail
70
71/**
72 * @brief Base class for any diagram filter.
73 *
74 * This class acts as a visitor for diagram elements. It provides a set of
75 * common methods which can be overriden by specific filters. If a filter
76 * does not implement a specific method, it is ignored through the 3 value
77 * logic implemented in @see clanguml::common::model::tvl
78 *
79 * @embed{filter_visitor_hierarchy_class.svg}
80 */
82public:
84
85 virtual ~filter_visitor() = default;
86
87 virtual tvl::value_t match(
88 const diagram &d, const common::model::element &e) const;
89
90 virtual tvl::value_t match(
91 const diagram &d, const common::model::relationship &r) const;
92
93 virtual tvl::value_t match(
94 const diagram &d, const common::model::relationship_t &r) const;
95
96 virtual tvl::value_t match(
97 const diagram &d, const common::model::access_t &a) const;
98
99 virtual tvl::value_t match(
100 const diagram &d, const common::model::namespace_ &ns) const;
101
102 virtual tvl::value_t match(
103 const diagram &d, const common::model::source_file &f) const;
104
105 virtual tvl::value_t match(
106 const diagram &d, const common::model::source_location &f) const;
107
108 virtual tvl::value_t match(
109 const diagram &d, const class_diagram::model::class_method &m) const;
110
111 virtual tvl::value_t match(
112 const diagram &d, const class_diagram::model::class_member &m) const;
113
114 virtual tvl::value_t match(
115 const diagram &d, const class_diagram::model::objc_method &m) const;
116
117 virtual tvl::value_t match(
118 const diagram &d, const class_diagram::model::objc_member &m) const;
119
120 virtual tvl::value_t match(
121 const diagram &d, const sequence_diagram::model::participant &p) const;
122
123 bool is_inclusive() const;
124 bool is_exclusive() const;
125
126 filter_t type() const;
127 filter_mode_t mode() const;
128 void set_mode(filter_mode_t mode);
129
130 virtual void reset();
131
132private:
134 filter_mode_t mode_{filter_mode_t::basic};
135};
136
139 filter_t type, std::vector<std::unique_ptr<filter_visitor>> filters);
140
141 ~anyof_filter() override = default;
142
144 const diagram &d, const common::model::element &e) const override;
145
146 tvl::value_t match(const diagram &d,
147 const common::model::relationship_t &r) const override;
148
150 const diagram &d, const common::model::access_t &a) const override;
151
153 const diagram &d, const common::model::namespace_ &ns) const override;
154
156 const diagram &d, const common::model::source_file &f) const override;
157
158 tvl::value_t match(const diagram &d,
159 const common::model::source_location &f) const override;
160
161 tvl::value_t match(const diagram &d,
162 const class_diagram::model::class_method &m) const override;
163
164 tvl::value_t match(const diagram &d,
165 const class_diagram::model::class_member &m) const override;
166
167 tvl::value_t match(const diagram &d,
168 const sequence_diagram::model::participant &p) const override;
169
170 void reset() override;
171
172private:
173 template <typename E>
174 tvl::value_t match_anyof(const diagram &d, const E &element) const
175 {
176 auto result = tvl::any_of(filters_.begin(), filters_.end(),
177 [&d, &element](const auto &f) { return f->match(d, element); });
178
179 if (mode() == filter_mode_t::advanced && !d.complete())
180 return type() == filter_t::kInclusive;
181
182 return result;
183 }
184
185 std::vector<std::unique_ptr<filter_visitor>> filters_;
186};
187
190 filter_t type, std::vector<std::unique_ptr<filter_visitor>> filters);
191
192 ~allof_filter() override = default;
193
195 const diagram &d, const common::model::element &e) const override;
196
197 tvl::value_t match(const diagram &d,
198 const common::model::relationship_t &r) const override;
199
201 const diagram &d, const common::model::access_t &a) const override;
202
204 const diagram &d, const common::model::namespace_ &ns) const override;
205
207 const diagram &d, const common::model::source_file &f) const override;
208
209 tvl::value_t match(const diagram &d,
210 const common::model::source_location &f) const override;
211
212 tvl::value_t match(const diagram &d,
213 const class_diagram::model::class_method &m) const override;
214
215 tvl::value_t match(const diagram &d,
216 const class_diagram::model::class_member &m) const override;
217
218 tvl::value_t match(const diagram &d,
219 const sequence_diagram::model::participant &p) const override;
220
221 void reset() override;
222
223private:
224 template <typename E>
225 tvl::value_t match_allof(const diagram &d, const E &element) const
226 {
227 return tvl::all_of(filters_.begin(), filters_.end(),
228 [&d, &element](const auto &f) { return f->match(d, element); });
229 }
230
231 std::vector<std::unique_ptr<filter_visitor>> filters_;
232};
233
234/**
235 * Match namespace or diagram element to a set of specified namespaces or
236 * regex patterns.
237 */
240 filter_t type, std::vector<common::namespace_or_regex> namespaces);
241
242 ~namespace_filter() override = default;
243
244 tvl::value_t match(const diagram &d, const namespace_ &ns) const override;
245
246 tvl::value_t match(const diagram &d, const element &e) const override;
247
248 tvl::value_t match(const diagram &d,
249 const sequence_diagram::model::participant &p) const override;
250
251private:
252 std::vector<common::namespace_or_regex> namespaces_;
253};
254
255/**
256 * Match diagram elements to a set of specified modules or
257 * module regex patterns.
258 */
260 modules_filter(filter_t type, std::vector<common::string_or_regex> modules);
261
262 ~modules_filter() override = default;
263
264 tvl::value_t match(const diagram &d, const element &e) const override;
265
266private:
267 std::vector<common::string_or_regex> modules_;
268};
269
270/**
271 * Match element's name to a set of names or regex patterns.
272 */
275 filter_t type, std::vector<config::element_filter_t> elements);
276
277 ~element_filter() override = default;
278
279 tvl::value_t match(const diagram &d, const element &e) const override;
280
281 tvl::value_t match(const diagram &d,
282 const class_diagram::model::class_method &m) const override;
283
284 tvl::value_t match(const diagram &d,
285 const class_diagram::model::class_member &m) const override;
286
287 tvl::value_t match(const diagram &d,
288 const class_diagram::model::objc_method &m) const override;
289
290 tvl::value_t match(const diagram &d,
291 const class_diagram::model::objc_member &m) const override;
292
293 tvl::value_t match(const diagram &d,
294 const sequence_diagram::model::participant &p) const override;
295
296private:
297 std::vector<config::element_filter_t> elements_;
298};
299
300/**
301 * Match diagram elements based on elements type (e.g. class).
302 */
304 element_type_filter(filter_t type, std::vector<std::string> element_types);
305
306 ~element_type_filter() override = default;
307
308 tvl::value_t match(const diagram &d, const element &e) const override;
309
310private:
311 std::vector<std::string> element_types_;
312};
313
314/**
315 * Match class methods based on their category (e.g. operator).
316 */
319 filter_t type, std::vector<config::method_type> method_types);
320
321 ~method_type_filter() override = default;
322
323 tvl::value_t match(const diagram &d,
324 const class_diagram::model::class_method &e) const override;
325
326private:
327 std::vector<config::method_type> method_types_;
328};
329
330/**
331 * Sequence diagram callee type filter.
332 */
334 callee_filter(filter_t type, std::vector<config::callee_type> callee_types);
335
336 ~callee_filter() override = default;
337
338 tvl::value_t match(const diagram &d,
339 const sequence_diagram::model::participant &p) const override;
340
341private:
342 std::vector<config::callee_type> callee_types_;
343};
344
345/**
346 * Match element based on whether it is a subclass of a set of base classes,
347 * or one of them.
348 */
350 subclass_filter(filter_t type, std::vector<common::string_or_regex> roots);
351
352 ~subclass_filter() override = default;
353
354 tvl::value_t match(const diagram &d, const element &e) const override;
355
356private:
357 std::vector<common::string_or_regex> roots_;
358};
359
360/**
361 * Match element based on whether it is a parent of a set of children, or one
362 * of them.
363 */
365 parents_filter(filter_t type, std::vector<common::string_or_regex> roots);
366
367 ~parents_filter() override = default;
368
369 tvl::value_t match(const diagram &d, const element &e) const override;
370
371private:
372 std::vector<common::string_or_regex> children_;
373};
374
375/**
376 * @brief Common template for filters involving traversing relationship graph.
377 *
378 * This class template provides a common implementation of a diagram
379 * relationship graph traversal. It is used for filters, which need to check
380 * for instance, whether an element is in some kind of relationship with other
381 * element.
382 *
383 * @tparam DiagramT Diagram type
384 * @tparam ElementT Element type
385 * @tparam ConfigEntryT Type of configuration option used to specify initial
386 * elements for traversal
387 * @tparam MatchOverrideT Type of the matched element
388 */
389template <typename DiagramT, typename ElementT,
390 typename ConfigEntryT = std::string,
391 typename MatchOverrideT = common::model::element>
393 edge_traversal_filter(filter_t type, std::vector<ConfigEntryT> roots,
394 relationship_t relationship, bool forward = false)
396 , roots_{std::move(roots)}
398 , forward_{forward}
399 {
400 }
401
402 ~edge_traversal_filter() override = default;
403
404 void reset() override
405 {
406 initialized_ = false;
407 matching_elements_.clear();
408 };
409
410 tvl::value_t match(const diagram &d, const MatchOverrideT &e) const override
411 {
412 // This filter should only be run only on diagram models after the
413 // entire AST has been visited
414 if (!d.complete())
415 return {};
416
417 if (!check_diagram_type<DiagramT>(d.type()))
418 return {};
419
420 if (roots_.empty())
421 return {};
422
423 const auto &cd = dynamic_cast<const DiagramT &>(d);
424
425 // Calculate the set of matching elements
426 init(cd);
427
428 const auto &fn = e.full_name(false);
429 auto element_ref = detail::get<ElementT>(cd, fn);
430
431 if (!element_ref.has_value())
432 // Couldn't find the element in the diagram
433 return false;
434
435 // Now check if the e element is contained in the calculated set
436 return std::any_of(matching_elements_.begin(), matching_elements_.end(),
437 [&e](const auto &te) {
438 std::string tes = te.get().full_name(false);
439 std::string es = e.full_name(false);
440
441 if (tes == es)
442 return true;
443
444 return false;
445 });
446 }
447
448private:
449 template <typename C, typename D>
450 bool add_adjacent(const C &from, const D &to,
451 const std::vector<relationship_t> &relationships) const
452 {
453 bool added_new_element{false};
454
455 for (const auto &from_el : from) {
456 // Check if any of its relationships of type relationship_
457 // points to an element already in the matching_elements_
458 // set
459 for (const auto &rel : from_el.get().relationships()) {
460 // Consider only if connected by one of specified relationships
461 if (util::contains(relationships, rel.type())) {
462 for (const auto &to_el : to) {
463 if (rel.destination() ==
464 detail::destination_comparator(to_el.get())) {
465 const auto &to_add = forward_ ? to_el : from_el;
466 if (matching_elements_.insert(to_add).second)
467 added_new_element = true;
468 }
469 }
470 }
471 }
472 }
473
474 return added_new_element;
475 }
476
477 void add_parents(const DiagramT &cd) const
478 {
479 decltype(matching_elements_) parents;
480
482 matching_elements_, [&cd, &parents](const auto &element) {
483 auto parent = detail::get<ElementT, DiagramT>(
484 cd, element.get().path().to_string());
485
486 while (parent.has_value()) {
487 parents.emplace(parent.value());
488 parent = detail::get<ElementT, DiagramT>(
489 cd, parent.value().path().to_string());
490 }
491 });
492
493 matching_elements_.insert(std::begin(parents), std::end(parents));
494 }
495
496 void init(const DiagramT &cd) const
497 {
498 if (initialized_)
499 return;
500
501 matching_elements_.clear();
502
503 // First get all elements specified in the filter configuration
504 // which will serve as starting points for the search
505 // of matching elements
506 for (const auto &root_pattern : roots_) {
507 if constexpr (std::is_same_v<ConfigEntryT,
509 auto root_refs = cd.template find<ElementT>(root_pattern);
510
511 for (auto &root : root_refs) {
512 if (root.has_value())
513 matching_elements_.emplace(root.value());
514 }
515 }
516 else {
517 auto root_ref = detail::get<ElementT>(cd, root_pattern);
518 if (root_ref.has_value()) {
519 matching_elements_.emplace(root_ref.value());
520 }
521 }
522 }
523
524 bool keep_looking{!matching_elements_.empty()};
525 while (keep_looking) {
526 keep_looking = false;
527 if (forward_) {
528 if (add_adjacent(matching_elements_, detail::view<ElementT>(cd),
529 {relationship_}))
530 keep_looking = true;
531 }
532 else {
533 if (add_adjacent(detail::view<ElementT>(cd), matching_elements_,
534 {relationship_}))
535 keep_looking = true;
536 }
537 }
538
539 // For nested diagrams, include also parent elements
540 if ((type() == filter_t::kInclusive) &&
541 (cd.type() == common::model::diagram_t::kPackage)) {
542 add_parents(cd);
543 }
544
545 initialized_ = true;
546 }
547
548 std::vector<ConfigEntryT> roots_;
550 mutable bool initialized_{false};
553};
554
555/**
556 * Match relationship types.
557 */
560 filter_t type, std::vector<relationship_t> relationships);
561
562 ~relationship_filter() override = default;
563
565 const diagram &d, const relationship_t &r) const override;
566
567private:
568 std::vector<relationship_t> relationships_;
569};
570
571/**
572 * Match class members and methods based on access (public, protected, private).
573 */
575 access_filter(filter_t type, std::vector<access_t> access);
576
577 ~access_filter() override = default;
578
579 tvl::value_t match(const diagram &d, const access_t &a) const override;
580
581private:
582 std::vector<access_t> access_;
583};
584
585/**
586 * Match diagram elements based on module access (public or private).
587 */
589 module_access_filter(filter_t type, std::vector<module_access_t> access);
590
591 ~module_access_filter() override = default;
592
593 tvl::value_t match(const diagram &d, const element &a) const override;
594
595private:
596 std::vector<module_access_t> access_;
597};
598
599/**
600 * Match diagram elements which are in within a 'radius' distance relationship
601 * to any of the elements specified in context.
602 */
604 context_filter(filter_t type, std::vector<config::context_config> context);
605
606 ~context_filter() override = default;
607
608 tvl::value_t match(const diagram &d, const element &r) const override;
609
610private:
611 void initialize(const diagram &d) const;
612
613 void initialize_effective_context(const diagram &d, unsigned idx) const;
614
616 const diagram &d, unsigned idx) const;
617
619 const diagram &d, unsigned idx) const;
620
621 bool is_inward(relationship_t r) const;
622
623 bool is_outward(relationship_t r) const;
624
625 template <typename NestedTraitT>
626 void process_elements(const diagram &d, const NestedTraitT &nt,
627 const config::context_config &context_cfg,
628 std::set<eid_t> &effective_context,
629 std::set<eid_t> &current_iteration_context) const
630 {
631 if (d.type() == diagram_t::kClass) {
632 for (const auto &p : nt) {
633 if (auto *pkg = dynamic_cast<package *>(p.get());
634 pkg != nullptr) {
635 process_elements(d, *pkg, context_cfg, effective_context,
636 current_iteration_context);
637 }
638 else {
639 for (const relationship &rel : p.get()->relationships()) {
640 if (!should_include(context_cfg, rel.type()) ||
641 !d.should_include(rel.type())) {
642 continue;
643 }
644 // At the moment aggregation and composition are added
645 // in the model in reverse direction, so we don't
646 // consider them here
647 if (context_cfg.direction ==
649 (rel.type() == relationship_t::kAggregation ||
650 rel.type() == relationship_t::kComposition)) {
651 continue;
652 }
653 if (context_cfg.direction ==
655 (rel.type() != relationship_t::kAggregation &&
656 rel.type() != relationship_t::kComposition)) {
657 continue;
658 }
659 for (const auto &element_id : effective_context) {
660 if (rel.destination() == element_id)
661 current_iteration_context.emplace(
662 p.get()->id());
663 }
664 }
665
666 // Now search current effective_context elements and add any
667 // elements of any type in the diagram which have a
668 // relationship to that element
669 for (const auto element_id : effective_context) {
670 const auto &maybe_element = d.get(element_id);
671
672 if (!maybe_element)
673 continue;
674
675 for (const relationship &rel :
676 maybe_element.value().relationships()) {
677 if (!should_include(context_cfg, rel.type()) ||
678 !d.should_include(rel.type())) {
679 continue;
680 }
681
682 if ((context_cfg.direction ==
684 (rel.type() != relationship_t::kAggregation &&
685 rel.type() !=
687 continue;
688 }
689 if (context_cfg.direction ==
691 (rel.type() == relationship_t::kAggregation ||
692 rel.type() ==
694 continue;
695 }
696
697 if (rel.destination() == p.get()->id())
698 current_iteration_context.emplace(
699 p.get()->id());
700 }
701 }
702 }
703 }
704 }
705 else if (d.type() == diagram_t::kPackage) {
706 for (const auto &p : nt) {
707 auto *pkg = dynamic_cast<package *>(p.get());
708 if (pkg == nullptr)
709 continue;
710
711 process_elements(d, *pkg, context_cfg, effective_context,
712 current_iteration_context);
713
714 for (const relationship &rel : p.get()->relationships()) {
715 if (!should_include(context_cfg, rel.type()) ||
716 !d.should_include(rel.type())) {
717 continue;
718 }
719 // At the moment aggregation and composition are added
720 // in the model in reverse direction, so we don't
721 // consider them here
722 if (context_cfg.direction ==
724 (rel.type() == relationship_t::kAggregation ||
725 rel.type() == relationship_t::kComposition)) {
726 continue;
727 }
728 if (context_cfg.direction ==
730 (rel.type() != relationship_t::kAggregation &&
731 rel.type() != relationship_t::kComposition)) {
732 continue;
733 }
734 for (const auto &element_id : effective_context) {
735 if (rel.destination() == element_id)
736 current_iteration_context.emplace(p.get()->id());
737 }
738 }
739
740 // Now search current effective_context elements and add any
741 // elements of any type in the diagram which have a
742 // relationship to that element
743 for (const auto element_id : effective_context) {
744 const auto &maybe_element = d.get(element_id);
745
746 if (!maybe_element)
747 continue;
748
749 for (const relationship &rel :
750 maybe_element.value().relationships()) {
751 if (!should_include(context_cfg, rel.type()) ||
752 !d.should_include(rel.type())) {
753 continue;
754 }
755
756 if ((context_cfg.direction ==
758 (rel.type() != relationship_t::kAggregation &&
759 rel.type() != relationship_t::kComposition)) {
760 continue;
761 }
762 if (context_cfg.direction ==
764 (rel.type() == relationship_t::kAggregation ||
765 rel.type() == relationship_t::kComposition)) {
766 continue;
767 }
768
769 if (rel.destination() == p.get()->id())
770 current_iteration_context.emplace(p.get()->id());
771 }
772 }
773 }
774 }
775 }
776
777 template <typename DiagramT>
779 const config::context_config &context_cfg,
780 std::set<eid_t> &effective_context,
781 std::set<eid_t> &current_iteration_context) const
782 {
783 const auto &cd = dynamic_cast<const DiagramT &>(d);
784
786 cd, cd, context_cfg, effective_context, current_iteration_context);
787 }
788
789 bool should_include(
790 const config::context_config &context_cfg, relationship_t r) const;
791
792 std::vector<config::context_config> context_;
793
794 /*!
795 * Represents all elements which should belong to the diagram based
796 * on this filter. It is populated by the initialize() method.
797 */
798 mutable std::vector<std::set<eid_t>> effective_contexts_;
799
800 /*! Flag to mark whether the filter context has been computed */
801 mutable bool initialized_{false};
802};
803
804/**
805 * Match elements based on their source location, whether it matches to
806 * a specified file paths.
807 */
809 public util::memoized<std::filesystem::path, bool,
810 std::filesystem::path> {
811 paths_filter(filter_t type, const std::vector<std::string> &p,
812 const std::filesystem::path &root);
813
814 ~paths_filter() override = default;
815
817 const diagram &d, const common::model::source_file &r) const override;
818
819 tvl::value_t match(const diagram &d,
820 const common::model::source_location &sl) const override;
821
823 const diagram &d, const common::model::element &e) const override;
824
825private:
826 std::vector<std::filesystem::path> paths_;
827 std::filesystem::path root_;
828};
829
830/**
831 * Match class method based on specified method categories.
832 */
834 class_method_filter(filter_t type, std::unique_ptr<access_filter> af,
835 std::unique_ptr<method_type_filter> mtf);
836
837 ~class_method_filter() override = default;
838
839 tvl::value_t match(const diagram &d,
840 const class_diagram::model::class_method &m) const override;
841
842private:
843 std::unique_ptr<access_filter> access_filter_;
844 std::unique_ptr<method_type_filter> method_type_filter_;
845};
846
847/**
848 * Match class members.
849 */
851 class_member_filter(filter_t type, std::unique_ptr<access_filter> af);
852
853 ~class_member_filter() override = default;
854
855 tvl::value_t match(const diagram &d,
856 const class_diagram::model::class_member &m) const override;
857
858private:
859 std::unique_ptr<access_filter> access_filter_;
860};
861
863
864/**
865 * @brief Composite of all diagrams filters.
866 *
867 * Instances of this class contain all filters specified in configuration
868 * file for a given diagram.
869 *
870 * @embed{diagram_filter_context_class.svg}
871 *
872 * @see clanguml::common::model::filter_visitor
873 */
875private:
877
878public:
881
882 void add_filter(filter_t filter_type, std::unique_ptr<filter_visitor> fv);
883
884 /**
885 * Add inclusive filter.
886 *
887 * @param fv Filter visitor.
888 */
889 void add_inclusive_filter(std::unique_ptr<filter_visitor> fv);
890
891 /** Add exclusive filter.
892 *
893 * @param fv Filter visitor.
894 */
895 void add_exclusive_filter(std::unique_ptr<filter_visitor> fv);
896
897 /**
898 * `should_include` overload for namespace and name.
899 *
900 * @param ns Namespace
901 * @param name Name
902 * @return Match result.
903 */
904 bool should_include(const namespace_ &ns, const std::string &name) const;
905
906 /**
907 * Generic `should_include` overload for various diagram elements.
908 *
909 * @tparam T Type to to match - must match one of filter_visitor's
910 * match(T)
911 * @param e Value of type T to match
912 * @return Match result.
913 */
914 template <typename T> bool should_include(const T &e) const
915 {
916 auto exc = tvl::any_of(
917 exclusive_.begin(), exclusive_.end(), [this, &e](const auto &ex) {
918 assert(ex.get() != nullptr);
919
920 return ex->match(diagram_, e);
921 });
922
923 if (tvl::is_true(exc))
924 return false;
925
926 auto inc = tvl::all_of(
927 inclusive_.begin(), inclusive_.end(), [this, &e](const auto &in) {
928 assert(in.get() != nullptr);
929
930 return in->match(diagram_, e);
931 });
932
933 return static_cast<bool>(tvl::is_undefined(inc) || tvl::is_true(inc));
934 }
935
936 filter_mode_t mode() const;
937
938 void set_mode(filter_mode_t mode);
939
940 void reset();
941
943
944private:
945 /*! List of inclusive filters */
946 std::vector<std::unique_ptr<filter_visitor>> inclusive_;
947
948 /*! List of exclusive filters */
949 std::vector<std::unique_ptr<filter_visitor>> exclusive_;
950
951 /*! Reference to the diagram model */
953
954 filter_mode_t mode_{filter_mode_t::basic};
955};
956
957template <typename Collection>
958void apply_filter(Collection &col, const diagram_filter &filter)
959{
960 col.erase(std::remove_if(col.begin(), col.end(),
961 [&filter](auto &&element) {
962 return !filter.should_include(element);
963 }),
964 col.end());
965}
966
967template <typename T>
969 std::vector<std::reference_wrapper<T>> &col, const diagram_filter &filter)
970{
971 col.erase(std::remove_if(col.begin(), col.end(),
972 [&filter](auto &&element) {
973 return !filter.should_include(element.get());
974 }),
975 col.end());
976}
977
978template <>
979bool diagram_filter::should_include<std::string>(const std::string &name) const;
980} // namespace clanguml::common::model