ARTS 2.5.4 (git: bcd8c674)
xml_io_base.cc
Go to the documentation of this file.
1/* Copyright (C) 2002-2012 Oliver Lemke <olemke@core-dump.info>
2
3 This program is free software; you can redistribute it and/or modify it
4 under the terms of the GNU General Public License as published by the
5 Free Software Foundation; either version 2, or (at your option) any
6 later version.
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License
14 along with this program; if not, write to the Free Software
15 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
16 USA. */
17
19// File description
21
30#include "xml_io_base.h"
31#include "arts.h"
32#include "bifstream.h"
33#include "bofstream.h"
34#include "file.h"
35#include <iterator>
36#include <string_view>
37
38namespace {
39constexpr std::string_view quotation_mark_replacement{"”"};
40constexpr std::string_view quotation_mark_original{"\""};
41} // namespace
42
44// XMLTag implementation
46
48
54void XMLTag::check_name(const String& expected_name) {
55 if (name != expected_name)
56 xml_parse_error("Tag <" + expected_name + "> expected but <" + name +
57 "> found.");
58}
59
67void XMLTag::add_attribute(const String& aname, String value) {
68 XMLAttribute attr;
69
70 attr.name = aname;
71
72 auto pos = value.find(quotation_mark_original);
73 while (pos not_eq std::string::npos) {
74 value.replace(pos, quotation_mark_original.length(), quotation_mark_replacement);
75 pos = value.find(quotation_mark_original);
76 }
77
78 attr.value = value;
79 attribs.push_back(attr);
81
83
88void XMLTag::add_attribute(const String& aname, const Index& value) {
89 ostringstream v;
90
91 v << value;
92 add_attribute(aname, v.str());
94
95void XMLTag::add_attribute(const String& aname, const Numeric& value) {
96 ostringstream v;
97
98 v << value;
99 add_attribute(aname, v.str());
100}
101
103
111void XMLTag::check_attribute(const String& aname, const String& value) {
112 String actual_value;
113
114 get_attribute_value(aname, actual_value);
115
116 if (actual_value == "*not found*") {
117 xml_parse_error("Required attribute " + aname + " does not exist");
118 } else if (actual_value != value) {
119 xml_parse_error("Attribute " + aname + " has value \"" + actual_value +
120 "\" but \"" + value + "\" was expected.");
121 }
122}
123
124
125bool XMLTag::has_attribute(const String& aname) const {
126 return std::any_of(attribs.cbegin(), attribs.cend(), [&](auto& attr){return attr.name == aname;});
127}
128
140void XMLTag::get_attribute_value(const String& aname, String& value) {
141 value = "";
142
143 auto it = attribs.begin();
144 while (it != attribs.end()) {
145 if (it->name == aname) {
146 value = it->value;
147 it = attribs.end();
148 } else {
149 it++;
150 }
151 }
152
153 auto pos = value.find(quotation_mark_replacement);
154 while (pos not_eq std::string::npos) {
155 value.replace(pos, quotation_mark_replacement.length(), quotation_mark_original);
156 pos = value.find(quotation_mark_replacement);
157 }
158}
159
161
169void XMLTag::get_attribute_value(const String& aname, Index& value) {
170 String attribute_value;
171 istringstream strstr("");
172
173 get_attribute_value(aname, attribute_value);
174 strstr.str(attribute_value);
175 strstr >> value;
176 if (strstr.fail()) {
177 xml_parse_error("Error while parsing value of " + aname + " from <" + name +
178 ">");
179 }
180}
181
182void XMLTag::get_attribute_value(const String& aname, Numeric& value) {
183 String attribute_value;
184 istringstream strstr("");
185
186 get_attribute_value(aname, attribute_value);
187 strstr.str(attribute_value);
188 strstr >> double_imanip() >> value;
189 if (strstr.fail()) {
190 xml_parse_error("Error while parsing value of " + aname + " from <" + name +
191 ">");
192 }
193}
194
196
201void XMLTag::read_from_stream(istream& is) {
203
204 String token;
205 stringbuf tag;
206 istringstream sstr("");
207 XMLAttribute attr;
208 char ch = 0;
209
210 attribs.clear();
211
212 while (is.good() && isspace(is.peek())) {
213 is.get();
214 }
215
216 is >> ch;
217
218 if (ch != '<') {
219 is >> token;
220 token = ch + token;
221
222 xml_parse_error("'<' expected but " + token + " found.");
223 }
224
225 is.get(tag, '>');
226
227 // Hit EOF while looking for '>'
228 if (is.bad() || is.eof()) {
229 xml_parse_error("Unexpected end of file while looking for '>'");
230 }
231
232 if (is.get() != '>') {
233 xml_parse_error("Closing > not found in tag: " + tag.str());
234 }
235
236 sstr.str(tag.str() + '>');
237 out3 << "Read: " << sstr.str() << '\n';
238
239 sstr >> name;
240
241 if (name[name.length() - 1] == '>') {
242 // Because closin > was found, the tag for sure has no
243 // attributes, set token to ">" to skip reading of attributes
244 name.erase(name.length() - 1, 1);
245 token = ">";
246 } else {
247 // Tag may have attributes, so read next token
248 sstr >> token;
249 }
250 out3 << "Name: " << name << '\n';
251
252 //extract attributes
253 while (token != ">") {
255
256 pos = token.find("=", 0);
257 if (pos == String::npos) {
258 xml_parse_error("Syntax error in tag: " + tag.str());
259 }
260
261 attr.name = token.substr(0, pos);
262 token.erase(0, pos + 1);
263
264 if (token[0] != '\"') {
265 xml_parse_error("Missing \" in tag: " + tag.str());
266 }
267
268 while ((pos = token.find("\"", 1)) == (String::size_type)String::npos &&
269 token != ">") {
270 String ntoken;
271 sstr >> ntoken;
272 if (!ntoken.length()) break;
273 token += " " + ntoken;
274 }
275
276 if (pos == (String::size_type)String::npos) {
277 xml_parse_error("Missing \" in tag: " + sstr.str());
278 }
279
280 if (pos == 1)
281 attr.value = "";
282 else
283 attr.value = token.substr(1, pos - 1);
284
285 attribs.push_back(attr);
286
287 out3 << "Attr: " << attr.name << '\n';
288 out3 << "Value: " << attr.value << '\n';
289
290 if (token[token.length() - 1] == '>') {
291 token = ">";
292 } else {
293 sstr >> token;
294 }
295 }
296
297 out3 << '\n';
298
299 // Skip comments
300 if (name == "comment") {
301 is.get(tag, '<');
302
303 // Hit EOF while looking for '<'
304 if (is.bad() || is.eof()) {
306 "Unexpected end of file while looking for "
307 "comment tag");
308 }
309
311 check_name("/comment");
313 }
314}
315
317
322void XMLTag::write_to_stream(ostream& os) {
323 os << "<" << name;
324
325 auto it = attribs.begin();
326
327 while (it != attribs.end()) {
328 os << ' ' << it->name << "=\"" << it->value << '\"';
329 it++;
330 }
331
332 os << ">";
333}
334
335FileType string2filetype(const String& file_format) {
336 if (file_format == "ascii") return FILE_TYPE_ASCII;
337 if (file_format == "zascii") return FILE_TYPE_ZIPPED_ASCII;
338 if (file_format == "binary") return FILE_TYPE_BINARY;
339
340 throw std::runtime_error(
341 "file_format contains illegal string. "
342 "Valid values are:\n"
343 " ascii: XML output\n"
344 " zascii: Zipped XML output\n"
345 " binary: XML + binary output");
346}
347
349// Functions to open and read XML files
351
353
359void xml_open_output_file(ofstream& file, const String& name) {
360 // Tell the stream that it should throw exceptions.
361 // Badbit means that the entire stream is corrupted, failbit means
362 // that the last operation has failed, but the stream is still
363 // valid. We don't want either to happen!
364 // FIXME: This does not yet work in egcs-2.91.66, try again later.
365 file.exceptions(ios::badbit | ios::failbit);
366
367 // c_str explicitly converts to c String.
368 try {
369 file.open(name.c_str());
370 } catch (const std::exception&) {
371 ostringstream os;
372 os << "Cannot open output file: " << name << '\n'
373 << "Maybe you don't have write access "
374 << "to the directory or the file?";
375 throw runtime_error(os.str());
376 }
377
378 // See if the file is ok.
379 // FIXME: This should not be necessary anymore in the future, when
380 // g++ stream exceptions work properly. (In that case we would not
381 // get here if there really was a problem, because of the exception
382 // thrown by open().)
383 if (!file) {
384 ostringstream os;
385 os << "Cannot open output file: " << name << '\n'
386 << "Maybe you don't have write access "
387 << "to the directory or the file?";
388 throw runtime_error(os.str());
389 }
390}
391
392#ifdef ENABLE_ZLIB
393
395
401void xml_open_output_file(ogzstream& file, const String& name) {
402 // Tell the stream that it should throw exceptions.
403 // Badbit means that the entire stream is corrupted, failbit means
404 // that the last operation has failed, but the stream is still
405 // valid. We don't want either to happen!
406 // FIXME: This does not yet work in egcs-2.91.66, try again later.
407 file.exceptions(ios::badbit | ios::failbit);
408
409 // c_str explicitly converts to c String.
410 String nname = name;
411
412 if (nname.nelem() < 3 || nname.substr(nname.length() - 3, 3) != ".gz") {
413 nname += ".gz";
414 }
415
416 try {
417 file.open(nname.c_str());
418 } catch (const ios::failure&) {
419 ostringstream os;
420 os << "Cannot open output file: " << nname << '\n'
421 << "Maybe you don't have write access "
422 << "to the directory or the file?";
423 throw runtime_error(os.str());
424 }
425
426 // See if the file is ok.
427 // FIXME: This should not be necessary anymore in the future, when
428 // g++ stream exceptions work properly. (In that case we would not
429 // get here if there really was a problem, because of the exception
430 // thrown by open().)
431 if (!file) {
432 ostringstream os;
433 os << "Cannot open output file: " << nname << '\n'
434 << "Maybe you don't have write access "
435 << "to the directory or the file?";
436 throw runtime_error(os.str());
437 }
438}
439
440#endif /* ENABLE_ZLIB */
441
443
449void xml_open_input_file(ifstream& ifs,
450 const String& name,
451 const Verbosity& verbosity) {
453
454 // Tell the stream that it should throw exceptions.
455 // Badbit means that the entire stream is corrupted.
456 // On the other hand, end of file will not lead to an exception, you
457 // have to check this manually!
458 ifs.exceptions(ios::badbit);
459
460 // c_str explicitly converts to c String.
461 try {
462 ifs.open(name.c_str());
463 } catch (const ios::failure&) {
464 ostringstream os;
465 os << "Cannot open input file: " << name << '\n'
466 << "Maybe the file does not exist?";
467 throw runtime_error(os.str());
468 }
469
470 // See if the file is ok.
471 // FIXME: This should not be necessary anymore in the future, when
472 // g++ stream exceptions work properly.
473 if (!ifs) {
474 ostringstream os;
475 os << "Cannot open input file: " << name << '\n'
476 << "Maybe the file does not exist?";
477 throw runtime_error(os.str());
478 }
479
480 out3 << "- Reading input file " << name << "\n";
481}
482
483#ifdef ENABLE_ZLIB
484
486
493 const String& name,
494 const Verbosity& verbosity) {
496
497 // Tell the stream that it should throw exceptions.
498 // Badbit means that the entire stream is corrupted.
499 // On the other hand, end of file will not lead to an exception, you
500 // have to check this manually!
501 ifs.exceptions(ios::badbit);
502
503 // c_str explicitly converts to c String.
504 try {
505 ifs.open(name.c_str());
506 } catch (const ios::failure&) {
507 ostringstream os;
508 os << "Cannot open input file: " << name << '\n'
509 << "Maybe the file does not exist?";
510 throw runtime_error(os.str());
511 }
512
513 // See if the file is ok.
514 // FIXME: This should not be necessary anymore in the future, when
515 // g++ stream exceptions work properly.
516 if (!ifs) {
517 ostringstream os;
518 os << "Cannot open input file: " << name << '\n'
519 << "Maybe the file does not exist?";
520 throw runtime_error(os.str());
521 }
522
523 out3 << "- Reading input file " << name << "\n";
524}
525
526#endif /* ENABLE_ZLIB */
527
529// General XML functions (file header, start root tag, end root tag)
531
533
539void xml_parse_error(const String& str_error) {
540 ostringstream os;
541 os << "XML parse error: " << str_error << '\n'
542 << "Check syntax of XML file\n";
543 throw runtime_error(os.str());
544}
545
547
554void xml_data_parse_error(XMLTag& tag, const String& str_error) {
555 ostringstream os;
556 os << "XML data parse error: Error reading ";
557 tag.write_to_stream(os);
558 os << str_error << "\n"
559 << "Check syntax of XML file. A possible cause is that the file "
560 << "contains NaN or Inf values.\n";
561 throw runtime_error(os.str());
562}
563
565
575 FileType& ftype,
576 NumericType& ntype,
577 EndianType& etype,
578 const Verbosity& verbosity) {
579 char str[6];
580 stringbuf strbuf;
581 XMLTag tag(verbosity);
582 String strtype;
583
584 while (!is.fail() && isspace(is.peek())) is.get();
585
586 is.get(str, 6, ' ');
587
588 if (string(str) != "<?xml") {
590 "Input file is not a valid xml file "
591 "(<?xml not found)");
592 }
593
594 is.get(strbuf, '>');
595 is.get();
596
597 if (is.fail()) {
598 xml_parse_error("Input file is not a valid xml file");
599 }
600
601 tag.read_from_stream(is);
602 tag.check_name("arts");
603
604 // Check file format
605 tag.get_attribute_value("format", strtype);
606 if (strtype == "binary") {
607 ftype = FILE_TYPE_BINARY;
608 } else {
609 ftype = FILE_TYPE_ASCII;
610 }
611
612 // Check endian type
613 tag.get_attribute_value("endian_type", strtype);
614 if (strtype == "little") {
615 etype = ENDIAN_TYPE_LITTLE;
616 }
617 if (strtype == "big") {
618 etype = ENDIAN_TYPE_BIG;
619 }
620 if (strtype == "") {
621 /* out1 << " Warning: Endian type not specified in XML file, "
622 << "assuming little endian (PC)\n";*/
623 etype = ENDIAN_TYPE_LITTLE;
624 } else {
625 ostringstream os;
626 os << " Error: Unknown endian type \"" << strtype
627 << "\" specified in XML file.\n";
628 throw runtime_error(os.str());
629 }
630
631 // Check numeric type
632 tag.get_attribute_value("numeric_type", strtype);
633 if (strtype == "float") {
634 ntype = NUMERIC_TYPE_FLOAT;
635 } else if (strtype == "double") {
636 ntype = NUMERIC_TYPE_DOUBLE;
637 } else if (strtype == "") {
638 /* out1 << " Warning: Numeric type not specified in XML file, "
639 << "assuming double\n";*/
640 ntype = NUMERIC_TYPE_DOUBLE;
641 } else {
642 ostringstream os;
643 os << " Error: Unknown numeric type \"" << strtype
644 << "\" specified in XML file.\n";
645 throw runtime_error(os.str());
646 }
647}
648
650
655void xml_read_footer_from_stream(istream& is, const Verbosity& verbosity) {
656 XMLTag tag(verbosity);
657
658 tag.read_from_stream(is);
659 tag.check_name("/arts");
660}
661
663
668 FileType ftype,
669 const Verbosity& verbosity) {
670 XMLTag tag(verbosity);
671
672 os << "<?xml version=\"1.0\"?>" << '\n';
673
674 tag.set_name("arts");
675 switch (ftype) {
676 case FILE_TYPE_ASCII:
678 tag.add_attribute("format", "ascii");
679 break;
680 case FILE_TYPE_BINARY:
681 tag.add_attribute("format", "binary");
682 break;
683 }
684
685 tag.add_attribute("version", "1");
686
687 tag.write_to_stream(os);
688
689 os << '\n';
690}
691
693
696void xml_write_footer_to_stream(ostream& os, const Verbosity& verbosity) {
697 XMLTag tag(verbosity);
698
699 tag.set_name("/arts");
700 tag.write_to_stream(os);
701
702 os << endl;
703}
704
705void xml_set_stream_precision(ostream& os) {
706 // Determine the precision, depending on whether Numeric is double
707 // or float:
708 int precision;
709#ifdef USE_FLOAT
710 precision = FLT_DIG;
711#else
712#ifdef USE_DOUBLE
713 precision = DBL_DIG;
714#else
715#error Numeric must be double or float
716#endif
717#endif
718
719 os << setprecision(precision);
720}
721
723void parse_xml_tag_content_as_string(std::istream& is_xml, String& content) {
724 char dummy;
725
726 content = "";
727 dummy = (char)is_xml.peek();
728 while (is_xml && dummy != '<') {
729 is_xml.get(dummy);
730 content += dummy;
731 dummy = (char)is_xml.peek();
732 }
733
734 if (!is_xml) throw std::runtime_error("Unexpected end of file.");
735}
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:60
String name
Definition: xml_io_base.h:62
String value
Definition: xml_io_base.h:63
The ARTS XML tag class.
Definition: xml_io_base.h:70
String name
Definition: xml_io_base.h:120
bool has_attribute(const String &aname) const
Returns if the attribute exists or not.
Definition: xml_io_base.cc:125
void add_attribute(const String &aname, String value)
Definition: xml_io_base.cc:67
void write_to_stream(ostream &os)
Write XML tag.
Definition: xml_io_base.cc:322
void check_name(const String &expected_name)
Check tag name.
Definition: xml_io_base.cc:54
void read_from_stream(istream &is)
Reads next XML tag.
Definition: xml_io_base.cc:201
Array< XMLAttribute > attribs
Definition: xml_io_base.h:121
void get_attribute_value(const String &aname, String &value)
Definition: xml_io_base.cc:140
void set_name(const String &new_name)
Definition: xml_io_base.h:78
void check_attribute(const String &aname, const String &value)
Checks whether attribute has the expected value.
Definition: xml_io_base.cc:111
Input manipulator class for doubles to enable nan and inf parsing.
Definition: double_imanip.h:42
void open(const char *name, int gz_open_mode=std::ios::in)
Definition: gzstream.h:100
Index nelem() const
Definition: mystring.h:188
static const Index npos
Define npos:
Definition: mystring.h:208
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 precision
Definition: logic.cc:46
NUMERIC Numeric
The type to use for all floating point numbers.
Definition: matpack.h:33
INDEX Index
The type to use for all integer numbers and indices.
Definition: matpack.h:39
#define CREATE_OUT3
Definition: messages.h:206
constexpr int isspace(int ch) noexcept
Returns 1 if x is a standard space-character.
Definition: nonstd.h:39
#define v
void xml_write_footer_to_stream(ostream &os, const Verbosity &verbosity)
Write closing root tag.
Definition: xml_io_base.cc:696
void xml_data_parse_error(XMLTag &tag, const String &str_error)
Throws XML parser runtime error.
Definition: xml_io_base.cc:554
void xml_open_input_file(ifstream &ifs, const String &name, const Verbosity &verbosity)
Open file for XML input.
Definition: xml_io_base.cc:449
void xml_read_footer_from_stream(istream &is, const Verbosity &verbosity)
Reads closing root tag.
Definition: xml_io_base.cc:655
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:723
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:574
void xml_write_header_to_stream(ostream &os, FileType ftype, const Verbosity &verbosity)
Writes XML header and root tag.
Definition: xml_io_base.cc:667
void xml_set_stream_precision(ostream &os)
Definition: xml_io_base.cc:705
FileType string2filetype(const String &file_format)
Definition: xml_io_base.cc:335
void xml_open_output_file(ofstream &file, const String &name)
Open file for XML output.
Definition: xml_io_base.cc:359
void xml_parse_error(const String &str_error)
Throws XML parser runtime error.
Definition: xml_io_base.cc:539
FileType
Definition: xml_io_base.h:42
@ FILE_TYPE_ZIPPED_ASCII
Definition: xml_io_base.h:44
@ FILE_TYPE_BINARY
Definition: xml_io_base.h:45
@ FILE_TYPE_ASCII
Definition: xml_io_base.h:43
NumericType
Definition: xml_io_base.h:48
@ NUMERIC_TYPE_FLOAT
Definition: xml_io_base.h:48
@ NUMERIC_TYPE_DOUBLE
Definition: xml_io_base.h:48
EndianType
Definition: xml_io_base.h:49
@ ENDIAN_TYPE_BIG
Definition: xml_io_base.h:49
@ ENDIAN_TYPE_LITTLE
Definition: xml_io_base.h:49