openMSX
XMLElement.hh
Go to the documentation of this file.
1#ifndef XMLELEMENT_HH
2#define XMLELEMENT_HH
3
4#include "MemBuffer.hh"
6#include "serialize_meta.hh"
7#include <cassert>
8#include <cstddef>
9#include <iterator>
10//#include <memory_resource>
11#include <string>
12#include <string_view>
13#include <vector>
14
15namespace openmsx {
16
17struct OldXMLElement; // for backwards compatible savestates
18
19// The classes XMLDocument, XMLElement and XMLAttribute together form an
20// in-memory representation of XML files (or at least the subset of the features
21// needed for openMSX).
22//
23// This representation is optimized for fast parsing and to be compact in
24// memory. This is achieved by:
25// - Keeping a buffer to the actual xml file, and modifying that buffer.
26// For example the strings are modified in place (resolve escape sequences
27// and add zero-terminators) and the actual XML classes refer to this buffer.
28// - The 'XMLElement' and 'XMLAttribute' objects are allocated from a monotonic
29// allocator.
30// - Both the file buffer and the monotonic allocator are owned by the
31// XMLDocument class.
32
33// Modifying the information (e.g. after it has been parsed from a file) is
34// possible. But this is not the main intended use-case. For example it is
35// possible to assign a new value-string to an attribute or an xml element. That
36// string will be allocated from the monotonic allocator, but the memory for the
37// old string will not be freed. So it's probably not a good idea to use these
38// classes in a heavy-modification scenario.
39
40
41// XMLAttribute is a name-value pair. This class only refers to the name and
42// value strings, it does not own these strings. The owner could be either
43// XMLDocument (in the file buffer or in the monotonic allocator) or it could
44// be a string with static lifetime (e.g. a string-literal).
45//
46// XMLAttributes are organized in a (single-)linked list. So this class also
47// contains a pointer to the next element in the list. The last element in the
48// list contains a nullptr.
50{
51public:
52 XMLAttribute(const char* name_, const char* value_)
53 : name(name_), value(value_) {}
54 [[nodiscard]] std::string_view getName() const { return name; }
55 [[nodiscard]] std::string_view getValue() const { return value; }
56 void setValue(const char* value_) { value = value_; }
57
59 assert(!nextAttribute);
60 nextAttribute = attribute;
61 return attribute;
62 }
63
64private:
65 const char* name;
66 const char* value;
67 XMLAttribute* nextAttribute = nullptr;
68
69 friend class XMLElement;
70 friend class XMLDocument;
71 friend class XMLDocumentHandler;
72};
73
74// This XMLElement class represents a single XML-element (or XML-node). It has a
75// name, attributes, a value (could be empty) and zero or more children. The
76// value and the children are mutually exclusive, in other words: elements with
77// children cannot have a non-empty value.
78//
79// String-ownership is the same as with XMLAttribute.
80//
81// Attributes are organized in a (single-)linked list. This class points to the
82// first attribute (possibly nullptr when there are no attributes) and that
83// attribute points to the next and so on.
84//
85// Hierarchy is achieved via two pointers. This class has a pointer to its first
86// child and to its next sibling. Thus getting all children of a specfic
87// elements requires to first follow the 'firstChild' pointer, and from there on
88// follow the 'nextSibling' pointers (and stop when any of these pointers in
89// nullptr). XMLElement objects do not have a pointer to their parent.
91{
92 // iterator classes for children and attributes
93 // TODO c++20: use iterator + sentinel instead of 2 x iterator
94 struct ChildIterator {
95 using difference_type = ptrdiff_t;
96 using value_type = const XMLElement;
97 using pointer = value_type*;
98 using reference = value_type&;
99 using iterator_category = std::forward_iterator_tag;
100
101 const XMLElement* elem;
102
103 const XMLElement& operator*() const { return *elem; }
104 ChildIterator operator++() { elem = elem->nextSibling; return *this; }
105 [[nodiscard]] bool operator==(const ChildIterator& i) const { return elem == i.elem; }
106 };
107 struct ChildRange {
108 const XMLElement* elem;
109 ChildIterator begin() const { return {elem->firstChild}; }
110 ChildIterator end() const { return {nullptr}; }
111 };
112
113 struct NamedChildIterator {
114 using difference_type = ptrdiff_t;
115 using value_type = const XMLElement*;
116 using pointer = value_type*;
117 using reference = value_type&;
118 using iterator_category = std::forward_iterator_tag;
119
120 const XMLElement* elem;
121 const std::string_view name;
122
123 NamedChildIterator(const XMLElement* elem_, std::string_view name_)
124 : elem(elem_), name(name_)
125 {
126 while (elem && elem->getName() != name) {
127 elem = elem->nextSibling;
128 }
129 }
130
131 const XMLElement* operator*() const { return elem; }
132 NamedChildIterator operator++() {
133 do {
134 elem = elem->nextSibling;
135 } while (elem && elem->getName() != name);
136 return *this;
137 }
138 [[nodiscard]] bool operator==(const NamedChildIterator& i) const { return elem == i.elem; }
139 };
140 struct NamedChildRange {
141 const XMLElement* elem;
142 std::string_view name;
143 NamedChildIterator begin() const { return {elem->firstChild, name}; }
144 NamedChildIterator end() const { return {nullptr, std::string_view{}}; }
145 };
146
147 struct AttributeIterator {
148 using difference_type = ptrdiff_t;
149 using value_type = const XMLAttribute;
150 using pointer = value_type*;
151 using reference = value_type&;
152 using iterator_category = std::forward_iterator_tag;
153
154 const XMLAttribute* attr;
155
156 const XMLAttribute& operator*() const { return *attr; }
157 AttributeIterator operator++() { attr = attr->nextAttribute; return *this; }
158 [[nodiscard]] bool operator==(const AttributeIterator& i) const { return attr == i.attr; }
159 };
160 struct AttributeRange {
161 const XMLElement* elem;
162 AttributeIterator begin() const { return {elem->firstAttribute}; }
163 AttributeIterator end() const { return {nullptr}; }
164 };
165
166public:
167 XMLElement(const char* name_) : name(name_) {}
168 XMLElement(const char* name_, const char* data_) : name(name_), data(data_) {}
169
170 [[nodiscard]] std::string_view getName() const { return name; }
171 void clearName() { name = ""; } // hack to 'remove' child from findChild()
172
173 [[nodiscard]] std::string_view getData() const {
174 return data ? std::string_view(data) : std::string_view();
175 }
176 XMLElement* setData(const char* data_) {
177 data = data_;
178 return this;
179 }
180
181 [[nodiscard]] bool hasChildren() const { return firstChild; }
182 [[nodiscard]] const XMLElement* getFirstChild() const { return firstChild; }
183 [[nodiscard]] const XMLElement* findChild(std::string_view childName) const;
184 [[nodiscard]] const XMLElement* findChild(std::string_view childName, const XMLElement*& hint) const;
185 [[nodiscard]] const XMLElement& getChild(std::string_view childName) const;
186
187 [[nodiscard]] std::string_view getChildData(std::string_view childName) const;
188 [[nodiscard]] std::string_view getChildData(std::string_view childName,
189 std::string_view defaultValue) const;
190 [[nodiscard]] bool getChildDataAsBool(std::string_view childName, bool defaultValue) const;
191 [[nodiscard]] int getChildDataAsInt(std::string_view childName, int defaultValue) const;
192
193 [[nodiscard]] size_t numChildren() const;
194 ChildRange getChildren() const { return {this}; }
195 NamedChildRange getChildren(std::string_view childName) const { return {this, childName}; }
196
197 [[nodiscard]] const XMLAttribute* findAttribute(std::string_view attrName) const;
198 [[nodiscard]] const XMLAttribute& getAttribute(std::string_view attrName) const;
199 [[nodiscard]] std::string_view getAttributeValue(std::string_view attrName) const;
200 [[nodiscard]] std::string_view getAttributeValue(std::string_view attrName,
201 std::string_view defaultValue) const;
202 [[nodiscard]] bool getAttributeValueAsBool(std::string_view attrName,
203 bool defaultValue) const;
204 [[nodiscard]] int getAttributeValueAsInt(std::string_view attrName,
205 int defaultValue) const;
206 [[nodiscard]] XMLAttribute** findAttributePointer(std::string_view attrName);
207 static void removeAttribute(XMLAttribute** attrPtr);
208 [[nodiscard]] size_t numAttributes() const;
209 AttributeRange getAttributes() const { return {this}; }
210
212 assert(!firstChild);
213 firstChild = child;
214 return child;
215 }
217 assert(!nextSibling);
218 nextSibling = sibling;
219 return sibling;
220 }
222 assert(!firstAttribute);
223 firstAttribute = attribute;
224 return attribute;
225 }
226
227private:
228 const char* name;
229 const char* data = nullptr;
230 XMLElement* firstChild = nullptr;
231 XMLElement* nextSibling = nullptr;
232 XMLAttribute* firstAttribute = nullptr;
233
234 friend class XMLDocument;
235 friend class XMLDocumentHandler;
236};
237
238// This class mainly exists to manage ownership over the objects involved in a
239// full XML document. These are the XMLElement and XMLAttribute objects but also
240// the name/value/attribute strings. This class also has a pointer to the root
241// element.
242//
243// Because of the way how ownership is handled, most modifying operations are
244// part of this class API. For example there's a method 'setChildData()' which
245// searches for a child with a specific name, if not found such a child is
246// created, then the child-data is set to a new value. Because this operation
247// possibly requires to allocate a new XMLElement (and the allocator is part of
248// this class) this method is part of this class rather than the XMLElement
249// class.
251{
252public:
253 // singleton-like document, mainly used as owner for static XML snippets
254 // (so with lifetime the whole openMSX session)
256 static XMLDocument doc;
257 return doc;
258 }
259
260 // Create an empty XMLDocument (root == nullptr). All constructor
261 // arguments are delegated to the monotonic allocator constructor.
262 template<typename ...Args>
263 XMLDocument(Args&& ...args)
264 : allocator(std::forward<Args>(args)...) {}
265
266 // Load/parse an xml file. Requires that the document is still empty.
267 void load(const std::string& filename, std::string_view systemID);
268
269 [[nodiscard]] const XMLElement* getRoot() const { return root; }
270 void setRoot(XMLElement* root_) { assert(!root); root = root_; }
271
272 [[nodiscard]] XMLElement* allocateElement(const char* name);
273 [[nodiscard]] XMLElement* allocateElement(const char* name, const char* data);
274 [[nodiscard]] XMLAttribute* allocateAttribute(const char* name, const char* value);
275 [[nodiscard]] const char* allocateString(std::string_view str);
276
277 [[nodiscard]] XMLElement* getOrCreateChild(XMLElement& parent, const char* childName, const char* childData);
278 XMLElement* setChildData(XMLElement& parent, const char* childName, const char* childData);
279 void setAttribute(XMLElement& elem, const char* attrName, const char* attrValue);
280
281 template<typename Range, typename UnaryOp>
282 void generateList(XMLElement& parent, const char* itemName, Range&& range, UnaryOp op) {
283 XMLElement** next = &parent.firstChild;
284 assert(!*next);
285 for (auto& r : range) {
286 auto* elem = allocateElement(itemName);
287 op(elem, r);
288 *next = elem;
289 next = &elem->nextSibling;
290 }
291 }
292
293 void load(OldXMLElement& elem); // bw compat
294
295 void serialize(MemInputArchive& ar, unsigned version);
296 void serialize(MemOutputArchive& ar, unsigned version);
297 void serialize(XmlInputArchive& ar, unsigned version);
298 void serialize(XmlOutputArchive& ar, unsigned version);
299
300private:
301 XMLElement* loadElement(MemInputArchive& ar);
302 XMLElement* clone(const XMLElement& inElem);
303 XMLElement* clone(const OldXMLElement& elem);
304
305private:
306 XMLElement* root = nullptr;
307 MemBuffer<char> buf;
308 // part of c++17, but not yet implemented in libc++
309 // std::pmr::monotonic_buffer_resource allocator;
310 monotonic_allocator allocator;
311
312 friend class XMLDocumentHandler;
313};
314
315
316// For backwards-compatibility with old savestates
317// needed with HardwareConfig-version <= 5.
318class FileContext;
320{
321 template<typename Archive>
322 void serialize(Archive& ar, unsigned version);
323
324 // For backwards compatibility with version=1 savestates
325 static std::unique_ptr<FileContext> getLastSerializedFileContext();
326
327 std::string name;
328 std::string data;
329 std::vector<OldXMLElement> children;
330 std::vector<std::pair<std::string, std::string>> attributes;
331};
333
334} // namespace openmsx
335
336#endif
std::string_view getName() const
Definition: XMLElement.hh:54
XMLAttribute * setNextAttribute(XMLAttribute *attribute)
Definition: XMLElement.hh:58
void setValue(const char *value_)
Definition: XMLElement.hh:56
XMLAttribute(const char *name_, const char *value_)
Definition: XMLElement.hh:52
std::string_view getValue() const
Definition: XMLElement.hh:55
XMLElement * setChildData(XMLElement &parent, const char *childName, const char *childData)
Definition: XMLElement.cc:189
void serialize(MemInputArchive &ar, unsigned version)
Definition: XMLElement.cc:389
XMLElement * getOrCreateChild(XMLElement &parent, const char *childName, const char *childData)
Definition: XMLElement.cc:170
void generateList(XMLElement &parent, const char *itemName, Range &&range, UnaryOp op)
Definition: XMLElement.hh:282
void setRoot(XMLElement *root_)
Definition: XMLElement.hh:270
static XMLDocument & getStaticDocument()
Definition: XMLElement.hh:255
XMLDocument(Args &&...args)
Definition: XMLElement.hh:263
void setAttribute(XMLElement &elem, const char *attrName, const char *attrValue)
Definition: XMLElement.cc:209
const char * allocateString(std::string_view str)
Definition: XMLElement.cc:311
const XMLElement * getRoot() const
Definition: XMLElement.hh:269
XMLElement * allocateElement(const char *name)
Definition: XMLElement.cc:293
XMLAttribute * allocateAttribute(const char *name, const char *value)
Definition: XMLElement.cc:305
void load(const std::string &filename, std::string_view systemID)
Definition: XMLElement.cc:320
std::string_view getName() const
Definition: XMLElement.hh:170
size_t numChildren() const
Definition: XMLElement.cc:89
NamedChildRange getChildren(std::string_view childName) const
Definition: XMLElement.hh:195
XMLElement * setData(const char *data_)
Definition: XMLElement.hh:176
XMLAttribute * setFirstAttribute(XMLAttribute *attribute)
Definition: XMLElement.hh:221
XMLAttribute ** findAttributePointer(std::string_view attrName)
Definition: XMLElement.cc:145
XMLElement * setNextSibling(XMLElement *sibling)
Definition: XMLElement.hh:216
XMLElement(const char *name_)
Definition: XMLElement.hh:167
int getChildDataAsInt(std::string_view childName, int defaultValue) const
Definition: XMLElement.cc:81
AttributeRange getAttributes() const
Definition: XMLElement.hh:209
const XMLAttribute * findAttribute(std::string_view attrName) const
Definition: XMLElement.cc:95
const XMLElement * findChild(std::string_view childName) const
Definition: XMLElement.cc:19
static void removeAttribute(XMLAttribute **attrPtr)
Definition: XMLElement.cc:156
std::string_view getAttributeValue(std::string_view attrName) const
Definition: XMLElement.cc:114
size_t numAttributes() const
Definition: XMLElement.cc:162
bool hasChildren() const
Definition: XMLElement.hh:181
int getAttributeValueAsInt(std::string_view attrName, int defaultValue) const
Definition: XMLElement.cc:134
XMLElement * setFirstChild(XMLElement *child)
Definition: XMLElement.hh:211
const XMLAttribute & getAttribute(std::string_view attrName) const
Definition: XMLElement.cc:106
const XMLElement & getChild(std::string_view childName) const
Definition: XMLElement.cc:53
std::string_view getChildData(std::string_view childName) const
Definition: XMLElement.cc:62
std::string_view getData() const
Definition: XMLElement.hh:173
bool getChildDataAsBool(std::string_view childName, bool defaultValue) const
Definition: XMLElement.cc:75
bool getAttributeValueAsBool(std::string_view attrName, bool defaultValue) const
Definition: XMLElement.cc:127
ChildRange getChildren() const
Definition: XMLElement.hh:194
const XMLElement * getFirstChild() const
Definition: XMLElement.hh:182
XMLElement(const char *name_, const char *data_)
Definition: XMLElement.hh:168
This file implemented 3 utility functions:
Definition: Autofire.cc:9
bool operator==(const Event &x, const Event &y)
Definition: Event.cc:11
constexpr const char *const filename
SERIALIZE_CLASS_VERSION(CassettePlayer, 2)
STL namespace.
void serialize(Archive &ar, unsigned version)
Definition: XMLElement.cc:536
std::vector< OldXMLElement > children
Definition: XMLElement.hh:329
std::vector< std::pair< std::string, std::string > > attributes
Definition: XMLElement.hh:330
static std::unique_ptr< FileContext > getLastSerializedFileContext()
Definition: XMLElement.cc:519
constexpr uint128 operator*(const uint128 &a, const uint128 &b)
Definition: uint128.hh:189
constexpr auto begin(const zstring_view &x)
constexpr auto end(const zstring_view &x)