0.5.4
C++ to UML diagram generator based on Clang
Loading...
Searching...
No Matches
diagram_filter.cc
Go to the documentation of this file.
1/**
2 * @file src/common/model/diagram_filter.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
19#include "diagram_filter.h"
20
21#include <utility>
22
25#include "glob/glob.hpp"
29
31
32namespace detail {
33
34template <>
37{
38 return d.classes();
39}
40
41template <>
44{
45 return d.enums();
46}
47
48template <>
51{
52 return d.packages();
53}
54
55template <>
58{
59 return d.files();
60}
61
62template <>
64 const class_diagram::model::diagram &d, const std::string &full_name)
65{
66 return d.find<class_diagram::model::class_>(full_name);
67}
68
69template <>
71 const package_diagram::model::diagram &d, const std::string &full_name)
72{
73 return d.find<package>(full_name);
74}
75
76template <>
78 const include_diagram::model::diagram &d, const std::string &full_name)
79{
80 return d.find<source_file>(full_name);
81}
82
83template <>
86{
87 return f.id();
88}
89} // namespace detail
90
92 : type_{type}
93{
94}
95
97 const diagram & /*d*/, const common::model::element & /*e*/) const
98{
99 return {};
100}
101
103 const diagram &d, const common::model::relationship &r) const
104{
105 return match(d, r.type());
106}
107
109 const diagram & /*d*/, const common::model::relationship_t & /*r*/) const
110{
111 return {};
112}
113
115 const diagram & /*d*/, const common::model::access_t & /*a*/) const
116{
117 return {};
118}
119
121 const diagram & /*d*/, const common::model::namespace_ & /*ns*/) const
122{
123 return {};
124}
125
127 const diagram & /*d*/, const common::model::source_file & /*f*/) const
128{
129 return {};
130}
131
133 const diagram & /*d*/, const common::model::source_location & /*f*/) const
134{
135 return {};
136}
137
139 const diagram &d, const class_diagram::model::class_method &m) const
140{
141 return match(d, m.access());
142}
143
145 const diagram &d, const class_diagram::model::class_member &m) const
146{
147 return match(d, m.access());
148}
149
151 const sequence_diagram::model::participant & /*p*/) const
152{
153 return {};
154}
155
157{
158 return type_ == filter_t::kInclusive;
159}
160
162{
163 return type_ == filter_t::kExclusive;
164}
165
167
168filter_mode_t filter_visitor::mode() const { return mode_; }
169
170void filter_visitor::set_mode(filter_mode_t mode) { mode_ = mode; }
171
173 filter_t type, std::vector<std::unique_ptr<filter_visitor>> filters)
174 : filter_visitor{type}
175 , filters_{std::move(filters)}
176{
177}
178
180 const diagram &d, const common::model::element &e) const
181{
182 return match_anyof(d, e);
183}
184
186 const diagram &d, const common::model::relationship_t &r) const
187{
188 return match_anyof(d, r);
189}
190
192 const diagram &d, const common::model::access_t &a) const
193{
194 return match_anyof(d, a);
195}
196
198 const diagram &d, const common::model::namespace_ &ns) const
199{
200 return match_anyof(d, ns);
201}
202
204 const diagram &d, const common::model::source_file &f) const
205{
206 return match_anyof(d, f);
207}
208
210 const diagram &d, const common::model::source_location &f) const
211{
212 return match_anyof(d, f);
213}
214
216 const diagram &d, const class_diagram::model::class_method &m) const
217{
218 return match_anyof(d, m);
219}
220
222 const diagram &d, const class_diagram::model::class_member &m) const
223{
224 return match_anyof(d, m);
225}
226
228 const diagram &d, const sequence_diagram::model::participant &p) const
229{
230 return match_anyof(d, p);
231}
232
234 filter_t type, std::vector<std::unique_ptr<filter_visitor>> filters)
235 : filter_visitor{type}
236 , filters_{std::move(filters)}
237{
238}
239
241 const diagram &d, const common::model::element &e) const
242{
243 return match_allof(d, e);
244}
245
247 const diagram &d, const common::model::relationship_t &r) const
248{
249 return match_allof(d, r);
250}
251
253 const diagram &d, const common::model::access_t &a) const
254{
255 return match_allof(d, a);
256}
257
259 const diagram &d, const common::model::namespace_ &ns) const
260{
261 return match_allof(d, ns);
262}
263
265 const diagram &d, const common::model::source_file &f) const
266{
267 return match_allof(d, f);
268}
269
271 const diagram &d, const common::model::source_location &f) const
272{
273 return match_allof(d, f);
274}
275
277 const diagram &d, const class_diagram::model::class_method &m) const
278{
279 return match_allof(d, m);
280}
281
283 const diagram &d, const class_diagram::model::class_member &m) const
284{
285 return match_allof(d, m);
286}
287
289 const diagram &d, const sequence_diagram::model::participant &p) const
290{
291 return match_allof(d, p);
292}
293
295 filter_t type, std::vector<common::namespace_or_regex> namespaces)
296 : filter_visitor{type}
297 , namespaces_{std::move(namespaces)}
298{
299}
300
302 const diagram & /*d*/, const namespace_ &ns) const
303{
304 if (ns.is_empty())
305 return {};
306
307 return tvl::any_of(namespaces_.begin(), namespaces_.end(),
308 [&ns, is_inclusive = is_inclusive()](const auto &nsit) {
309 if (std::holds_alternative<namespace_>(nsit.value())) {
310 const auto &ns_pattern = std::get<namespace_>(nsit.value());
311 if (is_inclusive)
312 return ns.starts_with(ns_pattern) ||
313 ns_pattern.starts_with(ns);
314
315 return ns.starts_with(ns_pattern);
316 }
317
318 const auto &regex = std::get<common::regex>(nsit.value());
319 return regex %= ns.to_string();
320 });
321}
322
324{
325 if (d.type() != diagram_t::kPackage &&
326 dynamic_cast<const package *>(&e) != nullptr) {
327 auto result = tvl::any_of(namespaces_.begin(), namespaces_.end(),
328 [&e, is_inclusive = is_inclusive()](const auto &nsit) {
329 if (std::holds_alternative<namespace_>(nsit.value())) {
330 const auto &ns_pattern = std::get<namespace_>(nsit.value());
331
332 auto element_full_name_starts_with_namespace =
333 namespace_{e.name_and_ns()}.starts_with(ns_pattern);
334
335 auto element_full_name_equals_pattern =
336 namespace_{e.name_and_ns()} == ns_pattern;
337
338 auto pattern_starts_with_element_full_name =
339 ns_pattern.starts_with(namespace_{e.name_and_ns()});
340
341 auto result = element_full_name_starts_with_namespace ||
342 element_full_name_equals_pattern;
343
344 if (is_inclusive)
345 result =
346 result || pattern_starts_with_element_full_name;
347
348 return result;
349 }
350
351 return std::get<common::regex>(nsit.value()) %=
352 e.full_name(false);
353 });
354
355 return result;
356 }
357
358 if (d.type() == diagram_t::kPackage) {
359 auto result = tvl::any_of(namespaces_.begin(), namespaces_.end(),
360 [&e, is_inclusive = is_inclusive()](const auto &nsit) {
361 if (std::holds_alternative<namespace_>(nsit.value())) {
362 auto e_ns = namespace_{e.full_name(false)};
363 auto nsit_ns = std::get<namespace_>(nsit.value());
364
365 if (is_inclusive)
366 return e_ns.starts_with(nsit_ns) ||
367 nsit_ns.starts_with(e_ns) || e_ns == nsit_ns;
368
369 return e_ns.starts_with(nsit_ns) || e_ns == nsit_ns;
370 }
371
372 return std::get<common::regex>(nsit.value()) %=
373 e.full_name(false);
374 });
375
376 return result;
377 }
378
379 auto result = tvl::any_of(
380 namespaces_.begin(), namespaces_.end(), [&e](const auto &nsit) {
381 if (std::holds_alternative<namespace_>(nsit.value())) {
382 return e.get_namespace().starts_with(
383 std::get<namespace_>(nsit.value()));
384 }
385
386 return std::get<common::regex>(nsit.value()) %= e.full_name(false);
387 });
388
389 return result;
390}
391
392tvl::value_t namespace_filter::match(
393 const diagram &d, const sequence_diagram::model::participant &p) const
394{
395 return match(d, dynamic_cast<const element &>(p));
396}
397
398modules_filter::modules_filter(
399 filter_t type, std::vector<common::string_or_regex> modules)
400 : filter_visitor{type}
401 , modules_{std::move(modules)}
402{
403}
404
406 const diagram & /*d*/, const element &e) const
407{
408 if (modules_.empty())
409 return {};
410
411 if (!e.module().has_value())
412 return {false};
413
414 auto module_toks =
415 path::split(e.module().value(), path_type::kModule); // NOLINT
416
417 if (dynamic_cast<const package *>(&e) != nullptr &&
419 module_toks.push_back(e.name());
420 }
421
422 auto result = tvl::any_of(modules_.begin(), modules_.end(),
423 [&e, &module_toks](const auto &modit) {
424 if (std::holds_alternative<std::string>(modit.value())) {
425 const auto &modit_str = std::get<std::string>(modit.value());
426 const auto modit_toks =
427 path::split(modit_str, path_type::kModule);
428
429 return e.module() == modit_str ||
430 util::starts_with(module_toks, modit_toks);
431 }
432
433 return std::get<common::regex>(modit.value()) %= e.module().value();
434 });
435
436 return result;
437}
438
440 filter_t type, std::vector<common::string_or_regex> elements)
441 : filter_visitor{type}
442 , elements_{std::move(elements)}
443{
444}
445
447{
448 // Do not apply element filter to packages in class diagrams
449 if (d.type() == diagram_t::kClass && e.type_name() == "package")
450 return std::nullopt;
451
452 return tvl::any_of(
453 elements_.begin(), elements_.end(), [&e](const auto &el) {
454 return ((el == e.full_name(false)) ||
455 (el == fmt::format("::{}", e.full_name(false))));
456 });
457}
458
460 const diagram &d, const sequence_diagram::model::participant &p) const
461{
464
465 if (d.type() != diagram_t::kSequence)
466 return {};
467
468 const auto &sequence_model =
469 dynamic_cast<const sequence_diagram::model::diagram &>(d);
470 return tvl::any_of(elements_.begin(), elements_.end(),
471 [&sequence_model, &p](const auto &el) {
472 if (p.type_name() == "method") {
473
474 const auto &m = dynamic_cast<const method &>(p);
475 const auto class_id = m.class_id();
476 const auto &class_participant =
477 sequence_model.get_participant<participant>(class_id)
478 .value();
479
480 return el == p.full_name(false) ||
481 el == class_participant.full_name(false);
482 }
483
484 return el == p.full_name(false);
485 });
486}
487
489 filter_t type, std::vector<std::string> element_types)
490 : filter_visitor{type}
491 , element_types_{std::move(element_types)}
492{
493}
494
496 const diagram & /*d*/, const element &e) const
497{
498 return tvl::any_of(element_types_.begin(), element_types_.end(),
499 [&e](const auto &element_type) {
500 return e.type_name() == element_type;
501 });
502}
503
505 filter_t type, std::vector<config::method_type> method_types)
506 : filter_visitor{type}
507 , method_types_{std::move(method_types)}
508{
509}
510
512 const diagram & /*d*/, const class_diagram::model::class_method &m) const
513{
514 return tvl::any_of(
515 method_types_.begin(), method_types_.end(), [&m](auto mt) {
516 switch (mt) {
517 case config::method_type::constructor:
518 return m.is_constructor();
519 case config::method_type::destructor:
520 return m.is_destructor();
521 case config::method_type::assignment:
522 return m.is_copy_assignment() || m.is_move_assignment();
523 case config::method_type::operator_:
524 return m.is_operator();
525 case config::method_type::defaulted:
526 return m.is_defaulted();
527 case config::method_type::deleted:
528 return m.is_deleted();
529 case config::method_type::static_:
530 return m.is_static();
531 }
532
533 return false;
534 });
535}
536
538 filter_t type, std::vector<config::callee_type> callee_types)
539 : filter_visitor{type}
540 , callee_types_{std::move(callee_types)}
541{
542}
543
545 const diagram &d, const sequence_diagram::model::participant &p) const
546{
551
552 auto is_lambda = [&d](const method &m) {
553 auto class_participant =
554 dynamic_cast<const sequence_diagram::model::diagram &>(d)
555 .get_participant<class_>(m.class_id());
556 if (!class_participant)
557 return false;
558
559 return class_participant.value().is_lambda();
560 };
561
563 callee_types_.begin(), callee_types_.end(), [&p, is_lambda](auto ct) {
564 auto is_function = [](const participant *p) {
565 return dynamic_cast<const function *>(p) != nullptr;
566 };
567
568 auto is_cuda_kernel = [](const participant *p) {
569 const auto *f = dynamic_cast<const function *>(p);
570 return (f != nullptr) && (f->is_cuda_kernel());
571 };
572
573 auto is_cuda_device = [](const participant *p) {
574 const auto *f = dynamic_cast<const function *>(p);
575 return (f != nullptr) && (f->is_cuda_device());
576 };
577
578 switch (ct) {
580 return p.type_name() == "method";
582 return p.type_name() == "method" &&
583 ((method &)p).is_constructor();
585 return p.type_name() == "method" &&
586 ((method &)p).is_assignment();
588 return is_function(&p) && ((function &)p).is_operator();
590 return p.type_name() == "method" &&
591 ((method &)p).is_defaulted();
593 return is_function(&p) && ((function &)p).is_static();
595 return p.type_name() == "function";
597 return p.type_name() == "function_template";
599 return p.type_name() == "method" && is_lambda((method &)p);
601 return is_cuda_kernel(&p);
603 return is_cuda_device(&p);
604 }
605
606 return false;
607 });
608
609 return res;
610}
611
613 filter_t type, std::vector<common::string_or_regex> roots)
614 : filter_visitor{type}
615 , roots_{std::move(roots)}
616{
617}
618
620{
621 if (d.type() != diagram_t::kClass)
622 return {};
623
624 if (roots_.empty())
625 return {};
626
627 if (!d.complete())
628 return {};
629
630 const auto &cd = dynamic_cast<const class_diagram::model::diagram &>(d);
631
632 // First get all parents of element e
634
635 const auto &fn = e.full_name(false);
636 auto class_ref = cd.find<class_diagram::model::class_>(fn);
637
638 if (!class_ref.has_value())
639 return false;
640
641 parents.emplace(class_ref.value());
642
643 cd.get_parents(parents);
644
645 std::vector<std::string> parents_names;
646 for (const auto p : parents)
647 parents_names.push_back(p.get().full_name(false));
648
649 // Now check if any of the parents matches the roots specified in the
650 // filter config
651 for (const auto &root : roots_) {
652 for (const auto &parent : parents) {
653 auto full_name = parent.get().full_name(false);
654 if (root == full_name) {
655 return true;
656 }
657 }
658 }
659
660 return false;
661}
662
664 filter_t type, std::vector<common::string_or_regex> children)
665 : filter_visitor{type}
666 , children_{std::move(children)}
667{
668}
669
671{
672 if (d.type() != diagram_t::kClass)
673 return {};
674
675 if (children_.empty())
676 return {};
677
678 if (!d.complete())
679 return {};
680
681 const auto &cd = dynamic_cast<const class_diagram::model::diagram &>(d);
682
683 // First get all parents of element e
685
686 for (const auto &child_pattern : children_) {
687 auto child_refs = cd.find<class_diagram::model::class_>(child_pattern);
688
689 for (auto &child : child_refs) {
690 if (child.has_value())
691 parents.emplace(child.value());
692 }
693 }
694
695 cd.get_parents(parents);
696
697 for (const auto &parent : parents) {
698 if (e == parent.get())
699 return true;
700 }
701
702 return false;
703}
704
706 filter_t type, std::vector<relationship_t> relationships)
707 : filter_visitor{type}
708 , relationships_{std::move(relationships)}
709{
710}
711
713 const diagram & /*d*/, const relationship_t &r) const
714{
715 return tvl::any_of(relationships_.begin(), relationships_.end(),
716 [&r](const auto &rel) { return r == rel; });
717}
718
719access_filter::access_filter(filter_t type, std::vector<access_t> access)
720 : filter_visitor{type}
721 , access_{std::move(access)}
722{
723}
724
726 const diagram & /*d*/, const access_t &a) const
727{
728 return tvl::any_of(access_.begin(), access_.end(),
729 [&a](const auto &access) { return a == access; });
730}
731
733 filter_t type, std::vector<module_access_t> access)
734 : filter_visitor{type}
735 , access_{std::move(access)}
736{
737}
738
740 const diagram & /*d*/, const element &e) const
741{
742 if (!e.module().has_value())
743 return {};
744
745 if (access_.empty())
746 return {};
747
748 return tvl::any_of(
749 access_.begin(), access_.end(), [&e](const auto &access) {
750 if (access == module_access_t::kPublic)
751 return !e.module_private();
752
753 return e.module_private();
754 });
755}
756
758 filter_t type, std::vector<config::context_config> context)
759 : filter_visitor{type}
760 , context_{std::move(context)}
761{
762}
763
765 const diagram &d, unsigned idx) const
766{
767 bool effective_context_extended{true};
768
769 auto &effective_context = effective_contexts_[idx];
770
771 // First add to effective context all elements matching context_ patterns
772 const auto &context_cfg = context_.at(idx);
773 const auto &context_matches =
774 dynamic_cast<const class_diagram::model::diagram &>(d)
775 .find<class_diagram::model::class_>(context_cfg.pattern);
776
777 for (const auto &maybe_match : context_matches) {
778 if (maybe_match)
779 effective_context.emplace(maybe_match.value().id());
780 }
781
782 const auto &context_enum_matches =
783 dynamic_cast<const class_diagram::model::diagram &>(d)
784 .find<class_diagram::model::enum_>(context_cfg.pattern);
785
786 for (const auto &maybe_match : context_enum_matches) {
787 if (maybe_match)
788 effective_context.emplace(maybe_match.value().id());
789 }
790
791 const auto &context_concept_matches =
792 dynamic_cast<const class_diagram::model::diagram &>(d)
793 .find<class_diagram::model::concept_>(context_cfg.pattern);
794
795 for (const auto &maybe_match : context_concept_matches) {
796 if (maybe_match)
797 effective_context.emplace(maybe_match.value().id());
798 }
799
800 // Now repeat radius times - extend the effective context with elements
801 // matching in direct relationship to what is in context
802 auto radius_counter = context_cfg.radius;
803 std::set<eid_t> current_iteration_context;
804
805 while (radius_counter > 0 && effective_context_extended) {
806 // If at any iteration the effective context was not extended - we
807 // don't to need to continue
808 radius_counter--;
809 effective_context_extended = false;
810 current_iteration_context.clear();
811
812 // For each class in the model
813 find_elements_in_direct_relationship<class_diagram::model::class_>(
814 d, context_cfg, effective_context, current_iteration_context);
815
817 d, context_cfg, effective_context, current_iteration_context);
818
819 // For each concept in the model
820 find_elements_in_direct_relationship<class_diagram::model::concept_>(
821 d, context_cfg, effective_context, current_iteration_context);
822
823 // For each enum in the model
824 find_elements_in_direct_relationship<class_diagram::model::enum_>(
825 d, context_cfg, effective_context, current_iteration_context);
826
827 for (auto id : current_iteration_context) {
828 if (effective_context.count(id) == 0) {
829 // Found new element to add to context
830 effective_context.emplace(id);
831 effective_context_extended = true;
832 }
833 }
834 }
835}
836
838 const config::context_config &context_cfg, relationship_t r) const
839{
840 return context_cfg.relationships.empty() ||
841 util::contains(context_cfg.relationships, r);
842}
843
845 const config::context_config &context_cfg,
846 std::set<eid_t> &effective_context,
847 std::set<eid_t> &current_iteration_context) const
848{
849 const auto &cd = dynamic_cast<const class_diagram::model::diagram &>(d);
850
851 if (!should_include(context_cfg, relationship_t::kExtension)) {
852 return;
853 }
854
855 for (const auto &c : cd.classes()) {
856 // Check if any of the elements parents are already in the
857 // effective context...
860 d, effective_context, current_iteration_context, cd, c);
861
862 // .. or vice-versa
865 effective_context, current_iteration_context, cd, c);
866 }
867}
868
870 std::set<eid_t> &effective_context,
871 std::set<eid_t> &current_iteration_context,
873 const std::reference_wrapper<class_diagram::model::class_> &c) const
874{
875 for (const auto &ec : effective_context) {
876 const auto &maybe_child = cd.find<class_diagram::model::class_>(ec);
877
878 // The element might not exist because it might have been
879 // something other than a class
880 if (!maybe_child)
881 continue;
882
883 for (const auto &p : maybe_child.value().parents()) {
884 if (p.name() == c.get().full_name(false)) {
885 current_iteration_context.emplace(c.get().id());
886 }
887 }
888 }
889}
891 std::set<eid_t> &effective_context,
892 std::set<eid_t> &current_iteration_context,
894 const std::reference_wrapper<class_diagram::model::class_> &c) const
895{
896 for (const class_diagram::model::class_parent &p : c.get().parents()) {
897 for (const auto &ec : effective_context) {
898 const auto &maybe_parent =
900 if (!maybe_parent)
901 continue;
902
904 maybe_parent.value().full_name(false) == p.name())
905 current_iteration_context.emplace(c.get().id());
906 }
907 }
908}
909
911{
912 if (initialized_)
913 return;
914
915 initialized_ = true;
916
917 // Prepare effective_contexts_
918 for (auto i = 0U; i < context_.size(); i++) {
919 effective_contexts_.push_back({}); // NOLINT
921 }
922}
923
925{
926 if (d.type() != diagram_t::kClass)
927 return {};
928
929 // Running this filter makes sense only after the entire diagram is
930 // generated - i.e. during diagram generation
931 if (!d.complete())
932 return {};
933
934 initialize(d);
935
936 if (std::all_of(effective_contexts_.begin(), effective_contexts_.end(),
937 [](const auto &ec) { return ec.empty(); }))
938 return {};
939
940 for (const auto &ec : effective_contexts_) {
941 if (ec.count(e.id()) > 0)
942 return true;
943 }
944
945 return false;
946}
947
949{
951}
952
954{
956}
957
958paths_filter::paths_filter(filter_t type, const std::vector<std::string> &p,
959 const std::filesystem::path &root)
960 : filter_visitor{type}
961 , root_{root}
962{
963 for (const auto &path : p) {
964 std::filesystem::path absolute_path;
965
966 if (path.empty() || path == ".")
967 absolute_path = root;
968 else if (std::filesystem::path{path}.is_relative())
969 absolute_path = root / path;
970 else
971 absolute_path = path;
972
973 bool match_successful{false};
974 for (auto &resolved_glob_path :
975 glob::glob(absolute_path.string(), true)) {
976 try {
977 auto resolved_absolute_path = absolute(resolved_glob_path);
978 resolved_absolute_path =
979 canonical(resolved_absolute_path.lexically_normal());
980
981 resolved_absolute_path.make_preferred();
982
983 LOG_DBG("Added path {} to paths_filter",
984 resolved_absolute_path.string());
985
986 paths_.emplace_back(std::move(resolved_absolute_path));
987
988 match_successful = true;
989 }
990 catch (std::filesystem::filesystem_error &e) {
991 LOG_WARN("Cannot add non-existent path {} to "
992 "paths filter",
993 absolute_path.string());
994 continue;
995 }
996 }
997
998 if (!match_successful)
999 LOG_WARN("Paths filter pattern '{}' did not match "
1000 "any files relative to '{}'",
1001 path, root_.string());
1002 }
1003}
1004
1006 const diagram & /*d*/, const common::model::source_file &p) const
1007{
1008 if (paths_.empty()) {
1009 return {};
1010 }
1011
1012 // Matching source paths doesn't make sense if they are not absolute
1013 if (!p.is_absolute()) {
1014 return {};
1015 }
1016
1017 auto pp = p.fs_path(root_);
1018 for (const auto &path : paths_) {
1019 if (pp.root_name().string() == path.root_name().string() &&
1020 util::starts_with(pp.relative_path(), path.relative_path())) {
1021
1022 return true;
1023 }
1024 }
1025
1026 return false;
1027}
1028
1030 const diagram & /*d*/, const common::model::source_location &p) const
1031{
1032 if (paths_.empty()) {
1033 return {};
1034 }
1035
1036 auto sl_path = std::filesystem::path{p.file()};
1037
1038 // Matching source paths doesn't make sens if they are not absolute or
1039 // empty
1040 if (p.file().empty() || sl_path.is_relative()) {
1041 return {};
1042 }
1043
1044 for (const auto &path : paths_) {
1045 if (sl_path.root_name().string() == path.root_name().string() &&
1046 util::starts_with(sl_path.relative_path(), path.relative_path())) {
1047
1048 return true;
1049 }
1050 }
1051
1052 return false;
1053}
1054
1056 std::unique_ptr<access_filter> af, std::unique_ptr<method_type_filter> mtf)
1057 : filter_visitor{type}
1058 , access_filter_{std::move(af)}
1059 , method_type_filter_{std::move(mtf)}
1060{
1061}
1062
1064 const diagram &d, const class_diagram::model::class_method &m) const
1065{
1066 tvl::value_t res = tvl::or_(
1067 access_filter_->match(d, m.access()), method_type_filter_->match(d, m));
1068
1069 return res;
1070}
1071
1073 filter_t type, std::unique_ptr<access_filter> af)
1074 : filter_visitor{type}
1075 , access_filter_{std::move(af)}
1076{
1077}
1078
1080 const diagram &d, const class_diagram::model::class_member &m) const
1081{
1082 return access_filter_->match(d, m.access());
1083}
1084
1086 const config::diagram & /*c*/, private_constructor_tag_t /*unused*/)
1087 : diagram_{d}
1088{
1089}
1090
1092 filter_t filter_type, std::unique_ptr<filter_visitor> fv)
1093{
1094 if (filter_type == filter_t::kInclusive)
1095 add_inclusive_filter(std::move(fv));
1096 else
1097 add_exclusive_filter(std::move(fv));
1098}
1099
1100void diagram_filter::add_inclusive_filter(std::unique_ptr<filter_visitor> fv)
1101{
1102 inclusive_.emplace_back(std::move(fv));
1103}
1104
1105void diagram_filter::add_exclusive_filter(std::unique_ptr<filter_visitor> fv)
1106{
1107 exclusive_.emplace_back(std::move(fv));
1108}
1109
1111 const namespace_ &ns, const std::string &name) const
1112{
1113 if (should_include(ns)) {
1114 element e{namespace_{}};
1115 e.set_name(name);
1116 e.set_namespace(ns);
1117
1118 return should_include(e);
1119 }
1120
1121 return false;
1122}
1123
1124template <>
1125bool diagram_filter::should_include<std::string>(const std::string &name) const
1126{
1127 if (name.empty())
1128 return false;
1129
1130 auto [ns, n] = common::split_ns(name);
1131
1132 return should_include(ns, n);
1133}
1134} // namespace clanguml::common::model