Blame Array.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) 2002,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
// (c) COPYRIGHT URI/MIT 1994-1999
Packit a4aae4
// Please read the full copyright statement in the file COPYRIGHT_URI.
Packit a4aae4
//
Packit a4aae4
// Authors:
Packit a4aae4
//      jhrg,jimg       James Gallagher <jgallagher@gso.uri.edu>
Packit a4aae4
Packit a4aae4
// Implementation for Array.
Packit a4aae4
//
Packit a4aae4
// jhrg 9/13/94
Packit a4aae4
Packit a4aae4
#include "config.h"
Packit a4aae4
Packit a4aae4
// #define DODS_DEBUG
Packit a4aae4
Packit a4aae4
#include <algorithm>
Packit a4aae4
#include <functional>
Packit a4aae4
#include <sstream>
Packit a4aae4
Packit a4aae4
#include "Array.h"
Packit a4aae4
#include "Grid.h"
Packit a4aae4
Packit a4aae4
#include "D4Attributes.h"
Packit a4aae4
#include "DMR.h"
Packit a4aae4
#include "D4Dimensions.h"
Packit a4aae4
#include "D4Maps.h"
Packit a4aae4
#include "D4Group.h"
Packit a4aae4
#include "D4EnumDefs.h"
Packit a4aae4
#include "D4Enum.h"
Packit a4aae4
#include "XMLWriter.h"
Packit a4aae4
Packit a4aae4
#include "util.h"
Packit a4aae4
#include "debug.h"
Packit a4aae4
#include "InternalErr.h"
Packit a4aae4
#include "escaping.h"
Packit a4aae4
Packit a4aae4
using namespace std;
Packit a4aae4
Packit a4aae4
namespace libdap {
Packit a4aae4
Packit a4aae4
Array::dimension::dimension(D4Dimension *d) : dim(d), use_sdim_for_slice(true)
Packit a4aae4
{
Packit a4aae4
    size = d->size();
Packit a4aae4
    name = d->name();
Packit a4aae4
Packit a4aae4
    start = 0;
Packit a4aae4
    stop = size - 1;
Packit a4aae4
    stride = 1;
Packit a4aae4
    c_size = size;
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
void
Packit a4aae4
Array::_duplicate(const Array &a)
Packit a4aae4
{
Packit a4aae4
    _shape = a._shape;
Packit a4aae4
Packit a4aae4
    // Deep copy the Maps if they are being used.
Packit a4aae4
    if (a.d_maps) {
Packit a4aae4
    	d_maps = new D4Maps(*(a.d_maps));
Packit a4aae4
    }
Packit a4aae4
    else {
Packit a4aae4
    	d_maps = 0;
Packit a4aae4
    }
Packit a4aae4
    // d_maps = a.d_maps ? new D4Maps(*(a.d_maps)) : 0;
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
// The first method of calculating length works when only one dimension is
Packit a4aae4
// constrained and you want the others to appear in total. This is important
Packit a4aae4
// when selecting from grids since users may not select from all dimensions
Packit a4aae4
// in which case that means they want the whole thing. Array projection
Packit a4aae4
// should probably work this way too, but it doesn't. 9/21/2001 jhrg
Packit a4aae4
Packit a4aae4
/** @deprecated Calling this method should never be necessary. It is used
Packit a4aae4
    internally called whenever the size of the Array is changed, e.g., by a
Packit a4aae4
    constraint.
Packit a4aae4
Packit a4aae4
    Changes the length property of the array.
Packit a4aae4
*/
Packit a4aae4
void
Packit a4aae4
Array::update_length(int)
Packit a4aae4
{
Packit a4aae4
    int length = 1;
Packit a4aae4
    for (Dim_citer i = _shape.begin(); i != _shape.end(); i++) {
Packit a4aae4
#if 0
Packit a4aae4
        // If the size of any dimension is zero, then the array is not
Packit a4aae4
        // capable of storing any values. jhrg 1/28/16
Packit a4aae4
        length *= (*i).c_size > 0 ? (*i).c_size : 1;
Packit a4aae4
#endif
Packit a4aae4
        length *= (*i).c_size;
Packit a4aae4
    }
Packit a4aae4
Packit a4aae4
    set_length(length);
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
// Construct an instance of Array. The (BaseType *) is assumed to be
Packit a4aae4
// allocated using new - The dtor for Vector will delete this object.
Packit a4aae4
Packit a4aae4
/** Build an array with a name and an element type. The name may be omitted,
Packit a4aae4
    which will create a nameless variable. The template (element type) pointer
Packit a4aae4
    may also be omitted, but if it is omitted when the Array is created, it
Packit a4aae4
    \e must be added (with \c add_var()) before \c read() or \c deserialize()
Packit a4aae4
    is called.
Packit a4aae4
Packit a4aae4
    @todo Force the Array::add_var() method to be used to add \e v.
Packit a4aae4
    This version of add_var() calls Vector::add_var().
Packit a4aae4
Packit a4aae4
    @param n A string containing the name of the variable to be
Packit a4aae4
    created.
Packit a4aae4
    @param v A pointer to a variable of the type to be included
Packit a4aae4
    in the Array. May be null and set later using add_var() or add_var_nocopy()
Packit a4aae4
    @brief Array constructor
Packit a4aae4
*/
Packit a4aae4
Array::Array(const string &n, BaseType *v, bool is_dap4 /* default:false */)
Packit a4aae4
	: Vector(n, 0, dods_array_c, is_dap4), d_maps(0)
Packit a4aae4
{
Packit a4aae4
    add_var(v); // Vector::add_var() stores null if v is null
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** Build an array on the server-side with a name, a dataset name from which
Packit a4aae4
    this Array is being created, and an element type.
Packit a4aae4
Packit a4aae4
    @todo Force the Array::add_var() method to be used to add \e v.
Packit a4aae4
    This version of add_var() calls Vector::add_var().
Packit a4aae4
Packit a4aae4
    @param n A string containing the name of the variable to be created.
Packit a4aae4
    @param d A string containing the name of the dataset from which this
Packit a4aae4
    variable is being created.
Packit a4aae4
    @param v A pointer to a variable of the type to be included
Packit a4aae4
    in the Array.
Packit a4aae4
    @brief Array constructor
Packit a4aae4
*/
Packit a4aae4
Array::Array(const string &n, const string &d, BaseType *v, bool is_dap4 /* default:false */)
Packit a4aae4
    : Vector(n, d, 0, dods_array_c, is_dap4), d_maps(0)
Packit a4aae4
{
Packit a4aae4
    add_var(v); // Vector::add_var() stores null if v is null
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** @brief The Array copy constructor. */
Packit a4aae4
Array::Array(const Array &rhs) : Vector(rhs)
Packit a4aae4
{
Packit a4aae4
    _duplicate(rhs);
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** @brief The Array destructor. */
Packit a4aae4
Array::~Array()
Packit a4aae4
{
Packit a4aae4
	delete d_maps;
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
BaseType *
Packit a4aae4
Array::ptr_duplicate()
Packit a4aae4
{
Packit a4aae4
    return new Array(*this);
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
Array &
Packit a4aae4
Array::operator=(const Array &rhs)
Packit a4aae4
{
Packit a4aae4
    if (this == &rhs)
Packit a4aae4
        return *this;
Packit a4aae4
Packit a4aae4
    dynamic_cast<Vector &>(*this) = rhs;
Packit a4aae4
Packit a4aae4
    _duplicate(rhs);
Packit a4aae4
Packit a4aae4
    return *this;
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
void
Packit a4aae4
Array::transform_to_dap4(D4Group *root, Constructor *container)
Packit a4aae4
{
Packit a4aae4
    DBG(cerr << __func__ << "() - BEGIN (array:" << name() << ")" << endl;);
Packit a4aae4
	Array *dest = static_cast<Array*>(ptr_duplicate());
Packit a4aae4
Packit a4aae4
	// If it's already a DAP4 object then we can just return it!
Packit a4aae4
	if(is_dap4()){
Packit a4aae4
        DBG(cerr << __func__ << "() - Already DAP4 type: Just making a copy and adding to container. " << endl;);
Packit a4aae4
	    container->add_var_nocopy(dest);
Packit a4aae4
	    DBG(cerr << __func__ << "() - END (Already DAP4 type)" << endl;);
Packit a4aae4
	}
Packit a4aae4
	// Process the Array's dimensions, making D4 shared dimensions for
Packit a4aae4
	// D2 dimensions that are named. If there is just a size, don't make
Packit a4aae4
	// a D4Dimension (In DAP4 you cannot share a dimension unless it has
Packit a4aae4
	// a name). jhrg 3/18/14
Packit a4aae4
Packit a4aae4
	D4Dimensions *root_dims = root->dims();
Packit a4aae4
	for (Array::Dim_iter dap2_dim = dest->dim_begin(), e = dest->dim_end(); dap2_dim != e; ++dap2_dim) {
Packit a4aae4
		if (!(*dap2_dim).name.empty()) {
Packit a4aae4
	        DBG(cerr << __func__ << "() - Processing the array dimension '" << (*dap2_dim).name << "'" << endl;);
Packit a4aae4
Packit a4aae4
			// If a D4Dimension with the name already exists, use it.
Packit a4aae4
			D4Dimension *d4_dim = root_dims->find_dim((*dap2_dim).name);
Packit a4aae4
			if (!d4_dim) {
Packit a4aae4
				d4_dim = new D4Dimension((*dap2_dim).name, (*dap2_dim).size);
Packit a4aae4
				root_dims->add_dim_nocopy(d4_dim);
Packit a4aae4
		        DBG(cerr << __func__ << "() -" <<
Packit a4aae4
		            " Added NEW D4Dimension '"<< d4_dim->name() << "' (" <<
Packit a4aae4
		            (void *)d4_dim << ") to root->dims()"<< endl;);
Packit a4aae4
			}
Packit a4aae4
	        else {
Packit a4aae4
	            DBG(cerr << __func__ << "() -" <<
Packit a4aae4
	                " Using Existing D4Dimension '"<< d4_dim->name() << "' (" <<
Packit a4aae4
	                (void *)d4_dim << ")"<< endl;);
Packit a4aae4
Packit a4aae4
	            if (d4_dim->size() != (unsigned long) (*dap2_dim).size) {
Packit a4aae4
                // TODO Revisit this decision. jhrg 3/18/14
Packit a4aae4
                // ...in case the name/size are different, make a unique D4Dimension
Packit a4aae4
                // but don't fiddle with the name. Not sure I like this idea, so I'm
Packit a4aae4
                // making the case explicit (could be rolled in to the block above).
Packit a4aae4
                // jhrg 3/18/14
Packit a4aae4
                //
Packit a4aae4
                // This is causing problems in the FITS handler because there are cases
Packit a4aae4
                // where two arrays have dimensions with the same name but different
Packit a4aae4
                // sizes. The deserializing code is using the first size listed, which is
Packit a4aae4
                // wrong in some cases. I'm going to try making this new D4Dimension using
Packit a4aae4
                // the dim name along with the variable name. jhrg 8/15/14
Packit a4aae4
                    d4_dim = new D4Dimension((*dap2_dim).name + "_" + name(), (*dap2_dim).size);
Packit a4aae4
                    DBG(cerr << __func__ << "() -" <<
Packit a4aae4
                         " Utilizing Name/Size Conflict Naming Artifice. name'"<< d4_dim->name() << "' (" <<
Packit a4aae4
                         (void *)d4_dim << ")"<< endl;);
Packit a4aae4
                    root_dims->add_dim_nocopy(d4_dim);
Packit a4aae4
	            }
Packit a4aae4
			}
Packit a4aae4
			// At this point d4_dim's name and size == those of (*d) so just set
Packit a4aae4
			// the D4Dimension pointer so it matches the one in the D4Group.
Packit a4aae4
			(*dap2_dim).dim = d4_dim;
Packit a4aae4
		}
Packit a4aae4
Packit a4aae4
	}
Packit a4aae4
Packit a4aae4
	// Copy the D2 attributes to D4 Attributes
Packit a4aae4
	dest->attributes()->transform_to_dap4(get_attr_table());
Packit a4aae4
	dest->set_is_dap4(true);
Packit a4aae4
	container->add_var_nocopy(dest);
Packit a4aae4
    DBG(cerr << __func__ << "() - END (array:" << name() << ")" << endl;);
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
bool Array::is_dap2_grid(){
Packit a4aae4
    bool is_grid = false;
Packit a4aae4
    if(this->is_dap4()){
Packit a4aae4
        DBG( cerr << __func__ << "() - Array '"<< name() << "' is DAP4 object!"  << endl;)
Packit a4aae4
        D4Maps *d4_maps = this->maps();
Packit a4aae4
        is_grid = d4_maps->size(); // It can't be a grid if there are no maps...
Packit a4aae4
        if(is_grid){
Packit a4aae4
            DBG( cerr << __func__ << "() - Array '"<< name() << "' has D4Maps." << endl;)
Packit a4aae4
            // hmmm this might be a DAP2 Grid...
Packit a4aae4
            D4Maps::D4MapsIter i = d4_maps->map_begin();
Packit a4aae4
            D4Maps::D4MapsIter e = d4_maps->map_end();
Packit a4aae4
            while(i!=e){
Packit a4aae4
                DBG( cerr << __func__ << "() - Map '"<< (*i)->array()->name() << " has " << (*i)->array()->_shape.size()  << " dimension(s)."  << endl;)
Packit a4aae4
                if((*i)->array()->_shape.size() > 1){
Packit a4aae4
                    is_grid = false;
Packit a4aae4
                    i = e;
Packit a4aae4
                }
Packit a4aae4
                else {
Packit a4aae4
                    i++;
Packit a4aae4
                }
Packit a4aae4
            }
Packit a4aae4
        }
Packit a4aae4
        else {
Packit a4aae4
            DBG( cerr << __func__ << "() - Array '"<< name() << "' has no D4Maps." << endl;)
Packit a4aae4
        }
Packit a4aae4
    }
Packit a4aae4
    DBG( cerr << __func__ << "() - is_grid: "<< (is_grid?"true":"false") << endl;)
Packit a4aae4
    return is_grid;
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/**
Packit a4aae4
 * @brief Transforms this instance of a D4Array into the corresponding DAP2 object.
Packit a4aae4
 *
Packit a4aae4
 * This transformation may return an Array or a Grid object. The DAP2 Grid construct
Packit a4aae4
 * is semantically contained in the DAP4 concept of arrays with Map arrays. If all
Packit a4aae4
 * of the Maps are one dimensional then the D4Array can be represented as a
Packit a4aae4
 * Grid object.
Packit a4aae4
 *
Packit a4aae4
 * @param  The AttrTable pointer parent_attr_table is used by Groups, which disappear
Packit a4aae4
 * from the DAP2 representation. Their children are returned in the the BAseType vector
Packit a4aae4
 * their attributes are added to parent_attr_table.
Packit a4aae4
 * @return A pointer to a vector of BaseType pointers (right?). In this D4Array case
Packit a4aae4
 * returned vector may contain a DAP2 Array or a Grid. Or, if the Array' prototype is
Packit a4aae4
 * a type that cannot be represented in DAP2 the return will be NULL.
Packit a4aae4
 */
Packit a4aae4
std::vector<BaseType *> *
Packit a4aae4
Array::transform_to_dap2(AttrTable *){
Packit a4aae4
    DBG(cerr << __func__ << "() - BEGIN Array '"<< name() << "'" << endl;);
Packit a4aae4
Packit a4aae4
    BaseType *dest;
Packit a4aae4
    if(is_dap4()){ // Don't convert a DAP2 thing
Packit a4aae4
Packit a4aae4
        // Can Precious be represented as a DAP2 Grid
Packit a4aae4
        if(is_dap2_grid()){
Packit a4aae4
            // Oh yay! Grids are special.
Packit a4aae4
            DBG(cerr << __func__ << "() - Array '"<< name() << "' is dap2 Grid!"  << endl;);
Packit a4aae4
            Grid *g = new Grid(name());
Packit a4aae4
            dest = g;
Packit a4aae4
            Array *grid_array = (Array *) this->ptr_duplicate();
Packit a4aae4
            g->set_array(grid_array);
Packit a4aae4
Packit a4aae4
            // Get the metadata into the Grid
Packit a4aae4
            AttrTable *grid_attrs = attributes()->get_AttrTable(name());
Packit a4aae4
            g->set_attr_table(*grid_attrs); // Copy it into the Grid object.
Packit a4aae4
            // grid_array->set_attr_table(*grid_attrs); // Copy it into the data Array.
Packit a4aae4
            delete grid_attrs;
Packit a4aae4
Packit a4aae4
            // Clear the Grid data Array attributes.
Packit a4aae4
            AttrTable at;
Packit a4aae4
            at.set_name(name());
Packit a4aae4
            grid_array->set_attr_table(at);
Packit a4aae4
Packit a4aae4
            // Process the Map Arrays.
Packit a4aae4
            D4Maps *d4_maps = this->maps();
Packit a4aae4
            vector<BaseType *> dropped_maps;
Packit a4aae4
            D4Maps::D4MapsIter miter = d4_maps->map_begin();
Packit a4aae4
            D4Maps::D4MapsIter end = d4_maps->map_end();
Packit a4aae4
            for( ; miter!=end; miter++){
Packit a4aae4
                D4Map *d4_map =  (*miter);
Packit a4aae4
                Array *d4_map_array = const_cast<Array*>(d4_map->array());
Packit a4aae4
                vector<BaseType *> *d2_result = d4_map_array->transform_to_dap2(&(g->get_attr_table()));
Packit a4aae4
                if(d2_result){
Packit a4aae4
                    if(d2_result->size()>1)
Packit a4aae4
                        throw Error(internal_error,string(__func__)+"() - ERROR: D4Map Array conversion resulted in multiple DAP2 objects.");
Packit a4aae4
Packit a4aae4
                    // TODO - This is probably slow and needs a better pattern. const_cast? static_cast?
Packit a4aae4
                    Array *d2_map_array = dynamic_cast<Array *>((*d2_result)[0]);
Packit a4aae4
                    if(d2_map_array){
Packit a4aae4
                        if(d2_map_array->dimensions()!=1)
Packit a4aae4
                            throw Error(internal_error,string(__func__)+"() - ERROR: DAP2 array from D4Map Array conversion has more than 1 dimension.");
Packit a4aae4
Packit a4aae4
                        g->add_map(d2_map_array,false);
Packit a4aae4
                        AttrTable at = d2_map_array->get_attr_table();
Packit a4aae4
                        DBG( cerr << __func__ << "() - " <<
Packit a4aae4
                            "DAS For Grid Map '" << d2_map_array->name() << "':" << endl;
Packit a4aae4
                             at.print(cerr); );
Packit a4aae4
                    }
Packit a4aae4
                    else {
Packit a4aae4
                        throw Error(internal_error,string(__func__)+"() - Unable to interpret returned DAP2 content.");
Packit a4aae4
                    }
Packit a4aae4
                    delete d2_result;
Packit a4aae4
                }
Packit a4aae4
                else {
Packit a4aae4
                    dropped_maps.push_back(d4_map_array);
Packit a4aae4
                }
Packit a4aae4
            }
Packit a4aae4
Packit a4aae4
            // Did we have a transform failure?
Packit a4aae4
            if(!dropped_maps.empty()){
Packit a4aae4
                // Yup... tell the story in the attributes.
Packit a4aae4
                AttrTable *dv_table = Constructor::make_dropped_vars_attr_table(&dropped_maps);
Packit a4aae4
                dest->get_attr_table().append_container(dv_table,dv_table->get_name());
Packit a4aae4
            }
Packit a4aae4
        }
Packit a4aae4
        else {
Packit a4aae4
            // It's not a Grid so we can make a simple copy of our Precious.
Packit a4aae4
            DBG( cerr << __func__ << "() - Array '"<< name() << "' is not a Grid!"  << endl);
Packit a4aae4
            BaseType *proto = this->prototype();
Packit a4aae4
            switch(proto->type()){
Packit a4aae4
            case dods_int64_c:
Packit a4aae4
            case dods_uint64_c:
Packit a4aae4
            case dods_enum_c:
Packit a4aae4
            case dods_opaque_c:
Packit a4aae4
            {
Packit a4aae4
                // For now we punt on these type as they have no easy representation in
Packit a4aae4
                // the DAP2 data model. By setting this to NULL we cause the Array to be
Packit a4aae4
                // dropped and this will be reflected in the metadata (DAS).
Packit a4aae4
                dest = NULL;
Packit a4aae4
                break;
Packit a4aae4
            }
Packit a4aae4
            default:
Packit a4aae4
            {
Packit a4aae4
                dest = this->ptr_duplicate();
Packit a4aae4
                // convert the d4 attributes to a dap2 attribute table.
Packit a4aae4
                AttrTable *attrs = this->attributes()->get_AttrTable(name());
Packit a4aae4
                dest->set_attr_table(*attrs);
Packit a4aae4
                dest->set_is_dap4(false);
Packit a4aae4
                AttrTable at = dest->get_attr_table();
Packit a4aae4
                DBG( cerr << __func__ << "() - " <<
Packit a4aae4
                    "DAS for new Array '" << dest->name() << "':" << endl;
Packit a4aae4
                    at.print(cerr); )
Packit a4aae4
Packit a4aae4
                break;
Packit a4aae4
            }
Packit a4aae4
            }
Packit a4aae4
        }
Packit a4aae4
Packit a4aae4
    }
Packit a4aae4
    else {
Packit a4aae4
        // If it's a DAP2 Array already then we just make a copy of our Precious.
Packit a4aae4
        dest = this->ptr_duplicate();
Packit a4aae4
    }
Packit a4aae4
    // attrs->print(cerr,"",true);
Packit a4aae4
    vector<BaseType *> *result;
Packit a4aae4
    if(dest){
Packit a4aae4
        result =  new vector<BaseType *>();
Packit a4aae4
        result->push_back(dest);
Packit a4aae4
    }
Packit a4aae4
    else {
Packit a4aae4
        result = NULL;
Packit a4aae4
    }
Packit a4aae4
    DBG( cerr << __func__ << "() - END Array '"<< name() << "'" << endl;);
Packit a4aae4
    return result;
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/**
Packit a4aae4
 * Hackery that helps build a new D4Group from an old one. We need to re-wire the
Packit a4aae4
 * D4Dimension (note the lack of an 's' at then end) that the copied Array objects
Packit a4aae4
 * hold. This code does that. Note that these are 'weak pointers' so they should
Packit a4aae4
 * never be freed - the D4Group object will take care of that.
Packit a4aae4
 *
Packit a4aae4
 * @note The order of the D4Dimension instances matches in 'old_dims' and 'new_dims'.
Packit a4aae4
 *
Packit a4aae4
 * @param old_dims The Old D4Dimension objects (held in a D4Dimensions instance)
Packit a4aae4
 * @param new_dims The New D4Dimension objects.
Packit a4aae4
 */
Packit a4aae4
void
Packit a4aae4
Array::update_dimension_pointers(D4Dimensions *old_dims, D4Dimensions *new_dims)
Packit a4aae4
{
Packit a4aae4
	std::vector<dimension>::iterator i = _shape.begin(), e = _shape.end();
Packit a4aae4
	while (i != e) {
Packit a4aae4
		D4Dimensions::D4DimensionsIter old_i = old_dims->dim_begin(), old_e = old_dims->dim_end();
Packit a4aae4
		while (old_i != old_e) {
Packit a4aae4
			if ((*i).dim == *old_i) {
Packit a4aae4
				(*i).dim =  new_dims->find_dim((*old_i)->name());
Packit a4aae4
			}
Packit a4aae4
			++old_i;
Packit a4aae4
		}
Packit a4aae4
Packit a4aae4
		++i;
Packit a4aae4
	}
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** @brief Add the BaseType pointer to this constructor type
Packit a4aae4
    instance.
Packit a4aae4
Packit a4aae4
    Propagate the name of the BaseType instance to this instance. This
Packit a4aae4
    ensures that variables at any given level of the DDS table have
Packit a4aae4
    unique names (i.e., that Arrays do not have their default name ""). If
Packit a4aae4
    <tt>v</tt>'s name is null, then assume that the array \e is named and
Packit a4aae4
    don't overwrite it with <tt>v</tt>'s null name.
Packit a4aae4
Packit a4aae4
    @note It is possible for the BaseType pointer to be null when this
Packit a4aae4
    method is called, a behavior that differs considerably from that of
Packit a4aae4
    the other 'add_var()' methods.
Packit a4aae4
Packit a4aae4
    @note This version checks to see if \e v is an array. If so, it calls
Packit a4aae4
    Vector::add_var() using the template variable of \e v and then appends
Packit a4aae4
    the dimensions of \e v to this array. This somewhat obscure behavior
Packit a4aae4
    simplifies 'translating' Sequences to arrays when the actual variable
Packit a4aae4
    being translated is not a regular Sequence but an array of Sequences.
Packit a4aae4
    This is of very debatable usefulness, but it's here all the same.
Packit a4aae4
Packit a4aae4
    @param v The template variable for the array
Packit a4aae4
    @param p The Part parameter defaults to nil and is ignored by this method.
Packit a4aae4
*/
Packit a4aae4
Packit a4aae4
void
Packit a4aae4
Array::add_var(BaseType *v, Part)
Packit a4aae4
{
Packit a4aae4
    // If 'v' is an Array, add the template instance to this object and
Packit a4aae4
    // then copy the dimension information. Odd semantics; I wonder if this
Packit a4aae4
    //is ever used. jhrg 6/13/12
Packit a4aae4
    if (v && v->type() == dods_array_c) {
Packit a4aae4
        Array *a = static_cast<Array*>(v);
Packit a4aae4
        Vector::add_var(a->var());
Packit a4aae4
Packit a4aae4
        Dim_iter i = a->dim_begin();
Packit a4aae4
        Dim_iter i_end = a->dim_end();
Packit a4aae4
        while (i != i_end) {
Packit a4aae4
            append_dim(a->dimension_size(i), a->dimension_name(i));
Packit a4aae4
            ++i;
Packit a4aae4
        }
Packit a4aae4
    }
Packit a4aae4
    else {
Packit a4aae4
        Vector::add_var(v);
Packit a4aae4
    }
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
void
Packit a4aae4
Array::add_var_nocopy(BaseType *v, Part)
Packit a4aae4
{
Packit a4aae4
    // If 'v' is an Array, add the template instance to this object and
Packit a4aae4
    // then copy the dimension information. Odd semantics; I wonder if this
Packit a4aae4
    //is ever used. jhrg 6/13/12
Packit a4aae4
    if (v && v->type() == dods_array_c) {
Packit a4aae4
        Array &a = dynamic_cast<Array&>(*v);
Packit a4aae4
        Vector::add_var_nocopy(a.var());
Packit a4aae4
        Dim_iter i = a.dim_begin();
Packit a4aae4
        Dim_iter i_end = a.dim_end();
Packit a4aae4
        while (i != i_end) {
Packit a4aae4
            append_dim(a.dimension_size(i), a.dimension_name(i));
Packit a4aae4
            ++i;
Packit a4aae4
        }
Packit a4aae4
    }
Packit a4aae4
    else {
Packit a4aae4
        Vector::add_var_nocopy(v);
Packit a4aae4
    }
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** Given a size and a name, this function adds a dimension to the
Packit a4aae4
    array.  For example, if the Array is already 10 elements long,
Packit a4aae4
    calling <tt>append_dim</tt> with a size of 5 will transform the array
Packit a4aae4
    into a 10x5 matrix.  Calling it again with a size of 2 will
Packit a4aae4
    create a 10x5x2 array, and so on.  This sets Vector's length
Packit a4aae4
    member as a side effect.
Packit a4aae4
Packit a4aae4
    @param size The size of the desired new row.
Packit a4aae4
    @param name The name of the new dimension.  This defaults to
Packit a4aae4
    an empty string.
Packit a4aae4
    @brief Add a dimension of a given size. */
Packit a4aae4
void
Packit a4aae4
Array::append_dim(int size, const string &name)
Packit a4aae4
{
Packit a4aae4
    dimension d(size, www2id(name));
Packit a4aae4
    _shape.push_back(d);
Packit a4aae4
Packit a4aae4
    update_length();
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
void
Packit a4aae4
Array::append_dim(D4Dimension *dim)
Packit a4aae4
{
Packit a4aae4
	dimension d(/*dim->size(), www2id(dim->name()),*/ dim);
Packit a4aae4
    _shape.push_back(d);
Packit a4aae4
Packit a4aae4
    update_length();
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** Creates a new OUTER dimension (slowest varying in rowmajor)
Packit a4aae4
 * for the array by prepending rather than appending it.
Packit a4aae4
 * @param size cardinality of the new dimension
Packit a4aae4
 * @param name  optional name for the new dimension
Packit a4aae4
 */
Packit a4aae4
void
Packit a4aae4
Array::prepend_dim(int size, const string& name/* = "" */)
Packit a4aae4
{
Packit a4aae4
  dimension d(size, www2id(name));
Packit a4aae4
  // Shifts the whole array, but it's tiny in general
Packit a4aae4
  _shape.insert(_shape.begin(), d);
Packit a4aae4
Packit a4aae4
  update_length(); // the number is ignored...
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
void
Packit a4aae4
Array::prepend_dim(D4Dimension *dim)
Packit a4aae4
{
Packit a4aae4
  dimension d(/*dim->size(), www2id(dim->name()),*/ dim);
Packit a4aae4
  // Shifts the whole array, but it's tiny in general
Packit a4aae4
  _shape.insert(_shape.begin(), d);
Packit a4aae4
Packit a4aae4
  update_length(); // the number is ignored...
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** Remove all the dimensions currently set for the Array. This also
Packit a4aae4
 * removes all constraint information.
Packit a4aae4
 */
Packit a4aae4
void
Packit a4aae4
Array::clear_all_dims()
Packit a4aae4
{
Packit a4aae4
	_shape.clear();
Packit a4aae4
}
Packit a4aae4
/** Resets the dimension constraint information so that the entire
Packit a4aae4
    array is selected.
Packit a4aae4
Packit a4aae4
    @brief Reset constraint to select entire array.
Packit a4aae4
*/
Packit a4aae4
Packit a4aae4
void
Packit a4aae4
Array::reset_constraint()
Packit a4aae4
{
Packit a4aae4
    set_length(-1);
Packit a4aae4
Packit a4aae4
    for (Dim_iter i = _shape.begin(); i != _shape.end(); i++) {
Packit a4aae4
        (*i).start = 0;
Packit a4aae4
        (*i).stop = (*i).size - 1;
Packit a4aae4
        (*i).stride = 1;
Packit a4aae4
        (*i).c_size = (*i).size;
Packit a4aae4
Packit a4aae4
        update_length();
Packit a4aae4
    }
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
Packit a4aae4
/** Tell the Array object to clear the constraint information about
Packit a4aae4
    dimensions. Do this once before calling <tt>add_constraint()</tt>
Packit a4aae4
    for each new constraint expression. Only the dimensions explicitly
Packit a4aae4
    selected using <tt>add_constraint()</tt> will be sent.
Packit a4aae4
Packit a4aae4
    @deprecated This should never be used.
Packit a4aae4
    @brief Clears the projection; add each projected dimension explicitly using
Packit a4aae4
    <tt>add_constraint</tt>.
Packit a4aae4
*/
Packit a4aae4
void
Packit a4aae4
Array::clear_constraint()
Packit a4aae4
{
Packit a4aae4
    reset_constraint();
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
// Note: MS VC++ won't tolerate embedded newlines in strings, hence the \n
Packit a4aae4
// is explicit.
Packit a4aae4
static const char *array_sss = \
Packit a4aae4
"Invalid constraint parameters: At least one of the start, stride or stop \n\
Packit a4aae4
specified do not match the array variable.";
Packit a4aae4
Packit a4aae4
/** Once a dimension has been created (see append_dim()), it can
Packit a4aae4
    be constrained.  This will make the array appear to the rest
Packit a4aae4
    of the world to be smaller than it is.  This functions sets the
Packit a4aae4
    projection for a dimension, and marks that dimension as part of the
Packit a4aae4
    current projection.
Packit a4aae4
Packit a4aae4
    @note A stride value <= 0 or > the array size is an error and causes
Packit a4aae4
    add_constraint to throw an Error. Similarly, start or stop values >
Packit a4aae4
    size also cause an Error exception to be thrown.
Packit a4aae4
Packit a4aae4
    @brief Adds a constraint to an Array dimension.
Packit a4aae4
Packit a4aae4
    @param i An iterator pointing to the dimension in the list of
Packit a4aae4
    dimensions.
Packit a4aae4
    @param start The start index of the constraint.
Packit a4aae4
    @param stride The stride value of the constraint.
Packit a4aae4
    @param stop The stop index of the constraint. A value of -1 indicates
Packit a4aae4
    'to the end' of the array.
Packit a4aae4
    @exception Error Thrown if the any of values of start, stop or stride
Packit a4aae4
    cannot be applied to this array. */
Packit a4aae4
void
Packit a4aae4
Array::add_constraint(Dim_iter i, int start, int stride, int stop)
Packit a4aae4
{
Packit a4aae4
    dimension &d = *i ;
Packit a4aae4
Packit a4aae4
    // if stop is -1, set it to the array's max element index
Packit a4aae4
    // jhrg 12/20/12
Packit a4aae4
    if (stop == -1)
Packit a4aae4
        stop = d.size - 1;
Packit a4aae4
Packit a4aae4
    // Check for bad constraints.
Packit a4aae4
    // Jose Garcia
Packit a4aae4
    // Usually invalid data for a constraint is the user's mistake
Packit a4aae4
    // because they build a wrong URL in the client side.
Packit a4aae4
    if (start >= d.size || stop >= d.size || stride > d.size || stride <= 0)
Packit a4aae4
        throw Error(malformed_expr, array_sss);
Packit a4aae4
Packit a4aae4
    if (((stop - start) / stride + 1) > d.size)
Packit a4aae4
        throw Error(malformed_expr, array_sss);
Packit a4aae4
Packit a4aae4
    d.start = start;
Packit a4aae4
    d.stop = stop;
Packit a4aae4
    d.stride = stride;
Packit a4aae4
Packit a4aae4
    d.c_size = (stop - start) / stride + 1;
Packit a4aae4
Packit a4aae4
    DBG(cerr << "add_constraint: c_size = " << d.c_size << endl);
Packit a4aae4
Packit a4aae4
    update_length();
Packit a4aae4
Packit a4aae4
    d.use_sdim_for_slice = false;
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
void
Packit a4aae4
Array::add_constraint(Dim_iter i, D4Dimension *dim)
Packit a4aae4
{
Packit a4aae4
    dimension &d = *i ;
Packit a4aae4
Packit a4aae4
    if (dim->constrained())
Packit a4aae4
    	add_constraint(i, dim->c_start(), dim->c_stride(), dim->c_stop());
Packit a4aae4
Packit a4aae4
    dim->set_used_by_projected_var(true);
Packit a4aae4
Packit a4aae4
	// In this case the value below overrides the value for use_sdim_for_slice
Packit a4aae4
	// set in the above call. jhrg 12/20/13
Packit a4aae4
	d.use_sdim_for_slice = true;
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** Returns an iterator to the first dimension of the Array. */
Packit a4aae4
Array::Dim_iter
Packit a4aae4
Array::dim_begin()
Packit a4aae4
{
Packit a4aae4
    return _shape.begin() ;
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** Returns an iterator past the last dimension of the Array. */
Packit a4aae4
Array::Dim_iter
Packit a4aae4
Array::dim_end()
Packit a4aae4
{
Packit a4aae4
    return _shape.end() ;
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
//TODO Many of these methods take a bool parameter that serves no use; remove.
Packit a4aae4
Packit a4aae4
/** Return the total number of dimensions contained in the array.
Packit a4aae4
    When constrained is TRUE, return the number of dimensions
Packit a4aae4
    given the most recently evaluated constraint expression.
Packit a4aae4
Packit a4aae4
    @brief Return the total number of dimensions in the array.
Packit a4aae4
    @param constrained A boolean flag to indicate whether the array is
Packit a4aae4
    constrained or not.  Ignored.
Packit a4aae4
*/
Packit a4aae4
unsigned int
Packit a4aae4
Array::dimensions(bool /*constrained*/)
Packit a4aae4
{
Packit a4aae4
    return _shape.size();
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** Return the size of the array dimension referred to by i.
Packit a4aae4
    If the dimension is constrained the constrained size is returned if
Packit a4aae4
    constrained is \c true.
Packit a4aae4
Packit a4aae4
    @brief Returns the size of the dimension.
Packit a4aae4
Packit a4aae4
    @param i The dimension.
Packit a4aae4
Packit a4aae4
    @param constrained If this parameter is TRUE, the method returns the
Packit a4aae4
    constrained size of the array so long as a constraint has been applied to
Packit a4aae4
    this dimension. If TRUE and no constraint has been applied, this method
Packit a4aae4
    returns zero. If it is FALSE, the method ignores any constraint that
Packit a4aae4
    has been applied to this dimension and returns the full size of the
Packit a4aae4
    dimension. The default value is FALSE.
Packit a4aae4
Packit a4aae4
    @return An integer containing the size of the specified dimension.
Packit a4aae4
*/
Packit a4aae4
int
Packit a4aae4
Array::dimension_size(Dim_iter i, bool constrained)
Packit a4aae4
{
Packit a4aae4
    int size = 0;
Packit a4aae4
Packit a4aae4
    if (!_shape.empty()) {
Packit a4aae4
        if (constrained)
Packit a4aae4
            size = (*i).c_size;
Packit a4aae4
        else
Packit a4aae4
            size = (*i).size;
Packit a4aae4
    }
Packit a4aae4
Packit a4aae4
    return size;
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** Use this function to return the start index of an array
Packit a4aae4
    dimension.  If the array is constrained (indicated with the
Packit a4aae4
    constrained argument), the start index of the constrained
Packit a4aae4
    array is returned (or zero if the dimension in question is not
Packit a4aae4
    selected at all).  See also <tt>dimension_stop()</tt> and
Packit a4aae4
    <tt>dimension_stride()</tt>.
Packit a4aae4
Packit a4aae4
    @brief Return the start index of a dimension.
Packit a4aae4
Packit a4aae4
    @param i The dimension.
Packit a4aae4
    @param constrained If this parameter is TRUE, the function
Packit a4aae4
    returns the start index only if the dimension is constrained
Packit a4aae4
    (subject to a start, stop, or stride constraint).  If
Packit a4aae4
    the dimension is not constrained, the function returns zero.  If it
Packit a4aae4
    is FALSE, the function returns the start index whether or not
Packit a4aae4
    the dimension is constrained.
Packit a4aae4
    @return The desired start index.
Packit a4aae4
*/
Packit a4aae4
int
Packit a4aae4
Array::dimension_start(Dim_iter i, bool /*constrained*/)
Packit a4aae4
{
Packit a4aae4
    return (!_shape.empty()) ? (*i).start : 0;
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** Use this function to return the stop index of an array
Packit a4aae4
    dimension.  If the array is constrained (indicated with the
Packit a4aae4
    constrained argument), the stop index of the constrained
Packit a4aae4
    array is returned (or zero if the dimension in question is not
Packit a4aae4
    selected at all).  See also <tt>dimension_start()</tt> and
Packit a4aae4
    <tt>dimension_stride()</tt>.
Packit a4aae4
Packit a4aae4
    @brief Return the stop index of the constraint.
Packit a4aae4
Packit a4aae4
    @param i The dimension.
Packit a4aae4
    @param constrained If this parameter is TRUE, the function
Packit a4aae4
    returns the stop index only if the dimension is  constrained
Packit a4aae4
    (subject to a start, stop, or stride constraint).  If
Packit a4aae4
    the dimension is not constrained, the function returns zero.  If it
Packit a4aae4
    is FALSE, the function returns the stop index whether or not
Packit a4aae4
    the dimension is constrained.
Packit a4aae4
    @return The desired stop index.
Packit a4aae4
*/
Packit a4aae4
int
Packit a4aae4
Array::dimension_stop(Dim_iter i, bool /*constrained*/)
Packit a4aae4
{
Packit a4aae4
    return (!_shape.empty()) ? (*i).stop : 0;
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** Use this function to return the stride value of an array
Packit a4aae4
    dimension.  If the array is constrained (indicated with the
Packit a4aae4
    constrained argument), the stride value of the constrained
Packit a4aae4
    array is returned (or zero if the dimension in question is not
Packit a4aae4
    selected at all).  See also <tt>dimension_stop()</tt> and
Packit a4aae4
    <tt>dimension_start()</tt>.
Packit a4aae4
Packit a4aae4
    @brief Returns the stride value of the constraint.
Packit a4aae4
Packit a4aae4
    @param i The dimension.
Packit a4aae4
    @param constrained If this parameter is TRUE, the function
Packit a4aae4
    returns the stride value only if the dimension is constrained
Packit a4aae4
    (subject to a start, stop, or stride constraint).  If
Packit a4aae4
    the dimension is not constrained, the function returns zero.  If it
Packit a4aae4
    is FALSE, the function returns the stride value whether or not
Packit a4aae4
    the dimension is constrained.
Packit a4aae4
    @return The stride value requested, or zero, if constrained
Packit a4aae4
    is TRUE and the dimension is not selected.
Packit a4aae4
*/
Packit a4aae4
int
Packit a4aae4
Array::dimension_stride(Dim_iter i, bool /*constrained*/)
Packit a4aae4
{
Packit a4aae4
    return (!_shape.empty()) ? (*i).stride : 0;
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** This function returns the name of the dimension indicated with
Packit a4aae4
    p.  Since this method is public, it is possible to call it
Packit a4aae4
    before the Array object has been properly initialized.  This will
Packit a4aae4
    cause an exception.  So don't do that.
Packit a4aae4
Packit a4aae4
    @brief Returns the name of the specified dimension.
Packit a4aae4
Packit a4aae4
    @param i The dimension.
Packit a4aae4
    @return A pointer to a string containing the dimension name.
Packit a4aae4
*/
Packit a4aae4
string
Packit a4aae4
Array::dimension_name(Dim_iter i)
Packit a4aae4
{
Packit a4aae4
    // Jose Garcia
Packit a4aae4
    // Since this method is public, it is possible for a user
Packit a4aae4
    // to call it before the Array object has been properly set
Packit a4aae4
    // this will cause an exception which is the user's fault.
Packit a4aae4
    // (User in this context is the developer of the surrogate library.)
Packit a4aae4
    if (_shape.empty())
Packit a4aae4
        throw  InternalErr(__FILE__, __LINE__,
Packit a4aae4
                           "*This* array has no dimensions.");
Packit a4aae4
    return (*i).name;
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
D4Dimension *
Packit a4aae4
Array::dimension_D4dim(Dim_iter i)
Packit a4aae4
{
Packit a4aae4
	return (!_shape.empty()) ? (*i).dim : 0;
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
D4Maps *
Packit a4aae4
Array::maps()
Packit a4aae4
{
Packit a4aae4
	if (!d_maps) d_maps = new D4Maps(this); 	// init with this as parent
Packit a4aae4
	return d_maps;
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
#if 0
Packit a4aae4
/**
Packit a4aae4
 * @brief Returns the width of the data, in bytes.
Packit a4aae4
 * @param constrained if true, return the size of the array in bytes taking into
Packit a4aae4
 * account the current constraints on various dimensions. False by default.
Packit a4aae4
 * @return The number of bytes needed to store the array values.
Packit a4aae4
 */
Packit a4aae4
unsigned int Array::width(bool constrained) const
Packit a4aae4
{
Packit a4aae4
Packit a4aae4
	if (constrained) {
Packit a4aae4
		// This preserves the original method's semantics when we ask for the
Packit a4aae4
		// size of the constrained array but no constraint has been applied.
Packit a4aae4
		// In this case, length will be -1. Wrong, I know...
Packit a4aae4
		return length() * var()->width(constrained);
Packit a4aae4
	}
Packit a4aae4
	else {
Packit a4aae4
		int length = 1;
Packit a4aae4
		for (Dim_iter i = _shape.begin(); i != _shape.end(); i++) {
Packit a4aae4
			length *= dimension_size(i, false);
Packit a4aae4
		}
Packit a4aae4
		return length * var()->width(false);
Packit a4aae4
	}
Packit a4aae4
}
Packit a4aae4
#endif
Packit a4aae4
Packit a4aae4
class PrintD4ArrayDimXMLWriter: public unary_function<Array::dimension&, void> {
Packit a4aae4
	XMLWriter &xml;
Packit a4aae4
	// Was this variable constrained using local/direct slicing? i.e., is d_local_constraint set?
Packit a4aae4
	// If so, don't use shared dimensions; instead emit Dim elements that are anonymous.
Packit a4aae4
	bool d_constrained;
Packit a4aae4
public:
Packit a4aae4
Packit a4aae4
	PrintD4ArrayDimXMLWriter(XMLWriter &xml, bool c) : xml(xml), d_constrained(c) { }
Packit a4aae4
Packit a4aae4
	void operator()(Array::dimension &d)
Packit a4aae4
	{
Packit a4aae4
		// This duplicates code in D4Dimensions (where D4Dimension::print_dap4() is defined
Packit a4aae4
		// because of the need to print the constrained size of a dimension. I think that
Packit a4aae4
		// the constraint information has to be kept here and not in the dimension (since they
Packit a4aae4
		// are shared dims). Could hack print_dap4() to take the constrained size, however.
Packit a4aae4
		if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*) "Dim") < 0)
Packit a4aae4
			throw InternalErr(__FILE__, __LINE__, "Could not write Dim element");
Packit a4aae4
Packit a4aae4
		string name = (d.dim) ? d.dim->fully_qualified_name() : d.name;
Packit a4aae4
		// If there is a name, there must be a Dimension (named dimension) in scope
Packit a4aae4
		// so write its name but not its size.
Packit a4aae4
		if (!d_constrained && !name.empty()) {
Packit a4aae4
			if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "name", (const xmlChar*) name.c_str())
Packit a4aae4
					< 0) throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
Packit a4aae4
		}
Packit a4aae4
		else if (d.use_sdim_for_slice) {
Packit a4aae4
			assert(!name.empty());
Packit a4aae4
			if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "name", (const xmlChar*) name.c_str())
Packit a4aae4
					< 0) throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
Packit a4aae4
		}
Packit a4aae4
		else {
Packit a4aae4
			ostringstream size;
Packit a4aae4
			size << (d_constrained ? d.c_size : d.size);
Packit a4aae4
			if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "size",
Packit a4aae4
					(const xmlChar*) size.str().c_str()) < 0)
Packit a4aae4
				throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
Packit a4aae4
		}
Packit a4aae4
Packit a4aae4
		if (xmlTextWriterEndElement(xml.get_writer()) < 0)
Packit a4aae4
			throw InternalErr(__FILE__, __LINE__, "Could not end Dim element");
Packit a4aae4
	}
Packit a4aae4
};
Packit a4aae4
Packit a4aae4
class PrintD4ConstructorVarXMLWriter: public unary_function<BaseType*, void> {
Packit a4aae4
	XMLWriter &xml;
Packit a4aae4
	bool d_constrained;
Packit a4aae4
public:
Packit a4aae4
	PrintD4ConstructorVarXMLWriter(XMLWriter &xml, bool c) : xml(xml), d_constrained(c) { }
Packit a4aae4
Packit a4aae4
	void operator()(BaseType *btp)
Packit a4aae4
	{
Packit a4aae4
		btp->print_dap4(xml, d_constrained);
Packit a4aae4
	}
Packit a4aae4
};
Packit a4aae4
Packit a4aae4
class PrintD4MapXMLWriter: public unary_function<D4Map*, void> {
Packit a4aae4
	XMLWriter &xml;
Packit a4aae4
Packit a4aae4
public:
Packit a4aae4
	PrintD4MapXMLWriter(XMLWriter &xml) : xml(xml) { }
Packit a4aae4
Packit a4aae4
	void operator()(D4Map *m)
Packit a4aae4
	{
Packit a4aae4
		m->print_dap4(xml);
Packit a4aae4
	}
Packit a4aae4
};
Packit a4aae4
Packit a4aae4
/**
Packit a4aae4
 * @brief Print the DAP4 representation of an array.
Packit a4aae4
 * @param xml
Packit a4aae4
 * @param constrained
Packit a4aae4
 */
Packit a4aae4
void
Packit a4aae4
Array::print_dap4(XMLWriter &xml, bool constrained /* default: false*/)
Packit a4aae4
{
Packit a4aae4
	if (constrained && !send_p()) return;
Packit a4aae4
Packit a4aae4
	if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*) var()->type_name().c_str()) < 0)
Packit a4aae4
		throw InternalErr(__FILE__, __LINE__, "Could not write " + type_name() + " element");
Packit a4aae4
Packit a4aae4
	if (!name().empty())
Packit a4aae4
		if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "name", (const xmlChar*)name().c_str()) < 0)
Packit a4aae4
			throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
Packit a4aae4
Packit a4aae4
	// Hack job... Copied from D4Enum::print_xml_writer. jhrg 11/12/13
Packit a4aae4
	if (var()->type() == dods_enum_c) {
Packit a4aae4
		D4Enum *e = static_cast<D4Enum*>(var());
Packit a4aae4
		string path = e->enumeration()->name();
Packit a4aae4
		if (e->enumeration()->parent()) {
Packit a4aae4
			// print the FQN for the enum def; D4Group::FQN() includes the trailing '/'
Packit a4aae4
			path = static_cast<D4Group*>(e->enumeration()->parent()->parent())->FQN() + path;
Packit a4aae4
		}
Packit a4aae4
		if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "enum", (const xmlChar*)path.c_str()) < 0)
Packit a4aae4
			throw InternalErr(__FILE__, __LINE__, "Could not write attribute for enum");
Packit a4aae4
	}
Packit a4aae4
Packit a4aae4
	if (prototype()->is_constructor_type()) {
Packit a4aae4
		Constructor &c = static_cast<Constructor&>(*prototype());
Packit a4aae4
		for_each(c.var_begin(), c.var_end(), PrintD4ConstructorVarXMLWriter(xml, constrained));
Packit a4aae4
		// bind2nd(mem_fun_ref(&BaseType::print_dap4), xml));
Packit a4aae4
	}
Packit a4aae4
Packit a4aae4
	// Drop the local_constraint which is per-array and use a per-dimension on instead
Packit a4aae4
    for_each(dim_begin(), dim_end(), PrintD4ArrayDimXMLWriter(xml, constrained));
Packit a4aae4
Packit a4aae4
	attributes()->print_dap4(xml);
Packit a4aae4
Packit a4aae4
	for_each(maps()->map_begin(), maps()->map_end(), PrintD4MapXMLWriter(xml));
Packit a4aae4
Packit a4aae4
	if (xmlTextWriterEndElement(xml.get_writer()) < 0)
Packit a4aae4
		throw InternalErr(__FILE__, __LINE__, "Could not end " + type_name() + " element");
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** Prints a declaration for the Array.  This is what appears in a
Packit a4aae4
    DDS.  If the Array is constrained, the declaration will reflect
Packit a4aae4
    the size of the Array once the constraint is applied.
Packit a4aae4
Packit a4aae4
    @brief Prints a DDS entry for the Array.
Packit a4aae4
Packit a4aae4
    @param out Write the output to this FILE *.
Packit a4aae4
    @param space A string containing spaces to precede the
Packit a4aae4
    declaration.
Packit a4aae4
    @param print_semi A boolean indicating whether to print a
Packit a4aae4
    semi-colon after the declaration.  (TRUE means ``print a
Packit a4aae4
    semi-colon.'')
Packit a4aae4
    @param constraint_info A boolean value.  See
Packit a4aae4
    <tt>BaseType::print_decl()</tt>.
Packit a4aae4
    @param constrained This argument should be TRUE if the Array is
Packit a4aae4
    constrained, and FALSE otherwise.
Packit a4aae4
*/
Packit a4aae4
void
Packit a4aae4
Array::print_decl(FILE *out, string space, bool print_semi,
Packit a4aae4
                  bool constraint_info, bool constrained)
Packit a4aae4
{
Packit a4aae4
    ostringstream oss;
Packit a4aae4
    print_decl(oss, space, print_semi, constraint_info, constrained);
Packit a4aae4
    fwrite(oss.str().data(), sizeof(char), oss.str().length(), out);
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** Prints a declaration for the Array.  This is what appears in a
Packit a4aae4
    DDS.  If the Array is constrained, the declaration will reflect
Packit a4aae4
    the size of the Array once the constraint is applied.
Packit a4aae4
Packit a4aae4
    @brief Prints a DDS entry for the Array.
Packit a4aae4
Packit a4aae4
    @param out Write the output to this ostream.
Packit a4aae4
    @param space A string containing spaces to precede the
Packit a4aae4
    declaration.
Packit a4aae4
    @param print_semi A boolean indicating whether to print a
Packit a4aae4
    semi-colon after the declaration.  (TRUE means ``print a
Packit a4aae4
    semi-colon.'')
Packit a4aae4
    @param constraint_info A boolean value.  See
Packit a4aae4
    <tt>BaseType::print_decl()</tt>.
Packit a4aae4
    @param constrained This argument should be TRUE if the Array is
Packit a4aae4
    constrained, and FALSE otherwise.
Packit a4aae4
*/
Packit a4aae4
void Array::print_decl(ostream &out, string space, bool print_semi, bool constraint_info, bool constrained)
Packit a4aae4
{
Packit a4aae4
    if (constrained && !send_p()) return;
Packit a4aae4
Packit a4aae4
    // print it, but w/o semicolon
Packit a4aae4
    var()->print_decl(out, space, false, constraint_info, constrained);
Packit a4aae4
Packit a4aae4
    for (Dim_citer i = _shape.begin(); i != _shape.end(); i++) {
Packit a4aae4
        out << "[";
Packit a4aae4
        if ((*i).name != "") {
Packit a4aae4
            out << id2www((*i).name) << " = ";
Packit a4aae4
        }
Packit a4aae4
        if (constrained) {
Packit a4aae4
            out << (*i).c_size << "]";
Packit a4aae4
        }
Packit a4aae4
        else {
Packit a4aae4
            out << (*i).size << "]";
Packit a4aae4
        }
Packit a4aae4
    }
Packit a4aae4
Packit a4aae4
    if (print_semi) {
Packit a4aae4
        out << ";\n";
Packit a4aae4
    }
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/**
Packit a4aae4
 * @deprecated
Packit a4aae4
 */
Packit a4aae4
void
Packit a4aae4
Array::print_xml(FILE *out, string space, bool constrained)
Packit a4aae4
{
Packit a4aae4
    XMLWriter xml(space);
Packit a4aae4
    print_xml_writer_core(xml, constrained, "Array");
Packit a4aae4
    fwrite(xml.get_doc(), sizeof(char), xml.get_doc_size(), out);
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/**
Packit a4aae4
 * @deprecated
Packit a4aae4
 */
Packit a4aae4
void
Packit a4aae4
Array::print_xml(ostream &out, string space, bool constrained)
Packit a4aae4
{
Packit a4aae4
    XMLWriter xml(space);
Packit a4aae4
    print_xml_writer_core(xml, constrained, "Array");
Packit a4aae4
    out << xml.get_doc();
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/**
Packit a4aae4
 * @deprecated
Packit a4aae4
 */
Packit a4aae4
void
Packit a4aae4
Array::print_as_map_xml(FILE *out, string space, bool constrained)
Packit a4aae4
{
Packit a4aae4
    XMLWriter xml(space);
Packit a4aae4
    print_xml_writer_core(xml, constrained, "Map");
Packit a4aae4
    fwrite(xml.get_doc(), sizeof(char), xml.get_doc_size(), out);
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/**
Packit a4aae4
 * @deprecated
Packit a4aae4
 */
Packit a4aae4
void
Packit a4aae4
Array::print_as_map_xml(ostream &out, string space, bool constrained)
Packit a4aae4
{
Packit a4aae4
    XMLWriter xml(space);
Packit a4aae4
    print_xml_writer_core(xml, constrained, "Map");
Packit a4aae4
    out << xml.get_doc();
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/**
Packit a4aae4
 * @deprecated
Packit a4aae4
 */
Packit a4aae4
void
Packit a4aae4
Array::print_xml_core(FILE *out, string space, bool constrained, string tag)
Packit a4aae4
{
Packit a4aae4
    XMLWriter xml(space);
Packit a4aae4
    print_xml_writer_core(xml, constrained, tag);
Packit a4aae4
    fwrite(xml.get_doc(), sizeof(char), xml.get_doc_size(), out);
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/**
Packit a4aae4
 * @deprecated
Packit a4aae4
 */
Packit a4aae4
void
Packit a4aae4
Array::print_xml_core(ostream &out, string space, bool constrained, string tag)
Packit a4aae4
{
Packit a4aae4
    XMLWriter xml(space);
Packit a4aae4
    print_xml_writer_core(xml, constrained, tag);
Packit a4aae4
    out << xml.get_doc();
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
void
Packit a4aae4
Array::print_xml_writer(XMLWriter &xml, bool constrained)
Packit a4aae4
{
Packit a4aae4
    print_xml_writer_core(xml, constrained, "Array");
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
void
Packit a4aae4
Array::print_as_map_xml_writer(XMLWriter &xml, bool constrained)
Packit a4aae4
{
Packit a4aae4
    print_xml_writer_core(xml, constrained, "Map");
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
class PrintArrayDimXMLWriter : public unary_function<Array::dimension&, void>
Packit a4aae4
{
Packit a4aae4
    XMLWriter &xml;
Packit a4aae4
    bool d_constrained;
Packit a4aae4
public:
Packit a4aae4
    PrintArrayDimXMLWriter(XMLWriter &xml, bool c) : xml(xml), d_constrained(c) {}
Packit a4aae4
Packit a4aae4
    void operator()(Array::dimension &d)
Packit a4aae4
    {
Packit a4aae4
        if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*)"dimension") < 0)
Packit a4aae4
            throw InternalErr(__FILE__, __LINE__, "Could not write dimension element");
Packit a4aae4
Packit a4aae4
        if (!d.name.empty())
Packit a4aae4
            if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "name", (const xmlChar*)d.name.c_str()) < 0)
Packit a4aae4
                throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
Packit a4aae4
Packit a4aae4
        ostringstream size;
Packit a4aae4
        size << (d_constrained ? d.c_size : d.size);
Packit a4aae4
        if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "size", (const xmlChar*)size.str().c_str()) < 0)
Packit a4aae4
            throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
Packit a4aae4
Packit a4aae4
        if (xmlTextWriterEndElement(xml.get_writer()) < 0)
Packit a4aae4
            throw InternalErr(__FILE__, __LINE__, "Could not end dimension element");
Packit a4aae4
    }
Packit a4aae4
};
Packit a4aae4
Packit a4aae4
void
Packit a4aae4
Array::print_xml_writer_core(XMLWriter &xml, bool constrained, string tag)
Packit a4aae4
{
Packit a4aae4
    if (constrained && !send_p())
Packit a4aae4
        return;
Packit a4aae4
Packit a4aae4
    if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*)tag.c_str()) < 0)
Packit a4aae4
        throw InternalErr(__FILE__, __LINE__, "Could not write " + tag + " element");
Packit a4aae4
Packit a4aae4
    if (!name().empty())
Packit a4aae4
        if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "name", (const xmlChar*)name().c_str()) < 0)
Packit a4aae4
            throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name");
Packit a4aae4
Packit a4aae4
    get_attr_table().print_xml_writer(xml);
Packit a4aae4
Packit a4aae4
    BaseType *btp = var();
Packit a4aae4
    string tmp_name = btp->name();
Packit a4aae4
    btp->set_name("");
Packit a4aae4
    btp->print_xml_writer(xml, constrained);
Packit a4aae4
    btp->set_name(tmp_name);
Packit a4aae4
Packit a4aae4
    for_each(dim_begin(), dim_end(), PrintArrayDimXMLWriter(xml, constrained));
Packit a4aae4
Packit a4aae4
    if (xmlTextWriterEndElement(xml.get_writer()) < 0)
Packit a4aae4
        throw InternalErr(__FILE__, __LINE__, "Could not end " + tag + " element");
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** Prints the values in ASCII of the entire (constrained) array. This method
Packit a4aae4
    Attempts to make an aesthetically pleasing display. However, it is
Packit a4aae4
    primarily intended for debugging purposes.
Packit a4aae4
Packit a4aae4
    @param out Write the output to this FILE *.
Packit a4aae4
    @param index
Packit a4aae4
    @param dims
Packit a4aae4
    @param shape
Packit a4aae4
Packit a4aae4
    @brief Print the value given the current constraint.
Packit a4aae4
*/
Packit a4aae4
unsigned int
Packit a4aae4
Array::print_array(FILE *out, unsigned int index, unsigned int dims,
Packit a4aae4
                   unsigned int shape[])
Packit a4aae4
{
Packit a4aae4
    ostringstream oss;
Packit a4aae4
    unsigned int i = print_array(oss, index, dims, shape);
Packit a4aae4
    fwrite(oss.str().data(), sizeof(char), oss.str().length(), out);
Packit a4aae4
Packit a4aae4
    return i;
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** Prints the values in ASCII of the entire (constrained) array. This method
Packit a4aae4
    Attempts to make an anesthetically pleasing display. However, it is
Packit a4aae4
    primarily intended for debugging purposes.
Packit a4aae4
Packit a4aae4
    @param out Write the output to this ostream
Packit a4aae4
    @param index
Packit a4aae4
    @param dims
Packit a4aae4
    @param shape
Packit a4aae4
Packit a4aae4
    @brief Print the value given the current constraint.
Packit a4aae4
*/
Packit a4aae4
unsigned int Array::print_array(ostream &out, unsigned int index, unsigned int dims, unsigned int shape[])
Packit a4aae4
{
Packit a4aae4
	if (dims == 1) {
Packit a4aae4
		out << "{";
Packit a4aae4
Packit a4aae4
		// Added test in case this method is passed an array with no elements. jhrg 1/27/16
Packit a4aae4
		if (shape[0] >= 1) {
Packit a4aae4
            for (unsigned i = 0; i < shape[0] - 1; ++i) {
Packit a4aae4
                var(index++)->print_val(out, "", false);
Packit a4aae4
                out << ", ";
Packit a4aae4
            }
Packit a4aae4
            var(index++)->print_val(out, "", false);
Packit a4aae4
        }
Packit a4aae4
Packit a4aae4
		out << "}";
Packit a4aae4
Packit a4aae4
		return index;
Packit a4aae4
	}
Packit a4aae4
	else {
Packit a4aae4
		out << "{";
Packit a4aae4
		// Fixed an off-by-one error in the following loop. Since the array
Packit a4aae4
		// length is shape[dims-1]-1 *and* since we want one less dimension
Packit a4aae4
		// than that, the correct limit on this loop is shape[dims-2]-1. From
Packit a4aae4
		// Todd Karakasian.
Packit a4aae4
		//
Packit a4aae4
		// The saga continues; the loop test should be `i < shape[0]-1'. jhrg
Packit a4aae4
		// 9/12/96.
Packit a4aae4
		//
Packit a4aae4
		// For arrays that hold zero values but have rank > 1, the print out
Packit a4aae4
		// may look a little odd (e.g., x[4][0] will print as { {}, {}, {}, {} })
Packit a4aae4
		// but it's not wrong and this is really for debugging mostly. jhrg 1/28/16
Packit a4aae4
        if (shape[0] > 0) {
Packit a4aae4
            for (unsigned i = 0; i < shape[0] - 1; ++i) {
Packit a4aae4
                index = print_array(out, index, dims - 1, shape + 1);
Packit a4aae4
                out << ",";
Packit a4aae4
            }
Packit a4aae4
Packit a4aae4
            index = print_array(out, index, dims - 1, shape + 1);
Packit a4aae4
        }
Packit a4aae4
Packit a4aae4
		out << "}";
Packit a4aae4
Packit a4aae4
		return index;
Packit a4aae4
	}
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
void
Packit a4aae4
Array::print_val(FILE *out, string space, bool print_decl_p)
Packit a4aae4
{
Packit a4aae4
    ostringstream oss;
Packit a4aae4
    print_val(oss, space, print_decl_p);
Packit a4aae4
    fwrite(oss.str().data(), sizeof(char), oss.str().length(), out);
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
void
Packit a4aae4
Array::print_val(ostream &out, string space, bool print_decl_p)
Packit a4aae4
{
Packit a4aae4
    // print the declaration if print decl is true.
Packit a4aae4
    // for each dimension,
Packit a4aae4
    //   for each element,
Packit a4aae4
    //     print the array given its shape, number of dimensions.
Packit a4aae4
    // Add the `;'
Packit a4aae4
Packit a4aae4
    if (print_decl_p) {
Packit a4aae4
        print_decl(out, space, false, false, false);
Packit a4aae4
	    out << " = " ;
Packit a4aae4
    }
Packit a4aae4
Packit a4aae4
    unsigned int *shape = new unsigned int[dimensions(true)];
Packit a4aae4
    unsigned int index = 0;
Packit a4aae4
    for (Dim_iter i = _shape.begin(); i != _shape.end() && index < dimensions(true); ++i)
Packit a4aae4
        shape[index++] = dimension_size(i, true);
Packit a4aae4
Packit a4aae4
    print_array(out, 0, dimensions(true), shape);
Packit a4aae4
Packit a4aae4
    delete [] shape; shape = 0;
Packit a4aae4
Packit a4aae4
    if (print_decl_p) {
Packit a4aae4
	    out << ";\n" ;
Packit a4aae4
    }
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** This function checks semantic features of the Array.  Currently,
Packit a4aae4
    the only check specific to the Array is that there must be
Packit a4aae4
    dimensions.  The rest is inherited from
Packit a4aae4
    <tt>BaseType::check_semantics()</tt>.
Packit a4aae4
Packit a4aae4
    @brief Check semantic features of the Array.
Packit a4aae4
    @return A boolean value.  FALSE means there was a problem.
Packit a4aae4
*/
Packit a4aae4
Packit a4aae4
bool
Packit a4aae4
Array::check_semantics(string &msg, bool)
Packit a4aae4
{
Packit a4aae4
    bool sem = BaseType::check_semantics(msg) && !_shape.empty();
Packit a4aae4
Packit a4aae4
    if (!sem)
Packit a4aae4
        msg = "An array variable must have dimensions";
Packit a4aae4
Packit a4aae4
    return sem;
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** @brief dumps information about this object
Packit a4aae4
 *
Packit a4aae4
 * Displays the pointer value of this instance and information about this
Packit a4aae4
 * instance.
Packit a4aae4
 *
Packit a4aae4
 * @param strm C++ i/o stream to dump the information to
Packit a4aae4
 * @return void
Packit a4aae4
 */
Packit a4aae4
void
Packit a4aae4
Array::dump(ostream &strm) const
Packit a4aae4
{
Packit a4aae4
    strm << DapIndent::LMarg << "Array::dump - ("
Packit a4aae4
    << (void *)this << ")" << endl ;
Packit a4aae4
    DapIndent::Indent() ;
Packit a4aae4
    Vector::dump(strm) ;
Packit a4aae4
    strm << DapIndent::LMarg << "shape:" << endl ;
Packit a4aae4
    DapIndent::Indent() ;
Packit a4aae4
    Dim_citer i = _shape.begin() ;
Packit a4aae4
    Dim_citer ie = _shape.end() ;
Packit a4aae4
    unsigned int dim_num = 0 ;
Packit a4aae4
    for (; i != ie; i++) {
Packit a4aae4
        strm << DapIndent::LMarg << "dimension " << dim_num++ << ":"
Packit a4aae4
	     << endl ;
Packit a4aae4
        DapIndent::Indent() ;
Packit a4aae4
        strm << DapIndent::LMarg << "name: " << (*i).name << endl ;
Packit a4aae4
        strm << DapIndent::LMarg << "size: " << (*i).size << endl ;
Packit a4aae4
        strm << DapIndent::LMarg << "start: " << (*i).start << endl ;
Packit a4aae4
        strm << DapIndent::LMarg << "stop: " << (*i).stop << endl ;
Packit a4aae4
        strm << DapIndent::LMarg << "stride: " << (*i).stride << endl ;
Packit a4aae4
        strm << DapIndent::LMarg << "constrained size: " << (*i).c_size
Packit a4aae4
             << endl ;
Packit a4aae4
        DapIndent::UnIndent() ;
Packit a4aae4
    }
Packit a4aae4
    DapIndent::UnIndent() ;
Packit a4aae4
    DapIndent::UnIndent() ;
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
} // namespace libdap
Packit a4aae4