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 
390  ap->append_dim(atoi(attribute_table["size"].value.c_str()),
391  attribute_table["name"].value);
392  }
393 }
394 
397 void DDXParser::process_blob(const xmlChar **attrs, int nb_attributes)
398 {
399  transfer_xml_attrs(attrs, nb_attributes);
400  if (check_required_attribute(string("href"))) {
401  set_state(inside_blob_href);
402  *blob_href = attribute_table["href"].value;
403  }
404 }
405 
412 inline bool
413 DDXParser::is_attribute_or_alias(const char *name, const xmlChar **attrs,
414  int nb_attributes)
415 {
416  if (strcmp(name, "Attribute") == 0) {
417  process_attribute_element(attrs, nb_attributes);
418  // next state: inside_attribtue or inside_attribute_container
419  return true;
420  }
421  else if (strcmp(name, "Alias") == 0) {
422  process_attribute_alias(attrs, nb_attributes);
423  // next state: inside_alias
424  return true;
425  }
426 
427  return false;
428 }
429 
435 inline bool DDXParser::is_variable(const char *name, const xmlChar **attrs,
436  int nb_attributes)
437 {
438  Type t;
439  if ((t = is_simple_type(name)) != dods_null_c) {
440  process_variable(t, inside_simple_type, attrs, nb_attributes);
441  return true;
442  }
443  else if (strcmp(name, "Array") == 0) {
444  process_variable(dods_array_c, inside_array, attrs, nb_attributes);
445  return true;
446  }
447  else if (strcmp(name, "Structure") == 0) {
448  process_variable(dods_structure_c, inside_structure, attrs, nb_attributes);
449  return true;
450  }
451  else if (strcmp(name, "Sequence") == 0) {
452  process_variable(dods_sequence_c, inside_sequence, attrs, nb_attributes);
453  return true;
454  }
455  else if (strcmp(name, "Grid") == 0) {
456  process_variable(dods_grid_c, inside_grid, attrs, nb_attributes);
457  return true;
458  }
459 
460  return false;
461 }
462 
463 void DDXParser::finish_variable(const char *tag, Type t, const char *expected)
464 {
465  if (strcmp(tag, expected) != 0) {
467  "Expected an end tag for a %s; found '%s' instead.",
468  expected, tag);
469  return;
470  }
471 
472  pop_state();
473 
474  BaseType *btp = bt_stack.top();
475 
476  bt_stack.pop();
477  at_stack.pop();
478 
479  if (btp->type() != t) {
481  "Internal error: Expected a %s variable.",
482  expected);
483  return;
484  }
485  // Once libxml2 validates, this can go away. 05/30/03 jhrg
486  if (t == dods_array_c
487  && dynamic_cast < Array * >(btp)->dimensions() == 0) {
489  "No dimension element included in the Array '%s'.",
490  btp->name().c_str());
491  return;
492  }
493 
494  BaseType *parent = bt_stack.top();
495 
496  if (!(parent->is_vector_type() || parent->is_constructor_type())) {
498  "Tried to add the array variable '%s' to a non-constructor type (%s %s).",
499  tag,
500  bt_stack.top()->type_name().c_str(),
501  bt_stack.top()->name().c_str());
502  return;
503  }
504 
505  parent->add_var(btp);
506 }
507 
514 
520 {
521  DDXParser *parser = static_cast<DDXParser*>(p);
522  parser->error_msg = "";
523  parser->char_data = "";
524 
525  // init attr table stack.
526  parser->at_stack.push(&parser->dds->get_attr_table());
527 
528  // Trick; DDS *should* be a child of Structure. To simplify parsing,
529  // stuff a Structure on the bt_stack and dump the top level variables
530  // there. Once we're done, transfer the variables to the DDS.
531  parser->bt_stack.push(new Structure("dummy_dds"));
532 
533  parser->set_state(parser_start);
534 
535  DBG2(cerr << "Parser state: " << states[parser->get_state()] << endl);
536 }
537 
541 {
542  DDXParser *parser = static_cast<DDXParser*>(p);
543  DBG2(cerr << "Ending state == " << states[parser->get_state()] <<
544  endl);
545 
546  if (parser->get_state() != parser_start)
548  "The document contained unbalanced tags.");
549 
550  // If we've found any sort of error, don't make the DDX; intern() will
551  // take care of the error.
552  if (parser->get_state() == parser_error)
553  return;
554 
555  // Pop the temporary Structure off the stack and transfer its variables
556  // to the DDS.
557  Constructor *cp = dynamic_cast < Constructor * >(parser->bt_stack.top());
558  if (!cp)
559  ddx_fatal_error(parser, "Parse error: Expected a Structure, Sequence or Grid variable.");
560 
561  for (Constructor::Vars_iter i = cp->var_begin(); i != cp->var_end();
562  ++i)
563  parser->dds->add_var(*i);
564 
565  parser->bt_stack.pop();
566  delete cp;
567 }
568 
570  const xmlChar *l, const xmlChar *prefix, const xmlChar *URI,
571  int nb_namespaces, const xmlChar **namespaces,
572  int nb_attributes, int /*nb_defaulted*/, const xmlChar **attributes)
573 {
574  DDXParser *parser = static_cast<DDXParser*>(p);
575  const char *localname = (const char *)l;
576 
577  DBG2(cerr << "start element: " << localname << ", states: "
578  << states[parser->get_state()]);
579 
580  switch (parser->get_state()) {
581  case parser_start:
582  if (strcmp(localname, "Dataset") == 0) {
583  parser->set_state(inside_dataset);
584  parser->root_ns = URI != 0 ? (const char *)URI: "";
585  parser->transfer_xml_attrs(attributes, nb_attributes);
586 
587  if (parser->check_required_attribute(string("name")))
588  parser->dds->set_dataset_name(parser->attribute_table["name"].value);
589 
590  if (parser->check_attribute("dapVersion"))
591  parser->dds->set_dap_version(parser->attribute_table["dapVersion"].value);
592  }
593  else
595  "Expected response to start with a Dataset element; found '%s' instead.",
596  localname);
597  break;
598 
599  case inside_dataset:
600  if (parser->is_attribute_or_alias(localname, attributes, nb_attributes))
601  break;
602  else if (parser->is_variable(localname, attributes, nb_attributes))
603  break;
604  else if (strcmp(localname, "blob") == 0 || strcmp(localname, "dataBLOB") == 0) {
605  parser->process_blob(attributes, nb_attributes);
606  // next state: inside_data_blob
607  }
608  else
610  "Expected an Attribute, Alias or variable element; found '%s' instead.",
611  localname);
612  break;
613 
614  case inside_attribute_container:
615  if (parser->is_attribute_or_alias(localname, attributes, nb_attributes))
616  break;
617  else
619  "Expected an Attribute or Alias element; found '%s' instead.",
620  localname);
621  break;
622 
623  case inside_attribute:
624  if (parser->is_attribute_or_alias(localname, attributes, nb_attributes))
625  break;
626  else if (strcmp(localname, "value") == 0)
627  parser->set_state(inside_attribute_value);
628  else
629  ddx_fatal_error(parser,
630  "Expected an 'Attribute', 'Alias' or 'value' element; found '%s' instead.",
631  localname);
632  break;
633 
634  case inside_attribute_value:
635  ddx_fatal_error(parser,
636  "Internal parser error; unexpected state, inside value while processing element '%s'.",
637  localname);
638  break;
639 
640  case inside_other_xml_attribute:
641  DBGN(cerr << endl << "\t inside_other_xml_attribute: " << localname << endl);
642 
643  parser->other_xml_depth++;
644 
645  // Accumulate the elements here
646 
647  parser->other_xml.append("<");
648  if (prefix) {
649  parser->other_xml.append((const char *)prefix);
650  parser->other_xml.append(":");
651  }
652  parser->other_xml.append(localname);
653 
654  if (nb_namespaces != 0) {
655  parser->transfer_xml_ns(namespaces, nb_namespaces);
656 
657  for (map<string,string>::iterator i = parser->namespace_table.begin();
658  i != parser->namespace_table.end();
659  ++i) {
660  parser->other_xml.append(" xmlns");
661  if (!i->first.empty()) {
662  parser->other_xml.append(":");
663  parser->other_xml.append(i->first);
664  }
665  parser->other_xml.append("=\"");
666  parser->other_xml.append(i->second);
667  parser->other_xml.append("\"");
668  }
669  }
670 
671  if (nb_attributes != 0) {
672  parser->transfer_xml_attrs(attributes, nb_attributes);
673  for (XMLAttrMap::iterator i = parser->attr_table_begin();
674  i != parser->attr_table_end();
675  ++i) {
676  parser->other_xml.append(" ");
677  if (!i->second.prefix.empty()) {
678  parser->other_xml.append(i->second.prefix);
679  parser->other_xml.append(":");
680  }
681  parser->other_xml.append(i->first);
682  parser->other_xml.append("=\"");
683  parser->other_xml.append(i->second.value);
684  parser->other_xml.append("\"");
685  }
686  }
687 
688  parser->other_xml.append(">");
689  break;
690 
691  case inside_alias:
692  ddx_fatal_error(parser,
693  "Internal parser error; unexpected state, inside alias while processing element '%s'.",
694  localname);
695  break;
696 
697  case inside_simple_type:
698  if (parser->is_attribute_or_alias(localname, attributes, nb_attributes))
699  break;
700  else
701  ddx_fatal_error(parser,
702  "Expected an 'Attribute' or 'Alias' element; found '%s' instead.",
703  localname);
704  break;
705 
706  case inside_array:
707  if (parser->is_attribute_or_alias(localname, attributes, nb_attributes))
708  break;
709  else if (is_not(localname, "Array")
710  && parser->is_variable(localname, attributes, nb_attributes))
711  break;
712  else if (strcmp(localname, "dimension") == 0) {
713  parser->process_dimension(attributes, nb_attributes);
714  // next state: inside_dimension
715  }
716  else
717  ddx_fatal_error(parser,
718  "Expected an 'Attribute' or 'Alias' element; found '%s' instead.",
719  localname);
720  break;
721 
722  case inside_dimension:
723  ddx_fatal_error(parser,
724  "Internal parser error; unexpected state, inside dimension while processing element '%s'.",
725  localname);
726  break;
727 
728  case inside_structure:
729  if (parser->is_attribute_or_alias(localname, attributes, nb_attributes))
730  break;
731  else if (parser->is_variable(localname, attributes, nb_attributes))
732  break;
733  else
735  "Expected an Attribute, Alias or variable element; found '%s' instead.",
736  localname);
737  break;
738 
739  case inside_sequence:
740  if (parser->is_attribute_or_alias(localname, attributes, nb_attributes))
741  break;
742  else if (parser->is_variable(localname, attributes, nb_attributes))
743  break;
744  else
746  "Expected an Attribute, Alias or variable element; found '%s' instead.",
747  localname);
748  break;
749 
750  case inside_grid:
751  if (parser->is_attribute_or_alias(localname, attributes, nb_attributes))
752  break;
753  else if (strcmp(localname, "Array") == 0)
754  parser->process_variable(dods_array_c, inside_array, attributes, nb_attributes);
755  else if (strcmp(localname, "Map") == 0)
756  parser->process_variable(dods_array_c, inside_map, attributes, nb_attributes);
757  else
759  "Expected an Attribute, Alias or variable element; found '%s' instead.",
760  localname);
761  break;
762 
763  case inside_map:
764  if (parser->is_attribute_or_alias(localname, attributes, nb_attributes))
765  break;
766  else if (is_not(localname, "Array") && is_not(localname, "Sequence")
767  && is_not(localname, "Grid")
768  && parser->is_variable(localname, attributes, nb_attributes))
769  break;
770  else if (strcmp(localname, "dimension") == 0) {
771  parser->process_dimension(attributes, nb_attributes);
772  // next state: inside_dimension
773  }
774  else
775  ddx_fatal_error(parser,
776  "Expected an 'Attribute', 'Alias', variable or 'dimension' element; found '%s' instead.",
777  localname);
778  break;
779 
780  case inside_blob_href:
781  ddx_fatal_error(parser,
782  "Internal parser error; unexpected state, inside blob href while processing element '%s'.",
783  localname);
784  break;
785 
786  case parser_unknown:
787  // *** Never used? If so remove/error
788  parser->set_state(parser_unknown);
789  break;
790 
791  case parser_error:
792  break;
793  }
794 
795  DBGN(cerr << " ... " << states[parser->get_state()] << endl);
796 }
797 
798 void DDXParser::ddx_sax2_end_element(void *p, const xmlChar *l,
799  const xmlChar *prefix, const xmlChar *URI)
800 {
801  DDXParser *parser = static_cast<DDXParser*>(p);
802  const char *localname = (const char *)l;
803 
804  DBG2(cerr << "End element " << localname << " (state "
805  << states[parser->get_state()] << ")" << endl);
806 
807  switch (parser->get_state()) {
808  case parser_start:
809  ddx_fatal_error(parser,
810  "Internal parser error; unexpected state, inside start state while processing element '%s'.",
811  localname);
812  break;
813 
814  case inside_dataset:
815  if (strcmp(localname, "Dataset") == 0)
816  parser->pop_state();
817  else
819  "Expected an end Dataset tag; found '%s' instead.",
820  localname);
821  break;
822 
823  case inside_attribute_container:
824  if (strcmp(localname, "Attribute") == 0) {
825  parser->pop_state();
826  parser->at_stack.pop(); // pop when leaving a container.
827  }
828  else
830  "Expected an end Attribute tag; found '%s' instead.",
831  localname);
832  break;
833 
834  case inside_attribute:
835  if (strcmp(localname, "Attribute") == 0)
836  parser->pop_state();
837  else
839  "Expected an end Attribute tag; found '%s' instead.",
840  localname);
841  break;
842 
843  case inside_attribute_value:
844  if (strcmp(localname, "value") == 0) {
845  parser->pop_state();
846  AttrTable *atp = parser->at_stack.top();
847  atp->append_attr(parser->dods_attr_name,
848  parser->dods_attr_type, parser->char_data);
849  parser->char_data = ""; // Null this after use.
850  }
851  else
853  "Expected an end value tag; found '%s' instead.",
854  localname);
855 
856  break;
857 
858  case inside_other_xml_attribute: {
859  if (strcmp(localname, "Attribute") == 0
860  && parser->root_ns == (const char *)URI) {
861 
862  DBGN(cerr << endl << "\t Popping the 'inside_other_xml_attribute' state"
863  << endl);
864 
865  parser->pop_state();
866 
867  AttrTable *atp = parser->at_stack.top();
868  atp->append_attr(parser->dods_attr_name,
869  parser->dods_attr_type, parser->other_xml);
870 
871  parser->other_xml = ""; // Null this after use.
872  }
873  else {
874  DBGN(cerr << endl << "\t inside_other_xml_attribute: " << localname
875  << ", depth: " << parser->other_xml_depth << endl);
876  if (parser->other_xml_depth == 0)
878  "Expected an OtherXML attribute to end! Instead I found '%s'",
879  localname);
880  parser->other_xml_depth--;
881 
882  parser->other_xml.append("</");
883  if (prefix) {
884  parser->other_xml.append((const char *)prefix);
885  parser->other_xml.append(":");
886  }
887  parser->other_xml.append(localname);
888  parser->other_xml.append(">");
889  }
890  break;
891  }
892  // Alias is busted in libdap++ 05/29/03 jhrg
893  case inside_alias:
894  parser->pop_state();
895  break;
896 
897  case inside_simple_type:
898  if (is_simple_type(localname) != dods_null_c) {
899  parser->pop_state();
900  BaseType *btp = parser->bt_stack.top();
901  parser->bt_stack.pop();
902  parser->at_stack.pop();
903 
904  BaseType *parent = parser->bt_stack.top();
905 
906  if (parent->is_vector_type() || parent->is_constructor_type())
907  parent->add_var(btp);
908  else
910  "Tried to add the simple-type variable '%s' to a non-constructor type (%s %s).",
911  localname,
912  parser->bt_stack.top()->
913  type_name().c_str(),
914  parser->bt_stack.top()->name().
915  c_str());
916  }
917  else
919  "Expected an end tag for a simple type; found '%s' instead.",
920  localname);
921  break;
922 
923  case inside_array:
924  parser->finish_variable(localname, dods_array_c, "Array");
925  break;
926 
927  case inside_dimension:
928  if (strcmp(localname, "dimension") == 0)
929  parser->pop_state();
930  else
932  "Expected an end dimension tag; found '%s' instead.",
933  localname);
934  break;
935 
936  case inside_structure:
937  parser->finish_variable(localname, dods_structure_c, "Structure");
938  break;
939 
940  case inside_sequence:
941  parser->finish_variable(localname, dods_sequence_c, "Sequence");
942  break;
943 
944  case inside_grid:
945  parser->finish_variable(localname, dods_grid_c, "Grid");
946  break;
947 
948  case inside_map:
949  parser->finish_variable(localname, dods_array_c, "Map");
950  break;
951 
952  case inside_blob_href:
953  if (strcmp(localname, "blob") == 0 || strcmp(localname, "dataBLOB") == 0)
954  parser->pop_state();
955  else
957  "Expected an end dataBLOB/blob tag; found '%s' instead.",
958  localname);
959  break;
960 
961  case parser_unknown:
962  parser->pop_state();
963  break;
964 
965  case parser_error:
966  break;
967  }
968 
969 
970  DBGN(cerr << " ... " << states[parser->get_state()] << endl);
971 }
972 
976 void DDXParser::ddx_get_characters(void * p, const xmlChar * ch, int len)
977 {
978  DDXParser *parser = static_cast<DDXParser*>(p);
979 
980  switch (parser->get_state()) {
981  case inside_attribute_value:
982  parser->char_data.append((const char *)(ch), len);
983  DBG2(cerr << "Characters: '" << parser->char_data << "'" << endl);
984  break;
985 
986  case inside_other_xml_attribute:
987  parser->other_xml.append((const char *)(ch), len);
988  DBG2(cerr << "Other XML Characters: '" << parser->other_xml << "'" << endl);
989  break;
990 
991  default:
992  break;
993  }
994 }
995 
1000 void DDXParser::ddx_ignoreable_whitespace(void *p, const xmlChar *ch,
1001  int len)
1002 {
1003  DDXParser *parser = static_cast<DDXParser*>(p);
1004 
1005  switch (parser->get_state()) {
1006  case inside_other_xml_attribute:
1007  parser->other_xml.append((const char *)(ch), len);
1008  break;
1009 
1010  default:
1011  break;
1012  }
1013 }
1014 
1020 void DDXParser::ddx_get_cdata(void *p, const xmlChar *value, int len)
1021 {
1022  DDXParser *parser = static_cast<DDXParser*>(p);
1023 
1024  switch (parser->get_state()) {
1025  case inside_other_xml_attribute:
1026  parser->other_xml.append((const char *)(value), len);
1027  break;
1028 
1029  case parser_unknown:
1030  break;
1031 
1032  default:
1034  "Found a CData block but none are allowed by DAP.");
1035 
1036  break;
1037  }
1038 }
1039 
1044 xmlEntityPtr DDXParser::ddx_get_entity(void *, const xmlChar * name)
1045 {
1046  return xmlGetPredefinedEntity(name);
1047 }
1048 
1056 void DDXParser::ddx_fatal_error(void * p, const char *msg, ...)
1057 {
1058  va_list args;
1059  DDXParser *parser = static_cast<DDXParser*>(p);
1060 
1061  parser->set_state(parser_error);
1062 
1063  va_start(args, msg);
1064  char str[1024];
1065  vsnprintf(str, 1024, msg, args);
1066  va_end(args);
1067 
1068  int line = xmlSAX2GetLineNumber(parser->ctxt);
1069 
1070  parser->error_msg += "At line " + long_to_string(line) + ": ";
1071  parser->error_msg += string(str) + string("\n");
1072 }
1073 
1075 
1076 void DDXParser::cleanup_parse(xmlParserCtxtPtr & context) const
1077 {
1078  if (!context->wellFormed) {
1079  context->sax = NULL;
1080  xmlFreeParserCtxt(context);
1081  throw
1082  DDXParseFailed(string
1083  ("\nThe DDX is not a well formed XML document.\n")
1084  + error_msg);
1085  }
1086 
1087  if (!context->valid) {
1088  context->sax = NULL;
1089  xmlFreeParserCtxt(context);
1090  throw DDXParseFailed(string("\nThe DDX is not a valid document.\n")
1091  + error_msg);
1092  }
1093 
1094  if (get_state() == parser_error) {
1095  context->sax = NULL;
1096  xmlFreeParserCtxt(context);
1097  throw DDXParseFailed(string("\nError parsing DDX response.\n") +
1098  error_msg);
1099  }
1100 
1101  context->sax = NULL;
1102  xmlFreeParserCtxt(context);
1103 }
1104 
1107 void DDXParser::intern_stream(FILE *in, DDS *dest_dds, string &cid,
1108  const string &boundary)
1109 {
1110  // Code example from libxml2 docs re: read from a stream.
1111 
1112  if (!in || feof(in) || ferror(in))
1113  throw InternalErr(__FILE__, __LINE__,
1114  "Input stream not open or read error");
1115 
1116  const int size = 1024;
1117  char chars[size];
1118 
1119  int res = fread(chars, 1, 4, in);
1120  if (res > 0) {
1121  xmlParserCtxtPtr context =
1122  xmlCreatePushParserCtxt(NULL, NULL, chars, res, "stream");
1123 
1124  ctxt = context; // need ctxt for error messages
1125  dds = dest_dds; // dump values here
1126  blob_href = &cid; // cid goes here
1127 
1128  xmlSAXHandler ddx_sax_parser;
1129  memset( &ddx_sax_parser, 0, sizeof(xmlSAXHandler) );
1130 
1131  ddx_sax_parser.getEntity = &DDXParser::ddx_get_entity;
1132  ddx_sax_parser.startDocument = &DDXParser::ddx_start_document;
1133  ddx_sax_parser.endDocument = &DDXParser::ddx_end_document;
1134  ddx_sax_parser.characters = &DDXParser::ddx_get_characters;
1135  ddx_sax_parser.ignorableWhitespace = &DDXParser::ddx_ignoreable_whitespace;
1136  ddx_sax_parser.cdataBlock = &DDXParser::ddx_get_cdata;
1137  ddx_sax_parser.warning = &DDXParser::ddx_fatal_error;
1138  ddx_sax_parser.error = &DDXParser::ddx_fatal_error;
1139  ddx_sax_parser.fatalError = &DDXParser::ddx_fatal_error;
1140  ddx_sax_parser.initialized = XML_SAX2_MAGIC;
1141  ddx_sax_parser.startElementNs = &DDXParser::ddx_sax2_start_element;
1142  ddx_sax_parser.endElementNs = &DDXParser::ddx_sax2_end_element;
1143 
1144  context->sax = &ddx_sax_parser;
1145  context->userData = this;
1146  context->validate = true;
1147 
1148  while ((fgets(chars, size, in) > 0) && !is_boundary(chars, boundary)) {
1149  DBG(cerr << "line: " << chars << endl);
1150  xmlParseChunk(ctxt, chars, strlen(chars), 0);
1151  }
1152  // This call ends the parse: The fourth argument of xmlParseChunk is
1153  // the bool 'terminate.'
1154  xmlParseChunk(ctxt, chars, 0, 1);
1155 
1156  cleanup_parse(context);
1157  }
1158 }
1159 
1160 
1172 void DDXParser::intern(const string & document, DDS * dest_dds, string &cid)
1173 {
1174  // Create the context pointer explicitly so that we can store a pointer
1175  // to it in the DDXParser instance. This provides a way to generate our
1176  // own error messages *with* line numbers. The messages are pretty
1177  // meaningless otherwise. This means that we use an interface from the
1178  // 'parser internals' header, and not the 'parser' header. However, this
1179  // interface is also used in one of the documented examples, so it's
1180  // probably pretty stable. 06/02/03 jhrg
1181  xmlParserCtxtPtr context = xmlCreateFileParserCtxt(document.c_str());
1182  if (!context)
1183  throw
1184  DDXParseFailed(string
1185  ("Could not initialize the parser with the file: '")
1186  + document + string("'."));
1187 
1188  dds = dest_dds; // dump values here
1189  blob_href = &cid;
1190  ctxt = context; // need ctxt for error messages
1191 
1192  xmlSAXHandler ddx_sax_parser;
1193  memset( &ddx_sax_parser, 0, sizeof(xmlSAXHandler) );
1194 
1195  ddx_sax_parser.getEntity = &DDXParser::ddx_get_entity;
1196  ddx_sax_parser.startDocument = &DDXParser::ddx_start_document;
1197  ddx_sax_parser.endDocument = &DDXParser::ddx_end_document;
1198  ddx_sax_parser.characters = &DDXParser::ddx_get_characters;
1199  ddx_sax_parser.ignorableWhitespace = &DDXParser::ddx_ignoreable_whitespace;
1200  ddx_sax_parser.cdataBlock = &DDXParser::ddx_get_cdata;
1201  ddx_sax_parser.warning = &DDXParser::ddx_fatal_error;
1202  ddx_sax_parser.error = &DDXParser::ddx_fatal_error;
1203  ddx_sax_parser.fatalError = &DDXParser::ddx_fatal_error;
1204  ddx_sax_parser.initialized = XML_SAX2_MAGIC;
1205  ddx_sax_parser.startElementNs = &DDXParser::ddx_sax2_start_element;
1206  ddx_sax_parser.endElementNs = &DDXParser::ddx_sax2_end_element;
1207 
1208  context->sax = &ddx_sax_parser;
1209  context->userData = this;
1210  context->validate = false;
1211 
1212  xmlParseDocument(context);
1213 
1214  cleanup_parse(context);
1215 }
1216 
1217 } // 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:146
#define not_used
Definition: config.h:521
virtual Int16 * NewInt16(const string &n="") const
std::vector< BaseType * >::iterator Vars_iter
Definition: Constructor.h:61
#define DBGN(x)
Definition: debug.h:59
static void ddx_start_document(void *parser)
virtual Structure * NewStructure(const string &n="") const
virtual UInt32 * NewUInt32(const string &n="") const
virtual UInt16 * NewUInt16(const string &n="") const
static void ddx_get_cdata(void *parser, const xmlChar *value, int len)
Holds a structure (aggregate) type.
Definition: Structure.h:100
virtual void add_var(BaseType *bt, Part part=nil)
Add a variable.
Definition: BaseType.cc:719
Type
Identifies the data type.
Definition: BaseType.h:131
#define DBG2(x)
Definition: debug.h:73
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:324
virtual bool is_constructor_type()
Returns true if the instance is a constructor (i.e., Structure, Sequence or Grid) type variable...
Definition: BaseType.cc:356
#define DBG(x)
Definition: debug.h:58
static void ddx_fatal_error(void *parser, const char *msg,...)
virtual Array * NewArray(const string &n="", BaseType *v=0) 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)
virtual Float32 * NewFloat32(const string &n="") const
virtual AttrTable & get_attr_table()
Definition: DDS.cc:460
virtual Grid * NewGrid(const string &n="") const
void set_dap_version(const string &version_string)
Definition: DDS.cc:494
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)
string long_to_string(long val, int base)
Definition: util.cc:440
virtual Sequence * NewSequence(const string &n="") const
bool is_boundary(const char *line, const string &boundary)
Definition: mime_util.cc:781
virtual unsigned int append_attr(const string &name, const string &type, const string &value)
Add an attribute to the table.
Definition: AttrTable.cc:239
void set_dataset_name(const string &n)
Definition: DDS.cc:451
virtual Str * NewStr(const string &n="") const
The basic data type for the DODS DAP types.
Definition: BaseType.h:190
virtual Int32 * NewInt32(const string &n="") const
Vars_iter var_begin()
Definition: Constructor.cc:101
virtual Float64 * NewFloat64(const string &n="") const
virtual Url * NewUrl(const string &n="") const
static void ddx_ignoreable_whitespace(void *parser, const xmlChar *ch, int len)
Vars_iter var_end()
Definition: Constructor.cc:295
void intern(const string &document, DDS *dest_dds, string &cid)
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:572
virtual Byte * NewByte(const string &n="") const