Blame DDXParserSAX2.cc

Packit a4aae4
Packit a4aae4
// -*- mode: c++; c-basic-offset:4 -*-
Packit a4aae4
Packit a4aae4
// This file is part of libdap, A C++ implementation of the OPeNDAP Data
Packit a4aae4
// Access Protocol.
Packit a4aae4
Packit a4aae4
// Copyright (c) 2003 OPeNDAP, Inc.
Packit a4aae4
// Author: James Gallagher <jgallagher@opendap.org>
Packit a4aae4
//
Packit a4aae4
// This library is free software; you can redistribute it and/or
Packit a4aae4
// modify it under the terms of the GNU Lesser General Public
Packit a4aae4
// License as published by the Free Software Foundation; either
Packit a4aae4
// version 2.1 of the License, or (at your option) any later version.
Packit a4aae4
//
Packit a4aae4
// This library is distributed in the hope that it will be useful,
Packit a4aae4
// but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit a4aae4
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit a4aae4
// Lesser General Public License for more details.
Packit a4aae4
//
Packit a4aae4
// You should have received a copy of the GNU Lesser General Public
Packit a4aae4
// License along with this library; if not, write to the Free Software
Packit a4aae4
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
Packit a4aae4
//
Packit a4aae4
// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
Packit a4aae4
Packit a4aae4
#include "config.h"
Packit a4aae4
Packit a4aae4
//#define DODS_DEBUG 1
Packit a4aae4
//#define DODS_DEBUG2 1
Packit a4aae4
Packit a4aae4
#include <cstring>
Packit a4aae4
#include <cstdarg>
Packit a4aae4
Packit a4aae4
#include "BaseType.h"
Packit a4aae4
#include "Byte.h"
Packit a4aae4
#include "Int16.h"
Packit a4aae4
#include "UInt16.h"
Packit a4aae4
#include "Int32.h"
Packit a4aae4
#include "UInt32.h"
Packit a4aae4
#include "Float32.h"
Packit a4aae4
#include "Float64.h"
Packit a4aae4
#include "Str.h"
Packit a4aae4
#include "Url.h"
Packit a4aae4
#include "Array.h"
Packit a4aae4
#include "Structure.h"
Packit a4aae4
#include "Sequence.h"
Packit a4aae4
#include "Grid.h"
Packit a4aae4
Packit a4aae4
#include "DDXParserSAX2.h"
Packit a4aae4
Packit a4aae4
#include "util.h"
Packit a4aae4
#include "mime_util.h"
Packit a4aae4
#include "debug.h"
Packit a4aae4
Packit a4aae4
namespace libdap {
Packit a4aae4
Packit a4aae4
#if defined(DODS_DEBUG) || defined(DODS_DEUG2)
Packit a4aae4
static const char *states[] =
Packit a4aae4
    {
Packit a4aae4
        "start",
Packit a4aae4
Packit a4aae4
        "dataset",
Packit a4aae4
Packit a4aae4
        "attribute_container",
Packit a4aae4
        "attribute",
Packit a4aae4
        "attribute_value",
Packit a4aae4
        "other_xml_attribute",
Packit a4aae4
Packit a4aae4
        "alias",
Packit a4aae4
Packit a4aae4
        "simple_type",
Packit a4aae4
Packit a4aae4
        "array",
Packit a4aae4
        "dimension",
Packit a4aae4
Packit a4aae4
        "grid",
Packit a4aae4
        "map",
Packit a4aae4
Packit a4aae4
        "structure",
Packit a4aae4
        "sequence",
Packit a4aae4
Packit a4aae4
        "blob href",
Packit a4aae4
Packit a4aae4
        "unknown",
Packit a4aae4
        "error"
Packit a4aae4
    };
Packit a4aae4
#endif
Packit a4aae4
// Glue the BaseTypeFactory to the enum-based factory defined statically
Packit a4aae4
// here.
Packit a4aae4
Packit a4aae4
BaseType *DDXParser::factory(Type t, const string & name)
Packit a4aae4
{
Packit a4aae4
    switch (t) {
Packit a4aae4
    case dods_byte_c:
Packit a4aae4
        return d_factory->NewByte(name);
Packit a4aae4
        break;
Packit a4aae4
Packit a4aae4
    case dods_int16_c:
Packit a4aae4
        return d_factory->NewInt16(name);
Packit a4aae4
        break;
Packit a4aae4
Packit a4aae4
    case dods_uint16_c:
Packit a4aae4
        return d_factory->NewUInt16(name);
Packit a4aae4
        break;
Packit a4aae4
Packit a4aae4
    case dods_int32_c:
Packit a4aae4
        return d_factory->NewInt32(name);
Packit a4aae4
        break;
Packit a4aae4
Packit a4aae4
    case dods_uint32_c:
Packit a4aae4
        return d_factory->NewUInt32(name);
Packit a4aae4
        break;
Packit a4aae4
Packit a4aae4
    case dods_float32_c:
Packit a4aae4
        return d_factory->NewFloat32(name);
Packit a4aae4
        break;
Packit a4aae4
Packit a4aae4
    case dods_float64_c:
Packit a4aae4
        return d_factory->NewFloat64(name);
Packit a4aae4
        break;
Packit a4aae4
Packit a4aae4
    case dods_str_c:
Packit a4aae4
        return d_factory->NewStr(name);
Packit a4aae4
        break;
Packit a4aae4
Packit a4aae4
    case dods_url_c:
Packit a4aae4
        return d_factory->NewUrl(name);
Packit a4aae4
        break;
Packit a4aae4
Packit a4aae4
    case dods_array_c:
Packit a4aae4
        return d_factory->NewArray(name);
Packit a4aae4
        break;
Packit a4aae4
Packit a4aae4
    case dods_structure_c:
Packit a4aae4
        return d_factory->NewStructure(name);
Packit a4aae4
        break;
Packit a4aae4
Packit a4aae4
    case dods_sequence_c:
Packit a4aae4
        return d_factory->NewSequence(name);
Packit a4aae4
        break;
Packit a4aae4
Packit a4aae4
    case dods_grid_c:
Packit a4aae4
        return d_factory->NewGrid(name);
Packit a4aae4
        break;
Packit a4aae4
Packit a4aae4
    default:
Packit a4aae4
        return 0;
Packit a4aae4
    }
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
static bool is_not(const char *name, const char *tag)
Packit a4aae4
{
Packit a4aae4
    return strcmp(name, tag) != 0;
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
void DDXParser::set_state(DDXParser::ParseState state)
Packit a4aae4
{
Packit a4aae4
    s.push(state);
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
DDXParser::ParseState DDXParser::get_state() const
Packit a4aae4
{
Packit a4aae4
    return s.top();
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
void DDXParser::pop_state()
Packit a4aae4
{
Packit a4aae4
    s.pop();
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** Dump XML attributes to local store so they can be easily manipulated.
Packit a4aae4
    Attribute names are always folded to lower case.
Packit a4aae4
    @param attrs The XML attribute array */
Packit a4aae4
void DDXParser::transfer_xml_attrs(const xmlChar **attributes, int nb_attributes)
Packit a4aae4
{
Packit a4aae4
    if (!attribute_table.empty())
Packit a4aae4
        attribute_table.clear(); // erase old attributes
Packit a4aae4
Packit a4aae4
    unsigned int index = 0;
Packit a4aae4
    for (int i = 0; i < nb_attributes; ++i, index += 5) {
Packit a4aae4
        // Make a value using the attribute name and the prefix, namespace URI
Packit a4aae4
        // and the value. The prefix might be null.
Packit a4aae4
        attribute_table.insert(map<string, XMLAttribute>::value_type(
Packit a4aae4
                string((const char *)attributes[index]),
Packit a4aae4
                XMLAttribute(attributes + index + 1)));
Packit a4aae4
Packit a4aae4
        DBG(cerr << "Attribute '" << (const char *)attributes[index] << "': "
Packit a4aae4
                << attribute_table[(const char *)attributes[index]].value << endl);
Packit a4aae4
    }
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
void DDXParser::transfer_xml_ns(const xmlChar **namespaces, int nb_namespaces)
Packit a4aae4
{
Packit a4aae4
    for (int i = 0; i < nb_namespaces; ++i ) {
Packit a4aae4
        // make a value with the prefix and namespace URI. The prefix might be
Packit a4aae4
        // null.
Packit a4aae4
        namespace_table.insert(map<string,string>::value_type(
Packit a4aae4
                namespaces[i*2] != 0 ? (const char *)namespaces[i*2] : "",
Packit a4aae4
                (const char *)namespaces[i*2+1]));
Packit a4aae4
    }
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** Is an attribute present? Attribute names are always lower case.
Packit a4aae4
    @note To use this method, first call transfer_xml_attrs.
Packit a4aae4
    @param attr The XML attribute
Packit a4aae4
    @return True if the XML attribute was present in the last tag */
Packit a4aae4
bool DDXParser::check_required_attribute(const string & attr)
Packit a4aae4
{
Packit a4aae4
    map < string, XMLAttribute >::iterator i = attribute_table.find(attr);
Packit a4aae4
    if (i == attribute_table.end())
Packit a4aae4
        ddx_fatal_error(this, "Required attribute '%s' not found.",
Packit a4aae4
                        attr.c_str());
Packit a4aae4
    return true;
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** Is an attribute present? Attribute names are always lower case.
Packit a4aae4
    @note To use this method, first call transfer_xml_attrs.
Packit a4aae4
    @param attr The XML attribute
Packit a4aae4
    @return True if the XML attribute was present in the last/current tag,
Packit a4aae4
    false otherwise. */
Packit a4aae4
bool DDXParser::check_attribute(const string & attr)
Packit a4aae4
{
Packit a4aae4
    return (attribute_table.find(attr) != attribute_table.end());
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** Given that an \c Attribute tag has just been read, determine whether the
Packit a4aae4
    element is a container or a simple type, set the state and, for a simple
Packit a4aae4
    type record the type and name for use when \c value elements are found.
Packit a4aae4
Packit a4aae4
    @note Modified to discriminate between OtherXML and the older DAP2.0
Packit a4aae4
    attribute types (container, Byte, ...).
Packit a4aae4
Packit a4aae4
    @param attrs The array of XML attribute values */
Packit a4aae4
void DDXParser::process_attribute_element(const xmlChar **attrs, int nb_attributes)
Packit a4aae4
{
Packit a4aae4
    // These methods set the state to parser_error if a problem is found.
Packit a4aae4
    transfer_xml_attrs(attrs, nb_attributes);
Packit a4aae4
Packit a4aae4
    bool error = !(check_required_attribute(string("name"))
Packit a4aae4
                   && check_required_attribute(string("type")));
Packit a4aae4
    if (error)
Packit a4aae4
        return;
Packit a4aae4
Packit a4aae4
    if (attribute_table["type"].value == "Container") {
Packit a4aae4
        set_state(inside_attribute_container);
Packit a4aae4
Packit a4aae4
        AttrTable *child;
Packit a4aae4
        AttrTable *parent = at_stack.top();
Packit a4aae4
Packit a4aae4
        child = parent->append_container(attribute_table["name"].value);
Packit a4aae4
        at_stack.push(child);   // save.
Packit a4aae4
        DBG2(cerr << "Pushing at" << endl);
Packit a4aae4
    }
Packit a4aae4
    else if (attribute_table["type"].value == "OtherXML") {
Packit a4aae4
        set_state(inside_other_xml_attribute);
Packit a4aae4
Packit a4aae4
        dods_attr_name = attribute_table["name"].value;
Packit a4aae4
        dods_attr_type = attribute_table["type"].value;
Packit a4aae4
    }
Packit a4aae4
    else {
Packit a4aae4
        set_state(inside_attribute);
Packit a4aae4
        // *** Modify parser. Add a special state for inside OtherXML since it
Packit a4aae4
        // does not use the <value> element.
Packit a4aae4
Packit a4aae4
        dods_attr_name = attribute_table["name"].value;
Packit a4aae4
        dods_attr_type = attribute_table["type"].value;
Packit a4aae4
    }
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** Given that an \c Alias tag has just been read, set the state and process
Packit a4aae4
    the alias.
Packit a4aae4
    @param attrs The XML attribute array */
Packit a4aae4
void DDXParser::process_attribute_alias(const xmlChar **attrs, int nb_attributes)
Packit a4aae4
{
Packit a4aae4
    transfer_xml_attrs(attrs, nb_attributes);
Packit a4aae4
    if (check_required_attribute(string("name"))
Packit a4aae4
        && check_required_attribute(string("attribute"))) {
Packit a4aae4
        set_state(inside_alias);
Packit a4aae4
        at_stack.top()->attr_alias(attribute_table["name"].value,
Packit a4aae4
                                   attribute_table["attribute"].value);
Packit a4aae4
    }
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** Given that a tag which opens a variable declaration has just been read,
Packit a4aae4
    create the variable. Once created, push the variable onto the stack of
Packit a4aae4
    variables, push that variables attribute table onto the attribute table
Packit a4aae4
    stack and update the state of the parser.
Packit a4aae4
    @param t The type of variable to create.
Packit a4aae4
    @param s The next state of the parser.
Packit a4aae4
    @param attrs the attributes read with the tag */
Packit a4aae4
void DDXParser::process_variable(Type t, ParseState s, const xmlChar **attrs,
Packit a4aae4
        int nb_attributes)
Packit a4aae4
{
Packit a4aae4
    transfer_xml_attrs(attrs, nb_attributes);
Packit a4aae4
Packit a4aae4
    set_state(s);
Packit a4aae4
Packit a4aae4
    if (bt_stack.top()->type() == dods_array_c
Packit a4aae4
            || check_required_attribute("name")) { // throws on error/false
Packit a4aae4
        BaseType *btp = factory(t, attribute_table["name"].value);
Packit a4aae4
        if (!btp) {
Packit a4aae4
            ddx_fatal_error(this, "Internal parser error; could not instantiate the variable '%s'.",
Packit a4aae4
                attribute_table["name"].value.c_str());
Packit a4aae4
        }
Packit a4aae4
        else {
Packit a4aae4
            // Only run this code if btp is not null! jhrg 9/14/15
Packit a4aae4
            // Once we make the new variable, we not only load it on to the
Packit a4aae4
            // BaseType stack, we also load its AttrTable on the AttrTable stack.
Packit a4aae4
            // The attribute processing software always operates on the AttrTable
Packit a4aae4
            // at the top of the AttrTable stack (at_stack).
Packit a4aae4
            bt_stack.push(btp);
Packit a4aae4
            at_stack.push(&btp->get_attr_table());
Packit a4aae4
        }
Packit a4aae4
    }
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** Given that a \c dimension tag has just been read, add that information to
Packit a4aae4
    the array on the top of the BaseType stack.
Packit a4aae4
    @param attrs The XML attributes included in the \c dimension tag */
Packit a4aae4
void DDXParser::process_dimension(const xmlChar **attrs, int nb_attributes)
Packit a4aae4
{
Packit a4aae4
    transfer_xml_attrs(attrs, nb_attributes);
Packit a4aae4
    if (check_required_attribute(string("size"))) {
Packit a4aae4
        set_state(inside_dimension);
Packit a4aae4
        Array *ap = dynamic_cast < Array * >(bt_stack.top());
Packit a4aae4
		if (!ap) {
Packit a4aae4
			ddx_fatal_error(this, "Parse error: Expected an array variable.");
Packit a4aae4
			return;
Packit a4aae4
		}
Packit a4aae4
		
Packit a4aae4
        ap->append_dim(atoi(attribute_table["size"].value.c_str()),
Packit a4aae4
                       attribute_table["name"].value);
Packit a4aae4
    }
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** Given that a \c blob tag has just been read, extract and save the CID
Packit a4aae4
    included in the element. */
Packit a4aae4
void DDXParser::process_blob(const xmlChar **attrs, int nb_attributes)
Packit a4aae4
{
Packit a4aae4
    transfer_xml_attrs(attrs, nb_attributes);
Packit a4aae4
    if (check_required_attribute(string("href"))) {
Packit a4aae4
        set_state(inside_blob_href);
Packit a4aae4
        *blob_href = attribute_table["href"].value;
Packit a4aae4
    }
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** Check to see if the current tag is either an \c Attribute or an \c Alias
Packit a4aae4
    start tag. This method is a glorified macro...
Packit a4aae4
Packit a4aae4
    @param name The start tag name
Packit a4aae4
    @param attrs The tag's XML attributes
Packit a4aae4
    @return True if the tag was an \c Attribute or \c Alias tag */
Packit a4aae4
inline bool
Packit a4aae4
DDXParser::is_attribute_or_alias(const char *name, const xmlChar **attrs,
Packit a4aae4
        int nb_attributes)
Packit a4aae4
{
Packit a4aae4
    if (strcmp(name, "Attribute") == 0) {
Packit a4aae4
        process_attribute_element(attrs, nb_attributes);
Packit a4aae4
        // next state: inside_attribtue or inside_attribute_container
Packit a4aae4
        return true;
Packit a4aae4
    }
Packit a4aae4
    else if (strcmp(name, "Alias") == 0) {
Packit a4aae4
        process_attribute_alias(attrs, nb_attributes);
Packit a4aae4
        // next state: inside_alias
Packit a4aae4
        return true;
Packit a4aae4
    }
Packit a4aae4
Packit a4aae4
    return false;
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** Check to see if the current tag is the start of a variable declaration.
Packit a4aae4
    If so, process it. A glorified macro...
Packit a4aae4
    @param name The start tag name
Packit a4aae4
    @param attrs The tag's XML attributes
Packit a4aae4
    @return True if the tag was a variable tag */
Packit a4aae4
inline bool DDXParser::is_variable(const char *name, const xmlChar **attrs,
Packit a4aae4
        int nb_attributes)
Packit a4aae4
{
Packit a4aae4
    Type t = get_type(name);
Packit a4aae4
    //if ((t = is_simple_type(name)) != dods_null_c) {
Packit a4aae4
    if (is_simple_type(t)) {
Packit a4aae4
        process_variable(t, inside_simple_type, attrs, nb_attributes);
Packit a4aae4
        return true;
Packit a4aae4
    }
Packit a4aae4
    else if (strcmp(name, "Array") == 0) {
Packit a4aae4
        process_variable(dods_array_c, inside_array, attrs, nb_attributes);
Packit a4aae4
        return true;
Packit a4aae4
    }
Packit a4aae4
    else if (strcmp(name, "Structure") == 0) {
Packit a4aae4
        process_variable(dods_structure_c, inside_structure, attrs, nb_attributes);
Packit a4aae4
        return true;
Packit a4aae4
    }
Packit a4aae4
    else if (strcmp(name, "Sequence") == 0) {
Packit a4aae4
        process_variable(dods_sequence_c, inside_sequence, attrs, nb_attributes);
Packit a4aae4
        return true;
Packit a4aae4
    }
Packit a4aae4
    else if (strcmp(name, "Grid") == 0) {
Packit a4aae4
        process_variable(dods_grid_c, inside_grid, attrs, nb_attributes);
Packit a4aae4
        return true;
Packit a4aae4
    }
Packit a4aae4
Packit a4aae4
    return false;
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
void DDXParser::finish_variable(const char *tag, Type t, const char *expected)
Packit a4aae4
{
Packit a4aae4
    if (strcmp(tag, expected) != 0) {
Packit a4aae4
        DDXParser::ddx_fatal_error(this,
Packit a4aae4
                                   "Expected an end tag for a %s; found '%s' instead.",
Packit a4aae4
                                   expected, tag);
Packit a4aae4
        return;
Packit a4aae4
    }
Packit a4aae4
Packit a4aae4
    pop_state();
Packit a4aae4
Packit a4aae4
    BaseType *btp = bt_stack.top();
Packit a4aae4
Packit a4aae4
    bt_stack.pop();
Packit a4aae4
    at_stack.pop();
Packit a4aae4
Packit a4aae4
    if (btp->type() != t) {
Packit a4aae4
        DDXParser::ddx_fatal_error(this,
Packit a4aae4
                                   "Internal error: Expected a %s variable.",
Packit a4aae4
                                   expected);
Packit a4aae4
        delete btp;
Packit a4aae4
        return;
Packit a4aae4
    }
Packit a4aae4
    // Once libxml2 validates, this can go away. 05/30/03 jhrg
Packit a4aae4
    if (t == dods_array_c
Packit a4aae4
        && static_cast<Array*>(btp)->dimensions() == 0) {
Packit a4aae4
        DDXParser::ddx_fatal_error(this,
Packit a4aae4
                                   "No dimension element included in the Array '%s'.",
Packit a4aae4
                                   btp->name().c_str());
Packit a4aae4
        delete btp;
Packit a4aae4
        return;
Packit a4aae4
    }
Packit a4aae4
Packit a4aae4
    BaseType *parent = bt_stack.top();
Packit a4aae4
Packit a4aae4
    if (!(parent->is_vector_type() || parent->is_constructor_type())) {
Packit a4aae4
        DDXParser::ddx_fatal_error(this,
Packit a4aae4
                                   "Tried to add the array variable '%s' to a non-constructor type (%s %s).",
Packit a4aae4
                                   tag,
Packit a4aae4
                                   bt_stack.top()->type_name().c_str(),
Packit a4aae4
                                   bt_stack.top()->name().c_str());
Packit a4aae4
        delete btp;
Packit a4aae4
        return;
Packit a4aae4
    }
Packit a4aae4
Packit a4aae4
    parent->add_var_nocopy(btp);
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** @name SAX Parser Callbacks
Packit a4aae4
Packit a4aae4
    These methods are declared static in the class header. This gives them C
Packit a4aae4
    linkage which allows them to be used as callbacks by the SAX parser
Packit a4aae4
    engine. */
Packit a4aae4
//@{
Packit a4aae4
Packit a4aae4
/** Initialize the SAX parser state object. This object is passed to each
Packit a4aae4
    callback as a void pointer. The initial state is parser_start.
Packit a4aae4
Packit a4aae4
    @param p The SAX parser  */
Packit a4aae4
void DDXParser::ddx_start_document(void * p)
Packit a4aae4
{
Packit a4aae4
    DDXParser *parser = static_cast<DDXParser*>(p);
Packit a4aae4
    parser->error_msg = "";
Packit a4aae4
    parser->char_data = "";
Packit a4aae4
Packit a4aae4
    // init attr table stack.
Packit a4aae4
    parser->at_stack.push(&parser->dds->get_attr_table());
Packit a4aae4
Packit a4aae4
    // Trick; DDS *should* be a child of Structure. To simplify parsing,
Packit a4aae4
    // stuff a Structure on the bt_stack and dump the top level variables
Packit a4aae4
    // there. Once we're done, transfer the variables to the DDS.
Packit a4aae4
    parser->bt_stack.push(new Structure("dummy_dds"));
Packit a4aae4
Packit a4aae4
    parser->set_state(parser_start);
Packit a4aae4
Packit a4aae4
    DBG2(cerr << "Parser state: " << states[parser->get_state()] << endl);
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** Clean up after finishing a parse.
Packit a4aae4
    @param p The SAX parser  */
Packit a4aae4
void DDXParser::ddx_end_document(void * p)
Packit a4aae4
{
Packit a4aae4
    DDXParser *parser = static_cast<DDXParser*>(p);
Packit a4aae4
    DBG2(cerr << "Ending state == " << states[parser->get_state()] <<
Packit a4aae4
         endl);
Packit a4aae4
Packit a4aae4
    if (parser->get_state() != parser_start)
Packit a4aae4
        DDXParser::ddx_fatal_error(parser, "The document contained unbalanced tags.");
Packit a4aae4
Packit a4aae4
    // If we've found any sort of error, don't make the DDX; intern() will
Packit a4aae4
    // take care of the error.
Packit a4aae4
    if (parser->get_state() == parser_error) {
Packit a4aae4
        return;
Packit a4aae4
    }
Packit a4aae4
Packit a4aae4
    // Pop the temporary Structure off the stack and transfer its variables
Packit a4aae4
    // to the DDS.
Packit a4aae4
    Constructor *cp = dynamic_cast < Constructor * >(parser->bt_stack.top());
Packit a4aae4
    if (!cp) {
Packit a4aae4
        delete parser->bt_stack.top();
Packit a4aae4
        parser->bt_stack.pop();
Packit a4aae4
    	ddx_fatal_error(parser, "Parse error: Expected a Structure, Sequence or Grid variable.");
Packit a4aae4
		return;
Packit a4aae4
    }
Packit a4aae4
Packit a4aae4
    for (Constructor::Vars_iter i = cp->var_begin(); i != cp->var_end(); ++i) {
Packit a4aae4
        (*i)->set_parent(0);        // top-level vars have no parents
Packit a4aae4
        parser->dds->add_var(*i);
Packit a4aae4
    }
Packit a4aae4
Packit a4aae4
    delete parser->bt_stack.top();
Packit a4aae4
    parser->bt_stack.pop();
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
void DDXParser::ddx_sax2_start_element(void *p,
Packit a4aae4
        const xmlChar *l, const xmlChar *prefix, const xmlChar *URI,
Packit a4aae4
        int nb_namespaces, const xmlChar **namespaces,
Packit a4aae4
        int nb_attributes, int /*nb_defaulted*/, const xmlChar **attributes)
Packit a4aae4
{
Packit a4aae4
    DDXParser *parser = static_cast<DDXParser*>(p);
Packit a4aae4
    const char *localname = (const char *)l;
Packit a4aae4
Packit a4aae4
    DBG2(cerr << "start element: " << localname << ", states: "
Packit a4aae4
         << states[parser->get_state()]);
Packit a4aae4
Packit a4aae4
    switch (parser->get_state()) {
Packit a4aae4
    case parser_start:
Packit a4aae4
        if (strcmp(localname, "Dataset") == 0) {
Packit a4aae4
            parser->set_state(inside_dataset);
Packit a4aae4
            parser->root_ns = URI != 0 ? (const char *)URI: "";
Packit a4aae4
            parser->transfer_xml_attrs(attributes, nb_attributes);
Packit a4aae4
Packit a4aae4
            if (parser->check_required_attribute(string("name")))
Packit a4aae4
                parser->dds->set_dataset_name(parser->attribute_table["name"].value);
Packit a4aae4
Packit a4aae4
            if (parser->check_attribute("dapVersion"))
Packit a4aae4
                parser->dds->set_dap_version(parser->attribute_table["dapVersion"].value);
Packit a4aae4
        }
Packit a4aae4
        else
Packit a4aae4
            DDXParser::ddx_fatal_error(parser,
Packit a4aae4
                                       "Expected response to start with a Dataset element; found '%s' instead.",
Packit a4aae4
                                       localname);
Packit a4aae4
        break;
Packit a4aae4
Packit a4aae4
    case inside_dataset:
Packit a4aae4
        if (parser->is_attribute_or_alias(localname, attributes, nb_attributes))
Packit a4aae4
            break;
Packit a4aae4
        else if (parser->is_variable(localname, attributes, nb_attributes))
Packit a4aae4
            break;
Packit a4aae4
        else if (strcmp(localname, "blob") == 0 || strcmp(localname, "dataBLOB") == 0) {
Packit a4aae4
            parser->process_blob(attributes, nb_attributes);
Packit a4aae4
            // next state: inside_data_blob
Packit a4aae4
        }
Packit a4aae4
        else
Packit a4aae4
            DDXParser::ddx_fatal_error(parser,
Packit a4aae4
                                       "Expected an Attribute, Alias or variable element; found '%s' instead.",
Packit a4aae4
                                       localname);
Packit a4aae4
        break;
Packit a4aae4
Packit a4aae4
    case inside_attribute_container:
Packit a4aae4
        if (parser->is_attribute_or_alias(localname, attributes, nb_attributes))
Packit a4aae4
            break;
Packit a4aae4
        else
Packit a4aae4
            DDXParser::ddx_fatal_error(parser,
Packit a4aae4
                                       "Expected an Attribute or Alias element; found '%s' instead.",
Packit a4aae4
                                       localname);
Packit a4aae4
        break;
Packit a4aae4
Packit a4aae4
    case inside_attribute:
Packit a4aae4
        if (parser->is_attribute_or_alias(localname, attributes, nb_attributes))
Packit a4aae4
            break;
Packit a4aae4
        else if (strcmp(localname, "value") == 0)
Packit a4aae4
            parser->set_state(inside_attribute_value);
Packit a4aae4
        else
Packit a4aae4
            ddx_fatal_error(parser,
Packit a4aae4
                            "Expected an 'Attribute', 'Alias' or 'value' element; found '%s' instead.",
Packit a4aae4
                            localname);
Packit a4aae4
        break;
Packit a4aae4
Packit a4aae4
    case inside_attribute_value:
Packit a4aae4
        ddx_fatal_error(parser,
Packit a4aae4
                        "Internal parser error; unexpected state, inside value while processing element '%s'.",
Packit a4aae4
                        localname);
Packit a4aae4
        break;
Packit a4aae4
Packit a4aae4
    case inside_other_xml_attribute:
Packit a4aae4
        DBGN(cerr << endl << "\t inside_other_xml_attribute: " << localname << endl);
Packit a4aae4
Packit a4aae4
        parser->other_xml_depth++;
Packit a4aae4
Packit a4aae4
        // Accumulate the elements here
Packit a4aae4
Packit a4aae4
        parser->other_xml.append("<");
Packit a4aae4
        if (prefix) {
Packit a4aae4
            parser->other_xml.append((const char *)prefix);
Packit a4aae4
            parser->other_xml.append(":");
Packit a4aae4
        }
Packit a4aae4
        parser->other_xml.append(localname);
Packit a4aae4
Packit a4aae4
        if (nb_namespaces != 0) {
Packit a4aae4
            parser->transfer_xml_ns(namespaces, nb_namespaces);
Packit a4aae4
Packit a4aae4
            for (map<string,string>::iterator i = parser->namespace_table.begin();
Packit a4aae4
                i != parser->namespace_table.end();
Packit a4aae4
                ++i) {
Packit a4aae4
                parser->other_xml.append(" xmlns");
Packit a4aae4
                if (!i->first.empty()) {
Packit a4aae4
                    parser->other_xml.append(":");
Packit a4aae4
                    parser->other_xml.append(i->first);
Packit a4aae4
                }
Packit a4aae4
                parser->other_xml.append("=\"");
Packit a4aae4
                parser->other_xml.append(i->second);
Packit a4aae4
                parser->other_xml.append("\"");
Packit a4aae4
            }
Packit a4aae4
        }
Packit a4aae4
Packit a4aae4
        if (nb_attributes != 0) {
Packit a4aae4
            parser->transfer_xml_attrs(attributes, nb_attributes);
Packit a4aae4
            for (XMLAttrMap::iterator i = parser->attr_table_begin();
Packit a4aae4
                i != parser->attr_table_end();
Packit a4aae4
                ++i) {
Packit a4aae4
                parser->other_xml.append(" ");
Packit a4aae4
                if (!i->second.prefix.empty()) {
Packit a4aae4
                    parser->other_xml.append(i->second.prefix);
Packit a4aae4
                    parser->other_xml.append(":");
Packit a4aae4
                }
Packit a4aae4
                parser->other_xml.append(i->first);
Packit a4aae4
                parser->other_xml.append("=\"");
Packit a4aae4
                parser->other_xml.append(i->second.value);
Packit a4aae4
                parser->other_xml.append("\"");
Packit a4aae4
            }
Packit a4aae4
        }
Packit a4aae4
Packit a4aae4
        parser->other_xml.append(">");
Packit a4aae4
        break;
Packit a4aae4
Packit a4aae4
    case inside_alias:
Packit a4aae4
        ddx_fatal_error(parser,
Packit a4aae4
                        "Internal parser error; unexpected state, inside alias while processing element '%s'.",
Packit a4aae4
                        localname);
Packit a4aae4
        break;
Packit a4aae4
Packit a4aae4
    case inside_simple_type:
Packit a4aae4
        if (parser->is_attribute_or_alias(localname, attributes, nb_attributes))
Packit a4aae4
            break;
Packit a4aae4
        else
Packit a4aae4
            ddx_fatal_error(parser,
Packit a4aae4
                            "Expected an 'Attribute' or 'Alias' element; found '%s' instead.",
Packit a4aae4
                            localname);
Packit a4aae4
        break;
Packit a4aae4
Packit a4aae4
    case inside_array:
Packit a4aae4
        if (parser->is_attribute_or_alias(localname, attributes, nb_attributes))
Packit a4aae4
            break;
Packit a4aae4
        else if (is_not(localname, "Array")
Packit a4aae4
                && parser->is_variable(localname, attributes, nb_attributes))
Packit a4aae4
            break;
Packit a4aae4
        else if (strcmp(localname, "dimension") == 0) {
Packit a4aae4
            parser->process_dimension(attributes, nb_attributes);
Packit a4aae4
            // next state: inside_dimension
Packit a4aae4
        }
Packit a4aae4
        else
Packit a4aae4
            ddx_fatal_error(parser,
Packit a4aae4
                            "Expected an 'Attribute' or 'Alias' element; found '%s' instead.",
Packit a4aae4
                            localname);
Packit a4aae4
        break;
Packit a4aae4
Packit a4aae4
    case inside_dimension:
Packit a4aae4
        ddx_fatal_error(parser,
Packit a4aae4
                        "Internal parser error; unexpected state, inside dimension while processing element '%s'.",
Packit a4aae4
                        localname);
Packit a4aae4
        break;
Packit a4aae4
Packit a4aae4
    case inside_structure:
Packit a4aae4
        if (parser->is_attribute_or_alias(localname, attributes, nb_attributes))
Packit a4aae4
            break;
Packit a4aae4
        else if (parser->is_variable(localname, attributes, nb_attributes))
Packit a4aae4
            break;
Packit a4aae4
        else
Packit a4aae4
            DDXParser::ddx_fatal_error(parser,
Packit a4aae4
                                       "Expected an Attribute, Alias or variable element; found '%s' instead.",
Packit a4aae4
                                       localname);
Packit a4aae4
        break;
Packit a4aae4
Packit a4aae4
    case inside_sequence:
Packit a4aae4
        if (parser->is_attribute_or_alias(localname, attributes, nb_attributes))
Packit a4aae4
            break;
Packit a4aae4
        else if (parser->is_variable(localname, attributes, nb_attributes))
Packit a4aae4
            break;
Packit a4aae4
        else
Packit a4aae4
            DDXParser::ddx_fatal_error(parser,
Packit a4aae4
                                       "Expected an Attribute, Alias or variable element; found '%s' instead.",
Packit a4aae4
                                       localname);
Packit a4aae4
        break;
Packit a4aae4
Packit a4aae4
    case inside_grid:
Packit a4aae4
        if (parser->is_attribute_or_alias(localname, attributes, nb_attributes))
Packit a4aae4
            break;
Packit a4aae4
        else if (strcmp(localname, "Array") == 0)
Packit a4aae4
            parser->process_variable(dods_array_c, inside_array, attributes, nb_attributes);
Packit a4aae4
        else if (strcmp(localname, "Map") == 0)
Packit a4aae4
            parser->process_variable(dods_array_c, inside_map, attributes, nb_attributes);
Packit a4aae4
        else
Packit a4aae4
            DDXParser::ddx_fatal_error(parser,
Packit a4aae4
                                       "Expected an Attribute, Alias or variable element; found '%s' instead.",
Packit a4aae4
                                       localname);
Packit a4aae4
        break;
Packit a4aae4
Packit a4aae4
    case inside_map:
Packit a4aae4
        if (parser->is_attribute_or_alias(localname, attributes, nb_attributes))
Packit a4aae4
            break;
Packit a4aae4
        else if (is_not(localname, "Array") && is_not(localname, "Sequence")
Packit a4aae4
                 && is_not(localname, "Grid")
Packit a4aae4
                 && parser->is_variable(localname, attributes, nb_attributes))
Packit a4aae4
            break;
Packit a4aae4
        else if (strcmp(localname, "dimension") == 0) {
Packit a4aae4
            parser->process_dimension(attributes, nb_attributes);
Packit a4aae4
            // next state: inside_dimension
Packit a4aae4
        }
Packit a4aae4
        else
Packit a4aae4
            ddx_fatal_error(parser,
Packit a4aae4
                            "Expected an 'Attribute', 'Alias', variable or 'dimension' element; found '%s' instead.",
Packit a4aae4
                            localname);
Packit a4aae4
        break;
Packit a4aae4
Packit a4aae4
    case inside_blob_href:
Packit a4aae4
        ddx_fatal_error(parser,
Packit a4aae4
                        "Internal parser error; unexpected state, inside blob href while processing element '%s'.",
Packit a4aae4
                        localname);
Packit a4aae4
        break;
Packit a4aae4
Packit a4aae4
    case parser_unknown:
Packit a4aae4
        // *** Never used? If so remove/error
Packit a4aae4
        parser->set_state(parser_unknown);
Packit a4aae4
        break;
Packit a4aae4
Packit a4aae4
    case parser_error:
Packit a4aae4
        break;
Packit a4aae4
    }
Packit a4aae4
Packit a4aae4
    DBGN(cerr << " ... " << states[parser->get_state()] << endl);
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
void DDXParser::ddx_sax2_end_element(void *p, const xmlChar *l,
Packit a4aae4
        const xmlChar *prefix, const xmlChar *URI)
Packit a4aae4
{
Packit a4aae4
    DDXParser *parser = static_cast<DDXParser*>(p);
Packit a4aae4
    const char *localname = (const char *)l;
Packit a4aae4
Packit a4aae4
    DBG2(cerr << "End element " << localname << " (state "
Packit a4aae4
         << states[parser->get_state()] << ")" << endl);
Packit a4aae4
Packit a4aae4
    switch (parser->get_state()) {
Packit a4aae4
    case parser_start:
Packit a4aae4
        ddx_fatal_error(parser,
Packit a4aae4
                        "Internal parser error; unexpected state, inside start state while processing element '%s'.",
Packit a4aae4
                        localname);
Packit a4aae4
        break;
Packit a4aae4
Packit a4aae4
    case inside_dataset:
Packit a4aae4
        if (strcmp(localname, "Dataset") == 0)
Packit a4aae4
            parser->pop_state();
Packit a4aae4
        else
Packit a4aae4
            DDXParser::ddx_fatal_error(parser,
Packit a4aae4
                                       "Expected an end Dataset tag; found '%s' instead.",
Packit a4aae4
                                       localname);
Packit a4aae4
        break;
Packit a4aae4
Packit a4aae4
    case inside_attribute_container:
Packit a4aae4
        if (strcmp(localname, "Attribute") == 0) {
Packit a4aae4
            parser->pop_state();
Packit a4aae4
            parser->at_stack.pop();     // pop when leaving a container.
Packit a4aae4
        }
Packit a4aae4
        else
Packit a4aae4
            DDXParser::ddx_fatal_error(parser,
Packit a4aae4
                                       "Expected an end Attribute tag; found '%s' instead.",
Packit a4aae4
                                       localname);
Packit a4aae4
        break;
Packit a4aae4
Packit a4aae4
    case inside_attribute:
Packit a4aae4
        if (strcmp(localname, "Attribute") == 0)
Packit a4aae4
            parser->pop_state();
Packit a4aae4
        else
Packit a4aae4
            DDXParser::ddx_fatal_error(parser,
Packit a4aae4
                                       "Expected an end Attribute tag; found '%s' instead.",
Packit a4aae4
                                       localname);
Packit a4aae4
        break;
Packit a4aae4
Packit a4aae4
    case inside_attribute_value:
Packit a4aae4
        if (strcmp(localname, "value") == 0) {
Packit a4aae4
            parser->pop_state();
Packit a4aae4
            AttrTable *atp = parser->at_stack.top();
Packit a4aae4
            atp->append_attr(parser->dods_attr_name,
Packit a4aae4
                             parser->dods_attr_type, parser->char_data);
Packit a4aae4
            parser->char_data = "";     // Null this after use.
Packit a4aae4
        }
Packit a4aae4
        else
Packit a4aae4
            DDXParser::ddx_fatal_error(parser,
Packit a4aae4
                                       "Expected an end value tag; found '%s' instead.",
Packit a4aae4
                                       localname);
Packit a4aae4
Packit a4aae4
        break;
Packit a4aae4
Packit a4aae4
    case inside_other_xml_attribute: {
Packit a4aae4
            if (strcmp(localname, "Attribute") == 0
Packit a4aae4
                    && parser->root_ns == (const char *)URI) {
Packit a4aae4
Packit a4aae4
                DBGN(cerr << endl << "\t Popping the 'inside_other_xml_attribute' state"
Packit a4aae4
                        << endl);
Packit a4aae4
Packit a4aae4
                parser->pop_state();
Packit a4aae4
Packit a4aae4
                AttrTable *atp = parser->at_stack.top();
Packit a4aae4
                atp->append_attr(parser->dods_attr_name,
Packit a4aae4
                        parser->dods_attr_type, parser->other_xml);
Packit a4aae4
Packit a4aae4
                parser->other_xml = ""; // Null this after use.
Packit a4aae4
            }
Packit a4aae4
            else {
Packit a4aae4
                DBGN(cerr << endl << "\t inside_other_xml_attribute: " << localname
Packit a4aae4
                        << ", depth: " << parser->other_xml_depth << endl);
Packit a4aae4
                if (parser->other_xml_depth == 0)
Packit a4aae4
                    DDXParser::ddx_fatal_error(parser,
Packit a4aae4
                                               "Expected an OtherXML attribute to end! Instead I found '%s'",
Packit a4aae4
                                               localname);
Packit a4aae4
                parser->other_xml_depth--;
Packit a4aae4
Packit a4aae4
                parser->other_xml.append("</");
Packit a4aae4
                if (prefix) {
Packit a4aae4
                    parser->other_xml.append((const char *)prefix);
Packit a4aae4
                    parser->other_xml.append(":");
Packit a4aae4
                }
Packit a4aae4
                parser->other_xml.append(localname);
Packit a4aae4
                parser->other_xml.append(">");
Packit a4aae4
            }
Packit a4aae4
            break;
Packit a4aae4
        }
Packit a4aae4
        // Alias is busted in libdap++ 05/29/03 jhrg
Packit a4aae4
    case inside_alias:
Packit a4aae4
        parser->pop_state();
Packit a4aae4
        break;
Packit a4aae4
Packit a4aae4
    case inside_simple_type: {
Packit a4aae4
        Type t = get_type(localname);
Packit a4aae4
        if (is_simple_type(t)) {
Packit a4aae4
            parser->pop_state();
Packit a4aae4
            BaseType *btp = parser->bt_stack.top();
Packit a4aae4
            parser->bt_stack.pop();
Packit a4aae4
            parser->at_stack.pop();
Packit a4aae4
Packit a4aae4
            BaseType *parent = parser->bt_stack.top();
Packit a4aae4
Packit a4aae4
            if (parent->is_vector_type() || parent->is_constructor_type()) {
Packit a4aae4
                parent->add_var(btp);
Packit a4aae4
                delete btp;
Packit a4aae4
            }
Packit a4aae4
            else {
Packit a4aae4
                DDXParser::ddx_fatal_error(parser,
Packit a4aae4
                                           "Tried to add the simple-type variable '%s' to a non-constructor type (%s %s).",
Packit a4aae4
                                           localname,
Packit a4aae4
                                           parser->bt_stack.top()->
Packit a4aae4
                                           type_name().c_str(),
Packit a4aae4
                                           parser->bt_stack.top()->name().
Packit a4aae4
                                           c_str());
Packit a4aae4
                delete btp;
Packit a4aae4
            }
Packit a4aae4
        }
Packit a4aae4
        else {
Packit a4aae4
            DDXParser::ddx_fatal_error(parser,
Packit a4aae4
                                       "Expected an end tag for a simple type; found '%s' instead.",
Packit a4aae4
                                       localname);
Packit a4aae4
        }
Packit a4aae4
        break;
Packit a4aae4
    }
Packit a4aae4
Packit a4aae4
    case inside_array:
Packit a4aae4
        parser->finish_variable(localname, dods_array_c, "Array");
Packit a4aae4
        break;
Packit a4aae4
Packit a4aae4
    case inside_dimension:
Packit a4aae4
        if (strcmp(localname, "dimension") == 0)
Packit a4aae4
            parser->pop_state();
Packit a4aae4
        else
Packit a4aae4
            DDXParser::ddx_fatal_error(parser,
Packit a4aae4
                                       "Expected an end dimension tag; found '%s' instead.",
Packit a4aae4
                                       localname);
Packit a4aae4
        break;
Packit a4aae4
Packit a4aae4
    case inside_structure:
Packit a4aae4
        parser->finish_variable(localname, dods_structure_c, "Structure");
Packit a4aae4
        break;
Packit a4aae4
Packit a4aae4
    case inside_sequence:
Packit a4aae4
        parser->finish_variable(localname, dods_sequence_c, "Sequence");
Packit a4aae4
        break;
Packit a4aae4
Packit a4aae4
    case inside_grid:
Packit a4aae4
        parser->finish_variable(localname, dods_grid_c, "Grid");
Packit a4aae4
        break;
Packit a4aae4
Packit a4aae4
    case inside_map:
Packit a4aae4
        parser->finish_variable(localname, dods_array_c, "Map");
Packit a4aae4
        break;
Packit a4aae4
Packit a4aae4
    case inside_blob_href:
Packit a4aae4
        if (strcmp(localname, "blob") == 0 || strcmp(localname, "dataBLOB") == 0)
Packit a4aae4
            parser->pop_state();
Packit a4aae4
        else
Packit a4aae4
            DDXParser::ddx_fatal_error(parser,
Packit a4aae4
                                       "Expected an end dataBLOB/blob tag; found '%s' instead.",
Packit a4aae4
                                       localname);
Packit a4aae4
        break;
Packit a4aae4
Packit a4aae4
    case parser_unknown:
Packit a4aae4
        parser->pop_state();
Packit a4aae4
        break;
Packit a4aae4
Packit a4aae4
    case parser_error:
Packit a4aae4
        break;
Packit a4aae4
    }
Packit a4aae4
Packit a4aae4
Packit a4aae4
    DBGN(cerr << " ... " << states[parser->get_state()] << endl);
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** Process/accumulate character data. This may be called more than once for
Packit a4aae4
    one logical clump of data. Only save character data when processing
Packit a4aae4
    'value' elements; throw away all other characters. */
Packit a4aae4
void DDXParser::ddx_get_characters(void * p, const xmlChar * ch, int len)
Packit a4aae4
{
Packit a4aae4
    DDXParser *parser = static_cast<DDXParser*>(p);
Packit a4aae4
Packit a4aae4
    switch (parser->get_state()) {
Packit a4aae4
        case inside_attribute_value:
Packit a4aae4
            parser->char_data.append((const char *)(ch), len);
Packit a4aae4
            DBG2(cerr << "Characters: '" << parser->char_data << "'" << endl);
Packit a4aae4
            break;
Packit a4aae4
Packit a4aae4
        case inside_other_xml_attribute:
Packit a4aae4
            parser->other_xml.append((const char *)(ch), len);
Packit a4aae4
            DBG2(cerr << "Other XML Characters: '" << parser->other_xml << "'" << endl);
Packit a4aae4
            break;
Packit a4aae4
Packit a4aae4
        default:
Packit a4aae4
            break;
Packit a4aae4
    }
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** Read whitespace that's not really important for content. This is used
Packit a4aae4
    only for the OtherXML attribute type to preserve formating of the XML.
Packit a4aae4
    Doing so makes the attribute value far easier to read.
Packit a4aae4
 */
Packit a4aae4
void DDXParser::ddx_ignoreable_whitespace(void *p, const xmlChar *ch,
Packit a4aae4
        int len)
Packit a4aae4
{
Packit a4aae4
    DDXParser *parser = static_cast<DDXParser*>(p);
Packit a4aae4
Packit a4aae4
    switch (parser->get_state()) {
Packit a4aae4
         case inside_other_xml_attribute:
Packit a4aae4
             parser->other_xml.append((const char *)(ch), len);
Packit a4aae4
             break;
Packit a4aae4
Packit a4aae4
         default:
Packit a4aae4
             break;
Packit a4aae4
    }
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** Get characters in a cdata block. DAP does not use CData, but XML in an
Packit a4aae4
    OtherXML attribute (the value of that DAP attribute) might use it. This
Packit a4aae4
    callback also allows CData when the parser is in the 'parser_unknown'
Packit a4aae4
    state since some future DAP element might use it.
Packit a4aae4
 */
Packit a4aae4
void DDXParser::ddx_get_cdata(void *p, const xmlChar *value, int len)
Packit a4aae4
{
Packit a4aae4
    DDXParser *parser = static_cast<DDXParser*>(p);
Packit a4aae4
Packit a4aae4
    switch (parser->get_state()) {
Packit a4aae4
         case inside_other_xml_attribute:
Packit a4aae4
             parser->other_xml.append((const char *)(value), len);
Packit a4aae4
             break;
Packit a4aae4
Packit a4aae4
         case parser_unknown:
Packit a4aae4
             break;
Packit a4aae4
Packit a4aae4
         default:
Packit a4aae4
             DDXParser::ddx_fatal_error(parser,
Packit a4aae4
                                        "Found a CData block but none are allowed by DAP.");
Packit a4aae4
Packit a4aae4
             break;
Packit a4aae4
    }
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** Handle the standard XML entities.
Packit a4aae4
Packit a4aae4
    @param parser The SAX parser
Packit a4aae4
    @param name The XML entity. */
Packit a4aae4
xmlEntityPtr DDXParser::ddx_get_entity(void *, const xmlChar * name)
Packit a4aae4
{
Packit a4aae4
    return xmlGetPredefinedEntity(name);
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** Process an XML fatal error. Note that SAX provides for warnings, errors
Packit a4aae4
    and fatal errors. This code treats them all as fatal errors since there's
Packit a4aae4
    typically no way to tell a user about the error since there's often no
Packit a4aae4
    user interface for this software.
Packit a4aae4
Packit a4aae4
    @param p The SAX parser
Packit a4aae4
    @param msg A printf-style format string. */
Packit a4aae4
void DDXParser::ddx_fatal_error(void * p, const char *msg, ...)
Packit a4aae4
{
Packit a4aae4
    va_list args;
Packit a4aae4
    DDXParser *parser = static_cast<DDXParser*>(p);
Packit a4aae4
Packit a4aae4
    parser->set_state(parser_error);
Packit a4aae4
Packit a4aae4
    va_start(args, msg);
Packit a4aae4
    char str[1024];
Packit a4aae4
    vsnprintf(str, 1024, msg, args);
Packit a4aae4
    va_end(args);
Packit a4aae4
Packit a4aae4
    int line = xmlSAX2GetLineNumber(parser->ctxt);
Packit a4aae4
Packit a4aae4
    parser->error_msg += "At line " + long_to_string(line) + ": ";
Packit a4aae4
    parser->error_msg += string(str) + string("\n");
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
//@}
Packit a4aae4
Packit a4aae4
void DDXParser::cleanup_parse(xmlParserCtxtPtr & context)
Packit a4aae4
{
Packit a4aae4
    bool wellFormed = context->wellFormed;
Packit a4aae4
    bool valid = context->valid;
Packit a4aae4
Packit a4aae4
    context->sax = NULL;
Packit a4aae4
    xmlFreeParserCtxt(context);
Packit a4aae4
Packit a4aae4
    // If there's an error, there may still be items on the stack at the
Packit a4aae4
    // end of the parse.
Packit a4aae4
    while (!bt_stack.empty()) {
Packit a4aae4
        delete bt_stack.top();
Packit a4aae4
        bt_stack.pop();
Packit a4aae4
    }
Packit a4aae4
Packit a4aae4
    if (!wellFormed) {
Packit a4aae4
        throw DDXParseFailed(string("The DDX is not a well formed XML document.\n") + error_msg);
Packit a4aae4
    }
Packit a4aae4
Packit a4aae4
    if (!valid) {
Packit a4aae4
        throw DDXParseFailed(string("The DDX is not a valid document.\n") + error_msg);
Packit a4aae4
    }
Packit a4aae4
Packit a4aae4
    if (get_state() == parser_error) {
Packit a4aae4
        throw DDXParseFailed(string("Error parsing DDX response.\n") + error_msg);
Packit a4aae4
    }
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** Read a DDX from a C++ input stream and populate a DDS object.
Packit a4aae4
 *
Packit a4aae4
 * @param in
Packit a4aae4
 * @param dds
Packit a4aae4
 * @param cid
Packit a4aae4
 * @param boundary
Packit a4aae4
 */
Packit a4aae4
void DDXParser::intern_stream(istream &in, DDS *dest_dds, string &cid, const string &boundary)
Packit a4aae4
{
Packit a4aae4
    // Code example from libxml2 docs re: read from a stream.
Packit a4aae4
    if (!in || in.eof())
Packit a4aae4
        throw InternalErr(__FILE__, __LINE__, "Input stream not open or read error");
Packit a4aae4
Packit a4aae4
    const int size = 1024;
Packit a4aae4
    char chars[size + 1];
Packit a4aae4
Packit a4aae4
    // int res = fread(chars, 1, 4, in);
Packit a4aae4
    in.read(chars, 4);
Packit a4aae4
    int res = in.gcount();
Packit a4aae4
    if (res > 0) {
Packit a4aae4
        chars[4]='\0';
Packit a4aae4
        xmlParserCtxtPtr context = xmlCreatePushParserCtxt(NULL, NULL, chars, res, "stream");
Packit a4aae4
Packit a4aae4
        if (!context)
Packit a4aae4
            throw DDXParseFailed("Error parsing DDX response: Input does not look like XML");
Packit a4aae4
Packit a4aae4
        ctxt = context;         // need ctxt for error messages
Packit a4aae4
        dds = dest_dds;         // dump values here
Packit a4aae4
        blob_href = &cid; 	// cid goes here
Packit a4aae4
Packit a4aae4
        xmlSAXHandler ddx_sax_parser;
Packit a4aae4
        memset( &ddx_sax_parser, 0, sizeof(xmlSAXHandler) );
Packit a4aae4
Packit a4aae4
        ddx_sax_parser.getEntity = &DDXParser::ddx_get_entity;
Packit a4aae4
        ddx_sax_parser.startDocument = &DDXParser::ddx_start_document;
Packit a4aae4
        ddx_sax_parser.endDocument = &DDXParser::ddx_end_document;
Packit a4aae4
        ddx_sax_parser.characters = &DDXParser::ddx_get_characters;
Packit a4aae4
        ddx_sax_parser.ignorableWhitespace = &DDXParser::ddx_ignoreable_whitespace;
Packit a4aae4
        ddx_sax_parser.cdataBlock = &DDXParser::ddx_get_cdata;
Packit a4aae4
        ddx_sax_parser.warning = &DDXParser::ddx_fatal_error;
Packit a4aae4
        ddx_sax_parser.error = &DDXParser::ddx_fatal_error;
Packit a4aae4
        ddx_sax_parser.fatalError = &DDXParser::ddx_fatal_error;
Packit a4aae4
        ddx_sax_parser.initialized = XML_SAX2_MAGIC;
Packit a4aae4
        ddx_sax_parser.startElementNs = &DDXParser::ddx_sax2_start_element;
Packit a4aae4
        ddx_sax_parser.endElementNs = &DDXParser::ddx_sax2_end_element;
Packit a4aae4
Packit a4aae4
        context->sax = &ddx_sax_parser;
Packit a4aae4
        context->userData = this;
Packit a4aae4
        context->validate = true;
Packit a4aae4
Packit a4aae4
        in.getline(chars, size);	// chars has size+1 elements
Packit a4aae4
        res = in.gcount();
Packit a4aae4
        chars[res-1] = '\n';		// libxml needs the newline; w/o it the parse will fail
Packit a4aae4
        chars[res] = '\0';
Packit a4aae4
        while (res > 0 && !is_boundary(chars, boundary)) {
Packit a4aae4
        	DBG(cerr << "line (" << res << "): " << chars << endl);
Packit a4aae4
        	xmlParseChunk(ctxt, chars, res, 0);
Packit a4aae4
Packit a4aae4
        	in.getline(chars, size);	// chars has size+1 elements
Packit a4aae4
        	res = in.gcount();
Packit a4aae4
        	if (res > 0) {
Packit a4aae4
        		chars[res-1] = '\n';
Packit a4aae4
        		chars[res] = '\0';
Packit a4aae4
        	}
Packit a4aae4
        }
Packit a4aae4
Packit a4aae4
        // This call ends the parse: The fourth argument of xmlParseChunk is
Packit a4aae4
        // the bool 'terminate.'
Packit a4aae4
        xmlParseChunk(ctxt, chars, 0, 1);
Packit a4aae4
Packit a4aae4
        cleanup_parse(context);
Packit a4aae4
    }
Packit a4aae4
    else {
Packit a4aae4
        throw DDXParseFailed("Error parsing DDX response: Could not read from input stream.");
Packit a4aae4
    }
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** @brief Read the DDX from a stream instead of a file.
Packit a4aae4
    @see DDXParser::intern(). */
Packit a4aae4
void DDXParser::intern_stream(FILE *in, DDS *dest_dds, string &cid, const string &boundary)
Packit a4aae4
{
Packit a4aae4
    // Code example from libxml2 docs re: read from a stream.
Packit a4aae4
    if (!in || feof(in) || ferror(in))
Packit a4aae4
        throw InternalErr(__FILE__, __LINE__, "Input stream not open or read error");
Packit a4aae4
Packit a4aae4
    const int size = 1024;
Packit a4aae4
    char chars[size];
Packit a4aae4
Packit a4aae4
    int res = fread(chars, 1, 4, in);
Packit a4aae4
    if (res > 0) {
Packit a4aae4
        chars[4]='\0';
Packit a4aae4
        xmlParserCtxtPtr context = xmlCreatePushParserCtxt(NULL, NULL, chars, res, "stream");
Packit a4aae4
Packit a4aae4
        if (!context)
Packit a4aae4
            throw DDXParseFailed("Error parsing DDX response: Input does not look like XML");
Packit a4aae4
Packit a4aae4
        ctxt = context;         // need ctxt for error messages
Packit a4aae4
        dds = dest_dds;         // dump values here
Packit a4aae4
        blob_href = &cid; 	// cid goes here
Packit a4aae4
Packit a4aae4
        xmlSAXHandler ddx_sax_parser;
Packit a4aae4
        memset( &ddx_sax_parser, 0, sizeof(xmlSAXHandler) );
Packit a4aae4
Packit a4aae4
        ddx_sax_parser.getEntity = &DDXParser::ddx_get_entity;
Packit a4aae4
        ddx_sax_parser.startDocument = &DDXParser::ddx_start_document;
Packit a4aae4
        ddx_sax_parser.endDocument = &DDXParser::ddx_end_document;
Packit a4aae4
        ddx_sax_parser.characters = &DDXParser::ddx_get_characters;
Packit a4aae4
        ddx_sax_parser.ignorableWhitespace = &DDXParser::ddx_ignoreable_whitespace;
Packit a4aae4
        ddx_sax_parser.cdataBlock = &DDXParser::ddx_get_cdata;
Packit a4aae4
        ddx_sax_parser.warning = &DDXParser::ddx_fatal_error;
Packit a4aae4
        ddx_sax_parser.error = &DDXParser::ddx_fatal_error;
Packit a4aae4
        ddx_sax_parser.fatalError = &DDXParser::ddx_fatal_error;
Packit a4aae4
        ddx_sax_parser.initialized = XML_SAX2_MAGIC;
Packit a4aae4
        ddx_sax_parser.startElementNs = &DDXParser::ddx_sax2_start_element;
Packit a4aae4
        ddx_sax_parser.endElementNs = &DDXParser::ddx_sax2_end_element;
Packit a4aae4
Packit a4aae4
        context->sax = &ddx_sax_parser;
Packit a4aae4
        context->userData = this;
Packit a4aae4
        context->validate = true;
Packit a4aae4
Packit a4aae4
Packit a4aae4
        while ((fgets(chars, size, in) != 0) && !is_boundary(chars, boundary)) {
Packit a4aae4
            DBG(cerr << "line (" << strlen(chars) << "): " << chars << endl);
Packit a4aae4
            xmlParseChunk(ctxt, chars, strlen(chars), 0);
Packit a4aae4
        }
Packit a4aae4
        // This call ends the parse: The fourth argument of xmlParseChunk is
Packit a4aae4
        // the bool 'terminate.'
Packit a4aae4
        xmlParseChunk(ctxt, chars, 0, 1);
Packit a4aae4
Packit a4aae4
        cleanup_parse(context);
Packit a4aae4
    }
Packit a4aae4
    else {
Packit a4aae4
        throw DDXParseFailed("Error parsing DDX response: Could not read from input file.");
Packit a4aae4
    }
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
Packit a4aae4
/** Parse a DDX document stored in a file. The XML in the document is parsed
Packit a4aae4
    and a binary DDX is built. This implementation stores the result in a DDS
Packit a4aae4
    object where each instance of BaseType can hold an AttrTable object.
Packit a4aae4
Packit a4aae4
    @param document Read the DDX from this file.
Packit a4aae4
    @param dest_dds Value/result parameter; dumps the information to this DDS
Packit a4aae4
    instance.
Packit a4aae4
    @param cid Value/result parameter; puts the href which references the \c
Packit a4aae4
    CID.
Packit a4aae4
    @exception DDXParseFailed Thrown if the XML document could not be
Packit a4aae4
    read or parsed. */
Packit a4aae4
void DDXParser::intern(const string & document, DDS * dest_dds, string &cid)
Packit a4aae4
{
Packit a4aae4
    // Create the context pointer explicitly so that we can store a pointer
Packit a4aae4
    // to it in the DDXParser instance. This provides a way to generate our
Packit a4aae4
    // own error messages *with* line numbers. The messages are pretty
Packit a4aae4
    // meaningless otherwise. This means that we use an interface from the
Packit a4aae4
    // 'parser internals' header, and not the 'parser' header. However, this
Packit a4aae4
    // interface is also used in one of the documented examples, so it's
Packit a4aae4
    // probably pretty stable. 06/02/03 jhrg
Packit a4aae4
    xmlParserCtxtPtr context = xmlCreateFileParserCtxt(document.c_str());
Packit a4aae4
    if (!context)
Packit a4aae4
        throw
Packit a4aae4
        DDXParseFailed(string
Packit a4aae4
                       ("Could not initialize the parser with the file: '")
Packit a4aae4
                       + document + string("'."));
Packit a4aae4
Packit a4aae4
    dds = dest_dds;             // dump values here
Packit a4aae4
    blob_href = &cid;
Packit a4aae4
    ctxt = context;             // need ctxt for error messages
Packit a4aae4
Packit a4aae4
    xmlSAXHandler ddx_sax_parser;
Packit a4aae4
    memset( &ddx_sax_parser, 0, sizeof(xmlSAXHandler) );
Packit a4aae4
Packit a4aae4
    ddx_sax_parser.getEntity = &DDXParser::ddx_get_entity;
Packit a4aae4
    ddx_sax_parser.startDocument = &DDXParser::ddx_start_document;
Packit a4aae4
    ddx_sax_parser.endDocument = &DDXParser::ddx_end_document;
Packit a4aae4
    ddx_sax_parser.characters = &DDXParser::ddx_get_characters;
Packit a4aae4
    ddx_sax_parser.ignorableWhitespace = &DDXParser::ddx_ignoreable_whitespace;
Packit a4aae4
    ddx_sax_parser.cdataBlock = &DDXParser::ddx_get_cdata;
Packit a4aae4
    ddx_sax_parser.warning = &DDXParser::ddx_fatal_error;
Packit a4aae4
    ddx_sax_parser.error = &DDXParser::ddx_fatal_error;
Packit a4aae4
    ddx_sax_parser.fatalError = &DDXParser::ddx_fatal_error;
Packit a4aae4
    ddx_sax_parser.initialized = XML_SAX2_MAGIC;
Packit a4aae4
    ddx_sax_parser.startElementNs = &DDXParser::ddx_sax2_start_element;
Packit a4aae4
    ddx_sax_parser.endElementNs = &DDXParser::ddx_sax2_end_element;
Packit a4aae4
Packit a4aae4
    context->sax = &ddx_sax_parser;
Packit a4aae4
    context->userData = this;
Packit a4aae4
    context->validate = false;
Packit a4aae4
Packit a4aae4
    xmlParseDocument(context);
Packit a4aae4
Packit a4aae4
    cleanup_parse(context);
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
} // namespace libdap