libdap++  Updated for version 3.8.2
DDXParserSAX2.cc
Go to the documentation of this file.
00001 
00002 // -*- mode: c++; c-basic-offset:4 -*-
00003 
00004 // This file is part of libdap, A C++ implementation of the OPeNDAP Data
00005 // Access Protocol.
00006 
00007 // Copyright (c) 2003 OPeNDAP, Inc.
00008 // Author: James Gallagher <jgallagher@opendap.org>
00009 //
00010 // This library is free software; you can redistribute it and/or
00011 // modify it under the terms of the GNU Lesser General Public
00012 // License as published by the Free Software Foundation; either
00013 // version 2.1 of the License, or (at your option) any later version.
00014 //
00015 // This library is distributed in the hope that it will be useful,
00016 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00017 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00018 // Lesser General Public License for more details.
00019 //
00020 // You should have received a copy of the GNU Lesser General Public
00021 // License along with this library; if not, write to the Free Software
00022 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00023 //
00024 // You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
00025 
00026 #include "config.h"
00027 
00028 //#define DODS_DEBUG 1
00029 //#define DODS_DEBUG2 1
00030 
00031 #include <cstring>
00032 #include <cstdarg>
00033 
00034 #include "BaseType.h"
00035 #include "Byte.h"
00036 #include "Int16.h"
00037 #include "UInt16.h"
00038 #include "Int32.h"
00039 #include "UInt32.h"
00040 #include "Float32.h"
00041 #include "Float64.h"
00042 #include "Str.h"
00043 #include "Url.h"
00044 #include "Array.h"
00045 #include "Structure.h"
00046 #include "Sequence.h"
00047 #include "Grid.h"
00048 
00049 #include "DDXParserSAX2.h"
00050 
00051 #include "util.h"
00052 #include "mime_util.h"
00053 #include "debug.h"
00054 
00055 namespace libdap {
00056 
00057 static const not_used char *states[] =
00058     {
00059         "start",
00060 
00061         "dataset",
00062 
00063         "attribute_container",
00064         "attribute",
00065         "attribute_value",
00066         "other_xml_attribute",
00067 
00068         "alias",
00069 
00070         "simple_type",
00071 
00072         "array",
00073         "dimension",
00074 
00075         "grid",
00076         "map",
00077 
00078         "structure",
00079         "sequence",
00080 
00081         "blob href",
00082 
00083         "unknown",
00084         "error"
00085     };
00086 
00087 // Glue the BaseTypeFactory to the enum-based factory defined statically
00088 // here.
00089 
00090 BaseType *DDXParser::factory(Type t, const string & name)
00091 {
00092     switch (t) {
00093     case dods_byte_c:
00094         return d_factory->NewByte(name);
00095         break;
00096 
00097     case dods_int16_c:
00098         return d_factory->NewInt16(name);
00099         break;
00100 
00101     case dods_uint16_c:
00102         return d_factory->NewUInt16(name);
00103         break;
00104 
00105     case dods_int32_c:
00106         return d_factory->NewInt32(name);
00107         break;
00108 
00109     case dods_uint32_c:
00110         return d_factory->NewUInt32(name);
00111         break;
00112 
00113     case dods_float32_c:
00114         return d_factory->NewFloat32(name);
00115         break;
00116 
00117     case dods_float64_c:
00118         return d_factory->NewFloat64(name);
00119         break;
00120 
00121     case dods_str_c:
00122         return d_factory->NewStr(name);
00123         break;
00124 
00125     case dods_url_c:
00126         return d_factory->NewUrl(name);
00127         break;
00128 
00129     case dods_array_c:
00130         return d_factory->NewArray(name);
00131         break;
00132 
00133     case dods_structure_c:
00134         return d_factory->NewStructure(name);
00135         break;
00136 
00137     case dods_sequence_c:
00138         return d_factory->NewSequence(name);
00139         break;
00140 
00141     case dods_grid_c:
00142         return d_factory->NewGrid(name);
00143         break;
00144 
00145     default:
00146         return 0;
00147     }
00148 }
00149 
00151 static Type get_type(const char *name)
00152 {
00153     if (strcmp(name, "Byte") == 0)
00154         return dods_byte_c;
00155 
00156     if (strcmp(name, "Int16") == 0)
00157         return dods_int16_c;
00158 
00159     if (strcmp(name, "UInt16") == 0)
00160         return dods_uint16_c;
00161 
00162     if (strcmp(name, "Int32") == 0)
00163         return dods_int32_c;
00164 
00165     if (strcmp(name, "UInt32") == 0)
00166         return dods_uint32_c;
00167 
00168     if (strcmp(name, "Float32") == 0)
00169         return dods_float32_c;
00170 
00171     if (strcmp(name, "Float64") == 0)
00172         return dods_float64_c;
00173 
00174     if (strcmp(name, "String") == 0)
00175         return dods_str_c;
00176 
00177     if (strcmp(name, "Url") == 0)
00178         return dods_url_c;
00179 
00180     if (strcmp(name, "Array") == 0)
00181         return dods_array_c;
00182 
00183     if (strcmp(name, "Structure") == 0)
00184         return dods_structure_c;
00185 
00186     if (strcmp(name, "Sequence") == 0)
00187         return dods_sequence_c;
00188 
00189     if (strcmp(name, "Grid") == 0)
00190         return dods_grid_c;
00191 
00192     return dods_null_c;
00193 }
00194 
00195 static Type is_simple_type(const char *name)
00196 {
00197     Type t = get_type(name);
00198     switch (t) {
00199     case dods_byte_c:
00200     case dods_int16_c:
00201     case dods_uint16_c:
00202     case dods_int32_c:
00203     case dods_uint32_c:
00204     case dods_float32_c:
00205     case dods_float64_c:
00206     case dods_str_c:
00207     case dods_url_c:
00208         return t;
00209     default:
00210         return dods_null_c;
00211     }
00212 }
00213 
00214 static bool is_not(const char *name, const char *tag)
00215 {
00216     return strcmp(name, tag) != 0;
00217 }
00218 
00219 void DDXParser::set_state(DDXParser::ParseState state)
00220 {
00221     s.push(state);
00222 }
00223 
00224 DDXParser::ParseState DDXParser::get_state() const
00225 {
00226     return s.top();
00227 }
00228 
00229 void DDXParser::pop_state()
00230 {
00231     s.pop();
00232 }
00233 
00237 void DDXParser::transfer_xml_attrs(const xmlChar **attributes, int nb_attributes)
00238 {
00239     if (!attribute_table.empty())
00240         attribute_table.clear(); // erase old attributes
00241 
00242     unsigned int index = 0;
00243     for (int i = 0; i < nb_attributes; ++i, index += 5) {
00244         // Make a value using the attribute name and the prefix, namespace URI
00245         // and the value. The prefix might be null.
00246         attribute_table.insert(map<string, XMLAttribute>::value_type(
00247                 string((const char *)attributes[index]),
00248                 XMLAttribute(attributes + index + 1)));
00249 
00250         DBG(cerr << "Attribute '" << (const char *)attributes[index] << "': "
00251                 << attribute_table[(const char *)attributes[index]].value << endl);
00252     }
00253 }
00254 
00255 void DDXParser::transfer_xml_ns(const xmlChar **namespaces, int nb_namespaces)
00256 {
00257     for (int i = 0; i < nb_namespaces; ++i ) {
00258         // make a value with the prefix and namespace URI. The prefix might be
00259         // null.
00260         namespace_table.insert(map<string,string>::value_type(
00261                 namespaces[i*2] != 0 ? (const char *)namespaces[i*2] : "",
00262                 (const char *)namespaces[i*2+1]));
00263     }
00264 }
00265 
00270 bool DDXParser::check_required_attribute(const string & attr)
00271 {
00272     map < string, XMLAttribute >::iterator i = attribute_table.find(attr);
00273     if (i == attribute_table.end())
00274         ddx_fatal_error(this, "Required attribute '%s' not found.",
00275                         attr.c_str());
00276     return true;
00277 }
00278 
00284 bool DDXParser::check_attribute(const string & attr)
00285 {
00286     return (attribute_table.find(attr) != attribute_table.end());
00287 }
00288 
00297 void DDXParser::process_attribute_element(const xmlChar **attrs, int nb_attributes)
00298 {
00299     // These methods set the state to parser_error if a problem is found.
00300     transfer_xml_attrs(attrs, nb_attributes);
00301 
00302     bool error = !(check_required_attribute(string("name"))
00303                    && check_required_attribute(string("type")));
00304     if (error)
00305         return;
00306 
00307     if (attribute_table["type"].value == "Container") {
00308         set_state(inside_attribute_container);
00309 
00310         AttrTable *child;
00311         AttrTable *parent = at_stack.top();
00312 
00313         child = parent->append_container(attribute_table["name"].value);
00314         at_stack.push(child);   // save.
00315         DBG2(cerr << "Pushing at" << endl);
00316     }
00317     else if (attribute_table["type"].value == "OtherXML") {
00318         set_state(inside_other_xml_attribute);
00319 
00320         dods_attr_name = attribute_table["name"].value;
00321         dods_attr_type = attribute_table["type"].value;
00322     }
00323     else {
00324         set_state(inside_attribute);
00325         // *** Modify parser. Add a special state for inside OtherXML since it
00326         // does not use the <value> element.
00327 
00328         dods_attr_name = attribute_table["name"].value;
00329         dods_attr_type = attribute_table["type"].value;
00330     }
00331 }
00332 
00336 void DDXParser::process_attribute_alias(const xmlChar **attrs, int nb_attributes)
00337 {
00338     transfer_xml_attrs(attrs, nb_attributes);
00339     if (check_required_attribute(string("name"))
00340         && check_required_attribute(string("attribute"))) {
00341         set_state(inside_alias);
00342         at_stack.top()->attr_alias(attribute_table["name"].value,
00343                                    attribute_table["attribute"].value);
00344     }
00345 }
00346 
00354 void DDXParser::process_variable(Type t, ParseState s, const xmlChar **attrs,
00355         int nb_attributes)
00356 {
00357     transfer_xml_attrs(attrs, nb_attributes);
00358 
00359     set_state(s);
00360     if (bt_stack.top()->type() == dods_array_c
00361             || check_required_attribute("name")) { // throws on error/false
00362         BaseType *btp = factory(t, attribute_table["name"].value);
00363         if (!btp)
00364             ddx_fatal_error(
00365                     this,
00366                     "Internal parser error; could not instantiate the variable '%s'.",
00367                     attribute_table["name"].value.c_str());
00368 
00369         // Once we make the new variable, we not only load it on to the
00370         // BaseType stack, we also load its AttrTable on the AttrTable stack.
00371         // The attribute processing software always operates on the AttrTable
00372         // at the top of the AttrTable stack (at_stack).
00373         bt_stack.push(btp);
00374         at_stack.push(&btp->get_attr_table());
00375     }
00376 }
00377 
00381 void DDXParser::process_dimension(const xmlChar **attrs, int nb_attributes)
00382 {
00383     transfer_xml_attrs(attrs, nb_attributes);
00384     if (check_required_attribute(string("size"))) {
00385         set_state(inside_dimension);
00386         Array *ap = dynamic_cast < Array * >(bt_stack.top());
00387                 if (!ap) {
00388                         ddx_fatal_error(this, "Parse error: Expected an array variable.");
00389                         return;
00390                 }
00391                 
00392         ap->append_dim(atoi(attribute_table["size"].value.c_str()),
00393                        attribute_table["name"].value);
00394     }
00395 }
00396 
00399 void DDXParser::process_blob(const xmlChar **attrs, int nb_attributes)
00400 {
00401     transfer_xml_attrs(attrs, nb_attributes);
00402     if (check_required_attribute(string("href"))) {
00403         set_state(inside_blob_href);
00404         *blob_href = attribute_table["href"].value;
00405     }
00406 }
00407 
00414 inline bool
00415 DDXParser::is_attribute_or_alias(const char *name, const xmlChar **attrs,
00416         int nb_attributes)
00417 {
00418     if (strcmp(name, "Attribute") == 0) {
00419         process_attribute_element(attrs, nb_attributes);
00420         // next state: inside_attribtue or inside_attribute_container
00421         return true;
00422     }
00423     else if (strcmp(name, "Alias") == 0) {
00424         process_attribute_alias(attrs, nb_attributes);
00425         // next state: inside_alias
00426         return true;
00427     }
00428 
00429     return false;
00430 }
00431 
00437 inline bool DDXParser::is_variable(const char *name, const xmlChar **attrs,
00438         int nb_attributes)
00439 {
00440     Type t;
00441     if ((t = is_simple_type(name)) != dods_null_c) {
00442         process_variable(t, inside_simple_type, attrs, nb_attributes);
00443         return true;
00444     }
00445     else if (strcmp(name, "Array") == 0) {
00446         process_variable(dods_array_c, inside_array, attrs, nb_attributes);
00447         return true;
00448     }
00449     else if (strcmp(name, "Structure") == 0) {
00450         process_variable(dods_structure_c, inside_structure, attrs, nb_attributes);
00451         return true;
00452     }
00453     else if (strcmp(name, "Sequence") == 0) {
00454         process_variable(dods_sequence_c, inside_sequence, attrs, nb_attributes);
00455         return true;
00456     }
00457     else if (strcmp(name, "Grid") == 0) {
00458         process_variable(dods_grid_c, inside_grid, attrs, nb_attributes);
00459         return true;
00460     }
00461 
00462     return false;
00463 }
00464 
00465 void DDXParser::finish_variable(const char *tag, Type t, const char *expected)
00466 {
00467     if (strcmp(tag, expected) != 0) {
00468         DDXParser::ddx_fatal_error(this,
00469                                    "Expected an end tag for a %s; found '%s' instead.",
00470                                    expected, tag);
00471         return;
00472     }
00473 
00474     pop_state();
00475 
00476     BaseType *btp = bt_stack.top();
00477 
00478     bt_stack.pop();
00479     at_stack.pop();
00480 
00481     if (btp->type() != t) {
00482         DDXParser::ddx_fatal_error(this,
00483                                    "Internal error: Expected a %s variable.",
00484                                    expected);
00485         return;
00486     }
00487     // Once libxml2 validates, this can go away. 05/30/03 jhrg
00488     if (t == dods_array_c
00489         && dynamic_cast < Array * >(btp)->dimensions() == 0) {
00490         DDXParser::ddx_fatal_error(this,
00491                                    "No dimension element included in the Array '%s'.",
00492                                    btp->name().c_str());
00493         return;
00494     }
00495 
00496     BaseType *parent = bt_stack.top();
00497 
00498     if (!(parent->is_vector_type() || parent->is_constructor_type())) {
00499         DDXParser::ddx_fatal_error(this,
00500                                    "Tried to add the array variable '%s' to a non-constructor type (%s %s).",
00501                                    tag,
00502                                    bt_stack.top()->type_name().c_str(),
00503                                    bt_stack.top()->name().c_str());
00504         return;
00505     }
00506 
00507     parent->add_var(btp);
00508 }
00509 
00516 
00521 void DDXParser::ddx_start_document(void * p)
00522 {
00523     DDXParser *parser = static_cast<DDXParser*>(p);
00524     parser->error_msg = "";
00525     parser->char_data = "";
00526 
00527     // init attr table stack.
00528     parser->at_stack.push(&parser->dds->get_attr_table());
00529 
00530     // Trick; DDS *should* be a child of Structure. To simplify parsing,
00531     // stuff a Structure on the bt_stack and dump the top level variables
00532     // there. Once we're done, transfer the variables to the DDS.
00533     parser->bt_stack.push(new Structure("dummy_dds"));
00534 
00535     parser->set_state(parser_start);
00536 
00537     DBG2(cerr << "Parser state: " << states[parser->get_state()] << endl);
00538 }
00539 
00542 void DDXParser::ddx_end_document(void * p)
00543 {
00544     DDXParser *parser = static_cast<DDXParser*>(p);
00545     DBG2(cerr << "Ending state == " << states[parser->get_state()] <<
00546          endl);
00547 
00548     if (parser->get_state() != parser_start)
00549         DDXParser::ddx_fatal_error(parser,
00550                                    "The document contained unbalanced tags.");
00551 
00552     // If we've found any sort of error, don't make the DDX; intern() will
00553     // take care of the error.
00554     if (parser->get_state() == parser_error)
00555         return;
00556 
00557     // Pop the temporary Structure off the stack and transfer its variables
00558     // to the DDS.
00559     Constructor *cp = dynamic_cast < Constructor * >(parser->bt_stack.top());
00560     if (!cp) {
00561         ddx_fatal_error(parser, "Parse error: Expected a Structure, Sequence or Grid variable.");
00562                 return;
00563     }
00564     
00565     for (Constructor::Vars_iter i = cp->var_begin(); i != cp->var_end();
00566          ++i)
00567         parser->dds->add_var(*i);
00568 
00569     parser->bt_stack.pop();
00570     delete cp;
00571 }
00572 
00573 void DDXParser::ddx_sax2_start_element(void *p,
00574         const xmlChar *l, const xmlChar *prefix, const xmlChar *URI,
00575         int nb_namespaces, const xmlChar **namespaces,
00576         int nb_attributes, int /*nb_defaulted*/, const xmlChar **attributes)
00577 {
00578     DDXParser *parser = static_cast<DDXParser*>(p);
00579     const char *localname = (const char *)l;
00580 
00581     DBG2(cerr << "start element: " << localname << ", states: "
00582          << states[parser->get_state()]);
00583 
00584     switch (parser->get_state()) {
00585     case parser_start:
00586         if (strcmp(localname, "Dataset") == 0) {
00587             parser->set_state(inside_dataset);
00588             parser->root_ns = URI != 0 ? (const char *)URI: "";
00589             parser->transfer_xml_attrs(attributes, nb_attributes);
00590 
00591             if (parser->check_required_attribute(string("name")))
00592                 parser->dds->set_dataset_name(parser->attribute_table["name"].value);
00593 
00594             if (parser->check_attribute("dapVersion"))
00595                 parser->dds->set_dap_version(parser->attribute_table["dapVersion"].value);
00596         }
00597         else
00598             DDXParser::ddx_fatal_error(parser,
00599                                        "Expected response to start with a Dataset element; found '%s' instead.",
00600                                        localname);
00601         break;
00602 
00603     case inside_dataset:
00604         if (parser->is_attribute_or_alias(localname, attributes, nb_attributes))
00605             break;
00606         else if (parser->is_variable(localname, attributes, nb_attributes))
00607             break;
00608         else if (strcmp(localname, "blob") == 0 || strcmp(localname, "dataBLOB") == 0) {
00609             parser->process_blob(attributes, nb_attributes);
00610             // next state: inside_data_blob
00611         }
00612         else
00613             DDXParser::ddx_fatal_error(parser,
00614                                        "Expected an Attribute, Alias or variable element; found '%s' instead.",
00615                                        localname);
00616         break;
00617 
00618     case inside_attribute_container:
00619         if (parser->is_attribute_or_alias(localname, attributes, nb_attributes))
00620             break;
00621         else
00622             DDXParser::ddx_fatal_error(parser,
00623                                        "Expected an Attribute or Alias element; found '%s' instead.",
00624                                        localname);
00625         break;
00626 
00627     case inside_attribute:
00628         if (parser->is_attribute_or_alias(localname, attributes, nb_attributes))
00629             break;
00630         else if (strcmp(localname, "value") == 0)
00631             parser->set_state(inside_attribute_value);
00632         else
00633             ddx_fatal_error(parser,
00634                             "Expected an 'Attribute', 'Alias' or 'value' element; found '%s' instead.",
00635                             localname);
00636         break;
00637 
00638     case inside_attribute_value:
00639         ddx_fatal_error(parser,
00640                         "Internal parser error; unexpected state, inside value while processing element '%s'.",
00641                         localname);
00642         break;
00643 
00644     case inside_other_xml_attribute:
00645         DBGN(cerr << endl << "\t inside_other_xml_attribute: " << localname << endl);
00646 
00647         parser->other_xml_depth++;
00648 
00649         // Accumulate the elements here
00650 
00651         parser->other_xml.append("<");
00652         if (prefix) {
00653             parser->other_xml.append((const char *)prefix);
00654             parser->other_xml.append(":");
00655         }
00656         parser->other_xml.append(localname);
00657 
00658         if (nb_namespaces != 0) {
00659             parser->transfer_xml_ns(namespaces, nb_namespaces);
00660 
00661             for (map<string,string>::iterator i = parser->namespace_table.begin();
00662                 i != parser->namespace_table.end();
00663                 ++i) {
00664                 parser->other_xml.append(" xmlns");
00665                 if (!i->first.empty()) {
00666                     parser->other_xml.append(":");
00667                     parser->other_xml.append(i->first);
00668                 }
00669                 parser->other_xml.append("=\"");
00670                 parser->other_xml.append(i->second);
00671                 parser->other_xml.append("\"");
00672             }
00673         }
00674 
00675         if (nb_attributes != 0) {
00676             parser->transfer_xml_attrs(attributes, nb_attributes);
00677             for (XMLAttrMap::iterator i = parser->attr_table_begin();
00678                 i != parser->attr_table_end();
00679                 ++i) {
00680                 parser->other_xml.append(" ");
00681                 if (!i->second.prefix.empty()) {
00682                     parser->other_xml.append(i->second.prefix);
00683                     parser->other_xml.append(":");
00684                 }
00685                 parser->other_xml.append(i->first);
00686                 parser->other_xml.append("=\"");
00687                 parser->other_xml.append(i->second.value);
00688                 parser->other_xml.append("\"");
00689             }
00690         }
00691 
00692         parser->other_xml.append(">");
00693         break;
00694 
00695     case inside_alias:
00696         ddx_fatal_error(parser,
00697                         "Internal parser error; unexpected state, inside alias while processing element '%s'.",
00698                         localname);
00699         break;
00700 
00701     case inside_simple_type:
00702         if (parser->is_attribute_or_alias(localname, attributes, nb_attributes))
00703             break;
00704         else
00705             ddx_fatal_error(parser,
00706                             "Expected an 'Attribute' or 'Alias' element; found '%s' instead.",
00707                             localname);
00708         break;
00709 
00710     case inside_array:
00711         if (parser->is_attribute_or_alias(localname, attributes, nb_attributes))
00712             break;
00713         else if (is_not(localname, "Array")
00714                 && parser->is_variable(localname, attributes, nb_attributes))
00715             break;
00716         else if (strcmp(localname, "dimension") == 0) {
00717             parser->process_dimension(attributes, nb_attributes);
00718             // next state: inside_dimension
00719         }
00720         else
00721             ddx_fatal_error(parser,
00722                             "Expected an 'Attribute' or 'Alias' element; found '%s' instead.",
00723                             localname);
00724         break;
00725 
00726     case inside_dimension:
00727         ddx_fatal_error(parser,
00728                         "Internal parser error; unexpected state, inside dimension while processing element '%s'.",
00729                         localname);
00730         break;
00731 
00732     case inside_structure:
00733         if (parser->is_attribute_or_alias(localname, attributes, nb_attributes))
00734             break;
00735         else if (parser->is_variable(localname, attributes, nb_attributes))
00736             break;
00737         else
00738             DDXParser::ddx_fatal_error(parser,
00739                                        "Expected an Attribute, Alias or variable element; found '%s' instead.",
00740                                        localname);
00741         break;
00742 
00743     case inside_sequence:
00744         if (parser->is_attribute_or_alias(localname, attributes, nb_attributes))
00745             break;
00746         else if (parser->is_variable(localname, attributes, nb_attributes))
00747             break;
00748         else
00749             DDXParser::ddx_fatal_error(parser,
00750                                        "Expected an Attribute, Alias or variable element; found '%s' instead.",
00751                                        localname);
00752         break;
00753 
00754     case inside_grid:
00755         if (parser->is_attribute_or_alias(localname, attributes, nb_attributes))
00756             break;
00757         else if (strcmp(localname, "Array") == 0)
00758             parser->process_variable(dods_array_c, inside_array, attributes, nb_attributes);
00759         else if (strcmp(localname, "Map") == 0)
00760             parser->process_variable(dods_array_c, inside_map, attributes, nb_attributes);
00761         else
00762             DDXParser::ddx_fatal_error(parser,
00763                                        "Expected an Attribute, Alias or variable element; found '%s' instead.",
00764                                        localname);
00765         break;
00766 
00767     case inside_map:
00768         if (parser->is_attribute_or_alias(localname, attributes, nb_attributes))
00769             break;
00770         else if (is_not(localname, "Array") && is_not(localname, "Sequence")
00771                  && is_not(localname, "Grid")
00772                  && parser->is_variable(localname, attributes, nb_attributes))
00773             break;
00774         else if (strcmp(localname, "dimension") == 0) {
00775             parser->process_dimension(attributes, nb_attributes);
00776             // next state: inside_dimension
00777         }
00778         else
00779             ddx_fatal_error(parser,
00780                             "Expected an 'Attribute', 'Alias', variable or 'dimension' element; found '%s' instead.",
00781                             localname);
00782         break;
00783 
00784     case inside_blob_href:
00785         ddx_fatal_error(parser,
00786                         "Internal parser error; unexpected state, inside blob href while processing element '%s'.",
00787                         localname);
00788         break;
00789 
00790     case parser_unknown:
00791         // *** Never used? If so remove/error
00792         parser->set_state(parser_unknown);
00793         break;
00794 
00795     case parser_error:
00796         break;
00797     }
00798 
00799     DBGN(cerr << " ... " << states[parser->get_state()] << endl);
00800 }
00801 
00802 void DDXParser::ddx_sax2_end_element(void *p, const xmlChar *l,
00803         const xmlChar *prefix, const xmlChar *URI)
00804 {
00805     DDXParser *parser = static_cast<DDXParser*>(p);
00806     const char *localname = (const char *)l;
00807 
00808     DBG2(cerr << "End element " << localname << " (state "
00809          << states[parser->get_state()] << ")" << endl);
00810 
00811     switch (parser->get_state()) {
00812     case parser_start:
00813         ddx_fatal_error(parser,
00814                         "Internal parser error; unexpected state, inside start state while processing element '%s'.",
00815                         localname);
00816         break;
00817 
00818     case inside_dataset:
00819         if (strcmp(localname, "Dataset") == 0)
00820             parser->pop_state();
00821         else
00822             DDXParser::ddx_fatal_error(parser,
00823                                        "Expected an end Dataset tag; found '%s' instead.",
00824                                        localname);
00825         break;
00826 
00827     case inside_attribute_container:
00828         if (strcmp(localname, "Attribute") == 0) {
00829             parser->pop_state();
00830             parser->at_stack.pop();     // pop when leaving a container.
00831         }
00832         else
00833             DDXParser::ddx_fatal_error(parser,
00834                                        "Expected an end Attribute tag; found '%s' instead.",
00835                                        localname);
00836         break;
00837 
00838     case inside_attribute:
00839         if (strcmp(localname, "Attribute") == 0)
00840             parser->pop_state();
00841         else
00842             DDXParser::ddx_fatal_error(parser,
00843                                        "Expected an end Attribute tag; found '%s' instead.",
00844                                        localname);
00845         break;
00846 
00847     case inside_attribute_value:
00848         if (strcmp(localname, "value") == 0) {
00849             parser->pop_state();
00850             AttrTable *atp = parser->at_stack.top();
00851             atp->append_attr(parser->dods_attr_name,
00852                              parser->dods_attr_type, parser->char_data);
00853             parser->char_data = "";     // Null this after use.
00854         }
00855         else
00856             DDXParser::ddx_fatal_error(parser,
00857                                        "Expected an end value tag; found '%s' instead.",
00858                                        localname);
00859 
00860         break;
00861 
00862     case inside_other_xml_attribute: {
00863             if (strcmp(localname, "Attribute") == 0
00864                     && parser->root_ns == (const char *)URI) {
00865 
00866                 DBGN(cerr << endl << "\t Popping the 'inside_other_xml_attribute' state"
00867                         << endl);
00868 
00869                 parser->pop_state();
00870 
00871                 AttrTable *atp = parser->at_stack.top();
00872                 atp->append_attr(parser->dods_attr_name,
00873                         parser->dods_attr_type, parser->other_xml);
00874 
00875                 parser->other_xml = ""; // Null this after use.
00876             }
00877             else {
00878                 DBGN(cerr << endl << "\t inside_other_xml_attribute: " << localname
00879                         << ", depth: " << parser->other_xml_depth << endl);
00880                 if (parser->other_xml_depth == 0)
00881                     DDXParser::ddx_fatal_error(parser,
00882                                                "Expected an OtherXML attribute to end! Instead I found '%s'",
00883                                                localname);
00884                 parser->other_xml_depth--;
00885 
00886                 parser->other_xml.append("</");
00887                 if (prefix) {
00888                     parser->other_xml.append((const char *)prefix);
00889                     parser->other_xml.append(":");
00890                 }
00891                 parser->other_xml.append(localname);
00892                 parser->other_xml.append(">");
00893             }
00894             break;
00895         }
00896         // Alias is busted in libdap++ 05/29/03 jhrg
00897     case inside_alias:
00898         parser->pop_state();
00899         break;
00900 
00901     case inside_simple_type:
00902         if (is_simple_type(localname) != dods_null_c) {
00903             parser->pop_state();
00904             BaseType *btp = parser->bt_stack.top();
00905             parser->bt_stack.pop();
00906             parser->at_stack.pop();
00907 
00908             BaseType *parent = parser->bt_stack.top();
00909 
00910             if (parent->is_vector_type() || parent->is_constructor_type())
00911                 parent->add_var(btp);
00912             else
00913                 DDXParser::ddx_fatal_error(parser,
00914                                            "Tried to add the simple-type variable '%s' to a non-constructor type (%s %s).",
00915                                            localname,
00916                                            parser->bt_stack.top()->
00917                                            type_name().c_str(),
00918                                            parser->bt_stack.top()->name().
00919                                            c_str());
00920         }
00921         else
00922             DDXParser::ddx_fatal_error(parser,
00923                                        "Expected an end tag for a simple type; found '%s' instead.",
00924                                        localname);
00925         break;
00926 
00927     case inside_array:
00928         parser->finish_variable(localname, dods_array_c, "Array");
00929         break;
00930 
00931     case inside_dimension:
00932         if (strcmp(localname, "dimension") == 0)
00933             parser->pop_state();
00934         else
00935             DDXParser::ddx_fatal_error(parser,
00936                                        "Expected an end dimension tag; found '%s' instead.",
00937                                        localname);
00938         break;
00939 
00940     case inside_structure:
00941         parser->finish_variable(localname, dods_structure_c, "Structure");
00942         break;
00943 
00944     case inside_sequence:
00945         parser->finish_variable(localname, dods_sequence_c, "Sequence");
00946         break;
00947 
00948     case inside_grid:
00949         parser->finish_variable(localname, dods_grid_c, "Grid");
00950         break;
00951 
00952     case inside_map:
00953         parser->finish_variable(localname, dods_array_c, "Map");
00954         break;
00955 
00956     case inside_blob_href:
00957         if (strcmp(localname, "blob") == 0 || strcmp(localname, "dataBLOB") == 0)
00958             parser->pop_state();
00959         else
00960             DDXParser::ddx_fatal_error(parser,
00961                                        "Expected an end dataBLOB/blob tag; found '%s' instead.",
00962                                        localname);
00963         break;
00964 
00965     case parser_unknown:
00966         parser->pop_state();
00967         break;
00968 
00969     case parser_error:
00970         break;
00971     }
00972 
00973 
00974     DBGN(cerr << " ... " << states[parser->get_state()] << endl);
00975 }
00976 
00980 void DDXParser::ddx_get_characters(void * p, const xmlChar * ch, int len)
00981 {
00982     DDXParser *parser = static_cast<DDXParser*>(p);
00983 
00984     switch (parser->get_state()) {
00985         case inside_attribute_value:
00986             parser->char_data.append((const char *)(ch), len);
00987             DBG2(cerr << "Characters: '" << parser->char_data << "'" << endl);
00988             break;
00989 
00990         case inside_other_xml_attribute:
00991             parser->other_xml.append((const char *)(ch), len);
00992             DBG2(cerr << "Other XML Characters: '" << parser->other_xml << "'" << endl);
00993             break;
00994 
00995         default:
00996             break;
00997     }
00998 }
00999 
01004 void DDXParser::ddx_ignoreable_whitespace(void *p, const xmlChar *ch,
01005         int len)
01006 {
01007     DDXParser *parser = static_cast<DDXParser*>(p);
01008 
01009     switch (parser->get_state()) {
01010          case inside_other_xml_attribute:
01011              parser->other_xml.append((const char *)(ch), len);
01012              break;
01013 
01014          default:
01015              break;
01016     }
01017 }
01018 
01024 void DDXParser::ddx_get_cdata(void *p, const xmlChar *value, int len)
01025 {
01026     DDXParser *parser = static_cast<DDXParser*>(p);
01027 
01028     switch (parser->get_state()) {
01029          case inside_other_xml_attribute:
01030              parser->other_xml.append((const char *)(value), len);
01031              break;
01032 
01033          case parser_unknown:
01034              break;
01035 
01036          default:
01037              DDXParser::ddx_fatal_error(parser,
01038                                         "Found a CData block but none are allowed by DAP.");
01039 
01040              break;
01041     }
01042 }
01043 
01048 xmlEntityPtr DDXParser::ddx_get_entity(void *, const xmlChar * name)
01049 {
01050     return xmlGetPredefinedEntity(name);
01051 }
01052 
01060 void DDXParser::ddx_fatal_error(void * p, const char *msg, ...)
01061 {
01062     va_list args;
01063     DDXParser *parser = static_cast<DDXParser*>(p);
01064 
01065     parser->set_state(parser_error);
01066 
01067     va_start(args, msg);
01068     char str[1024];
01069     vsnprintf(str, 1024, msg, args);
01070     va_end(args);
01071 
01072     int line = xmlSAX2GetLineNumber(parser->ctxt);
01073 
01074     parser->error_msg += "At line " + long_to_string(line) + ": ";
01075     parser->error_msg += string(str) + string("\n");
01076 }
01077 
01079 
01080 void DDXParser::cleanup_parse(xmlParserCtxtPtr & context) const
01081 {
01082     if (!context->wellFormed) {
01083         context->sax = NULL;
01084         xmlFreeParserCtxt(context);
01085         throw
01086         DDXParseFailed(string
01087                        ("\nThe DDX is not a well formed XML document.\n")
01088                        + error_msg);
01089     }
01090 
01091     if (!context->valid) {
01092         context->sax = NULL;
01093         xmlFreeParserCtxt(context);
01094         throw DDXParseFailed(string("\nThe DDX is not a valid document.\n")
01095                              + error_msg);
01096     }
01097 
01098     if (get_state() == parser_error) {
01099         context->sax = NULL;
01100         xmlFreeParserCtxt(context);
01101         throw DDXParseFailed(string("\nError parsing DDX response.\n") +
01102                              error_msg);
01103     }
01104 
01105     context->sax = NULL;
01106     xmlFreeParserCtxt(context);
01107 }
01108 
01111 void DDXParser::intern_stream(FILE *in, DDS *dest_dds, string &cid,
01112         const string &boundary)
01113 {
01114     // Code example from libxml2 docs re: read from a stream.
01115 
01116     if (!in || feof(in) || ferror(in))
01117         throw InternalErr(__FILE__, __LINE__,
01118                           "Input stream not open or read error");
01119 
01120     const int size = 1024;
01121     char chars[size];
01122 
01123     int res = fread(chars, 1, 4, in);
01124     if (res > 0) {
01125         xmlParserCtxtPtr context =
01126             xmlCreatePushParserCtxt(NULL, NULL, chars, res, "stream");
01127 
01128         ctxt = context;         // need ctxt for error messages
01129         dds = dest_dds;         // dump values here
01130         blob_href = &cid;       // cid goes here
01131 
01132         xmlSAXHandler ddx_sax_parser;
01133         memset( &ddx_sax_parser, 0, sizeof(xmlSAXHandler) );
01134 
01135         ddx_sax_parser.getEntity = &DDXParser::ddx_get_entity;
01136         ddx_sax_parser.startDocument = &DDXParser::ddx_start_document;
01137         ddx_sax_parser.endDocument = &DDXParser::ddx_end_document;
01138         ddx_sax_parser.characters = &DDXParser::ddx_get_characters;
01139         ddx_sax_parser.ignorableWhitespace = &DDXParser::ddx_ignoreable_whitespace;
01140         ddx_sax_parser.cdataBlock = &DDXParser::ddx_get_cdata;
01141         ddx_sax_parser.warning = &DDXParser::ddx_fatal_error;
01142         ddx_sax_parser.error = &DDXParser::ddx_fatal_error;
01143         ddx_sax_parser.fatalError = &DDXParser::ddx_fatal_error;
01144         ddx_sax_parser.initialized = XML_SAX2_MAGIC;
01145         ddx_sax_parser.startElementNs = &DDXParser::ddx_sax2_start_element;
01146         ddx_sax_parser.endElementNs = &DDXParser::ddx_sax2_end_element;
01147 
01148         context->sax = &ddx_sax_parser;
01149         context->userData = this;
01150         context->validate = true;
01151 
01152 
01153         while ((fgets(chars, size, in) > 0) && !is_boundary(chars, boundary)) {
01154             chars[size-1] = '\0';
01155             DBG(cerr << "line: " << chars << endl);
01156             xmlParseChunk(ctxt, chars, strlen(chars), 0);
01157         }
01158         // This call ends the parse: The fourth argument of xmlParseChunk is
01159         // the bool 'terminate.'
01160         xmlParseChunk(ctxt, chars, 0, 1);
01161 
01162         cleanup_parse(context);
01163     }
01164 }
01165 
01166 
01178 void DDXParser::intern(const string & document, DDS * dest_dds, string &cid)
01179 {
01180     // Create the context pointer explicitly so that we can store a pointer
01181     // to it in the DDXParser instance. This provides a way to generate our
01182     // own error messages *with* line numbers. The messages are pretty
01183     // meaningless otherwise. This means that we use an interface from the
01184     // 'parser internals' header, and not the 'parser' header. However, this
01185     // interface is also used in one of the documented examples, so it's
01186     // probably pretty stable. 06/02/03 jhrg
01187     xmlParserCtxtPtr context = xmlCreateFileParserCtxt(document.c_str());
01188     if (!context)
01189         throw
01190         DDXParseFailed(string
01191                        ("Could not initialize the parser with the file: '")
01192                        + document + string("'."));
01193 
01194     dds = dest_dds;             // dump values here
01195     blob_href = &cid;
01196     ctxt = context;             // need ctxt for error messages
01197 
01198     xmlSAXHandler ddx_sax_parser;
01199     memset( &ddx_sax_parser, 0, sizeof(xmlSAXHandler) );
01200 
01201     ddx_sax_parser.getEntity = &DDXParser::ddx_get_entity;
01202     ddx_sax_parser.startDocument = &DDXParser::ddx_start_document;
01203     ddx_sax_parser.endDocument = &DDXParser::ddx_end_document;
01204     ddx_sax_parser.characters = &DDXParser::ddx_get_characters;
01205     ddx_sax_parser.ignorableWhitespace = &DDXParser::ddx_ignoreable_whitespace;
01206     ddx_sax_parser.cdataBlock = &DDXParser::ddx_get_cdata;
01207     ddx_sax_parser.warning = &DDXParser::ddx_fatal_error;
01208     ddx_sax_parser.error = &DDXParser::ddx_fatal_error;
01209     ddx_sax_parser.fatalError = &DDXParser::ddx_fatal_error;
01210     ddx_sax_parser.initialized = XML_SAX2_MAGIC;
01211     ddx_sax_parser.startElementNs = &DDXParser::ddx_sax2_start_element;
01212     ddx_sax_parser.endElementNs = &DDXParser::ddx_sax2_end_element;
01213 
01214     context->sax = &ddx_sax_parser;
01215     context->userData = this;
01216     context->validate = false;
01217 
01218     xmlParseDocument(context);
01219 
01220     cleanup_parse(context);
01221 }
01222 
01223 } // namespace libdap