libdap++  Updated for version 3.11.7
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(); ++i) {
566  (*i)->set_parent(0); // top-level vars have no parents
567  parser->dds->add_var(*i);
568  }
569 
570  parser->bt_stack.pop();
571  delete cp;
572 }
573 
575  const xmlChar *l, const xmlChar *prefix, const xmlChar *URI,
576  int nb_namespaces, const xmlChar **namespaces,
577  int nb_attributes, int /*nb_defaulted*/, const xmlChar **attributes)
578 {
579  DDXParser *parser = static_cast<DDXParser*>(p);
580  const char *localname = (const char *)l;
581 
582  DBG2(cerr << "start element: " << localname << ", states: "
583  << states[parser->get_state()]);
584 
585  switch (parser->get_state()) {
586  case parser_start:
587  if (strcmp(localname, "Dataset") == 0) {
588  parser->set_state(inside_dataset);
589  parser->root_ns = URI != 0 ? (const char *)URI: "";
590  parser->transfer_xml_attrs(attributes, nb_attributes);
591 
592  if (parser->check_required_attribute(string("name")))
593  parser->dds->set_dataset_name(parser->attribute_table["name"].value);
594 
595  if (parser->check_attribute("dapVersion"))
596  parser->dds->set_dap_version(parser->attribute_table["dapVersion"].value);
597  }
598  else
600  "Expected response to start with a Dataset element; found '%s' instead.",
601  localname);
602  break;
603 
604  case inside_dataset:
605  if (parser->is_attribute_or_alias(localname, attributes, nb_attributes))
606  break;
607  else if (parser->is_variable(localname, attributes, nb_attributes))
608  break;
609  else if (strcmp(localname, "blob") == 0 || strcmp(localname, "dataBLOB") == 0) {
610  parser->process_blob(attributes, nb_attributes);
611  // next state: inside_data_blob
612  }
613  else
615  "Expected an Attribute, Alias or variable element; found '%s' instead.",
616  localname);
617  break;
618 
619  case inside_attribute_container:
620  if (parser->is_attribute_or_alias(localname, attributes, nb_attributes))
621  break;
622  else
624  "Expected an Attribute or Alias element; found '%s' instead.",
625  localname);
626  break;
627 
628  case inside_attribute:
629  if (parser->is_attribute_or_alias(localname, attributes, nb_attributes))
630  break;
631  else if (strcmp(localname, "value") == 0)
632  parser->set_state(inside_attribute_value);
633  else
634  ddx_fatal_error(parser,
635  "Expected an 'Attribute', 'Alias' or 'value' element; found '%s' instead.",
636  localname);
637  break;
638 
639  case inside_attribute_value:
640  ddx_fatal_error(parser,
641  "Internal parser error; unexpected state, inside value while processing element '%s'.",
642  localname);
643  break;
644 
645  case inside_other_xml_attribute:
646  DBGN(cerr << endl << "\t inside_other_xml_attribute: " << localname << endl);
647 
648  parser->other_xml_depth++;
649 
650  // Accumulate the elements here
651 
652  parser->other_xml.append("<");
653  if (prefix) {
654  parser->other_xml.append((const char *)prefix);
655  parser->other_xml.append(":");
656  }
657  parser->other_xml.append(localname);
658 
659  if (nb_namespaces != 0) {
660  parser->transfer_xml_ns(namespaces, nb_namespaces);
661 
662  for (map<string,string>::iterator i = parser->namespace_table.begin();
663  i != parser->namespace_table.end();
664  ++i) {
665  parser->other_xml.append(" xmlns");
666  if (!i->first.empty()) {
667  parser->other_xml.append(":");
668  parser->other_xml.append(i->first);
669  }
670  parser->other_xml.append("=\"");
671  parser->other_xml.append(i->second);
672  parser->other_xml.append("\"");
673  }
674  }
675 
676  if (nb_attributes != 0) {
677  parser->transfer_xml_attrs(attributes, nb_attributes);
678  for (XMLAttrMap::iterator i = parser->attr_table_begin();
679  i != parser->attr_table_end();
680  ++i) {
681  parser->other_xml.append(" ");
682  if (!i->second.prefix.empty()) {
683  parser->other_xml.append(i->second.prefix);
684  parser->other_xml.append(":");
685  }
686  parser->other_xml.append(i->first);
687  parser->other_xml.append("=\"");
688  parser->other_xml.append(i->second.value);
689  parser->other_xml.append("\"");
690  }
691  }
692 
693  parser->other_xml.append(">");
694  break;
695 
696  case inside_alias:
697  ddx_fatal_error(parser,
698  "Internal parser error; unexpected state, inside alias while processing element '%s'.",
699  localname);
700  break;
701 
702  case inside_simple_type:
703  if (parser->is_attribute_or_alias(localname, attributes, nb_attributes))
704  break;
705  else
706  ddx_fatal_error(parser,
707  "Expected an 'Attribute' or 'Alias' element; found '%s' instead.",
708  localname);
709  break;
710 
711  case inside_array:
712  if (parser->is_attribute_or_alias(localname, attributes, nb_attributes))
713  break;
714  else if (is_not(localname, "Array")
715  && parser->is_variable(localname, attributes, nb_attributes))
716  break;
717  else if (strcmp(localname, "dimension") == 0) {
718  parser->process_dimension(attributes, nb_attributes);
719  // next state: inside_dimension
720  }
721  else
722  ddx_fatal_error(parser,
723  "Expected an 'Attribute' or 'Alias' element; found '%s' instead.",
724  localname);
725  break;
726 
727  case inside_dimension:
728  ddx_fatal_error(parser,
729  "Internal parser error; unexpected state, inside dimension while processing element '%s'.",
730  localname);
731  break;
732 
733  case inside_structure:
734  if (parser->is_attribute_or_alias(localname, attributes, nb_attributes))
735  break;
736  else if (parser->is_variable(localname, attributes, nb_attributes))
737  break;
738  else
740  "Expected an Attribute, Alias or variable element; found '%s' instead.",
741  localname);
742  break;
743 
744  case inside_sequence:
745  if (parser->is_attribute_or_alias(localname, attributes, nb_attributes))
746  break;
747  else if (parser->is_variable(localname, attributes, nb_attributes))
748  break;
749  else
751  "Expected an Attribute, Alias or variable element; found '%s' instead.",
752  localname);
753  break;
754 
755  case inside_grid:
756  if (parser->is_attribute_or_alias(localname, attributes, nb_attributes))
757  break;
758  else if (strcmp(localname, "Array") == 0)
759  parser->process_variable(dods_array_c, inside_array, attributes, nb_attributes);
760  else if (strcmp(localname, "Map") == 0)
761  parser->process_variable(dods_array_c, inside_map, attributes, nb_attributes);
762  else
764  "Expected an Attribute, Alias or variable element; found '%s' instead.",
765  localname);
766  break;
767 
768  case inside_map:
769  if (parser->is_attribute_or_alias(localname, attributes, nb_attributes))
770  break;
771  else if (is_not(localname, "Array") && is_not(localname, "Sequence")
772  && is_not(localname, "Grid")
773  && parser->is_variable(localname, attributes, nb_attributes))
774  break;
775  else if (strcmp(localname, "dimension") == 0) {
776  parser->process_dimension(attributes, nb_attributes);
777  // next state: inside_dimension
778  }
779  else
780  ddx_fatal_error(parser,
781  "Expected an 'Attribute', 'Alias', variable or 'dimension' element; found '%s' instead.",
782  localname);
783  break;
784 
785  case inside_blob_href:
786  ddx_fatal_error(parser,
787  "Internal parser error; unexpected state, inside blob href while processing element '%s'.",
788  localname);
789  break;
790 
791  case parser_unknown:
792  // *** Never used? If so remove/error
793  parser->set_state(parser_unknown);
794  break;
795 
796  case parser_error:
797  break;
798  }
799 
800  DBGN(cerr << " ... " << states[parser->get_state()] << endl);
801 }
802 
803 void DDXParser::ddx_sax2_end_element(void *p, const xmlChar *l,
804  const xmlChar *prefix, const xmlChar *URI)
805 {
806  DDXParser *parser = static_cast<DDXParser*>(p);
807  const char *localname = (const char *)l;
808 
809  DBG2(cerr << "End element " << localname << " (state "
810  << states[parser->get_state()] << ")" << endl);
811 
812  switch (parser->get_state()) {
813  case parser_start:
814  ddx_fatal_error(parser,
815  "Internal parser error; unexpected state, inside start state while processing element '%s'.",
816  localname);
817  break;
818 
819  case inside_dataset:
820  if (strcmp(localname, "Dataset") == 0)
821  parser->pop_state();
822  else
824  "Expected an end Dataset tag; found '%s' instead.",
825  localname);
826  break;
827 
828  case inside_attribute_container:
829  if (strcmp(localname, "Attribute") == 0) {
830  parser->pop_state();
831  parser->at_stack.pop(); // pop when leaving a container.
832  }
833  else
835  "Expected an end Attribute tag; found '%s' instead.",
836  localname);
837  break;
838 
839  case inside_attribute:
840  if (strcmp(localname, "Attribute") == 0)
841  parser->pop_state();
842  else
844  "Expected an end Attribute tag; found '%s' instead.",
845  localname);
846  break;
847 
848  case inside_attribute_value:
849  if (strcmp(localname, "value") == 0) {
850  parser->pop_state();
851  AttrTable *atp = parser->at_stack.top();
852  atp->append_attr(parser->dods_attr_name,
853  parser->dods_attr_type, parser->char_data);
854  parser->char_data = ""; // Null this after use.
855  }
856  else
858  "Expected an end value tag; found '%s' instead.",
859  localname);
860 
861  break;
862 
863  case inside_other_xml_attribute: {
864  if (strcmp(localname, "Attribute") == 0
865  && parser->root_ns == (const char *)URI) {
866 
867  DBGN(cerr << endl << "\t Popping the 'inside_other_xml_attribute' state"
868  << endl);
869 
870  parser->pop_state();
871 
872  AttrTable *atp = parser->at_stack.top();
873  atp->append_attr(parser->dods_attr_name,
874  parser->dods_attr_type, parser->other_xml);
875 
876  parser->other_xml = ""; // Null this after use.
877  }
878  else {
879  DBGN(cerr << endl << "\t inside_other_xml_attribute: " << localname
880  << ", depth: " << parser->other_xml_depth << endl);
881  if (parser->other_xml_depth == 0)
883  "Expected an OtherXML attribute to end! Instead I found '%s'",
884  localname);
885  parser->other_xml_depth--;
886 
887  parser->other_xml.append("</");
888  if (prefix) {
889  parser->other_xml.append((const char *)prefix);
890  parser->other_xml.append(":");
891  }
892  parser->other_xml.append(localname);
893  parser->other_xml.append(">");
894  }
895  break;
896  }
897  // Alias is busted in libdap++ 05/29/03 jhrg
898  case inside_alias:
899  parser->pop_state();
900  break;
901 
902  case inside_simple_type:
903  if (is_simple_type(localname) != dods_null_c) {
904  parser->pop_state();
905  BaseType *btp = parser->bt_stack.top();
906  parser->bt_stack.pop();
907  parser->at_stack.pop();
908 
909  BaseType *parent = parser->bt_stack.top();
910 
911  if (parent->is_vector_type() || parent->is_constructor_type())
912  parent->add_var(btp);
913  else
915  "Tried to add the simple-type variable '%s' to a non-constructor type (%s %s).",
916  localname,
917  parser->bt_stack.top()->
918  type_name().c_str(),
919  parser->bt_stack.top()->name().
920  c_str());
921  }
922  else
924  "Expected an end tag for a simple type; found '%s' instead.",
925  localname);
926  break;
927 
928  case inside_array:
929  parser->finish_variable(localname, dods_array_c, "Array");
930  break;
931 
932  case inside_dimension:
933  if (strcmp(localname, "dimension") == 0)
934  parser->pop_state();
935  else
937  "Expected an end dimension tag; found '%s' instead.",
938  localname);
939  break;
940 
941  case inside_structure:
942  parser->finish_variable(localname, dods_structure_c, "Structure");
943  break;
944 
945  case inside_sequence:
946  parser->finish_variable(localname, dods_sequence_c, "Sequence");
947  break;
948 
949  case inside_grid:
950  parser->finish_variable(localname, dods_grid_c, "Grid");
951  break;
952 
953  case inside_map:
954  parser->finish_variable(localname, dods_array_c, "Map");
955  break;
956 
957  case inside_blob_href:
958  if (strcmp(localname, "blob") == 0 || strcmp(localname, "dataBLOB") == 0)
959  parser->pop_state();
960  else
962  "Expected an end dataBLOB/blob tag; found '%s' instead.",
963  localname);
964  break;
965 
966  case parser_unknown:
967  parser->pop_state();
968  break;
969 
970  case parser_error:
971  break;
972  }
973 
974 
975  DBGN(cerr << " ... " << states[parser->get_state()] << endl);
976 }
977 
981 void DDXParser::ddx_get_characters(void * p, const xmlChar * ch, int len)
982 {
983  DDXParser *parser = static_cast<DDXParser*>(p);
984 
985  switch (parser->get_state()) {
986  case inside_attribute_value:
987  parser->char_data.append((const char *)(ch), len);
988  DBG2(cerr << "Characters: '" << parser->char_data << "'" << endl);
989  break;
990 
991  case inside_other_xml_attribute:
992  parser->other_xml.append((const char *)(ch), len);
993  DBG2(cerr << "Other XML Characters: '" << parser->other_xml << "'" << endl);
994  break;
995 
996  default:
997  break;
998  }
999 }
1000 
1005 void DDXParser::ddx_ignoreable_whitespace(void *p, const xmlChar *ch,
1006  int len)
1007 {
1008  DDXParser *parser = static_cast<DDXParser*>(p);
1009 
1010  switch (parser->get_state()) {
1011  case inside_other_xml_attribute:
1012  parser->other_xml.append((const char *)(ch), len);
1013  break;
1014 
1015  default:
1016  break;
1017  }
1018 }
1019 
1025 void DDXParser::ddx_get_cdata(void *p, const xmlChar *value, int len)
1026 {
1027  DDXParser *parser = static_cast<DDXParser*>(p);
1028 
1029  switch (parser->get_state()) {
1030  case inside_other_xml_attribute:
1031  parser->other_xml.append((const char *)(value), len);
1032  break;
1033 
1034  case parser_unknown:
1035  break;
1036 
1037  default:
1039  "Found a CData block but none are allowed by DAP.");
1040 
1041  break;
1042  }
1043 }
1044 
1049 xmlEntityPtr DDXParser::ddx_get_entity(void *, const xmlChar * name)
1050 {
1051  return xmlGetPredefinedEntity(name);
1052 }
1053 
1061 void DDXParser::ddx_fatal_error(void * p, const char *msg, ...)
1062 {
1063  va_list args;
1064  DDXParser *parser = static_cast<DDXParser*>(p);
1065 
1066  parser->set_state(parser_error);
1067 
1068  va_start(args, msg);
1069  char str[1024];
1070  vsnprintf(str, 1024, msg, args);
1071  va_end(args);
1072 
1073  int line = xmlSAX2GetLineNumber(parser->ctxt);
1074 
1075  parser->error_msg += "At line " + long_to_string(line) + ": ";
1076  parser->error_msg += string(str) + string("\n");
1077 }
1078 
1080 
1081 void DDXParser::cleanup_parse(xmlParserCtxtPtr & context) const
1082 {
1083  if (!context->wellFormed) {
1084  context->sax = NULL;
1085  xmlFreeParserCtxt(context);
1086  throw
1087  DDXParseFailed(string
1088  ("\nThe DDX is not a well formed XML document.\n")
1089  + error_msg);
1090  }
1091 
1092  if (!context->valid) {
1093  context->sax = NULL;
1094  xmlFreeParserCtxt(context);
1095  throw DDXParseFailed(string("\nThe DDX is not a valid document.\n")
1096  + error_msg);
1097  }
1098 
1099  if (get_state() == parser_error) {
1100  context->sax = NULL;
1101  xmlFreeParserCtxt(context);
1102  throw DDXParseFailed(string("\nError parsing DDX response.\n") +
1103  error_msg);
1104  }
1105 
1106  context->sax = NULL;
1107  xmlFreeParserCtxt(context);
1108 }
1109 
1112 void DDXParser::intern_stream(FILE *in, DDS *dest_dds, string &cid,
1113  const string &boundary)
1114 {
1115  // Code example from libxml2 docs re: read from a stream.
1116 
1117  if (!in || feof(in) || ferror(in))
1118  throw InternalErr(__FILE__, __LINE__,
1119  "Input stream not open or read error");
1120 
1121  const int size = 1024;
1122  char chars[size];
1123 
1124  int res = fread(chars, 1, 4, in);
1125  if (res > 0) {
1126  chars[4]='\0';
1127  xmlParserCtxtPtr context =
1128  xmlCreatePushParserCtxt(NULL, NULL, chars, res, "stream");
1129 
1130  ctxt = context; // need ctxt for error messages
1131  dds = dest_dds; // dump values here
1132  blob_href = &cid; // cid goes here
1133 
1134  xmlSAXHandler ddx_sax_parser;
1135  memset( &ddx_sax_parser, 0, sizeof(xmlSAXHandler) );
1136 
1137  ddx_sax_parser.getEntity = &DDXParser::ddx_get_entity;
1138  ddx_sax_parser.startDocument = &DDXParser::ddx_start_document;
1139  ddx_sax_parser.endDocument = &DDXParser::ddx_end_document;
1140  ddx_sax_parser.characters = &DDXParser::ddx_get_characters;
1141  ddx_sax_parser.ignorableWhitespace = &DDXParser::ddx_ignoreable_whitespace;
1142  ddx_sax_parser.cdataBlock = &DDXParser::ddx_get_cdata;
1143  ddx_sax_parser.warning = &DDXParser::ddx_fatal_error;
1144  ddx_sax_parser.error = &DDXParser::ddx_fatal_error;
1145  ddx_sax_parser.fatalError = &DDXParser::ddx_fatal_error;
1146  ddx_sax_parser.initialized = XML_SAX2_MAGIC;
1147  ddx_sax_parser.startElementNs = &DDXParser::ddx_sax2_start_element;
1148  ddx_sax_parser.endElementNs = &DDXParser::ddx_sax2_end_element;
1149 
1150  context->sax = &ddx_sax_parser;
1151  context->userData = this;
1152  context->validate = true;
1153 
1154 
1155  while ((fgets(chars, size, in) > 0) && !is_boundary(chars, boundary)) {
1156  chars[size-1] = '\0';
1157  DBG(cerr << "line: " << chars << endl);
1158  xmlParseChunk(ctxt, chars, strlen(chars), 0);
1159  }
1160  // This call ends the parse: The fourth argument of xmlParseChunk is
1161  // the bool 'terminate.'
1162  xmlParseChunk(ctxt, chars, 0, 1);
1163 
1164  cleanup_parse(context);
1165  }
1166 }
1167 
1168 
1180 void DDXParser::intern(const string & document, DDS * dest_dds, string &cid)
1181 {
1182  // Create the context pointer explicitly so that we can store a pointer
1183  // to it in the DDXParser instance. This provides a way to generate our
1184  // own error messages *with* line numbers. The messages are pretty
1185  // meaningless otherwise. This means that we use an interface from the
1186  // 'parser internals' header, and not the 'parser' header. However, this
1187  // interface is also used in one of the documented examples, so it's
1188  // probably pretty stable. 06/02/03 jhrg
1189  xmlParserCtxtPtr context = xmlCreateFileParserCtxt(document.c_str());
1190  if (!context)
1191  throw
1192  DDXParseFailed(string
1193  ("Could not initialize the parser with the file: '")
1194  + document + string("'."));
1195 
1196  dds = dest_dds; // dump values here
1197  blob_href = &cid;
1198  ctxt = context; // need ctxt for error messages
1199 
1200  xmlSAXHandler ddx_sax_parser;
1201  memset( &ddx_sax_parser, 0, sizeof(xmlSAXHandler) );
1202 
1203  ddx_sax_parser.getEntity = &DDXParser::ddx_get_entity;
1204  ddx_sax_parser.startDocument = &DDXParser::ddx_start_document;
1205  ddx_sax_parser.endDocument = &DDXParser::ddx_end_document;
1206  ddx_sax_parser.characters = &DDXParser::ddx_get_characters;
1207  ddx_sax_parser.ignorableWhitespace = &DDXParser::ddx_ignoreable_whitespace;
1208  ddx_sax_parser.cdataBlock = &DDXParser::ddx_get_cdata;
1209  ddx_sax_parser.warning = &DDXParser::ddx_fatal_error;
1210  ddx_sax_parser.error = &DDXParser::ddx_fatal_error;
1211  ddx_sax_parser.fatalError = &DDXParser::ddx_fatal_error;
1212  ddx_sax_parser.initialized = XML_SAX2_MAGIC;
1213  ddx_sax_parser.startElementNs = &DDXParser::ddx_sax2_start_element;
1214  ddx_sax_parser.endElementNs = &DDXParser::ddx_sax2_end_element;
1215 
1216  context->sax = &ddx_sax_parser;
1217  context->userData = this;
1218  context->validate = false;
1219 
1220  xmlParseDocument(context);
1221 
1222  cleanup_parse(context);
1223 }
1224 
1225 } // namespace libdap