49#include "DDXParserSAX2.h"
57#if defined(DODS_DEBUG) || defined(DODS_DEUG2)
58static const char *states[] =
64 "attribute_container",
67 "other_xml_attribute",
91BaseType *DDXParser::factory(
Type t,
const string & name)
95 return d_factory->NewByte(name);
98 return d_factory->NewInt16(name);
101 return d_factory->NewUInt16(name);
104 return d_factory->NewInt32(name);
107 return d_factory->NewUInt32(name);
110 return d_factory->NewFloat32(name);
113 return d_factory->NewFloat64(name);
116 return d_factory->NewStr(name);
119 return d_factory->NewUrl(name);
122 return d_factory->NewArray(name);
124 case dods_structure_c:
125 return d_factory->NewStructure(name);
127 case dods_sequence_c:
128 return d_factory->NewSequence(name);
131 return d_factory->NewGrid(name);
138static bool is_not(
const char *name,
const char *tag)
140 return strcmp(name, tag) != 0;
143void DDXParser::set_state(DDXParser::ParseState state)
148DDXParser::ParseState DDXParser::get_state()
const
153void DDXParser::pop_state()
161void DDXParser::transfer_xml_attrs(
const xmlChar **attributes,
int nb_attributes)
163 if (!attribute_table.empty())
164 attribute_table.clear();
166 unsigned int index = 0;
167 for (
int i = 0; i < nb_attributes; ++i, index += 5) {
170 attribute_table.insert(map<string, XMLAttribute>::value_type(
171 string((
const char *)attributes[index]),
172 XMLAttribute(attributes + index + 1)));
174 DBG(cerr <<
"Attribute '" << (
const char *)attributes[index] <<
"': "
175 << attribute_table[(
const char *)attributes[index]].value << endl);
179void DDXParser::transfer_xml_ns(
const xmlChar **namespaces,
int nb_namespaces)
181 for (
int i = 0; i < nb_namespaces; ++i ) {
184 namespace_table.insert(map<string,string>::value_type(
185 namespaces[i*2] != 0 ? (
const char *)namespaces[i*2] :
"",
186 (
const char *)namespaces[i*2+1]));
194bool DDXParser::check_required_attribute(
const string & attr)
196 map < string, XMLAttribute >::iterator i = attribute_table.find(attr);
197 if (i == attribute_table.end())
208bool DDXParser::check_attribute(
const string & attr)
210 return (attribute_table.find(attr) != attribute_table.end());
221void DDXParser::process_attribute_element(
const xmlChar **attrs,
int nb_attributes)
224 transfer_xml_attrs(attrs, nb_attributes);
226 bool error = !(check_required_attribute(
string(
"name"))
227 && check_required_attribute(
string(
"type")));
231 if (attribute_table[
"type"].value ==
"Container") {
232 set_state(inside_attribute_container);
235 AttrTable *parent = at_stack.top();
237 child = parent->append_container(attribute_table[
"name"].value);
238 at_stack.push(child);
239 DBG2(cerr <<
"Pushing at" << endl);
241 else if (attribute_table[
"type"].value ==
"OtherXML") {
242 set_state(inside_other_xml_attribute);
244 dods_attr_name = attribute_table[
"name"].value;
245 dods_attr_type = attribute_table[
"type"].value;
248 set_state(inside_attribute);
252 dods_attr_name = attribute_table[
"name"].value;
253 dods_attr_type = attribute_table[
"type"].value;
260void DDXParser::process_attribute_alias(
const xmlChar **attrs,
int nb_attributes)
262 transfer_xml_attrs(attrs, nb_attributes);
263 if (check_required_attribute(
string(
"name"))
264 && check_required_attribute(
string(
"attribute"))) {
265 set_state(inside_alias);
266 at_stack.top()->attr_alias(attribute_table[
"name"].value,
267 attribute_table[
"attribute"].value);
278void DDXParser::process_variable(
Type t, ParseState s,
const xmlChar **attrs,
281 transfer_xml_attrs(attrs, nb_attributes);
285 if (bt_stack.top()->type() == dods_array_c
286 || check_required_attribute(
"name")) {
287 BaseType *btp = factory(t, attribute_table[
"name"].value);
289 ddx_fatal_error(
this,
"Internal parser error; could not instantiate the variable '%s'.",
290 attribute_table[
"name"].value.c_str());
299 at_stack.push(&btp->get_attr_table());
307void DDXParser::process_dimension(
const xmlChar **attrs,
int nb_attributes)
309 transfer_xml_attrs(attrs, nb_attributes);
310 if (check_required_attribute(
string(
"size"))) {
311 set_state(inside_dimension);
312 Array *ap =
dynamic_cast < Array *
>(bt_stack.top());
318 ap->append_dim(atoi(attribute_table[
"size"].value.c_str()),
319 attribute_table[
"name"].value);
325void DDXParser::process_blob(
const xmlChar **attrs,
int nb_attributes)
327 transfer_xml_attrs(attrs, nb_attributes);
328 if (check_required_attribute(
string(
"href"))) {
329 set_state(inside_blob_href);
330 *blob_href = attribute_table[
"href"].value;
341DDXParser::is_attribute_or_alias(
const char *name,
const xmlChar **attrs,
344 if (strcmp(name,
"Attribute") == 0) {
345 process_attribute_element(attrs, nb_attributes);
349 else if (strcmp(name,
"Alias") == 0) {
350 process_attribute_alias(attrs, nb_attributes);
363inline bool DDXParser::is_variable(
const char *name,
const xmlChar **attrs,
369 process_variable(t, inside_simple_type, attrs, nb_attributes);
372 else if (strcmp(name,
"Array") == 0) {
373 process_variable(dods_array_c, inside_array, attrs, nb_attributes);
376 else if (strcmp(name,
"Structure") == 0) {
377 process_variable(dods_structure_c, inside_structure, attrs, nb_attributes);
380 else if (strcmp(name,
"Sequence") == 0) {
381 process_variable(dods_sequence_c, inside_sequence, attrs, nb_attributes);
384 else if (strcmp(name,
"Grid") == 0) {
385 process_variable(dods_grid_c, inside_grid, attrs, nb_attributes);
392void DDXParser::finish_variable(
const char *tag,
Type t,
const char *expected)
394 if (strcmp(tag, expected) != 0) {
396 "Expected an end tag for a %s; found '%s' instead.",
403 BaseType *btp = bt_stack.top();
408 if (btp->type() != t) {
410 "Internal error: Expected a %s variable.",
416 if (t == dods_array_c
417 &&
static_cast<Array*
>(btp)->dimensions() == 0) {
419 "No dimension element included in the Array '%s'.",
420 btp->name().c_str());
425 BaseType *parent = bt_stack.top();
427 if (!(parent->is_vector_type() || parent->is_constructor_type())) {
429 "Tried to add the array variable '%s' to a non-constructor type (%s %s).",
431 bt_stack.top()->type_name().c_str(),
432 bt_stack.top()->name().c_str());
437 parent->add_var_nocopy(btp);
454 parser->error_msg =
"";
455 parser->char_data =
"";
463 parser->bt_stack.push(
new Structure(
"dummy_dds"));
465 parser->set_state(parser_start);
467 DBG2(cerr <<
"Parser state: " << states[parser->get_state()] << endl);
475 DBG2(cerr <<
"Ending state == " << states[parser->get_state()] <<
478 if (parser->get_state() != parser_start)
483 if (parser->get_state() == parser_error) {
491 delete parser->bt_stack.top();
492 parser->bt_stack.pop();
493 ddx_fatal_error(parser,
"Parse error: Expected a Structure, Sequence or Grid variable.");
502 delete parser->bt_stack.top();
503 parser->bt_stack.pop();
506void DDXParser::ddx_sax2_start_element(
void *p,
507 const xmlChar *l,
const xmlChar *prefix,
const xmlChar *URI,
508 int nb_namespaces,
const xmlChar **namespaces,
509 int nb_attributes,
int ,
const xmlChar **attributes)
512 const char *localname = (
const char *)l;
514 DBG2(cerr <<
"start element: " << localname <<
", states: "
515 << states[parser->get_state()]);
517 switch (parser->get_state()) {
519 if (strcmp(localname,
"Dataset") == 0) {
520 parser->set_state(inside_dataset);
521 parser->root_ns = URI != 0 ? (
const char *)URI:
"";
522 parser->transfer_xml_attrs(attributes, nb_attributes);
524 if (parser->check_required_attribute(
string(
"name")))
527 if (parser->check_attribute(
"dapVersion"))
528 parser->dds->
set_dap_version(parser->attribute_table[
"dapVersion"].value);
532 "Expected response to start with a Dataset element; found '%s' instead.",
537 if (parser->is_attribute_or_alias(localname, attributes, nb_attributes))
539 else if (parser->is_variable(localname, attributes, nb_attributes))
541 else if (strcmp(localname,
"blob") == 0 || strcmp(localname,
"dataBLOB") == 0) {
542 parser->process_blob(attributes, nb_attributes);
547 "Expected an Attribute, Alias or variable element; found '%s' instead.",
551 case inside_attribute_container:
552 if (parser->is_attribute_or_alias(localname, attributes, nb_attributes))
556 "Expected an Attribute or Alias element; found '%s' instead.",
560 case inside_attribute:
561 if (parser->is_attribute_or_alias(localname, attributes, nb_attributes))
563 else if (strcmp(localname,
"value") == 0)
564 parser->set_state(inside_attribute_value);
567 "Expected an 'Attribute', 'Alias' or 'value' element; found '%s' instead.",
571 case inside_attribute_value:
573 "Internal parser error; unexpected state, inside value while processing element '%s'.",
577 case inside_other_xml_attribute:
578 DBGN(cerr << endl <<
"\t inside_other_xml_attribute: " << localname << endl);
580 parser->other_xml_depth++;
584 parser->other_xml.append(
"<");
586 parser->other_xml.append((
const char *)prefix);
587 parser->other_xml.append(
":");
589 parser->other_xml.append(localname);
591 if (nb_namespaces != 0) {
592 parser->transfer_xml_ns(namespaces, nb_namespaces);
594 for (map<string,string>::iterator i = parser->namespace_table.begin();
595 i != parser->namespace_table.end();
597 parser->other_xml.append(
" xmlns");
598 if (!i->first.empty()) {
599 parser->other_xml.append(
":");
600 parser->other_xml.append(i->first);
602 parser->other_xml.append(
"=\"");
603 parser->other_xml.append(i->second);
604 parser->other_xml.append(
"\"");
608 if (nb_attributes != 0) {
609 parser->transfer_xml_attrs(attributes, nb_attributes);
610 for (XMLAttrMap::iterator i = parser->attr_table_begin();
611 i != parser->attr_table_end();
613 parser->other_xml.append(
" ");
614 if (!i->second.prefix.empty()) {
615 parser->other_xml.append(i->second.prefix);
616 parser->other_xml.append(
":");
618 parser->other_xml.append(i->first);
619 parser->other_xml.append(
"=\"");
620 parser->other_xml.append(i->second.value);
621 parser->other_xml.append(
"\"");
625 parser->other_xml.append(
">");
630 "Internal parser error; unexpected state, inside alias while processing element '%s'.",
634 case inside_simple_type:
635 if (parser->is_attribute_or_alias(localname, attributes, nb_attributes))
639 "Expected an 'Attribute' or 'Alias' element; found '%s' instead.",
644 if (parser->is_attribute_or_alias(localname, attributes, nb_attributes))
646 else if (is_not(localname,
"Array")
647 && parser->is_variable(localname, attributes, nb_attributes))
649 else if (strcmp(localname,
"dimension") == 0) {
650 parser->process_dimension(attributes, nb_attributes);
655 "Expected an 'Attribute' or 'Alias' element; found '%s' instead.",
659 case inside_dimension:
661 "Internal parser error; unexpected state, inside dimension while processing element '%s'.",
665 case inside_structure:
666 if (parser->is_attribute_or_alias(localname, attributes, nb_attributes))
668 else if (parser->is_variable(localname, attributes, nb_attributes))
672 "Expected an Attribute, Alias or variable element; found '%s' instead.",
676 case inside_sequence:
677 if (parser->is_attribute_or_alias(localname, attributes, nb_attributes))
679 else if (parser->is_variable(localname, attributes, nb_attributes))
683 "Expected an Attribute, Alias or variable element; found '%s' instead.",
688 if (parser->is_attribute_or_alias(localname, attributes, nb_attributes))
690 else if (strcmp(localname,
"Array") == 0)
691 parser->process_variable(dods_array_c, inside_array, attributes, nb_attributes);
692 else if (strcmp(localname,
"Map") == 0)
693 parser->process_variable(dods_array_c, inside_map, attributes, nb_attributes);
696 "Expected an Attribute, Alias or variable element; found '%s' instead.",
701 if (parser->is_attribute_or_alias(localname, attributes, nb_attributes))
703 else if (is_not(localname,
"Array") && is_not(localname,
"Sequence")
704 && is_not(localname,
"Grid")
705 && parser->is_variable(localname, attributes, nb_attributes))
707 else if (strcmp(localname,
"dimension") == 0) {
708 parser->process_dimension(attributes, nb_attributes);
713 "Expected an 'Attribute', 'Alias', variable or 'dimension' element; found '%s' instead.",
717 case inside_blob_href:
719 "Internal parser error; unexpected state, inside blob href while processing element '%s'.",
725 parser->set_state(parser_unknown);
732 DBGN(cerr <<
" ... " << states[parser->get_state()] << endl);
735void DDXParser::ddx_sax2_end_element(
void *p,
const xmlChar *l,
736 const xmlChar *prefix,
const xmlChar *URI)
738 DDXParser *parser =
static_cast<DDXParser*
>(p);
739 const char *localname = (
const char *)l;
741 DBG2(cerr <<
"End element " << localname <<
" (state "
742 << states[parser->get_state()] <<
")" << endl);
744 switch (parser->get_state()) {
747 "Internal parser error; unexpected state, inside start state while processing element '%s'.",
752 if (strcmp(localname,
"Dataset") == 0)
756 "Expected an end Dataset tag; found '%s' instead.",
760 case inside_attribute_container:
761 if (strcmp(localname,
"Attribute") == 0) {
763 parser->at_stack.pop();
767 "Expected an end Attribute tag; found '%s' instead.",
771 case inside_attribute:
772 if (strcmp(localname,
"Attribute") == 0)
776 "Expected an end Attribute tag; found '%s' instead.",
780 case inside_attribute_value:
781 if (strcmp(localname,
"value") == 0) {
783 AttrTable *atp = parser->at_stack.top();
784 atp->append_attr(parser->dods_attr_name,
785 parser->dods_attr_type, parser->char_data);
786 parser->char_data =
"";
790 "Expected an end value tag; found '%s' instead.",
795 case inside_other_xml_attribute: {
796 if (strcmp(localname,
"Attribute") == 0
797 && parser->root_ns == (
const char *)URI) {
799 DBGN(cerr << endl <<
"\t Popping the 'inside_other_xml_attribute' state"
804 AttrTable *atp = parser->at_stack.top();
805 atp->append_attr(parser->dods_attr_name,
806 parser->dods_attr_type, parser->other_xml);
808 parser->other_xml =
"";
811 DBGN(cerr << endl <<
"\t inside_other_xml_attribute: " << localname
812 <<
", depth: " << parser->other_xml_depth << endl);
813 if (parser->other_xml_depth == 0)
815 "Expected an OtherXML attribute to end! Instead I found '%s'",
817 parser->other_xml_depth--;
819 parser->other_xml.append(
"</");
821 parser->other_xml.append((
const char *)prefix);
822 parser->other_xml.append(
":");
824 parser->other_xml.append(localname);
825 parser->other_xml.append(
">");
834 case inside_simple_type: {
838 BaseType *btp = parser->bt_stack.top();
839 parser->bt_stack.pop();
840 parser->at_stack.pop();
842 BaseType *parent = parser->bt_stack.top();
844 if (parent->is_vector_type() || parent->is_constructor_type()) {
845 parent->add_var(btp);
850 "Tried to add the simple-type variable '%s' to a non-constructor type (%s %s).",
852 parser->bt_stack.top()->
854 parser->bt_stack.top()->name().
861 "Expected an end tag for a simple type; found '%s' instead.",
868 parser->finish_variable(localname, dods_array_c,
"Array");
871 case inside_dimension:
872 if (strcmp(localname,
"dimension") == 0)
876 "Expected an end dimension tag; found '%s' instead.",
880 case inside_structure:
881 parser->finish_variable(localname, dods_structure_c,
"Structure");
884 case inside_sequence:
885 parser->finish_variable(localname, dods_sequence_c,
"Sequence");
889 parser->finish_variable(localname, dods_grid_c,
"Grid");
893 parser->finish_variable(localname, dods_array_c,
"Map");
896 case inside_blob_href:
897 if (strcmp(localname,
"blob") == 0 || strcmp(localname,
"dataBLOB") == 0)
901 "Expected an end dataBLOB/blob tag; found '%s' instead.",
914 DBGN(cerr <<
" ... " << states[parser->get_state()] << endl);
924 switch (parser->get_state()) {
925 case inside_attribute_value:
926 parser->char_data.append((
const char *)(ch), len);
927 DBG2(cerr <<
"Characters: '" << parser->char_data <<
"'" << endl);
930 case inside_other_xml_attribute:
931 parser->other_xml.append((
const char *)(ch), len);
932 DBG2(cerr <<
"Other XML Characters: '" << parser->other_xml <<
"'" << endl);
949 switch (parser->get_state()) {
950 case inside_other_xml_attribute:
951 parser->other_xml.append((
const char *)(ch), len);
968 switch (parser->get_state()) {
969 case inside_other_xml_attribute:
970 parser->other_xml.append((
const char *)(value), len);
978 "Found a CData block but none are allowed by DAP.");
990 return xmlGetPredefinedEntity(name);
1005 parser->set_state(parser_error);
1007 va_start(args, msg);
1009 vsnprintf(str, 1024, msg, args);
1012 int line = xmlSAX2GetLineNumber(parser->ctxt);
1014 parser->error_msg +=
"At line " + long_to_string(line) +
": ";
1015 parser->error_msg += string(str) + string(
"\n");
1020void DDXParser::cleanup_parse(xmlParserCtxtPtr & context)
1022 bool wellFormed = context->wellFormed;
1023 bool valid = context->valid;
1025 context->sax = NULL;
1026 xmlFreeParserCtxt(context);
1030 while (!bt_stack.empty()) {
1031 delete bt_stack.top();
1036 throw DDXParseFailed(
string(
"The DDX is not a well formed XML document.\n") + error_msg);
1040 throw DDXParseFailed(
string(
"The DDX is not a valid document.\n") + error_msg);
1043 if (get_state() == parser_error) {
1044 throw DDXParseFailed(
string(
"Error parsing DDX response.\n") + error_msg);
1058 if (!in || in.eof())
1059 throw InternalErr(__FILE__, __LINE__,
"Input stream not open or read error");
1061 const int size = 1024;
1062 char chars[size + 1];
1066 int res = in.gcount();
1069 xmlParserCtxtPtr context = xmlCreatePushParserCtxt(NULL, NULL, chars, res,
"stream");
1072 throw DDXParseFailed(
"Error parsing DDX response: Input does not look like XML");
1078 xmlSAXHandler ddx_sax_parser;
1079 memset( &ddx_sax_parser, 0,
sizeof(xmlSAXHandler) );
1090 ddx_sax_parser.initialized = XML_SAX2_MAGIC;
1091 ddx_sax_parser.startElementNs = &DDXParser::ddx_sax2_start_element;
1092 ddx_sax_parser.endElementNs = &DDXParser::ddx_sax2_end_element;
1094 context->sax = &ddx_sax_parser;
1095 context->userData =
this;
1096 context->validate =
true;
1098 in.getline(chars, size);
1100 chars[res-1] =
'\n';
1102 while (res > 0 && !
is_boundary(chars, boundary)) {
1103 DBG(cerr <<
"line (" << res <<
"): " << chars << endl);
1104 xmlParseChunk(ctxt, chars, res, 0);
1106 in.getline(chars, size);
1109 chars[res-1] =
'\n';
1116 xmlParseChunk(ctxt, chars, 0, 1);
1118 cleanup_parse(context);
1121 throw DDXParseFailed(
"Error parsing DDX response: Could not read from input stream.");
1130 if (!in || feof(in) || ferror(in))
1131 throw InternalErr(__FILE__, __LINE__,
"Input stream not open or read error");
1133 const int size = 1024;
1136 int res = fread(chars, 1, 4, in);
1139 xmlParserCtxtPtr context = xmlCreatePushParserCtxt(NULL, NULL, chars, res,
"stream");
1142 throw DDXParseFailed(
"Error parsing DDX response: Input does not look like XML");
1148 xmlSAXHandler ddx_sax_parser;
1149 memset( &ddx_sax_parser, 0,
sizeof(xmlSAXHandler) );
1160 ddx_sax_parser.initialized = XML_SAX2_MAGIC;
1161 ddx_sax_parser.startElementNs = &DDXParser::ddx_sax2_start_element;
1162 ddx_sax_parser.endElementNs = &DDXParser::ddx_sax2_end_element;
1164 context->sax = &ddx_sax_parser;
1165 context->userData =
this;
1166 context->validate =
true;
1169 while ((fgets(chars, size, in) != 0) && !
is_boundary(chars, boundary)) {
1170 DBG(cerr <<
"line (" << strlen(chars) <<
"): " << chars << endl);
1171 xmlParseChunk(ctxt, chars, strlen(chars), 0);
1175 xmlParseChunk(ctxt, chars, 0, 1);
1177 cleanup_parse(context);
1180 throw DDXParseFailed(
"Error parsing DDX response: Could not read from input file.");
1205 xmlParserCtxtPtr context = xmlCreateFileParserCtxt(document.c_str());
1209 (
"Could not initialize the parser with the file: '")
1210 + document +
string(
"'."));
1216 xmlSAXHandler ddx_sax_parser;
1217 memset( &ddx_sax_parser, 0,
sizeof(xmlSAXHandler) );
1228 ddx_sax_parser.initialized = XML_SAX2_MAGIC;
1229 ddx_sax_parser.startElementNs = &DDXParser::ddx_sax2_start_element;
1230 ddx_sax_parser.endElementNs = &DDXParser::ddx_sax2_end_element;
1232 context->sax = &ddx_sax_parser;
1233 context->userData =
this;
1234 context->validate =
false;
1236 xmlParseDocument(context);
1238 cleanup_parse(context);
void set_dataset_name(const string &n)
virtual AttrTable & get_attr_table()
void set_dap_version(const string &version_string="2.0")
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 ...
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.
Holds a structure (aggregate) type.
top level DAP object to house generic methods
Type
Identifies the data type.
bool is_simple_type(Type t)
Returns true if the instance is a numeric, string or URL type variable.
ObjectType get_type(const string &value)
bool is_boundary(const char *line, const string &boundary)