Blame D4ParserSax2.cc

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) 2012 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
Packit a4aae4
#include <iostream>
Packit a4aae4
#include <sstream>
Packit a4aae4
Packit a4aae4
#include <cstring>
Packit a4aae4
#include <cstdarg>
Packit a4aae4
#include <cassert>
Packit a4aae4
Packit a4aae4
#include <libxml/parserInternals.h>
Packit a4aae4
Packit a4aae4
#include "DMR.h"
Packit a4aae4
Packit a4aae4
#include "BaseType.h"
Packit a4aae4
#include "Array.h"
Packit a4aae4
#include "D4Group.h"
Packit a4aae4
#include "D4Attributes.h"
Packit a4aae4
#include "D4Maps.h"
Packit a4aae4
#include "D4Enum.h"
Packit a4aae4
#include "D4BaseTypeFactory.h"
Packit a4aae4
Packit a4aae4
#include "DapXmlNamespaces.h"
Packit a4aae4
#include "D4ParserSax2.h"
Packit a4aae4
Packit a4aae4
#include "util.h"
Packit a4aae4
#include "debug.h"
Packit a4aae4
Packit a4aae4
namespace libdap {
Packit a4aae4
Packit a4aae4
static const char *states[] = {
Packit a4aae4
        "parser_start",
Packit a4aae4
Packit a4aae4
        "inside_dataset",
Packit a4aae4
Packit a4aae4
        // inside_group is the state just after parsing the start of a Group
Packit a4aae4
        // element.
Packit a4aae4
        "inside_group",
Packit a4aae4
Packit a4aae4
        "inside_attribute_container",
Packit a4aae4
        "inside_attribute",
Packit a4aae4
        "inside_attribute_value",
Packit a4aae4
        "inside_other_xml_attribute",
Packit a4aae4
Packit a4aae4
        "inside_enum_def",
Packit a4aae4
        "inside_enum_const",
Packit a4aae4
Packit a4aae4
        "inside_dim_def",
Packit a4aae4
Packit a4aae4
        // This covers Byte, ..., Url, Opaque
Packit a4aae4
        "inside_simple_type",
Packit a4aae4
Packit a4aae4
        // "inside_array",
Packit a4aae4
        "inside_dim",
Packit a4aae4
        "inside_map",
Packit a4aae4
Packit a4aae4
        "inside_constructor",
Packit a4aae4
Packit a4aae4
        "not_dap4_element",
Packit a4aae4
Packit a4aae4
        "parser_unknown",
Packit a4aae4
        "parser_error",
Packit a4aae4
        "parser_fatal_error",
Packit a4aae4
Packit a4aae4
        "parser_end"
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
/** @brief Return the current Enumeration definition
Packit a4aae4
 * Allocate the Enumeration definition if needed and return it. Once parsing the current
Packit a4aae4
 * enumeration definition is complete, the pointer allocated/returned by this method will
Packit a4aae4
 * be copied into the current Group and this internal storage will be 'reset' using
Packit a4aae4
 * clear_enum_def().
Packit a4aae4
 *
Packit a4aae4
 * @return
Packit a4aae4
 */
Packit a4aae4
D4EnumDef *
Packit a4aae4
D4ParserSax2::enum_def()
Packit a4aae4
{
Packit a4aae4
    if (!d_enum_def) d_enum_def = new D4EnumDef;
Packit a4aae4
Packit a4aae4
    return d_enum_def;
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** @brief Return the current Dimension definition
Packit a4aae4
 * Allocate the Dimension definition if needed and return it.
Packit a4aae4
 * @see enum_def() for an explanation of how this is used by the parser.
Packit a4aae4
 *
Packit a4aae4
 * @return
Packit a4aae4
 */
Packit a4aae4
D4Dimension *
Packit a4aae4
D4ParserSax2::dim_def()    {
Packit a4aae4
    if (!d_dim_def) d_dim_def = new D4Dimension;
Packit a4aae4
Packit a4aae4
    return d_dim_def;
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** Dump XML attributes to local store so they can be easily manipulated.
Packit a4aae4
 * XML attribute names are always folded to lower case.
Packit a4aae4
 * @param attributes The XML attribute array
Packit a4aae4
 * @param nb_attributes The number of attributes
Packit a4aae4
 */
Packit a4aae4
void D4ParserSax2::transfer_xml_attrs(const xmlChar **attributes, int nb_attributes)
Packit a4aae4
{
Packit a4aae4
    if (!xml_attrs.empty())
Packit a4aae4
        xml_attrs.clear(); // erase old attributes
Packit a4aae4
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
    unsigned int index = 0;
Packit a4aae4
    for (int i = 0; i < nb_attributes; ++i, index += 5) {
Packit a4aae4
        xml_attrs.insert(map<string, XMLAttribute>::value_type(string((const char *)attributes[index]),
Packit a4aae4
                                                               XMLAttribute(attributes + index + 1)));
Packit a4aae4
Packit a4aae4
        DBG(cerr << "XML Attribute '" << (const char *)attributes[index] << "': "
Packit a4aae4
                << xml_attrs[(const char *)attributes[index]].value << endl);
Packit a4aae4
    }
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** Transfer the XML namespaces to the local store so they can be manipulated
Packit a4aae4
 * more easily.
Packit a4aae4
 *
Packit a4aae4
 * @param namespaces Array of xmlChar*
Packit a4aae4
 * @param nb_namespaces The number of namespaces in the array.
Packit a4aae4
 */
Packit a4aae4
void D4ParserSax2::transfer_xml_ns(const xmlChar **namespaces, int nb_namespaces)
Packit a4aae4
{
Packit a4aae4
    // make a value with the prefix and namespace URI. The prefix might be null.
Packit a4aae4
    for (int i = 0; i < nb_namespaces; ++i) {
Packit a4aae4
        namespace_table.insert(map<string, string>::value_type(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 a required XML 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, otherwise
Packit a4aae4
 * it sets the global error state and returns false.
Packit a4aae4
 */
Packit a4aae4
bool D4ParserSax2::check_required_attribute(const string & attr)
Packit a4aae4
{
Packit a4aae4
    if (xml_attrs.find(attr) == xml_attrs.end()) {
Packit a4aae4
        dmr_error(this, "Required attribute '%s' not found.", attr.c_str());
Packit a4aae4
        return false;
Packit a4aae4
    }
Packit a4aae4
    else
Packit a4aae4
        return true;
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** Is a XML 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
 */
Packit a4aae4
bool D4ParserSax2::check_attribute(const string & attr)
Packit a4aae4
{
Packit a4aae4
    return (xml_attrs.find(attr) != xml_attrs.end());
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
bool D4ParserSax2::process_dimension_def(const char *name, const xmlChar **attrs, int nb_attributes)
Packit a4aae4
{
Packit a4aae4
    if (is_not(name, "Dimension"))
Packit a4aae4
        return false;
Packit a4aae4
Packit a4aae4
    transfer_xml_attrs(attrs, nb_attributes);
Packit a4aae4
Packit a4aae4
    if (!(check_required_attribute("name") && check_required_attribute("size"))) {
Packit a4aae4
        dmr_error(this, "The required attribute 'name' or 'size' was missing from a Dimension element.");
Packit a4aae4
        return false;
Packit a4aae4
    }
Packit a4aae4
Packit a4aae4
    // This getter (dim_def) allocates a new object if needed.
Packit a4aae4
    dim_def()->set_name(xml_attrs["name"].value);
Packit a4aae4
    try {
Packit a4aae4
        dim_def()->set_size(xml_attrs["size"].value);
Packit a4aae4
    }
Packit a4aae4
    catch (Error &e) {
Packit a4aae4
        dmr_error(this, e.get_error_message().c_str());
Packit a4aae4
        return false;
Packit a4aae4
    }
Packit a4aae4
Packit a4aae4
    return true;
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/**
Packit a4aae4
 * @brief Process a Dim element.
Packit a4aae4
 * If a Dim element is found, the current variable is an Array. If the BaseType
Packit a4aae4
 * on the TOS is not already an Array, make it one. Append the dimension
Packit a4aae4
 * information to the Array variable on the TOS.
Packit a4aae4
 *
Packit a4aae4
 * @note Dim elements can have two attributes: name or size. The latter defines
Packit a4aae4
 * an 'anonymous' dimension (one without a name that does not reference a
Packit a4aae4
 * shared dimension object. If the \c name attribute is used, then the shared
Packit a4aae4
 * dimension used is the one defined by the enclosing group or found using the
Packit a4aae4
 * fully qualified name. The \name and \c size attributes are mutually exclusive.
Packit a4aae4
 *
Packit a4aae4
 * @param name XML element name; must be Dim
Packit a4aae4
 * @param attrs XML Attributes
Packit a4aae4
 * @param nb_attributes The number of XML Attributes
Packit a4aae4
 * @return True if the element is a Dim, false otherwise.
Packit a4aae4
 */
Packit a4aae4
bool D4ParserSax2::process_dimension(const char *name, const xmlChar **attrs, int nb_attributes)
Packit a4aae4
{
Packit a4aae4
    if (is_not(name, "Dim"))
Packit a4aae4
        return false;
Packit a4aae4
Packit a4aae4
    transfer_xml_attrs(attrs, nb_attributes);
Packit a4aae4
Packit a4aae4
	if (check_attribute("size") && check_attribute("name")) {
Packit a4aae4
		dmr_error(this, "Only one of 'size' and 'name' are allowed in a Dim element, but both were used.");
Packit a4aae4
		return false;
Packit a4aae4
	}
Packit a4aae4
	if (!(check_attribute("size") || check_attribute("name"))) {
Packit a4aae4
		dmr_error(this, "Either 'size' or 'name' must be used in a Dim element.");
Packit a4aae4
		return false;
Packit a4aae4
	}
Packit a4aae4
Packit a4aae4
	if (!top_basetype()->is_vector_type()) {
Packit a4aae4
		// Make the top BaseType* an array
Packit a4aae4
		BaseType *b = top_basetype();
Packit a4aae4
		pop_basetype();
Packit a4aae4
Packit a4aae4
		Array *a = static_cast<Array*>(dmr()->factory()->NewVariable(dods_array_c, b->name()));
Packit a4aae4
		a->set_is_dap4(true);
Packit a4aae4
		a->add_var_nocopy(b);
Packit a4aae4
		a->set_attributes_nocopy(b->attributes());
Packit a4aae4
		// trick: instead of popping b's attributes, copying them and then pushing
Packit a4aae4
		// a's copy, just move the pointer (but make sure there's only one object that
Packit a4aae4
		// references that pointer).
Packit a4aae4
		b->set_attributes_nocopy(0);
Packit a4aae4
Packit a4aae4
		push_basetype(a);
Packit a4aae4
	}
Packit a4aae4
Packit a4aae4
	assert(top_basetype()->is_vector_type());
Packit a4aae4
Packit a4aae4
	Array *a = static_cast<Array*>(top_basetype());
Packit a4aae4
    if (check_attribute("size")) {
Packit a4aae4
    	a->append_dim(atoi(xml_attrs["size"].value.c_str())); // low budget code for now. jhrg 8/20/13
Packit a4aae4
        return true;
Packit a4aae4
    }
Packit a4aae4
    else if (check_attribute("name")) {
Packit a4aae4
    	string name = xml_attrs["name"].value;
Packit a4aae4
Packit a4aae4
    	D4Dimension *dim = 0;
Packit a4aae4
    	if (name[0] == '/')		// lookup the Dimension in the root group
Packit a4aae4
    		dim = dmr()->root()->find_dim(name);
Packit a4aae4
    	else					// get enclosing Group and lookup Dimension there
Packit a4aae4
    		dim = top_group()->find_dim(name);
Packit a4aae4
Packit a4aae4
    	if (!dim)
Packit a4aae4
    		throw Error("The dimension '" + name + "' was not found while parsing the variable '" + a->name() + "'.");
Packit a4aae4
    	a->append_dim(dim);
Packit a4aae4
    	return true;
Packit a4aae4
    }
Packit a4aae4
Packit a4aae4
    return false;
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
bool D4ParserSax2::process_map(const char *name, const xmlChar **attrs, int nb_attributes)
Packit a4aae4
{
Packit a4aae4
    if (is_not(name, "Map"))
Packit a4aae4
        return false;
Packit a4aae4
Packit a4aae4
    transfer_xml_attrs(attrs, nb_attributes);
Packit a4aae4
Packit a4aae4
	if (!check_attribute("name")) {
Packit a4aae4
		dmr_error(this, "The 'name' attribute must be used in a Map element.");
Packit a4aae4
		return false;
Packit a4aae4
	}
Packit a4aae4
Packit a4aae4
	if (!top_basetype()->is_vector_type()) {
Packit a4aae4
		// Make the top BaseType* an array
Packit a4aae4
		BaseType *b = top_basetype();
Packit a4aae4
		pop_basetype();
Packit a4aae4
Packit a4aae4
		Array *a = static_cast<Array*>(dmr()->factory()->NewVariable(dods_array_c, b->name()));
Packit a4aae4
		a->set_is_dap4(true);
Packit a4aae4
		a->add_var_nocopy(b);
Packit a4aae4
		a->set_attributes_nocopy(b->attributes());
Packit a4aae4
		// trick: instead of popping b's attributes, copying them and then pushing
Packit a4aae4
		// a's copy, just move the pointer (but make sure there's only one object that
Packit a4aae4
		// references that pointer).
Packit a4aae4
		b->set_attributes_nocopy(0);
Packit a4aae4
Packit a4aae4
		push_basetype(a);
Packit a4aae4
	}
Packit a4aae4
Packit a4aae4
	assert(top_basetype()->is_vector_type());
Packit a4aae4
Packit a4aae4
	Array *a = static_cast<Array*>(top_basetype());
Packit a4aae4
Packit a4aae4
	string map_name = xml_attrs["name"].value;
Packit a4aae4
	if (xml_attrs["name"].value[0] != '/')
Packit a4aae4
		map_name = top_group()->FQN() + map_name;
Packit a4aae4
Packit a4aae4
    Array *map_source = 0;	// The array variable that holds the data for the Map
Packit a4aae4
Packit a4aae4
	if (map_name[0] == '/')		// lookup the Map in the root group
Packit a4aae4
		map_source = dmr()->root()->find_map_source(map_name);
Packit a4aae4
	else					// get enclosing Group and lookup Map there
Packit a4aae4
		map_source = top_group()->find_map_source(map_name);
Packit a4aae4
Packit a4aae4
	// Change: If the parser is in 'strict' mode (the default) and the Array named by
Packit a4aae4
	// the Map cannot be fond, it is an error. If 'strict' mode is false (permissive
Packit a4aae4
	// mode), then this is not an error. However, the Array referenced by the Map will
Packit a4aae4
	// be null. This is a change in the parser's behavior to accommodate requests for
Packit a4aae4
	// Arrays that include Maps that do not also include the Map(s) in the request.
Packit a4aae4
	// See https://opendap.atlassian.net/browse/HYRAX-98. jhrg 4/13/16
Packit a4aae4
	if (!map_source && d_strict)
Packit a4aae4
		throw Error("The Map '" + map_name + "' was not found while parsing the variable '" + a->name() + "'.");
Packit a4aae4
Packit a4aae4
	a->maps()->add_map(new D4Map(map_name, map_source));
Packit a4aae4
Packit a4aae4
	return true;
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
bool D4ParserSax2::process_group(const char *name, const xmlChar **attrs, int nb_attributes)
Packit a4aae4
{
Packit a4aae4
    if (is_not(name, "Group"))
Packit a4aae4
        return false;
Packit a4aae4
Packit a4aae4
    transfer_xml_attrs(attrs, nb_attributes);
Packit a4aae4
Packit a4aae4
    if (!check_required_attribute("name")) {
Packit a4aae4
        dmr_error(this, "The required attribute 'name' was missing from a Group element.");
Packit a4aae4
        return false;
Packit a4aae4
    }
Packit a4aae4
Packit a4aae4
    BaseType *btp = dmr()->factory()->NewVariable(dods_group_c, xml_attrs["name"].value);
Packit a4aae4
    if (!btp) {
Packit a4aae4
        dmr_fatal_error(this, "Could not instantiate the Group '%s'.", xml_attrs["name"].value.c_str());
Packit a4aae4
        return false;
Packit a4aae4
    }
Packit a4aae4
Packit a4aae4
    D4Group *grp = static_cast<D4Group*>(btp);
Packit a4aae4
Packit a4aae4
    // Need to set this to get the D4Attribute behavior in the type classes
Packit a4aae4
    // shared between DAP2 and DAP4. jhrg 4/18/13
Packit a4aae4
    grp->set_is_dap4(true);
Packit a4aae4
Packit a4aae4
    // link it up and change the current group
Packit a4aae4
    D4Group *parent = top_group();
Packit a4aae4
	if (!parent) {
Packit a4aae4
		dmr_fatal_error(this, "No Group on the Group stack.");
Packit a4aae4
		return false;
Packit a4aae4
	}
Packit a4aae4
Packit a4aae4
	grp->set_parent(parent);
Packit a4aae4
	parent->add_group_nocopy(grp);
Packit a4aae4
Packit a4aae4
    push_group(grp);
Packit a4aae4
    push_attributes(grp->attributes());
Packit a4aae4
    return true;
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 D4ParserSax2::process_attribute(const char *name, const xmlChar **attrs, int nb_attributes)
Packit a4aae4
{
Packit a4aae4
    if (is_not(name, "Attribute"))
Packit a4aae4
        return false;
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
    // add error
Packit a4aae4
    if (!(check_required_attribute(string("name")) && check_required_attribute(string("type")))) {
Packit a4aae4
        dmr_error(this, "The required attribute 'name' or 'type' was missing from an Attribute element.");
Packit a4aae4
        return false;
Packit a4aae4
    }
Packit a4aae4
Packit a4aae4
    if (xml_attrs["type"].value == "Container") {
Packit a4aae4
        push_state(inside_attribute_container);
Packit a4aae4
Packit a4aae4
        DBG(cerr << "Pushing attribute container " << xml_attrs["name"].value << endl);
Packit a4aae4
        D4Attribute *child = new D4Attribute(xml_attrs["name"].value, attr_container_c);
Packit a4aae4
Packit a4aae4
        D4Attributes *tos = top_attributes();
Packit a4aae4
        // add return
Packit a4aae4
        if (!tos) {
Packit a4aae4
            delete child;
Packit a4aae4
            dmr_fatal_error(this, "Expected an Attribute container on the top of the attribute stack.");
Packit a4aae4
            return false;
Packit a4aae4
        }
Packit a4aae4
Packit a4aae4
        tos->add_attribute_nocopy(child);
Packit a4aae4
        push_attributes(child->attributes());
Packit a4aae4
    }
Packit a4aae4
    else if (xml_attrs["type"].value == "OtherXML") {
Packit a4aae4
        push_state(inside_other_xml_attribute);
Packit a4aae4
Packit a4aae4
        dods_attr_name = xml_attrs["name"].value;
Packit a4aae4
        dods_attr_type = xml_attrs["type"].value;
Packit a4aae4
    }
Packit a4aae4
    else {
Packit a4aae4
        push_state(inside_attribute);
Packit a4aae4
Packit a4aae4
        dods_attr_name = xml_attrs["name"].value;
Packit a4aae4
        dods_attr_type = xml_attrs["type"].value;
Packit a4aae4
    }
Packit a4aae4
Packit a4aae4
    return true;
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** Check to see if the current tag is an \c Enumeration start tag.
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 Enumeration */
Packit a4aae4
inline bool D4ParserSax2::process_enum_def(const char *name, const xmlChar **attrs, int nb_attributes)
Packit a4aae4
{
Packit a4aae4
    if (is_not(name, "Enumeration"))
Packit a4aae4
        return false;
Packit a4aae4
Packit a4aae4
    transfer_xml_attrs(attrs, nb_attributes);
Packit a4aae4
Packit a4aae4
    if (!(check_required_attribute("name") && check_required_attribute("basetype"))) {
Packit a4aae4
        dmr_error(this, "The required attribute 'name' or 'basetype' was missing from an Enumeration element.");
Packit a4aae4
        return false;
Packit a4aae4
    }
Packit a4aae4
Packit a4aae4
    Type t = get_type(xml_attrs["basetype"].value.c_str());
Packit a4aae4
    if (!is_integer_type(t)) {
Packit a4aae4
        dmr_error(this, "The Enumeration '%s' must have an integer type, instead the type '%s' was used.",
Packit a4aae4
                xml_attrs["name"].value.c_str(), xml_attrs["basetype"].value.c_str());
Packit a4aae4
        return false;
Packit a4aae4
    }
Packit a4aae4
Packit a4aae4
    // This getter allocates a new object if needed.
Packit a4aae4
    string enum_def_path = xml_attrs["name"].value;
Packit a4aae4
#if 0
Packit a4aae4
	// Use FQNs when things are referenced, not when they are defined
Packit a4aae4
    if (xml_attrs["name"].value[0] != '/')
Packit a4aae4
    	enum_def_path = top_group()->FQN() + enum_def_path;
Packit a4aae4
#endif
Packit a4aae4
    enum_def()->set_name(enum_def_path);
Packit a4aae4
    enum_def()->set_type(t);
Packit a4aae4
Packit a4aae4
    return true;
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
inline bool D4ParserSax2::process_enum_const(const char *name, const xmlChar **attrs, int nb_attributes)
Packit a4aae4
{
Packit a4aae4
    if (is_not(name, "EnumConst"))
Packit a4aae4
        return false;
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
    if (!(check_required_attribute("name") && check_required_attribute("value"))) {
Packit a4aae4
        dmr_error(this, "The required attribute 'name' or 'value' was missing from an EnumConst element.");
Packit a4aae4
        return false;
Packit a4aae4
    }
Packit a4aae4
Packit a4aae4
    istringstream iss(xml_attrs["value"].value);
Packit a4aae4
    long long value = 0;
Packit a4aae4
    iss >> skipws >> value;
Packit a4aae4
    if (iss.fail() || iss.bad()) {
Packit a4aae4
        dmr_error(this, "Expected an integer value for an Enumeration constant, got '%s' instead.",
Packit a4aae4
                xml_attrs["value"].value.c_str());
Packit a4aae4
    }
Packit a4aae4
    else if (!enum_def()->is_valid_enum_value(value)) {
Packit a4aae4
        dmr_error(this, "In an Enumeration constant, the value '%s' cannot fit in a variable of type '%s'.",
Packit a4aae4
                xml_attrs["value"].value.c_str(), D4type_name(d_enum_def->type()).c_str());
Packit a4aae4
    }
Packit a4aae4
    else {
Packit a4aae4
        // unfortunate choice of names... args are 'label' and 'value'
Packit a4aae4
        enum_def()->add_value(xml_attrs["name"].value, value);
Packit a4aae4
    }
Packit a4aae4
Packit a4aae4
    return true;
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** Check to see if the current element is the start of a variable declaration.
Packit a4aae4
 If so, process it. A glorified macro...
Packit a4aae4
 @param name The start element name
Packit a4aae4
 @param attrs The element's XML attributes
Packit a4aae4
 @return True if the element was a variable */
Packit a4aae4
inline bool D4ParserSax2::process_variable(const char *name, const xmlChar **attrs, int nb_attributes)
Packit a4aae4
{
Packit a4aae4
    Type t = get_type(name);
Packit a4aae4
    if (is_simple_type(t)) {
Packit a4aae4
        process_variable_helper(t, inside_simple_type, attrs, nb_attributes);
Packit a4aae4
        return true;
Packit a4aae4
    }
Packit a4aae4
    else {
Packit a4aae4
    	switch(t) {
Packit a4aae4
    	case dods_structure_c:
Packit a4aae4
            process_variable_helper(t, inside_constructor, attrs, nb_attributes);
Packit a4aae4
            return true;
Packit a4aae4
Packit a4aae4
    	case dods_sequence_c:
Packit a4aae4
            process_variable_helper(t, inside_constructor, attrs, nb_attributes);
Packit a4aae4
            return true;
Packit a4aae4
Packit a4aae4
    	default:
Packit a4aae4
    		return false;
Packit a4aae4
    	}
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 variable's 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 (e.g., inside_simple_type, ...)
Packit a4aae4
 @param attrs the attributes read with the tag */
Packit a4aae4
void D4ParserSax2::process_variable_helper(Type t, ParseState s, const xmlChar **attrs, int nb_attributes)
Packit a4aae4
{
Packit a4aae4
    transfer_xml_attrs(attrs, nb_attributes);
Packit a4aae4
Packit a4aae4
    if (check_required_attribute("name")) {
Packit a4aae4
        BaseType *btp = dmr()->factory()->NewVariable(t, xml_attrs["name"].value);
Packit a4aae4
        if (!btp) {
Packit a4aae4
            dmr_fatal_error(this, "Could not instantiate the variable '%s'.", xml_attrs["name"].value.c_str());
Packit a4aae4
            return;
Packit a4aae4
        }
Packit a4aae4
Packit a4aae4
        if ((t == dods_enum_c) && check_required_attribute("enum")) {
Packit a4aae4
            D4EnumDef *enum_def = 0;
Packit a4aae4
            string enum_path = xml_attrs["enum"].value;
Packit a4aae4
			if (enum_path[0] == '/')
Packit a4aae4
                enum_def = dmr()->root()->find_enum_def(enum_path);
Packit a4aae4
            else
Packit a4aae4
                enum_def = top_group()->find_enum_def(enum_path);
Packit a4aae4
Packit a4aae4
            if (!enum_def)
Packit a4aae4
                dmr_fatal_error(this, "Could not find the Enumeration definition '%s'.", enum_path.c_str());
Packit a4aae4
Packit a4aae4
            static_cast<D4Enum*>(btp)->set_enumeration(enum_def);
Packit a4aae4
        }
Packit a4aae4
Packit a4aae4
        btp->set_is_dap4(true); // see comment above
Packit a4aae4
        push_basetype(btp);
Packit a4aae4
Packit a4aae4
        push_attributes(btp->attributes());
Packit a4aae4
Packit a4aae4
        push_state(s);
Packit a4aae4
    }
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 D4ParserSax2::dmr_start_document(void * p)
Packit a4aae4
{
Packit a4aae4
    D4ParserSax2 *parser = static_cast<D4ParserSax2*>(p);
Packit a4aae4
    parser->d_error_msg = "";
Packit a4aae4
    parser->char_data = "";
Packit a4aae4
Packit a4aae4
    // Set this in intern_helper so that the loop test for the parser_end
Packit a4aae4
    // state works for the first iteration. It seems like XMLParseChunk calls this
Packit a4aae4
    // function on it's first run. jhrg 9/16/13
Packit a4aae4
    // parser->push_state(parser_start);
Packit a4aae4
Packit a4aae4
    parser->push_attributes(parser->dmr()->root()->attributes());
Packit a4aae4
Packit a4aae4
    if (parser->debug()) cerr << "Parser start 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 D4ParserSax2::dmr_end_document(void * p)
Packit a4aae4
{
Packit a4aae4
    D4ParserSax2 *parser = static_cast<D4ParserSax2*>(p);
Packit a4aae4
Packit a4aae4
    if (parser->debug()) cerr << "Parser end state: " << states[parser->get_state()] << endl;
Packit a4aae4
Packit a4aae4
    if (parser->get_state() != parser_end)
Packit a4aae4
        D4ParserSax2::dmr_error(parser, "The document contained unbalanced tags.");
Packit a4aae4
Packit a4aae4
    // If we've found any sort of error, don't make the DMR; intern() will
Packit a4aae4
    // take care of the error.
Packit a4aae4
    if (parser->get_state() == parser_error || parser->get_state() == parser_fatal_error)
Packit a4aae4
        return;
Packit a4aae4
Packit a4aae4
    if (!parser->empty_basetype() || parser->empty_group())
Packit a4aae4
    	D4ParserSax2::dmr_error(parser, "The document did not contain a valid root Group or contained unbalanced tags.");
Packit a4aae4
Packit a4aae4
    parser->pop_group();     // leave the stack 'clean'
Packit a4aae4
    parser->pop_attributes();
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/**
Packit a4aae4
 * Callback run when libxml2 reads the start of an element
Packit a4aae4
 *
Packit a4aae4
 * @param p Pointer to the parser object
Packit a4aae4
 * @param l Localname of the element
Packit a4aae4
 * @param prefix Namespace prefix of the element
Packit a4aae4
 * @param URI the Element namespace name if available
Packit a4aae4
 * @param nb_namespaces Number of namespace definitions on that node
Packit a4aae4
 * @param namespaces Pointer to the array of prefix/URI pairs namespace definitions
Packit a4aae4
 * @param nb_attributes The number of attributes on that node
Packit a4aae4
 * @param nb_defaulted The number of defaulted attributes. The defaulted ones are at the end of the array
Packit a4aae4
 * @param attributes Pointer to the array of (localname/prefix/URI/value/end) attribute values.
Packit a4aae4
 */
Packit a4aae4
void D4ParserSax2::dmr_start_element(void *p, const xmlChar *l, const xmlChar *prefix, const xmlChar *URI,
Packit a4aae4
        int nb_namespaces, const xmlChar **namespaces, int nb_attributes, int /*nb_defaulted*/,
Packit a4aae4
        const xmlChar **attributes)
Packit a4aae4
{
Packit a4aae4
    D4ParserSax2 *parser = static_cast<D4ParserSax2*>(p);
Packit a4aae4
    const char *localname = (const char *) l;
Packit a4aae4
Packit a4aae4
    if (parser->debug()) cerr << "Start element " << localname << "  prefix:  "<< (prefix?(char *)prefix:"null") << "  ns: "<< (URI?(char *)URI:"null")
Packit a4aae4
    		   << " (state: " << states[parser->get_state()] << ")" << endl;
Packit a4aae4
Packit a4aae4
    if(parser->get_state() != parser_error){
Packit a4aae4
        string dap4_ns_name = DapXmlNamspaces::getDapNamespaceString(DAP_4_0);
Packit a4aae4
        if (parser->debug()) cerr << "dap4_ns_name:         " << dap4_ns_name << endl;
Packit a4aae4
Packit a4aae4
        string this_element_ns_name = (URI != 0) ? ((char *)URI) : "";
Packit a4aae4
        if (parser->debug()) cerr << "this_element_ns_name: " << this_element_ns_name << endl;
Packit a4aae4
Packit a4aae4
        if(this_element_ns_name.compare(dap4_ns_name)){
Packit a4aae4
            if (parser->debug()) cerr << "Start of non DAP4 element: " << localname << " detected." << endl;
Packit a4aae4
        	parser->push_state(not_dap4_element);
Packit a4aae4
        	// return;
Packit a4aae4
        }
Packit a4aae4
    }
Packit a4aae4
Packit a4aae4
Packit a4aae4
    switch (parser->get_state()) {
Packit a4aae4
        case parser_start:
Packit a4aae4
            if (is_not(localname, "Dataset"))
Packit a4aae4
                D4ParserSax2::dmr_error(parser, "Expected DMR to start with a Dataset element; found '%s' instead.", localname);
Packit a4aae4
Packit a4aae4
            parser->root_ns = URI ? (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->dmr()->set_name(parser->xml_attrs["name"].value);
Packit a4aae4
Packit a4aae4
            if (parser->check_attribute("dapVersion"))
Packit a4aae4
                parser->dmr()->set_dap_version(parser->xml_attrs["dapVersion"].value);
Packit a4aae4
Packit a4aae4
            if (parser->check_attribute("dmrVersion"))
Packit a4aae4
                parser->dmr()->set_dmr_version(parser->xml_attrs["dmrVersion"].value);
Packit a4aae4
Packit a4aae4
            if (parser->check_attribute("base"))
Packit a4aae4
                parser->dmr()->set_request_xml_base(parser->xml_attrs["base"].value);
Packit a4aae4
Packit a4aae4
            if (!parser->root_ns.empty())
Packit a4aae4
                parser->dmr()->set_namespace(parser->root_ns);
Packit a4aae4
Packit a4aae4
            // Push the root Group on the stack
Packit a4aae4
            parser->push_group(parser->dmr()->root());
Packit a4aae4
Packit a4aae4
            parser->push_state(inside_dataset);
Packit a4aae4
Packit a4aae4
            break;
Packit a4aae4
Packit a4aae4
            // Both inside dataset and inside group can have the same stuff.
Packit a4aae4
            // The difference is that the Dataset holds the root group, which
Packit a4aae4
            // must be present; other groups are optional
Packit a4aae4
        case inside_dataset:
Packit a4aae4
        case inside_group:
Packit a4aae4
            if (parser->process_enum_def(localname, attributes, nb_attributes))
Packit a4aae4
                parser->push_state(inside_enum_def);
Packit a4aae4
            else if (parser->process_dimension_def(localname, attributes, nb_attributes))
Packit a4aae4
                parser->push_state(inside_dim_def);
Packit a4aae4
            else if (parser->process_group(localname, attributes, nb_attributes))
Packit a4aae4
                parser->push_state(inside_group);
Packit a4aae4
            else if (parser->process_variable(localname, attributes, nb_attributes))
Packit a4aae4
                // This will push either inside_simple_type or inside_structure
Packit a4aae4
                // onto the parser state stack.
Packit a4aae4
               break;
Packit a4aae4
            else if (parser->process_attribute(localname, attributes, nb_attributes))
Packit a4aae4
                // This will push either inside_attribute, inside_attribute_container
Packit a4aae4
                // or inside_otherxml_attribute onto the parser state stack
Packit a4aae4
                break;
Packit a4aae4
            else
Packit a4aae4
                D4ParserSax2::dmr_error(parser, "Expected an Attribute, Enumeration, Dimension, Group or variable element; found '%s' instead.", localname);
Packit a4aae4
            break;
Packit a4aae4
Packit a4aae4
        case inside_attribute_container:
Packit a4aae4
            if (parser->process_attribute(localname, attributes, nb_attributes))
Packit a4aae4
                break;
Packit a4aae4
            else
Packit a4aae4
                D4ParserSax2::dmr_error(parser, "Expected an Attribute element; found '%s' instead.", localname);
Packit a4aae4
            break;
Packit a4aae4
Packit a4aae4
        case inside_attribute:
Packit a4aae4
            if (parser->process_attribute(localname, attributes, nb_attributes))
Packit a4aae4
                break;
Packit a4aae4
            else if (strcmp(localname, "Value") == 0)
Packit a4aae4
                parser->push_state(inside_attribute_value);
Packit a4aae4
            else
Packit a4aae4
                dmr_error(parser, "Expected an 'Attribute' or 'Value' element; found '%s' instead.", localname);
Packit a4aae4
            break;
Packit a4aae4
Packit a4aae4
        case inside_attribute_value:
Packit a4aae4
            // Attribute values are processed by the end element code.
Packit a4aae4
            break;
Packit a4aae4
Packit a4aae4
        case inside_other_xml_attribute:
Packit a4aae4
            parser->other_xml_depth++;
Packit a4aae4
Packit a4aae4
            // Accumulate the elements here
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(); ++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->xml_attr_begin(); i != parser->xml_attr_end(); ++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_enum_def:
Packit a4aae4
            // process an EnumConst element
Packit a4aae4
            if (parser->process_enum_const(localname, attributes, nb_attributes))
Packit a4aae4
                parser->push_state(inside_enum_const);
Packit a4aae4
            else
Packit a4aae4
                dmr_error(parser, "Expected an 'EnumConst' element; found '%s' instead.", localname);
Packit a4aae4
            break;
Packit a4aae4
Packit a4aae4
        case inside_enum_const:
Packit a4aae4
            // No content; nothing to do
Packit a4aae4
            break;
Packit a4aae4
Packit a4aae4
        case inside_dim_def:
Packit a4aae4
            // No content; nothing to do
Packit a4aae4
            break;
Packit a4aae4
#if 0
Packit a4aae4
        case inside_dimension:
Packit a4aae4
            // No content.
Packit a4aae4
            break;
Packit a4aae4
#endif
Packit a4aae4
        case inside_dim:
Packit a4aae4
            // No content.
Packit a4aae4
            break;
Packit a4aae4
Packit a4aae4
        case inside_map:
Packit a4aae4
            // No content.
Packit a4aae4
            break;
Packit a4aae4
Packit a4aae4
        case inside_simple_type:
Packit a4aae4
            if (parser->process_attribute(localname, attributes, nb_attributes))
Packit a4aae4
                break;
Packit a4aae4
            else if (parser->process_dimension(localname, attributes, nb_attributes))
Packit a4aae4
            	parser->push_state(inside_dim);
Packit a4aae4
            else if (parser->process_map(localname, attributes, nb_attributes))
Packit a4aae4
            	parser->push_state(inside_map);
Packit a4aae4
            else
Packit a4aae4
                dmr_error(parser, "Expected an 'Attribute', 'Dim' or 'Map' element; found '%s' instead.", localname);
Packit a4aae4
            break;
Packit a4aae4
Packit a4aae4
        case inside_constructor:
Packit a4aae4
            if (parser->process_variable(localname, attributes, nb_attributes))
Packit a4aae4
                // This will push either inside_simple_type or inside_structure
Packit a4aae4
                // onto the parser state stack.
Packit a4aae4
                break;
Packit a4aae4
            else if (parser->process_attribute(localname, attributes, nb_attributes))
Packit a4aae4
                break;
Packit a4aae4
            else if (parser->process_dimension(localname, attributes, nb_attributes))
Packit a4aae4
                parser->push_state(inside_dim);
Packit a4aae4
            else if (parser->process_map(localname, attributes, nb_attributes))
Packit a4aae4
            	parser->push_state(inside_map);
Packit a4aae4
            else
Packit a4aae4
                D4ParserSax2::dmr_error(parser, "Expected an Attribute, Dim, Map or variable element; found '%s' instead.", localname);
Packit a4aae4
            break;
Packit a4aae4
Packit a4aae4
        case not_dap4_element:
Packit a4aae4
            if (parser->debug()) cerr << "Inside non DAP4 element. localname: " << localname << endl;
Packit a4aae4
        	break;
Packit a4aae4
Packit a4aae4
        case parser_unknown:
Packit a4aae4
            // FIXME?
Packit a4aae4
            // *** Never used? If so remove/error
Packit a4aae4
            parser->push_state(parser_unknown);
Packit a4aae4
            break;
Packit a4aae4
Packit a4aae4
        case parser_error:
Packit a4aae4
        case parser_fatal_error:
Packit a4aae4
            break;
Packit a4aae4
Packit a4aae4
        case parser_end:
Packit a4aae4
            // FIXME Error?
Packit a4aae4
            break;
Packit a4aae4
    }
Packit a4aae4
Packit a4aae4
    if (parser->debug()) cerr << "Start element exit state: " << states[parser->get_state()] << endl;
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
void D4ParserSax2::dmr_end_element(void *p, const xmlChar *l, const xmlChar *prefix, const xmlChar *URI)
Packit a4aae4
{
Packit a4aae4
    D4ParserSax2 *parser = static_cast<D4ParserSax2*>(p);
Packit a4aae4
    const char *localname = (const char *) l;
Packit a4aae4
Packit a4aae4
    if (parser->debug())
Packit a4aae4
        cerr << "End element " << localname << " (state " << states[parser->get_state()] << ")" << endl;
Packit a4aae4
Packit a4aae4
    switch (parser->get_state()) {
Packit a4aae4
    case parser_start:
Packit a4aae4
        dmr_fatal_error(parser, "Unexpected state, inside start state while processing element '%s'.", localname);
Packit a4aae4
        break;
Packit a4aae4
Packit a4aae4
    case inside_dataset:
Packit a4aae4
        if (is_not(localname, "Dataset"))
Packit a4aae4
            D4ParserSax2::dmr_error(parser, "Expected an end Dataset tag; found '%s' instead.", localname);
Packit a4aae4
Packit a4aae4
        parser->pop_state();
Packit a4aae4
        if (parser->get_state() != parser_start)
Packit a4aae4
            dmr_fatal_error(parser, "Unexpected state, expected start state.");
Packit a4aae4
        else {
Packit a4aae4
            parser->pop_state();
Packit a4aae4
            parser->push_state(parser_end);
Packit a4aae4
        }
Packit a4aae4
        break;
Packit a4aae4
Packit a4aae4
    case inside_group: {
Packit a4aae4
        if (is_not(localname, "Group"))
Packit a4aae4
            D4ParserSax2::dmr_error(parser, "Expected an end tag for a Group; found '%s' instead.", localname);
Packit a4aae4
Packit a4aae4
        if (!parser->empty_basetype() || parser->empty_group())
Packit a4aae4
            D4ParserSax2::dmr_error(parser,
Packit a4aae4
                    "The document did not contain a valid root Group or contained unbalanced tags.");
Packit a4aae4
Packit a4aae4
        parser->pop_group();
Packit a4aae4
        parser->pop_state();
Packit a4aae4
        break;
Packit a4aae4
    }
Packit a4aae4
Packit a4aae4
    case inside_attribute_container:
Packit a4aae4
        if (is_not(localname, "Attribute"))
Packit a4aae4
            D4ParserSax2::dmr_error(parser, "Expected an end Attribute tag; found '%s' instead.", localname);
Packit a4aae4
Packit a4aae4
        parser->pop_state();
Packit a4aae4
        parser->pop_attributes();
Packit a4aae4
        break;
Packit a4aae4
Packit a4aae4
    case inside_attribute:
Packit a4aae4
        if (is_not(localname, "Attribute"))
Packit a4aae4
            D4ParserSax2::dmr_error(parser, "Expected an end Attribute tag; found '%s' instead.", localname);
Packit a4aae4
Packit a4aae4
        parser->pop_state();
Packit a4aae4
        break;
Packit a4aae4
Packit a4aae4
    case inside_attribute_value: {
Packit a4aae4
        if (is_not(localname, "Value"))
Packit a4aae4
            D4ParserSax2::dmr_error(parser, "Expected an end value tag; found '%s' instead.", localname);
Packit a4aae4
Packit a4aae4
        parser->pop_state();
Packit a4aae4
Packit a4aae4
        // The old code added more values using the name and type as
Packit a4aae4
        // indexes to find the correct attribute. Use get() for that
Packit a4aae4
        // now. Or fix this code to keep a pointer to the to attribute...
Packit a4aae4
        D4Attributes *attrs = parser->top_attributes();
Packit a4aae4
        D4Attribute *attr = attrs->get(parser->dods_attr_name);
Packit a4aae4
        if (!attr) {
Packit a4aae4
            attr = new D4Attribute(parser->dods_attr_name, StringToD4AttributeType(parser->dods_attr_type));
Packit a4aae4
            attrs->add_attribute_nocopy(attr);
Packit a4aae4
        }
Packit a4aae4
        attr->add_value(parser->char_data);
Packit a4aae4
Packit a4aae4
        parser->char_data = ""; // Null this after use.
Packit a4aae4
        break;
Packit a4aae4
    }
Packit a4aae4
Packit a4aae4
    case inside_other_xml_attribute: {
Packit a4aae4
        if (strcmp(localname, "Attribute") == 0 && parser->root_ns == (const char *) URI) {
Packit a4aae4
            parser->pop_state();
Packit a4aae4
Packit a4aae4
            // The old code added more values using the name and type as
Packit a4aae4
            // indexes to find the correct attribute. Use get() for that
Packit a4aae4
            // now. Or fix this code to keep a pointer to the to attribute...
Packit a4aae4
            D4Attributes *attrs = parser->top_attributes();
Packit a4aae4
            D4Attribute *attr = attrs->get(parser->dods_attr_name);
Packit a4aae4
            if (!attr) {
Packit a4aae4
                attr = new D4Attribute(parser->dods_attr_name, StringToD4AttributeType(parser->dods_attr_type));
Packit a4aae4
                attrs->add_attribute_nocopy(attr);
Packit a4aae4
            }
Packit a4aae4
            attr->add_value(parser->other_xml);
Packit a4aae4
Packit a4aae4
            parser->other_xml = ""; // Null this after use.
Packit a4aae4
        }
Packit a4aae4
        else {
Packit a4aae4
            if (parser->other_xml_depth == 0) {
Packit a4aae4
                D4ParserSax2::dmr_error(parser, "Expected an OtherXML attribute to end! Instead I found '%s'",
Packit a4aae4
                        localname);
Packit a4aae4
                break;
Packit a4aae4
            }
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
Packit a4aae4
    case inside_enum_def:
Packit a4aae4
        if (is_not(localname, "Enumeration"))
Packit a4aae4
            D4ParserSax2::dmr_error(parser, "Expected an end Enumeration tag; found '%s' instead.", localname);
Packit a4aae4
        if (!parser->top_group())
Packit a4aae4
            D4ParserSax2::dmr_fatal_error(parser,
Packit a4aae4
                    "Expected a Group to be the current item, while finishing up an Enumeration.");
Packit a4aae4
        else {
Packit a4aae4
            // copy the pointer; not a deep copy
Packit a4aae4
            parser->top_group()->enum_defs()->add_enum_nocopy(parser->enum_def());
Packit a4aae4
            // Set the enum_def to null; next call to enum_def() will
Packit a4aae4
            // allocate a new object
Packit a4aae4
            parser->clear_enum_def();
Packit a4aae4
            parser->pop_state();
Packit a4aae4
        }
Packit a4aae4
        break;
Packit a4aae4
Packit a4aae4
    case inside_enum_const:
Packit a4aae4
        if (is_not(localname, "EnumConst"))
Packit a4aae4
            D4ParserSax2::dmr_error(parser, "Expected an end EnumConst tag; found '%s' instead.", localname);
Packit a4aae4
Packit a4aae4
        parser->pop_state();
Packit a4aae4
        break;
Packit a4aae4
Packit a4aae4
    case inside_dim_def: {
Packit a4aae4
        if (is_not(localname, "Dimension"))
Packit a4aae4
            D4ParserSax2::dmr_error(parser, "Expected an end Dimension tag; found '%s' instead.", localname);
Packit a4aae4
Packit a4aae4
        if (!parser->top_group())
Packit a4aae4
            D4ParserSax2::dmr_error(parser,
Packit a4aae4
                    "Expected a Group to be the current item, while finishing up an Dimension.");
Packit a4aae4
Packit a4aae4
        // FIXME Use the Group on the top of the group stack
Packit a4aae4
        // copy the pointer; not a deep copy
Packit a4aae4
        parser->top_group()->dims()->add_dim_nocopy(parser->dim_def());
Packit a4aae4
        //parser->dmr()->root()->dims()->add_dim_nocopy(parser->dim_def());
Packit a4aae4
        // Set the dim_def to null; next call to dim_def() will
Packit a4aae4
        // allocate a new object. Calling 'clear' is important because
Packit a4aae4
        // the cleanup method will free dim_def if it's not null and
Packit a4aae4
        // we just copied the pointer in the add_dim_nocopy() call
Packit a4aae4
        // above.
Packit a4aae4
        parser->clear_dim_def();
Packit a4aae4
        parser->pop_state();
Packit a4aae4
        break;
Packit a4aae4
    }
Packit a4aae4
Packit a4aae4
    case inside_simple_type:
Packit a4aae4
        if (is_simple_type(get_type(localname))) {
Packit a4aae4
            BaseType *btp = parser->top_basetype();
Packit a4aae4
            parser->pop_basetype();
Packit a4aae4
            parser->pop_attributes();
Packit a4aae4
Packit a4aae4
            BaseType *parent = 0;
Packit a4aae4
            if (!parser->empty_basetype())
Packit a4aae4
                parent = parser->top_basetype();
Packit a4aae4
            else if (!parser->empty_group())
Packit a4aae4
                parent = parser->top_group();
Packit a4aae4
            else {
Packit a4aae4
                dmr_fatal_error(parser, "Both the Variable and Groups stacks are empty while closing a %s element.",
Packit a4aae4
                        localname);
Packit a4aae4
                delete btp;
Packit a4aae4
                parser->pop_state();
Packit a4aae4
                break;
Packit a4aae4
            }
Packit a4aae4
Packit a4aae4
            if (parent->type() == dods_array_c)
Packit a4aae4
                static_cast<Array*>(parent)->prototype()->add_var_nocopy(btp);
Packit a4aae4
            else
Packit a4aae4
                parent->add_var_nocopy(btp);
Packit a4aae4
        }
Packit a4aae4
        else
Packit a4aae4
            D4ParserSax2::dmr_error(parser, "Expected an end tag for a simple type; found '%s' instead.", localname);
Packit a4aae4
Packit a4aae4
        parser->pop_state();
Packit a4aae4
        break;
Packit a4aae4
Packit a4aae4
    case inside_dim:
Packit a4aae4
        if (is_not(localname, "Dim"))
Packit a4aae4
            D4ParserSax2::dmr_fatal_error(parser, "Expected an end Dim tag; found '%s' instead.", localname);
Packit a4aae4
Packit a4aae4
        parser->pop_state();
Packit a4aae4
        break;
Packit a4aae4
Packit a4aae4
    case inside_map:
Packit a4aae4
        if (is_not(localname, "Map"))
Packit a4aae4
            D4ParserSax2::dmr_fatal_error(parser, "Expected an end Map tag; found '%s' instead.", localname);
Packit a4aae4
Packit a4aae4
        parser->pop_state();
Packit a4aae4
        break;
Packit a4aae4
Packit a4aae4
    case inside_constructor: {
Packit a4aae4
        if (strcmp(localname, "Structure") != 0 && strcmp(localname, "Sequence") != 0) {
Packit a4aae4
            D4ParserSax2::dmr_error(parser, "Expected an end tag for a constructor; found '%s' instead.", localname);
Packit a4aae4
            return;
Packit a4aae4
        }
Packit a4aae4
Packit a4aae4
        BaseType *btp = parser->top_basetype();
Packit a4aae4
        parser->pop_basetype();
Packit a4aae4
        parser->pop_attributes();
Packit a4aae4
Packit a4aae4
        BaseType *parent = 0;
Packit a4aae4
        if (!parser->empty_basetype())
Packit a4aae4
            parent = parser->top_basetype();
Packit a4aae4
        else if (!parser->empty_group())
Packit a4aae4
            parent = parser->top_group();
Packit a4aae4
        else {
Packit a4aae4
            dmr_fatal_error(parser, "Both the Variable and Groups stacks are empty while closing a %s element.",
Packit a4aae4
                    localname);
Packit a4aae4
            delete btp;
Packit a4aae4
            parser->pop_state();
Packit a4aae4
            break;
Packit a4aae4
        }
Packit a4aae4
Packit a4aae4
        // TODO Why doesn't this code mirror the simple_var case and test
Packit a4aae4
        // for the parent being an array? jhrg 10/13/13
Packit a4aae4
        parent->add_var_nocopy(btp);
Packit a4aae4
        parser->pop_state();
Packit a4aae4
        break;
Packit a4aae4
    }
Packit a4aae4
Packit a4aae4
    case not_dap4_element:
Packit a4aae4
        if (parser->debug()) cerr << "End of non DAP4 element: " << localname << endl;
Packit a4aae4
        parser->pop_state();
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
    case parser_fatal_error:
Packit a4aae4
        break;
Packit a4aae4
Packit a4aae4
    case parser_end:
Packit a4aae4
        // FIXME Error?
Packit a4aae4
        break;
Packit a4aae4
    }
Packit a4aae4
Packit a4aae4
    if (parser->debug()) cerr << "End element exit state: " << 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 D4ParserSax2::dmr_get_characters(void * p, const xmlChar * ch, int len)
Packit a4aae4
{
Packit a4aae4
    D4ParserSax2 *parser = static_cast<D4ParserSax2*>(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
            DBG(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
            DBG(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 D4ParserSax2::dmr_ignoreable_whitespace(void *p, const xmlChar *ch, int len)
Packit a4aae4
{
Packit a4aae4
    D4ParserSax2 *parser = static_cast<D4ParserSax2*>(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 D4ParserSax2::dmr_get_cdata(void *p, const xmlChar *value, int len)
Packit a4aae4
{
Packit a4aae4
    D4ParserSax2 *parser = static_cast<D4ParserSax2*>(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
            D4ParserSax2::dmr_error(parser, "Found a CData block but none are allowed by DAP4.");
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 D4ParserSax2::dmr_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
 @note This static function does not throw an exception or otherwise
Packit a4aae4
 alter flow of control except for altering the parser state.
Packit a4aae4
Packit a4aae4
 @param p The SAX parser
Packit a4aae4
 @param msg A printf-style format string. */
Packit a4aae4
void D4ParserSax2::dmr_fatal_error(void * p, const char *msg, ...)
Packit a4aae4
{
Packit a4aae4
    va_list args;
Packit a4aae4
    D4ParserSax2 *parser = static_cast<D4ParserSax2*>(p);
Packit a4aae4
Packit a4aae4
    parser->push_state(parser_fatal_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->d_context);
Packit a4aae4
Packit a4aae4
    if (!parser->d_error_msg.empty()) parser->d_error_msg += "\n";
Packit a4aae4
    parser->d_error_msg += "At line " + long_to_string(line) + ": " + string(str);
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
void D4ParserSax2::dmr_error(void *p, const char *msg, ...)
Packit a4aae4
{
Packit a4aae4
    va_list args;
Packit a4aae4
    D4ParserSax2 *parser = static_cast<D4ParserSax2*>(p);
Packit a4aae4
Packit a4aae4
    parser->push_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->d_context);
Packit a4aae4
Packit a4aae4
    if (!parser->d_error_msg.empty()) parser->d_error_msg += "\n";
Packit a4aae4
    parser->d_error_msg += "At line " + long_to_string(line) + ": " + string(str);
Packit a4aae4
}
Packit a4aae4
//@}
Packit a4aae4
Packit a4aae4
/** Clean up after a parse operation. If the parser encountered an error,
Packit a4aae4
 * throw either an Error or InternalErr object.
Packit a4aae4
 */
Packit a4aae4
void D4ParserSax2::cleanup_parse()
Packit a4aae4
{
Packit a4aae4
    bool wellFormed = d_context->wellFormed;
Packit a4aae4
    bool valid = d_context->valid;
Packit a4aae4
Packit a4aae4
    d_context->sax = NULL;
Packit a4aae4
    xmlFreeParserCtxt(d_context);
Packit a4aae4
Packit a4aae4
    delete d_enum_def;
Packit a4aae4
    d_enum_def = 0;
Packit a4aae4
Packit a4aae4
    delete d_dim_def;
Packit a4aae4
    d_dim_def = 0;
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 (!btp_stack.empty()) {
Packit a4aae4
        delete top_basetype();
Packit a4aae4
        pop_basetype();
Packit a4aae4
    }
Packit a4aae4
Packit a4aae4
    if (!wellFormed)
Packit a4aae4
        throw Error("The DMR was not well formed. " + d_error_msg);
Packit a4aae4
    else if (!valid)
Packit a4aae4
        throw Error("The DMR was not valid." + d_error_msg);
Packit a4aae4
    else if (get_state() == parser_error)
Packit a4aae4
        throw Error(d_error_msg);
Packit a4aae4
    else if (get_state() == parser_fatal_error)
Packit a4aae4
        throw InternalErr(d_error_msg);
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/**
Packit a4aae4
 * Read the DMR from a stream.
Packit a4aae4
 *
Packit a4aae4
 * @param f The input stream
Packit a4aae4
 * @param dest_dmr Value-result parameter. Pass a pointer to a DMR in and
Packit a4aae4
 * the information in the DMR will be added to it.
Packit a4aae4
 * @param boundary If not empty, use this as the boundary tag in a MPM document
Packit a4aae4
 * that marks the end of the part hat holds the DMR. Stop reading when the
Packit a4aae4
 * boundary is found.
Packit a4aae4
 * @param debug If true, ouput helpful debugging messages, False by default.
Packit a4aae4
 *
Packit a4aae4
 * @exception Error Thrown if the XML document could not be read or parsed.
Packit a4aae4
 * @exception InternalErr Thrown if an internal error is found.
Packit a4aae4
 */
Packit a4aae4
void D4ParserSax2::intern(istream &f, DMR *dest_dmr, bool debug)
Packit a4aae4
{
Packit a4aae4
    d_debug = debug;
Packit a4aae4
Packit a4aae4
    // Code example from libxml2 docs re: read from a stream.
Packit a4aae4
Packit a4aae4
    if (!f.good())
Packit a4aae4
        throw Error("Input stream not open or read error");
Packit a4aae4
    if (!dest_dmr)
Packit a4aae4
        throw InternalErr(__FILE__, __LINE__, "DMR object is null");
Packit a4aae4
Packit a4aae4
    d_dmr = dest_dmr; // dump values here
Packit a4aae4
Packit a4aae4
#if 0
Packit a4aae4
    // Old, bad, code. Lines are limited to 1023 chars including the element text.
Packit a4aae4
    const int size = 1024;
Packit a4aae4
    char chars[size];
Packit a4aae4
    int line = 1;
Packit a4aae4
Packit a4aae4
    f.getline(chars, size);
Packit a4aae4
    int res = f.gcount();
Packit a4aae4
    if (res == 0) throw Error("No input found while parsing the DMR.");
Packit a4aae4
Packit a4aae4
    getline(f, line);
Packit a4aae4
Packit a4aae4
    if (debug) cerr << "line: (" << line++ << "): " << chars << endl;
Packit a4aae4
Packit a4aae4
    d_context = xmlCreatePushParserCtxt(&d_dmr_sax_parser, this, chars, res - 1, "stream");
Packit a4aae4
    d_context->validate = true;
Packit a4aae4
    push_state(parser_start);
Packit a4aae4
Packit a4aae4
    f.getline(chars, size);
Packit a4aae4
    while ((f.gcount() > 0) && (get_state() != parser_end)) {
Packit a4aae4
        if (debug) cerr << "line: (" << line++ << "): " << chars << endl;
Packit a4aae4
        xmlParseChunk(d_context, chars, f.gcount() - 1, 0);
Packit a4aae4
        f.getline(chars, size);
Packit a4aae4
    }
Packit a4aae4
Packit a4aae4
    // This call ends the parse.
Packit a4aae4
    xmlParseChunk(d_context, chars, 0, 1/*terminate*/);
Packit a4aae4
#else
Packit a4aae4
    int line_num = 1;
Packit a4aae4
    string line;
Packit a4aae4
Packit a4aae4
    // Get the <xml ... ?> line
Packit a4aae4
    getline(f, line);
Packit a4aae4
    if (line.length() == 0) throw Error("No input found while parsing the DMR.");
Packit a4aae4
Packit a4aae4
    if (debug) cerr << "line: (" << line_num << "): " << endl << line << endl << endl;
Packit a4aae4
Packit a4aae4
    d_context = xmlCreatePushParserCtxt(&d_dmr_sax_parser, this, line.c_str(), line.length(), "stream");
Packit a4aae4
    d_context->validate = true;
Packit a4aae4
    push_state(parser_start);
Packit a4aae4
Packit a4aae4
    // Get the first line of stuff
Packit a4aae4
    getline(f, line);
Packit a4aae4
    ++line_num;
Packit a4aae4
Packit a4aae4
    if (debug) cerr << "line: (" << line_num << "): " << endl << line << endl << endl;
Packit a4aae4
Packit a4aae4
    while (!f.eof() && (get_state() != parser_end)) {
Packit a4aae4
        xmlParseChunk(d_context, line.c_str(), line.length(), 0);
Packit a4aae4
Packit a4aae4
        // Get the next line
Packit a4aae4
        getline(f, line);
Packit a4aae4
        ++line_num;
Packit a4aae4
Packit a4aae4
        if (debug) cerr << "line: (" << line_num << "): " << endl << line << endl << endl;
Packit a4aae4
    }
Packit a4aae4
Packit a4aae4
    // This call ends the parse.
Packit a4aae4
    xmlParseChunk(d_context, line.c_str(), 0, 1/*terminate*/);
Packit a4aae4
#endif
Packit a4aae4
Packit a4aae4
    // This checks that the state on the parser stack is parser_end and throws
Packit a4aae4
    // an exception if it's not (i.e., the loop exited with gcount() == 0).
Packit a4aae4
    cleanup_parse();
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** Parse a DMR document stored in a string.
Packit a4aae4
 *
Packit a4aae4
 * @param document Read the DMR from this string.
Packit a4aae4
 * @param dest_dmr Value/result parameter; dumps the information to this DMR
Packit a4aae4
 * instance.
Packit a4aae4
 * @param debug If true, ouput helpful debugging messages, False by default
Packit a4aae4
 *
Packit a4aae4
 * @exception Error Thrown if the XML document could not be read or parsed.
Packit a4aae4
 * @exception InternalErr Thrown if an internal error is found.
Packit a4aae4
 */
Packit a4aae4
void D4ParserSax2::intern(const string &document, DMR *dest_dmr, bool debug)
Packit a4aae4
{
Packit a4aae4
    intern(document.c_str(), document.length(), dest_dmr, debug);
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** Parse a DMR document stored in a char *buffer.
Packit a4aae4
 *
Packit a4aae4
 * @param document Read the DMR from this string.
Packit a4aae4
 * @param dest_dmr Value/result parameter; dumps the information to this DMR
Packit a4aae4
 * instance.
Packit a4aae4
 * @param debug If true, ouput helpful debugging messages, False by default
Packit a4aae4
 *
Packit a4aae4
 * @exception Error Thrown if the XML document could not be read or parsed.
Packit a4aae4
 * @exception InternalErr Thrown if an internal error is found.
Packit a4aae4
 */
Packit a4aae4
void D4ParserSax2::intern(const char *buffer, int size, DMR *dest_dmr, bool debug)
Packit a4aae4
{
Packit a4aae4
    if (!(size > 0)) return;
Packit a4aae4
Packit a4aae4
    d_debug = debug;
Packit a4aae4
Packit a4aae4
    // Code example from libxml2 docs re: read from a stream.
Packit a4aae4
Packit a4aae4
    if (!dest_dmr) throw InternalErr(__FILE__, __LINE__, "DMR object is null");
Packit a4aae4
    d_dmr = dest_dmr; // dump values in dest_dmr
Packit a4aae4
Packit a4aae4
    push_state(parser_start);
Packit a4aae4
    d_context = xmlCreatePushParserCtxt(&d_dmr_sax_parser, this, buffer, size, "stream");
Packit a4aae4
    d_context->validate = true;
Packit a4aae4
Packit a4aae4
    // This call ends the parse.
Packit a4aae4
    xmlParseChunk(d_context, buffer, 0, 1/*terminate*/);
Packit a4aae4
Packit a4aae4
    // This checks that the state on the parser stack is parser_end and throws
Packit a4aae4
    // an exception if it's not (i.e., the loop exited with gcount() == 0).
Packit a4aae4
    cleanup_parse();
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
} // namespace libdap