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  string_view getSystemID() const { return systemID; }
26  XMLElement& getRoot() { return root; }
27 
28 private:
29  XMLElement root;
30  std::vector<XMLElement*> current;
31  string_view systemID;
32 };
33 
34 XMLElement load(string_view 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  newElem = &current.back()->addChild(string(name));
75  } else {
76  root.setName(string(name));
77  newElem = &root;
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(string(name), string(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(string(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
openmsx::XMLLoader::XMLElementParser::stop
void stop()
Definition: XMLLoader.cc:103
openmsx::XMLException
Definition: XMLException.hh:9
one_of.hh
FileException.hh
openmsx::File::getSize
size_t getSize()
Returns the size of this file.
Definition: File.cc:103
XMLException.hh
utf8::unchecked::size
size_t size(std::string_view utf8)
Definition: utf8_unchecked.hh:227
rapidsax::NullHandler
Definition: rapidsax.hh:58
t
TclObject t
Definition: TclObject_test.cc:264
XMLElement.hh
openmsx::XMLElement
Definition: XMLElement.hh:16
openmsx::MemBuffer::resize
void resize(size_t size)
Grow or shrink the memory block.
Definition: MemBuffer.hh:111
XMLLoader.hh
File.hh
openmsx::XMLLoader::XMLElementParser::attribute
void attribute(string_view name, string_view value)
Definition: XMLLoader.cc:82
openmsx::MemBuffer< char >
one_of
Definition: one_of.hh:7
openmsx::XMLLoader::XMLElementParser::start
void start(string_view name)
Definition: XMLLoader.cc:70
openmsx::filename
constexpr const char *const filename
Definition: FirmwareSwitch.cc:10
openmsx::XMLLoader::XMLElementParser::doctype
void doctype(string_view txt)
Definition: XMLLoader.cc:108
openmsx::FileException
Definition: FileException.hh:9
MemBuffer.hh
openmsx::XMLLoader::XMLElementParser::text
void text(string_view txt)
Definition: XMLLoader.cc:92
openmsx::XMLLoader::load
XMLElement load(string_view filename, string_view systemID)
Definition: XMLLoader.cc:34
rapidsax.hh
openmsx::XMLLoader::XMLElementParser::getSystemID
string_view getSystemID() const
Definition: XMLLoader.cc:25
openmsx::File
Definition: File.hh:16
rapidsax::ParseError
Definition: rapidsax.hh:103
openmsx::MemBuffer::data
const T * data() const
Returns pointer to the start of the memory buffer.
Definition: MemBuffer.hh:81
openmsx::File::read
void read(void *buffer, size_t num)
Read from file.
Definition: File.cc:83
openmsx::XMLLoader::XMLElementParser::getRoot
XMLElement & getRoot()
Definition: XMLLoader.cc:26
rapidsax::ParseError::what
const char * what() const
Definition: rapidsax.hh:111
openmsx::XMLLoader
Definition: XMLLoader.cc:13
rapidsax::EXTRA_BUFFER_SPACE
constexpr size_t EXTRA_BUFFER_SPACE
Definition: rapidsax.hh:41
openmsx::MSXException::getMessage
const std::string & getMessage() const &
Definition: MSXException.hh:23
openmsx::XMLLoader::XMLElementParser
Definition: XMLLoader.cc:16
openmsx::XMLElement::setName
void setName(std::string name_)
Definition: XMLElement.hh:32