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