openMSX
XMLLoader.cc
Go to the documentation of this file.
1 #include "XMLLoader.hh"
2 #include "XMLElement.hh"
3 #include "XMLException.hh"
4 #include "File.hh"
5 #include "FileException.hh"
6 #include "MemBuffer.hh"
7 #include "one_of.hh"
8 #include "rapidsax.hh"
9 
10 using std::string_view;
11 
12 namespace openmsx::XMLLoader {
13 
15 {
16 public:
17  // rapidsax handler interface
18  void start(string_view name);
19  void attribute(string_view name, string_view value);
20  void text(string_view txt);
21  void stop();
22  void doctype(string_view txt);
23 
24  [[nodiscard]] string_view getSystemID() const { return systemID; }
25  [[nodiscard]] XMLElement& getRoot() { return root; }
26 
27 private:
28  XMLElement root;
29  std::vector<XMLElement*> current;
30  string_view systemID;
31 };
32 
33 XMLElement load(const std::string& filename, string_view systemID)
34 {
35  MemBuffer<char> buf;
36  try {
37  File file(filename);
38  auto size = file.getSize();
40  file.read(buf.data(), size);
41  buf[size] = 0;
42  } catch (FileException& e) {
43  throw XMLException(filename, ": failed to read: ", e.getMessage());
44  }
45 
46  XMLElementParser handler;
47  try {
48  rapidsax::parse<0>(handler, buf.data());
49  } catch (rapidsax::ParseError& e) {
50  throw XMLException(filename, ": Document parsing failed: ", e.what());
51  }
52  auto& root = handler.getRoot();
53  if (root.getName().empty()) {
54  throw XMLException(filename,
55  ": Document doesn't contain mandatory root Element");
56  }
57  if (handler.getSystemID().empty()) {
58  throw XMLException(filename, ": Missing systemID.\n"
59  "You're probably using an old incompatible file format.");
60  }
61  if (handler.getSystemID() != systemID) {
62  throw XMLException(filename, ": systemID doesn't match "
63  "(expected ", systemID, ", got ", handler.getSystemID(), ")\n"
64  "You're probably using an old incompatible file format.");
65  }
66  return std::move(root);
67 }
68 
69 void XMLElementParser::start(string_view name)
70 {
71  XMLElement* newElem = [&] {
72  if (!current.empty()) {
73  return &current.back()->addChild(name);
74  } else {
75  root.setName(name);
76  return &root;
77  }
78  }();
79  current.push_back(newElem);
80 }
81 
82 void XMLElementParser::attribute(string_view name, string_view value)
83 {
84  if (current.back()->hasAttribute(name)) {
85  throw XMLException(
86  "Found duplicate attribute \"", name, "\" in <",
87  current.back()->getName(), ">.");
88  }
89  current.back()->addAttribute(name, value);
90 }
91 
92 void XMLElementParser::text(string_view txt)
93 {
94  if (current.back()->hasChildren()) {
95  // no mixed-content elements
96  throw XMLException(
97  "Mixed text+subtags in <", current.back()->getName(),
98  ">: \"", txt, "\".");
99  }
100  current.back()->setData(txt);
101 }
102 
104 {
105  current.pop_back();
106 }
107 
108 void XMLElementParser::doctype(string_view txt)
109 {
110  auto pos1 = txt.find(" SYSTEM ");
111  if (pos1 == string_view::npos) return;
112  if ((pos1 + 8) >= txt.size()) return;
113  char q = txt[pos1 + 8];
114  if (q != one_of('"', '\'')) return;
115  auto t = txt.substr(pos1 + 9);
116  auto pos2 = t.find(q);
117  if (pos2 == string_view::npos) return;
118 
119  systemID = t.substr(0, pos2);
120 }
121 
122 } // namespace openmsx::XMLLoader
TclObject t
Definition: one_of.hh:7
void read(void *buffer, size_t num)
Read from file.
Definition: File.cc:91
size_t getSize()
Returns the size of this file.
Definition: File.cc:111
const std::string & getMessage() const &
Definition: MSXException.hh:23
const T * data() const
Returns pointer to the start of the memory buffer.
Definition: MemBuffer.hh:81
void resize(size_t size)
Grow or shrink the memory block.
Definition: MemBuffer.hh:111
void setName(String &&name_)
Definition: XMLElement.hh:51
void text(string_view txt)
Definition: XMLLoader.cc:92
string_view getSystemID() const
Definition: XMLLoader.cc:24
void start(string_view name)
Definition: XMLLoader.cc:69
void attribute(string_view name, string_view value)
Definition: XMLLoader.cc:82
void doctype(string_view txt)
Definition: XMLLoader.cc:108
XMLElement load(const std::string &filename, string_view systemID)
Definition: XMLLoader.cc:33
constexpr const char *const filename
constexpr size_t EXTRA_BUFFER_SPACE
Definition: rapidsax.hh:41
size_t size(std::string_view utf8)