openMSX
XMLElement.hh
Go to the documentation of this file.
1 #ifndef XMLELEMENT_HH
2 #define XMLELEMENT_HH
3 
4 #include "MemBuffer.hh"
5 #include "monotonic_allocator.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 
15 namespace openmsx {
16 
17 struct 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 {
51 public:
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 
64 private:
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  bool operator==(const ChildIterator& i) { return elem == i.elem; }
106  bool operator!=(const ChildIterator& i) { return !(*this == i); }
107  };
108  struct ChildRange {
109  const XMLElement* elem;
110  ChildIterator begin() const { return {elem->firstChild}; }
111  ChildIterator end() const { return {nullptr}; }
112  };
113 
114  struct NamedChildIterator {
115  using difference_type = ptrdiff_t;
116  using value_type = const XMLElement*;
117  using pointer = value_type*;
118  using reference = value_type&;
119  using iterator_category = std::forward_iterator_tag;
120 
121  const XMLElement* elem;
122  const std::string_view name;
123 
124  NamedChildIterator(const XMLElement* elem_, std::string_view name_)
125  : elem(elem_), name(name_)
126  {
127  while (elem && elem->getName() != name) {
128  elem = elem->nextSibling;
129  }
130  }
131 
132  const XMLElement* operator*() const { return elem; }
133  NamedChildIterator operator++() {
134  do {
135  elem = elem->nextSibling;
136  } while (elem && elem->getName() != name);
137  return *this;
138  }
139  bool operator==(const NamedChildIterator& i) { return elem == i.elem; }
140  bool operator!=(const NamedChildIterator& i) { return !(*this == i); }
141  };
142  struct NamedChildRange {
143  const XMLElement* elem;
144  std::string_view name;
145  NamedChildIterator begin() const { return {elem->firstChild, name}; }
146  NamedChildIterator end() const { return {nullptr, std::string_view{}}; }
147  };
148 
149  struct AttributeIterator {
150  using difference_type = ptrdiff_t;
151  using value_type = const XMLAttribute;
152  using pointer = value_type*;
153  using reference = value_type&;
154  using iterator_category = std::forward_iterator_tag;
155 
156  const XMLAttribute* attr;
157 
158  const XMLAttribute& operator*() const { return *attr; }
159  AttributeIterator operator++() { attr = attr->nextAttribute; return *this; }
160  bool operator==(const AttributeIterator& i) { return attr == i.attr; }
161  bool operator!=(const AttributeIterator& i) { return !(*this == i); }
162  };
163  struct AttributeRange {
164  const XMLElement* elem;
165  AttributeIterator begin() const { return {elem->firstAttribute}; }
166  AttributeIterator end() const { return {nullptr}; }
167  };
168 
169 public:
170  XMLElement(const char* name_) : name(name_) {}
171  XMLElement(const char* name_, const char* data_) : name(name_), data(data_) {}
172 
173  [[nodiscard]] std::string_view getName() const { return name; }
174  void clearName() { name = ""; } // hack to 'remove' child from findChild()
175 
176  [[nodiscard]] std::string_view getData() const {
177  return data ? std::string_view(data) : std::string_view();
178  }
179  XMLElement* setData(const char* data_) {
180  data = data_;
181  return this;
182  }
183 
184  [[nodiscard]] bool hasChildren() const { return firstChild; }
185  [[nodiscard]] const XMLElement* getFirstChild() const { return firstChild; }
186  [[nodiscard]] const XMLElement* findChild(std::string_view childName) const;
187  [[nodiscard]] const XMLElement* findChild(std::string_view childName, const XMLElement*& hint) const;
188  [[nodiscard]] const XMLElement& getChild(std::string_view childName) const;
189 
190  [[nodiscard]] std::string_view getChildData(std::string_view childName) const;
191  [[nodiscard]] std::string_view getChildData(std::string_view childName,
192  std::string_view defaultValue) const;
193  [[nodiscard]] bool getChildDataAsBool(std::string_view childName, bool defaultValue) const;
194  [[nodiscard]] int getChildDataAsInt(std::string_view childName, int defaultValue) const;
195 
196  [[nodiscard]] size_t numChildren() const;
197  ChildRange getChildren() const { return {this}; }
198  NamedChildRange getChildren(std::string_view childName) const { return {this, childName}; }
199 
200  [[nodiscard]] const XMLAttribute* findAttribute(std::string_view attrName) const;
201  [[nodiscard]] const XMLAttribute& getAttribute(std::string_view attrName) const;
202  [[nodiscard]] std::string_view getAttributeValue(std::string_view attrName) const;
203  [[nodiscard]] std::string_view getAttributeValue(std::string_view attrName,
204  std::string_view defaultValue) const;
205  [[nodiscard]] bool getAttributeValueAsBool(std::string_view attrName,
206  bool defaultValue) const;
207  [[nodiscard]] int getAttributeValueAsInt(std::string_view attrName,
208  int defaultValue) const;
209  [[nodiscard]] XMLAttribute** findAttributePointer(std::string_view attrName);
210  static void removeAttribute(XMLAttribute** attrPtr);
211  [[nodiscard]] size_t numAttributes() const;
212  AttributeRange getAttributes() const { return {this}; }
213 
215  assert(!firstChild);
216  firstChild = child;
217  return child;
218  }
220  assert(!nextSibling);
221  nextSibling = sibling;
222  return sibling;
223  }
225  assert(!firstAttribute);
226  firstAttribute = attribute;
227  return attribute;
228  }
229 
230 private:
231  const char* name;
232  const char* data = nullptr;
233  XMLElement* firstChild = nullptr;
234  XMLElement* nextSibling = nullptr;
235  XMLAttribute* firstAttribute = nullptr;
236 
237  friend class XMLDocument;
238  friend class XMLDocumentHandler;
239 };
240 
241 // This class mainly exists to manage ownership over the objects involved in a
242 // full XML document. These are the XMLElement and XMLAttribute objects but also
243 // the name/value/attribute strings. This class also has a pointer to the root
244 // element.
245 //
246 // Because of the way how ownership is handled, most modifying operations are
247 // part of this class API. For example there's a method 'setChildData()' which
248 // searches for a child with a specific name, if not found such a child is
249 // created, then the child-data is set to a new value. Because this operation
250 // possibly requires to allocate a new XMLElement (and the allocator is part of
251 // this class) this method is part of this class rather than the XMLElement
252 // class.
254 {
255 public:
256  // singleton-like document, mainly used as owner for static XML snippets
257  // (so with lifetime the whole openMSX session)
259  static XMLDocument doc;
260  return doc;
261  }
262 
263  // Create an empty XMLDocument (root == nullptr). All constructor
264  // arguments are delegated to the monotonic allocator constructor.
265  template<typename ...Args>
266  XMLDocument(Args&& ...args)
267  : allocator(std::forward<Args>(args)...) {}
268 
269  // Load/parse an xml file. Requires that the document is still empty.
270  void load(const std::string& filename, std::string_view systemID);
271 
272  [[nodiscard]] const XMLElement* getRoot() const { return root; }
273  void setRoot(XMLElement* root_) { assert(!root); root = root_; }
274 
275  [[nodiscard]] XMLElement* allocateElement(const char* name);
276  [[nodiscard]] XMLElement* allocateElement(const char* name, const char* data);
277  [[nodiscard]] XMLAttribute* allocateAttribute(const char* name, const char* value);
278  [[nodiscard]] const char* allocateString(std::string_view str);
279 
280  [[nodiscard]] XMLElement* getOrCreateChild(XMLElement& parent, const char* childName, const char* childData);
281  XMLElement* setChildData(XMLElement& parent, const char* childName, const char* childData);
282  void setAttribute(XMLElement& elem, const char* attrName, const char* attrValue);
283 
284  template<typename Range, typename UnaryOp>
285  void generateList(XMLElement& parent, const char* itemName, Range&& range, UnaryOp op) {
286  XMLElement** next = &parent.firstChild;
287  assert(!*next);
288  for (auto& r : range) {
289  auto* elem = allocateElement(itemName);
290  op(elem, r);
291  *next = elem;
292  next = &elem->nextSibling;
293  }
294  }
295 
296  void load(OldXMLElement& elem); // bw compat
297 
298  void serialize(MemInputArchive& ar, unsigned version);
299  void serialize(MemOutputArchive& ar, unsigned version);
300  void serialize(XmlInputArchive& ar, unsigned version);
301  void serialize(XmlOutputArchive& ar, unsigned version);
302 
303 private:
304  XMLElement* loadElement(MemInputArchive& ar);
305  XMLElement* clone(const XMLElement& inElem);
306  XMLElement* clone(const OldXMLElement& elem);
307 
308 private:
309  XMLElement* root = nullptr;
310  MemBuffer<char> buf;
311  // part of c++17, but not yet implemented in libc++
312  // std::pmr::monotonic_buffer_resource allocator;
313  monotonic_allocator allocator;
314 
315  friend class XMLDocumentHandler;
316 };
317 
318 
319 // For backwards-compatibility with old savestates
320 // needed with HardwareConfig-version <= 5.
321 class FileContext;
323 {
324  template<typename Archive>
325  void serialize(Archive& ar, unsigned version);
326 
327  // For backwards compatibility with version=1 savestates
328  static std::unique_ptr<FileContext> getLastSerializedFileContext();
329 
330  std::string name;
331  std::string data;
332  std::vector<OldXMLElement> children;
333  std::vector<std::pair<std::string, std::string>> attributes;
334 };
336 
337 } // namespace openmsx
338 
339 #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
const XMLElement * getRoot() const
Definition: XMLElement.hh:272
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:285
void setRoot(XMLElement *root_)
Definition: XMLElement.hh:273
static XMLDocument & getStaticDocument()
Definition: XMLElement.hh:258
XMLDocument(Args &&...args)
Definition: XMLElement.hh:266
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
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:173
size_t numChildren() const
Definition: XMLElement.cc:89
NamedChildRange getChildren(std::string_view childName) const
Definition: XMLElement.hh:198
XMLElement * setData(const char *data_)
Definition: XMLElement.hh:179
XMLAttribute ** findAttributePointer(std::string_view attrName)
Definition: XMLElement.cc:145
XMLElement(const char *name_)
Definition: XMLElement.hh:170
int getChildDataAsInt(std::string_view childName, int defaultValue) const
Definition: XMLElement.cc:81
AttributeRange getAttributes() const
Definition: XMLElement.hh:212
const XMLAttribute * findAttribute(std::string_view attrName) const
Definition: XMLElement.cc:95
const XMLElement * findChild(std::string_view childName) const
Definition: XMLElement.cc:19
const XMLElement * getFirstChild() const
Definition: XMLElement.hh:185
static void removeAttribute(XMLAttribute **attrPtr)
Definition: XMLElement.cc:156
std::string_view getAttributeValue(std::string_view attrName) const
Definition: XMLElement.cc:114
XMLAttribute * setFirstAttribute(XMLAttribute *attribute)
Definition: XMLElement.hh:224
size_t numAttributes() const
Definition: XMLElement.cc:162
XMLElement * setNextSibling(XMLElement *sibling)
Definition: XMLElement.hh:219
bool hasChildren() const
Definition: XMLElement.hh:184
int getAttributeValueAsInt(std::string_view attrName, int defaultValue) const
Definition: XMLElement.cc:134
XMLElement * setFirstChild(XMLElement *child)
Definition: XMLElement.hh:214
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:176
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:197
XMLElement(const char *name_, const char *data_)
Definition: XMLElement.hh:171
This file implemented 3 utility functions:
Definition: Autofire.cc:9
bool operator!=(const Event &x, const Event &y)
Definition: Event.hh:64
bool operator==(const Event &x, const Event &y)
Definition: Event.cc:11
constexpr const char *const filename
SERIALIZE_CLASS_VERSION(CassettePlayer, 2)
void serialize(Archive &ar, unsigned version)
Definition: XMLElement.cc:536
std::vector< OldXMLElement > children
Definition: XMLElement.hh:332
std::vector< std::pair< std::string, std::string > > attributes
Definition: XMLElement.hh:333
static std::unique_ptr< FileContext > getLastSerializedFileContext()
Definition: XMLElement.cc:519
constexpr uint128 operator*(const uint128 &a, const uint128 &b)
Definition: uint128.hh:187
constexpr auto begin(const zstring_view &x)
Definition: zstring_view.hh:83
constexpr auto end(const zstring_view &x)
Definition: zstring_view.hh:84