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