ARTS 2.5.11 (git: 6827797f)
xml_io_base.cc
Go to the documentation of this file.
1
2// File description
4
13#include "xml_io_base.h"
14#include "arts.h"
15#include "bifstream.h"
16#include "bofstream.h"
17#include "file.h"
18#include <iterator>
19#include <string_view>
20
21namespace {
22static inline std::string quotation_mark_replacement{"”"};
23static inline std::string quotation_mark_original{"\""};
24} // namespace
25
27// XMLTag implementation
29
31
37void XMLTag::check_name(const String& expected_name) {
38 if (name != expected_name)
39 xml_parse_error("Tag <" + expected_name + "> expected but <" + name +
40 "> found.");
41}
42
50void XMLTag::add_attribute(const String& aname, String value) {
51 XMLAttribute attr;
52
53 attr.name = aname;
54
55 auto pos = value.find(quotation_mark_original);
56 while (pos not_eq std::string::npos) {
57 value.replace(pos, quotation_mark_original.length(), quotation_mark_replacement);
58 pos = value.find(quotation_mark_original);
59 }
60
61 attr.value = value;
62 attribs.push_back(attr);
64
66
71void XMLTag::add_attribute(const String& aname, const Index& value) {
72 ostringstream v;
73
74 v << value;
75 add_attribute(aname, v.str());
77
78void XMLTag::add_attribute(const String& aname, const Numeric& value) {
79 ostringstream v;
80
81 v << value;
82 add_attribute(aname, v.str());
83}
84
86
94void XMLTag::check_attribute(const String& aname, const String& value) {
95 String actual_value;
96
97 get_attribute_value(aname, actual_value);
98
99 if (actual_value == "*not found*") {
100 xml_parse_error("Required attribute " + aname + " does not exist");
101 } else if (actual_value != value) {
102 xml_parse_error("Attribute " + aname + " has value \"" + actual_value +
103 "\" but \"" + value + "\" was expected.");
104 }
105}
106
107
108bool XMLTag::has_attribute(const String& aname) const {
109 return std::any_of(attribs.cbegin(), attribs.cend(), [&](auto& attr){return attr.name == aname;});
110}
111
123void XMLTag::get_attribute_value(const String& aname, String& value) {
124 value = "";
125
126 auto it = attribs.begin();
127 while (it != attribs.end()) {
128 if (it->name == aname) {
129 value = it->value;
130 it = attribs.end();
131 } else {
132 it++;
133 }
134 }
135
136 auto pos = value.find(quotation_mark_replacement);
137 while (pos not_eq std::string::npos) {
138 value.replace(pos, quotation_mark_replacement.length(), quotation_mark_original);
139 pos = value.find(quotation_mark_replacement);
140 }
141}
142
144
152void XMLTag::get_attribute_value(const String& aname, Index& value) {
153 String attribute_value;
154 istringstream strstr("");
155
156 get_attribute_value(aname, attribute_value);
157 strstr.str(attribute_value);
158 strstr >> value;
159 if (strstr.fail()) {
160 xml_parse_error("Error while parsing value of " + aname + " from <" + name +
161 ">");
162 }
163}
164
165void XMLTag::get_attribute_value(const String& aname, Numeric& value) {
166 String attribute_value;
167 istringstream strstr("");
168
169 get_attribute_value(aname, attribute_value);
170 strstr.str(attribute_value);
171 strstr >> double_imanip() >> value;
172 if (strstr.fail()) {
173 xml_parse_error("Error while parsing value of " + aname + " from <" + name +
174 ">");
175 }
176}
177
179
184void XMLTag::read_from_stream(istream& is) {
186
187 String token;
188 stringbuf tag;
189 istringstream sstr("");
190 XMLAttribute attr;
191 char ch = 0;
192
193 attribs.clear();
194
195 while (is.good() && isspace(is.peek())) {
196 is.get();
197 }
198
199 is >> ch;
200
201 if (ch != '<') {
202 is >> token;
203 token = ch + token;
204
205 xml_parse_error("'<' expected but " + token + " found.");
206 }
207
208 is.get(tag, '>');
209
210 // Hit EOF while looking for '>'
211 if (is.bad() || is.eof()) {
212 xml_parse_error("Unexpected end of file while looking for '>'");
213 }
214
215 if (is.get() != '>') {
216 xml_parse_error("Closing > not found in tag: " + tag.str());
217 }
218
219 sstr.str(tag.str() + '>');
220 out3 << "Read: " << sstr.str() << '\n';
221
222 sstr >> name;
223
224 if (name[name.length() - 1] == '>') {
225 // Because closin > was found, the tag for sure has no
226 // attributes, set token to ">" to skip reading of attributes
227 name.erase(name.length() - 1, 1);
228 token = ">";
229 } else {
230 // Tag may have attributes, so read next token
231 sstr >> token;
232 }
233 out3 << "Name: " << name << '\n';
234
235 //extract attributes
236 while (token != ">") {
238
239 pos = token.find("=", 0);
240 if (pos == String::npos) {
241 xml_parse_error("Syntax error in tag: " + tag.str());
242 }
243
244 attr.name = token.substr(0, pos);
245 token.erase(0, pos + 1);
246
247 if (token[0] != '\"') {
248 xml_parse_error("Missing \" in tag: " + tag.str());
249 }
250
251 while ((pos = token.find("\"", 1)) == (String::size_type)String::npos &&
252 token != ">") {
253 String ntoken;
254 sstr >> ntoken;
255 if (!ntoken.length()) break;
256 token += " " + ntoken;
257 }
258
259 if (pos == (String::size_type)String::npos) {
260 xml_parse_error("Missing \" in tag: " + sstr.str());
261 }
262
263 if (pos == 1)
264 attr.value = "";
265 else
266 attr.value = token.substr(1, pos - 1);
267
268 attribs.push_back(attr);
269
270 out3 << "Attr: " << attr.name << '\n';
271 out3 << "Value: " << attr.value << '\n';
272
273 if (token[token.length() - 1] == '>') {
274 token = ">";
275 } else {
276 sstr >> token;
277 }
278 }
279
280 out3 << '\n';
281
282 // Skip comments
283 if (name == "comment") {
284 is.get(tag, '<');
285
286 // Hit EOF while looking for '<'
287 if (is.bad() || is.eof()) {
289 "Unexpected end of file while looking for "
290 "comment tag");
291 }
292
294 check_name("/comment");
296 }
297}
298
300
305void XMLTag::write_to_stream(ostream& os) {
306 os << "<" << name;
307
308 auto it = attribs.begin();
309
310 while (it != attribs.end()) {
311 os << ' ' << it->name << "=\"" << it->value << '\"';
312 it++;
313 }
314
315 os << ">";
316}
317
318FileType string2filetype(const String& file_format) {
319 if (file_format == "ascii") return FILE_TYPE_ASCII;
320 if (file_format == "zascii") return FILE_TYPE_ZIPPED_ASCII;
321 if (file_format == "binary") return FILE_TYPE_BINARY;
322
323 throw std::runtime_error(
324 "file_format contains illegal string. "
325 "Valid values are:\n"
326 " ascii: XML output\n"
327 " zascii: Zipped XML output\n"
328 " binary: XML + binary output");
329}
330
332// Functions to open and read XML files
334
336
342void xml_open_output_file(ofstream& file, const String& name) {
343 // Tell the stream that it should throw exceptions.
344 // Badbit means that the entire stream is corrupted, failbit means
345 // that the last operation has failed, but the stream is still
346 // valid. We don't want either to happen!
347 // FIXME: This does not yet work in egcs-2.91.66, try again later.
348 file.exceptions(ios::badbit | ios::failbit);
349
350 // c_str explicitly converts to c String.
351 try {
352 file.open(name.c_str());
353 } catch (const std::exception&) {
354 ostringstream os;
355 os << "Cannot open output file: " << name << '\n'
356 << "Maybe you don't have write access "
357 << "to the directory or the file?";
358 throw runtime_error(os.str());
359 }
360
361 // See if the file is ok.
362 // FIXME: This should not be necessary anymore in the future, when
363 // g++ stream exceptions work properly. (In that case we would not
364 // get here if there really was a problem, because of the exception
365 // thrown by open().)
366 if (!file) {
367 ostringstream os;
368 os << "Cannot open output file: " << name << '\n'
369 << "Maybe you don't have write access "
370 << "to the directory or the file?";
371 throw runtime_error(os.str());
372 }
373}
374
375#ifdef ENABLE_ZLIB
376
378
384void xml_open_output_file(ogzstream& file, const String& name) {
385 // Tell the stream that it should throw exceptions.
386 // Badbit means that the entire stream is corrupted, failbit means
387 // that the last operation has failed, but the stream is still
388 // valid. We don't want either to happen!
389 // FIXME: This does not yet work in egcs-2.91.66, try again later.
390 file.exceptions(ios::badbit | ios::failbit);
391
392 // c_str explicitly converts to c String.
393 String nname = name;
394
395 if (nname.nelem() < 3 || nname.substr(nname.length() - 3, 3) != ".gz") {
396 nname += ".gz";
397 }
398
399 try {
400 file.open(nname.c_str());
401 } catch (const ios::failure&) {
402 ostringstream os;
403 os << "Cannot open output file: " << nname << '\n'
404 << "Maybe you don't have write access "
405 << "to the directory or the file?";
406 throw runtime_error(os.str());
407 }
408
409 // See if the file is ok.
410 // FIXME: This should not be necessary anymore in the future, when
411 // g++ stream exceptions work properly. (In that case we would not
412 // get here if there really was a problem, because of the exception
413 // thrown by open().)
414 if (!file) {
415 ostringstream os;
416 os << "Cannot open output file: " << nname << '\n'
417 << "Maybe you don't have write access "
418 << "to the directory or the file?";
419 throw runtime_error(os.str());
420 }
421}
422
423#endif /* ENABLE_ZLIB */
424
426
432void xml_open_input_file(ifstream& ifs,
433 const String& name,
434 const Verbosity& verbosity) {
436
437 // Tell the stream that it should throw exceptions.
438 // Badbit means that the entire stream is corrupted.
439 // On the other hand, end of file will not lead to an exception, you
440 // have to check this manually!
441 ifs.exceptions(ios::badbit);
442
443 // c_str explicitly converts to c String.
444 try {
445 ifs.open(name.c_str());
446 } catch (const ios::failure&) {
447 ostringstream os;
448 os << "Cannot open input file: " << name << '\n'
449 << "Maybe the file does not exist?";
450 throw runtime_error(os.str());
451 }
452
453 // See if the file is ok.
454 // FIXME: This should not be necessary anymore in the future, when
455 // g++ stream exceptions work properly.
456 if (!ifs) {
457 ostringstream os;
458 os << "Cannot open input file: " << name << '\n'
459 << "Maybe the file does not exist?";
460 throw runtime_error(os.str());
461 }
462
463 out3 << "- Reading input file " << name << "\n";
464}
465
466#ifdef ENABLE_ZLIB
467
469
476 const String& name,
477 const Verbosity& verbosity) {
479
480 // Tell the stream that it should throw exceptions.
481 // Badbit means that the entire stream is corrupted.
482 // On the other hand, end of file will not lead to an exception, you
483 // have to check this manually!
484 ifs.exceptions(ios::badbit);
485
486 // c_str explicitly converts to c String.
487 try {
488 ifs.open(name.c_str());
489 } catch (const ios::failure&) {
490 ostringstream os;
491 os << "Cannot open input file: " << name << '\n'
492 << "Maybe the file does not exist?";
493 throw runtime_error(os.str());
494 }
495
496 // See if the file is ok.
497 // FIXME: This should not be necessary anymore in the future, when
498 // g++ stream exceptions work properly.
499 if (!ifs) {
500 ostringstream os;
501 os << "Cannot open input file: " << name << '\n'
502 << "Maybe the file does not exist?";
503 throw runtime_error(os.str());
504 }
505
506 out3 << "- Reading input file " << name << "\n";
507}
508
509#endif /* ENABLE_ZLIB */
510
512// General XML functions (file header, start root tag, end root tag)
514
516
522void xml_parse_error(const String& str_error) {
523 ostringstream os;
524 os << "XML parse error: " << str_error << '\n'
525 << "Check syntax of XML file\n";
526 throw runtime_error(os.str());
527}
528
530
537void xml_data_parse_error(XMLTag& tag, const String& str_error) {
538 ostringstream os;
539 os << "XML data parse error: Error reading ";
540 tag.write_to_stream(os);
541 os << str_error << "\n"
542 << "Check syntax of XML file. A possible cause is that the file "
543 << "contains NaN or Inf values.\n";
544 throw runtime_error(os.str());
545}
546
548
558 FileType& ftype,
559 NumericType& ntype,
560 EndianType& etype,
561 const Verbosity& verbosity) {
562 char str[6];
563 stringbuf strbuf;
564 XMLTag tag(verbosity);
565 String strtype;
566
567 while (!is.fail() && isspace(is.peek())) is.get();
568
569 is.get(str, 6, ' ');
570
571 if (string(str) != "<?xml") {
573 "Input file is not a valid xml file "
574 "(<?xml not found)");
575 }
576
577 is.get(strbuf, '>');
578 is.get();
579
580 if (is.fail()) {
581 xml_parse_error("Input file is not a valid xml file");
582 }
583
584 tag.read_from_stream(is);
585 tag.check_name("arts");
586
587 // Check file format
588 tag.get_attribute_value("format", strtype);
589 if (strtype == "binary") {
590 ftype = FILE_TYPE_BINARY;
591 } else {
592 ftype = FILE_TYPE_ASCII;
593 }
594
595 // Check endian type
596 tag.get_attribute_value("endian_type", strtype);
597 if (strtype == "little") {
598 etype = ENDIAN_TYPE_LITTLE;
599 }
600 if (strtype == "big") {
601 etype = ENDIAN_TYPE_BIG;
602 }
603 if (strtype == "") {
604 /* out1 << " Warning: Endian type not specified in XML file, "
605 << "assuming little endian (PC)\n";*/
606 etype = ENDIAN_TYPE_LITTLE;
607 } else {
608 ostringstream os;
609 os << " Error: Unknown endian type \"" << strtype
610 << "\" specified in XML file.\n";
611 throw runtime_error(os.str());
612 }
613
614 // Check numeric type
615 tag.get_attribute_value("numeric_type", strtype);
616 if (strtype == "float") {
617 ntype = NUMERIC_TYPE_FLOAT;
618 } else if (strtype == "double") {
619 ntype = NUMERIC_TYPE_DOUBLE;
620 } else if (strtype == "") {
621 /* out1 << " Warning: Numeric type not specified in XML file, "
622 << "assuming double\n";*/
623 ntype = NUMERIC_TYPE_DOUBLE;
624 } else {
625 ostringstream os;
626 os << " Error: Unknown numeric type \"" << strtype
627 << "\" specified in XML file.\n";
628 throw runtime_error(os.str());
629 }
630}
631
633
638void xml_read_footer_from_stream(istream& is, const Verbosity& verbosity) {
639 XMLTag tag(verbosity);
640
641 tag.read_from_stream(is);
642 tag.check_name("/arts");
643}
644
646
651 FileType ftype,
652 const Verbosity& verbosity) {
653 XMLTag tag(verbosity);
654
655 os << "<?xml version=\"1.0\"?>" << '\n';
656
657 tag.set_name("arts");
658 switch (ftype) {
659 case FILE_TYPE_ASCII:
661 tag.add_attribute("format", "ascii");
662 break;
663 case FILE_TYPE_BINARY:
664 tag.add_attribute("format", "binary");
665 break;
666 }
667
668 tag.add_attribute("version", "1");
669
670 tag.write_to_stream(os);
671
672 os << '\n';
673}
674
676
679void xml_write_footer_to_stream(ostream& os, const Verbosity& verbosity) {
680 XMLTag tag(verbosity);
681
682 tag.set_name("/arts");
683 tag.write_to_stream(os);
684
685 os << endl;
686}
687
688void xml_set_stream_precision(ostream& os) {
689 // Determine the precision, depending on whether Numeric is double
690 // or float:
691 int precision;
692#ifdef USE_FLOAT
693 precision = FLT_DIG;
694#else
695#ifdef USE_DOUBLE
696 precision = DBL_DIG;
697#else
698#error Numeric must be double or float
699#endif
700#endif
701
702 os << setprecision(precision);
703}
704
706void parse_xml_tag_content_as_string(std::istream& is_xml, String& content) {
707 char dummy;
708
709 content = "";
710 dummy = (char)is_xml.peek();
711 while (is_xml && dummy != '<') {
712 is_xml.get(dummy);
713 content += dummy;
714 dummy = (char)is_xml.peek();
715 }
716
717 if (!is_xml) throw std::runtime_error("Unexpected end of file.");
718}
The global header file for ARTS.
This file contains the class declaration of bifstream.
This file contains the class declaration of bofstream.
XML attribute class.
Definition: xml_io_base.h:43
String name
Definition: xml_io_base.h:45
String value
Definition: xml_io_base.h:46
The ARTS XML tag class.
Definition: xml_io_base.h:53
String name
Definition: xml_io_base.h:103
bool has_attribute(const String &aname) const
Returns if the attribute exists or not.
Definition: xml_io_base.cc:108
void add_attribute(const String &aname, String value)
Definition: xml_io_base.cc:50
void write_to_stream(ostream &os)
Write XML tag.
Definition: xml_io_base.cc:305
void check_name(const String &expected_name)
Check tag name.
Definition: xml_io_base.cc:37
void read_from_stream(istream &is)
Reads next XML tag.
Definition: xml_io_base.cc:184
Array< XMLAttribute > attribs
Definition: xml_io_base.h:104
void get_attribute_value(const String &aname, String &value)
Definition: xml_io_base.cc:123
void set_name(const String &new_name)
Definition: xml_io_base.h:61
void check_attribute(const String &aname, const String &value)
Checks whether attribute has the expected value.
Definition: xml_io_base.cc:94
Input manipulator class for doubles to enable nan and inf parsing.
Definition: double_imanip.h:25
void open(const char *name, int gz_open_mode=std::ios::in)
Definition: gzstream.h:100
Index nelem() const
Definition: mystring.h:172
static const Index npos
Define npos:
Definition: mystring.h:192
void open(const char *name, int gz_open_mode=std::ios::out)
Definition: gzstream.h:111
This file contains basic functions to handle ASCII files.
#define CREATE_OUT3
Definition: messages.h:189
#define v
void xml_write_footer_to_stream(ostream &os, const Verbosity &verbosity)
Write closing root tag.
Definition: xml_io_base.cc:679
void xml_data_parse_error(XMLTag &tag, const String &str_error)
Throws XML parser runtime error.
Definition: xml_io_base.cc:537
void xml_open_input_file(ifstream &ifs, const String &name, const Verbosity &verbosity)
Open file for XML input.
Definition: xml_io_base.cc:432
void xml_read_footer_from_stream(istream &is, const Verbosity &verbosity)
Reads closing root tag.
Definition: xml_io_base.cc:638
void parse_xml_tag_content_as_string(std::istream &is_xml, String &content)
Get the content of an xml tag as a string.
Definition: xml_io_base.cc:706
void xml_read_header_from_stream(istream &is, FileType &ftype, NumericType &ntype, EndianType &etype, const Verbosity &verbosity)
Reads XML header and root tag.
Definition: xml_io_base.cc:557
void xml_write_header_to_stream(ostream &os, FileType ftype, const Verbosity &verbosity)
Writes XML header and root tag.
Definition: xml_io_base.cc:650
void xml_set_stream_precision(ostream &os)
Definition: xml_io_base.cc:688
FileType string2filetype(const String &file_format)
Definition: xml_io_base.cc:318
void xml_open_output_file(ofstream &file, const String &name)
Open file for XML output.
Definition: xml_io_base.cc:342
void xml_parse_error(const String &str_error)
Throws XML parser runtime error.
Definition: xml_io_base.cc:522
FileType
Definition: xml_io_base.h:25
@ FILE_TYPE_ZIPPED_ASCII
Definition: xml_io_base.h:27
@ FILE_TYPE_BINARY
Definition: xml_io_base.h:28
@ FILE_TYPE_ASCII
Definition: xml_io_base.h:26
NumericType
Definition: xml_io_base.h:31
@ NUMERIC_TYPE_FLOAT
Definition: xml_io_base.h:31
@ NUMERIC_TYPE_DOUBLE
Definition: xml_io_base.h:31
EndianType
Definition: xml_io_base.h:32
@ ENDIAN_TYPE_BIG
Definition: xml_io_base.h:32
@ ENDIAN_TYPE_LITTLE
Definition: xml_io_base.h:32
void xml_parse_error(const String &str_error)
Throws XML parser runtime error.
Definition: xml_io_base.cc:522