libdap++  Updated for version 3.13.1
DDXParserSAX2.cc
Go to the documentation of this file.
1 
2 // -*- mode: c++; c-basic-offset:4 -*-
3 
4 // This file is part of libdap, A C++ implementation of the OPeNDAP Data
5 // Access Protocol.
6 
7 // Copyright (c) 2003 OPeNDAP, Inc.
8 // Author: James Gallagher <jgallagher@opendap.org>
9 //
10 // This library is free software; you can redistribute it and/or
11 // modify it under the terms of the GNU Lesser General Public
12 // License as published by the Free Software Foundation; either
13 // version 2.1 of the License, or (at your option) any later version.
14 //
15 // This library is distributed in the hope that it will be useful,
16 // but WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 // Lesser General Public License for more details.
19 //
20 // You should have received a copy of the GNU Lesser General Public
21 // License along with this library; if not, write to the Free Software
22 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 //
24 // You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
25 
26 #include "config.h"
27 
28 //#define DODS_DEBUG 1
29 //#define DODS_DEBUG2 1
30 
31 #include <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 #if 0
152 
153 static Type get_type(const char *name)
154 {
155  if (strcmp(name, "Byte") == 0)
156  return dods_byte_c;
157 
158  if (strcmp(name, "Int16") == 0)
159  return dods_int16_c;
160 
161  if (strcmp(name, "UInt16") == 0)
162  return dods_uint16_c;
163 
164  if (strcmp(name, "Int32") == 0)
165  return dods_int32_c;
166 
167  if (strcmp(name, "UInt32") == 0)
168  return dods_uint32_c;
169 
170  if (strcmp(name, "Float32") == 0)
171  return dods_float32_c;
172 
173  if (strcmp(name, "Float64") == 0)
174  return dods_float64_c;
175 
176  if (strcmp(name, "String") == 0)
177  return dods_str_c;
178 
179  if (strcmp(name, "Url") == 0)
180  return dods_url_c;
181 
182  if (strcmp(name, "Array") == 0)
183  return dods_array_c;
184 
185  if (strcmp(name, "Structure") == 0)
186  return dods_structure_c;
187 
188  if (strcmp(name, "Sequence") == 0)
189  return dods_sequence_c;
190 
191  if (strcmp(name, "Grid") == 0)
192  return dods_grid_c;
193 
194  return dods_null_c;
195 }
196 #endif
197 
198 #if 0
199 // Not used. jhrg 1/17/13
200 static Type is_simple_type(const char *name)
201 {
202  Type t = get_type(name);
203  switch (t) {
204  case dods_byte_c:
205  case dods_int16_c:
206  case dods_uint16_c:
207  case dods_int32_c:
208  case dods_uint32_c:
209  case dods_float32_c:
210  case dods_float64_c:
211  case dods_str_c:
212  case dods_url_c:
213  return t;
214  default:
215  return dods_null_c;
216  }
217 }
218 #endif
219 
220 static bool is_not(const char *name, const char *tag)
221 {
222  return strcmp(name, tag) != 0;
223 }
224 
225 void DDXParser::set_state(DDXParser::ParseState state)
226 {
227  s.push(state);
228 }
229 
230 DDXParser::ParseState DDXParser::get_state() const
231 {
232  return s.top();
233 }
234 
235 void DDXParser::pop_state()
236 {
237  s.pop();
238 }
239 
243 void DDXParser::transfer_xml_attrs(const xmlChar **attributes, int nb_attributes)
244 {
245  if (!attribute_table.empty())
246  attribute_table.clear(); // erase old attributes
247 
248  unsigned int index = 0;
249  for (int i = 0; i < nb_attributes; ++i, index += 5) {
250  // Make a value using the attribute name and the prefix, namespace URI
251  // and the value. The prefix might be null.
252  attribute_table.insert(map<string, XMLAttribute>::value_type(
253  string((const char *)attributes[index]),
254  XMLAttribute(attributes + index + 1)));
255 
256  DBG(cerr << "Attribute '" << (const char *)attributes[index] << "': "
257  << attribute_table[(const char *)attributes[index]].value << endl);
258  }
259 }
260 
261 void DDXParser::transfer_xml_ns(const xmlChar **namespaces, int nb_namespaces)
262 {
263  for (int i = 0; i < nb_namespaces; ++i ) {
264  // make a value with the prefix and namespace URI. The prefix might be
265  // null.
266  namespace_table.insert(map<string,string>::value_type(
267  namespaces[i*2] != 0 ? (const char *)namespaces[i*2] : "",
268  (const char *)namespaces[i*2+1]));
269  }
270 }
271 
276 bool DDXParser::check_required_attribute(const string & attr)
277 {
278  map < string, XMLAttribute >::iterator i = attribute_table.find(attr);
279  if (i == attribute_table.end())
280  ddx_fatal_error(this, "Required attribute '%s' not found.",
281  attr.c_str());
282  return true;
283 }
284 
290 bool DDXParser::check_attribute(const string & attr)
291 {
292  return (attribute_table.find(attr) != attribute_table.end());
293 }
294 
303 void DDXParser::process_attribute_element(const xmlChar **attrs, int nb_attributes)
304 {
305  // These methods set the state to parser_error if a problem is found.
306  transfer_xml_attrs(attrs, nb_attributes);
307 
308  bool error = !(check_required_attribute(string("name"))
309  && check_required_attribute(string("type")));
310  if (error)
311  return;
312 
313  if (attribute_table["type"].value == "Container") {
314  set_state(inside_attribute_container);
315 
316  AttrTable *child;
317  AttrTable *parent = at_stack.top();
318 
319  child = parent->append_container(attribute_table["name"].value);
320  at_stack.push(child); // save.
321  DBG2(cerr << "Pushing at" << endl);
322  }
323  else if (attribute_table["type"].value == "OtherXML") {
324  set_state(inside_other_xml_attribute);
325 
326  dods_attr_name = attribute_table["name"].value;
327  dods_attr_type = attribute_table["type"].value;
328  }
329  else {
330  set_state(inside_attribute);
331  // *** Modify parser. Add a special state for inside OtherXML since it
332  // does not use the <value> element.
333 
334  dods_attr_name = attribute_table["name"].value;
335  dods_attr_type = attribute_table["type"].value;
336  }
337 }
338 
342 void DDXParser::process_attribute_alias(const xmlChar **attrs, int nb_attributes)
343 {
344  transfer_xml_attrs(attrs, nb_attributes);
345  if (check_required_attribute(string("name"))
346  && check_required_attribute(string("attribute"))) {
347  set_state(inside_alias);
348  at_stack.top()->attr_alias(attribute_table["name"].value,
349  attribute_table["attribute"].value);
350  }
351 }
352 
360 void DDXParser::process_variable(Type t, ParseState s, const xmlChar **attrs,
361  int nb_attributes)
362 {
363  transfer_xml_attrs(attrs, nb_attributes);
364 
365  set_state(s);
366  if (bt_stack.top()->type() == dods_array_c
367  || check_required_attribute("name")) { // throws on error/false
368  BaseType *btp = factory(t, attribute_table["name"].value);
369  if (!btp)
371  this,
372  "Internal parser error; could not instantiate the variable '%s'.",
373  attribute_table["name"].value.c_str());
374 
375  // Once we make the new variable, we not only load it on to the
376  // BaseType stack, we also load its AttrTable on the AttrTable stack.
377  // The attribute processing software always operates on the AttrTable
378  // at the top of the AttrTable stack (at_stack).
379  bt_stack.push(btp);
380  at_stack.push(&btp->get_attr_table());
381  }
382 }
383 
387 void DDXParser::process_dimension(const xmlChar **attrs, int nb_attributes)
388 {
389  transfer_xml_attrs(attrs, nb_attributes);
390  if (check_required_attribute(string("size"))) {
391  set_state(inside_dimension);
392  Array *ap = dynamic_cast < Array * >(bt_stack.top());
393  if (!ap) {
394  ddx_fatal_error(this, "Parse error: Expected an array variable.");
395  return;
396  }
397 
398  ap->append_dim(atoi(attribute_table["size"].value.c_str()),
399  attribute_table["name"].value);
400  }
401 }
402 
405 void DDXParser::process_blob(const xmlChar **attrs, int nb_attributes)
406 {
407  transfer_xml_attrs(attrs, nb_attributes);
408  if (check_required_attribute(string("href"))) {
409  set_state(inside_blob_href);
410  *blob_href = attribute_table["href"].value;
411  }
412 }
413 
420 inline bool
421 DDXParser::is_attribute_or_alias(const char *name, const xmlChar **attrs,
422  int nb_attributes)
423 {
424  if (strcmp(name, "Attribute") == 0) {
425  process_attribute_element(attrs, nb_attributes);
426  // next state: inside_attribtue or inside_attribute_container
427  return true;
428  }
429  else if (strcmp(name, "Alias") == 0) {
430  process_attribute_alias(attrs, nb_attributes);
431  // next state: inside_alias
432  return true;
433  }
434 
435  return false;
436 }
437 
443 inline bool DDXParser::is_variable(const char *name, const xmlChar **attrs,
444  int nb_attributes)
445 {
446  Type t = get_type(name);
447  //if ((t = is_simple_type(name)) != dods_null_c) {
448  if (is_simple_type(t)) {
449  process_variable(t, inside_simple_type, attrs, nb_attributes);
450  return true;
451  }
452  else if (strcmp(name, "Array") == 0) {
453  process_variable(dods_array_c, inside_array, attrs, nb_attributes);
454  return true;
455  }
456  else if (strcmp(name, "Structure") == 0) {
457  process_variable(dods_structure_c, inside_structure, attrs, nb_attributes);
458  return true;
459  }
460  else if (strcmp(name, "Sequence") == 0) {
461  process_variable(dods_sequence_c, inside_sequence, attrs, nb_attributes);
462  return true;
463  }
464  else if (strcmp(name, "Grid") == 0) {
465  process_variable(dods_grid_c, inside_grid, attrs, nb_attributes);
466  return true;
467  }
468 
469  return false;
470 }
471 
472 void DDXParser::finish_variable(const char *tag, Type t, const char *expected)
473 {
474  if (strcmp(tag, expected) != 0) {
476  "Expected an end tag for a %s; found '%s' instead.",
477  expected, tag);
478  return;
479  }
480 
481  pop_state();
482 
483  BaseType *btp = bt_stack.top();
484 
485  bt_stack.pop();
486  at_stack.pop();
487 
488  if (btp->type() != t) {
490  "Internal error: Expected a %s variable.",
491  expected);
492  return;
493  }
494  // Once libxml2 validates, this can go away. 05/30/03 jhrg
495  if (t == dods_array_c
496  && static_cast<Array*>(btp)->dimensions() == 0) {
498  "No dimension element included in the Array '%s'.",
499  btp->name().c_str());
500  delete btp;
501  return;
502  }
503 
504  BaseType *parent = bt_stack.top();
505 
506  if (!(parent->is_vector_type() || parent->is_constructor_type())) {
508  "Tried to add the array variable '%s' to a non-constructor type (%s %s).",
509  tag,
510  bt_stack.top()->type_name().c_str(),
511  bt_stack.top()->name().c_str());
512  delete btp;
513  return;
514  }
515 
516  parent->add_var(btp);
517  delete btp;
518 }
519 
526 
532 {
533  DDXParser *parser = static_cast<DDXParser*>(p);
534  parser->error_msg = "";
535  parser->char_data = "";
536 
537  // init attr table stack.
538  parser->at_stack.push(&parser->dds->get_attr_table());
539 
540  // Trick; DDS *should* be a child of Structure. To simplify parsing,
541  // stuff a Structure on the bt_stack and dump the top level variables
542  // there. Once we're done, transfer the variables to the DDS.
543  parser->bt_stack.push(new Structure("dummy_dds"));
544 
545  parser->set_state(parser_start);
546 
547  DBG2(cerr << "Parser state: " << states[parser->get_state()] << endl);
548 }
549 
553 {
554  DDXParser *parser = static_cast<DDXParser*>(p);
555  DBG2(cerr << "Ending state == " << states[parser->get_state()] <<
556  endl);
557 
558  if (parser->get_state() != parser_start)
559  DDXParser::ddx_fatal_error(parser, "The document contained unbalanced tags.");
560 
561  // If we've found any sort of error, don't make the DDX; intern() will
562  // take care of the error.
563  if (parser->get_state() == parser_error) {
564  delete parser->bt_stack.top();
565  return;
566  }
567 
568  // Pop the temporary Structure off the stack and transfer its variables
569  // to the DDS.
570  Constructor *cp = dynamic_cast < Constructor * >(parser->bt_stack.top());
571  if (!cp) {
572  ddx_fatal_error(parser, "Parse error: Expected a Structure, Sequence or Grid variable.");
573  delete cp;
574  return;
575  }
576 
577  for (Constructor::Vars_iter i = cp->var_begin(); i != cp->var_end(); ++i) {
578  (*i)->set_parent(0); // top-level vars have no parents
579  parser->dds->add_var(*i);
580  }
581 
582  parser->bt_stack.pop();
583  delete cp;
584 }
585 
587  const xmlChar *l, const xmlChar *prefix, const xmlChar *URI,
588  int nb_namespaces, const xmlChar **namespaces,
589  int nb_attributes, int /*nb_defaulted*/, const xmlChar **attributes)
590 {
591  DDXParser *parser = static_cast<DDXParser*>(p);
592  const char *localname = (const char *)l;
593 
594  DBG2(cerr << "start element: " << localname << ", states: "
595  << states[parser->get_state()]);
596 
597  switch (parser->get_state()) {
598  case parser_start:
599  if (strcmp(localname, "Dataset") == 0) {
600  parser->set_state(inside_dataset);
601  parser->root_ns = URI != 0 ? (const char *)URI: "";
602  parser->transfer_xml_attrs(attributes, nb_attributes);
603 
604  if (parser->check_required_attribute(string("name")))
605  parser->dds->set_dataset_name(parser->attribute_table["name"].value);
606 
607  if (parser->check_attribute("dapVersion"))
608  parser->dds->set_dap_version(parser->attribute_table["dapVersion"].value);
609  }
610  else
612  "Expected response to start with a Dataset element; found '%s' instead.",
613  localname);
614  break;
615 
616  case inside_dataset:
617  if (parser->is_attribute_or_alias(localname, attributes, nb_attributes))
618  break;
619  else if (parser->is_variable(localname, attributes, nb_attributes))
620  break;
621  else if (strcmp(localname, "blob") == 0 || strcmp(localname, "dataBLOB") == 0) {
622  parser->process_blob(attributes, nb_attributes);
623  // next state: inside_data_blob
624  }
625  else
627  "Expected an Attribute, Alias or variable element; found '%s' instead.",
628  localname);
629  break;
630 
631  case inside_attribute_container:
632  if (parser->is_attribute_or_alias(localname, attributes, nb_attributes))
633  break;
634  else
636  "Expected an Attribute or Alias element; found '%s' instead.",
637  localname);
638  break;
639 
640  case inside_attribute:
641  if (parser->is_attribute_or_alias(localname, attributes, nb_attributes))
642  break;
643  else if (strcmp(localname, "value") == 0)
644  parser->set_state(inside_attribute_value);
645  else
646  ddx_fatal_error(parser,
647  "Expected an 'Attribute', 'Alias' or 'value' element; found '%s' instead.",
648  localname);
649  break;
650 
651  case inside_attribute_value:
652  ddx_fatal_error(parser,
653  "Internal parser error; unexpected state, inside value while processing element '%s'.",
654  localname);
655  break;
656 
657  case inside_other_xml_attribute:
658  DBGN(cerr << endl << "\t inside_other_xml_attribute: " << localname << endl);
659 
660  parser->other_xml_depth++;
661 
662  // Accumulate the elements here
663 
664  parser->other_xml.append("<");
665  if (prefix) {
666  parser->other_xml.append((const char *)prefix);
667  parser->other_xml.append(":");
668  }
669  parser->other_xml.append(localname);
670 
671  if (nb_namespaces != 0) {
672  parser->transfer_xml_ns(namespaces, nb_namespaces);
673 
674  for (map<string,string>::iterator i = parser->namespace_table.begin();
675  i != parser->namespace_table.end();
676  ++i) {
677  parser->other_xml.append(" xmlns");
678  if (!i->first.empty()) {
679  parser->other_xml.append(":");
680  parser->other_xml.append(i->first);
681  }
682  parser->other_xml.append("=\"");
683  parser->other_xml.append(i->second);
684  parser->other_xml.append("\"");
685  }
686  }
687 
688  if (nb_attributes != 0) {
689  parser->transfer_xml_attrs(attributes, nb_attributes);
690  for (XMLAttrMap::iterator i = parser->attr_table_begin();
691  i != parser->attr_table_end();
692  ++i) {
693  parser->other_xml.append(" ");
694  if (!i->second.prefix.empty()) {
695  parser->other_xml.append(i->second.prefix);
696  parser->other_xml.append(":");
697  }
698  parser->other_xml.append(i->first);
699  parser->other_xml.append("=\"");
700  parser->other_xml.append(i->second.value);
701  parser->other_xml.append("\"");
702  }
703  }
704 
705  parser->other_xml.append(">");
706  break;
707 
708  case inside_alias:
709  ddx_fatal_error(parser,
710  "Internal parser error; unexpected state, inside alias while processing element '%s'.",
711  localname);
712  break;
713 
714  case inside_simple_type:
715  if (parser->is_attribute_or_alias(localname, attributes, nb_attributes))
716  break;
717  else
718  ddx_fatal_error(parser,
719  "Expected an 'Attribute' or 'Alias' element; found '%s' instead.",
720  localname);
721  break;
722 
723  case inside_array:
724  if (parser->is_attribute_or_alias(localname, attributes, nb_attributes))
725  break;
726  else if (is_not(localname, "Array")
727  && parser->is_variable(localname, attributes, nb_attributes))
728  break;
729  else if (strcmp(localname, "dimension") == 0) {
730  parser->process_dimension(attributes, nb_attributes);
731  // next state: inside_dimension
732  }
733  else
734  ddx_fatal_error(parser,
735  "Expected an 'Attribute' or 'Alias' element; found '%s' instead.",
736  localname);
737  break;
738 
739  case inside_dimension:
740  ddx_fatal_error(parser,
741  "Internal parser error; unexpected state, inside dimension while processing element '%s'.",
742  localname);
743  break;
744 
745  case inside_structure:
746  if (parser->is_attribute_or_alias(localname, attributes, nb_attributes))
747  break;
748  else if (parser->is_variable(localname, attributes, nb_attributes))
749  break;
750  else
752  "Expected an Attribute, Alias or variable element; found '%s' instead.",
753  localname);
754  break;
755 
756  case inside_sequence:
757  if (parser->is_attribute_or_alias(localname, attributes, nb_attributes))
758  break;
759  else if (parser->is_variable(localname, attributes, nb_attributes))
760  break;
761  else
763  "Expected an Attribute, Alias or variable element; found '%s' instead.",
764  localname);
765  break;
766 
767  case inside_grid:
768  if (parser->is_attribute_or_alias(localname, attributes, nb_attributes))
769  break;
770  else if (strcmp(localname, "Array") == 0)
771  parser->process_variable(dods_array_c, inside_array, attributes, nb_attributes);
772  else if (strcmp(localname, "Map") == 0)
773  parser->process_variable(dods_array_c, inside_map, attributes, nb_attributes);
774  else
776  "Expected an Attribute, Alias or variable element; found '%s' instead.",
777  localname);
778  break;
779 
780  case inside_map:
781  if (parser->is_attribute_or_alias(localname, attributes, nb_attributes))
782  break;
783  else if (is_not(localname, "Array") && is_not(localname, "Sequence")
784  && is_not(localname, "Grid")
785  && parser->is_variable(localname, attributes, nb_attributes))
786  break;
787  else if (strcmp(localname, "dimension") == 0) {
788  parser->process_dimension(attributes, nb_attributes);
789  // next state: inside_dimension
790  }
791  else
792  ddx_fatal_error(parser,
793  "Expected an 'Attribute', 'Alias', variable or 'dimension' element; found '%s' instead.",
794  localname);
795  break;
796 
797  case inside_blob_href:
798  ddx_fatal_error(parser,
799  "Internal parser error; unexpected state, inside blob href while processing element '%s'.",
800  localname);
801  break;
802 
803  case parser_unknown:
804  // *** Never used? If so remove/error
805  parser->set_state(parser_unknown);
806  break;
807 
808  case parser_error:
809  break;
810  }
811 
812  DBGN(cerr << " ... " << states[parser->get_state()] << endl);
813 }
814 
815 void DDXParser::ddx_sax2_end_element(void *p, const xmlChar *l,
816  const xmlChar *prefix, const xmlChar *URI)
817 {
818  DDXParser *parser = static_cast<DDXParser*>(p);
819  const char *localname = (const char *)l;
820 
821  DBG2(cerr << "End element " << localname << " (state "
822  << states[parser->get_state()] << ")" << endl);
823 
824  switch (parser->get_state()) {
825  case parser_start:
826  ddx_fatal_error(parser,
827  "Internal parser error; unexpected state, inside start state while processing element '%s'.",
828  localname);
829  break;
830 
831  case inside_dataset:
832  if (strcmp(localname, "Dataset") == 0)
833  parser->pop_state();
834  else
836  "Expected an end Dataset tag; found '%s' instead.",
837  localname);
838  break;
839 
840  case inside_attribute_container:
841  if (strcmp(localname, "Attribute") == 0) {
842  parser->pop_state();
843  parser->at_stack.pop(); // pop when leaving a container.
844  }
845  else
847  "Expected an end Attribute tag; found '%s' instead.",
848  localname);
849  break;
850 
851  case inside_attribute:
852  if (strcmp(localname, "Attribute") == 0)
853  parser->pop_state();
854  else
856  "Expected an end Attribute tag; found '%s' instead.",
857  localname);
858  break;
859 
860  case inside_attribute_value:
861  if (strcmp(localname, "value") == 0) {
862  parser->pop_state();
863  AttrTable *atp = parser->at_stack.top();
864  atp->append_attr(parser->dods_attr_name,
865  parser->dods_attr_type, parser->char_data);
866  parser->char_data = ""; // Null this after use.
867  }
868  else
870  "Expected an end value tag; found '%s' instead.",
871  localname);
872 
873  break;
874 
875  case inside_other_xml_attribute: {
876  if (strcmp(localname, "Attribute") == 0
877  && parser->root_ns == (const char *)URI) {
878 
879  DBGN(cerr << endl << "\t Popping the 'inside_other_xml_attribute' state"
880  << endl);
881 
882  parser->pop_state();
883 
884  AttrTable *atp = parser->at_stack.top();
885  atp->append_attr(parser->dods_attr_name,
886  parser->dods_attr_type, parser->other_xml);
887 
888  parser->other_xml = ""; // Null this after use.
889  }
890  else {
891  DBGN(cerr << endl << "\t inside_other_xml_attribute: " << localname
892  << ", depth: " << parser->other_xml_depth << endl);
893  if (parser->other_xml_depth == 0)
895  "Expected an OtherXML attribute to end! Instead I found '%s'",
896  localname);
897  parser->other_xml_depth--;
898 
899  parser->other_xml.append("</");
900  if (prefix) {
901  parser->other_xml.append((const char *)prefix);
902  parser->other_xml.append(":");
903  }
904  parser->other_xml.append(localname);
905  parser->other_xml.append(">");
906  }
907  break;
908  }
909  // Alias is busted in libdap++ 05/29/03 jhrg
910  case inside_alias:
911  parser->pop_state();
912  break;
913 
914  case inside_simple_type: {
915  Type t = get_type(localname);
916  if (is_simple_type(t)) {
917  parser->pop_state();
918  BaseType *btp = parser->bt_stack.top();
919  parser->bt_stack.pop();
920  parser->at_stack.pop();
921 
922  BaseType *parent = parser->bt_stack.top();
923 
924  if (parent->is_vector_type() || parent->is_constructor_type()) {
925  parent->add_var(btp);
926  delete btp;
927  }
928  else
930  "Tried to add the simple-type variable '%s' to a non-constructor type (%s %s).",
931  localname,
932  parser->bt_stack.top()->
933  type_name().c_str(),
934  parser->bt_stack.top()->name().
935  c_str());
936  }
937  else
939  "Expected an end tag for a simple type; found '%s' instead.",
940  localname);
941  break;
942  }
943 
944  case inside_array:
945  parser->finish_variable(localname, dods_array_c, "Array");
946  break;
947 
948  case inside_dimension:
949  if (strcmp(localname, "dimension") == 0)
950  parser->pop_state();
951  else
953  "Expected an end dimension tag; found '%s' instead.",
954  localname);
955  break;
956 
957  case inside_structure:
958  parser->finish_variable(localname, dods_structure_c, "Structure");
959  break;
960 
961  case inside_sequence:
962  parser->finish_variable(localname, dods_sequence_c, "Sequence");
963  break;
964 
965  case inside_grid:
966  parser->finish_variable(localname, dods_grid_c, "Grid");
967  break;
968 
969  case inside_map:
970  parser->finish_variable(localname, dods_array_c, "Map");
971  break;
972 
973  case inside_blob_href:
974  if (strcmp(localname, "blob") == 0 || strcmp(localname, "dataBLOB") == 0)
975  parser->pop_state();
976  else
978  "Expected an end dataBLOB/blob tag; found '%s' instead.",
979  localname);
980  break;
981 
982  case parser_unknown:
983  parser->pop_state();
984  break;
985 
986  case parser_error:
987  break;
988  }
989 
990 
991  DBGN(cerr << " ... " << states[parser->get_state()] << endl);
992 }
993 
997 void DDXParser::ddx_get_characters(void * p, const xmlChar * ch, int len)
998 {
999  DDXParser *parser = static_cast<DDXParser*>(p);
1000 
1001  switch (parser->get_state()) {
1002  case inside_attribute_value:
1003  parser->char_data.append((const char *)(ch), len);
1004  DBG2(cerr << "Characters: '" << parser->char_data << "'" << endl);
1005  break;
1006 
1007  case inside_other_xml_attribute:
1008  parser->other_xml.append((const char *)(ch), len);
1009  DBG2(cerr << "Other XML Characters: '" << parser->other_xml << "'" << endl);
1010  break;
1011 
1012  default:
1013  break;
1014  }
1015 }
1016 
1021 void DDXParser::ddx_ignoreable_whitespace(void *p, const xmlChar *ch,
1022  int len)
1023 {
1024  DDXParser *parser = static_cast<DDXParser*>(p);
1025 
1026  switch (parser->get_state()) {
1027  case inside_other_xml_attribute:
1028  parser->other_xml.append((const char *)(ch), len);
1029  break;
1030 
1031  default:
1032  break;
1033  }
1034 }
1035 
1041 void DDXParser::ddx_get_cdata(void *p, const xmlChar *value, int len)
1042 {
1043  DDXParser *parser = static_cast<DDXParser*>(p);
1044 
1045  switch (parser->get_state()) {
1046  case inside_other_xml_attribute:
1047  parser->other_xml.append((const char *)(value), len);
1048  break;
1049 
1050  case parser_unknown:
1051  break;
1052 
1053  default:
1055  "Found a CData block but none are allowed by DAP.");
1056 
1057  break;
1058  }
1059 }
1060 
1065 xmlEntityPtr DDXParser::ddx_get_entity(void *, const xmlChar * name)
1066 {
1067  return xmlGetPredefinedEntity(name);
1068 }
1069 
1077 void DDXParser::ddx_fatal_error(void * p, const char *msg, ...)
1078 {
1079  va_list args;
1080  DDXParser *parser = static_cast<DDXParser*>(p);
1081 
1082  parser->set_state(parser_error);
1083 
1084  va_start(args, msg);
1085  char str[1024];
1086  vsnprintf(str, 1024, msg, args);
1087  va_end(args);
1088 
1089  int line = xmlSAX2GetLineNumber(parser->ctxt);
1090 
1091  parser->error_msg += "At line " + long_to_string(line) + ": ";
1092  parser->error_msg += string(str) + string("\n");
1093 }
1094 
1096 
1097 void DDXParser::cleanup_parse(xmlParserCtxtPtr & context) const
1098 {
1099  if (!context->wellFormed) {
1100  context->sax = NULL;
1101  xmlFreeParserCtxt(context);
1102  throw
1103  DDXParseFailed(string
1104  ("\nThe DDX is not a well formed XML document.\n")
1105  + error_msg);
1106  }
1107 
1108  if (!context->valid) {
1109  context->sax = NULL;
1110  xmlFreeParserCtxt(context);
1111  throw DDXParseFailed(string("\nThe DDX is not a valid document.\n")
1112  + error_msg);
1113  }
1114 
1115  if (get_state() == parser_error) {
1116  context->sax = NULL;
1117  xmlFreeParserCtxt(context);
1118  throw DDXParseFailed(string("\nError parsing DDX response.\n") +
1119  error_msg);
1120  }
1121 
1122  context->sax = NULL;
1123  xmlFreeParserCtxt(context);
1124 }
1125 
1133 void DDXParser::intern_stream(istream &in, DDS *dest_dds, string &cid, const string &boundary)
1134 {
1135  // Code example from libxml2 docs re: read from a stream.
1136  if (!in || in.eof())
1137  throw InternalErr(__FILE__, __LINE__, "Input stream not open or read error");
1138 
1139  const int size = 1024;
1140  char chars[size + 1];
1141 
1142  // int res = fread(chars, 1, 4, in);
1143  in.readsome(chars, 4);
1144  int res = in.gcount();
1145  if (res > 0) {
1146  chars[4]='\0';
1147  xmlParserCtxtPtr context = xmlCreatePushParserCtxt(NULL, NULL, chars, res, "stream");
1148 
1149  ctxt = context; // need ctxt for error messages
1150  dds = dest_dds; // dump values here
1151  blob_href = &cid; // cid goes here
1152 
1153  xmlSAXHandler ddx_sax_parser;
1154  memset( &ddx_sax_parser, 0, sizeof(xmlSAXHandler) );
1155 
1156  ddx_sax_parser.getEntity = &DDXParser::ddx_get_entity;
1157  ddx_sax_parser.startDocument = &DDXParser::ddx_start_document;
1158  ddx_sax_parser.endDocument = &DDXParser::ddx_end_document;
1159  ddx_sax_parser.characters = &DDXParser::ddx_get_characters;
1160  ddx_sax_parser.ignorableWhitespace = &DDXParser::ddx_ignoreable_whitespace;
1161  ddx_sax_parser.cdataBlock = &DDXParser::ddx_get_cdata;
1162  ddx_sax_parser.warning = &DDXParser::ddx_fatal_error;
1163  ddx_sax_parser.error = &DDXParser::ddx_fatal_error;
1164  ddx_sax_parser.fatalError = &DDXParser::ddx_fatal_error;
1165  ddx_sax_parser.initialized = XML_SAX2_MAGIC;
1166  ddx_sax_parser.startElementNs = &DDXParser::ddx_sax2_start_element;
1167  ddx_sax_parser.endElementNs = &DDXParser::ddx_sax2_end_element;
1168 
1169  context->sax = &ddx_sax_parser;
1170  context->userData = this;
1171  context->validate = true;
1172 
1173  in.getline(chars, size); // chars has size+1 elements
1174  res = in.gcount();
1175  chars[res-1] = '\n'; // libxml needs the newline; w/o it the parse will fail
1176  chars[res] = '\0';
1177  while (res > 0 && !is_boundary(chars, boundary)) {
1178  DBG(cerr << "line (" << res << "): " << chars << endl);
1179  xmlParseChunk(ctxt, chars, res, 0);
1180 
1181  in.getline(chars, size); // chars has size+1 elements
1182  res = in.gcount();
1183  if (res > 0) {
1184  chars[res-1] = '\n';
1185  chars[res] = '\0';
1186  }
1187  }
1188 
1189  // This call ends the parse: The fourth argument of xmlParseChunk is
1190  // the bool 'terminate.'
1191  xmlParseChunk(ctxt, chars, 0, 1);
1192 
1193  cleanup_parse(context);
1194  }
1195 }
1196 
1199 void DDXParser::intern_stream(FILE *in, DDS *dest_dds, string &cid, const string &boundary)
1200 {
1201  // Code example from libxml2 docs re: read from a stream.
1202  if (!in || feof(in) || ferror(in))
1203  throw InternalErr(__FILE__, __LINE__,
1204  "Input stream not open or read error");
1205 
1206  const int size = 1024;
1207  char chars[size];
1208 
1209  int res = fread(chars, 1, 4, in);
1210  if (res > 0) {
1211  chars[4]='\0';
1212  xmlParserCtxtPtr context =
1213  xmlCreatePushParserCtxt(NULL, NULL, chars, res, "stream");
1214 
1215  ctxt = context; // need ctxt for error messages
1216  dds = dest_dds; // dump values here
1217  blob_href = &cid; // cid goes here
1218 
1219  xmlSAXHandler ddx_sax_parser;
1220  memset( &ddx_sax_parser, 0, sizeof(xmlSAXHandler) );
1221 
1222  ddx_sax_parser.getEntity = &DDXParser::ddx_get_entity;
1223  ddx_sax_parser.startDocument = &DDXParser::ddx_start_document;
1224  ddx_sax_parser.endDocument = &DDXParser::ddx_end_document;
1225  ddx_sax_parser.characters = &DDXParser::ddx_get_characters;
1226  ddx_sax_parser.ignorableWhitespace = &DDXParser::ddx_ignoreable_whitespace;
1227  ddx_sax_parser.cdataBlock = &DDXParser::ddx_get_cdata;
1228  ddx_sax_parser.warning = &DDXParser::ddx_fatal_error;
1229  ddx_sax_parser.error = &DDXParser::ddx_fatal_error;
1230  ddx_sax_parser.fatalError = &DDXParser::ddx_fatal_error;
1231  ddx_sax_parser.initialized = XML_SAX2_MAGIC;
1232  ddx_sax_parser.startElementNs = &DDXParser::ddx_sax2_start_element;
1233  ddx_sax_parser.endElementNs = &DDXParser::ddx_sax2_end_element;
1234 
1235  context->sax = &ddx_sax_parser;
1236  context->userData = this;
1237  context->validate = true;
1238 
1239 
1240  while ((fgets(chars, size, in) > 0) && !is_boundary(chars, boundary)) {
1241  //chars[size-1] = '\0';
1242  DBG(cerr << "line (" << strlen(chars) << "): " << chars << endl);
1243 
1244  xmlParseChunk(ctxt, chars, strlen(chars), 0);
1245  }
1246  // This call ends the parse: The fourth argument of xmlParseChunk is
1247  // the bool 'terminate.'
1248  xmlParseChunk(ctxt, chars, 0, 1);
1249 
1250  cleanup_parse(context);
1251  }
1252 }
1253 
1254 
1266 void DDXParser::intern(const string & document, DDS * dest_dds, string &cid)
1267 {
1268  // Create the context pointer explicitly so that we can store a pointer
1269  // to it in the DDXParser instance. This provides a way to generate our
1270  // own error messages *with* line numbers. The messages are pretty
1271  // meaningless otherwise. This means that we use an interface from the
1272  // 'parser internals' header, and not the 'parser' header. However, this
1273  // interface is also used in one of the documented examples, so it's
1274  // probably pretty stable. 06/02/03 jhrg
1275  xmlParserCtxtPtr context = xmlCreateFileParserCtxt(document.c_str());
1276  if (!context)
1277  throw
1278  DDXParseFailed(string
1279  ("Could not initialize the parser with the file: '")
1280  + document + string("'."));
1281 
1282  dds = dest_dds; // dump values here
1283  blob_href = &cid;
1284  ctxt = context; // need ctxt for error messages
1285 
1286  xmlSAXHandler ddx_sax_parser;
1287  memset( &ddx_sax_parser, 0, sizeof(xmlSAXHandler) );
1288 
1289  ddx_sax_parser.getEntity = &DDXParser::ddx_get_entity;
1290  ddx_sax_parser.startDocument = &DDXParser::ddx_start_document;
1291  ddx_sax_parser.endDocument = &DDXParser::ddx_end_document;
1292  ddx_sax_parser.characters = &DDXParser::ddx_get_characters;
1293  ddx_sax_parser.ignorableWhitespace = &DDXParser::ddx_ignoreable_whitespace;
1294  ddx_sax_parser.cdataBlock = &DDXParser::ddx_get_cdata;
1295  ddx_sax_parser.warning = &DDXParser::ddx_fatal_error;
1296  ddx_sax_parser.error = &DDXParser::ddx_fatal_error;
1297  ddx_sax_parser.fatalError = &DDXParser::ddx_fatal_error;
1298  ddx_sax_parser.initialized = XML_SAX2_MAGIC;
1299  ddx_sax_parser.startElementNs = &DDXParser::ddx_sax2_start_element;
1300  ddx_sax_parser.endElementNs = &DDXParser::ddx_sax2_end_element;
1301 
1302  context->sax = &ddx_sax_parser;
1303  context->userData = this;
1304  context->validate = false;
1305 
1306  xmlParseDocument(context);
1307 
1308  cleanup_parse(context);
1309 }
1310 
1311 } // 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:150
virtual Structure * NewStructure(const string &n="") const
virtual Sequence * NewSequence(const string &n="") const
std::vector< BaseType * >::iterator Vars_iter
Definition: Constructor.h:56
#define DBGN(x)
Definition: debug.h:59
virtual Byte * NewByte(const string &n="") const
virtual Str * NewStr(const string &n="") const
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:85
virtual void add_var(BaseType *bt, Part part=nil)
Add a variable.
Definition: BaseType.cc:852
Type
Identifies the data type.
Definition: BaseType.h:137
virtual Url * NewUrl(const string &n="") const
#define DBG2(x)
Definition: debug.h:73
virtual void set_parent(BaseType *parent)
Definition: BaseType.cc:770
A class for software fault reporting.
Definition: InternalErr.h:64
virtual bool is_vector_type()
Returns true if the instance is a vector (i.e., array) type variable.
Definition: BaseType.cc:402
virtual bool is_constructor_type()
Returns true if the instance is a constructor (i.e., Structure, Sequence or Grid) type variable...
Definition: BaseType.cc:449
#define DBG(x)
Definition: debug.h:58
static void ddx_fatal_error(void *parser, const char *msg,...)
ObjectType get_type(const string &value)
Definition: mime_util.cc:308
virtual Float32 * NewFloat32(const string &n="") const
virtual Float64 * NewFloat64(const string &n="") const
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:580
virtual AttrTable & get_attr_table()
Definition: DDS.cc:342
static void ddx_sax2_end_element(void *parser, const xmlChar *localname, const xmlChar *prefix, const xmlChar *URI)
static void ddx_sax2_start_element(void *parser, const xmlChar *localname, const xmlChar *prefix, const xmlChar *URI, int nb_namespaces, const xmlChar **namespaces, int nb_attributes, int nb_defaulted, const xmlChar **attributes)
virtual Array * NewArray(const string &n="", BaseType *v=0) const
string long_to_string(long val, int base)
Definition: util.cc:773
bool is_boundary(const char *line, const string &boundary)
Definition: mime_util.cc:926
virtual Grid * NewGrid(const string &n="") const
virtual Int16 * NewInt16(const string &n="") const
virtual unsigned int append_attr(const string &name, const string &type, const string &value)
Add an attribute to the table.
Definition: AttrTable.cc:311
void set_dataset_name(const string &n)
Definition: DDS.cc:333
The basic data type for the DODS DAP types.
Definition: BaseType.h:199
virtual Int32 * NewInt32(const string &n="") const
Vars_iter var_begin()
Definition: Constructor.cc:270
virtual UInt16 * NewUInt16(const string &n="") const
static void ddx_ignoreable_whitespace(void *parser, const xmlChar *ch, int len)
Vars_iter var_end()
Definition: Constructor.cc:278
void intern(const string &document, DDS *dest_dds, string &cid)
string type_name(Type t)
Returns the type of the class instance as a string.
Definition: util.cc:522
virtual UInt32 * NewUInt32(const string &n="") const
void set_dap_version(const string &version_string="2.0")
Definition: DDS.cc:410
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:556