openMSX
XMLElement.cc
Go to the documentation of this file.
1 #include "XMLElement.hh"
2 #include "StringOp.hh"
3 #include "FileContext.hh" // for bwcompat
4 #include "ConfigException.hh"
5 #include "serialize.hh"
6 #include "serialize_stl.hh"
7 #include "stl.hh"
8 #include "unreachable.hh"
9 #include "xrange.hh"
10 #include <cassert>
11 #include <algorithm>
12 
13 using std::unique_ptr;
14 using std::string;
15 
16 namespace openmsx {
17 
19  : name(name_.str())
20 {
21 }
22 
24  : name(name_.str())
25  , data(data_.str())
26 {
27 }
28 
30 {
31  children.emplace_back(childName);
32  return children.back();
33 }
35 {
36  children.emplace_back(childName, childData);
37  return children.back();
38 }
39 
41 {
42  children.erase(rfind_if_unguarded(children,
43  [&](Children::value_type& v) { return &v == &child; }));
44 }
45 
46 XMLElement::Attributes::iterator XMLElement::findAttribute(string_ref attrName)
47 {
48  return find_if(begin(attributes), end(attributes),
49  [&](Attribute& a) { return a.first == attrName; });
50 }
51 XMLElement::Attributes::const_iterator XMLElement::findAttribute(string_ref attrName) const
52 {
53  return find_if(begin(attributes), end(attributes),
54  [&](const Attribute& a) { return a.first == attrName; });
55 }
56 
58 {
59  assert(findAttribute(attrName) == end(attributes));
60  attributes.emplace_back(attrName.str(), value.str());
61 }
62 
64 {
65  auto it = findAttribute(attrName);
66  if (it != end(attributes)) {
67  it->second = value.str();
68  } else {
69  attributes.emplace_back(attrName.str(), value.str());
70  }
71 }
72 
74 {
75  auto it = findAttribute(attrName);
76  if (it != end(attributes)) {
77  attributes.erase(it);
78  }
79 }
80 
82 {
83  name = newName.str();
84 }
85 
87 {
88  name.clear();
89 }
90 
92 {
93  assert(children.empty()); // no mixed-content elements
94  data = newData.str();
95 }
96 
97 std::vector<const XMLElement*> XMLElement::getChildren(string_ref childName) const
98 {
99  std::vector<const XMLElement*> result;
100  for (auto& c : children) {
101  if (c.getName() == childName) {
102  result.push_back(&c);
103  }
104  }
105  return result;
106 }
107 
109 {
110  for (auto& c : children) {
111  if (c.getName() == childName) {
112  return &c;
113  }
114  }
115  return nullptr;
116 }
118 {
119  return const_cast<XMLElement*>(this)->findChild(childName);
120 }
121 
123  size_t& fromIndex) const
124 {
125  for (auto i : xrange(fromIndex, children.size())) {
126  if (children[i].getName() == childName) {
127  fromIndex = i + 1;
128  return &children[i];
129  }
130  }
131  for (auto i : xrange(fromIndex)) {
132  if (children[i].getName() == childName) {
133  fromIndex = i + 1;
134  return &children[i];
135  }
136  }
137  return nullptr;
138 }
139 
141  string_ref attName, string_ref attValue)
142 {
143  for (auto& c : children) {
144  if ((c.getName() == childName) &&
145  (c.getAttribute(attName) == attValue)) {
146  return &c;
147  }
148  }
149  return nullptr;
150 }
151 
153  string_ref attName, string_ref attValue) const
154 {
155  return const_cast<XMLElement*>(this)->findChildWithAttribute(
156  childName, attName, attValue);
157 }
158 
160 {
161  if (auto* elem = findChild(childName)) {
162  return *elem;
163  }
165  "Missing tag \"" << childName << "\".");
166 }
168 {
169  return const_cast<XMLElement*>(this)->getChild(childName);
170 }
171 
173  string_ref defaultValue)
174 {
175  if (auto* result = findChild(childName)) {
176  return *result;
177  }
178  return addChild(childName, defaultValue);
179 }
180 
182  string_ref childName, string_ref attName,
183  string_ref attValue)
184 {
185  if (auto* result = findChildWithAttribute(childName, attName, attValue)) {
186  return *result;
187  }
188  auto& result = addChild(childName);
189  result.addAttribute(attName, attValue);
190  return result;
191 }
192 
193 const string& XMLElement::getChildData(string_ref childName) const
194 {
195  return getChild(childName).getData();
196 }
197 
199  string_ref defaultValue) const
200 {
201  auto* child = findChild(childName);
202  return child ? child->getData() : defaultValue;
203 }
204 
205 bool XMLElement::getChildDataAsBool(string_ref childName, bool defaultValue) const
206 {
207  auto* child = findChild(childName);
208  return child ? StringOp::stringToBool(child->getData()) : defaultValue;
209 }
210 
211 int XMLElement::getChildDataAsInt(string_ref childName, int defaultValue) const
212 {
213  auto* child = findChild(childName);
214  return child ? StringOp::stringToInt(child->getData()) : defaultValue;
215 }
216 
218 {
219  if (auto* child = findChild(childName)) {
220  child->setData(value);
221  } else {
222  addChild(childName, value);
223  }
224 }
225 
227 {
228  children.clear();
229 }
230 
232 {
233  return findAttribute(attrName) != end(attributes);
234 }
235 
236 const string& XMLElement::getAttribute(string_ref attName) const
237 {
238  auto it = findAttribute(attName);
239  if (it == end(attributes)) {
240  throw ConfigException("Missing attribute \"" +
241  attName + "\".");
242  }
243  return it->second;
244 }
245 
247  string_ref defaultValue) const
248 {
249  auto it = findAttribute(attName);
250  return (it == end(attributes)) ? defaultValue : it->second;
251 }
252 
254  bool defaultValue) const
255 {
256  auto it = findAttribute(attName);
257  return (it == end(attributes)) ? defaultValue
258  : StringOp::stringToBool(it->second);
259 }
260 
262  int defaultValue) const
263 {
264  auto it = findAttribute(attName);
265  return (it == end(attributes)) ? defaultValue
266  : StringOp::stringToInt(it->second);
267 }
268 
270  unsigned& result) const
271 {
272  auto it = findAttribute(attName);
273  if (it != end(attributes)) {
274  result = StringOp::stringToInt(it->second);
275  return true;
276  } else {
277  return false;
278  }
279 }
280 
281 string XMLElement::dump() const
282 {
283  StringOp::Builder result;
284  dump(result, 0);
285  return result;
286 }
287 
288 void XMLElement::dump(StringOp::Builder& result, unsigned indentNum) const
289 {
290  string indent(indentNum, ' ');
291  result << indent << '<' << getName();
292  for (auto& p : attributes) {
293  result << ' ' << p.first
294  << "=\"" << XMLEscape(p.second) << '"';
295  }
296  if (children.empty()) {
297  if (data.empty()) {
298  result << "/>\n";
299  } else {
300  result << '>' << XMLEscape(data) << "</"
301  << getName() << ">\n";
302  }
303  } else {
304  result << ">\n";
305  for (auto& c : children) {
306  c.dump(result, indentNum + 2);
307  }
308  result << indent << "</" << getName() << ">\n";
309  }
310 }
311 
312 // This routine does the following substitutions:
313 // & -> &amp; must always be done
314 // < -> &lt; must always be done
315 // > -> &gt; always allowed, but must be done when it appears as ]]>
316 // ' -> &apos; always allowed, but must be done inside quoted attribute
317 // " -> &quot; always allowed, but must be done inside quoted attribute
318 // So to simplify things we always do these 5 substitutions.
319 string XMLElement::XMLEscape(const string& s)
320 {
321  static const char* const CHARS = "<>&\"'";
322  size_t i = s.find_first_of(CHARS);
323  if (i == string::npos) return s; // common case, no substitutions
324 
325  string result;
326  result.reserve(s.size() + 10); // extra space for at least 2 substitutions
327  size_t pos = 0;
328  do {
329  result += s.substr(pos, i - pos);
330  switch (s[i]) {
331  case '<' : result += "&lt;"; break;
332  case '>' : result += "&gt;"; break;
333  case '&' : result += "&amp;"; break;
334  case '"' : result += "&quot;"; break;
335  case '\'': result += "&apos;"; break;
336  default: UNREACHABLE;
337  }
338  pos = i + 1;
339  i = s.find_first_of(CHARS, pos);
340  } while (i != string::npos);
341  result += s.substr(pos);
342  return result;
343 }
344 
345 static unique_ptr<FileContext> lastSerializedFileContext;
347 {
348  return std::move(lastSerializedFileContext); // this also sets value to nullptr;
349 }
350 // version 1: initial version
351 // version 2: removed 'context' tag
352 // also removed 'parent', but that was never serialized
353 // 2b: (no need to increase version) name and data members are
354 // serialized as normal members instead of constructor parameters
355 // 2c: (no need to increase version) attributes were initially stored as
356 // map<string, string>, later this was changed to
357 // vector<pair<string, string>>. To keep bw-compat the serialize()
358 // method converted between these two formats. Though (by luck) in
359 // the XML output both datastructures are serialized to the same
360 // format, so we can drop this conversion step without breaking
361 // bw-compat.
362 template<typename Archive>
363 void XMLElement::serialize(Archive& ar, unsigned version)
364 {
365  ar.serialize("name", name);
366  ar.serialize("data", data);
367  ar.serialize("attributes", attributes);
368  ar.serialize("children", children);
369 
370  if (ar.versionBelow(version, 2)) {
371  assert(ar.isLoader());
372  unique_ptr<FileContext> context;
373  ar.serialize("context", context);
374  if (context) {
375  assert(!lastSerializedFileContext);
376  lastSerializedFileContext = std::move(context);
377  }
378  }
379 }
381 
382 } // namespace openmsx
const std::string & getName() const
Definition: XMLElement.hh:30
string_ref::const_iterator end(const string_ref &x)
Definition: string_ref.hh:161
bool stringToBool(string_ref str)
Definition: StringOp.cc:208
void removeAttribute(string_ref name)
Definition: XMLElement.cc:73
const XMLElement & getChild(string_ref name) const
Definition: XMLElement.cc:167
void setData(string_ref data)
Definition: XMLElement.cc:91
const XMLElement * findNextChild(string_ref name, size_t &fromIndex) const
Definition: XMLElement.cc:122
void setChildData(string_ref name, string_ref value)
Definition: XMLElement.cc:217
void removeChild(const XMLElement &child)
Definition: XMLElement.cc:40
void setName(string_ref name)
Definition: XMLElement.cc:81
const XMLElement * findChild(string_ref name) const
Definition: XMLElement.cc:117
This class implements a subset of the proposal for std::string_ref (proposed for the next c++ standar...
Definition: string_ref.hh:18
std::string dump() const
Definition: XMLElement.cc:281
bool findAttributeInt(string_ref attName, unsigned &result) const
Definition: XMLElement.cc:269
const XMLElement * findChildWithAttribute(string_ref name, string_ref attName, string_ref attValue) const
Definition: XMLElement.cc:152
XMLElement & getCreateChild(string_ref name, string_ref defaultValue={})
Definition: XMLElement.cc:172
bool getChildDataAsBool(string_ref name, bool defaultValue=false) const
Definition: XMLElement.cc:205
XMLElement & getCreateChildWithAttribute(string_ref name, string_ref attName, string_ref attValue)
Definition: XMLElement.cc:181
int getAttributeAsInt(string_ref attName, int defaultValue=0) const
Definition: XMLElement.cc:261
const std::string & getData() const
Definition: XMLElement.hh:35
void serialize(Archive &ar, unsigned version)
Definition: XMLElement.cc:363
bool hasAttribute(string_ref name) const
Definition: XMLElement.cc:231
static std::unique_ptr< FileContext > getLastSerializedFileContext()
Definition: XMLElement.cc:346
std::unique_ptr< Context > context
Definition: GLContext.cc:9
const std::string & getChildData(string_ref name) const
Definition: XMLElement.cc:193
std::string str() const
Definition: string_ref.cc:12
Thanks to enen for testing this on a real cartridge:
Definition: Autofire.cc:5
int getChildDataAsInt(string_ref name, int defaultValue=0) const
Definition: XMLElement.cc:211
int stringToInt(const string &str)
Definition: StringOp.cc:181
#define INSTANTIATE_SERIALIZE_METHODS(CLASS)
Definition: serialize.hh:840
void addAttribute(string_ref name, string_ref value)
Definition: XMLElement.cc:57
static std::string XMLEscape(const std::string &str)
Definition: XMLElement.cc:319
const std::string & getAttribute(string_ref attName) const
Definition: XMLElement.cc:236
XMLElement & addChild(string_ref name)
Definition: XMLElement.cc:29
void setAttribute(string_ref name, string_ref value)
Definition: XMLElement.cc:63
string_ref::const_iterator begin(const string_ref &x)
Definition: string_ref.hh:160
const Children & getChildren() const
Definition: XMLElement.hh:49
auto rfind_if_unguarded(RANGE &range, PRED pred) -> decltype(std::begin(range))
Definition: stl.hh:174
XRange< T > xrange(T e)
Definition: xrange.hh:98
bool getAttributeAsBool(string_ref attName, bool defaultValue=false) const
Definition: XMLElement.cc:253
#define UNREACHABLE
Definition: unreachable.hh:35