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  [[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 
166 public:
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 
227 private:
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 {
252 public:
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 
300 private:
301  XMLElement* loadElement(MemInputArchive& ar);
302  XMLElement* clone(const XMLElement& inElem);
303  XMLElement* clone(const OldXMLElement& elem);
304 
305 private:
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.
318 class 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
const XMLElement * getRoot() const
Definition: XMLElement.hh:269
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
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 ** findAttributePointer(std::string_view attrName)
Definition: XMLElement.cc:145
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
const XMLElement * getFirstChild() const
Definition: XMLElement.hh:182
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:221
size_t numAttributes() const
Definition: XMLElement.cc:162
XMLElement * setNextSibling(XMLElement *sibling)
Definition: XMLElement.hh:216
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
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)
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)