libdap Updated for version 3.21.1
libdap4 is an implementation of OPeNDAP's DAP protocol.
DDXParserSAX2.cc
Go to the documentation of this file.
1
2// -*- mode: c++; c-basic-offset:4 -*-
3
4// This file is part of libdap, A C++ implementation of the OPeNDAP Data
5// Access Protocol.
6
7// Copyright (c) 2003 OPeNDAP, Inc.
8// Author: James Gallagher <jgallagher@opendap.org>
9//
10// This library is free software; you can redistribute it and/or
11// modify it under the terms of the GNU Lesser General Public
12// License as published by the Free Software Foundation; either
13// version 2.1 of the License, or (at your option) any later version.
14//
15// This library is distributed in the hope that it will be useful,
16// but WITHOUT ANY WARRANTY; without even the implied warranty of
17// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18// Lesser General Public License for more details.
19//
20// You should have received a copy of the GNU Lesser General Public
21// License along with this library; if not, write to the Free Software
22// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23//
24// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
25
26#include "config.h"
27
28// #define DODS_DEBUG 1
29// #define DODS_DEBUG2 1
30
31#include <cstdarg>
32#include <cstring>
33#include <fstream>
34
35#include "Array.h"
36#include "BaseType.h"
37#include "Byte.h"
38#include "Float32.h"
39#include "Float64.h"
40#include "Grid.h"
41#include "Int16.h"
42#include "Int32.h"
43#include "Sequence.h"
44#include "Str.h"
45#include "Structure.h"
46#include "UInt16.h"
47#include "UInt32.h"
48#include "Url.h"
49
50#include "DDXParserSAX2.h"
51
52#include "debug.h"
53#include "mime_util.h"
54#include "util.h"
55
56namespace libdap {
57
58#if defined(DODS_DEBUG) || defined(DODS_DEUG2)
59static const char *states[] = {"start",
60
61 "dataset",
62
63 "attribute_container",
64 "attribute",
65 "attribute_value",
66 "other_xml_attribute",
67
68 "alias",
69
70 "simple_type",
71
72 "array",
73 "dimension",
74
75 "grid",
76 "map",
77
78 "structure",
79 "sequence",
80
81 "blob href",
82
83 "unknown",
84 "error"};
85#endif
86// Glue the BaseTypeFactory to the enum-based factory defined statically
87// here.
88
89BaseType *DDXParser::factory(Type t, const string &name) {
90 switch (t) {
91 case dods_byte_c:
92 return d_factory->NewByte(name);
93
94 case dods_int16_c:
95 return d_factory->NewInt16(name);
96
97 case dods_uint16_c:
98 return d_factory->NewUInt16(name);
99
100 case dods_int32_c:
101 return d_factory->NewInt32(name);
102
103 case dods_uint32_c:
104 return d_factory->NewUInt32(name);
105
106 case dods_float32_c:
107 return d_factory->NewFloat32(name);
108
109 case dods_float64_c:
110 return d_factory->NewFloat64(name);
111
112 case dods_str_c:
113 return d_factory->NewStr(name);
114
115 case dods_url_c:
116 return d_factory->NewUrl(name);
117
118 case dods_array_c:
119 return d_factory->NewArray(name);
120
121 case dods_structure_c:
122 return d_factory->NewStructure(name);
123
124 case dods_sequence_c:
125 return d_factory->NewSequence(name);
126
127 case dods_grid_c:
128 return d_factory->NewGrid(name);
129
130 default:
131 return 0;
132 }
133}
134
135static bool is_not(const char *name, const char *tag) { return strcmp(name, tag) != 0; }
136
137void DDXParser::set_state(DDXParser::ParseState state) { s.push(state); }
138
139DDXParser::ParseState DDXParser::get_state() const { return s.top(); }
140
141void DDXParser::pop_state() { s.pop(); }
142
146void DDXParser::transfer_xml_attrs(const xmlChar **attributes, int nb_attributes) {
147 if (!attribute_table.empty())
148 attribute_table.clear(); // erase old attributes
149
150 unsigned int index = 0;
151 for (int i = 0; i < nb_attributes; ++i, index += 5) {
152 // Make a value using the attribute name and the prefix, namespace URI
153 // and the value. The prefix might be null.
154 attribute_table.insert(map<string, XMLAttribute>::value_type(string((const char *)attributes[index]),
155 XMLAttribute(attributes + index + 1)));
156
157 DBG(cerr << "Attribute '" << (const char *)attributes[index]
158 << "': " << attribute_table[(const char *)attributes[index]].value << endl);
159 }
160}
161
162void DDXParser::transfer_xml_ns(const xmlChar **namespaces, int nb_namespaces) {
163 for (int i = 0; i < nb_namespaces; ++i) {
164 // make a value with the prefix and namespace URI. The prefix might be
165 // null.
166 namespace_table.insert(map<string, string>::value_type(
167 namespaces[i * 2] != 0 ? (const char *)namespaces[i * 2] : "", (const char *)namespaces[i * 2 + 1]));
168 }
169}
170
175bool DDXParser::check_required_attribute(const string &attr) {
176 map<string, XMLAttribute>::iterator i = attribute_table.find(attr);
177 if (i == attribute_table.end())
178 ddx_fatal_error(this, "Required attribute '%s' not found.", attr.c_str());
179 return true;
180}
181
187bool DDXParser::check_attribute(const string &attr) { return (attribute_table.find(attr) != attribute_table.end()); }
188
197void DDXParser::process_attribute_element(const xmlChar **attrs, int nb_attributes) {
198 // These methods set the state to parser_error if a problem is found.
199 transfer_xml_attrs(attrs, nb_attributes);
200
201 bool error = !(check_required_attribute(string("name")) && check_required_attribute(string("type")));
202 if (error)
203 return;
204
205 if (attribute_table["type"].value == "Container") {
206 set_state(inside_attribute_container);
207
208 AttrTable *child;
209 AttrTable *parent = at_stack.top();
210
211 child = parent->append_container(attribute_table["name"].value);
212 at_stack.push(child); // save.
213 DBG2(cerr << "Pushing at" << endl);
214 } else if (attribute_table["type"].value == "OtherXML") {
215 set_state(inside_other_xml_attribute);
216
217 dods_attr_name = attribute_table["name"].value;
218 dods_attr_type = attribute_table["type"].value;
219 } else {
220 set_state(inside_attribute);
221 // *** Modify parser. Add a special state for inside OtherXML since it
222 // does not use the <value> element.
223
224 dods_attr_name = attribute_table["name"].value;
225 dods_attr_type = attribute_table["type"].value;
226 }
227}
228
232void DDXParser::process_attribute_alias(const xmlChar **attrs, int nb_attributes) {
233 transfer_xml_attrs(attrs, nb_attributes);
234 if (check_required_attribute(string("name")) && check_required_attribute(string("attribute"))) {
235 set_state(inside_alias);
236 at_stack.top()->attr_alias(attribute_table["name"].value, attribute_table["attribute"].value);
237 }
238}
239
247void DDXParser::process_variable(Type t, ParseState s, const xmlChar **attrs, int nb_attributes) {
248 transfer_xml_attrs(attrs, nb_attributes);
249
250 set_state(s);
251
252 if (bt_stack.top()->type() == dods_array_c || check_required_attribute("name")) { // throws on error/false
253 BaseType *btp = factory(t, attribute_table["name"].value);
254 if (!btp) {
255 ddx_fatal_error(this, "Internal parser error; could not instantiate the variable '%s'.",
256 attribute_table["name"].value.c_str());
257 } else {
258 // Only run this code if btp is not null! jhrg 9/14/15
259 // Once we make the new variable, we not only load it on to the
260 // BaseType stack, we also load its AttrTable on the AttrTable stack.
261 // The attribute processing software always operates on the AttrTable
262 // at the top of the AttrTable stack (at_stack).
263 bt_stack.push(btp);
264 at_stack.push(&btp->get_attr_table());
265 }
266 }
267}
268
272void DDXParser::process_dimension(const xmlChar **attrs, int nb_attributes) {
273 transfer_xml_attrs(attrs, nb_attributes);
274 if (check_required_attribute(string("size"))) {
275 set_state(inside_dimension);
276 Array *ap = dynamic_cast<Array *>(bt_stack.top());
277 if (!ap) {
278 ddx_fatal_error(this, "Parse error: Expected an array variable.");
279 return;
280 }
281
282 ap->append_dim(atoi(attribute_table["size"].value.c_str()), attribute_table["name"].value);
283 }
284}
285
288void DDXParser::process_blob(const xmlChar **attrs, int nb_attributes) {
289 transfer_xml_attrs(attrs, nb_attributes);
290 if (check_required_attribute(string("href"))) {
291 set_state(inside_blob_href);
292 *blob_href = attribute_table["href"].value;
293 }
294}
295
302inline bool DDXParser::is_attribute_or_alias(const char *name, const xmlChar **attrs, int nb_attributes) {
303 if (strcmp(name, "Attribute") == 0) {
304 process_attribute_element(attrs, nb_attributes);
305 // next state: inside_attribtue or inside_attribute_container
306 return true;
307 } else if (strcmp(name, "Alias") == 0) {
308 process_attribute_alias(attrs, nb_attributes);
309 // next state: inside_alias
310 return true;
311 }
312
313 return false;
314}
315
321inline bool DDXParser::is_variable(const char *name, const xmlChar **attrs, int nb_attributes) {
322 Type t = get_type(name);
323 // if ((t = is_simple_type(name)) != dods_null_c) {
324 if (is_simple_type(t)) {
325 process_variable(t, inside_simple_type, attrs, nb_attributes);
326 return true;
327 } else if (strcmp(name, "Array") == 0) {
328 process_variable(dods_array_c, inside_array, attrs, nb_attributes);
329 return true;
330 } else if (strcmp(name, "Structure") == 0) {
331 process_variable(dods_structure_c, inside_structure, attrs, nb_attributes);
332 return true;
333 } else if (strcmp(name, "Sequence") == 0) {
334 process_variable(dods_sequence_c, inside_sequence, attrs, nb_attributes);
335 return true;
336 } else if (strcmp(name, "Grid") == 0) {
337 process_variable(dods_grid_c, inside_grid, attrs, nb_attributes);
338 return true;
339 }
340
341 return false;
342}
343
344void DDXParser::finish_variable(const char *tag, Type t, const char *expected) {
345 if (strcmp(tag, expected) != 0) {
346 DDXParser::ddx_fatal_error(this, "Expected an end tag for a %s; found '%s' instead.", expected, tag);
347 return;
348 }
349
350 pop_state();
351
352 BaseType *btp = bt_stack.top();
353
354 bt_stack.pop();
355 at_stack.pop();
356
357 if (btp->type() != t) {
358 DDXParser::ddx_fatal_error(this, "Internal error: Expected a %s variable.", expected);
359 delete btp;
360 return;
361 }
362 // Once libxml2 validates, this can go away. 05/30/03 jhrg
363 if (t == dods_array_c && static_cast<Array *>(btp)->dimensions() == 0) {
364 DDXParser::ddx_fatal_error(this, "No dimension element included in the Array '%s'.", btp->name().c_str());
365 delete btp;
366 return;
367 }
368
369 BaseType *parent = bt_stack.top();
370
371 if (!(parent->is_vector_type() || parent->is_constructor_type())) {
372 DDXParser::ddx_fatal_error(this, "Tried to add the array variable '%s' to a non-constructor type (%s %s).", tag,
373 bt_stack.top()->type_name().c_str(), bt_stack.top()->name().c_str());
374 delete btp;
375 return;
376 }
377
378 parent->add_var_nocopy(btp);
379}
380
387
393 DDXParser *parser = static_cast<DDXParser *>(p);
394 parser->error_msg = "";
395 parser->char_data = "";
396
397 // init attr table stack.
398 parser->at_stack.push(&parser->dds->get_attr_table());
399
400 // Trick; DDS *should* be a child of Structure. To simplify parsing,
401 // stuff a Structure on the bt_stack and dump the top level variables
402 // there. Once we're done, transfer the variables to the DDS.
403 parser->bt_stack.push(new Structure("dummy_dds"));
404
405 parser->set_state(parser_start);
406
407 DBG2(cerr << "Parser state: " << states[parser->get_state()] << endl);
408}
409
413 DDXParser *parser = static_cast<DDXParser *>(p);
414 DBG2(cerr << "Ending state == " << states[parser->get_state()] << endl);
415
416 if (parser->get_state() != parser_start)
417 DDXParser::ddx_fatal_error(parser, "The document contained unbalanced tags.");
418
419 // If we've found any sort of error, don't make the DDX; intern() will
420 // take care of the error.
421 if (parser->get_state() == parser_error) {
422 return;
423 }
424
425 // Pop the temporary Structure off the stack and transfer its variables
426 // to the DDS.
427 Constructor *cp = dynamic_cast<Constructor *>(parser->bt_stack.top());
428 if (!cp) {
429 delete parser->bt_stack.top();
430 parser->bt_stack.pop();
431 ddx_fatal_error(parser, "Parse error: Expected a Structure, Sequence or Grid variable.");
432 return;
433 }
434
435 for (Constructor::Vars_iter i = cp->var_begin(); i != cp->var_end(); ++i) {
436 (*i)->set_parent(0); // top-level vars have no parents
437 parser->dds->add_var(*i);
438 }
439
440 delete parser->bt_stack.top();
441 parser->bt_stack.pop();
442}
443
444void DDXParser::ddx_sax2_start_element(void *p, const xmlChar *l, const xmlChar *prefix, const xmlChar *URI,
445 int nb_namespaces, const xmlChar **namespaces, int nb_attributes,
446 int /*nb_defaulted*/, const xmlChar **attributes) {
447 DDXParser *parser = static_cast<DDXParser *>(p);
448 const char *localname = (const char *)l;
449
450 DBG2(cerr << "start element: " << localname << ", states: " << states[parser->get_state()]);
451
452 switch (parser->get_state()) {
453 case parser_start:
454 if (strcmp(localname, "Dataset") == 0) {
455 parser->set_state(inside_dataset);
456 parser->root_ns = URI != 0 ? (const char *)URI : "";
457 parser->transfer_xml_attrs(attributes, nb_attributes);
458
459 if (parser->check_required_attribute(string("name")))
460 parser->dds->set_dataset_name(parser->attribute_table["name"].value);
461
462 if (parser->check_attribute("dapVersion"))
463 parser->dds->set_dap_version(parser->attribute_table["dapVersion"].value);
464 } else
465 DDXParser::ddx_fatal_error(parser, "Expected response to start with a Dataset element; found '%s' instead.",
466 localname);
467 break;
468
469 case inside_dataset:
470 if (parser->is_attribute_or_alias(localname, attributes, nb_attributes))
471 break;
472 else if (parser->is_variable(localname, attributes, nb_attributes))
473 break;
474 else if (strcmp(localname, "blob") == 0 || strcmp(localname, "dataBLOB") == 0) {
475 parser->process_blob(attributes, nb_attributes);
476 // next state: inside_data_blob
477 } else
478 DDXParser::ddx_fatal_error(parser, "Expected an Attribute, Alias or variable element; found '%s' instead.",
479 localname);
480 break;
481
482 case inside_attribute_container:
483 if (parser->is_attribute_or_alias(localname, attributes, nb_attributes))
484 break;
485 else
486 DDXParser::ddx_fatal_error(parser, "Expected an Attribute or Alias element; found '%s' instead.",
487 localname);
488 break;
489
490 case inside_attribute:
491 if (parser->is_attribute_or_alias(localname, attributes, nb_attributes))
492 break;
493 else if (strcmp(localname, "value") == 0)
494 parser->set_state(inside_attribute_value);
495 else
496 ddx_fatal_error(parser, "Expected an 'Attribute', 'Alias' or 'value' element; found '%s' instead.",
497 localname);
498 break;
499
500 case inside_attribute_value:
501 ddx_fatal_error(parser, "Internal parser error; unexpected state, inside value while processing element '%s'.",
502 localname);
503 break;
504
505 case inside_other_xml_attribute:
506 DBGN(cerr << endl << "\t inside_other_xml_attribute: " << localname << endl);
507
508 parser->other_xml_depth++;
509
510 // Accumulate the elements here
511
512 parser->other_xml.append("<");
513 if (prefix) {
514 parser->other_xml.append((const char *)prefix);
515 parser->other_xml.append(":");
516 }
517 parser->other_xml.append(localname);
518
519 if (nb_namespaces != 0) {
520 parser->transfer_xml_ns(namespaces, nb_namespaces);
521
522 for (map<string, string>::iterator i = parser->namespace_table.begin(); i != parser->namespace_table.end();
523 ++i) {
524 parser->other_xml.append(" xmlns");
525 if (!i->first.empty()) {
526 parser->other_xml.append(":");
527 parser->other_xml.append(i->first);
528 }
529 parser->other_xml.append("=\"");
530 parser->other_xml.append(i->second);
531 parser->other_xml.append("\"");
532 }
533 }
534
535 if (nb_attributes != 0) {
536 parser->transfer_xml_attrs(attributes, nb_attributes);
537 for (XMLAttrMap::iterator i = parser->attr_table_begin(); i != parser->attr_table_end(); ++i) {
538 parser->other_xml.append(" ");
539 if (!i->second.prefix.empty()) {
540 parser->other_xml.append(i->second.prefix);
541 parser->other_xml.append(":");
542 }
543 parser->other_xml.append(i->first);
544 parser->other_xml.append("=\"");
545 parser->other_xml.append(i->second.value);
546 parser->other_xml.append("\"");
547 }
548 }
549
550 parser->other_xml.append(">");
551 break;
552
553 case inside_alias:
554 ddx_fatal_error(parser, "Internal parser error; unexpected state, inside alias while processing element '%s'.",
555 localname);
556 break;
557
558 case inside_simple_type:
559 if (parser->is_attribute_or_alias(localname, attributes, nb_attributes))
560 break;
561 else
562 ddx_fatal_error(parser, "Expected an 'Attribute' or 'Alias' element; found '%s' instead.", localname);
563 break;
564
565 case inside_array:
566 if (parser->is_attribute_or_alias(localname, attributes, nb_attributes))
567 break;
568 else if (is_not(localname, "Array") && parser->is_variable(localname, attributes, nb_attributes))
569 break;
570 else if (strcmp(localname, "dimension") == 0) {
571 parser->process_dimension(attributes, nb_attributes);
572 // next state: inside_dimension
573 } else
574 ddx_fatal_error(parser, "Expected an 'Attribute' or 'Alias' element; found '%s' instead.", localname);
575 break;
576
577 case inside_dimension:
578 ddx_fatal_error(parser,
579 "Internal parser error; unexpected state, inside dimension while processing element '%s'.",
580 localname);
581 break;
582
583 case inside_structure:
584 if (parser->is_attribute_or_alias(localname, attributes, nb_attributes))
585 break;
586 else if (parser->is_variable(localname, attributes, nb_attributes))
587 break;
588 else
589 DDXParser::ddx_fatal_error(parser, "Expected an Attribute, Alias or variable element; found '%s' instead.",
590 localname);
591 break;
592
593 case inside_sequence:
594 if (parser->is_attribute_or_alias(localname, attributes, nb_attributes))
595 break;
596 else if (parser->is_variable(localname, attributes, nb_attributes))
597 break;
598 else
599 DDXParser::ddx_fatal_error(parser, "Expected an Attribute, Alias or variable element; found '%s' instead.",
600 localname);
601 break;
602
603 case inside_grid:
604 if (parser->is_attribute_or_alias(localname, attributes, nb_attributes))
605 break;
606 else if (strcmp(localname, "Array") == 0)
607 parser->process_variable(dods_array_c, inside_array, attributes, nb_attributes);
608 else if (strcmp(localname, "Map") == 0)
609 parser->process_variable(dods_array_c, inside_map, attributes, nb_attributes);
610 else
611 DDXParser::ddx_fatal_error(parser, "Expected an Attribute, Alias or variable element; found '%s' instead.",
612 localname);
613 break;
614
615 case inside_map:
616 if (parser->is_attribute_or_alias(localname, attributes, nb_attributes))
617 break;
618 else if (is_not(localname, "Array") && is_not(localname, "Sequence") && is_not(localname, "Grid") &&
619 parser->is_variable(localname, attributes, nb_attributes))
620 break;
621 else if (strcmp(localname, "dimension") == 0) {
622 parser->process_dimension(attributes, nb_attributes);
623 // next state: inside_dimension
624 } else
625 ddx_fatal_error(parser,
626 "Expected an 'Attribute', 'Alias', variable or 'dimension' element; found '%s' instead.",
627 localname);
628 break;
629
630 case inside_blob_href:
631 ddx_fatal_error(parser,
632 "Internal parser error; unexpected state, inside blob href while processing element '%s'.",
633 localname);
634 break;
635
636 case parser_unknown:
637 // *** Never used? If so remove/error
638 parser->set_state(parser_unknown);
639 break;
640
641 case parser_error:
642 break;
643 }
644
645 DBGN(cerr << " ... " << states[parser->get_state()] << endl);
646}
647
648void DDXParser::ddx_sax2_end_element(void *p, const xmlChar *l, const xmlChar *prefix, const xmlChar *URI) {
649 DDXParser *parser = static_cast<DDXParser *>(p);
650 const char *localname = (const char *)l;
651
652 DBG2(cerr << "End element " << localname << " (state " << states[parser->get_state()] << ")" << endl);
653
654 switch (parser->get_state()) {
655 case parser_start:
656 ddx_fatal_error(parser,
657 "Internal parser error; unexpected state, inside start state while processing element '%s'.",
658 localname);
659 break;
660
661 case inside_dataset:
662 if (strcmp(localname, "Dataset") == 0)
663 parser->pop_state();
664 else
665 DDXParser::ddx_fatal_error(parser, "Expected an end Dataset tag; found '%s' instead.", localname);
666 break;
667
668 case inside_attribute_container:
669 if (strcmp(localname, "Attribute") == 0) {
670 parser->pop_state();
671 parser->at_stack.pop(); // pop when leaving a container.
672 } else
673 DDXParser::ddx_fatal_error(parser, "Expected an end Attribute tag; found '%s' instead.", localname);
674 break;
675
676 case inside_attribute:
677 if (strcmp(localname, "Attribute") == 0)
678 parser->pop_state();
679 else
680 DDXParser::ddx_fatal_error(parser, "Expected an end Attribute tag; found '%s' instead.", localname);
681 break;
682
683 case inside_attribute_value:
684 if (strcmp(localname, "value") == 0) {
685 parser->pop_state();
686 AttrTable *atp = parser->at_stack.top();
687 atp->append_attr(parser->dods_attr_name, parser->dods_attr_type, parser->char_data);
688 parser->char_data = ""; // Null this after use.
689 } else
690 DDXParser::ddx_fatal_error(parser, "Expected an end value tag; found '%s' instead.", localname);
691
692 break;
693
694 case inside_other_xml_attribute: {
695 if (strcmp(localname, "Attribute") == 0 && parser->root_ns == (const char *)URI) {
696
697 DBGN(cerr << endl << "\t Popping the 'inside_other_xml_attribute' state" << endl);
698
699 parser->pop_state();
700
701 AttrTable *atp = parser->at_stack.top();
702 atp->append_attr(parser->dods_attr_name, parser->dods_attr_type, parser->other_xml);
703
704 parser->other_xml = ""; // Null this after use.
705 } else {
706 DBGN(cerr << endl
707 << "\t inside_other_xml_attribute: " << localname << ", depth: " << parser->other_xml_depth
708 << endl);
709 if (parser->other_xml_depth == 0)
710 DDXParser::ddx_fatal_error(parser, "Expected an OtherXML attribute to end! Instead I found '%s'",
711 localname);
712 parser->other_xml_depth--;
713
714 parser->other_xml.append("</");
715 if (prefix) {
716 parser->other_xml.append((const char *)prefix);
717 parser->other_xml.append(":");
718 }
719 parser->other_xml.append(localname);
720 parser->other_xml.append(">");
721 }
722 break;
723 }
724 // Alias is busted in libdap++ 05/29/03 jhrg
725 case inside_alias:
726 parser->pop_state();
727 break;
728
729 case inside_simple_type: {
730 Type t = get_type(localname);
731 if (is_simple_type(t)) {
732 parser->pop_state();
733 BaseType *btp = parser->bt_stack.top();
734 parser->bt_stack.pop();
735 parser->at_stack.pop();
736
737 BaseType *parent = parser->bt_stack.top();
738
739 if (parent->is_vector_type() || parent->is_constructor_type()) {
740 parent->add_var(btp);
741 delete btp;
742 } else {
744 parser, "Tried to add the simple-type variable '%s' to a non-constructor type (%s %s).", localname,
745 parser->bt_stack.top()->type_name().c_str(), parser->bt_stack.top()->name().c_str());
746 delete btp;
747 }
748 } else {
749 DDXParser::ddx_fatal_error(parser, "Expected an end tag for a simple type; found '%s' instead.", localname);
750 }
751 break;
752 }
753
754 case inside_array:
755 parser->finish_variable(localname, dods_array_c, "Array");
756 break;
757
758 case inside_dimension:
759 if (strcmp(localname, "dimension") == 0)
760 parser->pop_state();
761 else
762 DDXParser::ddx_fatal_error(parser, "Expected an end dimension tag; found '%s' instead.", localname);
763 break;
764
765 case inside_structure:
766 parser->finish_variable(localname, dods_structure_c, "Structure");
767 break;
768
769 case inside_sequence:
770 parser->finish_variable(localname, dods_sequence_c, "Sequence");
771 break;
772
773 case inside_grid:
774 parser->finish_variable(localname, dods_grid_c, "Grid");
775 break;
776
777 case inside_map:
778 parser->finish_variable(localname, dods_array_c, "Map");
779 break;
780
781 case inside_blob_href:
782 if (strcmp(localname, "blob") == 0 || strcmp(localname, "dataBLOB") == 0)
783 parser->pop_state();
784 else
785 DDXParser::ddx_fatal_error(parser, "Expected an end dataBLOB/blob tag; found '%s' instead.", localname);
786 break;
787
788 case parser_unknown:
789 parser->pop_state();
790 break;
791
792 case parser_error:
793 break;
794 }
795
796 DBGN(cerr << " ... " << states[parser->get_state()] << endl);
797}
798
802void DDXParser::ddx_get_characters(void *p, const xmlChar *ch, int len) {
803 DDXParser *parser = static_cast<DDXParser *>(p);
804
805 switch (parser->get_state()) {
806 case inside_attribute_value:
807 parser->char_data.append((const char *)(ch), len);
808 DBG2(cerr << "Characters: '" << parser->char_data << "'" << endl);
809 break;
810
811 case inside_other_xml_attribute:
812 parser->other_xml.append((const char *)(ch), len);
813 DBG2(cerr << "Other XML Characters: '" << parser->other_xml << "'" << endl);
814 break;
815
816 default:
817 break;
818 }
819}
820
825void DDXParser::ddx_ignoreable_whitespace(void *p, const xmlChar *ch, int len) {
826 DDXParser *parser = static_cast<DDXParser *>(p);
827
828 switch (parser->get_state()) {
829 case inside_other_xml_attribute:
830 parser->other_xml.append((const char *)(ch), len);
831 break;
832
833 default:
834 break;
835 }
836}
837
843void DDXParser::ddx_get_cdata(void *p, const xmlChar *value, int len) {
844 DDXParser *parser = static_cast<DDXParser *>(p);
845
846 switch (parser->get_state()) {
847 case inside_other_xml_attribute:
848 parser->other_xml.append((const char *)(value), len);
849 break;
850
851 case parser_unknown:
852 break;
853
854 default:
855 DDXParser::ddx_fatal_error(parser, "Found a CData block but none are allowed by DAP.");
856
857 break;
858 }
859}
860
865xmlEntityPtr DDXParser::ddx_get_entity(void *, const xmlChar *name) { return xmlGetPredefinedEntity(name); }
866
874void DDXParser::ddx_fatal_error(void *p, const char *msg, ...) {
875 va_list args;
876 DDXParser *parser = static_cast<DDXParser *>(p);
877
878 parser->set_state(parser_error);
879
880 va_start(args, msg);
881 char str[1024];
882 vsnprintf(str, 1024, msg, args);
883 va_end(args);
884
885 int line = xmlSAX2GetLineNumber(parser->ctxt);
886
887 parser->error_msg += "At line " + long_to_string(line) + ": ";
888 parser->error_msg += string(str) + string("\n");
889}
890
892
893void DDXParser::cleanup_parse() {
894 bool wellFormed = ctxt->wellFormed;
895 bool valid = ctxt->valid;
896
897 xmlFreeParserCtxt(ctxt);
898
899 // If there's an error, there may still be items on the stack at the
900 // end of the parse.
901 while (!bt_stack.empty()) {
902 delete bt_stack.top();
903 bt_stack.pop();
904 }
905
906 if (!wellFormed) {
907 throw DDXParseFailed(string("The DDX is not a well formed XML document.\n") + error_msg);
908 }
909
910 if (!valid) {
911 throw DDXParseFailed(string("The DDX is not a valid document.\n") + error_msg);
912 }
913
914 if (get_state() == parser_error) {
915 throw DDXParseFailed(string("Error parsing DDX response.\n") + error_msg);
916 }
917}
918
926void DDXParser::intern_stream(istream &in, DDS *dest_dds, string &cid, const string &boundary) {
927 // Code example from libxml2 docs re: read from a stream.
928 if (!in || in.eof())
929 throw InternalErr(__FILE__, __LINE__, "Input stream not open or read error");
930
931 const int size = 1024;
932 char chars[size + 1];
933
934 // int res = fread(chars, 1, 4, in);
935 in.read(chars, 4);
936 int res = in.gcount();
937 if (res > 0) {
938 chars[4] = '\0';
939 ctxt = xmlCreatePushParserCtxt(&ddx_sax_parser, this, chars, res, "stream");
940
941 if (!ctxt)
942 throw DDXParseFailed("Error parsing DDX response: Input does not look like XML");
943
944 dds = dest_dds; // dump values here
945 blob_href = &cid; // cid goes here
946
947 ctxt->validate = true;
948
949 in.getline(chars, size); // chars has size+1 elements
950 res = in.gcount();
951 chars[res - 1] = '\n'; // libxml needs the newline; w/o it the parse will fail
952 chars[res] = '\0';
953 while (res > 0 && !is_boundary(chars, boundary)) {
954 DBG(cerr << "line (" << res << "): " << chars << endl);
955 xmlParseChunk(ctxt, chars, res, 0);
956
957 in.getline(chars, size); // chars has size+1 elements
958 res = in.gcount();
959 if (res > 0) {
960 chars[res - 1] = '\n';
961 chars[res] = '\0';
962 }
963 }
964
965 // This call ends the parse: The fourth argument of xmlParseChunk is
966 // the bool 'terminate.'
967 xmlParseChunk(ctxt, chars, 0, 1);
968
969 cleanup_parse();
970 } else {
971 throw DDXParseFailed("Error parsing DDX response: Could not read from input stream.");
972 }
973}
974
977void DDXParser::intern_stream(FILE *in, DDS *dest_dds, string &cid, const string &boundary) {
978 // Code example from libxml2 docs re: read from a stream.
979 if (!in || feof(in) || ferror(in))
980 throw InternalErr(__FILE__, __LINE__, "Input stream not open or read error");
981
982 const int size = 1024;
983 char chars[size];
984
985 int res = fread(chars, 1, 4, in);
986 if (res > 0) {
987 chars[4] = '\0';
988 ctxt = xmlCreatePushParserCtxt(&ddx_sax_parser, this, chars, res, "stream");
989
990 if (!ctxt)
991 throw DDXParseFailed("Error parsing DDX response: Input does not look like XML");
992
993 dds = dest_dds; // dump values here
994 blob_href = &cid; // cid goes here
995
996 ctxt->validate = true;
997
998 while ((fgets(chars, size, in) != 0) && !is_boundary(chars, boundary)) {
999 DBG(cerr << "line (" << strlen(chars) << "): " << chars << endl);
1000 xmlParseChunk(ctxt, chars, strlen(chars), 0);
1001 }
1002 // This call ends the parse: The fourth argument of xmlParseChunk is
1003 // the bool 'terminate.'
1004 xmlParseChunk(ctxt, chars, 0, 1);
1005
1006 cleanup_parse();
1007 } else {
1008 throw DDXParseFailed("Error parsing DDX response: Could not read from input file.");
1009 }
1010}
1011
1023void DDXParser::intern(const string &document, DDS *dest_dds, string &cid) {
1024
1025 ifstream ifs;
1026 ifs.open(document);
1027 if (ifs.good()) {
1028 intern_stream(ifs, dest_dds, cid, "");
1029 }
1030 ifs.close();
1031}
1032
1033#if 0
1034void DDXParser::intern(const string & document, DDS * dest_dds, string &cid)
1035{
1036 // Create the ctxt pointer explicitly so that we can store a pointer
1037 // to it in the DDXParser instance. This provides a way to generate our
1038 // own error messages *with* line numbers. The messages are pretty
1039 // meaningless otherwise. This means that we use an interface from the
1040 // 'parser internals' header, and not the 'parser' header. However, this
1041 // interface is also used in one of the documented examples, so it's
1042 // probably pretty stable. 06/02/03 jhrg
1043 ctxt = xmlCreateFileParserCtxt(document.c_str());
1044 if (!ctxt)
1045 throw
1046 DDXParseFailed(string
1047 ("Could not initialize the parser with the file: '")
1048 + document + string("'."));
1049
1050 dds = dest_dds; // dump values here
1051 blob_href = &cid;
1052
1053 ctxt->validate = false;
1054
1055 xmlParseDocument(ctxt);
1056
1057 cleanup_parse();
1058}
1059#endif
1060
1061} // namespace libdap
Contains the attributes for a dataset.
Definition AttrTable.h:150
virtual unsigned int append_attr(const string &name, const string &type, const string &value)
Add an attribute to the table.
Definition AttrTable.cc:308
The basic data type for the DODS DAP types.
Definition BaseType.h:118
virtual bool is_vector_type() const
Returns true if the instance is a vector (i.e., array) type variable.
Definition BaseType.cc:352
virtual bool is_constructor_type() const
Returns true if the instance is a constructor (i.e., Structure, Sequence or Grid) type variable.
Definition BaseType.cc:358
virtual void add_var(BaseType *bt, Part part=nil)
Add a variable.
Definition BaseType.cc:697
std::vector< BaseType * >::iterator Vars_iter
Definition Constructor.h:60
Vars_iter var_begin()
void set_dataset_name(const string &n)
Definition DDS.cc:271
virtual AttrTable & get_attr_table()
Definition DDS.cc:276
void set_dap_version(const string &version_string="2.0")
Definition DDS.cc:328
void add_var(BaseType *bt)
Adds a copy of the variable to the DDS. Using the ptr_duplicate() method, perform a deep copy on the ...
Definition DDS.cc:482
static void ddx_fatal_error(void *parser, const char *msg,...)
static void ddx_ignoreable_whitespace(void *parser, const xmlChar *ch, int len)
static void ddx_get_characters(void *parser, const xmlChar *ch, int len)
static void ddx_sax2_start_element(void *parser, const xmlChar *localname, const xmlChar *prefix, const xmlChar *URI, int nb_namespaces, const xmlChar **namespaces, int nb_attributes, int nb_defaulted, const xmlChar **attributes)
static void ddx_start_document(void *parser)
void intern_stream(FILE *in, DDS *dds, string &cid, const string &boundary="")
Read the DDX from a stream instead of a file.
static void ddx_sax2_end_element(void *parser, const xmlChar *localname, const xmlChar *prefix, const xmlChar *URI)
void intern(const string &document, DDS *dest_dds, string &cid)
static void ddx_end_document(void *parser)
static void ddx_get_cdata(void *parser, const xmlChar *value, int len)
static xmlEntityPtr ddx_get_entity(void *parser, const xmlChar *name)
A class for software fault reporting.
Definition InternalErr.h:61
Holds a structure (aggregate) type.
Definition Structure.h:82
#define DBGN(x)
Definition debug.h:59
#define DBG(x)
Definition debug.h:58
#define DBG2(x)
Definition debug.h:74
top level DAP object to house generic methods
Definition AISConnect.cc:30
Type
Identifies the data type.
Definition Type.h:94
@ dods_sequence_c
Definition Type.h:108
@ dods_uint32_c
Definition Type.h:100
@ dods_int16_c
Definition Type.h:97
@ dods_byte_c
Definition Type.h:96
@ dods_int32_c
Definition Type.h:99
@ dods_url_c
Definition Type.h:104
@ dods_float32_c
Definition Type.h:101
@ dods_uint16_c
Definition Type.h:98
@ dods_float64_c
Definition Type.h:102
@ dods_grid_c
Definition Type.h:111
@ dods_str_c
Definition Type.h:103
@ dods_structure_c
Definition Type.h:106
@ dods_array_c
Definition Type.h:107
string long_to_string(long val, int base)
Definition util.cc:946
bool is_simple_type(Type t)
Returns true if the instance is a numeric, string or URL type variable.
Definition util.cc:766
ObjectType get_type(const string &value)
Definition mime_util.cc:300
bool is_boundary(const char *line, const string &boundary)
Definition mime_util.cc:876