libdap Updated for version 3.21.0
libdap4 is an implementation of OPeNDAP's DAP protocol.
DDXParserSAX2.cc
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 <cstring>
32#include <cstdarg>
33#include <fstream>
34
35#include "BaseType.h"
36#include "Byte.h"
37#include "Int16.h"
38#include "UInt16.h"
39#include "Int32.h"
40#include "UInt32.h"
41#include "Float32.h"
42#include "Float64.h"
43#include "Str.h"
44#include "Url.h"
45#include "Array.h"
46#include "Structure.h"
47#include "Sequence.h"
48#include "Grid.h"
49
50#include "DDXParserSAX2.h"
51
52#include "util.h"
53#include "mime_util.h"
54#include "debug.h"
55
56namespace libdap {
57
58#if defined(DODS_DEBUG) || defined(DODS_DEUG2)
59static const char *states[] =
60 {
61 "start",
62
63 "dataset",
64
65 "attribute_container",
66 "attribute",
67 "attribute_value",
68 "other_xml_attribute",
69
70 "alias",
71
72 "simple_type",
73
74 "array",
75 "dimension",
76
77 "grid",
78 "map",
79
80 "structure",
81 "sequence",
82
83 "blob href",
84
85 "unknown",
86 "error"
87 };
88#endif
89// Glue the BaseTypeFactory to the enum-based factory defined statically
90// here.
91
92BaseType *DDXParser::factory(Type t, const string & name)
93{
94 switch (t) {
95 case dods_byte_c:
96 return d_factory->NewByte(name);
97
98 case dods_int16_c:
99 return d_factory->NewInt16(name);
100
101 case dods_uint16_c:
102 return d_factory->NewUInt16(name);
103
104 case dods_int32_c:
105 return d_factory->NewInt32(name);
106
107 case dods_uint32_c:
108 return d_factory->NewUInt32(name);
109
110 case dods_float32_c:
111 return d_factory->NewFloat32(name);
112
113 case dods_float64_c:
114 return d_factory->NewFloat64(name);
115
116 case dods_str_c:
117 return d_factory->NewStr(name);
118
119 case dods_url_c:
120 return d_factory->NewUrl(name);
121
122 case dods_array_c:
123 return d_factory->NewArray(name);
124
125 case dods_structure_c:
126 return d_factory->NewStructure(name);
127
128 case dods_sequence_c:
129 return d_factory->NewSequence(name);
130
131 case dods_grid_c:
132 return d_factory->NewGrid(name);
133
134 default:
135 return 0;
136 }
137}
138
139static bool is_not(const char *name, const char *tag)
140{
141 return strcmp(name, tag) != 0;
142}
143
144void DDXParser::set_state(DDXParser::ParseState state)
145{
146 s.push(state);
147}
148
149DDXParser::ParseState DDXParser::get_state() const
150{
151 return s.top();
152}
153
154void DDXParser::pop_state()
155{
156 s.pop();
157}
158
162void DDXParser::transfer_xml_attrs(const xmlChar **attributes, int nb_attributes)
163{
164 if (!attribute_table.empty())
165 attribute_table.clear(); // erase old attributes
166
167 unsigned int index = 0;
168 for (int i = 0; i < nb_attributes; ++i, index += 5) {
169 // Make a value using the attribute name and the prefix, namespace URI
170 // and the value. The prefix might be null.
171 attribute_table.insert(map<string, XMLAttribute>::value_type(
172 string((const char *)attributes[index]),
173 XMLAttribute(attributes + index + 1)));
174
175 DBG(cerr << "Attribute '" << (const char *)attributes[index] << "': "
176 << attribute_table[(const char *)attributes[index]].value << endl);
177 }
178}
179
180void DDXParser::transfer_xml_ns(const xmlChar **namespaces, int nb_namespaces)
181{
182 for (int i = 0; i < nb_namespaces; ++i ) {
183 // make a value with the prefix and namespace URI. The prefix might be
184 // null.
185 namespace_table.insert(map<string,string>::value_type(
186 namespaces[i*2] != 0 ? (const char *)namespaces[i*2] : "",
187 (const char *)namespaces[i*2+1]));
188 }
189}
190
195bool DDXParser::check_required_attribute(const string & attr)
196{
197 map < string, XMLAttribute >::iterator i = attribute_table.find(attr);
198 if (i == attribute_table.end())
199 ddx_fatal_error(this, "Required attribute '%s' not found.",
200 attr.c_str());
201 return true;
202}
203
209bool DDXParser::check_attribute(const string & attr)
210{
211 return (attribute_table.find(attr) != attribute_table.end());
212}
213
222void DDXParser::process_attribute_element(const xmlChar **attrs, int nb_attributes)
223{
224 // These methods set the state to parser_error if a problem is found.
225 transfer_xml_attrs(attrs, nb_attributes);
226
227 bool error = !(check_required_attribute(string("name"))
228 && check_required_attribute(string("type")));
229 if (error)
230 return;
231
232 if (attribute_table["type"].value == "Container") {
233 set_state(inside_attribute_container);
234
235 AttrTable *child;
236 AttrTable *parent = at_stack.top();
237
238 child = parent->append_container(attribute_table["name"].value);
239 at_stack.push(child); // save.
240 DBG2(cerr << "Pushing at" << endl);
241 }
242 else if (attribute_table["type"].value == "OtherXML") {
243 set_state(inside_other_xml_attribute);
244
245 dods_attr_name = attribute_table["name"].value;
246 dods_attr_type = attribute_table["type"].value;
247 }
248 else {
249 set_state(inside_attribute);
250 // *** Modify parser. Add a special state for inside OtherXML since it
251 // does not use the <value> element.
252
253 dods_attr_name = attribute_table["name"].value;
254 dods_attr_type = attribute_table["type"].value;
255 }
256}
257
261void DDXParser::process_attribute_alias(const xmlChar **attrs, int nb_attributes)
262{
263 transfer_xml_attrs(attrs, nb_attributes);
264 if (check_required_attribute(string("name"))
265 && check_required_attribute(string("attribute"))) {
266 set_state(inside_alias);
267 at_stack.top()->attr_alias(attribute_table["name"].value,
268 attribute_table["attribute"].value);
269 }
270}
271
279void DDXParser::process_variable(Type t, ParseState s, const xmlChar **attrs,
280 int nb_attributes)
281{
282 transfer_xml_attrs(attrs, nb_attributes);
283
284 set_state(s);
285
286 if (bt_stack.top()->type() == dods_array_c
287 || check_required_attribute("name")) { // throws on error/false
288 BaseType *btp = factory(t, attribute_table["name"].value);
289 if (!btp) {
290 ddx_fatal_error(this, "Internal parser error; could not instantiate the variable '%s'.",
291 attribute_table["name"].value.c_str());
292 }
293 else {
294 // Only run this code if btp is not null! jhrg 9/14/15
295 // Once we make the new variable, we not only load it on to the
296 // BaseType stack, we also load its AttrTable on the AttrTable stack.
297 // The attribute processing software always operates on the AttrTable
298 // at the top of the AttrTable stack (at_stack).
299 bt_stack.push(btp);
300 at_stack.push(&btp->get_attr_table());
301 }
302 }
303}
304
308void DDXParser::process_dimension(const xmlChar **attrs, int nb_attributes)
309{
310 transfer_xml_attrs(attrs, nb_attributes);
311 if (check_required_attribute(string("size"))) {
312 set_state(inside_dimension);
313 Array *ap = dynamic_cast < Array * >(bt_stack.top());
314 if (!ap) {
315 ddx_fatal_error(this, "Parse error: Expected an array variable.");
316 return;
317 }
318
319 ap->append_dim(atoi(attribute_table["size"].value.c_str()),
320 attribute_table["name"].value);
321 }
322}
323
326void DDXParser::process_blob(const xmlChar **attrs, int nb_attributes)
327{
328 transfer_xml_attrs(attrs, nb_attributes);
329 if (check_required_attribute(string("href"))) {
330 set_state(inside_blob_href);
331 *blob_href = attribute_table["href"].value;
332 }
333}
334
341inline bool
342DDXParser::is_attribute_or_alias(const char *name, const xmlChar **attrs,
343 int nb_attributes)
344{
345 if (strcmp(name, "Attribute") == 0) {
346 process_attribute_element(attrs, nb_attributes);
347 // next state: inside_attribtue or inside_attribute_container
348 return true;
349 }
350 else if (strcmp(name, "Alias") == 0) {
351 process_attribute_alias(attrs, nb_attributes);
352 // next state: inside_alias
353 return true;
354 }
355
356 return false;
357}
358
364inline bool DDXParser::is_variable(const char *name, const xmlChar **attrs,
365 int nb_attributes)
366{
367 Type t = get_type(name);
368 //if ((t = is_simple_type(name)) != dods_null_c) {
369 if (is_simple_type(t)) {
370 process_variable(t, inside_simple_type, attrs, nb_attributes);
371 return true;
372 }
373 else if (strcmp(name, "Array") == 0) {
374 process_variable(dods_array_c, inside_array, attrs, nb_attributes);
375 return true;
376 }
377 else if (strcmp(name, "Structure") == 0) {
378 process_variable(dods_structure_c, inside_structure, attrs, nb_attributes);
379 return true;
380 }
381 else if (strcmp(name, "Sequence") == 0) {
382 process_variable(dods_sequence_c, inside_sequence, attrs, nb_attributes);
383 return true;
384 }
385 else if (strcmp(name, "Grid") == 0) {
386 process_variable(dods_grid_c, inside_grid, attrs, nb_attributes);
387 return true;
388 }
389
390 return false;
391}
392
393void DDXParser::finish_variable(const char *tag, Type t, const char *expected)
394{
395 if (strcmp(tag, expected) != 0) {
397 "Expected an end tag for a %s; found '%s' instead.",
398 expected, tag);
399 return;
400 }
401
402 pop_state();
403
404 BaseType *btp = bt_stack.top();
405
406 bt_stack.pop();
407 at_stack.pop();
408
409 if (btp->type() != t) {
411 "Internal error: Expected a %s variable.",
412 expected);
413 delete btp;
414 return;
415 }
416 // Once libxml2 validates, this can go away. 05/30/03 jhrg
417 if (t == dods_array_c
418 && static_cast<Array*>(btp)->dimensions() == 0) {
420 "No dimension element included in the Array '%s'.",
421 btp->name().c_str());
422 delete btp;
423 return;
424 }
425
426 BaseType *parent = bt_stack.top();
427
428 if (!(parent->is_vector_type() || parent->is_constructor_type())) {
430 "Tried to add the array variable '%s' to a non-constructor type (%s %s).",
431 tag,
432 bt_stack.top()->type_name().c_str(),
433 bt_stack.top()->name().c_str());
434 delete btp;
435 return;
436 }
437
438 parent->add_var_nocopy(btp);
439}
440
447
453{
454 DDXParser *parser = static_cast<DDXParser*>(p);
455 parser->error_msg = "";
456 parser->char_data = "";
457
458 // init attr table stack.
459 parser->at_stack.push(&parser->dds->get_attr_table());
460
461 // Trick; DDS *should* be a child of Structure. To simplify parsing,
462 // stuff a Structure on the bt_stack and dump the top level variables
463 // there. Once we're done, transfer the variables to the DDS.
464 parser->bt_stack.push(new Structure("dummy_dds"));
465
466 parser->set_state(parser_start);
467
468 DBG2(cerr << "Parser state: " << states[parser->get_state()] << endl);
469}
470
474{
475 DDXParser *parser = static_cast<DDXParser*>(p);
476 DBG2(cerr << "Ending state == " << states[parser->get_state()] <<
477 endl);
478
479 if (parser->get_state() != parser_start)
480 DDXParser::ddx_fatal_error(parser, "The document contained unbalanced tags.");
481
482 // If we've found any sort of error, don't make the DDX; intern() will
483 // take care of the error.
484 if (parser->get_state() == parser_error) {
485 return;
486 }
487
488 // Pop the temporary Structure off the stack and transfer its variables
489 // to the DDS.
490 Constructor *cp = dynamic_cast < Constructor * >(parser->bt_stack.top());
491 if (!cp) {
492 delete parser->bt_stack.top();
493 parser->bt_stack.pop();
494 ddx_fatal_error(parser, "Parse error: Expected a Structure, Sequence or Grid variable.");
495 return;
496 }
497
498 for (Constructor::Vars_iter i = cp->var_begin(); i != cp->var_end(); ++i) {
499 (*i)->set_parent(0); // top-level vars have no parents
500 parser->dds->add_var(*i);
501 }
502
503 delete parser->bt_stack.top();
504 parser->bt_stack.pop();
505}
506
507void DDXParser::ddx_sax2_start_element(void *p,
508 const xmlChar *l, const xmlChar *prefix, const xmlChar *URI,
509 int nb_namespaces, const xmlChar **namespaces,
510 int nb_attributes, int /*nb_defaulted*/, const xmlChar **attributes)
511{
512 DDXParser *parser = static_cast<DDXParser*>(p);
513 const char *localname = (const char *)l;
514
515 DBG2(cerr << "start element: " << localname << ", states: "
516 << states[parser->get_state()]);
517
518 switch (parser->get_state()) {
519 case parser_start:
520 if (strcmp(localname, "Dataset") == 0) {
521 parser->set_state(inside_dataset);
522 parser->root_ns = URI != 0 ? (const char *)URI: "";
523 parser->transfer_xml_attrs(attributes, nb_attributes);
524
525 if (parser->check_required_attribute(string("name")))
526 parser->dds->set_dataset_name(parser->attribute_table["name"].value);
527
528 if (parser->check_attribute("dapVersion"))
529 parser->dds->set_dap_version(parser->attribute_table["dapVersion"].value);
530 }
531 else
533 "Expected response to start with a Dataset element; found '%s' instead.",
534 localname);
535 break;
536
537 case inside_dataset:
538 if (parser->is_attribute_or_alias(localname, attributes, nb_attributes))
539 break;
540 else if (parser->is_variable(localname, attributes, nb_attributes))
541 break;
542 else if (strcmp(localname, "blob") == 0 || strcmp(localname, "dataBLOB") == 0) {
543 parser->process_blob(attributes, nb_attributes);
544 // next state: inside_data_blob
545 }
546 else
548 "Expected an Attribute, Alias or variable element; found '%s' instead.",
549 localname);
550 break;
551
552 case inside_attribute_container:
553 if (parser->is_attribute_or_alias(localname, attributes, nb_attributes))
554 break;
555 else
557 "Expected an Attribute or Alias element; found '%s' instead.",
558 localname);
559 break;
560
561 case inside_attribute:
562 if (parser->is_attribute_or_alias(localname, attributes, nb_attributes))
563 break;
564 else if (strcmp(localname, "value") == 0)
565 parser->set_state(inside_attribute_value);
566 else
567 ddx_fatal_error(parser,
568 "Expected an 'Attribute', 'Alias' or 'value' element; found '%s' instead.",
569 localname);
570 break;
571
572 case inside_attribute_value:
573 ddx_fatal_error(parser,
574 "Internal parser error; unexpected state, inside value while processing element '%s'.",
575 localname);
576 break;
577
578 case inside_other_xml_attribute:
579 DBGN(cerr << endl << "\t inside_other_xml_attribute: " << localname << endl);
580
581 parser->other_xml_depth++;
582
583 // Accumulate the elements here
584
585 parser->other_xml.append("<");
586 if (prefix) {
587 parser->other_xml.append((const char *)prefix);
588 parser->other_xml.append(":");
589 }
590 parser->other_xml.append(localname);
591
592 if (nb_namespaces != 0) {
593 parser->transfer_xml_ns(namespaces, nb_namespaces);
594
595 for (map<string,string>::iterator i = parser->namespace_table.begin();
596 i != parser->namespace_table.end();
597 ++i) {
598 parser->other_xml.append(" xmlns");
599 if (!i->first.empty()) {
600 parser->other_xml.append(":");
601 parser->other_xml.append(i->first);
602 }
603 parser->other_xml.append("=\"");
604 parser->other_xml.append(i->second);
605 parser->other_xml.append("\"");
606 }
607 }
608
609 if (nb_attributes != 0) {
610 parser->transfer_xml_attrs(attributes, nb_attributes);
611 for (XMLAttrMap::iterator i = parser->attr_table_begin();
612 i != parser->attr_table_end();
613 ++i) {
614 parser->other_xml.append(" ");
615 if (!i->second.prefix.empty()) {
616 parser->other_xml.append(i->second.prefix);
617 parser->other_xml.append(":");
618 }
619 parser->other_xml.append(i->first);
620 parser->other_xml.append("=\"");
621 parser->other_xml.append(i->second.value);
622 parser->other_xml.append("\"");
623 }
624 }
625
626 parser->other_xml.append(">");
627 break;
628
629 case inside_alias:
630 ddx_fatal_error(parser,
631 "Internal parser error; unexpected state, inside alias while processing element '%s'.",
632 localname);
633 break;
634
635 case inside_simple_type:
636 if (parser->is_attribute_or_alias(localname, attributes, nb_attributes))
637 break;
638 else
639 ddx_fatal_error(parser,
640 "Expected an 'Attribute' or 'Alias' element; found '%s' instead.",
641 localname);
642 break;
643
644 case inside_array:
645 if (parser->is_attribute_or_alias(localname, attributes, nb_attributes))
646 break;
647 else if (is_not(localname, "Array")
648 && parser->is_variable(localname, attributes, nb_attributes))
649 break;
650 else if (strcmp(localname, "dimension") == 0) {
651 parser->process_dimension(attributes, nb_attributes);
652 // next state: inside_dimension
653 }
654 else
655 ddx_fatal_error(parser,
656 "Expected an 'Attribute' or 'Alias' element; found '%s' instead.",
657 localname);
658 break;
659
660 case inside_dimension:
661 ddx_fatal_error(parser,
662 "Internal parser error; unexpected state, inside dimension while processing element '%s'.",
663 localname);
664 break;
665
666 case inside_structure:
667 if (parser->is_attribute_or_alias(localname, attributes, nb_attributes))
668 break;
669 else if (parser->is_variable(localname, attributes, nb_attributes))
670 break;
671 else
673 "Expected an Attribute, Alias or variable element; found '%s' instead.",
674 localname);
675 break;
676
677 case inside_sequence:
678 if (parser->is_attribute_or_alias(localname, attributes, nb_attributes))
679 break;
680 else if (parser->is_variable(localname, attributes, nb_attributes))
681 break;
682 else
684 "Expected an Attribute, Alias or variable element; found '%s' instead.",
685 localname);
686 break;
687
688 case inside_grid:
689 if (parser->is_attribute_or_alias(localname, attributes, nb_attributes))
690 break;
691 else if (strcmp(localname, "Array") == 0)
692 parser->process_variable(dods_array_c, inside_array, attributes, nb_attributes);
693 else if (strcmp(localname, "Map") == 0)
694 parser->process_variable(dods_array_c, inside_map, attributes, nb_attributes);
695 else
697 "Expected an Attribute, Alias or variable element; found '%s' instead.",
698 localname);
699 break;
700
701 case inside_map:
702 if (parser->is_attribute_or_alias(localname, attributes, nb_attributes))
703 break;
704 else if (is_not(localname, "Array") && is_not(localname, "Sequence")
705 && is_not(localname, "Grid")
706 && parser->is_variable(localname, attributes, nb_attributes))
707 break;
708 else if (strcmp(localname, "dimension") == 0) {
709 parser->process_dimension(attributes, nb_attributes);
710 // next state: inside_dimension
711 }
712 else
713 ddx_fatal_error(parser,
714 "Expected an 'Attribute', 'Alias', variable or 'dimension' element; found '%s' instead.",
715 localname);
716 break;
717
718 case inside_blob_href:
719 ddx_fatal_error(parser,
720 "Internal parser error; unexpected state, inside blob href while processing element '%s'.",
721 localname);
722 break;
723
724 case parser_unknown:
725 // *** Never used? If so remove/error
726 parser->set_state(parser_unknown);
727 break;
728
729 case parser_error:
730 break;
731 }
732
733 DBGN(cerr << " ... " << states[parser->get_state()] << endl);
734}
735
736void DDXParser::ddx_sax2_end_element(void *p, const xmlChar *l,
737 const xmlChar *prefix, const xmlChar *URI)
738{
739 DDXParser *parser = static_cast<DDXParser*>(p);
740 const char *localname = (const char *)l;
741
742 DBG2(cerr << "End element " << localname << " (state "
743 << states[parser->get_state()] << ")" << endl);
744
745 switch (parser->get_state()) {
746 case parser_start:
747 ddx_fatal_error(parser,
748 "Internal parser error; unexpected state, inside start state while processing element '%s'.",
749 localname);
750 break;
751
752 case inside_dataset:
753 if (strcmp(localname, "Dataset") == 0)
754 parser->pop_state();
755 else
757 "Expected an end Dataset tag; found '%s' instead.",
758 localname);
759 break;
760
761 case inside_attribute_container:
762 if (strcmp(localname, "Attribute") == 0) {
763 parser->pop_state();
764 parser->at_stack.pop(); // pop when leaving a container.
765 }
766 else
768 "Expected an end Attribute tag; found '%s' instead.",
769 localname);
770 break;
771
772 case inside_attribute:
773 if (strcmp(localname, "Attribute") == 0)
774 parser->pop_state();
775 else
777 "Expected an end Attribute tag; found '%s' instead.",
778 localname);
779 break;
780
781 case inside_attribute_value:
782 if (strcmp(localname, "value") == 0) {
783 parser->pop_state();
784 AttrTable *atp = parser->at_stack.top();
785 atp->append_attr(parser->dods_attr_name,
786 parser->dods_attr_type, parser->char_data);
787 parser->char_data = ""; // Null this after use.
788 }
789 else
791 "Expected an end value tag; found '%s' instead.",
792 localname);
793
794 break;
795
796 case inside_other_xml_attribute: {
797 if (strcmp(localname, "Attribute") == 0
798 && parser->root_ns == (const char *)URI) {
799
800 DBGN(cerr << endl << "\t Popping the 'inside_other_xml_attribute' state"
801 << endl);
802
803 parser->pop_state();
804
805 AttrTable *atp = parser->at_stack.top();
806 atp->append_attr(parser->dods_attr_name,
807 parser->dods_attr_type, parser->other_xml);
808
809 parser->other_xml = ""; // Null this after use.
810 }
811 else {
812 DBGN(cerr << endl << "\t inside_other_xml_attribute: " << localname
813 << ", depth: " << parser->other_xml_depth << endl);
814 if (parser->other_xml_depth == 0)
816 "Expected an OtherXML attribute to end! Instead I found '%s'",
817 localname);
818 parser->other_xml_depth--;
819
820 parser->other_xml.append("</");
821 if (prefix) {
822 parser->other_xml.append((const char *)prefix);
823 parser->other_xml.append(":");
824 }
825 parser->other_xml.append(localname);
826 parser->other_xml.append(">");
827 }
828 break;
829 }
830 // Alias is busted in libdap++ 05/29/03 jhrg
831 case inside_alias:
832 parser->pop_state();
833 break;
834
835 case inside_simple_type: {
836 Type t = get_type(localname);
837 if (is_simple_type(t)) {
838 parser->pop_state();
839 BaseType *btp = parser->bt_stack.top();
840 parser->bt_stack.pop();
841 parser->at_stack.pop();
842
843 BaseType *parent = parser->bt_stack.top();
844
845 if (parent->is_vector_type() || parent->is_constructor_type()) {
846 parent->add_var(btp);
847 delete btp;
848 }
849 else {
851 "Tried to add the simple-type variable '%s' to a non-constructor type (%s %s).",
852 localname,
853 parser->bt_stack.top()->
854 type_name().c_str(),
855 parser->bt_stack.top()->name().
856 c_str());
857 delete btp;
858 }
859 }
860 else {
862 "Expected an end tag for a simple type; found '%s' instead.",
863 localname);
864 }
865 break;
866 }
867
868 case inside_array:
869 parser->finish_variable(localname, dods_array_c, "Array");
870 break;
871
872 case inside_dimension:
873 if (strcmp(localname, "dimension") == 0)
874 parser->pop_state();
875 else
877 "Expected an end dimension tag; found '%s' instead.",
878 localname);
879 break;
880
881 case inside_structure:
882 parser->finish_variable(localname, dods_structure_c, "Structure");
883 break;
884
885 case inside_sequence:
886 parser->finish_variable(localname, dods_sequence_c, "Sequence");
887 break;
888
889 case inside_grid:
890 parser->finish_variable(localname, dods_grid_c, "Grid");
891 break;
892
893 case inside_map:
894 parser->finish_variable(localname, dods_array_c, "Map");
895 break;
896
897 case inside_blob_href:
898 if (strcmp(localname, "blob") == 0 || strcmp(localname, "dataBLOB") == 0)
899 parser->pop_state();
900 else
902 "Expected an end dataBLOB/blob tag; found '%s' instead.",
903 localname);
904 break;
905
906 case parser_unknown:
907 parser->pop_state();
908 break;
909
910 case parser_error:
911 break;
912 }
913
914
915 DBGN(cerr << " ... " << states[parser->get_state()] << endl);
916}
917
921void DDXParser::ddx_get_characters(void * p, const xmlChar * ch, int len)
922{
923 DDXParser *parser = static_cast<DDXParser*>(p);
924
925 switch (parser->get_state()) {
926 case inside_attribute_value:
927 parser->char_data.append((const char *)(ch), len);
928 DBG2(cerr << "Characters: '" << parser->char_data << "'" << endl);
929 break;
930
931 case inside_other_xml_attribute:
932 parser->other_xml.append((const char *)(ch), len);
933 DBG2(cerr << "Other XML Characters: '" << parser->other_xml << "'" << endl);
934 break;
935
936 default:
937 break;
938 }
939}
940
945void DDXParser::ddx_ignoreable_whitespace(void *p, const xmlChar *ch,
946 int len)
947{
948 DDXParser *parser = static_cast<DDXParser*>(p);
949
950 switch (parser->get_state()) {
951 case inside_other_xml_attribute:
952 parser->other_xml.append((const char *)(ch), len);
953 break;
954
955 default:
956 break;
957 }
958}
959
965void DDXParser::ddx_get_cdata(void *p, const xmlChar *value, int len)
966{
967 DDXParser *parser = static_cast<DDXParser*>(p);
968
969 switch (parser->get_state()) {
970 case inside_other_xml_attribute:
971 parser->other_xml.append((const char *)(value), len);
972 break;
973
974 case parser_unknown:
975 break;
976
977 default:
979 "Found a CData block but none are allowed by DAP.");
980
981 break;
982 }
983}
984
989xmlEntityPtr DDXParser::ddx_get_entity(void *, const xmlChar * name)
990{
991 return xmlGetPredefinedEntity(name);
992}
993
1001void DDXParser::ddx_fatal_error(void * p, const char *msg, ...)
1002{
1003 va_list args;
1004 DDXParser *parser = static_cast<DDXParser*>(p);
1005
1006 parser->set_state(parser_error);
1007
1008 va_start(args, msg);
1009 char str[1024];
1010 vsnprintf(str, 1024, msg, args);
1011 va_end(args);
1012
1013 int line = xmlSAX2GetLineNumber(parser->ctxt);
1014
1015 parser->error_msg += "At line " + long_to_string(line) + ": ";
1016 parser->error_msg += string(str) + string("\n");
1017}
1018
1020
1021void DDXParser::cleanup_parse()
1022{
1023 bool wellFormed = ctxt->wellFormed;
1024 bool valid = ctxt->valid;
1025
1026 xmlFreeParserCtxt(ctxt);
1027
1028 // If there's an error, there may still be items on the stack at the
1029 // end of the parse.
1030 while (!bt_stack.empty()) {
1031 delete bt_stack.top();
1032 bt_stack.pop();
1033 }
1034
1035 if (!wellFormed) {
1036 throw DDXParseFailed(string("The DDX is not a well formed XML document.\n") + error_msg);
1037 }
1038
1039 if (!valid) {
1040 throw DDXParseFailed(string("The DDX is not a valid document.\n") + error_msg);
1041 }
1042
1043 if (get_state() == parser_error) {
1044 throw DDXParseFailed(string("Error parsing DDX response.\n") + error_msg);
1045 }
1046}
1047
1055void DDXParser::intern_stream(istream &in, DDS *dest_dds, string &cid, const string &boundary)
1056{
1057 // Code example from libxml2 docs re: read from a stream.
1058 if (!in || in.eof())
1059 throw InternalErr(__FILE__, __LINE__, "Input stream not open or read error");
1060
1061 const int size = 1024;
1062 char chars[size + 1];
1063
1064 // int res = fread(chars, 1, 4, in);
1065 in.read(chars, 4);
1066 int res = in.gcount();
1067 if (res > 0) {
1068 chars[4]='\0';
1069 ctxt = xmlCreatePushParserCtxt(&ddx_sax_parser, this, chars, res, "stream");
1070
1071 if (!ctxt)
1072 throw DDXParseFailed("Error parsing DDX response: Input does not look like XML");
1073
1074 dds = dest_dds; // dump values here
1075 blob_href = &cid; // cid goes here
1076
1077 ctxt->validate = true;
1078
1079 in.getline(chars, size); // chars has size+1 elements
1080 res = in.gcount();
1081 chars[res-1] = '\n'; // libxml needs the newline; w/o it the parse will fail
1082 chars[res] = '\0';
1083 while (res > 0 && !is_boundary(chars, boundary)) {
1084 DBG(cerr << "line (" << res << "): " << chars << endl);
1085 xmlParseChunk(ctxt, chars, res, 0);
1086
1087 in.getline(chars, size); // chars has size+1 elements
1088 res = in.gcount();
1089 if (res > 0) {
1090 chars[res-1] = '\n';
1091 chars[res] = '\0';
1092 }
1093 }
1094
1095 // This call ends the parse: The fourth argument of xmlParseChunk is
1096 // the bool 'terminate.'
1097 xmlParseChunk(ctxt, chars, 0, 1);
1098
1099 cleanup_parse();
1100 }
1101 else {
1102 throw DDXParseFailed("Error parsing DDX response: Could not read from input stream.");
1103 }
1104}
1105
1106
1109void DDXParser::intern_stream(FILE *in, DDS *dest_dds, string &cid, const string &boundary)
1110{
1111 // Code example from libxml2 docs re: read from a stream.
1112 if (!in || feof(in) || ferror(in))
1113 throw InternalErr(__FILE__, __LINE__, "Input stream not open or read error");
1114
1115 const int size = 1024;
1116 char chars[size];
1117
1118 int res = fread(chars, 1, 4, in);
1119 if (res > 0) {
1120 chars[4]='\0';
1121 ctxt = xmlCreatePushParserCtxt(&ddx_sax_parser, this, chars, res, "stream");
1122
1123 if (!ctxt)
1124 throw DDXParseFailed("Error parsing DDX response: Input does not look like XML");
1125
1126 dds = dest_dds; // dump values here
1127 blob_href = &cid; // cid goes here
1128
1129 ctxt->validate = true;
1130
1131
1132 while ((fgets(chars, size, in) != 0) && !is_boundary(chars, boundary)) {
1133 DBG(cerr << "line (" << strlen(chars) << "): " << chars << endl);
1134 xmlParseChunk(ctxt, chars, strlen(chars), 0);
1135 }
1136 // This call ends the parse: The fourth argument of xmlParseChunk is
1137 // the bool 'terminate.'
1138 xmlParseChunk(ctxt, chars, 0, 1);
1139
1140 cleanup_parse();
1141 }
1142 else {
1143 throw DDXParseFailed("Error parsing DDX response: Could not read from input file.");
1144 }
1145}
1146
1147
1159void DDXParser::intern(const string & document, DDS * dest_dds, string &cid)
1160{
1161
1162 ifstream ifs;
1163 ifs.open(document);
1164 if (ifs.good()) {
1165 intern_stream(ifs,dest_dds,cid,"");
1166 }
1167 ifs.close();
1168}
1169
1170#if 0
1171void DDXParser::intern(const string & document, DDS * dest_dds, string &cid)
1172{
1173 // Create the ctxt pointer explicitly so that we can store a pointer
1174 // to it in the DDXParser instance. This provides a way to generate our
1175 // own error messages *with* line numbers. The messages are pretty
1176 // meaningless otherwise. This means that we use an interface from the
1177 // 'parser internals' header, and not the 'parser' header. However, this
1178 // interface is also used in one of the documented examples, so it's
1179 // probably pretty stable. 06/02/03 jhrg
1180 ctxt = xmlCreateFileParserCtxt(document.c_str());
1181 if (!ctxt)
1182 throw
1183 DDXParseFailed(string
1184 ("Could not initialize the parser with the file: '")
1185 + document + string("'."));
1186
1187 dds = dest_dds; // dump values here
1188 blob_href = &cid;
1189
1190 ctxt->validate = false;
1191
1192 xmlParseDocument(ctxt);
1193
1194 cleanup_parse();
1195}
1196#endif
1197
1198} // namespace libdap
Vars_iter var_begin()
void set_dataset_name(const string &n)
Definition DDS.cc:292
virtual AttrTable & get_attr_table()
Definition DDS.cc:301
void set_dap_version(const string &version_string="2.0")
Definition DDS.cc:369
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:544
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_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.
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:65
Holds a structure (aggregate) type.
Definition Structure.h:84
top level DAP object to house generic methods
Definition AISConnect.cc:30
Type
Identifies the data type.
Definition Type.h:94
string type_name(Type t)
Definition util.cc:763
bool is_simple_type(Type t)
Returns true if the instance is a numeric, string or URL type variable.
Definition util.cc:778
ObjectType get_type(const string &value)
Definition mime_util.cc:324
bool is_boundary(const char *line, const string &boundary)
Definition mime_util.cc:939