Line | Branch | Exec | Source |
---|---|---|---|
1 | // SPDX-FileCopyrightText: 2022-2023 Dennis Gläser <dennis.glaeser@iws.uni-stuttgart.de> | ||
2 | // SPDX-License-Identifier: MIT | ||
3 | /*! | ||
4 | * \ingroup XML | ||
5 | * \copydoc GridFormat::XMLTag | ||
6 | */ | ||
7 | #ifndef GRIDFORMAT_XML_TAG_HPP_ | ||
8 | #define GRIDFORMAT_XML_TAG_HPP_ | ||
9 | |||
10 | #include <ranges> | ||
11 | #include <concepts> | ||
12 | #include <string> | ||
13 | #include <string_view> | ||
14 | #include <utility> | ||
15 | #include <vector> | ||
16 | #include <algorithm> | ||
17 | |||
18 | #include <gridformat/common/exceptions.hpp> | ||
19 | #include <gridformat/common/string_conversion.hpp> | ||
20 | |||
21 | namespace GridFormat { | ||
22 | |||
23 | #ifndef DOXYGEN | ||
24 | namespace Detail { | ||
25 | |||
26 | template<typename Attribute> | ||
27 | 1808557 | auto _attr_find_lambda(std::string_view name) { | |
28 | 5551508 | return [name] (const Attribute& attr) { return attr.first == name; }; | |
29 | } | ||
30 | |||
31 | template<typename T> | ||
32 | concept RepresentableAsString = requires(const T& t) { | ||
33 | { as_string(t) } -> std::convertible_to<std::string>; | ||
34 | }; | ||
35 | |||
36 | } // end namespace Detail | ||
37 | #endif // DOXYGEN | ||
38 | |||
39 | /*! | ||
40 | * \ingroup XML | ||
41 | * \brief Class to represent an XML tag consisting of a name and attributes. | ||
42 | */ | ||
43 | class XMLTag { | ||
44 | public: | ||
45 | using Attribute = std::pair<std::string, std::string>; | ||
46 | |||
47 | 260114 | explicit XMLTag(std::string name) | |
48 | 260114 | : _name(std::move(name)) | |
49 | 260114 | {} | |
50 | |||
51 | 2166125 | const std::string& name() const noexcept { | |
52 | 2166125 | return _name; | |
53 | } | ||
54 | |||
55 | 3 | std::size_t number_of_attributes() const noexcept { | |
56 | 3 | return _attributes.size(); | |
57 | } | ||
58 | |||
59 | 62501 | bool has_attribute(std::string_view name) const { | |
60 |
1/2✓ Branch 2 taken 62501 times.
✗ Branch 3 not taken.
|
62501 | return _find(name) != _attributes.end(); |
61 | } | ||
62 | |||
63 | template<Detail::RepresentableAsString ValueType> | ||
64 | 1714222 | void set_attribute(std::string attr_name, ValueType&& value) { | |
65 |
2/4✓ Branch 2 taken 857113 times.
✗ Branch 3 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 857113 times.
|
1714222 | if (auto it = _find(attr_name); it != _attributes.end()) |
66 | ✗ | *it = Attribute{std::move(attr_name), as_string(value)}; | |
67 | else | ||
68 |
4/6✓ Branch 2 taken 722166 times.
✓ Branch 3 taken 120647 times.
✓ Branch 4 taken 14300 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 842813 times.
✗ Branch 7 not taken.
|
1714222 | _attributes.emplace_back(Attribute{std::move(attr_name), as_string(value)}); |
69 | 1714222 | } | |
70 | |||
71 | 2 | bool remove_attribute(std::string_view attr_name) { | |
72 | 2 | return std::erase_if(_attributes, Detail::_attr_find_lambda<Attribute>(attr_name)); | |
73 | } | ||
74 | |||
75 | template<std::ranges::input_range R> | ||
76 | requires(std::convertible_to<std::ranges::range_value_t<R>, std::string_view>) | ||
77 | 1 | std::size_t remove_attributes(R&& attrs_to_remove) { | |
78 | 1 | return std::erase_if(_attributes, | |
79 | 2 | [&] (const Attribute& attr) { | |
80 | 2 | return std::ranges::any_of(attrs_to_remove, | |
81 | 3 | [&] (std::string_view attr_to_remove) { | |
82 | 3 | return attr_to_remove == attr.first; | |
83 | 2 | }); | |
84 | 1 | }); | |
85 | } | ||
86 | |||
87 | template<typename T = std::string> | ||
88 | 866566 | T get_attribute(std::string_view attr_name) const { | |
89 |
4/6✓ Branch 1 taken 866558 times.
✓ Branch 2 taken 1 times.
✓ Branch 4 taken 866558 times.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 866554 times.
|
1733122 | if (auto result = _get_attribute<T>(attr_name); result) |
90 | 1733120 | return *std::move(result); | |
91 | ✗ | throw InvalidState("No attribute with name '" + std::string(attr_name) + "'"); | |
92 | } | ||
93 | |||
94 | template<typename T = std::string> | ||
95 | 44764 | T get_attribute_or(T fallback, std::string_view attr_name) const { | |
96 |
2/4✓ Branch 1 taken 22382 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 10504 times.
✗ Branch 5 not taken.
|
44764 | return _get_attribute<T>(attr_name).value_or(fallback); |
97 | } | ||
98 | |||
99 | 217564 | friend decltype(auto) attributes(const XMLTag& tag) { | |
100 | 217564 | return std::views::keys(tag._attributes); | |
101 | } | ||
102 | |||
103 | private: | ||
104 | template<typename T> | ||
105 | 1094053 | std::optional<T> _get_attribute(std::string_view attr_name) const { | |
106 |
3/4✓ Branch 1 taken 888941 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 885487 times.
✓ Branch 6 taken 3454 times.
|
1094053 | if (const auto it = _find(attr_name); it != _attributes.end()) |
107 |
2/2✓ Branch 2 taken 885486 times.
✓ Branch 3 taken 1 times.
|
1087145 | return from_string<T>(it->second); |
108 | 6908 | return {}; | |
109 | } | ||
110 | |||
111 | 951442 | typename std::vector<Attribute>::const_iterator _find(std::string_view n) const { | |
112 | 951442 | return std::ranges::find_if(_attributes, Detail::_attr_find_lambda<Attribute>(n)); | |
113 | } | ||
114 | 857113 | typename std::vector<Attribute>::iterator _find(std::string_view n) { | |
115 | 857113 | return std::ranges::find_if(_attributes, Detail::_attr_find_lambda<Attribute>(n)); | |
116 | } | ||
117 | |||
118 | std::string _name; | ||
119 | std::vector<Attribute> _attributes; | ||
120 | }; | ||
121 | |||
122 | } // end namespace GridFormat | ||
123 | |||
124 | #endif // GRIDFORMAT_XML_TAG_HPP_ | ||
125 |