ARTS 2.5.0 (git: 9ee3ac6c)
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
36
38// XMLTag implementation
40
42
48void XMLTag::check_name(const String& expected_name) {
49 if (name != expected_name)
50 xml_parse_error("Tag <" + expected_name + "> expected but <" + name +
51 "> found.");
52}
53
55
60void XMLTag::add_attribute(const String& aname, const String& value) {
61 XMLAttribute attr;
62
63 attr.name = aname;
64 attr.value = value;
65 attribs.push_back(attr);
66}
67
69
74void XMLTag::add_attribute(const String& aname, const Index& value) {
75 ostringstream v;
76
77 v << value;
78 add_attribute(aname, v.str());
79}
81void XMLTag::add_attribute(const String& aname, const Numeric& value) {
82 ostringstream v;
83
84 v << value;
85 add_attribute(aname, v.str());
86}
89
97void XMLTag::check_attribute(const String& aname, const String& value) {
98 String actual_value;
99
100 get_attribute_value(aname, actual_value);
101
102 if (actual_value == "*not found*") {
103 xml_parse_error("Required attribute " + aname + " does not exist");
104 } else if (actual_value != value) {
105 xml_parse_error("Attribute " + aname + " has value \"" + actual_value +
106 "\" but \"" + value + "\" was expected.");
107 }
108}
109
110
111bool XMLTag::has_attribute(const String& aname) const {
112 return std::any_of(attribs.cbegin(), attribs.cend(), [&](auto& attr){return attr.name == aname;});
113}
114
116
124void XMLTag::get_attribute_value(const String& aname, String& value) {
125 value = "";
126
128 while (it != attribs.end()) {
129 if (it->name == aname) {
130 value = it->value;
131 it = attribs.end();
132 } else {
133 it++;
134 }
135 }
136}
137
139
147void XMLTag::get_attribute_value(const String& aname, Index& value) {
148 String attribute_value;
149 istringstream strstr("");
150
151 get_attribute_value(aname, attribute_value);
152 strstr.str(attribute_value);
153 strstr >> value;
154 if (strstr.fail()) {
155 xml_parse_error("Error while parsing value of " + aname + " from <" + name +
156 ">");
157 }
158}
159
160void XMLTag::get_attribute_value(const String& aname, Numeric& value) {
161 String attribute_value;
162 istringstream strstr("");
163
164 get_attribute_value(aname, attribute_value);
165 strstr.str(attribute_value);
166 strstr >> value;
167 if (strstr.fail()) {
168 xml_parse_error("Error while parsing value of " + aname + " from <" + name +
169 ">");
170 }
171}
172
174
179void XMLTag::read_from_stream(istream& is) {
181
182 String token;
183 stringbuf tag;
184 istringstream sstr("");
185 XMLAttribute attr;
186 char ch = 0;
187
188 attribs.clear();
189
190 while (is.good() && isspace(is.peek())) {
191 is.get();
192 }
193
194 is >> ch;
195
196 if (ch != '<') {
197 is >> token;
198 token = ch + token;
199
200 xml_parse_error("'<' expected but " + token + " found.");
201 }
202
203 is.get(tag, '>');
204
205 // Hit EOF while looking for '>'
206 if (is.bad() || is.eof()) {
207 xml_parse_error("Unexpected end of file while looking for '>'");
208 }
209
210 if (is.get() != '>') {
211 xml_parse_error("Closing > not found in tag: " + tag.str());
212 }
213
214 sstr.str(tag.str() + '>');
215 out3 << "Read: " << sstr.str() << '\n';
216
217 sstr >> name;
218
219 if (name[name.length() - 1] == '>') {
220 // Because closin > was found, the tag for sure has no
221 // attributes, set token to ">" to skip reading of attributes
222 name.erase(name.length() - 1, 1);
223 token = ">";
224 } else {
225 // Tag may have attributes, so read next token
226 sstr >> token;
227 }
228 out3 << "Name: " << name << '\n';
229
230 //extract attributes
231 while (token != ">") {
233
234 pos = token.find("=", 0);
235 if (pos == String::npos) {
236 xml_parse_error("Syntax error in tag: " + tag.str());
237 }
238
239 attr.name = token.substr(0, pos);
240 token.erase(0, pos + 1);
241
242 if (token[0] != '\"') {
243 xml_parse_error("Missing \" in tag: " + tag.str());
244 }
245
246 while ((pos = token.find("\"", 1)) == (String::size_type)String::npos &&
247 token != ">") {
248 String ntoken;
249 sstr >> ntoken;
250 if (!ntoken.length()) break;
251 token += " " + ntoken;
252 }
253
254 if (pos == (String::size_type)String::npos) {
255 xml_parse_error("Missing \" in tag: " + sstr.str());
256 }
257
258 if (pos == 1)
259 attr.value = "";
260 else
261 attr.value = token.substr(1, pos - 1);
262
263 attribs.push_back(attr);
264
265 out3 << "Attr: " << attr.name << '\n';
266 out3 << "Value: " << attr.value << '\n';
267
268 if (token[token.length() - 1] == '>') {
269 token = ">";
270 } else {
271 sstr >> token;
272 }
273 }
274
275 out3 << '\n';
276
277 // Skip comments
278 if (name == "comment") {
279 is.get(tag, '<');
280
281 // Hit EOF while looking for '<'
282 if (is.bad() || is.eof()) {
284 "Unexpected end of file while looking for "
285 "comment tag");
286 }
287
289 check_name("/comment");
291 }
292}
293
295
300void XMLTag::write_to_stream(ostream& os) {
301 os << "<" << name;
302
304
305 while (it != attribs.end()) {
306 os << ' ' << it->name << "=\"" << it->value << '\"';
307 it++;
308 }
309
310 os << ">";
311}
312
313FileType string2filetype(const String& file_format) {
314 if (file_format == "ascii") return FILE_TYPE_ASCII;
315 if (file_format == "zascii") return FILE_TYPE_ZIPPED_ASCII;
316 if (file_format == "binary") return FILE_TYPE_BINARY;
317
318 throw std::runtime_error(
319 "file_format contains illegal string. "
320 "Valid values are:\n"
321 " ascii: XML output\n"
322 " zascii: Zipped XML output\n"
323 " binary: XML + binary output");
324}
325
327// Functions to open and read XML files
329
331
337void xml_open_output_file(ofstream& file, const String& name) {
338 // Tell the stream that it should throw exceptions.
339 // Badbit means that the entire stream is corrupted, failbit means
340 // that the last operation has failed, but the stream is still
341 // valid. We don't want either to happen!
342 // FIXME: This does not yet work in egcs-2.91.66, try again later.
343 file.exceptions(ios::badbit | ios::failbit);
344
345 // c_str explicitly converts to c String.
346 try {
347 file.open(name.c_str());
348 } catch (const std::exception&) {
349 ostringstream os;
350 os << "Cannot open output file: " << name << '\n'
351 << "Maybe you don't have write access "
352 << "to the directory or the file?";
353 throw runtime_error(os.str());
354 }
355
356 // See if the file is ok.
357 // FIXME: This should not be necessary anymore in the future, when
358 // g++ stream exceptions work properly. (In that case we would not
359 // get here if there really was a problem, because of the exception
360 // thrown by open().)
361 if (!file) {
362 ostringstream os;
363 os << "Cannot open output file: " << name << '\n'
364 << "Maybe you don't have write access "
365 << "to the directory or the file?";
366 throw runtime_error(os.str());
367 }
368}
369
370#ifdef ENABLE_ZLIB
371
373
379void xml_open_output_file(ogzstream& file, const String& name) {
380 // Tell the stream that it should throw exceptions.
381 // Badbit means that the entire stream is corrupted, failbit means
382 // that the last operation has failed, but the stream is still
383 // valid. We don't want either to happen!
384 // FIXME: This does not yet work in egcs-2.91.66, try again later.
385 file.exceptions(ios::badbit | ios::failbit);
386
387 // c_str explicitly converts to c String.
388 String nname = name;
389
390 if (nname.nelem() < 3 || nname.substr(nname.length() - 3, 3) != ".gz") {
391 nname += ".gz";
392 }
393
394 try {
395 file.open(nname.c_str());
396 } catch (const ios::failure&) {
397 ostringstream os;
398 os << "Cannot open output file: " << nname << '\n'
399 << "Maybe you don't have write access "
400 << "to the directory or the file?";
401 throw runtime_error(os.str());
402 }
403
404 // See if the file is ok.
405 // FIXME: This should not be necessary anymore in the future, when
406 // g++ stream exceptions work properly. (In that case we would not
407 // get here if there really was a problem, because of the exception
408 // thrown by open().)
409 if (!file) {
410 ostringstream os;
411 os << "Cannot open output file: " << nname << '\n'
412 << "Maybe you don't have write access "
413 << "to the directory or the file?";
414 throw runtime_error(os.str());
415 }
416}
417
418#endif /* ENABLE_ZLIB */
419
421
427void xml_open_input_file(ifstream& ifs,
428 const String& name,
429 const Verbosity& verbosity) {
431
432 // Tell the stream that it should throw exceptions.
433 // Badbit means that the entire stream is corrupted.
434 // On the other hand, end of file will not lead to an exception, you
435 // have to check this manually!
436 ifs.exceptions(ios::badbit);
437
438 // c_str explicitly converts to c String.
439 try {
440 ifs.open(name.c_str());
441 } catch (const ios::failure&) {
442 ostringstream os;
443 os << "Cannot open input file: " << name << '\n'
444 << "Maybe the file does not exist?";
445 throw runtime_error(os.str());
446 }
447
448 // See if the file is ok.
449 // FIXME: This should not be necessary anymore in the future, when
450 // g++ stream exceptions work properly.
451 if (!ifs) {
452 ostringstream os;
453 os << "Cannot open input file: " << name << '\n'
454 << "Maybe the file does not exist?";
455 throw runtime_error(os.str());
456 }
457
458 out3 << "- Reading input file " << name << "\n";
459}
460
461#ifdef ENABLE_ZLIB
462
464
471 const String& name,
472 const Verbosity& verbosity) {
474
475 // Tell the stream that it should throw exceptions.
476 // Badbit means that the entire stream is corrupted.
477 // On the other hand, end of file will not lead to an exception, you
478 // have to check this manually!
479 ifs.exceptions(ios::badbit);
480
481 // c_str explicitly converts to c String.
482 try {
483 ifs.open(name.c_str());
484 } catch (const ios::failure&) {
485 ostringstream os;
486 os << "Cannot open input file: " << name << '\n'
487 << "Maybe the file does not exist?";
488 throw runtime_error(os.str());
489 }
490
491 // See if the file is ok.
492 // FIXME: This should not be necessary anymore in the future, when
493 // g++ stream exceptions work properly.
494 if (!ifs) {
495 ostringstream os;
496 os << "Cannot open input file: " << name << '\n'
497 << "Maybe the file does not exist?";
498 throw runtime_error(os.str());
499 }
500
501 out3 << "- Reading input file " << name << "\n";
502}
503
504#endif /* ENABLE_ZLIB */
505
507// General XML functions (file header, start root tag, end root tag)
509
511
517void xml_parse_error(const String& str_error) {
518 ostringstream os;
519 os << "XML parse error: " << str_error << '\n'
520 << "Check syntax of XML file\n";
521 throw runtime_error(os.str());
522}
523
525
532void xml_data_parse_error(XMLTag& tag, String str_error) {
533 ostringstream os;
534 os << "XML data parse error: Error reading ";
535 tag.write_to_stream(os);
536 os << str_error << "\n"
537 << "Check syntax of XML file. A possible cause is that the file "
538 << "contains NaN or Inf values.\n";
539 throw runtime_error(os.str());
540}
541
543
553 FileType& ftype,
554 NumericType& ntype,
555 EndianType& etype,
556 const Verbosity& verbosity) {
557 char str[6];
558 stringbuf strbuf;
559 XMLTag tag(verbosity);
560 String strtype;
561
562 while (!is.fail() && isspace(is.peek())) is.get();
563
564 is.get(str, 6, ' ');
565
566 if (string(str) != "<?xml") {
568 "Input file is not a valid xml file "
569 "(<?xml not found)");
570 }
571
572 is.get(strbuf, '>');
573 is.get();
574
575 if (is.fail()) {
576 xml_parse_error("Input file is not a valid xml file");
577 }
578
579 tag.read_from_stream(is);
580 tag.check_name("arts");
581
582 // Check file format
583 tag.get_attribute_value("format", strtype);
584 if (strtype == "binary") {
585 ftype = FILE_TYPE_BINARY;
586 } else {
587 ftype = FILE_TYPE_ASCII;
588 }
589
590 // Check endian type
591 tag.get_attribute_value("endian_type", strtype);
592 if (strtype == "little") {
593 etype = ENDIAN_TYPE_LITTLE;
594 }
595 if (strtype == "big") {
596 etype = ENDIAN_TYPE_BIG;
597 }
598 if (strtype == "") {
599 /* out1 << " Warning: Endian type not specified in XML file, "
600 << "assuming little endian (PC)\n";*/
601 etype = ENDIAN_TYPE_LITTLE;
602 } else {
603 ostringstream os;
604 os << " Error: Unknown endian type \"" << strtype
605 << "\" specified in XML file.\n";
606 throw runtime_error(os.str());
607 }
608
609 // Check numeric type
610 tag.get_attribute_value("numeric_type", strtype);
611 if (strtype == "float") {
612 ntype = NUMERIC_TYPE_FLOAT;
613 } else if (strtype == "double") {
614 ntype = NUMERIC_TYPE_DOUBLE;
615 } else if (strtype == "") {
616 /* out1 << " Warning: Numeric type not specified in XML file, "
617 << "assuming double\n";*/
618 ntype = NUMERIC_TYPE_DOUBLE;
619 } else {
620 ostringstream os;
621 os << " Error: Unknown numeric type \"" << strtype
622 << "\" specified in XML file.\n";
623 throw runtime_error(os.str());
624 }
625}
626
628
633void xml_read_footer_from_stream(istream& is, const Verbosity& verbosity) {
634 XMLTag tag(verbosity);
635
636 tag.read_from_stream(is);
637 tag.check_name("/arts");
638}
639
641
646 FileType ftype,
647 const Verbosity& verbosity) {
648 XMLTag tag(verbosity);
649
650 os << "<?xml version=\"1.0\"?>" << '\n';
651
652 tag.set_name("arts");
653 switch (ftype) {
654 case FILE_TYPE_ASCII:
656 tag.add_attribute("format", "ascii");
657 break;
658 case FILE_TYPE_BINARY:
659 tag.add_attribute("format", "binary");
660 break;
661 }
662
663 tag.add_attribute("version", "1");
664
665 tag.write_to_stream(os);
666
667 os << '\n';
668}
669
671
674void xml_write_footer_to_stream(ostream& os, const Verbosity& verbosity) {
675 XMLTag tag(verbosity);
676
677 tag.set_name("/arts");
678 tag.write_to_stream(os);
679
680 os << endl;
681}
682
683void xml_set_stream_precision(ostream& os) {
684 // Determine the precision, depending on whether Numeric is double
685 // or float:
686 int precision;
687#ifdef USE_FLOAT
688 precision = FLT_DIG;
689#else
690#ifdef USE_DOUBLE
691 precision = DBL_DIG;
692#else
693#error Numeric must be double or float
694#endif
695#endif
696
697 os << setprecision(precision);
698}
699
701void parse_xml_tag_content_as_string(std::istream& is_xml, String& content) {
702 char dummy;
703
704 content = "";
705 dummy = (char)is_xml.peek();
706 while (is_xml && dummy != '<') {
707 is_xml.get(dummy);
708 content += dummy;
709 dummy = (char)is_xml.peek();
710 }
711
712 if (!is_xml) throw std::runtime_error("Unexpected end of file.");
713}
The global header file for ARTS.
type upp char * str
This file contains the class declaration of bifstream.
This file contains the class declaration of bofstream.
This can be used to make arrays out of anything.
Definition: array.h:107
XML attribute class.
Definition: xml_io_base.h:58
String name
Definition: xml_io_base.h:60
String value
Definition: xml_io_base.h:61
The ARTS XML tag class.
Definition: xml_io_base.h:68
String name
Definition: xml_io_base.h:118
void add_attribute(const String &aname, const String &value)
Adds a String attribute to tag.
Definition: xml_io_base.cc:60
bool has_attribute(const String &aname) const
Returns if the attribute exists or not.
Definition: xml_io_base.cc:111
void write_to_stream(ostream &os)
Write XML tag.
Definition: xml_io_base.cc:300
void check_name(const String &expected_name)
Check tag name.
Definition: xml_io_base.cc:48
void read_from_stream(istream &is)
Reads next XML tag.
Definition: xml_io_base.cc:179
Array< XMLAttribute > attribs
Definition: xml_io_base.h:119
void get_attribute_value(const String &aname, String &value)
Returns value of attribute as String.
Definition: xml_io_base.cc:124
void set_name(const String &new_name)
Definition: xml_io_base.h:76
void check_attribute(const String &aname, const String &value)
Checks whether attribute has the expected value.
Definition: xml_io_base.cc:97
void open(const char *name, int gz_open_mode=std::ios::in)
Definition: gzstream.h:100
Index nelem() const
Number of elements.
Definition: mystring.h:253
static const Index npos
Define npos:
Definition: mystring.h:107
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:43
INDEX Index
The type to use for all integer numbers and indices.
Definition: matpack.h:39
NUMERIC Numeric
The type to use for all floating point numbers.
Definition: matpack.h:33
#define CREATE_OUT3
Definition: messages.h:207
#define v
void xml_write_footer_to_stream(ostream &os, const Verbosity &verbosity)
Write closing root tag.
Definition: xml_io_base.cc:674
void xml_open_input_file(ifstream &ifs, const String &name, const Verbosity &verbosity)
Open file for XML input.
Definition: xml_io_base.cc:427
void xml_read_footer_from_stream(istream &is, const Verbosity &verbosity)
Reads closing root tag.
Definition: xml_io_base.cc:633
void xml_data_parse_error(XMLTag &tag, String str_error)
Throws XML parser runtime error.
Definition: xml_io_base.cc:532
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:701
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:552
void xml_write_header_to_stream(ostream &os, FileType ftype, const Verbosity &verbosity)
Writes XML header and root tag.
Definition: xml_io_base.cc:645
void xml_set_stream_precision(ostream &os)
Definition: xml_io_base.cc:683
FileType string2filetype(const String &file_format)
Definition: xml_io_base.cc:313
void xml_open_output_file(ofstream &file, const String &name)
Open file for XML output.
Definition: xml_io_base.cc:337
void xml_parse_error(const String &str_error)
Throws XML parser runtime error.
Definition: xml_io_base.cc:517
FileType
Definition: xml_io_base.h:40
@ FILE_TYPE_ZIPPED_ASCII
Definition: xml_io_base.h:42
@ FILE_TYPE_BINARY
Definition: xml_io_base.h:43
@ FILE_TYPE_ASCII
Definition: xml_io_base.h:41
NumericType
Definition: xml_io_base.h:46
@ NUMERIC_TYPE_FLOAT
Definition: xml_io_base.h:46
@ NUMERIC_TYPE_DOUBLE
Definition: xml_io_base.h:46
EndianType
Definition: xml_io_base.h:47
@ ENDIAN_TYPE_BIG
Definition: xml_io_base.h:47
@ ENDIAN_TYPE_LITTLE
Definition: xml_io_base.h:47