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