libdap  Updated for version 3.20.2
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 
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 
55 namespace libdap {
56 
57 #if defined(DODS_DEBUG) || defined(DODS_DEUG2)
58 static 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 
91 BaseType *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 
151 static bool is_not(const char *name, const char *tag)
152 {
153  return strcmp(name, tag) != 0;
154 }
155 
156 void DDXParser::set_state(DDXParser::ParseState state)
157 {
158  s.push(state);
159 }
160 
161 DDXParser::ParseState DDXParser::get_state() const
162 {
163  return s.top();
164 }
165 
166 void DDXParser::pop_state()
167 {
168  s.pop();
169 }
170 
174 void 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 
192 void 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 
207 bool 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 
221 bool DDXParser::check_attribute(const string & attr)
222 {
223  return (attribute_table.find(attr) != attribute_table.end());
224 }
225 
234 void 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 
273 void 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 
291 void 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 
320 void 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 
338 void 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 
353 inline bool
354 DDXParser::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 
376 inline 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 
405 void 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 
519 void 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 
748 void 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 
933 void 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 
957 void 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 
977 void 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 
1001 xmlEntityPtr DDXParser::ddx_get_entity(void *, const xmlChar * name)
1002 {
1003  return xmlGetPredefinedEntity(name);
1004 }
1005 
1013 void 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 
1033 void 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 
1068 void 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 
1140 void 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 
1209 void 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
void intern_stream(FILE *in, DDS *dds, string &cid, const string &boundary="")
Read the DDX from a stream instead of a file.
Contains the attributes for a dataset.
Definition: AttrTable.h:142
static void ddx_start_document(void *parser)
static void ddx_get_cdata(void *parser, const xmlChar *value, int len)
Holds a structure (aggregate) type.
Definition: Structure.h:83
virtual void add_var(BaseType *bt, Part part=nil)
Add a variable.
Definition: BaseType.cc:807
Type
Identifies the data type.
Definition: Type.h:94
top level DAP object to house generic methods
Definition: AlarmHandler.h:35
A class for software fault reporting.
Definition: InternalErr.h:64
virtual bool is_vector_type() const
Returns true if the instance is a vector (i.e., array) type variable.
Definition: BaseType.cc:394
static void ddx_fatal_error(void *parser, const char *msg,...)
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:404
ObjectType get_type(const string &value)
Definition: mime_util.cc:326
static void ddx_end_document(void *parser)
static xmlEntityPtr ddx_get_entity(void *parser, const xmlChar *name)
static void ddx_get_characters(void *parser, const xmlChar *ch, int len)
bool is_simple_type(Type t)
Returns true if the instance is a numeric, string or URL type variable.
Definition: util.cc:775
virtual AttrTable & get_attr_table()
Definition: DDS.cc:372
bool is_boundary(const char *line, const string &boundary)
Definition: mime_util.cc:941
virtual unsigned int append_attr(const string &name, const string &type, const string &value)
Add an attribute to the table.
Definition: AttrTable.cc:307
void set_dataset_name(const string &n)
Definition: DDS.cc:363
The basic data type for the DODS DAP types.
Definition: BaseType.h:117
Vars_iter var_begin()
Definition: Constructor.cc:356
static void ddx_ignoreable_whitespace(void *parser, const xmlChar *ch, int len)
Vars_iter var_end()
Definition: Constructor.cc:364
void intern(const string &document, DDS *dest_dds, string &cid)
string type_name(Type t)
Definition: util.cc:760
void set_dap_version(const string &version_string="2.0")
Definition: DDS.cc:440
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:586