MUSX Document Model
Loading...
Searching...
No Matches
XmlInterface.h
1/*
2 * Copyright (C) 2025, Robert Patterson
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 * THE SOFTWARE.
21 */
22#pragma once
23
24#include <string>
25#include <memory>
26#include <vector>
27#include <sstream>
28#include <stdexcept>
29#include <algorithm>
30#include <functional>
31
32// Do not add header dependencies from musx.
33
34namespace musx {
35
40namespace xml {
41
45class load_error : public std::runtime_error
46{
47public:
48 using std::runtime_error::runtime_error;
49};
50
51#ifndef DOXYGEN_SHOULD_IGNORE_THIS
53inline std::string trim(const std::string& str)
54{
55 auto start = std::find_if_not(str.begin(), str.end(), ::isspace);
56 auto end = std::find_if_not(str.rbegin(), str.rend(), ::isspace).base();
57 return (start < end) ? std::string(start, end) : std::string();
58}
59#endif // DOXYGEN_SHOULD_IGNORE_THIS
60
65{
66public:
67 virtual ~IXmlAttribute() = default;
68
73 virtual std::string getName() const = 0;
74
79 virtual std::string getValue() const = 0;
80
84 std::string getValueTrimmed() const { return trim(getValue()); }
85
92 template <typename T>
93 T getValueAs() const {
94 std::istringstream iss(getValueTrimmed());
95 T value;
96 if constexpr (std::is_same_v<T, bool>) {
97 std::string str;
98 iss >> str;
99 std::transform(str.begin(), str.end(), str.begin(), ::tolower); // Ensure case insensitivity
100 if (str == "true") {
101 return true;
102 }
103 else if (str == "false") {
104 return false;
105 }
106 }
107 if (!(iss >> value)) {
108 throw std::invalid_argument("Failed to convert attribute value [" + iss.str() + "] to the specified type");
109 }
110 return value;
111 }
112
117 virtual std::shared_ptr<IXmlAttribute> nextAttribute() const = 0;
118};
119
120class IXmlElement;
121using XmlElementPtr = std::shared_ptr<IXmlElement>;
122template <typename T>
123using XmlElementPopulator = std::function<void(const XmlElementPtr&, const std::shared_ptr<T>&)>;
124template <typename T>
125using XmlElementDescriptor = std::tuple<const std::string_view, XmlElementPopulator<T>>;
126template <typename T>
127using XmlElementArray = std::vector<XmlElementDescriptor<T>>;
128
133{
134public:
135 virtual ~IXmlElement() = default;
136
141 virtual std::string getTagName() const = 0;
142
147 virtual std::string getText() const = 0;
148
152 std::string getTextTrimmed() const { return trim(getText()); }
153
161 template <typename T>
162 T getTextAs(T defaultValue = {}) const
163 {
164 static_assert(!std::is_same_v<T, bool>, "Do not use getTextAs with bool type. Simply assign true. (The presence of the node means true.)");
165
166 std::istringstream iss(getTextTrimmed());
167 if (iss.str().empty()) {
168 return defaultValue;
169 }
170
171 using ValueType = std::conditional_t<
172 std::is_same_v<T, char>
173 || std::is_same_v<T, uint8_t>
174 || std::is_same_v<T, char16_t>
175 || std::is_same_v<T, char32_t>, int, T>;
176 ValueType value = ValueType(defaultValue);
177
178 if (!(iss >> value)) {
179 throw std::invalid_argument("Failed to convert text content [" + iss.str() + "] to the specified type");
180 }
181
182 return T(value);
183 }
184
189 virtual std::shared_ptr<IXmlAttribute> getFirstAttribute() const = 0;
190
196 virtual std::shared_ptr<IXmlAttribute> findAttribute(const std::string& name) const = 0;
197
203 virtual XmlElementPtr getFirstChildElement(const std::string& tagName = {}) const = 0;
204
210 virtual XmlElementPtr getNextSibling(const std::string& tagName = {}) const = 0;
211
217 virtual XmlElementPtr getPreviousSibling(const std::string& tagName = {}) const = 0;
218
223 virtual XmlElementPtr getParent() const = 0;
224};
225
230{
231public:
232 virtual ~IXmlDocument() = default;
233
239 virtual void loadFromString(const std::string& xmlContent) = 0;
240
246 virtual void loadFromString(const std::vector<char>& xmlContent) = 0;
247
252 virtual std::shared_ptr<IXmlElement> getRootElement() const = 0;
253};
254
255} // namespace xml
256} // namespace musx
Interface for an XML attribute.
Definition XmlInterface.h:65
std::string getValueTrimmed() const
Gets the text content of the attribute with whitespace trimmed.
Definition XmlInterface.h:84
virtual std::string getValue() const =0
Gets the value of the attribute.
virtual std::shared_ptr< IXmlAttribute > nextAttribute() const =0
Advances to the next attribute.
T getValueAs() const
Gets the value of the attribute, converted to the specified type.
Definition XmlInterface.h:93
virtual std::string getName() const =0
Gets the name of the attribute.
Interface for an XML document.
Definition XmlInterface.h:230
virtual std::shared_ptr< IXmlElement > getRootElement() const =0
Gets the root element of the document.
virtual void loadFromString(const std::string &xmlContent)=0
Loads XML content from a string.
virtual void loadFromString(const std::vector< char > &xmlContent)=0
Loads XML content from a vector of characters.
Interface for an XML element.
Definition XmlInterface.h:133
virtual XmlElementPtr getPreviousSibling(const std::string &tagName={}) const =0
Gets the previous sibling element.
virtual XmlElementPtr getParent() const =0
Gets the parent element.
virtual XmlElementPtr getNextSibling(const std::string &tagName={}) const =0
Gets the next sibling element.
virtual std::shared_ptr< IXmlAttribute > getFirstAttribute() const =0
Gets the first attribute.
virtual std::string getText() const =0
Gets the text content of the element.
std::string getTextTrimmed() const
Gets the text content of the element with whitespace trimmed.
Definition XmlInterface.h:152
T getTextAs(T defaultValue={}) const
Gets the text content of the element, converted to the specified type.
Definition XmlInterface.h:162
virtual XmlElementPtr getFirstChildElement(const std::string &tagName={}) const =0
Finds the first child element.
virtual std::shared_ptr< IXmlAttribute > findAttribute(const std::string &name) const =0
Finds the first attribute.
virtual std::string getTagName() const =0
Gets the tag name of the element.
Exception for load xml error.
Definition XmlInterface.h:46
std::tuple< const std::string_view, XmlElementPopulator< T > > XmlElementDescriptor
associates an xml node name with and XmlElementPopulator
Definition XmlInterface.h:125
std::vector< XmlElementDescriptor< T > > XmlElementArray
an array type for XmlElementDescriptor instances.
Definition XmlInterface.h:127
std::shared_ptr< IXmlElement > XmlElementPtr
shared pointer to IXmlElement
Definition XmlInterface.h:121
std::function< void(const XmlElementPtr &, const std::shared_ptr< T > &)> XmlElementPopulator
function type for populating a field from an IXmlElement
Definition XmlInterface.h:123
object model for musx file (enigmaxml)
Definition BaseClasses.h:32