PLaSK library

Loading...
Searching...
No Matches
reader.cpp
Go to the documentation of this file.
1/*
2 * This file is part of PLaSK (https://plask.app) by Photonics Group at TUL
3 * Copyright (c) 2022 Lodz University of Technology
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, version 3.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 */
14#include "reader.hpp"
15#include <expat.h>
16
17#include "../../math.hpp"
18
19#include <fstream>
20
21 namespace plask {
22
23std::size_t XMLReader::StreamDataSource::read(char* buff, std::size_t buf_size) {
24 input->read(buff, buf_size);
25 if (input->bad())
26 throw XMLException("XML reader: Can't read input data from C++ stream.");
27 return input->gcount();
28}
29
30std::size_t XMLReader::CFileDataSource::read(char* buff, std::size_t buf_size) {
31 std::size_t read = fread(buff, sizeof(char), buf_size, desc);
32 if (read != buf_size && ferror(desc)) {
33 throw XMLException("XML reader: Can't read input data from C file.");
34 }
35 return read;
36}
37
38
39 void XMLReader::startTag(void *data, const char *element, const char **attribute) {
40 State& state = reinterpret_cast<XMLReader*>(data)->appendState(NODE_ELEMENT, element);
41 for (int i = 0; attribute[i]; i += 2) {
42 state.attributes[attribute[i]] = attribute[i+1];
43 }
44}
45
46 void XMLReader::endTag(void *data, const char *element) {
47 reinterpret_cast<XMLReader*>(data)->appendState(NODE_ELEMENT_END, element);
48}
49
50 void XMLReader::characterData(void* data, const XML_Char *string, int string_len) {
51 reinterpret_cast<XMLReader*>(data)->appendState(NODE_TEXT, std::string(string, string_len));
52}
53
54XMLReader::State& XMLReader::appendState(NodeType type, const std::string& text) {
55 if (!states.empty() && states.back().type == NODE_TEXT) {
56 if (type == NODE_TEXT) {
57 states.back().text.append(text);
58 return states.back();
59 } else
60 if (states.back().hasWhiteText())
61 states.pop_back();
62 }
63 states.emplace_back(type, XML_GetCurrentLineNumber(parser), XML_GetCurrentColumnNumber(parser), text);
64 return states.back();
65}
66
67 bool XMLReader::readSome() {
68 constexpr int buff_size = 1024 * 8;
69 char buff[buff_size];
70 int read = int(source->read(buff, buff_size));
71 bool has_more = buff_size == read;
72 if (XML_Parse(parser, buff, read, !has_more) == XML_STATUS_ERROR) {
73 auto error_code = XML_GetErrorCode(parser);
74 if (error_code != XML_ERROR_FINISHED) {
75 auto line = XML_GetCurrentLineNumber(parser);
76 throw XMLException("XML line " +
77 boost::lexical_cast<std::string>(line) + ": parse error: "
78 + XML_ErrorString(error_code), int(line));
79 }
80 }
81 return has_more;
82}
83
84 void XMLReader::initParser() {
85 parser = XML_ParserCreateNS(NULL, ' ');
86 XML_SetUserData(parser, this);
87 XML_SetElementHandler(parser, &XMLReader::startTag, &XMLReader::endTag);
88 XML_SetCharacterDataHandler(parser, &XMLReader::characterData);
89}
90
91 XMLReader::NodeType XMLReader::ensureNodeTypeIs(int required_types, const char *new_tag_name) const
92{
93 NodeType result = this->getNodeType();
94 if (((required_types & result) == 0) ||
95 (new_tag_name && (result == NODE_ELEMENT) && (getNodeName() != new_tag_name)))
96 {
97 std::string msg;
98 if (required_types & NODE_ELEMENT) {
99 if (new_tag_name) {
100 msg += "<";
101 msg += new_tag_name;
102 msg += ">";
103 } else
104 msg += "new tag";
105 }
106 if (required_types & NODE_ELEMENT_END) {
107 if (!msg.empty()) msg += " or ";
108 if (result == NODE_ELEMENT) {
109 assert(path.size() >= 2);
110 msg += "</" + path[path.size()-2] + ">";
111 } else
112 msg += "</" + path.back() + ">";
113 }
114 if (required_types & NODE_TEXT) {
115 if (!msg.empty()) msg += " or ";
116 msg += "content of <" + path.back() + "> tag";
117 }
118 throwUnexpectedElementException(msg);
119 }
120 return result;
121}
122
123#define DEFAULT_PARSERS &XMLReader::strToBool, &parse_complex<double>, &XMLReader::strToUnsigned
124
125 XMLReader::XMLReader(std::unique_ptr<DataSource> &&source):
126 source(std::move(source)), stringInterpreter(DEFAULT_PARSERS), check_if_all_attributes_were_read(true)
127{
128 initParser();
129}
130
131 XMLReader::XMLReader(std::unique_ptr<std::istream> &&istream):
132 source(new StreamDataSource(std::move(istream))), stringInterpreter(DEFAULT_PARSERS), check_if_all_attributes_were_read(true)
133{
134 initParser();
135}
136
137 XMLReader::XMLReader(const char* file_name):
138 source(new StreamDataSource(new std::ifstream(file_name))), stringInterpreter(DEFAULT_PARSERS), check_if_all_attributes_were_read(true)
139{
140 initParser();
141}
142
143 XMLReader::XMLReader(FILE* file):
144 source(new CFileDataSource(file)), stringInterpreter(DEFAULT_PARSERS), check_if_all_attributes_were_read(true)
145{
146 initParser();
147}
148
149#if (__cplusplus >= 201103L) || defined(__GXX_EXPERIMENTAL_CXX0X__)
150 XMLReader::XMLReader(XMLReader &&to_move)
151 : source(std::move(to_move.source)),
152 states(std::move(to_move.states)),
153 parser(to_move.parser),
154 path(std::move(to_move.path)),
155 read_attributes(std::move(to_move.read_attributes)),
156 stringInterpreter(std::move(to_move.stringInterpreter)),
157 check_if_all_attributes_were_read(to_move.check_if_all_attributes_were_read)
158{
159 to_move.parser = 0;
160}
161
162XMLReader &XMLReader::operator=(XMLReader &&to_move)
163{
164 swap(to_move);
165 return *this;
166}
167#endif
168
169 XMLReader::~XMLReader() {
170 XML_ParserFree(this->parser);
171}
172
173 void XMLReader::swap(XMLReader &to_swap)
174{
175 std::swap(source, to_swap.source);
176 std::swap(states, to_swap.states);
177 std::swap(parser, to_swap.parser);
178 std::swap(path, to_swap.path);
179 std::swap(read_attributes, to_swap.read_attributes);
180 std::swap(stringInterpreter, to_swap.stringInterpreter);
181 std::swap(check_if_all_attributes_were_read, to_swap.check_if_all_attributes_were_read);
182}
183
184 bool XMLReader::next() {
185 if (!states.empty()) {
186 if (getNodeType() == NODE_ELEMENT) {
187 if (check_if_all_attributes_were_read && (getAttributeCount() != read_attributes.size())) {
188 std::string attr;
189 for (const std::pair<const std::string, std::string>& a: getCurrent().attributes)
190 if (read_attributes.find(a.first) == read_attributes.end()) {
191 if (!attr.empty()) attr += ", ";
192 attr += a.first;
193 }
194 throw XMLUnexpectedAttrException(*this, attr);
195 }
196 read_attributes.clear();
197 }
198 else if (getCurrent().type == NODE_ELEMENT_END)
199 path.pop_back();
200 states.pop_front();
201 }
202 check_if_all_attributes_were_read = true;
203
204 while (!hasCurrent() && readSome())
205 ;
206
207 if (hasCurrent()) {
208 if (getCurrent().type == NODE_ELEMENT) path.push_back(getCurrent().text);
209 return true;
210 } else
211 return false;
212}
213
214 const std::map<std::string, std::string> XMLReader::getAttributes() const {
215 ensureHasCurrent();
216 this->ignoreAllAttributes();
217 if (attributeFilter) {
218 std::map<std::string, std::string> parsed;
219 for (const auto& attr: getCurrent().attributes) {
220 try {
221 parsed[attr.first] = attributeFilter(attr.second);
222 } catch (const std::exception& e) {
223 unsigned line = this->getCurrent().lineNr;
224 throw XMLException("XML line " + boost::lexical_cast<std::string>(line) +
225 " in <" + this->getCurrent().text + "> attribute '" + attr.first +
226 "': Bad parsed expression", e.what(), int(line));
227 }
228 }
229 return parsed;
230 } else {
231 return getCurrent().attributes;
232 }
233}
234
235 void XMLReader::removeAlienNamespaceAttr() {
236 if (getNodeType() != NODE_ELEMENT)
237 throwUnexpectedElementException("element");
238 auto iter = states.front().attributes.begin();
239 while (iter != states.front().attributes.end()) {
240 if (iter->first.find(' ') != std::string::npos) // not in default NS?
241 states.front().attributes.erase(iter++);
242 else
243 ++iter;
244 }
245}
246
247std::string XMLReader::getNodeName() const {
248 NodeType n = getNodeType();
249 if (n == NODE_TEXT)
250 return path.back();
251 //if (n == NODE_ELEMENT || n == NODE_ELEMENT_END)
252 // return getCurrent().text;
253 //throw XMLUnexpectedElementException(*this, "element or end of element");
254 return getCurrent().text;
255}
256
257std::string XMLReader::getTextContent() const {
258 if (getNodeType() != NODE_TEXT) {
259 if (getNodeType() == NODE_ELEMENT_END)
260 return "";
261 else
262 throw XMLUnexpectedElementException(*this, "text");
263 }
264 if (contentFilter) {
265 try {
266 return contentFilter(getCurrent().text);
267 } catch (const std::exception& e) {
268 unsigned line = this->getCurrent().lineNr;
269 throw XMLException("XML line " + boost::lexical_cast<std::string>(line) +
270 ": Bad parsed expression", e.what(), int(line));
271 }
272 } else
273 return getCurrent().text;
274}
275
276 plask::optional<std::string> XMLReader::getAttribute(const std::string& name) const {
277 auto res_it = this->getCurrent().attributes.find(name);
278 if (res_it == this->getCurrent().attributes.end())
279 return plask::optional<std::string>();
280 read_attributes.insert(name); //TODO should this be thread-safe?
281 if (attributeFilter) {
282 try {
283 return attributeFilter(res_it->second);
284 } catch (const std::exception& e) {
285 unsigned line = this->getCurrent().lineNr;
286 throw XMLException("XML line " + boost::lexical_cast<std::string>(line) +
287 " in <" + this->getCurrent().text + "> attribute '" + name +
288 "': Bad parsed expression", e.what(), int(line));
289 }
290 } else
291 return res_it->second;
292}
293
294std::string XMLReader::requireAttribute(const std::string& attr_name) const {
295 plask::optional<std::string> result = getAttribute(attr_name);
296 if (!result) throw XMLNoAttrException(*this, attr_name);
297 return *result;
298}
299
300 void XMLReader::requireNext() {
301 if (!next()) throwUnexpectedEndException();
302}
303
304 XMLReader::NodeType XMLReader::requireNext(int required_types, const char *new_tag_name) {
305 requireNext();
306 return ensureNodeTypeIs(required_types, new_tag_name);
307}
308
309 void XMLReader::requireTag() {
310 requireNext(NODE_ELEMENT);
311}
312
313 void XMLReader::requireTag(const std::string& name) {
314 requireNext(NODE_ELEMENT, name.c_str());
315}
316
317 bool XMLReader::requireTagOrEnd() {
318 return requireNext(NODE_ELEMENT | NODE_ELEMENT_END) == NODE_ELEMENT;
319}
320
321 bool XMLReader::requireTagOrEnd(const std::string& name) {
322 return requireNext(NODE_ELEMENT | NODE_ELEMENT_END, name.c_str()) == NODE_ELEMENT;
323}
324
325 void XMLReader::requireTagEnd() {
326 requireNext(NODE_ELEMENT_END);
327}
328
329std::string XMLReader::requireText() {
330 requireNext();
331 return getTextContent();
332}
333
334std::string XMLReader::requireTextInCurrentTag() {
335 std::string t = requireText();
336 if (t.length() != 0) requireTagEnd();
337 return t;
338}
339
340 bool XMLReader::gotoNextOnLevel(std::size_t required_level, NodeType required_type) {
341 ignoreAllAttributes();
342 while (next()) {
343 if (getLevel() == required_level && getNodeType() == required_type)
344 return true;
345 ignoreAllAttributes();
346 }
347 return false;
348}
349
350 bool XMLReader::gotoNextTagOnCurrentLevel() {
351 return gotoNextOnLevel(getLevel());
352}
353
354 void XMLReader::gotoEndOfCurrentTag() {
355 gotoNextOnLevel(getLevel(), NODE_ELEMENT_END);
356}
357
358
359} // namespace plask