From a4aae4c33d26c7e257d0e361cf2a620d0ea562cb Mon Sep 17 00:00:00 2001 From: Packit Date: Sep 14 2020 11:17:24 +0000 Subject: libdap-3.19.1 base --- diff --git a/AlarmHandler.h b/AlarmHandler.h new file mode 100644 index 0000000..370c40a --- /dev/null +++ b/AlarmHandler.h @@ -0,0 +1,94 @@ + +// -*- mode: c++; c-basic-offset:4 -*- + +// This file is part of libdap, A C++ implementation of the OPeNDAP Data +// Access Protocol. + +// Copyright (c) 2002,2003 OPeNDAP, Inc. +// Author: James Gallagher +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// +// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112. + +#ifndef alarm_handler_h +#define alarm_handler_h + +#include + +#include + +#include "EventHandler.h" + +namespace libdap +{ + +/** Handle the time out alarm. When an OPeNDAP server runs until the time out + alarm is triggered, this class provides the concrete implementation of + EventHandler::handle_signal(). + + @see EventHandler + @see SignalHandler + @author James Gallagher */ +class AlarmHandler : public EventHandler +{ +private: + FILE *d_file; // Sink for the Error object. + string d_version; + +public: + // Ensure that d_stream gets initialized... + AlarmHandler() : d_file( 0 )// , d_stream( cout ) + {} + + AlarmHandler(FILE *s) : d_file(s)//, d_stream( cout ) + {} + + /** Store information to be used by the handler. + @param out Write to this stream. + @deprecated The stream param is ignored. Use the default constructor instead. */ + AlarmHandler(ostream &) : d_file(0)//, d_stream( out ) + {} + + virtual ~AlarmHandler() + { + if( d_file ) + fclose( d_file ) ; + } + + /** Handle an alarm signal. When one of our servers gets an alarm, that + means it has hit its time out. We need to dump two CRLF pairs down + the stream and then send an Error object explaining that a timeout + has been reached. + + Because this is a signal handler, it should call only reentrant + system services, functions, et cetera. This handler never returns + to the code that was running when the alarm signal was raised. + + @param signum We know it is SIGALRM; here as a check + @return Never returns; calls exit after sending the Error object. */ + virtual void handle_signal(int signum) + { + if (signum != SIGALRM) + throw Error("SIGALRM handler caught another signal!"); + + throw Error("Timeout"); + } + +}; + +} // namespace libdap + +#endif diff --git a/Ancillary.cc b/Ancillary.cc new file mode 100644 index 0000000..1e7679f --- /dev/null +++ b/Ancillary.cc @@ -0,0 +1,240 @@ +// Ancillary.cc + +#include "config.h" + +//#define DODS_DEBUG + +#include "Ancillary.h" +#include "debug.h" + +#ifndef WIN32 +#ifdef HAVE_UNISTD_H +#include +#endif +#else +#include +#include +#include +// Win32 does not define this. 08/21/02 jhrg +#define F_OK 0 +#endif + +namespace libdap { + +/** This function accepts a dataset path name, and searches for a + matching ancillary data file name with a very specific set of + search rules, given here: + +
+    directory           filename          extension
+    same                same            `.'given
+    given               same            `.'given
+    same                given           `.'given
+    given               given           `.'given
+    
+ + Where ``same'' refers to the input dataset pathname, and ``given'' + refers to the function arguments. + + For example, If you call this function with a + dataset name of /a/data, an extension of das, a + directory of + b, and a filename of ralph, the function will + look (in order) + for the following files: + +
+    /a/data.das
+    /b/data.das
+    /a/ralph.das
+    /b/ralph.das
+    
+ + The function will return a string containing the name of the first + file in the list that exists, if any. + + @note This code now checks for pathname.ext 3/17/99 jhrg + + @brief Find a file with ancillary data. + @param pathname The input pathname of a dataset. + @param ext The input extension the desired file is to have. + @param dir The input directory in which the desired file may be + found. + @param file The input filename the desired file may have. + @return A string containing the pathname of the file found by + searching with the given components. If no file was found, the + null string is returned. +*/ +string +Ancillary::find_ancillary_file( const string &pathname, + const string &ext, + const string &dir, + const string &file ) +{ + string::size_type slash = pathname.rfind('/') + 1; + string directory = pathname.substr(0, slash); + string filename = pathname.substr(slash); + string basename = pathname.substr(slash, pathname.rfind('.') - slash); + + DBG(cerr << "find ancillary file params: " << pathname << ", " << ext + << ", " << dir << ", " << file << endl); + DBG(cerr << "find ancillary file comp: " << directory << ", " << filename + << ", " << basename << endl); + + string dot_ext = "." + ext; + + string name = directory + basename + dot_ext; + if (access(name.c_str(), F_OK) == 0) + return name; + + name = pathname + dot_ext; + if (access(name.c_str(), F_OK) == 0) + return name; + + name = directory + ext; + if (access(name.c_str(), F_OK) == 0) + return name; + + name = dir + basename + dot_ext; + if (access(name.c_str(), F_OK) == 0) + return name; + + name = directory + file + dot_ext; + if (access(name.c_str(), F_OK) == 0) + return name; + + name = dir + file + dot_ext; + if (access(name.c_str(), F_OK) == 0) + return name; + + name = dir + ext; + if (access(name.c_str(), F_OK) == 0) + return name; + + return ""; +} + +// Given a pathname to a datafile, take that pathname apart and look for an +// ancillary file that describes a group of datafiles of which this datafile +// is a member. Assume that groups follow a simple naming convention where +// files use either leading or trailing digits and a common basename to name +// group members. For example, 00stuff.hdf, 01stuff.hdf, 02stuff.hdf, ..., is +// a group and is has `stuff' as its basename. + +/** Assume that name refers to a file that is one of a + group of files which share a common `base' name and differ only by + some prefix or suffix digits (e.g. 00base, 01base, + ... or base00, ... have the base name base). This + function looks for a file base.ext. + + @param name The name (full or relative) to one member of a group + of files. + @param ext The extension of the group's ancillary file. Note that + ext should include a period (.) if that needs to + separate the base name from the extension. + @return The pathname to the group's ancillary file if found, otherwise + the empty string (""). */ +string +Ancillary::find_group_ancillary_file( const string &name, const string &ext ) +{ + // Given /usr/local/data/stuff.01.nc + // pathname = /usr/local/data, filename = stuff.01.nc and + // rootname = stuff.01 + string::size_type slash = name.find_last_of('/'); + string dirname = name.substr(0, slash); + string filename = name.substr(slash + 1); + string rootname = filename.substr(0, filename.find_last_of('.')); + + // Instead of using regexs, scan the filename for leading and then + // trailing digits. + string::iterator rootname_iter = rootname.begin(); + string::iterator rootname_end_iter = rootname.end(); + if (isdigit(*rootname_iter)) { + while (rootname_iter != rootname_end_iter + && isdigit(*++rootname_iter)) + ; + + // We want: new_name = dirname + "/" + + ext but without + // creating a bunch of temp objects. + string new_name = dirname; + new_name.append("/"); + new_name.append(rootname_iter, rootname_end_iter); + new_name.append(ext); + DBG(cerr << "New Name (iter): " << new_name << endl); + if (access(new_name.c_str(), F_OK) == 0) { + return new_name; + } + } + + string::reverse_iterator rootname_riter = rootname.rbegin(); + string::reverse_iterator rootname_end_riter = rootname.rend(); + if (isdigit(*rootname_riter)) { + while (rootname_riter != rootname_end_riter + && isdigit(*++rootname_riter)) + ; + string new_name = dirname; + new_name.append("/"); + // I used reverse iters to scan rootname backwards. To avoid + // reversing the fragment between end_riter and riter, pass append + // regular iters obtained using reverse_iterator::base(). See Meyers + // p. 123. 1/22/2002 jhrg + new_name.append(rootname_end_riter.base(), rootname_riter.base()); + new_name.append(ext); + DBG(cerr << "New Name (riter): " << new_name << endl); + if (access(new_name.c_str(), F_OK) == 0) { + return new_name; + } + } + + // If we're here either the file does not begin with leading digits or a + // template made by removing those digits was not found. + + return ""; +} + +void +Ancillary::read_ancillary_das( DAS &das, + const string &pathname, + const string &dir, + const string &file ) +{ + string name = find_ancillary_file( pathname, "das", dir, file ) ; + + DBG(cerr << "In Ancillary::read_ancillary_dds: name:" << name << endl); + + FILE *in = fopen( name.c_str(), "r" ) ; + if( in ) { + das.parse( in ) ; + (void)fclose( in ) ; +#if 0 + int res = fclose( in ) ; + if( res ) + DBG(cerr << "Ancillary::read_ancillary_das - Failed to close file " << (void *)in << endl) ; +#endif + } +} + +void +Ancillary::read_ancillary_dds( DDS &dds, + const string &pathname, + const string &dir, + const string &file ) +{ + string name = find_ancillary_file( pathname, "dds", dir, file ) ; + + DBG(cerr << "In Ancillary::read_ancillary_dds: name:" << name << endl); + + FILE *in = fopen( name.c_str(), "r" ) ; + if( in ) { + dds.parse( in ) ; + (void)fclose( in ) ; +#if 0 + int res = fclose( in ) ; + if( res ) + DBG(cerr << "Ancillary::read_ancillary_das - Failed to close file " << (void *)in << endl) ; +#endif + } +} + +} // namespace libdap + diff --git a/Ancillary.h b/Ancillary.h new file mode 100644 index 0000000..5ed7787 --- /dev/null +++ b/Ancillary.h @@ -0,0 +1,73 @@ +// Ancillary.h + +// -*- mode: c++; c-basic-offset:4 -*- + +// This file is part of libdap, A C++ implementation of the OPeNDAP Data +// Access Protocol. + +// Copyright (c) 2002,2003 OPeNDAP, Inc. +// Author: James Gallagher +// Patrick West +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// +// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112. + +// (c) COPYRIGHT URI/MIT 1994-1999 +// Please read the full copyright statement in the file COPYRIGHT_URI. +// +// Authors: +// jhrg,jimg James Gallagher +// pwest Patrick West + +#ifndef S_Ancillary_h +#define S_Ancillary_h 1 + +#include + +using std::string ; + +#include "DAS.h" +#include "DDS.h" + +namespace libdap +{ + +class Ancillary +{ +public: + static string find_ancillary_file( const string &pathname, + const string &ext, + const string &dir, + const string &file ) ; + + static string find_group_ancillary_file( const string &pathname, + const string &ext ) ; + + static void read_ancillary_das( DAS &das, + const string &pathname, + const string &dir = "", + const string &file = "" ) ; + + static void read_ancillary_dds( DDS &dds, + const string &pathname, + const string &dir = "", + const string &file = "" ) ; +} ; + +} // namespace libdap + +#endif // S_Ancillary_h + diff --git a/Array.cc b/Array.cc new file mode 100644 index 0000000..659a90e --- /dev/null +++ b/Array.cc @@ -0,0 +1,1378 @@ + +// -*- mode: c++; c-basic-offset:4 -*- + +// This file is part of libdap, A C++ implementation of the OPeNDAP Data +// Access Protocol. + +// Copyright (c) 2002,2003 OPeNDAP, Inc. +// Author: James Gallagher +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// +// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112. + +// (c) COPYRIGHT URI/MIT 1994-1999 +// Please read the full copyright statement in the file COPYRIGHT_URI. +// +// Authors: +// jhrg,jimg James Gallagher + +// Implementation for Array. +// +// jhrg 9/13/94 + +#include "config.h" + +// #define DODS_DEBUG + +#include +#include +#include + +#include "Array.h" +#include "Grid.h" + +#include "D4Attributes.h" +#include "DMR.h" +#include "D4Dimensions.h" +#include "D4Maps.h" +#include "D4Group.h" +#include "D4EnumDefs.h" +#include "D4Enum.h" +#include "XMLWriter.h" + +#include "util.h" +#include "debug.h" +#include "InternalErr.h" +#include "escaping.h" + +using namespace std; + +namespace libdap { + +Array::dimension::dimension(D4Dimension *d) : dim(d), use_sdim_for_slice(true) +{ + size = d->size(); + name = d->name(); + + start = 0; + stop = size - 1; + stride = 1; + c_size = size; +} + +void +Array::_duplicate(const Array &a) +{ + _shape = a._shape; + + // Deep copy the Maps if they are being used. + if (a.d_maps) { + d_maps = new D4Maps(*(a.d_maps)); + } + else { + d_maps = 0; + } + // d_maps = a.d_maps ? new D4Maps(*(a.d_maps)) : 0; +} + +// The first method of calculating length works when only one dimension is +// constrained and you want the others to appear in total. This is important +// when selecting from grids since users may not select from all dimensions +// in which case that means they want the whole thing. Array projection +// should probably work this way too, but it doesn't. 9/21/2001 jhrg + +/** @deprecated Calling this method should never be necessary. It is used + internally called whenever the size of the Array is changed, e.g., by a + constraint. + + Changes the length property of the array. +*/ +void +Array::update_length(int) +{ + int length = 1; + for (Dim_citer i = _shape.begin(); i != _shape.end(); i++) { +#if 0 + // If the size of any dimension is zero, then the array is not + // capable of storing any values. jhrg 1/28/16 + length *= (*i).c_size > 0 ? (*i).c_size : 1; +#endif + length *= (*i).c_size; + } + + set_length(length); +} + +// Construct an instance of Array. The (BaseType *) is assumed to be +// allocated using new - The dtor for Vector will delete this object. + +/** Build an array with a name and an element type. The name may be omitted, + which will create a nameless variable. The template (element type) pointer + may also be omitted, but if it is omitted when the Array is created, it + \e must be added (with \c add_var()) before \c read() or \c deserialize() + is called. + + @todo Force the Array::add_var() method to be used to add \e v. + This version of add_var() calls Vector::add_var(). + + @param n A string containing the name of the variable to be + created. + @param v A pointer to a variable of the type to be included + in the Array. May be null and set later using add_var() or add_var_nocopy() + @brief Array constructor +*/ +Array::Array(const string &n, BaseType *v, bool is_dap4 /* default:false */) + : Vector(n, 0, dods_array_c, is_dap4), d_maps(0) +{ + add_var(v); // Vector::add_var() stores null if v is null +} + +/** Build an array on the server-side with a name, a dataset name from which + this Array is being created, and an element type. + + @todo Force the Array::add_var() method to be used to add \e v. + This version of add_var() calls Vector::add_var(). + + @param n A string containing the name of the variable to be created. + @param d A string containing the name of the dataset from which this + variable is being created. + @param v A pointer to a variable of the type to be included + in the Array. + @brief Array constructor +*/ +Array::Array(const string &n, const string &d, BaseType *v, bool is_dap4 /* default:false */) + : Vector(n, d, 0, dods_array_c, is_dap4), d_maps(0) +{ + add_var(v); // Vector::add_var() stores null if v is null +} + +/** @brief The Array copy constructor. */ +Array::Array(const Array &rhs) : Vector(rhs) +{ + _duplicate(rhs); +} + +/** @brief The Array destructor. */ +Array::~Array() +{ + delete d_maps; +} + +BaseType * +Array::ptr_duplicate() +{ + return new Array(*this); +} + +Array & +Array::operator=(const Array &rhs) +{ + if (this == &rhs) + return *this; + + dynamic_cast(*this) = rhs; + + _duplicate(rhs); + + return *this; +} + +void +Array::transform_to_dap4(D4Group *root, Constructor *container) +{ + DBG(cerr << __func__ << "() - BEGIN (array:" << name() << ")" << endl;); + Array *dest = static_cast(ptr_duplicate()); + + // If it's already a DAP4 object then we can just return it! + if(is_dap4()){ + DBG(cerr << __func__ << "() - Already DAP4 type: Just making a copy and adding to container. " << endl;); + container->add_var_nocopy(dest); + DBG(cerr << __func__ << "() - END (Already DAP4 type)" << endl;); + } + // Process the Array's dimensions, making D4 shared dimensions for + // D2 dimensions that are named. If there is just a size, don't make + // a D4Dimension (In DAP4 you cannot share a dimension unless it has + // a name). jhrg 3/18/14 + + D4Dimensions *root_dims = root->dims(); + for (Array::Dim_iter dap2_dim = dest->dim_begin(), e = dest->dim_end(); dap2_dim != e; ++dap2_dim) { + if (!(*dap2_dim).name.empty()) { + DBG(cerr << __func__ << "() - Processing the array dimension '" << (*dap2_dim).name << "'" << endl;); + + // If a D4Dimension with the name already exists, use it. + D4Dimension *d4_dim = root_dims->find_dim((*dap2_dim).name); + if (!d4_dim) { + d4_dim = new D4Dimension((*dap2_dim).name, (*dap2_dim).size); + root_dims->add_dim_nocopy(d4_dim); + DBG(cerr << __func__ << "() -" << + " Added NEW D4Dimension '"<< d4_dim->name() << "' (" << + (void *)d4_dim << ") to root->dims()"<< endl;); + } + else { + DBG(cerr << __func__ << "() -" << + " Using Existing D4Dimension '"<< d4_dim->name() << "' (" << + (void *)d4_dim << ")"<< endl;); + + if (d4_dim->size() != (unsigned long) (*dap2_dim).size) { + // TODO Revisit this decision. jhrg 3/18/14 + // ...in case the name/size are different, make a unique D4Dimension + // but don't fiddle with the name. Not sure I like this idea, so I'm + // making the case explicit (could be rolled in to the block above). + // jhrg 3/18/14 + // + // This is causing problems in the FITS handler because there are cases + // where two arrays have dimensions with the same name but different + // sizes. The deserializing code is using the first size listed, which is + // wrong in some cases. I'm going to try making this new D4Dimension using + // the dim name along with the variable name. jhrg 8/15/14 + d4_dim = new D4Dimension((*dap2_dim).name + "_" + name(), (*dap2_dim).size); + DBG(cerr << __func__ << "() -" << + " Utilizing Name/Size Conflict Naming Artifice. name'"<< d4_dim->name() << "' (" << + (void *)d4_dim << ")"<< endl;); + root_dims->add_dim_nocopy(d4_dim); + } + } + // At this point d4_dim's name and size == those of (*d) so just set + // the D4Dimension pointer so it matches the one in the D4Group. + (*dap2_dim).dim = d4_dim; + } + + } + + // Copy the D2 attributes to D4 Attributes + dest->attributes()->transform_to_dap4(get_attr_table()); + dest->set_is_dap4(true); + container->add_var_nocopy(dest); + DBG(cerr << __func__ << "() - END (array:" << name() << ")" << endl;); +} + +bool Array::is_dap2_grid(){ + bool is_grid = false; + if(this->is_dap4()){ + DBG( cerr << __func__ << "() - Array '"<< name() << "' is DAP4 object!" << endl;) + D4Maps *d4_maps = this->maps(); + is_grid = d4_maps->size(); // It can't be a grid if there are no maps... + if(is_grid){ + DBG( cerr << __func__ << "() - Array '"<< name() << "' has D4Maps." << endl;) + // hmmm this might be a DAP2 Grid... + D4Maps::D4MapsIter i = d4_maps->map_begin(); + D4Maps::D4MapsIter e = d4_maps->map_end(); + while(i!=e){ + DBG( cerr << __func__ << "() - Map '"<< (*i)->array()->name() << " has " << (*i)->array()->_shape.size() << " dimension(s)." << endl;) + if((*i)->array()->_shape.size() > 1){ + is_grid = false; + i = e; + } + else { + i++; + } + } + } + else { + DBG( cerr << __func__ << "() - Array '"<< name() << "' has no D4Maps." << endl;) + } + } + DBG( cerr << __func__ << "() - is_grid: "<< (is_grid?"true":"false") << endl;) + return is_grid; +} + +/** + * @brief Transforms this instance of a D4Array into the corresponding DAP2 object. + * + * This transformation may return an Array or a Grid object. The DAP2 Grid construct + * is semantically contained in the DAP4 concept of arrays with Map arrays. If all + * of the Maps are one dimensional then the D4Array can be represented as a + * Grid object. + * + * @param The AttrTable pointer parent_attr_table is used by Groups, which disappear + * from the DAP2 representation. Their children are returned in the the BAseType vector + * their attributes are added to parent_attr_table. + * @return A pointer to a vector of BaseType pointers (right?). In this D4Array case + * returned vector may contain a DAP2 Array or a Grid. Or, if the Array' prototype is + * a type that cannot be represented in DAP2 the return will be NULL. + */ +std::vector * +Array::transform_to_dap2(AttrTable *){ + DBG(cerr << __func__ << "() - BEGIN Array '"<< name() << "'" << endl;); + + BaseType *dest; + if(is_dap4()){ // Don't convert a DAP2 thing + + // Can Precious be represented as a DAP2 Grid + if(is_dap2_grid()){ + // Oh yay! Grids are special. + DBG(cerr << __func__ << "() - Array '"<< name() << "' is dap2 Grid!" << endl;); + Grid *g = new Grid(name()); + dest = g; + Array *grid_array = (Array *) this->ptr_duplicate(); + g->set_array(grid_array); + + // Get the metadata into the Grid + AttrTable *grid_attrs = attributes()->get_AttrTable(name()); + g->set_attr_table(*grid_attrs); // Copy it into the Grid object. + // grid_array->set_attr_table(*grid_attrs); // Copy it into the data Array. + delete grid_attrs; + + // Clear the Grid data Array attributes. + AttrTable at; + at.set_name(name()); + grid_array->set_attr_table(at); + + // Process the Map Arrays. + D4Maps *d4_maps = this->maps(); + vector dropped_maps; + D4Maps::D4MapsIter miter = d4_maps->map_begin(); + D4Maps::D4MapsIter end = d4_maps->map_end(); + for( ; miter!=end; miter++){ + D4Map *d4_map = (*miter); + Array *d4_map_array = const_cast(d4_map->array()); + vector *d2_result = d4_map_array->transform_to_dap2(&(g->get_attr_table())); + if(d2_result){ + if(d2_result->size()>1) + throw Error(internal_error,string(__func__)+"() - ERROR: D4Map Array conversion resulted in multiple DAP2 objects."); + + // TODO - This is probably slow and needs a better pattern. const_cast? static_cast? + Array *d2_map_array = dynamic_cast((*d2_result)[0]); + if(d2_map_array){ + if(d2_map_array->dimensions()!=1) + throw Error(internal_error,string(__func__)+"() - ERROR: DAP2 array from D4Map Array conversion has more than 1 dimension."); + + g->add_map(d2_map_array,false); + AttrTable at = d2_map_array->get_attr_table(); + DBG( cerr << __func__ << "() - " << + "DAS For Grid Map '" << d2_map_array->name() << "':" << endl; + at.print(cerr); ); + } + else { + throw Error(internal_error,string(__func__)+"() - Unable to interpret returned DAP2 content."); + } + delete d2_result; + } + else { + dropped_maps.push_back(d4_map_array); + } + } + + // Did we have a transform failure? + if(!dropped_maps.empty()){ + // Yup... tell the story in the attributes. + AttrTable *dv_table = Constructor::make_dropped_vars_attr_table(&dropped_maps); + dest->get_attr_table().append_container(dv_table,dv_table->get_name()); + } + } + else { + // It's not a Grid so we can make a simple copy of our Precious. + DBG( cerr << __func__ << "() - Array '"<< name() << "' is not a Grid!" << endl); + BaseType *proto = this->prototype(); + switch(proto->type()){ + case dods_int64_c: + case dods_uint64_c: + case dods_enum_c: + case dods_opaque_c: + { + // For now we punt on these type as they have no easy representation in + // the DAP2 data model. By setting this to NULL we cause the Array to be + // dropped and this will be reflected in the metadata (DAS). + dest = NULL; + break; + } + default: + { + dest = this->ptr_duplicate(); + // convert the d4 attributes to a dap2 attribute table. + AttrTable *attrs = this->attributes()->get_AttrTable(name()); + dest->set_attr_table(*attrs); + dest->set_is_dap4(false); + AttrTable at = dest->get_attr_table(); + DBG( cerr << __func__ << "() - " << + "DAS for new Array '" << dest->name() << "':" << endl; + at.print(cerr); ) + + break; + } + } + } + + } + else { + // If it's a DAP2 Array already then we just make a copy of our Precious. + dest = this->ptr_duplicate(); + } + // attrs->print(cerr,"",true); + vector *result; + if(dest){ + result = new vector(); + result->push_back(dest); + } + else { + result = NULL; + } + DBG( cerr << __func__ << "() - END Array '"<< name() << "'" << endl;); + return result; +} + +/** + * Hackery that helps build a new D4Group from an old one. We need to re-wire the + * D4Dimension (note the lack of an 's' at then end) that the copied Array objects + * hold. This code does that. Note that these are 'weak pointers' so they should + * never be freed - the D4Group object will take care of that. + * + * @note The order of the D4Dimension instances matches in 'old_dims' and 'new_dims'. + * + * @param old_dims The Old D4Dimension objects (held in a D4Dimensions instance) + * @param new_dims The New D4Dimension objects. + */ +void +Array::update_dimension_pointers(D4Dimensions *old_dims, D4Dimensions *new_dims) +{ + std::vector::iterator i = _shape.begin(), e = _shape.end(); + while (i != e) { + D4Dimensions::D4DimensionsIter old_i = old_dims->dim_begin(), old_e = old_dims->dim_end(); + while (old_i != old_e) { + if ((*i).dim == *old_i) { + (*i).dim = new_dims->find_dim((*old_i)->name()); + } + ++old_i; + } + + ++i; + } +} + +/** @brief Add the BaseType pointer to this constructor type + instance. + + Propagate the name of the BaseType instance to this instance. This + ensures that variables at any given level of the DDS table have + unique names (i.e., that Arrays do not have their default name ""). If + v's name is null, then assume that the array \e is named and + don't overwrite it with v's null name. + + @note It is possible for the BaseType pointer to be null when this + method is called, a behavior that differs considerably from that of + the other 'add_var()' methods. + + @note This version checks to see if \e v is an array. If so, it calls + Vector::add_var() using the template variable of \e v and then appends + the dimensions of \e v to this array. This somewhat obscure behavior + simplifies 'translating' Sequences to arrays when the actual variable + being translated is not a regular Sequence but an array of Sequences. + This is of very debatable usefulness, but it's here all the same. + + @param v The template variable for the array + @param p The Part parameter defaults to nil and is ignored by this method. +*/ + +void +Array::add_var(BaseType *v, Part) +{ + // If 'v' is an Array, add the template instance to this object and + // then copy the dimension information. Odd semantics; I wonder if this + //is ever used. jhrg 6/13/12 + if (v && v->type() == dods_array_c) { + Array *a = static_cast(v); + Vector::add_var(a->var()); + + Dim_iter i = a->dim_begin(); + Dim_iter i_end = a->dim_end(); + while (i != i_end) { + append_dim(a->dimension_size(i), a->dimension_name(i)); + ++i; + } + } + else { + Vector::add_var(v); + } +} + +void +Array::add_var_nocopy(BaseType *v, Part) +{ + // If 'v' is an Array, add the template instance to this object and + // then copy the dimension information. Odd semantics; I wonder if this + //is ever used. jhrg 6/13/12 + if (v && v->type() == dods_array_c) { + Array &a = dynamic_cast(*v); + Vector::add_var_nocopy(a.var()); + Dim_iter i = a.dim_begin(); + Dim_iter i_end = a.dim_end(); + while (i != i_end) { + append_dim(a.dimension_size(i), a.dimension_name(i)); + ++i; + } + } + else { + Vector::add_var_nocopy(v); + } +} + +/** Given a size and a name, this function adds a dimension to the + array. For example, if the Array is already 10 elements long, + calling append_dim with a size of 5 will transform the array + into a 10x5 matrix. Calling it again with a size of 2 will + create a 10x5x2 array, and so on. This sets Vector's length + member as a side effect. + + @param size The size of the desired new row. + @param name The name of the new dimension. This defaults to + an empty string. + @brief Add a dimension of a given size. */ +void +Array::append_dim(int size, const string &name) +{ + dimension d(size, www2id(name)); + _shape.push_back(d); + + update_length(); +} + +void +Array::append_dim(D4Dimension *dim) +{ + dimension d(/*dim->size(), www2id(dim->name()),*/ dim); + _shape.push_back(d); + + update_length(); +} + +/** Creates a new OUTER dimension (slowest varying in rowmajor) + * for the array by prepending rather than appending it. + * @param size cardinality of the new dimension + * @param name optional name for the new dimension + */ +void +Array::prepend_dim(int size, const string& name/* = "" */) +{ + dimension d(size, www2id(name)); + // Shifts the whole array, but it's tiny in general + _shape.insert(_shape.begin(), d); + + update_length(); // the number is ignored... +} + +void +Array::prepend_dim(D4Dimension *dim) +{ + dimension d(/*dim->size(), www2id(dim->name()),*/ dim); + // Shifts the whole array, but it's tiny in general + _shape.insert(_shape.begin(), d); + + update_length(); // the number is ignored... +} + +/** Remove all the dimensions currently set for the Array. This also + * removes all constraint information. + */ +void +Array::clear_all_dims() +{ + _shape.clear(); +} +/** Resets the dimension constraint information so that the entire + array is selected. + + @brief Reset constraint to select entire array. +*/ + +void +Array::reset_constraint() +{ + set_length(-1); + + for (Dim_iter i = _shape.begin(); i != _shape.end(); i++) { + (*i).start = 0; + (*i).stop = (*i).size - 1; + (*i).stride = 1; + (*i).c_size = (*i).size; + + update_length(); + } +} + + +/** Tell the Array object to clear the constraint information about + dimensions. Do this once before calling add_constraint() + for each new constraint expression. Only the dimensions explicitly + selected using add_constraint() will be sent. + + @deprecated This should never be used. + @brief Clears the projection; add each projected dimension explicitly using + add_constraint. +*/ +void +Array::clear_constraint() +{ + reset_constraint(); +} + +// Note: MS VC++ won't tolerate embedded newlines in strings, hence the \n +// is explicit. +static const char *array_sss = \ +"Invalid constraint parameters: At least one of the start, stride or stop \n\ +specified do not match the array variable."; + +/** Once a dimension has been created (see append_dim()), it can + be constrained. This will make the array appear to the rest + of the world to be smaller than it is. This functions sets the + projection for a dimension, and marks that dimension as part of the + current projection. + + @note A stride value <= 0 or > the array size is an error and causes + add_constraint to throw an Error. Similarly, start or stop values > + size also cause an Error exception to be thrown. + + @brief Adds a constraint to an Array dimension. + + @param i An iterator pointing to the dimension in the list of + dimensions. + @param start The start index of the constraint. + @param stride The stride value of the constraint. + @param stop The stop index of the constraint. A value of -1 indicates + 'to the end' of the array. + @exception Error Thrown if the any of values of start, stop or stride + cannot be applied to this array. */ +void +Array::add_constraint(Dim_iter i, int start, int stride, int stop) +{ + dimension &d = *i ; + + // if stop is -1, set it to the array's max element index + // jhrg 12/20/12 + if (stop == -1) + stop = d.size - 1; + + // Check for bad constraints. + // Jose Garcia + // Usually invalid data for a constraint is the user's mistake + // because they build a wrong URL in the client side. + if (start >= d.size || stop >= d.size || stride > d.size || stride <= 0) + throw Error(malformed_expr, array_sss); + + if (((stop - start) / stride + 1) > d.size) + throw Error(malformed_expr, array_sss); + + d.start = start; + d.stop = stop; + d.stride = stride; + + d.c_size = (stop - start) / stride + 1; + + DBG(cerr << "add_constraint: c_size = " << d.c_size << endl); + + update_length(); + + d.use_sdim_for_slice = false; +} + +void +Array::add_constraint(Dim_iter i, D4Dimension *dim) +{ + dimension &d = *i ; + + if (dim->constrained()) + add_constraint(i, dim->c_start(), dim->c_stride(), dim->c_stop()); + + dim->set_used_by_projected_var(true); + + // In this case the value below overrides the value for use_sdim_for_slice + // set in the above call. jhrg 12/20/13 + d.use_sdim_for_slice = true; +} + +/** Returns an iterator to the first dimension of the Array. */ +Array::Dim_iter +Array::dim_begin() +{ + return _shape.begin() ; +} + +/** Returns an iterator past the last dimension of the Array. */ +Array::Dim_iter +Array::dim_end() +{ + return _shape.end() ; +} + +//TODO Many of these methods take a bool parameter that serves no use; remove. + +/** Return the total number of dimensions contained in the array. + When constrained is TRUE, return the number of dimensions + given the most recently evaluated constraint expression. + + @brief Return the total number of dimensions in the array. + @param constrained A boolean flag to indicate whether the array is + constrained or not. Ignored. +*/ +unsigned int +Array::dimensions(bool /*constrained*/) +{ + return _shape.size(); +} + +/** Return the size of the array dimension referred to by i. + If the dimension is constrained the constrained size is returned if + constrained is \c true. + + @brief Returns the size of the dimension. + + @param i The dimension. + + @param constrained If this parameter is TRUE, the method returns the + constrained size of the array so long as a constraint has been applied to + this dimension. If TRUE and no constraint has been applied, this method + returns zero. If it is FALSE, the method ignores any constraint that + has been applied to this dimension and returns the full size of the + dimension. The default value is FALSE. + + @return An integer containing the size of the specified dimension. +*/ +int +Array::dimension_size(Dim_iter i, bool constrained) +{ + int size = 0; + + if (!_shape.empty()) { + if (constrained) + size = (*i).c_size; + else + size = (*i).size; + } + + return size; +} + +/** Use this function to return the start index of an array + dimension. If the array is constrained (indicated with the + constrained argument), the start index of the constrained + array is returned (or zero if the dimension in question is not + selected at all). See also dimension_stop() and + dimension_stride(). + + @brief Return the start index of a dimension. + + @param i The dimension. + @param constrained If this parameter is TRUE, the function + returns the start index only if the dimension is constrained + (subject to a start, stop, or stride constraint). If + the dimension is not constrained, the function returns zero. If it + is FALSE, the function returns the start index whether or not + the dimension is constrained. + @return The desired start index. +*/ +int +Array::dimension_start(Dim_iter i, bool /*constrained*/) +{ + return (!_shape.empty()) ? (*i).start : 0; +} + +/** Use this function to return the stop index of an array + dimension. If the array is constrained (indicated with the + constrained argument), the stop index of the constrained + array is returned (or zero if the dimension in question is not + selected at all). See also dimension_start() and + dimension_stride(). + + @brief Return the stop index of the constraint. + + @param i The dimension. + @param constrained If this parameter is TRUE, the function + returns the stop index only if the dimension is constrained + (subject to a start, stop, or stride constraint). If + the dimension is not constrained, the function returns zero. If it + is FALSE, the function returns the stop index whether or not + the dimension is constrained. + @return The desired stop index. +*/ +int +Array::dimension_stop(Dim_iter i, bool /*constrained*/) +{ + return (!_shape.empty()) ? (*i).stop : 0; +} + +/** Use this function to return the stride value of an array + dimension. If the array is constrained (indicated with the + constrained argument), the stride value of the constrained + array is returned (or zero if the dimension in question is not + selected at all). See also dimension_stop() and + dimension_start(). + + @brief Returns the stride value of the constraint. + + @param i The dimension. + @param constrained If this parameter is TRUE, the function + returns the stride value only if the dimension is constrained + (subject to a start, stop, or stride constraint). If + the dimension is not constrained, the function returns zero. If it + is FALSE, the function returns the stride value whether or not + the dimension is constrained. + @return The stride value requested, or zero, if constrained + is TRUE and the dimension is not selected. +*/ +int +Array::dimension_stride(Dim_iter i, bool /*constrained*/) +{ + return (!_shape.empty()) ? (*i).stride : 0; +} + +/** This function returns the name of the dimension indicated with + p. Since this method is public, it is possible to call it + before the Array object has been properly initialized. This will + cause an exception. So don't do that. + + @brief Returns the name of the specified dimension. + + @param i The dimension. + @return A pointer to a string containing the dimension name. +*/ +string +Array::dimension_name(Dim_iter i) +{ + // Jose Garcia + // Since this method is public, it is possible for a user + // to call it before the Array object has been properly set + // this will cause an exception which is the user's fault. + // (User in this context is the developer of the surrogate library.) + if (_shape.empty()) + throw InternalErr(__FILE__, __LINE__, + "*This* array has no dimensions."); + return (*i).name; +} + +D4Dimension * +Array::dimension_D4dim(Dim_iter i) +{ + return (!_shape.empty()) ? (*i).dim : 0; +} + +D4Maps * +Array::maps() +{ + if (!d_maps) d_maps = new D4Maps(this); // init with this as parent + return d_maps; +} + +#if 0 +/** + * @brief Returns the width of the data, in bytes. + * @param constrained if true, return the size of the array in bytes taking into + * account the current constraints on various dimensions. False by default. + * @return The number of bytes needed to store the array values. + */ +unsigned int Array::width(bool constrained) const +{ + + if (constrained) { + // This preserves the original method's semantics when we ask for the + // size of the constrained array but no constraint has been applied. + // In this case, length will be -1. Wrong, I know... + return length() * var()->width(constrained); + } + else { + int length = 1; + for (Dim_iter i = _shape.begin(); i != _shape.end(); i++) { + length *= dimension_size(i, false); + } + return length * var()->width(false); + } +} +#endif + +class PrintD4ArrayDimXMLWriter: public unary_function { + XMLWriter &xml; + // Was this variable constrained using local/direct slicing? i.e., is d_local_constraint set? + // If so, don't use shared dimensions; instead emit Dim elements that are anonymous. + bool d_constrained; +public: + + PrintD4ArrayDimXMLWriter(XMLWriter &xml, bool c) : xml(xml), d_constrained(c) { } + + void operator()(Array::dimension &d) + { + // This duplicates code in D4Dimensions (where D4Dimension::print_dap4() is defined + // because of the need to print the constrained size of a dimension. I think that + // the constraint information has to be kept here and not in the dimension (since they + // are shared dims). Could hack print_dap4() to take the constrained size, however. + if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*) "Dim") < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write Dim element"); + + string name = (d.dim) ? d.dim->fully_qualified_name() : d.name; + // If there is a name, there must be a Dimension (named dimension) in scope + // so write its name but not its size. + if (!d_constrained && !name.empty()) { + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "name", (const xmlChar*) name.c_str()) + < 0) throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name"); + } + else if (d.use_sdim_for_slice) { + assert(!name.empty()); + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "name", (const xmlChar*) name.c_str()) + < 0) throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name"); + } + else { + ostringstream size; + size << (d_constrained ? d.c_size : d.size); + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "size", + (const xmlChar*) size.str().c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name"); + } + + if (xmlTextWriterEndElement(xml.get_writer()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not end Dim element"); + } +}; + +class PrintD4ConstructorVarXMLWriter: public unary_function { + XMLWriter &xml; + bool d_constrained; +public: + PrintD4ConstructorVarXMLWriter(XMLWriter &xml, bool c) : xml(xml), d_constrained(c) { } + + void operator()(BaseType *btp) + { + btp->print_dap4(xml, d_constrained); + } +}; + +class PrintD4MapXMLWriter: public unary_function { + XMLWriter &xml; + +public: + PrintD4MapXMLWriter(XMLWriter &xml) : xml(xml) { } + + void operator()(D4Map *m) + { + m->print_dap4(xml); + } +}; + +/** + * @brief Print the DAP4 representation of an array. + * @param xml + * @param constrained + */ +void +Array::print_dap4(XMLWriter &xml, bool constrained /* default: false*/) +{ + if (constrained && !send_p()) return; + + if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*) var()->type_name().c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write " + type_name() + " element"); + + if (!name().empty()) + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "name", (const xmlChar*)name().c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name"); + + // Hack job... Copied from D4Enum::print_xml_writer. jhrg 11/12/13 + if (var()->type() == dods_enum_c) { + D4Enum *e = static_cast(var()); + string path = e->enumeration()->name(); + if (e->enumeration()->parent()) { + // print the FQN for the enum def; D4Group::FQN() includes the trailing '/' + path = static_cast(e->enumeration()->parent()->parent())->FQN() + path; + } + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "enum", (const xmlChar*)path.c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute for enum"); + } + + if (prototype()->is_constructor_type()) { + Constructor &c = static_cast(*prototype()); + for_each(c.var_begin(), c.var_end(), PrintD4ConstructorVarXMLWriter(xml, constrained)); + // bind2nd(mem_fun_ref(&BaseType::print_dap4), xml)); + } + + // Drop the local_constraint which is per-array and use a per-dimension on instead + for_each(dim_begin(), dim_end(), PrintD4ArrayDimXMLWriter(xml, constrained)); + + attributes()->print_dap4(xml); + + for_each(maps()->map_begin(), maps()->map_end(), PrintD4MapXMLWriter(xml)); + + if (xmlTextWriterEndElement(xml.get_writer()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not end " + type_name() + " element"); +} + +/** Prints a declaration for the Array. This is what appears in a + DDS. If the Array is constrained, the declaration will reflect + the size of the Array once the constraint is applied. + + @brief Prints a DDS entry for the Array. + + @param out Write the output to this FILE *. + @param space A string containing spaces to precede the + declaration. + @param print_semi A boolean indicating whether to print a + semi-colon after the declaration. (TRUE means ``print a + semi-colon.'') + @param constraint_info A boolean value. See + BaseType::print_decl(). + @param constrained This argument should be TRUE if the Array is + constrained, and FALSE otherwise. +*/ +void +Array::print_decl(FILE *out, string space, bool print_semi, + bool constraint_info, bool constrained) +{ + ostringstream oss; + print_decl(oss, space, print_semi, constraint_info, constrained); + fwrite(oss.str().data(), sizeof(char), oss.str().length(), out); +} + +/** Prints a declaration for the Array. This is what appears in a + DDS. If the Array is constrained, the declaration will reflect + the size of the Array once the constraint is applied. + + @brief Prints a DDS entry for the Array. + + @param out Write the output to this ostream. + @param space A string containing spaces to precede the + declaration. + @param print_semi A boolean indicating whether to print a + semi-colon after the declaration. (TRUE means ``print a + semi-colon.'') + @param constraint_info A boolean value. See + BaseType::print_decl(). + @param constrained This argument should be TRUE if the Array is + constrained, and FALSE otherwise. +*/ +void Array::print_decl(ostream &out, string space, bool print_semi, bool constraint_info, bool constrained) +{ + if (constrained && !send_p()) return; + + // print it, but w/o semicolon + var()->print_decl(out, space, false, constraint_info, constrained); + + for (Dim_citer i = _shape.begin(); i != _shape.end(); i++) { + out << "["; + if ((*i).name != "") { + out << id2www((*i).name) << " = "; + } + if (constrained) { + out << (*i).c_size << "]"; + } + else { + out << (*i).size << "]"; + } + } + + if (print_semi) { + out << ";\n"; + } +} + +/** + * @deprecated + */ +void +Array::print_xml(FILE *out, string space, bool constrained) +{ + XMLWriter xml(space); + print_xml_writer_core(xml, constrained, "Array"); + fwrite(xml.get_doc(), sizeof(char), xml.get_doc_size(), out); +} + +/** + * @deprecated + */ +void +Array::print_xml(ostream &out, string space, bool constrained) +{ + XMLWriter xml(space); + print_xml_writer_core(xml, constrained, "Array"); + out << xml.get_doc(); +} + +/** + * @deprecated + */ +void +Array::print_as_map_xml(FILE *out, string space, bool constrained) +{ + XMLWriter xml(space); + print_xml_writer_core(xml, constrained, "Map"); + fwrite(xml.get_doc(), sizeof(char), xml.get_doc_size(), out); +} + +/** + * @deprecated + */ +void +Array::print_as_map_xml(ostream &out, string space, bool constrained) +{ + XMLWriter xml(space); + print_xml_writer_core(xml, constrained, "Map"); + out << xml.get_doc(); +} + +/** + * @deprecated + */ +void +Array::print_xml_core(FILE *out, string space, bool constrained, string tag) +{ + XMLWriter xml(space); + print_xml_writer_core(xml, constrained, tag); + fwrite(xml.get_doc(), sizeof(char), xml.get_doc_size(), out); +} + +/** + * @deprecated + */ +void +Array::print_xml_core(ostream &out, string space, bool constrained, string tag) +{ + XMLWriter xml(space); + print_xml_writer_core(xml, constrained, tag); + out << xml.get_doc(); +} + +void +Array::print_xml_writer(XMLWriter &xml, bool constrained) +{ + print_xml_writer_core(xml, constrained, "Array"); +} + +void +Array::print_as_map_xml_writer(XMLWriter &xml, bool constrained) +{ + print_xml_writer_core(xml, constrained, "Map"); +} + +class PrintArrayDimXMLWriter : public unary_function +{ + XMLWriter &xml; + bool d_constrained; +public: + PrintArrayDimXMLWriter(XMLWriter &xml, bool c) : xml(xml), d_constrained(c) {} + + void operator()(Array::dimension &d) + { + if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*)"dimension") < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write dimension element"); + + if (!d.name.empty()) + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "name", (const xmlChar*)d.name.c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name"); + + ostringstream size; + size << (d_constrained ? d.c_size : d.size); + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "size", (const xmlChar*)size.str().c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name"); + + if (xmlTextWriterEndElement(xml.get_writer()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not end dimension element"); + } +}; + +void +Array::print_xml_writer_core(XMLWriter &xml, bool constrained, string tag) +{ + if (constrained && !send_p()) + return; + + if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*)tag.c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write " + tag + " element"); + + if (!name().empty()) + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "name", (const xmlChar*)name().c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name"); + + get_attr_table().print_xml_writer(xml); + + BaseType *btp = var(); + string tmp_name = btp->name(); + btp->set_name(""); + btp->print_xml_writer(xml, constrained); + btp->set_name(tmp_name); + + for_each(dim_begin(), dim_end(), PrintArrayDimXMLWriter(xml, constrained)); + + if (xmlTextWriterEndElement(xml.get_writer()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not end " + tag + " element"); +} + +/** Prints the values in ASCII of the entire (constrained) array. This method + Attempts to make an aesthetically pleasing display. However, it is + primarily intended for debugging purposes. + + @param out Write the output to this FILE *. + @param index + @param dims + @param shape + + @brief Print the value given the current constraint. +*/ +unsigned int +Array::print_array(FILE *out, unsigned int index, unsigned int dims, + unsigned int shape[]) +{ + ostringstream oss; + unsigned int i = print_array(oss, index, dims, shape); + fwrite(oss.str().data(), sizeof(char), oss.str().length(), out); + + return i; +} + +/** Prints the values in ASCII of the entire (constrained) array. This method + Attempts to make an anesthetically pleasing display. However, it is + primarily intended for debugging purposes. + + @param out Write the output to this ostream + @param index + @param dims + @param shape + + @brief Print the value given the current constraint. +*/ +unsigned int Array::print_array(ostream &out, unsigned int index, unsigned int dims, unsigned int shape[]) +{ + if (dims == 1) { + out << "{"; + + // Added test in case this method is passed an array with no elements. jhrg 1/27/16 + if (shape[0] >= 1) { + for (unsigned i = 0; i < shape[0] - 1; ++i) { + var(index++)->print_val(out, "", false); + out << ", "; + } + var(index++)->print_val(out, "", false); + } + + out << "}"; + + return index; + } + else { + out << "{"; + // Fixed an off-by-one error in the following loop. Since the array + // length is shape[dims-1]-1 *and* since we want one less dimension + // than that, the correct limit on this loop is shape[dims-2]-1. From + // Todd Karakasian. + // + // The saga continues; the loop test should be `i < shape[0]-1'. jhrg + // 9/12/96. + // + // For arrays that hold zero values but have rank > 1, the print out + // may look a little odd (e.g., x[4][0] will print as { {}, {}, {}, {} }) + // but it's not wrong and this is really for debugging mostly. jhrg 1/28/16 + if (shape[0] > 0) { + for (unsigned i = 0; i < shape[0] - 1; ++i) { + index = print_array(out, index, dims - 1, shape + 1); + out << ","; + } + + index = print_array(out, index, dims - 1, shape + 1); + } + + out << "}"; + + return index; + } +} + +void +Array::print_val(FILE *out, string space, bool print_decl_p) +{ + ostringstream oss; + print_val(oss, space, print_decl_p); + fwrite(oss.str().data(), sizeof(char), oss.str().length(), out); +} + +void +Array::print_val(ostream &out, string space, bool print_decl_p) +{ + // print the declaration if print decl is true. + // for each dimension, + // for each element, + // print the array given its shape, number of dimensions. + // Add the `;' + + if (print_decl_p) { + print_decl(out, space, false, false, false); + out << " = " ; + } + + unsigned int *shape = new unsigned int[dimensions(true)]; + unsigned int index = 0; + for (Dim_iter i = _shape.begin(); i != _shape.end() && index < dimensions(true); ++i) + shape[index++] = dimension_size(i, true); + + print_array(out, 0, dimensions(true), shape); + + delete [] shape; shape = 0; + + if (print_decl_p) { + out << ";\n" ; + } +} + +/** This function checks semantic features of the Array. Currently, + the only check specific to the Array is that there must be + dimensions. The rest is inherited from + BaseType::check_semantics(). + + @brief Check semantic features of the Array. + @return A boolean value. FALSE means there was a problem. +*/ + +bool +Array::check_semantics(string &msg, bool) +{ + bool sem = BaseType::check_semantics(msg) && !_shape.empty(); + + if (!sem) + msg = "An array variable must have dimensions"; + + return sem; +} + +/** @brief dumps information about this object + * + * Displays the pointer value of this instance and information about this + * instance. + * + * @param strm C++ i/o stream to dump the information to + * @return void + */ +void +Array::dump(ostream &strm) const +{ + strm << DapIndent::LMarg << "Array::dump - (" + << (void *)this << ")" << endl ; + DapIndent::Indent() ; + Vector::dump(strm) ; + strm << DapIndent::LMarg << "shape:" << endl ; + DapIndent::Indent() ; + Dim_citer i = _shape.begin() ; + Dim_citer ie = _shape.end() ; + unsigned int dim_num = 0 ; + for (; i != ie; i++) { + strm << DapIndent::LMarg << "dimension " << dim_num++ << ":" + << endl ; + DapIndent::Indent() ; + strm << DapIndent::LMarg << "name: " << (*i).name << endl ; + strm << DapIndent::LMarg << "size: " << (*i).size << endl ; + strm << DapIndent::LMarg << "start: " << (*i).start << endl ; + strm << DapIndent::LMarg << "stop: " << (*i).stop << endl ; + strm << DapIndent::LMarg << "stride: " << (*i).stride << endl ; + strm << DapIndent::LMarg << "constrained size: " << (*i).c_size + << endl ; + DapIndent::UnIndent() ; + } + DapIndent::UnIndent() ; + DapIndent::UnIndent() ; +} + +} // namespace libdap + diff --git a/Array.h b/Array.h new file mode 100644 index 0000000..3d676b3 --- /dev/null +++ b/Array.h @@ -0,0 +1,295 @@ + +// -*- mode: c++; c-basic-offset:4 -*- + +// This file is part of libdap, A C++ implementation of the OPeNDAP Data +// Access Protocol. + +// Copyright (c) 2002,2003 OPeNDAP, Inc. +// Author: James Gallagher +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// +// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112. + +// (c) COPYRIGHT URI/MIT 1994-1999 +// Please read the full copyright statement in the file COPYRIGHT_URI. +// +// Authors: +// jhrg,jimg James Gallagher + +// Class for array variables. The dimensions of the array are stored in the +// list SHAPE. +// +// jhrg 9/6/94 + +#ifndef _array_h +#define _array_h 1 + +#include +#include + +#ifndef _dods_limits_h +#include "dods-limits.h" +#endif + +#ifndef _vector_h +#include "Vector.h" +#endif + +//#include "D4Dimensions.h" + +namespace libdap +{ +class D4Group; +class D4Maps; +class XMLWriter; +class D4Dimension; +class D4Dimensions; + +const int DODS_MAX_ARRAY = DODS_INT_MAX; + +/** This class is used to hold arrays of data. The elements of the array can + be simple or compound data types. There is no limit on the number of + dimensions an array can have, or on the size of each dimension. + + If desired, the user can give each dimension of an array a name. You can, + for example, have a 360x180 array of temperatures, covering the whole + globe with one-degree squares. In this case, you could name the first + dimension \e Longitude and the second dimension \e Latitude. This can + help prevent a great deal of confusion. + + The Array is used as part of the Grid class, where the dimension names + are crucial to its structure. The dimension names correspond to \e Map + vectors, holding the actual values for that column of the array. + + In DAP4, the Array may be a Coverage or a simple Array. In the former case + the Array will have both named dimensions and maps, where the maps (instances + of D4Map) are what make the Array a Coverage. Coverages are a generalization + of DAP2 Grids. + + Each array dimension carries with it its own projection information. The + projection information takes the form of three integers: the start, stop, + and stride values. This is clearest with an example. Consider a + one-dimensional array 10 elements long. If the start value of the + dimension constraint is 3, then the constrained array appears to be seven + elements long. If the stop value is changed to 7, then the array appears + to be five elements long. If the stride is changed to two, the array will + appear to be 3 elements long. Array constraints are written as: + [start:stride:stop]. + + \verbatim + A = [1 2 3 4 5 6 7 8 9 10] + + A[3::] = [4 5 6 7 8 9 10] + + A[3::7] = [4 5 6 7 8] + + A[3:2:7] = [4 6 8] + + A[0:3:9] = [1 4 7 10] + \endverbatim + + @note Arrays use zero-based indexing. + @note This class is used for both DAP2 and DAP4. + + @brief A multidimensional array of identical data types. + @see Grid + @see Vector + @see dimension */ + +class Array: public Vector +{ +public: + /** Information about a dimension. Each Array has one or more dimensions. + For each of an Array's dimensions, a corresponding instance of this + struct holds the natural size, name, constraint information and + constrained size. + + @note Instead of using this struct's fields directly, use Array's + dimension accessor methods. + + @note This struct is public because its type is used in public + typedefs. */ + struct dimension + { + // In DAP2, the name and size of a dimension is stored here, along + // with information about any constraint. In DAP4, either the name + // and size are stored in the two fields below _or_ the name and + // size information comes from a dimension object defined in a + // group that is referenced by the 'dim' pointer. Do not free this + // pointer; it is shared between the array and the Group where the + // Dimension is defined. To keep Array manageable to implement, size + // will be set here using the value from 'dim' if it is not null. + int size; ///< The unconstrained dimension size. + string name; ///< The name of this dimension. + + D4Dimension *dim; ///< If not null, a weak pointer to the D4Dimension + + // when a DMR is printed for a data response, if an array uses shared + // dimensions and those sdims have been sliced, make sure to use those + // and get the syntax correct. That's what this field does - in every + // case the array records the sizes of its dimensions and their slices + // regardless of whether they were provided explicitly in a CE or inherited + // from a sliced sdim. + bool use_sdim_for_slice; ///< Used to control printing the DMR in data responses + + int start; ///< The constraint start index + int stop; ///< The constraint end index + int stride; ///< The constraint stride + int c_size; ///< Size of dimension once constrained + + dimension() : size(0), name(""), dim(0), use_sdim_for_slice(false) { + // this information changes with each constraint expression + start = 0; + stop = 0; + stride = 1; + c_size = size; + } + + dimension(unsigned long s, string n) : size(s), name(n), dim(0), use_sdim_for_slice(false) { + start = 0; + stop = size - 1; + stride = 1; + c_size = size; + } + + dimension(D4Dimension *d); + }; + + D4Maps *d_maps; + +private: + std::vector _shape; // list of dimensions (i.e., the shape) + + void update_dimension_pointers(D4Dimensions *old_dims, D4Dimensions *new_dims); + + friend class ArrayTest; + friend class D4Group; + + bool is_dap2_grid(); + +protected: + void _duplicate(const Array &a); + + unsigned int print_array(FILE *out, unsigned int index, + unsigned int dims, unsigned int shape[]); + + unsigned int print_array(ostream &out, unsigned int index, + unsigned int dims, unsigned int shape[]); + +public: + /** A constant iterator used to access the various dimensions of an + Array. + + @see dim_begin() + @see dim_end() */ + typedef std::vector::const_iterator Dim_citer; + + /** An iterator used to access the various dimensions of an + Array. Most of the methods that access various properties of a + dimension use an instance of Dim_iter. + + @see dim_begin() + @see dim_end() */ + typedef std::vector::iterator Dim_iter; + + Array(const string &n, BaseType *v, bool is_dap4 = false); + Array(const string &n, const string &d, BaseType *v, bool is_dap4 = false); + Array(const Array &rhs); + virtual ~Array(); + + Array &operator=(const Array &rhs); + virtual BaseType *ptr_duplicate(); + + virtual void transform_to_dap4(D4Group *root, Constructor *container); + virtual std::vector *transform_to_dap2(AttrTable *parent_attr_table); + + void add_var(BaseType *v, Part p = nil); + void add_var_nocopy(BaseType *v, Part p = nil); + + void append_dim(int size, const string &name = ""); + void append_dim(D4Dimension *dim); + void prepend_dim(int size, const string& name = ""); + void prepend_dim(D4Dimension *dim); + void clear_all_dims(); + + virtual void add_constraint(Dim_iter i, int start, int stride, int stop); + virtual void add_constraint(Dim_iter i, D4Dimension *dim); + virtual void reset_constraint(); + + virtual void clear_constraint(); // deprecated + + virtual void update_length(int size = 0); // should be used internally only + + Dim_iter dim_begin() ; + Dim_iter dim_end() ; + + virtual int dimension_size(Dim_iter i, bool constrained = false); + virtual int dimension_start(Dim_iter i, bool constrained = false); + virtual int dimension_stop(Dim_iter i, bool constrained = false); + virtual int dimension_stride(Dim_iter i, bool constrained = false); + virtual string dimension_name(Dim_iter i); + virtual D4Dimension *dimension_D4dim(Dim_iter i); + + virtual unsigned int dimensions(bool constrained = false); + + virtual D4Maps *maps(); + + virtual void print_dap4(XMLWriter &xml, bool constrained = false); + + // These are all DAP2 output methods + + virtual void print_decl(ostream &out, string space = " ", + bool print_semi = true, + bool constraint_info = false, + bool constrained = false); + + virtual void print_xml(ostream &out, string space = " ", + bool constrained = false); + + virtual void print_xml_writer(XMLWriter &xml, bool constrained = false); + virtual void print_xml_writer_core(XMLWriter &out, bool constrained, string tag); + virtual void print_as_map_xml_writer(XMLWriter &xml, bool constrained); + + virtual void print_xml_core(FILE *out, string space, bool constrained, string tag); + virtual void print_xml_core(ostream &out, string space, bool constrained, string tag); + + // not used (?) + virtual void print_as_map_xml(ostream &out, string space = " ", + bool constrained = false); + + virtual void print_val(ostream &out, string space = "", + bool print_decl_p = true); + + virtual void print_xml(FILE *out, string space = " ", + bool constrained = false); + virtual void print_as_map_xml(FILE *out, string space = " ", + bool constrained = false); + virtual void print_val(FILE *out, string space = "", + bool print_decl_p = true); + virtual void print_decl(FILE *out, string space = " ", + bool print_semi = true, + bool constraint_info = false, + bool constrained = false); + + virtual bool check_semantics(string &msg, bool all = false); + + + virtual void dump(ostream &strm) const ; +}; + +} // namespace libdap + +#endif // _array_h diff --git a/AttrTable.cc b/AttrTable.cc new file mode 100644 index 0000000..31fd2c8 --- /dev/null +++ b/AttrTable.cc @@ -0,0 +1,1559 @@ +// -*- mode: c++; c-basic-offset:4 -*- + +// This file is part of libdap, A C++ implementation of the OPeNDAP Data +// Access Protocol. + +// Copyright (c) 2002,2003 OPeNDAP, Inc. +// Author: James Gallagher +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// +// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112. + +// (c) COPYRIGHT URI/MIT 1994-1999 +// Please read the full copyright statement in the file COPYRIGHT_URI. +// +// Authors: +// jhrg,jimg James Gallagher + +// jhrg 7/29/94 + +#include "config.h" + +#include +#include + +#include "AttrTable.h" + +#include "util.h" +#include "escaping.h" + +#include "debug.h" + +// Should the www2id and id2www functions be used to encode attribute names? +// Probably not... jhrg 11/16/11 +#define WWW_ENCODING 0 +// See the note for del_attr_table(). That method now deletes the contained +// AttrTable. +#define NEW_DEL_ATTR_TABLE_BEHAVIOR 0 + +using std::cerr; +using std::string; +using std::endl; +using std::vector; + +namespace libdap { + +/** Remove %20 space encoding */ +string remove_space_encoding(const string &s) +{ + string::size_type pos = s.find("%20"); + if (pos != string::npos) { + string n = s; + do { + n.replace(pos, 3, " "); + pos = n.find("%20"); + } while (pos != string::npos); + return n; + } + else { + return s; + } +} + +/** Add %20 space encoding. */ +string add_space_encoding(const string &s) +{ + string::size_type pos = s.find(" "); + if (pos != string::npos) { + string n = s; + do { + n.replace(pos, 1, "%20"); + pos = n.find(" "); + } while (pos != string::npos); + return n; + } + else { + return s; + } +} + +/** Convert an AttrType to it's string representation. + @param at The Attribute Type. + @return The type's string representation */ +string AttrType_to_String(const AttrType at) +{ + switch (at) { + case Attr_container: + return "Container"; + case Attr_byte: + return "Byte"; + case Attr_int16: + return "Int16"; + case Attr_uint16: + return "UInt16"; + case Attr_int32: + return "Int32"; + case Attr_uint32: + return "UInt32"; + case Attr_float32: + return "Float32"; + case Attr_float64: + return "Float64"; + case Attr_string: + return "String"; + case Attr_url: + return "Url"; + case Attr_other_xml: + return "OtherXML"; + default: + return ""; + } +} + +AttrType String_to_AttrType(const string &s) +{ + string s2 = s; + downcase(s2); + + if (s2 == "container") + return Attr_container; + else if (s2 == "byte") + return Attr_byte; + else if (s2 == "int16") + return Attr_int16; + else if (s2 == "uint16") + return Attr_uint16; + else if (s2 == "int32") + return Attr_int32; + else if (s2 == "uint32") + return Attr_uint32; + else if (s2 == "float32") + return Attr_float32; + else if (s2 == "float64") + return Attr_float64; + else if (s2 == "string") + return Attr_string; + else if (s2 == "url") + return Attr_url; + else if (s2 == "otherxml") + return Attr_other_xml; + else + return Attr_unknown; +} + +/** Clone the given attribute table in this. + Protected. */ +void AttrTable::clone(const AttrTable &at) +{ + d_name = at.d_name; + d_is_global_attribute = at.d_is_global_attribute; + + // Set the parent to null (no parent, not in container) + // since using at.d_parent is semantically incorrect + // and potentially dangerous. + d_parent = 0; + + Attr_citer i = at.attr_map.begin(); + Attr_citer ie = at.attr_map.end(); + for (; i != ie; ++i) { + // this deep-copies containers recursively + entry *e = new entry(*(*i)); + attr_map.push_back(e); + + // If the entry being added was a container, + // set its parent to this to maintain invariant. + if (e->type == Attr_container) { + assert(e->attributes); + e->attributes->d_parent = this; + } + } +} + +/** @name Instance management functions */ + +//@{ +AttrTable::AttrTable() : + DapObj(), d_name(""), d_parent(0), attr_map(), d_is_global_attribute(true) +{ +} + +AttrTable::AttrTable(const AttrTable &rhs) : + DapObj() +{ + clone(rhs); +} + +// Private +void AttrTable::delete_attr_table() +{ + for (Attr_iter i = attr_map.begin(); i != attr_map.end(); ++i) { + delete *i; + } + attr_map.clear(); +} + +AttrTable::~AttrTable() +{ + delete_attr_table(); +} + +AttrTable & +AttrTable::operator=(const AttrTable &rhs) +{ + if (this != &rhs) { + delete_attr_table(); + clone(rhs); + } + + return *this; +} +//@} + +/** Attributes that are containers count one attribute, as do + attributes with both scalar and vector values. + @return The number of entries. + @brief Get the number of entries in this attribute table. + */ +unsigned int AttrTable::get_size() const +{ + return attr_map.size(); +} + +/** @brief Get the name of this attribute table. + @return A string containing the name. */ +string AttrTable::get_name() const +{ + return d_name; +} + +/** @brief Set the name of this attribute table. + @param n The new name of the attribute table. */ +void AttrTable::set_name(const string &n) +{ +#if WWW_ENCODING + d_name = www2id(n); +#else + d_name = remove_space_encoding(n); +#endif +} + +#if 0 +// This was taken from das.y and could be used here to make the 'dods_errors' +// attribute container like the parser used to. Then again, maybe this feature +// was just BS. jhrg (ticket 1469) +static void add_bad_attribute(AttrTable *attr, const string &type, const string &name, const string &value, + const string &msg) { + // First, if this bad value is already in a *_dods_errors container, + // then just add it. This can happen when the server side processes a DAS + // and then hands it off to a client which does the same. + // Make a new container. Call it _errors. If that container + // already exists, use it. + // Add the attribute. + // Add the error string to an attribute in the container called + // `get_name().find("_dods_errors") != string::npos) { + attr->append_attr(name, type, value); + } + else { + // I think _dods_errors should be _dap_error. jhrg 11/16/11 + string error_cont_name = attr->get_name() + "_dods_errors"; + AttrTable *error_cont = attr->get_attr_table(error_cont_name); + if (!error_cont) + error_cont = attr->append_container(error_cont_name); + + error_cont->append_attr(name, type, value); + +#ifndef ATTR_STRING_QUOTE_FIX + error_cont->append_attr(name + "_dap_explanation", "String", "\"" + msg + "\""); +#else + error_cont->append_attr(name + "_dap_explanation", "String", msg); +#endif + } +} +#endif + +/** If the given name already refers to an attribute, and the attribute has a + value, the given value is appended to the attribute vector. Calling this + function repeatedly is the way to append to an attribute vector. + + The function throws an Error if the attribute is a container, + or if the type of the input value does not match the existing attribute's + type. Use append_container() to add container attributes. + + This method performs a simple search for name in this attribute + table only; sub-tables are not searched and the dot notation is not + recognized. + + @brief Add an attribute to the table. + @return Returns the length of the added attribute value. + @param name The name of the attribute to add or modify. + @param type The type of the attribute to add or modify. + @param value The value to add to the attribute table. */ +unsigned int AttrTable::append_attr(const string &name, const string &type, const string &value) +{ + DBG(cerr << "Entering AttrTable::append_attr" << endl); +#if WWW_ENCODING + string lname = www2id(name); +#else + string lname = remove_space_encoding(name); +#endif + + Attr_iter iter = simple_find(lname); + + // If the types don't match OR this attribute is a container, calling + // this mfunc is an error! + if (iter != attr_map.end() && ((*iter)->type != String_to_AttrType(type))) + throw Error(string("An attribute called `") + name + string("' already exists but is of a different type")); + if (iter != attr_map.end() && (get_type(iter) == "Container")) + throw Error(string("An attribute called `") + name + string("' already exists but is a container.")); + + if (iter != attr_map.end()) { // Must be a new attribute value; add it. + (*iter)->attr->push_back(value); + return (*iter)->attr->size(); + } + else { // Must be a completely new attribute; add it + entry *e = new entry; + + e->name = lname; + e->is_alias = false; + e->type = String_to_AttrType(type); // Record type using standard names. + e->attr = new vector ; + e->attr->push_back(value); + + attr_map.push_back(e); + + return e->attr->size(); // return the length of the attr vector + } +} + +/** This version of append_attr() takes a vector of values. + If the given name already refers to an attribute, and the attribute has + values, append the new values to the existing ones. + + The function throws an Error if the attribute is a container, + or if the type of the input value does not match the existing attribute's + type. Use append_container() to add container attributes. + + This method performs a simple search for name in this attribute + table only; sub-tables are not searched and the dot notation is not + recognized. + + @brief Add an attribute to the table. + @return Returns the length of the added attribute value. + @param name The name of the attribute to add or modify. + @param type The type of the attribute to add or modify. + @param values A vector of values. Note: The vector is COPIED, not stored. */ + +unsigned int AttrTable::append_attr(const string &name, const string &type, vector *values) +{ + DBG(cerr << "Entering AttrTable::append_attr(..., vector)" << endl); +#if WWW_ENCODING + string lname = www2id(name); +#else + string lname = remove_space_encoding(name); +#endif + Attr_iter iter = simple_find(lname); + + // If the types don't match OR this attribute is a container, calling + // this mfunc is an error! + if (iter != attr_map.end() && ((*iter)->type != String_to_AttrType(type))) + throw Error(string("An attribute called `") + name + string("' already exists but is of a different type")); + if (iter != attr_map.end() && (get_type(iter) == "Container")) + throw Error(string("An attribute called `") + name + string("' already exists but is a container.")); + + if (iter != attr_map.end()) { // Must be new attribute values; add. + vector::iterator i = values->begin(); + while (i != values->end()) + (*iter)->attr->push_back(*i++); + + return (*iter)->attr->size(); + } + else { // Must be a completely new attribute; add it + entry *e = new entry; + + e->name = lname; + e->is_alias = false; + e->type = String_to_AttrType(type); // Record type using standard names. + e->attr = new vector (*values); + + attr_map.push_back(e); + + return e->attr->size(); // return the length of the attr vector + } +} + +/** Create and append an attribute container to this AttrTable. If this + attribute table already contains an attribute container called + name an exception is thrown. Return a pointer to the new container. + + @brief Add a container to the attribute table. + @param name The name of the container to create. + @return A pointer to the new AttrTable object. + */ + +AttrTable * +AttrTable::append_container(const string &name) +{ + AttrTable *new_at = new AttrTable; + AttrTable *ret = NULL; + try { + ret = append_container(new_at, name); + } catch (Error &e) { + // an error occurred, attribute with that name already exists + delete new_at; + new_at = 0; + throw; + } + return ret; +} + +/** Append a new attribute container to this attribute table. The new + container is at and its name is set to + name. If this attribute + table already contains an attribute container called + name an exception is thrown. + + @note The value of \e name will override the name of \e at set using the + set_name() method. + + @brief Add a container to the attribute table. + @param at A pointer to the new attribute table to append. + @param name The name of the new attribute table. + @return A pointer to the new AttrTable object. + */ +AttrTable * +AttrTable::append_container(AttrTable *at, const string &name) +{ +#if WWW_ENCODING + string lname = www2id(name); +#else + string lname = remove_space_encoding(name); +#endif + + if (simple_find(name) != attr_end()) + throw Error("There already exists a container called '" + name + "' in this attribute table (" + at->get_name() + "). (1)"); + + DBG(cerr << "Setting appended attribute container name to: " << lname << endl); + at->set_name(lname); + + entry *e = new entry; + e->name = lname; + e->is_alias = false; + e->type = Attr_container; + e->attributes = at; + + attr_map.push_back(e); + + at->d_parent = this; + + return e->attributes; +} + +/** Look for an attribute or an attribute container. If used to search + for an attribute container, this method returns the container's \e + parent using the value-result parameter \c at and a reference to the + container using the iterator value-result parameter \c iter. If used + to search for an attribute, the attribute's container is returned using + \c at; the attribute itself can be accessed using the iterator \c iter. + + @param target The name (using dot notation) of the attribute or + container to find. + @param at A value-result used to return the attribute container in + which \c target was found. Null if \c target was not found. + @param iter The iterator which will reference the attribute found. + Can be used to access \c target from within \c at. References + dim_end() within \c at if the attribute or container does not exist. */ +void AttrTable::find(const string &target, AttrTable **at, Attr_iter *iter) +{ + string::size_type dotpos = target.rfind('.'); + if (dotpos != string::npos) { + string container = target.substr(0, dotpos); + string field = target.substr(dotpos + 1); + + *at = find_container(container); + if (*at) { + *iter = (*at)->simple_find(field); + } + else { + *iter = attr_map.end(); + } + } + else { + *at = recurrsive_find(target, iter); + } +} + +/** This method scans for attributes using recursion to look inside containers + even when the name of the attribute is not fully qualified. It starts + looking in itself and descends into its children depth first. It will find + attributes and attribute containers. + + @param target Look for the attribute with this name. + @param location A value-result parameter. This returns an iterator to the + attribute within the returned AttrTable object + @return Returns a pointer to the AttrTable which holds \e target, or null + if \e target is not found. In the latter case, the value of \e location is + attr_end() for this AttrTable. */ +AttrTable * +AttrTable::recurrsive_find(const string &target, Attr_iter *location) +{ + Attr_iter i = attr_begin(); + while (i != attr_end()) { + if (target == (*i)->name) { + *location = i; + return this; + } + else if ((*i)->type == Attr_container) { + AttrTable *at = (*i)->attributes->recurrsive_find(target, location); + if (at) + return at; + } + + ++i; + } + + *location = i; + return 0; +} + +// Made public for callers that want non-recursive find. [mjohnson 6 oct 09] +/** Look in this AttrTable for the attribute called \c name. If found return + an Attr_iter which references it, otherwise return the end iterator for + this AttrTable. + + @param target The name of the attribute. + @return An Attr_iter which references \c target. */ +AttrTable::Attr_iter AttrTable::simple_find(const string &target) +{ + Attr_iter i; + for (i = attr_map.begin(); i != attr_map.end(); ++i) { + if (target == (*i)->name) { + break; + } + } + return i; +} + +/** Look in this attribute table for an attribute container named + target. The search starts at this attribute table; + target should + use the dot notation to name containers held within children of this + attribute table. + + To search the entire DAS object, make sure to invoke this method from + that object. + + @brief Find an attribute with a given name. + @param target The attribute container to find. + @return A pointer to the attribute table or null if the container + cannot be found. */ +AttrTable * +AttrTable::find_container(const string &target) +{ + string::size_type dotpos = target.find('.'); + if (dotpos != string::npos) { + string container = target.substr(0, dotpos); + string field = target.substr(dotpos + 1); + + AttrTable *at = simple_find_container(container); + return (at) ? at->find_container(field) : 0; + } + else { + return simple_find_container(target); + } +} + +// Made public for callers that want non-recursive find. [mjohnson 6 oct 09] +AttrTable * +AttrTable::simple_find_container(const string &target) +{ + if (get_name() == target) + return this; + + for (Attr_iter i = attr_map.begin(); i != attr_map.end(); ++i) { + if (is_container(i) && target == (*i)->name) { + return (*i)->attributes; + } + } + + return 0; +} + +/** Each of the following accessors get information using the name of an + attribute. They perform a simple search for the name in this + attribute table only; sub-tables are not searched and the dot + notation is not recognized. + + @name Accessors using an attribute name */ +//@{ + +/** @brief Get an attribute container. */ +AttrTable * +AttrTable::get_attr_table(const string &name) +{ + return find_container(name); +} + +/** @brief Get the type name of an attribute within this attribute table. */ +string AttrTable::get_type(const string &name) +{ + Attr_iter p = simple_find(name); + return (p != attr_map.end()) ? get_type(p) : (string) ""; +} + +/** @brief Get the type of an attribute. + @return The AttrType value describing the attribute. */ +AttrType AttrTable::get_attr_type(const string &name) +{ + Attr_iter p = simple_find(name); + return (p != attr_map.end()) ? get_attr_type(p) : Attr_unknown; +} + +/** If the indicated attribute is a container attribute, this function + returns the number of attributes in its attribute table. If the + indicated attribute is not a container, the method returns the number + of values for the attribute (1 for a scalar attribute, N for a vector + attribute value). + @brief Get the number of attributes in this container. + */ +unsigned int AttrTable::get_attr_num(const string &name) +{ + Attr_iter iter = simple_find(name); + return (iter != attr_map.end()) ? get_attr_num(iter) : 0; +} + +/** Get a pointer to the vector of values associated with the attribute + referenced by Pix p or named name. + + Note that all values in an attribute table are stored as string data. + They may be converted to a more appropriate internal format by the + calling program. + + @return If the indicated attribute is a container, this function + returns the null pointer. Otherwise returns a pointer to the + the attribute vector value. + @brief Get a vector-valued attribute. + */ +vector * +AttrTable::get_attr_vector(const string &name) +{ + Attr_iter p = simple_find(name); + return (p != attr_map.end()) ? get_attr_vector(p) : 0; +} + +/** Delete the attribute named name. If i is given, and + the attribute has a vector value, delete the i$^th$ + element of the vector. + + You can use this function to delete container attributes, although + the i parameter has no meaning for that operation. + + @brief Deletes an attribute. + @param name The name of the attribute to delete. This can be an + attribute of any type, including containers. However, this method + looks only in this attribute table and does not recognize the dot + notation. + @param i If the named attribute is a vector, and i is + non-negative, the i-th entry in the vector is deleted, and the + array is repacked. If i equals -1 (the default), the + entire attribute is deleted. */ +void AttrTable::del_attr(const string &name, int i) +{ +#if WWW_ENCODING + string lname = www2id(name); +#else + string lname = remove_space_encoding(name); +#endif + + Attr_iter iter = simple_find(lname); + if (iter != attr_map.end()) { + if (i == -1) { // Delete the whole attribute + entry *e = *iter; + attr_map.erase(iter); + delete e; + e = 0; + } + else { // Delete one element from attribute array + // Don't try to delete elements from the vector of values if the + // map is a container! + if ((*iter)->type == Attr_container) + return; + + vector *sxp = (*iter)->attr; + + assert(i >= 0 && i < (int) sxp->size()); + sxp->erase(sxp->begin() + i); // rm the element + } + } +} + +//@} Accessors using an attribute name + +/** @name get information using an iterator */ +//@{ +/** Get an iterator to the first entry in this attribute table. + @return Attr_iter; references the end of the array if empty list. */ +AttrTable::Attr_iter AttrTable::attr_begin() +{ + return attr_map.begin(); +} + +/** Get an iterator to the end attribute table. Does not point to + the last attribute in the table + @return Attr_iter */ +AttrTable::Attr_iter AttrTable::attr_end() +{ + return attr_map.end(); +} + +/** Given an index \c i, return the \c Attr_iter to the corresponding + element. This method provides a way to use all the methods that take an + \c Attr_iter using a simple integer index. Use the get_attr_num() or + get_size() methods to determine how many items the AttrTable contains. + + @param i The index + @return The corresponding Attr_iter + @see get_attr_num, get_size */ +AttrTable::Attr_iter AttrTable::get_attr_iter(int i) +{ + return attr_map.begin() + i; +} + +/** Returns the name of the attribute referenced by \e iter. */ +string AttrTable::get_name(Attr_iter iter) +{ + assert(iter != attr_map.end()); + + return (*iter)->name; +} + +/** Returns true if the attribute referenced by \e i is a container. */ +bool AttrTable::is_container(Attr_iter i) +{ + return (*i)->type == Attr_container; +} + +/** Get the attribute container referenced by \e iter. If no + such container exists, then return a reference to the end of the + table. + @param iter Reference to a table contained by this object. + @return The child attribute table. */ +AttrTable * +AttrTable::get_attr_table(Attr_iter iter) +{ + assert(iter != attr_map.end()); + return (*iter)->type == Attr_container ? (*iter)->attributes : 0; +} + +/** Delete the iterator. Since AttrTable stores pointers to AttrTable + objects, the caller should be sure to delete the AttrTable itself. + The caller will gain control of the AttrTable* located at + get_attr_table(iter) prior to this call. + + @note The original semantics of this methods were odd. The caller was + responsible for deleting the AttrTable, but if they did that before calling + this, then memory corruption would happen (because this code accesses a + field of the table). If the caller did not delete the table, memory leaked. + The only correct way to call the method was to grab the pointer, call this + and then delete the pointer. I added a call to delete the contained + AttrTable pointer, which changes the behavior of this, but probably in a + way that will fix leaks in existing code. This change can be reverted by + setting NEW_DEL_ATTR_TABLE_BEHAVIOR to false. jhrg 4/26/13 + + @note calling this method invalidates the iterator \e iter. + @param iter points to the entry to be deleted. + @return The Attr_iter for the element following \e iter */ +AttrTable::Attr_iter AttrTable::del_attr_table(Attr_iter iter) +{ + if ((*iter)->type != Attr_container) + return ++iter; + + // the caller intends to delete/reuse the contained AttrTable, + // so zero it out so it doesn't get deleted before we delete the entry + // [mjohnson] + struct entry *e = *iter; + // container no longer has a parent. + if (e->attributes) { + e->attributes->d_parent = 0; + +#if NEW_DEL_ATTR_TABLE_BEHAVIOR + delete e->attributes; +#endif + e->attributes = 0; + } + + delete e; + + return attr_map.erase(iter); +} + +/** Get the type name of an attribute referenced by \e iter. + @param iter Reference to the Attribute. + @return A string with the name of this attribute datatype. */ +string AttrTable::get_type(Attr_iter iter) +{ + assert(iter != attr_map.end()); + return AttrType_to_String((*iter)->type); +} + +/** Get the type of the attribute referenced by \e iter. + @param iter + @return The datatype of this attribute in an instance of AttrType. */ +AttrType AttrTable::get_attr_type(Attr_iter iter) +{ + return (*iter)->type; +} + +/** If the attribute referenced by \e iter is a container attribute, this + method returns the number of attributes in its attribute table. + If the indicated attribute is not a container, the method returns the + number of values for the attribute (1 for a scalar attribute, N for a + vector attribute value). + @param iter Reference to an attribute + @return The number of elements in the attribute. */ +unsigned int AttrTable::get_attr_num(Attr_iter iter) +{ + assert(iter != attr_map.end()); + return ((*iter)->type == Attr_container) ? (*iter)->attributes->get_size() : (*iter)->attr->size(); +} + +/** Returns the value of an attribute. If the attribute has a vector + value, you can indicate which is the desired value with the index + argument, \e i. If the argument is omitted, the first value is + returned. If the attribute has only a single value, the index + argument is ignored. If \e i is greater than the number of + elements in the attribute, an error is produced. + + All values in an attribute table are stored as string data. They may + be converted to a more appropriate internal format by the calling + program. + + @param iter Reference to an attribute + @param i The attribute value index, zero-based. Default value: 0 + @return If the indicated attribute is a container, this function + returns the string ``None''. If using a name to refer to the attribute + and the named attribute does not exist, return the empty string. */ +string AttrTable::get_attr(Attr_iter iter, unsigned int i) +{ + assert(iter != attr_map.end()); + + return (*iter)->type == Attr_container ? (string) "None" : (*(*iter)->attr)[i]; +} + +string AttrTable::get_attr(const string &name, unsigned int i) +{ + Attr_iter p = simple_find(name); + return (p != attr_map.end()) ? get_attr(p, i) : (string) ""; +} + +/** Returns a pointer to the vector of values associated with the + attribute referenced by iterator \e iter. + + Note that all values in an attribute table are stored as string data. + They may be converted to a more appropriate internal format by the + calling program. + + @param iter Reference to the Attribute. + @return If the indicated attribute is a container, this function + returns the null pointer. Otherwise returns a pointer to the + the attribute vector value. */ +vector * +AttrTable::get_attr_vector(Attr_iter iter) +{ + assert(iter != attr_map.end()); + return (*iter)->type != Attr_container ? (*iter)->attr : 0; +} + +bool AttrTable::is_global_attribute(Attr_iter iter) +{ + assert(iter != attr_map.end()); + if ((*iter)->type == Attr_container) + return (*iter)->attributes->is_global_attribute(); + else + return (*iter)->is_global; +} + +void AttrTable::set_is_global_attribute(Attr_iter iter, bool ga) +{ + assert(iter != attr_map.end()); + if ((*iter)->type == Attr_container) + (*iter)->attributes->set_is_global_attribute(ga); + else + (*iter)->is_global = ga; +} + +//@} Accessors that use an iterator + +// Alias an attribute table. The alias should be added to this object. +/** @brief Add an alias to a container held by this attribute table. + @param name The name of the alias. May not use dot notation. + @param src The existing attribute container to alias. + @exception Error if an attribute, container or alias called + name already exists in this attribute table. */ +void AttrTable::add_container_alias(const string &name, AttrTable *src) +{ +#if WWW_ENCODING + string lname = www2id(name); +#else + string lname = remove_space_encoding(name); +#endif + + if (simple_find(lname) != attr_end()) + throw Error(string("There already exists a container called `") + name + string("in this attribute table. (2)")); + + entry *e = new entry; + e->name = lname; + e->is_alias = true; + e->aliased_to = src->get_name(); + e->type = Attr_container; + + e->attributes = src; + + attr_map.push_back(e); +} + +/** Assume \e source names an attribute value in some container. Add an alias + \e name for that value in this object. + + @brief Add an alias for an attribute. + + @param das + @param name The name of the alias. May not use dot notation. + @param source The name of the attribute to alias. May use dot + notation. + @exception Error if the attribute table already contains an + attribute, container or alias called name or if an + attribute called source does not exist. */ +void AttrTable::add_value_alias(AttrTable *das, const string &name, const string &source) +{ +#if WWW_ENCODING + string lname = www2id(name); +#else + string lname = remove_space_encoding(name); +#endif + +#if WWW_ENCODING + string lsource = www2id(source); +#else + string lsource = remove_space_encoding(source); +#endif + + // find the container that holds source and its (sources's) iterator + // within that container. Search at the uppermost level of the attribute + // object to find values defined `above' the current container. + AttrTable *at; + Attr_iter iter; + das->find(lsource, &at, &iter); + + // If source is not found by looking at the topmost level, look in the + // current table (i.e., alias z x where x is in the current container + // won't be found by looking for `x' at the top level). See test case 26 + // in das-testsuite. + if (!at || (iter == at->attr_end()) || !*iter) { + find(lsource, &at, &iter); + if (!at || (iter == at->attr_end()) || !*iter) + throw Error(string("Could not find the attribute `") + source + string("' in the attribute object.")); + } + + // If we've got a value to alias and it's being added at the top level of + // the DAS, that's an error. + if (at && !at->is_container(iter) && this == das) + throw Error( + string( + "A value cannot be aliased to the top level of the DAS;\nOnly containers may be present at that level of the DAS.")); + + if (simple_find(lname) != attr_end()) + throw Error(string("There already exists a container called `") + name + string("in this attribute table. (3)")); + + entry *e = new entry; + e->name = lname; + e->is_alias = true; + e->aliased_to = lsource; + e->type = get_attr_type(iter); + if (at && e->type == Attr_container) + e->attributes = at->get_attr_table(iter); + else + e->attr = (*iter)->attr; + + attr_map.push_back(e); +} + +// Deprecated +/** Once an alias is + inserted into an attribute table, reading the attributes for + alias will return those stored for name. + + Two forms for this function exist: one searches for name + in the AttrTable referenced by at while the other uses + this. You can use DAS::get_attr_table() to + get the attribute table for an arbitrary name. + + @brief Adds an alias to the set of attributes. + @see get_attr_table + @deprecated The current alias design is flawed. It is impossible to map + this onto the XML implementation where the DAS and DDS information are + combined in one object. + @param alias The alias to insert into the attribute table. + @param name The name of the already-existing attribute to which + the alias will refer. + @param at An attribute table in which to insert the alias. */ +bool AttrTable::attr_alias(const string &alias, AttrTable *at, const string &name) +{ + add_value_alias(at, alias, name); + return true; +} + +/** @deprecated The current alias design is flawed. It is impossible to map + this onto the XML implementation where the DAS and DDS information are + combined in one object. + + @param alias The alias to insert into the attribute table. + @param name The name of the already-existing attribute to which + the alias will refer. */ +bool AttrTable::attr_alias(const string &alias, const string &name) +{ + return attr_alias(alias, this, name); +} + +/** Erase the entire attribute table. This returns an AttrTable to the empty + state that's the same as the object generated by the null constructor. + @brief Erase the attribute table. */ +void AttrTable::erase() +{ + for (Attr_iter i = attr_map.begin(); i != attr_map.end(); ++i) { + delete *i; + *i = 0; + } + + attr_map.erase(attr_map.begin(), attr_map.end()); + + d_name = ""; +} + +const string double_quote = "\""; + +// This is here as a result of the problem described in ticket #1163 where +// the data handlers are adding quotes to string attributes so the DAS will +// be printed correctly. But that has the affect of adding the quotes to the +// attribute's _value_ not just it's print representation. As part of the fix +// I made the code here add the quotes if the handlers are fixed (but not if +// handlers are still adding them). The other part of 1163 is to fix all of +// the handlers... What this fix means is that attributes whose values really +// do contain bracketing quotes might be misunderstood, since we're assuming +// those quotes were added by the handlers as a hack to get the output +// formatting correct for the DAS. jhrg 7/30/08 + +static void write_string_attribute_for_das(ostream &out, const string &value, const string &term) +{ + if (is_quoted(value)) + out << value << term; + else + out << double_quote << value << double_quote << term; +} + +#if 0 +static void +write_string_attribute_for_das(FILE *out, const string &value, const string &term) +{ + if (is_quoted(value)) + fprintf(out, "%s%s", value.c_str(), term.c_str()); + else + fprintf(out, "\"%s\"%s", value.c_str(), term.c_str()); +} +#endif + +// Special treatment for XML: Make sure to escape double quotes when XML is +// printed in a DAS. +static void write_xml_attribute_for_das(ostream &out, const string &value, const string &term) +{ + if (is_quoted(value)) + out << escape_double_quotes(value) << term; + else + out << double_quote << escape_double_quotes(value) << double_quote << term; +} + +#if 0 +static void +write_xml_attribute_for_das(FILE *out, const string &value, const string &term) +{ + if (is_quoted(value)) + fprintf(out, "%s%s", escape_double_quotes(value).c_str(), term.c_str()); + else + fprintf(out, "\"%s\"%s", escape_double_quotes(value).c_str(), term.c_str()); +} +#endif + +/** A simple printer that does nothing fancy with aliases. + Protected. */ +void AttrTable::simple_print(FILE *out, string pad, Attr_iter i, bool dereference) +{ + ostringstream oss; + simple_print(oss, pad, i, dereference); + fwrite(oss.str().data(), 1, oss.str().length(), out); + +#if 0 + switch ((*i)->type) { + case Attr_container: +#if WWW_ENCODING + fprintf(out, "%s%s {\n", pad.c_str(), id2www(get_name(i)).c_str()); +#else + fprintf(out, "%s%s {\n", pad.c_str(), get_name(i).c_str()); +#endif + (*i)->attributes->print(out, pad + " ", dereference); + + fprintf(out, "%s}\n", pad.c_str()); + break; + + case Attr_string: { +#if WWW_ENCODING + fprintf(out, "%s%s %s ", pad.c_str(), get_type(i).c_str(), id2www(get_name(i)).c_str()); +#else + fprintf(out, "%s%s %s ", pad.c_str(), get_type(i).c_str(), get_name(i).c_str()); +#endif + vector *sxp = (*i)->attr; + vector::iterator last = sxp->end() - 1; + for (vector::iterator i = sxp->begin(); i != last; ++i) { + write_string_attribute_for_das(out, *i, ", "); + } + write_string_attribute_for_das(out, *last, ";\n"); + } + break; + + case Attr_other_xml: { +#if WWW_ENCODING + fprintf(out, "%s%s %s ", pad.c_str(), get_type(i).c_str(), id2www(get_name(i)).c_str()); +#else + fprintf(out, "%s%s %s ", pad.c_str(), get_type(i).c_str(), get_name(i).c_str()); +#endif + vector *sxp = (*i)->attr; + vector::iterator last = sxp->end() - 1; + for (vector::iterator i = sxp->begin(); i != last; ++i) { + write_xml_attribute_for_das(out, *i, ", "); + } + write_xml_attribute_for_das(out, *last, ";\n"); + } + break; + + default: { +#if WWW_ENCODING + fprintf(out, "%s%s %s ", pad.c_str(), get_type(i).c_str(), id2www(get_name(i)).c_str()); +#else + fprintf(out, "%s%s %s ", pad.c_str(), get_type(i).c_str(), get_name(i).c_str()); +#endif + + vector *sxp = (*i)->attr; + vector::iterator last = sxp->end() - 1; + for (vector::iterator i = sxp->begin(); i != last; ++i) { + fprintf(out, "%s%s", (*i).c_str(), ", "); + } + fprintf(out, "%s%s", (*last).c_str(), ";\n"); + } + break; + } +#endif +} + +/** A simple printer that does nothing fancy with aliases. + Protected. */ +void AttrTable::simple_print(ostream &out, string pad, Attr_iter i, bool dereference) +{ + switch ((*i)->type) { + case Attr_container: +#if WWW_ENCODING + out << pad << id2www(get_name(i)) << " {\n"; +#else + out << pad << add_space_encoding(get_name(i)) << " {\n"; +#endif + (*i)->attributes->print(out, pad + " ", dereference); + out << pad << "}\n"; + break; + + case Attr_string: { +#if WWW_ENCODING + out << pad << get_type(i) << " " << id2www(get_name(i)) << " "; +#else + out << pad << get_type(i) << " " << add_space_encoding(get_name(i)) << " "; +#endif + vector *sxp = (*i)->attr; + vector::iterator last = sxp->end() - 1; + for (vector::iterator i = sxp->begin(); i != last; ++i) { + write_string_attribute_for_das(out, *i, ", "); + } + write_string_attribute_for_das(out, *last, ";\n"); + } + break; + + case Attr_other_xml: { +#if WWW_ENCODING + out << pad << get_type(i) << " " << id2www(get_name(i)) << " "; +#else + out << pad << get_type(i) << " " << add_space_encoding(get_name(i)) << " "; +#endif + vector *sxp = (*i)->attr; + vector::iterator last = sxp->end() - 1; + for (vector::iterator i = sxp->begin(); i != last; ++i) { + write_xml_attribute_for_das(out, *i, ", "); + } + write_xml_attribute_for_das(out, *last, ";\n"); + } + break; + + default: { +#if WWW_ENCODING + out << pad << get_type(i) << " " << id2www(get_name(i)) << " "; +#else + out << pad << get_type(i) << " " << add_space_encoding(get_name(i)) << " "; +#endif + vector *sxp = (*i)->attr; + vector::iterator last = sxp->end() - 1; + for (vector::iterator i = sxp->begin(); i != last; ++i) { + out << *i << ", "; + } + out << *last << ";\n"; + } + break; + } +} + +/** Prints an ASCII representation of the attribute table to the + indicated FILE pointer. The \c pad argument is prefixed to each + line of the output to provide control of indentation. + + @brief Prints the attribute table. + @param out Print to the given output FILE. + @param pad Indent elements of a table using this string of spaces. By + default this is a string of four spaces + @param dereference If true, follow aliases. Default is false. */ + +void AttrTable::print(FILE *out, string pad, bool dereference) +{ + ostringstream oss; + print(oss, pad, dereference); + fwrite(oss.str().data(), 1, oss.str().length(), out); + +#if 0 + for (Attr_iter i = attr_map.begin(); i != attr_map.end(); ++i) { + if ((*i)->is_alias) { + if (dereference) { + simple_print(out, pad, i, dereference); + } + else { +#if WWW_ENCODING + fprintf(out, "%sAlias %s %s;\n", + pad.c_str(), + id2www(get_name(i)).c_str(), + id2www((*i)->aliased_to).c_str()); +#else + fprintf(out, "%sAlias %s %s;\n", + pad.c_str(), add_space_encoding(get_name(i)).c_str(), add_space_encoding((*i)->aliased_to).c_str()); + +#endif + } + } + else { + simple_print(out, pad, i, dereference); + } + } +#endif +} + +/** Prints an ASCII representation of the attribute table to the + indicated output stream. The \c pad argument is prefixed to each + line of the output to provide control of indentation. + + @brief Prints the attribute table. + @param out Print to the given output stream. + @param pad Indent elements of a table using this string of spaces. By + default this is a string of four spaces + @param dereference If true, follow aliases. Default is false. */ + +void AttrTable::print(ostream &out, string pad, bool dereference) +{ + for (Attr_iter i = attr_map.begin(); i != attr_map.end(); ++i) { + if ((*i)->is_alias) { + if (dereference) { + simple_print(out, pad, i, dereference); + } + else { +#if WWW_ENCODING + out << pad << "Alias " << id2www(get_name(i)) + << " " << id2www((*i)->aliased_to) << ";\n"; +#else + out << pad << "Alias " << add_space_encoding(get_name(i)) << " " + << add_space_encoding((*i)->aliased_to) << ";\n"; +#endif + } + } + else { + simple_print(out, pad, i, dereference); + } + } +} + +/** Print the attribute table in XML. + @param out Destination + @param pad Indent lines of text/xml this much. Default is four spaces. + @param constrained Not used + @deprecated */ +void AttrTable::print_xml(FILE *out, string pad, bool /*constrained*/) +{ + XMLWriter xml(pad); + print_xml_writer(xml); + fwrite(xml.get_doc(), sizeof(char), xml.get_doc_size(), out); + +#if OLD_XML_MOETHODS + ostringstream oss; + print_xml(oss, pad); + fwrite(oss.str().data(), 1, oss.str().length(), out); +#endif + +#if 0 + // Why this works: AttrTable is really a hacked class that used to + // implement a single-level set of attributes. Containers + // were added several years later by dropping in the 'entry' structure. + // It's not a class in its own right; instead accessors from AttrTable + // are used to access information from entry. So... the loop below + // actually iterates over the entries of *this* (which is an instance of + // AttrTable). A container is an entry whose sole value is an AttrTable + // instance. 05/19/03 jhrg + for (Attr_iter i = attr_begin(); i != attr_end(); ++i) { + if ((*i)->is_alias) { + fprintf(out, "%s\n", + pad.c_str(), id2xml(get_name(i)).c_str(), + (*i)->aliased_to.c_str()); + + } + else if (is_container(i)) { + fprintf(out, "%s\n", + pad.c_str(), id2xml(get_name(i)).c_str(), + get_type(i).c_str()); + + get_attr_table(i)->print_xml(out, pad + " "/*, constrained*/); + + fprintf(out, "%s\n", pad.c_str()); + } + else { + fprintf(out, "%s\n", + pad.c_str(), id2xml(get_name(i)).c_str(), get_type(i).c_str()); + + string value_pad = pad + " "; + // Special handling for the OtherXML attribute type - don't escape + // the XML and don't include the element. Note that there + // cannot be an vector of XML things as can be with the other types. + if (get_attr_type(i) == Attr_other_xml) { + if (get_attr_num(i) != 1) + throw Error("OtherXML attributes cannot be vector-valued."); + fprintf(out, "%s%s\n", value_pad.c_str(), get_attr(i, 0).c_str()); + } + else { + for (unsigned j = 0; j < get_attr_num(i); ++j) { + fprintf(out, "%s%s\n", value_pad.c_str(), + id2xml(get_attr(i, j)).c_str()); + } + } + fprintf(out, "%s\n", pad.c_str()); + } + } +#endif +} + +/** + * @deprecated + */ +void AttrTable::print_xml(ostream &out, string pad, bool /*constrained*/) +{ + XMLWriter xml(pad); + print_xml_writer(xml); + out << xml.get_doc(); + +#if 0 + for (Attr_iter i = attr_begin(); i != attr_end(); ++i) { + if ((*i)->is_alias) { + out << pad << "aliased_to << "\"/>\n"; + + } + else if (is_container(i)) { + out << pad << "\n"; + + get_attr_table(i)->print_xml(out, pad + " "/*, constrained*/); + + out << pad << "\n"; + } + else { + out << pad << "\n"; + + string value_pad = pad + " "; + if (get_attr_type(i) == Attr_other_xml) { + if (get_attr_num(i) != 1) + throw Error("OtherXML attributes cannot be vector-valued."); + out << value_pad << get_attr(i, 0) << "\n"; + } + else { + string value_pad = pad + " "; + for (unsigned j = 0; j < get_attr_num(i); ++j) { + out << value_pad << "" << id2xml(get_attr(i, j)) << "\n"; + } + } + out << pad << "\n"; + } + } +#endif +} + +/** Print the attribute table in XML. + @param out Destination stream + @param pad Indent lines of text/xml this much. Default is four spaces. + @param constrained Not used */ +void AttrTable::print_xml_writer(XMLWriter &xml) +{ + for (Attr_iter i = attr_begin(); i != attr_end(); ++i) { + if ((*i)->is_alias) { + if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*) "Alias") < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write Alias element"); + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "name", + (const xmlChar*) get_name(i).c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name"); + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "Attribute", + (const xmlChar*) (*i)->aliased_to.c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name"); + if (xmlTextWriterEndElement(xml.get_writer()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not end Alias element"); + } + else if (is_container(i)) { + if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*) "Attribute") < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write Attribute element"); + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "name", + (const xmlChar*) get_name(i).c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name"); + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "type", + (const xmlChar*) get_type(i).c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name"); + + get_attr_table(i)->print_xml_writer(xml); + + if (xmlTextWriterEndElement(xml.get_writer()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not end Attribute element"); + } + else { + if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*) "Attribute") < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write Attribute element"); + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "name", + (const xmlChar*) get_name(i).c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name"); + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "type", + (const xmlChar*) get_type(i).c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name"); + + if (get_attr_type(i) == Attr_other_xml) { + if (get_attr_num(i) != 1) + throw Error("OtherXML attributes cannot be vector-valued."); + // Replaced xmltextWriterWriteString with xmlTextWriterWriteRaw to keep the + // libxml2 code from escaping the xml (which was breaking all of the inferencing + // code. jhrg + if (xmlTextWriterWriteRaw(xml.get_writer(), (const xmlChar*) get_attr(i, 0).c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write OtherXML value"); + } + else { + for (unsigned j = 0; j < get_attr_num(i); ++j) { + if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*) "value") < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write value element"); + + if (xmlTextWriterWriteString(xml.get_writer(), (const xmlChar*) get_attr(i, j).c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute value"); + + if (xmlTextWriterEndElement(xml.get_writer()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not end value element"); + } + } + if (xmlTextWriterEndElement(xml.get_writer()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not end Attribute element"); + } + } +} + +/** Write the DAP4 XML representation for this attribute table. This + * method is used to build the DAP4 DMR response object. + * + * @param xml An XMLWriter that will do the serialization + */ +void +AttrTable::print_dap4(XMLWriter &xml) +{ + print_xml_writer(xml); +} + +/** @brief dumps information about this object + * + * Displays the pointer value of this instance and all attributes stored + * + * @param strm C++ i/o stream to dump the information to + * @return void + */ +void AttrTable::dump(ostream &strm) const +{ + strm << DapIndent::LMarg << "AttrTable::dump - (" << (void *) this << ")" << endl; + DapIndent::Indent(); + strm << DapIndent::LMarg << "table name: " << d_name << endl; + if (attr_map.size()) { + strm << DapIndent::LMarg << "attributes: " << endl; + DapIndent::Indent(); + Attr_citer i = attr_map.begin(); + Attr_citer ie = attr_map.end(); + for (; i != ie; ++i) { + entry *e = (*i); + string type = AttrType_to_String(e->type); + if (e->is_alias) { + strm << DapIndent::LMarg << "alias: " << e->name << " aliased to: " << e->aliased_to << endl; + } + else if (e->type == Attr_container) { + strm << DapIndent::LMarg << "attr: " << e->name << " of type " << type << endl; + DapIndent::Indent(); + e->attributes->dump(strm); + DapIndent::UnIndent(); + } + else { + strm << DapIndent::LMarg << "attr: " << e->name << " of type " << type << endl; + DapIndent::Indent(); + strm << DapIndent::LMarg; + vector::const_iterator iter = e->attr->begin(); + vector::const_iterator last = e->attr->end() - 1; + for (; iter != last; ++iter) { + strm << (*iter) << ", "; + } + strm << (*(e->attr->end() - 1)) << endl; + DapIndent::UnIndent(); + } + } + DapIndent::UnIndent(); + } + else { + strm << DapIndent::LMarg << "attributes: empty" << endl; + } + if (d_parent) { + strm << DapIndent::LMarg << "parent table:" << d_name << ":" << (void *) d_parent << endl; + } + else { + strm << DapIndent::LMarg << "parent table: none" << d_name << endl; + } + DapIndent::UnIndent(); +} + +} // namespace libdap + diff --git a/AttrTable.h b/AttrTable.h new file mode 100644 index 0000000..d1456cf --- /dev/null +++ b/AttrTable.h @@ -0,0 +1,353 @@ + +// -*- mode: c++; c-basic-offset:4 -*- + +// This file is part of libdap, A C++ implementation of the OPeNDAP Data +// Access Protocol. + +// Copyright (c) 2002,2003 OPeNDAP, Inc. +// Author: James Gallagher +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// +// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112. + +// (c) COPYRIGHT URI/MIT 1994-1999 +// Please read the full copyright statement in the file COPYRIGHT_URI. +// +// Authors: +// jhrg,jimg James Gallagher + +// An AttrTable is a table of attributes (type-name-value tuples). + +#ifndef _attrtable_h +#define _attrtable_h 1 + + +#include +#include + +#ifndef _error_h +#include "Error.h" +#endif + +using std::vector; +using std::string; +using std::vector; + +#ifndef A_DapObj_h +#include "DapObj.h" +#endif + +#ifndef XMLWRITER_H_ +#include "XMLWriter.h" +#endif + +namespace libdap +{ + +/** AttrType identifies the data types which may appear in an + attribute table object. + + \code + enum AttrType { + Attr_unknown, + Attr_container, + Attr_byte, + Attr_int16, + Attr_uint16, + Attr_int32, + Attr_uint32, + Attr_float32, + Attr_float64, + Attr_string, + Attr_url, + Attr_other_xml + }; + \endcode + + @see AttrTable */ +enum AttrType { + Attr_unknown, + Attr_container, + Attr_byte, + Attr_int16, + Attr_uint16, + Attr_int32, + Attr_uint32, + Attr_float32, + Attr_float64, + Attr_string, + Attr_url, + Attr_other_xml +}; + +string AttrType_to_String(const AttrType at); +AttrType String_to_AttrType(const string &s); + +/** An AttrTable (``Attribute Table'') stores a set of names and, for + each name, either a type and a value, or another attribute table. + The attribute value can be a vector containing many values of the + same type. The attributes can have any of the types listed in the + AttrType list. However, all attribute types are stored as + string data, except for the container type, which is stored as a + pointer to another attribute table. + + Each element in the attribute table can itself be an attribute + table. The table can also contain ``alias'' attributes whose + value is given by the value of another attribute to which it is + linked. + + The attribute tables have a standard printed representation. + There is a member function print() for writing this form. Use + the DAS::parse() function to read the printed form. + + An attribute table might look something like this: + + \verbatim + string long_name "Weekly Means of Sea Surface Temperature"; + actual_range { + Float64 min -1.8; + Float64 max 35.09; + } + string units "degC"; + conversion_data { + Float64 add_offset 0.; + Float64 scale_factor 0.0099999998; + } + Int32 missing_value 32767; + \endverbatim + + Here, long_name, units, and + missing_value are simple + attributes, and actual_range and conversion_data + are container attributes containing other attribute tables. + + @note This class is used only for DAP2. + + @brief Contains the attributes for a dataset. + @see DAS + @see AttrType */ +class AttrTable : public DapObj +{ + // entry needs to be made public to make up for issues with this class' + // design. It should probably be moved to it's own class. 05/22/03 jhrg +public: + /** Each AttrTable has zero or more entries. Instead of accessing this + struct's members directly, use AttrTable methods. + + This struct is public because its type is used in public typedefs. */ + struct entry + { + string name; + AttrType type; + + bool is_alias; + string aliased_to; + + bool is_global; // use this to mark non-container attributes. see below. + + // If type == Attr_container, use attributes to read the contained + // table, otherwise use attr to read the vector of values. + AttrTable *attributes; + std::vector *attr; // a vector of values. jhrg 12/5/94 + + entry(): name(""), type(Attr_unknown), is_alias(false), + aliased_to(""), is_global(true), attributes(0), attr(0) {} + + entry(const entry &rhs): name(rhs.name), type(rhs.type), is_alias(rhs.is_alias), + aliased_to(rhs.aliased_to), is_global(rhs.is_global),attributes(0), attr(0) + { + clone(rhs); + } + + void delete_entry() + { + if (is_alias) // alias copies the pointers. + return; + if (type == Attr_container) { + delete attributes; attributes = 0; + } + else { + delete attr; attr = 0; + } + } + + virtual ~entry() + { + delete_entry(); + } + + void clone(const entry &rhs) + { +#if 0 + name = rhs.name; + type = rhs.type; + is_alias = rhs.is_alias; + aliased_to = rhs.aliased_to; + is_global = rhs.is_global; +#endif + switch (rhs.type) { + case Attr_unknown: + break; + case Attr_container: { + if (rhs.is_alias) + attributes = rhs.attributes; + else + attributes = new AttrTable(*rhs.attributes); + break; + } + default: { + if (rhs.is_alias) + attr = rhs.attr; + else + attr = new std::vector(*rhs.attr); + break; + } + } + } + + entry &operator=(const entry &rhs) + { + if (this != &rhs) { + delete_entry(); + clone(rhs); + } + return *this; + } + }; + + typedef std::vector::const_iterator Attr_citer ; + typedef std::vector::iterator Attr_iter ; + +private: + string d_name; + AttrTable *d_parent; + std::vector attr_map; + + // Use this to mark container attributes. Look at the methods + // is_global_attribute() and set_is_...., esp. at the versions that take + // an iterator. This code is tricky because it has to track both whole + // containers that are global and individual attributes that are 'global' + // relative to a constructor. That is, there are some attributes that are + // bound to a container and not any of the container's children. + bool d_is_global_attribute; + + void delete_attr_table(); + + friend class AttrTableTest; + +protected: + void clone(const AttrTable &at); + + void simple_print(FILE *out, string pad, Attr_iter i, + bool dereference); + void simple_print(ostream &out, string pad, Attr_iter i, + bool dereference); + +public: + AttrTable(); + AttrTable(const AttrTable &rhs); + virtual ~AttrTable(); + AttrTable & operator=(const AttrTable &rhs); + + virtual void erase(); + + virtual unsigned int get_size() const; + virtual string get_name() const; + virtual void set_name(const string &n); + + /** Return a pointer to the AttrTable which holds this table (aka, its + parent. If this AttrTable has no parent, this returns null. + @return A pointer to the parent AttrTable. */ + virtual AttrTable *get_parent() const + { + return d_parent; + } + + virtual bool is_global_attribute() const { return d_is_global_attribute; } + virtual void set_is_global_attribute(bool ga) { d_is_global_attribute = ga; } + + virtual unsigned int append_attr(const string &name, const string &type, + const string &value); + virtual unsigned int append_attr(const string &name, const string &type, + vector *values); + + virtual AttrTable *append_container(const string &name); + virtual AttrTable *append_container(AttrTable *at, const string &name); + + virtual void find(const string &target, AttrTable **at, Attr_iter *iter); + virtual AttrTable *find_container(const string &target); + virtual AttrTable *recurrsive_find(const string &target, + Attr_iter *location); + + Attr_iter simple_find(const string &target); + AttrTable *simple_find_container(const string &target); + + + virtual AttrTable *get_attr_table(const string &name); + virtual string get_type(const string &name); + virtual AttrType get_attr_type(const string &name); + virtual unsigned int get_attr_num(const string &name); + virtual string get_attr(const string &name, unsigned int i = 0); + virtual vector *get_attr_vector(const string &name); + virtual void del_attr(const string &name, int i = -1); + + virtual Attr_iter attr_begin(); + virtual Attr_iter attr_end(); + virtual Attr_iter get_attr_iter(int i); + virtual string get_name(Attr_iter iter); + virtual bool is_container(Attr_iter iter); + virtual AttrTable *get_attr_table(Attr_iter iter); + virtual Attr_iter del_attr_table(Attr_iter iter); + virtual string get_type(Attr_iter iter); + virtual AttrType get_attr_type(Attr_iter iter); + virtual unsigned int get_attr_num(Attr_iter iter); + virtual string get_attr(Attr_iter iter, unsigned int i = 0); + virtual std::vector *get_attr_vector(Attr_iter iter); + virtual bool is_global_attribute(Attr_iter iter); + virtual void set_is_global_attribute(Attr_iter iter, bool ga); + + virtual void add_container_alias(const string &name, AttrTable *src); + virtual void add_value_alias(AttrTable *at, const string &name, + const string &source); + virtual bool attr_alias(const string &alias, + AttrTable *at, + const string &name); + virtual bool attr_alias(const string &alias, const string &name); + + virtual void print(FILE *out, string pad = " ", + bool dereference = false); + virtual void print(ostream &out, string pad = " ", + bool dereference = false); + + virtual void print_xml(FILE *out, string pad = " ", + bool constrained = false); + virtual void print_xml(ostream &out, string pad = " ", + bool constrained = false); + + void print_xml_writer(XMLWriter &xml); + + void print_dap4(XMLWriter &xml); + + virtual void dump(ostream &strm) const ; +}; + + +string remove_space_encoding(const string &s); +string add_space_encoding(const string &s); + +} // namespace libdap + +#endif // _attrtable_h diff --git a/BaseType.cc b/BaseType.cc new file mode 100644 index 0000000..2a74d6c --- /dev/null +++ b/BaseType.cc @@ -0,0 +1,1300 @@ + +// -*- mode: c++; c-basic-offset:4 -*- + +// This file is part of libdap, A C++ implementation of the OPeNDAP Data +// Access Protocol. + +// Copyright (c) 2002,2003 OPeNDAP, Inc. +// Author: James Gallagher +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// +// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112. + +// (c) COPYRIGHT URI/MIT 1994-1999 +// Please read the full copyright statement in the file COPYRIGHT_URI. +// +// Authors: +// jhrg,jimg James Gallagher + +// Implementation for BaseType. +// +// jhrg 9/6/94 + +#include "config.h" + +#include // for stdin and stdout + +#include +#include + +//#define DODS_DEBUG + +#include "BaseType.h" +#include "Byte.h" +#include "Int16.h" +#include "UInt16.h" +#include "Int32.h" +#include "UInt32.h" +#include "Float32.h" +#include "Float64.h" +#include "Str.h" +#include "Url.h" +#include "Array.h" +#include "Structure.h" +#include "Sequence.h" +#include "Grid.h" + +#include "D4Attributes.h" +#include "DMR.h" +#include "XMLWriter.h" +#include "D4BaseTypeFactory.h" + +#include "InternalErr.h" + +#include "util.h" +#include "escaping.h" + +#include "debug.h" + +using namespace std; + +namespace libdap { + +// Protected copy mfunc + +/** Perform a deep copy. Copies the values of \e bt into \c *this. Pointers + are dereferenced and their values are copied into a newly allocated + instance. + + @brief Perform a deep copy. + @param bt The source object. */ +void +BaseType::m_duplicate(const BaseType &bt) +{ + DBG(cerr << "In BaseType::m_duplicate for " << bt.name() << endl); + + d_name = bt.d_name; + d_type = bt.d_type; + d_dataset = bt.d_dataset; + d_is_read = bt.d_is_read; // added, reza + d_is_send = bt.d_is_send; // added, reza + d_in_selection = bt.d_in_selection; + d_is_synthesized = bt.d_is_synthesized; // 5/11/2001 jhrg + + d_parent = bt.d_parent; // copy pointers 6/4/2001 jhrg + + d_attr = bt.d_attr; // Deep copy. + + if (bt.d_attributes) + d_attributes = new D4Attributes(*bt.d_attributes); // deep copy + else + d_attributes = 0; // init to null if not used. + + d_is_dap4 = bt.d_is_dap4; + + DBG(cerr << "Exiting BaseType::m_duplicate for " << bt.name() << endl); +} + +// Public mfuncs + +/** The BaseType constructor needs a name and a type. + The BaseType class exists to provide data to + type classes that inherit from it. The constructors of those + classes call the BaseType constructor; it is never called + directly. + + @brief The BaseType constructor. + + @param n A string containing the name of the new variable. + @param t The type of the variable. + @param is_dap4 True if this is a DAP4 variable. Default is False + @see Type */ +BaseType::BaseType(const string &n, const Type &t, bool is_dap4) +: d_name(n), d_type(t), d_dataset(""), d_is_read(false), d_is_send(false), + d_parent(0), d_attributes(0), d_is_dap4(is_dap4), + d_in_selection(false), d_is_synthesized(false) +{} + +/** The BaseType constructor needs a name, a dataset, and a type. + The BaseType class exists to provide data to + type classes that inherit from it. The constructors of those + classes call the BaseType constructor; it is never called + directly. + + @brief The BaseType constructor. + @param n A string containing the name of the new variable. + @param d A string containing the dataset name. + @param t The type of the variable. Default is False + @param is_dap4 True if this is a DAP4 variable. + @see Type */ +BaseType::BaseType(const string &n, const string &d, const Type &t, bool is_dap4) +: d_name(n), d_type(t), d_dataset(d), d_is_read(false), d_is_send(false), + d_parent(0), d_attributes(0), d_is_dap4(is_dap4), + d_in_selection(false), d_is_synthesized(false) +{} + +/** @brief The BaseType copy constructor. */ +BaseType::BaseType(const BaseType ©_from) : DapObj() +{ + DBG(cerr << "In BaseTpe::copy_ctor for " << copy_from.name() << endl); + m_duplicate(copy_from); +} + +BaseType::~BaseType() +{ + DBG2(cerr << "Entering ~BaseType (" << this << ")" << endl); + + if (d_attributes) + delete d_attributes; + + DBG2(cerr << "Exiting ~BaseType" << endl); +} + +BaseType & +BaseType::operator=(const BaseType &rhs) +{ + DBG(cerr << "Entering BaseType::operator=" << endl); + if (this == &rhs) + return *this; + + m_duplicate(rhs); + + DBG(cerr << "Exiting BaseType::operator=" << endl); + return *this; +} + +/** Write out the object's internal fields in a string. To be used for + debugging when regular inspection w/ddd or gdb isn't enough. + + @return A string which shows the object's internal stuff. */ +string BaseType::toString() +{ + ostringstream oss; + oss << "BaseType (" << this << "):" << endl + << " _name: " << name() << endl + << " _type: " << type_name() << endl + << " _dataset: " << d_dataset << endl + << " _read_p: " << d_is_read << endl + << " _send_p: " << d_is_send << endl + << " _synthesized_p: " << d_is_synthesized << endl + << " d_parent: " << d_parent << endl + << " d_attr: " << hex << &d_attr << dec << endl; + + return oss.str(); +} + +/** @brief DAP2 to DAP4 transform + * + * For the current BaseType, return a DAP4 'copy' of the variable. + * + * @note For most DAP2 types, in this implementation of DAP4 the corresponding + * DAP4 type is the same. The different types are Sequences (which are D4Sequences + * in the DAP4 implementation), Grids (which are coverages) and Arrays (which use + * shared dimensions). + * + * @param root The root group that should hold this new variable. Add Group-level + * stuff here (e.g., D4Dimensions). + * @param container Add the new variable to this container. + * + * @return A pointer to the transformed variable + */ +void +BaseType::transform_to_dap4(D4Group */*root*/, Constructor *container) +{ + BaseType *dest = ptr_duplicate(); + // If it's already a DAP4 object then we can just return it! + if(!is_dap4()){ + dest->attributes()->transform_to_dap4(get_attr_table()); + dest->set_is_dap4(true); + } + container->add_var_nocopy(dest); +} + + +/** @brief DAP4 to DAP2 transform + * + * For the current BaseType, return a DAP2 'copy' of the variable. + * + * @note For most DAP4 types, in this implementation of DAP2 the corresponding + * DAP4 type is the same. + * These types have a different representations in DAP2 and DAP4: + * Sequences (which are D4Sequences in the DAP4 implementation), + * - Grids (which are semantically subsumed by coverages in DAP4) + * - Arrays (which use shared dimensions in DAP4) + * + * Additionally DAP4 adds the following types: + * - UInt8, Int8, and Char which map to Byte in DAP2. + * - Int64, Unit64 which have no natural representation in DAP2. + * - Opaque Possible Byte stuff[] plus metadata? + * - Enum's can be represented as Int32. + * + * - Groups, with the exception of the root group "disappear" into the + * names of their member variables. Specifically the Group name is add as a prefix + * followed by a "/" separator to the names of all of the Group's member groups + * variables. + * + * @param The AttrTable pointer parent_attr_table is used by Groups, which disappear + * from the DAP2 representation. Their children are returned in the the BAseType vector + * their attributes are added to parent_attr_table; + * @return A pointer to a vector of BaseType pointers (right?). In most cases this vector + * will contain a single pointer but DAP4 types 'disappear' such as Group will return all + * of their member variables in the vector. DAP4 types with no representation in DAP2 + * (ex: UInt64) the will return a NULL pointer and so this must be tested! + */ +std::vector * +BaseType::transform_to_dap2(AttrTable *) +{ + BaseType *dest = this->ptr_duplicate(); + // convert the d4 attributes to a dap2 attribute table. + AttrTable *attrs = this->attributes()->get_AttrTable(name()); + dest->set_attr_table(*attrs); + dest->set_is_dap4(false); + // attrs->print(cerr,"",true); + + vector *result = new vector(); + result->push_back(dest); + return result; +} + + +/** @brief dumps information about this object + * + * Displays the pointer value of this instance and then displays information + * about this base type. + * + * @param strm C++ i/o stream to dump the information to + * @return void + */ +void +BaseType::dump(ostream &strm) const +{ + strm << DapIndent::LMarg << "BaseType::dump - (" + << (void *)this << ")" << endl ; + DapIndent::Indent() ; + + strm << DapIndent::LMarg << "name: " << name() << endl ; + strm << DapIndent::LMarg << "type: " << type_name() << endl ; + strm << DapIndent::LMarg << "dataset: " << d_dataset << endl ; + strm << DapIndent::LMarg << "read_p: " << d_is_read << endl ; + strm << DapIndent::LMarg << "send_p: " << d_is_send << endl ; + strm << DapIndent::LMarg << "synthesized_p: " << d_is_synthesized << endl ; + strm << DapIndent::LMarg << "parent: " << (void *)d_parent << endl ; + strm << DapIndent::LMarg << "attributes: " << endl ; + DapIndent::Indent() ; + + if (d_attributes) + d_attributes->dump(strm); + else + d_attr.dump(strm) ; + + DapIndent::UnIndent() ; + + DapIndent::UnIndent() ; +} + +/** @brief Returns the name of the class instance. + */ +string +BaseType::name() const +{ + return d_name; +} + +/** + * Return the FQN for this variable. This will include the D4 Group + * component of the name. + * + * @return The FQN in a string + */ +string +BaseType::FQN() const +{ + if (get_parent() == 0) + return name(); + else if (get_parent()->type() == dods_group_c) + return get_parent()->FQN() + name(); + else + return get_parent()->FQN() + "." + name(); +} + +/** @brief Sets the name of the class instance. */ +void +BaseType::set_name(const string &n) +{ + string name = n; + d_name = www2id(name); // www2id writes into its param. +} + +/** @brief Returns the name of the dataset used to create this instance + + A dataset from which the data is to be read. The meaning of this string + will vary among different types of data sources. It \e may be the name + of a data file or an identifier used to read data from a relational + database. + */ +string +BaseType::dataset() const +{ + return d_dataset; +} + +/** @brief Returns the type of the class instance. */ +Type +BaseType::type() const +{ + return d_type; +} + +/** @brief Sets the type of the class instance. */ +void +BaseType::set_type(const Type &t) +{ + d_type = t; +} + +/** @brief Returns the type of the class instance as a string. */ +string +BaseType::type_name() const +{ + if (is_dap4()) + return libdap::D4type_name(d_type); + else + return libdap::D2type_name(d_type); +} + +/** @brief Returns true if the instance is a numeric, string or URL + type variable. + @return True if the instance is a scalar numeric, String or URL variable, + False otherwise. Arrays (even of simple types) return False. + @see is_vector_type() */ +bool +BaseType::is_simple_type() const +{ + return libdap::is_simple_type(type()); +} + +/** @brief Returns true if the instance is a vector (i.e., array) type + variable. + @return True if the instance is an Array, False otherwise. */ +bool +BaseType::is_vector_type() const +{ + return libdap::is_vector_type(type()); +} + +/** @brief Returns true if the instance is a constructor (i.e., Structure, + Sequence or Grid) type variable. + @return True if the instance is a Structure, Sequence or Grid, False + otherwise. */ +bool +BaseType::is_constructor_type() const +{ + return libdap::is_constructor_type(type()); +} + +/** Return a count of the total number of variables in this variable. + This is used to count the number of variables held by a constructor + variable - for simple type and vector variables it always + returns 1. + + For compound data types, there are two ways to count members. + You can count the members, or you can count the simple members + and add that to the count of the compound members. For + example, if a Structure contains an Int32 and another + Structure that itself contains two Int32 members, the element + count of the top-level structure could be two (one Int32 and + one Structure) or three (one Int32 by itself and two Int32's + in the subsidiary Structure). Use the leaves parameter + to control which kind of counting you desire. + + @brief Count the members of constructor types. + @return Returns 1 for simple + types. For compound members, the count depends on the + leaves argument. + @param leaves This parameter is only relevant if the object + contains other compound data types. If FALSE, the function + counts only the data variables mentioned in the object's + declaration. If TRUE, it counts the simple members, and adds + that to the sum of the counts for the compound members. + This parameter has no effect for simple type variables. */ +int +BaseType::element_count(bool) +{ + return 1; +} + +/** Returns true if the variable is a synthesized variable. A synthesized + variable is one that is added to the dataset by the server (usually + with a `projection function'. */ +bool +BaseType::synthesized_p() +{ + return d_is_synthesized; +} + +/** Set the synthesized flag. Before setting this flag be sure to set the + read_p() state. Once this flag is set you cannot + alter the state of the read_p flag! + + @see synthesized_p() */ +void +BaseType::set_synthesized_p(bool state) +{ + d_is_synthesized = state; +} + +// Return the state of d_is_read (true if the value of the variable has been +// read (and is in memory) false otherwise). + +/** Returns true if the value(s) for this variable have been read from the + data source, otherwise returns false. This method is used to determine + when values need to be read using the read() method. When read_p() + returns true, this library assumes that buf2val() (and other methods + such as get_vec()) can be used to access the value(s) of a variable. + + @brief Has this variable been read? + @return True if the variable's value(s) have been read, false otherwise. */ +bool +BaseType::read_p() +{ + return d_is_read; +} + +/** Sets the value of the read_p property. This indicates that the + value(s) of this variable has/have been read. An implementation of the + read() method should use this to set the \c read_p property to true. + + @note If the is_synthesized property is true, this method will _not_ + alter the is_read property. If you need that behavior, specialize the + method in your subclasses if the various types. + + @note For most of the types the default implementation of this method is + fine. However, if you're building a server which must handle data + represented using nested sequences, then you may need to provide a + specialization of Sequence::set_read_p(). By default Sequence::set_read_p() + recursively sets the \e read_p property for all child variables to + \e state. For servers where one Sequence reads an outer set of values + and another reads an inner set, this is cumbersome. In such a case, it is + easier to specialize Sequence::set_read_p() so that it does \e not + recursively set the \e read_p property for the inner Sequence. Be sure + to see the documentation for the read() method! + + @todo Look at making synthesized variables easier to implement and at + making them more integrated into the overall CE evaluation process. + Maybe the code that computes the synthesized var's value should be in the + that variable's read() method? This might provide a way to get rid of the + awkward 'projection functions' by replacing them with real children of + BaseType. It would also provide a way to clean up the way the + \e synthesized_p prop intrudes on the \e read_p prop. + + @see BaseType::read() + @brief Sets the value of the \e read_p property. + @param state Set the \e read_p property to this state. */ +void +BaseType::set_read_p(bool state) +{ + // The this comment is/was wrong! + // The is_synthesized property was not being used and the more I thought + // about how this was coded, the more this code below seemed like a bad idea. + // Once the property was set, the read_p property could not be changed. + // That seems a little silly. Also, I think I need to use this is_synthesized + // property for some of the server function code I'm working on for Raytheon, + // and I'd like to be able to control the read_p property! jhrg 3/9/15 + + // What's true: The is_synthesized property is used by + // 'projection functions' in the freeform handler. It might be better + // to modify the FFtypes to support this behavior, but for now I'm returning + // the library to its old behavior. That this change (setting is_read + // of the value of is_syn...) broke the FF handler was not detected + // because the FF tests were not being run due to an error in the FF + // bes-testsuite Makefile.am). jhrg 9/9/15 + +#if 1 + if (!d_is_synthesized) { + d_is_read = state; + } +#else + d_is_read = state; +#endif +} + +/** Returns the state of the \c send_p property. If true, this variable + should be sent to the client, if false, it should not. If no constraint + expression (CE) has been evaluated, this property is true for all + variables in a data source (i.e., for all the variables listed in a DDS). + If a CE has been evaluated, this property is true only for those + variables listed in the projection part of the CE. + + @brief Should this variable be sent? + @return True if the variable should be sent to the client, false + otherwise. */ +bool +BaseType::send_p() +{ + return d_is_send; +} + +/** Sets the value of the send_p flag. This + function is meant to be called from within the constraint evaluator of + other code which determines that this variable should be returned to the + client. Data are ready to be sent when both the d_is_send + and d_is_read flags are set to TRUE. + + @param state The logical state to set the send_p flag. + */ +void +BaseType::set_send_p(bool state) +{ + DBG2(cerr << "Calling BaseType::set_send_p() for: " << this->name() + << endl); + d_is_send = state; +} + + +/** Get this variable's AttrTable. It's generally a bad idea to return a + reference to a contained object, but in this case it seems that building + an interface inside BaseType is overkill. + + Use the AttrTable methods to manipulate the table. */ +AttrTable & +BaseType::get_attr_table() +{ + return d_attr; +} + +/** Set this variable's attribute table. + @param at Source of the attributes. */ +void +BaseType::set_attr_table(const AttrTable &at) +{ + d_attr = at; +} + +/** DAP4 Attribute methods + * @{ + */ +D4Attributes * +BaseType::attributes() +{ + if (!d_attributes) d_attributes = new D4Attributes(); + return d_attributes; +} + +void +BaseType::set_attributes(D4Attributes *attrs) +{ + d_attributes = new D4Attributes(*attrs); +} + +void +BaseType::set_attributes_nocopy(D4Attributes *attrs) +{ + d_attributes = attrs; +} +///@} + +/** + * Transfer attributes from a DAS object into this variable. Because of the + * rough history of the DAS object and the way that various server code built + * the DAS, this is necessarily a heuristic process. The intent is that this + * method will be overridden by handlers that need to look for certain patterns + * in the DAS (e.g., hdf4's odd variable_dim_n; where n = 0, 1, 2, ...) + * attribute containers. + * + * There should be a one-to-one + * mapping between variables and attribute containers. However, in some cases + * one variable has attributes spread across several top level containers and + * in some cases one container is used by several variables + * + * @note This method is technically \e unnecessary because a server (or + * client) can easily add attributes directly using the DDS::get_attr_table + * or BaseType::get_attr_table methods and then poke values in using any + * of the methods AttrTable provides. This method exists to ease the + * transition to DDS objects which contain attribute information for the + * existing servers (Since they all make DAS objects separately from the + * DDS). They could be modified to use the same AttrTable methods but + * operate on the AttrTable instances in a DDS/BaseType instead of those in + * a DAS. + * + * @param at_container Transfer attributes from this container. + * @return void + */ +void BaseType::transfer_attributes(AttrTable *at_container) { + + DBG(cerr << __func__ << "() - BEGIN name:'" << name() << "'" << endl); + + AttrTable *at = at_container->get_attr_table(name()); + DBG(cerr << __func__ << "() - at: "<< (void *) at << endl); + + + if (at) { + at->set_is_global_attribute(false); + DBG(cerr << __func__ << "() - Processing AttrTable: " << at->get_name() << endl); + + AttrTable::Attr_iter at_p = at->attr_begin(); + while (at_p != at->attr_end()) { + DBG(cerr << __func__ << "() - Attribute '" << at->get_name(at_p) << "' is type: " << at->get_type(at_p) << endl); + if (at->get_attr_type(at_p) == Attr_container){ + // An attribute container may actually represent a child member variable. When + // that's the case we don't want to add the container to the parent type, but + // rather let any child of BaseType deal with those containers in the child's + // overridden transfer_attributes() method. + // We capitalize on the magic of the BaseType API and utilize the var() method + // to check for a child variable of the same name and, if one exists, we'll skip + // this AttrTable and let a child constructor class like Grid or Constructor + // deal with it. + BaseType *bt = var(at->get_name(at_p),true); + if(bt==0){ + DBG(cerr << __func__ << "() - Adding container '" << at->get_name(at_p) << endl); + get_attr_table().append_container(new AttrTable(*at->get_attr_table(at_p)), at->get_name(at_p)); + } + else { + DBG(cerr << __func__ << "() - Found child var: '"<< bt->type_name()<< " " << bt->name() << " (address:" << (void *) bt << ")" << endl); + DBG(cerr << __func__ << "() - Skipping container '" << at->get_name(at_p) << endl); + } + } + else { + DBG(cerr << __func__ << "() - Adding Attribute '" << at->get_name(at_p) << endl); + get_attr_table().append_attr(at->get_name(at_p), at->get_type(at_p), at->get_attr_vector(at_p)); + } + at_p++; + } + } + else { + DBG(cerr << __func__ << "() - Unable to locate AttrTable '" << name() << "' SKIPPING" << endl); + + } +} + +/** Does this variable appear in either the selection part or as a function + argument in the current constrain expression. If this property is set + (true) then implementations of the read() method should read this + variable. + + @note This method does not check, nor does it know about the semantics of, + string arguments passed to functions. Those functions might include + variable names in strings; they are responsible for reading those variables. + See the grid (func_grid_select()) for an example. + @see BaseType::read() + @brief Is this variable part of the current selection? */ +bool +BaseType::is_in_selection() +{ + return d_in_selection; +} + +/** Set the \e in_selection property to \e state. This property indicates + that the variable is used as a parameter to a constraint expression + function or that it appears as an argument in a selection sub-expression. + If set (true), implementations of the BaseType::read() method should read + this variable. + + @param state Set the \e in_selection property to this state. + @see BaseType::read() + @see BaseType::is_in_selection() for more information. */ +void +BaseType::set_in_selection(bool state) +{ + d_in_selection = state; +} + +// Protected method. +/** Set the parent property for this variable. + + @note Added ability to set parent to null. 10/19/12 jhrg + + @param parent Pointer to the Constructor of Vector parent variable or null + if the variable has no parent (if it is at the top-level of a DAP2/3 DDS). + @exception InternalErr thrown if called with anything other than a + Constructor, Vector or Null. */ +void +BaseType::set_parent(BaseType *parent) +{ + if (!dynamic_cast(parent) + && !dynamic_cast(parent) + && parent != 0) + throw InternalErr("Call to set_parent with incorrect variable type."); + + d_parent = parent; +} + +// Public method. + +/** Return a pointer to the Constructor or Vector which holds (contains) + this variable. If this variable is at the top level, this method + returns null. + + @return A BaseType pointer to the variable's parent. */ +BaseType * +BaseType::get_parent() const +{ + return d_parent; +} + +// Documented in the header file. +BaseType * +BaseType::var(const string &/*name*/, bool /*exact_match*/, btp_stack */*s*/) +{ + return static_cast(0); +} + +/** This version of var(...) searches for name and returns a + pointer to the BaseType object if found. It uses the same search + algorithm as BaseType::var(const string &, bool, btp_stack *) when + exact_match is false. In addition to returning a pointer to + the variable, it pushes onto s a BaseType pointer to each + constructor type that ultimately contains name. + + @note The BaseType implementation always returns null. There are no default + values for the parameters. If var() is called w/o any params, the three + parameter version will be used. + + @deprecated This method is deprecated because it tries first to use + exact_match and, if that fails, then tries leaf_match. It's better to use + the alternate form of var(...) and specify exactly what you'd like to do. + + @return A pointer to the named variable. */ +BaseType * +BaseType::var(const string &, btp_stack &) +{ + return static_cast(0); +} + +/** Adds a variable to an instance of a constructor class, such as Array, + Structure et cetera. This function is only used by those + classes. For constructors with more than one variable, the variables + appear in the same order in which they were added (i.e., the order in + which add_var() was called). Since this method is only for use by Vectors + and Constructors, the BaseType implementation throws InternalErr. + + @note For the implementation of this method in Structure, Sequence, et c., + first copy \e bt and then insert the copy. If \e bt is itself a constructor + type you must either use the var() method to get a pointer to the actual + instance added to \c *this or you must first add all of bt's + children to it before adding it to \c *this. The implementations should use + m_duplicate() to perform a deep copy of \e bt. + + @brief Add a variable. + + @todo We should get rid of the Part parameter and adopt the convention + that the first variable is the Array and all subsequent ones are Maps + (when dealing with a Grid, the only time Part matters). This would enable + several methods to migrate from Structure, Sequence and Grid to + Constructor. + + @param bt The variable to be added to this instance. The caller of this + method must free memory it allocates for v. This method + will make a deep copy of the object pointed to by v. + @param part The part of the constructor data to be modified. Only + meaningful for Grid variables. + + @see Part */ +void +BaseType::add_var(BaseType *, Part) +{ + throw InternalErr(__FILE__, __LINE__, "BaseType::add_var unimplemented"); +} + +void +BaseType::add_var_nocopy(BaseType *, Part) +{ + throw InternalErr(__FILE__, __LINE__, "BaseType::add_var_nocopy unimplemented"); +} + +/** This method should be implemented for each of the data type classes (Byte, + ..., Grid) when using the DAP class library to build a server. This + method is only for DAP servers. The library provides a default + definition here which throws an InternalErr exception \e unless the read_p + property has been set. In that case it returns false, indicating that all + the data have been read. The latter case can happen when building a + constant value that needs to be passed to a function. The variable/constant + is loaded with a value when it is created. + + When implementing a new DAP server, the Byte, ..., Grid data type classes + are usually specialized. In each of those specializations read() should + be defined to read values from the data source and store them in the + object's local buffer. The read() method is called by other methods in + this library. When writing read(), follow these rules: + +
    +
  • read() should throw Error if it encounters an error. The message + should be verbose enough to be understood by someone running a + client on a different machine.
  • +
  • The value(s) should be read if and only if either send_p() or + is_in_selection() return true. If neither of these return true, the + value(s) should not be read. This is important when writing read() + for a Constructor type such as Grid where a client may ask for only + the map vectors (and thus reading the much larger Array part is not + needed).
  • +
  • For each specialization of read(), the method should first test + the value of the \c read_p property (using the read_p() method) + and read values only if the value of read_p() is false. Once the + read() method reads data and stores it in the instance, it must + set the value of the \c read_p property to true using set_read_p(). + If your read() methods fail to do this data may not serialize + correctly.
  • +
  • The Array::read() and Grid::read() methods should take into account + any restrictions on Array sizes.
  • +
  • If you are writing Sequence::read(), be sure to check the + documentation for Sequence::read_row() and Sequence::serialize() + so you understand how Sequence::read() is being called.
  • +
  • For Sequence::read(), your specialization must correctly manage the + \c unsent_data property and row count in addition to the \c read_p + property (handle the \c read_p property as describe above). For a + Sequence to serialize correctly, once all data from the Sequence + has been read, \c unsent_data property must be set to false (use + Sequence::set_unsent_data()). Also, at that time the row number + counter must be reset (use Sequence::reset_row_counter()). Typically + the correct time to set \c unsent_data to false and reset the row + counter is the time when Sequence::read() return false indicating + that all the data for the Sequence have been read. Failure to + handle these tasks will break serialization of nested Sequences. Note + that when Sequence::read() returns with a result of true (indicating + there is more data to send, the value of the \c unsent_data property + should be true. + + Also, if you server must handle nested sequences, be sure to read + about subclassing set_read_p().
  • +
+ + @brief Read data into a local buffer. + + @todo Modify the D4 serialize code so that it supports the true/false + behavior of read() for arrays. + + @todo Modify all of the stock handlers so they conform to this! + + @return False means more data remains to be read, True indicates that no + more data need to be read. For Sequence and D4Sequence, this method will + generally read one instance of the Sequence; for other types it will generally + read the entire variable modulo any limitations due to a constraint. However, + the library should be written so that read can return less than all of the data + for a variable - serialize() would then call the function until it returns + True. + + @see BaseType */ +bool +BaseType::read() +{ + if (d_is_read) + return true; + + throw InternalErr("Unimplemented BaseType::read() method called for the variable named: " + name()); +} + +void +BaseType::intern_data(ConstraintEvaluator &, DDS &/*dds*/) +{ +#if USE_LOCAL_TIMEOUT_SCHEME + dds.timeout_on(); +#endif + DBG2(cerr << "BaseType::intern_data: " << name() << endl); + if (!read_p()) + read(); // read() throws Error and InternalErr +#if USE_LOCAL_TIMEOUT_SCHEME + dds.timeout_off(); +#endif +} + +/** + * @brief Read data into this variable + * @param eval Evaluator for a constraint expression + * @param dmr DMR for the whole dataset + */ +void +BaseType::intern_data(/*Crc32 &checksum, DMR &, ConstraintEvaluator &*/) +{ + if (!read_p()) + read(); // read() throws Error and InternalErr +#if 0 + compute_checksum(checksum); +#endif +} + +bool +BaseType::serialize(ConstraintEvaluator &, DDS &, Marshaller &, bool) +{ + throw InternalErr(__FILE__, __LINE__, "The DAP2 serialize() method has not been implemented for " + type_name()); +} + +bool +BaseType::deserialize(UnMarshaller &, DDS *, bool) +{ + throw InternalErr(__FILE__, __LINE__, "The DAP2 deserialize() method has not been implemented for " + type_name()); +} + +void +BaseType::serialize(D4StreamMarshaller &, DMR &, /*ConstraintEvaluator &,*/ bool) +{ + throw InternalErr(__FILE__, __LINE__, "The DAP4 serialize() method has not been implemented for " + type_name()); +} + +void +BaseType::deserialize(D4StreamUnMarshaller &, DMR &) +{ + throw InternalErr(__FILE__, __LINE__, "The DAP4 deserialize() method has not been implemented for " + type_name()); +} + +/** Write the variable's declaration in a C-style syntax. This + function is used to create textual representation of the Data + Descriptor Structure (DDS). See The DODS User Manual for + information about this structure. + + A simple array declaration might look like this: + \verbatim + Float64 lat[lat = 180]; + \endverbatim + While a more complex declaration (for a Grid, in this case), + would look like this: + \verbatim + Grid { + ARRAY: + Int32 sst[time = 404][lat = 180][lon = 360]; + MAPS: + Float64 time[time = 404]; + Float64 lat[lat = 180]; + Float64 lon[lon = 360]; + } sst; + \endverbatim + + @brief Print an ASCII representation of the variable structure. + @param out The output stream on which to print the + declaration. + @param space Each line of the declaration will begin with the + characters in this string. Usually used for leading spaces. + @param print_semi A boolean value indicating whether to print a + semicolon at the end of the declaration. + @param constraint_info A boolean value indicating whether + constraint information is to be printed with the declaration. + If the value of this parameter is TRUE, print_decl() prints + the value of the variable's send_p() flag after the + declaration. + @param constrained If this boolean value is TRUE, the variable's + declaration is only printed if is the send_p() flag is TRUE. + If a constraint expression is in place, and this variable is not + requested, the send_p() flag is FALSE. + + @see DDS + @see DDS::CE + */ +void +BaseType::print_decl(FILE *out, string space, bool print_semi, + bool constraint_info, bool constrained) +{ + ostringstream oss; + print_decl(oss, space, print_semi, constraint_info, constrained); + fwrite(oss.str().data(), sizeof(char), oss.str().length(), out); +} + +/** Write the variable's declaration in a C-style syntax. This + function is used to create textual representation of the Data + Descriptor Structure (DDS). See The DODS User Manual for + information about this structure. + + A simple array declaration might look like this: + \verbatim + Float64 lat[lat = 180]; + \endverbatim + While a more complex declaration (for a Grid, in this case), + would look like this: + \verbatim + Grid { + ARRAY: + Int32 sst[time = 404][lat = 180][lon = 360]; + MAPS: + Float64 time[time = 404]; + Float64 lat[lat = 180]; + Float64 lon[lon = 360]; + } sst; + \endverbatim + + @brief Print an ASCII representation of the variable structure. + @param out The output stream on which to print the + declaration. + @param space Each line of the declaration will begin with the + characters in this string. Usually used for leading spaces. + @param print_semi A boolean value indicating whether to print a + semicolon at the end of the declaration. + @param constraint_info A boolean value indicating whether + constraint information is to be printed with the declaration. + If the value of this parameter is TRUE, print_decl() prints + the value of the variable's send_p() flag after the + declaration. + @param constrained If this boolean value is TRUE, the variable's + declaration is only printed if is the send_p() flag is TRUE. + If a constraint expression is in place, and this variable is not + requested, the send_p() flag is FALSE. + + @see DDS + @see DDS::CE + */ +void +BaseType::print_decl(ostream &out, string space, bool print_semi, + bool constraint_info, bool constrained) +{ + // if printing the constrained declaration, exit if this variable was not + // selected. + if (constrained && !send_p()) + return; + + out << space << type_name() << " " << id2www(name()) ; + + if (constraint_info) { + if (send_p()) + out << ": Send True" ; + else + out << ": Send False" ; + } + + if (print_semi) + out << ";\n" ; +} + +/** Prints the value of the variable, with its declaration. This +function is primarily intended for debugging DODS +applications. However, it can be overloaded and used to do +some useful things. Take a look at the asciival and writeval +clients, both of which overload this to output the values of +variables in different ways. + +@brief Prints the value of the variable. + +@param out The output stream on which to print the value. +@param space This value is passed to the print_decl() +function, and controls the leading spaces of the output. +@param print_decl_p A boolean value controlling whether the +variable declaration is printed as well as the value. */ +void +BaseType::print_val(FILE *out, string space, bool print_decl_p) +{ + ostringstream oss; + print_val(oss, space, print_decl_p); + fwrite(oss.str().data(), sizeof(char), oss.str().length(), out); +} + +/** Write the XML representation of this variable. This method is used to + build the DDX XML response. + @param out Destination. + @param space Use this to indent child declarations. Default is "". + @param constrained If true, only print this if it's part part of the + current projection. Default is False. + @deprecated */ +void +BaseType::print_xml(FILE *out, string space, bool constrained) +{ + XMLWriter xml(space); + print_xml_writer(xml, constrained); + fwrite(xml.get_doc(), sizeof(char), xml.get_doc_size(), out); +} + +/** Write the XML representation of this variable. This method is used to + build the DDX XML response. + @param out Destination output stream + @param space Use this to indent child declarations. Default is "". + @param constrained If true, only print this if it's part part of the + current projection. Default is False. + @deprecated */ +void +BaseType::print_xml(ostream &out, string space, bool constrained) +{ + XMLWriter xml(space); + print_xml_writer(xml, constrained); + out << xml.get_doc(); +} + +/** Write the XML representation of this variable. This method is used to + build the DDX XML response. + @param out Destination output stream + @param space Use this to indent child declarations. Default is "". + @param constrained If true, only print this if it's part part of the + current projection. Default is False. */ +void +BaseType::print_xml_writer(XMLWriter &xml, bool constrained) +{ + if (constrained && !send_p()) + return; + + if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*)type_name().c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write " + type_name() + " element"); + + if (!name().empty()) + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "name", (const xmlChar*)name().c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name"); + + if (is_dap4()) + attributes()->print_dap4(xml); + + if (!is_dap4() && get_attr_table().get_size() > 0) + get_attr_table().print_xml_writer(xml); + + if (xmlTextWriterEndElement(xml.get_writer()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not end " + type_name() + " element"); +} + +/** Write the DAP4 XML representation for this variable. This method is used + * to build the DAP4 DMR response object. + * + * @param xml An XMLWriter that will do the serialization + * @param constrained True if the response should show the variables subject + * to the current constraint expression. + */ +void +BaseType::print_dap4(XMLWriter &xml, bool constrained) +{ + print_xml_writer(xml, constrained); +} + +// Compares the object's current state with the semantics of a particular +// type. This will typically be defined in ctor classes (which have +// complicated semantics). For BaseType, an object is semantically correct if +// it has both a non-null name and type. +// +// NB: This is not the same as an invariant -- during the parse objects exist +// but have no name. Also, the bool ALL defaults to false for BaseType. It is +// used by children of CtorType. +// +// Returns: true if the object is semantically correct, false otherwise. + +/** This function checks the class instance for internal + consistency. This is important to check for complex constructor + classes. For BaseType, an object is semantically correct if it + has both a non-null name and type. + + For example, an Int32 instance would return FALSE if it had no + name or no type defined. A Grid instance might return FALSE for + more complex reasons, such as having Map arrays of the wrong + size or shape. + + This function is used by the DDS class, and will rarely, if + ever, be explicitly called by a DODS application program. A + variable must pass this test before it is sent, but there may be + many other stages in a retrieve operation where it would fail. + + @brief Compare an object's current state with the semantics of its + type. + @return Returns FALSE when the current state violates some + aspect of the type semantics, TRUE otherwise. + + @param msg A returned string, containing a message indicating + the source of any problem. + @param all For complex constructor types (Grid, + Sequence, Structure), this flag indicates whether to check the + semantics of the member variables, too. + + @see DDS::check_semantics + */ +bool +BaseType::check_semantics(string &msg, bool) +{ + bool sem = (d_type != dods_null_c && name().length()); + + if (!sem) + msg = "Every variable must have both a name and a type\n"; + + return sem; +} + +/** This method contains the relational operators used by the constraint + expression evaluator in the DDS class. Each class that wants to be able + to evaluate relational expressions must overload this function. The + implementation in BaseType throws an InternalErr exception. The DAP + library classes Byte, ..., Url provide specializations of this method. It + is not meaningful for classes such as Array because relational + expressions using Array are not supported. + + The op argument refers to a table generated by bison from + the constraint expression parser. Use statements like the + following to correctly interpret its value: + + \verbatim + switch (op) { + case EQUAL: return i1 == i2; + case NOT_EQUAL: return i1 != i2; + case GREATER: return i1 > i2; + case GREATER_EQL: return i1 >= i2; + case LESS: return i1 < i2; + case LESS_EQL: return i1 <= i2; + case REGEXP: throw Error("Regular expressions are not supported for integer values"); + default: throw Error("Unknown operator"); + } + \endverbatim + + This function is used by the constraint expression evaluator. + + @brief Evaluate relational operators. + @param b Compare the value of this instance with \e b. + @param op An integer index indicating which relational operator + is implied. Choose one from the following: EQUAL, + NOT_EQUAL, GREATER, GREATER_EQL, + LESS, LESS_EQL, and REGEXP. + @return The boolean value of the comparison. + @see BaseType::d4_ops(BaseType *, int) + */ +bool +BaseType::ops(BaseType *, int) +{ + // Even though ops is a public method, it can never be called because + // they will never have a BaseType object since this class is abstract, + // however any of the child classes could by mistake call BaseType::ops + // so this is an internal error. Jose Garcia + throw InternalErr(__FILE__, __LINE__, "Unimplemented operator."); +} + +/** + * @brief Evaluator a relop for DAP4 + * + * This method is used by the filter expression evaluation code in DAP4. + * Each of the 'data type' classes that support relops must overload this + * method. In an expression of the form arg1 op arg2, this object is arg1, + * the parameter 'b' is arg2 and op is the relational operator. + * + * @note I used the same relop codes for DAP4 as in the DAP2 parser/scanner + * which makes for some coupling between them, but cuts way down on the + * duplication of the evaluator logic, which is somewhat involved. + * + * @param b The second argument in the relational expression + * @param op The infix relational operator + * @return True if the expression is true, False otherwise. + */ +bool +BaseType::d4_ops(BaseType *, int) +{ + throw InternalErr(__FILE__, __LINE__, "Unimplemented operator."); +} + +/** + * @brief How many bytes does this use + * Return the number of bytes of storage this variable uses. For scalar types, + * this is pretty simple (an int32 uses 4 bytes, etc.). For arrays and Constructors, + * it is a bit more complex. Note that a scalar String variable uses sizeof(String*) + * bytes, not the length of the string. In other words, the value returned is + * independent of the type. Also note width() of a String array returns the number of + * elements in the array times sizeof(String*). That is, each different array size + * is a different data type. + * + * @param constrained Should the current constraint be taken into account? + * @return Bytes of storage + */ +unsigned int +BaseType::width(bool /* constrained */) const +{ + throw InternalErr(__FILE__, __LINE__, "not implemented"); +#if 0 + return width(constrained); +#endif +} + +} // namespace libdap diff --git a/BaseType.h b/BaseType.h new file mode 100644 index 0000000..7338eac --- /dev/null +++ b/BaseType.h @@ -0,0 +1,594 @@ + +// -*- mode: c++; c-basic-offset:4 -*- + +// This file is part of libdap, A C++ implementation of the OPeNDAP Data +// Access Protocol. + +// Copyright (c) 2002,2003 OPeNDAP, Inc. +// Author: James Gallagher +// Dan Holloway +// Reza Nekovei +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// +// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112. + +// (c) COPYRIGHT URI/MIT 1994-1999 +// Please read the full copyright statement in the file COPYRIGHT_URI. +// +// Authors: +// jhrg,jimg James Gallagher +// dan Dan Holloway +// reza Reza Nekovei + +// Abstract base class for the variables in a dataset. This is used to store +// the type-invariant information that describes a variable as given in the +// DODS API. +// +// jhrg 9/6/94 + +#ifndef _basetype_h +#define _basetype_h 1 + +#include +#include +#include +#include + +#include "AttrTable.h" + +#include "InternalErr.h" + +#include "dods-datatypes.h" +#include "Type.h" + +#include "DapObj.h" + +using namespace std; + +class Crc32; + +namespace libdap +{ + +class ConstraintEvaluator; + +class DDS; +class Marshaller; +class UnMarshaller; + +class Constructor; +class XMLWrter; + +class DMR; +class D4Group; +class XMLWriter; +class D4StreamMarshaller; +class D4StreamUnMarshaller; + +class D4Attributes; + +/** This defines the basic data type features for the DODS data access + protocol (DAP) data types. All the DAP type classes (Float64, Array, + etc.) subclass it. This class is an abstract one; no variables will ever + be stored as BaseType instances, only as instances of its child classes. + + These classes and their methods give a user the capacity to set up + sophisticated data types. They do not provide sophisticated ways to + access and use this data. On the server side, in many cases, the class + instances will have no data in them at all until the + serialize function + is called to send data to the client. On the client side, most DAP + application programs will unpack the data promptly into whatever local + data structure the programmer deems the most useful. + + In order to use these classes on the server side of a DAP + client/server connection, you must write a read method + for each of the data types you expect to encounter in the + application. This function, whose purpose is to read data from a + local source into the class instance data buffer, is called in + serialize, when the data is about to be sent to the + client. The read function may be called earlier, in the + case of data subset requests (constraint expressions) whose + evaluation requires it. (For example, the constraint expression + ``a,b&b>c'' requires that c be read even though + it will not be sent.) + + For some data types, the read function must be aware of + the constraints + to be returned. These cautions are outlined where they occur. + + @note This class is ued by both DAP2 and DAP4. + + @brief The basic data type for the DODS DAP types. */ + +class BaseType : public DapObj +{ +private: + string d_name; // name of the instance + Type d_type; // instance's type + string d_dataset; // name of the dataset used to create this BaseType + + bool d_is_read; // true if the value has been read + bool d_is_send; // Is the variable in the projection? + + // d_parent points to the Constructor or Vector which holds a particular + // variable. It is null for simple variables. The Vector and Constructor + // classes must maintain this variable. + BaseType *d_parent; + + // Attributes for this variable. Added 05/20/03 jhrg + AttrTable d_attr; + + D4Attributes *d_attributes; + + bool d_is_dap4; // True if this is a DAP4 variable, false ... DAP2 + + // These are non-empty only for DAP4 variables. Added 9/27/12 jhrg + +protected: + // These were/are used for DAP2 CEs, but not for DAP4 ones + bool d_in_selection; // Is the variable in the selection? + bool d_is_synthesized; // true if the variable is synthesized + + void m_duplicate(const BaseType &bt); + +public: + typedef stack btp_stack; + + // These ctors assume is_dap4 is false + BaseType(const string &n, const Type &t, bool is_dap4 = false); + BaseType(const string &n, const string &d, const Type &t, bool is_dap4 = false); + + BaseType(const BaseType ©_from); + virtual ~BaseType(); + + virtual string toString(); + + virtual void transform_to_dap4(D4Group *root, Constructor *container); + virtual std::vector *transform_to_dap2(AttrTable *parent_attr_table); + + virtual void dump(ostream &strm) const ; + + BaseType &operator=(const BaseType &rhs); + + /** + * Remove any read or set data in the private data of the variable, + * setting read_p() to false. Used to clear any dynamically allocated + * storage that holds (potentially large) data. For the simple types, + * this no-op version is all that's needed. Vector and some other classes + * define a special version and have serialize() implementations that + * call it to free data as soon as possible after sending it. + * + * @note Added 7/5/15 jhrg + * @note Any specialization of this should make sure to reset the read_p + * property. + */ + virtual void clear_local_data() { set_read_p(false); } + + virtual bool is_dap4() const { return d_is_dap4; } + virtual void set_is_dap4(const bool v) { d_is_dap4 = v;} + + /** Clone this instance. Allocate a new instance and copy \c *this into + it. This method must perform a deep copy. + + @note This method should \e not copy data values, but must copy all + other fields in the object. + @return A newly allocated copy of \c this. */ + virtual BaseType *ptr_duplicate() = 0; + + virtual string name() const; + virtual void set_name(const string &n); + virtual std::string FQN() const; + + virtual Type type() const; + virtual void set_type(const Type &t); + virtual string type_name() const; + + virtual string dataset() const ; + + /** + * @brief How many elements are in this variable. + * @todo change the return type to int64_t + * @return The number of elements; 1 for scalars + */ + virtual int length() const { return 1; } + + /** + * @brief Set the number of elements for this variable + * @todo change param type to int64_t + * @param l The number of elements + */ + virtual void set_length(int) { } + + virtual bool is_simple_type() const; + virtual bool is_vector_type() const; + virtual bool is_constructor_type() const; + + virtual bool synthesized_p(); + virtual void set_synthesized_p(bool state); + + virtual int element_count(bool leaves = false); + + virtual bool read_p(); + virtual void set_read_p(bool state); + + virtual bool send_p(); + virtual void set_send_p(bool state); + + virtual AttrTable &get_attr_table(); + virtual void set_attr_table(const AttrTable &at); + + // DAP4 attributes + virtual D4Attributes *attributes(); + virtual void set_attributes(D4Attributes *); + virtual void set_attributes_nocopy(D4Attributes *); + + virtual bool is_in_selection(); + virtual void set_in_selection(bool state); + + virtual void set_parent(BaseType *parent); + virtual BaseType *get_parent() const; + + virtual void transfer_attributes(AttrTable *at); + + // I put this comment here because the version in BaseType.cc does not + // include the exact_match or s variables since they are not used. Doxygen + // was gaging on the comment. + + /** Returns a pointer to the contained variable in a composite class. The + composite classes are those made up of aggregated simple data types. + Array, Grid, and Structure are composite types, while Int and Float are + simple types. This function is only used by composite classes. The + BaseType implementation always returns null. + + Several of the subclasses provide alternate access methods + that make sense for that particular data type. For example, + the Array class defines a *var(int i) method that + returns the ith entry in the Array data, and the Structure + provides a *var(Vars_iter) function using a + pseudo-index to access the different members of the structure. + + @brief Returns a pointer to a member of a constructor class. + @param name The name of the class member. Defaults to "" + @param exact_match True if only interested in variables whose + full names match \e n exactly. If false, returns the first + variable whose name matches \e name. For example, if \e name + is \c x and \c point.x is a variable, then var("x", false) + would return a BaseType pointer to \c point.x. If \e + exact_match was true then \e name would need to be \c + "point.x" for var to return that pointer. This feature + simplifies constraint expressions for datasets which have + complex, nested, constructor variables. Defaults to true. + @param s Record the path to \e name. Defaults to null, in + which case it is not used. + @return A pointer to the member named in the \e n argument. If + no name is given, the function returns the first (only) + variable. For example, an Array has only one variable, while a + Structure can have many. */ + virtual BaseType *var(const string &name = "", bool exact_match = true, btp_stack *s = 0); + virtual BaseType *var(const string &name, btp_stack &s); + + virtual void add_var(BaseType *bt, Part part = nil); + virtual void add_var_nocopy(BaseType *bt, Part part = nil); + + virtual bool read(); + + virtual bool check_semantics(string &msg, bool all = false); + + virtual bool ops(BaseType *b, int op); + virtual bool d4_ops(BaseType *b, int op); + + virtual unsigned int width(bool constrained = false) const; + + virtual void print_decl(FILE *out, string space = " ", + bool print_semi = true, + bool constraint_info = false, + bool constrained = false); + + virtual void print_xml(FILE *out, string space = " ", + bool constrained = false); + + virtual void print_decl(ostream &out, string space = " ", + bool print_semi = true, + bool constraint_info = false, + bool constrained = false); + + virtual void print_xml(ostream &out, string space = " ", + bool constrained = false); + + virtual void print_xml_writer(XMLWriter &xml, bool constrained = false); + + virtual void print_dap4(XMLWriter &xml, bool constrained = false); + + /** @name Abstract Methods */ + //@{ +#if 0 + /** Return the number of bytes that are required to hold the + instance's value. In the case of simple types such as Int32, + this is the size of one Int32 (four bytes). For a String or + Url type, width(bool constrained = false) returns the number of bytes needed + for a String * variable, not the bytes needed for all + the characters, since that value cannot be determined from + type information alone. For Structure, and other constructor + types size() returns the number of bytes needed to store + pointers to the C++ objects. + + @brief Returns the size of the class instance data. */ + virtual unsigned int width(bool constrained = false) = 0; +#endif + /** Reads the class data into the memory referenced by val. + The caller should either allocate enough storage to val + to hold the class data or set \c *val to null. If *val + is NULL, memory will be allocated by this function with + new(). If the memory is allocated this way, the + caller is responsible for deallocating that memory. Array and + values for simple types are stored as C would store an array. + + @deprecated Use value() in the leaf classes. + + @brief Reads the class data. + + @param val A pointer to a pointer to the memory into which the + class data will be copied. If the value pointed to is NULL, + memory will be allocated to hold the data, and the pointer + value modified accordingly. The calling program is responsible + for deallocating the memory references by this pointer. + + @return The size (in bytes) of the information copied to val. + */ + virtual unsigned int buf2val(void **val) = 0; + + /** Store the value pointed to by val in the object's + internal buffer. This function does not perform any checks, so + users must be sure that the thing pointed to can actually be + stored in the object's buffer. + + Only simple objects (Int, Float, Byte, and so on) and arrays + of these simple objects may be stored using this function. To + put data into more complex constructor types, use the + functions provided by that class. + + @deprecated Use set_value() in the leaf classes. + + @brief Loads class data. + + @param val A pointer to the data to be inserted into the class + data buffer. + + @param reuse A boolean value, indicating whether the class + internal data storage can be reused or not. If this argument + is TRUE, the class buffer is assumed to be large enough to + hold the incoming data, and it is not reallocated. If + FALSE, new storage is allocated. If the internal buffer has + not been allocated at all, this argument has no effect. This + is currently used only in the Vector class. + + @return The size (in bytes) of the information copied from + val. + @see Grid + @see Vector::val2buf */ + virtual unsigned int val2buf(void *val, bool reuse = false) = 0; + + /** Similar to using serialize() and deserialize() together in one object. + Data are read as for serialize and those values are stored in the + objects as deserialize() does but does not write and then read data + to/from a stream. + + This method is defined by the various data type classes. It calls the + read() abstract method. Unlike serialize(), this method does not + clear the memory use to hold the data values, so the caller should + make sure to delete the DDS or the variable as soon as possible. + + @param eval Use this as the constraint expression evaluator. + @param dds The Data Descriptor Structure object corresponding + to this dataset. See The DODS User Manual for + information about this structure. */ + virtual void intern_data(ConstraintEvaluator &eval, DDS &dds); + + /** Sends the data from the indicated (local) dataset through the + connection identified by the Marshaller parameter. If the + data is not already incorporated into the DDS object, read the + data from the dataset. Once the data are sent (written to the + Marshaller), they are deleted from the object and the object + state is reset so that they will be read again if the read() + method is called. + + This function is only used on the server side of the + client/server connection, and is generally only called from + the ResponseBuilder functions. It has no BaseType + implementation; each datatype child class supplies its own + implementation. + + @brief Move data to the net, then remove them from the object. + + @param eval Use this as the constraint expression evaluator. + @param dds The Data Descriptor Structure object corresponding + to this dataset. See The DODS User Manual for + information about this structure. + @param m A marshaller used to serialize data types + @param ce_eval A boolean value indicating whether to evaluate + the DODS constraint expression that may accompany this + dataset. The constraint expression is stored in the dds. + @return This method always returns true. Older versions used + the return value to signal success or failure. + + @note We changed the default behavior of this method so that it + calls BaseType::clear_local_data() once the values are sent. This, + combined with the behavior that read() is called by this method + just before data are sent, means that data for any given variable + remain in memory for the shortest time possible. Furthermore, since + variables are serialized one at a time, no more than one variable's + data will be in memory at any given time when using the default + behavior. Some code - code that uses intern_data() or server functions - + might alter this default behavior. Only Array (i.e. Vector), Sequence, + D4Sequence and D4Opaque types actually hold data in dynamically allocated + memory, so sonly those types have the new/changed behavior. + This change was made on 7/5/15. + + @exception InternalErr. + @exception Error. + @see DDS */ + virtual bool serialize(ConstraintEvaluator &eval, DDS &dds, Marshaller &m, bool ce_eval = true); + +#if 0 + /** + * Provide a way to get the old behavior of serialize() - calling this + * method will serialize the BaseType object's data but _not_ delete its + * data storage. + * + * @note This method's behavior differs only for Array (i.e. Vector), Sequence, + * D4Sequence and D4Opaque types; the other types do not use dynamic memory to + * hold data values. + * + * @param eval Use this as the constraint expression evaluator. + * @param dds The Data Descriptor Structure object corresponding + * to this dataset. See The DODS User Manual for + * information about this structure. + * @param m A marshaller used to serialize data types + * @param ce_eval A boolean value indicating whether to evaluate + * the DODS constraint expression that may accompany this + * @return This method always returns true. Older versions used + * the return value to signal success or failure. + * @param + */ + virtual bool serialize_no_release(ConstraintEvaluator &eval, DDS &dds, Marshaller &m, bool ce_eval = true) { + return serialize(eval, dds, m, ce_eval); + } +#endif + + /** + * @brief include the data for this variable in the checksum + * DAP4 includes a checksum with every data response. This method adds the + * variable's data to that checksum. + * @param checksum A Crc32 instance that holds the current checksum. + */ + virtual void compute_checksum(Crc32 &checksum) = 0; + + virtual void intern_data(/*Crc32 &checksum, DMR &dmr, ConstraintEvaluator &eval*/); + + /** + * @brief The DAP4 serialization method. + * Serialize a variable's values for DAP4. This does not write the DMR + * persistent representation but does write that part of the binary + * data blob that holds a variable's data. Once a variable's data are + * serialized, that memory is reclaimed (by calling BaseType::clear_local_data()) + * + * @param m + * @param dmr + * @param eval + * @param filter True if there is one variable that should be 'filtered' + * @exception Error or InternalErr + */ + virtual void serialize(D4StreamMarshaller &m, DMR &dmr, bool filter = false); + +#if 0 + /** + * @brief Variation on the DAP4 serialization method - retain data after serialization + * Serialize a variable's values for DAP4. This does not write the DMR + * persistent representation but does write that part of the binary + * data blob that holds a variable's data. Once a variable's data are + * serialized, that memory is reclaimed (by calling BaseType::clear_local_data()) + * + * @note This version does not delete the storage of Array, D4Sequence or + * D4Opaque variables, as it the case with serialize(). For other types, + * this method and serialize have the same beavior (since those types do + * not us dynamic memory to hold data values). + * + * @param m + * @param dmr + * @param eval + * @param filter True if there is one variable that should be 'filtered' + * @exception Error or InternalErr + */ + virtual void serialize_no_release(D4StreamMarshaller &m, DMR &dmr, bool filter = false) { + serialize(m, dmr, filter); + } +#endif + + /** Receives data from the network connection identified by the + source parameter. The data is put into the class data + buffer according to the input dds. + + This function is only used on the client side of the DODS + client/server connection. + + @brief Receive data from the net. + + @param um An UnMarshaller that knows how to deserialize data types + @param dds The Data Descriptor Structure object corresponding + to this dataset. See The DODS User Manual for + information about this structure. This would have been + received from the server in an earlier transmission. + @param reuse A boolean value, indicating whether the class + internal data storage can be reused or not. If this argument + is TRUE, the class buffer is assumed to be large enough to + hold the incoming data, and it is not reallocated. If + FALSE, new storage is allocated. If the internal buffer has + not been allocated at all, this argument has no effect. + @return Always returns TRUE. + @exception Error when a problem reading from the UnMarshaller is + found. + @see DDS */ + virtual bool deserialize(UnMarshaller &um, DDS *dds, bool reuse = false); + + /** + * The DAP4 deserialization method. + * @param um + * @param dmr + * @exception Error or InternalErr + */ + virtual void deserialize(D4StreamUnMarshaller &um, DMR &dmr); + + /** Prints the value of the variable, with its declaration. This + function is primarily intended for debugging DODS + applications. However, it can be overloaded and used to do + some useful things. Take a look at the asciival and writeval + clients, both of which overload this to output the values of + variables in different ways. + + @brief Prints the value of the variable. + + @param out The output stream on which to print the value. + @param space This value is passed to the print_decl() + function, and controls the leading spaces of the output. + @param print_decl_p A boolean value controlling whether the + variable declaration is printed as well as the value. */ + + virtual void print_val(FILE *out, string space = "", + bool print_decl_p = true); + + /** Prints the value of the variable, with its declaration. This + function is primarily intended for debugging DODS + applications. However, it can be overloaded and used to do + some useful things. Take a look at the asciival and writeval + clients, both of which overload this to output the values of + variables in different ways. + + @brief Prints the value of the variable. + + @param out The output ostream on which to print the value. + @param space This value is passed to the print_decl() + function, and controls the leading spaces of the output. + @param print_decl_p A boolean value controlling whether the + variable declaration is printed as well as the value. */ + virtual void print_val(ostream &out, string space = "", + bool print_decl_p = true) = 0; + //@} +}; + +} // namespace libdap + +#endif // _basetype_h diff --git a/BaseTypeFactory.cc b/BaseTypeFactory.cc new file mode 100644 index 0000000..fa9c4fd --- /dev/null +++ b/BaseTypeFactory.cc @@ -0,0 +1,166 @@ + +// -*- mode: c++; c-basic-offset:4 -*- + +// This file is part of libdap, A C++ implementation of the OPeNDAP Data +// Access Protocol. + +// Copyright (c) 2005 OPeNDAP, Inc. +// Author: James Gallagher +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// +// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112. + +#include "config.h" + +#include + +#include "Byte.h" +#include "Int16.h" +#include "UInt16.h" +#include "Int32.h" +#include "UInt32.h" +#include "Float32.h" +#include "Float64.h" +#include "Str.h" +#include "Url.h" +#include "Array.h" +#include "Structure.h" +#include "Sequence.h" +#include "Grid.h" + +#include "BaseTypeFactory.h" +#include "debug.h" + +namespace libdap { + +BaseType * +BaseTypeFactory::NewVariable(Type type, const string &name) const +{ + switch (type) { + case dods_byte_c: + return NewByte(name); + case dods_int16_c: + return NewInt16(name); + case dods_uint16_c: + return NewUInt16(name); + case dods_int32_c: + return NewInt32(name); + case dods_uint32_c: + return NewUInt32(name); + case dods_float32_c: + return NewFloat32(name); + case dods_float64_c: + return NewFloat64(name); + + case dods_str_c: + return NewStr(name); + case dods_url_c: + return NewUrl(name); + + case dods_array_c: + return NewArray(name); + case dods_structure_c: + return NewStructure(name); + case dods_sequence_c: + return NewSequence(name); + case dods_grid_c: + return NewGrid(name); + default: + throw InternalErr(__FILE__, __LINE__, "Unknown type"); + } +} + +Byte * +BaseTypeFactory::NewByte(const string &n) const +{ + return new Byte(n); +} + +Int16 * +BaseTypeFactory::NewInt16(const string &n) const +{ + return new Int16(n); +} + +UInt16 * +BaseTypeFactory::NewUInt16(const string &n) const +{ + return new UInt16(n); +} + +Int32 * +BaseTypeFactory::NewInt32(const string &n) const +{ + DBG(cerr << "Inside BaseTypeFactory::NewInt32" << endl); + return new Int32(n); +} + +UInt32 * +BaseTypeFactory::NewUInt32(const string &n) const +{ + return new UInt32(n); +} + +Float32 * +BaseTypeFactory::NewFloat32(const string &n) const +{ + return new Float32(n); +} + +Float64 * +BaseTypeFactory::NewFloat64(const string &n) const +{ + return new Float64(n); +} + +Str * +BaseTypeFactory::NewStr(const string &n) const +{ + return new Str(n); +} + +Url * +BaseTypeFactory::NewUrl(const string &n) const +{ + return new Url(n); +} + +Array * +BaseTypeFactory::NewArray(const string &n , BaseType *v) const +{ + return new Array(n, v); +} + +Structure * +BaseTypeFactory::NewStructure(const string &n) const +{ + return new Structure(n); +} + +Sequence * +BaseTypeFactory::NewSequence(const string &n) const +{ + DBG(cerr << "Inside BaseTypeFactory::NewSequence" << endl); + return new Sequence(n); +} + +Grid * +BaseTypeFactory::NewGrid(const string &n) const +{ + return new Grid(n); +} + +} // namespace libdap diff --git a/BaseTypeFactory.h b/BaseTypeFactory.h new file mode 100644 index 0000000..85a02b7 --- /dev/null +++ b/BaseTypeFactory.h @@ -0,0 +1,127 @@ + +// -*- mode: c++; c-basic-offset:4 -*- + +// This file is part of libdap, A C++ implementation of the OPeNDAP Data +// Access Protocol. + +// Copyright (c) 2005 OPeNDAP, Inc. +// Author: James Gallagher +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// +// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112. + +#ifndef base_type_factory_h +#define base_type_factory_h + +#include + +#include "Type.h" +#include "InternalErr.h" + +// Class declarations; Make sure to include the corresponding headers in the +// implementation file. + +namespace libdap +{ + +class Byte; +class Int16; +class UInt16; +class Int32; +class UInt32; +class Float32; +class Float64; +class Str; +class Url; +class Array; +class Structure; +class Sequence; +class Grid; +class BaseType; + +/** A factory to create instances of the leaf nodes of BaseType (Byte, ... + Grid). Clients of libdap++ which require special behavior for the types + should subclass this factory and provide an implementation which creates + instances of those specializations. Make sure to pass a reference to the + new factory to DDS's constructor since by default it uses this factory. + + To define and use your own factory, first make sure that you are not + using the compile time constant 'DEFAULT_BASETYPE_FACTORY.' Then pass a + pointer to an instance of your factory to the DDS/DataDDS constructors. + When the parser is used to build a DDS from a DAP response, the factory + will be used to instantiate the different variable-type classes. + + @note The easiest way to subclass this is to follow the pattern of using + a separate class declaration and implementation. It's possible to use one + file to hold + both, but that is complicated somewhat because DDS.h, which includes this + class, also includes many of the type classes (Array.h, ..., Grid.h) and + the order of their inclusion can create compilation problems where the + Vector and/or Constructor base classes are not defined. It's easiest to + split the declaration and implementation and include forward declarations + of the type classes in the declaration (\c .h) file and then include the + type class' headers in the implementation (\c .cc) file. + + @author James Gallagher + @see DDS */ +class BaseTypeFactory +{ +public: + BaseTypeFactory() + {} + virtual ~BaseTypeFactory() + {} + + /** + * Build a new variable and return it using a BaseType pointer. The + * type of the variable is given using Type enumeration. + * + * @note Added for DAP4 + * + * @param t The type of the variable to create + * @parma name The (optional) name of the variable. + */ + virtual BaseType *NewVariable(Type t, const string &name = "") const; + + /** + * Clone this object and return a pointer to the clone. + * + * @note added for DAP4 + */ + virtual BaseTypeFactory *ptr_duplicate() const { + throw InternalErr(__FILE__, __LINE__, "Not Implemented."); + } + + virtual Byte *NewByte(const string &n = "") const; + virtual Int16 *NewInt16(const string &n = "") const; + virtual UInt16 *NewUInt16(const string &n = "") const; + virtual Int32 *NewInt32(const string &n = "") const; + virtual UInt32 *NewUInt32(const string &n = "") const; + virtual Float32 *NewFloat32(const string &n = "") const; + virtual Float64 *NewFloat64(const string &n = "") const; + + virtual Str *NewStr(const string &n = "") const; + virtual Url *NewUrl(const string &n = "") const; + + virtual Array *NewArray(const string &n = "", BaseType *v = 0) const; + virtual Structure *NewStructure(const string &n = "") const; + virtual Sequence *NewSequence(const string &n = "") const; + virtual Grid *NewGrid(const string &n = "") const; +}; + +} // namespace libdap + +#endif // base_type_factory_h diff --git a/Byte.cc b/Byte.cc new file mode 100644 index 0000000..7644d56 --- /dev/null +++ b/Byte.cc @@ -0,0 +1,390 @@ + +// -*- mode: c++; c-basic-offset:4 -*- + +// This file is part of libdap, A C++ implementation of the OPeNDAP Data +// Access Protocol. + +// Copyright (c) 2002,2003 OPeNDAP, Inc. +// Author: James Gallagher +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// +// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112. + +// (c) COPYRIGHT URI/MIT 1994-1999 +// Please read the full copyright statement in the file COPYRIGHT_URI. +// +// Authors: +// jhrg,jimg James Gallagher + +// Implementation for Byte. +// +// jhrg 9/7/94 + +//#define DODS_DEBUG + +#include "config.h" + +#include + +#include "Byte.h" // synonymous with UInt8 and Char +#include "Int8.h" +#include "Int16.h" +#include "UInt16.h" +#include "Int32.h" +#include "UInt32.h" +#include "Int64.h" +#include "UInt64.h" +#include "Float32.h" +#include "Float64.h" +#include "Str.h" +#include "Url.h" + +#include "DDS.h" +#include "Operators.h" +#include "Marshaller.h" +#include "UnMarshaller.h" + +#include "DMR.h" +#include "D4Attributes.h" +#include "D4StreamMarshaller.h" +#include "D4StreamUnMarshaller.h" + +#include "debug.h" +#include "util.h" +#include "parser.h" +#include "dods-limits.h" +#include "InternalErr.h" + +using std::cerr; +using std::endl; + +namespace libdap { + +/** The Byte constructor requires only the name of the variable + to be created. The name may be omitted, which will create a + nameless variable. This may be adequate for some applications. + + @brief The Byte constructor. + @param n A string containing the name of the variable to be + created. + +*/ +Byte::Byte(const string & n): BaseType(n, dods_byte_c), d_buf(0) +{} + +/** This Byte constructor requires the name of the variable to be created + and the name of the dataset from which this variable is being created. + This constructor is used in server-side processing, loading structure in + from a dataset. + + @brief The Byte server-side constructor. + @param n A string containing the name of the variable to be created. + @param d A string containing the name of the dataset from which the + variable is being created. +*/ +Byte::Byte(const string &n, const string &d): BaseType(n, d, dods_byte_c), d_buf(0) +{} + +Byte::Byte(const Byte & copy_from): BaseType(copy_from) +{ + d_buf = copy_from.d_buf; +} + +BaseType *Byte::ptr_duplicate() +{ + return new Byte(*this); +} + +Byte & Byte::operator=(const Byte & rhs) +{ + if (this == &rhs) + return *this; + + dynamic_cast < BaseType & >(*this) = rhs; + + d_buf = rhs.d_buf; + + return *this; +} + +unsigned int Byte::width(bool) const +{ + return sizeof(dods_byte); +} + +/** Serialize the contents of member _BUF (the object's internal + buffer, used to hold data) and write the result to stdout. If + FLUSH is true, write the contents of the output buffer to the + kernel. FLUSH is false by default. If CE_EVAL is true, evaluate + the current constraint expression; only send data if the CE + evaluates to true. + + @return False if a failure to read, send or flush is detected, true + otherwise. +*/ +bool Byte::serialize(ConstraintEvaluator & eval, DDS & dds, Marshaller &m, bool ce_eval) +{ +#if USE_LOCAL_TIMEOUT_SCHEME + dds.timeout_on(); +#endif + if (!read_p()) + read(); // read() throws Error and InternalErr + + if (ce_eval && !eval.eval_selection(dds, dataset())) + return true; +#if USE_LOCAL_TIMEOUT_SCHEME + dds.timeout_off(); +#endif + m.put_byte( d_buf ) ; + + return true; +} + +/** @brief Deserialize the char on stdin and put the result in + _BUF. +*/ +bool Byte::deserialize(UnMarshaller &um, DDS *, bool) +{ + um.get_byte( d_buf ) ; + + return false; +} + +void +Byte::compute_checksum(Crc32 &checksum) +{ + checksum.AddData(reinterpret_cast(&d_buf), sizeof(d_buf)); +} + +/** + * @brief Serialize a Byte + * @param m + * @param dmr Unused + * @param eval Unused + * @param filter Unused + * @exception Error is thrown if the value needs to be read and that operation fails. + */ +void +Byte::serialize(D4StreamMarshaller &m, DMR &, /*ConstraintEvaluator &,*/ bool) +{ + if (!read_p()) + read(); // read() throws Error + + m.put_byte( d_buf ) ; +} + +void +Byte::deserialize(D4StreamUnMarshaller &um, DMR &) +{ + um.get_byte( d_buf ) ; +} + +/** Store the value referenced by val in the object's internal + buffer. reuse has no effect because this class does not + dynamically allocate storage for the internal buffer. + + @return The size (in bytes) of the value's representation. */ +unsigned int Byte::val2buf(void *val, bool) +{ + // Jose Garcia + // This method is public therefore and I believe it has being designed + // to be use by read which must be implemented on the surrogate library, + // thus if the pointer val is NULL, is an Internal Error. + if (!val) + throw InternalErr("the incoming pointer does not contain any data."); + + d_buf = *(dods_byte *) val; + + return width(); +} + +unsigned int Byte::buf2val(void **val) +{ + // Jose Garcia + // The same comment justifying throwing an Error in val2buf applies here. + if (!val) + throw InternalErr("NULL pointer"); + + if (!*val) + *val = new dods_byte; + + *(dods_byte *) * val = d_buf; + + return width(); +} + +/** Set the value of this instance. + @param value The value + @return Always returns true; the return type of bool is for compatibility + with the Passive* subclasses written by HAO. */ +bool Byte::set_value(dods_byte value) +{ + d_buf = value; + set_read_p(true); + + return true; +} + +/** Get the value of this instance. + @return The value. */ +dods_byte Byte::value() const +{ + return d_buf; +} + +void Byte::print_val(FILE * out, string space, bool print_decl_p) +{ + ostringstream oss; + print_val(oss, space, print_decl_p); + fwrite(oss.str().data(), sizeof(char), oss.str().length(), out); +} + +void Byte::print_val(ostream &out, string space, bool print_decl_p) +{ + if (print_decl_p) { + print_decl(out, space, false); + out << " = " << (int) d_buf << ";\n"; + } + else + out << (int) d_buf; +} + +bool Byte::ops(BaseType * b, int op) +{ + + // Extract the Byte arg's value. + if (!read_p() && !read()) { + // Jose Garcia + // Since the read method is virtual and implemented outside + // libdap++ if we cannot read the data that is the problem + // of the user or of whoever wrote the surrogate library + // implementing read therefore it is an internal error. + throw InternalErr("This value not read!"); + } + // Extract the second arg's value. + if (!b || !(b->read_p() || b->read())) { + // Jose Garcia + // Since the read method is virtual and implemented outside + // libdap++ if we cannot read the data that is the problem + // of the user or of whoever wrote the surrogate library + // implementing read therefore it is an internal error. + throw InternalErr("This value not read!"); + } + + // By using the same operator code numbers for both the DAP2 and DAP4 + // parser/evaluator we can use the same evaluation code. + return d4_ops(b, op); +} + +/** + * @see BaseType::d4_ops(BaseType *, int) + */ +bool Byte::d4_ops(BaseType *b, int op) +{ + switch (b->type()) { + case dods_int8_c: + return USCmp(op, d_buf, static_cast(b)->value()); + case dods_byte_c: + return Cmp(op, d_buf, static_cast(b)->value()); + case dods_int16_c: + return USCmp(op, d_buf, static_cast(b)->value()); + case dods_uint16_c: + return Cmp(op, d_buf, static_cast(b)->value()); + case dods_int32_c: + return USCmp(op, d_buf, static_cast(b)->value()); + case dods_uint32_c: + return Cmp(op, d_buf, static_cast(b)->value()); + case dods_int64_c: + return USCmp(op, d_buf, static_cast(b)->value()); + case dods_uint64_c: + return Cmp(op, d_buf, static_cast(b)->value()); + case dods_float32_c: + return USCmp(op, d_buf, static_cast(b)->value()); + case dods_float64_c: + return USCmp(op, d_buf, static_cast(b)->value()); + case dods_str_c: + case dods_url_c: + throw Error(malformed_expr, "Relational operators can only compare compatible types (number, string)."); + default: + throw Error(malformed_expr, "Relational operators only work with scalar types."); + } +} + + + + +/** @brief DAP4 to DAP2 transform + * + * Return a DAP2 'copy' of the variable. + * + * NOTE: This little bit of magic ensures that the DAP4 shenanigans + * in which UInt8, Char , and Byte are synonymous is reduced + * to the DAP2 simplicity of Byte. + * + * + * @param root The root group that should hold this new variable. Add Group-level + * stuff here (e.g., D4Dimensions). + * @param container Add the new variable to this container. + * + * @return A pointer to the transformed variable + */ +std::vector * +Byte::transform_to_dap2(AttrTable *parent_attr_table) +{ + DBG(cerr << __func__ << "() - BEGIN" << endl;); + vector *vec = BaseType::transform_to_dap2(parent_attr_table); + if(vec->size()!=1){ + ostringstream oss; + oss << __func__ << "() - Something Bad Happened. This transform should produce only "; + oss << " a single BaseType yet it produced " << vec->size(); + throw new Error(internal_error,oss.str()); + } + + BaseType *dest = (*vec)[0]; + DBG(cerr << __func__ << "() - type(): " << type() << endl;); + DBG(cerr << __func__ << "() - dest->type(): " << dest->type() << endl;); + + // This little bit of magic ensures that the DAP4 shenanigans + // in which UInt8, Char , and Byte are synonymous is reduced + // to the DAP2 simplicity of Byte. + if(type()!=dods_byte_c){ + dest->set_type(dods_byte_c); + } + DBG (dest->get_attr_table().print(cerr);); + + DBG(cerr << __func__ << "() - END" << endl;); + return vec; +} + +/** @brief dumps information about this object + * + * Displays the pointer value of this instance and information about this + * instance. + * + * @param strm C++ i/o stream to dump the information to + * @return void + */ +void Byte::dump(ostream & strm) const +{ + strm << DapIndent::LMarg << "Byte::dump - (" + << (void *) this << ")" << endl; + DapIndent::Indent(); + BaseType::dump(strm); + strm << DapIndent::LMarg << "value: " << d_buf << endl; + DapIndent::UnIndent(); +} + +} // namespace libdap diff --git a/Byte.h b/Byte.h new file mode 100644 index 0000000..a2ef04d --- /dev/null +++ b/Byte.h @@ -0,0 +1,108 @@ + +// -*- mode: c++; c-basic-offset:4 -*- + +// This file is part of libdap, A C++ implementation of the OPeNDAP Data +// Access Protocol. + +// Copyright (c) 2002,2003 OPeNDAP, Inc. +// Author: James Gallagher +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// +// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112. + +// (c) COPYRIGHT URI/MIT 1994-1999 +// Please read the full copyright statement in the file COPYRIGHT_URI. +// +// Authors: +// jhrg,jimg James Gallagher + +// Interface for Byte type. +// +// jhrg 9/7/94 + +#ifndef _byte_h +#define _byte_h 1 + +#ifndef _dods_datatypes_h +#include "dods-datatypes.h" +#endif + +#ifndef _basetype_h +#include "BaseType.h" +#endif + +#ifndef constraint_evaluator_h +#include "ConstraintEvaluator.h" +#endif + +namespace libdap +{ + +/** This class is used to hold eight bits of information. No sign + information is implied in its value. + + @brief Holds a single byte. + @see BaseType + */ +class Byte: public BaseType +{ +protected: + dods_byte d_buf; + +public: + Byte(const string &n); + Byte(const string &n, const string &d); + + virtual ~Byte() + {} + + Byte(const Byte ©_from); + + Byte &operator=(const Byte &rhs); + + virtual unsigned int width(bool constrained = false) const; + + virtual BaseType *ptr_duplicate(); + + // DAP2 + virtual bool serialize(ConstraintEvaluator &eval, DDS &dds, Marshaller &m, bool ce_eval); + virtual bool deserialize(UnMarshaller &um, DDS *, bool); + + // DAP4 + virtual void compute_checksum(Crc32 &checksum); + virtual void serialize(D4StreamMarshaller &m, DMR &dmr, bool filter = false); + virtual void deserialize(D4StreamUnMarshaller &um, DMR &dmr); + + virtual unsigned int val2buf(void *val, bool reuse = false); + virtual unsigned int buf2val(void **val); + + virtual bool set_value(const dods_byte value); + virtual dods_byte value() const; + + virtual void print_val(FILE *out, string space = "", bool print_decl_p = true); + virtual void print_val(ostream &out, string space = "", bool print_decl_p = true); + + virtual bool ops(BaseType *b, int op); + virtual bool d4_ops(BaseType *b, int op); + virtual std::vector *transform_to_dap2(AttrTable *parent_attr_table); + + virtual void dump(ostream &strm) const ; +}; + +} // namespace libdap + +#endif // _byte_h + diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..060b0e8 --- /dev/null +++ b/COPYING @@ -0,0 +1,508 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + Free Software Foundation + 51 Franklin Street, Suite 500 + Boston, MA 02110-1335 + USA + + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/COPYRIGHT_URI b/COPYRIGHT_URI new file mode 100644 index 0000000..a368e51 --- /dev/null +++ b/COPYRIGHT_URI @@ -0,0 +1,32 @@ + + $Id$ + + (c) Copyright 1994-2000 by + The University of Rhode Island and The Massachusetts Institute of Technology + + Portions of this software were developed by the Graduate School of + Oceanography (GSO) at the University of Rhode Island (URI) in collaboration + with The Massachusetts Institute of Technology (MIT). + + Access and use of this software shall impose the following obligations and + understandings on the user. The user is granted the right, without any fee + or cost, to use, copy, modify, alter, enhance and distribute this software, + and any derivative works thereof, and its supporting documentation for any + purpose whatsoever, provided that this entire notice appears in all copies + of the software, derivative works and supporting documentation. The names + URI, MIT and/or GSO, may not be used in any advertising or publicity to + endorse or promote any products or commercial entity unless specific + written permission is obtained from URI/MIT. The user also understands that + URI/MIT is not obligated to provide the user with any support, consulting, + training or assistance of any kind with regard to the use, operation and + performance of this software nor to provide the user with any updates, + revisions, new versions or "bug fixes". + + THIS SOFTWARE IS PROVIDED BY URI/MIT "AS IS" AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + EVENT SHALL URI/MIT BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTUOUS + ACTION, ARISING OUT OF OR IN CONNECTION WITH THE ACCESS, USE OR PERFORMANCE + OF THIS SOFTWARE. diff --git a/COPYRIGHT_W3C b/COPYRIGHT_W3C new file mode 100644 index 0000000..1975516 --- /dev/null +++ b/COPYRIGHT_W3C @@ -0,0 +1,50 @@ + + W3C� SOFTWARE NOTICE AND LICENSE + +Copyright � 1994-2002 World Wide Web Consortium, (Massachusetts Institute of +Technology, Institut National de Recherche en Informatique et en Automatique, +Keio University). All Rights Reserved. http://www.w3.org/Consortium/Legal/ + +This W3C work (including software, documents, or other related items) is +being provided by the copyright holders under the following license. By +obtaining, using and/or copying this work, you (the licensee) agree that you +have read, understood, and will comply with the following terms and +conditions: + +Permission to use, copy, modify, and distribute this software and its +documentation, with or without modification, for any purpose and without fee +or royalty is hereby granted, provided that you include the following on ALL +copies of the software and documentation or portions thereof, including +modifications, that you make: + + 1. The full text of this NOTICE in a location viewable to users of the + redistributed or derivative work. + + 2. Any pre-existing intellectual property disclaimers, notices, or terms + and conditions. If none exist, a short notice of the following form + (hypertext is preferred, text is permitted) should be used within the + body of any redistributed or derivative code: "Copyright � + [$date-of-software] World Wide Web Consortium, (Massachusetts Institute + of Technology, Institut National de Recherche en Informatique et en + Automatique, Keio University). All Rights Reserved. + http://www.w3.org/Consortium/Legal/" + + 3. Notice of any changes or modifications to the W3C files, including the + date changes were made. (We recommend you provide URIs to the location + from which the code is derived.) + +THIS SOFTWARE AND DOCUMENTATION IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS +MAKE NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT +LIMITED TO, WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR +PURPOSE OR THAT THE USE OF THE SOFTWARE OR DOCUMENTATION WILL NOT INFRINGE +ANY THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. + +COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE SOFTWARE OR +DOCUMENTATION. + +The name and trademarks of copyright holders may NOT be used in advertising +or publicity pertaining to the software without specific, written prior +permission. Title to copyright in this software and any associated +documentation will at all times remain with copyright holders. + diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..9260193 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,12005 @@ +2017-08-25 James Gallagher + + Added a comment to my HTTPCacheTest change... + [skip ci] + + Removed a test for block_size in HTTPCacheTest that failed on + Fedora ppc64le system with XFS system + + Merge pull request #32 from OPENDAP/hyrax390 + Hyrax390 + + Marked two of the Dmr Round Trip Tests as failing. + This matched the results of the master branch. Thus we can merge + the hyrax390 branch and move on to either fix those issues or + accept the behavior as 'good enough.' + + Merge branch 'master' into hyrax390 + + Merge pull request #36 from OPENDAP/gnulib-update + Updated gnulib and added mkstemps() to avoid portability issues. + + Added malloca.valgrind + + Added more in gl + + Added more ... + + Added more still more + + Added more files in gl + + Added gl/gettimeofday.c + + Added more gnulib m4 files + +2017-08-24 James Gallagher + + Updated gnulib and added mkstemps() to avoid portability issues. + + Added comments to DMR.h + +2017-07-18 James Gallagher + + Fixed a warning in D4EnumTest and CppUnit usage in DmrRoundTripTest + + Merge branch 'master' of https://github.com/opendap/libdap4.git + + Added --disable-dependency-tracking to configure in travis.yml + Will it build faster? Also, fixed a comment in D4Maps.h + +2017-05-31 Slav Korolev + + Ticket #390. Cleaned 4 files. + + Ticket #390. Cleaned 4 files. + +2017-05-22 Nathan Potter + + Merge pull request #29 from OPENDAP/hyrax390 + Task #390. The CppUnit unit tests in libdap4 should all support runni… + + Merge pull request #31 from OPENDAP/hyrax-400 + Hyrax 400 + +2017-05-19 James Gallagher + + Fixed vec_resize() in Vector. + I was mixing cpacity() and resize() and that leads to some odd + behavior - I'm not sure I really understand it, but comments in + Vector::vec_resize() are a stab at an explanation. The bottom line + is that the fix is to use size() with resize() and not capacity(). + I also modified TestArray so that its read() method allocates + enough space for a vector of Structures before it starts writing + to it, just to demonstrate good behavior. + +2017-05-19 Slav Korolev + + #390. More changes + +2017-05-18 James Gallagher + + Checkpoint + + Checkpoint - run using valgrind on centos + +2017-05-18 Slav Korolev + + Merge branch 'master' into hyrax390 + +2017-05-17 James Gallagher + + I edited the ByteTest.cc file so it compiles and works. + +2017-05-17 Nathan Potter + + Merge pull request #30 from OPENDAP/attr_bug + Attr bug + +2017-05-17 ndp-opendap + + Dropped references to missing files. + +2017-05-17 Slav Korolev + + "Task #390." + +2017-05-16 ndp-opendap + + instrumentaiton + +2017-05-12 ndp-opendap + + Fixed possible null pointer deref + + Dropped unused file + +2017-05-11 Nathan Potter + + Merge pull request #26 from OPENDAP/dmr2dds + Dmr2dds + +2017-05-11 ndp-opendap + + Changed return value of transform_to_dap4() to void. + +2017-05-10 Nathan Potter + + Merge branch 'master' into dmr2dds + +2017-05-09 ndp-opendap + + Fixed bad implmentations of transform_to_dap4 in Test classes. Instrumenttion + + Fixed bad implmentations of transform_to_dap4 in Test classes. Instrumenttion + +2017-05-08 ndp-opendap + + Reworked transform_to_dap4() implmentation so that they all (in + libdap4) return NULL and populate the container passed in. Next + will be to change the return from BAseType* to void + + Reworked transform_to_dap4() implmentation so that they all (in + libdap4) return NULL and populate the container passed in. Next + will be to change the return from BAseType* to void + + First pass at fixing the duplicate variables bug in + DMR::build_using_dds() and Grid::transform_to_dap4() + +2017-05-06 ndp-opendap + + Patched DMR tests to use the new CppUnit test pattern. + +2017-05-05 ndp-opendap + + Repairing baselines + + Fixed Grid::transform_to_dap4() so maps have correct names (FQN names) + + Fixed Grid::transform_to_dap4() so maps have correct names (FQN names) + + Fixed namespace issue in tests that prevented individual tests + from being run byt command line parameters. + +2017-05-04 ndp-opendap + + Alternate Array assignment in Grid::transform_to_dap4() + + sync + + Stuff + +2017-05-04 James Gallagher + + Merge pull request #27 from OPENDAP/new_docs_target + New docs target + +2017-05-04 ndp-opendap + + A stab at correcting the FQN issue described in Hyrax-389 + +2017-05-04 James Gallagher + + Removed unused tests. + Grid::transform_to_dap4() had used dynamic_cast<>() but now uses + static_cast<>(), so it no longer needs to test for null returns from + the cast operator. + +2017-05-04 ndp-opendap + + Supressing warnings + + Merge branch 'master' into dmr2dds + +2017-05-04 James Gallagher + + Merge pull request #28 from OPENDAP/dmr_parser_fix + Fixed fixed length line limit in the DMR Sax parser + +2017-05-04 ndp-opendap + + Added some DAP4 related TODO items. + + Added some DAP4 related TODO items. + + Merge branch 'master' into dmr2dds + + unwinding small change + +2017-05-03 James Gallagher + + Changed class field names so they start with 'd_' + modified: D4ParserSax2.cc + modified: D4ParserSax2.h + + Fixed fixed length line limit in the DMR Sax parser + modified: D4ParserSax2.cc + +2017-05-03 ndp-opendap + + Updated .gitignore for new test executables + + Adding two more working DMR to Grid tests. Failing test still fails. + + Checking in new dmr2Grid test which fails because the supplied DMR + fails to parse, despite the fact that it was generated by Hyrax + 1.13.3 + + Added new dmr2dds with Grid test. + + Added new dmr2dds with Grid test. + + Updating comments etc. + + Updating comments etc. + +2017-05-02 ndp-opendap + + Ooops. Forgot test baseline + + Silencing more debug chatter. + + Silencing more debug chatter. + + First cut at extracting dap2 Grid objects from dap4 Arrays with maps. + + First cut at extracting dap2 Grid objects from dap4 Arrays with maps. + + Turned off compiled in debug. + + Refactored the transform_to_dap2() aip. woot. + +2017-05-02 James Gallagher + + Modified Makefile.am to use the new build-gh-docs.sh script. + This seems to work, although I think it might be fragile. + + A better way to build the docs and push them to gh-pages new file: build-gh-docs.sh + + Working on using a script instead of makefile targets modified: Makefile.am + + modified: .gitignore + + Fixed spaces/tabs in Makefile.am + + deleted: doxy_private.conf + + Modified doxy.conf.in so it writes output to 'html' + It used to write output to docs/html. + + I added the new docs targets to the Makefile.am + These will build and commit/push doxygen docs to github.io via gh-pages. + The gh-pages branch has to be made first and should be empty. + +2017-04-28 ndp-opendap + + Weekend update baby... More tests working. Need to sort out Groups next. + + Refactored DMR test suite, add round-trip and dmr_to_dap2 test suites. + +2017-04-26 James Gallagher + + Added -g3 to the coverage flags + + Removed generated docs + + Deploy to GitHub Pages + +2017-04-26 ndp-opendap + + Still dorting tests + +2017-04-25 ndp-opendap + + Merged master to dmr2dds branch + + Structures now transfer attributes correctly + + Space indented BAseType (why was it not before?) + + Structures now transfer attributes correctly + +2017-04-24 ndp-opendap + + Structures now transfer attributes correctly + + Structures now transfer attributes correctly + + sync + + Provisional patch to DMR.print_das() + +2017-04-23 ndp-opendap + + Provisional patch to DMR.print_das() + +2017-04-21 ndp-opendap + + Created DMR specific testsuite. Migrated exisiting DMR tests from dds-testsuite. + + TEst draft (broken) + +2017-04-20 ndp-opendap + + sync + +2017-04-18 James Gallagher + + Added comments to Connect and mime_util regarding tainted data + + Fixed minor issue in D4ParserSax2. + The namespace prefix could be null and was passed directly to + std::string's ctor + + Fixed for potential issue in D4ConstraintEvaluator. + The pointer 'dim' could be dereferenced when it was null. + +2017-02-17 James Gallagher + + Travis should work now... + We need a better way to test changes to this process + +2017-02-16 James Gallagher + + Removed codecov.io from the build. + Debug on CentOS and stop hacking at Travis... + + Added libgcov a an attempt to fix the travis build. + + Turn on coverage in travis.yml + + more travis hacks + + travis hacks for code cov io + + more travis hacks + + another tack for travis and codecov... + + tedious... (still travis debugging) + + travis test... + + travis.yml fix for codecov.io + + Mods for codecov.io (https://codecov.io/gh/OPENDAP/libdap4) + + Added code coverage to travis.yml + + Merge branch 'master' of http://github.com/opendap/libdap4 + + Added code coverage script (get_code_cov.sh) + +2017-02-08 James Gallagher + + Hacked up gitignore for various build stuff [ci skip] + +2017-01-06 James Gallagher + + Version 3.18.3 + + Modified etags for the files at the new test.o.o site. +2016-12-08 James Gallagher + + Merge pull request #25 from OPENDAP/chunked_istream_bug + Fixed a memory issue in chunked_istream.h + + Fixed a memory issue in chunked_istream.h (new[] mis-matched with delete) + +2016-11-18 James Gallagher + + Merge pull request #24 from OPENDAP/dmr++ + Dmr++ + + Merge pull request #23 from OPENDAP/D4Group_ptr_duplicate_fix + Fix fir D4Group::ptr_duplicate(). It was not returning BaseType* + +2016-11-17 James Gallagher + + Fix fir D4Group::ptr_duplicate(). It was not returning BaseType* + Instead, it returned D4Group. There may have been a good reason, + But I think consistency is better. ...maybe + +2016-11-16 ndp-opendap + + DMR parser will now ignore and XML not in the DAP4 namespace. woot. + +2016-11-15 ndp-opendap + + XML namespace thing sorted. DMR parser will now ignore all + elements that are not in DAP4 namespace. woot. + + Still working the namespace thing + +2016-11-15 James Gallagher + + Merge branch 'master' of https://github.com/opendap/libdap4.git + +2016-11-15 ndp-opendap + + Trying to get name space centralization class working. ugh. Static methods + +2016-11-15 James Gallagher + + Added comment for dmr_start_element() + +2016-11-04 James Gallagher + + Corrected the file: URL in HTTPConnectTest - I think ... + + Merge branch 'master' of http://github.com/opendap/libdap4 + + Another try at fixing the travis build + + Trial fix of test_config.h error on Travis. + + Stopped running the tests using multiple jobs on Travis. + This might have been the cause of failures there. + +2016-11-03 James Gallagher + + Merge branch 'master' of http://github.com/opendap/libdap4 + +2016-11-02 James Gallagher + + Updated the library version info for version 3.18.2 + + Version 3.18.2 + +2016-11-01 James Gallagher + + Formatting Grid.cc + +2016-10-20 James Gallagher + + Fixed Hyrax-267 + The DAP4 ce parser was not removing double quotes or the % + escaping, so names that needed those were not 'found' in the + dataset. Also, the grammar used by the parser had a syntax error + in it where /s in the id name were turned into dots and the + resulting names could not be found. The result was a valid names + were being rejected. + +2016-10-12 James Gallagher + + Corrected a comment in Vector.h + +2016-08-30 James Gallagher + + Merge pull request #22 from sharkcz/getopt + type fix for getopt + +2016-08-30 Dan Horák + + type fix for getopt + + The getopt() function uses int as return type and using char for a + variable storing the return value makes problem on platform where + char is unsgined by default. + http://www.arm.linux.org.uk/docs/faqs/signedchar.php + + Fix by Orion Poplawski for + https://bugzilla.redhat.com/show_bug.cgi?id=1366787 + +2016-08-26 James Gallagher + + Merge pull request #21 from sharkcz/dmr-bigendian-2 + add missing big endian baselines + +2016-08-26 Dan Horák + + add missing big endian baselines + +2016-08-22 James Gallagher + + Added main_page.doxygen to gitignore + +2016-07-14 James Gallagher + + Merge branch 'master' of https://github.com/opendap/libdap4 + + Added libuuid to the requirements listed in INSTALL. + Also added a comment and fixed error message text in util.cc for + void extract_double_array(Array *a, vector &dest) + +2016-06-09 James Gallagher + + Fixed a compiler warning when USE_POSIX_THREADS is not def'd in XDRStreamMarshaller + +2016-06-08 James Gallagher + + Removed generagted file since it's causing grief on Travis + + Updates for the source dist and unit-tests + +2016-05-31 James Gallagher + + Version 3.18.0 +2016-05-25 James Gallagher + + Fixed ugly DBG2() macro calls + +2016-05-18 James Gallagher + + Merge remote-tracking branch 'origin/Hyrax-98' + Conflicts: + D4Connect.cc + d4_ce/D4ConstraintEvaluator.cc + d4_ce/d4_ce_parser.yy + +2016-05-17 James Gallagher + + Merge pull request #19 from OPENDAP/response_cache + Response cache + +2016-05-16 James Gallagher + + Added recursive Sequence::reset_row_number() and comments + +2016-05-15 James Gallagher + + Minor edits after testing specializing read_row() for the ResponseCache + See bes/dap CachedSequence and CacheTypeFactory for more info. + +2016-05-13 James Gallagher + + Checkpoint - Nathan's hack for Sequence plus debugging + + Added comments for the hacked set_send_p() and set_read_p() methods + +2016-05-12 James Gallagher + + Added a (potential) fix for Vector::set_send_p() + + Hacks for the bes branch 'response_cache' + +2016-05-10 James Gallagher + + Changes for Coverity + [ci skip] + + Comments + +2016-05-09 James Gallagher + + Coverity edits - both minor + [ci skip] + + Removed extraneous include + +2016-05-06 James Gallagher + + Added parser-util.cc back in after deleting it by mistake. + +2016-05-05 James Gallagher + + Minor patches for issues found by Coverity Changes to be + committed: modified: Connect.cc deleted: parser-util.cc + + Improved error checking for the DDX parser. + When text is feed into the parser and its not valid xml, the parser + was just ignoring that. No more... + + Arrg. baselines were not in the last commit... + + Moved the newest tests' baselines to the 'universal' directory + It's time to switch the new DMR tests to ones that don't have to + have a separate set of baselines for bin- and little-endian + machines. It takes too long to build up the big-endian test + baselines (get a b-e VM, etc.) and we have a good set of baselines + for each arch so if issues pop up on one or the otehr arch, + it should be easy to catch them. + + Merge pull request #18 from OPENDAP/jg_filters + Jg filters + + Merge branch 'master' into jg_filters + Conflicts: D4Sequence.h + + Added 'universal' tests for DMRTest + The DMR includes a checksum when it's built from received data and + that checksum is a function of word order of the sender. This made + building baselines for the DMR tests a pain. We've built a number of + tests for both big- and little-endian machines - from now on new + tests should use 'universal' baselines (built by removing the checksum + using sed). + +2016-05-03 James Gallagher + + Merge pull request #17 from OPENDAP/D4Sequence_value_ref + Added a new method to D4Sequence + + Cosmetic editing + + Optimized the read_sequence_values() code. + Child sequences do not have copies of the filter clauses. These are not + needed by them since they have already been filtered. + + Added tests for complex CEs involving nested seqeunces + + Inner sequence filter issue resolved + The code still needs tests and there is a potential optimization and + the syntax needs to be documented because the way fields in a + sequence can be referenced is less general than other places in the CE. + + Fixed the issue with child sequences in DAP4 + +2016-05-02 James Gallagher + + Partial fix for Hyrax-182 + https://opendap.atlassian.net/browse/HYRAX-182 + +2016-04-29 James Gallagher + + More test baselines + + Scanner fix + + Fixed the numeric constant error with the filter code + Numeric constants were showing up as the value '1.' The checking_*() + functions were the culprit. In the process I added missing methods in + the FilterClause code and debugging in a number of places. + + Added tests for the new filter code. Not all of the tests pass... + There is some work to be done with parsing the != and ~= operators + and also mixing int and float arguments. There may be issues with + filed references as well, but we'll have to check the spec for that. + +2016-04-25 James Gallagher + + Fixed the issue with not working. + +2016-04-24 James Gallagher + + Fixed three operand filter clauses in the CE evaluator, but... + they still don't work correctly. There seems to be a problem with the + parsing of constants. + + Removed debugging statements; Using the 'ND' operator is an error. + + The parser now builds filter clauses and binds them to a D4Sequence. + Some by-hand tests show not everything works, but mostly it's a problem + with relops and String variables or constants. + +2016-04-23 James Gallagher + + Parser tweaks - relops now are returned as strings + + Fixed a problem with the TestByte, ..., classes when used for 'series values' + Some of the classes had the odd behavior that the series of values + was per-process and not per object instance. So, TestStr's value + would not repeat until a new process was started, it instead kept + increasing "... 1", "... 2", etc., across different instances of + the class. Fixing this made it easy to fix the TestD4Sequence code. + +2016-04-22 James Gallagher + + Added tests for D4Sequences, including filter clause evaluation + + Added D4SequenceTest + + D4Sequence now supports evaluating a FilterClauseList. + Added tests too. I have only tested D4Sequence::intern_data(), + not serialize(). + + D4FilterClauseList now used by D4Sequence::serialize() + + Eh, I rewrote the relops operator code. + It now uses the same approach that I used for DAP2 - a method + for each type (Byte, ...). + + Added D4FilterClauseTest + +2016-04-21 James Gallagher + + Fixed a bug in the was D4Function was used + In the ServerFunctionList and d4_function_parser.yy, I should + have been using a poiter to D4Function and not the object itself. + Now the DMRTests for functions pass + + Hacks to the D4FilterClause code + This also includes fixes to D4RValue and tests for the filter clause + code. Two minor changes to the parsers. + +2016-04-20 James Gallagher + + Merge branch 'master' into jg_filters + + Added a new method to D4Sequence + D4SeqValues &D4Sequence::value_ref(): Returns a reference to the + internal data held by a D4Sequence instance. + +2016-04-20 Nathan Potter + + Updated doxygen version number + + Merge branch 'master' of https://github.com/opendap/libdap4 + + Added debugging statements to various Sequence items. + +2016-04-18 James Gallagher + + Merge branch 'master' of https://github.com/opendap/libdap4 + + Version 3.17.3 + + Version 3.17.2; Fixed a soname error + Adding a const constructor seems to have *removed* the old constructor + as far as some code is concerned (it's still there, but with a + different mangled name). +2016-04-15 James Gallagher + + Merge pull request #15 from sharkcz/cppunit + fallback to pkg-config if cppunit-config is not available + + Fixed an error in the script related to Coverity support + The env var COVERITY_SCAN_BRANCH should have been quoted. + + Merge pull request #13 from sharkcz/dmr-bigendian + add missing big endian baselines + +2016-04-15 Dan Horák + + fallback to pkg-config if cppunit-config is not available + Recent version of cppunit packages do not distribute the cppunit-config script + and expect the usage of pkgconfig file. + +2016-04-14 Dan Horák + + remove the condition because we added the missing baselines + + add big endian baseline files for new tests + +2016-04-08 James Gallagher + + Added comments to D4 parser code. + +2016-04-06 James Gallagher + + Added dump() methods for D4Attribute and D4Attributes. + +2016-04-01 James Gallagher + + Added #include to D4RValue.h to fix an issue over in the BES + +2016-03-22 James Gallagher + + Merge pull request #11 from OPENDAP/d4-threads + D4StreamMarshaller now uses the double buffering code for transmission + +2016-03-18 James Gallagher + + Added #include for linux + + D4StreamMarshaller now uses the double buffering code for + transmission of vector and opaque data. + + Merge branch 'master' of https://github.com/opendap/libdap4 + + Possible fix for the memory issue in Hyrax-152. + Push this to github and test on linux. + +2016-03-01 Nathan Potter + + Updated getdap4 usage statement to better reflect the new behavior of the application. + +2016-02-29 Nathan Potter + + Fixed BES-102 in which getdap4 was bumbling the DAP4 CE. + +2016-02-26 Nathan Potter + + Merge branch 'master' of https://github.com/opendap/libdap4 + + Removed timing code fro MarshllerThread using our friend #if 0 + +2016-02-24 James Gallagher + + Merge pull request #9 from sharkcz/endian + fix big endian detection + +2016-02-22 James Gallagher + + Edit Int8::print_val() so that Int8 values always print as numbers. + +2016-02-20 Dan Horák + + fix big endian detection + Currently there is a endian detection during configure, but the code relies + on non-portable __BIG_ENDIAN__ define. The default actions in AC_C_BIGENDIAN + break setting the correct value for WORD_BIGENDIAN. + See also https://bugzilla.redhat.com/show_bug.cgi?id=962091#c48 + +2016-02-18 James Gallagher + + Added long-needed const constructor and operator=() + This was done to support some experiments with memory caching, + but it's a long-overdue fix in general. + + Zero-length array support: Vector::serialize() traps calls when length() == 0 + This is part of a series of changes to support netCDF files + that use zero-length arrays as arbitrary attribute containers. + + Removed old code that was commented out. + + Replaced fgets() > 0 with fgets() != 0 in the parser's main loop + A suggestions from David Binderman. + +2016-02-12 James Gallagher + + Patch for gcc 6 from Orion Poplawski + +2016-02-02 James Gallagher + + Merge pull request #8 from OPENDAP/BES-97 + Bes 97 + +2016-01-29 James Gallagher + + Modified Vector::val2buf() in support of zero-length arrays + val2buf() was throwing an InternalErr when called with a null data + pointer. I modified it so that if the pointer is null and the length() + of the vector/array is zero, it returns zero. The exception is still + thrown if the pointer is null and the length() is not zero. + + Removed #define DODS_DEBUG + + Tests for empty DAP2 Structures + + Tests for empty structures added. + +2016-01-28 James Gallagher + + Zero-length arrays now work with libdap, with caveats + I have implemented and tested zero-length arrays of the + cardinal types for DAP2 and 4. There's more to be done and + no testing outside of libdap has been done. + + Added documentation comments in crc.h + + Spelling fixes in the ChangeLog, ... + +2016-01-26 James Gallagher + + ChangeLog, et al., updated Version 3.17.0 + +2016-01-20 James Gallagher + + Removed #if 0 in the D4Function/ConstraintEvaluator::error() methods + +2016-01-19 James Gallagher + + Found one more place in the DAP4 CE and Function parser where + errors did not result in an exception. + +2016-01-13 James Gallagher + + Merge pull request #6 from OPENDAP/BES-87 + BES 87 + +2016-01-12 James Gallagher + + Added comments re how the Error object codes map to HTTP codes + + Added mapping between libdap and HTTP codes (suggestions) + + Added a code to Error; made sure Error was used consistently + Changes to mesh with those in the OLFS. Error(unknown_error, ...) + maps to an HTTP 400, internal_error --> 500, not_implemented -->501, + etc. + + DAP2 and 4 scanners now throw Error(malformed_expr, ) + They were writing to stderr... + + DAP2 CE parser uses no_such_identifier where apropos + +2016-01-11 James Gallagher + + Added codes Error response in the DAP2 parser + + There were only a handful since most of the Error throws were well + behaved. + + Modified the DAP4 ce and function parsers to include code w/Errors + See https://opendap.atlassian.net/browse/BES-87. + +2016-01-05 James Gallagher + + Merge branch 'master' into bes-40 + +2016-01-04 James Gallagher + + Modified the CRC32 byte-order change so that it's not included by default + + Popped old changes for Connect; fix for Makefile.am; etc. + +2015-12-31 James Gallagher + + Hacks for doxygen. + The Makefile works now (rebuilds doxy.conf when needed) and the docs + are better. I stopped including docs for methods w/o doc comments + and those are now listed in the doxygen_warnings.txt file. + [ci skip] + +2015-12-30 James Gallagher + + Tried CLion; added CMakeLists.txt to gitignore + + Removed the calls to DDS:timeout_{on,off} + This code is no longer used. The timeout feature of the BES is far + better. + +2015-12-08 James Gallagher + + Removed commented-out code + + Merge branch 'master' of https://github.com/opendap/libdap4 + + Float32 attributes mis-coded as Byte in the DMR + +2015-12-03 Nathan Potter + + Travis install check + + Travis install check + +2015-11-30 Patrick West + + Missing xml2 libraries in unit-tests build + Added $(XML2_LIBS) to the Makefile.am in unit-tests so that tests would + build. On Ubuntu 14.04. + +2015-11-12 James Gallagher + + I removed the deploy lines from the travis.yml file + These seem broken - I have to investigate how to handle + deployment of RPMs out of travis + +2015-10-22 James Gallagher + + Fixed error in printing of child groups. + The child group values were printed twice. + +2015-10-21 James Gallagher + + Source release version, ChangeLog, News, ... for 3.16.0 + +2015-10-19 James Gallagher + + Fixes #1 + + Added changes to exit when 'report_errors' (-r) is true + + Fix for the missing 'child groups' in getdap4's output + And some minor mods to XMLWriter - clean up the includes a bit. + +2015-10-15 James Gallagher + + Merge pull request #5 from OPENDAP/intern_no_crc32 + Intern no crc32 + + Merge pull request #4 from OPENDAP/endian_fixes + Endian fixes + +2015-10-14 James Gallagher + + Some of the DAP4-only types were not setting is_dap4 + This caused the DMR output code to throw an error (Unknown Type) in + cases where those types were used. + See https://opendap.atlassian.net/browse/HR-63 + +2015-10-13 James Gallagher + + fixed a warning in dmr-test - /* in a comment + +2015-10-13 jgallagher59701 + + Added test baselines for intern_data with no checksum + I dropped computing the checksum in the intern_data() method, + so the baselines for the tests had to be updated. + +2015-10-13 James Gallagher + + Now the intern_data() method used with DAP4 does not compute the + checksum It is only computed by the code that serializes the + response - sends it to a remote client. I added baselines + specifically for the intern tests on little endian machines. Will + add ones for big endian next... + + Merge branch 'intern_no_crc32' of + https://github.com/opendap/libdap4.git into intern_no_crc32 + +2015-10-12 James Gallagher + + Removed crc32 generation from intern_data() + This seems better - I think computing the CRC code + when interning values is not necessary. Those should + only be computed when the server is going to send values. + + Completed changes to D4UnMarshallerTest.cc + This copy of the tests should work on both little- and big-endian + hosts. + + More changes to D4UnMarshallerTest.cc + +2015-10-12 jgallagher59701 + + Partial fix for D4UnMarshallerTest.cc + +2015-10-12 James Gallagher + + Changed WORDS_BIGENDIAN to __BIG_ENDIAN__ + +2015-10-07 jgallagher59701 + + One step closer to the tests working on big endian machines + D4MarshallerTest now works + + Update to D4Marshaller/UnMarshaller tests - added baselines for + big-endian ...There's still an issue, however. + +2015-10-07 James Gallagher + + D4UnMarshallerTest also needs two sets of baselines. Added + little-endian ones. + + D4MarshallerTest need two sets of baselines... + One for big- and one for litte-endian machines. I made the little + endian set. + +2015-10-07 jgallagher59701 + + Changed to D4EnumTest to support changes for big-endian machines. + +2015-10-07 James Gallagher + + The change to D4Enum::compute_checksum() was correct. This fixes + the last of the issues with Enum on big-endian machines. The + problems we caused by the assumption that 64-, 32-, 16- and 8-bit + integers would align on the LSB, which is the case for + little-endian machines (but not big...). In several places where + reinterpret_cast<>() was used I first assigned the Enum _value_ to + a temp variable of the correct size and then passed the address of + that to the checksum calculation function. High cost in one sense, + but really, scalar Enum variables will be rare and Arrays of Enums + are handled differently. I also have dropped the overly-complex + design that used a union. It eliminated some casts, but was based + on the erroneous assumption regarding alignment. + + Test change in computing checksum for big-endian machines. + + Debugging big-endian issues still.. + +2015-10-07 James Gallagher + + Merge branch 'endian_fixes' of https://github.com/opendap/libdap4 + into endian_fixes + + Cleanup of a version of this that should work on big and + little-endian machines. + +2015-10-06 jgallagher59701 + + Baselines for Enum tests on big endian machines + Now only the tests 54 and 55 fail on the big endian machines... + +2015-10-06 James Gallagher + + Removed union in the D4Enum implementation + This is an attempt to fix the issues found on big endian machines. + +2015-10-01 James Gallagher + + The DMR tests now work on little endian machines, mostly + There is an issue with D4Enum that causes some tests to fail. + +2015-10-01 jgallagher59701 + + Fixes for function/ce tests modified: tests/DMRTest.at + +2015-09-30 jgallagher59701 + + Baselines for a big endian machine. + +2015-09-30 James Gallagher + + Fail: fixed a syntax error that got pushed. + + Added capability to build baselines that's sane + + Continue with fixes for data + ce tests + + DMRTest is dependent on word order - this is a trial fix. + +2015-09-29 James Gallagher + + Fixed an issue in HTTPCacheTest; version number bump. + In HTTPCacheTest, a quirk of RCReader meant that when the tests + were run as part of distcheck they would fail if the user has set + USE_CACHE to 1 in their ~/.dodsrc. Version numbers bumped to 3.15.1. + +2015-09-23 James Gallagher + + Merge branch 'master' of https://github.com/opendap/libdap4 + + Fixed a syntax error in cgi_util.h + ... and added commentary to MarshallerThread.cc + +2015-09-20 James Gallagher + + Version 3.15.1 + +2015-09-18 James Gallagher + + Added empty Coverity model file + + Fixed a bug in the DAP4 CE evaluator + The DAP4 CE evaluator was using just the stop index value + as the size of a dimension when testing if the stride value + was valid. When a dimension of length 1 (i.e., [0:0]) was + found, it's length was '0' and a stride of '1' was invalid. + I fixed this and the test now uses stop-start +1 for the + dimension length. + +2015-09-17 James Gallagher + + HTTPCacheTable uses verbose errors + ... instead of obscure error codes + + Modified configure.ac to fail if uuid.h is not found. + +2015-09-16 James Gallagher + + Added #error if uuid.h is not found modified: DODSFilter.cc modified: tests/ResponseBuilder.cc + + Fixed a syntax error modified: tests/ResponseBuilder.cc + + An attempt to fix the uuid.h header issues. + I added a test to test for uuid.h in addition to uuid/uuid.h + in the hope that it'll find something and compile. I may have to + work on this a bit more since it's caused such a hassle on the + different build systems. + +2015-09-15 James Gallagher + + Minor issue in D4EnumDefs.cc with range checking + + Added #include where needed + +2015-09-14 James Gallagher + + Minor fix for precision in Float64, other fixes. + + CID 81430, CID 81412, CID 81409, CID 81405, CID 81402, CID 81401, + CID 81397, formatting, CID 81389, CID 81382, CID 81377, CID 81375, + CID 81373, CID 81370, CID 81363, CID 81362, CID 81361, CID 81360, + CID 81359 + +015-09-10 James Gallagher + + Updated files for the release of 3.15 + +2015-09-09 James Gallagher + + Merge branch 'master' into parallel_io + + An error introduced in March has been fixed; Freeform tests now + pass. Commit 6b005387 introduced a change where set_read_p() no + longer tested the state of the is_synthesized property. Before, if + the property was set the method would not change the state of + is_read. The change made it so the is_read property was changed + regardless of the state of is_synthesized. This broke the + 'projection functions' in the FreeForm handler. I have reverted to + the old libdap behavior, and the tests now pass. + +2015-09-04 James Gallagher + + Removed the old serialize_no_release() method + +2015-09-02 James Gallagher + + Now has a special class for RAII on the lock in the child thread. + +2015-08-31 James Gallagher + + Cleaned out more dead code from Vector.cc + +2015-08-28 James Gallagher + + Updated the version number to 3.15 + This because of the parallel IO code and pipelining support. + +2015-08-28 James Gallagher + + Removed old code from MarshallerThread refactor. + + Moved the pthread code from XDRStreamMarshaller + It's now in MarshallerThread and used by XDRStreamMarshaller via the + 'pointer to an implementation' pattern. This means that the pthread.h, + which configure looks for but may not find, is not included in XDR- + StreamMarshaller's header anymore. + + I did not mention this in the git log, but I've removed the + serialize_no_release() method I added after refactoring the code + so that it wasn't needed (I moved the 'release' calls to the BES + dap module, so libdap's behavior is not changed). + +2015-08-27 James Gallagher + + Added support for put_vector_part() to D4StreamMarshaller + + Refactor: CLEAR_LOCAL_DATA and USE_POSIX_THREADS + We found that we could call clear_local_data() right after variables + were serialized and get a much flatter memory profile. However, putting + those calls in serialize() changed its behavior. I've moved them to + bes/dap/ResponseBuilder - but that is controllable using a compile-time + directive. Similarly, the compile-time switch USE_POSIX_THREADS + can be used to turn off that feature (#undef). See XDRStreamMarshaller. + + Refactor: Parallel I/O methods + I refactored the three new parallel I/O methods back into the matching + put_vector() methods of the same/similar names. This cuts down on the + naming mess and means that other classes like XDFFileStream don't have + to include hacked methods that throw exceptions. This commit also + includes changes that wrap the clear_local_data() calls in #ifdef + CLEAR_LOCAL_DATA. Lastly, this refactor will also require a matching + edit to ncml_module. + +2015-08-24 James Gallagher + + Patch/fix/hack for XDRStreamMarshaller + This class' header requires pthread.h but that makes for issues since + we cannot put an include of config.h in a .h file that is installed. + To get on with testing, I'm checking in this version that just includes + it; later on I may modify the class to encapsulate the pthreads stuff. + + Fix for clear_local_data() race condition + Took advantage of the current XDRStreamMarshaller impl that copies + data before writing it out. This means our code can delete the original + data held by the object right after the thread used to send that data + has been called (actually, it could delete it _before_ starting the + thread, but doing so afterward keeps that feature out of the marshaller + class, which may come in handy later on...). Later on we may switch + to reference counted pointers and then this issue will go away. + + Removed call to clear_local_data() in Vector::Serialize() + This was colliding with the call to clear_local_data() in the thread + when put_vector_thread() was called. This showed up as a 'sometimes' + failure of expr test #21. The bug happens only when serializing an + array of structure that contains arrays. + +2015-08-22 James Gallagher + + Corrected a spelling mistake with 'serialize_no_release' + + I've added synchronization code to the XDRStreamMarshaller dtor + This causes the XDRStreamMarshaller dtor to block on the d_out_mutex + until the child thread (possibly) writing to the output stream + completes. + +2015-08-21 James Gallagher + + Added parallel_io to the set of branches Travis will build. + + Now threads in XDRStreamMarshaller are created detached state + + Added the new put_vector_thread methods to Marshaller + I made them have a default implementation so that child classes + won't break. I'm not sure if that's the best way to go... + Also dropped the new *_thread() methods into Vector::serialize(), + but they don't work so those are turned off by using #undef + USE_POSIX_THREADS + + Parallel I/O for vector data maybe working + I have coded put_vector_thread() and put_vector_part_thread() and + they are passing unit tests. + +2015-08-18 James Gallagher + + Removed some of the TODOs in libdap + [ci skip] + +2015-08-17 James Gallagher + + Merge branch 'master' of https://github.com/opendap/libdap4 + + Modified Vector::get_buf() so it can be used in the ncml handler + +2015-08-17 Nathan Potter + + Merged the new (template-less) version of Vector into master branch + +2015-08-17 James Gallagher + + Completed DAP2 put_vector_part() and associated methods. + +2015-08-14 Nathan Potter + + Typo + +2015-08-14 James Gallagher + + Partial Vector/Array serialization partly working! + Must still implement the parts for Arrays of int16, ..., types; + only byte arrays work now. + + Checkpoint: Adding methods to allow serialization of Vectors in parts. + Missing methods in D4StreamMarshaller, XDRFileMarsahller and parts of + methods in XDRStreamMarshaller. + + Added three protected accessors to Vector + These provide access to the underlying data held in a Vector/Array + and are accessible to descendant classes only. + +2015-08-14 Nathan Potter + + Replaced template methods in Vector.cc with more pedantic + implementations. Made the templed methods private and used them in + the implementations of the new pedantic methods. + +2015-08-10 James Gallagher + + Bumped up the version number to 3.14.2 + + Bumped up the version (and SO numbers). This is 3.14.2 + + Merge branch 'serialize_n_release' + + Added a serialize_no_release() method + This rounds out the serialize_n_release branch. The new behavior of + freeing the dynamic memory is very desirable, so it's the default, + but if some code wants to serialize, do something else and then delete + the storage, the new serialize_no_release provides that option. All + of our libdap and bes/module tests pass. This will be merged to the + master branch once the latter has been tagged. + +2015-08-07 James Gallagher + + Fixed overwriting commit for Vector::serialize() + We decided to make the serialize and release code real, but in the + process that collided with changes to BaseType::name() (making it + a virtual method). One line was broken, but since it was the call to + clear_local_data() in Vector::serialize(), it was an important line! + +2015-08-06 James Gallagher + + distcheck works locally + + Fix for distcheck and getdapTest + +2015-08-05 James Gallagher + + make check now supports parallel execution + Modified test/Makefile.am so that autotest parallel tests work. Run + make check with TESTSUITEFLAGS=-jN + +2015-08-05 Nathan Potter + + pulled from github and merged conflicts + + Refactored use of the name() method in BaseType so that it is now override-able + +2015-08-05 James Gallagher + + Added BaseType::clear_local_data() + The clear_local_data() method is used to free just the parts of a + BaseType (child of, really) that hold large amounts of data. It applies + to only a handful of the variable types (Vector, Sequence, D4Opaque and + D4Sequence). It is used to free memory right after data values are + sent to the client using serialize(). This means that data are no longer + held by the variable objects once serialize() returns - at least for + Array, Sequence, ... Other types like Byte, Int16, ..., will retain + their values because those values are not held in dynamically allocated + storage. + +2015-08-04 James Gallagher + + Hacked the marshT unit-test + I modified this so that it no longer relies on data being present after + serialize() has been called. It seems that none of the handlers use + this 'feature' and we can change the code so that memory is freed right + after the bits are sent fir each variable. This means that only one + variate's data are in memory at any given time. + +2015-08-03 Nathan Potter + + Checking in change to _always_ release array memory after serialization. This causes the Marshaller test for Structure to seg fault. + +2015-07-14 James Gallagher + + Removed the bison version print out + ...I think this is working fine now + [ci skip] + +2015-07-13 James Gallagher + + Added distcheck to travis + + travis caching + Modified travis.yml and install_libdap4_source_deps.sh so that + the bison source build is cached. + + Another fix: added '/bin' to $HOME/bison in $PATH + + Removed odd ^S from install_libdap4_source_deps.sh + + More travis hacks - added sudo: false back in; remove tabs + +2015-07-12 James Gallagher + + travis: The build was not triggered with the last push... + ... so try removing 'sudo: false' + + Switched travis to use docker/containers + +2015-07-09 James Gallagher + + Added .NOTPARALLEL: back to the C++ parser Makefile.am files + We need this because stack.hh, ..., are built in one shot by + bison. + + Updated the library version + + Grammar fixes - for flex and its C++ scanner code. + There is a conflict between the defined return type of + FlexLexer::LexerInput. Some versions of flex define that + method as returning an int and some a returning a size_t. + Including generated code using one will break when a build + machine uses the other. I used dist-hook to just 'rm' those + generated lex.*.cc files from d4_ce and d4_function - the + other generated scanners are OK. This is really a work-around + since we should be able to include the generated scanners + and then drop the build requirement for both bison and flex. + +2015-07-07 James Gallagher + + Added absolute-header-m4 and removed .gitignore + I think the .gitignore is from the gnulib source + and is fine for their git repo, but not for clients + of the library. + + Changed the tag for non-master travis builds to _travis + ... from -travis. Minor, but it bugged me. + + Modified travis.yml so that branches ending in -travis will be built. + + Updated gnulib code - this may affect the coverity results + +2015-07-02 James Gallagher + + Coverity fix in dds.yy + Fixed a potential leak where storage allocated is overwritten. + An unlikely bug, but not a big deal to fix. + + Issues found using coverity fixed + +2015-06-29 James Gallagher + + Fixed cppunit package for travis.yml + + Fixed a memory leak in dds.y + +2015-06-26 James Gallagher + + Minor syntax error in TestArray; Travis now includes cppunit tests + + Memory leak in TestArray.cc plugged + + More changes WRT coverity and travis + ...in the travis.yml. Removed the before_build_command + stuff from the coverity scan addon section since it seems + to be redundant. + + Added coverity scan to travis CI + +2015-06-25 James Gallagher + + Merge branch 'master' of https://github.com/opendap/libdap4.git + + Issues found in D4ParserSax2, DAS and DDS using Coverity. + +2015-06-20 James Gallagher + + Finally fixed...? + Using autoconf 2.69 make dist is building a tar-gz that includes the + BUILT_SOURCES, which would be great for builders without bison 3. I'm + not sure if that will work with autoconf 2.63. + +2015-06-19 James Gallagher + + Move the D4 Function parser to its own directory. + This is an attempt to really fix the stack, position and location.hh + problem for once and for all. The Travis build is working, but maybe + we're just lucky because a build from the tar.gz made using make dist + was still failing. + +2015-06-18 James Gallagher + + Merge branch 'master' of https://github.com/opendap/libdap4.git + +2015-06-18 Ubuntu + + Modified build-deb-pkg.sh so that dap-config matches the installed + files. + +2015-06-18 James Gallagher + + Merge branch 'master' of https://github.com/opendap/libdap4.git + + Try using NOTPARALLEL with the libdap build - another travis hack. + +2015-06-18 Ubuntu + + Modified DEBIAN/control to use libcurl4-openssl-dev as a dependency. + Not sure why this was not working before. + [ci skip] + + Merge branch 'master' of https://github.com/opendap/libdap4 + + Added hackery script to DEBIAN directory. + This will help with my simple deb file used for the bes build. + +2015-06-18 James Gallagher + + Modified Makefile.am and d4_ce/... so bison is called only once + This should stop the semi-random build fails for libdap4 on Travis + and for other people. + +2015-06-17 James Gallagher + + Added .NOTPARALLEL to d4_ce/Makefile.am for the grammar files + I hope this fixes the odd issue with the generated grammar files + breaking hte build in a random fashion. The error may be do to parallel + builds running bison several times for each of the generated files, all + at the same time. + + Cleaned up the d4_ce code + The Makefile.am had leftover parts from the attempt to make the build + work when bison 3 was not present. That may have been the cause of the + seemingly random build errors I see on Travis CI + + Added dpkg -l to travis.yml + This is part of debugging the bes travis build - I cannot seem to sort + out the libcurl dependency since my Ubuntu AMI is a 14.x but the build + is using 12.x + + travis bison hack... + +2015-06-17 Ubuntu + + Merge branch 'master' of https://github.com/opendap/libdap4 + + Changed DEBIAN/control arch to amd64 + +2015-06-17 James Gallagher + + travis.yml edits + Now uses deploy section only for tagged pushes; removed unneeded + --force-yes with apt-get; and will build tags in addition to the + master branch. + + Fixed build-deb-pkg.sh and added 'deploy' section to travis. + Not sure about the deploy bit... + +2015-06-16 James Gallagher + + Use newly built bison 3 + Added /usr/local/bin to PATH... + + Build bison from source (circling back) + Travis-CI is currently using ubuntu 12 which using bison 2; Ubuntu 14 + has bison 3 in its package system. + + More tweaks to the simple deb package build + This is a deb package for use with travis-ci when we get a bes CI + build working + + Added debian package build to travis.yml + ...also tried using --force-yes to get bison to load with apt-get + [ci skip] + +2015-06-16 Ubuntu + + Added the build-deb-pkg.sh script. + + Added DEBIAN/control + Use this along with dpkg-deb --build libdap_ to make a + debian package. + +2015-06-16 James Gallagher + + Another change to the travis.yml file + It now uses bison installed using apt-get + +2015-06-08 James Gallagher + + travis-ci error fixed + I left --prefix=... on the configure of bison but $prefix's definition + was moved to a later step. + + Reorganization of the travis.yml script. + +2015-06-07 James Gallagher + + clang... + + ...another try at clang support + + ...another try at clang + +2015-06-06 James Gallagher + + minor tweak to 'make install' with sudo -i + + fix for clang + + more travis ci changes - learning what this can do... + + Added some package installs to travis ci + + travis ci fix + + travis ci: added install of libdap + + travis ci change: install bison 3 using sudo + +2015-06-05 James Gallagher + + Travis CI updates + + Added an initial .travis.yml file + +2015-06-01 James Gallagher + + Added DBG2N - no line number version of DBG2 + +2015-05-24 James Gallagher + + Fixed comment + +2015-05-14 James Gallagher + + Added a new function to make a temp file using mkstemps() + This new function returns the temp file's name and a C++ fstream + object that is opened to the temp file. This change adds to the + libdap API. + +2015-05-12 James Gallagher + + Fixed missing include. Patch from O. Poplawski. + + Fix getopt() use. Patch from O. Poplawski + + Broke --enable-developer; now fixed. + + Fixed issues in configure and Makefiles reported by O. Poplawski. + --enable-coverage and --enable-valgrind were broken. + Setting CXXFLAGS to nothing broke builds on fedroa 21 + + Added coverage file to gitignore + +2015-05-01 James Gallagher + + Formatting, old code removeal, doxygen updates + +2015-04-05 James Gallagher + + Bison 3 is required for the build. + This might change, but for the next release (April 2015) it is a + requirement for building the code. It will be possible to get the + devel rpm and, however, and develop code that uses libdap w/o + needing bison 3. + +2015-04-03 James Gallagher + + Bumped up the library versions + Each of libdap, libdapclient and libdapsever likely have new interfaces + but the old interfaces are intact, so CURRENT and AGE are increased, + and REVISION is reset to 0. + + Various changes as needed for the release. + +2015-03-31 James Gallagher + + Added to gitignore + + Hacked more for the FlexLexer.h kludge... + + Resurrected the D4*Scanner.h files + + Modified the FlexLexer.h scheme + Now it fits more with the way I'm handling the other generated + files. The header is in the generated files dir and is only + copied out when bison 3 is not found. It's still a kludge... + + Added FlexLexer.h + This might be a mistake - remove if this fails on linux, etc. + +2015-03-30 James Gallagher + + Rest of changes for template grammar files + Now if bison 3.0+ is not found, the build will use + template versions of the generated grammar files. This + removes the dependence on bison 3.0 for most source + builds. Of course, if you want to modify the D4 grammar + you need bison 3. + + Added generated grammar sources + When a machine does not have bison 3, it's a pain to install it, esp. + at a site that doesn't let the builder do that! The work-around is to + provide a backup set of sources that can be copied by the Makefile in + lieu of the real thing. + +2015-03-25 James Gallagher + + Merge branch 'master' of https://github.com/opendap/libdap4 + + Dropped the autoconf required version to 2.63 + This accommodates RH/CentOS 6. I also had to change the AT_ARG_OPTION_ARG + calls so that multi-word options used underscores and not dashes. + + Change to libdap.spec + I've added the dist info to the RPM and fixed up some of the + description, too. + +2015-03-17 James Gallagher + + Edited comments and reformatted. + Also added a new method to Sequence that I subsequently removed + from the code using the preprocessor. ...it might come in handy + some day. + + Refactored TestCommon + Now TestCommon.cc is not used; the constructors, etc. are now + in TestCommon.h. In the future this class could be modified so + that the 'series_values' property was accessible from TestCommon + and not just the individual type classes. + + Change a comment emmited by configure less convoluted. + +2015-03-16 James Gallagher + + Merge branch 'master' of https://github.com/opendap/libdap4 + + Changes to build and to the Test* classes. + Now configure tests for the uuid/uuid.h header (before it tested only + for the library which meant that installing the libuuid package and + not the needed libuuid-devel package would pass configure but fail + during the build). I also removed some unused code from the Test* classes. + The TestCommon.cc file was not needed. + + rpm target fixes + Added getdap4 to the files list for RPM and fixed distclean/distcheck + where some DMR tests are marked xfail (expected fail). With autotest, + xfail leaves some cruft behind in the DMRTest.dir directory that + ./DMRTest --clean should remove (see the clean-local makefile target). + However, because a space was added to the names in the Makefile.am, + those --clean calls were not being made. Fixed. Somewhat exasperating... + +2015-02-26 James Gallagher + + Merge branch 'master' of https://github.com/opendap/libdap4 + + Added convenience function to util + The new function extracts doubles from numeric arrays using a vector + to cu down on memory management complexity (at the cost of an addition + to the interface). + +2015-02-21 James Gallagher + + Fixed GetOpt.cc includes + This code used old includes (string.h, etc.) and was missing + stdio. I added and changed stdlib.h and string.h to + cstdlib and cstring. Removed a duplicate header. Build now works + on AWS Centos 6. + +2015-02-19 James Gallagher + + More Cleanup + I re-indented TestSequence and removed some old code + as part of my work on the aggregation server (which + may not make it into the BES, another story...) + + Cleanup + I removed unused code from Sequence, DDS.cc and AttrTable.cc + +2015-02-09 James Gallagher + + Tweaked D4Sequence for BES function's tabular() + The d_length field is now set when set_values() + is called. + +2015-02-02 James Gallagher + + Removed incorrect comment in Vector.h + +2015-01-14 James Gallagher + + Added a type_name() function to util.cc/h + In the merge with DAP4, type_name() split into D2type_name() and + D4type_name(). That removes the symbol 'type_name()' from the library. + I added a function with that name that first tries to apply D4... and + then D2. If both fail, an exception is thrown. This provides forward + looking backward compatibility. + + Fixed an issue with String constant arguments to DAP4 functions. + The function parser for DAP4 was including the double quotes that + delimit string constants in the value itself. I modified the parser + to eliminate those. + +2015-01-07 James Gallagher + + HTTPCacheTest was failing; fixed. + The test assumed that the number of headers for a cached response + would equal the headers in a response. This might not be the case. + For example, the received response might include a Keep-Alive header. + + Error in D4Maps copy ctor fixed. + This addresses an error in the DMRTest.cc unit test for the DMR + class. DMR's copy ctor was failing to copy the maps from Arrays + in the src object to the dest object because of an error in the + D4Maps m_duplicate method. + + Corrected some comment text in DMR.cc + +2015-01-05 James Gallagher + + Minor changes to the Makefile.am files + +2014-12-31 James Gallagher + + Merge remote-tracking branch 'origin/dap4' + Conflicts: + unit-tests/HTTPConnectTest.cc + +2014-12-12 James Gallagher + + Added eclipse meta files to gitignore. + + Merge branch 'master' of https://github.com/opendap/libdap + + comment change in main_page.doxygen + +2014-12-03 James Gallagher + + Added files left over from a full build to gitignore + +2014-11-19 James Gallagher + + Added .gitignore and hacked up Makefile.am so that it uses subdir-objects. + +2014-10-21 James Gallagher + + Added a valgrind 'suppressions' file for OSX 10.9. Not sure where + this is going, but valgrind for unit tests and regression tests is + probably a good thing. + + Fixed to the Makefile.am in d4_ce so that the grammar for DAP4 builds correctly. + +2014-10-20 James Gallagher + + Changed HTTPConnect.cc on the release branch and then copied here. Minor fix in the test baseline to accommadate a change in the test host (etag changed with a change in the Apache server version). + M unit-tests/HTTPConnectTest.cc + +2014-10-16 James Gallagher + + Fixed ticket 2260 - The issue where the DMR copy ctor built a DMR that would subsequently cause a segfault has been fixed. The problem was in Array::update_dimension_pointers(). The D4Dimension objects were not copied correctly between the old and new set of D4Dimensions in the Groups in the source and dest DMRs + +2014-10-14 James Gallagher + + Leak fix in the D4CE code - both the CE and Functions parser leaked memory - fixed. Tests HTTPCacheTest and HTTPConnectTest fixed - there were two issue with the new test.o2 server. Array.h - removed the D4Dimensions.h include. + + Bug fix in ServerFunction: The setFunction setters in ServerFunction were setting other function pointers to null so that the ServerFunction object could hold only one kind of function for any given name. If we want linear_scale, e.g., to work for both DAP2 and DAP4, the class has to be able to support a btp_func and D4Function. I fixed the ctors and setters so that works. + +2014-09-17 James Gallagher + + Renamed the bison 'Driver' classes to be D4ConstraintEvaluator and D4FunctionEvaluator. Added an eval() method to the latter so that the BES can evaluation DAP4 Functions in a some sort of a sane way. I also moved the old and crufty D4ResponseBuilder code out of the libdap dir; there's a class with the same name in tests, but it does different (and more sane) things. + + Comments and changed template type from 't' to 'T'. + + Comments... + +2014-09-12 James Gallagher + + Fixed FQN() for Constructors that hold arrays. + +2014-09-11 James Gallagher + + I removed the DMR& parameter to BaseType::intern_data(). This seems to be a relic from DAP2 (where it may not have actually been used either) and it makes employing delegation somewhat harder in the 'transmitter' handlers I'm working on porting over to DAP4. + + Modified to use the new 'DMR-free' version of intern_data(). Also fixed the help message. + + Added a DMR that holds a nested sequence so this can be tested - currently does not work and is not part of the tests. + +2014-09-08 James Gallagher + + Marked four tests a xfail. See ticket #2244 + M tests/DMRTest.at + + I added specializations for transform_to_dap4() to TestStructure and TestSequence so that, among other things, the dapreader code can build DAP4 data responses using DAP2 response files. + +2014-09-05 James Gallagher + + Fixed hiccup in Makefile.am that prevented the D4Connect.h header from being installed - it was not listed in pkginclude_HEADERS. Added subdir-objects to the options list in the Makefile.am too. Fixed an error message in getdap4 - text change only. + +2014-09-04 James Gallagher + + Added a FQN() method that will return the FQN for any variable, including the group part. Also added a decent comment on the D4Group::find_var() method. Unit tests added to the D4GroupTest unit test. + +2014-09-03 James Gallagher + + Removed duplicate code in Sequence and Structure for the transform_to_dap4() method; moved the common code to Constructor. + +2014-08-27 James Gallagher + + Added an update of the weak pointers held by Array so that the DMR::m_duplicate() method can use D4Group::ptr_duplicate(). This was useful when porting the gdal_handler code to DAP4. + +2014-08-21 James Gallagher + + Change to Constructor::serialize(). It now calls read(). This was removed because it broke the behavior of D4Sequence - at least in the test code (it still does). More work needs to be done on this, but the FreeForm handler, which builds sequences is working with D4Sequence (DMR/DAP4 Data via the DDS --> DMR hack). I will need to revisit this... + +2014-08-19 James Gallagher + + Now has a separate method to 'blend in' variables from a DDS into the DMR. This is used by the DDS-ctor for DMR but it can also be called on an existing DMR like the ones built by the BES. This was done so that information set when the BES builds the DMR won't be lost. + + Changes to the transform_to_dap4() method - both Sequence and Structure have implementations that work for tests and clients, but not most servers. Array should be pretty general and may work for most handlers. + +2014-08-18 James Gallagher + + Removed unused code - part of an aborted attempt to add a factory class as a param to BaseType::transform_to_dap4(...). Instead, it's likely better to subclass that method in the types like FFSequence (in the handlers). In the process I found a fair amount of old code that was commented out and removed it. + +2014-08-15 James Gallagher + + Fixed an issue in Array where D4Dimensions made using the 'transform dap2 to dap4 code' with the same name but different sizes could be made. This was bad; I changed the code so that when a name conflict is found, the D4Dimension will have a different name (made using the orig dimension name and the variable name). In this case, the fist D4Dimension gets to use the name exactly as it appears in the Array while the second D4Dimension gets a more cumbersome, but unique, name. + + Fixed the order of printing of attributes and variables for constructor types when working with DAP2 responses (i.e., the DDX). The order (attr; vars) is reversed in DAP4. + +2014-08-14 James Gallagher + + Patch for getopt applied + _M . + M unit-tests/DDSTest.cc + M unit-tests/DDXParserTest.cc + MM unit-tests/SequenceTest.cc + M unit-tests/ServerFunctionsListUnitTest.cc + + Patch from Orion Poplawski to fix some of the getopt calls in the unit-tests + M unit-tests/DDSTest.cc + M unit-tests/DDXParserTest.cc + M unit-tests/SequenceTest.cc + M unit-tests/ServerFunctionsListUnitTest.cc + +2014-08-11 James Gallagher + + Changes from H 1.9.9 on the trunk + +2014-08-09 James Gallagher + + Merge from the trunk to include changes for Hyrax 1.9.9 + _M . + M RValue.cc + M XMLWriter.h + M configure.ac + M ChangeLog + M main_page.doxygen + M INSTALL + M libdap.spec + M README + M Makefile.am + M NEWS + M ce_expr.yy + M unit-tests/HTTPConnectTest.cc + M doxy.conf + +2014-08-08 James Gallagher + + Version 3.13.3 + M ChangeLog + M INSTALL + M NEWS + M README + M configure.ac + M doxy.conf + M libdap.spec + M main_page.doxygen + + Fixed a leak in RValue.cc using a patch from Aron (ADB) + +2014-08-08 James Gallagher + + Fixed a leak in RValue.cc using a patch from Aron (ADB) + +2014-08-01 James Gallagher + + Release checklist steps for 3.13.2 + + M ChangeLog + M INSTALL + M NEWS + M README + M configure.ac + M doxy.conf + M libdap.spec + M main_page.doxygen + +2014-08-01 James Gallagher + + Release checklist steps for 3.13.2 + + M ChangeLog + M INSTALL + M NEWS + M README + M configure.ac + M doxy.conf + M libdap.spec + M main_page.doxygen + +2014-07-30 James Gallagher + + Second fix for ticket 2240 - SCAN_STR from the CE scanner + was leaking because the parser did not delete the reutrned + string. I also modified the first fix so that a local tmp + string was used instead of new dynamic storage. + + M ce_expr.yy + +2014-07-30 James Gallagher + + Fixed a memory leak reported by Aron.Bartle@mechdyne.com. See ticket + 2240. + + M ce_expr.yy + +2014-07-30 James Gallagher + + I added a comment about a potential memory leak to ce_expr.yy + +;; Local Variables: +;; coding: utf-8 +;; End: +2014-07-30 James Gallagher + + Second fix for ticket 2240 - SCAN_STR from the CE scanner + was leaking because the parser did not delete the reutrned + string. I also modified the first fix so that a local tmp + string was used instead of new dynamic storage. + + M ce_expr.yy + +2014-07-30 James Gallagher + + Fixed a memory leak reported by Aron.Bartle@mechdyne.com. See ticket + 2240. + + M ce_expr.yy + +2014-07-30 James Gallagher + + I added a comment about a potential memory leak to ce_expr.yy + +2014-05-07 James Gallagher + + spelling + +2014-05-07 James Gallagher + + Fixed a test baseline in HTTPConnectTest so that it will work with + the new headers returned by H 1.9.3 (which include so things from + DAP4). + +2014-05-05 James Gallagher + + namespace libdap addex + + M XMLWriter.h + +2014-05-02 James Gallagher + + Minor edit to Makefile.am for builds on centos 5 - there libgl + builds a copy of unistd.h that doesn't work with flex generated cc + files. So I pulled -I./gl from CPP flags for the parsers library. + +2014-04-22 James Gallagher + + Updated for 3.13 + + M INSTALL + M README + M configure.ac + +2014-04-15 James Gallagher + + Updates to NEWS, ChangeLog to match the bump in the vesion number. + +2014-04-11 James Gallagher + + Added comment info to abi_checker.xml.in so we'll remember how to use the abi checker. + +2014-04-11 James Gallagher + + path fix/typo + +2014-04-11 James Gallagher + + Added abi_checker.xml.in file for the perl-based abi checker. Seems to work only with gcc. + +;; Local Variables: +;; coding: utf-8 +;; End: +2014-04-11 James Gallagher + + Added comment info to abi_checker.xml.in so we'll remember how to use the abi checker. + +2014-04-11 James Gallagher + + path fix/typo + +2014-04-11 James Gallagher + + Added abi_checker.xml.in file for the perl-based abi checker. Seems to work only with gcc. + +2014-03-26 James Gallagher + +2014-02-05 James Gallagher + + Fixed the SequenceTest 'baseline' regex. + + M unit-tests/SequenceTest.cc + +2014-02-04 James Gallagher + + Unit test updates after the OS upgrade from OSX 10.8 to 10.9. + There seems to be an issue with the sizeof(std::string) and also + with libxml2 and/or my code to read from a C++ stream and have + libxml2 parse text/xml. For now I'm removing these tests... but + will return to the libxml2 issue later (the sizeof(std::string) + maybe a bad idea since that could be pretty variable between + libstdc++ versions - I might just drop that test). + +2013-11-05 James Gallagher + + Minor formatting changes + +2013-10-24 James Gallagher + + All tests now pass for array projections for server function + arguments. + +2013-10-24 James Gallagher + + tests pass for array projections in function arg lists + +2013-10-24 James Gallagher + + Fixed an obscure bug in the test function - for the scalar case it + was not reading the arg's value. + +2013-10-23 James Gallagher + + missed one baseline for the function stuff... + +2013-10-23 James Gallagher + + Added support for array subsetting (projection) in the argument + list of a function. Tests need work and there's an issue when the + function arg is a scalar not an array. Also, the intern_data() + methods don't work, but this may be a larger issue with the hack + to the function evaluation scheme added for 'grads syntax' + support. + +2013-10-21 hyoklee + + fixed the bug #2016. + +2013-10-21 Hyo-Kyung Lee + + * escaping.cc (escattr): fixed the double escaping problem by changing + the order of the first two while() loops. + +2013-10-18 James Gallagher + + svn:ignore + +2013-10-17 James Gallagher + + Manually patched changes to Sequence::read_row and + TestSequence::read() from the DAP4 development branch so they work + with the newly modified handlers (the DAP4 branch uses _trunk_ copies + of the handlers). We will have to document this change before + releasing the code! + + M tests/TestSequence.cc + M Sequence.cc + +2013-10-17 James Gallagher + + Added to svn:ignore + +2013-10-15 James Gallagher + + Added + + A gl/glthread + A gl/glthread/threadlib.c + A gl/glthread/lock.c + A gl/glthread/lock.h + +2013-10-15 James Gallagher + + Added + + AM config.rpath + +2013-10-15 James Gallagher + + Added to gl + + A gl/m4/lib-link.m4 + A gl/m4/lock.m4 + A gl/m4/lib-prefix.m4 + A gl/m4/lib-ld.m4 + A gl/m4/eealloc.m4 + A gl/m4/extern-inline.m4 + A gl/m4/threadlib.m4 + A gl/wctype-h.c + A gl/unistd.c + +2013-10-15 James Gallagher + + Updated gnulib. + + M XMLWriter.cc + M conf/snippet/c++defs.h + M conf/snippet/warn-on-use.h + M conf/snippet/arg-nonnull.h + M Byte.h + M XDRUtils.cc + M ServerFunction.cc + M Url.cc + M XDRFileUnMarshaller.cc + M gl/m4/fcntl-o.m4 + M gl/m4/configmake.m4 + M gl/m4/localcharset.m4 + M gl/m4/locale_h.m4 + M gl/m4/sys_types_h.m4 + M gl/m4/mbrtowc.m4 + M gl/m4/btowc.m4 + M gl/m4/longlong.m4 + M gl/m4/00gnulib.m4 + M gl/m4/ssize_t.m4 + M gl/m4/glibc21.m4 + M gl/m4/localeconv.m4 + M gl/m4/codeset.m4 + M gl/m4/alloca.m4 + M gl/m4/off_t.m4 + M gl/m4/locale-fr.m4 + M gl/m4/langinfo_h.m4 + M gl/m4/wint_t.m4 + M gl/m4/mbsinit.m4 + M gl/m4/stdint.m4 + M gl/m4/gnulib-common.m4 + M gl/m4/stdbool.m4 + M gl/m4/malloc.m4 + M gl/m4/regex.m4 + M gl/m4/warn-on-use.m4 + M gl/m4/wchar_h.m4 + M gl/m4/gnulib-comp.m4 + M gl/m4/unistd_h.m4 + M gl/m4/wcrtomb.m4 + M gl/m4/locale-zh.m4 + M gl/m4/stddef_h.m4 + M gl/m4/gnulib-cache.m4 + M gl/m4/include_next.m4 + M gl/m4/mbstate_t.m4 + M gl/m4/wctype_h.m4 + M gl/m4/nl_langinfo.m4 + M gl/m4/locale-ja.m4 + M gl/m4/wchar_t.m4 + M gl/m4/multiarch.m4 + M gl/m4/stdlib_h.m4 + M gl/m4/mbtowc.m4 + M gl/m4/gnulib-tool.m4 + M gl/m4/byteswap.m4 + M gl/m4/extensions.m4 + M gl/localcharset.c + M gl/mbsinit.c + M gl/regex_internal.c + M gl/malloc.c + M gl/byteswap.in.h + M gl/localcharset.h + M gl/locale.in.h + M gl/regex_internal.h + M gl/sys_types.in.h + M gl/wcrtomb.c + M gl/btowc.c + M gl/regexec.c + M gl/ref-del.sin + M gl/mbtowc.c + M gl/regcomp.c + M gl/mbtowc-impl.h + M gl/streq.h + M gl/alloca.in.h + M gl/langinfo.in.h + M gl/ref-add.sin + M gl/verify.h + M gl/regex.c + M gl/config.charset + M gl/mbrtowc.c + M gl/wchar.in.h + M gl/regex.h + M gl/stdint.in.h + M gl/stdbool.in.h + M gl/unistd.in.h + M gl/stddef.in.h + M gl/localeconv.c + M gl/wctype.in.h + M gl/nl_langinfo.c + M gl/Makefile.am + M gl/gettext.h + M gl/stdlib.in.h + M BaseTypeFactory.cc + M XDRStreamMarshaller.cc + M escaping.cc + M DapIndent.cc + M XDRFileMarshaller.cc + +2013-10-01 James Gallagher + + Updated the FSF address in the copyright headers + +2013-09-30 James Gallagher + + This checkin contains a fix for the conflicting definitions of + width() in BaseType, Vector and Array, where Array provided a + version/definition that clashes with the one in BaseType. BaseType + defined width() and Array defined width(bool constrained = true), + which shadowed the former. I changed BaseType width so that its + signature is width(bool constrained = false) and made that the + only version of the method. There are some changes to uses of + width() in libdap and the BES, but not in the handlers. + +2013-09-27 James Gallagher + + misc changes; a minor fix for Array; a fix for the DDX parser; A + minor change in the scanners for the Error, DAS, DDS and CE + parsers (added %option noinput to suppress a warning); removed + -fno-defer-pop from the Makefile.ams. + +2013-09-17 James Gallagher + + I changed the character used for the Type special form (from # to + $, because # is a URL delim. and tomcat was filtering it). + +2013-08-28 James Gallagher + + Build/Audit fixes. + + M libdap/XDRFileUnMarshaller.cc + M libdap/XDRFileUnMarshaller.h + M bes/dispatch/unit-tests/keysT.cc + M bes/functions/Makefile.am + +2013-08-27 James Gallagher + + Backed out an errant call to fclose() + +;; Local Variables: +;; coding: utf-8 +;; End: +2013-08-28 James Gallagher + + Build/Audit fixes. + + M libdap/XDRFileUnMarshaller.cc + M libdap/XDRFileUnMarshaller.h + M bes/dispatch/unit-tests/keysT.cc + M bes/functions/Makefile.am + +2013-08-27 James Gallagher + + Backed out an errant call to fclose() + +2013-08-27 James Gallagher + + Removed errant call to fclose() in XDRFileUnMarshaller. This + caused a segfault on Linux. + +2013-08-27 James Gallagher + + audit fixes + + M libdap/Makefile.am + M bes/dispatch/unit-tests/BESCache3Test.cc + M bes/functions/tests/functionsTest.at + M bes/functions/tests/bescmd/bind_name_4.dods.bescmd.baseline + M bes/functions/tests/bescmd/bind_name_2.dods.bescmd.baseline + +2013-08-27 James Gallagher + + Source release info + + M configure.ac + M ChangeLog + M INSTALL + M README + M NEWS + +2013-08-27 James Gallagher + + Audit fixes. + + M RValue.cc + M HTTPCacheTable.cc + M Array.cc + M DDXParserSAX2.cc + M Makefile.am + M HTTPCache.cc + M XDRStreamUnMarshaller.cc + M Structure.cc + M Sequence.cc + M XDRFileUnMarshaller.cc + M XDRFileUnMarshaller.h + M mime_util.cc + M util.cc + M util_mit.cc + +2013-08-27 James Gallagher + + Changed the required bison version number to 2.4 and removed a bogus include + from das.yy that breaks bison 2.4. + + M das.yy + M Error.yy + M dds.yy + M ce_expr.yy + +2013-08-27 James Gallagher + + Removed bad character from the Makefile.am + + M Makefile.am + +2013-08-27 James Gallagher + + Added the newly hacked grammar files - these along with updated versions + of DAS.cc, DDS.cc, ConstraintEvaluator.cc and Error.cc and Makefile.am + were copied over from the dap4 branch. A kind of cheap merge operation... + + A das.yy + A Error.yy + A dds.yy + A ce_expr.yy + +;; Local Variables: +;; coding: utf-8 +;; End: +2013-08-27 James Gallagher + + Audit fixes. + + M RValue.cc + M HTTPCacheTable.cc + M Array.cc + M DDXParserSAX2.cc + M Makefile.am + M HTTPCache.cc + M XDRStreamUnMarshaller.cc + M Structure.cc + M Sequence.cc + M XDRFileUnMarshaller.cc + M XDRFileUnMarshaller.h + M mime_util.cc + M util.cc + M util_mit.cc + +2013-08-27 James Gallagher + + Changed the required bison version number to 2.4 and removed a + bogus include from das.yy that breaks bison 2.4. + + M das.yy + M Error.yy + M dds.yy + M ce_expr.yy + +2013-08-27 James Gallagher + + Removed bad character from the Makefile.am + + M Makefile.am + +2013-08-27 James Gallagher + + Added the newly hacked grammar files - these along with updated + versions of DAS.cc, DDS.cc, ConstraintEvaluator.cc and Error.cc + and Makefile.am were copied over from the dap4 branch. A kind of + cheap merge operation... + + A das.yy + A Error.yy + A dds.yy + A ce_expr.yy + +2013-08-26 James Gallagher + + Chnages so that a newer bison can be used. + + M Error.lex + M Error.cc + M ce_parser.h + M ConstraintEvaluator.cc + M DDS.cc + M Makefile.am + M dds.lex + M Vector.cc + M das.lex + M Vector.h + M DAS.cc + M ce_expr.lex + +2013-07-23 James Gallagher + + Removed the openssl/evp.h header because it winds up including + des_old.h on OSX 10.5 and that has a conflict with set_key() in + BESKeys.h. Also, the code no longer uses the evp.h header... + + M XDRStreamMarshaller.h + +2013-07-23 James Gallagher + + Memory leaks fixed in the DAP4 Stream Marshaller classes - but + these are in code in the trunk. will check in all the same and + make corresponding changes to the code on the dap4 branch (which + uses the filename prefix 'D4' instead of 'DAP4'). + +2013-07-23 James Gallagher + + Modified ConstraintEvaluator so that it always frees the + scanner/parser string buffer when an exception is thrown. I made + this change while working on a problem that turned out to be in + the scanner (state was not reset when an error was found) but even + though this change is not the fix for that - see ce_expr.lex - it + seems like a good idea. + +2013-07-23 James Gallagher + + Fixed the rpm build - libtest-types.a was missiing from the spec file. + +2013-07-10 James Gallagher + + Fixed a bug where a CE with mismatched quotes left the CE parser + in the state, making subsequent requests fail. The fix was + in the scanner to reset the parser (technically the scanner's) + state to INITIAL when an error was detected. I also added code to + catch the Error object thrown by the scanner and delete the parse + buffer, although that might be overkill. + +2013-07-10 James Gallagher + + Merged changes from the miic_6.2013 branch. These changes support the + new #() special forms that enable big arrays to be use in CEs. + + _M . + M Error.lex + M ConstraintEvaluator.cc + M Array.cc + M tests/expr-test.cc + A + tests/ResponseBuilder.cc + A + tests/ResponseBuilder.h + M tests/Makefile.am + M DDXParserSAX2.cc + M Makefile.am + M ce_expr.y + M Error.y + M dds.lex + MM mime_util.h + M XDRStreamUnMarshaller.cc + M RValue.h + M BaseTypeFactory.h + M das.lex + MM mime_util.cc + M das.y + M BaseTypeFactory.cc + M util.cc + D unit-tests/server-testsuite/geturl.0 + D unit-tests/server-testsuite/config + _M unit-tests/ancT.cc + M unit-tests/DDXParserTest.cc + D unit-tests/response_cache + M unit-tests/Makefile.am + M dds.y + M Array.h + M ce_expr.lex + +2013-07-09 James Gallagher + + Trunk fixes for the functions module in the libdap andbes spec + files. Also a repair to XDRStreamMarshaller.cc + +2013-06-06 Nathan Potter + + libdap: new value() methods so that the correct thinmgs are + accessed and returned. + +2013-06-06 Nathan Potter + + libdap: Added new 'value' function to Vector that allows you use + an subset_index array to retrieve non contiguous values in a + single operation + +2013-06-05 James Gallagher + + Added libdap-* to svn-ignore + +2013-05-21 James Gallagher + + Fixes for linux + + M unit-tests/ResponseBuilderTest.cc + M unit-tests/ResponseCacheTest.cc + +2013-05-21 James Gallagher + + added valgrind suppressions for stuff in libxml2 + +2013-05-20 James Gallagher + + Moved four set_mime_*() methods from ResponseBuilder to mime_util. + +2013-05-20 James Gallagher + + Modified DDXParser so that it includes an intern_stream() method + that takes a cpp istream. Added tests. Also modified + ResponseBuilder so that it frees the AlarmHandler allocated when + establish_timeout() is called. + +2013-05-20 James Gallagher + + Fixed memory leaks in the DDXParserSax2 code. Other minor edits. + +2013-05-20 James Gallagher + + Test fixes + +2013-05-20 James Gallagher + + added + +2013-05-17 James Gallagher + + Added these baseline files for ResponseBuilderTest + +2013-05-17 James Gallagher + + Fixes for ResponseCache - I added tests and have tested most + modes. Not tested: how it works with server functions. There are + also fixes/modifications for ResponseBuilder that ResponseCache + uses. Added a note to ServerFunctionsList that it stores weak + pointers. I worked on the ResponseBuilderTests but never got the + test for server functions to work completely; the test examines + the response document and checks that the metadata part is correct + but not the (trivial) data part. + +2013-05-17 James Gallagher + + Added get_next_mime_header() for an istream (the existing version + took a FILE*) and moved some of the MIME header methods from + ResponseBuilder here. + +2013-05-15 Nathan Potter + + libdap: Moving trunk/libdap/unit-tests/ce-functions-testsuite to + trunk/bes/functions/unit-tests because that's where the functions + are that need the contents of that directory. + +2013-05-14 James Gallagher + + ResponseCache mostly working... + +2013-05-14 James Gallagher + + We now install the library and *Test headers. + +2013-05-14 Nathan Potter + + libdap: Repaired problems with conditional build in ServerFcuntionsList + +2013-05-14 James Gallagher + + Added HAVE_STDLIB_H and HAVE_ATEXIT to DAPCache3.cc. + +2013-05-10 Nathan Potter + + libdap: Instrumentation in Regex. Fixed baseline for + ResponseBuilderTest::invoke_server_side_function_test + +2013-05-10 Nathan Potter + +2013-05-10 Nathan Potter + +2013-05-10 Nathan Potter + +2013-05-10 Nathan Potter + +2013-05-10 Nathan Potter + + libdap: Added life cycle control to DAPCache3 singleton. + +2013-05-10 Nathan Potter + + libdap: Added life cycle control to DAPCache3 singleton. + +2013-05-10 James Gallagher + + (potential) fix for the ResponseBuilder cache bug + +2013-05-10 Nathan Potter + + libdap: Updated ResponseBuilderTest to run a server side function. + +2013-05-10 James Gallagher + + Removed (commented) #define DODS_DEBUG from ServerFunctionsList. + +2013-05-10 Nathan Potter + + libdap: Added instrumentation (enabled in this check in) to + ServerFunctionsList in an effort to understand the seg fault + problem that seems to be associated with server side function + calls. + +2013-05-10 Nathan Potter + + libdap: Added instrumentation (enabled in this check in) to + ServerFunctionsList in an effort to understand the seg fault + problem that seems to be associated with server side function + calls. + +2013-05-10 Nathan Potter + + libdap: Added instrumentation (enabled in this check in) to + ServerFunctionsList in an effort to understand the seg fault + problem that seems to be associated with server side function + calls. + +2013-05-09 Nathan Potter + + libdap: Checking in repaired ServerFunctionsList + +2013-05-06 James Gallagher + + Removed old code + +2013-05-06 James Gallagher + + DAPCache3 is now in the libdap namespace. The cache-specific + methods of ResponseBuilder have been factored out to + ResponseCache; RB still makes explicit calls to the cache code, + however. The ResponseCache class was added. + +2013-05-03 James Gallagher + + Added correct copyright headers for the ServerFunctions classes. + +2013-05-03 Nathan Potter + + libdap: Attempting to fix memory leaks in ServerFucntionsList - adding unit tests for same. + +2013-05-02 James Gallagher + + Removed old code in util + +2013-05-02 James Gallagher + + Removed old code from ResponseBuilder + +2013-05-01 Nathan Potter + + libdap: Change type signature for setter methods in ServerFunction + so that they accept const parameters + +2013-05-01 Nathan Potter + + libdap: Change type signature for setter methods in ServerFunction + so that they accept const parameters + +2013-04-27 James Gallagher + + Wrapped the two DAP4-only includes in #if DAP4. Those headers are not + installed in a regular build and the includes break clients that + included the file. + + M BaseType.h + +2013-04-03 James Gallagher + + distcheck works on linux now... + + A + D4ParserSax2.cc + M Makefile.am + D D4ParserSAX2.cc + +2013-04-03 James Gallagher + +2013-04-02 James Gallagher + + Added climit include to fix 'UINT/INT_MAX missing' on Linux. + + M util.cc + +2013-04-01 James Gallagher + + Added unit tests for D4Dimensions and D4EnumDefs. Removed old code from the AttrTable tests. + +2013-04-01 James Gallagher + + Added D4EnumDefs + +2013-03-26 Nathan Potter + +2013-03-25 Nathan Potter + + libdap: Disabling cruft code from earlier ugrid work. + +2013-02-25 Nathan Potter + + libdap: Comments + +2013-02-12 Nathan Potter + + libdap: comments + +2013-02-08 Nathan Potter + + libdap: Updated ServerFunction API + +2013-02-07 Nathan Potter + + libdap: Added methods to ServerFunctionsList to allow inspection of the list contents. + +2013-02-06 Nathan Potter + + libdap: Moved AbstractFunction class to ServerFunction class + +2013-02-06 Nathan Potter + + libdap: Added the use of AbstractFunction class. + +2013-02-05 Nathan Potter + + team sync + +2013-02-05 Nathan Potter + + team sync + +2013-02-04 Nathan Potter + +2013-02-04 Nathan Potter + +2013-02-04 Nathan Potter + + libdap: Adding AbstractServerSideFunction + +2013-01-31 James Gallagher + + Fixes for bes distcheck + + M ServerFunctionsList.cc + M ServerFunctionsList.h + M ConstraintEvaluator.h + +2013-01-30 James Gallagher + + Distcheck now works; removed more old server function code. + + _M . + M configure.ac + M main_page.doxygen + M ConstraintEvaluator.cc + _M tests + M Makefile.am + D swath2grid + D ugridFunctions + M NEWS + M ConstraintEvaluator.h + M unit-tests/HTTPCacheTest.cc + M doxy.conf + +2013-01-30 James Gallagher + + Added stuff to svn:ignore + +2013-01-30 Nathan Potter + + libdap: Updating configre.ac for automake-1.13 + +2013-01-29 James Gallagher + + Changed the way functions are registered so that CE does not have to copy all the functions into every instance of the CE class. Now CE uses functions directly from a list in libdap. + +2013-01-29 James Gallagher + + Changed the way functions are registered so that CE does not have to copy all the functions into every instance of the CE class. Now CE uses functions directly from a list in libdap. + +2013-01-29 James Gallagher + + Fixed checkin - I checked in some code with debugging on + +2013-01-29 James Gallagher + + Removing 'basic' server functions. + + _M . + M Keywords2.cc + M ConstraintEvaluator.cc + M tests/TestArray.cc + M tests/EXPRTest.at + M Makefile.am + M swath2grid/reproj_functions.cc + M HTTPCache.cc + M util.h + M expr.h + M util.cc + M unit-tests/HTTPCacheTest.cc + D unit-tests/ArrayGeoConstraintTest.cc + RM + unit-tests/ancT.cc + M unit-tests/ddsT.cc + M unit-tests/ResponseBuilderTest.cc + D unit-tests/CEFunctionsTest.cc + D unit-tests/GridGeoConstraintTest.cc + M unit-tests/Makefile.am + M unit-tests/Keywords2Test.cc + +2013-01-29 James Gallagher + + Added stuff to svn:ignore + +2013-01-29 James Gallagher + + Added stuff to svn:ignore + +2013-01-29 James Gallagher + + Added stuff to svn:ignore + +2013-01-25 Patrick West + + Removing dependency on DAP4 code for DAP3 responses + M ResponseBuilder.h + M ResponseBuilder.cc + M Int64.h + +2013-01-25 Patrick West + + Need autoconf 2.61, not 2.62 + M configure.ac + +2013-01-18 James Gallagher + + Merge of the code from hyrax 1.8 release (version 3.11.7) + + _M . + A + gridfields_functions.h + M configure.ac + M Connect.h + M ChangeLog + A + gridfields_functions.cc + M ResponseBuilder.h + M main_page.doxygen + A + DAPCache3.cc + A + DAPCache3.h + _M OSX_Resources + M ConstraintEvaluator.cc + M DDS.cc + M HTTPCacheTable.cc + M Array.cc + _M tests + M tests/TestGrid.cc + M tests/expr-test.cc + M tests/TestStructure.cc + M INSTALL + M DDXParserSAX2.cc + M libdap.spec + M ResponseBuilder.cc + M README + M BaseType.cc + M DDS.h + _M win32/gl + M Makefile.am + AM + swath2grid + M DataDDS.cc + M ce_expr.y + M util.h + AM + ugridFunctions + M XDRStreamUnMarshaller.cc + M NEWS + A + doxy.conf.in + M Vector.cc + A + main_page.doxygen.in + _M gl + M ce_functions.cc + M expr.h + MM mime_util.cc + M GridGeoConstraint.cc + M Vector.h + M util.cc + M Grid.cc + M Connect.cc + _M unit-tests + MM unit-tests/MIMEUtilTest.cc + M unit-tests/ResponseBuilderTest.cc + M Clause.cc + M Array.h + M doxy.conf + M ce_expr.lex + +2012-11-07 James Gallagher + + Changed svn ignore properties + +2012-11-07 James Gallagher + + Changed svn ignore properties + +2012-11-07 James Gallagher + + Changes for libdap 3.11.7 so that when the CE Function cache + directory is not present, caching is off. Before the code was just + crashing when a function was used. I also refactored + ResponseBuilder (where the fix is) so that it's got less + redundancy. + +2012-11-02 James Gallagher + + Release 3.11.6 + + M configure.ac + M ChangeLog + M main_page.doxygen + M tests/package.m4 + M INSTALL + M libdap.spec + M README + M NEWS + M doxy.conf + +2012-11-02 James Gallagher + + Fixes for a resource leak in the server function cache code. + + M ResponseBuilder.cc + +2012-10-31 James Gallagher + + fixed an error in the include of DDXParserSAX2.h - OSX file system + case issues + +2012-10-24 James Gallagher + + Changed svn ignore properties + +2012-10-24 James Gallagher + + Changes so that users - handlers primarily - of libdap don't have to + change their link libraries even though ResponseBuilder has the new + caching code. The first version used Connect, this version uses hacked + copies of (tiny) parts of Connect. This keeps the handlers from needing + libdapclient, libcurl and libuuid to link. + + M ResponseBuilder.h + M ResponseBuilder.cc + M Makefile.am + M mime_util.cc + +2012-10-24 James Gallagher + + Fixes both from a code review and to move Connect into libdap proper + so that clients will link correctly (now that Connect is used both by + clients and servers. + + M HTTPCacheTable.cc + M ResponseBuilder.cc + M Makefile.am + M util.cc + M Connect.cc + +2012-10-24 James Gallagher + + Changed svn ignore properties + +2012-10-24 James Gallagher + + Added DAPCache3. + +2012-10-24 James Gallagher + +2012-10-24 James Gallagher + + Changed svn ignore properties + +2012-10-23 James Gallagher + + These changes support server functions in DAP2 using the F-TDS/GDS 'in + path' syntax. They aslo add support for retrieving the DDS and DAS from + URLs with functions (both the F-TDS/GDS syntax and ours) and cache the + results of function calls. + + M Connect.h + M ResponseBuilder.h + M ConstraintEvaluator.cc + M DDS.cc + M tests/TestGrid.cc + M tests/expr-test.cc + M tests/TestStructure.cc + M DDXParserSAX2.cc + M ResponseBuilder.cc + M BaseType.cc + M DDS.h + M Makefile.am + M ce_expr.y + M XDRStreamUnMarshaller.cc + M Vector.cc + M ce_functions.cc + M mime_util.cc + M GridGeoConstraint.cc + M Connect.cc + M unit-tests/ResponseBuilderTest.cc + M Clause.cc + +2012-09-28 James Gallagher + + Changed to using named branches for the server release. + + A https://scm.opendap.org/svn/branch/libdap/hyrax-1.8-release + +2012-07-26 James Gallagher + + Memory leak in XDRStreamMarshaller fixed. + +2012-07-26 James Gallagher + + Found and fixed an error in the add_var_nocopy() method for DDS. I + also added add_var_nocopy() methods to Vector and Array and noted + that when a BaseType is passed to Array's ctor the add_var() + method is used, thus making a copy. If the 'nocopy' behavior is + desired, then pass null to the ctor and call add_var_nocopy() 'by + hand.' I also changed two instances of dynamic_cast so that + static_cast is used instead. These changes are backward compatible + to the previous version of the library. Bumped the version up to + 3.12.0 + +2012-07-26 James Gallagher + + Memory leak in XDRStreamMarshaller fixed. + +2012-07-26 James Gallagher + + Found and fixed an error in the add_var_nocopy() method for DDS. I + also added add_var_nocopy() methods to Vector and Array and noted + that when a BaseType is passed to Array's ctor the add_var() + method is used, thus making a copy. If the 'nocopy' behavior is + desired, then pass null to the ctor and call add_var_nocopy() 'by + hand.' I also changed two instances of dynamic_cast so that + static_cast is used instead. These changes are backward compatible + to the previous version of the library. Bumped the version up to + 3.12.0 + +2012-06-27 James Gallagher + + Removed errant instrumentation in serialize() + +2012-06-22 James Gallagher + + Added some debugging output for/because of work on the GDAL handler. + +2012-06-05 James Gallagher + + Fixed an error in the MIMEUtilTest code. The test failed when the + time zone was to the east of the prime meridian. See ticket 1907. + +2012-05-31 James Gallagher + + doxygen updates plus added an example function for the MIIC folks to + ce_functions.cc. + + M configure.ac + M main_page.doxygen + M ConstraintEvaluator.cc + A doxy.conf.in + A main_page.doxygen.in + M ce_functions.cc + M doxy.conf + +2012-05-11 Nathan Potter + + libdap: updating version number + +2012-04-24 James Gallagher + + Fixed lack of loop index init in setup(). + +2012-04-17 James Gallagher + + Updated NEWS + + M NEWS + +2012-04-12 James Gallagher + + Result of merging changes from the trunk. This code was then merged to + the trunk and tagged. + + _M . + M configure.ac + M ChangeLog + _M OSX_Resources/Info.plist.proto + M HTTPCacheTable.cc + _M tests/old_tests/grid-func-testsuite + _M tests/old_tests/server-testsuite + M tests/TestArray.cc + M tests/package.m4 + M ResponseBuilder.cc + M Makefile.am + _M mime_util.h + M Structure.cc + M Vector.cc + _M mime_util.cc + M XDRStreamMarshaller.h + M XDRStreamMarshaller.cc + M Grid.cc + _M unit-tests/MIMEUtilTest.cc + _M unit-tests/fdiostreamTest.cc + M unit-tests/Makefile.am + M unit-tests/MarshallerTest.cc + M HTTPConnect.h + _M fdiostream.h + +2012-04-11 James Gallagher + + Removed the 'override' file feature - it was never used and its implementaion + was a problem. + + M HTTPCacheTable.cc + M mime_util.h + M mime_util.cc + M XDRStreamMarshaller.cc + +2012-02-16 James Gallagher + + Added unistd .h to ResponseBuilder.cc + +2012-02-07 James Gallagher + + Version 3.11.3 + +2012-02-07 James Gallagher + + Removed the static curl libs from dap-config. + +;; Local Variables: +;; coding: utf-8 +;; End: + +2012-02-07 James Gallagher + + Removed the static curl libs from dap-config. + +2012-01-17 James Gallagher + + News was updated. + + M NEWS + +2012-01-10 James Gallagher + + Update for Hyrax 1.8 release. + + M ChangeLog + M tests/package.m4 + M INSTALL + M README + M NEWS + +2012-01-04 James Gallagher + + version number bump in NEWS + +2011-12-30 James Gallagher + + Bumped up the versions in libdap to 3.11.2. Not sure if this will + be the version number we use for the release. + +2011-12-30 James Gallagher + + Fixed the rpm build; included the man page stuff. + + M libdap.spec + +2011-12-30 James Gallagher + + Added HTTPCacheMacros.h + +2011-12-29 James Gallagher + + Fixes for distcheck given the new man pages + + M Makefile.am + +2011-12-29 James Gallagher + + Added man pages for getdap and dap-config + + M Makefile.am + A getdap.man1 + A dap-config.man1 + +2011-12-29 James Gallagher + + Removed calls to exit(). Fixed the FSF address. Half of ticket 1868. + +2011-12-19 James Gallagher + + comment fixes + +2011-11-30 James Gallagher + + Formatting, Spelling and an error message's text fixed. + +2011-11-30 James Gallagher + + Fix for Fedora builds; the extra newlines in these baseline files are + likely breaking the tests + + M tests/expr-testsuite/test.6b.base + M tests/expr-testsuite/test.5e.base + +2011-11-23 Patrick West + + With HTTPConnect including CURL headers, added CURL_CFLAGS to the + --cflags option M dap-config.in + +2011-11-23 Patrick West + + CURL_CFLAGS was not being used when compiling tests. Added. + M unit-tests/Makefile.am + +2011-11-23 Patrick West + + curl includes were not being included properly. Works if in common + place, but not if in custom location. M tests/Makefile.am + +2011-11-23 Patrick West + + curl/types.h is deprecated and no longer needed + M HTTPConnect.h + +2011-11-16 James Gallagher + + AttrTable was using id2www() to escape spaces and other characters + in attribute names. This was done probably because of the + spaces (spaces in the attribute names break the DAP2 DAS object + parser), but no matter. It was breaking DAS and DDX objects + because names with '%' characters in them were mangled. I replaced + id2www and www2id with new functions that encode/decode only + spaces. I have not fiddled with the find functions; it's fine to + use names with spaces in them with the API, but the printed + representation of the DAS will show those spaces as %20. + Similarly, when the printed representation is parsed, those %20 + escape codes are replaced with real spaces. This means that the + names in the binary objects and XML representation are correct. + +2011-11-15 James Gallagher + + Fix for ticket 1841 - the xml attribute 'base' in the Dataset root + element should have been preceeded by the namespace 'xml'. It is now. + +2011-11-13 James Gallagher + + Fixed, I hope, the problem with the xmlTextWriter code in version + 2.6 of libxml. I think what was happening was really tripping over + an odd feature of the xmlTextWriter API where the TextWriter + object has to be freed before the pointer it has been writing to + can be used. Actually, I suspect that freeing the object calls the + buffer flush function, so calling that would probably work, too. I + opted for the call sequence in the only example I can dig up... + +2011-11-13 James Gallagher + + Fix for ticket 1837. The code libxml2 was escaping attributes with + with OtherXML, turning them into useless goo. I switched from the + ...WriteString to ...WriteRaw function for this type of attribute + and order was restored. + +2011-10-31 Patrick West + + The code was printing a blank line when certain conditions existed. + Removed that code. + M DDS.cc + +2011-10-26 James Gallagher + + Updates for the new DDX tests... nearly done + +2011-10-24 Patrick West + + Forgot a quote + M dap-config.in + +2011-10-24 Patrick West + + Added xml2 includes to cflags + M dap-config.in + +2011-10-24 James Gallagher + + some poking around regarding the DDX/XML bug (1829). Doesn't look + like it's in libdap. Some cleanup, though. + +2011-10-24 James Gallagher + + Fixed the distcheck target. + + M unit-tests/dasT.cc + M unit-tests/AISMergeTest.cc + M unit-tests/MIMEUtilTest.cc + M unit-tests/ddsT.cc + M unit-tests/ByteTest.cc + M unit-tests/Makefile.am + +2011-10-24 Nathan Potter + + libdap: Fixed problems with libxml dependencies. + +2011-10-21 James Gallagher + + Added testFle.h in unit-tests + +2011-10-21 James Gallagher + + Added libparsers_la_CPPFLAGS so that -I for libxml2 will be used + when building the grammar files (really only needed for the DAS + stuff?). Also fixed minor warning in HTTPCache where a File* could + be used uninitialized. + + M Makefile.am + M HTTPCache.cc + +2011-10-21 James Gallagher + + I added a new method to DDS that can insert a variable anywhere in + the DDS. I also added 'nocopy' versions of the new insert_var() + method and the older ad_var() method. Once the ncml handler + stabilizes, we can start using those methods to boost performance. + Lastly, I forgot to add the new XMLWriter class on the last + commit, + +2011-10-12 James Gallagher + + I added a new method to BaseType, DDS and DAS: print_xml_writer() + that takes a XMLWriter object and uses libxml2's xml writer API to + build the DDX response. This should fix all of our woes WRT xml + encoding. The new method is called by the DDS::print_xml method + although it could be used in place of that method since it has the + same parameters. I added tests and, in the course of that, + modified the way some of the tests are done (using files instead + of string variables for the regexs). I also tried adding some + globbing functions to the util.cc file but those proved less than + totally useful - I've left them there all the same. + +2011-09-23 James Gallagher + + Fix for the unit test of get_request_size(). For this function, + different compilers return different values for sizeof(string) so + I made the unit test an OR of two values. Fixed here on the branch + and on the trunk. + + M unit-tests/DDSTest.cc + +2011-09-21 James Gallagher + + Hyrax 1.8 + + A http://scm.opendap.org/svn/branch/libdap/3.11.2 + +2011-09-13 James Gallagher + + Refactor - no change to the ABI. + +2011-08-24 James Gallagher + + Merge of Hyrax 1.7.1 + + _M . + _M OSX_Resources/Info.plist.proto + M DAS.h + M DDS.cc + _M tests/old_tests/grid-func-testsuite + _M tests/old_tests/server-testsuite + M tests/expr-testsuite/test.6b.base + M DDS.h + M BaseType.cc + M Constructor.h + M AttrTable.cc + _M mime_util.h + _M mime_util.cc + M Constructor.cc + M unit-tests/cache-testsuite/Makefile.am + _M unit-tests/MIMEUtilTest.cc + _M unit-tests/fdiostreamTest.cc + _M fdiostream.h + +2011-07-15 James Gallagher + + Fixed an issues with the response too big error message - the sizes were returned in Bytes, but the units were listed as KB. Now the values are in KB (truncated, but good enough). + +2011-06-30 Patrick West + + Merge hyrax 1.7 branch to trunk + +2011-06-09 James Gallagher + + Added support for the first version of response size limits - does not do much for Sequences. + +2011-05-30 James Gallagher + + Added get/set_response_limit() and get_request_size() (first version) to libdap:DDS. + +2011-05-10 James Gallagher + + Added --with-curl and --with-xml2 to configure. + +2011-04-25 James Gallagher + + Removed doc rpm - this breaks when the build hosts lacks dot or + dot does not make inamges. + + M libdap.spec + +2011-04-20 James Gallagher + + Hyrax 1.7 releaseHyrax 1.7 release + + A http://scm.opendap.org/svn/branch/libdap/3.11.1 + +2011-04-08 James Gallagher + + Build fixes for h 1.7 osx meta package + + M Makefile.am + +2011-03-28 James Gallagher + + Loop/Race-condition in HTTPCacheTable.h fixed + +2011-03-28 James Gallagher + + 1.7RC1 + +2011-03-17 Nathan Potter + + libdap: Automated change of version number for OSX package builds. + +2011-03-08 James Gallagher + + Merge for hyrax 1.7 from the hyrax_1.6.2_release branch of shrew + to the trunk + + _M . + M RValue.cc + M DODSFilter.cc + M OSX_Resources/InstallationCheck + M OSX_Resources/update_mac_package_contents.pl + R + OSX_Resources/Info.plist + A + OSX_Resources/Info.plist.proto + M DDS.cc + M Array.cc + _M tests/old_tests/grid-func-testsuite + _M tests/old_tests/server-testsuite + M DDXParserSAX2.cc + M AlarmHandler.h + M libdap.spec + M BaseType.cc + M Makefile.am + _M mime_util.h + M Vector.cc + M ce_functions.cc + MM mime_util.cc + M GridGeoConstraint.cc + M Constructor.cc + M DAS.cc + M Grid.cc + M escaping.cc + _M unit-tests/MIMEUtilTest.cc + _M unit-tests/fdiostreamTest.cc + _M fdiostream.h + M Clause.cc + +2011-03-03 Patrick West + + These files are generated + D gl/alloca.h + D gl/arg-nonnull.h + D gl/langinfo.h + D gl/stdint.h + D gl/c++defs.h + D gl/wctype.h + D gl/warn-on-use.h + D gl/wchar.h + D gl/unistd.h + +2011-03-03 James Gallagher + + All tests back in. + + M unit-tests/HTTPConnectTest.cc + +2011-03-03 James Gallagher + + Fixed HTTPCache hack that provides access to the cached response + body. Also fixed an error in Keywords that broke a gazillion + tests. + +2011-03-03 James Gallagher + + Eh - ctor fix for Keywords + +2011-03-03 James Gallagher + + Keywords2 was modified so it'll build on older (4.1) gcc + compilers (with an older stl implementation). + +2011-03-02 James Gallagher + + Modified HTTPConnect to store the filename of the cached body when + returning cached responses. Use the HTTPResponse::get_file() + method to use this. Once the HTTPResponse is deleted, the name is + no loner valid. + +2011-03-01 James Gallagher + + Added Keywords to DDS and modified the behavior of set_dap_version + so that '2' parses as '2.0' (really 'x' --> 'x.0'). + +2011-03-01 Patrick West + + Created from .in file. + D gl/stdlib.h + +2011-02-28 Patrick West + + added include of cstring for strlen and strncpy + M ResponseBuilder.cc + +2011-02-25 James Gallagher + + Removed old Keywords impl. + +2011-02-25 James Gallagher + + Keywords2 now tests for legal values. + +2011-02-25 James Gallagher + + Keywords2 works - this syntax looks like functions but is to be parsed before the CE is formally processed. + +2011-02-25 James Gallagher + + Keywrods2Test.cc... + +2011-02-25 James Gallagher + + Keywords2... + +2011-02-24 James Gallagher + + new Keywords implementations started. + +2011-02-24 James Gallagher + + Next version of Keywords - This implements the version where keywords are bound to a pair made up of a kind and a value. Probably won't use this because of potential problems with ambiguity - it's not possible to guarantee that 'dap3.2' won't be an identifier in a data set. + +2011-02-24 James Gallagher + + g++ 4.? warnings about char* conversion in HTTPConnectTest + +2011-02-24 James Gallagher + + New Keywords class first version working/tested + +2011-02-23 James Gallagher + + Factored keywords out of ResponseBuilder and into their own class (Keywords). + +2011-02-15 Patrick West + + This file is automatically generated + D gl/configmake.h + +2011-02-08 James Gallagher + + Initial cut at support for Version information in the CE. + +2011-02-08 James Gallagher + + I modified the documentation for mime_util and ResponseBuilder so that they describe a truer version of the world - most of the MIME header code is no longer used by libdap or the BES/handlers (instead the headers are added by the OLFS). Some of the mime_util code is used by the client side, however, and a tiny bit is used by the parts of ResponseBuilder I added so that the BES can use ResponseBuilder in those cases where there is no OLFS. + +2011-02-08 James Gallagher + + Reverted to pre-DAP4 hack versions of these... The new features are + implemented in ResponseBuilder. + + M DODSFilter.cc + M DODSFilter.h + +2011-02-07 James Gallagher + + ResponseBuilder now assumes that the default protocol for a response is the DAP_PROTOCOL_VERSION read from config.h. This can be overridden using a parameter to the various set_mime_*() methods it defines (which are different from those in mime_util.cc). NB: These are not used by Hyrax, but the BES might use these sometimes and I've got mods pending in the BES to use this code. + +2011-02-07 James Gallagher + + Comments about ResponseBuilder + +2011-02-07 James Gallagher + + Comments about ResponseBuilder + +2011-02-07 James Gallagher + + Comments about ResponseBuilder + +2011-02-07 James Gallagher + + Added comment about ResponseBuilder + +2011-02-07 James Gallagher + + Added comment about ResponseBuilder + +2011-02-03 James Gallagher + + These are templates needed by the code in 'gl.' I think ... + + A conf/c++defs.h + A conf/warn-on-use.h + A conf/arg-nonnull.h + +2011-02-02 James Gallagher + + Removed these files as a result of the massive commit + + D gl/m4/wchar.m4 + D gl/m4/ulonglong.m4 + D gl/m4/absolute-header.m4 + D gl/m4/wctype.m4 + D gl/alloca_.h + D gl/wchar_.h + D gl/stdint_.h + D gl/stdbool_.h + D gl/unistd_.h + D gl/wctype_.h + D gl/stdlib_.h + D gl/alloca.c + +2011-02-02 James Gallagher + + Added as part of the massive update. + + A gl/m4/fcntl-o.m4 + A gl/m4/configmake.m4 + A gl/m4/btowc.m4 + A gl/m4/00gnulib.m4 + A gl/m4/locale-fr.m4 + A gl/m4/langinfo_h.m4 + A gl/m4/mbsinit.m4 + A gl/m4/warn-on-use.m4 + A gl/m4/wchar_h.m4 + A gl/m4/wcrtomb.m4 + A gl/m4/locale-zh.m4 + A gl/m4/stddef_h.m4 + A gl/m4/mbstate_t.m4 + A gl/m4/wctype_h.m4 + A gl/m4/nl_langinfo.m4 + A gl/m4/locale-ja.m4 + A gl/m4/wchar_t.m4 + A gl/m4/multiarch.m4 + A gl/alloca.h + A gl/arg-nonnull.h + A gl/langinfo.h + A gl/mbsinit.c + A gl/stdint.h + A gl/wcrtomb.c + A gl/btowc.c + A gl/c++defs.h + A gl/warn-on-use.h + A gl/wctype.h + A gl/streq.h + A gl/alloca.in.h + A gl/langinfo.in.h + A gl/configmake.h + A gl/verify.h + A gl/wchar.h + A gl/iswblank.c + A gl/mbrtowc.c + A gl/wchar.in.h + A gl/stdint.in.h + A gl/stdbool.in.h + A gl/unistd.in.h + A gl/stddef.in.h + A gl/wctype.in.h + A gl/nl_langinfo.c + A gl/stdlib.in.h + +2011-02-02 James Gallagher + + Massive (and massively overdue) updates - this may be the fix for ticket #1702 + But regardless, it's certainly needed. + + M gl/m4/wint_t.m4 + M gl/m4/localcharset.m4 + M gl/m4/stdint.m4 + M gl/m4/stdbool.m4 + M gl/m4/gnulib-common.m4 + M gl/m4/malloc.m4 + M gl/m4/regex.m4 + M gl/m4/gnulib-comp.m4 + M gl/m4/mbrtowc.m4 + M gl/m4/unistd_h.m4 + M gl/m4/longlong.m4 + M gl/m4/ssize_t.m4 + M gl/m4/include_next.m4 + M gl/m4/gnulib-cache.m4 + M gl/m4/glibc21.m4 + M gl/m4/codeset.m4 + M gl/m4/stdlib_h.m4 + M gl/m4/gnulib-tool.m4 + M gl/m4/alloca.m4 + M gl/m4/extensions.m4 + M gl/localcharset.c + M gl/regex_internal.c + M gl/malloc.c + M gl/localcharset.h + M gl/regex_internal.h + M gl/regexec.c + M gl/ref-del.sin + M gl/stdlib.h + M gl/regcomp.c + M gl/ref-add.sin + M gl/regex.c + M gl/unistd.h + M gl/config.charset + M gl/regex.h + M gl/Makefile.am + M gl/gettext.h + +2011-02-02 James Gallagher + + Corrected + +2011-01-31 James Gallagher + + Expanded DDS so that it's easier to get the protocol version string; moved the relevant MIME header code into ResponseBuilder on the assumption that someone might be using this still (like CEDAR). Updated the unit tests. + +2011-01-29 James Gallagher + + Leaner version of ResponseBuilder - the new replacement for DODSFilter. + +2011-01-28 James Gallagher + + Commented out test code that got in SVN by mistake (in expr-test.cc) + +2011-01-28 James Gallagher + + Changes/Additions for Version via Projection Functions and/or Keywords. + +2010-10-28 mjohnson + + Merge from Hyrax 1.6.2 Shrew Release Branch. + + Not clear what the property modifications are, the ones I checked seem the same. I'm just letting them go since they seem harmless. + +2010-09-14 James Gallagher + + Fixed a bug in the www2id() function of escaping.cc that was causing symbols that start with a percent sign to be mangled. It's not clear why the server was/is using this, but this fixes the function and we'll have to sort out whether using the function is correct later. It may be that the BES is routing the DAS through an unnecessary cycle of print/parse/print operations. + +2010-09-14 James Gallagher + + Added tests for www2id() and the case where an attribute starts with a percent sign. + +2010-09-14 James Gallagher + + Spelling errors in GeoConstraint.cc + +2010-09-14 James Gallagher + + Fixed an error in calling long_to_string with doubles in exception handling code - found during testing. + +2010-09-14 James Gallagher + + Updated for fixes to a problem foud in initial testing of the server for the 1.6.2 release. + +2010-09-14 James Gallagher + + Added a second version of eval_function_clauses() that takes and returns a DataDDS instead of a DDS. This enables easier use of the method in handlers that are using subclasses of the types and building responses from DataDDS objects passed around in the BES. I also updated some of the library version information in configure.ac to match more closely the addition to ConstraintEvaluator's interface. + +;; Local Variables: +;; coding: utf-8 +;; End: +2010-09-14 James Gallagher + + Added a second version of eval_function_clauses() that takes and + returns a DataDDS instead of a DDS. This enables easier use of the + method in handlers that are using subclasses of the types and + building responses from DataDDS objects passed around in the BES. + I also updated some of the library version information in + configure.ac to match more closely the addition to + ConstraintEvaluator's interface. + +2010-09-13 James Gallagher + + Changes for 3.11.0 release + + M configure.ac + M ChangeLog + M INSTALL + M libdap.spec + M README + M NEWS + M ce_functions.cc + +2010-09-07 James Gallagher + + Added to doc comments + +2010-09-03 James Gallagher + + Updated the version number for geogrid. + +2010-09-02 James Gallagher + + Constraint expressions can now include multiple function calls. + Two tests included. Some fixes were added to expr-test.cc which + was looking a little long in the tooth... + +2010-09-01 James Gallagher + + Added new functions to ConstraintEvaluator. Untested. + +2010-09-01 mjohnson + + o Fix for memory bug in ticket #1637. + +2010-08-27 James Gallagher + + Grid now prints the XML declaration correctly when a constrained + Grid variable's type decays to a Structure. The expr-test program + now has an XML option (-x) for use with constrained DDS/DDX + output. + +2010-08-27 James Gallagher + + Fixed the Grid projection bug - should clean up the code a bit. + The error was in projection yields grid which was not correctly + testing that each Grid dimension had a matching Map vector that + was in the current projection. + +2010-08-27 James Gallagher + + Added regression tests for the new version of geogrid(). + Removed some old stuff from the dejagnu days. + +2010-08-27 James Gallagher + + Added more tests (unit tests) for GridGeoConstraint for the Grid, + Array, Array, ..., version of the constructor. + +2010-08-27 James Gallagher + + geogrid() now takes both (grid, ) and (grid, lat + array, lon array ). This includes some minimal testing + of the new code. + +2010-08-26 James Gallagher + + Changes to the geogrid function so that it can take lat/lon maps + as explicit arguments. + +2010-07-16 James Gallagher + + I removed the AIS functions in libdap 3.8 but failed to correctly + set the Age of the library in libdap 3.10. Fixed. + +2010-06-17 mjohnson + + Removed debug AM_CXXFLAGS entries. + +2010-05-24 James Gallagher + + Updated tests given that the nph-dods alias is no longer supported by + test.opendap.org + + M unit-tests/HTTPCacheTest.cc + M unit-tests/generalUtilTest.cc + +2010-05-19 Patrick West + + Seems to fix bug #1563. When adding an attribute table to the DAS when a + container is currently set, set that new attribute table to not be + global using set_is_global_attribute(false) + + M DAS.cc + +2010-05-12 James Gallagher + + Fixed etag for 304 response test - the etag changed when the server was + updated. + + M unit-tests/HTTPConnectTest.cc + +2010-05-11 James Gallagher + + Merge from the shrew-1.6 branch (which was tagged 3.10.2) + + _M . + M SignalHandler.cc + M configure.ac + M ChangeLog + M conf/libdap.m4 + M DDS.cc + M BaseType.h + _M tests/old_tests/grid-func-testsuite + _M tests/old_tests/server-testsuite + M tests/das-test.cc + M tests/package.m4 + M tests/DASTest.at + M tests/DDSTest.at + M tests/expr-test.cc + M tests/EXPRTest.at + M tests/Makefile.am + M tests/dds-test.cc + M INSTALL + M DDXParserSAX2.cc + M libdap.spec + M README + M DDS.h + M BaseType.cc + M Makefile.am + M Constructor.h + M RCReader.cc + M parser-util.cc + M AttrTable.cc + _M mime_util.h + M Grid.h + M NEWS + M ce_functions.cc + M AttrTable.h + _M mime_util.cc + M Constructor.cc + M Grid.cc + M Connect.cc + M escaping.cc + M unit-tests/DODSFilterTest.cc + M unit-tests/DDSTest.cc + _M unit-tests/MIMEUtilTest.cc + MM unit-tests/fdiostreamTest.cc + M unit-tests/generalUtilTest.cc + M unit-tests/dds-testsuite/test.19b.das + M unit-tests/dds-testsuite/test.19e.das + M unit-tests/dds-testsuite/test.19g.das + A + unit-tests/dds-testsuite/coads_climatology.nc.dds + A + unit-tests/dds-testsuite/coads_climatology.nc.das + M unit-tests/Makefile.am + M unit-tests/RCReaderTest.cc + M unit-tests/MarshallerTest.cc + _M fdiostream.h + +2010-05-11 James Gallagher + + ci prior to merge + + M tests/DASTest.at + M tests/DDSTest.at + M tests/EXPRTest.at + +2010-04-30 James Gallagher + + Fixed an error in AttrTable::is_global_attribute() where the function did not + always return a value. + + M AttrTable.cc + +2010-04-30 James Gallagher + + Commented the fdiostreamTest stuff - automake was complaining. Removed a + bunch of AIS-related things that are no longer needed. + +2010-04-30 mjohnson + + AttrTable.cc, AttrTable.h: Changes for #1544. clone() wasn't + preserving parent/child ptrs properly leading to dangling + pointers. + +2010-04-26 Patrick West + + Fixed version check when a (alpha) or b (beta) at the end of the version + + M conf/libdap.m4 + +2010-04-22 James Gallagher + + Modified the transfer_attributes code so that it checks the + is_global property for individual attributes and + containers (before it was just containers). This entailed a change + to AttrTable in both the way it tracks and sets that property. + Fixed the unit tests. Several tests became invalid because they + tested handler-specific features that are now dealt with in the + handlers themselves. + +2010-04-22 mjohnson + + Added version update to INSTALL as well for #1540 + +2010-04-22 Patrick West + + Requires libxml2 2.6.16 + M libdap.spec + +2010-04-21 James Gallagher + + Initial check in of the new methods to handle the transfer (copy) + of attributes from the DAS to the DDS object. The new approach + includes a new set of methods that are defined in BaseType and + Constructor that are designed to be subclassed by handlers that + build odd DAS objects. The default methods handle merging a DAS + the follows the DAP 2.0 and later specs. The method in DDS calls + these 'type-based' methods. The old approach is commented out + using #if 0 ... #endif. + +2010-04-21 mjohnson + + Added #include for va_start and va_end Some systems + don't find it by default. + +2010-04-16 mjohnson + + Fixed invalid memory and broken invariant problem with using + deleted copy in Grid::add_var(). Ticket #1530 revealed this. + +2010-04-15 James Gallagher + + Added dist-hook target to remove the .svn subdirs that were being included in + the dist files. + + M libdap/Makefile.am + M bes/Makefile.am + +2010-04-15 James Gallagher + + Added Requires: lines to the .spec files for libdap and bes. + +2010-03-23 James Gallagher + + Fixes for distcheck build on 64-bit linux (FC 11) + + M SignalHandler.cc + M RCReader.cc + M parser-util.cc + M unit-tests/MarshallerTest.cc + +2010-03-23 James Gallagher + + Fixes from the 64-bit linux build. Includes fix for the DAS value issue where + values larger than 32-bits were being allowed in int32 attributes. + + M tests/das-test.cc + M tests/dds-test.cc + M libdap.spec + M Makefile.am + M parser-util.cc + M unit-tests/RCReaderTest.cc + +2010-03-23 James Gallagher + + Removed fdiostream from libdap and from the tests. Changed + generalUtilTest to match the new version of id2xml() + +2010-03-19 James Gallagher + + More changes for ticket 1521. Modified the id2xml() function so + that it no longer encodes the stuff in an octal escape sequence. + However, the value of the element in the DDX _is_ encoded + so that things like '<' will be escaped for the XML parser. Also, + bumped the version in configure.ac to 3.10.1b. One unit test fails + - this is a stop-gap fix so we can come back to that test and this + fix later. + +2010-03-19 James Gallagher + + Modified Connect.cc so that the FILE_METHODS are used, which + effectively removed the fdiostream.h header from the compilation. + Also, the DAP version has been bumped to 3.3 - a fix since libdap + included in the hyrax 1.6 beta release said 3.2 by mistake. + +2010-03-19 James Gallagher + + Fixes for ticket 1512. The function id2xml() was being used to + encode String (DAP) Attribute values and this was causing problems + when those values were not valid UTF-8 characters (e.g., when they + were less than 0x20). As a fix we dropped the encoding altogether + and fell back to the same encoding used by the DAS for the + values (that encoding represents all non ASCII values using octal + escape codes). I also fixed one of the unit tests that was + explicitly testing for the XDAP header to be 3.2 and made it + accept any value. + +2010-02-10 Patrick West + + fdiostreamtest.txt file added to EXTRA_DIST + M unit-tests/Makefile.am + +2010-02-07 Patrick West + + EXPRTest.at wasn't included in EXTRA_DIST + M tests/Makefile.am + +2010-02-05 Patrick West + + Added DISTCLEANFILES to both of these to clean up unit-tests and tests + files left around. + M tests/Makefile.am + M unit-tests/Makefile.am + +2010-02-05 Patrick West + + Including test_config.h, which has the local definition of abs_srcdir + and can be used to find files to load. + M fdiostreamTest.cc + +2010-02-05 Patrick West + + Using abs_srcdir instead of just srcdir. This fixes this part of the + distcheck problem. + M tests/DASTest.at + M tests/DDSTest.at + M tests/EXPRTest.at + +2010-02-05 Patrick West + + README.AIS has been removed + M libdap.spec + +2010-02-03 James Gallagher + + Fixes for autotest that I forgot to check in to the branch. + $srcdir/ --> $abs_builddir/ + +2010-02-01 James Gallagher + + Branch for Hyrax 1.6 + + A http://scm.opendap.org/svn/branch/libdap/3.10.0 + +2010-02-01 James Gallagher + + Changes for version 3.10.0 + + M configure.ac + M ChangeLog + M INSTALL + M libdap.spec + M README + M NEWS + +2010-02-01 James Gallagher + + Modified the way errors in attribute values are tested for. Now we + test errno first, then for values of 0.0 with ptr values non-null. + This matches the man page more closely then the old tests. + +2010-01-12 James Gallagher + + Fixed potential read beyond bounds in + XDRStreamUnMarshaller::get_vector. + +2010-01-12 James Gallagher + + Fixed return type in + Vector::set_value_slice_from_row_major_vector (changed to unsigned + int). + +2010-01-12 James Gallagher + + Fixed a comment + +2010-01-08 Patrick West + + --clean to clean build and src files for rpmbuild + M Makefile.am + +2010-01-07 James Gallagher + + Changed the documentation for server-side functions so that the + bulk of the documentation is now on the web. + +2010-01-07 James Gallagher + + Fixes to geogrid() so that the longitude wrapping feature works + and so that if latitude is upside down in the data, it's returned + as 'north up' data. + +2009-12-29 James Gallagher + + Shrew build changes that will hopefully lead to a version that we + can build using NMI's B&T system. Also, I added 'cccc' targets so + that we can take a 'metrics snapshot' without undue pain. + +2009-12-23 rduncan + + changes for win32 build + +2009-12-03 James Gallagher + + Added chmod of InstallationCheck script which seems to be needed + when buidling on osx 10.5.8. + + M update_mac_package_contents.pl + +2009-12-01 mjohnson + + o Needed to add explicit #include in order to find + memcpy for Red Hat Enterprise 5 using gcc-4.3.2. Seems they + cleaned up headers and you need to be explicit about the include + now. + +2009-11-30 mjohnson + + Fix for link bug on some platforms: initializing variables in a + switch without declaring a local scope block is an error. + +2009-11-30 mjohnson + + Fix for link bug on some platforms: initializing variables in a + switch without declaring a local scope block is an error. + +2009-11-24 mjohnson + + DDS.cc: fixed minor memory leak + + Array.cc, Array.h: added prepend_dim function for adding outer + dimensions. + + Grid.h, Grid.cc: added function to set the array part explicitly + without copying it. fixed potential memory leaks. added a few + extra checks on proper types being set. added function to add maps + to (to beginning or end of map vector to support new outer + dimensions). made _duplicate protected for subclasses to be able + to call it. + +2009-11-19 James Gallagher + + ticket 1399 - Grids that have only one member after projection + still must be wrapped in a Structure (because there might be two + Grids so projected and they might have members with the same names + - this is a real case). The Structures provide for unique FQN as + the DAP requires. + +2009-11-10 James Gallagher + + Removed libdapserver from the getdap link. + +2009-11-05 James Gallagher + + I changed how the FILE_METHODS compile-time switch was defined, + Now it's defined in the headers and not, directly in the config.h + or *.cc files. That was a bug. All tests pass now. I'm going to + check this in and then run tests on the entire shrew project. + +2009-11-04 James Gallagher + + Removed lots of accumulated 'old code' and reinstated the + FILE_METHODS compilation flag. + +2009-11-04 James Gallagher + + Likely fixes for the win32 build. Test these on a vm. + +2009-11-04 James Gallagher + + tweaks to get the ddx and dataddx responses working with the trunk + BES. See getdap's -x (ddx) and -X (DataDDX) responses. These work + using the -p x.y switch to set the DAP Accept version to x.y. Also + added the -t option to turn on HTTP protocol tracing. The new + parser DDXParserSXA2 reads the dapVersion XML attribute and sets + the value in the DDS instance. I also modified the Connect class + to recognize the dods_ddx and dods-ddx values for + Content-Description. + +2009-10-22 mjohnson + + Ticket #1442: + + o Fixed several potential memory leaks. + + o set_value now sets length. + + o Refactored cut & pasted sections to use helper funcs. + +2009-10-07 mjohnson + + Made simple_find() and simple_find_container() public for callers + that require non-recursing lookups. + +2009-10-06 James Gallagher + + I fixed problems with two sets of tests. First, the "Array of + structures with child arrays" test was failing because the + TestCommon and TestArray classes were not generating valid output + in the case where intern_data() was used. Second, the test for + String regexes where nothing matched was botched because when a + selection is applied to anything other than a Sequence and nothing + is selected the response from the server is broken. This will be + an issue for DAP4. In place of the test that used a Structure and + selected nothing, I added two tests of Sequences with strings and + tried both selections that return and don't return values. Both + cases work. + +2009-10-05 mjohnson + + Vector.h, Vector.cc: Cleaned up and moved doxygen comments to impl + file. + +2009-10-02 Patrick West + + This function is no longer used in libdap and is specific to usage in + dap-server. Moved the function. + M mime_util.h + +2009-10-02 James Gallagher + + Regarding the previous commit: On the IOOS branch I removed a 'bad + fix' added at some point where includes within util.h supplied the + class definitions for Byte, ..., Grid. So it looked like the .cc + files were not including must at all, but in fact not only were + they (and they need those includes) but _everything_ that used + util.cc/h was too. So here are the includes, back in the .cc files + where they belong. + +2009-10-02 James Gallagher + + Merged code from ioos/src/libdap rev 20393 that contained the + 'OtherXML' attribute implementation. This was apparently lost when + that branch was updated between the commit of that revision and + now. In addition to changes in AttrTable and the DAS and DDX + parsers, there are changes in tests/ and unit-tests/. There's also + a new function for converting octal escapes codes that is in + escaping.cc/h. I updated the DDX parser to use the SAX2 interface + and it now correctly processes namespaces and as a result, it's in + a new file 'DDXParserSAX2.cc/h'. Oddly, when I performed the + merge, an old 'feature' of the Type (Byte, ...) classes reemerged, + the 'include the world' feature. In testing I could not get a good + compile without these includes after the merge (but before the + merge it was fine). I'm going to leave those changes out of this + commit and see if the resulting code is OK. If not, I'l commit + those changes, too. + +2009-10-02 mjohnson + + Vector.cc, Vector.h: Added support for reserving data buffer + storage in advance, clearing out data buffers, and for copying a + Vector into a subset of the value buffer of another Vector. In + support of aggregation to allow Vector's to be combined into a + larger Vector. Ticket #1427 has more information. This was a + monotonic interface addition and code change only --- no + interfaces removed or changed. + +2009-09-25 James Gallagher + + Removed XDRFileMarshaller.h from DODSFilter.cc + +2009-09-25 James Gallagher + + It just seem right to save things at this point. Most of the + configure.ac files have been updated. The libdap software has had + a number of the FILE* methods/functions commented out using #if + FILE_METHODS and then #undef FILE_METHODS. We'll see how much of + the redundant code can be removed before the next Hyrax 1.6 + release. + +2009-09-25 James Gallagher + + Added fdiostream to the library because it seems we might be using + it someday soon. Modified expr-test to use temp. files and the + methods in DODSFilter and Connect - this is a better test of the + library. I modified XDRStreamUnMarshaller a fair amount but it's + still not work 100% and needs more error checking. This code still + uses XDRFileUnMarshaller. + + M DODSFilter.cc + A fdiostream.cc + M tests/expr-test.cc + M tests/TestByte.cc + M tests/Makefile.am + M Marshaller.h + D GNU/fdiostream_test.cc + D GNU/fdiostream.h + M Makefile.am + M DODSFilter.h + M XDRStreamUnMarshaller.cc + M HTTPConnect.cc + M XDRStreamMarshaller.cc + M Connect.cc + A + unit-tests/fdiostreamTest.cc + M unit-tests/Makefile.am + M unit-tests/MarshallerTest.cc + AM + fdiostream.h + +2009-09-22 James Gallagher + + Removed the xstream code - if we need this it's available on the web. + +2009-09-22 James Gallagher + + Fixed a bug in fdiostream and added support for a istream that + reads from a FILE* (ugh). + +2009-09-18 James Gallagher + + Uses the new file descriptor stream classes - that is, it tests + both input and output. + +2009-09-18 James Gallagher + + Added XDRStreamUnMarshaller to the Makefile.am + +2009-09-18 James Gallagher + + Added XDRStreamUnMarshaller + +2009-09-18 James Gallagher + + Minor fixes to the XDRStreamMarshaller class + +2009-09-17 James Gallagher + + Now includes the newly revamped stream buffer classes (fdiostream.h) + +2009-09-17 James Gallagher + + Added input stream buf for file descriptors and renamed the files. + Updated the README. + +2009-09-11 mjohnson + + Grid.cc: Made Grid add it's array and map vectors to the + Constructor _vars list so they get iterated by the superclass + Vars_Iter calls. For ticket #1406 + +2009-09-02 Nathan Potter + +2009-09-02 Nathan Potter + + libdap: tweaked configure.ac to fix uuid lib problem + +2009-09-02 James Gallagher + + Updated the pkg-config and dap-config scripts so they contain the UUID + library when needed. + + M libdap.pc.in + M libdapserver.pc.in + M dap-config.in + +2009-09-02 James Gallagher + + Build fixes for linux. Linux uses libuuid for the uuid functions + while OSX does not - I added a test for that library to + configure.ac and the corrsponding changes to the Makefile.am. I + also found and fixed two other errors that were not showing up on + the OSX platform. + + M DODSFilter.cc + M configure.ac + M tests/Makefile.am + M Makefile.am + +2009-09-02 mjohnson + + o Fixed typo causing build error. + +2009-08-31 James Gallagher + + Replace fgetln with fgets in DDXParser.cc. oddly package.m4 in + tests has the version number for the library. I changed that to 3.10.0 + (we should have something for the trunk better than a version number + that looks like a release number) since I didn't think 3.9.2 was a + good value... + +2009-08-31 James Gallagher + + Configure fixes. + + M configure.ac + +2009-08-31 James Gallagher + + Updates for make dist. + + M tests/Makefile.am + M INSTALL + M Makefile.am + +2009-08-31 James Gallagher + + Added + +2009-08-31 James Gallagher + + Added. + +2009-08-28 James Gallagher + + Refactor of libdap: AIS removed from build. In addition, + bug tickets addressed for Array, DDS, Vector and parser-util.cc + +2009-08-27 James Gallagher + + Moved mime_util to libdap from libdapserver. + +2009-08-27 James Gallagher + + Removed unnecessary libdapserver use for the unit-tests after moving cgi_util to mime_util + and putting the latter in libdap. + +2009-08-27 James Gallagher + + Added code to build and read the DataDDX response for DAP4. + Connect has request_data_ddx() and request_data_ddx_url() + methods that will ask a server for the DataDDX and parse + the returned response. Connect::read_data() can read a DataDDX + response from a file; getdap -M - can read the DataDDX returned + by besstandalone when it's asked to return a DataDDX. DODSFilter + now has a send_data_ddx() method and appropriate functions are + now in mime_util.cc/h to build and parse the multipart MIME + response. I also refactored the code somewhat renaming cgi_util to + mime_util and removing redundant MIME parsing code in Connect and + HTTPConnect - now all the MIME header parsing is done by functions + in mime_util. Since these are used by both client and server + operations, that should be moved to libdap (from libdapserver). + +2009-08-21 James Gallagher + + Changed ID to Id to match the RFCs in the set_mime...() functions.. + Also removed the set_client_dap_version() method and the matching get + methods - These were causing more confusion than anything else. Use + set_dap_version() and the matching get methods. + + M cgi_util.cc + M DDS.cc + M DDS.h + +2009-08-21 James Gallagher + + Changed ID to Id in the set_mime...() code to match the RFCs; fixed here in + tests. + + M unit-tests/DODSFilterTest.cc + +2009-08-21 James Gallagher + + Fixed an error where getdomainname() can return nothing + and not indicate and error. Now the default domain name + is opendap.org in that case. + +2009-08-20 James Gallagher + + Updates for the DataDDX response. New values were added + to the ObjectType and and EncodingType enums. DODSFilter + now has a send_data_ddx method that can either supply just + the body (as needed by the OLFS) or the complete message. + Unit tests added in unit-tests.DODSFilterTest. In HTTPConnect, + get_description_type() and get_object() were also updated. + Lastly, I change the values of Content-Description slightly + so they were a little less clunky and made the dap4 prefixed + values use dashes instead of underscores. + +2009-08-19 James Gallagher + + Interim version of autogen.sh for our projects - fix the --force option. + + M autogen.sh + +2009-08-19 James Gallagher + + Removed generated files. + + D conf/mkinstalldirs + D conf/depcomp + D conf/compile + D conf/missing + D conf/config.guess + D conf/config.sub + D conf/ltmain.sh + D conf/install-sh + +2009-08-18 Nathan Potter + + libdap: Repaired bad string syntax and formatting. + +2009-08-18 mjohnson + + Added set_name() override to set the name of the template var() as + well since this is what is output in a DDS and they didn't match. + Ticket #1346. + +2009-08-18 tomss + + Changes to documentation only. Formatted ce function documentation + to be easier to read and removed a bad paragraph from the main + doxygen docs. + +2009-08-04 James Gallagher + + Added autogen.sh which is probably better than always running + autoreconf. + + AM autogen.sh + +2009-08-04 James Gallagher + + Changes for gcc 4.4: Removed warnings about misplaced or missing const + qualifiers and logic issues. Also fixed a missing header under FC 11 + (util_mit.cc needs for sprintf). + + M Int16.cc + M conf/mkinstalldirs + M conf/depcomp + M conf/missing + M conf/config.guess + M conf/config.sub + M conf/ltmain.sh + M conf/install-sh + M INSTALL + M UInt16.cc + M Byte.cc + M parser-util.cc + M Float32.cc + M HTTPConnect.cc + M Str.cc + M ce_functions.cc + M das.y + M UInt32.cc + M util_mit.cc + M dds.y + M RCReader.h + +2009-07-28 James Gallagher + + Modified AISDataBaseParser.cc so that the library will build + with the --enable-debug=2 configure option. ticket 1318 + +2009-07-21 mjohnson + + del_attr_table needed to 0 out parent as well to preserve invariant. + +2009-07-21 mjohnson + + Fixed memory leak in del_attr_table for Ticket #1326 and #1327. + +2009-07-21 mjohnson + + Fixed several issues with the DAP type checking routines (which I re-used in NcML handler) failing to reject out of range Int32 and accepting a negative number as valid UInt32. + +2009-05-08 James Gallagher + + Merged changes for ticket 1301 from the tags/libdap/3.9.2 code. + + M Makefile.am + M das.y + M unit-tests/AISMergeTest.cc + M unit-tests/DASTest.cc + M unit-tests/DDSTest.cc + +2009-04-09 James Gallagher + + Changes for autoconf 2.63 (should work with 2.62 also). + + M EXPRTest.at + +2009-04-08 James Gallagher + + Merged the 3.9.2 tagged code back into the trunk. + + _M . + M RValue.cc + M Int16.cc + M configure.ac + M DDXParser.cc + M cgi_util.cc + M ChangeLog + A + conf/link-warning.h + M DDS.cc + M Array.cc + _M tests/das-testsuite/test.1.das + _M tests/expr-testsuite/test.2 + _M tests/expr-testsuite/test.3 + _M tests/expr-testsuite/test.c0 + _M tests/expr-testsuite/test.4 + _M tests/expr-testsuite/test.c1 + _M tests/expr-testsuite/test.c2 + _M tests/expr-testsuite/test.5 + _M tests/expr-testsuite/test.c3 + _M tests/expr-testsuite/test.6 + _M tests/expr-testsuite/test.c4 + _M tests/expr-testsuite/test.7 + _M tests/expr-testsuite/test.8 + _M tests/expr-testsuite/test.9 + _M tests/expr-testsuite/test.a + _M tests/expr-testsuite/test.b + _M tests/expr-testsuite/test.d + _M tests/expr-testsuite/test.61 + _M tests/expr-testsuite/test.2a + _M tests/expr-testsuite/test.cc0 + _M tests/expr-testsuite/test.cc1 + _M tests/expr-testsuite/test.1 + M tests/package.m4 + M INSTALL + M UInt16.cc + M libdap.spec + M Byte.cc + M GNU/GNURegex.cc + M README + M Makefile.am + M HTTPCache.cc + M ce_expr.y + M Float32.cc + M dds.lex + M util.h + M grammarfiles/ce_expr.tab.cc + M NEWS + M Str.cc + M Sequence.cc + A + gl/m4/absolute-header.m4 + M gl/m4/wchar.m4 + M gl/m4/stdint.m4 + A + gl/m4/malloc.m4 + M gl/m4/regex.m4 + M gl/m4/gnulib-comp.m4 + A + gl/m4/unistd_h.m4 + M gl/m4/include_next.m4 + M gl/m4/gnulib-cache.m4 + D gl/m4/onceonly_2_57.m4 + A + gl/m4/stdlib_h.m4 + M gl/m4/extensions.m4 + M gl/localcharset.c + M gl/regex_internal.c + M gl/malloc.c + M gl/localcharset.h + M gl/regex_internal.h + M gl/regexec.c + A + gl/stdlib.h + M gl/regcomp.c + M gl/alloca_.h + M gl/wchar_.h + M gl/stdint_.h + A + gl/unistd_.h + M gl/stdbool_.h + M gl/regex.c + A + gl/unistd.h + M gl/regex.h + M gl/wctype_.h + A + gl/stdlib_.h + M gl/Makefile.am + M gl/gettext.h + M gl/alloca.c + M ce_functions.cc + M expr.h + M AttrTable.h + M GridGeoConstraint.cc + M util.cc + M UInt32.cc + M XDRStreamMarshaller.cc + M Grid.cc + M escaping.cc + M unit-tests/HTTPCacheTest.cc + M unit-tests/AttrTableTest.cc + M unit-tests/generalUtilTest.cc + M dds.y + M Clause.cc + M Array.h + M ce_expr.lex + +2009-04-08 James Gallagher + + Bumped up the autoconf version to 2.61 which we need for the top_srcdir + variable. Other minor fixes like spelling errors in comments. + + M configure.ac + M INSTALL + M GNU/outbuf.h + M ce_expr.y + M Clause.cc + M AISDatabaseParser.cc + +;; Local Variables: +;; coding: utf-8 +;; End: +2009-04-06 James Gallagher + + Bug fix: entry::clone() was performing a deep copy in aliases when + the dtor expected aliases to use a shallow copy operation. This + resulted in a memory leak. See #1293. + +2009-03-16 James Gallagher + + Version 3.9.1 + + M configure.ac + M ChangeLog + M INSTALL + M libdap.spec + M NEWS + +2009-03-10 James Gallagher + + Removed! + + D gl/alloca.h + +2009-03-10 James Gallagher + + Added. + + AM gl/alloca.h + +2009-03-05 James Gallagher + + Repaired a problem in the CE parser where the new test for + multiply projected arrays was failing because the test + was applied after the array was marked and so the projected + sizes differed, which looked like to different projections + but was actually two steps of the projection process. + +2009-03-05 James Gallagher + +2009-03-05 James Gallagher + + Fixes from code review. + + M RValue.cc + M Int16.cc + M DDXParser.cc + M cgi_util.cc + M UInt16.cc + M Byte.cc + M GNU/GNURegex.cc + M Makefile.am + M Float32.cc + M dds.lex + M util.h + M Str.cc + M Sequence.cc + M gl/localcharset.c + M ce_functions.cc + M GridGeoConstraint.cc + M util.cc + M UInt32.cc + M XDRStreamMarshaller.cc + M Grid.cc + +2009-02-26 James Gallagher + + I fixed a lingering problem with HTTPCache where its per-process + lock on the cache could not be obtained because the cache created + by a previous process was not writable. I used umask(0) to solve + that. I also made sure that sending a CE that constrains an array + of structures in two different ways returns an error. Lastly, I + removed old code from the Array class + +2009-02-20 James Gallagher + + Modified the ce_expr.y parser so that variable names can be + quoted (double quotes). + +2009-02-18 James Gallagher + + New version of www2id(). Now the third parameter can accept more than + one escape code to leave untouched. + +2009-01-30 James Gallagher + + Moved here to coalesce branches + +2009-01-30 James Gallagher + + Updated the 'CURRENT' revision for libdap and libdapserver. + Bummer, but we changed interfaces. + +2009-01-29 James Gallagher + + Move the code to a branch while we're in the beta period. + + A http://scm.opendap.org/svn/branch/libdap/3.9.0 + D http://scm.opendap.org/svn/tags/libdap/3.9.0 + +2009-01-28 James Gallagher + + Changes for the distcheck target. + + M configure.ac + M INSTALL + M GNU/outbuf.h + M ce_expr.y + M dds.y + M Clause.cc + M AISDatabaseParser.cc + +2009-01-28 James Gallagher + + Release 3.9.0 + + A http://scm.opendap.org/svn/tags/libdap/3.9.0 + +2009-01-28 James Gallagher + + Updates for release 3.9.0 + + M configure.ac + M ChangeLog + M libdap.spec + M GNU/outbuf.h + M README + M Makefile.am + M NEWS + M unit-tests/Makefile.am + +2009-01-28 James Gallagher + + removed xstream references - we're not using this code although + I'd like to keep the source in the library since we might use it + at some point. + + M Makefile.am + +2009-01-14 James Gallagher + + Patch from Patrice for missing string.h header. + Spelling errors in Makefile.am. + + M Makefile.amcon + M ce_expr.y + +2009-01-12 James Gallagher + + I changed the etag value used in the HTTPConnetTest so the test + will pass. The file changed and thus the etag changed when we + moved the test server. + +2008-12-04 Patrick West + + curl.h is included in HTTPConnect.h, which is included in + AISMerge.h, which is included in getdap.cc. So need CURL_CFLAGS. + Not sure why this doesn't show up in other builds, however. + + M Makefile.am + +2008-12-03 James Gallagher + + Commented-out the #if FILE_METHODS ... #endif preprocessor lines. + For some reason these are breaking the library when FILE_METHODS + is one (but not when it's zero). Anyway, the FILE* methods are + needed for loaddap. I'm leaving the directives in as comments + since maybe someday I'll figure out _why_ these broke the library. + + M DODSFilter.cc + M Int32.h + M Int16.cc + M Structure.h + M Str.h + M Sequence.h + M Int16.h + M DDS.cc + M BaseType.h + M Array.cc + M Float64.h + M UInt16.cc + M AlarmHandler.h + M Byte.cc + M Byte.h + M DDS.h + M BaseType.cc + M Constructor.h + M Int32.cc + M DODSFilter.h + M Float32.cc + M Grid.h + M Structure.cc + M Str.cc + M Sequence.cc + M Float32.h + M Constructor.cc + M UInt32.h + M Float64.cc + M UInt32.cc + M Grid.cc + M unit-tests/SequenceTest.cc + M unit-tests/Makefile.am + AM unit-tests/MarshallerTest.cc + M Array.h + M UInt16.h + +2008-11-25 James Gallagher + + Always build with the FILE_METHODS compile-time directive true. This + may cause problems... + +2008-11-25 James Gallagher + + Found more 'dataset' arguments, previously commented out, and + removed them. Makefile.am fixes that may address some build issues + seen by other people. + +2008-11-25 James Gallagher + + I had previously commented out all of the places where the data + set name was passed around to the various methods that evaluate a + CE. Patrick has moved information about the data set name into the + BaseType class so that a single DDS/DataDDS/DDX/... can be built + from multiple files/data sets. Passing the parameter was + redundant, so I commented it out. I completed the removal by + deleting those comments. + + M RValue.cc + M DDXParser.h + M DDXParser.cc + M ce_functions.h + M ConstraintEvaluator.cc + M ArrayGeoConstraint.h + M GeoConstraint.h + M GridGeoConstraint.h + M ce_expr.y + M RValue.h + M ce_functions.cc + M expr.h + M GridGeoConstraint.cc + M ConstraintEvaluator.h + M Clause.h + M ArrayGeoConstraint.cc + M unit-tests/ArrayGeoConstraintTest.cc + M unit-tests/GridGeoConstraintTest.cc + M Clause.cc + M GeoConstraint.cc + +2008-11-17 James Gallagher + + Added check of C:\opendap when looking for .dodsrc on WIN32 + +2008-11-14 James Gallagher + + Merged the xmlrequest branch to the trunk. + + _M . + M RValue.cc + M DDXParser.h + M DODSFilter.cc + A + xstream + A + xstream/fd.h + A + xstream/except_posix.h + A + xstream/posix.cpp + A + xstream/posix.h + A + xstream/common.cpp + A + xstream/fd.cpp + A + xstream/common.h + A + xstream/except.h + M Int32.h + M Int16.cc + M Structure.h + M Str.h + M Sequence.h + M configure.ac + M Connect.h + M DDXParser.cc + M Ancillary.cc + M ce_functions.h + M Int16.h + D docs/html.tar.gz + M ConstraintEvaluator.cc + M DDS.cc + M BaseType.h + M Array.cc + M tests/das-testsuite/config/unix.exp + M tests/expr-testsuite/expr-test.0/test.g.exp + M tests/expr-testsuite/expr-test.0/test.6.exp + M tests/expr-testsuite/config/unix.exp + M tests/expr-test.cc + A + tests/io_test.cc + M tests/grid-func-testsuite/config/unix.exp + M tests/Makefile.am + M tests/dds-test.cc + M Float64.h + M UInt16.cc + M AlarmHandler.h + M Byte.cc + M Byte.h + D GNU/Pix.h.never + D GNU/GNUerror.cc.never + A + GNU/outbuf.cc + D GNU/builtin.h.never + A + GNU/outbuf.h + M GNU/README + M BaseType.cc + M DDS.h + M GeoConstraint.h + M Makefile.am + M GridGeoConstraint.h + M Constructor.h + M Int32.cc + M DODSFilter.h + M ce_expr.y + M Float32.cc + M Grid.h + M HTTPConnect.cc + M Structure.cc + M RValue.h + M getdap.cc + M Str.cc + M Sequence.cc + M Float32.h + M ce_functions.cc + M expr.h + M GridGeoConstraint.cc + M Constructor.cc + M UInt32.h + M AISMerge.cc + M ConstraintEvaluator.h + M Clause.h + M ArrayGeoConstraint.cc + M Float64.cc + M UInt32.cc + M Grid.cc + M Connect.cc + M HTTPConnect.h + D unit-tests/ais_testsuite/ais_database.xml + M unit-tests/ais_testsuite/ais_database.xml.in + M unit-tests/AISResourcesTest.cc + M unit-tests/DDSTest.cc + M unit-tests/test_config.h.in + M unit-tests/HTTPConnectTest.cc + M unit-tests/DDXParserTest.cc + M unit-tests/ddsT.cc + M unit-tests/ByteTest.cc + M unit-tests/CEFunctionsTest.cc + A + unit-tests/ddx-testsuite/error.06.ddx + A + unit-tests/ddx-testsuite/test.0c.ddx + A + unit-tests/ddx-testsuite/test.0d.ddx + A + unit-tests/ddx-testsuite/test.00.ddx + M unit-tests/Makefile.am + M Clause.cc + M GeoConstraint.cc + M Array.h + M UInt16.h + +2008-11-12 James Gallagher + + Fixed the libcurl and libxml2 tests in confgure.ac - they now + test the values of the two programs correctly. + +2008-11-06 James Gallagher + + Minor changes that are part of a fix for ticket 975. The complete + changes are not done yet since the tests are not all passing for + the intern_data() methods (but do pass for the + serialize()/deserialize() methods and it might be that the problem + lies in the TestArray class and not in the libdap code outside the + tests. See svn/tags/libdap/partial_975_fix and the ticket itself + for more information. + + M Array.cc + M tests/expr-test.cc + M tests/TestStructure.cc + M BaseType.cc + M ce_expr.y + M Structure.cc + M Vector.cc + M Array.h + +2008-10-30 James Gallagher + + Tests d1 and d3 will fail until I sort out just what is happening with + the array of structures and CEs in the Test* classes. + +2008-10-10 James Gallagher + + Added data file for Structure Array tests. + +2008-10-10 James Gallagher + + Modified ce_expr.y so that arrays of structure parse using the new + syntax. Example: s2[0:4].m[2:7]. See ticket 975. + + M DDS.cc + M Vector.cc + M ce_expr.y + +2008-10-10 James Gallagher + + Added tests for Array of Structures fix - see ticket #975 + +2008-10-08 James Gallagher + + Ticket 1188: can not --> cannot. This was for the a handful + of error messages, but I wound up changing it everywhere since + that was easier. + +2008-10-08 James Gallagher + + Fix for ticket 1121 - DDX not well formed for some HDF4 datasets + because the element Alias was not closed. + + M AttrTable.cc + +2008-09-25 James Gallagher + + Added support for cookies to libdap. Cookies will be saved in a + 'cookie jar' if that option/parameter is set int he .dodsrc file. + + _M tests + M RCReader.cc + M HTTPConnect.cc + M HTTPConnect.h + _M unit-tests/cache-testsuite + M RCReader.h + +2008-09-25 James Gallagher + + Removed a test case fro AISResourceTest.cc since it was causing false + negatives when run as root on FC8. + +2008-09-19 James Gallagher + + Corrections for comments + +2008-09-18 James Gallagher + + Fixes for tests after moving test.opendap.org - some of the + test files appear to be different (not sure I understand + that, but it's true, they are). Also, minor fixes for the + new DDX31 symbol and xml header info to fix build errors. + +2008-09-17 James Gallagher + + Merged multifile branch + +2008-09-17 James Gallagher + + Updated docs. + + AM docs/html.tar.gz + +2008-09-16 James Gallagher + + Check in simple changes before branching to start the bulk + of the DAP 3.2 changes. + +2008-08-26 James Gallagher + + Added a one option to getdap (-M) that enables it to + read from files that don't have MIME headers. + +2008-08-06 Patrick West + + File accidentally added by eclipse + + D ais_database.xml + +2008-08-06 Patrick West + + Deleting fines accidentally added by eclipse + + D ce_expr.tab.hh + D 3.8.1.diffs + D libdap-3.8.1.tar.gz + D tmp16780 + D deflate + D ce_expr.output + D .cproject + D libdap-3.8.2.fpr + D libdap.pc + D tests/das-test.sum + D tests/dds-test.sum + D tests/expr-test + D tests/das-test + D tests/dds-test + D tests/expr-test.sum + D tmp16480 + D das.tab.hh + D dds.tab.hh + D ChangeLog.tmp + D Error.tab.hh + D gse.tab.hh + D libdapclient.pc + D das.output + D dds.output + D libdapserver.pc + D Error.output + D gse.output + D unit-tests/CEFunctionsTest + D unit-tests/cache-testsuite/cleanup.sh + D unit-tests/parserUtilTest + D unit-tests/sequenceT + D unit-tests/marshT + D unit-tests/DDSTest + D unit-tests/generalUtilTest + D unit-tests/ArrayTest + D unit-tests/ArrayGeoConstraintTest + D unit-tests/DDXParserTest + D unit-tests/.dodsrc + D unit-tests/cgiUtilTest + D unit-tests/arrayT + D unit-tests/DASTest + D unit-tests/SequenceTest + D unit-tests/AISDatabaseParserTest + D unit-tests/ancT + D unit-tests/HTTPConnectTest + D unit-tests/test_config.h + D unit-tests/AISMergeTest + D unit-tests/ByteTest + D unit-tests/structT + D unit-tests/AttrTableTest + D unit-tests/GridGeoConstraintTest + D unit-tests/RCReaderTest + D unit-tests/ddsT + D unit-tests/DODSFilterTest + D unit-tests/HTTPCacheTest + D unit-tests/attrTableT + D unit-tests/AISResourcesTest + D unit-tests/RegexTest + D unit-tests/SignalHandlerTest + D svn-commit.tmp + D getdap + +2008-08-04 James Gallagher + + Moved get_type() out of ObjectType.h and into HTTPConnect.cc and + changed its name to get_description_type() which is not only + more accurate but also less likely to conflict with other functions + (there are two other 'get_type()'s in libdap already). There are + other related changes. + +2008-08-01 James Gallagher + + Minor fix in dds.lex - test for an error from fgets(). Closes + ticket 1068. + +2008-08-01 James Gallagher + + Fixed the expr-test tests - they were only running the + intern_data() code and not the serialize/deserialize code. Also, + removed use of move_dds() from expr-test as it is no longer + needed. + +2008-07-31 James Gallagher + + String attribute values are always quoted when the DAS is written + out (now). This is part of the fix for ticket 1163 (the other part + being changes to the handlers). See README for more information. + + M tests/das-testsuite/das-test.0/test.8.exp + M tests/das-testsuite/das-test.0/test.9.exp + M tests/das-testsuite/das-test.0/test.11.exp + M tests/das-testsuite/das-test.0/test.1.exp + M tests/das-testsuite/das-test.0/test.2.exp + M tests/das-testsuite/das-test.0/test.3.exp + M tests/das-testsuite/das-test.0/test.4.exp + M README + M util.h + M AttrTable.cc + M Vector.cc + M das.y + M util.cc + M unit-tests/AttrTableTest.cc + +2008-07-28 hyoklee + + Added dods_url_c type for Vector::value() function. + +2008-07-23 James Gallagher + + Merged changes from tags/libdap/3.8.2 (-r18931:19106 + $svn/tags/libdap/3.8.2 .) + + M Ancillary.cc + M VCPP/BeforeInstall.txt + M VCPP/libdap.iss + M VCPP/config.h + M VCPP/Makefile + +2008-07-15 James Gallagher + + Patch from Patrice Dumas for gnulib/autoconf 2.62 compatibility. + + M gl/m4/extensions.m4 + +2008-07-15 Patrick West + + Problem where nested sequences not having any values selected + because of constraint evaluation, so nested sequences aren't being + pushed to the value stack. When coming out of parent_part_two to + the end of parent_part_one, the stack is popped to get that + sequences values. Since nothing was being pushed, the values for + that sequence were being popped leaving an empty stack. So when + top is called on an empty stack, caused bus error. + + Also, in Sequence.cc, a dynamic cast was being made to a Sequence + variable called tmp. dynamic casts were called three more times in + the method. These can get expensive, so removed the other dynamic + casts and using first variable called tmp. + + Added and modified DBG statements. + M tests/TestInt32.cc + M tests/TestFloat32.cc + M tests/TestByte.cc + M tests/TestSequence.cc + M BaseType.cc + M Sequence.cc + M AISDatabaseParser.cc + +2008-06-25 James Gallagher + + Fixed one more problem with the xdr-datatype header. Assuming + that XDR uses the C99 types if the inttypes.h header is present + works on new FC machines but not OS/X, et cetera. I removed that + from the Makefile.am. + + M OSX_Resources/InstallationCheck + M OSX_Resources/Info.plist + M conf/acinclude.m4 + M Makefile.am + +2008-06-25 James Gallagher + + Fixes - I hope - to the *-datatypes headers. Other updates for + comments and documentation. + + M AISResources.cc + M configure.ac + M xdr-datatypes-static.h + M xdr-datatypes-config.h.in + M main_page.doxygen + M Error.cc + M conf/acinclude.m4 + M HTTPCacheTable.cc + M doxy.conf + +2008-06-23 James Gallagher + + Removed apue_db. Updates to the documentation for 3.8.2 + + M ChangeLog + D apue_db + M INSTALL + M README + M NEWS + +2008-06-23 James Gallagher + + Minor changes - comments & docs. + + M HTTPCacheTable.cc + M HTTPCacheTable.h + +2008-06-23 James Gallagher + + Minor changes from the 3.8.1 code except that the HTTP Caching + changes developed here on the trunk of libdap are now + included (libdap 3.8.1 excluded those fixes). + + M configure.ac + M ChangeLog + M NEWS + M ce_functions.cc + M unit-tests/AISMergeTest.cc + M unit-tests/HTTPCacheTest.cc + +2008-06-16 James Gallagher + + Merged changes from the 3.8.1 tag. + + M VCPP/BeforeInstall.txt + M VCPP/libdap.iss + A VCPP/unistd.h + M libdap.spec + M README + +2008-06-16 James Gallagher + + AISMergeTest updated to work with the newer test.opendap.org + fnoc1 test data set (which has slightly different attributes). + +2008-06-16 James Gallagher + + Removed HTTPCacheTable::CacheEntry's lock since it was redundant + and was causing a dead lock. Also removed RCReader's debug output. + +2008-06-16 Patrick West + + Added access to response file. + M HTTPResponse.h + M Response.h + +2008-06-10 James Gallagher + + The lock used to protect the CacheEntry object was redundant and I + removed it. Also, I backed out other changes but retained some of + the added encapsulation added to the CacheEntry struct. I checked + this code in because I fairly certain it works, but the test + server is down and so I cannot run the unit tests for HTTPCache. + + M HTTPCacheTable.cc + M HTTPCacheTable.h + M unit-tests/HTTPCacheTest.cc + +2008-06-09 Dan Holloway + + Added ifdef HAVE_UNISTD_H, include , required on RedHat 3.+ + +2008-05-30 Patrick West + + Moved ancillary functions from DODSFilter and cgi_util to + Ancillary class as static methods. Moved the ancillary tests from + cgiUtilTest to ancT. Had to update configure.ac and Makefile.am to + get libdap to compile, removing apue directory. + + M DODSFilter.cc + M configure.ac + M cgi_util.cc + A Ancillary.cc + M Makefile.am + M cgi_util.h + A Ancillary.h + A unit-tests/ancT.cc + M unit-tests/cgiUtilTest.cc + M unit-tests/Makefile.am + +2008-05-30 James Gallagher + + Test svn switch (I added a space to the to the top of the file). + + M util.cc + +2008-05-22 James Gallagher + + Fixed comments in the ce functions - they said to use "version" + when it's actually a blank arg list that returns the response. + +2008-05-21 James Gallagher + + Made the directory name nicer + + M configure.ac + A apue_db + D apue_db/apue.h + D apue_db/include + D apue_db/db + A apue_db/db.c + D apue_db/lib + A apue_db/lockreg.c + D apue_db/Make.defines.linux + A apue_db/t4.c + A apue_db/error.c + M Makefile.am + D apue_db_lib + M das.lex + +2008-05-21 James Gallagher + + Removed include subdir + + D apue_db_lib/include + +2008-05-21 James Gallagher + + Removed apue.h header. + + D apue_db_lib/apue.h + M apue_db_lib/db.c + M apue_db_lib/lockreg.c + M apue_db_lib/t4.c + M apue_db_lib/error.c + +2008-05-21 James Gallagher + + Removed + + D apue_db_lib/Make.defines.linux + +2008-05-21 James Gallagher + + Get rid of some temporary directories... + + D apue_db_lib/db + D apue_db_lib/lib + +2008-05-21 James Gallagher + + Added Advanced Programming in the Unix Environment DB. + + A apue_db_lib/apue.h + A apue_db_lib/apue_db.h + M apue_db_lib/lib/libapue.a + A apue_db_lib/db.c + A apue_db_lib/lockreg.c + A apue_db_lib/t4.c + AM apue_db_lib/Makefile.am + A apue_db_lib/error.c + +2008-05-21 James Gallagher + + Copied + +2008-05-20 James Gallagher + + Removed cruft from the Steven's db checkin. + +2008-05-20 James Gallagher + + Added the Steven's db code in apue_db_lib (which I might rename later). + +2008-05-20 James Gallagher + + Removed #if 0 .. #endif lines. + +2008-05-20 James Gallagher + + Read/Write locking in place. Ready to start changing over from a + single index file to a distributed way of storing the entries. + +2008-05-15 James Gallagher + + Moved the locking of entries into the cache table class. This sets + the stage for changes to the table implementation so that it can be + stored on disk and not in memory. + +2008-05-14 James Gallagher + + removed #if 0 after refactoring HTTPCache, HTTPCacheTable + and CacheEntry. Tests now run. + +2008-05-14 James Gallagher + + CacheEntry has not been encapsulated, but only to the extent + that fields are manipulated with accessor/mutator methods. + The basic logic is still exposed to HTTPCache. + +2008-05-14 Patrick West + + Requires using unistd.h if HAVE_UNISTD_H is defined, which requires + including config.h. + M unit-tests/marshT.cc + M unit-tests/RCReaderTest.cc + +2008-05-14 Patrick West + + unlink requires unistd.h on linux machines. + M unit-tests/marshT.cc + +2008-05-13 James Gallagher + + Added HTTPCacheTable to the VCPP Makefile + +2008-05-13 James Gallagher + + I Have refactored HTTPCache so that most of the operations + on the index file and in-memory 'database' of the cache + contents is now in the HTTPCacheTable class. + +2008-05-09 James Gallagher + + Added HTTPCacheTable class. HTTPCache will now be refactored + so that it makes all cache table accesses through this class' + interface. See ticket #1114 and #1118. + +2008-04-25 James Gallagher + + Wrapped CURL_PROXYAUTH in #ifdef #endif to accommodate older versions of + libcurl. + + M HTTPConnect.cc + +2008-04-25 James Gallagher + + Spelling errors fixed. + + M VCPP/BeforeInstall.txt + M README.dodsrc + M RCReader.cc + +2008-04-25 James Gallagher + + Build tools no longer installed + +2008-04-24 James Gallagher + + Added installer setup file and License for the installer + +2008-04-23 James Gallagher + + Comments and strings modified as part of the win32 build of 3.8.1 + from svn + +2008-04-23 James Gallagher + + Removed Win32 definition of vsnprintf() for VC 2008 compat - edit + for use with VC 2005 + +2008-04-23 James Gallagher + + Fixes for libdap 3.8.0 + +2008-04-21 James Gallagher + + Ticket 1095: The proxy support in HTTPConnect baroque and caused + many people problems. I expanded on the syntax allowed (the old + syntax is still supported) so that it mirrors the syntax most + users expect. I updated the docs including the comments in the + .dodsrc file. + + M Makefile.am + M RCReader.cc + M NEWS + M HTTPConnect.cc + M unit-tests/RCReaderTest.cc + A unit-tests/rcreader-testsuite/test4.rc + A unit-tests/rcreader-testsuite/test5.rc + +2008-04-15 James Gallagher + + Added code to follow redirects in HTTPConnect.cc + +2008-04-07 Patrick West + + Delete being called on array without [], as is being called below the + exception condition. + M HTTPCache.cc + +2008-03-19 James Gallagher + + Added info about win32 locations for .dodsrc + + M README.dodsrc + +2008-03-19 James Gallagher + + Added Patrice Dumas' patch for + + M SignalHandler.cc + M gse.lex + M dds.lex + M HTTPConnect.cc + M das.lex + M ce_expr.lex + +2008-03-04 Patrick West + + Now checks for which architecture the build is being run on and + updates the InstallationCheck script M + update_mac_package_contents.pl + +2008-03-04 Patrick West + + 3.8.0 release of libdap. + + M configure.ac + M ChangeLog + M OSX_Resources/Info.plist + M OSX_Resources/ReadMe.txt + M libdap.spec + M README + M NEWS + +2008-03-03 James Gallagher + + Added conditional include of unistd.h for RHEL3 build. + + M SignalHandler.cc + M tests/TestArray.cc + M HTTPConnect.cc + M DAS.cc + +2008-03-03 James Gallagher + + Header patch from Patrice Dumas. + + Many files modified. + +2008-03-03 James Gallagher + + Directory regex --> pathname_ok() call. A minor change. + + M Util.cc + +2008-03-02 James Gallagher + + Added shell commands so build works with bison 1.28. That + version treats the -o option differently than the 2.x version. + + M Makefile.am + +;; Local Variables: +;; coding: utf-8 +;; End: +2008-03-03 James Gallagher + + Added conditional include of unistd.h for RHEL3 build. + M SignalHandler.cc + M tests/TestArray.cc + M HTTPConnect.cc + M DAS.cc + +2008-03-03 James Gallagher + + Header patch from Patrice Dumas. + Many files modified. + +2008-03-03 James Gallagher + + Directory regex --> pathname_ok() call. A minor change. + M Util.cc + +2008-03-02 James Gallagher + + Added shell commands so build works with bison 1.28. That + version treats the -o option differently than the 2.x version. + M Makefile.am + +2008-03-01 James Gallagher + + Tweaked the comments about the YY_INPUT macro. + M dds.lex + +2008-02-29 James Gallagher + + Switch back to the old YY_INPUT macro for now... + M dds.lex + +2008-02-29 James Gallagher + + I found that a number of the places where a *.tab.h was included did + not get changed when I went to the .hh file names (which is how + bison does things when you tell it to make the output a *.tab.cc + file). Here are the fixes. the distcheck and rpm targets now work. + M Error.lex + M Int16.cc + M error-test.cc + M tests/expr-test.cc + M tests/dds-test.cc + M UInt16.cc + M Int32.cc + M gse-test.cc + M Str.cc + M Float64.cc + M Operators.h + M UInt32.cc + +2008-02-29 James Gallagher + + There are three changes in these files: 1. formatting; + 2. Grammar builds; 3. Security fixes. For the formatting + changes some files just got messed up and I was sick of + looking at it. Grammar changes amount to simplifying the + builds of the lex and y files. Now the sed calls in the + Makefile have been removed - commented out to be completely + removed later. 3. Nothing major on the security front + but I made some changes all the same. + +2008-02-29 Patrick West + + Updates for release of 3.7.11. + M configure.ac + M ChangeLog + M INSTALL + M libdap.spec + M README + M NEWS + +;; Local Variables: +;; coding: utf-8 +;; End: +2008-02-28 Patrick West + + If cleanup.sh does not exist, then a make clean will error out. + clean-local depends on cleanup.sh. + M cache-testsuite/Makefile.am + +2008-02-28 Patrick West + + Added comment regarding ais_testsuite directory and how those files + are being distributed. + M unit-tests/Makefile.am + +2008-02-28 Patrick West + + Removing generated files from Makefile.am. Removing temporary files + in marshT created for writing marshalled data. cleanup script + cleaned up. cache-testsuite makefile running local cleanup.sh since + cleanup.sh is generated, also added distclean-local to remove the + dods_cache directory. unit-tests makefile adds individual + ais_testsuite files because there is the generated file there so + just adding the directory wasn't working. + M Makefile.am + M unit-tests/marshT.cc + M unit-tests/cache-testsuite/cleanup.sh.in + M unit-tests/cache-testsuite/Makefile.am + M unit-tests/Makefile.am + +2008-02-28 Patrick West + + Modified tests and configuration to get unit-tests to run during + distcheck. + M unit-tests/AISMergeTest.cc + M unit-tests/DODSFilterTest.cc + D unit-tests/ais_testsuite/ais_database.xml + A unit-tests/ais_testsuite/ais_database.xml.in + D unit-tests/cache-testsuite/cleanup.sh + A unit-tests/cache-testsuite/cleanup.sh.in + M unit-tests/cache-testsuite/Makefile.am + M unit-tests/DASTest.cc + M unit-tests/AISResourcesTest.cc + M unit-tests/DDSTest.cc + A unit-tests/test_config.h.in + M unit-tests/HTTPConnectTest.cc + M unit-tests/DDXParserTest.cc + M unit-tests/cgiUtilTest.cc + M unit-tests/AISDatabaseParserTest.cc + M unit-tests/CEFunctionsTest.cc + M unit-tests/GridGeoConstraintTest.cc + M unit-tests/Makefile.am + M unit-tests/RCReaderTest.cc + +2008-02-27 Patrick West + + Added check for cppunit in configure. If present, then the tests + will be built and run. if not then a message will appear saying that + can't run the tests without the library, but it's not an error. + M configure.ac + A conf/cppunit.m4 + M Makefile.am + M unit-tests/Makefile.am + +2008-02-27 James Gallagher + + Updated indentation using "indent -kr" + M gl/alloca_.h + M gl/mbchar.h + M gl/localcharset.c + M gl/strcasecmp.c + M gl/regex_internal.c + M gl/strnlen1.c + M gl/malloc.c + M gl/wchar_.h + M gl/localcharset.h + M gl/stdint_.h + M gl/strcase.h + M gl/regex.c + M gl/stdbool_.h + M gl/memchr.c + M gl/regex_internal.h + M gl/strnlen1.h + M gl/mbuiter.h + M gl/regex.h + M gl/wctype_.h + M gl/regexec.c + M gl/strncasecmp.c + M gl/gettext.h + M gl/alloca.c + M gl/mbchar.c + M gl/regcomp.c + +2008-02-26 Patrick West + + No longer need the MacOSX specific readme and install files. + M OSX_Resources/Info.plist + D INSTALL_MacOSX.rtf + D README_MacOSX.rtf + +2008-02-23 James Gallagher + + Auto reset of Subversion properties + +2008-02-22 James Gallagher + + Updated + +2008-02-22 James Gallagher + + Fixed an error that crept into xdr-datatypes-static from the xp + port. I made the change in a new copy of the xdr-datatypes.h header + added to the VCPP directory. Also, I removed some old code from the + XDRUtils.cc file. + M xdr-datatypes-static.h + AM VCPP/xdr-datatypes.h + M XDRUtils.cc + +2008-02-21 Patrick West + + Added remove of temporary directories when pkg build is done. + M Makefile.am + +2008-02-21 James Gallagher + + Modified Makefile to install xdr-datatypes.h from VCPP + +2008-02-21 James Gallagher + + Removed a patch + M XDRStreamMarshaller.cc + +2008-02-21 James Gallagher + + Removed a patch + M XDRFileMarshaller.cc + +2008-02-21 James Gallagher + + Backed out some changes for my version of XP. + M XDRFileUnMarshaller.cc + +2008-02-21 James Gallagher + + Fixes for the win32 build from Anna Nokolov - I turned her files + into a patch but there were some rejects. + M xdr-datatypes-static.h + M XDRFileUnMarshaller.cc + M gl/stdint_.h + M XDRStreamMarshaller.cc + +2008-02-21 James Gallagher + + Added patch from Anna Nokolov. + M XDRFileMarshaller.cc + +2008-02-21 James Gallagher + + Applied Win32 patches from Anna Nokolov. + M cgi_util.cc + M VCPP/Makefile + M GNU/GNURegex.cc + M parser-util.cc + M util.h + M das.lex + M gl/localcharset.c + M gl/stdint_.h + M gl/regex_internal.h + M util.cc + M XDRFileMarshaller.cc + +2008-02-18 Patrick West + + functional expressions were not being evaluated when building the + ddx. Updated this and removed newline from error message if problem + with functional expressions. + M DODSFilter.cc + +2008-02-14 Patrick West + + Fixed sed issue with discovering version inormation in libdap.m4. + This one should be used by anyone who wants to use libdap. + M conf/libdap.m4 + +2008-02-13 James Gallagher + + Added unistd.h include. + M unit-tests/AISResourcesTest.cc + M unit-tests/ByteTest.cc + M unit-tests/testFile.cc + +2008-02-13 James Gallagher + + Added config.h header include to AttrTableTest.cc + +2008-02-13 James Gallagher + + Added include of unistd.h to AttrTableTest.cc for RHEL3 + +2008-02-05 James Gallagher + + Removed two tests that fail on machines where the tests are run as + root. + M HTTPCacheTest.cc + +2008-02-05 James Gallagher + + Auto reset of Subversion properties + +2008-02-04 Patrick West + + Added method to not only return the FILE pointer but also the cached + file name when getting the cached response. Also, when parsing + headers, taking into account a blank line and a malformed header. + M HTTPCache.cc + M HTTPCache.h + M HTTPConnect.cc + +2008-02-04 Patrick West + + Includes added for unlink function call + M unit-tests/ddsT.cc + +2008-02-02 Patrick West + + Added namespace libdap + M Error.lex + M RValue.cc + M DDXParser.h + M DODSFilter.cc + M HTTPResponse.h + M Int16.cc + M Int32.h + M Structure.h + M AISResources.cc + M Sequence.h + M Str.h + M ResponseTooBigErr.cc + M SignalHandler.cc + M ResponseTooBigErr.h + M SignalHandler.h + M XDRUtils.h + M escaping.h + M Connect.h + M DDXParser.cc + M cgi_util.cc + M dods-datatypes-static.h + M ce_functions.h + M gse.lex + M DDXExceptions.h + M Resource.h + M Error.cc + M HTTPCacheDisconnectedMode.h + M UnMarshaller.h + M Response.h + M Int16.h + M SignalHandlerRegisteredErr.h + M ce_parser.h + M ConstraintEvaluator.cc + M DAS.h + M DDS.cc + M XDRFileMarshaller.h + M tests/TestInt32.h + M tests/TestStructure.h + M tests/TestInt16.h + M tests/TestUrl.h + M tests/TestSequence.h + M tests/TestStr.h + M tests/TestFloat64.h + M tests/das-test.cc + M tests/TestGrid.h + M tests/TestByte.h + M tests/TestTypeFactory.h + M tests/TestFloat32.h + M tests/TestArray.h + M tests/TestUInt32.h + M tests/dds-test.cc + M tests/TestUInt16.h + M tests/TestCommon.cc + M Array.cc + M BaseType.h + M HTTPCacheInterruptHandler.h + M EventHandler.h + M Float64.h + M AISConnect.cc + M UInt16.cc + M InternalErr.h + M EncodingType.h + M gse_parser.h + M Marshaller.h + M AlarmHandler.h + M Byte.cc + M GNU/GNURegex.cc + M GNU/GNURegex.h + M Byte.h + M DataDDS.h + M BaseType.cc + M DDS.h + M XDRUtils.cc + M ArrayGeoConstraint.h + M GeoConstraint.h + M HTTPCacheResponse.h + M RCReader.cc + M GridGeoConstraint.h + M Constructor.h + M util_mit.h + M Error.h + M Int32.cc + M DODSFilter.h + M DapObj.h + M gse.y + M HTTPCache.cc + M DataDDS.cc + M HTTPCache.h + M parser-util.cc + M DapIndent.h + M ce_expr.y + M AISResources.h + M ObjectType.h + M Float32.cc + M Error.y + M dds.lex + M PipeResponse.h + M util.h + M AISExceptions.h + M AISDatabaseParser.h + M AttrTable.cc + M InternalErr.cc + M Grid.h + M HTTPConnect.cc + M AISMerge.h + M RValue.h + M Structure.cc + M cgi_util.h + M Url.cc + M Vector.cc + M getdap.cc + M BaseTypeFactory.h + M Str.cc + M Sequence.cc + M das.lex + M XDRFileUnMarshaller.cc + M Float32.h + M XDRFileUnMarshaller.h + M ce_functions.cc + M expr.h + M AttrTable.h + M GridGeoConstraint.cc + M Constructor.cc + M UInt32.h + M XDRStreamMarshaller.h + M das.y + M AISMerge.cc + M parser.h + M BaseTypeFactory.cc + M Url.h + M Vector.h + M DAS.cc + M ConstraintEvaluator.h + M GSEClause.cc + M GSEClause.h + M util.cc + M Clause.h + M ArrayGeoConstraint.cc + M Float64.cc + M Operators.h + M UInt32.cc + M XDRStreamMarshaller.cc + M Grid.cc + M util_mit.cc + M Connect.cc + M escaping.cc + M unit-tests/AISMergeTest.cc + M unit-tests/sequenceT.cc + M unit-tests/DODSFilterTest.cc + M unit-tests/HTTPCacheTest.cc + M unit-tests/DASTest.cc + M unit-tests/AISResourcesTest.cc + M unit-tests/DDSTest.cc + M unit-tests/SignalHandlerTest.cc + M unit-tests/ArrayGeoConstraintTest.cc + M unit-tests/ArrayTest.cc + M unit-tests/AttrTableTest.cc + M unit-tests/HTTPConnectTest.cc + M unit-tests/DDXParserTest.cc + M unit-tests/ddsT.cc + M unit-tests/cgiUtilTest.cc + M unit-tests/ByteTest.cc + M unit-tests/attrTableT.cc + M unit-tests/SequenceTest.cc + M unit-tests/RegexTest.cc + M unit-tests/generalUtilTest.cc + M unit-tests/AISDatabaseParserTest.cc + M unit-tests/GridGeoConstraintTest.cc + M unit-tests/RCReaderTest.cc + M unit-tests/parserUtilTest.cc + M HTTPConnect.h + M dds.y + M StdinResponse.h + M DapIndent.cc + M XDRFileMarshaller.cc + M Clause.cc + M AISDatabaseParser.cc + M GeoConstraint.cc + M Array.h + M AISConnect.h + M UInt16.h + M ce_expr.lex + M RCReader.h + +2008-02-01 James Gallagher + + Added some file to svn:ignore + + M gl + +2008-02-01 James Gallagher + + Added conditional set of the gcc -Wall, et c., flags. + M Makefile.am + +2008-01-31 Nathan Potter + + libdap: Changed Error handling in ce_functions.cc so that the errors + thrown appropriatley referenced an error code other than unknown + +2008-01-31 James Gallagher + + Added conditional set of the gcc -Wall, et c., flags. + +2008-01-30 James Gallagher + + Include for all builds, not just win32. This is a fix + for AIX and its native compiler. + +2008-01-29 James Gallagher + + Update to the package target for OSX. + M OSX_Resources/InstallationCheck + M OSX_Resources/update_mac_package_contents.pl + M OSX_Resources/InstallationCheck.strings + M Makefile.am + +2008-01-28 James Gallagher + + It seems that universal binaries are not yet working - I commented + out that part of the Makefile. + M Makefile.am + +2008-01-28 James Gallagher + + Hacked the Makefile.am so that the 'pkg' target makes a universal + binary - maybe. Fixed up the OSX resources' text. + M OSX_Resources/Info.plist + M OSX_Resources/Description.plist + M Makefile.am + +2008-01-22 James Gallagher + + Added support to filter out ^M that's introduced by DOS editors. + M update_mac_package_contents.pl + +2008-01-16 James Gallagher + + Updated comments + M Makefile.am + +2008-01-16 James Gallagher + + Removed major and minor version. + M OSX_Resources/Info.plist + +2008-01-14 James Gallagher + + Applied pkgconfig patch from Patrice. + M libdap.spec + M Makefile.am + M libdapclient.pc.in + M libdap.pc.in + M unit-tests/testFile.cc + M unit-tests/Makefile.am + M libdapserver.pc.in + A dap-config-pkgconfig + +2008-01-12 James Gallagher + + Auto reset of Subversion properties + +2008-01-11 James Gallagher + + Added. These help automate the build process. + AM OSX_Resources/macify_license_file.pl + AM OSX_Resources/update_mac_package_contents.pl + +2008-01-11 James Gallagher + + Updates to the OS/X build so that it is more automatic. No more + pmproj file, either. I added tags in the README so that the + perl script that updates the Mac version of that file will look + decent. + M OSX_Resources/InstallationCheck + M OSX_Resources/License.txt + M OSX_Resources/Info.plist + M OSX_Resources/InstallationCheck.strings + M OSX_Resources/ReadMe.txt + M OSX_Resources/Welcome.html + M README + M Makefile.am + D mac_osx + D libdap.pmproj + +2008-01-10 Patrick West + + Certain size vector of bytes was failing when writing to the xdr + stream. Changed the size of the buffer to add enough to cover the + word boundary. Updated unit test to test for this. + M Vector.cc + M XDRStreamMarshaller.cc + M unit-tests/marshT.cc + M unit-tests/arrayT.cc + +2008-01-08 James Gallagher + + Removed leading spaces of the lines so that the file can be used + easily on the Mac where lines will wrap in the Installer. + +2008-01-08 James Gallagher + + Fixed up these two tests to match the changes in the ais database + they used. + M unit-tests/AISMergeTest.cc + M unit-tests/AISResourcesTest.cc + +2008-01-07 James Gallagher + + make check now runs the unit tests (ticket 1012) + M Makefile.am + +2008-01-07 James Gallagher + + Fixed the AIS database parser tests so they now use Hyrax and not + some kludge. + M unit-tests/ais_testsuite/ais_database.xml + M unit-tests/AISDatabaseParserTest.cc + +2007-12-01 James Gallagher + + Auto reset of Subversion properties + +2007-11-30 Patrick West + + MacOSX resources updated. Added a README and INSTALL file to be put + alongside the .pkg in the .dmg. + M OSX_Resources/Info.plist + D README_MaxOSX.rtf + M Makefile.am + A README_MacOSX.rtf + +2007-11-30 James Gallagher + + Auto reset of Subversion properties + +2007-11-29 Patrick West + + the .pc files needed to be dealt with during the pkg build so that + the prefix was set correctly. + M Makefile.am + +2007-11-29 Patrick West + + README and INSTALL files for a MacOSX installation. To be put into + the .dmg along with the .pkg. + A README_MaxOSX.rtf + A INSTALL_MacOSX.rtf + +2007-11-28 James Gallagher + + Updated for release 3.7.10 + + M configure.ac + M ChangeLog + M INSTALL + M libdap.spec + M README + M NEWS + +;; Local Variables: +;; coding: utf-8 +;; End: +2007-11-28 James Gallagher + + Fixed warnings about potentially uninitialized uses of ENTRY in + HTTPCache.cc + +2007-11-28 Patrick West + + Added private default constructor, copy constructor, and operator= + to the classes, all through InternalErr. Added copyright + information as well. + + M XDRUtils.h + M UnMarshaller.h + M XDRFileMarshaller.h + M Marshaller.h + M XDRUtils.cc + M XDRFileUnMarshaller.cc + M XDRFileUnMarshaller.h + M XDRStreamMarshaller.h + M XDRStreamMarshaller.cc + M XDRFileMarshaller.cc + +2007-11-26 Patrick West + + In Vector.cc, checking if _var is null before attempting delete. + Didn't appear to be an issue, but seemed to be a needed test. + + In XDRStreamMarshaller, not allocating such a large buffer. + Allocating only what is needed in put_str and both put_vector + calls. The other methods are using the static buffer so we don't + have to allocate each time. Prettied up the code, added more + comments, and corrected some exception messages. + + M Vector.cc + M XDRStreamMarshaller.cc + +2007-11-21 James Gallagher + + libdapclient and libdapserver library versions bumped up. + + M configure.ac + M depend.sh + M libdap.spec + M NEWS + +2007-11-14 James Gallagher + + Fixed the LIBDAP CHECK macro + + M conf/libdap.m4 + +2007-11-13 James Gallagher + + Updated for version 3.7.9 + + M configure.ac + M ChangeLog + M INSTALL + M libdap.spec + M README + M NEWS + +2007-11-13 James Gallagher + + Fixeed ticket #995. The problem was in intern_data_for_leaf. + This problem was fixed by changing the way intern_data_for_leaf() + worked, slightly. It was always popping the stack of `d_values` + object pointers regardless of the return value of read_row(). + However, if no values are read then the call to + intern_data_for_parent_part_two() would never be called and the + d_values field of the child sequence wouldn't be pushed onto the stack. + Hence the pop should not happen - because there's no child there to + pop. I fixed this with a slight change in the code. The method now + skips the while loop and subsequent pop operation if the first call + to read_row() returns false. The while loop won't be run anyway, + so the actual change is minor and eliminates an unnecessary test + in the case where read_row does return false initially. + + All of the tests that are expected to pass now do. + +2007-11-12 James Gallagher + + Grid.cc: Changed the way the declarataion is printed by print_val() + so that intern_data can use this too and work with the expr-test + test driver. + Sequence.cc: transfer_data_part_two() now uses intern_data() when + it should in place of read(). Also, print_one_row() is now more + flexible in the way it prints rows. Before it expected that the + first row would alway be printed (because it assume that the row + had been read by deserialize()). Now it checks to see if the row + should be printed because it knows that intern_data() might be + run and that method is working with local data - the first row + might not satisfy the CE. + Sequence.h: Formatting + Structure.cc: Switched from transfer_data to intern_data. + +2007-11-12 James Gallagher + + Fixed this test. The error message changed and the test failed + for that reason, not that error went undetected. + + M dds-testsuite/dds-test.0/test.17.exp + +2007-11-12 James Gallagher + + Copied and munged these so that they call the expr-test_start + procedure and test intern_data() in libdap. + + M test.9.exp + M test.ya.exp + M test.yb.exp + M test.yc.exp + M test.yd.exp + M test.ye.exp + M test.yf.exp + M test.yg.exp + M test.a.exp + M test.b.exp + M test.c.exp + M test.d.exp + M test.zz0.exp + M test.e.exp + M test.zz1.exp + M test.zz2.exp + M test.f.exp + M test.zz3.exp + M test.g.exp + M test.zz4.exp + M test.61a.exp + M test.h.exp + M test.zz5.exp + M test.61b.exp + M test.i.exp + M test.61c.exp + M test.j.exp + M test.61d.exp + M test.k.exp + M test.l.exp + M test.m.exp + M test.n.exp + M test.o.exp + M test.p.exp + M test.q.exp + M test.r.exp + M test.s.exp + M test.t.exp + M test.u.exp + M test.v.exp + M test.w.exp + M test.wa.exp + M test.x.exp + M test.wb.exp + M test.y.exp + M test.wc.exp + M test.xa.exp + M test.xb.exp + M test.xc.exp + M test.xd.exp + M test.xe.exp + M test.z0.exp + M test.z1.exp + M test.z2.exp + M test.z3.exp + M test.z4.exp + M test.z5.exp + M test.z6.exp + M test.1.exp + M test.z7.exp + M test.2.exp + M test.5.exp + M test.6.exp + +2007-11-12 James Gallagher + + Updated test code so that the new intern_data() method is tested. + + M tests/TestInt32.cc + M tests/TestInt32.h + M tests/TestInt16.cc + M tests/TestStructure.h + M tests/TestInt16.h + M tests/TestUrl.h + M tests/TestStr.h + M tests/TestSequence.h + M tests/TestFloat32.cc + M tests/TestArray.cc + M tests/TestFloat64.cc + M tests/expr-testsuite/config/unix.exp + M tests/TestFloat64.h + M tests/TestUInt32.cc + M tests/TestGrid.cc + M tests/TestUInt16.cc + M tests/TestGrid.h + M tests/TestCommon.h + M tests/expr-test.cc + M tests/TestStructure.cc + M tests/TestUrl.cc + M tests/TestByte.cc + M tests/TestByte.h + M tests/TestSequence.cc + M tests/TestStr.cc + M tests/README + M tests/TestFloat32.h + M tests/TestArray.h + M tests/TestUInt32.h + M tests/TestUInt16.h + M tests/TestCommon.cc + +2007-11-08 James Gallagher + + Added the intern_data() method as a replacement and fix for/to + transfer_data(). This method should correctly handle the types + presented by HDF5 like Array of Structure + + M Structure.h + M Sequence.h + M BaseType.h + M BaseType.cc + M Grid.h + M Structure.cc + M Vector.cc + M Sequence.cc + M Vector.h + M Grid.cc + M unit-tests/HTTPCacheTest.cc + M unit-tests/SequenceTest.cc + +2007-11-08 Patrick West + + All characters in the pathname need to match the regular + expression. So need to compare the result of the match call in + pathname_ok to the length of the path being matched. If not all + characters match, then return false. + + M util.cc + +2007-10-31 James Gallagher + + Added pathname_ok() prototype to util.h and added malloc + check to XDRStreamMarshaller.cc + +2007-10-30 James Gallagher + + Fixed a parse error in the pathname sanitizer function. + + M util.cc + +2007-10-24 James Gallagher + + Formatting + +2007-09-27 James Gallagher + + Auto reset of Subversion properties + +2007-09-26 Patrick West + + marshaller test + + A unit-tests/marshT.cc + +2007-09-24 James Gallagher + + Applied patch from Patrice to package/ship generated + files with source dists. + + M Makefile.am + +2007-09-21 James Gallagher + + Fixed various issues. + +2007-08-28 James Gallagher + + Modified because issues show up in the nightly builds. + + M XDRUtils.cc + M Vector.cc + +2007-08-28 James Gallagher + + unit-test/Makefile.am: Removed comments that shadowed various + unit tests since I think that was checked in by mistake. + +2007-08-28 James Gallagher + + Auto reset of Subversion properties + +2007-08-27 Patrick West + + Added methods to be able to send dap objects to iostream in + addition to the methods to send to FILE pointers. Created + Marshaller and UnMarshaller classes to pass to serialize and + deserialize methods (respectively) to separate out that code from + BaseType classes. Created XDRFileMarshaller to serialize into XDR + using FILE pointer. Created XDRStreamMarshaller to serialize into + XDR using ostream (XDR memory buffer). Created XDRFileUnMarshaller + to read from XDR using FILE pointer. Did NOT create + XDRStreamUnmarshaller as it didn't seem to be needed. Separated + out anything to do with XDR into separate header files and utility + class XDRUtils. + + M DODSFilter.cc + M Int16.cc + M Int32.h + M Structure.h + M Sequence.h + M Str.h + M configure.ac + A XDRUtils.h + A xdr-datatypes-static.h + M cgi_util.cc + A xdr-datatypes-config.h.in + M dods-datatypes-static.h + M Error.cc + A UnMarshaller.h + M Int16.h + M DAS.h + M DDS.cc + A XDRFileMarshaller.h + M tests/expr-test.cc + M Array.cc + M BaseType.h + M Float64.h + M UInt16.cc + M AlarmHandler.h + A Marshaller.h + M Byte.cc + M Byte.h + M DDS.h + M BaseType.cc + A XDRUtils.cc + M Makefile.am + M Constructor.h + M Error.h + M Int32.cc + M DODSFilter.h + M DataDDS.cc + M Float32.cc + M util.h + M AttrTable.cc + M Grid.h + M Structure.cc + M cgi_util.h + M Vector.cc + M Str.cc + M Sequence.cc + A XDRFileUnMarshaller.cc + M Float32.h + A XDRFileUnMarshaller.h + M AttrTable.h + M Constructor.cc + M UInt32.h + A XDRStreamMarshaller.h + M dods-datatypes-config.h.in + M Vector.h + M DAS.cc + M util.cc + M Float64.cc + M UInt32.cc + A XDRStreamMarshaller.cc + M Grid.cc + M Connect.cc + M unit-tests/Makefile.am + A XDRFileMarshaller.cc + M Array.h + M UInt16.h + +2007-08-24 James Gallagher + + Makefile.am: Updated fortify targets + +2007-08-22 Patrick West + + Functionality of Passive classes was passed to their parent + classes. Added set_value(vector) methods to Vector. Added + default read method to Structure that iterates through vars and + calls read, just as PassiveStructure did. Removed Passive classes + from Makefile. + + D PassiveUrl.cc + D PassiveInt16.h + M Structure.h + D PassiveStr.cc + D PassiveStr.h + D PassiveUInt16.h + D PassiveInt16.cc + D PassiveByte.cc + D PassiveFloat32.h + D PassiveStructure.cc + D PassiveStructure.h + D PassiveUInt32.cc + D PassiveFloat32.cc + D PassiveArray.cc + M Makefile.am + D PassiveInt32.cc + M Structure.cc + M Vector.cc + D PassiveFloat64.cc + D PassiveArray.h + D PassiveInt32.h + D PassiveUrl.h + D PassiveByte.h + M Vector.h + D PassiveFloat64.h + D PassiveUInt32.h + D PassiveUInt16.cc + +2007-08-22 James Gallagher + + Str.h: Changed the size of max_str_len from 32767 to 65535 + (DODS_USHORT_INT-1) because 32k was not big enough for the + AURA HDF5 data files. See ticket 962 and 956. + +2007-08-21 James Gallagher + + Response.h: Made the new 'status' parameter have a default + value of '0' so that specializations do not need modification + if they know nothing about the new param. This change was + made for PipeResponse.h - arguably something that should be + removed, but it's there as long as we support the CGI server. + StdinResponse.h: Removed the second param to teh ctor since + it is no longer needed. + +2007-08-21 James Gallagher + + Makefile.am: Removed -Werror since on OS/X and RHEL3 + the grammar files generate warnings about unused + variables. + +2007-08-20 James Gallagher + + Removed warnings about strict aliasing rules by adding + Vector::void *value() and replacing calls to buf2_val() + with it. See ticket #941. + +2007-08-20 James Gallagher + + Updates to a number of the doxygen comments. + +2007-08-20 James Gallagher + + Fixed a problem in util.cc where the sense of size_ok() + was backwards! Also rewrote parts of ce_functions.cc so + that value() was used in place of buf2val() which is far + better from a type-safety standpoint and removes a number + of warnings about strict aliasing of types. Also fixed + error in Error.cc where 'undefined_error' was used where + zero (0) should have been used. Modified RValue"build_btp_args() + so that the size of the arg list is added to the malformed + args message. + +2007-08-18 James Gallagher + + Patch from Patrice Dumas that fixes the distclean target and + repairs the spec file now that the generated grammar files are no + longer included checked into subversion. + + M libdap.spec + M Makefile.am + +2007-08-17 James Gallagher + + Added a target so that the generated grammar files are copied to a + directory so that they don't _have_ to be built using bison/flex. + + M INSTALL + M Makefile.am + +2007-08-17 James Gallagher + + I removed the generated grammar files from svn (but put copies in + the new 'grammarfile' subdirectory). Now, svn checkouts need bison + and flex to build. + + D lex.gse_.cc + D lex.dds.cc + D expr.tab.h + D dds.tab.h + D ce_expr.tab.cc + M GNU/GNURegex.cc + D lex.Error.cc + M Makefile.am + D ce_expr.tab.h + D Error.tab.h + A grammarfiles + A grammarfiles/ce_expr.tab.h + A grammarfiles/lex.das.cc + A grammarfiles/Error.tab.cc + A grammarfiles/Error.tab.h + A grammarfiles/lex.dds.cc + A grammarfiles/lex.gse_.cc + A grammarfiles/lex.ce_expr.cc + A grammarfiles/das.tab.cc + A grammarfiles/das.tab.h + A grammarfiles/dds.tab.cc + A grammarfiles/lex.Error.cc + A grammarfiles/dds.tab.h + A grammarfiles/gse.tab.cc + A grammarfiles/gse.tab.h + A grammarfiles/expr.tab.h + A grammarfiles/ce_expr.tab.cc + D lex.das.cc + D lex.ce_expr.cc + D das.tab.h + D das.tab.cc + D dds.tab.cc + D Error.tab.cc + D gse.tab.cc + D gse.tab.h + +2007-08-15 James Gallagher + + Fixes + +2007-08-15 James Gallagher + + Fixes + + M lex.gse_.cc + M lex.dds.cc + M dds.tab.h + M lex.Error.cc + M Makefile.am + M util.h + M lex.das.cc + M lex.ce_expr.cc + M util.cc + M dds.tab.cc + M GeoConstraint.cc + +2007-08-15 James Gallagher + + Patch to add HTTP response status to the Response object and to fix the + spelling of getdap (it was geturl in places). From Darren Hardy. + + M HTTPResponse.h + M lex.gse_.cc + M lex.dds.cc + M Response.h + M dds.tab.h + M lex.Error.cc + M HTTPCacheResponse.h + M HTTPConnect.cc + M lex.das.cc + M getdap.cc + M lex.ce_expr.cc + M AISMerge.cc + M dds.tab.cc + M StdinResponse.h + +2007-08-13 James Gallagher + + Modified as part of refactoring. + + M RValue.cc + M cgi_util.cc + M Error.cc + M ConstraintEvaluator.cc + M DDS.cc + M Array.cc + M Makefile.am + M Error.h + M HTTPCache.cc + M DataDDS.cc + M dds.lex + M DAS.cc + M util.cc + M Connect.cc + M dods-limits.h + M dds.y + M Array.h + M ce_expr.lex + +2007-08-09 James Gallagher + + Applied the hyrex_redirect patch from NASA. This code now detects and + records the value of a Location: HTTP header. + + M HTTPConnect.cc + +2007-08-07 Patrick West + + OPeNDAPDir and OPeNDAPFile not needed in libdap, specific to cedar + project. M Makefile.am + +2007-08-07 Patrick West + + Not needed in libdap, specific to cedar project. + + A http://scm.opendap.org:8090/svn/trunk/cedar-handler/OPeNDAPFile.h + D http://scm.opendap.org:8090/svn/trunk/libdap/OPeNDAPFile.h + +2007-08-07 Patrick West + + Not needed in libdap, specific to cedar project. + + A http://scm.opendap.org:8090/svn/trunk/cedar-handler/OPeNDAPFile.cc + D http://scm.opendap.org:8090/svn/trunk/libdap/OPeNDAPFile.cc + +2007-08-07 Patrick West + + Not needed in libdap, specific to cedar project. + + A http://scm.opendap.org:8090/svn/trunk/cedar-handler/OPeNDAPDir.cc + D http://scm.opendap.org:8090/svn/trunk/libdap/OPeNDAPDir.cc + +2007-08-07 Patrick West + + Not needed in libdap, specific to cedar project. + A http://scm.opendap.org:8090/svn/trunk/cedar-handler/OPeNDAPDir.h + D http://scm.opendap.org:8090/svn/trunk/libdap/OPeNDAPDir.h + +2007-06-27 James Gallagher + + unit-tests/Makefile.am: Added -I$(topsrcdir) to the CPPFLAGS for + distcheck. + +2007-06-27 James Gallagher + + tests/Makefile.am: Added -I$(topsrcdir) to the CPPFLAGS for + distcheck. + +2007-06-27 James Gallagher + + Updated for version 3.7.8 + + M INSTALL + M README + M NEWS + +2007-06-26 James Gallagher + + Updates for version 3.7.8 - still need to do README and INSTALL. + + M DODSFilter.cc + M configure.ac + M ChangeLog + M libdap.spec + M NEWS + +2007-06-26 James Gallagher + + Replaced unidata support email with the opendap-tech list address. + +2007-06-26 James Gallagher + + Auto reset of Subversion properties + +2007-06-25 James Gallagher + + Added. + + AM gl/config.charset + +2007-06-25 James Gallagher + + Added files from the gnulib update. + + A gl/m4/wint_t.m4 + A gl/m4/wchar.m4 + A gl/m4/localcharset.m4 + A gl/m4/ulonglong.m4 + A gl/m4/stdint.m4 + A gl/m4/gnulib-common.m4 + A gl/m4/wctype.m4 + A gl/m4/longlong.m4 + A gl/m4/include_next.m4 + A gl/m4/glibc21.m4 + AM gl/localcharset.c + A gl/ref-del.sed + AM gl/localcharset.h + A gl/ref-del.sin + A gl/ref-add.sed + AM gl/wchar_.h + A gl/ref-add.sin + AM gl/stdint_.h + AM gl/wctype_.h + +2007-06-22 James Gallagher + + Updated gnulib and fixed a memory problem in the GNURegex class + (which should really be renamed to DAPRegex, but that would break + other code). The problem was that to use the gnulib regex code the + config.h header has to be included and the gl subdir must be + searched. I moved the declaration of the regex_t field of Regex + into the implementation file to avoid having config.h in a header + that is installed in /include. This fixes ticket #953 + which first showed up in libnc-dap. + + M tests/Makefile.am + M GNU/GNURegex.cc + M GNU/GNURegex.h + M Makefile.am + M regex_test.cc + M gl/alloca_.h + M gl/m4/regex.m4 + M gl/m4/gnulib-comp.m4 + M gl/m4/ssize_t.m4 + M gl/m4/gnulib-cache.m4 + M gl/m4/onceonly_2_57.m4 + M gl/m4/codeset.m4 + M gl/m4/alloca.m4 + M gl/m4/extensions.m4 + M gl/regex_internal.c + M gl/malloc.c + M gl/stdbool_.h + M gl/regex.c + M gl/regex_internal.h + M gl/regex.h + M gl/regexec.c + M gl/Makefile.am + M gl/gettext.h + M gl/alloca.c + M gl/regcomp.c + M util.cc + M Operators.h + M unit-tests/RegexTest.cc + M unit-tests/Makefile.am + +2007-06-21 James Gallagher + + Reverted to the old version of get_attr() for release. + + M AttrTable.cc + +2007-06-21 James Gallagher + + escaping.cc: one last call to size replaced. + +2007-06-21 James Gallagher + + escaping.cc: Replaced calls to string::size() with ones to + string::length(). They return the same information, but it's + cleaner to use the same method everywhere + +2007-06-19 James Gallagher + + Fixed a problem in save_raw_http_header() where a header from an + old server (terminated by a newline but no return) caused a read + violation when the header was empty. + + M HTTPConnect.cc + +2007-06-08 James Gallagher + + Spelling in comments and strings + + M AlarmHandler.h + M GeoConstraint.h + M expr.h + M RCReader.h + +2007-06-04 James Gallagher + + Updated comments. + + M lex.gse_.cc + M AISResources.cc + M configure.ac + M cgi_util.cc + M lex.dds.cc + M ChangeLog + M ConstraintEvaluator.cc + M Array.cc + M ce_expr.tab.cc + M Byte.cc + M lex.Error.cc + M ce_expr.tab.h + M Error.tab.h + M AttrTable.cc + M lex.das.cc + M lex.ce_expr.cc + M das.tab.h + M ce_functions.cc + M Constructor.cc + M das.tab.cc + M ArrayGeoConstraint.cc + M Connect.cc + M Error.tab.cc + M Clause.cc + M doxy.conf + M gse.tab.cc + M gse.tab.h + +2007-05-25 James Gallagher + + Auto reset of Subversion properties + +2007-05-24 Patrick West + + Standard location of PackageMaker command line app. + + M Makefile.am + +2007-05-24 Patrick West + + Ability to build .pkg file from the Makefile instead of running + PackageMaker GUI. + + A OSX_Resources/Info.plist + A OSX_Resources/Description.plist + M Makefile.am + +2007-05-18 James Gallagher + + Updated pkg.m4 macros + + M pkg.m4 + +2007-05-17 James Gallagher + + Fixed typos in grammar files. + + M Error.lex + M gse.lex + M gse.y + M ce_expr.y + M Error.y + M dds.lex + M das.lex + M das.y + M ce_expr.lex + +2007-05-17 James Gallagher + + Fixed typos in libdap headers. + + M DDXParser.h + M Sequence.h + M SignalHandler.h + M debug.h + M escaping.h + M Connect.h + M ce_functions.h + M SignalHandlerRegisteredErr.h + M gse_parser.h + M DDS.h + M ArrayGeoConstraint.h + M GeoConstraint.h + M HTTPCacheResponse.h + M Error.h + M AISResources.h + M PipeResponse.h + M util.h + M Grid.h + M AttrTable.h + M PassiveUrl.h + M Url.h + M Clause.h + M Operators.h + M HTTPConnect.h + M StdinResponse.h + M Array.h + M UInt16.h + +2007-05-17 James Gallagher + + Auto reset of Subversion properties + +2007-05-16 James Gallagher + + Added pkg.m4 to conf so build from svn will work on machines w/o + pkg-config. Updated comments. + + A conf/pkg.m4 + M Makefile.am + M Constructor.h + M unit-tests/Makefile.am + +2007-05-14 James Gallagher + + Removed the html docs from svn; RPM now builds a 'doc' package. + + D docs/html.tar.gz + M libdap.spec + +2007-05-14 James Gallagher + + Resolved conflicted state of AISDatabaseParser.h + +2007-05-14 James Gallagher + + Auto reset of Subversion properties + +2007-05-13 James Gallagher + + Patrice Dumas' changes to dods-datatypes.h - Now the header uses + the C99 types unless the stdint.h header is not present. + + D dods-datatypes.h.in + M configure.ac + A dods-datatypes-static.h + M conf/acinclude.m4 + M Makefile.am + A dods-datatypes-config.h.in + +2007-05-13 James Gallagher + + Auto reset of Subversion properties + +2007-05-12 James Gallagher + + Added Patrice Dumas' pkgconf patches + + M dods-datatypes.h.in + M configure.ac + AM conf/compile + M conf/libdap.m4 + A conf/check_zlib.m4 + M libdap.spec + M BaseType.cc + M Makefile.am + M Structure.cc + A libdapclient.pc.in + A libdap.pc.in + A libdapserver.pc.in + M dap-config.in + +2007-05-10 James Gallagher + + Comments & Strings + + M BaseType.h + M BaseType.cc + M Float32.cc + M AISDatabaseParser.h + M Float32.h + M AISDatabaseParser.cc + M AISConnect.h + +2007-05-02 James Gallagher + + Updates for 3.7.7 + + M configure.ac + M ChangeLog + M libdap.spec + M README + M NEWS + +2007-03-29 James Gallagher + + Added definition of trunc() for Solaris < 10. + +2007-03-29 James Gallagher + + Added definition of trunc() for Solaris < 10. + +2007-03-28 James Gallagher + + All files (.cc and .h) formatted using astyle. I found one + introduced error and one issue with the strings passed to the + Error exceptions. + +2007-03-27 James Gallagher + + Minor formatting changes. + + M Byte.cc + M unit-tests/HTTPConnectTest.cc + +2007-03-27 James Gallagher + + Wrapped the calls to ConstraitEvaluator::eval() in a compile-time + constant (EVAL) which defaults to zero (so the calls are not made + for non-sequence types). Also wrapped the server-side compression + code in a compile-time constant COMPRESSION_FOR_SERVER3 which + is also zero by default. Lastly, added DapObj constructor init + for BAS, DDS and BaseTyep constructors. + +2007-03-26 James Gallagher + + DDS.cc, unit-tests/DDSTest.cc: Fixed ticket # 708. The dataBLOB + element now gets an empty href attribute. + +2007-03-26 James Gallagher + + README: Added a note about thread-saftey. + +2007-03-26 James Gallagher + + Patch from Patrice Dumas: Fix the order of library builds! This + closes #880. + +2007-03-23 James Gallagher + + Fixed the help/version/args messages for the server-side functions + so that they are somewhat rational. There is a ticket to develop a + 'capabilities' response for Hyrax and that should replace these + messages. + + M ce_functions.cc + M GeoConstraint.cc + +2007-03-23 James Gallagher + + Updated test for changes to the 'wrong number of args" messages + from the first set of server-side functions. + + M tests/expr-testsuite/data.zz0 + M tests/expr-testsuite/data.z0 + +2007-03-12 James Gallagher + + Resolved signed/unsigned compares + + M Vector.cc + +2007-03-12 James Gallagher + + Removed some int/unsigned compares + + M tests/TestArray.cc + +2007-03-12 James Gallagher + + Updated notes and version information for 3.7.6. + + M configure.ac + M README.AIS + M ChangeLog + M INSTALL + M README.dodsrc + M libdap.spec + M README + M NEWS + +2007-03-12 James Gallagher + + Fixed some errors in the unit tests, one bug in the RCReader when + reading the VALIDATE_SSL keyword. Updated the parser stuff. + + M lex.dds.cc + M RCReader.cc + M lex.das.cc + M das.tab.h + M das.y + M das.tab.cc + M unit-tests/RegexTest.cc + M unit-tests/RCReaderTest.cc + +2007-03-11 James Gallagher + + Removed DODS_DEBUG + + M ce_functions.cc + +2007-03-09 James Gallagher + + Fixed ticket #857: The linear_scale function was crashing because + of a failure to test that a parent variable existed before getting + its type. I also fixed an unrelated problem where double quotes + around an attribute value broke the string --> double code and + added a new feature where files which omit the y intercept because + it's zero do not return an error but instead use 0.0 as a default. + Finally, the error message text was improved. + + M ce_functions.cc + +2007-02-16 James Gallagher + + Makefile.am: removed some errant tabs. + +2007-02-16 James Gallagher + + dap-config.in: fixed #816; --help was wrong. + +2007-02-16 James Gallagher + + I removed throw(Error) from the method declarations in the header + and impl. files. I removed the include of Error.h from the header. + I added the include using "" in the impl. This should fix the + ml-structs build error that started on 1/7/07 when Rob made some + changes for the win32 build. + + M GNU/GNURegex.cc + M GNU/GNURegex.h + +2007-02-09 James Gallagher + + Updated for version 3.7.5 + + M docs/html.tar.gz + M libdap.spec + +2007-02-07 James Gallagher + + main_page.doxygen: Updated the version number to 3.7.5. + +2007-02-07 James Gallagher + + ce_functions.cc, GeoConstraint.cc, HTTPConnect.cc: Formatting + lex.dds,cc, les.gse_.cc: Regenerated. + +2007-02-05 James Gallagher + + data.z2-7: updated the data values for two and three + dimensional arrays given the new code in GeoConstraint. + +2007-02-05 James Gallagher + + expr-test.cc, TestArray.cc: formatting changes + +2007-02-05 James Gallagher + + AISMergeTest.cc, HTTPConnectTest.cc: Fixed errors that have + cropped upover time, esp. as Server4 has become the default. + +2007-02-02 Patrick West + + Added information about PATH and LD_LIBRARY_PATH. + M INSTALL + +2007-01-30 James Gallagher + + ce_functions.cc: Added support for missing values to linear_scale. + Now, if missing_value is present (in the attributes or given as a + parameter) values which match will not be scaled. + +2007-01-29 James Gallagher + + ce_functions.cc: Added a new version of get + get_attribute_double_value() that takes a vector of strings + and returns the value of the first attribute found in that + vector. Also cleaned up the memory use in the linear_scale + function so that function no longer leaks the new template + variable. This was part of the fix for #660 (See also the + checkin for Clause.cc from today). + +2007-01-29 James Gallagher + + Clause.cc: Added a call to set_read_p(true) in Clause::value + so that handlers' read() methods will not try to 'read over' + the values inserted to the variables by a CE function. + +2007-01-29 James Gallagher + + GeoConstraint.cc: Minor formatting fix. + +2007-01-26 James Gallagher + + Updates to comments. + +2007-01-26 James Gallagher + + ce_functions.cc: Fixed ticket 659 where array data was not + read when a relational constraint was not used. The bug + crept in while making another fix (for the HDF4 handler). + The problem was that Grid::read() was used to read the maps + (bit not the Array) and as a side effect read_p was set for + the entire Grid, not just the maps. I reset the Array read_p + value; see ce_functions.cc + +2007-01-21 Rob Morris + + Synch win32 Makefile wrt recent libdap code changes + +2007-01-12 James Gallagher + + Auto reset of Subversion properties + +2007-01-11 Patrick West + + comment headers updated with correct information and Copyright. + M DapObj.h + M DapIndent.h + M DapIndent.cc + +2007-01-11 Patrick West + + Added dump method to the BaseType classes, DAS, DDS, DataDDS. To + do this, created a DapObj base class for all of these to inherit + from (directly or indirectly) and in the DapObj class is the + operator<< method. This will aid in debugging. Created an + indentation classes to help with dumping objects. + + M Int16.cc + M Int32.h + M Structure.h + M Sequence.h + M Str.h + M Int16.h + M DAS.h + M DDS.cc + M BaseType.h + M Array.cc + M Float64.h + M UInt16.cc + M Byte.cc + M Byte.h + M DataDDS.h + M BaseType.cc + M DDS.h + M Makefile.am + M Constructor.h + M Int32.cc + A DapObj.h + M DataDDS.cc + A DapIndent.h + M Float32.cc + M AttrTable.cc + M Grid.h + M Structure.cc + M Vector.cc + M Str.cc + M Sequence.cc + M Float32.h + M AttrTable.h + M Constructor.cc + M UInt32.h + M Vector.h + M DAS.cc + M Float64.cc + M UInt32.cc + M Grid.cc + A DapIndent.cc + M Array.h + M UInt16.h + +2007-01-08 James Gallagher + + Auto reset of Subversion properties + +2007-01-07 Rob Morris + + Last Makefile tweeks to automate pkging for win32 + +2007-01-07 Rob Morris + + Add doc's that accompany win32 libdap releases to the repository + +2007-01-07 James Gallagher + + Auto reset of Subversion properties + +2007-01-06 Rob Morris + + Prevent "error.h" from getting picked up during ml-structs build + under win32 (that's error.h with a little "e"). + +2007-01-06 Rob Morris + + Removed VC++-6.x-specific ifdefs. 6.x not supported any longer anyway + as some old template-related hacks are no longer in the code. + +2007-01-06 Rob Morris + + Add sample code for compiling/linking with libdap under win32 + +2007-01-03 James Gallagher + + Vector.h: Removed 'Vector::' prefix in a bunch on accessors; these + caused g++ 4.1.1 to gag. Fix from Rob Cermak. + +2007-01-02 Patrick West + + 3.7.4 release + M configure.ac + M ChangeLog + M libdap.spec + M NEWS + +2006-12-31 Rob Morris + + Synchronize select win32 Makefiles to build/pkg the same way + +2006-12-20 James Gallagher + + Added Structure::transfer_data() so that a handler can simulate the + actions of serialize() without actually sending the data. + M Structure.h + M Structure.cc + M Sequence.cc + +2006-12-20 James Gallagher + + DDXParser.cc: Fixed an error introduced by the Eclipse + code formatter. + +2006-12-19 James Gallagher + + DDXParser.cc, DODSFilter.cc: Formatting changes + +2006-12-13 James Gallagher + + Updated libdap.pmproj to/for version 3.7.3 + +2006-12-10 Rob Morris + + Change of pkging location simplifies user builds + +2006-12-06 Rob Morris + + Set LIBDAP_ROOT appropriately in the MS Windows case + +2006-12-06 Rob Morris + + Remove external .manifest as an issue by internalizing into binaries + +2006-12-05 James Gallagher + + getdap.cc: Fixed a bug where the constraint expression passed + in using -c was not used when getting either a DDS or a DDX + (but it was used when getting data). + +2006-12-05 James Gallagher + + Added comments to DDS and DataDDS about when it's OK + to delete their type factories (only when you're done + using the DDS or if you're about to switch to a new + factory). + +2006-12-05 Rob Morris + + Require *.exe.manifest to accompany .exe's if they are to run. I + guess I don't yet know why these are generated + +2006-12-05 Rob Morris + + Improve win32 exe linking + +2006-12-03 Rob Morris + + Just libdap for win32 makefile tweeks + +2006-12-02 Rob Morris + + New Makefile code to gen exports for dll's automatically + +2006-12-02 Rob Morris + + Module export def's no longer required. Constructed at build time. + +2006-11-25 Rob Morris + + Automate module export definition regeneration for dll's (hard) + +2006-11-24 James Gallagher + + Updated OSX ReadME.txt for 3.7.3 + +2006-11-24 James Gallagher + + Documentation and build file update for the 3.7.3 release. + +2006-11-24 James Gallagher + + AISMergeTest.cc: UPdated baseline to account for new fnoc1.das + file in data/nc. + +2006-11-22 James Gallagher + + Updated ChangeLog + +2006-11-21 James Gallagher + + Fixed unescattr() in escaping.cc. Now the code works when there + are several escaped characters or when the escapes are escaped. + That is, this version can be used by the libnc-dap library and the + result is code that passes all the nc_test tests for char and text + attributes. + +2006-11-12 Rob Morris + + Make win32 pkging dir locaton slightly more flexible + +2006-11-12 Rob Morris + + Update module export definitions for dll versions of the Core + +2006-11-11 James Gallagher + + Removed errant 3.7.3 sub directory. + +2006-11-07 James Gallagher + + HTTPConnectTest.cc: Turned off debugging + +2006-11-07 James Gallagher + + HTTPConnectTest.cc: Fixed up the tests for headers once again... + +2006-11-07 James Gallagher + + Auto reset of Subversion properties + +2006-11-06 James Gallagher + + configure.ac: I moved the DAP Protocol setting up where + it's likely to be updated and not glossed over. + +2006-11-06 James Gallagher + + getdap.cc: getdap now reports the protocol version in addition + to the server version. + +2006-11-06 James Gallagher + + Added tests for the new parameter VALIDATE_SSL + +2006-11-06 James Gallagher + + HTTPConnect.cc, RCReader: The .dodsrc file now supports a + VALIDATE_SSL option which controls libcurl's validation of SSL + hosts and certificates. If this is set (the default), certificates + must be signed by a recognized Certificate Authority and hosts + must be properly named. Clear (set to zero) to get the old + behavior from libcurl which does not perform this validation. + +2006-11-03 James Gallagher + + HTTPConnect.cc: Modified so that gzip and compress are also + accepted encodings. + +2006-11-02 Patrick West + + Removed DODSResponseObject from the Makefile after removing the + file from libdap. DAS and DDS no longer inherit from + DODSResponseObject. New classes representing DAS, DDS and DataDDS + were created in BES. M Makefile.am + +2006-11-02 Patrick West + + Moved DODSResponseObject.h to the BES, naming it + BESResponseObject. Created in BES three classes to represent DAS, + DDS, and DataDDS. D DODSResponseObject.h M DAS.h M DDS.h M DDS.cc + +2006-10-30 James Gallagher + + expr-tests for geogrid() and geoarray(): Fixed the tests so they + work using the new notation which passed geo points using lat,lon + as opposed to lon,lat. + +2006-10-30 James Gallagher + + Makefile.am: Removed the debug flags from AM_CXXFLAGS. -Werror + really breaks the builds because of issues in the bison/flex + code. + +2006-10-28 James Gallagher + + Auto reset of Subversion properties + +2006-10-27 James Gallagher + + Constructor.cc, ArrayGeoConstructor.cc: Removed DODS_DEBUG + +2006-10-27 James Gallagher + + DDSTest.cc: Removed compiler warnings. + +2006-10-27 James Gallagher + + HTTPConnect.cc: Fixed a bug in save_raw_http_headers() where the + test for the \r\n line terminator was broken (tested nmemb-1 instead + of nmemb-2. + Makefile.am: added a target for test coverage and added the debugging + options to CXXFLAGS by default. Make sure to back this out before + release + Unit Tests: Fixed up warnings in the unit tests + +2006-10-27 James Gallagher + + DDS.h: Added declarations for the new transfer_attributes() + methods + +2006-10-27 James Gallagher + + Constructor.h: Added declarations for the new transfer_attributes() + methods. + +2006-10-27 James Gallagher + + BaseType.h: Added a little text to the ptr_duplicate() documentation. + +2006-10-27 James Gallagher + + HTTPCache.cc: Changed the way the mutex which is used to protect + the various methods is initialized. + +2006-10-27 James Gallagher + + GeoConstraint.cc: Fixed two warnings, one about using a prefix + operator with modulus and assignment back to the same variable and + the other about field initialization order. + +2006-10-27 James Gallagher + + DODSFilter.cc: Fixed the broken definition of send_blob(), + not that it matters... + +2006-10-27 James Gallagher + + DDS.cc: I removed the second attempt at transfer_attributes and + started on a third. This one seems to work much better; see the + tests in DDSTest.cc. This version takes the _dim attributes from + the hdf4 server and bundles them with a Grid Map or puts them in + a sub-container for an Array. This should not break libnc-dap while + still preserving all the attribute data. + +2006-10-27 James Gallagher + + Constructor: Added code to merge attributes into a constructor. + This method is called from DDS. + +2006-10-27 James Gallagher + + ce_functions.cc: Removed unused parameters. + +2006-10-27 James Gallagher + + ArrayGeoConstraint: Fixed a warning where one version of the + constructor was not using all of its parameters. + +2006-10-27 James Gallagher + + ArrayGeoConstraintTest: added + +2006-10-27 James Gallagher + + DDSTest.cc, Makefile.am: Updated with tests for + DDS::transfer_attributes. + +2006-10-27 James Gallagher + + Added test data for DDS::transfer_attributes new new version. + +2006-10-24 James Gallagher + + HTTPConnect.cc: Removed debugging. + +2006-10-24 James Gallagher + + DDS.cc: Removed the 'sub table' code from transfer_attributes(). + getdap.cc: Added -X option to build the DDX on the client to simplify + debugging (e.g., ticket 480) + +2006-10-24 James Gallagher + + Massive removal of exception qualifiers/declarations. I've left + only the exception declarations in RCReader (which declare that + no exceptions are thrown) and those for constructors. In + HTTPConnect.cc:save_raw_http_headers() fixed a bug where headers + were assumed to have \r\n terminators. That is true only for new + servers. See ticket #631 for more information. + +2006-10-24 James Gallagher + + cgi_util, DODSFilter: Made many params & methods const + +2006-10-23 James Gallagher + + Fixed AISDatabaseParser.cc + +2006-10-23 James Gallagher + + AISDatabaseParser.cc: Replaced calls to getLineNumber and + xmlSAX2GetLineNumber with calls to one or the other depending on + the library version. In some places the SAX2 API was used + exclusively, in others the old API was used. The changes mean the + new API will always be used OR the old API will always be used, + depending on library version. + +2006-10-23 James Gallagher + + DDXParser.cc: I put the call to xml2's getLinNumber() Back in the + code since older distros don't have the newer library. + +2006-10-20 James Gallagher + + Closed some tickets for the server-side functions and added + the version function. + +2006-10-20 James Gallagher + + Fixes for ticket 616: I added code to GeoConstraint that checks + the bounding box to make sure it includes data and returns an + Error if it does not. Also, the order of the bounding box lat and + lon were switched to fit more with practice. The GeoConstraint + method arguments were no changes, only the order of lat and lon + passed to the geogrid() and geoarray() functions. + +2006-10-20 James Gallagher + + Auto reset of Subversion properties + +2006-10-19 James Gallagher + + Data for geoarray tests added. + +2006-10-19 James Gallagher + + Baseline data for geoarray tests added. + +2006-10-19 James Gallagher + + Tests for geoarray: Added + +2006-10-19 James Gallagher + + Added geoarray() which required changes to GeoConstraint and + GridGeoConstraint (I created GridGeoConstraint and made + GeoConstraint abstract). ce_functions.cc was also modified. + +2006-10-19 James Gallagher + + ArrayGeoConstraint.cc: Added + +2006-10-19 James Gallagher + + ArrayGeoConstraint.h: Added + +2006-10-19 James Gallagher + + Auto reset of Subversion properties + +2006-10-18 James Gallagher + + Updated baseline for text.z3.exp (data.z3) after fixing + GeoConstraint::find_latitude_indeces(). + +2006-10-18 James Gallagher + + Added data.61a-d + +2006-10-18 James Gallagher + + GridGeoConstraintTest: Updated with a new test for + find_latitude_indeces. + +2006-10-18 James Gallagher + + Updated the data.z? files given that the range selection behavior in + GeoConstraint has been changed. + +2006-10-16 Rob Morris + + Update win32 module export definitions for latest libdap*. + +2006-10-16 Rob Morris + + Provide header for find_if under win32 + +2006-10-16 Rob Morris + + Change funcs that return nothing to be declared so + +2006-10-14 James Gallagher + + Auto reset of Subversion properties + +2006-10-13 James Gallagher + + GridGeoConstraint: Fixed the calls so that they no longer use the + dds argument + +2006-10-13 James Gallagher + + GridGeoConstraint: Added + +2006-10-13 James Gallagher + + Removed DDS from GeoConstraint. + +2006-10-13 James Gallagher + + HTTPCache.cc: Minor change to a comment. + +2006-10-13 James Gallagher + + Refactored GeoConstraint so that all the Grid-specific code is now + in GridGeoConstraint (created/added GridGeoConstraint). Modified + ce_functions so that geogrid uses the new object. Updated the unit + tests. Added the new class to the Makefile.am + +2006-10-13 James Gallagher + + Auto reset of Subversion properties + +2006-10-12 James Gallagher + + Added tests. + +2006-10-12 James Gallagher + + Test baseline data for the geogrid() function + +2006-10-12 James Gallagher + + Changes test.10 to test.a + +2006-10-12 James Gallagher + + Auto reset of Subversion properties + +2006-10-11 James Gallagher + + Added + +2006-10-11 James Gallagher + + Added tests to expr-testsuite for geogrid. Somewhat dubious + at the moment. + +2006-10-10 James Gallagher + + expr-test.cc: reformatted + +2006-10-09 James Gallagher + + BaseType, RValue, et c.: Closed ticket regarding optimization + of build_btp_args. Args are always BaseTypes and before they + were always read. This could cause huge Grid/Array variables to + be read when those were arguments to functions, even if the + running the function would result in only a small part of the + Grid/Array being returned. Now arguments are not read. If functions + need values for the arguments, they must read them themselves. + +2006-10-09 James Gallagher + + cgi_util.cc: Fixed a bug in set_mime_text where the wrong 'type' + was returned. The type was always set to dap4_ddx. + +2006-10-09 James Gallagher + + CEFunctionsTest: Paths easy to set for debugger. + +2006-10-09 James Gallagher + + DDXParserTest: Turned off debug + +2006-10-09 James Gallagher + + AISDatabaseParser: Replaced old xml getLineNumber with the newer + SAX2 api call. + +2006-10-06 James Gallagher + + Vector: Added set_value() and value() methods. + Updated docs + +2006-10-06 James Gallagher + + ce_functions.cc: Now the linear_scale function will work for + Grid Maps in the hdf4 handler. + +2006-10-06 James Gallagher + + Check point: I've modified Connect, getdap and ObjectType so they + support a Connect::request_ddx() method. I've added a stream + reader to DDXParser and am in the process of figuring out where + the attributes are going! + +2006-10-05 James Gallagher + + DDXParser.h: Added an intern_stream() method to read from a stream and + updated the code (one function) to use the new SAX2 API from libxml2. + +2006-10-05 James Gallagher + + DDXParser.cc: Added an intern_stream() method to read from a stream and + updated the code (one function) to use the new SAX2 API from libxml2. + +2006-10-05 James Gallagher + + Vector.cc: Ran indent + +2006-10-04 James Gallagher + + ConstraintEvaluator: Now uses a list to store the list of functions + and the add_function() methods now overwrite functions with the + same names so it's possible to provide specializations of the + standard functions in ce_functions. + +2006-10-04 James Gallagher + + Added value()/set_value() methods to Float32/64, Int16/32, + UInt16/32 and Str. + +2006-10-04 James Gallagher + + ce_functions.cc: Both grid and geogrid now use a little hack to + read the map vectors of a Grid. The HDF handler cannot read the + Grid Maps directly (maps on HDF are not really variables but are + attributes instead). This hack involves setting send_p for the + just the maps and then calling the Grid's read() method. + +2006-10-04 James Gallagher + + ConstraintEvaluator: Now uses a list to store the list of functions + and the add_function() methods now overwrite functions with the + same names so it's possible to provide specializations of the + standard functions in ce_functions. + +2006-10-04 James Gallagher + + Vector.cc: Simplified the code in val2buf() + +2006-10-03 James Gallagher + + Auto reset of Subversion properties + +2006-10-02 James Gallagher + + Float64, Int32: Added value/set_value() methods + Grid: Added an reverse iterator typedef and rbegin/rend + methods. This provides an easy way to access the 'rightmost' + map vectors (used by geogrid()). + +2006-10-02 James Gallagher + + Vector.cc: Ran through astyle indent utility. + +2006-10-02 James Gallagher + + parser-util.cc: Ran through astyle indent utility. + +2006-10-02 James Gallagher + + CEFunctionsTest: Added tests for linear_scale(). + +2006-10-02 James Gallagher + + ce_functions: Added linear_scale function. + CEFunctionsTest: Added tests for linear_scale(). + +2006-09-30 James Gallagher + + Auto reset of Subversion properties + +2006-09-29 James Gallagher + + Makefile.am: Added GEOConstraintTest + +2006-09-29 James Gallagher + + GeoConstraintTest: Extracted from CEFunctionsTest and added to + svn. + +2006-09-28 James Gallagher + + Clause.cc: Fixed up some comments. + +2006-09-28 James Gallagher + + GeoConstraint.cc: Turned off instrumentation; removed throw() + from declarations, fixed a problem with bad latitude constraint + information that caused a fatal error (now it sends a message + indicating how to fix the problem). + +2006-09-28 James Gallagher + + DODSFilter.cc: Modified send_data() so that errors thrown by + BaseType functions will be returned to clients correctly. + +2006-09-28 James Gallagher + + ce_functions.cc, GeoConstraint.h: Removed 'throw(Error)' from + declarations since C++ calls abort() when/if another type of + excpetion is thrown, even if there's a catch() clause for it. + +2006-09-28 James Gallagher + + ce_functions.h: Removed 'throw(Error)' from declarations since C++ + calls abort() when/if another type of excpetion is thrown, even if + there's a catch() clause for it. + +2006-09-27 James Gallagher + + Vector.cc: I made the instrumentation in the ctor and dtor + 'level two' statements so that the output would be less cluttered. + +2006-09-27 James Gallagher + + GeoConstraint.cc: reorder_data_longitude_axis() now works. + CEFunctionsTest.cc: Turned off debugging. + +2006-09-26 James Gallagher + + GeoConstraint: Checkpoint; passes unit tests but still fails with + a real dataset (COADS_CLIMATOLOGY). Problem with circular + longitude axis. + +2006-09-25 James Gallagher + + ce_functions.cc: The grid() function is now implemented so that + geogrid() can use it to evaluate GSEs (ticket 569) + +2006-09-25 James Gallagher + + ce_functions.cc/h: The grid() function is now a BaseType* function. + CEFunctionsTests: Work with the new grid() implementation changes. + +2006-09-24 James Gallagher + + ce_functions.cc: Removed old code that was getting in the way. + + GeoConstraint.cc: Fixed a buf in reorder_data_longitude_axis(): + off-by-one error when applying the first constraint (used + d_lon_length instead of d_lon_length-1 for the end point). + + CEFunctionsTest: Added code to use TestTypes from the 'test' + directory and generally fixed up the tests so they work with the + new BaseType function version of geogrid(). + +2006-09-23 James Gallagher + + Auto reset of Subversion properties + +2006-09-22 James Gallagher + + Modified TestArray so that it's possible to see that arrays really + are subsampled. That is, if a constraint is set on an array, + TestArray::read() will first allocate the whole array and then + sample it, simulating a read() method that's interacting with an + API that knows how to sample an array. Works only for two dims. + +2006-09-22 James Gallagher + + test.c{0,1}: Added + +2006-09-22 James Gallagher + + Grid: Added get_array() which returns an Array* so the cast that + was needed when using array_var() can be removed. I changed all + the calls to array_var() to get_array() (DDS, ce_expr, ce_functions). + + RValue.cc: I removed the read() call when build_btp_args() builds + an argument list. This change may break client software, but it means + that variables are not complete read when they are passed to CE + functions, which should be a big boost in efficiency when working + with EOS Grids in geogrid(). + + Added code in TestArray to make arrays with constrained values. + GeoConstraint: Updates; almost complete. + +2006-09-22 James Gallagher + + GeoConstraint: Added a new method and an enum to record the 'sense' of + the latitude map (normal or inverted). + Added tests for the find_latitude_indeces() method to the unit tests + +2006-09-21 James Gallagher + + GeoConstraint/geogrid checkpoint. + Byte,Str: Added value() and set_value() methods + TestArray: Improved the read() method so it uses the series_values + property and so it returns reasonable values for lat/lon vectors + expr-test.cc: Now can be used to test BaseType functions + +2006-09-20 James Gallagher + + Another GeoConstraint checkpoint + +2006-09-20 James Gallagher + + GeoConstraint checkpoint + +2006-09-19 James Gallagher + + Makefile.am: Patch from Patrice Dumas regarding the libdapserver + and libdapclient libraries. + +2006-09-19 James Gallagher + + libdap.spec: Patch from Patrice Dumas regarding the libdapserver + and libdapclient libraries. + +2006-09-19 James Gallagher + + dap-config.in: Changed the semantics of --libs so that it matches + the old behavior. + +2006-09-15 James Gallagher + + Updated the version to 3.7.2 and the associated ChangeLog, + configure.ac, et c., files. The docs have also been updated + (docs/html.tar.gz). + +2006-09-15 James Gallagher + + HTTPConnect.cc: Changed the way headers are parsed. Now the code + assumes that each header is terminated by a \r\n pair (previously + the code assumed the line terminator was a \n character only). + +2006-09-15 James Gallagher + + HTTPConnectTest: Fixed most, but not all, of the failures. See + ticket 552 for more info. + +2006-09-15 James Gallagher + + test.f.exp: The change to TestStr so that the number included in the + string increments broke this test. Fixed. + +2006-09-15 James Gallagher + + Sequence: Doxygen was complaining about the types used in the + new transfer_data_*() methods because they were not exactly the + same string (vector versus sequence_values_stack_t). + I made them the same. + +2006-09-15 James Gallagher + + doxy.conf: Now the method listings are in alphabetical order. + +2006-09-14 James Gallagher + + SequenceTest.cc: Now tests the new transfer_data() method. + +2006-09-14 James Gallagher + + Added the transfer_data() method to Sequence. This method reads + data using the read() method and loads values into the local object's + d_values field. Values can then be used by methods like print_val() + which expect to see information stored in the Sequence objects + as if the object was part of a client and had read the data off of + the wire. I also modified the print_vals_by_row so that nested + Sequences values are indented by four spaces. + +2006-09-14 James Gallagher + + TestStr.cc: Modified the string so that the value of count + increments by one for each instance. + +2006-09-14 James Gallagher + + TestInt32.cc: Modified so that on 64-bit machines the overflow of the + '* 32' operation does not reset the value of _buf to zero, but instead + _buf cycles back to 32. + +2006-09-13 James Gallagher + + Sequence: Added transfer_data and related methods. These are used + to move data on the server-side from a data source into the + d_values field, loading the variables in the sequence with data in + a way that is very similar to the operation of all the other + variables. This provides a way to pass a DDS to other code which + is loaded with data. SequenceTest.cc and Makefile.am in + unit-tests: Test the transfer_data method. Int32: I added a + value() method similar to those added to Float32/64 a while back. + This simplifies testing so much... configure.ac: Bumped up the + revision number of the library. + +2006-09-08 James Gallagher + + GNURegex: I change the regex_t field from a regular variable to a + pointer. Seems to fix the problem, although I don't know why. Not + the most reassuring feeling... + +2006-09-05 James Gallagher + + BaseType.cc: Modified add_var() so that it now throws an InternalErr + and fixed up the documentation so it reads correctly. + +2006-09-02 James Gallagher + + Auto reset of Subversion properties + +2006-09-01 James Gallagher + + ce_functions.cc: geogrid() no longer uses the projection/selection + function model. Now it's a pure projection function. + +2006-09-01 James Gallagher + + GeoConstraint: removed old code and modified find_lon_lat_maps() + so that I can test with the COADS dataset. Tests not working... + +2006-09-01 James Gallagher + + GeoConstraint: removed old code and modified find_lon_lat_maps() + so that I can test with the COADS dataset. Tests not working... + +2006-09-01 James Gallagher + + checkpoint: GeoConstraint/geogrid() nearly working Changes in + DDS, ConstraintEvaluator, ce_functions (now uses the new libdap + namespace), RValue, various tests. + +2006-09-01 James Gallagher + + Updates/Changes to the grid() testsuite that uses remote servers. + I changed the tests so they now run using servers on test.opendap.org + and use getdap (the tests were originally written to use geturl). + +2006-08-29 James Gallagher + + GeoConstraint: Changed code to do the data translation for split + constraints so that it uses memcpy(). Untested. + +2006-08-28 James Gallagher + + GeoConstraint: Added some of the code needed to swap parts of a 'split' + longitude constraint. + +2006-08-28 James Gallagher + + CEFunctionsTest.cc: Added unit tests for vector swapping coode. + +2006-08-28 James Gallagher + + Added separate lon/lat bounding box marking code to GeoConstraint. + +2006-08-25 James Gallagher + + More work on geogrid(). + M ce_functions.h + M GeoConstraint.h + M Grid.h + M Vector.cc + M ce_functions.cc + M Grid.cc + M unit-tests/CEFunctionsTest.cc + M GeoConstraint.cc + +2006-08-25 James Gallagher + + Auto reset of Subversion properties + +2006-08-24 James Gallagher + + Progress on the geogrid() server-side function. Changed/Added files: + M RValue.cc + M ce_functions.h + M GeoConstraint.h + M Makefile.am + M gse-test.cc + M ce_functions.cc + A unit-tests/ce-functions-testsuite + A unit-tests/ce-functions-testsuite/two_grid.dds + A unit-tests/ce-functions-testsuite/geo_grid.dds + M unit-tests/CEFunctionsTest.cc + M Clause.cc + M GeoConstraint.cc + +2006-08-22 James Gallagher + + Auto reset of Subversion properties + +2006-08-21 James Gallagher + + Updated release files for the 3.7.1 source release. + +2006-08-21 James Gallagher + + ce_functions.cc, DDS.cc, CEFunctionsTest.cc, docs/html.tar.gz: + Updates for documentation. For CEFunctionsTest.cc: Added tests + for the new grid() and nascent geogrid() code. + +2006-08-21 James Gallagher + + GeoConstraint.cc: Added + +2006-08-21 James Gallagher + + DDS.cc: Fixed tcket 480. The transfer_attributes() method + was not handling the HDF4 *_dim_? attribute containers + correctly, at least as far as the libnc-dap was concerned. + Now those attribute containers are made subtables of the + associated variable. That is, if there's a variable + and a container _dim_0, then the attribute table + will have a subtable 'dim_0'. + +2006-08-21 James Gallagher + + ce_functions.cc: Added stuff for gwogrid() (which still doesn't + work). + +2006-08-21 James Gallagher + + BaseType.cc: Updates to comments. + +2006-08-21 James Gallagher + + Grid.h: Comment fixes, moved some doxygen comments into + Grid.cc. + +2006-08-21 James Gallagher + + GeoConstraint.h: Added. Currently does nothing. + +2006-08-21 James Gallagher + + ce_functions.h: changed the name of func_grid_select() to + projection_function_grid(). Added projection _function_geogrid(). + +2006-08-21 James Gallagher + + AttrTableTest.cc: Added a test for the new append_attr() method. + +2006-08-21 James Gallagher + + DDSTest.cc: Added a test for ticket 480 (transfer_attributes()). + +2006-08-21 James Gallagher + + S2000415.HDF.test1.das: Added for more DDS::transfer_attributes() + tests. + +2006-08-21 James Gallagher + + Mkaefile.am: GetConstraint.cc Added. + +2006-08-21 James Gallagher + + RValue.cc: Fixes for some comments. + +2006-08-21 James Gallagher + + Grid.cc: Fixes for some comments. + +2006-08-21 James Gallagher + + DAS.cc: Removed the 'char*' versions of the methods. These have not + been needed since we stopped using the GNU String class. + +2006-08-17 James Gallagher + + BaseType.h: Reformatted a doc comment for var(). + +2006-08-17 James Gallagher + + AttrTable: Added append_attr() method that takes a vector of values + so that client code does not have to call the single value version + in a loop. + +2006-08-17 James Gallagher + + AttrTable: Added append_attr() method that takes a vector of values + so that client code does not have to call the single value version + in a loop. + +2006-08-15 James Gallagher + + Auto reset of Subversion properties + +2006-08-14 James Gallagher + + das.tab.cc: Updated (edits to das.y). + +2006-08-14 James Gallagher + + das.y: config_dap.h --> config.h + +2006-08-14 James Gallagher + + Makefile.am, CEFunctionsTest.cc: Added unit tests for the the + grid() ce function. + +2006-08-14 James Gallagher + + Str.cc: No longer includes an extern declaration for escattr() since + that function is not called by this code. + +2006-08-14 James Gallagher + + GSEClause.cc: Added the word 'map' to the error messages about + missing map variables. + +2006-08-11 James Gallagher + + ce_functions.cc: Modified so that the errors returned match those + in the design. + +2006-08-01 James Gallagher + + DDS.cc, DDXParser.cc/h, tests/ddx-testsuite/*.ddx: Changed the name + of the dodsBLOB element to dataBLOB and the attribute from URL to href. + See ticket #454. + +2006-08-01 James Gallagher + + DDSTest.cc: Changed expected name to dataBLOB from dodsBLOB and + attribute of that element to href from URL. + +2006-08-01 James Gallagher + + html.tar.gz: Updated build of reference docs. + +2006-08-01 James Gallagher + + generalUtilTest.cc: Fixed test for the new id2www/www2id behavior + which escapes the percent sign. + +2006-08-01 James Gallagher + + BaseType.h, ConstraintEvaluator.cc, DODSFilter.cc, escaping.cc/.h, + Sequence.cc: Updated comments. + +2006-08-01 James Gallagher + + libdap.m4: Applied Patrice Dumas' patch which checks for the newer + dap-config and uses it's options for the three library setup. + +2006-07-31 James Gallagher + + Auto reset of Subversion properties + +2006-07-30 Rob Morris + + w32 Makefile rewrite & export def's for 3-lib split-up of libdap + +2006-07-09 Rob Morris + + Tweeks for win32 porting effort + +2006-07-09 Rob Morris + + New makefile for win32 libdap version to accomodate 3-library split + +2006-07-07 James Gallagher + + docs/html.tar.gz, escaping.cc: Updated/corrected documentation. + +2006-07-07 James Gallagher + + cgi_util.cc, configure.ac: Fixed bug 437. When a handler is not passed + a version number, it will now use the library's version number set at + compile time. + +2006-07-03 Rob Morris + + Patch core to use libcurl as a dll without crashing + +2006-06-14 James Gallagher + + Removed GNU error and builtin from Makefile.am (they were already + removed from the directry GNU. + +2006-06-14 James Gallagher + + Updates to ChangeLog, README, INSTALL, NEWS, libdap.spec, + Makefile.am and html.tar.gz for the beta releae 3.7.0. + +2006-06-13 James Gallagher + + lex.gse_.cc: Comments + +2006-06-13 James Gallagher + + ce_functions.cc, gse.lex: fixed comments + +2006-06-09 James Gallagher + + INSTALL.AIX: Added newer comments from Dan S. + +2006-06-09 James Gallagher + + INSTALL: Updated by adding a reference to the INSTALL.AIX file. + +2006-06-06 James Gallagher + + Auto reset of Subversion properties + +2006-06-05 James Gallagher + + GNURegex: Added Error thows back into the code + +2006-06-05 James Gallagher + + In GNU: Removed some now-unused files and fixed a bug in + GNURegex.cc where config.h was not included when it must be. This + caused a failure on OS/X because the rpl_regexec(), ..., symbols + were not defined. + +2006-06-02 James Gallagher + + regex_test.cc: Added + +2006-06-01 James Gallagher + + Auto reset of Subversion properties + +2006-05-31 Nathan Potter + + GNURegex.cc: kludge! Added a workaround patch to + GNURegex.cc:match() to avoid a illegal memory access segmentation + fault under OS-X 10.4.6. Inline comments provide greater detail. + (ndp) + +2006-05-31 James Gallagher + + ssize_t.m4: Added during most recent update. + +2006-05-30 James Gallagher + + In gl/: Update to the gnulib code. This may fix a problem on OS/X + where the gnulib regexec function appears to corrupt the stack. + +2006-05-19 James Gallagher + + Various types (Int16, et c.): Removed include of ce_expr.tab.h since + that header is included by Operators.h + +2006-05-19 James Gallagher + + Makefile.am: Removed include of libgnu from libdapclient and + libdapserver. This should fix the multiple define error at link time. + +2006-05-19 James Gallagher + + ce_expr.y: fixed multiple use of the variable name 'table' in + declarations. + +2006-05-18 James Gallagher + + Makefile.am: reordered libraries and added ce_parser.h to DAP_HDR + +2006-05-10 James Gallagher + + AISDatabaseParser.h, AISResources.cc, AISResources.h, Resource.cc: + Updates to doxygen comments. + +2006-05-08 James Gallagher + + Operators.h: Made dods_max() inline. This should improve performance + and also fix a problem introduced by the recently applied AIX patch + where dods_max() was multiply defined. + +2006-05-04 James Gallagher + + INSTALL.AIX: Updated the information from Dan Stromberg and Martin + Peschke. + +2006-05-04 James Gallagher + + Operators.h, RValue.cc, deflate.c: Applied patches from Dan Stromberg + which fix problems found during a port to the native AIX compiler. + +2006-05-01 James Gallagher + + libdap.m4: Added DAP_CLIENT_LIBS and DAP_SERVER_LIBS to symbols + that are written out. + +2006-04-20 James Gallagher + + Auto reset of Subversion properties + +2006-04-19 James Gallagher + + ce_expr.y: The expression parser now passes the ConstraintEvaluator + object to projection functions. + +2006-04-19 James Gallagher + + ConstraintEvaluator: Removed some code that was commented out. + +2006-04-19 James Gallagher + + expr.h: The projection function typedef now includes a fourth + argument so that the ConstraintEvaluator can be passed to the + projection function. This provides a way for the proj_function to + register the matching selection function. + +2006-04-19 James Gallagher + + ce_parser.h: Added. Used to pass both the DDS and ConstraintEvaluator + to the CE Parser. + +2006-04-19 James Gallagher + + Auto reset of Subversion properties + +2006-04-18 James Gallagher + + configure.ac, dap-config.in, conf/libdap.m4: bumped up the revision + number to 3.7.0 (which will be the next revision), added options to + dap-config --server-libs and --client-libs and updated the libdap.m4 + macro to match the new three-library organization of libdap. + +2006-04-18 James Gallagher + + expr.lex, expr.y, lex.expr.cc, expr.tab.cc: Removed/Replaced with + ce_expr... + +2006-04-18 James Gallagher + + Removed_functions.txt: Updated for the latest changes. + +2006-04-18 James Gallagher + + lex.ce_expr.cc: Added + +2006-04-18 James Gallagher + + expr-test.cc, tests/Makefile.am: Tests updated for changes in DDS, + DODSFilter, et cetera. + +2006-04-18 James Gallagher + + DODSFilter: Modified so that this class now takes a ConstraintEvaluator + object in the places where needed. + +2006-04-18 James Gallagher + + DDS: Removed the constraint evaluator and the send() method. + +2006-04-18 James Gallagher + + BaseType.h and it's child classes: Now the serialize() method + takes not only a DDS but also a ConstraintEvaluator object. + +2006-04-18 James Gallagher + + ce_expr.lex, ce_expr.y, ce_expr.tab.{cc,h}: Added files that + contains a modified ce parser. This parser takes two objects as + input: ConstraintEvaluator and DDS. The old parser took only the + DDS. + +2006-04-18 James Gallagher + + ConstraintEvaluator: Added this class. Now the ce evaluator is separate + from the DDS object. + +2006-04-18 James Gallagher + + Clause.h: Added private methods for the const ctor and operator= + so that default versions would not get called by accident. + +2006-04-18 James Gallagher + + cgi_util.cc: Removed the do_data_transfer() function. Use DODSFilter. + +2006-04-18 James Gallagher + + ce_functions.cc: Minor change to the func_length code. It no + longer uses the 'constants' vector in DDS to store the BaseType + used for the return value. The caller can easily free the storage. + Also, this function is never used! + +2006-04-18 James Gallagher + + INSTALL.AIX: Fixed spelling and added a note about not bugging + Martin or assuming he's going to guarantee the steps will work. + +2006-04-18 James Gallagher + + Makefile.am: Now builds the ce_expr parser in place of the expr + parser. There's no real difference in the grammar accepted by the + new parser, but ce_expr takes both the ConstraintEvaluator and + DDS objects, where the old one took only the DDS. + +2006-04-18 James Gallagher + + Operators.h: Now uses the ce_expr parser header. + +2006-04-18 James Gallagher + + parser.h: Removed the GSEClause include that I'd commented out earlier. + +2006-04-16 Rob Morris + + Rename, add dirs and prune as appropriate for VC++ port + +2006-04-04 James Gallagher + + Auto reset of Subversion properties + +2006-04-03 James Gallagher + + gse_parser.h: Added. This holds parser info used by the grid() ce + function. The contents of this file were originally in parser.h, which + holds definitions for the DAP parsers. Moving the info about grid() + here maens that the grid() code can be part of the dapserver library. + +2006-04-03 Rob Morris + + Edit 'scalar deleting destructors' out of win32 module def's + +2006-03-31 James Gallagher + + For ticket 307 I modified the build so that three libraries are + made instead of just one. The libdap library now contains only + classes used for the DAP2 implementation. The client and server + support code is now in libdapclient and libdapserver. Many files + were modified since I trimmed the includes so that classes in + libdapclient don't include headers in libdapserver and vice versa. + +2006-03-31 James Gallagher + + Auto reset of Subversion properties + +2006-03-30 James Gallagher + + Added INSTALL.AIX + +2006-03-27 James Gallagher + + Massive propset on libdap to get svn to ignore many things so + eclipse will work more smoothly + +2006-03-24 James Gallagher + + Updated svn:ignore for some files. + +2006-03-21 James Gallagher + + Build of docs/html.tar.gz for 3.6.2 on 3/21/06 + +2006-03-21 James Gallagher + + Auto reset of Subversion properties + +2006-03-20 James Gallagher + + Moved tests/expr-testsuite/test.a to tests/expr-testsuite/test.10. + +2006-03-20 James Gallagher + + test.c.exp: Removed log + +2006-03-19 Rob Morris + + Win32 libnc-dods requires core's regex.h installed. None stock on + windows. + +2006-03-19 Rob Morris + + Update export module definitions per recent core changes + +2006-03-16 James Gallagher + + NEWS: Spelling... one more time + +2006-03-16 James Gallagher + + NEWS: Spelling... + +2006-03-14 James Gallagher + + Updated ChangeLog, configure.ac, libdap.spec and NEWS for version + 3.6.2 + +2006-03-14 James Gallagher + + I changed the way DODSFilter handles the compression sub-process. + The class was caling waitpid on the child process which was blocking + even after the child had exited. I found that calling fflush on + the FILE* where teh server is writing the response is sufficient + to guarantee that all the data from the compressor is written w/o + using waitpod. This is a fix for ticket #335 + +2006-03-10 James Gallagher + + Added a new public method to Connect read_data_no_mime() which can + be used to read from a file. This method read a DAP2 data object + from a file but does not try to parse MIME headers before reading + the data. I modified getdap.cc so that it uses this when reading + from a file that's not stdin. + +2006-03-10 James Gallagher + + Auto reset of Subversion properties + +2006-03-09 James Gallagher + + Updated for 3.6.1 + +2006-03-09 James Gallagher + + Bumped up the version to 3.6.1. Files affected: configure.ac libdap.spec, + INSTALL, README, NEWS. + +2006-03-09 James Gallagher + + Updated tests/expr-testsuite/expr-test.0/test.c by changing its name to + test.d so that Eclipse's indexer won't gag on it. + +2006-03-09 James Gallagher + + removed unused test directories expr-testsuite and grid-func-testsuite + +2006-03-09 James Gallagher + + Added -t option to DODSFilter.cc. This was left out by mistake. + +2006-03-09 James Gallagher + +2006-03-09 James Gallagher + + Changed tests from dodsdev to test.opendap.org + +;; Local Variables: +;; coding: utf-8 +;; End: +2006-03-09 James Gallagher + + Bumped up the version to 3.6.1. Files affected: configure.ac libdap.spec, + INSTALL, README, NEWS. + +2006-03-09 James Gallagher + + Updated tests/expr-testsuite/expr-test.0/test.c by changing its name to + test.d so that Eclipse's indexer won't gag on it. + +2006-03-09 James Gallagher + + removed unused test directories expr-testsuite and grid-func-testsuite + +2006-03-09 James Gallagher + + Added -t option to DODSFilter.cc. This was left out by mistake. + +2006-03-09 James Gallagher + + Changed tests from dodsdev to test.opendap.org + +2006-03-08 James Gallagher + + Fixed the C++-style comment in deflate.c from a bug reprot + from Jim Davis at Nasa. + +2006-03-06 James Gallagher + + Updated for 3.6.0-2 + +2006-03-06 James Gallagher + + Increased the release number to two. + +;; Local Variables: +;; coding: utf-8 +;; End: +2006-03-06 James Gallagher + + Increased the release number to two. + +2006-03-05 Rob Morris + + Trivial change in comment in win32 libdap makefile + +2006-03-05 Rob Morris + + Redo recently clobbered changes from gl/ refresh + +2006-03-01 James Gallagher + + Updated the OSX_Resources/Readme.txt file for 3.6.0. + +2006-02-28 James Gallagher + + Updated grammar files and added rpm and OS/X package targets to Makefile.am + +2006-02-28 James Gallagher + + Auto reset of Subversion properties + +2006-02-27 James Gallagher + + Updated NEWS. + +2006-02-27 James Gallagher + + Updated the Mac/OSX project file for 3.6.0 + +2006-02-27 James Gallagher + + Added unit-tests/testFile.cc: This provides a way to link the FILE* + output code with C++ strings so that unit tests can work their magic. + +2006-02-27 James Gallagher + + Updated documentation files (README, NEWS, ChangeLog), version information + (configure.ac, libdap.spec) and removed two unused unit test files. + +2006-02-27 James Gallagher + + Imported the latest gnulib code (see the Makefile.am in gl for the + gnulib-tool arguments). + +2006-02-25 James Gallagher + + Auto reset of Subversion properties + +2006-02-24 James Gallagher + + Removed lines in Makefile.am that were commented out. + +2006-02-24 James Gallagher + + Removed html.tar.z and added html.tar.gz + +2006-02-24 James Gallagher + + Removed docs.tar.gz + +2006-02-24 James Gallagher + + Added docs and docs/html.tar.gz + +2006-02-24 James Gallagher + + Fixed up the regression tests so that they don't use deprecated + Error methods. + +2006-02-24 James Gallagher + + Removed LongIterAdapter.cc from tests + +2006-02-24 James Gallagher + + Removed files no longer needed. Removed depreacted methods. Updated + documentation. + +2006-02-23 James Gallagher + + Removed most $Log$ entries since they are not supported by + Subversion. Will do the rest tomorrow... + +2006-02-23 James Gallagher + + Removed all the method overloads that used const char * + parameters. Those methods were added so that the Pix objects, + which were passed as void*, would not get called with character + strings by mistake. Since the Pix methods are gone, these are no + longer needed. + +2006-02-21 James Gallagher + + I fixed the constructors for both PipeResource and StdinResource + so that the parent class and fields were initialized in the + correct order. I also fixed up the documentation for + DODSResponseObject. + +2006-02-20 Rob Morris + + Tweek linker settings for libdap dll + +2006-02-20 Rob Morris + + Sync win32 makefile with unix version, rewrite + +2006-02-20 Rob Morris + + Update module definitions for dll version of libdap + +2006-02-20 Rob Morris + + Tweek for win32 port + +2006-02-15 Patrick West + + fixed indentation + +2006-02-15 Patrick West + + commented out DataDDS constructor that doesn't take BaseTypeFactory + +2006-02-15 James Gallagher + + DataDDS.cc: Added a call to protocol_string_to_numbers in the + constructor. This sets the internal protocol version numbers so + that calls to Sequence:: deserialize() work (they now use the + protcol version number to select the correct algorithm for reading + from the stream instead of the server version). + +2006-02-15 James Gallagher + + AttrTable.cc: Corrected a problem in add_value_alias where looking + for attributes could cause a segmentation fault. The problem was + that AttrTable::find() was setting the return iterator to + attr_end() for the AttrTable object it returned as a value-result + parameter (so the test to see if the iterator was pointing to the + end needed to be done using that table and not the calling object. + +2006-02-14 James Gallagher + + NEWS and ChangeLog both updated. + +2006-02-14 James Gallagher + + Auto reset of Subversion properties + +2006-02-13 James Gallagher + + Modified the print_xml method in Constructor and grid. It no + longer prints anything for variables that are not part of the + current projection when printing the constrained ddx. This works + because the DDS::mark method marks the parents of projected + variables. + +2006-02-13 James Gallagher + + Added PipeResponse.h and StdinResponse.h; changed getdap and + Connect to use them. + +2006-02-13 Rob Morris + + Flush weekend win32 porting work back to trunk + +2006-02-10 Rob Morris + + Removed xdr routines from win32 subdir under libdap. Code now in + base tools used to build under win32. + +2006-02-09 James Gallagher + + empty comment + +2006-02-09 James Gallagher + + Removed all deprecated methods as per ticket 248. This includes + Pix methods, ostream I/O methods used by servers and various junk + in Connect. + +2006-01-25 James Gallagher + + Auto reset of Subversion properties + +2006-01-24 James Gallagher + + Added Removed_functions.txt: This file contains the type + signitures of methods/functions recently (version 3.6 and later) + removed. + +2006-01-24 James Gallagher + + The ostream I/O was removed and many files were modified. The code + is still present inside #if 0 ... #endif preprocessor lines. + +2006-01-23 James Gallagher + + All unit tests updated to work after removing the iostream + functions/methods. + +2006-01-19 James Gallagher + + Added protocol version to the library. The version number is set + in the configure.ac file. I've modified the unit tests so that + they work. I've also removed (#if 0 ... #endif) all of the MIME + header functions which used the C++ iostream stuff (which is part + of the task to remove deprecated code). + +2006-01-12 James Gallagher + + Updated ChangLog + +2006-01-12 James Gallagher + + DDS.cc: transfer_attributes used odd logic and an unneeded + variable. Fixed. + +2006-01-12 James Gallagher + + expr-test.cc: changed new exception code to use fprintf() (it was + using cerr). + +2006-01-12 James Gallagher + + GSEClause: Removed the old, unimplemented, methods that were + supposed to take the grid() function subexpressions. Fixed up some + comments in the class as well. + +2006-01-12 James Gallagher + + cgi_util.cc: In remove_mime_header() \n was used as the MIME + header separator when CRLF should have been used. Since the rest + of the code was fixed for 3.5.3, remove_mime_header() didn't find + the separator. This broke the HTML form response. + +2006-01-12 James Gallagher + + DDS.cc: transfer_attributes used odd logic and an unneeded + variable. Fixed. + +2006-01-12 James Gallagher + + expr-test.cc: changed new exception code to use fprintf() (it was + using cerr). + +2006-01-12 James Gallagher + + GSEClause: Removed the old, unimplemented, methods that were + supposed to take the grid() function subexpressions. Fixed up some + comments in the class as well. + +2006-01-12 James Gallagher + + cgi_util.cc: In remove_mime_header() \n was used as the MIME + header separator when CRLF should have been used. Since the rest + of the code was fixed for 3.5.3, remove_mime_header() didn't find + the separator. This broke the HTML form response. + +2005-12-30 Rob Morris + + Work toward accomodating gcc under mingw + +2005-12-27 James Gallagher + + Installed GNU indent code formatter to eclipse and used it on + expr-test.cc + +2005-12-27 James Gallagher + + In expr-test, the function parse_mime() did not test for the new + CRLF line terminator. This caused an infinite loop. Fixed. I also + added a comment to cgi-util.cc noting that if the CRLF constant is + changed, then the one in expr-test.cc should be changed too. Since + the test code is, well, test code, it doesn't seem like the + constant should be moved out into one of the header files. The + CRLF constant is really used only by the cgi-util code. + +2005-12-27 James Gallagher + + Removed regex.loT + +2005-12-23 James Gallagher + + Removed extra qualtification on the private method Regex::init in + GNURegex.h. + +2005-12-23 James Gallagher + + Removed the send version method body and replaced it with a call + to do_version() in cgi_util.cc. This consolodates the code that + writes HTTP headers to one file. + +2005-12-23 James Gallagher + + Changed all calls to fprintf that write parts of the HTTP resonse + header so that they terminate lines using CRLF pairs and not just + LF (\n or 0x0a). I also fixed the spelling of Content-Type (the + 'T' in 'Type' was not capitalized). + +2005-12-12 Rob Morris + + Missing colon in switch from recent porting tweeks. + +2005-12-11 Rob Morris + + ifdef profoundly-non-portable signal handling code. Windows has + few signals. + +2005-12-11 Rob Morris + + Hardcode blocksize on win32 + +2005-12-11 Rob Morris + + Reverse ancient VC++ 6.0 template hacks. Microsoft has come toward + the standard. + +2005-12-05 James Gallagher + + Updated + +2005-11-22 James Gallagher + + Made docs a PHONY target + +2005-11-22 James Gallagher + + Fixed source0 using Patrice's patch + +2005-11-16 James Gallagher + + Auto reset of Subversion properties + +2005-11-15 James Gallagher + + Added commentary + +2005-11-15 James Gallagher + + Added/Hacked + +2005-11-11 James Gallagher + + Auto reset of Subversion properties + +2005-11-10 James Gallagher + + Added note about dejagnu not working on some systems unless + config.guess is linked here or at the top. + +2005-11-09 James Gallagher + + The XML schema site has chamged to xml.opendap.org. I stopped + testing these values because that's not really what the DDS class + unit tests should be looking at. + +2005-11-09 James Gallagher + + The set of chars allowed in an identifier was enlarged to include + %. The secind test was altered to fit that. + +2005-10-31 James Gallagher + + Hacked up the pkg target so that the dap-config script has the + correct values for the places where the osx package will be + installed, not where the build dumps it prior to packaging. + +2005-10-25 Patrick West + + added function to access the libdap name from PACKAGE_NAME + +2005-10-25 James Gallagher + + Auto reset of Subversion properties + +2005-10-24 James Gallagher + + Added + +2005-10-18 James Gallagher + + Update from build after failed attempt to remove Pix.h + +2005-10-18 James Gallagher + + Fixed som grammatical problems with the DDX response. + +2005-10-12 James Gallagher + + Updates to the documentation files. + +2005-10-07 James Gallagher + + escattr() no longer uses Regex. + +2005-10-07 James Gallagher + + Updated to match the new libdap behavior for escaping. + +2005-10-05 James Gallagher + + I made the standard print representation of a Grid use Array and + Maps instead of ARRAY and MAPS. So I fixed the tests too. + +2005-10-05 James Gallagher + + Don't have these in svn. + +2005-10-05 James Gallagher + + Don't have these in svn. + +2005-10-05 James Gallagher + + Fix for ticket #44 (which was filed under the hdf4 handler). I + made the set of characters not escaped by id2www and id2www_ce the + same as those that dds.les and expr.lex expect in an id. Actually + the asterisk is escaped even though dds.lex will parse it, + +2005-10-05 James Gallagher + + Auto reset of Subversion properties + +2005-10-04 James Gallagher + + Fixed the background one last time + +2005-10-04 James Gallagher + + Added a background image to the osx installer + +2005-10-04 James Gallagher + + Added + +2005-10-04 James Gallagher + + Updates for the OS/X packages stuff. + +2005-10-03 James Gallagher + + Nodified cleanup to not copy the svn parts of dods_cahce_init when + it makes dods_cache. This will help keep Eclipse's svn module from + being confused. + +2005-10-03 James Gallagher + + Build improvements from Patrice Dumas. + +2005-10-03 Patrick West + + calling fflush rather that fclose in send_data + +2005-10-01 James Gallagher + + Auto reset of Subversion properties + +2005-09-30 James Gallagher + + Updated NEWS with info about regex. + +2005-09-30 James Gallagher + + Updated for the changes to Regex. + +2005-09-30 James Gallagher + + Added + +2005-09-29 James Gallagher + + Removed regex from esc2undescore. + +2005-09-29 James Gallagher + + Fixed an error in char2ASCII. + +2005-09-29 James Gallagher + + Grammar update + +2005-09-29 James Gallagher + + Fixes for gnulib regex code. + +2005-09-28 Yuan Ho + + adding install include dir + +2005-09-28 James Gallagher + + Auto reset of Subversion properties + +2005-09-27 Yuan Ho + + adding install script + +2005-09-27 James Gallagher + + New libtool + +2005-09-27 James Gallagher + + Comment about the hack... + +2005-09-27 James Gallagher + + New grammar generated files and some temporary fixes in gl/m4/regex.m4 + +2005-09-27 James Gallagher + + Fixed an error where a signed and unsigned were compared. + +2005-09-27 James Gallagher + + Added new copy. + +2005-09-27 James Gallagher + + New gnulib changes; first half, removing the old gl code. I don't + think this was necessary, but it's hard to tell. + +2005-09-23 James Gallagher + + Auto reset of Subversion properties + +2005-09-22 James Gallagher + + Fixed install-sh; it was left out of the earlier mv + +2005-09-22 James Gallagher + + Moved the configuration files into 'conf.' + +2005-09-22 James Gallagher + + Changed the das lexer error message to include a character code. + +2005-09-19 Patrick West + + missing declaration of BaseType + +2005-09-16 James Gallagher + + Added code to DODSFilter so support its use in something other + than a Unix command line filter program. In the process I found a + bug in escaping.cc:escattr() (ticket 210) which I fixed. + +2005-09-08 James Gallagher + + Fixed a bug where _REENTRANT is defined and HAVE_STRFTIME is not. + This shows up on FC3/4 but not RHEL3 or OS/X. + +2005-09-08 James Gallagher + + Removed + +2005-09-08 James Gallagher + + Auto reset of Subversion properties + +2005-09-07 James Gallagher + + Build update + +2005-09-07 James Gallagher + + Added + +2005-09-06 James Gallagher + + Added alloc to gl_MODULES; for the os/x build. + +2005-09-06 James Gallagher + + Added the new gl directory. + +2005-09-06 James Gallagher + + Clean up. + +2005-09-06 James Gallagher + + Midway through resetting gl to only use the regex code. + +2005-09-03 James Gallagher + + Auto reset of Subversion properties + +2005-08-19 James Gallagher + + Updated libdap.spec so that it now requires libcurl 7.10.6 (a + downgrade from 7.12.0 so that we can build RPMs on RHEL3). This + seems to work just fine. Also fixed up the HTPPCacheTest unit test + so that it works the first time with a new check out. + +2005-08-19 James Gallagher + + downgrade to libcurl-7.10.6. This seems to work but needs more testing + +2005-08-17 James Gallagher + + Another shot at getting the build to work... + +2005-08-17 James Gallagher + + Build fixes; rpms now split into plain and -devel; fix for + libdap.m4 macro + +2005-08-16 James Gallagher + + Patched libdap.m4 (from Patrice) and added text the README + describing the macro. + +2005-08-16 James Gallagher + + Commented out the set of CFLAGS/CXXFLAGS (which was a left-over + from the non-automake build). + +2005-08-15 James Gallagher + + Updated for the release of 3.5.2. + +2005-08-12 James Gallagher + + Added back. + +2005-08-12 James Gallagher + + Test delete + +2005-08-11 James Gallagher + + config_dap.h --> config.h; also removed TRACE_NEW, #pragma + interface/impl. in test and unit-tests. + +2005-08-11 James Gallagher + + Removed trace_new.h; use valgrind instead. + +2005-08-11 James Gallagher + + Removed #pragma interface/implementation + +2005-08-11 James Gallagher + + So, I added IteratorAdapter.cc back in again. + +2005-08-11 James Gallagher + + Hmmm... Eclipse still doesn't seem to get the deletes quite right. + +2005-08-11 James Gallagher + + Tests updated for new float32 and 64 value computation. + +2005-08-11 James Gallagher + + Changed how sequential values are generated. The values should be + the same on 32 and 64-bit machines. + +2005-08-11 James Gallagher + + Updated... + +2005-08-11 James Gallagher + + I removed the DODS_GTAR and DODS_PERL calls; Added AC_DEFINE for + two version sof libxml2 and stripped away some code that was + commented out months ago. + +2005-08-11 James Gallagher + + Resolved conflicts & added some comments about the state of the + test driver. + +2005-08-11 James Gallagher + + Clarified commentary. + +2005-08-11 James Gallagher + + Added conditional compilation of the XML SAX callback struct. The + code now uses the library version number to determine which fields + should be included since later version have additional fields. + +2005-08-11 James Gallagher + + Removed unused params in methods as reported by gcc 3.4. This may + break gcc 3.2, we will see... + +2005-08-11 James Gallagher + + Removed unused params in methods as reported by gcc 3.4. This may + break gcc 3.2, we will see... + +2005-08-10 James Gallagher + + Changed comparison in set_max_size() so that it uses ULONG_MAX + (since the method uses a long) and does not test for negative + sizes (since size is unsigned). + +2005-08-10 James Gallagher + + Moved debug options from AM_CXXFLAGS. Set the CXXFLAGS environment + variable (sample values are assigned to CXXFLAGS_DEBUG). + +2005-08-10 James Gallagher + + commented out #define DODS_DEBUG 1 + +2005-08-10 James Gallagher + + Reverted to previous version (which uses long instead of int). The + change breaks the unit tests (which otherwise work on both 32- and + 64-bit machines. + +2005-08-10 James Gallagher + + Added + +2005-08-10 James Gallagher + + Latest and greatest eclipse/cdt settings... + +2005-08-10 James Gallagher + + Addressed various warnings from gcc (compiled using -Wall -W + -Wcast-align) as part of making the code 64-bit clean. + +2005-08-10 James Gallagher + + Added default init of TestCommon in the copy ctors for the + TestByte, ... classes. + +2005-08-10 James Gallagher + + Added proper error return to all unit tests. + +2005-08-10 James Gallagher + + Now GNURegex uses exceptions (std::invalid_argument and domain_error) + to signal problems. Controlled using USE_EXCEPTIONS. If this passes + on the nightly builds, then remove GNUerror.cc and builtin.h. + +2005-08-10 James Gallagher + + Fixed read() method: It did not include the 16-bit int types int + he switch statement. + +2005-08-08 James Gallagher + + Fixed a leak where a specialization of BaseTypeFactory was used + but not deleted. + +2005-08-08 root + + Auto reset of Subversion properties + +2005-07-28 James Gallagher + + Changes for the OS/X build. Particularly for the regex code in gl. + +2005-07-25 James Gallagher + + Fixed; the macro was using ifelse and m4_syscmd/m4_sysval but + these didn't seem to be working. Also replaced obsolete AC_TRY_TUN + with AC_RUN_IFELSE(). + +2005-07-23 James Gallagher + + Added libdap.m4: A macro that other packages can use to check for + the library. From Patrice Dumas. + +2005-07-22 James Gallagher + + Updated requirements for automake, et cetera. + +2005-07-22 James Gallagher + + Updated for automake 1.9.6. + +2005-07-21 James Gallagher + + Removed calls to OK() in the set_error_code() and + set_error_message() methods since these are called by the parser + on partially built objects. + +2005-07-20 James Gallagher + + Improved the documentation of BaseTypeFactory; removed + DEFAULT_BASETYPE_FACTORY from Makefile.am; added a note to + util.cc:prune_spaces questioning if that function is still needed; + added a test of prune_spaces to generalUtilTest. + +2005-07-20 James Gallagher + + Removed DEFAULT_BASETYPE_FACTORY switch. Updated build files using + autoreconf --install --force (using automake 1.9.2 from FC3). + +2005-07-19 James Gallagher + + Updated cdtproject file. Should this be in SVN? + +2005-07-19 James Gallagher + + Added gl/m4 to EXTRA_DIST; needed to get the distcheck target workig + with automake 1.6.3. Newer versions of automake set this correctly + given that ACLOCAL_AMFLAGS is used. + +2005-07-18 James Gallagher + + Removed $(top_srcdir) from ACLOCAL_AMFLAGS since this breaks + autoreconf. + +2005-07-18 James Gallagher + + Changed ACLOCAL_AMFLAGS from '-I gl/m4' to '-I $(top_srcdir)/gl/m4' to + fix one last problem with distcheck. + +2005-07-18 James Gallagher + + Applied Patrice's patch for unit-tests; make distcheck should now work. + +2005-07-18 James Gallagher + + Changed the cache dirs with initial values to _init and use + the cleanup.sh script to cp -r <*>_init <*> to restore them. This + way the tests work w/o svn. + +2005-07-18 James Gallagher + + Added blurb about using autoreconf to (re)build the autotools stuff. + +2005-07-15 James Gallagher + + Updated Makefile.am and the README/INSTALL/NEWS files. The 'dist' + target now works for make (although 'distcheck' fails for an odd + reason). + +2005-07-15 James Gallagher + + Copied/added + +2005-07-15 James Gallagher + + Removed + +2005-07-15 James Gallagher + + Moved/Added + +2005-07-15 James Gallagher + + Moved the unit tests from 'tests' to 'unit-tests.' The unit tests + are now only run when you cd into unit-tests and use make check. + This change was made because those tests use network services + which might not be present on some hosts used to build the + package. + +2005-07-15 James Gallagher + + More fixes for the tests + +2005-07-14 James Gallagher + + Fixed again... + +2005-07-14 James Gallagher + + Removed extra line. This hosed the test since it causes the + HTTPCache class to think there's a second zero-size entry. + +2005-07-14 James Gallagher + + Updated + +2005-07-14 James Gallagher + + Updated + +2005-07-14 James Gallagher + + Updated + +2005-07-14 James Gallagher + + Changed URL; chages cache directory number + +2005-07-14 James Gallagher + + AISMergeTest now works (required files are now installed on + test.opendap.org). Also, applied Patrice Dumas' patch for the + Makefile.am. + +2005-07-13 James Gallagher + + Fixed HTTPConnectTest + +2005-07-13 James Gallagher + + Spec file now includes support for a separate devel package + (commented out). From Patrice Dumas. + +2005-07-13 James Gallagher + + expr-test tests x, xa, xc, y, ya, and yc now expect to fail (and + do on 64bit hosts). + +2005-07-13 James Gallagher + + The last fix for this file? I hope so... + +2005-07-12 James Gallagher + + Added data files for tests in DDSTest. Bug 112. + +2005-07-12 James Gallagher + + mistakenly removed alloca_.h from svn. Fixed tests/cache_testsuite. + +2005-07-12 James Gallagher + + Test updates: expr-test now works except that 6 tests fail on + 64bit machines. Also, I updated the gnulib replacement functions + so that gnulib-tool can be used in the future to update. I moved + the gnulib functions into the 'gl' subdir and put the m4 macros in + there inside the subdir 'gl/m4.' There's a note about the + procedure to use to update the functions in INSTALL. + +2005-07-12 James Gallagher + + Added again... + +2005-07-12 James Gallagher + + removed + +2005-07-12 James Gallagher + + Added + +2005-07-12 James Gallagher + + Removed + +2005-07-12 James Gallagher + + Removed. + +2005-07-11 James Gallagher + + My previous fix for the gnulib mktime bug was bogus since it left + a replacement copy of mktime in the library using that name (which + is reserved). I've stopped using the gnulib mktime for now since + it doesn't seems absolutely necessary. + +2005-07-11 James Gallagher + + Modification from Patrice, et al. These were part of an attempt to work + around a bug with using mktime but they did not work. + +2005-07-08 James Gallagher + + Moved all tests to the 'tests' subdirectory. There are some tests + that are broken; only expr-test seems broken now but was not + before. Most of the broken tests require a localhost web daemon + and specific test files for it to serve; configure + test.opendap.org for these instead. + +2005-07-08 James Gallagher + + Removed use of the GPL regex-0.12 code and switched to gnulib + (which provides regex and other functions which are covered by + LGPL). These turned out to be fairly massive changes. The work was + done by Patrice Dumas (Thanks!). I removed a AC_DEFINE macro in + m4/mktime.m4 to work around a compilation problem when using + gnulib's mktime.c replacement function. + +2005-07-06 Patrick West + + Added methods to Connect class to connect with the given URL + instead of building the URL to connect with, such as adding .das, + .dds, etc... + +2005-07-05 Rob Morris + + Set eol-style to native on all text file in the trunk + +2005-07-05 James Gallagher + + Fixed the library version numbers. The order was backwards for the + last two digits. + +2005-07-04 James Gallagher + + Added Patrice Dumas' patches for libtool, et cetera. + +2005-07-01 James Gallagher + + Moved the unit tests into the tests subdirectory. + +2005-07-01 James Gallagher + + After adding libtool support, I ran autoreconf --force --install + which in turn ran libtoolize. this updated a number of the support + code for the build. Added ltmain.sh. + +2005-07-01 James Gallagher + + Added. These generated files should be in svn. + +2005-07-01 James Gallagher + + Added support for libtool using a patch from Patrice Dumas. + +2005-07-01 James Gallagher + + Set exec + +2005-07-01 James Gallagher + + ModifiedMakefile.am so that the dejagnu_driver.sh script now works. + +2005-06-30 James Gallagher + + Automake changes; these work and build the tests. the DejaGNU test + support has not been tested yet but the unit tests all run and the + dejagnu drivers all build. + +2005-06-30 James Gallagher + + Switched to automake. Not complete but functional. + +2005-06-30 James Gallagher + + Applied patch from Patrice Dumas for the automake build. + +2005-06-22 James Gallagher + + Added for automake build + +2005-06-22 James Gallagher + + Started work on an automake file for this code; only partly done. + The library builds but nothing else is in place. The grammars are + in progress. They build but the way gse is handled is the the way + they all should be unless I can get automake's lex/yacc rules to + work. See Makefile.am.in_progress and configure.ac.for_am. Also + included in this check in is a patch to libdap.spec from Patrice + Dumas. + +2005-06-22 James Gallagher + + Remove the attempts to utilize automake's support for bison/flex + introduced in the past 9 revisions. + +2005-06-21 James Gallagher + + moved... + +2005-06-21 James Gallagher + + checkpoint + +2005-06-21 James Gallagher + + moved from *.lex files for automake + +2005-06-21 James Gallagher + + Moved from gse.lex for automake + +2005-06-20 James Gallagher + + Fixed the source path. + +2005-06-20 James Gallagher + + Commented out the Patch: and %patch lines since the patched code is + now in svn. + +2005-06-20 James Gallagher + + Accepted patches from Patrice Dumas which + improve the build by using the system's regex library if found. + Also included in the patches were improvements to the rpm spec + file; the file now uses the fedora template and includes a fix for + Mandrake ('Tue' --> 'Tues' in the change log. + +2005-06-15 James Gallagher + + Fixed configure so that it does not include -gstabs on OS/X. + +2005-06-15 James Gallagher + + Changed eol-style, mime-type, other properties. Removed generated + files from svn. Modified configure.ac so that it tests for bison + and does not use bison -y. + +2005-06-15 James Gallagher + + Generated... Should these even be in SCM? + +2005-05-23 James Gallagher + + Removed cast to int at tokenlength = ... to avoid a warning about + differring pointer and int sizes on x86 64-bit CPUs. + +2005-05-23 James Gallagher + + Now uses local common-tests.exp and test.opendap.org. + +2005-05-23 James Gallagher + + Tests now use the local copy of common-tests.exp. + +2005-05-23 James Gallagher + + Reformat using indent and indent-eclipse plugin. + +2005-05-17 Patrick West + + updated dap_version using libdap_version + +2005-05-13 James Gallagher + + 3.5.1 + +2005-05-13 James Gallagher + + 3.5.1 + +2005-05-13 James Gallagher + + checkpoint + +2005-05-12 James Gallagher + + moved + +2005-05-12 James Gallagher + + Renamed libdap-config to dap-config to fall in line more with what + other projects are doing. + +2005-05-12 James Gallagher + + 05/12/05 jhrg + +2005-05-12 James Gallagher + + Modified install so that it no longer appends a version number to the + static library or includes. When we build a dynamic lib, look into the + version number thing then. + +2005-05-11 James Gallagher + + 5/11/05 + +2005-05-10 James Gallagher + + Changed to work with rpm. Don't alter the values of variables like + $includedir because values for these are passed into make by + rpmbuild, overriding the values set by the here. This was + happening with $includedir which had the string '/libdap' appended + early on in the Makefile and then @PACKAGE_VERSION@ appended in + the install-header target. When 'make' was run from the command + line, this worked OK, but not when rpmbuild passed in a new value + for $includedir. + +2005-05-10 James Gallagher + + Added + +2005-05-10 James Gallagher + + 5/10/2005 + +2005-05-10 James Gallagher + + * libdap.spec (Prefix): Built spec file; tested. Some problems + remain, mostly that the curl and xml2 packages are not part of + the RHEL3 dist! I'll have to sort that out before this dist + mechanism becomes a reality. + +2005-04-15 James Gallagher + + * Removed include of config_dap.h from HTTPCache.h. This is the + only header file in the library that includes the config header. + Removing the include means that users of the library never see the + library's internal configuration. There's a potential problem with + this, however, in that users of the library must now use + opendap-config --cflags when building _or_ define HAVE_PTHREADS_H + themselves. I can make this last requirement go away if pthreads + becomes required for libdap++ (then the code won't need the + compile time symbol). + +2005-02-08 James Gallagher + + * Merged with release-3-4-10 + +2005-02-08 James Gallagher + + * bumped the version up to 3.4.10; tagged. + + * System upgrade to FC3/gcc-3.4; minor fixes for the build. + +2005-01-28 James Gallagher + + * Merged with release-3-4-9. + +2005-01-27 James Gallagher + + * Added code to Test* classes so that Sequences are easier to + test. These classes now return values similar to Nathan's DTS code + on the Java module (although not exactly the same). + + * Fixed the way Sequence CEs are handled when Sequences are + nested. Tested to three levels deep. The serialization code in + Sequence is nw quite complex. + + * Incremented version to 3.4.9; I've made some changes since the + last version and I need these in the trunk to test. + +2004-07-07 James Gallagher + + * Merged with release-3-4-8FCS. + +2004-07-06 James Gallagher + + * Bumped the version to 3.4.8. + +2004-07-02 James Gallagher + + * I removed (commented) the pragma interface/implementation lines. + This increases the size of the library by ~1MB, which seems pretty + insignificant at this point. The change means that the code will + compile on HP/UX with gcc 3.2. It _may_ be that gcc 3.4 fixes this + problem, but it seems that the pragmas cause more headaches than + they are worth. It should be easy to 'add them back in' if we + decide to do that, since they are just commented out. + +2004-03-11 James Gallagher + + * Bumped the version to 3.4.7. + + * I fixed a problem in SignalHandler and RCReader. Both od=f these + class had code that re-initialized a pthread_once_t mutex in their + delete_instance() method. This was used by some unit tests to make + a delete instances of these two singleton classes. The problem is + that while this runs (sort of, we had some reports of + crash-on-exit behavior) on Linux, it fails outright on Solaris. As + Rob M points out, it's a non-standard use of pthreads. I removed + the re-init and changed the unit tests. + +2004-02-18 James Gallagher + + * Merge release-3-4-2FCS. + +2004-02-16 James Gallagher + + * Other fixes: + The DBG() macros now include namespace std declarations. + Used valgrind to isolate some memory leaks; others remain and this + will be an on-going process. + Made sure all calls to delete also set the pointer to null; this + fixes some dual-delete bugs. + Some of the doxygen comments have been improved. + Fixed places were the switch from strstream to stringstream was + botched. + + * Fixed the following bugs since version 3.4.5: + 696: Infinite loop in the HDF4 server (w/character variables). + 698: libdap++ now returns better error messages when the httpd + reports an error. + 692: The info response now works. + 695: The client-side cache is more robust. It does not leave cruft + around when a user interrupts a program. + 691: Error responses were not parsed/processed correctly when + received by clients in some circumstances. Now they are. + +2004-01-30 James Gallagher + + * usage.cc: Fixed bug 692. Only one global attribute and one + variable were output. The cause: ends operators left over from the + strstream code... + +2004-01-22 James Gallagher + + * Added namespace std declarations to some files. The DBG() macro + uses cerr and thus the std namespace or std::cerr must be declared + for newer compilers. + +2003-12-08 Yuan Ho + + * Merge release-3-4 into trunk. + +2003-11-10 James Gallagher + + * Added the methods Float32::value() and Float64::value(). I think + the DAP should have a suite of methods which return values w/o + using the awkward void pointer scheme. These methods (will) require + a downcast unlike the void pointer methods. But they wind up being + much easier to use in situations where you know the type of the + BaseType object (which seems to be most of the time, in practice). + +2003-10-03 James Gallagher + + * I changed Connect's request_das, ..., methods so that they now + treat a response that lacks a Content-Description header + identifying the response as one of ours as a fatal error. + + * I Fixed what seems like, in hindsight, a bug. The scanners were + treating illegal characters as non-fatal errors and writing + messages on stderr. In reality the scanners should have been + flagging those responses as bogus. Also, the scanners were using + exit(1) when they found a fatal error; I changed that so they now + throw an Error object. + +2003-09-19 James Gallagher + + * Fixed bug #655. Here's how the DAP library looks for the RC + file: First it checks the env variable DODS_CONF, then HOME. If + neither is set it uses default values and disables the cache. + Here's how DODS_CONF is used: If it is the name of a file that + exists, use that as the RC file. If DODS_CONF names a directory, + look in that directory for a file called '.dodsrc'. If that file + exists, it uses it, otherwise try to create it using default + values *except* that the value of CACHE_ROOT is the directory part + of DODS_CONF with '.dods_cache/' appended. If DODS_CONF is not set + to any value, the code looks at HOME and applies the same rules as + for DODS_CONF (except that HOME should never be the name of a file + so some of the rules don't really apply). We may need to tune this + code for WIN32. + + Note that caching is off by default and a .dodsrc created by the + library will have USE_CACHE set to zero. Users must actively turn + caching on by editing the RC file. + +2003-09-08 James Gallagher + + * HTTPConnect.cc (fetch_url): Fixed bug #661. + +2003-08-29 James Gallagher + + * Added a new property to BaseType which can be used to + differentiate between variables that must be read by a server + because they are in the projection and those which must be read + because they are used in a selection clause or as a function + argument. Previously we were using the send_p property for both + these and it was causing problems when CEs contained both + projected variables and functions (see bug #657). The new property + is called 'in_selection' and is true if the variable is part of + the current CE's selection or is used in any type of CE function. + Servers should ensure that if their read() method implementation + test send_p before reading data they also test in_selection. Data + values should be read it *either* property is true. This fixes bug + #657. + +2003-07-28 James Gallagher + + * Added support for Digest authentication. There's also potential + support for NTLM and GSS-Negotiate authentication, but I don't + have any way to test those. + +2003-07-24 James Gallagher + + * Added a new test driver called server_handler. It's much more + like a server's handler executable; combine it with geturl reading + from stdin to make tests. See also sh-testsuite. Nascent but still + useful. + + * DDS:send() has been incorporated into DODSFilter::send_data() as + part of a refactoring of DDS. + + * Timeouts are now smart. + +2003-07-23 James Gallagher + + * Added a timeout mechanism to the server and modified the Test* + classes so this is easier to test. Right now it's a pretty + rudimentary thing. However, the timeout can be set in the newly + expanded dods.rc configuration file. In the future when we have + reliable Error delivery this code will send an Error object that + explains that there was a time out, et cetera. Check out the + ChangeLog in DODS/etc for information about the expanded dods.rc file. + +2003-05-15 James Gallagher + + * Removed the List class/datatype. We've talked about this for + months (years). Done. + +2003-05-13 James Gallagher + + * Modified DODSFilter so that it takes a new switch, -o, which can + be used to tell a handler which object is to be returned. Handlers + can now be built which return all of the responses; the + DODS_Dispatch CGI module tells the hander which response to + generate. + +2003-04-21 James Gallagher + + * Merged release-3-3-1. + + * The RCReader class now supports using the environment variable + DODS_CONF to point toward a dodsrc file. The value of the envar + supersedes the default ~/.odsrc. + + * In HTTPCache: changed catch(...) clauses so that they explicitly + name exceptions. The code was catching and re-throwing exceptions + and in methods where the types of exceptions were caught this + caused abort to be called, even when the re-thrown exception was + one of the declared types. + + * Several bugs in the grid() function were fixed. First, the + function was not checking to make sure that map vectors using in + the selection sub-expressions passed to grid() actually existed. + Also, the mechanism used to read values of variables used as + arguments to CE functions was broken; constructor types were not + completely read but *were* marked as though they were. Look in + RValue for the latter fix. + + * Some of the unit tests included code that wouldn't compile with + gcc 3.2.x; fixed. I'm now using gcc 3.2.2 for development (but + nightly builds also continue with 2.95.3 and VC++ as well). + + * Added methods to DAS, AttrTable and DDS so that an integer index + can be used to access variables and attributes. This was done so + that the idl command-line client could iterate over instances + without actually having to work with C++ iterator objects. + + * IteratorAdapter is no longer templated. This change was made to + accommodate the MS VC++ compiler. Also, doxygen comments have been + added to the code. + +2003-01-23 + + * Made release 3.3.0. Not merged since there were very, very few + changes. + +2003-01-22 + + * I changed the source files so that they are clearly covered by + the GNU Lesser GPL. + +2003-01-10 + + * NEWS: Added. + + * Merged with changes tagged release-3-2-10 (from the release-3-2 + branch). + +2002-08-08 + + * I Replaced the single VirtualCtors.cc file, which had a virtual + constructor for each of the data type classes in it (see Meyers + for a discussion of virtual ctors), with a set of files with one + virtual ctor per file. The old VirtualCtors.o file was *not* + included in the libdap++ library because of link-time problems it + would introduce. The new files *are* included. For any of the + data type classes you specialize, you must provide a virtual ctor + that returns an instance of your specialization. For any of the + data types you do *not* specialize, the library will use the + default virtual ctor. + + * The library libdap++.a is now `apartment thread safe.' The + phrase comes from Microsoft's documentation; it means that the + library can be used in a limited sense with MT software. The limit + is that objects created using the library cannot be shared by + threads. A MT program can create several instances of Connect, for + example, but each instance can only be used by a single thread. + +2002-06-21 + + * Connect.cc: Fairly extensive additions to Connect's interface. + The old interface is still supported, but its use is deprecated. + + * VirtualCtors.cc: Added this file. It can be linked with clients + so that they don't have to provide definitions for the NewByte(), + ..., NewGrid() 'virutal constructor' functions. This will work + only for those clients that don't need to subclass the various + types. There maybe a better way to do this; either use a real + factory class passed to DDS or by putting each of the New*() + virtual ctors in its own file and adding those to the library. + That way the linker will only extract them if they are needed. + +2002-06-11 + + * expr.y (process_array_indices): Arrays and Grids can no longer + be partially constrained (those constraints didn't work). + +2002-06-10 + + * das.lex: Added '*' to the set of chars allowed in a WORD. Same + for dds.lex. + +2002-06-05 + + * usage.cc: Modified so that option inforamtion passed in by + DODS_Dispatch.pm gets passed onto the filter programs (see bug 453). + + + +2002-06-03 + + * Sequence.cc (print_all_vals): Removed Sequence::level and + set_level() methods and the private _level field. + + * dods-limits.h (DODS_ULONG_MAX): Added U suffix to unsigned + numerical constants. + +2002-05-31 + + * das.y: Bug 450; The reserved word attributes can now be used as + an attribute name. + +2002-05-28 + + * Makefile.in: Geturl is no linked without the Test* classes. This + demonstrates that libdap++ can be used without subclassing. Look + at geturl.cc for an example. Of course, there are limitations on + what you can do without subclassing the data types... + + * geturl.cc: Geturl no longer links with the Test* classes. It was + modified so that it can use the versions of the classes that are + part of libdap++.a (which is a recent change to the library). The + geturl.cc file now contains the `virtual ctors' used by the + library to instantiate the instances of the various variables. + +2002-05-26 + + * Connect.h: Removed code specific to the GUI-based progress + indicator. + + * Error.h: Removed code specific to the GUI-based progress indicator. + +2002-05-22 + + * Byte.h, Byte.cc, ..., Grid.h, Grid.cc: It is no longer necessary + to subclass the data type classes when building a client. + +2002-05-10 + + * Connect.cc (www_lib_init): Joe Sirott found and fixed a bug when + making (many) multiple requests. + +2002-05-09 + + * BaseType.cc (read): This method is not longer abstract. + + * usage.cc (main): Fixed a bug triggered when PATH lacks `.' + +2002-04-30 + + * Merged with release-3-2-9 + +2002-04-03 + + * Makefile.in (binary-tar): Fixed binary-tar target. + + * BaseType.cc: Added using std::endl and ::ends + +2002-04-02 + + * DDS.cc: Fixed broken use of using std::strstream. + + * Connect.cc (server_handler): Fixed bug 416. + +2002-03-27 + + * usage.cc (write_global_attributes): Fixed usage's output of + nested attributes. + +2002-03-12 + + * Bug 400 fixed. + + * Servers now should recognize that a file called `das' in a + directory with data files is an ancillary DAS for any of those + files. If an ancillary DAS exists that has a more speciific name, + that file will be used instead of the directory-wide DAS. + +2001-10-13 James Gallagher + + * Merged with release-3-2-8. + + * Various bug fixes. + + * Connect now uses exceptions to signal errors for all network I/O. + +2001-10-01 James Gallagher + + * Changed serialize and deserialize so that they use exceptions to + signal errors. The return values can be ignored. For backward + compatibility serialize always returns true and deserialize always + returns false (even though that's counter intuitive, that's the + old behavior for `no more data to read'). + +2001-09-28 James Gallagher + + * Merged with release 3.2.7. The next merge should use + release-3-2-7 -j release-3-2. + +2001-09-26 James Gallagher + + * Fixed a bug in the progress indicator's cancel button that + caused a crash. + + * Fixed the grid() server-side function. This function can be used + in constraint expressions to *select* parts of Grid variables + based on the values of their map vectors. Here's an example using + the COADS dataset: The dataset can be found at: + http://dcz.dods.org/dods-3.2/nph-dods/data/nc/coads_climatology.nc. + Look at the DDS and at the Grid SST: + Dataset { + Float64 COADSX[COADSX = 180]; + Float64 COADSY[COADSY = 90]; + Float64 TIME[TIME = 12]; + Grid { + ARRAY: + Float32 SST[TIME = 12][COADSY = 90][COADSX = 180]; + MAPS: + Float64 TIME[TIME = 12]; + Float64 COADSY[COADSY = 90]; + Float64 COADSX[COADSX = 180]; + } SST; + ... + } coads_climatology; + From the attribute object you can find out about the map vector's + ranges: + Attributes { + COADSX { + String units "degrees_east"; + String modulo " "; + String point_spacing "even"; + } + COADSY { + String units "degrees_north"; + String point_spacing "even"; + } + ... + Using the grid function you can ask for the part of SST that falls + between 30 and 50 degrees east and greater than 20 degrees north + using: grid(SST,"30 + + * Sequence::deserialize(...) now reads all the sequence values at + once. Its call semantics are the same as the other classes' + versions. Values are stored in the Sequence object using a + vector for each row (those are themselves held in a + vector). Three new accessor methods have been added to Sequence + (row_value() and two versions of var_value()). + BaseType::deserialize(...) now always returns true. This matches + with the expectations of most client code (the seqeunce version + returned false when it was done reading, but all the calls for + sequences must be changed anyway). If an XDR error is found, + deserialize throws InternalErr. This fixes bug 258. + + +2001-08-21 James Gallagher + + * After running tests for asciival, I decided to allow some more + characters in the CE part of a URL. I created id2www_ce which + allows all the characters in id2www plus [], {}, :, &, <>, = and + , (comma). + +2001-07-27 James Gallagher + + * New escaping semantics. The DAP library now supports escaping + names. Servers (and clients) no longer need to escape characters + themselves. The functions used to escape characters have been + changed, too. The old functions escaped chars that could not be + included in a DAP URL. The new functions only escape chars that + cannot be present in a URI as defined by RFC 2396 (``Uniform + Resource Identifiers (URI): Generic Syntax''). The DAP's scanners + and parsers have been expanded so that many more characters are + now allowed. The regular expressions used to define IDs and NAMEs + are: + ID [a-zA-Z_/%.][-a-zA-Z0-9_/%.#:+\\]* + NAME [a-zA-Z_/%.0-9][-a-zA-Z0-9_/%.#:+\\]* + These changes mean that datasets which contain IDs with spaces + will work. Note that a space is not allowed in a URI so it will be + escaped. If you're typing in a URL to a browser, you must supply + the %20 escape. However, Connect is smart enough to escape the + space if it's present in a CE. On the server-side, DODSFilter and + the type classes remove the escape. + +2001-07-10 James Gallagher + + * If the `read from a file or stdin' feature of geturl is used and + the input source cannot be opened, an error message is printed. + This fixes a bug where a null input data source pointer caused a + segmentation violation. I also added a throw to + Connect::read_data() so that other clients that don't test the + data source pointer (which is a FILE *) will get an exception (as + opposed to a core file). + +2001-06-22 James Gallagher + + * I added `#' to the set of characters allowed in IDs. See bug 179. + + * I normalized the definitions of ID, FLOAT and NEVER in the four + main scanners in the DAP library. They now all share the same set + of characters except for a few odd outliers. + +2001-06-18 James Gallagher + + * Fixed a bug that crept into the processing of the server's + version number information in the (client) class DataDDS. This + made it impossible for clients to process Sequences sent from + new servers because they we using the old (default) + deserialization scheme. + +2001-06-15 James Gallagher + + * Merged with release-3-2-4. The next merge should use -j + release-3-2-4 -j release-3-2. + +2001-06-05 James Gallagher + + * das.y: Changed the way errors are handled. Many errors still + cause exceptions to be thrown. However, if an attribtue's value is + wrong (e.g., a floating point value that is out of range), rather + than throw an exception a note in the form of a container and + explanation are added to the attribute table where the error + occurred. This is particularly important for attributes like + numerical values since they are often machine-dependent. Here's an + example of a DAS which contains an error: + + Attributes { + a { + Byte size 7, 700; + Float64 type 6.02e400, 3.4; + Float64 type 6.02e400, 2.7, 10e-400; + } + } + + And here is how the DAS looks once it is processed by the + client-side software: + + Attributes { + a { + Byte size 7; + a_errors { + Byte size 700; + String size_explanation `700' is not a Byte value.; + Float64 type 6.02e400, 6.02e400, 10e-400; + String type_explanation `6.02e400' is not a Float64 value., `6.02e400' is not a Float64 value., `10e-400' is not a Float64 value.; + } + Float64 type 3.4, 2.7; + } + } + + * Makefile.in: Added a short script to the Makefile.in so that + when CppUnit stuff is not defined a message is printed. This is + only triggered for the unit_tests targets, so it's really a + reminder for the folks that are doing development. + + * configure.in: Changed so that if CFLGAS/CXXFLAGS are defined + those values are used instead of the defaults. This simplifies + development and debugging because it is easier to get builds + without optimization and with optimal debugging without hand + editing the Makefile every time configure is run. + +2001-05-03 James Gallagher + + * Added Cache-Control: no-cache header for error responses. This + will prevent internal server error responses (which look like + valid responses to the cache) from getting cached. + + * Added support for conditional requests to the DODSFilter class, + the cgi_util functions and the DODS_Dispatch.pm Perl module. + Servers can now see conditional requests as such and respond + sensibly. If a server cannot use the modification time of a file, + then it should subclass DODSFilter and supply different versions + of the last modified time methods. + +2001-04-23 James Gallagher + + * Fixed the unit test targets and variables. These are now + included in the Makefile.in but are commented out. You must + remove the comments to use the target. Also needed is the CppUnit + software which is in CVS in the CppUnit module. + + * Added support for the Last-Modified MIME header. All DODS + servers which use the functions in cgi_util.cc and/or the class + DODSFilter can now easily include the Last-Modified header in + their responses. The only change that servers should make is to + pass the ancillary files location directory to + DODSFilter::send_data(). Use of the send_das() and send_dds() + methods should require no change. In addition, calling the + send_data() method without giving the ancillary files location + will work OK with the exception that ancillary files won't + contribute to the LM computation (as they will for the DAS and DDS + responses). + + Currently the LM computation works only for file-based datasets. + Servers that provide access to other types of data should subclass + DODSFilter and provide alternate implementations of the relevant + methods. + + * Fixed some warnings in Error.cc from g++. + +2001-02-13 James Gallagher + + * Merged authentication changes from the trunk (where that feature + was added) here so that it can be included in 3.2 without too much + confusion. + +2001-02-09 James Gallagher + + * Substantial changes to Connect to support HTTP authentication. + libdap++ now support three ways of supplying credentials: via + Connect's ctor, in a URL (using Netscape's convention and via a + popup. + +2001-01-26 James Gallagher + + * Merged with release-3-2-3. The next merge should use -j + release-3-2-3 -j release-3-2. + + * Tagged release-3-2-3 + +2000-12-06 James Gallagher + + * There was an old problem with libwww which I patched in June or + July which fixed its caching of compressed responses. This patch + has not made it into the regular distribution, so when I upgraded + the patch was lost. I've re-applied the patch to the code in + src/packages/libwww. This, together with the .dodsrc bug fixed + below, fixes reported problems with the caching system (on unix at + least, I don't have any information about the problems on win32). + + * Fixed .dodsrc file reading. Comments at the top of the file + caused the reader to stop processing the file and thus none of the + parameter values were being read. + +2000-12-04 James Gallagher + + * Note that the attribute table tests added are both unit tests + (look at the unit_tests target in the Makefile) and dejaGNU-type + tests. Take a look at AttrTableTest.cc to see how unit tests are + written for a class. + +2000-11-29 James Gallagher + + * Fixed problems with attribute aliases. Added unit tests for + AttrTable. DAS is now a child of AttrTable, so the AttrTable + methods can be used at any level of the DAS object. The DAS + methods still work, but they are now implemented using AttrTable + methods. + +2000-09-21 James Gallagher + + * Merged Jose Garcia's exception handling code. + + * Merging the exception software means that all definitions of the + read() method must change. See BaseType.h for details. + + * Improved error messages returned by the parsers. The DAS and DDS + parsers now include a line number and, in many cases, context, for + each message. + + * Normalized all the #ifndef #endif guards in the header files so + they use the same naming scheme. Added guards around #include + lines for headers to speed compile times (see Lakos). + + * Reorganized the source files so that the CVS logs appear at the + end rather than the start of each file. + +2000-09-11 James Gallagher + + * Added methods to Sequence to provide access to row number + constraint information. These methods (get_starting_row_number, + ...) provide access to the indices given in a CE. Also added is a + method to set these values (until explicitly set the stating + and ending row numbers and the stride are -1). In addition there + is a method to get the current row number (useful when reading + row-by-row with read()) and a method to read the ith row into the + sequence. + + * Added Sequence row number selection. It is now possible to + request the first 10 rows of a sequence, or row 7 through 11 and + to do so using a stride. The notation is the same as for Arrays + and Grids; it uses the square brackets and colon separated + integers for start, stride and stop. + +2000-08-31 James Gallagher + + * Merged with 3.1.10. The next merge should use update -d -j + release-3-1-10 -j release-3-1. + + * Fixed a bug in all the scanners (except the GSE scanner) where + \r characters in text sent from win32-hosted servers caused the + problems. The \r characters are either ignored or treated like + newlines when paired with \n (depending on the circumstances). + +2000-08-29 James Gallagher + + * Merged with 3.1.9. The next merge should use update -d -j + release-3-1-9 -j release-3-1. + +2000-08-25 James Gallagher + + * Fixed a bug in esc2underscore(). The function used a static + instance of Regex; whatever regular expression was passed in on + the first call would be used for all subsequent calls. The same + problem existed for other functions in the file. + +2000-08-02 James Gallagher + + * Merged with 3.1.8. The next merge should use cvs update -j + release-3-1-8 -j release-3-1. + +2000-08-02 James Gallagher + + * Fixed a config_dap.h goof. That header was included by some of + the DAP headers which might, in turn, be included by other + software. The HAVE_CONFIG_H symbol in config_dap.h was confusing + other code (e.g., the Cgicc library). I changed config_dap.h and + its use; the header is now only included by DAP .cc or .c files. + DAP header files which needed config_dap.h for the dods_int32, + etc., typedefs now get those from dods-datatypes.h. Headers which + used config_dap.h for the DVR symbol (which holds the dap version + information) have been rewritten. + + This change means that code which now included config_dap.h should + be rewritten to use the dods-datatypes.h header or otherwise + changed so that config_dap.h is not used. + +2000-06-16 James Gallagher + + * Note: This release was made to patch a bug which shows up in the + version of loaddods that supports loading the DAS into a + structure (version 3.1.5 of loaddods). You need this code to build + that version of loaddods. + + * Fixed the tests so they work on my machine (where I don't have + `.' in my PATH). I also added some `/'s in places where is *seems* + DejaGnu was supplying them before. If extras creep in that should + not e a problem. + + * Fixed (by Ethan) the tar and binary-tar targets. + + * Added a fair amount of instrumentation to the dtors of various + methods. + + * Fixed a bug in DDS. The del_var method must delete the BaseType + before deleting the list element. I also changes from a SLList to + a DLList to get a cleaner delete method. + +2000-06-07 James Gallagher + + * Merged with version 3.1.6. Used cvs update -d -j release-3-1-5 + -j release-3-1. The next merge should use -j release-3-1-6 -j + release-3-1. + + * Added code to Vector so that each element of vector + _vec is explicitly copied and deleted. + + * Fixed bugs in Grid, Structure and Sequence. The methods used to + get the Pix pointing to the first variable would segfault if there + were no variables. Now they return 0. + + * DODSFilter now has a set_ce() method. This means that a server + can easily rewrite the CE it gets without loosing the other + features of DODSFilter. + + * Connect.cc: Fixed bugs with local connections and the libwww and + Gui initializations. + + * Fixed a bug in AttrTable. Container attributes below the top + level were broken in the last release. Changed the get_attr_num() + method so that it returns a sensible value for both containers and + `plain' attributes. Added to struct entry so that it is more a + complete class. Added methods so that operator= and the copy-ctor + really work. The IFREMER server needs these fixes. + + * Added to InternalErr a new ctor which makes it simpler to report + the file name and line number when an error is thrown. + +2000-06-06 James Gallagher + + * Merged changes from Rob's PC port. Used cvs update -j + pc-port-branch. I have done minimal testing on Linux (dejagnu is + not yet installed) and the code seems fine. + +2000-04-17 James Gallagher + + * Fixed two bugs in Connect.cc that caused client sessions which + mixed remote and local connections to fail. + +2000-04-06 James Gallagher + + * Merged with Brent's gui2 code. This version of the progress + popup adds a cancel button. The software was checked in on the + gui2 branch and the point at which it was merged was tagged, on + that branch, as gui2-0. Future merges should use update -j gui2-0 + -j gui2. + +2000-03-31 James Gallagher + + * Merged with release-3-1-5. The next merge should use -j + release-3-1-5 -j release. + +2000-03-28 James Gallagher + + * Look in the Makefile for this software to see the new support + library configure macros in action. These simplify getting the + right libraries on the link line without duplicates (which + lengthen link times). + + * Added libdap++-gui.a to the build/install/tar/binary-tar stuff. + This library is built with `GUI-enabled' versions of Connect and + Error. This is to support our progress indicator and cancel button. + + * Added a new feature to the .dodsrc file and caching. Users can + set the default expiration time for a response using a parameter + in the RC file. By default the expiration time is 24 hours + (written in seconds). + + * Fixed bugs in the caching software. Compressed responses now are + cached correctly and responses with several variables are also + correctly handled. + +2000-03-31 James Gallagher + + * escaping.cc: Fixed a bug where an int was used to index into a + string object. + + * parser-util.cc (check_float32): Float32, Float64 attributes no + longer check ranges. + +2000-03-20 James Gallagher + + * parser-util.cc (check_byte): Byte attributes can now be either + signed or not (i.e., their range is -128 to 255). This is to + support clients that assume bytes to be signed as well as those + that assume bytes to be unsigned. + +2000-01-26 James Gallagher + + * Merged with release3-1-4. The next merge should use: update -j + release-3-1-4 -j release-3-1. + +2000-01-26 James Gallagher + + * Fixes for Red hat 6.x (from Ethan). + + * Fixed the use of the stdc++ library's string::find() method. Its + return type was assigned to an unsigned int in many places. On 64 + bit machines this caused problems. The type now used is + string::size_type (although size_t also works). + +2000-01-24 James Gallagher + + * Fixed a bug in the DAS and DDS parsers (das.y, dds.y). These + parsers used static global C++ objects which require that their + constructors run before main(). However, when the dap library is + linked by a linker that is not aware of this (e.g., Sun's ld, when + using GNU C++) those constructors are never called and accessing + those objects causes a segmentation fault. Fixing this problem + might fix undiscovered bugs in netCDF clients and definitely fixes + problems with the nascent loaddods rewrite (where loaddods gets + rewritten in C++). + +1999-12-31 James Gallagher + + * Added Gui (progress indicator) code back into the core. The + interpreter is now embedded in DODS. + +1999-12-14 James Gallagher + + * Fixed a bug in Connect where once the Cache was closed it would + never be opened again. + + * Fixes/improvements for the caching code. The code now correctly + handles the case where many URLs are opened using Connect objects + created both sequentially and in an interleaved fashion. The Cache + index is now updated after each URL request. The Cache is now + closed only after all connect objects are destroyed. + + * Connect.h: Added static members to track use of Connect objects + in conjunction with the new caching code. + +1999-11-04 James Gallagher + + * Merged: cvs update -j release-3-1-2 -j release-3-1, the next + merge command should use -j release-3-1-3 -j release-3-1. + +1999-11-03 James Gallagher + + * Fixed the usage filter (nothing to do with the comment about a + function of the same name in cgi_utils.cc). The filter was slurping + entire datasets because it failed to append the .html suffix when + looking for user-defined documentation about a dataset. + + * DDS::send now catches exceptions thrown by the expression + evaluator. This means more reliable handling of errors from within + server-side function. However, this is only a stop-gap solution to + the general problem of poor error processing within the library. + Look for a *real* solution to this problem in about 2 months. + + * Fixed a range-check bug for unsigned 16-bit integers. The values + were being tested as if they were signed. + + * Removed usage() from cgi_utils.cc since it was not used and was + causing conflicts with servers which defined a function with the + same name. + + * Documentation updates. + + * Substantial changes to the Makefile.in. See the top level ChangeLog. + +1999-08-27 James Gallagher + + * Fixed the value of DODS_SHRT_MIN in dods-limits.h + + * Now compiles with gcc 2.95 + +1999-05-27 James Gallagher + + * Misc bug fixes. + + * Error and InternalErr objects now are better behaved and DDS + throws them more predictably (although there's still a lot of work + to do there). + + * Fixes merged from the alpha-3-0-0 branch. + +1999-05-24 James Gallagher + + * The DODS support address is now correct in all the places in the + dap. + + * Fixed some bugs that arose because C and C++ I/O streams may no + longer be synchronized. + + * Dan removed the old GUI code. See the comments in Connect.cc. + +1999-05-19 James Gallagher + + * The dap++ library now builds using -fPIC (position independent + code) so sharable libraries should be easier to make. + + * In DODS_Dispatch.pm, variables are checked for odd characters + that might be used to embed shell commands. + + * Fixed security holes in the DODS_Dispatch.pm code. It now + returns the command as elements of an array which can be passed to + the exec() thus by passing the shell. + + * Fixed all occurrences of the support email address to be the new + address. + +1999-05-04 James Gallagher + + * Added to DODSFilter and DODS_Dispatch.pm so that server version + number information can be passed from the CGI script to the core + software. Now when you ask for the version of a server, you will + get the server's version information in addition to the core's. In + addition, the XDODS-Server header that is returned with every + response will now contain the server's version number. This + assumes that the server's dispatch script initializes + DODS_Dispatch correctly. + + * Added InternalErr class. This class should be thrown by any code + that finds an internal error (i.e., the sort of thing that might + otherwise be trapped by an assert). + + * Committed changes that form the base of version 3. This combines + the new simple types with a reduced dependence on the old libg++ + code and misc. fixes. + +1999-04-21 James Gallagher + + * Small fix in prune_spaces(). This fixed a problem in the mexcdf + client (and presumably other netcdf or matlab programs to). + +1999-04-14 James Gallagher + + * Fixed a bug in Connection::del_connect() where an assert tested + false for a valid condition. It is OK for the conns[i] element to + be NULL when this method is run. + + * Fixed a bug in Connect where the member _tv would be deleted + without being first allocated when local files are accessed. This + member is no longer needed (it was used by libwww 5.0). + +1999-04-09 James Gallagher + + * This release is a special ferret-only release. It is a stable + alpha version of what will be DODS 3.0 once some more features are + added. + +1999-03-24 James Gallagher + + * Added support for the new Int16, UInt16 and Float32 types. + +1999-03-22 James Gallagher + + * Added the Int16, UInt16 and Float32 datatypes to attributes. See + the tests numbered test.18--20. + +1999-03-19 James Gallagher + + * Added a call to setprecision(15) in geturl. This makes it so + that floating point numbers will print with the full range or + precision. It might be that I have to move this when 32 bit floats + are added because they have lower precision (8). However, setting + the precision at this level (once, before the calls to print_val() + start) is probably more efficient. + +1999-03-17 James Gallagher + + * Changed cgi_util.cc:find_ancillary_file() so that it looks for + .ext in addition to .ext and the other + variations. This means that the current convention of naming + ancillary DAS files .das will work for both the + DODS_Date(), etc., functions (whose Factory classes were using + this convention) and the regular DAS filter program. + +1999-02-24 James Gallagher + + * An interim revision changed the way incoming data documents were + processed so that the DDS contained in the data document did not + have to copied to a separate file before being processed. However, + this broken programs that read data docs with a pipe. This problem + has been fixed. The document's DDS still is not copied, but + programs like asciival that read from stdin via a pipe now work. + +1999-01-21 James Gallagher + + * Added the Grid Selection function. Grid variable may now be + `selected' based on the values of the Map vectors (since this a + constraint that returns data based on those data's values, I'm + calling it a selection operation). The syntax looks like: + grid(, , , ..., ). An + example: grid(dsp_band_1, "29.7 < lat <= 34.8", "lon > -70.9", " + -60 < lon"). This deliberately shows different forms the + `expressions' can take. Note that these expressions can only name + the grid's map vectors; they are not general expressions. Also + note that the function appears in the projection part of a CE. + +1999-01-15 James Gallagher + + * Changed the way the expression and DDS parsers work. Now the + expression scanner/parser can process strings directly. This means + that expression parsing is cleaner, somewhat faster and more + reliable since a temporary file is no longer created as part of + the parse process. Similarly, the DDS parser no longer requires + that a temporary file be created to parse the DDS bound to a data + document. + +1998-11-23 James Gallagher + + * Fixed a bug in DAS where the destructor would free memory that + was just garbage. I replaced the old genclass-based DASVHMap code + with an SLList of structs. + +1998-11-11 James Gallagher + + * Fixes for various memory leaks found while working on the FF + server. + +1998-11-05 James Gallagher + + * Fixed error messages for CEs involving arrays. When an array + name was misspelled in a CE you got no error message, now you do. + +1998-10-21 James Gallagher + + * Added projection functions to the CE syntax. These functions are + executed *during* the parse of the projection clause. They can be + used to add new (synthetic) variables to the DDS. + +1998-10-14 James Gallagher + + * expr.y: Added parse of single array indeces to the projection + list in a CE. i[3] asks for the fourth element of the array i. The + old syntax i[3:3] still works. + +1998-09-17 James Gallagher + + * The release (version 2.18) is the last that will use String.h. + WRONG! 11/11/98 jhrg + +1998-09-03 James Gallagher + + * Feature: CEs no longer require fully-qualified names for fileds + of ctor types. + + * Makefile.in (clean): Added GNU to the list of directories + cleaned. This prevents sending out .o files with the source code. + +1998-04-07 James Gallagher + + * Fixed Connect: leading spaces in a URL and/or CE caused either a + crash or other problem. + +1998-04-03 James Gallagher + + * Added patches from Jake Hamby which fix various problems with + Sequences. + +1998-03-27 James Gallagher + + * Added targets for test coverage analysis. + + * Added fix from Jake Hamby. + + * Makefile.in (GNUHDRS): Added String.h & String.cc to the + distributions. + +1998-03-19 James Gallagher + + * Added error messages when array indices are out of range. + + * Fixed yesterday's fix... Error objects are no longer compressed. + I moved compression into DDS::send (from DODSFilter::send_data) + and structured send() so that the mime header generators no longer + use a hack to write themselves without compression. Previously + set_mime_*() used to `write past' the compressor process and this + caused synchronization problems - which I tried to fix yesterday. + Now the headers are written and then the compressor is opened. + +1998-03-18 James Gallagher + + * Fixed an ugly bug where Error objects were compressed (sometimes + they still are) but the return MIME document header didn't say so. + Now the return document header says if the Error object is + compressed. Note that compressing Error objects is not going to + save any (real) space but once the compressing data sink is set up + *whatever* gets written to it will be compressed... + +1998-03-17 James Gallagher + + * Added the ASCII filter to the set of things that comprise a DODS + server. Use .ascii or .asc as a URL suffix to get comma-separated + ASCII data back from a DODS server. + + * Added boolean member functions is_simple_type(), + is_vector_type() and is_constructor_type(). This reduce the size + of various parts of the code (but I have not gone through and + replaced all the old code yet). + + * Added some new mfuncs to the core. element_count() returns the + number of elements in a constructor type (one for a simple type). + +1998-03-09 James Gallagher + + * Removed the -fno-rtti flag from the DAP library build. The + library needs RTTI so that software written using exceptions can + catch derived exception classes. + +1998-02-11 James Gallagher + + * Added the DODS_Cache.pm class (which was in the hdf-dods server + code only). This Perl module is not installed as part of the + default stuff in DODS_ROOT/etc. + + * Added support for compression. Servers recognize the + Accept-Encoding header in a request document. when the value of + that header is `deflate' the zlib LZW algorithm is used to + compress data. Clients automatically respond to document that + contain the header Content-Encoding: deflate by un compressing the + data (a new feature of libwww 5.1). The new servers are compatible + with old clients and the new clients are compatible with old + servers (although neither combination can use compression). + + * Added a help option to the servers (via an addition in + DODS_Dispatch.pm). Use this with the `file name' /help or the + extension .help. + +Tue Dec 16 00:32:31 1997 James Gallagher + + * Added DODS_Dispatch.pm perl class. This class centralizes a lot + of the code that used to be replicated in each of the perl scripts + used to dispatch the various `methods' of a DODS server. + +Mon Dec 15 17:26:37 1997 James Gallagher + + * Many small bug fixes that were fixed before being added to the + TODO list. + + * Added DataDDS class which makes the version number read by + Connect available to any member function that can access a DDS + pointer (since DDS *s can reference DataDDS objects). + + * Added the DODSFilter class to simplify writing servers that + conform to a set of standards Re: the placement and naming of + ancillary files, version numbers, etc. + + * Added new Sequence processing code along with backward + compatibility. Nested sequences are sent more efficiently. In + addition, sequences may be followed by other variables in a Data + object (an old bug). + + * Added new characters to those allowed in DDS NAME lexeme. This + means that a file with _ and . in the name will parse. + +Wed Aug 20 14:12:54 1997 James Gallagher + + * Added newline after data in process_data() in geturl.cc. + +Thu Jun 5 16:08:10 1997 James Gallagher + + * See TODO for a list of reported bugs fixed and outstanding. + + * Changed the way request_data works so that Connect can now be + used to create clients that read not only from the network but + also from standard input. + + * Wrote DODS_Dispatch.pm Perl `class' that can be used to write + the nph-* dispatch scripts. This should simplify maintenance of + those scripts since they was a lot of replication of code from one + script to another. Now must of the real functionality is + concentrated in the one file. + + * When projections on Grids result in an object that is no longer + a `true' Grid (no longer satisfies Grid::check_semantics()) then + send the resulting variables as a Structure of Arrays or a simple + array (the latter if only one array remains after applying the + projection to the Grid. + + * Changed the way servers report the version number of the core + software. The message is now generated by the *_dods data filter + program using its -v option (all the data filter programs + support this option). This gets rid of the dods-core-version perl + script. + + * Changed geturl so that it can read from stdin (similar to + writeval). + + * Fixed bugs in util.cc. + +Wed May 21 22:25:41 1997 James Gallagher + + * See the TODO list for information on bugs found and fixed. + + * Added DAS aliases and hierarchies. + +Sun Mar 23 17:01:12 1997 James Gallagher + + * Fixed the nasty decompression bug where child process exit + status was never caught. This caused process tables to fill, etc. + +Thu Mar 6 21:14:38 1997 James Gallagher + + * See TODO list for bugs fixed. + +Sun Feb 9 18:40:25 1997 James Gallagher + + * See TODO for a list of bugs fixed. + +Thu Feb 6 16:17:06 1997 James Gallagher + + * Changed the return type of request_data() from DDS & to DDS *. + Also changed the return types of append_constraint() and + constraint_dds() from DDS & to DDS *. When request_data() + encounters an error it no longer exits; now it returns NULL. + +Wed Dec 18 11:18:05 1996 James Gallagher + + * Version 2.10 prepared + +Wed Dec 11 20:15:10 1996 James Gallagher + + * Created the Usage server. + + * Fixed bugs in cgi_util.cc - error Content-Description items were + not processed. + +Tue Dec 3 09:43:02 1996 James Gallagher + + * Added to DDS::parse_constraint() two new variables: ostream os + and bool server. These are used to control processing of error + messages that are returned from the CE parser. Rather than have + the CE parser write all its error messages to stderr, the parser + now returns an error object. The new code in parse_constraint() + ensures that an `error' header is written to OS and that the error + object is serialized if the member function is run from within a + server. Since parse_constraint() is called from within + DDS::send(), users of the library don't need to do anything + different unless they have coded their own versions of send(). + + * Added code to ensure that variables can be used in the selection + part of a constraint expression without having to be also in the + projection part. This means that a CE can be used to request a + single variable (e.g., the sequence `s') but select by comparing + to other variables in the data set. + +Mon Dec 2 11:50:29 1996 James Gallagher + + * Added three new versions of int_ops to process all the possible + permutations of signed and unsigned integer operands. Ignore the + warnings in util.cc about comparisons between signed and unsigned. + + * Added cases for unsigned integer variables in the Vector, Byte, + Int and Float classes. Also added cases in BaseType and some of + the Test* classes. This fixes problems with the processing of + this type. + +Wed Nov 27 11:02:44 1996 James Gallagher + + * Added DDS as a third argument to the user-defined functions in + the CE evaluator. This means that a function (which may be server + specific) may look at the value of any variable in the dataset, + not just those values passed as parameters. This will facilitate + creation of functions which use implicit relations between + variables when computing values or performing selections (e.g., + when geo-location information depends on the implicit relation + between two or more arrays). + + * Changed the way Makefile dependencies are processed so that + Makefile (re)generation is faster (since dependencies rarely + change). + +Sun Nov 24 19:45:22 1996 James Gallagher + + * Tested the library code in a number of situations and fixed many + little bugs. + + * The asynchronous mode for data transfers is broken and is never + used. + + * Significantly improved processing of errors - web errors are now + handled correctly for the DAS and DDS - alas the data will still + exit when an error is encountered. Note that to fix this problem + I'll need to use exceptions *or* change the return type of the + request_data() member function to DDS * (from DDS &). Either one + will break everyone's code... + + * Fixed the lame-looking progress indicator. + + * dods_root is no longer a global variable that depends on run + time initialization from g++; now it is a function. This ensures + that the core software can be linked to program core using linkers + other than g++. + + * Added better compressor function - look at jg_dods.cc to see how + it is used. + + * Fixed broken decompression. + +Wed Nov 13 10:43:56 1996 James Gallagher + + * Added error MIME headers to the set that can be generated by + helper functions in cgi_util.cc. The error headers are necessary + since DODS now must generate all its own MIME headers (see the + section on NPH CGIs in the CGI spec). MIME error headers should be + used when an http or www error has been detected - don't use the + DODS error objects in those cases. + + * Made changes in the Int data types so that expressions with + UInt32s should work. + + * Fixed some problems with the GUI. It should work more reliably + but I doubt that all the problems have been fixed. Needs testing. + + * Now reports most www/http errors, although some clients still + can't seem to get the GUI to work (e.g., writeval) even though + geturl can. + + * Now requires version 5.0a of the WWW Library. + + * Many changes based on feedback from the initial version 2.09 + distribution. + +Tue Oct 29 08:23:30 1996 James Gallagher + + * Added UInt32 (32 bit unsigned integers) to the DAP. This + includes both using them in the DDS and the DAS. When an UInt32 is + used as an operand in a CE, the other operand is cast to unsigned + for the comparison operation. + +Fri Oct 18 09:35:07 1996 James Gallagher + + * Fixed various errors in the new Unsigned int classes. + + * Changed the operation of Connect::request_das() and + request_dds(). They now pass any initial constraint along to the + respective servers. An initial constraint is one which is supplied + along with the URL at the time the Connect object is made. + + * The functions set_mine_{text,binary} now produce complete + HTTP/MIME headers. Thus DODS servers can now be invoked using the + `nph' feature of CGI 1.1 (done by naming the DODS server dispatch + script `nph-*'). This solve two problems which appeared with large + data transfers: Transfers were cut-off before completion when a + server-determined timeout period elapsed and 2) Some large + transmissions had an extra byte added into the data stream. + +Tue Oct 8 09:23:57 1996 James Gallagher + + * Modified geturl so that CEs can be passed in using ? in the URL + in addition to the program's -c option. + + * Added `%' to the set of characters allowable in identifiers. + + * Added code so that a constraint expression appended to a URL is + properly handled. The CE is stored in in the Connect object. When + a CE is passed to the request_data member function, Connect + correctly combines the projection and selection parts of that CE + with the matching parts of any initial CE. + + * Fixed up the grammar files for Bison 1.25 + +Thu Sep 12 14:05:49 1996 James Gallagher + + * Added a new option to geturl; -D which can be used to get data. + + * Fixed Array::print_array; used indirectly by the new option to + geturl. + +Mon Aug 26 13:18:21 1996 James Gallagher + + * Fixed various configure.in files so that the different size ints + are defined properly. + + * Fixed up the config_dap.h problem in the jg-dods directory. + + * Added classes for 32 bit floats, 32 bit unsigned ints and 16 bit + signed and unsigned ints. These are *not* built by default - I'll + let the various users have a chance to include these new classes in + their software before they are added to the build (and thus must + be sub-classed by users of the DAP). + +Fri Aug 16 09:34:00 1996 James Gallagher + + * Fixed configure tests for sem.h prototypes and the semctl union. + A new test which scans the sem.h header is in aclocal.m4 (in etc) + and the configure script uses that test. + +Tue Aug 13 10:54:02 1996 James Gallagher + + * Fixed bugs in Grid: bad constraint expressions would cause core + dumps and a bug on the SGI (maybe others) where valid Grids would + not parse. + + * Added to the Error class so that its methods are more useful + for correcting errors and displaying messages (see Error.cc). + + * Switched to parser_arg object on all the parsers. parser_arg is + a simple object used to pass values to and from the bison gnerated + parsers. You need bison 1.24 or newer. + + * Added _unused_ to config_dap.h; this macro expands to gcc's + __attribute__((__unused__)) thing. I then tacked `__unused__' on the + char rcsid[] arrays to suppress warnings from gcc -Wall (and + highlite the warnings that should be fixed!) + + * Fixed a bug in Clause where non-existent functions were + evaluated - this caused a core dump. + +Fri Aug 9 10:21:06 1996 James Gallagher + + * Fixed bug in Connect where local accesses were not correctly + detected. Also modified geturl to print a diagnostic when supplied + with a local file name (or, more to the point, a name without + `http'). + +Wed Jul 17 15:04:01 1996 James Gallagher + + * Added expr-check target in Makefile. + + * Added new tests for das, dds objects. Added tests for expr-test + driver. + +Mon Jul 8 16:25:11 1996 James Gallagher + + * Gui.cc: Added compile-time switch HAVE_EXPECT which is used to + control use/requirement of expect and tcl. When defined as 1 Expect + and Tcl are used to communicate with a GUI subprocess. When 0, + Expect and Tcl are not used (thus the libraries are not required). + Note that without Expect the GUI used for the transmission + progress indicator and error reporting is not available. + +$Id$ + diff --git a/Clause.cc b/Clause.cc new file mode 100644 index 0000000..77a6b45 --- /dev/null +++ b/Clause.cc @@ -0,0 +1,249 @@ + +// -*- mode: c++; c-basic-offset:4 -*- + +// This file is part of libdap, A C++ implementation of the OPeNDAP Data +// Access Protocol. + +// Copyright (c) 2002,2003 OPeNDAP, Inc. +// Author: James Gallagher +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// +// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112. + +// (c) COPYRIGHT URI/MIT 1996,1998,1999 +// Please first read the full copyright statement in the file COPYRIGHT_URI. +// +// Authors: +// jhrg,jimg James Gallagher + +// Implementation for the CE Clause class. + + +#include "config.h" + +#include +#include + +#include "expr.h" +#include "Byte.h" +#include "Int16.h" +#include "UInt16.h" +#include "Int32.h" +#include "UInt32.h" +#include "DDS.h" +#include "Clause.h" + +using std::cerr; +using std::endl; + +namespace libdap { + +Clause::Clause(const int oper, rvalue *a1, rvalue_list *rv) + : _op(oper), _b_func(0), _bt_func(0), _argc(0), _arg1(a1), _args(rv) +{ + assert(OK()); +} +#if 1 +Clause::Clause(bool_func func, rvalue_list *rv) + : _op(0), _b_func(func), _bt_func(0), _argc(0), _arg1(0), _args(rv) +{ + assert(OK()); + + if (_args) // account for null arg list + _argc = _args->size(); + else + _argc = 0; +} +#endif +Clause::Clause(btp_func func, rvalue_list *rv) + : _op(0), _b_func(0), _bt_func(func), _argc(0), _arg1(0), _args(rv) +{ + assert(OK()); + + if (_args) + _argc = _args->size(); + else + _argc = 0; +} + +Clause::Clause() : _op(0), _b_func(0), _bt_func(0), _argc(0), _arg1(0), _args(0) +{} + +static inline void +delete_rvalue(rvalue *rv) +{ + delete rv; rv = 0; +} + +Clause::~Clause() +{ + if (_arg1) { + delete _arg1; _arg1 = 0; + } + + if (_args) { + // _args is a pointer to a vector and we must must delete + // each rvalue pointer here explicitly. 02/03/04 jhrg + for_each(_args->begin(), _args->end(), delete_rvalue); + delete _args; _args = 0; + } +} + +/** @brief Checks the "representation invariant" of a clause. */ +bool +Clause::OK() +{ + // Each clause object can contain one of: a relational clause, a boolean + // function clause or a BaseType pointer function clause. It must have a + // valid argument list. + // + // But, a valid arg list might contain zero arguments! 10/16/98 jhrg + bool relational = (_op && !_b_func && !_bt_func); +#if 1 + bool boolean = (!_op && _b_func && !_bt_func); +#endif + bool basetype = (!_op && !_b_func && _bt_func); + + if (relational) + return _arg1 && _args; + else if (boolean || basetype) + return true; // Until we check arguments...10/16/98 jhrg + else + return false; +} + +/** @brief Return true if the clause returns a boolean value. */ +bool +Clause::boolean_clause() +{ + assert(OK()); + + return _op || _b_func; +} + +/** @brief Return true if the clause returns a value in a BaseType pointer. */ +bool +Clause::value_clause() +{ + assert(OK()); + + return (_bt_func != 0); +} + +/** @brief Evaluate a clause which returns a boolean value + This method must only be evaluated for clauses with relational + expressions or boolean functions. + + @param dds Use variables from this DDS when evaluating the + expression + + @return True if the clause is true, false otherwise. + @exception InternalErr if called for a clause that returns a + BaseType pointer. */ +bool +Clause::value(DDS &dds) +{ + assert(OK()); + assert(_op || _b_func); + + if (_op) { // Is it a relational clause? + // rvalue::bvalue(...) returns the rvalue encapsulated in a + // BaseType *. + BaseType *btp = _arg1->bvalue(dds); + // The list of rvalues is an implicit logical OR, so assume + // FALSE and return TRUE for the first TRUE subclause. + bool result = false; + for (rvalue_list_iter i = _args->begin(); + i != _args->end() && !result; + i++) { + result = result || btp->ops((*i)->bvalue(dds), _op); + } + + return result; + } + else if (_b_func) { // ...A bool function? + BaseType **argv = build_btp_args(_args, dds); + + bool result = false; + (*_b_func)(_argc, argv, dds, &result); + delete[] argv; // Cache me! + argv = 0; + + return result; + } + else { + throw InternalErr(__FILE__, __LINE__, + "A selection expression must contain only boolean clauses."); + } +} + +/** @brief Evaluate a clause that returns a value via a BaseType + pointer. + This method should be called only for those clauses that return values. + + @param dds Use variables from this DDS when evaluating the + expression + @param value A value-result parameter + + @return True if the the BaseType pointer is not null, false otherwise. + @exception InternalErr if called for a clause that returns a + boolean value. Not that this method itself \e does return a + boolean value. */ +bool +Clause::value(DDS &dds, BaseType **value) +{ + assert(OK()); + assert(_bt_func); + + if (_bt_func) { + // build_btp_args() is a function defined in RValue.cc. It no longer + // reads the values as it builds the arguments, that is now left up + // to the functions themselves. 9/25/06 jhrg + BaseType **argv = build_btp_args(_args, dds); + + (*_bt_func)(_argc, argv, dds, value); + + delete[] argv; // Cache me! + argv = 0; + + if (*value) { + // FIXME This comment is likely wrong... 10/19/12 + // This call to set_send_p was removed because new logic used + // in ResponseBuilder will handle it. See send_data(), ... + // When the second part of the CE is parsed, if it is null, + // then all the variables in the DDS that holds the function + // result variables will be sent. If there's a projection in + // that second CE, it will denote what is to be sent. Setting + // set_send_p(true) here had the affect of overriding that + // second CE. Note, however, that the code in send_data() clears + // all of the send_p properties for variables in the DDS, so + // removing the call here is just removing something that will + // actually have no affect. jhrg 10/19/12 + (*value)->set_send_p(true); + (*value)->set_read_p(true); + return true; + } + else { + return false; + } + } + else { + throw InternalErr(__FILE__, __LINE__, + "Clause::value() was called in a context expecting a BaseType pointer return, but the Clause was boolean-valued instead."); + } +} + +} // namespace libdap diff --git a/Clause.h b/Clause.h new file mode 100644 index 0000000..9b4d11c --- /dev/null +++ b/Clause.h @@ -0,0 +1,131 @@ + +// -*- mode: c++; c-basic-offset:4 -*- + +// This file is part of libdap, A C++ implementation of the OPeNDAP Data +// Access Protocol. + +// Copyright (c) 2002,2003 OPeNDAP, Inc. +// Author: James Gallagher +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// +// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112. + +// (c) COPYRIGHT URI/MIT 1995-1999 +// Please first read the full copyright statement in the file COPYRIGHT_URI. +// +// Authors: +// jhrg,jimg James Gallagher + +// Interface for the CE Clause class. + +#ifndef _clause_h +#define _clause_h + + +#ifndef _expr_h +#include "expr.h" +#endif + +#ifndef _rvalue_h +#include "RValue.h" +#endif + +namespace libdap +{ + +/** The selection part of a a DAP constraint expression may contain one or + more clauses, separated by ampersands (\&). This is modeled in the DDS + class structure as a singly-linked list of Clause objects. In addition, a + constraint expression may be a single function call, also represented in + the DDS using an instance of Clause. + + Each clause object can contain a representation of one of three + possible forms: + +
    + +
  1. A relational clause, where an operator tests the relation + between two operands. This kind of clause evaluates to a boolean + value. For example: a > b. + +
  2. A boolean function, where some function operates on + arguments in the clause to return a boolean value. For example, + consider a scalar A and a list L. The clause find(A,L) might + return TRUE if A is a member of L (if the find() function is + defined). + +
  3. A clause that returns a pointer to a DAP BaseType value. + This is a clause that evaluates to some data value (be it scalar + or vector). For example, sig0() might be included in the + constraint expression parser to calculate density from pressure, + temperature, and salinity. In this case, sig0(p,t,s) would be a + clause that evaluates to a data value. + +
+ + This might be a bit confusing; in the first, and by far more common, form + of constraint expressions (CEs) only the first two types of clauses may + appear. In the second form of the CE only the last type of clause may + occur. The Clause class, however, can store them all. + + The Clause object holds the constraint expression after it + has been parsed. The parser renders the relational operator into + an integer, and the functions into pointers. + + @brief Holds a fragment of a constraint expression. + @see DDS::parse_constraint */ +struct Clause +{ + +private: + /** The relational operator, if any. */ + int _op; + /** A pointer to a valid boolean function. */ + bool_func _b_func; + + /** A pointer to a valid function that returns a pointer to a + BaseType. */ + btp_func _bt_func; + + int _argc; // arg count + rvalue *_arg1; // only for operator + rvalue_list *_args; // vector arg + + Clause(const Clause &); + Clause &operator=(const Clause &); + +public: + Clause(const int oper, rvalue *a1, rvalue_list *rv); + Clause(bool_func func, rvalue_list *rv); + Clause(btp_func func, rvalue_list *rv); + Clause(); + + virtual ~Clause(); + + bool OK(); + + bool boolean_clause(); + + bool value_clause(); + + bool value(DDS &dds); + + bool value(DDS &dds, BaseType **value); +}; + +} // namespace libdap + +#endif // _clause_h diff --git a/Connect.cc b/Connect.cc new file mode 100644 index 0000000..36a6213 --- /dev/null +++ b/Connect.cc @@ -0,0 +1,1207 @@ +// -*- mode: c++; c-basic-offset:4 -*- + +// This file is part of libdap, A C++ implementation of the OPeNDAP Data +// Access Protocol. + +// Copyright (c) 2002,2003 OPeNDAP, Inc. +// Author: James Gallagher +// Dan Holloway +// Reza Nekovei +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// +// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112. + +// (c) COPYRIGHT URI/MIT 1994-2002 +// Please read the full copyright statement in the file COPYRIGHT_URI. +// +// Authors: +// jhrg,jimg James Gallagher +// dan Dan Holloway +// reza Reza Nekovei + +#include "config.h" + +//#define DODS_DEBUG +#define FILE_UN_MARSHALLER 1 + +#include +#include + +#include +#include + +#include "debug.h" +#include "DataDDS.h" +#include "Connect.h" +#include "escaping.h" +//#include "RCReader.h" +#include "DDXParserSAX2.h" +#if FILE_UN_MARSHALLER +#include "XDRFileUnMarshaller.h" +#else +#include "fdiostream.h" +#include "XDRStreamUnMarshaller.h" +#endif +#include "mime_util.h" + +using std::cerr; +using std::endl; +using std::ifstream; +using std::ofstream; +using std::min; + +namespace libdap { + +/** This private method process data from both local and remote sources. It + exists to eliminate duplication of code. */ +void Connect::process_data(DataDDS &data, Response *rs) +{ + DBG(cerr << "Entering Connect::process_data" << endl); + + data.set_version(rs->get_version()); + data.set_protocol(rs->get_protocol()); + + DBG(cerr << "Entering process_data: d_stream = " << rs << endl); + switch (rs->get_type()) { + case dods_error: { + Error e; + if (!e.parse(rs->get_stream())) + throw InternalErr(__FILE__, __LINE__, "Could not parse the Error object returned by the server!"); + throw e; + } + + case web_error: + // Web errors (those reported in the return document's MIME header) + // are processed by the WWW library. + throw InternalErr(__FILE__, __LINE__, + "An error was reported by the remote httpd; this should have been processed by HTTPConnect.."); + +#if 0 + // This code triggers a security warning from Coverity; since it is not used, + // I have removed it. jhrg 5/5/16 + case dods_data_ddx: { + // Parse the DDX; throw an exception on error. + DDXParser ddx_parser(data.get_factory()); + + // Read the MPM boundary and then read the subsequent headers + string boundary = read_multipart_boundary(rs->get_stream()); + DBG(cerr << "MPM Boundary: " << boundary << endl); + read_multipart_headers(rs->get_stream(), "text/xml", dods_ddx); + + // Parse the DDX, reading up to and including the next boundary. + // Return the CID for the matching data part + string data_cid; + ddx_parser.intern_stream(rs->get_stream(), &data, data_cid, boundary); + + // Munge the CID into something we can work with + data_cid = cid_to_header_value(data_cid); + DBG(cerr << "Data CID: " << data_cid << endl); + + // Read the data part's MPM part headers (boundary was read by + // DDXParse::intern) + read_multipart_headers(rs->get_stream(), "application/octet-stream", dap4_data, data_cid); + + // Now read the data +#if FILE_UN_MARSHALLER + XDRFileUnMarshaller um(rs->get_stream()); +#else + fpistream in ( rs->get_stream() ); + XDRStreamUnMarshaller um( in ); +#endif + for (DDS::Vars_iter i = data.var_begin(); i != data.var_end(); i++) { + (*i)->deserialize(um, &data); + } + return; + } +#endif + + case dods_data: + default: { + // Parse the DDS; throw an exception on error. + data.parse(rs->get_stream()); +#if FILE_UN_MARSHALLER + XDRFileUnMarshaller um(rs->get_stream()); +#else + fpistream in ( rs->get_stream() ); + XDRStreamUnMarshaller um( in ); +#endif + // Load the DDS with data. + for (DDS::Vars_iter i = data.var_begin(); i != data.var_end(); i++) { + (*i)->deserialize(um, &data); + } + return; + } + } +} + +/** This private method process data from both local and remote sources. It + exists to eliminate duplication of code. */ +void Connect::process_data(DDS &data, Response *rs) +{ + DBG(cerr << "Entering Connect::process_data" << endl); + + data.set_dap_version(rs->get_protocol()); + + DBG(cerr << "Entering process_data: d_stream = " << rs << endl); + switch (rs->get_type()) { + case dods_error: { + Error e; + if (!e.parse(rs->get_stream())) + throw InternalErr(__FILE__, __LINE__, "Could not parse the Error object returned by the server!"); + throw e; + } + + case web_error: + // Web errors (those reported in the return document's MIME header) + // are processed by the WWW library. + throw InternalErr(__FILE__, __LINE__, + "An error was reported by the remote web server; this should have been processed by HTTPConnect."); + +#if 0 + // FIXME: The following case is never used. There is no such response. jhrg 10/20/15 + // This code triggers a security warning from Coverity; since it is not used, + // I have removed it. jhrg 5/5/16 + case dods_data_ddx: { + // Parse the DDX; throw an exception on error. + DDXParser ddx_parser(data.get_factory()); + + // Read the MPM boundary and then read the subsequent headers + string boundary = read_multipart_boundary(rs->get_stream()); + DBG(cerr << "MPM Boundary: " << boundary << endl); + read_multipart_headers(rs->get_stream(), "text/xml", dods_ddx); + + // Parse the DDX, reading up to and including the next boundary. + // Return the CID for the matching data part + string data_cid; + ddx_parser.intern_stream(rs->get_stream(), &data, data_cid, boundary); + + // Munge the CID into something we can work with + data_cid = cid_to_header_value(data_cid); + DBG(cerr << "Data CID: " << data_cid << endl); + + // Read the data part's MPM part headers (boundary was read by + // DDXParse::intern) + read_multipart_headers(rs->get_stream(), "application/octet-stream", dap4_data, data_cid); + + // Now read the data + XDRFileUnMarshaller um(rs->get_stream()); + for (DDS::Vars_iter i = data.var_begin(); i != data.var_end(); i++) { + (*i)->deserialize(um, &data); + } + return; + } +#endif + + case dods_data: + default: { + // Parse the DDS; throw an exception on error. + data.parse(rs->get_stream()); + + XDRFileUnMarshaller um(rs->get_stream()); + + // Load the DDS with data. + for (DDS::Vars_iter i = data.var_begin(); i != data.var_end(); i++) { + (*i)->deserialize(um, &data); + } + + return; + } + } +} + +// Barely a parser... This is used when reading from local sources of DODS +// Data objects. It simulates the important actions of the libwww MIME header +// parser. Those actions fill in certain fields in the Connect object. jhrg +// 5/20/97 +// +// Make sure that this parser reads from data_source without disturbing the +// information in data_source that follows the MIME header. Since the DDS +// (which follows the MIME header) is parsed by a flex/bison scanner/parser, +// make sure to use I/O calls that will mesh with ANSI C I/O calls. In the +// old GNU libg++, the C++ calls were synchronized with the C calls, but that +// may no longer be the case. 5/31/99 jhrg + +/** Use when you cannot use libcurl. + + @note This method tests for MIME headers with lines terminated by CRLF + (\r\n) and Newlines (\n). In either case, the line terminators are removed + before each header is processed. + + @note FIXME The code uses tainted data via get_next_mime_header() whcih + should be fixed. See the note in mime_util.cc + + @param data_source Read from this stream. + @param rs Value/Result parameter. Dump version and type information here. + */ +void Connect::parse_mime(Response *rs) +{ + rs->set_version("dods/0.0"); // initial value; for backward compatibility. + rs->set_protocol("2.0"); + + FILE *data_source = rs->get_stream(); + string mime = get_next_mime_header(data_source); + while (!mime.empty()) { + string header, value; + parse_mime_header(mime, header, value); + + // Note that this is an ordered list + if (header == "content-description:") { + DBG(cout << header << ": " << value << endl); + rs->set_type(get_description_type(value)); + } + // Use the value of xdods-server only if no other value has been read + else if (header == "xdods-server:" && rs->get_version() == "dods/0.0") { + DBG(cout << header << ": " << value << endl); + rs->set_version(value); + } + // This trumps 'xdods-server' and 'server' + else if (header == "xopendap-server:") { + DBG(cout << header << ": " << value << endl); + rs->set_version(value); + } + else if (header == "xdap:") { + DBG(cout << header << ": " << value << endl); + rs->set_protocol(value); + } + // Only look for 'server' if no other header supplies this info. + else if (rs->get_version() == "dods/0.0" && header == "server:") { + DBG(cout << header << ": " << value << endl); + rs->set_version(value); + } + + mime = get_next_mime_header(data_source); + } +} + +// public mfuncs + +/** The Connect constructor requires a name, which is the URL to + which the connection is to be made. + + @param n The URL for the virtual connection. + @param uname Use this username for authentication. Null by default. + @param password Password to use for authentication. Null by default. + @brief Create an instance of Connect. */ +Connect::Connect(const string &n, string uname, string password) : + d_http(0), d_version("unknown"), d_protocol("2.0") +{ + string name = prune_spaces(n); + + // Figure out if the URL starts with 'http', if so, make sure that we + // talk to an instance of HTTPConnect. + if (name.find("http") == 0) { + DBG(cerr << "Connect: The identifier is an http URL" << endl); + d_http = new HTTPConnect(RCReader::instance()); + + // Find and store any CE given with the URL. + string::size_type dotpos = name.find('?'); + if (dotpos != name.npos) { + _URL = name.substr(0, dotpos); + string expr = name.substr(dotpos + 1); + + dotpos = expr.find('&'); + if (dotpos != expr.npos) { + _proj = expr.substr(0, dotpos); + _sel = expr.substr(dotpos); // XXX includes '&' + } + else { + _proj = expr; + _sel = ""; + } + } + else { + _URL = name; + _proj = ""; + _sel = ""; + } + + _local = false; + } + else { + DBG(cerr << "Connect: The identifier is a local data source." << endl); + + d_http = 0; + _URL = ""; + _local = true; // local in this case means non-DAP + } + + set_credentials(uname, password); +} + +Connect::~Connect() +{ + DBG2(cerr << "Entering the Connect dtor" << endl); + + if (d_http) + delete d_http; + d_http = 0; + + DBG2(cerr << "Leaving the Connect dtor" << endl); +} + +/** Get version information from the server. This is a new method which will + ease the transition to DAP 4. + + @note Use request_protocol() to get the DAP protocol version. + + @return The DAP version string. + @see request_protocol() */ +string Connect::request_version() +{ + string version_url = _URL + ".ver"; + if (_proj.length() + _sel.length()) + version_url = version_url + "?" + id2www_ce(_proj + _sel); + + Response *rs = 0; + try { + rs = d_http->fetch_url(version_url); + } + catch (Error &e) { + delete rs; + rs = 0; + throw; + } + + d_version = rs->get_version(); + d_protocol = rs->get_protocol(); + + delete rs; + rs = 0; + + return d_version; +} + +/** Get protocol version information from the server. This is a new method + which will ease the transition to DAP 4. Note that this method returns + the version of the DAP protocol implemented by the server. The + request_version() method returns the \e server's version number, not + the DAP protocol version. + + @note This method actually asks the server for the protocol version - use + get_protocol() to get the protocol information from the most recent + response (e.g., from the last DDX response returned by the server). + + @return The DAP protocol version string. */ +string Connect::request_protocol() +{ + string version_url = _URL + ".ver"; + if (_proj.length() + _sel.length()) + version_url = version_url + "?" + id2www_ce(_proj + _sel); + + Response *rs = 0; + try { + rs = d_http->fetch_url(version_url); + } + catch (Error &e) { + delete rs; + rs = 0; + throw; + } + + d_version = rs->get_version(); + d_protocol = rs->get_protocol(); + + delete rs; + rs = 0; + + return d_protocol; +} + +/** Reads the DAS corresponding to the dataset in the Connect + object's URL. Although DAP does not support using CEs with DAS + requests, if present in the Connect object's instance, they will be + escaped and passed as the query string of the request. + + @brief Get the DAS from a server. + @param das Result. */ +void Connect::request_das(DAS &das) +{ + string das_url = _URL + ".das"; + if (_proj.length() + _sel.length()) + das_url = das_url + "?" + id2www_ce(_proj + _sel); + + Response *rs = 0; + try { + rs = d_http->fetch_url(das_url); + } + catch (Error &e) { + delete rs; + rs = 0; + throw; + } + + d_version = rs->get_version(); + d_protocol = rs->get_protocol(); + + switch (rs->get_type()) { + case dods_error: { + Error e; + if (!e.parse(rs->get_stream())) { + delete rs; + rs = 0; + throw InternalErr(__FILE__, __LINE__, "Could not parse error returned from server."); + } + delete rs; + rs = 0; + throw e; + } + + case web_error: + // We should never get here; a web error should be picked up read_url + // (called by fetch_url) and result in a thrown Error object. + break; + + case dods_das: + default: + // DAS::parse throws an exception on error. + try { + das.parse(rs->get_stream()); // read and parse the das from a file + } + catch (Error &e) { + delete rs; + rs = 0; + throw; + } + + break; + } + + delete rs; + rs = 0; +} + +/** Reads the DAS corresponding to the dataset in the Connect + object's URL. Although DAP does not support using CEs with DAS + requests, if present in the Connect object's instance, they will be + escaped and passed as the query string of the request. + + Different from request_das method in that this method uses the URL as + given without attaching .das or projections or selections. + + @brief Get the DAS from a server. + @param das Result. */ +void Connect::request_das_url(DAS &das) +{ + string use_url = _URL + "?" + _proj + _sel; + Response *rs = 0; + try { + rs = d_http->fetch_url(use_url); + } + catch (Error &e) { + delete rs; + rs = 0; + throw; + } + + d_version = rs->get_version(); + d_protocol = rs->get_protocol(); + + switch (rs->get_type()) { + case dods_error: { + Error e; + if (!e.parse(rs->get_stream())) { + delete rs; + rs = 0; + throw InternalErr(__FILE__, __LINE__, "Could not parse error returned from server."); + } + delete rs; + rs = 0; + throw e; + } + + case web_error: + // We should never get here; a web error should be picked up read_url + // (called by fetch_url) and result in a thrown Error object. + break; + + case dods_das: + default: + // DAS::parse throws an exception on error. + try { + das.parse(rs->get_stream()); // read and parse the das from a file + } + catch (Error &e) { + delete rs; + rs = 0; + throw; + } + + break; + } + + delete rs; + rs = 0; +} + +/** Reads the DDS corresponding to the dataset in the Connect object's URL. + If present in the Connect object's instance, a CE will be escaped, + combined with \c expr and passed as the query string of the request. + + @note If you need the DDS to hold specializations of the type classes, + be sure to include the factory class which will instantiate those + specializations in the DDS. Either pass a pointer to the factory to + DDS constructor or use the DDS::set_factory() method after the + object is built. + + @brief Get the DDS from a server. + @param dds Result. + @param expr Send this constraint expression to the server. */ +void Connect::request_dds(DDS &dds, string expr) +{ + string proj, sel; + string::size_type dotpos = expr.find('&'); + if (dotpos != expr.npos) { + proj = expr.substr(0, dotpos); + sel = expr.substr(dotpos); + } + else { + proj = expr; + sel = ""; + } + + string dds_url = _URL + ".dds" + "?" + id2www_ce(_proj + proj + _sel + sel); + + Response *rs = 0; + try { + rs = d_http->fetch_url(dds_url); + } + catch (Error &e) { + delete rs; + rs = 0; + throw; + } + + d_version = rs->get_version(); + d_protocol = rs->get_protocol(); + + switch (rs->get_type()) { + case dods_error: { + Error e; + if (!e.parse(rs->get_stream())) { + delete rs; + rs = 0; + throw InternalErr(__FILE__, __LINE__, "Could not parse error returned from server."); + } + delete rs; + rs = 0; + throw e; + } + + case web_error: + // We should never get here; a web error should be picked up read_url + // (called by fetch_url) and result in a thrown Error object. + break; + + case dods_dds: + default: + // DDS::prase throws an exception on error. + try { + dds.parse(rs->get_stream()); // read and parse the dds from a file + } + catch (Error &e) { + delete rs; + rs = 0; + throw; + } + break; + } + + delete rs; + rs = 0; +} + +/** Reads the DDS corresponding to the dataset in the Connect object's URL. + If present in the Connect object's instance, a CE will be escaped, + combined with \c expr and passed as the query string of the request. + + Different from request_dds method above in that this method assumes + URL is complete and does not add anything to the command, such as .dds + or projections or selections. + + @note If you need the DDS to hold specializations of the type classes, + be sure to include the factory class which will instantiate those + specializations in the DDS. Either pass a pointer to the factory to + DDS constructor or use the DDS::set_factory() method after the + object is built. + + @brief Get the DDS from a server. + @param dds Result. */ +void Connect::request_dds_url(DDS &dds) +{ + string use_url = _URL + "?" + _proj + _sel; + Response *rs = 0; + try { + rs = d_http->fetch_url(use_url); + } + catch (Error &e) { + delete rs; + rs = 0; + throw; + } + + d_version = rs->get_version(); + d_protocol = rs->get_protocol(); + + switch (rs->get_type()) { + case dods_error: { + Error e; + if (!e.parse(rs->get_stream())) { + delete rs; + rs = 0; + throw InternalErr(__FILE__, __LINE__, "Could not parse error returned from server."); + } + delete rs; + rs = 0; + throw e; + } + + case web_error: + // We should never get here; a web error should be picked up read_url + // (called by fetch_url) and result in a thrown Error object. + break; + + case dods_dds: + default: + // DDS::prase throws an exception on error. + try { + dds.parse(rs->get_stream()); // read and parse the dds from a file + } + catch (Error &e) { + delete rs; + rs = 0; + throw; + } + break; + } + + delete rs; + rs = 0; +} + +/** Reads the DDX corresponding to the dataset in the Connect object's URL. + If present in the Connect object's instance, a CE will be escaped, + combined with \c expr and passed as the query string of the request. + + @note A DDX is represented as XML on the wire but in memory libdap uses a + DDS object with variables that hold their own attributes (the DDS itself holds + the global attributes). + + @brief Get the DDX from a server. + @param dds Result. + @param expr Send this constraint expression to the server. */ +void Connect::request_ddx(DDS &dds, string expr) +{ + string proj, sel; + string::size_type dotpos = expr.find('&'); + if (dotpos != expr.npos) { + proj = expr.substr(0, dotpos); + sel = expr.substr(dotpos); + } + else { + proj = expr; + sel = ""; + } + + string ddx_url = _URL + ".ddx" + "?" + id2www_ce(_proj + proj + _sel + sel); + + Response *rs = 0; + try { + rs = d_http->fetch_url(ddx_url); + } + catch (Error &e) { + delete rs; + throw; + } + + d_version = rs->get_version(); + d_protocol = rs->get_protocol(); + + switch (rs->get_type()) { + case dods_error: { + Error e; + if (!e.parse(rs->get_stream())) { + delete rs; + rs = 0; + throw InternalErr(__FILE__, __LINE__, "Could not parse error returned from server."); + } + delete rs; + throw e; + } + + case web_error: + // We should never get here; a web error should be picked up read_url + // (called by fetch_url) and result in a thrown Error object. + break; + + case dods_ddx: + try { + string blob; + + DDXParser ddxp(dds.get_factory()); + ddxp.intern_stream(rs->get_stream(), &dds, blob); + } + catch (Error &e) { + delete rs; + throw; + } + break; + + default: + ObjectType ot = rs->get_type(); + delete rs; + throw Error("Invalid response type when requesting a DDX response. Response type: " + long_to_string(ot)); + } + + delete rs; +} + +/** @brief The 'url' version of request_ddx + @see Connect::request_ddx. */ +void Connect::request_ddx_url(DDS &dds) +{ + string use_url = _URL + "?" + _proj + _sel; + + Response *rs = 0; + try { + rs = d_http->fetch_url(use_url); + } + catch (Error &e) { + delete rs; + throw; + } + + d_version = rs->get_version(); + d_protocol = rs->get_protocol(); + + switch (rs->get_type()) { + case dods_error: { + Error e; + if (!e.parse(rs->get_stream())) { + delete rs; + throw InternalErr(__FILE__, __LINE__, "Could not parse error returned from server."); + } + delete rs; + throw e; + } + + case web_error: + // We should never get here; a web error should be picked up read_url + // (called by fetch_url) and result in a thrown Error object. + delete rs; + throw InternalErr(__FILE__, __LINE__, "Web error."); + + case dods_ddx: + try { + string blob; + + DDXParser ddxp(dds.get_factory()); + ddxp.intern_stream(rs->get_stream(), &dds, blob); + } + catch (Error &e) { + delete rs; + throw; + } + break; + + default: { + ObjectType ot = rs->get_type(); + delete rs; + + throw Error("Invalid response type when requesting a DDX response. Response type: " + long_to_string(ot)); + } + } + + delete rs; +} + +/** Reads the DataDDS object corresponding to the dataset in the Connect + object's URL. If present in the Connect object's instance, a CE will be + escaped, combined with \c expr and passed as the query string of the + request. The result is a DataDDS which contains the data values bound to + variables. + + @note If you need the DataDDS to hold specializations of the type classes, + be sure to include the factory class which will instantiate those + specializations in the DataDDS. Either pass a pointer to the factory to + DataDDS constructor or use the DDS::set_factory() method after the + object is built. + + @brief Get the DAS from a server. + @param data Result. + @param expr Send this constraint expression to the server. */ +void Connect::request_data(DataDDS &data, string expr) +{ + string proj, sel; + string::size_type dotpos = expr.find('&'); + if (dotpos != expr.npos) { + proj = expr.substr(0, dotpos); + sel = expr.substr(dotpos); + } + else { + proj = expr; + sel = ""; + } + + string data_url = _URL + ".dods?" + id2www_ce(_proj + proj + _sel + sel); + + Response *rs = 0; + // We need to catch Error exceptions to ensure calling close_output. + try { + rs = d_http->fetch_url(data_url); + + d_version = rs->get_version(); + d_protocol = rs->get_protocol(); + + process_data(data, rs); + delete rs; + rs = 0; + } + catch (Error &e) { + delete rs; + rs = 0; + throw; + } +} + +/** Reads the DataDDS object corresponding to the dataset in the Connect + object's URL. If present in the Connect object's instance, a CE will be + escaped, combined with \c expr and passed as the query string of the + request. The result is a DataDDS which contains the data values bound to + variables. + + Different from request_data in that this method uses the syntax of the + new OPeNDAP server commands using dispatch + + @note If you need the DataDDS to hold specializations of the type classes, + be sure to include the factory class which will instantiate those + specializations in the DataDDS. Either pass a pointer to the factory to + DataDDS constructor or use the DDS::set_factory() method after the + object is built. + + @brief Get the DAS from a server. + @param data Result. */ +void Connect::request_data_url(DataDDS &data) +{ + string use_url = _URL + "?" + _proj + _sel; + Response *rs = 0; + // We need to catch Error exceptions to ensure calling close_output. + try { + rs = d_http->fetch_url(use_url); + + d_version = rs->get_version(); + d_protocol = rs->get_protocol(); + + process_data(data, rs); + delete rs; + rs = 0; + } + catch (Error &e) { + delete rs; + rs = 0; + throw; + } +} + +// FIXME Unused? +void Connect::request_data_ddx(DataDDS &data, string expr) +{ + string proj, sel; + string::size_type dotpos = expr.find('&'); + if (dotpos != expr.npos) { + proj = expr.substr(0, dotpos); + sel = expr.substr(dotpos); + } + else { + proj = expr; + sel = ""; + } + + string data_url = _URL + ".dap?" + id2www_ce(_proj + proj + _sel + sel); + + Response *rs = 0; + // We need to catch Error exceptions to ensure calling close_output. + try { + rs = d_http->fetch_url(data_url); + + d_version = rs->get_version(); + d_protocol = rs->get_protocol(); + + process_data(data, rs); + delete rs; + rs = 0; + } + catch (Error &e) { + delete rs; + rs = 0; + throw; + } +} + +// FIXME Unused? +void Connect::request_data_ddx_url(DataDDS &data) +{ + string use_url = _URL + "?" + _proj + _sel; + Response *rs = 0; + // We need to catch Error exceptions to ensure calling close_output. + try { + rs = d_http->fetch_url(use_url); + + d_version = rs->get_version(); + d_protocol = rs->get_protocol(); + + process_data(data, rs); + delete rs; + rs = 0; + } + catch (Error &e) { + delete rs; + rs = 0; + throw; + } +} + +/** @brief Read data which is preceded by MIME headers. + This method works for both data dds and data ddx responses. + + @note If you need the DataDDS to hold specializations of the type classes, + be sure to include the factory class which will instantiate those + specializations in the DataDDS. Either pass a pointer to the factory to + DataDDS constructor or use the DDS::set_factory() method after the + object is built. + + @see read_data_no_mime() + @param data Result. + @param rs Read from this Response object. */ + +void Connect::read_data(DataDDS &data, Response *rs) +{ + if (!rs) + throw InternalErr(__FILE__, __LINE__, "Response object is null."); + + // Read from data_source and parse the MIME headers specific to DAP2/4. + parse_mime(rs); + + read_data_no_mime(data, rs); +} +void +Connect::read_data(DDS &data, Response *rs) +{ + if (!rs) + throw InternalErr(__FILE__, __LINE__, "Response object is null."); + + // Read from data_source and parse the MIME headers specific to DAP2/4. + parse_mime(rs); + + read_data_no_mime(data, rs); +} + +// This function looks at the input stream and makes its best guess at what +// lies in store for downstream processing code. Definitely heuristic. +// Assumptions: +// #1 The current file position is past any MIME headers (if they were present). +// #2 We must reset the FILE* position to the start of the DDS or DDX headers +static void divine_type_information(Response *rs) +{ + // Consume whitespace + int c = getc(rs->get_stream()); + while (!feof(rs->get_stream()) && !ferror(rs->get_stream()) && isspace(c)) { + c = getc(rs->get_stream()); + } + + + if (ferror(rs->get_stream())) + throw Error("Error reading response type information: " + string(strerror(errno))); + if (feof(rs->get_stream())) + throw Error("Error reading response type information: Found EOF"); + + // The heuristic here is that a DataDDX is a multipart MIME document and + // The first non space character found after the headers is the start of + // the first part which looks like '--' while a DataDDS starts + // with a DDS (;Dataset {' ...). I take into account that our parsers have + // accepted both 'Dataset' and 'dataset' for a long time. + switch (c) { + case '-': + rs->set_type(dods_data_ddx); + break; + case 'D': + case 'd': + rs->set_type(dods_data); + break; + default: + throw InternalErr(__FILE__, __LINE__, "Could not determine type of response object in stream."); + } + + ungetc(c, rs->get_stream()); +} + +/** @brief Read data from a file which does not have response MIME headers. + This method is a companion to read_data(). While read_data() assumes that + the response has MIME headers, this method does not. If you call this + with a Response that does contain headers, it will throw an Error (and + the message is likely to be inscrutable). + + @note This method will use the 'type' information in the Response object + to choose between processing the response as a data dds or data ddx. If + there is no type information, it will attempt to figure it out. + + @param data Result. + @param rs Read from this Response object. */ +void Connect::read_data_no_mime(DataDDS &data, Response *rs) +{ + if (rs->get_type() == unknown_type) + divine_type_information(rs); + + switch (rs->get_type()) { + case dods_data: + d_version = rs->get_version(); + d_protocol = rs->get_protocol(); + process_data(data, rs); + break; + case dods_data_ddx: + process_data(data, rs); + d_version = rs->get_version(); + d_protocol = data.get_protocol(); + break; + default: + throw InternalErr(__FILE__, __LINE__, "Should have been a DataDDS or DataDDX."); + } +} +void Connect::read_data_no_mime(DDS &data, Response *rs) +{ + if (rs->get_type() == unknown_type) + divine_type_information(rs); + + switch (rs->get_type()) { + case dods_data: + d_version = rs->get_version(); + d_protocol = rs->get_protocol(); + process_data(data, rs); + break; + case dods_data_ddx: + process_data(data, rs); + d_version = rs->get_version(); + // TODO should check to see if this hack is a correct replacement + // for get_protocol from DataDDS + d_protocol = data.get_dap_version(); + break; + default: + throw InternalErr(__FILE__, __LINE__, "Should have been a DataDDS or DataDDX."); + } +} + +bool +Connect::is_local() +{ + return _local; +} + +/** Return the Connect object's URL in a string. The URL was set by + the class constructor, and may not be reset. If you want to + open another URL, you must create another Connect object. There + is a Connections class created to handle the management of + multiple Connect objects. + + @brief Get the object's URL. + @see Connections + @return A string containing the URL of the data to which the + Connect object refers. If the object refers to local data, + the function returns the null string. + @param ce If TRUE, the returned URL will include any constraint + expression enclosed with the Connect object's URL (including the + ?). If FALSE, any constraint expression will be removed from + the URL. The default is TRUE. + */ +string Connect::URL(bool ce) +{ + if (_local) + throw InternalErr(__FILE__, __LINE__, "URL(): This call is only valid for a DAP data source."); + + if (ce) + return _URL + "?" + _proj + _sel; + else + return _URL; +} + +/** Return the constraint expression (CE) part of the Connect URL. Note + that this CE is supplied as part of the URL passed to the + Connect's constructor. It is not the CE passed to the + request_data() function. + + @brief Get the Connect's constraint expression. + @return A string containing the constraint expression (if any) + submitted to the Connect object's constructor. */ +string Connect::CE() +{ + if (_local) + throw InternalErr(__FILE__, __LINE__, "CE(): This call is only valid for a DAP data source."); + + return _proj + _sel; +} + +/** @brief Set the credentials for responding to challenges while dereferencing + URLs. + @param u The username. + @param p The password. + @see extract_auth_info() */ +void Connect::set_credentials(string u, string p) +{ + if (d_http) + d_http->set_credentials(u, p); +} + +/** Set the \e accept deflate property. + @param deflate True if the client can accept compressed responses, False + otherwise. */ +void Connect::set_accept_deflate(bool deflate) +{ + if (d_http) + d_http->set_accept_deflate(deflate); +} + +/** Set the \e XDAP-Accept property/header. This is used to send to a server + the (highest) DAP protocol version number that this client understands. + + @param major The client dap protocol major version + @param minor The client dap protocol minor version */ +void Connect::set_xdap_protocol(int major, int minor) +{ + if (d_http) + d_http->set_xdap_protocol(major, minor); +} + +/** Disable any further use of the client-side cache. In a future version + of this software, this should be handled so that the www library is + not initialized with the cache running by default. */ +void Connect::set_cache_enabled(bool cache) +{ + if (d_http) + d_http->set_cache_enabled(cache); +} + +bool Connect::is_cache_enabled() +{ + bool status; + DBG(cerr << "Entering is_cache_enabled (" << hex << d_http << dec + << ")... "); + if (d_http) + status = d_http->is_cache_enabled(); + else + status = false; + DBGN(cerr << "exiting" << endl); + return status; +} + +} // namespace libdap diff --git a/Connect.h b/Connect.h new file mode 100644 index 0000000..1ffce11 --- /dev/null +++ b/Connect.h @@ -0,0 +1,224 @@ + +// -*- mode: c++; c-basic-offset:4 -*- + +// This file is part of libdap, A C++ implementation of the OPeNDAP Data +// Access Protocol. + +// Copyright (c) 2002,2003 OPeNDAP, Inc. +// Author: James Gallagher +// Dan Holloway +// Reza Nekovei +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// +// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112. + +// (c) COPYRIGHT URI/MIT 1994-1999,2001,2002 +// Please first read the full copyright statement in the file COPYRIGHT_URI. +// +// Authors: +// jhrg,jimg James Gallagher +// dan Dan Holloway +// reza Reza Nekovei + +// Connect objects are used as containers for information pertaining to a +// connection that a user program makes to a dataset. The dataset may be +// either local (i.e., a file on the user's own computer) or a remote +// dataset. In the later case a DAP2 URL will be used to reference the +// dataset. +// +// Connect contains methods which can be used to read the DOS DAS and DDS +// objects from the remote dataset as well as reading reading data. The class +// understands in a rudimentary way how DAP2 constraint expressions are +// formed and how to manage the CEs generated by a API to request specific +// variables with the URL initially presented to the class when the object +// was instantiated. +// +// Connect also provides additional services such as error processing. +// +// Connect is not intended for use on the server-side. +// +// jhrg 9/29/94 + +#ifndef _connect_h +#define _connect_h + + +#include + +#ifndef _das_h +#include "DAS.h" +#endif + +#ifndef _dds_h +#include "DDS.h" +#endif + +#ifndef _error_h +#include "Error.h" +#endif + +#ifndef _util_h +#include "util.h" +#endif + +#ifndef _datadds_h +#include "DataDDS.h" +#endif + +#ifndef _httpconnect_h +#include "HTTPConnect.h" +#endif + +#ifndef response_h +#include "Response.h" +#endif + +using std::string; + +namespace libdap +{ + +/** Connect objects are used as containers for information pertaining + to the connection a user program makes to a dataset. The + dataset may be either local (for example, a file on the user's own + computer) or a remote dataset. In the latter case a DAP2 URL will + be used to reference the dataset, instead of a filename. + + Connect contains methods which can be used to read the DAP2 DAS and + DDS objects from the remote dataset as well as reading + data. The class understands in a rudimentary way how DAP2 + constraint expressions are formed and how to manage them. + + Connect also provides additional services such as automatic + decompression of compressed data and and error processing. + + @note Update: I removed the DEFAULT_BASETYPE_FACTORY switch because it + caused more confusion than it avoided. See Trac #130. + + @note The compile-time symbol DEFAULT_BASETYPE_FACTORY controls whether + the old (3.4 and earlier) DDS and DataDDS constructors are supported. + These constructors now use a default factory class (BaseTypeFactory, + implemented by this library) to instantiate Byte, ..., Grid variables. To + use the default ctor in your code you must also define this symbol. If + you \e do choose to define this and fail to provide a specialization of + BaseTypeFactory when your software needs one, you code may not link or + may fail at run time. In addition to the older ctors for DDS and DataDDS, + defining the symbol also makes some of the older methods in Connect + available (because those methods require the older DDS and DataDDS ctors. + + @brief Holds information about the link from a DAP2 client to a + dataset. + @see DDS + @see DAS + @see Error + @author jhrg */ + +class Connect +{ +private: + bool _local; // Is this a local connection? + + HTTPConnect *d_http; + string _URL; // URL to remote dataset (minus CE) + string _proj; // Projection part of initial CE. + string _sel; // Selection of initial CE + + string d_version; // Server implementation information + string d_protocol; // DAP protocol from the server + + void process_data(DataDDS &data, Response *rs); + void process_data(DDS &data, Response *rs); + + // Use when you cannot use libwww/libcurl. Reads HTTP response. + void parse_mime(Response *rs); + +protected: + /** @name Suppress the C++ defaults for these. */ + //@{ + Connect(); + Connect(const Connect &); + Connect &operator=(const Connect &); + //@} + +public: + Connect(const string &name, string uname = "", string password = ""); + + virtual ~Connect(); + + bool is_local(); + + // *** Add get_* versions of accessors. 02/27/03 jhrg + virtual string URL(bool CE = true); + virtual string CE(); + + void set_credentials(string u, string p); + void set_accept_deflate(bool deflate); + void set_xdap_protocol(int major, int minor); + + void set_cache_enabled(bool enabled); + bool is_cache_enabled(); + + void set_xdap_accept(int major, int minor); + + /** Return the protocol/implementation version of the most recent + response. This is a poorly designed method, but it returns + information that is useful when used correctly. Before a response is + made, this contains the string "unknown." This should ultimately hold + the \e protocol version; it currently holds the \e implementation + version. + + @see get_protocol() + @deprecated */ + string get_version() + { + return d_version; + } + + /** Return the DAP protocol version of the most recent + response. Before a response is made, this contains the string "2.0." + */ + string get_protocol() + { + return d_protocol; + } + + virtual string request_version(); + virtual string request_protocol(); + + virtual void request_das(DAS &das); + virtual void request_das_url(DAS &das); + + virtual void request_dds(DDS &dds, string expr = ""); + virtual void request_dds_url(DDS &dds); + + virtual void request_ddx(DDS &dds, string expr = ""); + virtual void request_ddx_url(DDS &dds); + + virtual void request_data(DataDDS &data, string expr = ""); + virtual void request_data_url(DataDDS &data); + + virtual void request_data_ddx(DataDDS &data, string expr = ""); + virtual void request_data_ddx_url(DataDDS &data); + + virtual void read_data(DataDDS &data, Response *rs); + virtual void read_data_no_mime(DataDDS &data, Response *rs); + virtual void read_data(DDS &data, Response *rs); + virtual void read_data_no_mime(DDS &data, Response *rs); +}; + +} // namespace libdap + +#endif // _connect_h diff --git a/ConstraintEvaluator.cc b/ConstraintEvaluator.cc new file mode 100644 index 0000000..2d460a6 --- /dev/null +++ b/ConstraintEvaluator.cc @@ -0,0 +1,388 @@ +// -*- mode: c++; c-basic-offset:4 -*- + +// This file is part of libdap, A C++ implementation of the OPeNDAP Data +// Access Protocol. + +// Copyright (c) 2002,2003 OPeNDAP, Inc. +// Author: James Gallagher +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// +// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112. + +#include "config.h" + +//#define DODS_DEBUG + +#include "ServerFunctionsList.h" +#include "ConstraintEvaluator.h" +#include "Clause.h" +#include "DataDDS.h" + +#include "ce_parser.h" +#include "debug.h" +#include "parser.h" +#include "expr.h" + +struct yy_buffer_state; + +int ce_exprparse(libdap::ce_parser_arg *arg); + +// Glue routines declared in expr.lex +void ce_expr_switch_to_buffer(void *new_buffer); +void ce_expr_delete_buffer(void * buffer); +void *ce_expr_string(const char *yy_str); + +extern int ce_exprdebug; + +namespace libdap { + +ConstraintEvaluator::ConstraintEvaluator() +{ + // Functions are now held in BES modules. jhrg 1/30/13 + + // modules load functions to this list; this class searches the list + // instead of having it's own copy. This is very similar to the BES' + // various List classes, but this one is part of libdap and not the + // BES. The List class is a singleton, so each function module can + // register it's functions to the list object. + d_functions_list = ServerFunctionsList::TheList(); +} + +ConstraintEvaluator::~ConstraintEvaluator() +{ + // delete all the constants created by the parser for CE evaluation + for (Constants_iter j = constants.begin(); j != constants.end(); j++) { + BaseType *btp = *j; + delete btp; + btp = 0; + } + + for (Clause_iter k = expr.begin(); k != expr.end(); k++) { + Clause *cp = *k; + delete cp; + cp = 0; + } +} + +/** Returns the first clause in a parsed constraint expression. */ +ConstraintEvaluator::Clause_iter ConstraintEvaluator::clause_begin() +{ + return expr.begin(); +} + +/** Returns a reference to the end of the list of clauses in a parsed + constraint expression. It does not reference the last clause */ +ConstraintEvaluator::Clause_iter ConstraintEvaluator::clause_end() +{ + return expr.end(); +} + +/** Returns the value of the indicated clause of a constraint + expression. */ +bool ConstraintEvaluator::clause_value(Clause_iter &iter, DDS &dds/*, const string &***/) +{ + if (expr.empty()) + throw InternalErr(__FILE__, __LINE__, "There are no CE clauses for *this* DDS object."); + + return (*iter)->value(dds); +} + +/** @brief Add a clause to a constraint expression. + + This function adds an operator clause to the constraint + expression. + + @param op An integer indicating the operator in use. These + values are generated by \c bison. + @param arg1 A pointer to the argument on the left side of the + operator. + @param arg2 A pointer to a list of the arguments on the right + side of the operator. + */ +void ConstraintEvaluator::append_clause(int op, rvalue *arg1, rvalue_list *arg2) +{ + Clause *clause = new Clause(op, arg1, arg2); + + expr.push_back(clause); +} + +/** @brief Add a clause to a constraint expression. + + This function adds a boolean function clause to the constraint + expression. + + @param func A pointer to a boolean function from the list of + supported functions. + @param args A list of arguments to that function. + */ +void ConstraintEvaluator::append_clause(bool_func func, rvalue_list *args) +{ + Clause *clause = new Clause(func, args); + + expr.push_back(clause); +} + +/** @brief Add a clause to a constraint expression. + + This function adds a real-valued (BaseType) function clause to + the constraint expression. + + @param func A pointer to a BaseType function from the list of + supported functions. + @param args A list of arguments to that function. + */ +void ConstraintEvaluator::append_clause(btp_func func, rvalue_list *args) +{ + Clause *clause = new Clause(func, args); + + expr.push_back(clause); +} + +/** The Constraint Evaluator maintains a list of BaseType pointers for all the + constants that the constraint expression parser generates. These objects + are deleted by the Constraint Evaluator destructor. Note that there are no + list accessors; these constants are never accessed from the list. The list + is simply a convenient way to make sure the constants are disposed of + properly. + */ +void ConstraintEvaluator::append_constant(BaseType *btp) +{ + constants.push_back(btp); +} + +/** @brief Find a Boolean function with a given name in the function list. */ +bool ConstraintEvaluator::find_function(const string &name, bool_func *f) const +{ + return d_functions_list->find_function(name, f); +} + +/** @brief Find a BaseType function with a given name in the function list. */ +bool ConstraintEvaluator::find_function(const string &name, btp_func *f) const +{ + return d_functions_list->find_function(name, f); +} + +/** @brief Find a projection function with a given name in the function list. */ +bool ConstraintEvaluator::find_function(const string &name, proj_func *f) const +{ + return d_functions_list->find_function(name, f); +} +//@} + +/** @brief Does the current constraint expression return a BaseType + pointer? + This method does not evaluate the clause, it provides information to the + evaluator regarding _how_ to evaluate the clause. + @return True if the clause is a function that returns a BaseType* and + false otherwise + @deprecated + */ +bool ConstraintEvaluator::functional_expression() +{ + if (expr.empty()) + return false; + + Clause *cp = expr[0]; + return cp->value_clause(); +} + +/** @brief Evaluate a function-valued constraint expression. + * @deprecated + * */ +BaseType * +ConstraintEvaluator::eval_function(DDS &dds, const string &) +{ + if (expr.size() != 1) + throw InternalErr(__FILE__, __LINE__, "The length of the list of CE clauses is not 1."); + + Clause *cp = expr[0]; + BaseType *result; + if (cp->value(dds, &result)) + return result; + else + return NULL; +} + +/** @brief Does the current constraint expression contain function clauses + + This method does not evaluate the clauses, it provides information to the + evaluator regarding _how_ to evaluate the clause. + + @note Added for libdap 3.11 + + @return True if the current constraint contains function clauses, + otherwise returns False */ +bool ConstraintEvaluator::function_clauses() +{ + if (expr.empty()) + return false; + + for (unsigned int i = 0; i < expr.size(); ++i) { + Clause *cp = expr[i]; + if (!cp->value_clause()) + return false; + } + + return true; +} + +/** @brief Evaluate a function-valued constraint expression that contains + several function calls. + + This method can be called for any function-valued constraint expression. + Unlike eval_function(), it will package the return value in a new DDS + object. The server should free this object once it has been serialized + and sent. + + @note While there is another type of function that can appear in a CE (a + 'projection function') those are evaluated by the ce parser - they are used + to insert new variables into the DDS as a side effect of CE evaluation. + That kind of function can never appear here; these are all functions that + return BaseType pointers. + + @note Added for libdap 3.11 */ +DDS * +ConstraintEvaluator::eval_function_clauses(DDS &dds) +{ + if (expr.empty()) + throw InternalErr(__FILE__, __LINE__, "The constraint expression is empty."); + + DDS *fdds = new DDS(dds.get_factory(), "function_result_" + dds.get_dataset_name()); + for (unsigned int i = 0; i < expr.size(); ++i) { + Clause *cp = expr[i]; + BaseType *result; + if (cp->value(dds, &result)) { + // This is correct: The function must allocate the memory for the result + // variable. 11/30/12 jhrg + fdds->add_var_nocopy(result); + } + else { + delete fdds; + throw Error(internal_error, "A function was called but failed to return a value."); + } + } + + return fdds; +} + +/** @brief Evaluate a function-valued constraint expression that contains + several function calls. Takes and returns a DataDDS. + + @see ConstraintEvaluator::eval_function_clauses(DataDDS &dds) + @note Added for libdap 3.11 */ +DataDDS * +ConstraintEvaluator::eval_function_clauses(DataDDS &dds) +{ + if (expr.empty()) + throw InternalErr(__FILE__, __LINE__, "The constraint expression is empty."); + + DataDDS *fdds = new DataDDS(dds.get_factory(), "function_result_" + dds.get_dataset_name(), dds.get_version(), + dds.get_protocol()); + + for (unsigned int i = 0; i < expr.size(); ++i) { + Clause *cp = expr[i]; + BaseType *result; + if (cp->value(dds, &result)) { + fdds->add_var_nocopy(result); + } + else { + delete fdds; + throw Error(internal_error, "A function was called but failed to return a value."); + } + } + + return fdds; +} + +/** @brief Does the current constraint expression return a boolean value? */ +bool ConstraintEvaluator::boolean_expression() +{ + if (expr.empty()) + return false; + + bool boolean = true; + for (Clause_iter i = expr.begin(); i != expr.end(); i++) { + boolean = boolean && (*i)->boolean_clause(); + } + + return boolean; +} + +/** @brief Evaluate a boolean-valued constraint expression. + This is main method for the evaluator and is called by the + BaseType::serialize() methods. + + @param dds Use these variables when evaluating the expressions. + @param dataset This string is passed to the read() methods. + @return True if the expression is true, false otherwise. */ +bool ConstraintEvaluator::eval_selection(DDS &dds, const string &) +{ + if (expr.empty()) { + DBG(cerr << "No selection recorded" << endl); + return true; + } + + DBG(cerr << "Eval selection" << endl); + + // A CE is made up of zero or more clauses, each of which has a boolean + // value. The value of the CE is the logical AND of the clause + // values. See ConstraintEvaluator::clause::value(...) for information on logical ORs in + // CEs. + bool result = true; + for (Clause_iter i = expr.begin(); i != expr.end() && result; i++) { + // A selection expression *must* contain only boolean clauses! + if (!((*i)->boolean_clause())) + throw InternalErr(__FILE__, __LINE__, "A selection expression must contain only boolean clauses."); + result = result && (*i)->value(dds); + } + + return result; +} + +/** @brief Parse the constraint expression given the current DDS. + + Evaluate the constraint expression; return the value of the expression. + As a side effect, mark the DDS so that BaseType's mfuncs can be used to + correctly read the variable's value and send it to the client. + + @param constraint A string containing the constraint expression. + @param dds The DDS that provides the environment within which the + constraint is evaluated. + @exception Throws Error if the constraint does not parse. */ +void ConstraintEvaluator::parse_constraint(const string &constraint, DDS &dds) +{ + void *buffer = ce_expr_string(constraint.c_str()); + + // Toggle this to debug the parser. A last resort... + ce_exprdebug = false; + + ce_expr_switch_to_buffer(buffer); + + ce_parser_arg arg(this, &dds); + + // For all errors, exprparse will throw Error. + try { + ce_exprparse(&arg); + ce_expr_delete_buffer(buffer); + } + catch (...) { + // Make sure to remove the buffer when there's an error + ce_expr_delete_buffer(buffer); + throw; + } +} + +} // namespace libdap diff --git a/ConstraintEvaluator.h b/ConstraintEvaluator.h new file mode 100644 index 0000000..3ea8031 --- /dev/null +++ b/ConstraintEvaluator.h @@ -0,0 +1,100 @@ +// -*- mode: c++; c-basic-offset:4 -*- + +// This file is part of libdap, A C++ implementation of the OPeNDAP Data +// Access Protocol. + +// Copyright (c) 2006 OPeNDAP, Inc. +// Author: James Gallagher +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// +// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112. + +#ifndef constraint_evaluator_h +#define constraint_evaluator_h + +#include + +#include "expr.h" +#include "RValue.h" + +namespace libdap +{ + +class DDS; +class DataDDS; +struct Clause; +class ServerFunctionsList; + +/** @brief Evaluate a constraint expression */ +class ConstraintEvaluator +{ +private: + std::vector expr; // List of CE Clauses + + std::vector constants;// List of temporary objects + + ServerFunctionsList *d_functions_list; // Known external functions from + // modules + + // The default versions of these methods will break this class. Because + // Clause does not support deep copies, that class will need to be modified + // before these can be properly implemented. jhrg 4/3/06 + ConstraintEvaluator(const ConstraintEvaluator &); + ConstraintEvaluator &operator=(const ConstraintEvaluator &); + + friend class func_name_is; + +public: + typedef std::vector::const_iterator Clause_citer ; + typedef std::vector::iterator Clause_iter ; + + typedef std::vector::const_iterator Constants_citer ; + typedef std::vector::iterator Constants_iter ; + + ConstraintEvaluator(); + + virtual ~ConstraintEvaluator(); + bool find_function(const std::string &name, bool_func *f) const; + bool find_function(const std::string &name, btp_func *f) const; + bool find_function(const std::string &name, proj_func *f) const; + + void append_clause(int op, rvalue *arg1, rvalue_list *arg2); + void append_clause(bool_func func, rvalue_list *args); + void append_clause(btp_func func, rvalue_list *args); + + bool functional_expression(); + bool boolean_expression(); + bool eval_selection(DDS &dds, const std::string &dataset); + BaseType *eval_function(DDS &dds, const std::string &dataset); + + // New for libdap 3.11. These methods provide a way to evaluate multiple + // functions in one CE + bool function_clauses(); + DDS *eval_function_clauses(DDS &dds); + DataDDS *eval_function_clauses(DataDDS &dds); + + Clause_iter clause_begin(); + Clause_iter clause_end(); + bool clause_value(Clause_iter &i, DDS &dds); + + void parse_constraint(const std::string &constraint, DDS &dds); + void append_constant(BaseType *btp); + +}; + +} // namespace libdap + +#endif // constraint_evaluator_h diff --git a/Constructor.cc b/Constructor.cc new file mode 100644 index 0000000..ca7b2f7 --- /dev/null +++ b/Constructor.cc @@ -0,0 +1,931 @@ + +// -*- mode: c++; c-basic-offset:4 -*- + +// This file is part of libdap, A C++ implementation of the OPeNDAP Data +// Access Protocol. + +// Copyright (c) 2002,2003 OPeNDAP, Inc. +// Author: James Gallagher +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// +// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112. + +// (c) COPYRIGHT URI/MIT 1995-1999 +// Please read the full copyright statement in the file COPYRIGHT_URI. +// +// Authors: +// jhrg,jimg James Gallagher + + +#include "config.h" + +//#define DODS_DEBUG + +#include +#include +#include +#include + +#include + +#include "crc.h" + +#include "Constructor.h" +#include "Grid.h" + +#include "DMR.h" +#include "XMLWriter.h" +#include "D4StreamMarshaller.h" +#include "D4StreamUnMarshaller.h" +#include "D4Group.h" + +#include "D4Attributes.h" + +#include "escaping.h" +#include "util.h" +#include "Error.h" +#include "InternalErr.h" + + +// #define DODS_DEBUG 1 +#include "debug.h" + +using namespace std; + +namespace libdap { + +// Private member functions + +void +Constructor::m_duplicate(const Constructor &c) +{ + DBG(cerr << "In Constructor::m_duplicate for " << c.name() << endl); + // Clear out any spurious vars in Constructor::d_vars + // Moved from Grid::m_duplicate. jhrg 4/3/13 + d_vars.clear(); // [mjohnson 10 Sep 2009] + + Vars_citer i = c.d_vars.begin(); + while (i != c.d_vars.end()) { + BaseType *btp = (*i++)->ptr_duplicate(); + btp->set_parent(this); + d_vars.push_back(btp); + } + + DBG(cerr << "Exiting Constructor::m_duplicate for " << c.name() << endl); +} + +// Public member functions + +Constructor::Constructor(const string &name, const Type &type, bool is_dap4) + : BaseType(name, type, is_dap4) +{} + +/** Server-side constructor that takes the name of the variable to be + * created, the dataset name from which this variable is being created, and + * the type of data being stored in the Constructor. This is a protected + * constructor, available only to derived classes of Constructor + * + * @param name string containing the name of the variable to be created + * @param dataset string containing the name of the dataset from which this + * variable is being created + * @param type type of data being stored + */ +Constructor::Constructor(const string &name, const string &dataset, const Type &type, bool is_dap4) + : BaseType(name, dataset, type, is_dap4) +{} + +Constructor::Constructor(const Constructor &rhs) : BaseType(rhs), d_vars(0) +{ + DBG(cerr << "In Constructor::copy_ctor for " << rhs.name() << endl); + m_duplicate(rhs); +} + +Constructor::~Constructor() +{ + Vars_iter i = d_vars.begin(); + while (i != d_vars.end()) { + delete *i++; + } +} + +Constructor & +Constructor::operator=(const Constructor &rhs) +{ + DBG(cerr << "Entering Constructor::operator=" << endl); + if (this == &rhs) + return *this; + + dynamic_cast(*this) = rhs; // run BaseType= + + m_duplicate(rhs); + + DBG(cerr << "Exiting Constructor::operator=" << endl); + return *this; +} + +// A public method, but just barely... +void +Constructor::transform_to_dap4(D4Group *root, Constructor *dest) +{ + DBG(cerr << __func__ << "() - BEGIN (name:"<< name() << + ")(type:"<< type_name()<< + ")(root:'"<< root->name()<<"':"<<(void*)root << + ")(dest:'"<< dest->name()<<"':"<< (void *) dest<< ")" + << endl;); + + for (Constructor::Vars_citer i = var_begin(), e = var_end(); i != e; ++i) { + BaseType *d4_var = dest->var((*i)->name()); + // Don't add duplicate variables. We have to make this check + // because some of the child variables may add arrays + // to the root object. For example, this happens in + // Grid with the Map Arrays - ndp - 05/08/17 + if(!d4_var){ + /* + BaseType *new_var = (*i)->transform_to_dap4(root, dest); + if (new_var) { // Might be a Grid; see the comment in BaseType::transform_to_dap4() + new_var->set_parent(dest); + dest->add_var_nocopy(new_var); + } + */ + DBG(cerr << __func__ << "() - Transforming variable: '" << + (*i)->name() << "'" << endl; ); + (*i)->transform_to_dap4(root /*group*/, dest /*container*/); + } + else { + DBG(cerr << __func__ << "() - Skipping variable: " << + d4_var->type_name() << " " << d4_var->name() << " because a variable with" << + " this name already exists in the root group." << endl; ); + } + } + dest->attributes()->transform_to_dap4(get_attr_table()); + dest->set_is_dap4(true); + DBG(cerr << __func__ << "() - END (name:"<< name() << ")(type:"<< type_name()<< ")" << endl;); +} + + + +string +Constructor::FQN() const +{ + if (get_parent() == 0) + return name(); + else if (get_parent()->type() == dods_group_c) + return get_parent()->FQN() + name(); + else if (get_parent()->type() == dods_array_c) + return get_parent()->FQN(); + else + return get_parent()->FQN() + "." + name(); +} + +int +Constructor::element_count(bool leaves) +{ + if (!leaves) + return d_vars.size(); + else { + int i = 0; + for (Vars_iter j = d_vars.begin(); j != d_vars.end(); j++) { + i += (*j)->element_count(leaves); + } + return i; + } +} + +void +Constructor::set_send_p(bool state) +{ + for (Vars_iter i = d_vars.begin(); i != d_vars.end(); i++) { + (*i)->set_send_p(state); + } + + BaseType::set_send_p(state); +} + +void +Constructor::set_read_p(bool state) +{ + for (Vars_iter i = d_vars.begin(); i != d_vars.end(); i++) { + (*i)->set_read_p(state); + } + + BaseType::set_read_p(state); +} + +#if 0 +// TODO Recode to use width(bool). Bur see comments in BaseType.h +unsigned int +Constructor::width() +{ + unsigned int sz = 0; + + for (Vars_iter i = d_vars.begin(); i != d_vars.end(); i++) { + sz += (*i)->width(); + } + + return sz; +} +#endif +/** This version of width simply returns the same thing as width() for simple + types and Arrays. For Structure it returns the total size if constrained + is false, or the size of the elements in the current projection if true. + + @param constrained If true, return the size after applying a constraint. + @return The number of bytes used by the variable. + */ +unsigned int +Constructor::width(bool constrained) const +{ + unsigned int sz = 0; + + for (Vars_citer i = d_vars.begin(); i != d_vars.end(); i++) { + if (constrained) { + if ((*i)->send_p()) + sz += (*i)->width(constrained); + } + else { + sz += (*i)->width(constrained); + } + } + + return sz; +} + +BaseType * +Constructor::var(const string &name, bool exact_match, btp_stack *s) +{ + string n = www2id(name); + + if (exact_match) + return m_exact_match(n, s); + else + return m_leaf_match(n, s); +} + +/** @deprecated See comment in BaseType */ +BaseType * +Constructor::var(const string &n, btp_stack &s) +{ + // This should probably be removed. The BES code should remove web encoding + // with the possible exception of spaces. jhrg 11/25/13 + string name = www2id(n); + + BaseType *btp = m_exact_match(name, &s); + if (btp) + return btp; + + return m_leaf_match(name, &s); +} + +// Protected method +BaseType * +Constructor::m_leaf_match(const string &name, btp_stack *s) +{ + for (Vars_iter i = d_vars.begin(); i != d_vars.end(); i++) { + if ((*i)->name() == name) { + if (s) { + DBG(cerr << "Pushing " << this->name() << endl); + s->push(static_cast(this)); + } + return *i; + } + if ((*i)->is_constructor_type()) { + BaseType *btp = (*i)->var(name, false, s); + if (btp) { + if (s) { + DBG(cerr << "Pushing " << this->name() << endl); + s->push(static_cast(this)); + } + return btp; + } + } + } + + return 0; +} + +// Protected method +BaseType * +Constructor::m_exact_match(const string &name, btp_stack *s) +{ + // Look for name at the top level first. + for (Vars_iter i = d_vars.begin(); i != d_vars.end(); i++) { + if ((*i)->name() == name) { + if (s) + s->push(static_cast(this)); + + return *i; + } + } + + // If it was not found using the simple search, look for a dot and + // search the hierarchy. + string::size_type dot_pos = name.find("."); // zero-based index of `.' + if (dot_pos != string::npos) { + string aggregate = name.substr(0, dot_pos); + string field = name.substr(dot_pos + 1); + + BaseType *agg_ptr = var(aggregate); + if (agg_ptr) { + if (s) + s->push(static_cast(this)); + + return agg_ptr->var(field, true, s); // recurse + } + else + return 0; // qualified names must be *fully* qualified + } + + return 0; +} + +/** Returns an iterator referencing the first structure element. */ +Constructor::Vars_iter +Constructor::var_begin() +{ + return d_vars.begin() ; +} + +/** Returns an iterator referencing the end of the list of structure + elements. Does not reference the last structure element. */ +Constructor::Vars_iter +Constructor::var_end() +{ + return d_vars.end() ; +} + +/** Return a reverse iterator that references the last element. */ +Constructor::Vars_riter +Constructor::var_rbegin() +{ + return d_vars.rbegin(); +} + +/** Return a reverse iterator that references a point 'before' the first + element. */ +Constructor::Vars_riter +Constructor::var_rend() +{ + return d_vars.rend(); +} + +/** Return the iterator for the \e ith variable. + @param i the index + @return The corresponding Vars_iter */ +Constructor::Vars_iter +Constructor::get_vars_iter(int i) +{ + return d_vars.begin() + i; +} + +/** Return the BaseType pointer for the \e ith variable. + @param i This index + @return The corresponding BaseType*. */ +BaseType * +Constructor::get_var_index(int i) +{ + return *(d_vars.begin() + i); +} + +/** Adds an element to a Constructor. + + @param bt A pointer to the variable to add to this Constructor. + @param part Not used by this class, defaults to nil */ +void +Constructor::add_var(BaseType *bt, Part) +{ + // Jose Garcia + // Passing and invalid pointer to an object is a developer's error. + if (!bt) + throw InternalErr(__FILE__, __LINE__, "The BaseType parameter cannot be null."); +#if 0 + if (bt->is_dap4_only_type()) + throw InternalErr(__FILE__, __LINE__, "Attempt to add a DAP4 type to a DAP2 Structure."); +#endif + // Jose Garcia + // Now we add a copy of bt so the external user is able to destroy bt as + // he/she wishes. The policy is: "If it is allocated outside, it is + // deallocated outside, if it is allocated inside, it is deallocated + // inside" + BaseType *btp = bt->ptr_duplicate(); + btp->set_parent(this); + d_vars.push_back(btp); +} + +/** Adds an element to a Constructor. + + @param bt A pointer to thee variable to add to this Constructor. + @param part Not used by this class, defaults to nil */ +void +Constructor::add_var_nocopy(BaseType *bt, Part) +{ + if (!bt) + throw InternalErr(__FILE__, __LINE__, "The BaseType parameter cannot be null."); +#if 0 + if (bt->is_dap4_only_type()) + throw InternalErr(__FILE__, __LINE__, "Attempt to add a DAP4 type to a DAP2 Structure."); +#endif + bt->set_parent(this); + d_vars.push_back(bt); +} + +/** Remove an element from a Constructor. + + @param n name of the variable to remove */ +void +Constructor::del_var(const string &n) +{ + // TODO remove_if? find_if? + for (Vars_iter i = d_vars.begin(); i != d_vars.end(); i++) { + if ((*i)->name() == n) { + BaseType *bt = *i ; + d_vars.erase(i) ; + delete bt ; bt = 0; + return; + } + } +} + +void +Constructor::del_var(Vars_iter i) +{ + if (*i != 0) { + BaseType *bt = *i; + d_vars.erase(i); + delete bt; + } +} + +/** @brief simple implementation of read that iterates through vars + * and calls read on them + * + * @return returns false to signify all has been read + */ +bool Constructor::read() +{ + DBG(cerr << "Entering Constructor::read..." << endl); + if (!read_p()) { + for (Vars_iter i = d_vars.begin(); i != d_vars.end(); i++) { + (*i)->read(); + } + set_read_p(true); + } + + return false; +} + +void +Constructor::intern_data(ConstraintEvaluator & eval, DDS & dds) +{ + DBG(cerr << "Constructor::intern_data: " << name() << endl); + if (!read_p()) + read(); // read() throws Error and InternalErr + + for (Vars_iter i = d_vars.begin(); i != d_vars.end(); i++) { + if ((*i)->send_p()) { + (*i)->intern_data(eval, dds); + } + } +} + +bool +Constructor::serialize(ConstraintEvaluator &eval, DDS &dds, Marshaller &m, bool ce_eval) +{ +#if USE_LOCAL_TIMEOUT_SCHEME + dds.timeout_on(); +#endif + if (!read_p()) + read(); // read() throws Error and InternalErr + + if (ce_eval && !eval.eval_selection(dds, dataset())) + return true; +#if USE_LOCAL_TIMEOUT_SCHEME + dds.timeout_off(); +#endif + for (Vars_iter i = d_vars.begin(); i != d_vars.end(); i++) { + if ((*i)->send_p()) { +#ifdef CHECKSUMS + XDRStreamMarshaller *sm = dynamic_cast(&m); + if (sm && sm->checksums() && (*i)->type() != dods_structure_c && (*i)->type() != dods_grid_c) + sm->reset_checksum(); + + (*i)->serialize(eval, dds, m, false); + + if (sm && sm->checksums() && (*i)->type() != dods_structure_c && (*i)->type() != dods_grid_c) + sm->get_checksum(); +#else + // (*i)->serialize(eval, dds, m, false); + // Only Sequence and Vector run the evaluator. + (*i)->serialize(eval, dds, m, true); +#endif + } + } + + return true; +} + +bool +Constructor::deserialize(UnMarshaller &um, DDS *dds, bool reuse) +{ + for (Vars_iter i = d_vars.begin(); i != d_vars.end(); i++) { + (*i)->deserialize(um, dds, reuse); + } + + return false; +} + +void +Constructor::compute_checksum(Crc32 &) +{ + throw InternalErr(__FILE__, __LINE__, "Computing a checksum alone is not supported for Constructor types."); +} + +void +Constructor::intern_data(/*Crc32 &checksum, DMR &dmr, ConstraintEvaluator & eval*/) +{ + for (Vars_iter i = d_vars.begin(); i != d_vars.end(); i++) { + if ((*i)->send_p()) { + (*i)->intern_data(/*checksum, dmr, eval*/); + } + } +} + + +/** + * @brief Serialize a Constructor + * + * @todo See notebook for 8/21/14 + * + * @param m + * @param dmr Unused + * @param eval Unused + * @param filter Unused + * @exception Error is thrown if the value needs to be read and that operation fails. + */ +void +Constructor::serialize(D4StreamMarshaller &m, DMR &dmr, /*ConstraintEvaluator &eval,*/ bool filter) +{ +#if 1 + // Not used for the same reason the equivalent code in D4Group::serialize() + // is not used. Fail for D4Sequence and general issues with memory use. + // + // Revisit this - I had to uncomment this to get the netcdf_handler code + // to work - it relies on having NCStructure::read() called. The D4Sequence + // ::serialize() method calls read_next_instance(). What seems to be happening + // is that this call to read gets the first set of values, but does not store + // them; the call to serialize then runs the D4Sequence::serialize() method that + // _does_ read all of the sequence data and then serialize it. However, the first + // sequence instance is missing... + if (!read_p()) + read(); // read() throws Error +#endif +#if 0 + // place holder for now. There may be no need for this; only Array and Seq? + // jhrg 9/6/13 + if (filter && !eval.eval_selection(dmr, dataset())) + return true; +#endif + + for (Vars_iter i = d_vars.begin(); i != d_vars.end(); i++) { + if ((*i)->send_p()) { + (*i)->serialize(m, dmr, /*eval,*/ filter); + } + } +} + +void +Constructor::deserialize(D4StreamUnMarshaller &um, DMR &dmr) +{ + for (Vars_iter i = d_vars.begin(); i != d_vars.end(); i++) { + (*i)->deserialize(um, dmr); + } +} + +void +Constructor::print_decl(FILE *out, string space, bool print_semi, + bool constraint_info, bool constrained) +{ + ostringstream oss; + print_decl(oss, space, print_semi, constraint_info, constrained); + fwrite(oss.str().data(), sizeof(char), oss.str().length(), out); +} + +void +Constructor::print_decl(ostream &out, string space, bool print_semi, + bool constraint_info, bool constrained) +{ + if (constrained && !send_p()) + return; + + out << space << type_name() << " {\n" ; + for (Vars_citer i = d_vars.begin(); i != d_vars.end(); i++) { + (*i)->print_decl(out, space + " ", true, constraint_info, constrained); + } + out << space << "} " << id2www(name()) ; + + if (constraint_info) { // Used by test drivers only. + if (send_p()) + out << ": Send True"; + else + out << ": Send False"; + } + + if (print_semi) + out << ";\n" ; +} + +void +Constructor::print_val(FILE *out, string space, bool print_decl_p) +{ + ostringstream oss; + print_val(oss, space, print_decl_p); + fwrite(oss.str().data(), sizeof(char), oss.str().length(), out); +} + +void +Constructor::print_val(ostream &out, string space, bool print_decl_p) +{ + if (print_decl_p) { + print_decl(out, space, false); + out << " = " ; + } + + out << "{ " ; + for (Vars_citer i = d_vars.begin(), e = d_vars.end(); i != e; + i++, (void)(i != e && out << ", ")) { + + DBG(cerr << (*i)->name() << " isa " << (*i)->type_name() << endl); + + (*i)->print_val(out, "", false); + } + + out << " }" ; + + if (print_decl_p) + out << ";\n" ; +} + +/** + * @deprecated + */ +void +Constructor::print_xml(FILE *out, string space, bool constrained) +{ + XMLWriter xml(space); + print_xml_writer(xml, constrained); + fwrite(xml.get_doc(), sizeof(char), xml.get_doc_size(), out); +} + +/** + * @deprecated + */ +void +Constructor::print_xml(ostream &out, string space, bool constrained) +{ + XMLWriter xml(space); + print_xml_writer(xml, constrained); + out << xml.get_doc(); +} + +class PrintFieldXMLWriter : public unary_function +{ + XMLWriter &d_xml; + bool d_constrained; +public: + PrintFieldXMLWriter(XMLWriter &x, bool c) + : d_xml(x), d_constrained(c) + {} + + void operator()(BaseType *btp) + { + btp->print_xml_writer(d_xml, d_constrained); + } +}; + +void +Constructor::print_xml_writer(XMLWriter &xml, bool constrained) +{ + if (constrained && !send_p()) + return; + + if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*)type_name().c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write " + type_name() + " element"); + + if (!name().empty()) + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "name", (const xmlChar*)name().c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name"); + + // DAP2 prints attributes first. For some reason we decided that DAP4 should + // print them second. No idea why... jhrg 8/15/14 + if (!is_dap4() && get_attr_table().get_size() > 0) + get_attr_table().print_xml_writer(xml); + + bool has_variables = (var_begin() != var_end()); + if (has_variables) + for_each(var_begin(), var_end(), PrintFieldXMLWriter(xml, constrained)); + + if (is_dap4()) + attributes()->print_dap4(xml); + +#if 0 + // Moved up above so that the DDX tests for various handles will still work. + // jhrg 8/15/14 + if (!is_dap4() && get_attr_table().get_size() > 0) + get_attr_table().print_xml_writer(xml); +#endif + + if (xmlTextWriterEndElement(xml.get_writer()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not end " + type_name() + " element"); +} + +class PrintDAP4FieldXMLWriter : public unary_function +{ + XMLWriter &d_xml; + bool d_constrained; +public: + PrintDAP4FieldXMLWriter(XMLWriter &x, bool c) : d_xml(x), d_constrained(c) {} + + void operator()(BaseType *btp) + { + btp->print_dap4(d_xml, d_constrained); + } +}; + + +void +Constructor::print_dap4(XMLWriter &xml, bool constrained) +{ + if (constrained && !send_p()) + return; + + if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*)type_name().c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write " + type_name() + " element"); + + if (!name().empty()) + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "name", (const xmlChar*)name().c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name"); + + bool has_variables = (var_begin() != var_end()); + if (has_variables) + for_each(var_begin(), var_end(), PrintDAP4FieldXMLWriter(xml, constrained)); + + attributes()->print_dap4(xml); + + if (xmlTextWriterEndElement(xml.get_writer()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not end " + type_name() + " element"); +} + + +bool +Constructor::check_semantics(string &msg, bool all) +{ + if (!BaseType::check_semantics(msg)) + return false; + + if (!unique_names(d_vars, name(), type_name(), msg)) + return false; + + if (all) + for (Vars_iter i = d_vars.begin(); i != d_vars.end(); i++) { + if (!(*i)->check_semantics(msg, true)) { + return false; + } + } + + return true; +} + +/** True if the instance can be flattened and printed as a single table + of values. For Arrays and Grids this is always false. For Structures + and Sequences the conditions are more complex. The implementation + provided by this class always returns false. Other classes should + override this implementation. + + @todo Change the name to is_flattenable or something like that. 05/16/03 + jhrg + + @brief Check to see whether this variable can be printed simply. + @return True if the instance can be printed as a single table of + values, false otherwise. */ +bool +Constructor::is_linear() +{ + return false; +} + +/** Set the \e in_selection property for this variable and all of its + children. + + @brief Set the \e in_selection property. + @param state Set the property value to \e state. */ +void +Constructor::set_in_selection(bool state) +{ + for (Vars_iter i = d_vars.begin(); i != d_vars.end(); i++) { + (*i)->set_in_selection(state); + } + + BaseType::set_in_selection(state); +} + + +void Constructor::transfer_attributes(AttrTable *at_container) +{ + AttrTable *at = at_container->get_attr_table(name()); + DBG(cerr << "Constructor::transfer_attributes() - processing " << name() << "' addr: "<< (void*) at << endl); + if (at) { + BaseType::transfer_attributes(at_container); + for (Vars_iter i = d_vars.begin(); i != d_vars.end(); i++) { + BaseType *bt = (*i); + bt->transfer_attributes(at); + } + + } +} + +AttrTable * +Constructor::make_dropped_vars_attr_table(vector *dropped_vars) { + DBG( cerr << __func__ << "() - BEGIN" << endl;); + + AttrTable *dv_table = NULL; + if(!dropped_vars->empty()){ + dv_table = new AttrTable; + dv_table->set_name("dap4:dropped_members"); + vector::iterator dvIter = dropped_vars->begin(); + vector::iterator dvEnd = dropped_vars->end(); + unsigned int i = 0; + for( ; dvIter!=dvEnd ; dvIter++, i++){ + BaseType *bt = (*dvIter); + AttrTable *bt_attr_table = new AttrTable(bt->get_attr_table()); + bt_attr_table->set_name(bt->name()); + string type_name = bt->type_name(); + if(bt->is_vector_type()){ + Array *array = dynamic_cast (bt); + if(array){ + type_name = array->prototype()->type_name(); + DBG( cerr << __func__ << "() - The variable " << bt->name() << " is an Array of '"<< type_name << "'" << endl;); + Array::Dim_iter d_iter = array->dim_begin(); + Array::Dim_iter end = array->dim_end(); + for( ; d_iter< end ; d_iter++){ + + ostringstream dim_size; + dim_size << (*d_iter).size; + bt_attr_table->append_attr( + "array_dimensions", + AttrType_to_String(Attr_uint32), + dim_size.str()); + } + } + } + bt_attr_table->append_attr("dap4:type","String", type_name); + dv_table->append_container(bt_attr_table,bt_attr_table->get_name()); + // Clear entry now that we're done. + (*dvIter) = 0; + } + } + DBG( cerr << __func__ << "() - END " << endl;); + return dv_table; + +} + + +/** @brief dumps information about this object + * + * Displays the pointer value of this instance and information about this + * instance. + * + * @param strm C++ i/o stream to dump the information to + * @return void + */ +void +Constructor::dump(ostream &strm) const +{ + strm << DapIndent::LMarg << "Constructor::dump - (" + << (void *)this << ")" << endl ; + DapIndent::Indent() ; + BaseType::dump(strm) ; + strm << DapIndent::LMarg << "vars: " << endl ; + DapIndent::Indent() ; + Vars_citer i = d_vars.begin() ; + Vars_citer ie = d_vars.end() ; + for (; i != ie; i++) { + (*i)->dump(strm) ; + } + DapIndent::UnIndent() ; + DapIndent::UnIndent() ; +} + +} // namespace libdap + diff --git a/Constructor.h b/Constructor.h new file mode 100644 index 0000000..79d8f6f --- /dev/null +++ b/Constructor.h @@ -0,0 +1,159 @@ + +// -*- mode: c++; c-basic-offset:4 -*- + +// This file is part of libdap, A C++ implementation of the OPeNDAP Data +// Access Protocol. + +// Copyright (c) 2002,2003 OPeNDAP, Inc. +// Author: James Gallagher +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// +// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112. + +#ifndef _constructor_h +#define _constructor_h 1 + +#include + +#include "BaseType.h" + +class Crc32; + +namespace libdap +{ + +class DMR; +class XMLWriter; +class D4StreamUnMarshaller; + +/** Common methods for all constructor types. */ +class Constructor: public BaseType +{ +private: + Constructor(); // No default ctor. + +protected: + std::vector d_vars; + + void m_duplicate(const Constructor &s); + BaseType *m_leaf_match(const string &name, btp_stack *s = 0); + BaseType *m_exact_match(const string &name, btp_stack *s = 0); + + Constructor(const string &name, const Type &type, bool is_dap4 = false); + Constructor(const string &name, const string &d, const Type &type, bool is_dap4 = false); + + Constructor(const Constructor ©_from); + +public: + typedef std::vector::const_iterator Vars_citer ; + typedef std::vector::iterator Vars_iter ; + typedef std::vector::reverse_iterator Vars_riter ; + + virtual ~Constructor(); + + Constructor &operator=(const Constructor &rhs); + virtual void transform_to_dap4(D4Group *root, Constructor *dest); + + virtual std::string FQN() const; + + virtual int element_count(bool leaves = false); + + virtual void set_send_p(bool state); + virtual void set_read_p(bool state); + + virtual unsigned int width(bool constrained = false) const; +#if 0 + virtual unsigned int width(bool constrained); +#endif + // TODO Rewrite these methods to use the back pointers and keep this + // for older code. + /// btp_stack no longer needed; use back pointers (BaseType::get_parent()) + virtual BaseType *var(const string &name, bool exact_match = true, btp_stack *s = 0); + /// @deprecated + virtual BaseType *var(const string &n, btp_stack &s); + + Vars_iter var_begin(); + Vars_iter var_end(); + Vars_riter var_rbegin(); + Vars_riter var_rend(); + Vars_iter get_vars_iter(int i); + BaseType *get_var_index(int i); + + virtual void add_var(BaseType *bt, Part part = nil); + virtual void add_var_nocopy(BaseType *bt, Part part = nil); + + virtual void del_var(const string &name); + virtual void del_var(Vars_iter i); + + virtual bool read(); + + // DAP2 + virtual void intern_data(ConstraintEvaluator &eval, DDS &dds); + virtual bool serialize(ConstraintEvaluator &eval, DDS &dds, Marshaller &m, bool ce_eval = true); + virtual bool deserialize(UnMarshaller &um, DDS *dds, bool reuse = false); + + // DAP4 + virtual void compute_checksum(Crc32 &checksum); + virtual void intern_data(/*Crc32 &checksum, DMR &dmr, ConstraintEvaluator &eval*/); + virtual void serialize(D4StreamMarshaller &m, DMR &dmr, /*ConstraintEvaluator &eval,*/ bool filter = false); + virtual void deserialize(D4StreamUnMarshaller &um, DMR &dmr); + + // Do not store values in memory as for C; users work with the C++ objects + virtual unsigned int val2buf(void *, bool) { + throw InternalErr(__FILE__, __LINE__, "Never use this method; see the programmer's guide documentation."); + } + virtual unsigned int buf2val(void **) { + throw InternalErr(__FILE__, __LINE__, "Never use this method; see the programmer's guide documentation."); + } + + virtual bool is_linear(); + virtual void set_in_selection(bool state); + + virtual void print_decl(ostream &out, string space = " ", + bool print_semi = true, + bool constraint_info = false, + bool constrained = false); + + virtual void print_xml(ostream &out, string space = " ", + bool constrained = false); + + void print_dap4(XMLWriter &xml, bool constrained = false); + + virtual void print_xml_writer(XMLWriter &xml, bool constrained = false); + + virtual void print_decl(FILE *out, string space = " ", + bool print_semi = true, + bool constraint_info = false, + bool constrained = false); + virtual void print_xml(FILE *out, string space = " ", + bool constrained = false); + + virtual void print_val(FILE *out, string space = "", + bool print_decl_p = true); + virtual void print_val(ostream &out, string space = "", + bool print_decl_p = true); + + virtual bool check_semantics(string &msg, bool all = false); + + virtual void transfer_attributes(AttrTable *at); + static AttrTable *make_dropped_vars_attr_table(vector *dropped_vars); + + virtual void dump(ostream &strm) const ; +}; + +} // namespace libdap + +#endif // _constructor_h diff --git a/D4AsyncUtil.cc b/D4AsyncUtil.cc new file mode 100644 index 0000000..ac4e73a --- /dev/null +++ b/D4AsyncUtil.cc @@ -0,0 +1,357 @@ +/* + * D4AsyncUtil.cc + * + * Created on: Feb 18, 2014 + * Author: ndp + */ + +#include "config.h" + +#include + +#include "XMLWriter.h" + +#include "Error.h" +#include "InternalErr.h" +#include "util.h" + +#include "D4AsyncUtil.h" +#include "DapXmlNamespaces.h" + +namespace libdap { + +const string D4AsyncUtil::STYLESHEET_REFERENCE_KEY = "DAP.Async.StyleSheet.Ref"; + +D4AsyncUtil::D4AsyncUtil() {} + +D4AsyncUtil::~D4AsyncUtil() {} + +/** + * @brief Print the AsyncRequired response to the. + * Print the AsyncRequired in XML form. + * @param xml Print to this XMLWriter instance + */ +void D4AsyncUtil::writeD4AsyncRequired(XMLWriter &xml, long expectedDelay, long responseLifetime, string *stylesheet_ref) { + + // ------ AsynchronousResponse Element and Attributes - BEGIN + + /* + int xmlTextWriterWriteAttributeNS (xmlTextWriterPtr writer, + const xmlChar * prefix, + const xmlChar * name, + const xmlChar * namespaceURI, + const xmlChar * content) + */ + + if(stylesheet_ref){ + string href = "href='" + *stylesheet_ref +"'"; + if(xmlTextWriterStartPI(xml.get_writer(), (const xmlChar*) "xml-stylesheet") < 0) + throw InternalErr(__FILE__, __LINE__, "Could not start XML Processing Instruction."); + if(xmlTextWriterWriteString(xml.get_writer(), (const xmlChar*) "type='text/xsl'") < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write Processing Instruction content."); + if(xmlTextWriterWriteString(xml.get_writer(), (const xmlChar*) " ") < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write Processing Instruction content."); + if(xmlTextWriterWriteString(xml.get_writer(), (const xmlChar*) href.c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write Processing Instruction content."); + if(xmlTextWriterEndPI(xml.get_writer()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not Close XML Processing Instruction."); + } + + DapXmlNamspaces dapns; + if (xmlTextWriterStartElementNS(xml.get_writer(), + (const xmlChar*)"dap", + (const xmlChar*) "AsynchronousResponse", + (const xmlChar*) dapns.getDapNamespaceString(DAP_4_0).c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write AsynchronousResponse element"); + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "status", (const xmlChar *) "required") < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute for 'status'"); + + + // ------ expectedDelay Element and Attributes + if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*) "dap:expectedDelay") < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write expectedDelay element"); + ostringstream oss; + oss << expectedDelay; + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "seconds", (const xmlChar*) oss.str().c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute for 'status'"); + if (xmlTextWriterEndElement(xml.get_writer()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not end expectedDelay element"); + // ------ expectedDelay Element and Attributes - END + + + // ------ responseLifetime Element and Attributes + if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*) "dap:responseLifetime") < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write expectedDelay element"); + ostringstream oss2; + oss2 << responseLifetime; + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "seconds", (const xmlChar*) oss2.str().c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute for 'seconds'"); + if (xmlTextWriterEndElement(xml.get_writer()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not end responseLifetime element"); + // ------ responseLifetime Element and Attributes - END + + + if (xmlTextWriterEndElement(xml.get_writer()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not end AsynchronousResponse element"); + // ------ AsynchronousResponse Element and Attributes - END +} + + +/** + * @brief Print the AsyncRequired response to the. + * Print the AsyncRequired in XML form. + * @param xml Print to this XMLWriter instance + */ +void D4AsyncUtil::writeD4AsyncAccepted(XMLWriter &xml, long expectedDelay, long responseLifetime, string asyncResourceUrl, string *stylesheet_ref) { + + // ------ AsynchronousResponse Element and Attributes - BEGIN + DapXmlNamspaces dapns; + + if(stylesheet_ref){ + string href = "href='" + *stylesheet_ref +"'"; + if(xmlTextWriterStartPI(xml.get_writer(), (const xmlChar*) "xml-stylesheet") < 0) + throw InternalErr(__FILE__, __LINE__, "Could not start XML Processing Instruction."); + if(xmlTextWriterWriteString(xml.get_writer(), (const xmlChar*) "type='text/xsl'") < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write Processing Instruction content."); + if(xmlTextWriterWriteString(xml.get_writer(), (const xmlChar*) " ") < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write Processing Instruction content."); + if(xmlTextWriterWriteString(xml.get_writer(), (const xmlChar*) href.c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write Processing Instruction content."); + if(xmlTextWriterEndPI(xml.get_writer()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not Close XML Processing Instruction."); + } + + if (xmlTextWriterStartElementNS(xml.get_writer(), + (const xmlChar*)"dap", + (const xmlChar*) "AsynchronousResponse", + (const xmlChar*) dapns.getDapNamespaceString(DAP_4_0).c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write AsynchronousResponse element"); + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "status", (const xmlChar *) "accepted") < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute for 'status'"); + + + // ------ expectedDelay Element and Attributes + if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*) "dap:expectedDelay") < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write expectedDelay element"); + ostringstream oss; + oss << expectedDelay; + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "seconds", (const xmlChar*) oss.str().c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute for 'seconds'"); + if (xmlTextWriterEndElement(xml.get_writer()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not end expectedDelay element"); + // ------ expectedDelay Element and Attributes - END + + + // ------ responseLifetime Element and Attributes + if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*) "dap:responseLifetime") < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write expectedDelay element"); + ostringstream oss2; + oss2 << responseLifetime; + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "seconds", (const xmlChar*) oss2.str().c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute for 'seconds'"); + if (xmlTextWriterEndElement(xml.get_writer()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not end responseLifetime element"); + // ------ responseLifetime Element and Attributes - END + + + // ------ link Element and Attributes + if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*) "dap:link") < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write expectedDelay element"); + + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "href", (const xmlChar*) asyncResourceUrl.c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute for 'href'"); + if (xmlTextWriterEndElement(xml.get_writer()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not end link element"); + // ------ link Element and Attributes - END + + + if (xmlTextWriterEndElement(xml.get_writer()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not end AsynchronousResponse element"); + // ------ AsynchronousResponse Element and Attributes - END +} + +/** + * @brief Print the AsyncRequired response to the. + * Print the AsyncRequired in XML form. + * @param xml Print to this XMLWriter instance + */ +void D4AsyncUtil::writeD4AsyncPending(XMLWriter &xml, string *stylesheet_ref) { + + // ------ AsynchronousResponse Element and Attributes - BEGIN + DapXmlNamspaces dapns; + + + if(stylesheet_ref){ + string href = "href='" + *stylesheet_ref +"'"; + if(xmlTextWriterStartPI(xml.get_writer(), (const xmlChar*) "xml-stylesheet") < 0) + throw InternalErr(__FILE__, __LINE__, "Could not start XML Processing Instruction."); + if(xmlTextWriterWriteString(xml.get_writer(), (const xmlChar*) "type='text/xsl'") < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write Processing Instruction content."); + if(xmlTextWriterWriteString(xml.get_writer(), (const xmlChar*) " ") < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write Processing Instruction content."); + if(xmlTextWriterWriteString(xml.get_writer(), (const xmlChar*) href.c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write Processing Instruction content."); + if(xmlTextWriterEndPI(xml.get_writer()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not Close XML Processing Instruction."); + } + + if (xmlTextWriterStartElementNS(xml.get_writer(), + (const xmlChar*)"dap", + (const xmlChar*) "AsynchronousResponse", + (const xmlChar*) dapns.getDapNamespaceString(DAP_4_0).c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write AsynchronousResponse element"); + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "status", (const xmlChar *) "pending") < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute for 'status'"); + + if (xmlTextWriterEndElement(xml.get_writer()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not end AsynchronousResponse element"); + // ------ AsynchronousResponse Element and Attributes - END +} + + +/** + * @brief Print the AsyncRequired response to the. + * Print the AsyncRequired in XML form. + * @param xml Print to this XMLWriter instance + */ +void D4AsyncUtil::writeD4AsyncResponseGone(XMLWriter &xml, string *stylesheet_ref) { + + // ------ AsynchronousResponse Element and Attributes - BEGIN + DapXmlNamspaces dapns; + + + if(stylesheet_ref){ + string href = "href='" + *stylesheet_ref +"'"; + if(xmlTextWriterStartPI(xml.get_writer(), (const xmlChar*) "xml-stylesheet") < 0) + throw InternalErr(__FILE__, __LINE__, "Could not start XML Processing Instruction."); + if(xmlTextWriterWriteString(xml.get_writer(), (const xmlChar*) "type='text/xsl'") < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write Processing Instruction content."); + if(xmlTextWriterWriteString(xml.get_writer(), (const xmlChar*) " ") < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write Processing Instruction content."); + if(xmlTextWriterWriteString(xml.get_writer(), (const xmlChar*) href.c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write Processing Instruction content."); + if(xmlTextWriterEndPI(xml.get_writer()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not Close XML Processing Instruction."); + } + + if (xmlTextWriterStartElementNS(xml.get_writer(), + (const xmlChar*)"dap", + (const xmlChar*) "AsynchronousResponse", + (const xmlChar*) dapns.getDapNamespaceString(DAP_4_0).c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write AsynchronousResponse element"); + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "status", (const xmlChar *) "gone") < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute for 'status'"); + + if (xmlTextWriterEndElement(xml.get_writer()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not end AsynchronousResponse element"); + // ------ AsynchronousResponse Element and Attributes - END +} + + +/** + * @brief Print the AsyncRequired response to the. + * Print the AsyncRequired in XML form. + * @param xml Print to this XMLWriter instance + */ +void D4AsyncUtil::writeD4AsyncResponseRejected(XMLWriter &xml, RejectReasonCode code, string description, string *stylesheet_ref) { + + // ------ AsynchronousResponse Element and Attributes - BEGIN + DapXmlNamspaces dapns; + + + if(stylesheet_ref){ + string href = "href='" + *stylesheet_ref +"'"; + if(xmlTextWriterStartPI(xml.get_writer(), (const xmlChar*) "xml-stylesheet") < 0) + throw InternalErr(__FILE__, __LINE__, "Could not start XML Processing Instruction."); + if(xmlTextWriterWriteString(xml.get_writer(), (const xmlChar*) "type='text/xsl'") < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write Processing Instruction content."); + if(xmlTextWriterWriteString(xml.get_writer(), (const xmlChar*) " ") < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write Processing Instruction content."); + if(xmlTextWriterWriteString(xml.get_writer(), (const xmlChar*) href.c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write Processing Instruction content."); + if(xmlTextWriterEndPI(xml.get_writer()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not Close XML Processing Instruction."); + } + + if (xmlTextWriterStartElementNS(xml.get_writer(), + (const xmlChar*)"dap", + (const xmlChar*) "AsynchronousResponse", + (const xmlChar*) dapns.getDapNamespaceString(DAP_4_0).c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write AsynchronousResponse element"); + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "status", (const xmlChar *) "rejected") < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute for 'status'"); + + // ------ reason Element and Attributes + if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*) "dap:reason") < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write reason element"); + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "code", (const xmlChar*) getRejectReasonCodeString(code).c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute for 'code'"); + if (xmlTextWriterEndElement(xml.get_writer()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not end reason element"); + // ------ reason Element and Attributes - END + + + // ------ description Element and Attributes + if (xmlTextWriterWriteElement(xml.get_writer(), (const xmlChar*) "dap:description", (const xmlChar*) description.c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write description element"); + + // ------ description Element and Attributes - END + + if (xmlTextWriterEndElement(xml.get_writer()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not end AsynchronousResponse element"); + // ------ AsynchronousResponse Element and Attributes - END + + +} + +string D4AsyncUtil::getRejectReasonCodeString(RejectReasonCode code){ + + string codeStr; + switch(code){ + case TIME: + codeStr = "time"; + break; + + case UNAVAILABLE: + codeStr = "unavailable"; + break; + + case PRIVILEGES: + codeStr = "privileges"; + break; + + case OTHER: + codeStr = "other"; + break; + + default: + throw InternalErr(__FILE__, __LINE__, "D4AsyncUtil::getRejectReasonCodeString() - Unrecognized reject_reason_code."); + break; + + } + return codeStr; +} + +// Unused paramters generate warnings, so I removed/commented them below. jhrg 3/12/14 +void D4AsyncUtil::writeD2AsyncRequired(XMLWriter &/*xml*/, long /*expectedDelay*/, long /*responseLifetime*/) { + throw InternalErr(__FILE__, __LINE__, "DAP2 Doesn't handle Async."); +} + +void D4AsyncUtil::writeD2AsyncAccepted(XMLWriter &, long , long , string /*asyncResourceUrl*/) { + throw InternalErr(__FILE__, __LINE__, "DAP2 Doesn't handle Async."); +} + + +void D4AsyncUtil::writeD2AsyncPending(XMLWriter &) { + throw InternalErr(__FILE__, __LINE__, "DAP2 Doesn't handle Async."); +} + +void D4AsyncUtil::writeD2AsyncResponseGone(XMLWriter &) { + throw InternalErr(__FILE__, __LINE__, "DAP2 Doesn't handle Async."); +} + +void D4AsyncUtil::writeD2AsyncResponseRejected(XMLWriter &, RejectReasonCode /*code*/, string /*description*/) { + throw InternalErr(__FILE__, __LINE__, "DAP2 Doesn't handle Async."); +} + +} /* namespace libdap */ diff --git a/D4AsyncUtil.h b/D4AsyncUtil.h new file mode 100644 index 0000000..f31f0f9 --- /dev/null +++ b/D4AsyncUtil.h @@ -0,0 +1,86 @@ +/* + * D4AsyncUtil.h + * + * Created on: Feb 18, 2014 + * Author: ndp + */ + +#ifndef D4ASYNCUTIL_H_ +#define D4ASYNCUTIL_H_ + +#include "XMLWriter.h" + +namespace libdap { + + +enum RejectReasonCode { TIME, UNAVAILABLE, PRIVILEGES, OTHER }; + + +class D4AsyncUtil { +private: +#if 0 + // Not used + string *d_stylesheet_ref; +#endif + +public: + D4AsyncUtil(); + virtual ~D4AsyncUtil(); + + const static string STYLESHEET_REFERENCE_KEY; + + + /** + * @brief Write the DAP4 AsyncRequired response. + * Print the AsyncRequired in XML form. + * @param xml Print to this XMLWriter instance + */ + void writeD4AsyncRequired(XMLWriter &xml, long expectedDelay, long responseLifetime, string *stylesheet_ref=0); + + + /** + * @brief Write the DAP4 AsyncAccepted response. + * Write the AsyncAccepted in XML form. + * @param xml Print to this XMLWriter instance + */ + void writeD4AsyncAccepted(XMLWriter &xml, long expectedDelay, long responseLifetime, string asyncResourceUrl, string *stylesheet_ref=0); + + /** + * @brief Write the DAP4 AsyncPending response. + * Write the DAP4 AsyncPending in XML form. + * @param xml Print to this XMLWriter instance + */ + void writeD4AsyncPending(XMLWriter &xml, string *stylesheet_ref=0); + + + /** + * @brief Write the DAP4 AsyncResponseGone response. + * Write the DAP4 AsyncRequired in XML form. + * @param xml Print to this XMLWriter instance + */ + void writeD4AsyncResponseGone(XMLWriter &xml, string *stylesheet_ref=0); + + /** + * @brief Write the DAP4 ResponseRejected response. + * Write the DAP4 AsyncRequired in XML form. + * @param xml Print to this XMLWriter instance + */ + void writeD4AsyncResponseRejected(XMLWriter &xml, RejectReasonCode code, string description, string *stylesheet_ref=0); + string getRejectReasonCodeString(RejectReasonCode code); + + /** + * @brief Write the DAP2 AsyncRequired response . + * Write the DAP2 AsyncRequired in XML form. + * @param xml Print to this XMLWriter instance + */ + void writeD2AsyncRequired(XMLWriter &xml, long expectedDelay, long responseLifetime); + void writeD2AsyncAccepted(XMLWriter &xml, long expectedDelay, long responseLifetime, string asyncResourceUrl); + void writeD2AsyncPending(XMLWriter &xml); + void writeD2AsyncResponseGone(XMLWriter &xml); + void writeD2AsyncResponseRejected(XMLWriter &xml, RejectReasonCode code, string description); + + +}; + +} /* namespace libdap */ +#endif /* D4ASYNCUTIL_H_ */ diff --git a/D4AttributeType.h b/D4AttributeType.h new file mode 100644 index 0000000..80960cf --- /dev/null +++ b/D4AttributeType.h @@ -0,0 +1,67 @@ +// -*- mode: c++; c-basic-offset:4 -*- + +// This file is part of libdap, A C++ implementation of the OPeNDAP Data +// Access Protocol. + +// Copyright (c) 2013 OPeNDAP, Inc. +// Author: James Gallagher +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112. + +#ifndef d4attributetype_h +#define d4attributetype_h + +/** D4AttributeType identifies the data type stored in a particular attribute + + @see D4Attributes +*/ + +enum D4AttributeType { + attr_null_c, + + attr_byte_c, + attr_int16_c, + attr_uint16_c, + attr_int32_c, // Added `attr_' to fix clash with IRIX 5.3. + attr_uint32_c, + attr_float32_c, + attr_float64_c, + attr_str_c, + attr_url_c, + + // Added for DAP4 + attr_int8_c, + attr_uint8_c, + + attr_int64_c, + attr_uint64_c, + +#if 0 + // just use attr_url_c. jhrg 8/15/13 + attr_url4_c, +#endif + + attr_enum_c, + attr_opaque_c, + + // These are specific to attributes while the other types are + // also supported by the variables. jhrg 4/17/13 + attr_container_c, + attr_otherxml_c +}; + +#endif /* d4attributetype_h */ diff --git a/D4Attributes.cc b/D4Attributes.cc new file mode 100644 index 0000000..11c2507 --- /dev/null +++ b/D4Attributes.cc @@ -0,0 +1,516 @@ +// -*- mode: c++; c-basic-offset:4 -*- + +// This file is part of libdap, A C++ implementation of the OPeNDAP Data +// Access Protocol. + +// Copyright (c) 2013 OPeNDAP, Inc. +// Author: James Gallagher +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112. + +#include "config.h" + +//#define DODS_DEBUG + +#include "D4Attributes.h" +#include "D4AttributeType.h" +#include "InternalErr.h" + +#include "AttrTable.h" + +#include "util.h" +#include "debug.h" + +namespace libdap { + +/** Convert an AttrType to it's string representation. + @param at The Attribute Type. + @return The type's string representation */ +string D4AttributeTypeToString(D4AttributeType at) +{ + switch(at) { + case attr_null_c: + return "null"; + + case attr_byte_c: + return "Byte"; + + case attr_int16_c: + return "Int16"; + + case attr_uint16_c: + return "UInt16"; + + case attr_int32_c: + return "Int32"; + + case attr_uint32_c: + return "UInt32"; + + case attr_float32_c: + return "Float32"; + + case attr_float64_c: + return "Float64"; + + case attr_str_c: + return "String"; + + case attr_url_c: + return "Url"; + + // Added for DAP4 + case attr_int8_c: + return "Int8"; + + case attr_uint8_c: + return "UInt8"; + + case attr_int64_c: + return "Int64"; + + case attr_uint64_c: + return "UInt64"; + + case attr_enum_c: + return "Enum"; + + case attr_opaque_c: + return "Opaque"; + + // These are specific to attributes while the other types are + // also supported by the variables. jhrg 4/17/13 + case attr_container_c: + return "Container"; + + case attr_otherxml_c: + return "OtherXML"; + + default: + throw InternalErr(__FILE__, __LINE__, "Unsupported attribute type"); + } +} + +D4AttributeType StringToD4AttributeType(string s) +{ + downcase(s); + + if (s == "container") + return attr_container_c; + + else if (s == "byte") + return attr_byte_c; + else if (s == "int8") + return attr_int8_c; + else if (s == "uint8") + return attr_uint8_c; + else if (s == "int16") + return attr_int16_c; + else if (s == "uint16") + return attr_uint16_c; + else if (s == "int32") + return attr_int32_c; + else if (s == "uint32") + return attr_uint32_c; + else if (s == "int64") + return attr_int64_c; + else if (s == "uint64") + return attr_uint64_c; + + else if (s == "float32") + return attr_float32_c; + else if (s == "float64") + return attr_float64_c; + + else if (s == "string") + return attr_str_c; + else if (s == "url") + return attr_url_c; + else if (s == "otherxml") + return attr_otherxml_c; + else + return attr_null_c; +} + +void +D4Attribute::m_duplicate(const D4Attribute &src) +{ + d_name = src.d_name; + d_type = src.d_type; + d_values = src.d_values; + if (src.d_attributes) + d_attributes = new D4Attributes(*src.d_attributes); + else + d_attributes = 0; +} + +D4Attribute::D4Attribute(const D4Attribute &src) +{ + m_duplicate(src); +} + +D4Attribute::~D4Attribute() +{ + delete d_attributes; +} + +D4Attribute & +D4Attribute::operator=(const D4Attribute &rhs) +{ + if (this == &rhs) return *this; + m_duplicate(rhs); + return *this; +} + +D4Attributes * +D4Attribute::attributes() +{ + if (!d_attributes) d_attributes = new D4Attributes(); + return d_attributes; +} + +/** @brief copy attributes from DAP2 to DAP4 + * + * Given a DAP2 AttrTable, copy all of its attributes into + * this DAP4 D4Attributes object as D4Attribute object instances. + * + * + * @param at Read the DAP2 attributes from here. + */ +void +D4Attributes::transform_to_dap4(AttrTable &at) +{ + // for every attribute in at, copy it to this. + for (AttrTable::Attr_iter i = at.attr_begin(), e = at.attr_end(); i != e; ++i) { + string name = at.get_name(i); + AttrType type = at.get_attr_type(i); + + switch (type) { + case Attr_container: { + D4Attribute *a = new D4Attribute(name, attr_container_c); + D4Attributes *attributes = a->attributes(); // allocates a new object + attributes->transform_to_dap4(*at.get_attr_table(i)); + add_attribute_nocopy(a); + break; + } + case Attr_byte: { + D4Attribute *a = new D4Attribute(name, attr_byte_c); + a->add_value_vector(*at.get_attr_vector(i)); + add_attribute_nocopy(a); + break; + } + case Attr_int16: { + D4Attribute *a = new D4Attribute(name, attr_int16_c); + a->add_value_vector(*at.get_attr_vector(i)); + add_attribute_nocopy(a); + break; + } + case Attr_uint16: { + D4Attribute *a = new D4Attribute(name, attr_uint16_c); + a->add_value_vector(*at.get_attr_vector(i)); + add_attribute_nocopy(a); + break; + } + case Attr_int32: { + D4Attribute *a = new D4Attribute(name, attr_int32_c); + a->add_value_vector(*at.get_attr_vector(i)); + add_attribute_nocopy(a); + break; + } + case Attr_uint32: { + D4Attribute *a = new D4Attribute(name, attr_uint32_c); + a->add_value_vector(*at.get_attr_vector(i)); + add_attribute_nocopy(a); + break; + } + case Attr_float32: { + D4Attribute *a = new D4Attribute(name, attr_float32_c); + a->add_value_vector(*at.get_attr_vector(i)); + add_attribute_nocopy(a); + break; + } + case Attr_float64: { + D4Attribute *a = new D4Attribute(name, attr_float64_c); + a->add_value_vector(*at.get_attr_vector(i)); + add_attribute_nocopy(a); + break; + } + case Attr_string: { + D4Attribute *a = new D4Attribute(name, attr_str_c); + a->add_value_vector(*at.get_attr_vector(i)); + add_attribute_nocopy(a); + break; + } + case Attr_url: { + D4Attribute *a = new D4Attribute(name, attr_url_c); + a->add_value_vector(*at.get_attr_vector(i)); + add_attribute_nocopy(a); + break; + } + case Attr_other_xml: { + D4Attribute *a = new D4Attribute(name, attr_otherxml_c); + a->add_value_vector(*at.get_attr_vector(i)); + add_attribute_nocopy(a); + break; + } + default: + throw InternalErr(__FILE__, __LINE__, "Unknown DAP2 attribute type in D4Attributes::copy_from_dap2()"); + } + } +} + + +AttrType get_dap2_AttrType(D4AttributeType d4_type){ + switch (d4_type) { + case attr_container_c: { return Attr_container; } + case attr_byte_c: { return Attr_byte; } + case attr_int16_c: { return Attr_int16; } + case attr_uint16_c: { return Attr_uint16; } + case attr_int32_c: { return Attr_int32; } + case attr_uint32_c: { return Attr_uint32; } + case attr_float32_c: { return Attr_float32; } + case attr_float64_c: { return Attr_float64; } + case attr_str_c: { return Attr_string; } + case attr_url_c: { return Attr_url; } + case attr_otherxml_c: { return Attr_other_xml; } + default: + throw InternalErr(__FILE__, __LINE__, "Unknown DAP4 attribute"); + } +} + + +void +D4Attributes::load_AttrTable(AttrTable *d2_attr_table, D4Attributes *d4_attrs) +{ + // cerr << __func__ << "() - Loading attribute table: '" << d2_attr_table->get_name() << "' addr: " << (void *)d2_attr_table << endl; + + // for every attribute in at, copy it to this. + for ( D4Attributes::D4AttributesIter i = d4_attrs->attribute_begin(), e = d4_attrs->attribute_end(); i != e; ++i) { + string name = (*i)->name(); + D4AttributeType d4_attr_type = (*i)->type(); + AttrType d2_attr_type = get_dap2_AttrType(d4_attr_type); + string d2_attr_type_name = AttrType_to_String(d2_attr_type); + + D4Attribute::D4AttributeIter vitr =(*i)->value_begin(); + D4Attribute::D4AttributeIter end =(*i)->value_end(); + + vector values; + for(;vitr!=end; vitr++){ + values.push_back((*vitr)); + } + + switch (d4_attr_type) { + case attr_container_c: { + // Attr_container + AttrTable *child_attr_table = new AttrTable(); + child_attr_table->set_name(name); + // cerr << __func__ << "() - Created child attribute table: " << name << " addr: " << (void *)child_attr_table << endl; + load_AttrTable(child_attr_table,(*i)->attributes()); + d2_attr_table->append_container(child_attr_table,name); + break; + } + default:{ + // cerr << __func__ << "() - "<< name << " has " << values.size() << " value(s). d2_attr_type_name: " << d2_attr_type_name << endl; + d2_attr_table->append_attr(name,d2_attr_type_name, &values); + break; + } + } + } +} + + +/** @brief copy attributes from DAP4 to DAP2 + * + * Given a DAP4 AttrTable, copy all of its attributes into a DAP4 D4Attributes + * object. + * + * @param at Read the DAP2 attributes from here. + */ +AttrTable *D4Attributes::get_AttrTable(const string name) +{ + AttrTable *my_pretty_pony = new AttrTable(); + load_AttrTable(my_pretty_pony, this); + my_pretty_pony->set_name(name); + return my_pretty_pony; +} + + +D4Attribute * +D4Attributes::find_depth_first(const string &name, D4AttributesIter i) +{ + if (i == attribute_end()) + return 0; + else if ((*i)->name() == name) + return *i; + else if ((*i)->type() == attr_container_c) + return find_depth_first(name, (*i)->attributes()->attribute_begin()); + else + return find_depth_first(name, ++i); +} + +D4Attribute * +D4Attributes::find(const string &name) +{ + return find_depth_first(name, attribute_begin()); +} + +/** Return a pointer to the D4Attribute object that has the given FQN. + * @note A FQN for an attribute is a series of names separated by dots. + */ +D4Attribute * +D4Attributes::get(const string &fqn) +{ + // name1.name2.name3 + // name1 + // name1.name2 + size_t pos = fqn.find('.'); + string part = fqn.substr(0, pos); + string rest= ""; + + if (pos != string::npos) + rest = fqn.substr(pos + 1); + + DBG(cerr << "part: '" << part << "'; rest: '" << rest << "'" << endl); + + if (!part.empty()) { + if (!rest.empty()) { + D4AttributesIter i = attribute_begin(); + while (i != attribute_end()) { + if ((*i)->name() == part && (*i)->type() == attr_container_c) + return (*i)->attributes()->get(rest); + ++i; + } + } + else { + D4AttributesIter i = attribute_begin(); + while (i != attribute_end()) { + if ((*i)->name() == part) + return (*i); + ++i; + } + } + } + + return 0; +} + +void +D4Attribute::print_dap4(XMLWriter &xml) const +{ + if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*) "Attribute") < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write Attribute element"); + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "name", (const xmlChar*)name().c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name"); + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "type", (const xmlChar*) D4AttributeTypeToString(type()).c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute for type"); + + switch (type()) { + case attr_container_c: + if (!d_attributes) + throw InternalErr(__FILE__, __LINE__, "Null Attribute container"); + d_attributes->print_dap4(xml); + break; + + case attr_otherxml_c: + if (num_values() != 1) + throw Error("OtherXML attributes cannot be vector-valued."); + if (xmlTextWriterWriteRaw(xml.get_writer(), (const xmlChar*) value(0).c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write OtherXML value"); + break; + + default: { + // Assume only valid types make it into instances + D4AttributeCIter i = d_values.begin();//value_begin(); + while (i != d_values.end()) { + if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*) "Value") < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write value element"); + + if (xmlTextWriterWriteString(xml.get_writer(), (const xmlChar*) (*i++).c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute value"); + + if (xmlTextWriterEndElement(xml.get_writer()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not end value element"); + } + + break; + } + } + + if (xmlTextWriterEndElement(xml.get_writer()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not end Attribute element"); +} + +/** @brief dumps information about this object + * + * Displays the pointer value of this instance and then displays information + * about this base type. + * + * @param strm C++ i/o stream to dump the information to + * @return void + */ +void +D4Attribute::dump(ostream &strm) const +{ + strm << DapIndent::LMarg << "D4Attribute::dump - (" << (void *)this << ")" << endl; + + DapIndent::Indent() ; + + XMLWriter xml; + print_dap4(xml); + strm << DapIndent::LMarg << xml.get_doc() << flush; + + DapIndent::UnIndent() ; +} + + +void +D4Attributes::print_dap4(XMLWriter &xml) const +{ + if (empty()) + return; + + D4AttributesCIter i = d_attrs.begin(); + while (i != d_attrs.end()) { + (*i++)->print_dap4(xml); + } +} + +/** @brief dumps information about this object + * + * Displays the pointer value of this instance and then displays information + * about this base type. + * + * @param strm C++ i/o stream to dump the information to + * @return void + */ +void +D4Attributes::dump(ostream &strm) const +{ + strm << DapIndent::LMarg << "D4Attributes::dump - (" << (void *)this << ")" << endl; + + DapIndent::Indent() ; + + XMLWriter xml; + print_dap4(xml); + strm << DapIndent::LMarg << xml.get_doc() << flush; + + DapIndent::UnIndent() ; +} + + +} // namespace libdap + diff --git a/D4Attributes.h b/D4Attributes.h new file mode 100644 index 0000000..a543eb6 --- /dev/null +++ b/D4Attributes.h @@ -0,0 +1,169 @@ + +// -*- mode: c++; c-basic-offset:4 -*- + +// This file is part of libdap, A C++ implementation of the OPeNDAP Data +// Access Protocol. + +// Copyright (c) 2013 OPeNDAP, Inc. +// Author: James Gallagher +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112. + +#ifndef _d4attributes_h +#define _d4attributes_h 1 + +#include +#include + +#include "DapObj.h" +#include "D4AttributeType.h" +#include "XMLWriter.h" + +using namespace std; + +namespace libdap +{ + +class AttrTable; +class D4Attributes; + +class D4Attribute : public DapObj { + string d_name; + D4AttributeType d_type; // Attributes are limited to the simple types + + // If d_type is attr_container_c is true, use d_attributes to read + // the contained attributes, otherwise use d_values to read the vector + // of values. + D4Attributes *d_attributes; + + // IF d_type is attr_otherxml_c, the first string in d_values holds the + // XML, otherwise, the strings hold attributes of type d_type. + vector d_values; + + // perform a deep copy + void m_duplicate(const D4Attribute &src); + +public: + typedef vector::iterator D4AttributeIter; + typedef vector::const_iterator D4AttributeCIter; + + D4Attribute() : d_name(""), d_type(attr_null_c), d_attributes(0) {} + D4Attribute(const string &name, D4AttributeType type) + : d_name(name), d_type(type), d_attributes(0) {} + + D4Attribute(const D4Attribute &src); + ~D4Attribute(); + D4Attribute &operator=(const D4Attribute &rhs); + + string name() const { return d_name; } + void set_name(const string &name) { d_name = name; } + + D4AttributeType type() const { return d_type; } + void set_type(D4AttributeType type) { d_type = type; } + + void add_value(const string &value) { d_values.push_back(value); } + void add_value_vector(const vector &values) { d_values = values; } + + D4AttributeIter value_begin() { return d_values.begin(); } + D4AttributeIter value_end() { return d_values.end(); } + + unsigned int num_values() const { return d_values.size(); } + string value(unsigned int i) const { return d_values[i]; } + + D4Attributes *attributes(); + + void print_dap4(XMLWriter &xml) const; + + virtual void dump(ostream &strm) const; +}; + +class D4Attributes : public DapObj { +public: + typedef vector::iterator D4AttributesIter; + typedef vector::const_iterator D4AttributesCIter; + +private: + vector d_attrs; + + void m_duplicate(const D4Attributes &src) { + D4AttributesCIter i = src.d_attrs.begin(); + while (i != src.d_attrs.end()) { + d_attrs.push_back(new D4Attribute(**i++)); // deep copy + } + } + + D4Attribute *find_depth_first(const string &name, D4AttributesIter i); + +public: + + D4Attributes() {} + D4Attributes(const D4Attributes &rhs) { + m_duplicate(rhs); + } + + virtual ~D4Attributes() { + D4AttributesIter i = d_attrs.begin(); + while(i != d_attrs.end()) { + delete *i++; + } + } + + D4Attributes &operator=(const D4Attributes &rhs) { + if (this == &rhs) return *this; + m_duplicate(rhs); + return *this; + } + + void transform_to_dap4(AttrTable &at); + + AttrTable *get_AttrTable(const std::string name); + static void load_AttrTable(AttrTable *d2_attr_table, D4Attributes *d4_attrs); + + bool empty() const { return d_attrs.empty(); } + + void add_attribute(D4Attribute *attr) { + d_attrs.push_back(new D4Attribute(*attr)); + } + void add_attribute_nocopy(D4Attribute *attr) { + d_attrs.push_back(attr); + } + + /// Get an iterator to the start of the enumerations + D4AttributesIter attribute_begin() { return d_attrs.begin(); } + + /// Get an iterator to the end of the enumerations + D4AttributesIter attribute_end() { return d_attrs.end(); } + + D4Attribute *find(const string &name); + D4Attribute *get(const string &fqn); + + // D4Attribute *find_container(const string &name); + // D4Attribute *get_container(const string &fqn); + + // Might add erase() + + void print_dap4(XMLWriter &xml) const; + + virtual void dump(ostream &strm) const; +}; + +string D4AttributeTypeToString(D4AttributeType at); +D4AttributeType StringToD4AttributeType(string s); + +} // namespace libdap + +#endif // _d4attributes_h diff --git a/D4BaseTypeFactory.cc b/D4BaseTypeFactory.cc new file mode 100644 index 0000000..c4f8b6b --- /dev/null +++ b/D4BaseTypeFactory.cc @@ -0,0 +1,298 @@ + +// -*- mode: c++; c-basic-offset:4 -*- + +// This file is part of libdap, A C++ implementation of the OPeNDAP Data +// Access Protocol. + +// Copyright (c) 2005 OPeNDAP, Inc. +// Author: James Gallagher +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// +// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112. + +#include "config.h" + +#include + +#include "BaseType.h" +#include "Type.h" + +#include "Byte.h" +#include "Int8.h" +#include "Int16.h" +#include "UInt16.h" +#include "Int32.h" +#include "UInt32.h" + +#include "Int64.h" +#include "UInt64.h" + +#include "Float32.h" +#include "Float64.h" + +#include "D4Enum.h" + +#include "Str.h" +#include "Url.h" + +#include "D4Opaque.h" + +#include "Array.h" + +#include "Structure.h" +#include "D4Sequence.h" + +#include "D4Group.h" + +#include "D4BaseTypeFactory.h" +#include "debug.h" + +namespace libdap { + +BaseType *D4BaseTypeFactory::NewVariable(Type t, const string &name) const +{ + switch (t) { + case dods_byte_c: + return NewByte(name); + case dods_char_c: + return NewChar(name); + case dods_uint8_c: + return NewUInt8(name); + case dods_int8_c: + return NewInt8(name); + + case dods_int16_c: + return NewInt16(name); + case dods_uint16_c: + return NewUInt16(name); + case dods_int32_c: + return NewInt32(name); + case dods_uint32_c: + return NewUInt32(name); + + case dods_int64_c: + return NewInt64(name); + case dods_uint64_c: + return NewUInt64(name); + + case dods_float32_c: + return NewFloat32(name); + case dods_float64_c: + return NewFloat64(name); + + case dods_enum_c: + return NewEnum(name); + + case dods_str_c: + return NewStr(name); + case dods_url_c: + return NewURL(name); + + case dods_opaque_c: + return NewOpaque(name); + + case dods_structure_c: + return NewStructure(name); + + case dods_sequence_c: + return NewD4Sequence(name); + + case dods_array_c: + return NewArray(name); + + case dods_group_c: + return NewGroup(name); + + default: + throw InternalErr(__FILE__, __LINE__, "Unimplemented type in DAP4"); + } +} + +Byte * +D4BaseTypeFactory::NewByte(const string &n) const +{ + Byte *b = new Byte(n); + b->set_is_dap4(true); + return b; +} + +// Use the type constants specific to Char and UInt8 so the print reps will +// match the server's idea of the types. +Byte * +D4BaseTypeFactory::NewChar(const string &n) const +{ + Byte *b = new Byte(n); + b->set_type(dods_char_c); + b->set_is_dap4(true); + return b; +} + +Byte * +D4BaseTypeFactory::NewUInt8(const string &n) const +{ + Byte *b = new Byte(n); + b->set_type(dods_uint8_c); + b->set_is_dap4(true); + return b; +} + +Int8 * +D4BaseTypeFactory::NewInt8(const string &n) const +{ + Int8 *b = new Int8(n); + b->set_is_dap4(true); + return b; +} + +Int16 * +D4BaseTypeFactory::NewInt16(const string &n) const +{ + Int16 *b = new Int16(n); + b->set_is_dap4(true); + return b; +} + +UInt16 * +D4BaseTypeFactory::NewUInt16(const string &n) const +{ + UInt16 *b = new UInt16(n); + b->set_is_dap4(true); + return b; +} + +Int32 * +D4BaseTypeFactory::NewInt32(const string &n) const +{ + DBG(cerr << "Inside DAP4BaseTypeFactory::NewInt32" << endl); + Int32 *b = new Int32(n); + b->set_is_dap4(true); + return b; +} + +UInt32 * +D4BaseTypeFactory::NewUInt32(const string &n) const +{ + UInt32 *b = new UInt32(n); + b->set_is_dap4(true); + return b; +} + +Int64 * +D4BaseTypeFactory::NewInt64(const string &n) const +{ + DBG(cerr << "Inside DAP4BaseTypeFactory::NewInt64" << endl); + Int64 *b = new Int64(n); + b->set_is_dap4(true); + return b; +} + +UInt64 * +D4BaseTypeFactory::NewUInt64(const string &n) const +{ + UInt64 *b = new UInt64(n); + b->set_is_dap4(true); + return b; +} + +Float32 * +D4BaseTypeFactory::NewFloat32(const string &n) const +{ + Float32 *b = new Float32(n); + b->set_is_dap4(true); + return b; +} + +Float64 * +D4BaseTypeFactory::NewFloat64(const string &n) const +{ + Float64 *b = new Float64(n); + b->set_is_dap4(true); + return b; +} + +/** + * Enums need a name and the name of an enumeration that was defined by the + * dataset. If the later is not known, it must be set before the enum is used. + * @param name + * @param enum_name + * @return + */ +D4Enum * +D4BaseTypeFactory::NewEnum(const string &name, Type type) const +{ + return new D4Enum(name, type); +} + + +Str * +D4BaseTypeFactory::NewStr(const string &n) const +{ + Str *b = new Str(n); + b->set_is_dap4(true); + return b; +} + +Url * +D4BaseTypeFactory::NewUrl(const string &n) const +{ + Url *b = new Url(n); + b->set_is_dap4(true); + return b; +} + +D4Opaque * +D4BaseTypeFactory::NewOpaque(const string &n) const +{ + return new D4Opaque(n); +} + +/** Note that this method is called NewURL - URL in caps. + */ +Url * +D4BaseTypeFactory::NewURL(const string &n) const +{ + Url *b = new Url(n); + b->set_is_dap4(true); + return b; +} + +Array * +D4BaseTypeFactory::NewArray(const string &n, BaseType *v) const +{ + return new Array(n, v, true /* is_dap4 */); +} + +Structure * +D4BaseTypeFactory::NewStructure(const string &n) const +{ + Structure *b = new Structure(n); + b->set_is_dap4(true); + return b; +} + +D4Sequence * +D4BaseTypeFactory::NewD4Sequence(const string &n) const +{ + return new D4Sequence(n); +} + +D4Group * +D4BaseTypeFactory::NewGroup(const string &n) const +{ + return new D4Group(n); +} + +} // namespace libdap diff --git a/D4BaseTypeFactory.h b/D4BaseTypeFactory.h new file mode 100644 index 0000000..b302c16 --- /dev/null +++ b/D4BaseTypeFactory.h @@ -0,0 +1,123 @@ + +// -*- mode: c++; c-basic-offset:4 -*- + +// This file is part of libdap, A C++ implementation of the OPeNDAP Data +// Access Protocol. + +// Copyright (c) 2012 OPeNDAP, Inc. +// Author: James Gallagher +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// +// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112. + +#ifndef dap4_base_type_factory_h +#define dap4_base_type_factory_h + +#include + +#include "BaseTypeFactory.h" +#include "Type.h" + +// Class declarations; Make sure to include the corresponding headers in the +// implementation file. + +namespace libdap +{ + +class Byte; +class Int8; +class Int16; +class UInt16; +class Int32; +class UInt32; +class Int64; +class UInt64; + +class Float32; +class Float64; + +class Str; +class Url; + +class D4Enum; +class D4Opaque; + +class Array; + +class Structure; +class D4Sequence; + +class D4Group; + +class BaseType; + +/** + * Return instances of objects that are to be stored in a DDS for a + * DAP4 dataset. + * + */ +class D4BaseTypeFactory: public BaseTypeFactory +{ +public: + D4BaseTypeFactory() + {} + virtual ~D4BaseTypeFactory() + {} + + virtual BaseType *NewVariable(Type t, const string &name) const; + + virtual BaseTypeFactory *ptr_duplicate() const { + return new D4BaseTypeFactory; + } + + virtual Byte *NewByte(const string &n = "") const; + + // The Int8 types are new for DAP4 + virtual Int8 *NewInt8(const string &n = "") const; + virtual Byte *NewChar(const string &n = "") const; + virtual Byte *NewUInt8(const string &n = "") const; + + virtual Int16 *NewInt16(const string &n = "") const; + virtual UInt16 *NewUInt16(const string &n = "") const; + virtual Int32 *NewInt32(const string &n = "") const; + virtual UInt32 *NewUInt32(const string &n = "") const; + + // New for DAP4 + virtual Int64 *NewInt64(const string &n = "") const; + virtual UInt64 *NewUInt64(const string &n = "") const; + + virtual Float32 *NewFloat32(const string &n = "") const; + virtual Float64 *NewFloat64(const string &n = "") const; + + virtual D4Enum *NewEnum(const string &n = "", Type type = dods_null_c) const; + + virtual Str *NewStr(const string &n = "") const; + virtual Url *NewUrl(const string &n = "") const; + virtual Url *NewURL(const string &n = "") const; + + virtual D4Opaque *NewOpaque(const string &n = "") const; + + virtual Array *NewArray(const string &n = "", BaseType *v = 0) const; + + virtual Structure *NewStructure(const string &n = "") const; + virtual D4Sequence *NewD4Sequence(const string &n = "") const; + + virtual D4Group *NewGroup(const string &n = "") const; +}; + +} // namespace libdap + +#endif // dap4_base_type_factory_h diff --git a/D4Connect.cc b/D4Connect.cc new file mode 100644 index 0000000..f5725e0 --- /dev/null +++ b/D4Connect.cc @@ -0,0 +1,556 @@ +// -*- mode: c++; c-basic-offset:4 -*- + +// This file is part of libdap, A C++ implementation of the OPeNDAP Data +// Access Protocol. + +// Copyright (c) 2002,2003 OPeNDAP, Inc. +// Author: James Gallagher +// Dan Holloway +// Reza Nekovei +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// +// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112. + +// (c) COPYRIGHT URI/MIT 1994-2002 +// Please read the full copyright statement in the file COPYRIGHT_URI. +// +// Authors: +// jhrg,jimg James Gallagher +// dan Dan Holloway +// reza Reza Nekovei + +#include "config.h" +// #define DODS_DEBUG 1 + + + +#include +#include +#include + +#include "D4Connect.h" +#include "HTTPConnect.h" +#include "Response.h" +#include "DMR.h" +#include "D4Group.h" + +#include "D4ParserSax2.h" +#include "chunked_stream.h" +#include "chunked_istream.h" +#include "D4StreamUnMarshaller.h" + +#include "escaping.h" +#include "mime_util.h" +#include "debug.h" + + + +using namespace std; + +namespace libdap { + +/** This private method process data from both local and remote sources. It + exists to eliminate duplication of code. */ +void D4Connect::process_dmr(DMR &dmr, Response &rs) +{ + DBG(cerr << "Entering D4Connect::process_dmr" << endl); + + dmr.set_dap_version(rs.get_protocol()); + + DBG(cerr << "Entering process_data. Response.getVersion() = " << rs.get_version() << endl); + switch (rs.get_type()) { + case dap4_error: { +#if 0 + Error e; + if (!e.parse(rs.get_stream())) + throw InternalErr(__FILE__, __LINE__, "Could not parse the Error object returned by the server!"); + throw e; +#endif + throw InternalErr(__FILE__, __LINE__, "DAP4 errors not processed yet: FIXME!"); + } + + case web_error: + // Web errors (those reported in the return document's MIME header) + // are processed by the WWW library. + throw InternalErr(__FILE__, __LINE__, + "An error was reported by the remote httpd; this should have been processed by HTTPConnect.."); + + case dap4_dmr: { + // parse the DMR + try { + D4ParserSax2 parser; + // When parsing a data response, we use the permissive mode of the DMR parser + // (which allows Map elements to reference Arrays that are not in the DMR). + // Do not use that mode when parsing the DMR response - assume the DMR is + // valid. jhrg 4/13/16 + parser.intern(*rs.get_cpp_stream(), &dmr); + } + catch (Error &e) { + cerr << "Exception: " << e.get_error_message() << endl; + return; + } + catch (std::exception &e) { + cerr << "Exception: " << e.what() << endl; + return; + } + catch (...) { + cerr << "Exception: unknown error" << endl; + return; + } + + return; + } + + default: + throw Error("Unknown response type"); + } +} + +/** This private method process data from both local and remote sources. It + exists to eliminate duplication of code. */ +void D4Connect::process_data(DMR &data, Response &rs) +{ + DBG(cerr << "Entering D4Connect::process_data" << endl); + + assert(rs.get_cpp_stream()); // DAP4 code uses cpp streams + + data.set_dap_version(rs.get_protocol()); + + DBG(cerr << "Entering process_data. Response.getVersion() = " << rs.get_version() << endl); + switch (rs.get_type()) { + case dap4_error: { +#if 0 + Error e; + if (!e.parse(rs.get_cpp_stream())) + throw InternalErr(__FILE__, __LINE__, "Could not parse the Error object returned by the server!"); + throw e; +#endif + throw InternalErr(__FILE__, __LINE__, "DAP4 errors not processed yet: FIXME!"); + } + + case web_error: + // Web errors (those reported in the return document's MIME header) + // are processed by the WWW library. + throw InternalErr(__FILE__, __LINE__, + "An error was reported by the remote httpd; this should have been processed by HTTPConnect.."); + + case dap4_data: { +#if BYTE_ORDER_PREFIX + // Read the byte-order byte; used later on + char byte_order; + *rs.get_cpp_stream() >> byte_order; + //if (debug) cerr << "Byte order: " << ((byte_order) ? "big endian" : "little endian") << endl; +#endif + // get a chunked input stream +#if BYTE_ORDER_PREFIX + chunked_istream cis(*rs.get_cpp_stream(), 1024, byte_order); +#else + chunked_istream cis(*(rs.get_cpp_stream()), CHUNK_SIZE); +#endif + // parse the DMR, stopping when the boundary is found. + try { + // force chunk read + // get chunk size + int chunk_size = cis.read_next_chunk(); + if (chunk_size < 0) + throw Error("Found an unexpected end of input (EOF) while reading a DAP4 data response. (1)"); + + // get chunk + char chunk[chunk_size]; + cis.read(chunk, chunk_size); + // parse char * with given size + D4ParserSax2 parser; + // permissive mode allows references to Maps that are not in the response. + // Use this mode when parsing a data response (but not the DMR). jhrg 4/13/16 + parser.set_strict(false); + + // '-2' to discard the CRLF pair + parser.intern(chunk, chunk_size - 2, &data); + } + catch (Error &e) { + cerr << "Exception: " << e.get_error_message() << endl; + return; + } + catch (std::exception &e) { + cerr << "Exception: " << e.what() << endl; + return; + } + catch (...) { + cerr << "Exception: unknown error" << endl; + return; + } + +#if BYTE_ORDER_PREFIX + D4StreamUnMarshaller um(cis, byte_order); +#else + D4StreamUnMarshaller um(cis, cis.twiddle_bytes()); +#endif + data.root()->deserialize(um, data); + + return; + } + + default: + throw Error("Unknown response type"); + } +} + +/** Use when you cannot use libcurl. + + @note This method tests for MIME headers with lines terminated by CRLF + (\r\n) and Newlines (\n). In either case, the line terminators are removed + before each header is processed. + + @param rs Value/Result parameter. Dump version and type information here. + */ +void D4Connect::parse_mime(Response &rs) +{ + rs.set_version("dods/0.0"); // initial value; for backward compatibility. + rs.set_protocol("2.0"); + + istream &data_source = *rs.get_cpp_stream(); + string mime = get_next_mime_header(data_source); + while (!mime.empty()) { + string header, value; + parse_mime_header(mime, header, value); + + // Note that this is an ordered list + if (header == "content-description") { + DBG(cout << header << ": " << value << endl); + rs.set_type(get_description_type(value)); + } + // Use the value of xdods-server only if no other value has been read + else if (header == "xdods-server" && rs.get_version() == "dods/0.0") { + DBG(cout << header << ": " << value << endl); + rs.set_version(value); + } + // This trumps 'xdods-server' and 'server' + else if (header == "xopendap-server") { + DBG(cout << header << ": " << value << endl); + rs.set_version(value); + } + else if (header == "xdap") { + DBG(cout << header << ": " << value << endl); + rs.set_protocol(value); + } + // Only look for 'server' if no other header supplies this info. + else if (rs.get_version() == "dods/0.0" && header == "server") { + DBG(cout << header << ": " << value << endl); + rs.set_version(value); + } + + mime = get_next_mime_header(data_source); + } +} + +// public mfuncs + +/** The D4Connect constructor requires a URL or local file. + + @param n The URL for the virtual connection. + @param uname Use this username for authentication. Null by default. + @param password Password to use for authentication. Null by default. + @brief Create an instance of Connect. */ +D4Connect::D4Connect(const string &url, string uname, string password) : + d_http(0), d_local(false), d_URL(""), d_UrlQueryString(""), d_server("unknown"), d_protocol("4.0") +{ + string name = prune_spaces(url); + + // Figure out if the URL starts with 'http', if so, make sure that we + // talk to an instance of HTTPConnect. + if (name.find("http") == 0) { + DBG(cerr << "Connect: The identifier is an http URL" << endl); + d_http = new HTTPConnect(RCReader::instance()); + d_http->set_use_cpp_streams(true); + + d_URL = name; + + // Find and store any CE given with the URL. + string::size_type dotpos = name.find('?'); + if (dotpos != std::string::npos) { // Found a match. + d_URL = name.substr(0, dotpos); + + d_UrlQueryString = name.substr(dotpos + 1); + + if (d_UrlQueryString.find(DAP4_CE_QUERY_KEY) != std::string::npos) { + std::stringstream msg; + msg << endl; + msg << "WARNING: A DAP4 constraint expression key was found in the query string!" << endl; + msg << "The submitted dataset URL: " << name << endl; + msg << "Contains the query string: " << d_UrlQueryString << endl; + msg << "This will cause issues when making DAP4 requests that specify additional constraints. " << endl; + cerr << msg.str() << endl; + // throw Error(malformed_expr, msg.str()); + } + + } + } + else { + DBG(cerr << "Connect: The identifier is a local data source." << endl); + d_local = true; // local in this case means non-DAP + } + + set_credentials(uname, password); +} + +D4Connect::~D4Connect() +{ + if (d_http) delete d_http; +} + +std::string D4Connect::build_dap4_ce(const string requestSuffix, const string dap4ce) +{ + std::stringstream url; + bool needsAmpersand = false; + + url << d_URL << requestSuffix << "?"; + + if (d_UrlQueryString.length() > 0) { + url << d_UrlQueryString; + needsAmpersand = true; + } + + if (dap4ce.length() > 0) { + if (needsAmpersand) url << "&"; + + url << DAP4_CE_QUERY_KEY << "=" << id2www_ce(dap4ce); + } + + DBG(cerr << "D4Connect::build_dap4_ce() - Source URL: " << d_URL << endl); + DBG(cerr << "D4Connect::build_dap4_ce() - Source URL Query String: " << d_UrlQueryString << endl); + DBG(cerr << "D4Connect::build_dap4_ce() - dap4ce: " << dap4ce << endl); + DBG(cerr << "D4Connect::build_dap4_ce() - request URL: " << url.str() << endl); + + return url.str(); +} + +void D4Connect::request_dmr(DMR &dmr, const string expr) +{ + string url = build_dap4_ce(".dmr", expr); + + Response *rs = 0; + try { + rs = d_http->fetch_url(url); + + d_server = rs->get_version(); + d_protocol = rs->get_protocol(); + + switch (rs->get_type()) { + case unknown_type: + DBG(cerr << "Response type unknown, assuming it's a DMR response." << endl); + /* no break */ + case dap4_dmr: { + D4ParserSax2 parser; + parser.intern(*rs->get_cpp_stream(), &dmr); + break; + } + + case dap4_error: + throw InternalErr(__FILE__, __LINE__, "DAP4 errors are not processed yet."); + + case web_error: + // We should never get here; a web error should be picked up read_url + // (called by fetch_url) and result in a thrown Error object. + throw InternalErr(__FILE__, __LINE__, "Web error found where it should never be."); + break; + + default: + throw InternalErr(__FILE__, __LINE__, + "Response type not handled (got " + long_to_string(rs->get_type()) + ")."); + } + } + catch (...) { + delete rs; + throw; + } + + delete rs; +} + +void D4Connect::request_dap4_data(DMR &dmr, const string expr) +{ + string url = build_dap4_ce(".dap", expr); + + Response *rs = 0; + try { + rs = d_http->fetch_url(url); + + d_server = rs->get_version(); + d_protocol = rs->get_protocol(); + + switch (rs->get_type()) { + case unknown_type: + DBG(cerr << "Response type unknown, assuming it's a DAP4 Data response." << endl); + /* no break */ + case dap4_data: { +#if BYTE_ORDER_PREFIX + istream &in = *rs->get_cpp_stream(); + // Read the byte-order byte; used later on + char byte_order; + in >> byte_order; +#endif + + // get a chunked input stream +#if BYTE_ORDER_PREFIX + chunked_istream cis(*(rs->get_cpp_stream()), 1024, byte_order); +#else + chunked_istream cis(*(rs->get_cpp_stream()), CHUNK_SIZE); +#endif + + // parse the DMR, stopping when the boundary is found. + + // force chunk read + // get chunk size + int chunk_size = cis.read_next_chunk(); + if (chunk_size < 0) + throw Error("Found an unexpected end of input (EOF) while reading a DAP4 data response. (2)"); + + // get chunk + char chunk[chunk_size]; + cis.read(chunk, chunk_size); + // parse char * with given size + D4ParserSax2 parser; + // permissive mode allows references to Maps that are not in the response. + parser.set_strict(false); + // '-2' to discard the CRLF pair + parser.intern(chunk, chunk_size - 2, &dmr, false /*debug*/); + + // Read data and store in the DMR +#if BYTE_ORDER_PREFIX + D4StreamUnMarshaller um(cis, byte_order); +#else + D4StreamUnMarshaller um(cis, cis.twiddle_bytes()); +#endif + dmr.root()->deserialize(um, dmr); + + break; + } + + case dap4_error: + throw InternalErr(__FILE__, __LINE__, "DAP4 errors are not processed yet."); + + case web_error: + // We should never get here; a web error should be picked up read_url + // (called by fetch_url) and result in a thrown Error object. + throw InternalErr(__FILE__, __LINE__, "Web error found where it should never be."); + break; + + default: + throw InternalErr(__FILE__, __LINE__, + "Response type not handled (got " + long_to_string(rs->get_type()) + ")."); + } + } + catch (...) { + delete rs; + throw; + } + + delete rs; +} + +void D4Connect::read_dmr(DMR &dmr, Response &rs) +{ + parse_mime(rs); + if (rs.get_type() == unknown_type) throw Error("Unknown response type."); + + read_dmr_no_mime(dmr, rs); +} + +void D4Connect::read_dmr_no_mime(DMR &dmr, Response &rs) +{ + // Assume callers know what they are doing + if (rs.get_type() == unknown_type) rs.set_type(dap4_dmr); + + switch (rs.get_type()) { + case dap4_dmr: + process_dmr(dmr, rs); + d_server = rs.get_version(); + d_protocol = dmr.dap_version(); + break; + default: + throw Error("Expected a DAP4 DMR response."); + } +} + +void D4Connect::read_data(DMR &data, Response &rs) +{ + parse_mime(rs); + if (rs.get_type() == unknown_type) throw Error("Unknown response type."); + + read_data_no_mime(data, rs); +} + +void D4Connect::read_data_no_mime(DMR &data, Response &rs) +{ + // Assume callers know what they are doing + if (rs.get_type() == unknown_type) rs.set_type(dap4_data); + + switch (rs.get_type()) { + case dap4_data: + process_data(data, rs); + d_server = rs.get_version(); + d_protocol = data.dap_version(); + break; + default: + throw Error("Expected a DAP4 Data response."); + } +} + +/** @brief Set the credentials for responding to challenges while dereferencing + URLs. + @param u The username. + @param p The password. + @see extract_auth_info() */ +void D4Connect::set_credentials(string u, string p) +{ + if (d_http) d_http->set_credentials(u, p); +} + +/** Set the \e accept deflate property. + @param deflate True if the client can accept compressed responses, False + otherwise. */ +void D4Connect::set_accept_deflate(bool deflate) +{ + if (d_http) d_http->set_accept_deflate(deflate); +} + +/** Set the \e XDAP-Accept property/header. This is used to send to a server + the (highest) DAP protocol version number that this client understands. + + @param major The client dap protocol major version + @param minor The client dap protocol minor version */ +void D4Connect::set_xdap_protocol(int major, int minor) +{ + if (d_http) d_http->set_xdap_protocol(major, minor); +} + +/** Disable any further use of the client-side cache. In a future version + of this software, this should be handled so that the www library is + not initialized with the cache running by default. */ +void D4Connect::set_cache_enabled(bool cache) +{ + if (d_http) d_http->set_cache_enabled(cache); +} + +bool D4Connect::is_cache_enabled() +{ + if (d_http) + return d_http->is_cache_enabled(); + else + return false; +} + +} // namespace libdap diff --git a/D4Connect.h b/D4Connect.h new file mode 100644 index 0000000..40fb2e3 --- /dev/null +++ b/D4Connect.h @@ -0,0 +1,116 @@ + +// -*- mode: c++; c-basic-offset:4 -*- + +// This file is part of libdap, A C++ implementation of the OPeNDAP Data +// Access Protocol. + +// Copyright (c) 2013 OPeNDAP, Inc. +// Author: James Gallagher +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// +// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112. + +#ifndef _d4connect_h +#define _d4connect_h + +#include + +#define DAP4_CE_QUERY_KEY "dap4.ce" + +namespace libdap +{ + +class HTTPConnect; +class DMR; +class Response; + +class D4Connect +{ +private: + HTTPConnect *d_http; + + bool d_local; // Is this a local connection? + std::string d_URL; // URL to remote dataset (minus CE) + std::string d_UrlQueryString; // CE + + std::string d_server; // Server implementation information (the XDAP-Server header) + std::string d_protocol; // DAP protocol from the server (XDAP) + + void process_data(DMR &data, Response &rs); + void process_dmr(DMR &data, Response &rs); + + // Use when you cannot use but have a complete response with MIME headers + void parse_mime(Response &rs); + + std::string build_dap4_ce(const std::string requestSuffix, const std::string expr); + +protected: + /** @name Suppress the C++ defaults for these. */ + D4Connect(); + D4Connect(const D4Connect &); + D4Connect &operator=(const D4Connect &); + +public: + D4Connect(const std::string &url, std::string uname = "", std::string password = ""); + + virtual ~D4Connect(); + + bool is_local() const { return d_local; } + + virtual std::string URL() const { return d_URL; } + virtual std::string CE() const { return d_UrlQueryString; } + + void set_credentials(std::string u, std::string p); + void set_accept_deflate(bool deflate); + void set_xdap_protocol(int major, int minor); + + void set_cache_enabled(bool enabled); + bool is_cache_enabled(); + + void set_xdap_accept(int major, int minor); + + /** Return the protocol/implementation version of the most recent + response. This is a poorly designed method, but it returns + information that is useful when used correctly. Before a response is + made, this contains the std::string "unknown." This should ultimately hold + the \e protocol version; it currently holds the \e implementation + version. + + @see get_protocol() + @deprecated */ + std::string get_version() { return d_server; } + + /** Return the DAP protocol version of the most recent + response. Before a response is made, this contains the std::string "2.0." + */ + std::string get_protocol() { return d_protocol; } + + virtual void request_dmr(DMR &dmr, const std::string expr = ""); + virtual void request_dap4_data(DMR &dmr, const std::string expr = ""); +#if 0 + virtual void request_version(); +#endif + + virtual void read_dmr(DMR &dmr, Response &rs); + virtual void read_dmr_no_mime(DMR &dmr, Response &rs); + + virtual void read_data(DMR &data, Response &rs); + virtual void read_data_no_mime(DMR &data, Response &rs); +}; + +} // namespace libdap + +#endif // _d4connect_h diff --git a/D4Dimensions.cc b/D4Dimensions.cc new file mode 100644 index 0000000..86a16cc --- /dev/null +++ b/D4Dimensions.cc @@ -0,0 +1,142 @@ +// -*- mode: c++; c-basic-offset:4 -*- + +// This file is part of libdap, A C++ implementation of the OPeNDAP Data +// Access Protocol. + +// Copyright (c) 2013 OPeNDAP, Inc. +// Author: James Gallagher +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112. + +#include "config.h" + +#include + +#include "XMLWriter.h" +#include "D4Dimensions.h" +#include "D4Group.h" + +#include "Error.h" +#include "InternalErr.h" + +namespace libdap { + +void +D4Dimension::set_size(const string &size) +{ + unsigned long value = 0; + istringstream iss(size); + iss >> value; + + // First test if the stream is OK, then look to see if we read all + // of the chars. + if (!iss || !iss.eof()) throw Error("Invalid value '" + size + "' passed to D4Dimension::set_size."); + set_size(value); +} + +/** + * @brief Get the FQN for the dimension + * @return The D4Dimension as a fully qualified name. + */ +string +D4Dimension::fully_qualified_name() const +{ + string name = d_name; + + // d_parent is the D4Dimensions container and its parent is the Group where + // this Dimension is defined. + D4Group *grp = d_parent->parent(); + while (grp) { + // The root group is named "/" (always); this avoids '//name' + name = (grp->name() == "/") ? "/" + name : grp->name() + "/" + name; + + if (grp->get_parent()) + grp = static_cast(grp->get_parent()); + else + grp = 0; + } + + return name; +} + +/** + * @brief Print the Dimension declaration. + * Print the Dimension in a form suitable for use in a Group definition/declaration. + * @see print_dap4(XMLWriter &xml, bool print_fqn) + * @param xml Print to this XMLWriter instance + */ +void +D4Dimension::print_dap4(XMLWriter &xml) const +{ + if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*) "Dimension") < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write Dimension element"); + + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "name", (const xmlChar*)d_name.c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name"); +#if 0 + // Use FQNs when things are referenced, not when they are defined + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "name", (const xmlChar*)fully_qualified_name().c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name"); +#endif + ostringstream oss; + if (d_constrained) + oss << (d_c_stop - d_c_start) / d_c_stride + 1; + else + oss << d_size; + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "size", (const xmlChar*) oss.str().c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute for size"); + + if (xmlTextWriterEndElement(xml.get_writer()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not end Dimension element"); +} + +// Note that in order for this to work the second argument must not be a reference. +// jhrg 8/20/13 +static bool +dim_name_eq(D4Dimension *d, const string name) +{ + return d->name() == name; +} + +D4Dimension * +D4Dimensions::find_dim(const string &name) +{ + D4DimensionsIter d = find_if(d_dims.begin(), d_dims.end(), bind2nd(ptr_fun(dim_name_eq), name)); + return (d != d_dims.end()) ? *d: 0; +} + +void +D4Dimensions::print_dap4(XMLWriter &xml, bool constrained) const +{ + D4DimensionsCIter i = d_dims.begin(); + while (i != d_dims.end()) { +#if 0 + if (!constrained || parent()->find_first_var_that_uses_dimension(*i)) + (*i)->print_dap4(xml); +#endif + if (constrained) { + if ((*i)->used_by_projected_var()) + (*i)->print_dap4(xml); + } + else { + (*i)->print_dap4(xml); + } + ++i; + } +} + +} /* namespace libdap */ diff --git a/D4Dimensions.h b/D4Dimensions.h new file mode 100644 index 0000000..f4eeeaf --- /dev/null +++ b/D4Dimensions.h @@ -0,0 +1,194 @@ +// -*- mode: c++; c-basic-offset:4 -*- + +// This file is part of libdap, A C++ implementation of the OPeNDAP Data +// Access Protocol. + +// Copyright (c) 2013 OPeNDAP, Inc. +// Author: James Gallagher +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112. + +#ifndef D4DIMENSIONS_H_ +#define D4DIMENSIONS_H_ + +#include +#include + +// #include "XMLWriter.h" + +using namespace std; + +namespace libdap { + +class D4Group; +class D4Dimensions; +class XMLWriter; + +class D4Dimension { + string d_name; + unsigned long d_size; + + D4Dimensions *d_parent; // This is used to get the Dimensions and then the Group object + + bool d_constrained; + unsigned long long d_c_start, d_c_stride, d_c_stop; + + bool d_used_by_projected_var; + +public: + D4Dimension() : d_name(""), d_size(0), d_parent(0), d_constrained(false), d_c_start(0), d_c_stride(0), + d_c_stop(0), d_used_by_projected_var(false) {} + D4Dimension(const string &name, unsigned long size, D4Dimensions *d = 0) : d_name(name), d_size(size), d_parent(d), + d_constrained(false), d_c_start(0), d_c_stride(0), d_c_stop(0), d_used_by_projected_var(false) {} + + string name() const {return d_name;} + void set_name(const string &name) { d_name = name; } + string fully_qualified_name() const; + + unsigned long size() const { return d_size; } + void set_size(unsigned long size) { d_size = size; } + // Because we build these in the XML parser and it's all text... + void set_size(const string &size); + + D4Dimensions *parent() const { return d_parent; } + void set_parent(D4Dimensions *d) { d_parent = d; } + + bool constrained() const { return d_constrained; } + unsigned long long c_start() const { return d_c_start; } + unsigned long long c_stride() const { return d_c_stride; } + unsigned long long c_stop() const { return d_c_stop; } + + bool used_by_projected_var() const { return d_used_by_projected_var; } + void set_used_by_projected_var(bool state) { d_used_by_projected_var = state; } + + /** + * Set this Shared Diemension's constraint. While an Array Dimension object uses a + * stop value of -1 to indicate the end of the dimension, this method does not support + * that; the caller will have to sort out the correct end value for 'stop'. + * @param start Starting index (zero-based) + * @param stride The stride for the slice + * @param stop The stopping index (never greater than size -1) + */ + void set_constraint(unsigned long long start, unsigned long long stride, unsigned long long stop) { + d_c_start = start; + d_c_stride = stride; + d_c_stop = stop; + d_constrained = true; + } + + void print_dap4(XMLWriter &xml) const; +}; + +/** + * This class holds information about dimensions. This can be used to store + * actual dimension information in an instance of BaseType and it can be + * used to store the definition of a dimension in an instance of Group. + */ +class D4Dimensions { + vector d_dims; + + D4Group *d_parent; // the group that holds this set of D4Dimensions; weak pointer, don't delete + +protected: + // Note Code in Array depends on the order of these 'new' dimensions + // matching the 'old' dimensions they are derived from. See + // Array::update_dimension_pointers. jhrg 8/25/14 + void m_duplicate(const D4Dimensions &rhs) { + D4DimensionsCIter i = rhs.d_dims.begin(); + while (i != rhs.d_dims.end()) { + d_dims.push_back(new D4Dimension(**i++)); // deep copy + d_dims.back()->set_parent(this); // Set the Dimension's parent + } + + d_parent = rhs.d_parent; + } + +public: + /// Iterator used for D4Dimensions + typedef vector::iterator D4DimensionsIter; + typedef vector::const_iterator D4DimensionsCIter; + + D4Dimensions() : d_parent(0) {} + D4Dimensions(D4Group *g) : d_parent(g) {} + D4Dimensions(const D4Dimensions &rhs) : d_parent(0) { m_duplicate(rhs); } + + virtual ~D4Dimensions() { + D4DimensionsIter i = d_dims.begin(); + while (i != d_dims.end()) + delete *i++; + } + + D4Dimensions &operator=(const D4Dimensions &rhs) { + if (this == &rhs) return *this; + m_duplicate(rhs); + return *this; + } + + /// Does this D4Dimensions object actually have dimensions? + bool empty() const { return d_dims.empty(); } + + D4Group *parent() const { return d_parent;} + void set_parent(D4Group *g) { d_parent = g; } + + /** Append a new dimension. + * In DAP4 dimensions are either of a known size or are varying. For + * fixed-size dimensions, the value of varying should be false. For varying + * dimensions the value of 'size' will be ignored - any value can be used + * when called this method. + * + * @param dim Pointer to the D4Dimension object to add; deep copy + */ + void add_dim(D4Dimension *dim) { add_dim_nocopy(new D4Dimension(*dim)); } + + /** Append a new dimension. + * @param dim Pointer to the D4Dimension object to add; copies the pointer + */ + void add_dim_nocopy(D4Dimension *dim) { dim->set_parent(this); d_dims.push_back(dim); } + + /// Get an iterator to the start of the dimensions + D4DimensionsIter dim_begin() { return d_dims.begin(); } + + /// Get an iterator to the end of the dimensions + D4DimensionsIter dim_end() { return d_dims.end(); } + + D4Dimension *find_dim(const string &name); + + /** Insert a dimension. + * Insert a dimension before the position specified by the iterator. + * @note Calling this method invalidates all iterators that reference this + * D4Dimension object. + * @param dim Inserted before i; deep copy + * @param i iterator + */ + void insert_dim(D4Dimension *dim, D4DimensionsIter i) { + insert_dim_nocopy(new D4Dimension(*dim), i); + } + + /** Insert a dimension. + * @param dim Inserted before i; pointer copy + * @param i iterator + */ + void insert_dim_nocopy(D4Dimension *dim, D4DimensionsIter i) { + dim->set_parent(this); + d_dims.insert(i, dim); + } + + void print_dap4(XMLWriter &xml, bool constrained = false) const; +}; + +} /* namespace libdap */ +#endif /* D4DIMENSIONS_H_ */ diff --git a/D4Enum.cc b/D4Enum.cc new file mode 100644 index 0000000..1a8165b --- /dev/null +++ b/D4Enum.cc @@ -0,0 +1,710 @@ + +// -*- mode: c++; c-basic-offset:4 -*- + +// This file is part of libdap, A C++ implementation of the OPeNDAP Data +// Access Protocol. + +// Copyright (c) 2013 OPeNDAP, Inc. +// Author: James Gallagher +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112. + +#include "config.h" + +// #define DODS_DEBUG + +#include +#include + +#include + +#include "Byte.h" // synonymous with UInt8 and Char +#include "Int8.h" +#include "Int16.h" +#include "UInt16.h" +#include "Int32.h" +#include "UInt32.h" +#include "Int64.h" +#include "UInt64.h" + +#include "D4Group.h" +#include "D4Enum.h" +#include "D4EnumDefs.h" +#include "D4Attributes.h" + +#include "Float32.h" +#include "Float64.h" + +#include "D4StreamMarshaller.h" +#include "D4StreamUnMarshaller.h" + +#include "Operators.h" +#include "InternalErr.h" +#include "dods-datatypes.h" +#include "dods-limits.h" +#include "util.h" +#include "debug.h" + +using std::cerr; +using std::endl; + +namespace libdap { + +// Private +void D4Enum::m_duplicate(const D4Enum &src) +{ + d_buf = src.d_buf; + d_element_type = src.d_element_type; + d_enum_def = src.d_enum_def; +#if 0 + // The enum_def is a weak pointer managed by D4Group. We just copy it + // and do not delete it. jhrg 1019/15 + d_enum_def = src.d_enum_def == 0 ? 0 : new D4EnumDef(*(src.d_enum_def)); +#endif + d_is_signed = src.d_is_signed; +} + + +/** + * + * + * + * + * + * + */ +std::vector * +D4Enum::transform_to_dap2(AttrTable *){ + BaseType *my_pretty_pony; + + DBG(cerr << __func__ << "() - BEGIN" << endl;); + + + switch (d_element_type) { + case dods_byte_c: + case dods_int8_c: + case dods_uint8_c: + { + Byte *var = new Byte(name()); + dods_byte val; + this->value(&val); + var->set_value(val); + my_pretty_pony = var; + break; + } + case dods_uint16_c: + { + UInt16 *var = new UInt16(name()); + dods_uint16 val; + this->value(&val); + var->set_value(val); + my_pretty_pony = var; + break; + } + case dods_uint32_c: + { + UInt32 *var = new UInt32(name()); + dods_uint32 val; + this->value(&val); + var->set_value(val); + my_pretty_pony = var; + break; + } + case dods_uint64_c: + { + UInt64 *var = new UInt64(name()); + dods_uint64 val; + this->value(&val); + var->set_value(val); + my_pretty_pony = var; + break; + } + case dods_int16_c: + { + Int16 *var = new Int16(name()); + dods_int16 val; + this->value(&val); + var->set_value(val); + my_pretty_pony = var; + break; + } + case dods_int32_c: + { + Int32 *var = new Int32(name()); + dods_int32 val; + this->value(&val); + var->set_value(val); + my_pretty_pony = var; + break; + } + case dods_int64_c: + { + Int64 *var = new Int64(name()); + dods_int64 val; + this->value(&val); + var->set_value(val); + my_pretty_pony = var; + break; + } + default: + { + ostringstream oss; + oss << __func__ << "() - ERROR! Unknown D4Enum type:"<< d_element_type << " name: " << name() << endl; + throw InternalErr(__FILE__,__LINE__,oss.str()); + break; + } + } + + DBG( cerr << __func__ << "() - Processing Enum type:"<< + my_pretty_pony->type_name() << " name: " << my_pretty_pony->name() << endl;); + /** + * Grab the attributes! + */ + AttrTable d2_attrs = *(this->attributes()->get_AttrTable(name())); + my_pretty_pony->set_attr_table(d2_attrs); + + // cerr << "D4Enum::transform_to_dap2() - my_pretty_pony attrs: "<< endl; + // my_pretty_pony->get_attr_table().print(cerr); + + /** + * Now we make the Enum label, and the enum's definition + * into DAP2 attributes for our returned item. + */ + long long my_value; + this->value(&my_value); + DBG(cerr << __func__ << "() - value: "<< my_value << endl;); + + string my_label=""; + AttrTable *enum_def = new AttrTable(); + enum_def->set_name("d4:enum_def"); + + D4EnumDef::D4EnumValueIter dIter = d_enum_def->value_begin(); + D4EnumDef::D4EnumValueIter dEnd = d_enum_def->value_end(); + while( dIter!=dEnd){ + long long a_value = (*dIter).value; + string a_label = (*dIter).label; + ostringstream oss; + oss << a_value; + DBG(cerr << __func__ << "() - a_value: "<< a_value << endl;); + enum_def->append_attr(a_label,my_pretty_pony->type_name(),oss.str()); + if(a_value == my_value){ + my_label = (*dIter).label; + } + dIter++; + } + if(!my_label.empty()) + my_pretty_pony->get_attr_table().append_attr("d4:enum_label","String",my_label); + my_pretty_pony->get_attr_table().append_container(enum_def,enum_def->get_name()); + + vector *result = new vector(); + result->push_back(my_pretty_pony); + DBG(cerr << __func__ << "() - END" << endl;); + return result; +} + +void D4Enum::m_check_value(int64_t v) const +{ + switch (d_element_type) { + case dods_byte_c: + case dods_uint8_c: + if ((uint64_t)v > DODS_UCHAR_MAX || v < 0) { + ostringstream oss; + oss << "The value " << v << " will not fit in an unsigned byte. (" << __func__ << ")"; + throw Error(oss.str()); + } + break; + case dods_uint16_c: + if ((uint64_t)v > DODS_USHRT_MAX || v < 0) { + ostringstream oss; + oss << "The value " << v << " will not fit in an unsigned 16-bit integer. (" << __func__ << ")"; + throw Error(oss.str()); + } + break; + case dods_uint32_c: + if ((uint64_t)v > DODS_UINT_MAX || v < 0) { + ostringstream oss; + oss << "The value " << v << " will not fit in an unsigned 32-bit integer. (" << __func__ << ")"; + throw Error(oss.str()); + } + break; + case dods_uint64_c: + // If 'v' can never be bigger than ULLONG_MAX + break; + + case dods_int8_c: + if (v > DODS_SCHAR_MAX || v < DODS_SCHAR_MIN) { + ostringstream oss; + oss << "The value " << v << " will not fit in an unsigned byte. (" << __func__ << ")"; + throw Error(oss.str()); + } + + break; + case dods_int16_c: + if (v > DODS_SHRT_MAX || v < DODS_SHRT_MIN) { + ostringstream oss; + oss << "The value " << v << " will not fit in an unsigned byte. (" << __func__ << ")"; + throw Error(oss.str()); + } + break; + case dods_int32_c: + if (v > DODS_INT_MAX || v < DODS_INT_MIN) { + ostringstream oss; + oss << "The value " << v << " will not fit in an unsigned byte. (" << __func__ << ")"; + throw Error(oss.str()); + } + break; + case dods_int64_c: + // There's no value 'v' can have that won't fit into a 64-bit int. + break; + default: + assert(!"illegal type for D4Enum"); + } +} + +D4Enum::D4Enum(const string &name, const string &enum_type) : + BaseType(name, dods_enum_c, true /*is_dap4*/), d_buf(0), d_element_type(dods_null_c), d_enum_def(0) +{ + d_element_type = get_type(enum_type.c_str()); + + if (!is_integer_type(d_element_type)) d_element_type = dods_uint64_c; + set_is_signed(d_element_type); +} + +D4Enum::D4Enum(const string &name, Type type) : + BaseType(name, dods_enum_c, true /*is_dap4*/), d_buf(0), d_element_type(type), d_enum_def(0) +{ + if (!is_integer_type(d_element_type)) d_element_type = dods_uint64_c; + set_is_signed(d_element_type); +} + +D4Enum::D4Enum(const string &name, const string &dataset, Type type) : + BaseType(name, dataset, dods_enum_c, true /*is_dap4*/), d_buf(0), d_element_type(type), d_enum_def(0) +{ + if (!is_integer_type(d_element_type)) d_element_type = dods_uint64_c; + set_is_signed(d_element_type); +} + +// Explicit instantiation of the template member function 'value(T *)'. +// This is required in order to have the library contain these member +// functions when its own code does not use them. Normally, C++ instantiates +// templates when they are used, and this forces that process so the +// library file contains the various versions of the member function. +template void D4Enum::value(dods_byte *v) const; +template void D4Enum::value(dods_int16 *v) const; +template void D4Enum::value(dods_uint16 *v) const; +template void D4Enum::value(dods_int32 *v) const; +template void D4Enum::value(dods_uint32 *v) const; +template void D4Enum::value(dods_int64 *v) const; +template void D4Enum::value(dods_uint64 *v) const; + +template void D4Enum::set_value(dods_byte v, bool check_value); +template void D4Enum::set_value(dods_int16 v, bool check_value); +template void D4Enum::set_value(dods_uint16 v, bool check_value); +template void D4Enum::set_value(dods_int32 v, bool check_value); +template void D4Enum::set_value(dods_uint32 v, bool check_value); +template void D4Enum::set_value(dods_int64 v, bool check_value); +template void D4Enum::set_value(dods_uint64 v, bool check_value); + +void +D4Enum::set_enumeration(D4EnumDef *enum_def) { + d_enum_def = enum_def; + d_element_type = enum_def->type(); +} + +void +D4Enum::compute_checksum(Crc32 &checksum) +{ + DBG(cerr << __func__ << ": element type: " << ::libdap::type_name(d_element_type) << endl); + + switch (d_element_type) { + case dods_byte_c: + case dods_uint8_c: + case dods_int8_c: { + dods_byte v = static_cast(d_buf); + checksum.AddData(reinterpret_cast(&v), sizeof(uint8_t)); + break; + } + case dods_uint16_c: + case dods_int16_c: { + dods_int16 v = static_cast(d_buf); + checksum.AddData(reinterpret_cast(&v), sizeof(uint16_t)); + break; + } + case dods_uint32_c: + case dods_int32_c: { + dods_int32 v = static_cast(d_buf); + checksum.AddData(reinterpret_cast(&v), sizeof(uint32_t)); + break; + } + case dods_uint64_c: + case dods_int64_c: + checksum.AddData(reinterpret_cast(&d_buf), sizeof(uint64_t)); + break; + + default: + assert(!"illegal type for D4Enum"); + } +} + +void +D4Enum::set_is_signed(Type t) +{ + switch (t) { + case dods_byte_c: + case dods_uint8_c: + case dods_uint16_c: + case dods_uint32_c: + case dods_uint64_c: + d_is_signed = false; + break; + + case dods_int8_c: + case dods_int16_c: + case dods_int32_c: + case dods_int64_c: + d_is_signed = true; + break; + + default: + assert(!"illegal type for D4Enum"); + throw InternalErr(__FILE__, __LINE__, "Illegal type"); + } +} + + +/** + * @brief Serialize a D4Enum + * Use the (integer) data type associated with an Enumeration definition to + * serialize the value of a D4Enum variable. This send just the bits that + * correspond to the declared type, not all 64-bits of storage used by a + * scalar D4Enum. + * @param m + * @param dmr Unused + * @param eval Unused + * @param filter Unused + * @exception Error is thrown if the value needs to be read and that operation fails. + */ +void +D4Enum::serialize(D4StreamMarshaller &m, DMR &, /*ConstraintEvaluator &,*/ bool) +{ + if (!read_p()) + read(); // read() throws Error + + switch (d_element_type) { + case dods_byte_c: + case dods_uint8_c: + m.put_byte(d_buf); + break; + case dods_uint16_c: + m.put_uint16(d_buf); + break; + case dods_uint32_c: + m.put_uint32(d_buf); + break; + case dods_uint64_c: + m.put_uint64(d_buf); + break; + + case dods_int8_c: + m.put_int8(d_buf); + break; + case dods_int16_c: + m.put_int16(d_buf); + break; + case dods_int32_c: + m.put_int32(d_buf); + break; + case dods_int64_c: + m.put_int64(d_buf); + break; + default: + assert(!"illegal type for D4Enum"); + } +} + +void +D4Enum::deserialize(D4StreamUnMarshaller &um, DMR &) +{ + switch (d_element_type) { + case dods_byte_c: + case dods_uint8_c: { + dods_byte v; + um.get_byte(v); + d_buf = v; + break; + } + case dods_uint16_c: { + dods_uint16 v; + um.get_uint16(v); + d_buf = v; + break; + } + case dods_uint32_c: { + dods_uint32 v; + um.get_uint32(v); + d_buf = v; + break; + } + case dods_uint64_c: { + dods_uint64 v; + um.get_uint64(v); + d_buf = v; + break; + } + + case dods_int8_c: { + dods_int8 v; + um.get_int8(v); + d_buf = v; + break; + } + case dods_int16_c: { + dods_int16 v; + um.get_int16(v); + d_buf = v; + break; + } + case dods_int32_c: { + dods_int32 v; + um.get_int32(v); + d_buf = v; + break; + } + case dods_int64_c: { + dods_int64 v; + um.get_int64(v); + d_buf = v; + break; + } + default: + assert(!"illegal type for D4Enum"); + } +} + +unsigned int D4Enum::val2buf(void *val, bool) +{ + if (!val) + throw InternalErr("The incoming pointer does not contain any data."); + + switch (d_element_type) { + case dods_byte_c: + case dods_uint8_c: + d_buf = *(dods_byte*)val; + break; + case dods_uint16_c: + d_buf = *(dods_uint16*)val; + break; + case dods_uint32_c: + d_buf = *(dods_uint32*)val; + break; + case dods_uint64_c: + d_buf = *(dods_uint64*)val; + break; + + case dods_int8_c: + d_buf = *(dods_int8*)val; + break; + case dods_int16_c: + d_buf = *(dods_int16*)val; + break; + case dods_int32_c: + d_buf = *(dods_int32*)val; + break; + case dods_int64_c: + d_buf = *(dods_int64*)val; + break; + default: + assert(!"illegal type for D4Enum"); + } + + return width(); +} + +unsigned int D4Enum::buf2val(void **val) +{ + if (!val) + throw InternalErr("NULL pointer"); + + switch (d_element_type) { + case dods_byte_c: + case dods_uint8_c: + if (!*val) *val = new dods_byte; + *(dods_byte *) * val = d_buf; + break; + case dods_uint16_c: + if (!*val) *val = new dods_uint16; + *(dods_uint16 *) * val = d_buf; + break; + case dods_uint32_c: + if (!*val) *val = new dods_uint32; + *(dods_uint32 *) * val = d_buf; + break; + case dods_uint64_c: + if (!*val) *val = new dods_uint64; + *(dods_uint64 *) * val = d_buf; + break; + + case dods_int8_c: + if (!*val) *val = new dods_int8; + *(dods_int8*) * val = d_buf; + break; + case dods_int16_c: + if (!*val) *val = new dods_int16; + *(dods_int16 *) * val = d_buf; + break; + case dods_int32_c: + if (!*val) *val = new dods_int32; + *(dods_int32 *) * val = d_buf; + break; + case dods_int64_c: + if (!*val) *val = new dods_int64; + *(dods_int64 *) * val = d_buf; + break; + default: + assert(!"illegal type for D4Enum"); + } + + return width(); +} + +void D4Enum::print_val(ostream &out, string space, bool print_decl_p) +{ + if (print_decl_p) { + print_decl(out, space, false); + out << " = "; + } + + DBG(cerr << "Enum union value: " << hex << d_buf << dec << endl); + + if (is_signed()) { + int64_t v; + value(&v); + out << v; + } + else { + uint64_t v; + value(&v); + out << v; + } + + if (print_decl_p) + out << ";" << endl; +} + +/** Write the XML representation of this variable. This method is used to + build the DDX XML response. + @param out Destination output stream + @param space Use this to indent child declarations. Default is "". + @param constrained If true, only print this if it's part part of the + current projection. Default is False. */ +void +D4Enum::print_xml_writer(XMLWriter &xml, bool constrained) +{ + if (constrained && !send_p()) + return; + + if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*)"Enum") < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write Enum element"); + + if (!name().empty()) + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "name", (const xmlChar*)name().c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name"); + + + string path = d_enum_def->name(); + // Not every D4EnumDef is a member of an instance of D4EnumDefs - the D4EnumDefs instance + // holds a reference to the D4Group that holds the Enum definitions. + // TODO Should this be changed - so the EnumDef holds a reference to its parent Group? + if (d_enum_def->parent()) { + // print the FQN for the enum def; D4Group::FQN() includes the trailing '/' + path = static_cast(d_enum_def->parent()->parent())->FQN() + path; + } + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "enum", (const xmlChar*)path.c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute for enum"); + + attributes()->print_dap4(xml); + + if (get_attr_table().get_size() > 0) + get_attr_table().print_xml_writer(xml); + + if (xmlTextWriterEndElement(xml.get_writer()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not end Enum element"); +} + + +bool +D4Enum::ops(BaseType *b, int op) +{ + // Get the arg's value. + if (!read_p() && !read()) + throw InternalErr(__FILE__, __LINE__, "This value not read!"); + + // Get the second arg's value. + if (!b->read_p() && !b->read()) + throw InternalErr(__FILE__, __LINE__, "This value not read!"); + + switch (b->type()) { + case dods_int8_c: + return Cmp(op, d_buf, static_cast(b)->value()); + case dods_byte_c: + return SUCmp(op, d_buf, static_cast(b)->value()); + case dods_int16_c: + return Cmp(op, d_buf, static_cast(b)->value()); + case dods_uint16_c: + return SUCmp(op, d_buf, static_cast(b)->value()); + case dods_int32_c: + return Cmp(op, d_buf, static_cast(b)->value()); + case dods_uint32_c: + return SUCmp(op, d_buf, static_cast(b)->value()); +#if 0 + // FIXME + case dods_int64_c: + return Cmp(op, d_buf, static_cast(b)->value()); + case dods_uint64_c: + return SUCmp(op, d_buf, static_cast(b)->value()); +#endif + case dods_float32_c: + return Cmp(op, d_buf, static_cast(b)->value()); + case dods_float64_c: + return Cmp(op, d_buf, static_cast(b)->value()); + default: + return false; + } + + return false; +} + +/** @brief dumps information about this object + * + * Displays the pointer value of this instance and information about this + * instance. + * + * @param strm C++ i/o stream to dump the information to + * @return void + */ +void +D4Enum::dump(ostream &strm) const +{ + strm << DapIndent::LMarg << "D4Enum::dump - (" << (void *) this << ")" << endl; + DapIndent::Indent(); + BaseType::dump(strm); + strm << DapIndent::LMarg << "value: " << d_buf << endl; + DapIndent::UnIndent(); +} + +} // namespace libdap + diff --git a/D4Enum.h b/D4Enum.h new file mode 100644 index 0000000..dd63cc3 --- /dev/null +++ b/D4Enum.h @@ -0,0 +1,200 @@ + +// -*- mode: c++; c-basic-offset:4 -*- + +// This file is part of libdap, A C++ implementation of the OPeNDAP Data +// Access Protocol. + +// Copyright (c) 2013 OPeNDAP, Inc. +// Author: James Gallagher +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112. + +#ifndef _D4Enum_h +#define _D4Enum_h 1 + +#include + +#include "BaseType.h" +#include "dods-datatypes.h" + +#if 0 +#include "InternalErr.h" +#include "dods-datatypes.h" +#include "dods-limits.h" +#include "util.h" +#endif + +namespace libdap +{ + +class D4EnumDef; +class ConstraintEvaluator; +class Marshaller; +class UnMarshaller; + +/** + * @brief Holds a DAP4 enumeration. + * + * @note When constructed a type for the Enum must be specified. If + * it is not an integer type, the Enum will use unsigned int 64. This + * is not the same as the enumeration type that is defined using the + * Enumeration XML element in the DMR - that information is stored + * in additional fields and used for checking values and printing the + * variable's declaration, but not for the internal storage of values. + * + * @todo Note the hack to remove the union... + */ +class D4Enum: public BaseType +{ + friend class D4EnumTest; + +protected: + // Use an unsigned 64-bit int. the value() and set_value() + // accessors cast to other types as needed, including signed ones. + uint64_t d_buf; + +private: + Type d_element_type; + D4EnumDef *d_enum_def; // This is a weak pointer; don't delete + bool d_is_signed; + + void m_duplicate(const D4Enum &src); + void m_check_value(int64_t v) const; + + unsigned int m_type_width() const { + switch(d_element_type) { + case dods_byte_c: + case dods_int8_c: + case dods_uint8_c: + return 1; + case dods_int16_c: + case dods_uint16_c: + return 2; + case dods_int32_c: + case dods_uint32_c: + return 4; + case dods_int64_c: + case dods_uint64_c: + return 8; + case dods_null_c: + default: + assert(!"illegal type for D4Enum"); + return 0; + } + } + + D4Enum(); // No empty constructor + +public: + D4Enum(const string &name, const string &enum_type); + + D4Enum(const string &name, Type type); + + D4Enum(const string &name, const string &dataset, Type type); + + D4Enum(const D4Enum &src) : BaseType(src) { m_duplicate(src); } + + D4Enum &operator=(const D4Enum &rhs) { + if (this == &rhs) + return *this; + static_cast(*this) = rhs; + m_duplicate(rhs); + return *this; + } + + virtual ~D4Enum() { } + + virtual D4EnumDef *enumeration() const { return d_enum_def; } + virtual void set_enumeration(D4EnumDef *enum_def); + + virtual BaseType *ptr_duplicate() { return new D4Enum(*this); } + + Type element_type() { return d_element_type; } + void set_element_type(Type type) { d_element_type = type; } + + bool is_signed() const { return d_is_signed; } + void set_is_signed(Type t); + + /** + * @brief Get the value of an Enum + * Get the value of this instance. The caller is responsible + * for using a type T than can hold the value. + * + * @param v Value-result parameter; return the value of the Enum + * in this variable. + */ + template void value(T *v) const { + *v = static_cast(d_buf); + } + + /** + * @brief Set the value of the Enum + * Template member function to set the value of the Enum. The libdap library + * contains versions of this member function for dods_byte, ..., dods_uint64 + * types for the parameter \c v. + * + * @param v Set the Enum to this value. + * @param check_value If true test the value 'v' against the type of the + * Enum. Defaults to true. + */ + template void set_value(T v, bool check_value = true) + { + if (check_value) m_check_value(v); + d_buf = static_cast(v); + } + + /** + * @brief Return the number of bytes in an instance of an Enum. + * This returns the number of bytes an instance of Enum will use + * in memory or on the wire (i.e., in a serialization of + * the type). On the wire this type uses the minimum number of + * bytes for the given Enum type - an Enum with type Byte uses + * one byte, Int16 uses two, and so on. In memory, a single instance + * uses 64-bits but a vector of these will use the same number of + * bytes per value as the on-the-wire representation. + * + * @note The private method m_type_width() returns the byte width + * used for the on-the-wire representation of values. + * + * @return The number of bytes used by a value. + */ + virtual unsigned int width(bool /* constrained */ = false) const { return /*sizeof(int64_t);*/ m_type_width();} + + // DAP4 + virtual void compute_checksum(Crc32 &checksum); + virtual void serialize(D4StreamMarshaller &m, DMR &dmr, /*ConstraintEvaluator &eval,*/ bool filter = false); + virtual void deserialize(D4StreamUnMarshaller &um, DMR &dmr); + + virtual void print_val(ostream &out, string space = "", bool print_decl_p = true); + + virtual void print_xml_writer(XMLWriter &xml, bool constrained); + + virtual bool ops(BaseType *b, int op); + + virtual void dump(ostream &strm) const; + + unsigned int val2buf(void *, bool); + unsigned int buf2val(void **); + + virtual std::vector *transform_to_dap2(AttrTable *parent_attr_table); + +}; + +} // namespace libdap + +#endif // _D4Enum_h + diff --git a/D4EnumDefs.cc b/D4EnumDefs.cc new file mode 100644 index 0000000..61b152b --- /dev/null +++ b/D4EnumDefs.cc @@ -0,0 +1,136 @@ +// -*- mode: c++; c-basic-offset:4 -*- + +// This file is part of libdap, A C++ implementation of the OPeNDAP Data +// Access Protocol. + +// Copyright (c) 2013 OPeNDAP, Inc. +// Author: James Gallagher +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// +// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112. + +#include "config.h" + +#include "D4Group.h" +#include "D4EnumDefs.h" + +#include + +#include "dods-limits.h" +#include "util.h" + +namespace libdap { + +/** Test if a particular value is legal for a given type. In a D4EnumDef, + * all values are actually stored in a long long, but the different + * enumerations can specify different types like Byte, Int32, ..., and this + * method is used to test that the values match those types. + */ +bool +D4EnumDef::is_valid_enum_value(long long value) +{ + switch (type()) { + case dods_int8_c: + return (value >= DODS_SCHAR_MIN && value <= DODS_SCHAR_MAX); + case dods_byte_c: + case dods_uint8_c: + return (value >= 0 && static_cast(value) <= DODS_UCHAR_MAX); + case dods_int16_c: + return (value >= DODS_SHRT_MIN && value <= DODS_SHRT_MAX); + case dods_uint16_c: + return (value >= 0 && static_cast(value) <= DODS_USHRT_MAX); + case dods_int32_c: + return (value >= DODS_INT_MIN && value <= DODS_INT_MAX); + case dods_uint32_c: + return (value >= 0 && static_cast(value) <= DODS_UINT_MAX); + case dods_int64_c: + return true; // This is always true: (value >= DODS_LLONG_MIN && value <= DODS_LLONG_MAX); + case dods_uint64_c: + return (value >= 0 /*Always true: && static_cast(value) <= DODS_ULLONG_MAX*/); + default: + return false; + } +} + +// Note that in order for this to work the second argument must not be a reference. +// jhrg 8/20/13 +static bool +enum_def_name_eq(D4EnumDef *d, const string name) +{ + return d->name() == name; +} + +D4EnumDef * +D4EnumDefs::find_enum_def(const string &name) +{ + D4EnumDefIter d = find_if(d_enums.begin(), d_enums.end(), bind2nd(ptr_fun(enum_def_name_eq), name)); + return (d != d_enums.end()) ? *d: 0; +} + +void D4EnumDef::print_value(XMLWriter &xml, const D4EnumDef::tuple &tuple) const +{ + if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*)"EnumConst") < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write EnumConst element"); + + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "name", (const xmlChar*)tuple.label.c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name"); + + ostringstream oss; + oss << tuple.value; + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "value", (const xmlChar*)oss.str().c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute for value"); + + if (xmlTextWriterEndElement(xml.get_writer()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not end EnumConst element"); +} + +void D4EnumDef::print_dap4(XMLWriter &xml) const +{ + vector::const_iterator i = d_tuples.begin(); + while(i != d_tuples.end()) { + print_value(xml, *i++); + } +} + +void D4EnumDefs::m_print_enum(XMLWriter &xml, D4EnumDef *e) const +{ + if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*)"Enumeration") < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write Enumeration element"); + + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "name", (const xmlChar*)e->name().c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name"); + + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "basetype", (const xmlChar*)D4type_name(e->type()).c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name"); + + // print each of e.values + e->print_dap4(xml); + + if (xmlTextWriterEndElement(xml.get_writer()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not end Enumeration element"); +} + +void D4EnumDefs::print_dap4(XMLWriter &xml, bool constrained) const +{ + D4EnumDefCIter i = d_enums.begin(); + while (i != d_enums.end()) { + if (!constrained || parent()->find_first_var_that_uses_enumeration(*i)) + m_print_enum(xml, *i); + ++i; + } +} + +} /* namespace libdap */ diff --git a/D4EnumDefs.h b/D4EnumDefs.h new file mode 100644 index 0000000..98a340d --- /dev/null +++ b/D4EnumDefs.h @@ -0,0 +1,181 @@ +// -*- mode: c++; c-basic-offset:4 -*- + +// This file is part of libdap, A C++ implementation of the OPeNDAP Data +// Access Protocol. + +// Copyright (c) 2013 OPeNDAP, Inc. +// Author: James Gallagher +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// +// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112. + +#ifndef D4ENUMDEF_H_ +#define D4ENUMDEF_H_ + +#include +#include +#include +#include + +#include "BaseType.h" + +using namespace std; + +namespace libdap { + +class D4EnumDefs; +class D4Group; + +class D4EnumDef { + string d_name; + Type d_type; + D4EnumDefs *d_parent; + + struct tuple { + string label; + long long value; + + tuple(const string &l, long long v) : label(l), value(v) {} + }; + + vector d_tuples; + + void m_duplicate(const D4EnumDef &rhs) + { + d_name = rhs.d_name; + d_type = rhs.d_type; + d_parent = rhs.d_parent; + d_tuples = rhs.d_tuples; + } + + void print_value(XMLWriter &xml, const D4EnumDef::tuple &tuple) const; + +public: + typedef vector::iterator D4EnumValueIter; + + D4EnumDef() : d_name(""), d_type(dods_null_c), d_parent(0) {} + D4EnumDef(const string &n, const Type &t, D4EnumDefs *e = 0) : d_name(n), d_type(t), d_parent(e) {} + D4EnumDef(const D4EnumDef &rhs) { + m_duplicate(rhs); + } + + string name() const { return d_name; } + void set_name(const string &n) { d_name = n; } + + Type type() const { return d_type; } + void set_type(Type t) { d_type = t; } + + D4EnumDefs *parent() const { return d_parent; } + void set_parent(D4EnumDefs *e) { d_parent = e; } + + bool empty() const { return d_tuples.empty(); } + + void add_value(const string &label, long long value) { + d_tuples.push_back(tuple(label, value)); + } + + D4EnumValueIter value_begin() { return d_tuples.begin(); } + D4EnumValueIter value_end() { return d_tuples.end(); } + string &label(D4EnumValueIter i) { return (*i).label; } + long long value(D4EnumValueIter i) { return (*i).value; } + + bool is_valid_enum_value(long long value); + void print_dap4(XMLWriter &xml) const; +}; + +/** The Enumerations defined for a Group. */ +class D4EnumDefs { + vector d_enums; + + D4Group *d_parent; // the group that holds this set of D4EnumDefs; weak pointer, don't delete + + void m_print_enum(XMLWriter &xml, D4EnumDef *e) const; + + void m_duplicate(const D4EnumDefs &rhs) { + D4EnumDefCIter i = rhs.d_enums.begin(); + while (i != rhs.d_enums.end()) { + d_enums.push_back(new D4EnumDef(**i++)); // deep copy + } + + d_parent = rhs.d_parent; + } + +public: + typedef vector::iterator D4EnumDefIter; + typedef vector::const_iterator D4EnumDefCIter; + + D4EnumDefs() : d_parent(0) {} + D4EnumDefs(const D4EnumDefs &rhs) { + m_duplicate(rhs); + } + + virtual ~D4EnumDefs() { + D4EnumDefIter i = d_enums.begin(); + while(i != d_enums.end()) { + delete *i++; + } + } + + D4EnumDefs &operator=(const D4EnumDefs &rhs) { + if (this == &rhs) return *this; + m_duplicate(rhs); + return *this; + } + + bool empty() const { return d_enums.empty(); } + + D4Group *parent() const { return d_parent; } + void set_parent(D4Group *p) { d_parent = p; } + + /** Append a new D4EnumDef. + * + * @param enum_def The enumeration. + */ + void add_enum(D4EnumDef *enum_def) { + add_enum_nocopy(new D4EnumDef(*enum_def)); + } + void add_enum_nocopy(D4EnumDef *enum_def) { + enum_def->set_parent(this); + d_enums.push_back(enum_def); + } + + /// Get an iterator to the start of the enumerations + D4EnumDefIter enum_begin() { return d_enums.begin(); } + + /// Get an iterator to the end of the enumerations + D4EnumDefIter enum_end() { return d_enums.end(); } + + D4EnumDef *find_enum_def(const string &name); + + /** + * @brief Insert a D4EnumDef. + * Insert a D4EnumDef before the position specified by the iterator. + * @note Calling this method invalidates all iterators that reference this + * D4EnumDef object. + * @param enum_def Make a deep copy and insert the enumeration definition + * @param i iterator + */ + void insert_enum(D4EnumDef *enum_def, D4EnumDefIter i) { + D4EnumDef *enum_def_copy = new D4EnumDef(*enum_def); + enum_def_copy->set_parent(this); + d_enums.insert(i, enum_def_copy); + } + + void print_dap4(XMLWriter &xml, bool constrained = false) const; +}; + +} /* namespace libdap */ +#endif /* D4ENUMDEF_H_ */ diff --git a/D4FilterClause.cc b/D4FilterClause.cc new file mode 100644 index 0000000..8ae68e2 --- /dev/null +++ b/D4FilterClause.cc @@ -0,0 +1,212 @@ + +// -*- mode: c++; c-basic-offset:4 -*- + +// This file is part of libdap, A C++ implementation of the OPeNDAP Data +// Access Protocol. + +// Copyright (c) 2002,2003 OPeNDAP, Inc. +// Author: James Gallagher +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// +// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112. + +// (c) COPYRIGHT URI/MIT 1996,1998,1999 +// Please first read the full copyright statement in the file COPYRIGHT_URI. +// +// Authors: +// jhrg,jimg James Gallagher + +// Implementation for the CE Clause class. + + +#include "config.h" + +#include "D4RValue.h" +#include "D4FilterClause.h" + +using namespace std; + +namespace libdap { + +void +D4FilterClauseList::m_duplicate(const D4FilterClauseList &src) +{ + //D4FilterClauseList &non_c_src = const_cast(src); + + for (D4FilterClauseList::citer i = src.cbegin(), e = src.cend(); i != e; ++i) { + D4FilterClause *fc = *i; + d_clauses.push_back(new D4FilterClause(*fc)); + } +} + +D4FilterClauseList::~D4FilterClauseList() +{ + for (D4FilterClauseList::iter i = d_clauses.begin(), e = d_clauses.end(); i != e; ++i) { + delete *i; + } +} + +/** + * @brief Evaluate the list of clauses + * + * Evaluate the list of clauses and return false when/if one is found to be false. + * This evaluates the clauses in the order they are stored and stops evaluation a + * the first false clause. + * + * @param dmr Use this DMR when evaluating clauses - for clauses that contain functions, + * not currently in the DAP4 specification. + * @return True if each of the clauses' value is true, otherwise false + */ +bool +D4FilterClauseList::value(DMR &dmr) +{ + for (D4FilterClauseList::iter i = d_clauses.begin(), e = d_clauses.end(); i != e; ++i) { + if ((*i)->value(dmr) == false) + return false; + } + + return true; +} + +/** + * @brief Evaluate the list of clauses + * + * This version of value() does not need a DMR parameter (but will not work + * if the clauses contain a function call (which is not currently supported + * by the spec). + * + * @return True if each clauses' value is true, false otherwise + * @see D4FilterClauseList::value(DMR &dmr) + */ +bool +D4FilterClauseList::value() +{ + for (D4FilterClauseList::iter i = d_clauses.begin(), e = d_clauses.end(); i != e; ++i) { + if ((*i)->value() == false) + return false; + } + + return true; +} + +void D4FilterClause::m_duplicate(const D4FilterClause &rhs) { + d_op = rhs.d_op; + + d_arg1 = new D4RValue(*rhs.d_arg1); + d_arg2 = new D4RValue(*rhs.d_arg2); + +#if 0 + // Copy the D4RValue pointer if the 'value_kind' is a basetype, + // but build a new D4RValue if it is a constant (because the + // basetype is a weak pointer. + switch (rhs.d_arg1->get_kind()) { + case D4RValue::basetype: + d_arg1 = rhs.d_arg1; + break; + case D4RValue::constant: + d_arg1 = new D4RValue(*(rhs.d_arg1)); + break; + default: + throw Error(malformed_expr, "found a filter clause with a function call."); + } + + switch (rhs.d_arg2->get_kind()) { + case D4RValue::basetype: + d_arg2 = rhs.d_arg2; + break; + case D4RValue::constant: + d_arg2 = new D4RValue(*(rhs.d_arg2)); + break; + default: + throw Error(malformed_expr, "found a filter clause with a function call."); + } +#endif +} + +/** + * @brief Get the value of this relational expression. + * This version of value() works for function clauses, although that's + * not supported by the syntax at this time. + * @param dmr The DMR to use when evaluating a function + * @return True if the clause is true, false otherwise. + */ +bool D4FilterClause::value(DMR &dmr) +{ + switch (d_op) { + case null: + throw InternalErr(__FILE__, __LINE__, "While evaluating a constraint filter clause: Found a null operator"); + + case less: + case greater: + case less_equal: + case greater_equal: + case equal: + case not_equal: + case match: + return cmp(d_op, d_arg1->value(dmr), d_arg2->value(dmr)); + + case ND: + case map: + throw InternalErr(__FILE__, __LINE__, "While evaluating a constraint filter clause: Filter operator not implemented"); + + default: + throw InternalErr(__FILE__, __LINE__, "While evaluating a constraint filter clause: Unrecognized operator"); + } +} + +/** + * @brief Get the value of this relational expression. + * This version of value() will not work for clauses where one of the + * rvalues is a function call. This is not currently supported by the + * DAP4 specification, so it's probably no great loss. + * @return True if the clause is true, false otherwise. + */ +bool D4FilterClause::value() +{ + switch (d_op) { + case null: + throw InternalErr(__FILE__, __LINE__, "While evaluating a constraint filter clause: Found a null operator"); + + case less: + case greater: + case less_equal: + case greater_equal: + case equal: + case not_equal: + case match: + return cmp(d_op, d_arg1->value(), d_arg2->value()); + + case ND: + case map: + throw InternalErr(__FILE__, __LINE__, "While evaluating a constraint filter clause: Filter operator not implemented"); + + default: + throw InternalErr(__FILE__, __LINE__, "While evaluating a constraint filter clause: Unrecognized operator"); + } +} + +// It may be better to use the code in the Byte, ..., classes that was +// impl'd for DAP2 (with extensions). For now, test this and build the +// rest of the filter implementation. But there is certainly a more _compact_ +// way to code this! +// +// Optimize the extraction of constant values. +bool D4FilterClause::cmp(ops op, BaseType *arg1, BaseType *arg2) +{ + return arg1->d4_ops(arg2, op); +} + +} // namespace libdap diff --git a/D4FilterClause.h b/D4FilterClause.h new file mode 100644 index 0000000..9b64327 --- /dev/null +++ b/D4FilterClause.h @@ -0,0 +1,202 @@ + +// -*- mode: c++; c-basic-offset:4 -*- + +// This file is part of libdap, A C++ implementation of the OPeNDAP Data +// Access Protocol. + +// Copyright (c) 2015 OPeNDAP, Inc. +// Author: James Gallagher +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// +// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112. + +#ifndef _d4_filter_clause_h +#define _d4_filter_clause_h + +#include +#include + +#include "ce_expr.tab.hh" // Use the same codes for D4 as we use in DAP2 + +namespace libdap +{ + +class D4Rvalue; +class D4FilterClause; + +/** + * @brief List of DAP4 Filter Clauses + * + */ +class D4FilterClauseList +{ +private: + std::vector d_clauses; + + void m_duplicate(const D4FilterClauseList &src); + +public: + typedef std::vector::iterator iter; + typedef std::vector::const_iterator citer; + + D4FilterClauseList() { } + D4FilterClauseList(const D4FilterClauseList &src) { m_duplicate(src); } + + D4FilterClauseList(D4FilterClause *c) { add_clause(c); } + + virtual ~D4FilterClauseList(); + + D4FilterClauseList &operator=(const D4FilterClauseList &rhs) { + if (this == &rhs) + return *this; + + m_duplicate(rhs); + + return *this; + } + + void add_clause(D4FilterClause *c) { + d_clauses.push_back(c); + } + + D4FilterClause *get_clause(unsigned int i) { + return d_clauses.at(i); + } + + citer cbegin() const { return d_clauses.begin(); } + citer cend() const { return d_clauses.end(); } + + unsigned int size() const { return d_clauses.size(); } + + // get the clause value; this version supports functional clauses + bool value(DMR &dmr); + + bool value(); +}; + +/** + * @brief DAP4 filter clauses + * + * The DAP4 constraint expression provides a way to filter the values of + * Sequences (and possibly arrays and coverages, although those are still + * more ideas than anything at this point). This class holds the operator + * and operands of one DAP4 constraint's filter clause. The object is built + * during the parse phase of the constraint evaluation but is not evaluated + * until the data are sent or interned (read into the DAP4 variable object + * so that they can be used as input to some process other than directly + * being sent to a remote client). + * + * For filter clauses that are to be applied to a Sequence, each D4RValue + * will either be a constant or a BaseType* that will reference one of the + * Sequences fields. The method 'value()' is effectively the evaluator for + * the clause and nominally reads values from the rvalue objects. + * + * @note Potential optimization: Because Sequences might have an optimized + * representation as a STL vector of some built in types, there could be a + * value() method that takes a value and compares it to the clause's constant + * value using the supplied op. + * + * @note The 'ND' and 'map' ops are 'still just an idea' parts. + */ +class D4FilterClause +{ +public: + enum ops { + // Stock relops + null = 0, + less = SCAN_LESS, + greater = SCAN_GREATER, + less_equal = SCAN_LESS_EQL, + greater_equal = SCAN_GREATER_EQL, + equal = SCAN_EQUAL, + not_equal = SCAN_NOT_EQUAL, + // Regex match for strings + match = SCAN_REGEXP, + // The mapping operator; not sure if this will be implemented + map, + // No Data 'operator' for array filtering; may not be impl'd + ND + }; + +private: + /** The operator */ + ops d_op; + + D4RValue *d_arg1, *d_arg2; + + D4FilterClause() : d_op(null), d_arg1(0), d_arg2(0) { } + + void m_duplicate(const D4FilterClause &rhs); + + // These methods factor out first the first argument and then the + // second. I could write one really large cmp() for all of this... + //template bool cmp(ops op, BaseType *arg1, T arg2); + bool cmp(ops op, BaseType *arg1, BaseType *arg2); + + friend class D4FilterClauseList; + +public: + /** + * Build a D4FilterClause. The clause will take ownership of + * the two pointer arguments and delete them. + * + * @note When comparing an unsigned variable (UInt16) with a constant, + * at parse time (i.e., when the D4FilterClause is made) check that the + * constant is >= 0 and store it in an unsigned value at that time. This + * will avoid having to make the test repeatedly during filter evaluation. + * + * @note When parsing a constant, extract the value from the BaseType and + * store it in a local field, to avoid the overhead of extracting the + * value and looking up its type over an over. + * + * @param op The operator + * @param arg1 The left-hand operand + * @param arg2 The right-hand operand + */ + D4FilterClause(const ops op, D4RValue *arg1, D4RValue *arg2) : + d_op(op), d_arg1(arg1), d_arg2(arg2) { + assert(op != null && "null operator"); + assert(arg1 && "null arg1"); + assert(arg2 && "null arg2"); + } + + D4FilterClause(const D4FilterClause &src) { + m_duplicate(src); + } + + D4FilterClause &operator=(const D4FilterClause &rhs) { + if (this == &rhs) + return *this; + + m_duplicate(rhs); + + return *this; + } + + virtual ~D4FilterClause() { + delete d_arg1; + delete d_arg2; + } + + // get the clause value; this version supports functional clauses + bool value(DMR &dmr); + + bool value(); +}; + +} // namespace libdap + +#endif // _clause_h diff --git a/D4Function.h b/D4Function.h new file mode 100644 index 0000000..172b0fe --- /dev/null +++ b/D4Function.h @@ -0,0 +1,46 @@ +// -*- mode: c++; c-basic-offset:4 -*- + +// This file is part of libdap, A C++ implementation of the OPeNDAP Data +// Access Protocol. + +// Copyright (c) 2014 OPeNDAP, Inc. +// Author: James Gallagher +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// +// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112. + + +#ifndef D_FUNCTION_H_ +#define D_FUNCTION_H_ + +namespace libdap { + +class BaseType; +class D4RValueList; +class DMR; + +// D4Function is a pointer to a function that takes a pointer to an RValueList and +// a reference to a DMR and returns a pointer to a BaseTYpe. +// +// I think this would be better as a 'function that takes...' instead of a 'pointer +// to a function that takes...' but I used this to make the code fit more closely to +// the pattern established by the DAP2 CE functions. jhrg 3/10/14 + +typedef BaseType* (*D4Function)(D4RValueList *, DMR &); + +}// namespace libdap + +#endif /* D_FUNCTION_H_ */ diff --git a/D4Group.cc b/D4Group.cc new file mode 100644 index 0000000..8013fb6 --- /dev/null +++ b/D4Group.cc @@ -0,0 +1,745 @@ +// -*- mode: c++; c-basic-offset:4 -*- + +// This file is part of libdap, A C++ implementation of the OPeNDAP Data +// Access Protocol. + +// Copyright (c) 2013 OPeNDAP, Inc. +// Author: James Gallagher +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// +// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112. + +#include "config.h" + +//#define DODS_DEBUG + +#include +#include +#include + +#include + +#include "crc.h" + +#include "BaseType.h" +#include "Array.h" + +#include "XMLWriter.h" +#include "D4Attributes.h" +#include "D4Dimensions.h" +#include "D4Group.h" +#include "D4Enum.h" + +#include "D4StreamMarshaller.h" +#include "D4StreamUnMarshaller.h" + +#include "debug.h" + +/** + * Define this symbol iff we decide to include information about the + * byte order of the response (as sent from the server) so that the + * client can determine the correct CRC32 hash code. jhrg 1/4/16 + */ +#undef INCLUDE_SOURCE_BYTE_ORDER + +namespace libdap { + +void D4Group::m_duplicate(const D4Group &g) +{ + DBG(cerr << "In D4Group::m_duplicate for " << g.name() << endl); + + // dims; deep copy, this is the parent + if (g.d_dims) { + d_dims = new D4Dimensions(*(g.d_dims)); + d_dims->set_parent(this); + + // Update all of the D4Dimension weak pointers in the Array objects. + // This is a hack - we know that Constructor::m_duplicate() has been + // called at this point and any Array instances have dimension pointers + // that reference the 'old' dimensions (g.d_dims) and not the 'new' + // dimensions made above. Scan every array and re-wire the weak pointers. + // jhrg 8/15/14 + Vars_citer vi = d_vars.begin(); + while (vi != d_vars.end()) { + if ((*vi)->type() == dods_array_c) + static_cast(*vi)->update_dimension_pointers(g.d_dims, d_dims); + ++vi; + } + } + +#if 0 + // Moved this block up inside the if because g.d_dims might be false. jhrg 9/14/15 + Vars_citer vi = d_vars.begin(); + while (vi != d_vars.end()) { + if ((*vi)->type() == dods_array_c) + static_cast(*vi)->update_dimension_pointers(g.d_dims, d_dims); + ++vi; + } +#endif + + // enums; deep copy + if (g.d_enum_defs) d_enum_defs = new D4EnumDefs(*g.d_enum_defs); + + // groups + groupsCIter i = g.d_groups.begin(); + while(i != g.d_groups.end()) { + // Only D4Groups are in the d_groups container. + D4Group *g = static_cast((*i++)->ptr_duplicate()); + add_group_nocopy(g); + } + + DBG(cerr << "Exiting D4Group::m_duplicate" << endl); +} + +/** The D4Group constructor requires only the name of the variable + to be created. The name may be omitted, which will create a + nameless variable. This may be adequate for some applications. + + @note This type is available in DAP4 only. + See http://docs.opendap.org/index.php/DAP4:_Specification_Volume_1#Groups + + + @param n A string containing the name of the variable. +*/ +D4Group::D4Group(const string &name) + : Constructor(name, dods_group_c, /*is_dap4*/true), d_dims(0), d_enum_defs(0) +{} + +/** The D4Group server-side constructor requires the name of the variable + to be created and the dataset name from which this variable is being + created. Used on server-side handlers. + + @note This type is available in DAP4 only. + See http://docs.opendap.org/index.php/DAP4:_Specification_Volume_1#Groups + + @param n A string containing the name of the variable. + @param d A string containing the name of the dataset. +*/ +D4Group::D4Group(const string &name, const string &dataset) + : Constructor(name, dataset, dods_group_c, /*is_dap4*/true), d_dims(0), d_enum_defs(0) +{} + +/** The D4Group copy constructor. */ +D4Group::D4Group(const D4Group &rhs) : Constructor(rhs), d_dims(0), d_enum_defs(0) +{ + DBG(cerr << "In D4Group::copy_ctor for " << rhs.name() << endl); + m_duplicate(rhs); +} + +D4Group::~D4Group() +{ + delete d_dims; + delete d_enum_defs; + + groupsIter i = d_groups.begin(); + while(i != d_groups.end()) + delete *i++; +} + +#if 0 +D4Group * + +// I think this was a mistake. jhrg 11/17/16 +#endif +BaseType * +D4Group::ptr_duplicate() +{ + return new D4Group(*this); +} + +D4Group & +D4Group::operator=(const D4Group &rhs) +{ + if (this == &rhs) + return *this; + + dynamic_cast(*this) = rhs; // run Constructor= + + m_duplicate(rhs); + + return *this; +} + +/** + * Get the Fully Qualified Name for this Group, including the Group. This + * uses the name representation described in the DAP4 specification. + * + * @return The FQN in a string + */ +string +D4Group::FQN() const +{ + // The root group is named "/" (always) + return (name() == "/") ? "/" : static_cast(get_parent())->FQN() + name() + "/"; +} + +// Note that in order for this to work the second argument must not be a reference. +// jhrg 8/20/13 +static bool +name_eq(D4Group *g, const string name) +{ + return g->name() == name; +} + +D4Group * +D4Group::find_child_grp(const string &grp_name) +{ + groupsIter g = find_if(grp_begin(), grp_end(), bind2nd(ptr_fun(name_eq), grp_name)); + return (g == grp_end()) ? 0: *g; +} + +// TODO Add constraint param? jhrg 11/17/13 +BaseType * +D4Group::find_first_var_that_uses_dimension(D4Dimension *dim) +{ + // for each group, starting with the root group + // for each variable in the group that is marked to send and is an array + // return the btp if it uses the D4Dimension + // if it contains child groups, search those + // return the btp if it uses the D4Dimension + // return null + + // exhaustive breadth-first search for 'dim + + // root group + for (Vars_iter i = var_begin(), e = var_end(); i != e; ++i) { + if ((*i)->send_p() && (*i)->type() == dods_array_c) { + Array *a = static_cast(*i); + for (Array::Dim_iter di = a->dim_begin(), de = a->dim_end(); di != de; ++di) { + if (a->dimension_D4dim(di) == dim) + return a; + } + } + } + + for (groupsIter i = grp_begin(), e = grp_end(); i != e; ++i) { + BaseType *btp = (*i)->find_first_var_that_uses_dimension(dim); + if (btp) return btp; + } + + return 0; +} + +BaseType * +D4Group::find_first_var_that_uses_enumeration(D4EnumDef *enum_def) +{ + // for each group, starting with the root group + // for each variable in the group that is marked to send and is an array + // return the btp if it uses the D4EnumDef + // if it contains child groups, search those + // return the btp if it uses the D4EnumDef + // return null + + // exhaustive breadth-first search for 'dim + + // root group + for (Vars_iter i = var_begin(), e = var_end(); i != e; ++i) { + if ((*i)->send_p() && (*i)->type() == dods_enum_c) { + D4Enum *e = static_cast(*i); + if (e->enumeration() == enum_def) + return e; + } + } + + for (groupsIter i = grp_begin(), e = grp_end(); i != e; ++i) { + BaseType *btp = (*i)->find_first_var_that_uses_enumeration(enum_def); + if (btp) return btp; + } + + return 0; +} + +/** + * @brief Find the dimension using a path. + * Using the DAP4 name syntax, lookup a dimension. The dimension must + * be defined before it is used. The \c path argument may be either an + * absolute path or a relative path. Note that the name syntax does not + * provide for paths to contain an 'up one level' symbol. + * @param path The path to the dimension + * @return A pointer to the D4Dimension object. + */ +D4Dimension * +D4Group::find_dim(const string &path) +{ + string lpath = path; // get a mutable copy + + // special-case for the root group + if (lpath[0] == '/') { + if (name() != "/") + throw InternalErr(__FILE__, __LINE__, "Lookup of a FQN starting in non-root group."); + else + lpath = lpath.substr(1); + } + + string::size_type pos = lpath.find('/'); + if (pos == string::npos) { + // name looks like 'bar' + return dims()->find_dim(lpath); + } + + // name looks like foo/bar/baz where foo and bar must be groups + string grp_name = lpath.substr(0, pos); + lpath = lpath.substr(pos + 1); + + D4Group *grp = find_child_grp(grp_name); + return (grp == 0) ? 0: grp->find_dim(lpath); +} + +Array * +D4Group::find_map_source(const string &path) +{ + BaseType *map_source = m_find_map_source_helper(path); + + // TODO more complete semantic checking jhrg 10/16/13 + if (map_source && map_source->type() == dods_array_c) return static_cast(map_source); + + return 0; +} + +BaseType * +D4Group::m_find_map_source_helper(const string &path) +{ + string lpath = path; // get a mutable copy + + // special-case for the root group + if (lpath[0] == '/') { + if (name() != "/") + throw InternalErr(__FILE__, __LINE__, "Lookup of a FQN starting in non-root group."); + else + lpath = lpath.substr(1); + } + + string::size_type pos = lpath.find('/'); + if (pos == string::npos) { + // name looks like 'bar' + return var(lpath); + } + + // name looks like foo/bar/baz where foo an bar must be groups + string grp_name = lpath.substr(0, pos); + lpath = lpath.substr(pos + 1); + + D4Group *grp = find_child_grp(grp_name); + return (grp == 0) ? 0: grp->var(lpath); +} + +D4EnumDef * +D4Group::find_enum_def(const string &path) +{ + string lpath = path; // get a mutable copy + + // special-case for the root group + if (lpath[0] == '/') { + if (name() != "/") + throw InternalErr(__FILE__, __LINE__, "Lookup of a FQN starting in non-root group."); + else + lpath = lpath.substr(1); + } + + string::size_type pos = lpath.find('/'); + if (pos == string::npos) { + // name looks like 'bar' + return enum_defs()->find_enum_def(lpath); + } + + // name looks like foo/bar/baz where foo and bar must be groups + string grp_name = lpath.substr(0, pos); + lpath = lpath.substr(pos + 1); + + D4Group *grp = find_child_grp(grp_name); + return (grp == 0) ? 0: grp->enum_defs()->find_enum_def(lpath); +} + +/** + * Find a variable using it's FUlly Qualified Name (FQN). The leading '/' is optional. + * + * @param path The FQN to the variable + * @return A BaseType* to the variable of null if it was not found + * @see BaseType::FQN() + */ +BaseType * +D4Group::find_var(const string &path) +{ + string lpath = path; // get a mutable copy + + // special-case for the root group + if (lpath[0] == '/') { + if (name() != "/") + throw InternalErr(__FILE__, __LINE__, "Lookup of a FQN starting in non-root group."); + else + lpath = lpath.substr(1); + } + + string::size_type pos = lpath.find('/'); + if (pos == string::npos) { + // name looks like 'bar' or bar.baz; lookup in the Constructor that's part of the Group + return var(lpath); + } + + // name looks like foo/bar/baz where foo and bar must be groups + string grp_name = lpath.substr(0, pos); + lpath = lpath.substr(pos + 1); + + D4Group *grp = find_child_grp(grp_name); + return (grp == 0) ? 0 : grp->find_var(lpath); +} + +/** Compute the size of all of the variables in this group and it's children, + * in kilobytes + * + * @param constrained Should the current constraint be taken into account? + * @return The size in kilobytes + */ +long +D4Group::request_size(bool constrained) +{ + long long size = 0; + // variables + Constructor::Vars_iter v = var_begin(); + while (v != var_end()) { + if (constrained) { + if ((*v)->send_p()) + size += (*v)->width(constrained); + } + else { + size += (*v)->width(constrained); + } + + ++v; + } + + // groups + groupsIter g = d_groups.begin(); + while (g != d_groups.end()) + size += (*g++)->request_size(constrained); + + return size / 1024; +} + +void +D4Group::set_read_p(bool state) +{ + groupsIter g = d_groups.begin(); + while (g != d_groups.end()) + (*g++)->set_read_p(state); + + Constructor::set_read_p(state); +} + +void +D4Group::set_send_p(bool state) +{ + groupsIter g = d_groups.begin(); + while (g != d_groups.end()) + (*g++)->set_send_p(state); + + Constructor::set_send_p(state); +} + +void +D4Group::intern_data(/*Crc32 &checksum, DMR &dmr, ConstraintEvaluator &eval*/) +{ + groupsIter g = d_groups.begin(); + while (g != d_groups.end()) + (*g++)->intern_data(/*checksum, dmr, eval*/); + + // Specialize how the top-level variables in any Group are sent; include + // a checksum for them. A subset operation might make an interior set of + // variables, but the parent structure will still be present and the checksum + // will be computed for that structure. In other words, DAP4 does not try + // to sort out which variables are the 'real' top-level variables and instead + // simply computes the CRC for whatever appears as a variable in the root + // group. + for (Vars_iter i = d_vars.begin(); i != d_vars.end(); i++) { + // Only send the stuff in the current subset. + if ((*i)->send_p()) { +#if 0 + checksum.Reset(); +#endif + (*i)->intern_data(/*checksum, dmr, eval*/); +#if 0 + D4Attribute *a = new D4Attribute("DAP4_Checksum_CRC32", attr_str_c); + + ostringstream oss; + oss.setf(ios::hex, ios::basefield); + oss << setfill('0') << setw(8) << checksum.GetCrc32(); + a->add_value(oss.str()); +#if INCLUDE_SOURCE_BYTE_ORDER + if (um.is_source_big_endian()) + a->add_value("source:big-endian"); + else + a->add_value("source:little-endian"); +#endif + (*i)->attributes()->add_attribute_nocopy(a); + DBG(cerr << "CRC32: " << oss.str() << " for " << (*i)->name() << endl); +#endif + } + } +} + +/** + * @brief Serialize a Group + * @param m The DAP4 Stream Marshaller. This object serializes the data values and + * writes checksums (using CRC32) for the top level variables in every Group for which + * one or more variables are sent. The DAP4 Marshaller object can be made so that only + * the checksums are written. + * @param dmr Unused + * @param eval Unused + * @param filter Unused + * @exception Error is thrown if the value needs to be read and that operation fails. + */ +void +D4Group::serialize(D4StreamMarshaller &m, DMR &dmr, /*ConstraintEvaluator &eval,*/ bool filter) +{ +#if 0 + // This will call Constructor read which will, for everything but a Sequence, + // read all of the data in one shot. However, the serialize() methods for the + // Arrays, Structures, etc., also have read() calls in them and those can be + // used to control how long the data are in memory, e.g., limiting the lifetime + // of a large array and avoiding having overlapping arrays when they are not + // needed. For a sequence read() has different semantics. It is called once + // for every instance and the read_p flag is not used. + if (!read_p()) + read(); // read() throws Error +#endif + + groupsIter g = d_groups.begin(); + while (g != d_groups.end()) + (*g++)->serialize(m, dmr, filter); + + // Specialize how the top-level variables in any Group are sent; include + // a checksum for them. A subset operation might make an interior set of + // variables, but the parent structure will still be present and the checksum + // will be computed for that structure. In other words, DAP4 does not try + // to sort out which variables are the 'real' top-level variables and instead + // simply computes the CRC for whatever appears as a variable in the root + // group. + for (Vars_iter i = d_vars.begin(); i != d_vars.end(); i++) { + // Only send the stuff in the current subset. + if ((*i)->send_p()) { + m.reset_checksum(); + + DBG(cerr << "Serializing variable " << (*i)->type_name() << " " << (*i)->name() << endl); + (*i)->serialize(m, dmr, filter); + + DBG(cerr << "Wrote CRC32: " << m.get_checksum() << " for " << (*i)->name() << endl); + m.put_checksum(); + } + } +} + +void D4Group::deserialize(D4StreamUnMarshaller &um, DMR &dmr) +{ + groupsIter g = d_groups.begin(); + while (g != d_groups.end()) { + DBG(cerr << "Deserializing group " << (*g)->name() << endl); + (*g++)->deserialize(um, dmr); + } + // Specialize how the top-level variables in any Group are received; read + // their checksum and store the value in a magic attribute of the variable + for (Vars_iter i = d_vars.begin(); i != d_vars.end(); i++) { + DBG(cerr << "Deserializing variable " << (*i)->type_name() << " " << (*i)->name() << endl); + (*i)->deserialize(um, dmr); + + D4Attribute *a = new D4Attribute("DAP4_Checksum_CRC32", attr_str_c); + string crc = um.get_checksum_str(); + a->add_value(crc); +#if INCLUDE_SOURCE_BYTE_ORDER + if (um.is_source_big_endian()) + a->add_value("source:big-endian"); + else + a->add_value("source:little-endian"); +#endif + DBG(cerr << "Read CRC32: " << crc << " for " << (*i)->name() << endl); + (*i)->attributes()->add_attribute_nocopy(a); + } +} + +void +D4Group::print_dap4(XMLWriter &xml, bool constrained) +{ + if (!name().empty() && name() != "/") { + // For named groups, if constrained is true only print if this group + // has variables that are marked for transmission. For the root group + // this test is not made. + if (constrained && !send_p()) + return; + + if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*) type_name().c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write " + type_name() + " element"); + + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "name", (const xmlChar*) name().c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name"); + } + + // dims + if (!dims()->empty()) + dims()->print_dap4(xml, constrained); + + // enums + if (!enum_defs()->empty()) + enum_defs()->print_dap4(xml, constrained); + + // variables + Constructor::Vars_iter v = var_begin(); + while (v != var_end()) + (*v++)->print_dap4(xml, constrained); + + // attributes + attributes()->print_dap4(xml); + + // groups + groupsIter g = d_groups.begin(); + while (g != d_groups.end()) + (*g++)->print_dap4(xml, constrained); + + if (!name().empty() && name() != "/") { + if (xmlTextWriterEndElement(xml.get_writer()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not end " + type_name() + " element"); + } +} + + +/** @brief DAP4 to DAP2 transform + * + * D4Group objects, with the exception of the root group, "disappear" + * into the names of their member variables. Specifically the Group + * name is add as a prefix followed by a "/" separator to the names + * of all of the Group's member groups variables. The Group attributes + * (metadata) are transfered to the parent_attr_table. The Group + * members are collected returned in vector. + * + * + * @param The AttrTable pointer parent_attr_table is used by Groups, which disappear + * from the DAP2 representation. Their children are returned in the the BAseType vector + * their attributes are added to parent_attr_table; + * @return A pointer to a vector of BaseType pointers (right?). In this D4Group case the + * vector will contain DAP2 versions of all of the member variables of the D4Group instance. + * (ex: UInt64) the will return a NULL pointer and so this must be tested! + */ +vector * +D4Group::transform_to_dap2(AttrTable *parent_attr_table){ + return transform_to_dap2(parent_attr_table,false); +} +vector * +D4Group::transform_to_dap2(AttrTable *parent_attr_table, bool is_root) +{ + DBG( cerr << __func__ << "() - BEGIN ("<< name() << " is_root: "<< (is_root?"true":"false") << ")" << endl;); + vector *results = new vector(); + vector dropped_vars; + + AttrTable *group_attrs; + group_attrs = attributes()->get_AttrTable(name()); + + /** + * If this is the root group then we handle the attributes differently. + */ + if(is_root){ + DBG( cerr << __func__ << "() - Promoting group attributes to parent" << endl;); + // If it's a root group we copy all the stuff up into the parent attr table + for (AttrTable::Attr_iter i = group_attrs->attr_begin(), e = group_attrs->attr_end(); i != e; ++i) { + if ((*i)->type == Attr_container) { + // copy the source container so that the DAS passed in can be + // deleted after calling this method. + AttrTable *at = new AttrTable(*(*i)->attributes); + parent_attr_table->append_container(at, at->get_name()); + } + else { + parent_attr_table->append_attr( + (*i)->name, + AttrType_to_String((*i)->type), + (*i)->attr); + } + } + delete group_attrs; + group_attrs = parent_attr_table; + } + + /** + * Now we process the child variables of this group + */ + for (D4Group::Vars_citer varIter = var_begin(), e = var_end(); varIter != e; ++varIter) { + DBG( cerr << __func__ << "() - Processing member variable '" << (*varIter)->name() << + "' root: " << (is_root?"true":"false") << endl;); + vector *new_vars = (*varIter)->transform_to_dap2(group_attrs); + if (new_vars) { // Might be un-mappable + // It's not so game on.. + vector::iterator vIter = new_vars->begin(); + vector::iterator end = new_vars->end(); + for( ; vIter!=end ; vIter++ ){ + BaseType *new_var = (*vIter); + + string new_name = (is_root?"":FQN()) + new_var->name(); + new_var->set_name(new_name); + results->push_back(new_var); + (*vIter) = NULL; + DBG( cerr << __func__ << "() - Added member variable '" << (*varIter)->name() << "' " << + "to results vector. root: "<< (is_root?"true":"false") << endl;); + } + delete new_vars; + } + else { + DBG( cerr << __func__ << "() - Dropping member variable " << (*varIter)->name() << + " root: " << (is_root?"true":"false") << endl;); + // Got back a NULL, so we are dropping this var. + dropped_vars.push_back(*varIter); + } + } + // Process dropped DAP4 vars + DBG( cerr << __func__ << "() - Processing " << dropped_vars.size() << " Dropped Variable(s)" << endl;); + AttrTable *dv_attr_table = make_dropped_vars_attr_table(&dropped_vars); + if(dv_attr_table){ + DBG( cerr << __func__ << "() - Adding Dropped Variables AttrTable" << endl;); + group_attrs->append_container(dv_attr_table,dv_attr_table->get_name()); + } + else { + DBG( cerr << __func__ << "() - No Dropped Variables AttrTable returned." << endl;); + + } + + /** + * Get all the child groups. + */ + D4Group::groupsIter gIter = grp_begin(); + D4Group::groupsIter gEnd = grp_end(); + for( ; gIter!=gEnd ; gIter++){ + D4Group *grp = *gIter; + DBG( cerr << __func__ << "() - Processing D4Group " << grp->name() << endl;); + vector *d2_vars = grp->transform_to_dap2(group_attrs); + if(d2_vars){ + DBG( cerr << __func__ << "() - Processing " << grp->name() << " Member Variables." << endl;); + vector::iterator vIter = d2_vars->begin(); + vector::iterator vEnd = d2_vars->end(); + for( ; vIter!=vEnd; vIter++){ + DBG( cerr << __func__ << "() - Processing " << grp->name() << " Member Variable: " << (*vIter)->name() << endl;); + results->push_back(*vIter); + } + } + + + + } + + if(!is_root){ + group_attrs->set_name(name()); + parent_attr_table->append_container(group_attrs,group_attrs->get_name()); + } + DBG( cerr << __func__ << "() - END" << endl;); + return results; +} + + +} /* namespace libdap */ diff --git a/D4Group.h b/D4Group.h new file mode 100644 index 0000000..683afe3 --- /dev/null +++ b/D4Group.h @@ -0,0 +1,149 @@ +// -*- mode: c++; c-basic-offset:4 -*- + +// This file is part of libdap, A C++ implementation of the OPeNDAP Data +// Access Protocol. + +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// +// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112. + +#ifndef D4GROUP_H_ +#define D4GROUP_H_ + +#include + +#include "Constructor.h" +#include "D4Dimensions.h" +#include "D4EnumDefs.h" + +class Crc32; + +namespace libdap { + +class BaseType; +class Array; + +/** A DAP4 Group object. A Group is-a Constructor, so it inherits a set of + * BaseType objects and an attribute table, along with methods to search for + * variables by name where dots (.) in a fully qualified name serve as + * separators. + */ +class D4Group :public Constructor { +private: + // Note that because Constructor is a BaseType, this class inherits + // both a back pointer to its parent, an AttrTable and, directly from the + // Constructor class, a vector of BaseTypes. + + // This instance of D4Dimensions holds the Group's definitions; the same + // class is used by Array to hold the actual dimensions for a variable. + D4Dimensions *d_dims; + + // This holds the Group's enumeration definitions; a different class is + // used for the Enumeration type + D4EnumDefs *d_enum_defs; + + // This is a pointer so that the factory class(es) that return pointers + // work as expected when making Groups. + vector d_groups; + + BaseType *m_find_map_source_helper(const string &name); + +protected: + void m_duplicate(const D4Group &g); + +public: + typedef vector::iterator groupsIter; + typedef vector::const_iterator groupsCIter; + + D4Group(const string &name); + D4Group(const string &name, const string &dataset); + + D4Group(const D4Group &rhs); + virtual ~D4Group(); + + D4Group &operator=(const D4Group &rhs); + + // This method returned a D4Group * previously. jhrg 11/17/16 + virtual BaseType *ptr_duplicate(); + + /// Get the dimensions defined for this Group + D4Dimensions *dims() { + // If not built yet, make one and set this as parent. + if (!d_dims) d_dims = new D4Dimensions(this); + return d_dims; + } + + virtual std::string FQN() const; + + D4Dimension *find_dim(const string &path); + + Array *find_map_source(const string &path); + + D4EnumDef *find_enum_def(const string &path); + + /// Get the enumerations defined for this Group + D4EnumDefs *enum_defs() { + if (!d_enum_defs) { + d_enum_defs = new D4EnumDefs; + d_enum_defs->set_parent(this); + } + return d_enum_defs; + } + + BaseType *find_first_var_that_uses_dimension(D4Dimension *dim); + BaseType *find_first_var_that_uses_enumeration(D4EnumDef *enum_def); + + BaseType *find_var(const string &name); + + /// Get an iterator to the start of the values + groupsIter grp_begin() { return d_groups.begin(); } + + /// Get an iterator to the end of the values + groupsIter grp_end() { return d_groups.end(); } + + void add_group(const D4Group *g) { + add_group_nocopy(new D4Group(*g)); + } + + void add_group_nocopy(D4Group *g) { + g->set_parent(this); + d_groups.push_back(g); + } + void insert_group_nocopy(D4Group *g, groupsIter i) { + g->set_parent(this); + d_groups.insert(i, g); + } + + D4Group *find_child_grp(const string &grp_name); + + long request_size(bool constrained); + + virtual void set_send_p(bool state); + virtual void set_read_p(bool state); + + // DAP4 + virtual void intern_data(/*Crc32 &checksum, DMR &dmr, ConstraintEvaluator &eval*/); + virtual void serialize(D4StreamMarshaller &m, DMR &dmr, /*ConstraintEvaluator &eval,*/ bool filter = false); + virtual void deserialize(D4StreamUnMarshaller &um, DMR &dmr); + + void print_dap4(XMLWriter &xml, bool constrained = false); + + virtual std::vector *transform_to_dap2(AttrTable *parent_attr_table); + virtual std::vector *transform_to_dap2(AttrTable *parent_attr_table, bool use_name_prefix); + +}; + +} /* namespace libdap */ +#endif /* D4GROUP_H_ */ diff --git a/D4Maps.cc b/D4Maps.cc new file mode 100644 index 0000000..384cf9a --- /dev/null +++ b/D4Maps.cc @@ -0,0 +1,53 @@ +// -*- mode: c++; c-basic-offset:4 -*- + +// This file is part of libdap, A C++ implementation of the OPeNDAP Data +// Access Protocol. + +// Copyright (c) 2013 OPeNDAP, Inc. +// Author: James Gallagher +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// +// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112. + +#include "config.h" + +#include "XMLWriter.h" +#include "InternalErr.h" +#include "D4Maps.h" + +using namespace libdap; + +void +D4Map::print_dap4(XMLWriter &xml) +{ + if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*) "Map") < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write Map element"); + + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "name", (const xmlChar*)d_name.c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name"); + + if (xmlTextWriterEndElement(xml.get_writer()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not end Map element"); + +} + +D4Maps& +D4Maps::operator=(const D4Maps &rhs) +{ + if (this == &rhs) return *this; + m_duplicate(rhs); + return *this; +} diff --git a/D4Maps.h b/D4Maps.h new file mode 100644 index 0000000..ae5c254 --- /dev/null +++ b/D4Maps.h @@ -0,0 +1,151 @@ +// -*- mode: c++; c-basic-offset:4 -*- + +// This file is part of libdap, A C++ implementation of the OPeNDAP Data +// Access Protocol. + +// Copyright (c) 2013 OPeNDAP, Inc. +// Author: James Gallagher +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// +// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112. + +#ifndef D4MAPS_H_ +#define D4MAPS_H_ + +#include +#include + +using namespace std; + +namespace libdap { + +class Array; +class XMLWriter; + +/** + * A 'Map' in DAP4 is an Array in the dataset that is used to provide the + * domain values for a Coverage (aka a Grid). These Maps are a more + * general case of the DAP2 'Map vectors' because a DAP4 Map can have N + * dimensions. + * + * Because the Maps can be shared by any or all of the Arrays in a dataset, + * they also correspond to the NetCDF/CF notion of a Shared Dimension. + * + * In this implementation of the D4Map, each Map has a name and two weak + * pointers, one to the Array that holds the domain values and one to the + * Array that uses the Map. Note that while Maps can be shared by Arrays, + * each Array has it's own collection of these D4Map objects. This makes + * processing constraints possible (because it is possible to write + * different constraints for two arrays that share Maps). + */ +class D4Map { + std::string d_name; + Array *d_array; // the actual map data; weak pointer + Array *d_parent; // what array holds this map; weak pointer + +public: + D4Map() : d_name(""), d_array(0), d_parent(0) { } + D4Map(const string &name, Array *array, Array *parent = 0) : d_name(name), d_array(array), d_parent(parent) { } + + virtual ~D4Map() { } + + const string& name() const { return d_name; } + void set_name(const string& name) { d_name = name; } + + const Array* array() const { return d_array; } + void set_array(Array* array) { d_array = array; } + + /** + * @brief The Array that holds this Map + */ + const Array* parent() const { return d_parent; } + void set_parent(Array* parent) { d_parent = parent; } + + virtual void print_dap4(XMLWriter &xml); +}; + +/** + * The D4Maps object holds pointers to all of the Maps used by + * a given Array. + */ +class D4Maps { +public: + typedef vector::iterator D4MapsIter; + typedef vector::const_iterator D4MapsCIter; + +private: + vector d_maps; + Array *d_parent; // Array these Maps belong to; weak pointer + + void m_duplicate(const D4Maps &maps) { + d_parent = maps.d_parent; + for (D4MapsCIter ci = maps.d_maps.begin(), ce = maps.d_maps.end(); ci != ce; ++ci) { + d_maps.push_back(new D4Map(**ci)); + } + } + +public: + D4Maps() {} + D4Maps(Array* parent) : d_parent(parent) { } + D4Maps(const D4Maps &maps) { m_duplicate(maps); } + virtual ~D4Maps() { + for (D4MapsIter i = d_maps.begin(), e = d_maps.end(); i != e; ++i) + delete *i; + } + + D4Maps &operator=(const D4Maps &rhs); + + /** + * Add a map. This does not test for duplicate names or Array pointers; + * It assumes that the caller has done that! + */ + void add_map(D4Map *map) { + d_maps.push_back(map); + // if the Map parent is not set, do so now + if (!d_maps.back()->parent()) + d_maps.back()->set_parent(d_parent); + } + + void remove_map(D4Map *map) { + for (D4MapsIter i = d_maps.begin(), e = d_maps.end(); i != e; ++i) { + /* && (*i)->parent() == map->parent() */ + // Don't test if the map->parent() matches - we only care about the name and array. + // This method is intended for processing CE array slices that are edge cases and + // is only called from code where we know map->parent() matches *i->parent(). + // jhrg 4/12/16 + if ((*i)->name() == map->name() && (*i)->array() == map->array()) { + d_maps.erase(i); + break; + } + } + } + + D4Map* get_map(int i) { return d_maps.at(i); } + + D4MapsIter map_begin() { return d_maps.begin(); } + D4MapsIter map_end() { return d_maps.end(); } + + int size() const { return d_maps.size(); } + bool empty() const { return d_maps.empty(); } + + virtual void print_dap4(XMLWriter &xml) { + for (D4MapsIter i = d_maps.begin(), e = d_maps.end(); i != e; ++i) + (*i)->print_dap4(xml); + } +}; + +} /* namespace libdap */ +#endif /* D4MAPS_H_ */ diff --git a/D4Opaque.cc b/D4Opaque.cc new file mode 100644 index 0000000..dd53b5a --- /dev/null +++ b/D4Opaque.cc @@ -0,0 +1,184 @@ +// -*- mode: c++; c-basic-offset:4 -*- + +// This file is part of libdap, A C++ implementation of the OPeNDAP Data +// Access Protocol. + +// Copyright (c) 2013 OPeNDAP, Inc. +// Author: James Gallagher +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin D4Opaqueeet, Fifth Floor, Boston, MA 02110-1301 USA +// +// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112. + +//#define DODS_DEBUG + +#include "config.h" + +#include +#include + +#include "D4Opaque.h" + +#include "DMR.h" +#include "D4StreamMarshaller.h" +#include "D4StreamUnMarshaller.h" + +#include "util.h" +#include "crc.h" + +#include "debug.h" + +#undef CLEAR_LOCAL_DATA + +using namespace std; + +namespace libdap { + +D4Opaque & +D4Opaque::operator=(const D4Opaque &rhs) +{ + if (this == &rhs) + return *this; + + // Call BaseType::operator= + dynamic_cast(*this) = rhs; + + d_buf = rhs.d_buf; + + return *this; +} + +void +D4Opaque::clear_local_data() +{ + if (!d_buf.empty()) { + d_buf.erase(d_buf.begin(), d_buf.end()); + d_buf.resize(0); + } + + set_read_p(false); +} + +void +D4Opaque::compute_checksum(Crc32 &checksum) +{ + checksum.AddData(&d_buf[0], d_buf.size()); +} + +void +D4Opaque::serialize(D4StreamMarshaller &m, DMR &, bool) +{ + if (!read_p()) + read(); // read() throws Error + + m.put_opaque_dap4( reinterpret_cast(&d_buf[0]), d_buf.size() ) ; + +#ifdef CLEAR_LOCAL_DATA + clear_local_data(); +#endif + +} + +void +D4Opaque::deserialize(D4StreamUnMarshaller &um, DMR &) +{ + um.get_opaque_dap4( d_buf ) ; +} + +unsigned int +D4Opaque::buf2val(void **val) +{ + assert(val); + + // If *val is null, then the caller has not allocated storage for the + // value; we must. If there is storage there, assume it is a vector + // (i.e., dods_opaque) and assign d_buf's value to that storage. + if (!*val) + *val = new vector; + else + *static_cast*>(*val) = d_buf; + + return sizeof(vector*); +} + +unsigned int +D4Opaque::val2buf(void *val, bool) +{ + assert(val); + + d_buf = *static_cast(val); + + return sizeof(dods_opaque*); +} + +/** Set the value of this instance. + @param value The value + @return Always returns true; the return type of bool is for compatibility + with the Passive* subclasses written by HAO. */ +bool +D4Opaque::set_value(const dods_opaque &value) +{ + d_buf = value; + set_read_p(true); + + return true; +} + +/** Get the value of this instance. + @return The value. */ +D4Opaque::dods_opaque +D4Opaque::value() const +{ + return d_buf; +} + +std::vector * +D4Opaque::transform_to_dap2(AttrTable *){ + DBG(cerr << __func__ << "() - Transform not implemented DAP4 Opaque type." << endl;); + return NULL; +} + + +void +D4Opaque::print_val(ostream &out, string space, bool print_decl_p) +{ + if (print_decl_p) print_decl(out, space, false); + + if (d_buf.size()) { + // end() - 1 is only OK if size() is > 0 + std::ostream_iterator out_it(out, ","); + std::copy(d_buf.begin(), d_buf.end() - 1, out_it); + out << (unsigned int) d_buf.back(); // can also use: *(d_buf.end()-1); + } + + if (print_decl_p) out << ";" << endl; +} + +void +D4Opaque::dump(ostream &strm) const +{ + strm << DapIndent::LMarg << "D4Opaque::dump - (" + << (void *)this << ")" << endl ; + DapIndent::Indent() ; + BaseType::dump(strm) ; + //strm << DapIndent::LMarg << "value: " << d_buf << endl ; + ostream_iterator out_it (strm," "); + std::copy ( d_buf.begin(), d_buf.end(), out_it ); + + DapIndent::UnIndent() ; +} + +} // namespace libdap + diff --git a/D4Opaque.h b/D4Opaque.h new file mode 100644 index 0000000..008fc3e --- /dev/null +++ b/D4Opaque.h @@ -0,0 +1,110 @@ +// -*- mode: c++; c-basic-offset:4 -*- + +// This file is part of libdap, A C++ implementation of the OPeNDAP Data +// Access Protocol. + +// Copyright (c) 2013 OPeNDAP, Inc. +// Author: James Gallagher +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// +// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112. + +#ifndef _d4_opaque_h +#define _d4_opaque_h 1 + +#include + +#include "BaseType.h" +#include "InternalErr.h" + +class Crc32; + +namespace libdap +{ + +class D4Opaque: public BaseType +{ +public: + typedef std::vector dods_opaque; + +protected: + dods_opaque d_buf; + +public: + D4Opaque(const std::string &n) : BaseType(n, dods_opaque_c, true /*is_dap4*/), d_buf(0) { } + D4Opaque(const std::string &n, const std::string &d) : BaseType(n, d, dods_opaque_c, true /*is_dap4*/), d_buf(0) { } + + virtual ~D4Opaque() { } + + D4Opaque(const D4Opaque ©_from) : BaseType(copy_from) { + d_buf = copy_from.d_buf; + } + + D4Opaque &operator=(const D4Opaque &rhs); + + virtual BaseType *ptr_duplicate() { return new D4Opaque(*this); } + + virtual void clear_local_data(); + + virtual unsigned int width(bool = false) const { return sizeof(vector); } + + // Return the length of the stored data or zero if no string has been + // stored in the instance's internal buffer. + virtual int length() const { return d_buf.size(); } + + // DAP2 + virtual bool serialize(ConstraintEvaluator &, DDS &, Marshaller &, bool = true) { + throw InternalErr(__FILE__, __LINE__, "Unimplemented method"); + } + virtual bool deserialize(UnMarshaller &, DDS *, bool = false) { + throw InternalErr(__FILE__, __LINE__, "Unimplemented method"); + } + + // DAP4 + virtual void compute_checksum(Crc32 &checksum); + virtual void serialize(D4StreamMarshaller &m, DMR &dmr, /*ConstraintEvaluator &eval,*/ bool filter = false); +#if 0 + virtual void serialize_no_release(D4StreamMarshaller &m, DMR &dmr, bool filter = false); +#endif + virtual void deserialize(D4StreamUnMarshaller &um, DMR &dmr); + + virtual unsigned int val2buf(void *val, bool reuse = false); + virtual unsigned int buf2val(void **val); + + virtual bool set_value(const dods_opaque &value); + virtual dods_opaque value() const; + + virtual void print_val(FILE *, std::string = "", bool = true) { + throw InternalErr(__FILE__, __LINE__, "Unimplemented method"); + } + virtual void print_val(std::ostream &out, std::string space = "", bool print_decl_p = true); + + //virtual void print_dap4(XMLWriter &xml, bool constrained = false); + + virtual bool ops(BaseType *, int) { + throw InternalErr(__FILE__, __LINE__, "Unimplemented method"); + } + + virtual std::vector *transform_to_dap2(AttrTable *parent_attr_table); + + virtual void dump(std::ostream &strm) const ; + +}; + +} // namespace libdap + +#endif // _d4_opaque_h + diff --git a/D4ParserSax2.cc b/D4ParserSax2.cc new file mode 100644 index 0000000..b62b2f3 --- /dev/null +++ b/D4ParserSax2.cc @@ -0,0 +1,1408 @@ +// -*- mode: c++; c-basic-offset:4 -*- + +// This file is part of libdap, A C++ implementation of the OPeNDAP Data +// Access Protocol. + +// Copyright (c) 2012 OPeNDAP, Inc. +// Author: James Gallagher +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// +// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112. + +#include "config.h" + +//#define DODS_DEBUG 1 + +#include +#include + +#include +#include +#include + +#include + +#include "DMR.h" + +#include "BaseType.h" +#include "Array.h" +#include "D4Group.h" +#include "D4Attributes.h" +#include "D4Maps.h" +#include "D4Enum.h" +#include "D4BaseTypeFactory.h" + +#include "DapXmlNamespaces.h" +#include "D4ParserSax2.h" + +#include "util.h" +#include "debug.h" + +namespace libdap { + +static const char *states[] = { + "parser_start", + + "inside_dataset", + + // inside_group is the state just after parsing the start of a Group + // element. + "inside_group", + + "inside_attribute_container", + "inside_attribute", + "inside_attribute_value", + "inside_other_xml_attribute", + + "inside_enum_def", + "inside_enum_const", + + "inside_dim_def", + + // This covers Byte, ..., Url, Opaque + "inside_simple_type", + + // "inside_array", + "inside_dim", + "inside_map", + + "inside_constructor", + + "not_dap4_element", + + "parser_unknown", + "parser_error", + "parser_fatal_error", + + "parser_end" +}; + +static bool is_not(const char *name, const char *tag) +{ + return strcmp(name, tag) != 0; +} + +/** @brief Return the current Enumeration definition + * Allocate the Enumeration definition if needed and return it. Once parsing the current + * enumeration definition is complete, the pointer allocated/returned by this method will + * be copied into the current Group and this internal storage will be 'reset' using + * clear_enum_def(). + * + * @return + */ +D4EnumDef * +D4ParserSax2::enum_def() +{ + if (!d_enum_def) d_enum_def = new D4EnumDef; + + return d_enum_def; +} + +/** @brief Return the current Dimension definition + * Allocate the Dimension definition if needed and return it. + * @see enum_def() for an explanation of how this is used by the parser. + * + * @return + */ +D4Dimension * +D4ParserSax2::dim_def() { + if (!d_dim_def) d_dim_def = new D4Dimension; + + return d_dim_def; +} + +/** Dump XML attributes to local store so they can be easily manipulated. + * XML attribute names are always folded to lower case. + * @param attributes The XML attribute array + * @param nb_attributes The number of attributes + */ +void D4ParserSax2::transfer_xml_attrs(const xmlChar **attributes, int nb_attributes) +{ + if (!xml_attrs.empty()) + xml_attrs.clear(); // erase old attributes + + // Make a value using the attribute name and the prefix, namespace URI + // and the value. The prefix might be null. + unsigned int index = 0; + for (int i = 0; i < nb_attributes; ++i, index += 5) { + xml_attrs.insert(map::value_type(string((const char *)attributes[index]), + XMLAttribute(attributes + index + 1))); + + DBG(cerr << "XML Attribute '" << (const char *)attributes[index] << "': " + << xml_attrs[(const char *)attributes[index]].value << endl); + } +} + +/** Transfer the XML namespaces to the local store so they can be manipulated + * more easily. + * + * @param namespaces Array of xmlChar* + * @param nb_namespaces The number of namespaces in the array. + */ +void D4ParserSax2::transfer_xml_ns(const xmlChar **namespaces, int nb_namespaces) +{ + // make a value with the prefix and namespace URI. The prefix might be null. + for (int i = 0; i < nb_namespaces; ++i) { + namespace_table.insert(map::value_type(namespaces[i * 2] != 0 ? (const char *)namespaces[i * 2] : "", + (const char *)namespaces[i * 2 + 1])); + } +} + +/** Is a required XML attribute present? Attribute names are always lower case. + * @note To use this method, first call transfer_xml_attrs. + * @param attr The XML attribute + * @return True if the XML attribute was present in the last tag, otherwise + * it sets the global error state and returns false. + */ +bool D4ParserSax2::check_required_attribute(const string & attr) +{ + if (xml_attrs.find(attr) == xml_attrs.end()) { + dmr_error(this, "Required attribute '%s' not found.", attr.c_str()); + return false; + } + else + return true; +} + +/** Is a XML attribute present? Attribute names are always lower case. + * @note To use this method, first call transfer_xml_attrs. + * @param attr The XML attribute + * @return True if the XML attribute was present in the last/current tag, + * false otherwise. + */ +bool D4ParserSax2::check_attribute(const string & attr) +{ + return (xml_attrs.find(attr) != xml_attrs.end()); +} + +bool D4ParserSax2::process_dimension_def(const char *name, const xmlChar **attrs, int nb_attributes) +{ + if (is_not(name, "Dimension")) + return false; + + transfer_xml_attrs(attrs, nb_attributes); + + if (!(check_required_attribute("name") && check_required_attribute("size"))) { + dmr_error(this, "The required attribute 'name' or 'size' was missing from a Dimension element."); + return false; + } + + // This getter (dim_def) allocates a new object if needed. + dim_def()->set_name(xml_attrs["name"].value); + try { + dim_def()->set_size(xml_attrs["size"].value); + } + catch (Error &e) { + dmr_error(this, e.get_error_message().c_str()); + return false; + } + + return true; +} + +/** + * @brief Process a Dim element. + * If a Dim element is found, the current variable is an Array. If the BaseType + * on the TOS is not already an Array, make it one. Append the dimension + * information to the Array variable on the TOS. + * + * @note Dim elements can have two attributes: name or size. The latter defines + * an 'anonymous' dimension (one without a name that does not reference a + * shared dimension object. If the \c name attribute is used, then the shared + * dimension used is the one defined by the enclosing group or found using the + * fully qualified name. The \name and \c size attributes are mutually exclusive. + * + * @param name XML element name; must be Dim + * @param attrs XML Attributes + * @param nb_attributes The number of XML Attributes + * @return True if the element is a Dim, false otherwise. + */ +bool D4ParserSax2::process_dimension(const char *name, const xmlChar **attrs, int nb_attributes) +{ + if (is_not(name, "Dim")) + return false; + + transfer_xml_attrs(attrs, nb_attributes); + + if (check_attribute("size") && check_attribute("name")) { + dmr_error(this, "Only one of 'size' and 'name' are allowed in a Dim element, but both were used."); + return false; + } + if (!(check_attribute("size") || check_attribute("name"))) { + dmr_error(this, "Either 'size' or 'name' must be used in a Dim element."); + return false; + } + + if (!top_basetype()->is_vector_type()) { + // Make the top BaseType* an array + BaseType *b = top_basetype(); + pop_basetype(); + + Array *a = static_cast(dmr()->factory()->NewVariable(dods_array_c, b->name())); + a->set_is_dap4(true); + a->add_var_nocopy(b); + a->set_attributes_nocopy(b->attributes()); + // trick: instead of popping b's attributes, copying them and then pushing + // a's copy, just move the pointer (but make sure there's only one object that + // references that pointer). + b->set_attributes_nocopy(0); + + push_basetype(a); + } + + assert(top_basetype()->is_vector_type()); + + Array *a = static_cast(top_basetype()); + if (check_attribute("size")) { + a->append_dim(atoi(xml_attrs["size"].value.c_str())); // low budget code for now. jhrg 8/20/13 + return true; + } + else if (check_attribute("name")) { + string name = xml_attrs["name"].value; + + D4Dimension *dim = 0; + if (name[0] == '/') // lookup the Dimension in the root group + dim = dmr()->root()->find_dim(name); + else // get enclosing Group and lookup Dimension there + dim = top_group()->find_dim(name); + + if (!dim) + throw Error("The dimension '" + name + "' was not found while parsing the variable '" + a->name() + "'."); + a->append_dim(dim); + return true; + } + + return false; +} + +bool D4ParserSax2::process_map(const char *name, const xmlChar **attrs, int nb_attributes) +{ + if (is_not(name, "Map")) + return false; + + transfer_xml_attrs(attrs, nb_attributes); + + if (!check_attribute("name")) { + dmr_error(this, "The 'name' attribute must be used in a Map element."); + return false; + } + + if (!top_basetype()->is_vector_type()) { + // Make the top BaseType* an array + BaseType *b = top_basetype(); + pop_basetype(); + + Array *a = static_cast(dmr()->factory()->NewVariable(dods_array_c, b->name())); + a->set_is_dap4(true); + a->add_var_nocopy(b); + a->set_attributes_nocopy(b->attributes()); + // trick: instead of popping b's attributes, copying them and then pushing + // a's copy, just move the pointer (but make sure there's only one object that + // references that pointer). + b->set_attributes_nocopy(0); + + push_basetype(a); + } + + assert(top_basetype()->is_vector_type()); + + Array *a = static_cast(top_basetype()); + + string map_name = xml_attrs["name"].value; + if (xml_attrs["name"].value[0] != '/') + map_name = top_group()->FQN() + map_name; + + Array *map_source = 0; // The array variable that holds the data for the Map + + if (map_name[0] == '/') // lookup the Map in the root group + map_source = dmr()->root()->find_map_source(map_name); + else // get enclosing Group and lookup Map there + map_source = top_group()->find_map_source(map_name); + + // Change: If the parser is in 'strict' mode (the default) and the Array named by + // the Map cannot be fond, it is an error. If 'strict' mode is false (permissive + // mode), then this is not an error. However, the Array referenced by the Map will + // be null. This is a change in the parser's behavior to accommodate requests for + // Arrays that include Maps that do not also include the Map(s) in the request. + // See https://opendap.atlassian.net/browse/HYRAX-98. jhrg 4/13/16 + if (!map_source && d_strict) + throw Error("The Map '" + map_name + "' was not found while parsing the variable '" + a->name() + "'."); + + a->maps()->add_map(new D4Map(map_name, map_source)); + + return true; +} + +bool D4ParserSax2::process_group(const char *name, const xmlChar **attrs, int nb_attributes) +{ + if (is_not(name, "Group")) + return false; + + transfer_xml_attrs(attrs, nb_attributes); + + if (!check_required_attribute("name")) { + dmr_error(this, "The required attribute 'name' was missing from a Group element."); + return false; + } + + BaseType *btp = dmr()->factory()->NewVariable(dods_group_c, xml_attrs["name"].value); + if (!btp) { + dmr_fatal_error(this, "Could not instantiate the Group '%s'.", xml_attrs["name"].value.c_str()); + return false; + } + + D4Group *grp = static_cast(btp); + + // Need to set this to get the D4Attribute behavior in the type classes + // shared between DAP2 and DAP4. jhrg 4/18/13 + grp->set_is_dap4(true); + + // link it up and change the current group + D4Group *parent = top_group(); + if (!parent) { + dmr_fatal_error(this, "No Group on the Group stack."); + return false; + } + + grp->set_parent(parent); + parent->add_group_nocopy(grp); + + push_group(grp); + push_attributes(grp->attributes()); + return true; +} + +/** Check to see if the current tag is either an \c Attribute or an \c Alias + start tag. This method is a glorified macro... + + @param name The start tag name + @param attrs The tag's XML attributes + @return True if the tag was an \c Attribute or \c Alias tag */ +inline bool D4ParserSax2::process_attribute(const char *name, const xmlChar **attrs, int nb_attributes) +{ + if (is_not(name, "Attribute")) + return false; + + // These methods set the state to parser_error if a problem is found. + transfer_xml_attrs(attrs, nb_attributes); + + // add error + if (!(check_required_attribute(string("name")) && check_required_attribute(string("type")))) { + dmr_error(this, "The required attribute 'name' or 'type' was missing from an Attribute element."); + return false; + } + + if (xml_attrs["type"].value == "Container") { + push_state(inside_attribute_container); + + DBG(cerr << "Pushing attribute container " << xml_attrs["name"].value << endl); + D4Attribute *child = new D4Attribute(xml_attrs["name"].value, attr_container_c); + + D4Attributes *tos = top_attributes(); + // add return + if (!tos) { + delete child; + dmr_fatal_error(this, "Expected an Attribute container on the top of the attribute stack."); + return false; + } + + tos->add_attribute_nocopy(child); + push_attributes(child->attributes()); + } + else if (xml_attrs["type"].value == "OtherXML") { + push_state(inside_other_xml_attribute); + + dods_attr_name = xml_attrs["name"].value; + dods_attr_type = xml_attrs["type"].value; + } + else { + push_state(inside_attribute); + + dods_attr_name = xml_attrs["name"].value; + dods_attr_type = xml_attrs["type"].value; + } + + return true; +} + +/** Check to see if the current tag is an \c Enumeration start tag. + + @param name The start tag name + @param attrs The tag's XML attributes + @return True if the tag was an \c Enumeration */ +inline bool D4ParserSax2::process_enum_def(const char *name, const xmlChar **attrs, int nb_attributes) +{ + if (is_not(name, "Enumeration")) + return false; + + transfer_xml_attrs(attrs, nb_attributes); + + if (!(check_required_attribute("name") && check_required_attribute("basetype"))) { + dmr_error(this, "The required attribute 'name' or 'basetype' was missing from an Enumeration element."); + return false; + } + + Type t = get_type(xml_attrs["basetype"].value.c_str()); + if (!is_integer_type(t)) { + dmr_error(this, "The Enumeration '%s' must have an integer type, instead the type '%s' was used.", + xml_attrs["name"].value.c_str(), xml_attrs["basetype"].value.c_str()); + return false; + } + + // This getter allocates a new object if needed. + string enum_def_path = xml_attrs["name"].value; +#if 0 + // Use FQNs when things are referenced, not when they are defined + if (xml_attrs["name"].value[0] != '/') + enum_def_path = top_group()->FQN() + enum_def_path; +#endif + enum_def()->set_name(enum_def_path); + enum_def()->set_type(t); + + return true; +} + +inline bool D4ParserSax2::process_enum_const(const char *name, const xmlChar **attrs, int nb_attributes) +{ + if (is_not(name, "EnumConst")) + return false; + + // These methods set the state to parser_error if a problem is found. + transfer_xml_attrs(attrs, nb_attributes); + + if (!(check_required_attribute("name") && check_required_attribute("value"))) { + dmr_error(this, "The required attribute 'name' or 'value' was missing from an EnumConst element."); + return false; + } + + istringstream iss(xml_attrs["value"].value); + long long value = 0; + iss >> skipws >> value; + if (iss.fail() || iss.bad()) { + dmr_error(this, "Expected an integer value for an Enumeration constant, got '%s' instead.", + xml_attrs["value"].value.c_str()); + } + else if (!enum_def()->is_valid_enum_value(value)) { + dmr_error(this, "In an Enumeration constant, the value '%s' cannot fit in a variable of type '%s'.", + xml_attrs["value"].value.c_str(), D4type_name(d_enum_def->type()).c_str()); + } + else { + // unfortunate choice of names... args are 'label' and 'value' + enum_def()->add_value(xml_attrs["name"].value, value); + } + + return true; +} + +/** Check to see if the current element is the start of a variable declaration. + If so, process it. A glorified macro... + @param name The start element name + @param attrs The element's XML attributes + @return True if the element was a variable */ +inline bool D4ParserSax2::process_variable(const char *name, const xmlChar **attrs, int nb_attributes) +{ + Type t = get_type(name); + if (is_simple_type(t)) { + process_variable_helper(t, inside_simple_type, attrs, nb_attributes); + return true; + } + else { + switch(t) { + case dods_structure_c: + process_variable_helper(t, inside_constructor, attrs, nb_attributes); + return true; + + case dods_sequence_c: + process_variable_helper(t, inside_constructor, attrs, nb_attributes); + return true; + + default: + return false; + } + } +} + +/** Given that a tag which opens a variable declaration has just been read, + create the variable. Once created, push the variable onto the stack of + variables, push that variable's attribute table onto the attribute table + stack and update the state of the parser. + @param t The type of variable to create. + @param s The next state of the parser (e.g., inside_simple_type, ...) + @param attrs the attributes read with the tag */ +void D4ParserSax2::process_variable_helper(Type t, ParseState s, const xmlChar **attrs, int nb_attributes) +{ + transfer_xml_attrs(attrs, nb_attributes); + + if (check_required_attribute("name")) { + BaseType *btp = dmr()->factory()->NewVariable(t, xml_attrs["name"].value); + if (!btp) { + dmr_fatal_error(this, "Could not instantiate the variable '%s'.", xml_attrs["name"].value.c_str()); + return; + } + + if ((t == dods_enum_c) && check_required_attribute("enum")) { + D4EnumDef *enum_def = 0; + string enum_path = xml_attrs["enum"].value; + if (enum_path[0] == '/') + enum_def = dmr()->root()->find_enum_def(enum_path); + else + enum_def = top_group()->find_enum_def(enum_path); + + if (!enum_def) + dmr_fatal_error(this, "Could not find the Enumeration definition '%s'.", enum_path.c_str()); + + static_cast(btp)->set_enumeration(enum_def); + } + + btp->set_is_dap4(true); // see comment above + push_basetype(btp); + + push_attributes(btp->attributes()); + + push_state(s); + } +} + +/** @name SAX Parser Callbacks + + These methods are declared static in the class header. This gives them C + linkage which allows them to be used as callbacks by the SAX parser + engine. */ +//@{ + +/** Initialize the SAX parser state object. This object is passed to each + callback as a void pointer. The initial state is parser_start. + + @param p The SAX parser */ +void D4ParserSax2::dmr_start_document(void * p) +{ + D4ParserSax2 *parser = static_cast(p); + parser->d_error_msg = ""; + parser->char_data = ""; + + // Set this in intern_helper so that the loop test for the parser_end + // state works for the first iteration. It seems like XMLParseChunk calls this + // function on it's first run. jhrg 9/16/13 + // parser->push_state(parser_start); + + parser->push_attributes(parser->dmr()->root()->attributes()); + + if (parser->debug()) cerr << "Parser start state: " << states[parser->get_state()] << endl; +} + +/** Clean up after finishing a parse. + @param p The SAX parser */ +void D4ParserSax2::dmr_end_document(void * p) +{ + D4ParserSax2 *parser = static_cast(p); + + if (parser->debug()) cerr << "Parser end state: " << states[parser->get_state()] << endl; + + if (parser->get_state() != parser_end) + D4ParserSax2::dmr_error(parser, "The document contained unbalanced tags."); + + // If we've found any sort of error, don't make the DMR; intern() will + // take care of the error. + if (parser->get_state() == parser_error || parser->get_state() == parser_fatal_error) + return; + + if (!parser->empty_basetype() || parser->empty_group()) + D4ParserSax2::dmr_error(parser, "The document did not contain a valid root Group or contained unbalanced tags."); + + parser->pop_group(); // leave the stack 'clean' + parser->pop_attributes(); +} + +/** + * Callback run when libxml2 reads the start of an element + * + * @param p Pointer to the parser object + * @param l Localname of the element + * @param prefix Namespace prefix of the element + * @param URI the Element namespace name if available + * @param nb_namespaces Number of namespace definitions on that node + * @param namespaces Pointer to the array of prefix/URI pairs namespace definitions + * @param nb_attributes The number of attributes on that node + * @param nb_defaulted The number of defaulted attributes. The defaulted ones are at the end of the array + * @param attributes Pointer to the array of (localname/prefix/URI/value/end) attribute values. + */ +void D4ParserSax2::dmr_start_element(void *p, const xmlChar *l, const xmlChar *prefix, const xmlChar *URI, + int nb_namespaces, const xmlChar **namespaces, int nb_attributes, int /*nb_defaulted*/, + const xmlChar **attributes) +{ + D4ParserSax2 *parser = static_cast(p); + const char *localname = (const char *) l; + + if (parser->debug()) cerr << "Start element " << localname << " prefix: "<< (prefix?(char *)prefix:"null") << " ns: "<< (URI?(char *)URI:"null") + << " (state: " << states[parser->get_state()] << ")" << endl; + + if(parser->get_state() != parser_error){ + string dap4_ns_name = DapXmlNamspaces::getDapNamespaceString(DAP_4_0); + if (parser->debug()) cerr << "dap4_ns_name: " << dap4_ns_name << endl; + + string this_element_ns_name = (URI != 0) ? ((char *)URI) : ""; + if (parser->debug()) cerr << "this_element_ns_name: " << this_element_ns_name << endl; + + if(this_element_ns_name.compare(dap4_ns_name)){ + if (parser->debug()) cerr << "Start of non DAP4 element: " << localname << " detected." << endl; + parser->push_state(not_dap4_element); + // return; + } + } + + + switch (parser->get_state()) { + case parser_start: + if (is_not(localname, "Dataset")) + D4ParserSax2::dmr_error(parser, "Expected DMR to start with a Dataset element; found '%s' instead.", localname); + + parser->root_ns = URI ? (const char *) URI : ""; + parser->transfer_xml_attrs(attributes, nb_attributes); + + if (parser->check_required_attribute(string("name"))) + parser->dmr()->set_name(parser->xml_attrs["name"].value); + + if (parser->check_attribute("dapVersion")) + parser->dmr()->set_dap_version(parser->xml_attrs["dapVersion"].value); + + if (parser->check_attribute("dmrVersion")) + parser->dmr()->set_dmr_version(parser->xml_attrs["dmrVersion"].value); + + if (parser->check_attribute("base")) + parser->dmr()->set_request_xml_base(parser->xml_attrs["base"].value); + + if (!parser->root_ns.empty()) + parser->dmr()->set_namespace(parser->root_ns); + + // Push the root Group on the stack + parser->push_group(parser->dmr()->root()); + + parser->push_state(inside_dataset); + + break; + + // Both inside dataset and inside group can have the same stuff. + // The difference is that the Dataset holds the root group, which + // must be present; other groups are optional + case inside_dataset: + case inside_group: + if (parser->process_enum_def(localname, attributes, nb_attributes)) + parser->push_state(inside_enum_def); + else if (parser->process_dimension_def(localname, attributes, nb_attributes)) + parser->push_state(inside_dim_def); + else if (parser->process_group(localname, attributes, nb_attributes)) + parser->push_state(inside_group); + else if (parser->process_variable(localname, attributes, nb_attributes)) + // This will push either inside_simple_type or inside_structure + // onto the parser state stack. + break; + else if (parser->process_attribute(localname, attributes, nb_attributes)) + // This will push either inside_attribute, inside_attribute_container + // or inside_otherxml_attribute onto the parser state stack + break; + else + D4ParserSax2::dmr_error(parser, "Expected an Attribute, Enumeration, Dimension, Group or variable element; found '%s' instead.", localname); + break; + + case inside_attribute_container: + if (parser->process_attribute(localname, attributes, nb_attributes)) + break; + else + D4ParserSax2::dmr_error(parser, "Expected an Attribute element; found '%s' instead.", localname); + break; + + case inside_attribute: + if (parser->process_attribute(localname, attributes, nb_attributes)) + break; + else if (strcmp(localname, "Value") == 0) + parser->push_state(inside_attribute_value); + else + dmr_error(parser, "Expected an 'Attribute' or 'Value' element; found '%s' instead.", localname); + break; + + case inside_attribute_value: + // Attribute values are processed by the end element code. + break; + + case inside_other_xml_attribute: + parser->other_xml_depth++; + + // Accumulate the elements here + parser->other_xml.append("<"); + if (prefix) { + parser->other_xml.append((const char *) prefix); + parser->other_xml.append(":"); + } + parser->other_xml.append(localname); + + if (nb_namespaces != 0) { + parser->transfer_xml_ns(namespaces, nb_namespaces); + + for (map::iterator i = parser->namespace_table.begin(); + i != parser->namespace_table.end(); ++i) { + parser->other_xml.append(" xmlns"); + if (!i->first.empty()) { + parser->other_xml.append(":"); + parser->other_xml.append(i->first); + } + parser->other_xml.append("=\""); + parser->other_xml.append(i->second); + parser->other_xml.append("\""); + } + } + + if (nb_attributes != 0) { + parser->transfer_xml_attrs(attributes, nb_attributes); + for (XMLAttrMap::iterator i = parser->xml_attr_begin(); i != parser->xml_attr_end(); ++i) { + parser->other_xml.append(" "); + if (!i->second.prefix.empty()) { + parser->other_xml.append(i->second.prefix); + parser->other_xml.append(":"); + } + parser->other_xml.append(i->first); + parser->other_xml.append("=\""); + parser->other_xml.append(i->second.value); + parser->other_xml.append("\""); + } + } + + parser->other_xml.append(">"); + break; + + case inside_enum_def: + // process an EnumConst element + if (parser->process_enum_const(localname, attributes, nb_attributes)) + parser->push_state(inside_enum_const); + else + dmr_error(parser, "Expected an 'EnumConst' element; found '%s' instead.", localname); + break; + + case inside_enum_const: + // No content; nothing to do + break; + + case inside_dim_def: + // No content; nothing to do + break; +#if 0 + case inside_dimension: + // No content. + break; +#endif + case inside_dim: + // No content. + break; + + case inside_map: + // No content. + break; + + case inside_simple_type: + if (parser->process_attribute(localname, attributes, nb_attributes)) + break; + else if (parser->process_dimension(localname, attributes, nb_attributes)) + parser->push_state(inside_dim); + else if (parser->process_map(localname, attributes, nb_attributes)) + parser->push_state(inside_map); + else + dmr_error(parser, "Expected an 'Attribute', 'Dim' or 'Map' element; found '%s' instead.", localname); + break; + + case inside_constructor: + if (parser->process_variable(localname, attributes, nb_attributes)) + // This will push either inside_simple_type or inside_structure + // onto the parser state stack. + break; + else if (parser->process_attribute(localname, attributes, nb_attributes)) + break; + else if (parser->process_dimension(localname, attributes, nb_attributes)) + parser->push_state(inside_dim); + else if (parser->process_map(localname, attributes, nb_attributes)) + parser->push_state(inside_map); + else + D4ParserSax2::dmr_error(parser, "Expected an Attribute, Dim, Map or variable element; found '%s' instead.", localname); + break; + + case not_dap4_element: + if (parser->debug()) cerr << "Inside non DAP4 element. localname: " << localname << endl; + break; + + case parser_unknown: + // FIXME? + // *** Never used? If so remove/error + parser->push_state(parser_unknown); + break; + + case parser_error: + case parser_fatal_error: + break; + + case parser_end: + // FIXME Error? + break; + } + + if (parser->debug()) cerr << "Start element exit state: " << states[parser->get_state()] << endl; +} + +void D4ParserSax2::dmr_end_element(void *p, const xmlChar *l, const xmlChar *prefix, const xmlChar *URI) +{ + D4ParserSax2 *parser = static_cast(p); + const char *localname = (const char *) l; + + if (parser->debug()) + cerr << "End element " << localname << " (state " << states[parser->get_state()] << ")" << endl; + + switch (parser->get_state()) { + case parser_start: + dmr_fatal_error(parser, "Unexpected state, inside start state while processing element '%s'.", localname); + break; + + case inside_dataset: + if (is_not(localname, "Dataset")) + D4ParserSax2::dmr_error(parser, "Expected an end Dataset tag; found '%s' instead.", localname); + + parser->pop_state(); + if (parser->get_state() != parser_start) + dmr_fatal_error(parser, "Unexpected state, expected start state."); + else { + parser->pop_state(); + parser->push_state(parser_end); + } + break; + + case inside_group: { + if (is_not(localname, "Group")) + D4ParserSax2::dmr_error(parser, "Expected an end tag for a Group; found '%s' instead.", localname); + + if (!parser->empty_basetype() || parser->empty_group()) + D4ParserSax2::dmr_error(parser, + "The document did not contain a valid root Group or contained unbalanced tags."); + + parser->pop_group(); + parser->pop_state(); + break; + } + + case inside_attribute_container: + if (is_not(localname, "Attribute")) + D4ParserSax2::dmr_error(parser, "Expected an end Attribute tag; found '%s' instead.", localname); + + parser->pop_state(); + parser->pop_attributes(); + break; + + case inside_attribute: + if (is_not(localname, "Attribute")) + D4ParserSax2::dmr_error(parser, "Expected an end Attribute tag; found '%s' instead.", localname); + + parser->pop_state(); + break; + + case inside_attribute_value: { + if (is_not(localname, "Value")) + D4ParserSax2::dmr_error(parser, "Expected an end value tag; found '%s' instead.", localname); + + parser->pop_state(); + + // The old code added more values using the name and type as + // indexes to find the correct attribute. Use get() for that + // now. Or fix this code to keep a pointer to the to attribute... + D4Attributes *attrs = parser->top_attributes(); + D4Attribute *attr = attrs->get(parser->dods_attr_name); + if (!attr) { + attr = new D4Attribute(parser->dods_attr_name, StringToD4AttributeType(parser->dods_attr_type)); + attrs->add_attribute_nocopy(attr); + } + attr->add_value(parser->char_data); + + parser->char_data = ""; // Null this after use. + break; + } + + case inside_other_xml_attribute: { + if (strcmp(localname, "Attribute") == 0 && parser->root_ns == (const char *) URI) { + parser->pop_state(); + + // The old code added more values using the name and type as + // indexes to find the correct attribute. Use get() for that + // now. Or fix this code to keep a pointer to the to attribute... + D4Attributes *attrs = parser->top_attributes(); + D4Attribute *attr = attrs->get(parser->dods_attr_name); + if (!attr) { + attr = new D4Attribute(parser->dods_attr_name, StringToD4AttributeType(parser->dods_attr_type)); + attrs->add_attribute_nocopy(attr); + } + attr->add_value(parser->other_xml); + + parser->other_xml = ""; // Null this after use. + } + else { + if (parser->other_xml_depth == 0) { + D4ParserSax2::dmr_error(parser, "Expected an OtherXML attribute to end! Instead I found '%s'", + localname); + break; + } + parser->other_xml_depth--; + + parser->other_xml.append("other_xml.append((const char *) prefix); + parser->other_xml.append(":"); + } + parser->other_xml.append(localname); + parser->other_xml.append(">"); + } + break; + } + + case inside_enum_def: + if (is_not(localname, "Enumeration")) + D4ParserSax2::dmr_error(parser, "Expected an end Enumeration tag; found '%s' instead.", localname); + if (!parser->top_group()) + D4ParserSax2::dmr_fatal_error(parser, + "Expected a Group to be the current item, while finishing up an Enumeration."); + else { + // copy the pointer; not a deep copy + parser->top_group()->enum_defs()->add_enum_nocopy(parser->enum_def()); + // Set the enum_def to null; next call to enum_def() will + // allocate a new object + parser->clear_enum_def(); + parser->pop_state(); + } + break; + + case inside_enum_const: + if (is_not(localname, "EnumConst")) + D4ParserSax2::dmr_error(parser, "Expected an end EnumConst tag; found '%s' instead.", localname); + + parser->pop_state(); + break; + + case inside_dim_def: { + if (is_not(localname, "Dimension")) + D4ParserSax2::dmr_error(parser, "Expected an end Dimension tag; found '%s' instead.", localname); + + if (!parser->top_group()) + D4ParserSax2::dmr_error(parser, + "Expected a Group to be the current item, while finishing up an Dimension."); + + // FIXME Use the Group on the top of the group stack + // copy the pointer; not a deep copy + parser->top_group()->dims()->add_dim_nocopy(parser->dim_def()); + //parser->dmr()->root()->dims()->add_dim_nocopy(parser->dim_def()); + // Set the dim_def to null; next call to dim_def() will + // allocate a new object. Calling 'clear' is important because + // the cleanup method will free dim_def if it's not null and + // we just copied the pointer in the add_dim_nocopy() call + // above. + parser->clear_dim_def(); + parser->pop_state(); + break; + } + + case inside_simple_type: + if (is_simple_type(get_type(localname))) { + BaseType *btp = parser->top_basetype(); + parser->pop_basetype(); + parser->pop_attributes(); + + BaseType *parent = 0; + if (!parser->empty_basetype()) + parent = parser->top_basetype(); + else if (!parser->empty_group()) + parent = parser->top_group(); + else { + dmr_fatal_error(parser, "Both the Variable and Groups stacks are empty while closing a %s element.", + localname); + delete btp; + parser->pop_state(); + break; + } + + if (parent->type() == dods_array_c) + static_cast(parent)->prototype()->add_var_nocopy(btp); + else + parent->add_var_nocopy(btp); + } + else + D4ParserSax2::dmr_error(parser, "Expected an end tag for a simple type; found '%s' instead.", localname); + + parser->pop_state(); + break; + + case inside_dim: + if (is_not(localname, "Dim")) + D4ParserSax2::dmr_fatal_error(parser, "Expected an end Dim tag; found '%s' instead.", localname); + + parser->pop_state(); + break; + + case inside_map: + if (is_not(localname, "Map")) + D4ParserSax2::dmr_fatal_error(parser, "Expected an end Map tag; found '%s' instead.", localname); + + parser->pop_state(); + break; + + case inside_constructor: { + if (strcmp(localname, "Structure") != 0 && strcmp(localname, "Sequence") != 0) { + D4ParserSax2::dmr_error(parser, "Expected an end tag for a constructor; found '%s' instead.", localname); + return; + } + + BaseType *btp = parser->top_basetype(); + parser->pop_basetype(); + parser->pop_attributes(); + + BaseType *parent = 0; + if (!parser->empty_basetype()) + parent = parser->top_basetype(); + else if (!parser->empty_group()) + parent = parser->top_group(); + else { + dmr_fatal_error(parser, "Both the Variable and Groups stacks are empty while closing a %s element.", + localname); + delete btp; + parser->pop_state(); + break; + } + + // TODO Why doesn't this code mirror the simple_var case and test + // for the parent being an array? jhrg 10/13/13 + parent->add_var_nocopy(btp); + parser->pop_state(); + break; + } + + case not_dap4_element: + if (parser->debug()) cerr << "End of non DAP4 element: " << localname << endl; + parser->pop_state(); + break; + + case parser_unknown: + parser->pop_state(); + break; + + case parser_error: + case parser_fatal_error: + break; + + case parser_end: + // FIXME Error? + break; + } + + if (parser->debug()) cerr << "End element exit state: " << states[parser->get_state()] << endl; +} + +/** Process/accumulate character data. This may be called more than once for + one logical clump of data. Only save character data when processing + 'value' elements; throw away all other characters. */ +void D4ParserSax2::dmr_get_characters(void * p, const xmlChar * ch, int len) +{ + D4ParserSax2 *parser = static_cast(p); + + switch (parser->get_state()) { + case inside_attribute_value: + parser->char_data.append((const char *) (ch), len); + DBG(cerr << "Characters: '" << parser->char_data << "'" << endl); + break; + + case inside_other_xml_attribute: + parser->other_xml.append((const char *) (ch), len); + DBG(cerr << "Other XML Characters: '" << parser->other_xml << "'" << endl); + break; + + default: + break; + } +} + +/** Read whitespace that's not really important for content. This is used + only for the OtherXML attribute type to preserve formating of the XML. + Doing so makes the attribute value far easier to read. + */ +void D4ParserSax2::dmr_ignoreable_whitespace(void *p, const xmlChar *ch, int len) +{ + D4ParserSax2 *parser = static_cast(p); + + switch (parser->get_state()) { + case inside_other_xml_attribute: + parser->other_xml.append((const char *) (ch), len); + break; + + default: + break; + } +} + +/** Get characters in a cdata block. DAP does not use CData, but XML in an + OtherXML attribute (the value of that DAP attribute) might use it. This + callback also allows CData when the parser is in the 'parser_unknown' + state since some future DAP element might use it. + */ +void D4ParserSax2::dmr_get_cdata(void *p, const xmlChar *value, int len) +{ + D4ParserSax2 *parser = static_cast(p); + + switch (parser->get_state()) { + case inside_other_xml_attribute: + parser->other_xml.append((const char *) (value), len); + break; + + case parser_unknown: + break; + + default: + D4ParserSax2::dmr_error(parser, "Found a CData block but none are allowed by DAP4."); + + break; + } +} + +/** Handle the standard XML entities. + + @param parser The SAX parser + @param name The XML entity. */ +xmlEntityPtr D4ParserSax2::dmr_get_entity(void *, const xmlChar * name) +{ + return xmlGetPredefinedEntity(name); +} + +/** Process an XML fatal error. Note that SAX provides for warnings, errors + and fatal errors. This code treats them all as fatal errors since there's + typically no way to tell a user about the error since there's often no + user interface for this software. + + @note This static function does not throw an exception or otherwise + alter flow of control except for altering the parser state. + + @param p The SAX parser + @param msg A printf-style format string. */ +void D4ParserSax2::dmr_fatal_error(void * p, const char *msg, ...) +{ + va_list args; + D4ParserSax2 *parser = static_cast(p); + + parser->push_state(parser_fatal_error); + + va_start(args, msg); + char str[1024]; + vsnprintf(str, 1024, msg, args); + va_end(args); + + int line = xmlSAX2GetLineNumber(parser->d_context); + + if (!parser->d_error_msg.empty()) parser->d_error_msg += "\n"; + parser->d_error_msg += "At line " + long_to_string(line) + ": " + string(str); +} + +void D4ParserSax2::dmr_error(void *p, const char *msg, ...) +{ + va_list args; + D4ParserSax2 *parser = static_cast(p); + + parser->push_state(parser_error); + + va_start(args, msg); + char str[1024]; + vsnprintf(str, 1024, msg, args); + va_end(args); + + int line = xmlSAX2GetLineNumber(parser->d_context); + + if (!parser->d_error_msg.empty()) parser->d_error_msg += "\n"; + parser->d_error_msg += "At line " + long_to_string(line) + ": " + string(str); +} +//@} + +/** Clean up after a parse operation. If the parser encountered an error, + * throw either an Error or InternalErr object. + */ +void D4ParserSax2::cleanup_parse() +{ + bool wellFormed = d_context->wellFormed; + bool valid = d_context->valid; + + d_context->sax = NULL; + xmlFreeParserCtxt(d_context); + + delete d_enum_def; + d_enum_def = 0; + + delete d_dim_def; + d_dim_def = 0; + + // If there's an error, there may still be items on the stack at the + // end of the parse. + while (!btp_stack.empty()) { + delete top_basetype(); + pop_basetype(); + } + + if (!wellFormed) + throw Error("The DMR was not well formed. " + d_error_msg); + else if (!valid) + throw Error("The DMR was not valid." + d_error_msg); + else if (get_state() == parser_error) + throw Error(d_error_msg); + else if (get_state() == parser_fatal_error) + throw InternalErr(d_error_msg); +} + +/** + * Read the DMR from a stream. + * + * @param f The input stream + * @param dest_dmr Value-result parameter. Pass a pointer to a DMR in and + * the information in the DMR will be added to it. + * @param boundary If not empty, use this as the boundary tag in a MPM document + * that marks the end of the part hat holds the DMR. Stop reading when the + * boundary is found. + * @param debug If true, ouput helpful debugging messages, False by default. + * + * @exception Error Thrown if the XML document could not be read or parsed. + * @exception InternalErr Thrown if an internal error is found. + */ +void D4ParserSax2::intern(istream &f, DMR *dest_dmr, bool debug) +{ + d_debug = debug; + + // Code example from libxml2 docs re: read from a stream. + + if (!f.good()) + throw Error("Input stream not open or read error"); + if (!dest_dmr) + throw InternalErr(__FILE__, __LINE__, "DMR object is null"); + + d_dmr = dest_dmr; // dump values here + +#if 0 + // Old, bad, code. Lines are limited to 1023 chars including the element text. + const int size = 1024; + char chars[size]; + int line = 1; + + f.getline(chars, size); + int res = f.gcount(); + if (res == 0) throw Error("No input found while parsing the DMR."); + + getline(f, line); + + if (debug) cerr << "line: (" << line++ << "): " << chars << endl; + + d_context = xmlCreatePushParserCtxt(&d_dmr_sax_parser, this, chars, res - 1, "stream"); + d_context->validate = true; + push_state(parser_start); + + f.getline(chars, size); + while ((f.gcount() > 0) && (get_state() != parser_end)) { + if (debug) cerr << "line: (" << line++ << "): " << chars << endl; + xmlParseChunk(d_context, chars, f.gcount() - 1, 0); + f.getline(chars, size); + } + + // This call ends the parse. + xmlParseChunk(d_context, chars, 0, 1/*terminate*/); +#else + int line_num = 1; + string line; + + // Get the line + getline(f, line); + if (line.length() == 0) throw Error("No input found while parsing the DMR."); + + if (debug) cerr << "line: (" << line_num << "): " << endl << line << endl << endl; + + d_context = xmlCreatePushParserCtxt(&d_dmr_sax_parser, this, line.c_str(), line.length(), "stream"); + d_context->validate = true; + push_state(parser_start); + + // Get the first line of stuff + getline(f, line); + ++line_num; + + if (debug) cerr << "line: (" << line_num << "): " << endl << line << endl << endl; + + while (!f.eof() && (get_state() != parser_end)) { + xmlParseChunk(d_context, line.c_str(), line.length(), 0); + + // Get the next line + getline(f, line); + ++line_num; + + if (debug) cerr << "line: (" << line_num << "): " << endl << line << endl << endl; + } + + // This call ends the parse. + xmlParseChunk(d_context, line.c_str(), 0, 1/*terminate*/); +#endif + + // This checks that the state on the parser stack is parser_end and throws + // an exception if it's not (i.e., the loop exited with gcount() == 0). + cleanup_parse(); +} + +/** Parse a DMR document stored in a string. + * + * @param document Read the DMR from this string. + * @param dest_dmr Value/result parameter; dumps the information to this DMR + * instance. + * @param debug If true, ouput helpful debugging messages, False by default + * + * @exception Error Thrown if the XML document could not be read or parsed. + * @exception InternalErr Thrown if an internal error is found. + */ +void D4ParserSax2::intern(const string &document, DMR *dest_dmr, bool debug) +{ + intern(document.c_str(), document.length(), dest_dmr, debug); +} + +/** Parse a DMR document stored in a char *buffer. + * + * @param document Read the DMR from this string. + * @param dest_dmr Value/result parameter; dumps the information to this DMR + * instance. + * @param debug If true, ouput helpful debugging messages, False by default + * + * @exception Error Thrown if the XML document could not be read or parsed. + * @exception InternalErr Thrown if an internal error is found. + */ +void D4ParserSax2::intern(const char *buffer, int size, DMR *dest_dmr, bool debug) +{ + if (!(size > 0)) return; + + d_debug = debug; + + // Code example from libxml2 docs re: read from a stream. + + if (!dest_dmr) throw InternalErr(__FILE__, __LINE__, "DMR object is null"); + d_dmr = dest_dmr; // dump values in dest_dmr + + push_state(parser_start); + d_context = xmlCreatePushParserCtxt(&d_dmr_sax_parser, this, buffer, size, "stream"); + d_context->validate = true; + + // This call ends the parse. + xmlParseChunk(d_context, buffer, 0, 1/*terminate*/); + + // This checks that the state on the parser stack is parser_end and throws + // an exception if it's not (i.e., the loop exited with gcount() == 0). + cleanup_parse(); +} + +} // namespace libdap diff --git a/D4ParserSax2.h b/D4ParserSax2.h new file mode 100644 index 0000000..d160b11 --- /dev/null +++ b/D4ParserSax2.h @@ -0,0 +1,326 @@ + +// -*- mode: c++; c-basic-offset:4 -*- + +// This file is part of libdap, A C++ implementation of the OPeNDAP Data +// Access Protocol. + +// Copyright (c) 2012 OPeNDAP, Inc. +// Author: James Gallagher +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// +// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112. + +#ifndef d4_parser_sax2_h +#define d4_parser_sax2_h + +#define ATTR 1 + +#include + +#include +#include +#include +#include + +#include + +#define CRLF "\r\n" + +namespace libdap +{ + +class DMR; +class BaseType; +class D4BaseTypeFactory; +class D4Group; +class D4Attributes; +class D4EnumDef; +class D4Dimension; + +/** Parse the XML text which encodes the network/persistent representation of + the DMR object. In the current implementation, the DMR is held by an + instance of the class DDS which in turn holds variables which include + attributes. + + This parser for the DMR document uses the SAX interface of libxml2. + Static methods are used as callbacks for the SAX parser. These static + methods are public because making them private complicates compilation. + They should not be called by anything other than the intern method. + They do not throw exceptions because exceptions from within callbacks are + not reliable or portable. To signal errors, the methods record + information in the D4ParserSax2 object. Once the error handler is called, + construction of an DMR object ends even though the SAX parser still + calls the various callback functions. The parser treats warnings, + errors and fatal_errors the same way; when any are found parsing + stops. The intern method throws an Error of InternalErr exception if an + error was found. + + Note that this class uses the C++-supplied default definitions for the + default and copy constructors as well as the destructor and assignment + operator. + + @see DMR */ +class D4ParserSax2 +{ +private: + /** States used by DDXParserDAP4State. These are the states of the SAX parser + state-machine. */ + enum ParseState { + parser_start, + + inside_dataset, + + // inside_group is the state just after parsing the start of a Group + // element. + inside_group, + + inside_attribute_container, + inside_attribute, + inside_attribute_value, + inside_other_xml_attribute, + + inside_enum_def, + inside_enum_const, + + inside_dim_def, + + // This covers Byte, ..., Url, Opaque + inside_simple_type, + + // inside_array, + inside_dim, + inside_map, + + inside_constructor, + + // inside_sequence, Removed from merged code jhrg 5/2/14 + + not_dap4_element, + + parser_unknown, + parser_error, + parser_fatal_error, + + parser_end + }; + + xmlSAXHandler d_dmr_sax_parser; + + // The results of the parse operation are stored in these fields. + // This is passed into the parser using the intern() methods. + DMR *d_dmr; // dump DMR here + DMR *dmr() const { return d_dmr; } + + // These stacks hold the state of the parse as it progresses. + stack s; // Current parse state + void push_state(D4ParserSax2::ParseState state) { s.push(state); } + D4ParserSax2::ParseState get_state() const { return s.top(); } + void pop_state() { s.pop(); } + bool empty_state() const { return s.empty(); } + + stack btp_stack; // current variable(s) + void push_basetype(BaseType *btp) { btp_stack.push(btp); } + BaseType *top_basetype() const { return btp_stack.top(); } + void pop_basetype() { btp_stack.pop(); } + bool empty_basetype() const { return btp_stack.empty(); } + + stack grp_stack; // current groups(s) + void push_group(D4Group *grp) { grp_stack.push(grp); } + D4Group *top_group() const { return grp_stack.top(); } + void pop_group() { grp_stack.pop(); } + bool empty_group() const { return grp_stack.empty(); } + + stack d_attrs_stack; // DAP4 Attributes + void push_attributes(D4Attributes *attr) { d_attrs_stack.push(attr); } + D4Attributes *top_attributes() const { return d_attrs_stack.top(); } + void pop_attributes() { d_attrs_stack.pop(); } + bool empty_attributes() const { return d_attrs_stack.empty(); } + + D4EnumDef *d_enum_def; + D4EnumDef *enum_def(); + void clear_enum_def() { d_enum_def = 0; } + + D4Dimension *d_dim_def; + D4Dimension *dim_def(); + void clear_dim_def() { d_dim_def = 0; } + + // Accumulate stuff inside an 'OtherXML' DAP attribute here + string other_xml; + + // When we're parsing unknown XML, how deeply is it nested? This is used + // for the OtherXML DAP attributes. + unsigned int other_xml_depth; + unsigned int unknown_depth; + + // These are used for processing errors. + string d_error_msg; // Error message(s), if any. + xmlParserCtxtPtr d_context; // used for error message line numbers + + // These hold temporary values read during the parse. + string dods_attr_name; // DAP4 attributes, not XML attributes + string dods_attr_type; // ... not XML ... + string char_data; // char data in value elements; null after use + string root_ns; // What is the namespace of the root node (Group) + + bool d_debug; + bool debug() const { return d_debug; } + + bool d_strict; + + class XMLAttribute { + public: + string prefix; + string nsURI; + string value; + + void clone(const XMLAttribute &src) { + prefix = src.prefix; + nsURI = src.nsURI; + value = src.value; + } + + XMLAttribute() : prefix(""), nsURI(""), value("") {} + XMLAttribute(const string &p, const string &ns, const string &v) + : prefix(p), nsURI(ns), value(v) {} + // 'attributes' as passed from libxml2 is a five element array but this + // ctor gets the back four elements. + XMLAttribute(const xmlChar **attributes/*[4]*/) { + prefix = attributes[0] != 0 ? (const char *)attributes[0]: ""; + nsURI = attributes[1] != 0 ? (const char *)attributes[1]: ""; + value = string((const char *)attributes[2], (const char *)attributes[3]); + } + XMLAttribute(const XMLAttribute &rhs) { + clone(rhs); + } + XMLAttribute &operator=(const XMLAttribute &rhs) { + if (this == &rhs) + return *this; + clone(rhs); + return *this; + } + }; + + typedef map XMLAttrMap; + XMLAttrMap xml_attrs; // dump XML attributes here + + XMLAttrMap::iterator xml_attr_begin() { return xml_attrs.begin(); } + + XMLAttrMap::iterator xml_attr_end() { return xml_attrs.end(); } + + map namespace_table; + + void cleanup_parse(); + + /** @name Parser Actions + + These methods are the 'actions' carried out by the start_element and + end_element callbacks. Most of what takes place in those has been + factored out to this set of functions. */ + //@{ + void transfer_xml_attrs(const xmlChar **attrs, int nb_attributes); + void transfer_xml_ns(const xmlChar **namespaces, int nb_namespaces); + bool check_required_attribute(const string &attr); + bool check_attribute(const string & attr); + void process_variable_helper(Type t, ParseState s, const xmlChar **attrs, int nb_attributes); + + void process_enum_const_helper(const xmlChar **attrs, int nb_attributes); + void process_enum_def_helper(const xmlChar **attrs, int nb_attributes); + + bool process_dimension(const char *name, const xmlChar **attrs, int nb_attrs); + bool process_dimension_def(const char *name, const xmlChar **attrs, int nb_attrs); + bool process_map(const char *name, const xmlChar **attrs, int nb_attributes); + bool process_attribute(const char *name, const xmlChar **attrs, int nb_attributes); + bool process_variable(const char *name, const xmlChar **attrs, int nb_attributes); + bool process_group(const char *name, const xmlChar **attrs, int nb_attributes); + bool process_enum_def(const char *name, const xmlChar **attrs, int nb_attributes); + bool process_enum_const(const char *name, const xmlChar **attrs, int nb_attributes); + + void finish_variable(const char *tag, Type t, const char *expected); + //@} + + friend class D4ParserSax2Test; + +public: + D4ParserSax2() : + d_dmr(0), d_enum_def(0), d_dim_def(0), + other_xml(""), other_xml_depth(0), unknown_depth(0), + d_error_msg(""), d_context(0), + dods_attr_name(""), dods_attr_type(""), + char_data(""), root_ns(""), d_debug(false), d_strict(true) + { + //xmlSAXHandler ddx_sax_parser; + memset(&d_dmr_sax_parser, 0, sizeof(xmlSAXHandler)); + + d_dmr_sax_parser.getEntity = &D4ParserSax2::dmr_get_entity; + d_dmr_sax_parser.startDocument = &D4ParserSax2::dmr_start_document; + d_dmr_sax_parser.endDocument = &D4ParserSax2::dmr_end_document; + d_dmr_sax_parser.characters = &D4ParserSax2::dmr_get_characters; + d_dmr_sax_parser.ignorableWhitespace = &D4ParserSax2::dmr_ignoreable_whitespace; + d_dmr_sax_parser.cdataBlock = &D4ParserSax2::dmr_get_cdata; + d_dmr_sax_parser.warning = &D4ParserSax2::dmr_error; + d_dmr_sax_parser.error = &D4ParserSax2::dmr_error; + d_dmr_sax_parser.fatalError = &D4ParserSax2::dmr_fatal_error; + d_dmr_sax_parser.initialized = XML_SAX2_MAGIC; + d_dmr_sax_parser.startElementNs = &D4ParserSax2::dmr_start_element; + d_dmr_sax_parser.endElementNs = &D4ParserSax2::dmr_end_element; + } + + void intern(istream &f, DMR *dest_dmr, bool debug = false); + void intern(const string &document, DMR *dest_dmr, bool debug = false); + void intern(const char *buffer, int size, DMR *dest_dmr, bool debug = false); + + /** + * @defgroup strict The 'strict' mode + * @{ + * The strict mode of the parser is the default. In this mode any error + * will result in an exception and parsing will stop. When strict mode + * is set to false (forgiving mode?), some errors will be silently ignored. + * The list of allowed errors is: + * The Array named by a Map element is not required to be in the DMR. + * There are no other allowed errors at this time (4/13/16) + */ + + /** @brief Set the 'strict' mode to true or false. */ + void set_strict(bool s) { d_strict = s; } + /** @brief Get the setting of the 'strict' mode. + * @return True or False. + */ + bool get_strict() const { return d_strict; } + /** @} */ + + static void dmr_start_document(void *parser); + static void dmr_end_document(void *parser); + + static void dmr_start_element(void *parser, + const xmlChar *localname, const xmlChar *prefix, const xmlChar *URI, + int nb_namespaces, const xmlChar **namespaces, int nb_attributes, + int nb_defaulted, const xmlChar **attributes); + static void dmr_end_element(void *parser, const xmlChar *localname, + const xmlChar *prefix, const xmlChar *URI); + + static void dmr_get_characters(void *parser, const xmlChar *ch, int len); + static void dmr_ignoreable_whitespace(void *parser, + const xmlChar * ch, int len); + static void dmr_get_cdata(void *parser, const xmlChar *value, int len); + + static xmlEntityPtr dmr_get_entity(void *parser, const xmlChar *name); + static void dmr_fatal_error(void *parser, const char *msg, ...); + static void dmr_error(void *parser, const char *msg, ...); +}; + +} // namespace libdap + +#endif // d4_parser_sax2_h diff --git a/D4RValue.cc b/D4RValue.cc new file mode 100644 index 0000000..e1ece71 --- /dev/null +++ b/D4RValue.cc @@ -0,0 +1,324 @@ + +// -*- mode: c++; c-basic-offset:4 -*- + +// This file is part of libdap, A C++ implementation of the OPeNDAP Data +// Access Protocol. + +// Copyright (c) 2014 OPeNDAP, Inc. +// Author: James Gallagher +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// +// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112. + +#include "config.h" + +#include + +#include "BaseType.h" +#include "Array.h" +#include "Byte.h" +#include "Int8.h" +#include "UInt16.h" +#include "Int16.h" +#include "UInt32.h" +#include "Int32.h" +#include "UInt64.h" +#include "Int64.h" +#include "Float32.h" +#include "Float64.h" +#include "Str.h" + +#include "D4RValue.h" +#include "InternalErr.h" + +#include "dods-datatypes.h" +#include "dods-limits.h" +#include "parser-util.h" +#include "util.h" + +using namespace std; + +namespace libdap { + +void +D4RValueList::m_duplicate(const D4RValueList &src) +{ + for (std::vector::const_iterator i = src.d_rvalues.begin(), e = src.d_rvalues.end(); i != e; ++i) { + D4RValue *rv = *i; + d_rvalues.push_back(new D4RValue(*rv)); + } +} + +D4RValueList::~D4RValueList() +{ + for (std::vector::iterator i = d_rvalues.begin(), e = d_rvalues.end(); i != e; ++i) + delete *i; +} + +void +D4RValue::m_duplicate(const D4RValue &src) +{ + d_value_kind = src.d_value_kind; + + d_variable = src.d_variable; // weak pointers + + d_func = src.d_func; + d_args = (src.d_args != 0) ? new D4RValueList(*src.d_args) : 0; // deep copy these + + d_constant = (src.d_constant != 0) ? src.d_constant->ptr_duplicate() : 0; +} + +template +static BaseType * +build_constant_array(vector &values, DAP_TYPE &dt) +{ + Array *array = new Array("", &dt); + array->append_dim(values.size()); + + // TODO Make set_value_nocopy() methods so that values' pointers can be copied + // instead of allocating memory twice. jhrg 7/5/13 + + array->set_value(values, values.size()); + + array->set_read_p(true); + + static unsigned long counter = 1; + array->set_name(string("g") + long_to_string(counter++)); + + return array; +} + +D4RValue::D4RValue(unsigned long long ull) : d_variable(0), d_func(0), d_args(0), d_constant(0), d_value_kind(constant) +{ + UInt64 *ui = new UInt64("constant"); + ui->set_value(ull); + d_constant = ui; +} + +D4RValue::D4RValue(long long ll) : d_variable(0), d_func(0), d_args(0), d_constant(0), d_value_kind(constant) +{ + Int64 *i = new Int64("constant"); + i->set_value(ll); + d_constant = i; +} + +D4RValue::D4RValue(double r) : d_variable(0), d_func(0), d_args(0), d_constant(0), d_value_kind(constant) +{ + Float64 *f = new Float64("constant"); + f->set_value(r); + d_constant = f; +} + +D4RValue::D4RValue(std::string cpps) : d_variable(0), d_func(0), d_args(0), d_constant(0), d_value_kind(constant) +{ + Str *s = new Str("constant"); + s->set_value(remove_quotes(cpps)); + d_constant = s; +} + +D4RValue::D4RValue(std::vector &byte_args) + : d_variable(0), d_func(0), d_args(0), d_constant(0), d_value_kind(constant) +{ + Byte b(""); + d_constant = build_constant_array(byte_args, b); +} + +D4RValue::D4RValue(std::vector &byte_int8) + : d_variable(0), d_func(0), d_args(0), d_constant(0), d_value_kind(constant) +{ + Int8 b(""); + d_constant = build_constant_array(byte_int8, b); +} + +D4RValue::D4RValue(std::vector &byte_uint16) + : d_variable(0), d_func(0), d_args(0), d_constant(0), d_value_kind(constant) +{ + UInt16 b(""); + d_constant = build_constant_array(byte_uint16, b); +} + +D4RValue::D4RValue(std::vector &byte_int16) + : d_variable(0), d_func(0), d_args(0), d_constant(0), d_value_kind(constant) +{ + Int16 b(""); + d_constant = build_constant_array(byte_int16, b); +} + +D4RValue::D4RValue(std::vector &byte_uint32) + : d_variable(0), d_func(0), d_args(0), d_constant(0), d_value_kind(constant) +{ + UInt32 b(""); + d_constant = build_constant_array(byte_uint32, b); +} + +D4RValue::D4RValue(std::vector &byte_int32) + : d_variable(0), d_func(0), d_args(0), d_constant(0), d_value_kind(constant) +{ + Int32 b(""); + d_constant = build_constant_array(byte_int32, b); +} + +D4RValue::D4RValue(std::vector &byte_uint64) + : d_variable(0), d_func(0), d_args(0), d_constant(0), d_value_kind(constant) +{ + UInt64 b(""); + d_constant = build_constant_array(byte_uint64, b); +} + +D4RValue::D4RValue(std::vector &byte_int64) + : d_variable(0), d_func(0), d_args(0), d_constant(0), d_value_kind(constant) +{ + Int64 b(""); + d_constant = build_constant_array(byte_int64, b); +} + +D4RValue::D4RValue(std::vector &byte_float32) + : d_variable(0), d_func(0), d_args(0), d_constant(0), d_value_kind(constant) +{ + Float32 b(""); + d_constant = build_constant_array(byte_float32, b); +} + +D4RValue::D4RValue(std::vector &byte_float64) + : d_variable(0), d_func(0), d_args(0), d_constant(0), d_value_kind(constant) +{ + Float64 b(""); + d_constant = build_constant_array(byte_float64, b); +} + +D4RValue::~D4RValue() { + // d_variable and d_func are weak pointers; don't delete. + delete d_args; + delete d_constant; +} + +/** + * @brief Build an appropriate RValue + * + * Look at the value in the string parameter and build an appropriate + * BaseType, use that as a constant and build an RValue. This can be used + * by the DAP4 parser directly to build the constants in filter clauses. + * + * @param cpps The string argument read by the parser. + * @return A D4RValue pointer. + */ +D4RValue *D4RValueFactory(std::string cpps) +{ + char *ptr; + + // First check if the string is a uint64, ..., then convert it. + // Since the check_* function use the strtoull() functions, no + // need to test for errors when building the actual values. + if (check_uint64(cpps.c_str())) { + return new D4RValue(strtoull(cpps.c_str(), &ptr, 0)); + } + else if (check_int64(cpps.c_str())) { + return new D4RValue(strtoll(cpps.c_str(), &ptr, 0)); + } + else if (check_float64(cpps.c_str())) { +#ifdef WIN32 + return new D4RValue(w32strtod(cpps.c_str(), &ptr)); +#else + return new D4RValue(strtod(cpps.c_str(), &ptr)); +#endif + } + else { + return new D4RValue(cpps); + } +} + +/** + * @brief Get the value for a RValue object + * Return the BaseType * for a given RValue. For a dataset variable, read the + * variable's value and, for a function, evaluate that function. Since read() + * is called for a dataset variable each time this method is called, if the + * variable is part of a Sequence, the next value in the sequence will be returned. + * However, since this code also sets the read_p property after calling read(), + * if the variable does not have a new value, read() will not be called (using the + * read_p property, the read() method is called only when the variable has a new + * value to be read. + * + * @note Unlike the DAP2 functions, we have an easier-to-follow memory model for + * function values. The values (BaseType*) returned by this method will be packaged + * up in a RValueList and deleted when that list is deleted. Constant values and + * function result values will be deleted at that time; variables will not. Thus + * Server Functions should always allocate storage for their return values. + * + * @todo Could move the operation that wraps a constant in a BaseType to this method + * while providing other ways to access the value(s) (methods to determine if the + * rvalue is a constant and what DAP type it is, e.g.). This would provide an optimization + * for the filter evaluator which may access the values many times. We might also + * modify the server side functions so they could access constant values more efficiently. + * + * @param dmr The DMR to pass to a function. + * @return A BaseType* that holds the value. + */ +BaseType * +D4RValue::value(DMR &dmr) +{ + switch (d_value_kind) { + case basetype: + d_variable->read(); + d_variable->set_read_p(true); + return d_variable; + + case function: + return (*d_func)(d_args, dmr); + + case constant: + return d_constant; + + default: + throw InternalErr(__FILE__, __LINE__, "Unknown rvalue type."); + } + + return 0; // nullptr; added return to quiet warning. jhrg 3/24/15 +} + +/** + * @brief Get the value for a RValue object + * + * This version of value() will not work for function RValues, but has the advantage that + * it can be used more easily for the D4RValue objects built for, and stored in, D4Filter- + * Clause instances. + * + * @see D4RValue::value(DMR&) + * @return The value wrapped in a BaseType* + */ +BaseType * +D4RValue::value() +{ + switch (d_value_kind) { + case basetype: + d_variable->read(); + d_variable->set_read_p(true); + return d_variable; + + case function: + throw Error(malformed_expr, "An expression that included a function call was used in a place where that won't work."); + + case constant: + return d_constant; + + default: + throw InternalErr(__FILE__, __LINE__, "Unknown rvalue type."); + } + + return 0; // nullptr; added return to quiet warning. jhrg 3/24/15 +} + +} // namespace libdap + diff --git a/D4RValue.h b/D4RValue.h new file mode 100644 index 0000000..826c4b1 --- /dev/null +++ b/D4RValue.h @@ -0,0 +1,155 @@ + +// -*- mode: c++; c-basic-offset:4 -*- + +// This file is part of libdap, A C++ implementation of the OPeNDAP Data +// Access Protocol. + +// Copyright (c) 2014 OPeNDAP, Inc. +// Author: James Gallagher +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// +// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112. + +#ifndef _D4RValue_h +#define _D4RValue_h + +#include +#include + +#include +#include + +namespace libdap +{ + +class BaseType; +class D4RValue; + +// Factory class to build RValue objects. User by the parser/ce-evaluator +D4RValue *D4RValueFactory(std::string cpps); + +class D4RValueList +{ +private: + std::vector d_rvalues; + + void m_duplicate(const D4RValueList &src); + +public: + typedef std::vector::iterator iter; + + D4RValueList() { } + D4RValueList(const D4RValueList &src) { m_duplicate(src); } + D4RValueList(D4RValue *rv) { add_rvalue(rv); } + + virtual ~D4RValueList(); + + void add_rvalue(D4RValue *rv) { + d_rvalues.push_back(rv); + } + + D4RValue *get_rvalue(unsigned int i) { + return d_rvalues.at(i); + } + + iter begin() { return d_rvalues.begin(); } + iter end() { return d_rvalues.end(); } + + unsigned int size() const { return d_rvalues.size(); } + +}; + +/** + * Holds the RValues for the D4 function parser and for the filter + * expression evaluator. + */ +class D4RValue +{ +public: + enum value_kind { + unknown, + basetype, + function, + constant + }; + +private: + BaseType *d_variable; // This is a weak pointer; do not delete + + D4Function d_func; // (weak) pointer to a function returning BaseType * + D4RValueList *d_args; // pointer to arguments to the function; delete + + BaseType *d_constant; // pointer; delete. + + value_kind d_value_kind; + + /** @brief Clone 'src' to 'this'. */ + void m_duplicate(const D4RValue &src); + + friend class D4RValueList; + +public: + D4RValue() : d_variable(0), d_func(0), d_args(0), d_constant(0), d_value_kind(unknown) { } + D4RValue(const D4RValue &src) { m_duplicate(src); } + D4RValue(BaseType *btp) : d_variable(btp), d_func(0), d_args(0), d_constant(0), d_value_kind(basetype) { } + D4RValue(D4Function f, D4RValueList *args) : d_variable(0), d_func(f), d_args(args), d_constant(0), d_value_kind(function) { } + + D4RValue(unsigned long long ui); + D4RValue(long long i); + D4RValue(double r); + D4RValue(std::string s); + D4RValue(std::vector &byte_args); + D4RValue(std::vector &byte_int8); + D4RValue(std::vector &byte_uint16); + D4RValue(std::vector &byte_int16); + D4RValue(std::vector &byte_uint32); + D4RValue(std::vector &byte_int32); + D4RValue(std::vector &byte_uint64); + D4RValue(std::vector &byte_int64); + D4RValue(std::vector &byte_float32); + D4RValue(std::vector &byte_float64); + + virtual ~D4RValue(); + + D4RValue &operator=(D4RValue &rhs) { + if (this == &rhs) + return *this; + + m_duplicate(rhs); + + return *this; + } + + /** + * @brief What kind of thing holds the value + * Values in DAP4 constraints are either constants, dataset variables + * or function results. It might be nice to know the source of a + * given value in order to optimize the evaluation of certain kinds of + * expressions. + * @return The 'value_kind' of this value. + */ + value_kind get_kind() const { return d_value_kind; } + + // This is the call that will be used to return the value of a function. + // jhrg 3/10/14 + virtual BaseType *value(DMR &dmr); + // And this optimizes value() for filters, where functions are not supported. + virtual BaseType *value(); + +}; + +} // namespace libdap +#endif // _RValue_h diff --git a/D4Sequence.cc b/D4Sequence.cc new file mode 100644 index 0000000..7a9abd0 --- /dev/null +++ b/D4Sequence.cc @@ -0,0 +1,590 @@ +// -*- mode: c++; c-basic-offset:4 -*- + +// This file is part of libdap, A C++ implementation of the OPeNDAP Data +// Access Protocol. + +// Copyright (c) 2013 OPeNDAP, Inc. +// Author: James Gallagher +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112. + +#include "config.h" + +//#define DODS_DEBUG + +#include +#include +#include + +#include "D4Sequence.h" + +#include "D4StreamMarshaller.h" +#include "D4StreamUnMarshaller.h" + +#include "D4RValue.h" +#include "D4FilterClause.h" // also contains D4FilterClauseList + +#include "debug.h" +#include "Error.h" +#include "InternalErr.h" +#include "util.h" +#include "escaping.h" + +#undef CLEAR_LOCAL_DATA + +using namespace std; + +namespace libdap { + +#if 0 +// Keep this stuff around in case we decide to switch back to sentinels + +static const unsigned char end_of_sequence = 0xA5;// binary pattern 1010 0101 +static const unsigned char start_of_instance = 0x5A;// binary pattern 0101 1010 + +static void +write_end_of_sequence(Marshaller &m) +{ + m.put_opaque( (char *)&end_of_sequence, 1 ); +} + +static void +write_start_of_instance(Marshaller &m) +{ + m.put_opaque( (char *)&start_of_instance, 1 ); +} + +static unsigned char +read_marker(UnMarshaller &um) +{ + unsigned char marker; + um.get_opaque( (char *)&marker, 1 ); + + return marker; +} + +static bool +is_start_of_instance(unsigned char marker) +{ + return (marker == start_of_instance); +} + +static bool +is_end_of_sequence(unsigned char marker) +{ + return (marker == end_of_sequence); +} +#endif + +// Private member functions + +// A reminder of these type defs +// +// typedef vector D4SeqRow; +// typedef vector D4SeqValues; +// D4SeqValues d_values; + +void D4Sequence::m_duplicate(const D4Sequence &s) +{ + d_length = s.d_length; +#if INDEX_SUBSETTING + d_starting_row_number = s.d_starting_row_number; + d_ending_row_number = s.d_ending_row_number; + d_row_stride = s.d_row_stride; +#endif + // Deep copy for the values + for (D4SeqValues::const_iterator i = s.d_values.begin(), e = s.d_values.end(); i != e; ++i) { + D4SeqRow &row = **i; + D4SeqRow *dest = new D4SeqRow; + for (D4SeqRow::const_iterator j = row.begin(), e = row.end(); j != e; ++j) { + // *j is a BaseType* + dest->push_back((*j)->ptr_duplicate()); + } + + d_values.push_back(dest); + } + + d_copy_clauses = s.d_copy_clauses; + d_clauses = (s.d_clauses != 0) ? new D4FilterClauseList(*s.d_clauses) : 0; // deep copy if != 0 +} + +// Public member functions + +/** The Sequence constructor requires only the name of the variable + to be created. The name may be omitted, which will create a + nameless variable. This may be adequate for some applications. + + @param n A string containing the name of the variable to be + created. + + @brief The Sequence constructor. */ +D4Sequence::D4Sequence(const string &n) : + Constructor(n, dods_sequence_c, true /* is dap4 */), d_clauses(0), d_copy_clauses(true), d_length(0) +{ +} + +/** The Sequence server-side constructor requires the name of the variable + to be created and the dataset name from which this variable is being + created. + + @param n A string containing the name of the variable to be + created. + @param d A string containing the name of the dataset from which this + variable is being created. + + @brief The Sequence server-side constructor. */ +D4Sequence::D4Sequence(const string &n, const string &d) : + Constructor(n, d, dods_sequence_c, true /* is dap4 */), d_clauses(0), d_copy_clauses(true), d_length(0) +{ +} + +/** @brief The Sequence copy constructor. */ +D4Sequence::D4Sequence(const D4Sequence &rhs) : Constructor(rhs) +{ + m_duplicate(rhs); +} + +BaseType * +D4Sequence::ptr_duplicate() +{ + return new D4Sequence(*this); +} + +static inline void delete_bt(BaseType *bt_ptr) +{ + delete bt_ptr; +} + +static inline void delete_rows(D4SeqRow *bt_row_ptr) +{ + for_each(bt_row_ptr->begin(), bt_row_ptr->end(), delete_bt); + + delete bt_row_ptr; +} + +D4Sequence::~D4Sequence() +{ + clear_local_data(); + delete d_clauses; +} + +void D4Sequence::clear_local_data() +{ + if (!d_values.empty()) { + for_each(d_values.begin(), d_values.end(), delete_rows); + d_values.resize(0); + } + + set_read_p(false); +} + +D4Sequence & +D4Sequence::operator=(const D4Sequence &rhs) +{ + if (this == &rhs) return *this; + + dynamic_cast(*this) = rhs; // run Constructor= + + m_duplicate(rhs); + + return *this; +} + +/** + * @brief Read the next instance of the sequence + * While the rest of the variables' read() methods are assumed to return the entire + * variable in one call (modulo enhancements of the library to support streaming + * large variables), this class assumes that the underlying data store is returning + * data from a table of unknown size. Thus, D4Sequence::read() is assumed to return + * one instance (or element or row) of the sequence per call and return true when the + * EOF (end of the sequence) is reached. + * + * For each call to read, the values for each of the sequence's members + * are expected to have been loaded into the member's BaseType variables; this + * method will copy them out and store then in the D4Sequence's internal storage. + * This method always returns the next instance that satisfies the CE when 'filter' + * is true. + * + * @note This method is called by D4Sequence::serialize() and it will evaluate the + * CE for each set of values read. + * + * @param dmr + * @param eval + * @param filter + * @return False when read() indicates that the EOF was found, true otherwise. + */ +bool D4Sequence::read_next_instance(bool filter) +{ + bool eof = false; + bool done = false; + + do { + eof = read(); + if (eof) { // bail if EOF + continue; + } + // if we are supposed to filter and the clauses eval to true, we're done + else if (filter && d_clauses && d_clauses->value()) { + d_length++; + done = true; + } + // else if we're not supposed to filter or there are no clauses, we're done + else if (!filter || !d_clauses) { + d_length++; + done = true; + } + + // Set up the next call to get another row's worth of data + set_read_p(false); + + } while (!eof && !done); + + return !eof; +} + +void D4Sequence::intern_data() +{ + read_sequence_values(true); + +#if 0 + // Read the data values, then serialize. + while (read_next_instance(true /*filter*/)) { + D4SeqRow *row = new D4SeqRow; + for (Vars_iter i = d_vars.begin(), e = d_vars.end(); i != e; i++) { + if ((*i)->send_p()) { + // store the variable's value. + row->push_back((*i)->ptr_duplicate()); + // the copy should have read_p true to prevent the serialize() call + // below in the nested for loops from triggering a second call to + // read(). + row->back()->set_read_p(true); + } + } + d_values.push_back(row); + } + + set_length(d_values.size()); +#endif +} + +/** + * @brief Read a Sequence's value into memory + * + * This is a helper method for serialize() that enables the code + * to recursively read values for child sequences. This method assumes + * that the D4Sequence::read() method does not call itself recursively + * for child sequences, as is the case with DAP2 sequences. If you + * have a data store that requires the outer-most sequence to read + * values for its child sequences, you will need to specialize this + * method. See also the methods associated with the sequence values + * because unlike DAP2 sequences, in DAP4 the sequences hold all their + * values in memory before writing them out. + * + * @note We may revisit the idea that values must be held in memory + * before being written. That is a consequence of using a length prefix + * instead of a series of sentinel values. + * + * @param filter True if the/a file expression bound to this sequence + * should be evaluated. + * @see set_value() + */ +void D4Sequence::read_sequence_values(bool filter) +{ + DBG(cerr << __PRETTY_FUNCTION__ << " BEGIN" << endl); + + if (read_p()) return; + + // Read the data values, then serialize. NB: read_next_instance sets d_length + // evaluates the filter expression + while (read_next_instance(filter)) { + DBG(cerr << "read_sequence_values() - Adding row" << endl); + D4SeqRow* row = new D4SeqRow; + for (Vars_iter i = d_vars.begin(), e = d_vars.end(); i != e; i++) { + if ((*i)->send_p()) { + DBG(cerr << ":serialize() - reading data for " << (*i)->type_name() << " " << (*i)->name() << endl); + if ((*i)->type() == dods_sequence_c) { + DBG(cerr << "Reading child sequence values for " << (*i)->name() << endl); + D4Sequence *d4s = static_cast(*i); + d4s->read_sequence_values(filter); + d4s->d_copy_clauses = false; + row->push_back(d4s->ptr_duplicate()); + d4s->d_copy_clauses = true; // Must be sure to not break the object in general + row->back()->set_read_p(true); + } + else { + // store the variable's value. + row->push_back((*i)->ptr_duplicate()); + // the copy should have read_p true to prevent the serialize() call + // below in the nested for loops from triggering a second call to + // read(). + row->back()->set_read_p(true); + } + } + } + + // When specializing this, use set_value() + d_values.push_back(row); + DBG(cerr << " read_sequence_values() - Row completed" << endl); + } + + set_length(d_values.size()); + + DBGN(cerr << __PRETTY_FUNCTION__ << " END added " << d_values.size() << endl); +} + +/** + * @brief Serialize the values of a D4Sequence + * This method assumes that the underlying data store cannot/does not return a count + * of items separately from the items themselves. For a data store that does, this + * method should probably be specialized to take advantage of that. Because the DAP4 + * spec requires that a sequence be prefixed by a count, this method reads the entire + * sequence into memory before sending it (and counts the number of elements in the + * the process). For a data store where the count is available a priori, this could + * be rewritten so that the count is sent and then each instance/element of the sequence + * sent in succession. + * + * If this method is specialized, once the data are loaded into the D4SeqValues instance, + * make sure to set d_length and make sure to set_read_p for each BaseType in D4SeqValues. + * + * @param m Stream data sink + * @param dmr DMR object for the evaluator + * @param eval CE Evaluator object + * @param filter True if the CE should be evaluated, false otherwise. + */ +void D4Sequence::serialize(D4StreamMarshaller &m, DMR &dmr, bool filter) +{ + DBGN(cerr << __PRETTY_FUNCTION__ << " BEGIN" << endl); + + // Read the data values, then serialize. NB: read_next_instance sets d_length + // evaluates the filter expression + read_sequence_values(filter); + + // write D4Sequecne::length(); don't include the length in the checksum + m.put_count(d_length); + + // By this point the d_values object holds all and only the values to be sent; + // use the serialize methods to send them (but no need to test send_p). + for (D4SeqValues::iterator i = d_values.begin(), e = d_values.end(); i != e; ++i) { + for (D4SeqRow::iterator j = (*i)->begin(), f = (*i)->end(); j != f; ++j) { + (*j)->serialize(m, dmr, /*eval,*/false); + } + } + + DBGN(cerr << __PRETTY_FUNCTION__ << " END" << endl); +} + +void D4Sequence::deserialize(D4StreamUnMarshaller &um, DMR &dmr) +{ + int64_t um_count = um.get_count(); + + set_length(um_count); + + for (int64_t i = 0; i < d_length; ++i) { + D4SeqRow *row = new D4SeqRow; + for (Vars_iter i = d_vars.begin(), e = d_vars.end(); i != e; ++i) { + (*i)->deserialize(um, dmr); + row->push_back((*i)->ptr_duplicate()); + } + d_values.push_back(row); + } +} + +/** + * @brief Access the filter clauses for this D4Sequence + * + * When a filter is supplied with a DAP4 constraint, the expression is + * parsed and one or more D4FilterClause objects are built and stored in + * a D4FilterClauseList bound to the D4Sequence to be filtered. + * + * @return A reference to this D4Sequence's filter clause list + * @see D4FilterClauseList + */ +D4FilterClauseList & D4Sequence::clauses() +{ + if (!d_clauses) d_clauses = new D4FilterClauseList(); + return *d_clauses; +} + + +#if INDEX_SUBSETTING +/** Set the start, stop and stride for a row-number type constraint. + This should be used only when the sequence is constrained using the + bracket notation (which supplies start, stride and stop information). + If omitted, the stride defaults to 1. + + @param start The starting row number. The first row is row zero. + @param stop The ending row number. The 20th row is row 19. + @param stride The stride. A stride of two skips every other row. */ +virtual void set_row_number_constraint(int start, int stop, int stride) +{ + if (stop < start) + throw Error(malformed_expr, "Starting row number must precede the ending row number."); + + d_starting_row_number = start; + d_row_stride = stride; + d_ending_row_number = stop; +} +#endif + +/** @brief Get a whole row from the sequence. + @param row Get row number row from the sequence. + @return A BaseTypeRow object (vector). Null if there's no such + row number as \e row. */ +D4SeqRow * +D4Sequence::row_value(size_t row) +{ + if (row >= d_values.size()) return 0; + return d_values[row]; +} + +static bool base_type_name_eq(BaseType *btp, const string name) +{ + return btp->name() == name; +} + +/** @brief Get the BaseType pointer to the named variable of a given row. + @param row Read from row in the sequence. + @param name Return name from row. + @return A BaseType which holds the variable and its value. + @see number_of_rows */ +BaseType * +D4Sequence::var_value(size_t row_num, const string &name) +{ + D4SeqRow *row = row_value(row_num); + if (!row) return 0; + + D4SeqRow::iterator elem = find_if(row->begin(), row->end(), bind2nd(ptr_fun(base_type_name_eq), name)); + return (elem != row->end()) ? *elem : 0; +} + +/** @brief Get the BaseType pointer to the $i^{th}$ variable of row. + @param row Read from row in the sequence. + @param i Return the $i^{th}$ variable from row. + @return A BaseType which holds the variable and its value. + @see number_of_rows */ +BaseType * +D4Sequence::var_value(size_t row_num, size_t i) +{ + D4SeqRow *row = row_value(row_num); + if (!row) return 0; + + if (i >= row->size()) return 0; + + return (*row)[i]; +} + +void D4Sequence::print_one_row(ostream &out, int row, string space, bool print_row_num) +{ + if (print_row_num) out << "\n" << space << row << ": "; + + out << "{ "; + + int elements = element_count(); + int j = 0; + BaseType *bt_ptr = 0; + + // This version of print_one_row() works for both data read with + // deserialize(), where each variable is assumed to have valid data, and + // intern_data(), where some/many variables do not. Because of that, it's + // not correct to assume that all of the elements will be printed, which + // is what the old code did. + + // Print the first value + while (j < elements && !bt_ptr) { + bt_ptr = var_value(row, j++); + if (bt_ptr) { // data + if (bt_ptr->type() == dods_sequence_c) static_cast(bt_ptr)->print_val_by_rows(out, + space + " ", false, print_row_num); + else + bt_ptr->print_val(out, space, false); + } + } + + // Print the remaining values + while (j < elements) { + bt_ptr = var_value(row, j++); + if (bt_ptr) { // data + out << ", "; + if (bt_ptr->type() == dods_sequence_c) static_cast(bt_ptr)->print_val_by_rows(out, + space + " ", false, print_row_num); + else + bt_ptr->print_val(out, space, false); + } + } + + out << " }"; +} + +void D4Sequence::print_val_by_rows(ostream &out, string space, bool print_decl_p, bool print_row_numbers) +{ + if (print_decl_p) { + print_decl(out, space, false); + out << " = "; + } + + out << "{ "; + + if (length() != 0) { + int rows = length() - 1; // -1 because the last row is treated specially + for (int i = 0; i < rows; ++i) { + print_one_row(out, i, space, print_row_numbers); + out << ", "; + } + print_one_row(out, rows, space, print_row_numbers); + } + + out << " }"; + + if (print_decl_p) out << ";\n"; +} + +void D4Sequence::print_val(ostream &out, string space, bool print_decl_p) +{ + DBG(cerr << name() << " isa " << type_name() << endl); + + print_val_by_rows(out, space, print_decl_p, false); +} + +/** @brief dumps information about this object + * + * Displays the pointer value of this instance and information about this + * instance. + * + * @param strm C++ i/o stream to dump the information to + * @return void + */ +void D4Sequence::dump(ostream &strm) const +{ + strm << DapIndent::LMarg << "Sequence::dump - (" << (void *) this << ")" << endl; + DapIndent::Indent(); + Constructor::dump(strm); + strm << DapIndent::LMarg << "# rows deserialized: " << d_length << endl; + strm << DapIndent::LMarg << "bracket notation information:" << endl; + + DapIndent::Indent(); +#if INDEX_SUBSETTING + strm << DapIndent::LMarg << "starting row #: " << d_starting_row_number << endl; + strm << DapIndent::LMarg << "row stride: " << d_row_stride << endl; + strm << DapIndent::LMarg << "ending row #: " << d_ending_row_number << endl; +#endif + DapIndent::UnIndent(); + + DapIndent::UnIndent(); +} + +} // namespace libdap + diff --git a/D4Sequence.h b/D4Sequence.h new file mode 100644 index 0000000..4b606f5 --- /dev/null +++ b/D4Sequence.h @@ -0,0 +1,310 @@ +// -*- mode: c++; c-basic-offset:4 -*- + +// This file is part of libdap, A C++ implementation of the OPeNDAP Data +// Access Protocol. + +// Copyright (c) 2013 OPeNDAP, Inc. +// Author: James Gallagher +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112. + +#ifndef _d4sequence_h +#define _d4sequence_h 1 + +#include "Constructor.h" + +// DAP2 Sequence supported subsetting using the array notation. This might +// be introduced into DAP4 later on. +#define INDEX_SUBSETTING 0 + +class Crc32; + +namespace libdap +{ +class BaseType; +class D4FilterClauseList; + +/** The type BaseTypeRow is used to store single rows of values in an + instance of D4Sequence. Values are stored in instances of BaseType. */ +typedef vector D4SeqRow; + +/** This type holds all of the values of a D4Sequence. */ +typedef vector D4SeqValues; + +/** The type BaseTypeRow is used to store single rows of values in an + instance of Sequence. Values are stored in instances of BaseType. */ +typedef vector BaseTypeRow; + +/** This type holds all of the values of a Sequence. */ +typedef vector SequenceValues; + + +/** This is the interface for the class D4Sequence. A sequence contains + a single set of variables, all at the same lexical level just like + a Structure. Like a Structure, a D4Sequence may contain other + compound types, including other D4Sequences. Unlike a Structure, a + D4Sequence defines a pattern that is repeated N times for a sequence + of N elements. It is useful to think of a D4Sequence as representing + a table of values (like a relational database), with each row of + the table corresponding to a D4Sequence ``instance.'' (This usage + can be confusing, since ``instance'' also refers to a particular + item of class D4Sequence.) For example: + +
+    D4Sequence {
+      String name;
+      Int32 age;
+    } person;
+    
+ + This represents a Sequence of ``person'' records, each instance of + which contains a name and an age: + +
+    Fred       34
+    Ralph      23
+    Andrea     29
+    ...
+    
+ + A D4Sequence can be arbitrarily long, which is to say that its + length is not part of its declaration. A D4Sequence can contain + other D4Sequences: + +
+    D4Sequence {
+      String name;
+      Int32 age;
+      D4Sequence {
+        String friend;
+      } friend_list;
+    } person;
+    
+ +
+    Fred       34     Norman
+                      Andrea
+                      Ralph
+                      Lisa
+    Ralph      23     Norman
+                      Andrea
+                      Lisa
+                      Marth
+                      Throckmorton
+                      Helga
+                      Millicent
+    Andrea     29     Ralph
+                      Natasha
+                      Norman
+    ...        ..     ...
+    
+ + Internally, the D4Sequence is represented by a vector of vectors. The + members of the outer vector are the members of the D4Sequence. This + includes the nested D4Sequences, as in the above example. + + Because the length of a D4Sequence is indeterminate, there are + changes to the behavior of the functions to read this class of + data. The read() function for D4Sequence must be written so that + successive calls return values for successive rows of the D4Sequence. + + Similar to a C structure, you refer to members of D4Sequence + elements with a ``.'' notation. For example, if the D4Sequence has + a member D4Sequence called ``Tom'' and Tom has a member Float32 + called ``shoe_size'', you can refer to Tom's shoe size as + ``Tom.shoe_size''. + + @brief Holds a sequence. */ + +class D4Sequence: public Constructor +{ +private: + // This may be zero (nullptr) but the accessor (clauses()) allocates an + // instance if that is the case. + D4FilterClauseList *d_clauses; + + // Use this to control if ptr_duplicate(), ..., copy the filter clauses. + // Because the values of a child sequence are held in copies of the Seq + // object they clauses will bound to the 'master' instance will be copied + // but the copies will never be used. This field can be used to control + // that. ...purely an optimization. + bool d_copy_clauses; + +protected: + // This holds the values of the sequence. Values are stored in + // instances of BaseTypeRow objects which hold instances of BaseType. + // + // Allow these values to be accessed by subclasses + D4SeqValues d_values; + + int64_t d_length; // How many elements are in the sequence; -1 if not currently known + +#if INDEX_SUBSETTING + int d_starting_row_number; + int d_row_stride; + int d_ending_row_number; +#endif + + void m_duplicate(const D4Sequence &s); + + // Specialize this if you have a data source that requires read() + // recursively call itself for child sequences. + void read_sequence_values(bool filter); + + friend class D4SequenceTest; + +public: + + D4Sequence(const string &n); + D4Sequence(const string &n, const string &d); + + D4Sequence(const D4Sequence &rhs); + + virtual ~D4Sequence(); + + D4Sequence &operator=(const D4Sequence &rhs); + + virtual BaseType *ptr_duplicate(); + + virtual void clear_local_data(); + + /** + * @brief The number of elements in a Sequence object. + * @note This is not the number of items in a row, but the number + * of rows in the complete sequence object. + * + * @return 0 if the number of elements is unknown, else + * return the number of elements. + */ + virtual int length() const { return (int)d_length; } + + /** + * Set the length of the sequence. + * @param count + */ + virtual void set_length(int count) { d_length = (int64_t)count; } + + virtual bool read_next_instance(bool filter); + + virtual void intern_data(ConstraintEvaluator &, DDS &) { + throw InternalErr(__FILE__, __LINE__, "Not implemented for DAP4"); + } + virtual bool serialize(ConstraintEvaluator &, DDS &, Marshaller &, bool ) { + throw InternalErr(__FILE__, __LINE__, "Not implemented for DAP4"); + } + virtual bool deserialize(UnMarshaller &, DDS *, bool ) { + throw InternalErr(__FILE__, __LINE__, "Not implemented for DAP4"); + } + + // DAP4 + virtual void intern_data(/*Crc32 &checksum, DMR &dmr, ConstraintEvaluator &eval*/); + virtual void serialize(D4StreamMarshaller &m, DMR &dmr, /*ConstraintEvaluator &eval,*/ bool filter = false); + virtual void deserialize(D4StreamUnMarshaller &um, DMR &dmr); + + D4FilterClauseList &clauses(); + +#if INDEX_SUBSETTING + /** Return the starting row number if the sequence was constrained using + row numbers (instead of, or in addition to, a relational constraint). + If a relational constraint was also given, the row number corresponds + to the row number of the sequence after applying the relational + constraint. + + If the bracket notation was not used to constrain this sequence, this + method returns -1. + + @brief Get the starting row number. + @return The starting row number. */ + virtual int get_starting_row_number() const { return d_starting_row_number; } + + /** Return the row stride number if the sequence was constrained using + row numbers (instead of, or in addition to, a relational constraint). + If a relational constraint was also given, the row stride is applied + to the sequence after applying the relational constraint. + + If the bracket notation was not used to constrain this sequence, this + method returns -1. + + @brief Get the row stride. + @return The row stride. */ + virtual int get_row_stride() const { return d_row_stride; } + + /** Return the ending row number if the sequence was constrained using + row numbers (instead of, or in addition to, a relational constraint). + If a relational constraint was also given, the row number corresponds + to the row number of the sequence after applying the + relational constraint. + + If the bracket notation was not used to constrain this sequence, this + method returns -1. + + @brief Get the ending row number. + @return The ending row number. */ + virtual int get_ending_row_number() const { return d_ending_row_number; } + + virtual void set_row_number_constraint(int start, int stop, int stride = 1); +#endif + + /** + * @brief Set the internal value. + * The 'values' of a D4Sequence is a vector of vectors of BaseType* objects. + * Using this method does not perform a deep copy; the BaseType*s are + * copied so the caller should not free them. Note that this does set + * d_length but the read_p flag for the BaseTypes should all be set to + * keep the serializer from trying to read each of them. + * @param values + */ + virtual void set_value(D4SeqValues &values) { d_values = values; d_length = d_values.size(); } + + /** + * @brief Get the values for this D4Sequence + * This method returns a reference to the values held by the instance. + * You should make sure that the instance really holds values before + * calling it! Do not free the BaseType*s contained in the vector of + * vectors. + * @return A reference tp the vector of vector of BaseType* + */ + virtual D4SeqValues value() const { return d_values; } + + /** + * @brief Get the sequence values by reference + * This method returns a reference to the D4Sequence's values, + * eliminating the copy of all the pointers. For large sequences, + * that could be a substantial number of values (even though + * they are 'just' pointers). + * @return A reference to the vector of vector of BaseType* + */ + virtual D4SeqValues &value_ref() { return d_values; } + + virtual D4SeqRow *row_value(size_t row); + virtual BaseType *var_value(size_t row, const string &name); + virtual BaseType *var_value(size_t row, size_t i); + + virtual void print_one_row(ostream &out, int row, string space, + bool print_row_num = false); + virtual void print_val_by_rows(ostream &out, string space = "", + bool print_decl_p = true, + bool print_row_numbers = true); + virtual void print_val(ostream &out, string space = "", + bool print_decl_p = true); + + virtual void dump(ostream &strm) const ; +}; + +} // namespace libdap + +#endif //_sequence_h diff --git a/D4StreamMarshaller.cc b/D4StreamMarshaller.cc new file mode 100644 index 0000000..d881b28 --- /dev/null +++ b/D4StreamMarshaller.cc @@ -0,0 +1,826 @@ +// D4StreamMarshaller.cc + +// -*- mode: c++; c-basic-offset:4 -*- + +// This file is part of libdap, A C++ implementation of the OPeNDAP Data +// Access Protocol. + +// Copyright (c) 2012 OPeNDAP, Inc. +// Author: James Gallagher +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112. + +#include "config.h" + +#include +#include +#include + +#include +#include +#include +#include + +//#define DODS_DEBUG 1 + +#ifdef HAVE_PTHREAD_H +#include +#endif + +#include "D4StreamMarshaller.h" +#ifdef USE_POSIX_THREADS +#include "MarshallerThread.h" +#endif + +#if USE_XDR_FOR_IEEE754_ENCODING +#include "XDRUtils.h" +#include "util.h" +#endif + +#include "debug.h" + +using namespace std; + +namespace libdap { + +#if 0 +// We decided to use int64_t to represent sizes of both arrays and strings, +// So this code is not used. jhrg 10/4/13 + +// From the Google protobuf library +inline uint8_t* WriteVarint64ToArrayInline(uint64_t value, uint8_t* target) { + // Splitting into 32-bit pieces gives better performance on 32-bit + // processors. + uint32_t part0 = static_cast(value ); + uint32_t part1 = static_cast(value >> 28); + uint32_t part2 = static_cast(value >> 56); + + int size; + + // Here we can't really optimize for small numbers, since the value is + // split into three parts. Checking for numbers < 128, for instance, + // would require three comparisons, since you'd have to make sure part1 + // and part2 are zero. However, if the caller is using 64-bit integers, + // it is likely that they expect the numbers to often be very large, so + // we probably don't want to optimize for small numbers anyway. Thus, + // we end up with a hard coded binary search tree... + if (part2 == 0) { + if (part1 == 0) { + if (part0 < (1 << 14)) { + if (part0 < (1 << 7)) { + size = 1; goto size1; + } else { + size = 2; goto size2; + } + } else { + if (part0 < (1 << 21)) { + size = 3; goto size3; + } else { + size = 4; goto size4; + } + } + } else { + if (part1 < (1 << 14)) { + if (part1 < (1 << 7)) { + size = 5; goto size5; + } else { + size = 6; goto size6; + } + } else { + if (part1 < (1 << 21)) { + size = 7; goto size7; + } else { + size = 8; goto size8; + } + } + } + } else { + if (part2 < (1 << 7)) { + size = 9; goto size9; + } else { + size = 10; goto size10; + } + } + + // GOOGLE_LOG(FATAL) << "Can't get here."; + + size10: target[9] = static_cast((part2 >> 7) | 0x80); + size9 : target[8] = static_cast((part2 ) | 0x80); + size8 : target[7] = static_cast((part1 >> 21) | 0x80); + size7 : target[6] = static_cast((part1 >> 14) | 0x80); + size6 : target[5] = static_cast((part1 >> 7) | 0x80); + size5 : target[4] = static_cast((part1 ) | 0x80); + size4 : target[3] = static_cast((part0 >> 21) | 0x80); + size3 : target[2] = static_cast((part0 >> 14) | 0x80); + size2 : target[1] = static_cast((part0 >> 7) | 0x80); + size1 : target[0] = static_cast((part0 ) | 0x80); + + target[size-1] &= 0x7F; + return target + size; +} +#endif + +#if USE_XDR_FOR_IEEE754_ENCODING +void D4StreamMarshaller::m_serialize_reals(char *val, unsigned int num, int width, Type type) +{ + dods_uint64 size = num * width; + + char *buf = new char[size]; + XDR xdr; + xdrmem_create(&xdr, &buf[0], size, XDR_ENCODE); + try { + if(!xdr_array(&xdr, &val, (unsigned int *)&num, size, width, XDRUtils::xdr_coder(type))) + throw InternalErr(__FILE__, __LINE__, "Error serializing a Float64 array"); + + if (xdr_getpos(&xdr) != size) + throw InternalErr(__FILE__, __LINE__, "Error serializing a Float64 array"); + + // If this is a little-endian host, twiddle the bytes + static bool twiddle_bytes = !is_host_big_endian(); + if (twiddle_bytes) { + if (width == 4) { + dods_float32 *lbuf = reinterpret_cast(&buf[0]); + while (num--) { + dods_int32 *i = reinterpret_cast(lbuf++); + *i = bswap_32(*i); + } + } + else { // width == 8 + dods_float64 *lbuf = reinterpret_cast(&buf[0]); + while (num--) { + dods_int64 *i = reinterpret_cast(lbuf++); + *i = bswap_64(*i); + } + } + } +#ifdef USE_POSIX_THREADS + Locker lock(tm->get_mutex(), tm->get_cond(), tm->get_child_thread_count()); + + tm->increment_child_thread_count(); + tm->start_thread(MarshallerThread::write_thread, d_out, buf, size); + + // The child thread will delete buf when it's done + xdr_destroy(&xdr); +#else + d_out.write(&buf[0], size); + xdr_destroy(&xdr); + delete [] buf; +#endif + } + catch (...) { + xdr_destroy(&xdr); + delete [] buf; + + throw; + } +} +#endif + +/** Build an instance of D4StreamMarshaller. Bind the C++ stream out to this + * instance. If the write_data parameter is true, write the data in addition + * to computing and sending the checksum. + * + * @param out Write to this stream object. + * @param write_data If true, write data values. True by default + */ +D4StreamMarshaller::D4StreamMarshaller(ostream &out, bool write_data) : + d_out(out), d_write_data(write_data), tm(0) +{ + assert(sizeof(std::streamsize) >= sizeof(int64_t)); + +#if USE_XDR_FOR_IEEE754_ENCODING + // XDR is used if the call std::numeric_limits::is_iec559() + // returns false indicating that the compiler is not using IEEE 754. + // If it is, we just write out the bytes. + xdrmem_create(&d_scalar_sink, d_ieee754_buf, sizeof(dods_float64), XDR_ENCODE); +#endif + +#ifdef USE_POSIX_THREADS + tm = new MarshallerThread; +#endif + + // This will cause exceptions to be thrown on i/o errors. The exception + // will be ostream::failure + out.exceptions(ostream::failbit | ostream::badbit); +} + +D4StreamMarshaller::~D4StreamMarshaller() +{ +#if USE_XDR_FOR_IEEE754_ENCODING + xdr_destroy(&d_scalar_sink); +#endif + + delete tm; +} + +/** Initialize the checksum buffer. This resets the checksum calculation. + */ +void D4StreamMarshaller::reset_checksum() +{ + d_checksum.Reset(); +} + +/** + * Get the current checksum. It is not possible to continue computing the + * checksum once this has been called. + * + * @note This method is not intended to be called often or for inserting the + * checksum into an I/O stream; see put_checksum(). This is intended for + * instrumentation code. + * + * @return The checksum in a string object that always has eight characters. + */ +string D4StreamMarshaller::get_checksum() +{ + ostringstream oss; + oss.setf(ios::hex, ios::basefield); + oss << setfill('0') << setw(8) << d_checksum.GetCrc32(); + + return oss.str(); +} + +/** + * @brief Write the checksum + * Write the checksum for the data sent since the last call to reset_checksum() + * to the I/O stream associated with this marshaller. Use this to send the + * checksum, not get_checksum(). + */ +void D4StreamMarshaller::put_checksum() +{ + Crc32::checksum chk = d_checksum.GetCrc32(); +#ifdef USE_POSIX_THREADS + Locker lock(tm->get_mutex(), tm->get_cond(), tm->get_child_thread_count()); +#endif + d_out.write(reinterpret_cast(&chk), sizeof(Crc32::checksum)); +} + +/** + * Update the current CRC 32 checksum value. Calling this with len equal to + * zero has no effect on the checksum value. + */ +void D4StreamMarshaller::checksum_update(const void *data, unsigned long len) +{ + d_checksum.AddData(reinterpret_cast(data), len); +} + +void D4StreamMarshaller::put_byte(dods_byte val) +{ + checksum_update(&val, sizeof(dods_byte)); + + if (d_write_data) { + DBG( std::cerr << "put_byte: " << val << std::endl ); +#ifdef USE_POSIX_THREADS + // make sure that a child thread is not writing to d_out. + Locker lock(tm->get_mutex(), tm->get_cond(), tm->get_child_thread_count()); +#endif + d_out.write(reinterpret_cast(&val), sizeof(dods_byte)); + } +} + +void D4StreamMarshaller::put_int8(dods_int8 val) +{ + checksum_update(&val, sizeof(dods_int8)); + + if (d_write_data) { + DBG( std::cerr << "put_int8: " << val << std::endl ); +#ifdef USE_POSIX_THREADS + Locker lock(tm->get_mutex(), tm->get_cond(), tm->get_child_thread_count()); +#endif + d_out.write(reinterpret_cast(&val), sizeof(dods_int8)); + } +} + +void D4StreamMarshaller::put_int16(dods_int16 val) +{ + checksum_update(&val, sizeof(dods_int16)); + + if (d_write_data) { +#ifdef USE_POSIX_THREADS + Locker lock(tm->get_mutex(), tm->get_cond(), tm->get_child_thread_count()); +#endif + d_out.write(reinterpret_cast(&val), sizeof(dods_int16)); + } +} + +void D4StreamMarshaller::put_int32(dods_int32 val) +{ + checksum_update(&val, sizeof(dods_int32)); + + if (d_write_data) { +#ifdef USE_POSIX_THREADS + Locker lock(tm->get_mutex(), tm->get_cond(), tm->get_child_thread_count()); +#endif + d_out.write(reinterpret_cast(&val), sizeof(dods_int32)); + } +} + +void D4StreamMarshaller::put_int64(dods_int64 val) +{ + checksum_update(&val, sizeof(dods_int64)); + + if (d_write_data) { +#ifdef USE_POSIX_THREADS + Locker lock(tm->get_mutex(), tm->get_cond(), tm->get_child_thread_count()); +#endif + d_out.write(reinterpret_cast(&val), sizeof(dods_int64)); + } +} + +void D4StreamMarshaller::put_float32(dods_float32 val) +{ +#if !USE_XDR_FOR_IEEE754_ENCODING + assert(std::numeric_limits::is_iec559); + + checksum_update(&val, sizeof(dods_float32)); + + if (d_write_data) { +#ifdef USE_POSIX_THREADS + Locker lock(tm->get_mutex(), tm->get_cond(), tm->get_child_thread_count()); +#endif + d_out.write(reinterpret_cast(&val), sizeof(dods_float32)); + } + +#else + // This code uses XDR to convert from a local representation to IEEE754; + // The extra 'twiddle' operation makes the byte-order correct for this + // host should it not be big-endian. Also note the assert() at the + // start of the method. + + if (d_write_data) { + if (std::numeric_limits::is_iec559 ) { +#ifdef USE_POSIX_THREADS + Locker lock(tm->get_mutex(), tm->get_cond(), tm->get_child_thread_count()); +#endif + d_out.write(reinterpret_cast(&val), sizeof(dods_float32)); + } + else { + if (!xdr_setpos(&d_scalar_sink, 0)) + throw InternalErr(__FILE__, __LINE__, "Error serializing a Float32 variable"); + + if (!xdr_float(&d_scalar_sink, &val)) + throw InternalErr(__FILE__, __LINE__, "Error serializing a Float32 variable"); + + if (xdr_getpos(&d_scalar_sink) != sizeof(dods_float32)) + throw InternalErr(__FILE__, __LINE__, "Error serializing a Float32 variable"); + + // If this is a little-endian host, twiddle the bytes + static bool twiddle_bytes = !is_host_big_endian(); + if (twiddle_bytes) { + dods_int32 *i = reinterpret_cast(&d_ieee754_buf); + *i = bswap_32(*i); + } +#ifdef USE_POSIX_THREADS + Locker lock(tm->get_mutex(), tm->get_cond(), tm->get_child_thread_count()); +#endif + d_out.write(d_ieee754_buf, sizeof(dods_float32)); + } + } +#endif +} + +void D4StreamMarshaller::put_float64(dods_float64 val) +{ +#if !USE_XDR_FOR_IEEE754_ENCODING + assert(std::numeric_limits::is_iec559); + + checksum_update(&val, sizeof(dods_float64)); + + if (d_write_data) { +#ifdef USE_POSIX_THREADS + Locker lock(tm->get_mutex(), tm->get_cond(), tm->get_child_thread_count()); +#endif + d_out.write(reinterpret_cast(&val), sizeof(dods_float64)); + } + +#else + // See the comment above in put_float32() + if (d_write_data) { + if (std::numeric_limits::is_iec559) { +#ifdef USE_POSIX_THREADS + Locker lock(tm->get_mutex(), tm->get_cond(), tm->get_child_thread_count()); +#endif + d_out.write(reinterpret_cast(&val), sizeof(dods_float64));} + } + else { + if (!xdr_setpos(&d_scalar_sink, 0)) + throw InternalErr(__FILE__, __LINE__, "Error serializing a Float64 variable"); + + if (!xdr_double(&d_scalar_sink, &val)) + throw InternalErr(__FILE__, __LINE__, "Error serializing a Float64 variable"); + + if (xdr_getpos(&d_scalar_sink) != sizeof(dods_float64)) + throw InternalErr(__FILE__, __LINE__, "Error serializing a Float64 variable"); + + // If this is a little-endian host, twiddle the bytes + static bool twiddle_bytes = !is_host_big_endian(); + if (twiddle_bytes) { + dods_int64 *i = reinterpret_cast(&d_ieee754_buf); + *i = bswap_64(*i); + } + +#ifdef USE_POSIX_THREADS + Locker lock(tm->get_mutex(), tm->get_cond(), tm->get_child_thread_count()); +#endif + d_out.write(d_ieee754_buf, sizeof(dods_float64)); + } + } +#endif +} + +void D4StreamMarshaller::put_uint16(dods_uint16 val) +{ + checksum_update(&val, sizeof(dods_uint16)); + + if (d_write_data) { +#ifdef USE_POSIX_THREADS + Locker lock(tm->get_mutex(), tm->get_cond(), tm->get_child_thread_count()); +#endif + d_out.write(reinterpret_cast(&val), sizeof(dods_uint16)); + } +} + +void D4StreamMarshaller::put_uint32(dods_uint32 val) +{ + checksum_update(&val, sizeof(dods_uint32)); + + if (d_write_data) { +#ifdef USE_POSIX_THREADS + Locker lock(tm->get_mutex(), tm->get_cond(), tm->get_child_thread_count()); +#endif + d_out.write(reinterpret_cast(&val), sizeof(dods_uint32)); + } +} + +void D4StreamMarshaller::put_uint64(dods_uint64 val) +{ + checksum_update(&val, sizeof(dods_uint64)); + + if (d_write_data) { +#ifdef USE_POSIX_THREADS + Locker lock(tm->get_mutex(), tm->get_cond(), tm->get_child_thread_count()); +#endif + d_out.write(reinterpret_cast(&val), sizeof(dods_uint64)); + } +} + +/** + * Used only for Sequences, where the count must be added to the stream + * and then the fields sent using separate calls to methods here. The + * methods put_opaque_dap4(), ..., that need counts sent as prefixes to + * their data handle it themselves. + * + * @param count How many elements follow. + */ +void D4StreamMarshaller::put_count(int64_t count) +{ +#ifdef USE_POSIX_THREADS + Locker lock(tm->get_mutex(), tm->get_cond(), tm->get_child_thread_count()); +#endif + d_out.write(reinterpret_cast(&count), sizeof(int64_t)); +} + +void D4StreamMarshaller::put_str(const string &val) +{ + checksum_update(val.c_str(), val.length()); + + if (d_write_data) { + int64_t len = val.length(); +#ifdef USE_POSIX_THREADS + Locker lock(tm->get_mutex(), tm->get_cond(), tm->get_child_thread_count()); +#endif + d_out.write(reinterpret_cast(&len), sizeof(int64_t)); + d_out.write(val.data(), val.length()); + } +} + +void D4StreamMarshaller::put_url(const string &val) +{ + put_str(val); +} + +void D4StreamMarshaller::put_opaque_dap4(const char *val, int64_t len) +{ + assert(val); + assert(len >= 0); + + checksum_update(val, len); + + if (d_write_data) { +#ifdef USE_POSIX_THREADS + Locker lock(tm->get_mutex(), tm->get_cond(), tm->get_child_thread_count()); + + d_out.write(reinterpret_cast(&len), sizeof(int64_t)); + + char *byte_buf = new char[len]; + memcpy(byte_buf, val, len); + + tm->increment_child_thread_count(); + tm->start_thread(MarshallerThread::write_thread, d_out, byte_buf, len); +#else + d_out.write(reinterpret_cast(&len), sizeof(int64_t)); + d_out.write(val, len); +#endif + } +} + +/** + * @brief Write a fixed size vector + * @param val Pointer to the data + * @param num Number of bytes to write + */ +void D4StreamMarshaller::put_vector(char *val, int64_t num_bytes) +{ + assert(val); + assert(num_bytes >= 0); + + checksum_update(val, num_bytes); + + if (d_write_data) { +#ifdef USE_POSIX_THREADS + Locker lock(tm->get_mutex(), tm->get_cond(), tm->get_child_thread_count()); + + char *buf = new char[num_bytes]; + memcpy(buf, val, num_bytes); + + tm->increment_child_thread_count(); + tm->start_thread(MarshallerThread::write_thread, d_out, buf, num_bytes); +#else + d_out.write(val, num_bytes); +#endif + } +} + +void D4StreamMarshaller::put_vector(char *val, int64_t num_elem, int elem_size) +{ + assert(val); + assert(num_elem >= 0); + assert(elem_size > 0); + + int64_t bytes; + + switch (elem_size) { + case 1: + assert(!"Don't call this method for bytes, use put_vector(val, bytes) instead"); + bytes = num_elem; + break; + case 2: + // Don't bother testing the sign bit + assert(!(num_elem & 0x4000000000000000)); // 0x 40 00 --> 0100 0000 + bytes = num_elem << 1; + break; + case 4: + assert(!(num_elem & 0x6000000000000000)); // 0x 60 00 --> 0110 0000 + bytes = num_elem << 2; + break; + case 8: + assert(!(num_elem & 0x7000000000000000)); // 0111 0000 + bytes = num_elem << 3; + break; + default: + bytes = num_elem * elem_size; + break; + } + + checksum_update(val, bytes); + + if (d_write_data) { +#ifdef USE_POSIX_THREADS + Locker lock(tm->get_mutex(), tm->get_cond(), tm->get_child_thread_count()); + + char *buf = new char[bytes]; + memcpy(buf, val, bytes); + + tm->increment_child_thread_count(); + tm->start_thread(MarshallerThread::write_thread, d_out, buf, bytes); +#else + d_out.write(val, bytes); +#endif + } +} + +/** + * @brief Write a fixed size vector + * @note This method and its companion for float64 exists in case we need to + * support machine that do not use IEEE754 for their floating point representation. + * @param val Pointer to the data + * @param num Number of elements + * @param width Size of a single element + * @param type DAP variable type; used to handle float32 and float64 types correctly + */ +void D4StreamMarshaller::put_vector_float32(char *val, int64_t num_elem) +{ +#if !USE_XDR_FOR_IEEE754_ENCODING + + assert(std::numeric_limits::is_iec559); + assert(val); + assert(num_elem >= 0); + // sizeof() a 32-bit float is 4, so we're going to send 4 * num_elem bytes, so + // make sure that doesn't overflow a 63-bit integer (the max positive value in + // a signed int64; use 1110 0000 0.. (0xe000 ...) to mask for non-zero bits + // to test that num can be multiplied by 4. A + assert(!(num_elem & 0xe000000000000000)); + + num_elem = num_elem << 2; // num_elem is now the number of bytes + + checksum_update(val, num_elem); + + if (d_write_data) { +#ifdef USE_POSIX_THREADS + Locker lock(tm->get_mutex(), tm->get_cond(), tm->get_child_thread_count()); + + char *buf = new char[num_elem]; + memcpy(buf, val, num_elem); + + tm->increment_child_thread_count(); + tm->start_thread(MarshallerThread::write_thread, d_out, buf, num_elem); +#else + d_out.write(val, num_elem); +#endif + } + +#else + assert(val); + assert(num_elem >= 0); + // sizeof() a 32-bit float is 4, so we're going to send 4 * num_elem bytes, so + // make sure that doesn't overflow a 63-bit integer (the max positive value in + // a signed int64; use 1110 0000 0.. (0xe000 ...) to mask for non-zero bits + // to test that num can be multiplied by 4. A + assert(!(num_elem & 0xe000000000000000)); + + int64_t bytes = num_elem << 2; // num_elem is now the number of bytes + + checksum_update(val, bytes); + + if (d_write_data) { + if (!std::numeric_limits::is_iec559) { + // If not using IEEE 754, use XDR to get it that way. + m_serialize_reals(val, num_elem, 4, type); + } + else { +#ifdef USE_POSIX_THREADS + Locker lock(tm->get_mutex(), tm->get_cond(), tm->get_child_thread_count()); + + char *buf = new char[bytes]; + memcpy(buf, val, bytes); + + tm->increment_child_thread_count(); + tm->start_thread(MarshallerThread::write_thread, d_out, buf, bytes); +#else + d_out.write(val, bytes); +#endif + } + } +#endif +} + +/** + * @brief Write a fixed size vector of float64s + * + * @param val Pointer to the data + * @param num Number of elements + * @param width Size of a single element + * @param type DAP variable type; used to handle float32 and float64 types correctly + */ +void D4StreamMarshaller::put_vector_float64(char *val, int64_t num_elem) +{ +#if !USE_XDR_FOR_IEEE754_ENCODING + + assert(std::numeric_limits::is_iec559); + assert(val); + assert(num_elem >= 0); + // See comment above + assert(!(num_elem & 0xf000000000000000)); + + num_elem = num_elem << 3; // num_elem is now the number of bytes + + checksum_update(val, num_elem); + + if (d_write_data) { +#ifdef USE_POSIX_THREADS + Locker lock(tm->get_mutex(), tm->get_cond(), tm->get_child_thread_count()); + + char *buf = new char[num_elem]; + memcpy(buf, val, num_elem); + + tm->increment_child_thread_count(); + tm->start_thread(MarshallerThread::write_thread, d_out, buf, num_elem); +#else + d_out.write(val, num_elem); +#endif + } +#else + assert(val); + assert(num_elem >= 0); + // sizeof() a 32-bit float is 4, so we're going to send 4 * num_elem bytes, so + // make sure that doesn't overflow a 63-bit integer (the max positive value in + // a signed int64; use 1110 0000 0.. (0xe000 ...) to mask for non-zero bits + // to test that num can be multiplied by 4. A + assert(!(num_elem & 0xe000000000000000)); + + int64_t bytes = num_elem << 3; // num_elem is now the number of bytes + + checksum_update(val, bytes); + + if (d_write_data) { + if (!std::numeric_limits::is_iec559) { + // If not using IEEE 754, use XDR to get it that way. + m_serialize_reals(val, num_elem, 8, type); + } + else { +#ifdef USE_POSIX_THREADS + Locker lock(tm->get_mutex(), tm->get_cond(), tm->get_child_thread_count()); + + char *buf = new char[bytes]; + memcpy(buf, val, bytes); + + tm->increment_child_thread_count(); + tm->start_thread(MarshallerThread::write_thread, d_out, buf, bytes); +#else + d_out.write(val, bytes); +#endif + } + } +#endif + +} + +void D4StreamMarshaller::put_vector_part(char *val, unsigned int num, int width, Type type) +{ + assert(val); + assert(num >= 0); + assert(width > 0); + + switch(type) { + case dods_byte_c: + case dods_char_c: + case dods_int8_c: + case dods_uint8_c: + put_vector(val, num); + break; + + case dods_int16_c: + case dods_uint16_c: + case dods_int32_c: + case dods_uint32_c: + case dods_int64_c: + case dods_uint64_c: + put_vector(val, num, width); + break; + + case dods_enum_c: + if (width == 1) + put_vector(val, num); + else + put_vector(val, num, width); + break; + + case dods_float32_c: + put_vector_float32(val, num); + break; + + case dods_float64_c: + put_vector_float32(val, num); + break; + + case dods_str_c: + case dods_url_c: + throw InternalErr(__FILE__, __LINE__, "Array of String should not be passed to put_vector."); + + case dods_array_c: + throw InternalErr(__FILE__, __LINE__, "Array of Array not allowed."); + + case dods_opaque_c: + case dods_structure_c: + case dods_sequence_c: + throw InternalErr(__FILE__, __LINE__, "Array of String should not be passed to put_vector."); + + case dods_grid_c: + throw InternalErr(__FILE__, __LINE__, "Grid is not part of DAP4."); + + default: + throw InternalErr(__FILE__, __LINE__, "Unknown datatype."); + break; + } +} + +void D4StreamMarshaller::dump(ostream &strm) const +{ + strm << DapIndent::LMarg << "D4StreamMarshaller::dump - (" << (void *) this << ")" << endl; +} + +} // namespace libdap + diff --git a/D4StreamMarshaller.h b/D4StreamMarshaller.h new file mode 100644 index 0000000..3d2247c --- /dev/null +++ b/D4StreamMarshaller.h @@ -0,0 +1,178 @@ +// D4StreamMarshaller.h + +// -*- mode: c++; c-basic-offset:4 -*- + +// This file is part of libdap, A C++ implementation of the OPeNDAP Data +// Access Protocol. + +// Copyright (c) 2002,2003,2012 OPeNDAP, Inc. +// Author: Patrick West , +// James Gallagher +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112. + +#ifndef I_D4StreamMarshaller_h +#define I_D4StreamMarshaller_h 1 + +#include + +// By default, only support platforms that use IEEE754 for floating point values. +// Hacked up code leftover from an older version of the class; largely untested. +// jhrg 10/3/13 +#define USE_XDR_FOR_IEEE754_ENCODING 0 + +#if USE_XDR_FOR_IEEE754_ENCODING +#ifdef WIN32 +#include +#include +#include +#else +#include +#include +#include +#endif +#endif + +#include +#include "crc.h" + +#include "Marshaller.h" +#include "InternalErr.h" + +namespace libdap { + +class Vector; +class MarshallerThread; + +/** @brief Marshaller that knows how to marshal/serialize dap data objects + * to a C++ iostream using DAP4's receiver-makes-right scheme. This code + * adds checksums to the stream and uses the xdr library to encode real + * values if the underlying representation is not IEEE 754. It also supports + * computing the checksum only. + * + * @note This class uses the Marshaller interface; it could be rewritten + * to use far fewer methods since all of the put_*() methods take different + * types. + */ +class D4StreamMarshaller: public Marshaller { + +private: +#if USE_XDR_FOR_IEEE754_ENCODING + XDR d_scalar_sink; + char d_ieee754_buf[sizeof(dods_float64)]; // used to serialize a float or double +#endif + + ostream &d_out; + bool d_write_data; // jhrg 1/27/12 + + Crc32 d_checksum; + + MarshallerThread *tm; + + // These are private so they won't ever get used. + D4StreamMarshaller(); + D4StreamMarshaller(const D4StreamMarshaller &); + D4StreamMarshaller & operator=(const D4StreamMarshaller &); + +#if USE_XDR_FOR_IEEE754_ENCODING + void m_serialize_reals(char *val, int64_t num, int width, Type type); +#endif + +public: + D4StreamMarshaller(std::ostream &out, bool write_data = true); + virtual ~D4StreamMarshaller(); + + virtual void reset_checksum(); + virtual string get_checksum(); + virtual void checksum_update(const void *data, unsigned long len); + + virtual void put_checksum(); + virtual void put_count(int64_t count); + + virtual void put_byte(dods_byte val); + virtual void put_int8(dods_int8 val); + + virtual void put_int16(dods_int16 val); + virtual void put_int32(dods_int32 val); + // Added + virtual void put_int64(dods_int64 val); + + virtual void put_float32(dods_float32 val); + virtual void put_float64(dods_float64 val); + + virtual void put_uint16(dods_uint16 val); + virtual void put_uint32(dods_uint32 val); + // Added + virtual void put_uint64(dods_uint64 val); + + virtual void put_str(const string &val); + virtual void put_url(const string &val); + + virtual void put_opaque(char *, unsigned int) { + throw InternalErr(__FILE__, __LINE__, "Not implemented for DAP4; use put_opaque_dap4() instead."); + } + + virtual void put_opaque_dap4(const char *val, int64_t len); + + // Never use put_int() to send length information in DAP4. + virtual void put_int(int) { + throw InternalErr(__FILE__, __LINE__, "Not Implemented; use put_length_prefix."); + } + + virtual void put_vector(char *val, int64_t num_bytes); + virtual void put_vector(char *val, int64_t num_elem, int elem_size); + virtual void put_vector_float32(char *val, int64_t num_elem); + virtual void put_vector_float64(char *val, int64_t num_elem); + + virtual void put_vector(char *, int , Vector &) { + throw InternalErr(__FILE__, __LINE__, "Not Implemented; use other put_vector() versions."); + } + virtual void put_vector(char *, int , int , Vector &) { + throw InternalErr(__FILE__, __LINE__, "Not Implemented; use other put_vector() versions."); + } + + /** + * Prepare to send a single array/vector using a series of 'put' calls. + * In DAP4 this does nothing because arrays are serialized using the server's + * binary representation (i.e., using 'reader make right'). + * + * @param num Ignored + * @see put_vector_part() + * @see put_vector_end() + */ + virtual void put_vector_start(int /*num*/) { + } + + virtual void put_vector_part(char */*val*/, unsigned int /*num*/, int /*width*/, Type /*type*/); + + /** + * Close a vector when its values are written using put_vector_part(). + * In DAP4 this does nothing because arrays are serialized using the server's + * binary representation (i.e., using 'reader make right'). + * + * @see put_vector_start() + * @see put_vector_part() + */ + virtual void put_vector_end() { + } + + virtual void dump(std::ostream &strm) const; +}; + +} // namespace libdap + +#endif // I_D4StreamMarshaller_h diff --git a/D4StreamUnMarshaller.cc b/D4StreamUnMarshaller.cc new file mode 100644 index 0000000..c7ec806 --- /dev/null +++ b/D4StreamUnMarshaller.cc @@ -0,0 +1,475 @@ +// D4StreamUnMarshaller.cc + +// -*- mode: c++; c-basic-offset:4 -*- + +// This file is part of libdap, A C++ implementation of the OPeNDAP Data +// Access Protocol. + +// Copyright (c) 2012 OPeNDAP, Inc. +// Author: James Gallagher +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112. + +#include "config.h" + +#include +#include + +#include +#include +#include +#include +#include + +//#define DODS_DEBUG2 1 +//#define DODS_DEBUG 1 + +#include "util.h" +//#include "XDRUtils.h" +#include "InternalErr.h" +#include "D4StreamUnMarshaller.h" +#include "debug.h" + +namespace libdap { + +/** + * @brief Build a DAP4 Stream unMarshaller. + * + * Build a DAP4 Stream UnMarshaller initialed to read from am istream object. + * Figure out if the words read for values need to be 'twiddled' based on the + * byte-order of the stream an this host (see set_twiddle_bytes()). + * + * @param in Read from this input stream + * @param is_stream_bigendian The byte order of the data in the stream + */ +D4StreamUnMarshaller::D4StreamUnMarshaller(istream &in, bool twiddle_bytes) : d_in( in ), d_twiddle_bytes(twiddle_bytes) +{ + assert(sizeof(std::streamsize) >= sizeof(int64_t)); + +#if USE_XDR_FOR_IEEE754_ENCODING + // XDR is used to handle transforming non-ieee754 reals, nothing else. + xdrmem_create(&d_source, d_buf, sizeof(dods_float64), XDR_DECODE); +#endif + + // This will cause exceptions to be thrown on i/o errors. The exception + // will be ostream::failure + d_in.exceptions(istream::failbit | istream::badbit); + +} + +/** + * When using this constructor, set_twiddle_bytes() should be called + * before data are processed. + * + * @param in + */ +D4StreamUnMarshaller::D4StreamUnMarshaller(istream &in) : d_in( in ), d_twiddle_bytes(false) +{ + assert(sizeof(std::streamsize) >= sizeof(int64_t)); + +#if USE_XDR_FOR_IEEE754_ENCODING + // XDR is used to handle transforming non-ieee754 reals, nothing else. + xdrmem_create(&d_source, d_buf, sizeof(dods_float64), XDR_DECODE); +#endif + + // This will cause exceptions to be thrown on i/o errors. The exception + // will be ostream::failure + d_in.exceptions(istream::failbit | istream::badbit); +} + +D4StreamUnMarshaller::~D4StreamUnMarshaller( ) +{ +#if USE_XDR_FOR_IEEE754_ENCODING + xdr_destroy(&d_source); +#endif +} + +Crc32::checksum D4StreamUnMarshaller::get_checksum() +{ + Crc32::checksum c; + d_in.read(reinterpret_cast(&c), sizeof(Crc32::checksum)); + + return c; +} + +string D4StreamUnMarshaller::get_checksum_str() +{ + ostringstream oss; + oss.setf(ios::hex, ios::basefield); + oss << setfill('0') << setw(8) << get_checksum(); + + return oss.str(); +} + +void +D4StreamUnMarshaller::get_byte( dods_byte &val ) +{ + d_in.read(reinterpret_cast(&val), sizeof(dods_byte)); +} + +void +D4StreamUnMarshaller::get_int8( dods_int8 &val ) +{ + d_in.read(reinterpret_cast(&val), sizeof(dods_int8)); +} + +void +D4StreamUnMarshaller::get_int16( dods_int16 &val ) +{ + d_in.read(reinterpret_cast(&val), sizeof(dods_int16)); + if (d_twiddle_bytes) + val = bswap_16(val); +} + +void +D4StreamUnMarshaller::get_int32( dods_int32 &val ) +{ + d_in.read(reinterpret_cast(&val), sizeof(dods_int32)); + if (d_twiddle_bytes) + val = bswap_32(val); +} + +void +D4StreamUnMarshaller::get_int64( dods_int64 &val ) +{ + d_in.read(reinterpret_cast(&val), sizeof(dods_int64)); + if (d_twiddle_bytes) + val = bswap_64(val); +} + +void +D4StreamUnMarshaller::get_float32( dods_float32 &val ) +{ +#if !USE_XDR_FOR_IEEE754_ENCODING + assert(std::numeric_limits::is_iec559); + + d_in.read(reinterpret_cast(&val), sizeof(dods_float32)); + if (d_twiddle_bytes) { + dods_int32 *i = reinterpret_cast(&val); + *i = bswap_32(*i); + } + +#else + if (std::numeric_limits::is_iec559) { + d_in.read(reinterpret_cast(&val), sizeof(dods_float32)); + if (d_twiddle_bytes) { + dods_int32 *i = reinterpret_cast(&val); + *i = bswap_32(*i); + } + + } + else { + xdr_setpos( &d_source, 0); + d_in.read(d_buf, sizeof(dods_float32)); + + if (!xdr_float(&d_source, &val)) + throw Error("Network I/O Error. Could not read float 64 data."); + } +#endif +} + +void +D4StreamUnMarshaller::get_float64( dods_float64 &val ) +{ +#if !USE_XDR_FOR_IEEE754_ENCODING + assert(std::numeric_limits::is_iec559); + + d_in.read(reinterpret_cast(&val), sizeof(dods_float64)); + if (d_twiddle_bytes) { + dods_int64 *i = reinterpret_cast(&val); + *i = bswap_64(*i); + } + +#else + if (std::numeric_limits::is_iec559) { + d_in.read(reinterpret_cast(&val), sizeof(dods_float64)); + if (d_twiddle_bytes) { + dods_int64 *i = reinterpret_cast(&val); + *i = bswap_64(*i); + } + } + else { + xdr_setpos( &d_source, 0); + d_in.read(d_buf, sizeof(dods_float64)); + + if (!xdr_double(&d_source, &val)) + throw Error("Network I/O Error. Could not read float 64 data."); + } +#endif +} + +void +D4StreamUnMarshaller::get_uint16( dods_uint16 &val ) +{ + d_in.read(reinterpret_cast(&val), sizeof(dods_uint16)); + if (d_twiddle_bytes) + val = bswap_16(val); +} + +void +D4StreamUnMarshaller::get_uint32( dods_uint32 &val ) +{ + d_in.read(reinterpret_cast(&val), sizeof(dods_uint32)); + if (d_twiddle_bytes) + val = bswap_32(val); +} + +void +D4StreamUnMarshaller::get_uint64( dods_uint64 &val ) +{ + d_in.read(reinterpret_cast(&val), sizeof(dods_uint64)); + if (d_twiddle_bytes) + val = bswap_64(val); +} + +void +D4StreamUnMarshaller::get_str( string &val ) +{ + int64_t len; + d_in.read(reinterpret_cast(&len), sizeof(int64_t)); + + val.resize(len); + d_in.read(&val[0], len); +} + +void +D4StreamUnMarshaller::get_url( string &val ) +{ + get_str( val ) ; +} + +/** + * Read a count value from the stream. This is used with D4Sequence + * which needs to use various other 'get' methods to read its fields. + * Methods like get_opaque_dap4() handle reading their count values + * themselves. + * + * @param count The number of elements to follow. + */ +int64_t +D4StreamUnMarshaller::get_count() +{ + int64_t count; + d_in.read(reinterpret_cast(&count), sizeof(count)); + return count; +} + +/** + * Get opaque data when the size of the data to be read is not known in + * advance. + * + * @param val Value-result parameter for the data; caller must delete. + * @param len value-result parameter for the length of the data + */ +void +D4StreamUnMarshaller::get_opaque_dap4( char **val, int64_t &len ) +{ + //len = get_length_prefix(); + d_in.read(reinterpret_cast(&len), sizeof(len)); + + *val = new char[len]; + d_in.read(*val, len); +} + +void +D4StreamUnMarshaller::get_opaque_dap4( vector &val ) +{ + //len = get_length_prefix(); + int64_t len; + d_in.read(reinterpret_cast(&len), sizeof(len)); + + val.resize(len); + d_in.read(reinterpret_cast(&val[0]), len); +} + +void +D4StreamUnMarshaller::get_vector( char *val, int64_t bytes ) +{ + d_in.read(val, bytes); +} + +#if USE_XDR_FOR_IEEE754_ENCODING +void D4StreamUnMarshaller::m_deserialize_reals(char *val, int64_t num, int width, Type type) +{ + int64_t size = num * width; + // char *buf = (char*)malloc(size); jhrg 7/23/13 + vector buf(size); + XDR xdr; + xdrmem_create(&xdr, &buf[0], size, XDR_DECODE); + try { + xdr_setpos(&d_source, 0); + d_in.read(&buf[0], size); + + if(!xdr_array(&xdr, &val, (unsigned int *)&num, size, width, XDRUtils::xdr_coder(type))) + throw InternalErr(__FILE__, __LINE__, "Error deserializing a Float64 array"); + + if (xdr_getpos(&xdr) != size) + throw InternalErr(__FILE__, __LINE__, "Error deserializing a Float64 array"); + } + catch (...) { + xdr_destroy(&xdr); + throw; + } + xdr_destroy(&xdr); +} +#endif + +void D4StreamUnMarshaller::m_twidle_vector_elements(char *vals, int64_t num, int width) +{ + switch (width) { + case 2: { + dods_int16 *local = reinterpret_cast(vals); + while (num--) { + *local = bswap_16(*local); + local++; + } + break; + } + case 4: { + dods_int32 *local = reinterpret_cast(vals);; + while (num--) { + *local = bswap_32(*local); + local++; + } + break; + } + case 8: { + dods_int64 *local = reinterpret_cast(vals);; + while (num--) { + *local = bswap_64(*local); + local++; + } + break; + } + default: + throw InternalErr(__FILE__, __LINE__, "Unrecognized word size."); + } +} + +void +D4StreamUnMarshaller::get_vector(char *val, int64_t num_elem, int elem_size) +{ + assert(std::numeric_limits::is_iec559); + assert(std::numeric_limits::is_iec559); + assert(val); + assert(num_elem >= 0); + assert(elem_size > 0); + + int64_t bytes; + + switch (elem_size) { + case 1: + assert(!"Don't call this method for bytes, use put_vector(val, bytes) instead"); + bytes = num_elem; + break; + case 2: + // Don't bother testing the sign bit + assert(!(num_elem & 0x4000000000000000)); // 0x 40 00 --> 0100 0000 + bytes = num_elem << 1; + break; + case 4: + assert(!(num_elem & 0x6000000000000000)); // 0x 60 00 --> 0110 0000 + bytes = num_elem << 2; + break; + case 8: + assert(!(num_elem & 0x7000000000000000)); // 0111 0000 + bytes = num_elem << 3; + break; + default: + bytes = num_elem * elem_size; + break; + } + + d_in.read(val, bytes); + + if (d_twiddle_bytes) + m_twidle_vector_elements(val, num_elem, elem_size); +} + +void +D4StreamUnMarshaller::get_vector_float32(char *val, int64_t num_elem) +{ +#if !USE_XDR_FOR_IEEE754_ENCODING + assert(std::numeric_limits::is_iec559); + assert(val); + assert(num_elem >= 0); + assert(!(num_elem & 0x6000000000000000)); // 0x 60 00 --> 0110 0000 + + int64_t bytes = num_elem << 2; + + d_in.read(val, bytes); + + if (d_twiddle_bytes) + m_twidle_vector_elements(val, num_elem, sizeof(dods_float32)); + +#else + if (type == dods_float32_c && !std::numeric_limits::is_iec559) { + // If not using IEEE 754, use XDR to get it that way. + m_deserialize_reals(val, num, 4, type); + } + else if (type == dods_float64_c && !std::numeric_limits::is_iec559) { + m_deserialize_reals(val, num, 8, type); + } + else { + d_in.read(val, num * width); + if (d_twiddle_bytes) + m_twidle_vector_elements(val, num, width); + } +#endif +} + +void +D4StreamUnMarshaller::get_vector_float64(char *val, int64_t num_elem) +{ +#if !USE_XDR_FOR_IEEE754_ENCODING + assert(std::numeric_limits::is_iec559); + assert(val); + assert(num_elem >= 0); + assert(!(num_elem & 0x7000000000000000)); // 0x 70 00 --> 0111 0000 + + int64_t bytes = num_elem << 3; + + d_in.read(val, bytes); + + if (d_twiddle_bytes) + m_twidle_vector_elements(val, num_elem, sizeof(dods_float64)); + +#else + if (type == dods_float32_c && !std::numeric_limits::is_iec559) { + // If not using IEEE 754, use XDR to get it that way. + m_deserialize_reals(val, num, 4, type); + } + else if (type == dods_float64_c && !std::numeric_limits::is_iec559) { + m_deserialize_reals(val, num, 8, type); + } + else { + d_in.read(val, num * width); + if (d_twiddle_bytes) + m_twidle_vector_elements(val, num, width); + } +#endif +} + +void +D4StreamUnMarshaller::dump(ostream &strm) const +{ + strm << DapIndent::LMarg << "D4StreamUnMarshaller::dump - (" + << (void *)this << ")" << endl ; +} + +} // namespace libdap + diff --git a/D4StreamUnMarshaller.h b/D4StreamUnMarshaller.h new file mode 100644 index 0000000..310158b --- /dev/null +++ b/D4StreamUnMarshaller.h @@ -0,0 +1,164 @@ +// D4StreamUnMarshaller.h + +// -*- mode: c++; c-basic-offset:4 -*- + +// This file is part of libdap, A C++ implementation of the OPeNDAP Data +// Access Protocol. + +// Copyright (c) 2012 OPeNDAP, Inc. +// Author: James Gallagher +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112. + +#ifndef I_D4StreamUnMarshaller_h +#define I_D4StreamUnMarshaller_h 1 + +#include + +// See comment in D4StreamMarshaller +#define USE_XDR_FOR_IEEE754_ENCODING 0 + +#if USE_XDR_FOR_IEEE754_ENCODING +#ifdef WIN32 +#include +#include +#include +#else +#include +#include +#include +#endif +#endif + +#include + +// #include "Type.h" +#include "dods-datatypes.h" +#include "UnMarshaller.h" +#include "InternalErr.h" + +#include "util.h" +#include "debug.h" + +using std::istream; + +namespace libdap { + +class Vector; + +/** @brief Read data from the stream made by D4StreamMarshaller. + */ +class D4StreamUnMarshaller: public UnMarshaller { +public: + const static unsigned int c_checksum_length = 4; + +private: + istream &d_in; + bool d_twiddle_bytes; + +#if USE_XDR_FOR_IEEE754_ENCODING + // These are used for reals that need to be converted from IEEE 754 + XDR d_source; + char d_buf[sizeof(dods_float64)]; +#endif + + D4StreamUnMarshaller(); + D4StreamUnMarshaller(const D4StreamUnMarshaller &); + D4StreamUnMarshaller & operator=(const D4StreamUnMarshaller &); +#if USE_XDR_FOR_IEEE754_ENCODING + void m_deserialize_reals(char *val, int64_t num, int width, Type type); +#endif + void m_twidle_vector_elements(char *vals, int64_t num, int width); + +public: + D4StreamUnMarshaller(istream &in, bool twiddle_bytes); + D4StreamUnMarshaller(istream &in); + virtual ~D4StreamUnMarshaller(); + + void set_twiddle_bytes(bool twiddle) { d_twiddle_bytes = twiddle; } + + /** + * @brief Is the data source we are reading from a big-endian machine? + * We need this because the value of the CRC32 checksum is dependent on + * byte order. + * + * @note This is somewhat tortured logic, but if this host is big-endian and + * twiddle_bytes is not true, then the remote host must be big-endian. Similarly, + * if this host is not big-endian and twiddle_bytes is true, then the remote + * host must be big-endian + */ + bool is_source_big_endian() const { return (is_host_big_endian() && !d_twiddle_bytes) + || (!is_host_big_endian() && d_twiddle_bytes); } + + Crc32::checksum get_checksum(); + string get_checksum_str(); + int64_t get_count(); + + virtual void get_byte(dods_byte &val); + virtual void get_int8(dods_int8 &val); + + virtual void get_int16(dods_int16 &val); + virtual void get_int32(dods_int32 &val); + + virtual void get_int64(dods_int64 &val); + + virtual void get_float32(dods_float32 &val); + virtual void get_float64(dods_float64 &val); + + virtual void get_uint16(dods_uint16 &val); + virtual void get_uint32(dods_uint32 &val); + + virtual void get_uint64(dods_uint64 &val); + + virtual void get_str(string &val); + virtual void get_url(string &val); + + virtual void get_opaque(char *, unsigned int) { + throw InternalErr(__FILE__, __LINE__, "Not implemented for DAP4, use get_opaque_dap4() instead."); + } + + virtual void get_opaque_dap4(char **val, int64_t &len); + virtual void get_opaque_dap4( vector &val ); + + virtual void get_int(int &) { + throw InternalErr(__FILE__, __LINE__, "Not implemented for DAP4"); + } + + // Note that DAP4 assumes clients know the size of arrays when they + // read the data; it's the 'varying' get methods that read & return the + // number of elements. These methods are here to appease the UnMarshaller + // 'interface' code + virtual void get_vector(char **, unsigned int &, Vector &) { + throw InternalErr(__FILE__, __LINE__, "Not implemented for DAP4"); + } + + virtual void get_vector(char **, unsigned int &, int, Vector & ) { + throw InternalErr(__FILE__, __LINE__, "Not implemented for DAP4"); + } + + virtual void get_vector(char *val, int64_t num_bytes); + virtual void get_vector(char *val, int64_t num_elem, int elem_size); + virtual void get_vector_float32(char *val, int64_t num_elem); + virtual void get_vector_float64(char *val, int64_t num_elem); + + virtual void dump(ostream &strm) const; +}; + +} // namespace libdap + +#endif // I_D4StreamUnMarshaller_h + diff --git a/DAS.cc b/DAS.cc new file mode 100644 index 0000000..3c60010 --- /dev/null +++ b/DAS.cc @@ -0,0 +1,383 @@ + +// -*- mode: c++; c-basic-offset:4 -*- + +// This file is part of libdap, A C++ implementation of the OPeNDAP Data +// Access Protocol. + +// Copyright (c) 2002,2003 OPeNDAP, Inc. +// Author: James Gallagher +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// +// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112. + +// (c) COPYRIGHT URI/MIT 1994-1999 +// Please read the full copyright statement in the file COPYRIGHT_URI. +// +// Authors: +// jhrg,jimg James Gallagher + +// Methods for the class DAS - a class used to parse the dataset attribute +// structure. +// +// jhrg 7/25/94 + +#include "config.h" + +#include + +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef WIN32 +#include +#endif + +#include +#include + +#include "DAS.h" +#include "AttrTable.h" +#include "Error.h" +#include "InternalErr.h" +#include "parser.h" +#include "escaping.h" +#include "debug.h" + +using std::cerr; +using std::endl; + +// Glue routines declared in das.lex +extern void das_switch_to_buffer(void *new_buffer); +extern void das_delete_buffer(void * buffer); +extern void *das_buffer(FILE *fp); + +//extern void dasrestart(FILE *yyin); +//extern int dasparse(void *arg); // defined in das.tab.c +extern int dasparse(libdap::parser_arg *arg); // defined in das.tab.c + +namespace libdap { + +void DAS::duplicate(const DAS &src) +{ + // If the container field is set, perform a deep copy + if (src.d_container) + d_container = new AttrTable(*src.d_container); + else + d_container = 0; + + d_container_name = src.d_container_name; + d_attrs = src.d_attrs; +} + +DAS &DAS::operator=(const DAS &rhs) +{ + if (this == &rhs) + return *this; + + duplicate(rhs); + + return *this; +} + +/** @brief Sets the name of the current attribute container when multiple + * files used to build this DAS. + * + * @param cn container name + */ +void DAS::container_name(const string &cn) +{ + // We want to find a top level attribute table with the given name. So + // set d_container to null first so that we aren't searching some + // previous container + if (cn != d_container_name) { + d_container = 0; + if (!cn.empty()) { + d_container = get_table(cn); + if (!d_container) { + d_container = add_table(cn, new AttrTable); + } + } + d_container_name = cn; + } +} + +/** @brief Returns the number of attributes in the current attribute table + * + * If the there is a container set, then return the number of variable + * attribute tables for the current container. If not set then return the + * number of current attribute tables in the outermost attribute table. + */ +unsigned int DAS::get_size() const +{ + if (d_container) { + return d_container->get_size(); + } + return d_attrs.get_size(); +} + +/** @brief erase all attributes in this DAS + */ +void DAS::erase() +{ + if (d_container) { + d_container->erase(); + } + else { + d_attrs.erase(); + } +} + +/** @brief Returns a reference to the attribute table for the first variable. + */ +AttrTable::Attr_iter DAS::var_begin() +{ + if (d_container) { + return d_container->attr_begin(); + } + return d_attrs.attr_begin(); +} + +/** Returns a reference to the end of the attribute table. Does not + * point to an attribute table. + */ +AttrTable::Attr_iter DAS::var_end() +{ + if (d_container) { + return d_container->attr_end(); + } + return d_attrs.attr_end(); +} + +/** @brief Returns the name of the referenced variable attribute table. + */ +string DAS::get_name(AttrTable::Attr_iter &i) +{ + if (d_container) { + return d_container->get_name(i); + } + return d_attrs.get_name(i); +} + +/** @brief Returns the referenced variable attribute table. + */ +AttrTable * +DAS::get_table(AttrTable::Attr_iter &i) +{ + if (d_container) { + return d_container->get_attr_table(i); + } + return d_attrs.get_attr_table(i); +} + +/** @brief Returns the variable attribute table with the given name. + */ +AttrTable * +DAS::get_table(const string &name) +{ + if (d_container) { + return d_container->get_attr_table(name); + } + return d_attrs.get_attr_table(name); +} + +//@} + +/** @brief Adds an attribute table to the DAS. + @name add_table() +*/ +//@{ + +/** @brief Adds a variable attribute table to the DAS or the current + * dataset container attribute table. + */ +AttrTable * +DAS::add_table( const string &name, AttrTable *at ) +{ + if (d_container) { + at->set_is_global_attribute(false); + return d_container->append_container(at, name); + } + return d_attrs.append_container( at, name ) ; +} + +//@} + +/** @brief Reads a DAS in from an external source. + + @name parse() +*/ +//@{ + + +/** @brief Reads a DAS from the named file. + + Read attributes from a file. Returns false if unable to open + the file, otherwise returns the result of the mfunc parse. */ +void +DAS::parse(string fname) +{ + FILE *in = fopen(fname.c_str(), "r"); + + if (!in) { + throw Error(cannot_read_file, "Could not open: " + fname); + } + + parse(in); + + int res = fclose(in); + if (res) { + DBG(cerr << "DAS::parse - Failed to close file " << (void *)in << endl ;) ; + } +} + +/** @brief Read attributes from a file descriptor. + + If the file descriptor cannot be fdopen'd, return false, otherwise + return the status of the mfunc parse. + + \note Added call to dup() within fdopen so that once the FILE * is + closed the decriptor fd will not also be closed (instead the + duplicate descriptor will be closed). Thus further information can + be read from the descriptor fd. +*/ +void +DAS::parse(int fd) +{ +#ifdef WIN32 + int new_fd = _dup(fd); +#else + int new_fd = dup(fd); +#endif + + if (new_fd < 0) + throw InternalErr(__FILE__, __LINE__, "Could not access file."); + FILE *in = fdopen(new_fd, "r"); + + if (!in) { + throw InternalErr(__FILE__, __LINE__, "Could not access file."); + } + + parse(in); + + int res = fclose(in); + if (res) { + DBG(cerr << "DAS::parse(fd) - Failed to close " << (void *)in << endl ;) ; + } +} + + + +/** @brief Reads a DAS from an open file descriptor. + + Read attributes from in (which defaults to stdin). If + dasrestart() fails, return false, otherwise return the status + of dasparse(). +*/ +void +DAS::parse(FILE *in) +{ + if (!in) { + throw InternalErr(__FILE__, __LINE__, "Null input stream."); + } + + void *buffer = das_buffer(in); + das_switch_to_buffer(buffer); + + parser_arg arg(this); + + //bool status = dasparse((void *) & arg) == 0; + bool status = dasparse(&arg) == 0; + + das_delete_buffer(buffer); + + // STATUS is the result of the parser function; if a recoverable error + // was found it will be true but arg.status() will be false. + if (!status || !arg.status()) {// Check parse result + if (arg.error()) + throw *arg.error(); + } +} + +//@} + +/** Creates an ASCII representation of a DAS on the given output + stream. + + When an identifier contains a character that contains + characters that cannot be present in a URL (e.g., a space) + AttrTable::print replaces those characters with WWW + escape codes. 7/13/2001 jhrg + + @param out output FILE on which to print the DAS + @param dereference If true, follow aliases. Default is false. +*/ + +void +DAS::print(FILE *out, bool dereference) +{ + fprintf(out, "Attributes {\n") ; + + d_attrs.print(out, " ", dereference); + + fprintf(out, "}\n") ; +} + +/** Creates an ASCII representation of a DAS on the given output + stream. + + When an identifier contains a character that contains + characters that cannot be present in a URL (e.g., a space) + AttrTable::print replaces those characters with WWW + escape codes. 7/13/2001 jhrg + + @param out output ostream on which to print the DAS + @param dereference If true, follow aliases. Default is false. +*/ + +void +DAS::print(ostream &out, bool dereference) +{ + out << "Attributes {\n" ; + + d_attrs.print(out, " ", dereference); + + out << "}\n" ; +} + +/** @brief dumps information about this object + * + * Displays the pointer value of this instance and then calls parent dump + * + * @param strm C++ i/o stream to dump the information to + * @return void + */ +void DAS::dump(ostream &strm) const +{ + strm << DapIndent::LMarg << "DAS::dump - (" << (void *) this << ")" << endl; + DapIndent::Indent(); + if (d_container) { + strm << DapIndent::LMarg << "current container: " << d_container_name << endl; + } + else { + strm << DapIndent::LMarg << "current container: NONE" << endl; + } + d_attrs.dump(strm); + DapIndent::UnIndent(); +} + +} // namespace libdap + diff --git a/DAS.h b/DAS.h new file mode 100644 index 0000000..7a5c5f3 --- /dev/null +++ b/DAS.h @@ -0,0 +1,200 @@ + +// -*- mode: c++; c-basic-offset:4 -*- + +// This file is part of libdap, A C++ implementation of the OPeNDAP Data +// Access Protocol. + +// Copyright (c) 2002,2003 OPeNDAP, Inc. +// Author: James Gallagher +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// +// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112. + +// (c) COPYRIGHT URI/MIT 1994-1999 +// Please read the full copyright statement in the file COPYRIGHT_URI. +// +// Authors: +// jhrg,jimg James Gallagher + +// Using the DASVHMap class, build a parser for the DAS and add functions +// that provide access to the variables, their attributes and values. +// +// jhrg 7/25/94 + +#ifndef _das_h +#define _das_h 1 + + +#include +#include +#include + +#ifndef _attrtable_h +#include "AttrTable.h" +#endif + +using std::cout; + +namespace libdap +{ + +/** @brief Hold attribute data for a DAP2 dataset. + + The Data Attribute Structure is a set of name-value pairs used to + describe the data in a particular dataset. The name-value pairs are + called the ``attributes''. The values may be of any of the DAP2 simple + data types (Byte, Int16, UInt16, Int32, UInt32, Float32, Float64, String + and URL), and may be scalar or vector. Note that all values are actually + stored as String data, making the easy to read/check using a web browser. + + A value may also consist of a set of other name-value pairs. This + makes it possible to nest collections of attributes, giving rise + to a hierarchy of attributes. DAP2 uses this structure to provide + information about variables in a dataset. For example, consider + the dataset used in the DDS example earlier. + + In the following example of a DAS, several of the attribute + collections have names corresponding to the names of variables in + the DDS example. The attributes in that collection are said to + belong to that variable. For example, the lat variable has an + attribute ``units'' of ``degrees_north''. + +
+    Attributes {
+        GLOBAL {
+            String title "Reynolds Optimum Interpolation (OI) SST";
+        }
+        lat {
+            String units "degrees_north";
+            String long_name "Latitude";
+            Float64 actual_range 89.5, -89.5;
+        }
+        lon {
+            String units "degrees_east";
+            String long_name "Longitude";
+            Float64 actual_range 0.5, 359.5;
+        }
+        time {
+            String units "days since 1-1-1 00:00:00";
+            String long_name "Time";
+            Float64 actual_range 726468., 729289.;
+            String delta_t "0000-00-07 00:00:00";
+        }
+        sst {
+            String long_name "Weekly Means of Sea Surface Temperature";
+            Float64 actual_range -1.8, 35.09;
+            String units "degC";
+            Float64 add_offset 0.;
+            Float64 scale_factor 0.0099999998;
+            Int32 missing_value 32767;
+        }
+    }
+    
+ + Attributes may have arbitrary names, although in most datasets it + is important to choose these names so a reader will know what they + describe. In the above example, the ``GLOBAL'' attribute provides + information about the entire dataset. + + Data attribute information is an important part of the the data + provided to a DAP2 client by a server, and the DAS is how this + data is packaged for sending (and how it is received). + + The DAS class is simply a sequence of attribute tables and names. + It may be thought of as the top level of the attribute hierarchy. + + @see DDS + @see AttrTable */ +class DAS : public DapObj +{ +private: + // The DAS support the notion of a current attribute table for a given + // container. Containers are used by the BES to support datasets that + // are built using several files (but not exactly the same way as + // NCML builds them. + AttrTable *d_container ; + string d_container_name ; + + // A DAS is a shell around an attribute table. Since tables can be nested, + // there is one top-level table and the attribute tables for individual + // variables are its children. + AttrTable d_attrs ; + + void duplicate(const DAS &src); + +public: + DAS() : DapObj(), d_container( 0 ) { } + DAS(const DAS &das) { duplicate(das); } + + virtual ~DAS() { } + + DAS & operator=(const DAS &rhs); + + /** @brief Returns the name of the current attribute container when multiple + * files used to build this DAS + */ + virtual string container_name() const {return d_container_name; } + + virtual void container_name( const string &cn ) ; + + /** @brief Returns the current attribute container when multiple files + * used to build this DAS. + * + * @return current attribute table for current container + */ + virtual AttrTable *container() { return d_container; } + + /** @brief Returns the top most set of attributes + * + * This could be the top most variable attribute tables, or it could be + * the top most dataset container attribute tables, if we have multiple + * datasets being used to construct this DAS + */ + virtual AttrTable *get_top_level_attributes() { + if (d_container) + return d_container; + return &d_attrs; + } + + virtual void erase() ; + + virtual unsigned int get_size() const ; + + AttrTable::Attr_iter var_begin() ; + AttrTable::Attr_iter var_end() ; + + string get_name(AttrTable::Attr_iter &i); + AttrTable *get_table(AttrTable::Attr_iter &i); + + virtual AttrTable *get_table(const string &name); + + virtual AttrTable *add_table(const string &name, AttrTable *at); + + /** Read a DAS by parsing the specified file*/ + virtual void parse(string fname); + virtual void parse(int fd); + virtual void parse(FILE *in = stdin); + + /** Print the DAS */ + virtual void print(FILE *out, bool dereference = false); + virtual void print(ostream &out, bool dereference = false); + + virtual void dump(ostream &strm) const ; +}; + +} // namespace libdap + +#endif // _das_h diff --git a/DDS.cc b/DDS.cc new file mode 100644 index 0000000..e3607e0 --- /dev/null +++ b/DDS.cc @@ -0,0 +1,1566 @@ +// -*- mode: c++; c-basic-offset:4 -*- + +// This file is part of libdap, A C++ implementation of the OPeNDAP Data +// Access Protocol. + +// Copyright (c) 2002,2003 OPeNDAP, Inc. +// Author: James Gallagher +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// +// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112. + +// (c) COPYRIGHT URI/MIT 1994-1999 +// Please read the full copyright statement in the file COPYRIGHT_URI. +// +// Authors: +// jhrg,jimg James Gallagher + +// +// jhrg 9/7/94 + +#include "config.h" + +#include +#include +#include + +#ifdef WIN32 +#include +#include +#include +#else +#include // for alarm and dup +#include +#endif + +#include +#include +#include +#include + +// #define DODS_DEBUG +//#define DODS_DEBUG2 + +#include "GNURegex.h" + +#include "DAS.h" +#include "Clause.h" +#include "Error.h" +#include "InternalErr.h" +#include "Keywords2.h" + +#include "parser.h" +#include "debug.h" +#include "util.h" + +#include "Byte.h" +#include "Int16.h" +#include "UInt16.h" +#include "Int32.h" +#include "UInt32.h" +#include "Float32.h" +#include "Float64.h" +#include "Str.h" +#include "Url.h" +#include "Array.h" +#include "Structure.h" +#include "Sequence.h" +#include "Grid.h" + +#include "escaping.h" + +/** + * ############################################################################################ + * ############################################################################################ + * ############################################################################################ + * DapXmlNamespaces + * + * FIXME Replace all usages of the following variable with calls to DapXmlNamespaces + * TODO Replace all usages of the following variable with calls to DapXmlNamespaces + * + */ +const string c_xml_xsi = "http://www.w3.org/2001/XMLSchema-instance"; +const string c_xml_namespace = "http://www.w3.org/XML/1998/namespace"; + +const string grddl_transformation_dap32 = "http://xml.opendap.org/transforms/ddxToRdfTriples.xsl"; + +const string c_default_dap20_schema_location = "http://xml.opendap.org/dap/dap2.xsd"; +const string c_default_dap32_schema_location = "http://xml.opendap.org/dap/dap3.2.xsd"; +const string c_default_dap40_schema_location = "http://xml.opendap.org/dap/dap4.0.xsd"; + +const string c_dap20_namespace = "http://xml.opendap.org/ns/DAP2"; +const string c_dap32_namespace = "http://xml.opendap.org/ns/DAP/3.2#"; +const string c_dap40_namespace = "http://xml.opendap.org/ns/DAP/4.0#"; + +const string c_dap_20_n_sl = c_dap20_namespace + " " + c_default_dap20_schema_location; +const string c_dap_32_n_sl = c_dap32_namespace + " " + c_default_dap32_schema_location; +const string c_dap_40_n_sl = c_dap40_namespace + " " + c_default_dap40_schema_location; +/** + * + * DapXmlNamespaces + * ############################################################################################ + * ############################################################################################ + * ############################################################################################ + */ + + + +using namespace std; + +int ddsparse(libdap::parser_arg *arg); + +// Glue for the DDS parser defined in dds.lex +void dds_switch_to_buffer(void *new_buffer); +void dds_delete_buffer(void * buffer); +void *dds_buffer(FILE *fp); + +namespace libdap { + +void +DDS::duplicate(const DDS &dds) +{ + DBG(cerr << "Entering DDS::duplicate... " < vars; // Variables at the top level + + int d_timeout; // alarm time in seconds. If greater than + // zero, raise the alarm signal if more than + // d_timeout seconds are spent reading data. + Keywords d_keywords; // Holds keywords parsed from the CE + + long d_max_response_size; // In bytes +#endif + + d_factory = dds.d_factory; + + d_name = dds.d_name; + d_filename = dds.d_filename; + d_container_name = dds.d_container_name; + d_container = dds.d_container; + + d_dap_major = dds.d_dap_major; + d_dap_minor = dds.d_dap_minor; + + d_dap_version = dds.d_dap_version; // String version of the protocol + d_request_xml_base = dds.d_request_xml_base; + d_namespace = dds.d_namespace; + + d_attr = dds.d_attr; + + DDS &dds_tmp = const_cast(dds); + + // copy the things pointed to by the list, not just the pointers + for (Vars_iter i = dds_tmp.var_begin(); i != dds_tmp.var_end(); i++) { + add_var(*i); // add_var() dups the BaseType. + } + + d_timeout = dds.d_timeout; + + d_keywords = dds.d_keywords; // value copy; Keywords contains no pointers + + d_max_response_size = dds.d_max_response_size; +} + +/** + * Make a DDS which uses the given BaseTypeFactory to create variables. + * + * @note The default DAP version is 3.2 - this is really DAP2 with a handful + * of enhancements that our WCS software relies on. + * + * @param factory The BaseTypeFactory to use when creating instances of + * DAP variables. The caller must ensure the factory's lifetime is at least + * that of the DDS instance. + * @param name The name of the DDS - usually derived from the name of the + * pathname or table name of the dataset. + */ +DDS::DDS(BaseTypeFactory *factory, const string &name) + : d_factory(factory), d_name(name), d_container_name(""), d_container(0), + d_request_xml_base(""), + d_timeout(0), d_keywords(), d_max_response_size(0) +{ + DBG(cerr << "Building a DDS for the default version (2.0)" << endl); + + // This method sets a number of values, including those returned by + // get_protocol_major(), ..., get_namespace(). + set_dap_version("2.0"); +} + +/** + * Make a DDS with the DAP protocol set to a specific value. This method + * provides an easy way to build DDS objects for use in a server or client + * that will process DAP4, for example. It's roughly equivalent to calling + * set_dap_version() after making an instance using + * DDS::DDS(BaseTypeFactory *, const string &). + * + * @param factory The BaseTypeFactory to use when creating instances of + * DAP variables. The caller must ensure the factory's lifetime is at least + * that of the DDS instance. + * @param name The name of the DDS - usually derived from the name of the + * pathname or table name of the dataset. + * @param version The DAP version to support. This sets the DAP version, as + * well as a number of other dependent constants. + */ +DDS::DDS(BaseTypeFactory *factory, const string &name, const string &version) + : d_factory(factory), d_name(name), d_container_name(""), d_container(0), + d_request_xml_base(""), + d_timeout(0), d_keywords(), d_max_response_size(0) +{ + DBG(cerr << "Building a DDS for version: " << version << endl); + + // This method sets a number of values, including those returned by + // get_protocol_major(), ..., get_namespace(). + set_dap_version(version); +} + +/** The DDS copy constructor. */ +DDS::DDS(const DDS &rhs) : DapObj() +{ + DBG(cerr << "Entering DDS(const DDS &rhs) ..." << endl); + duplicate(rhs); + DBG(cerr << " bye." << endl); +} + +DDS::~DDS() +{ + // delete all the variables in this DDS + for (Vars_iter i = vars.begin(); i != vars.end(); i++) { + BaseType *btp = *i ; + delete btp ; btp = 0; + } +} + +DDS & +DDS::operator=(const DDS &rhs) +{ + DBG(cerr << "Entering DDS::operator= ..." << endl); + if (this == &rhs) + return *this; + + duplicate(rhs); + + DBG(cerr << " bye." << endl); + return *this; +} + +/** + * This is the main method used to transfer attributes from a DAS object into a + * DDS. This uses the BaseType::transfer_attributes() method and the various + * implementations found here (in the constructors classes) and in handlers. + * + * This method uses a deep copy to transfer the attributes, so it is safe to + * delete the source DAS object passed to this method once it is done. + * + * @note To accommodate oddly built DAS objects produced by various handlers, + * specialize the methods there. + * + * @param das Transfer (copy) attributes from this DAS object. + */ +void DDS::transfer_attributes(DAS *das) +{ + // If there is a container set in the DDS then check the container from + // the DAS. If they are not the same container, then throw an exception + // (should be working on the same container). If the container does not + // exist in the DAS, then throw an exception + if (d_container && das->container_name() != d_container_name) + throw InternalErr(__FILE__, __LINE__, + "Error transferring attributes: working on a container in dds, but not das"); + + // Give each variable a chance to claim its attributes. + AttrTable *top = das->get_top_level_attributes(); + + for (DDS::Vars_iter i = var_begin(), e = var_end(); i != e; i++) { + (*i)->transfer_attributes(top); + } +#if 0 + Vars_iter var = var_begin(); + while (var != var_end()) { + try { + DBG(cerr << "Processing the attributes for: " << (*var)->d_name() << " a " << (*var)->type_name() << endl); + (*var)->transfer_attributes(top); + var++; + } + catch (Error &e) { + DBG(cerr << "Got this exception: " << e.get_error_message() << endl); + var++; + throw e; + } + } +#endif + // Now we transfer all of the attributes still marked as global to the + // global container in the DDS. + for (AttrTable::Attr_iter i = top->attr_begin(), e = top->attr_end(); i != e; ++i) { + if ((*i)->type == Attr_container && (*i)->attributes->is_global_attribute()) { + // copy the source container so that the DAS passed in can be + // deleted after calling this method. + AttrTable *at = new AttrTable(*(*i)->attributes); + d_attr.append_container(at, at->get_name()); + } + } +#if 0 + AttrTable::Attr_iter at_cont_p = top_level->attr_begin(); + while (at_cont_p != top_level->attr_end()) { + // In truth, all of the top level attributes should be containers, but + // this test handles the abnormal case where somehow someone makes a + // top level attribute that is not a container by silently dropping it. + if ((*at_cont_p)->type == Attr_container && (*at_cont_p)->attributes->is_global_attribute()) { + DBG(cerr << (*at_cont_p)->d_name << " is a global attribute." << endl); + // copy the source container so that the DAS passed in can be + // deleted after calling this method. + AttrTable *at = new AttrTable(*(*at_cont_p)->attributes); + d_attr.append_container(at, at->get_name()); + } + + at_cont_p++; + } +#endif +} + +/** Get and set the dataset's d_name. This is the d_name of the dataset + itself, and is not to be confused with the d_name of the file or + disk on which it is stored. + + @d_name Dataset Name Accessors */ + +//@{ + +/** Returns the dataset's d_name. */ +string +DDS::get_dataset_name() const +{ + return d_name; +} + +/** Sets the dataset d_name. */ +void +DDS::set_dataset_name(const string &n) +{ + d_name = n; +} + +//@} + +/** Get the attribute table for the global attributes. */ +AttrTable & +DDS::get_attr_table() +{ + return d_attr; +} + +/** Get and set the dataset's filename. This is the physical + location on a disk where the dataset exists. The dataset d_name + is simply a title. + + @d_name File Name Accessor + @see Dataset Name Accessors */ + +//@{ +/** Gets the dataset file d_name. */ +string +DDS::filename() const +{ + return d_filename; +} + +/** Set the dataset's filename. */ +void +DDS::filename(const string &fn) +{ + d_filename = fn; +} +//@} + +/** + * @deprecated + */ +void +DDS::set_dap_major(int p) +{ + d_dap_major = p; + + // This works because regardless of the order set_dap_major and set_dap_minor + // are called, once they both are called, the value in the string is + // correct. I protect against negative numbers because that would be + // nonsensical. + if (d_dap_minor >= 0) { + ostringstream oss; + oss << d_dap_major << "." << d_dap_minor; + d_dap_version = oss.str(); + } +} + +/** + * @deprecated + */ +void +DDS::set_dap_minor(int p) +{ + d_dap_minor = p; + + if (d_dap_major >= 0) { + ostringstream oss; + oss << d_dap_major << "." << d_dap_minor; + d_dap_version = oss.str(); + } +} + +/** + * Given the DAP protocol version, parse that string and set the DDS fields. + * + * @param v The version string. + */ +void +DDS::set_dap_version(const string &v /* = "2.0" */) +{ + istringstream iss(v); + + int major = -1, minor = -1; + char dot; + if (!iss.eof() && !iss.fail()) + iss >> major; + if (!iss.eof() && !iss.fail()) + iss >> dot; + if (!iss.eof() && !iss.fail()) + iss >> minor; + + if (major == -1 || minor == -1 or dot != '.') + throw InternalErr(__FILE__, __LINE__, "Could not parse dap version. Value given: " + v); + + d_dap_version = v; + + d_dap_major = major; + d_dap_minor = minor; + + // Now set the related XML constants. These might be overwritten if + // the DDS instance is being built from a document parse, but if it's + // being constructed by a server the code to generate the XML document + // needs these values to match the DAP version information. + switch (d_dap_major) { + case 2: + d_namespace = c_dap20_namespace; + break; + case 3: + d_namespace = c_dap32_namespace; + break; + case 4: + d_namespace = c_dap40_namespace; + break; + default: + throw InternalErr(__FILE__, __LINE__, "Unknown DAP version."); + } +} + +/** Old way to set the DAP version. + * + * @note Don't use this - two interfaces to set the version number is overkill + * + * @param d The protocol version requested by the client, as a double. + * @deprecated + */ +void +DDS::set_dap_version(double d) +{ + int major = floor(d); + int minor = (d-major)*10; + + DBG(cerr << "Major: " << major << ", Minor: " << minor << endl); + + ostringstream oss; + oss << major << "." << minor; + + set_dap_version(oss.str()); +} + +/** Get and set the current container. If there are multiple files being + used to build this DDS, using a container will set a virtual structure + for the current container. + + @d_name Container Name Accessor + @see Dataset Name Accessors */ + +//@{ +/** Gets the dataset file d_name. */ +string +DDS::container_name() +{ + return d_container_name; +} + +/** Set the current container d_name and get or create a structure for that + * d_name. */ +void +DDS::container_name(const string &cn) +{ + // we want to search the DDS for the top level structure with the given + // d_name. Set the container to null so that we don't search some previous + // container. + d_container = 0 ; + if( !cn.empty() ) + { + d_container = dynamic_cast( var( cn ) ) ; + if( !d_container ) + { + // create a structure for this container. Calling add_var + // while_container is null will add the new structure to DDS and + // not some sub structure. Adding the new structure makes a copy + // of it. So after adding it, go get it and set d_container. + Structure *s = new Structure( cn ) ; + add_var( s ) ; + delete s ; + s = 0 ; + d_container = dynamic_cast( var( cn ) ) ; + } + } + d_container_name = cn; + +} + +/** Get the current container structure. */ +Structure * +DDS::container() +{ + return d_container ; +} + +//@} + +/** Get the size of a response. This method looks at the variables in the DDS + * a computes the number of bytes in the response. + * + * @note This version of the method does a poor job with Sequences. A better + * implementation would look at row-constraint-based limitations and use them + * for size computations. If a row-constraint is missing, return an error. + * + * @param constrained Should the size of the whole DDS be used or should the + * current constraint be taken into account? + */ +int +DDS::get_request_size(bool constrained) +{ + int w = 0; + for (Vars_iter i = vars.begin(); i != vars.end(); i++) { + if (constrained) { + if ((*i)->send_p()) + w += (*i)->width(constrained); + } + else { + w += (*i)->width(constrained); + } + } + + return w; +} + +/** @brief Adds a copy of the variable to the DDS. + Using the ptr_duplicate() method, perform a deep copy on the variable + \e bt and adds the result to this DDS. + @note The copy will not copy data values. + @param bt Source variable. */ +void DDS::add_var(BaseType *bt) { + if (!bt) + throw InternalErr(__FILE__, __LINE__, "Trying to add a BaseType object with a NULL pointer."); +#if 0 + if (bt->is_dap4_only_type()) + throw InternalErr(__FILE__, __LINE__, "Attempt to add a DAP4 type to a DAP2 DDS."); +#endif + DBG2(cerr << "In DDS::add_var(), bt's address is: " << bt << endl); + + BaseType *btp = bt->ptr_duplicate(); + DBG2(cerr << "In DDS::add_var(), btp's address is: " << btp << endl); + if (d_container) { + // Mem leak fix [mjohnson nov 2009] + // Structure::add_var() creates ANOTHER copy. + d_container->add_var(bt); + // So we need to delete btp or else it leaks + delete btp; + btp = 0; + } + else { + vars.push_back(btp); + } +} + +/** @brief Adds the variable to the DDS. + @param bt Source variable. */ +void +DDS::add_var_nocopy(BaseType *bt) +{ + if (!bt) + throw InternalErr(__FILE__, __LINE__, "Trying to add a BaseType object with a NULL pointer."); +#if 0 + //FIXME There's no longer a DAP2 and DAP4 DDS + if (bt->is_dap4_only_type()) + throw InternalErr(__FILE__, __LINE__, "Attempt to add a DAP4 type to a DAP2 DDS."); +#endif + + DBG2(cerr << "In DDS::add_var(), bt's address is: " << bt << endl); + + if (d_container) { + d_container->add_var_nocopy(bt); + } + else { + vars.push_back(bt); + } +} + + +/** Remove the named variable from the DDS. This method is not smart about + looking up names. The variable must exist at the top level of the DDS and + must match \e exactly the d_name given. + + @note Invalidates any iterators that reference the contents of the DDS. + @param n The d_name of the variable to remove. */ +void +DDS::del_var(const string &n) +{ + if( d_container ) + { + d_container->del_var( n ) ; + return ; + } + + for (Vars_iter i = vars.begin(); i != vars.end(); i++) { + if ((*i)->name() == n) { + BaseType *bt = *i ; + vars.erase(i) ; + delete bt ; bt = 0; + return; + } + } +} + +/** Remove the variable referenced by the iterator and free its storage. + + @note Invalidates any iterators that reference the contents of the DDS. + @param i The Vars_iter which refers to the variable. */ +void +DDS::del_var(Vars_iter i) +{ + if (i != vars.end()) { + BaseType *bt = *i ; + vars.erase(i) ; + delete bt ; bt = 0; + } +} + +/** Remove the variables referenced by the range of iterators and free their + storage. + + @note Invalidates any iterators that reference the contents of the DDS. + @param i1 The start of the range. + @param i2 The end of the range. */ +void +DDS::del_var(Vars_iter i1, Vars_iter i2) +{ + for (Vars_iter i_tmp = i1; i_tmp != i2; i_tmp++) { + BaseType *bt = *i_tmp ; + delete bt ; bt = 0; + } + vars.erase(i1, i2) ; +} + +/** Search for for variable n as above but record all + compound type variables which ultimately contain n on + s. This stack can then be used to mark the contained + compound-type variables as part of the current projection. + + @return A BaseType pointer to the variable n or 0 if n + could not be found. */ +BaseType * +DDS::var(const string &n, BaseType::btp_stack &s) +{ + return var(n, &s); +} +/** @brief Find the variable with the given d_name. + + Returns a pointer to the named variable. If the d_name contains one or + more field separators then the function looks for a variable whose + name matches exactly. If the d_name contains no field separators then + the function looks first in the top level and then in all subsequent + levels and returns the first occurrence found. In general, this + function searches constructor types in the order in which they appear + in the DDS, but there is no requirement that it do so. + + @note If a dataset contains two constructor types which have field names + that are the same (say point.x and pair.x) you should use fully qualified + names to get each of those variables. + + @param n The name of the variable to find. + @param s If given, this value-result parameter holds the path to the + returned BaseType. Thus, this method can return the FQN for the variable + \e n. + @return A BaseType pointer to the variable or null if not found. */ +BaseType * +DDS::var(const string &n, BaseType::btp_stack *s) +{ + string name = www2id(n); + if( d_container ) + return d_container->var( name, false, s ) ; + + BaseType *v = exact_match(name, s); + if (v) + return v; + + return leaf_match(name, s); +} + +BaseType * +DDS::leaf_match(const string &n, BaseType::btp_stack *s) +{ + DBG(cerr << "DDS::leaf_match: Looking for " << n << endl); + + for (Vars_iter i = vars.begin(); i != vars.end(); i++) { + BaseType *btp = *i; + DBG(cerr << "DDS::leaf_match: Looking for " << n << " in: " << btp->name() << endl); + // Look for the d_name in the dataset's top-level + if (btp->name() == n) { + DBG(cerr << "Found " << n << " in: " << btp->name() << endl); + return btp; + } + + if (btp->is_constructor_type()) { + BaseType *found = btp->var(n, false, s); + if (found) { + DBG(cerr << "Found " << n << " in: " << btp->name() << endl); + return found; + } + } +#if STRUCTURE_ARRAY_SYNTAX_OLD + if (btp->is_vector_type() && btp->var()->is_constructor_type()) { + s->push(btp); + BaseType *found = btp->var()->var(n, false, s); + if (found) { + DBG(cerr << "Found " << n << " in: " << btp->var()->d_name() << endl); + return found; + } + } +#endif + } + + return 0; // It is not here. +} + +BaseType * +DDS::exact_match(const string &name, BaseType::btp_stack *s) +{ + for (Vars_iter i = vars.begin(); i != vars.end(); i++) { + BaseType *btp = *i; + DBG2(cerr << "Looking for " << d_name << " in: " << btp << endl); + // Look for the d_name in the current ctor type or the top level + if (btp->name() == name) { + DBG2(cerr << "Found " << d_name << " in: " << btp << endl); + return btp; + } + } + + string::size_type dot_pos = name.find("."); + if (dot_pos != string::npos) { + string aggregate = name.substr(0, dot_pos); + string field = name.substr(dot_pos + 1); + + BaseType *agg_ptr = var(aggregate, s); + if (agg_ptr) { + DBG2(cerr << "Descending into " << agg_ptr->name() << endl); + return agg_ptr->var(field, true, s); + } + else + return 0; // qualified names must be *fully* qualified + } + + return 0; // It is not here. +} + + +/** @brief Returns the first variable in the DDS. */ + +DDS::Vars_iter +DDS::var_begin() +{ + return vars.begin(); +} + +DDS::Vars_riter +DDS::var_rbegin() +{ + return vars.rbegin(); +} + +DDS::Vars_iter +DDS::var_end() +{ + return vars.end() ; +} + +DDS::Vars_riter +DDS::var_rend() +{ + return vars.rend() ; +} + +/** Return the iterator for the \e ith variable. + @param i the index + @return The corresponding Vars_iter */ +DDS::Vars_iter +DDS::get_vars_iter(int i) +{ + return vars.begin() + i; +} + +/** Return the \e ith variable. + @param i the index + @return The corresponding variable */ +BaseType * +DDS::get_var_index(int i) +{ + return *(vars.begin() + i); +} + +/** Insert a copy of the BaseType before the position given. + * @param i The iterator that marks the position + * @param ptr The BaseType object to copy and insert + */ +void +DDS::insert_var(Vars_iter i, BaseType *ptr) +{ +#if 0 + if (ptr->is_dap4_only_type()) + throw InternalErr(__FILE__, __LINE__, "Attempt to add a DAP4 type to a DAP2 DDS."); +#endif + vars.insert(i, ptr->ptr_duplicate()); +} + +/** Insert the BaseType before the position given. + * @note Does not copy the BaseType object - that caller must not + * free the inserted object's pointer. This object will, however, + * delete the pointer when it is deleted. + * @param i The iterator that marks the position + * @param ptr The BaseType object to insert + */ +void +DDS::insert_var_nocopy(Vars_iter i, BaseType *ptr) +{ +#if 0 + if (ptr->is_dap4_only_type()) + throw InternalErr(__FILE__, __LINE__, "Attempt to add a DAP4 type to a DAP2 DDS."); +#endif + vars.insert(i, ptr); +} + +/** @brief Returns the number of variables in the DDS. */ +int +DDS::num_var() +{ + return vars.size(); +} + +void +DDS::timeout_on() +{ +#if USE_LOCAL_TIMEOUT_SCHEME +#ifndef WIN32 + alarm(d_timeout); +#endif +#endif +} + +void +DDS::timeout_off() +{ +#if USE_LOCAL_TIMEOUT_SCHEME +#ifndef WIN32 + // Old behavior commented out. I think it is an error to change the value + // of d_timeout. The way this will likely be used is to set the timeout + // value once and then 'turn on' or turn off' that timeout as the situation + // dictates. The initeded use for the DDS timeout is so that timeouts for + // data responses will include the CPU resources needed to build the response + // but not the time spent transmitting the response. This may change when + // more parallelism is added to the server... These methods are called from + // BESDapResponseBuilder in bes/dap. jhrg 12/22/15 + + // d_timeout = alarm(0); + + alarm(0); +#endif +#endif +} + +void +DDS::set_timeout(int) +{ +#if USE_LOCAL_TIMEOUT_SCHEME + // Has no effect under win32 + d_timeout = t; +#endif +} + +int +DDS::get_timeout() +{ +#if USE_LOCAL_TIMEOUT_SCHEME + // Has to effect under win32 + return d_timeout; +#endif + return 0; +} + +/** @brief Traverse DDS, set Sequence leaf nodes. */ +void +DDS::tag_nested_sequences() +{ + for (Vars_iter i = vars.begin(); i != vars.end(); i++) { + if ((*i)->type() == dods_sequence_c) + dynamic_cast(**i).set_leaf_sequence(); + else if ((*i)->type() == dods_structure_c) + dynamic_cast(**i).set_leaf_sequence(); + } +} + +/** @brief Parse a DDS from a file with the given d_name. */ +void +DDS::parse(string fname) +{ + FILE *in = fopen(fname.c_str(), "r"); + + if (!in) { + throw Error(cannot_read_file, "Could not open: " + fname); + } + + try { + parse(in); + fclose(in); + } + catch (Error &e) { + fclose(in); + throw ; + } +} + + +/** @brief Parse a DDS from a file indicated by the input file descriptor. */ +void +DDS::parse(int fd) +{ +#ifdef WIN32 + int new_fd = _dup(fd); +#else + int new_fd = dup(fd); +#endif + + if (new_fd < 0) + throw InternalErr(__FILE__, __LINE__, "Could not access file."); + FILE *in = fdopen(new_fd, "r"); + + if (!in) { + throw InternalErr(__FILE__, __LINE__, "Could not access file."); + } + + try { + parse(in); + fclose(in); + } + catch (Error &e) { + fclose(in); + throw ; + } +} + +/** @brief Parse a DDS from a file indicated by the input file descriptor. + Read the persistent representation of a DDS from the FILE *in, parse it + and create a matching binary object. + @param in Read the persistent DDS from this FILE*. + @exception InternalErr Thrown if \c in is null + @exception Error Thrown if the parse fails. */ +void +DDS::parse(FILE *in) +{ + if (!in) { + throw InternalErr(__FILE__, __LINE__, "Null input stream."); + } + + void *buffer = dds_buffer(in); + dds_switch_to_buffer(buffer); + + parser_arg arg(this); + + bool status = ddsparse(&arg) == 0; + + dds_delete_buffer(buffer); + + DBG2(cout << "Status from parser: " << status << endl); + + // STATUS is the result of the parser function; if a recoverable error + // was found it will be true but arg.status() will be false. + if (!status || !arg.status()) {// Check parse result + if (arg.error()) + throw *arg.error(); + } +} + +/** @brief Print the entire DDS to the specified file. */ +void +DDS::print(FILE *out) +{ + ostringstream oss; + print(oss); + fwrite(oss.str().data(), sizeof(char), oss.str().length(), out); +} + +/** @brief Print the entire DDS to the specified ostream. */ +void +DDS::print(ostream &out) +{ + out << "Dataset {\n" ; + + for (Vars_citer i = vars.begin(); i != vars.end(); i++) { + (*i)->print_decl(out) ; + } + + out << "} " << id2www(d_name) << ";\n" ; + + return ; +} + +/** + * Print the DAP2 DAS object using attribute information recorded + * this DDS object. + * + * @note Uses default indenting of four spaces and does not follow + * (now deprecated) attribute aliases. + * + * @param out Write the DAS here. + */ +static string four_spaces = " "; +void print_var_das(ostream &out, BaseType *bt, string indent=""){ + + AttrTable attr_table = bt->get_attr_table(); + out << indent << add_space_encoding(bt->name()) << " {" << endl; + attr_table.print(out, indent+four_spaces); + Constructor *cnstrctr = dynamic_cast < Constructor * >(bt); + if(cnstrctr) { + Constructor::Vars_iter i = cnstrctr->var_begin(); + Constructor::Vars_iter e = cnstrctr->var_end(); + for (; i!=e; i++) { + print_var_das(out,*i,indent+four_spaces); + } + + } + out << indent << "}" << endl; + +} + +void +DDS::print_das(ostream &out) +{ + string indent(" "); + out << "Attributes {" << endl ; + for (Vars_citer i = vars.begin(); i != vars.end(); i++) { + print_var_das(out, *i, four_spaces); + } + // Print the global attributes at the end. + d_attr.print(out,indent); + out << "}" << endl ; +} + +/** @brief Print a constrained DDS to the specified file. + + Print those parts (variables) of the DDS structure to OS that + are marked to be sent after evaluating the constraint + expression. + + @note This function only works for scalars at the top level. + + @returns true. +*/ +void +DDS::print_constrained(FILE *out) +{ + ostringstream oss; + print_constrained(oss); + fwrite(oss.str().data(), sizeof(char), oss.str().length(), out); +} + +/** @brief Print a constrained DDS to the specified ostream. + + Print those parts (variables) of the DDS structure to OS that + are marked to be sent after evaluating the constraint + expression. + + \note This function only works for scalars at the top level. + + @returns true. +*/ +void +DDS::print_constrained(ostream &out) +{ + out << "Dataset {\n" ; + + for (Vars_citer i = vars.begin(); i != vars.end(); i++) { + // for each variable, indent with four spaces, print a trailing + // semicolon, do not print debugging information, print only + // variables in the current projection. + (*i)->print_decl(out, " ", true, false, true) ; + } + + out << "} " << id2www(d_name) << ";\n" ; + + return; +} + +/** Print an XML representation of this DDS. This method is used to generate + the part of the DDX response. The \c Dataset tag is \e not written by + this code. The caller of this method must handle writing that and + including the \c dataBLOB tag. + + @param out Destination. + @param constrained True if the output should be limited to just those + variables that are in the projection of the current constraint + expression. + @param blob The dataBLOB href. + @deprecated */ +void +DDS::print_xml(FILE *out, bool constrained, const string &blob) +{ + ostringstream oss; + print_xml_writer(oss, constrained, blob); + fwrite(oss.str().data(), 1, oss.str().length(), out); +} + +/** Print an XML representation of this DDS. This method is used to generate + the part of the DDX response. The \c Dataset tag is \e not written by + this code. The caller of this method must handle writing that and + including the \c dataBLOB tag. + + @param out Destination ostream. + @param constrained True if the output should be limited to just those + variables that are in the projection of the current constraint + expression. + @param blob The dataBLOB href. + @deprecated */ +void +DDS::print_xml(ostream &out, bool constrained, const string &blob) +{ + print_xml_writer(out, constrained, blob); +} + +class VariablePrintXMLWriter : public unary_function +{ + XMLWriter &d_xml; + bool d_constrained; +public: + VariablePrintXMLWriter(XMLWriter &xml, bool constrained) + : d_xml(xml), d_constrained(constrained) + {} + void operator()(BaseType *bt) + { + bt->print_xml_writer(d_xml, d_constrained); + } +}; + +/** + * Print the DDX. This code uses the libxml2 'TextWriter' interface; something + * that seems to be a good compromise between doing it by hand (although more + * verbose it is also more reliable) and DOM. + * + * @note This code handles several different versions of DAP in a fairly + * crude way. I've broken it up into three different responses: DAP2, DAP3.2 + * and DAP4. + * + * @param out Write the XML to this output sink + * @param constrained True if the only variables to print are those in the + * current projection. If true, this will also suppress printing attributes. + * @param blob This is an href (DAP2) or a cid (DAP3.4 and 4). The href + * points to the binary data; the cid is the M-MIME separator for the binary + * data. + */ +void +DDS::print_xml_writer(ostream &out, bool constrained, const string &blob) +{ + XMLWriter xml(" "); + + // Stamp and repeat for these sections; trying to economize is makes it + // even more confusing + if (get_dap_major() >= 4) { + if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*) "Group") < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write Group element"); + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "name", (const xmlChar*)d_name.c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name"); + + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "dapVersion", (const xmlChar*)get_dap_version().c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute for dapVersion"); + + if (!get_request_xml_base().empty()) { + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "xmlns:xml", (const xmlChar*)c_xml_namespace.c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute for xmlns:xml"); + + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "xml:base", (const xmlChar*)get_request_xml_base().c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute for xml:base"); + } + if (!get_namespace().empty()) { + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "xmlns", (const xmlChar*)get_namespace().c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute for xmlns"); + } + } + else if (get_dap_major() == 3 && get_dap_minor() >= 2) { + if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*) "Dataset") < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write Dataset element"); + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "name", (const xmlChar*)d_name.c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name"); + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "xmlns:xsi", (const xmlChar*)"http://www.w3.org/2001/XMLSchema-instance") < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute for xmlns:xsi"); + + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "xsi:schemaLocation", (const xmlChar*)c_dap_32_n_sl.c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute for xmlns:schemaLocation"); + + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "xmlns:grddl", (const xmlChar*)"http://www.w3.org/2003/g/data-view#") < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute for xmlns:grddl"); + + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "grddl:transformation", (const xmlChar*)grddl_transformation_dap32.c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute for xmlns:transformation"); + + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "xmlns", (const xmlChar*)c_dap32_namespace.c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute for xmlns"); + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "xmlns:dap", (const xmlChar*)c_dap32_namespace.c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute for xmlns:dap"); + + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "dapVersion", (const xmlChar*)"3.2") < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute for dapVersion"); + + if (!get_request_xml_base().empty()) { + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "xmlns:xml", (const xmlChar*)c_xml_namespace.c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute for xmlns:xml"); + + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "xml:base", (const xmlChar*)get_request_xml_base().c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute for xml:base"); + } + } + else { // dap2 + if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*) "Dataset") < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write Dataset element"); + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "name", (const xmlChar*)d_name.c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute for d_name"); + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "xmlns:xsi", (const xmlChar*)"http://www.w3.org/2001/XMLSchema-instance") < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute for xmlns:xsi"); + + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "xmlns", (const xmlChar*)c_dap20_namespace.c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute for xmlns"); + + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "xsi:schemaLocation", (const xmlChar*)c_dap_20_n_sl.c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute for xmlns:schemaLocation"); + } + + // Print the global attributes + d_attr.print_xml_writer(xml); + + // Print each variable + for_each(var_begin(), var_end(), VariablePrintXMLWriter(xml, constrained)); + + // For DAP 3.2 and greater, use the new syntax and value. The 'blob' is + // the CID of the MIME part that holds the data. For DAP2 (which includes + // 3.0 and 3.1), the blob is an href. For DAP4, only write the CID if it's + // given. + if (get_dap_major() >= 4) { + if (!blob.empty()) { + if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*) "blob") < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write blob element"); + string cid = "cid:" + blob; + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "href", (const xmlChar*) cid.c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute for d_name"); + if (xmlTextWriterEndElement(xml.get_writer()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not end blob element"); + } + } + else if (get_dap_major() == 3 && get_dap_minor() >= 2) { + if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*) "blob") < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write blob element"); + string cid = "cid:" + blob; + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "href", (const xmlChar*) cid.c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute for d_name"); + if (xmlTextWriterEndElement(xml.get_writer()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not end blob element"); + } + else { // dap2 + if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*) "dataBLOB") < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write dataBLOB element"); + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "href", (const xmlChar*) "") < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute for d_name"); + if (xmlTextWriterEndElement(xml.get_writer()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not end dataBLOB element"); + } + + if (xmlTextWriterEndElement(xml.get_writer()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not end Dataset element"); + + out << xml.get_doc();// << ends;// << endl; +} + +/** + * Print the DAP4 DMR object. + * This method prints the DMR. If the dap version is not >= 4.0, it's an + * error to call this method. + * + * @note Calling methods that print the DDS or DDX when get_dap_major() + * returns a value >= 4 is undefined. Use this method to get the DAP4 + * metadata response. + * + * @param out Write the XML to this stream + * @param constrained Should the DMR be subject to a constraint? + */ +void +DDS::print_dmr(ostream &out, bool constrained) +{ + if (get_dap_major() < 4) + throw InternalErr(__FILE__, __LINE__, "Tried to print a DMR with DAP major version less than 4"); + + XMLWriter xml(" "); + + // DAP4 wraps a dataset in a top-level Group element. + if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*) "Group") < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write Group element"); + + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "xmlns:xml", + (const xmlChar*) c_xml_namespace.c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute for xmlns:xml"); + + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "xmlns:xsi", (const xmlChar*) c_xml_xsi.c_str()) + < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute for xmlns:xsi"); + + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "xsi:schemaLocation", + (const xmlChar*) c_dap_40_n_sl.c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute for xmlns:schemaLocation"); + + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "xmlns", + (const xmlChar*) get_namespace().c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute for xmlns"); + + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "dapVersion", + (const xmlChar*) get_dap_version().c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute for dapVersion"); + + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "dmrVersion", (const xmlChar*) get_dmr_version().c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute for dapVersion"); + + if (!get_request_xml_base().empty()) { + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "xml:base", + (const xmlChar*) get_request_xml_base().c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute for xml:base"); + } + + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "name", (const xmlChar*) d_name.c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name"); + + // Print the global attributes + d_attr.print_xml_writer(xml); + + // Print each variable + for_each(var_begin(), var_end(), VariablePrintXMLWriter(xml, constrained)); + +#if 0 + // For DAP 3.2 and greater, use the new syntax and value. The 'blob' is + // the CID of the MIME part that holds the data. For DAP2 (which includes + // 3.0 and 3.1), the blob is an href. For DAP4, only write the CID if it's + // given. + if (get_dap_major() >= 4) { + if (!blob.empty()) { + if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*) "blob") < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write blob element"); + string cid = "cid:" + blob; + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "href", (const xmlChar*) cid.c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute for d_name"); + if (xmlTextWriterEndElement(xml.get_writer()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not end blob element"); + } + } +#endif + + if (xmlTextWriterEndElement(xml.get_writer()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not end the top-level Group element"); + + out << xml.get_doc(); +} + +// Used by DDS::send() when returning data from a function call. +/** @brief Check the semantics of each of the variables represented in the + DDS. + + Check the semantics of the DDS describing a complete dataset. If ALL is + true, check not only the semantics of THIS->TABLE, but also recursively + all ctor types in the THIS->TABLE. By default, ALL is false since parsing + a DDS input file runs semantic checks on all variables (but not the + dataset itself. + + @return TRUE if the conventions for the DDS are not violated, FALSE + otherwise. + @param all If true, recursively check the individual members of + compound variables. + @see BaseType::check_semantics */ +bool +DDS::check_semantics(bool all) +{ + // The dataset must have a d_name + if (d_name == "") { + cerr << "A dataset must have a d_name" << endl; + return false; + } + + string msg; + if (!unique_names(vars, d_name, "Dataset", msg)) + return false; + + if (all) + for (Vars_iter i = vars.begin(); i != vars.end(); i++) + if (!(*i)->check_semantics(msg, true)) + return false; + + return true; +} + +/** @brief Mark the send_p flag of the named variable to + state. + + Mark the named variable by setting its SEND_P flag to STATE (true + indicates that it is to be sent). Names must be fully qualified. + + @note For aggregate types this sets each part to STATE when STATE is + True. Thus, if State is True and N is `exp1.test', then both `exp1' and + `test' have their SEND_P flag set to True. If STATE is False, then the + SEND_P flag of the `test' is set to False, but `exp1' is left + unchanged. This means that a single variable can be removed from the + current projection without removing all the other children of its + parent. See the mfunc set_send_p(). + + @return True if the named variable was found, false otherwise. + + @todo This should throw an exception on error!!! + + @todo These methods that use the btp_stack to keep track of the path from + the top of a dataset to a particular variable can be rewritten to use the + parent field instead. + + @todo All the methods that use names to identify variables should have + counterparts that take BaseType pointers. +*/ +bool +DDS::mark(const string &n, bool state) +{ + // TODO use auto_ptr + BaseType::btp_stack *s = new BaseType::btp_stack; + + DBG2(cerr << "DDS::mark: Looking for " << n << endl); + + BaseType *variable = var(n, s); + if (!variable) { + DBG2(cerr << "Could not find variable " << n << endl); + delete s; s = 0; + return false; + } + variable->set_send_p(state); + + DBG2(cerr << "DDS::mark: Set variable " << variable->d_name() + << " (a " << variable->type_name() << ")" << endl); + + // Now check the btp_stack and run BaseType::set_send_p for every + // BaseType pointer on the stack. Using BaseType::set_send_p() will + // set the property for a Constructor but not its contained variables + // which preserves the semantics of projecting just one field. + while (!s->empty()) { + s->top()->BaseType::set_send_p(state); + + DBG2(cerr << "DDS::mark: Set variable " << s->top()->d_name() + << " (a " << s->top()->type_name() << ")" << endl); + // FIXME get_parent() hosed? +#if 1 + string parent_name = (s->top()->get_parent()) ? s->top()->get_parent()->name(): "none"; + string parent_type = (s->top()->get_parent()) ? s->top()->get_parent()->type_name(): "none"; + DBG2(cerr << "DDS::mark: Parent variable " << parent_name << " (a " << parent_type << ")" << endl); +#endif + s->pop(); + } + + delete s ; s = 0; + + return true; +} + +/** Mark the member variable send_p flags to + state. + + @return Void +*/ +void +DDS::mark_all(bool state) +{ + for (Vars_iter i = vars.begin(); i != vars.end(); i++) + (*i)->set_send_p(state); +} + +/** @brief dumps information about this object + * + * Displays the pointer value of this instance and then calls parent dump + * + * @param strm C++ i/o stream to dump the information to + * @return void + */ +void +DDS::dump(ostream &strm) const +{ + strm << DapIndent::LMarg << "DDS::dump - (" + << (void *)this << ")" << endl ; + DapIndent::Indent() ; + strm << DapIndent::LMarg << "d_name: " << d_name << endl ; + strm << DapIndent::LMarg << "filename: " << d_filename << endl ; + strm << DapIndent::LMarg << "protocol major: " << d_dap_major << endl; + strm << DapIndent::LMarg << "protocol minor: " << d_dap_minor << endl; + strm << DapIndent::LMarg << "factory: " << (void *)d_factory << endl ; + + strm << DapIndent::LMarg << "global attributes:" << endl ; + DapIndent::Indent() ; + d_attr.dump(strm) ; + DapIndent::UnIndent() ; + + if (vars.size()) { + strm << DapIndent::LMarg << "vars:" << endl ; + DapIndent::Indent() ; + Vars_citer i = vars.begin() ; + Vars_citer ie = vars.end() ; + for (; i != ie; i++) { + (*i)->dump(strm) ; + } + DapIndent::UnIndent() ; + } + else { + strm << DapIndent::LMarg << "vars: none" << endl ; + } + + DapIndent::UnIndent() ; +} + +} // namespace libdap diff --git a/DDS.h b/DDS.h new file mode 100644 index 0000000..340f2b4 --- /dev/null +++ b/DDS.h @@ -0,0 +1,388 @@ +// -*- mode: c++; c-basic-offset:4 -*- + +// This file is part of libdap, A C++ implementation of the OPeNDAP Data +// Access Protocol. + +// Copyright (c) 2002,2003 OPeNDAP, Inc. +// Author: James Gallagher +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// +// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112. + +// (c) COPYRIGHT URI/MIT 1994-1999 +// Please read the full copyright statement in the file COPYRIGHT_URI. +// +// Authors: +// jhrg,jimg James Gallagher + +// Provide access to the DDS. This class is used to parse DDS text files, to +// produce a printed representation of the in-memory variable table, and to +// update the table on a per-variable basis. +// +// jhrg 9/8/94 + +#ifndef _dds_h +#define _dds_h 1 + +#include +#include +#include +#include + +#ifndef _basetype_h +#include "BaseType.h" +#endif + +#ifndef _constructor_h +#include "Constructor.h" +#endif + +#ifndef base_type_factory_h +#include "BaseTypeFactory.h" +#endif + +#ifndef _das_h +#include "DAS.h" +#endif + +#ifndef A_DapObj_h +#include "DapObj.h" +#endif + +#ifndef KEYWORDS_H_ +#include "Keywords2.h" +#endif + +#ifndef XMLWRITER_H_ +#include "XMLWriter.h" +#endif + +using std::cout; + +namespace libdap +{ + +/** The DAP2 Data Descriptor Object (DDS) is a data structure used by + the DAP2 software to describe datasets and subsets of those + datasets. The DDS may be thought of as the declarations for the + data structures that will hold data requested by some DAP2 client. + Part of the job of a DAP2 server is to build a suitable DDS for a + specific dataset and to send it to the client. Depending on the + data access API in use, this may involve reading part of the + dataset and inferring the DDS. Other APIs may require the server + simply to read some ancillary data file with the DDS in it. + + On the server side, in addition to the data declarations, the DDS + holds the clauses of any constraint expression that may have + accompanied the data request from the DAP2 client. The DDS object + includes methods for modifying the DDS according to the given + constraint expression. It also has methods for directly modifying + a DDS, and for transmitting it from a server to a client. + + For the client, the DDS object includes methods for reading the + persistent form of the object sent from a server. This includes parsing + the ASCII representation of the object and, possibly, reading data + received from a server into a data object. + + Note that the class DDS is used to instantiate both DDS and DataDDS + objects. A DDS that is empty (contains no actual data) is used by servers + to send structural information to the client. The same DDS can becomes a + DataDDS when data values are bound to the variables it defines. + + For a complete description of the DDS layout and protocol, please + refer to The OPeNDAP User Guide. + + The DDS has an ASCII representation, which is what is transmitted + from a DAP2 server to a client. Here is the DDS representation of + an entire dataset containing a time series of worldwide grids of + sea surface temperatures: + +
+    Dataset {
+        Float64 lat[lat = 180];
+        Float64 lon[lon = 360];
+        Float64 time[time = 404];
+        Grid {
+         ARRAY:
+            Int32 sst[time = 404][lat = 180][lon = 360];
+         MAPS:
+            Float64 time[time = 404];
+            Float64 lat[lat = 180];
+            Float64 lon[lon = 360];
+        } sst;
+    } weekly;
+    
+ + If the data request to this dataset includes a constraint + expression, the corresponding DDS might be different. For + example, if the request was only for northern hemisphere data + at a specific time, the above DDS might be modified to appear like + this: + +
+    Dataset {
+        Grid {
+         ARRAY:
+            Int32 sst[time = 1][lat = 90][lon = 360];
+         MAPS:
+            Float64 time[time = 1];
+            Float64 lat[lat = 90];
+            Float64 lon[lon = 360];
+        } sst;
+    } weekly;
+    
+ + Since the constraint has narrowed the area of interest, the range + of latitude values has been halved, and there is only one time + value in the returned array. Note that the simple arrays (lat, + lon, and time) described in the dataset are also + part of the sst Grid object. They can be requested by + themselves or as part of that larger object. + + See the The OPeNDAP User Guide, or the documentation of the + BaseType class for descriptions of the DAP2 data types. + + @note Make sure to pass a valid pointer to the DDS constructor or use + the set_factory() method before actually using the DDS. Also make sure + that the Factory's lifetime thereafter is the same as the DDS's. Never + delete the factory until you're done using the DDS. + + @note Update: I removed the DEFAULT_BASETYPE_FACTORY switch because it + caused more confusion than it avoided. See Trac #130. jhrg + + @note The compile-time symbol DEFAULT_BASETYPE_FACTORY controls whether + the old (3.4 and earlier) DDS and DataDDS constructors are supported. + These constructors now use a default factory class (BaseTypeFactory, + implemented by this library) to instantiate Byte, ..., Grid variables. To + use the default ctor in your code you must also define this symbol. If + you \e do choose to define this and fail to provide a specialization of + BaseTypeFactory when your software needs one, you code may not link or + may fail at run time. In addition to the older ctors for DDS and DataDDS, + defining the symbol also makes some of the older methods in Connect + available (because those methods require the older DDS and DataDDS ctors. + + @see BaseType + @see DAS */ + +class DDS : public DapObj +{ +private: + BaseTypeFactory *d_factory; + + string d_name; // The dataset d_name + string d_filename; // File d_name (or other OS identifier) for + string d_container_name; // d_name of container structure + Structure *d_container; // current container for container d_name + // dataset or part of dataset. + + int d_dap_major; // The protocol major version number + int d_dap_minor; // ... and minor version number + string d_dap_version; // String version of the protocol + string d_request_xml_base; + string d_namespace; + + AttrTable d_attr; // Global attributes. + + vector vars; // Variables at the top level + + int d_timeout; // alarm time in seconds. If greater than + // zero, raise the alarm signal if more than + // d_timeout seconds are spent reading data. + Keywords d_keywords; // Holds keywords parsed from the CE + + long d_max_response_size; // In bytes... + + friend class DDSTest; + +protected: + void duplicate(const DDS &dds); + BaseType *leaf_match(const string &name, BaseType::btp_stack *s = 0); + BaseType *exact_match(const string &name, BaseType::btp_stack *s = 0); + +public: + typedef std::vector::const_iterator Vars_citer ; + typedef std::vector::iterator Vars_iter ; + typedef std::vector::reverse_iterator Vars_riter ; + + DDS(BaseTypeFactory *factory, const string &name = ""); + DDS(BaseTypeFactory *factory, const string &name, const string &version); + DDS(const DDS &dds); + + virtual ~DDS(); + + DDS & operator=(const DDS &rhs); + + virtual void transfer_attributes(DAS *das); + + string get_dataset_name() const; + void set_dataset_name(const string &n); + + /** Return the factory which makes instances of the Byte, ..., Grid + type classes. Specialize BaseTypeFactory so that a DDS will be + populated with your client or server's specialized types. + @return An instance of BaseTypeFactory. */ + BaseTypeFactory *get_factory() const + { + return d_factory; + } + + /** Set the factory class used to instantiate variables during the + parse of a DDS. + @param factory The factory this DDS should use. Caller must free + factory when done with this DDS. + @return The old factory. + @see BaseTypeFactory */ + BaseTypeFactory *set_factory(BaseTypeFactory *factory) + { + BaseTypeFactory *t = d_factory; + d_factory = factory; + return t; + } + + virtual AttrTable &get_attr_table(); + + string filename() const; + void filename(const string &fn); + + /// Get the DAP major version as sent by the client + int get_dap_major() const { return d_dap_major; } + /// Get the DAP minor version as sent by the client + int get_dap_minor() const { return d_dap_minor; } + + void set_dap_version(const string &version_string = "2.0"); + string get_dap_version() const { return d_dap_version; } + string get_dmr_version() const { return "1.0"; } + + /// @deprecated + void set_dap_major(int p); + /// @deprecated + void set_dap_minor(int p); + /// @deprecated + void set_dap_version(double d); + + Keywords &get_keywords() { return d_keywords; } + + /// Get the URL that will return this DDS/DDX/DataThing + string get_request_xml_base() const { return d_request_xml_base; } + + /// @see get_request_xml_base + void set_request_xml_base(const string &xb) { d_request_xml_base = xb; } + + /// Get the namespace associated with the DDS - likely set only by DDX responses + string get_namespace() const { return d_namespace; } + + /// Set the namespace for this DDS/DDX object/response + void set_namespace(const string &ns) { d_namespace = ns; } + + /// Get the maximum response size, in KB. Zero indicates no limit. + long get_response_limit() { return d_max_response_size; } + + /** Set the maximum response size. Zero is the default value. The size + is given in kilobytes. + @param size The maximum size of the response in kilobytes. */ + void set_response_limit(long size) { d_max_response_size = size * 1024; } + + /// Get the estimated response size. + int get_request_size(bool constrained); + + string container_name() ; + void container_name( const string &cn ) ; + Structure *container() ; + + void add_var(BaseType *bt); + void add_var_nocopy(BaseType *bt); + + /// Removes a variable from the DDS. + void del_var(const string &n); + + BaseType *var(const string &n, BaseType::btp_stack &s); + BaseType *var(const string &n, BaseType::btp_stack *s = 0); + int num_var(); + + /// Return an iterator to the first variable + Vars_iter var_begin(); +#if 0 + /// Return a const iterator. + Vars_citer var_cbegin() const { return vars.cbegin(); } +#endif + /// Return a reverse iterator + Vars_riter var_rbegin(); + /// Return an iterator + Vars_iter var_end(); +#if 0 + /// Return a const iterator + Vars_citer var_cend() const { return vars.cend(); } +#endif + /// Return a reverse iterator + Vars_riter var_rend(); + /// Get an iterator + Vars_iter get_vars_iter(int i); + /// Get a variable + BaseType *get_var_index(int i); + /// Insert a variable before the referenced element + void insert_var(Vars_iter i, BaseType *ptr); + void insert_var_nocopy(Vars_iter i, BaseType *ptr); + /// Removes a variable from the DDS. + void del_var(Vars_iter i); + /// Removes a range of variables from the DDS. + void del_var(Vars_iter i1, Vars_iter i2); + + /** @name DDS_timeout + * Old deprecated DDS timeout code + * @deprecated + */ + ///@{ + void timeout_on(); + void timeout_off(); + void set_timeout(int t); + int get_timeout(); + //@} + + // These parse the DAP2 curly-brace document and make a C++ object. + void parse(string fname); + void parse(int fd); + void parse(FILE *in = stdin); + + // These print the Binary object in either the curly-brace or XML reps + void print(FILE *out); + void print_constrained(FILE *out); + void print_xml(FILE *out, bool constrained, const string &blob = ""); + + // Same as above, but using C++ i/o streams + void print(ostream &out); + void print_constrained(ostream &out); + void print_xml(ostream &out, bool constrained, const string &blob = ""); + + // Print the XML using libxml2; the other print_xml methods use this impl. + void print_xml_writer(ostream &out, bool constrained, const string &blob = ""); + + // Print the DAP4 DMR 'object' + void print_dmr(ostream &out, bool constrained); + + void print_das(ostream &out); + + void mark_all(bool state); + bool mark(const string &name, bool state); + bool check_semantics(bool all = false); + + void tag_nested_sequences(); + + virtual void dump(ostream &strm) const ; +}; + +} // namespace libdap + +#endif // _dds_h diff --git a/DDXExceptions.h b/DDXExceptions.h new file mode 100644 index 0000000..0a63ac0 --- /dev/null +++ b/DDXExceptions.h @@ -0,0 +1,49 @@ + +// -*- mode: c++; c-basic-offset:4 -*- + +// This file is part of libdap, A C++ implementation of the OPeNDAP Data +// Access Protocol. + +// Copyright (c) 2003 OPeNDAP, Inc. +// Author: James Gallagher +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// +// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112. + +#ifndef ddx_exceptions_h +#define ddx_exceptions_h + +#ifndef _error_h +#include "Error.h" +#endif + +namespace libdap +{ + +/** Thrown when the DDX response cannot be parsed.. */ +class DDXParseFailed : public Error +{ +public: + DDXParseFailed() : Error("The DDX response document parse failed.") + {} + DDXParseFailed(const string &msg) : + Error(string("The DDX response document parse failed: ") + msg) + {} +}; + +} // namespace libdap + +#endif // ddx_exceptions_h diff --git a/DDXParserSAX2.cc b/DDXParserSAX2.cc new file mode 100644 index 0000000..be364df --- /dev/null +++ b/DDXParserSAX2.cc @@ -0,0 +1,1254 @@ + +// -*- mode: c++; c-basic-offset:4 -*- + +// This file is part of libdap, A C++ implementation of the OPeNDAP Data +// Access Protocol. + +// Copyright (c) 2003 OPeNDAP, Inc. +// Author: James Gallagher +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// +// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112. + +#include "config.h" + +//#define DODS_DEBUG 1 +//#define DODS_DEBUG2 1 + +#include +#include + +#include "BaseType.h" +#include "Byte.h" +#include "Int16.h" +#include "UInt16.h" +#include "Int32.h" +#include "UInt32.h" +#include "Float32.h" +#include "Float64.h" +#include "Str.h" +#include "Url.h" +#include "Array.h" +#include "Structure.h" +#include "Sequence.h" +#include "Grid.h" + +#include "DDXParserSAX2.h" + +#include "util.h" +#include "mime_util.h" +#include "debug.h" + +namespace libdap { + +#if defined(DODS_DEBUG) || defined(DODS_DEUG2) +static const char *states[] = + { + "start", + + "dataset", + + "attribute_container", + "attribute", + "attribute_value", + "other_xml_attribute", + + "alias", + + "simple_type", + + "array", + "dimension", + + "grid", + "map", + + "structure", + "sequence", + + "blob href", + + "unknown", + "error" + }; +#endif +// Glue the BaseTypeFactory to the enum-based factory defined statically +// here. + +BaseType *DDXParser::factory(Type t, const string & name) +{ + switch (t) { + case dods_byte_c: + return d_factory->NewByte(name); + break; + + case dods_int16_c: + return d_factory->NewInt16(name); + break; + + case dods_uint16_c: + return d_factory->NewUInt16(name); + break; + + case dods_int32_c: + return d_factory->NewInt32(name); + break; + + case dods_uint32_c: + return d_factory->NewUInt32(name); + break; + + case dods_float32_c: + return d_factory->NewFloat32(name); + break; + + case dods_float64_c: + return d_factory->NewFloat64(name); + break; + + case dods_str_c: + return d_factory->NewStr(name); + break; + + case dods_url_c: + return d_factory->NewUrl(name); + break; + + case dods_array_c: + return d_factory->NewArray(name); + break; + + case dods_structure_c: + return d_factory->NewStructure(name); + break; + + case dods_sequence_c: + return d_factory->NewSequence(name); + break; + + case dods_grid_c: + return d_factory->NewGrid(name); + break; + + default: + return 0; + } +} + +static bool is_not(const char *name, const char *tag) +{ + return strcmp(name, tag) != 0; +} + +void DDXParser::set_state(DDXParser::ParseState state) +{ + s.push(state); +} + +DDXParser::ParseState DDXParser::get_state() const +{ + return s.top(); +} + +void DDXParser::pop_state() +{ + s.pop(); +} + +/** Dump XML attributes to local store so they can be easily manipulated. + Attribute names are always folded to lower case. + @param attrs The XML attribute array */ +void DDXParser::transfer_xml_attrs(const xmlChar **attributes, int nb_attributes) +{ + if (!attribute_table.empty()) + attribute_table.clear(); // erase old attributes + + unsigned int index = 0; + for (int i = 0; i < nb_attributes; ++i, index += 5) { + // Make a value using the attribute name and the prefix, namespace URI + // and the value. The prefix might be null. + attribute_table.insert(map::value_type( + string((const char *)attributes[index]), + XMLAttribute(attributes + index + 1))); + + DBG(cerr << "Attribute '" << (const char *)attributes[index] << "': " + << attribute_table[(const char *)attributes[index]].value << endl); + } +} + +void DDXParser::transfer_xml_ns(const xmlChar **namespaces, int nb_namespaces) +{ + for (int i = 0; i < nb_namespaces; ++i ) { + // make a value with the prefix and namespace URI. The prefix might be + // null. + namespace_table.insert(map::value_type( + namespaces[i*2] != 0 ? (const char *)namespaces[i*2] : "", + (const char *)namespaces[i*2+1])); + } +} + +/** Is an attribute present? Attribute names are always lower case. + @note To use this method, first call transfer_xml_attrs. + @param attr The XML attribute + @return True if the XML attribute was present in the last tag */ +bool DDXParser::check_required_attribute(const string & attr) +{ + map < string, XMLAttribute >::iterator i = attribute_table.find(attr); + if (i == attribute_table.end()) + ddx_fatal_error(this, "Required attribute '%s' not found.", + attr.c_str()); + return true; +} + +/** Is an attribute present? Attribute names are always lower case. + @note To use this method, first call transfer_xml_attrs. + @param attr The XML attribute + @return True if the XML attribute was present in the last/current tag, + false otherwise. */ +bool DDXParser::check_attribute(const string & attr) +{ + return (attribute_table.find(attr) != attribute_table.end()); +} + +/** Given that an \c Attribute tag has just been read, determine whether the + element is a container or a simple type, set the state and, for a simple + type record the type and name for use when \c value elements are found. + + @note Modified to discriminate between OtherXML and the older DAP2.0 + attribute types (container, Byte, ...). + + @param attrs The array of XML attribute values */ +void DDXParser::process_attribute_element(const xmlChar **attrs, int nb_attributes) +{ + // These methods set the state to parser_error if a problem is found. + transfer_xml_attrs(attrs, nb_attributes); + + bool error = !(check_required_attribute(string("name")) + && check_required_attribute(string("type"))); + if (error) + return; + + if (attribute_table["type"].value == "Container") { + set_state(inside_attribute_container); + + AttrTable *child; + AttrTable *parent = at_stack.top(); + + child = parent->append_container(attribute_table["name"].value); + at_stack.push(child); // save. + DBG2(cerr << "Pushing at" << endl); + } + else if (attribute_table["type"].value == "OtherXML") { + set_state(inside_other_xml_attribute); + + dods_attr_name = attribute_table["name"].value; + dods_attr_type = attribute_table["type"].value; + } + else { + set_state(inside_attribute); + // *** Modify parser. Add a special state for inside OtherXML since it + // does not use the element. + + dods_attr_name = attribute_table["name"].value; + dods_attr_type = attribute_table["type"].value; + } +} + +/** Given that an \c Alias tag has just been read, set the state and process + the alias. + @param attrs The XML attribute array */ +void DDXParser::process_attribute_alias(const xmlChar **attrs, int nb_attributes) +{ + transfer_xml_attrs(attrs, nb_attributes); + if (check_required_attribute(string("name")) + && check_required_attribute(string("attribute"))) { + set_state(inside_alias); + at_stack.top()->attr_alias(attribute_table["name"].value, + attribute_table["attribute"].value); + } +} + +/** Given that a tag which opens a variable declaration has just been read, + create the variable. Once created, push the variable onto the stack of + variables, push that variables attribute table onto the attribute table + stack and update the state of the parser. + @param t The type of variable to create. + @param s The next state of the parser. + @param attrs the attributes read with the tag */ +void DDXParser::process_variable(Type t, ParseState s, const xmlChar **attrs, + int nb_attributes) +{ + transfer_xml_attrs(attrs, nb_attributes); + + set_state(s); + + if (bt_stack.top()->type() == dods_array_c + || check_required_attribute("name")) { // throws on error/false + BaseType *btp = factory(t, attribute_table["name"].value); + if (!btp) { + ddx_fatal_error(this, "Internal parser error; could not instantiate the variable '%s'.", + attribute_table["name"].value.c_str()); + } + else { + // Only run this code if btp is not null! jhrg 9/14/15 + // Once we make the new variable, we not only load it on to the + // BaseType stack, we also load its AttrTable on the AttrTable stack. + // The attribute processing software always operates on the AttrTable + // at the top of the AttrTable stack (at_stack). + bt_stack.push(btp); + at_stack.push(&btp->get_attr_table()); + } + } +} + +/** Given that a \c dimension tag has just been read, add that information to + the array on the top of the BaseType stack. + @param attrs The XML attributes included in the \c dimension tag */ +void DDXParser::process_dimension(const xmlChar **attrs, int nb_attributes) +{ + transfer_xml_attrs(attrs, nb_attributes); + if (check_required_attribute(string("size"))) { + set_state(inside_dimension); + Array *ap = dynamic_cast < Array * >(bt_stack.top()); + if (!ap) { + ddx_fatal_error(this, "Parse error: Expected an array variable."); + return; + } + + ap->append_dim(atoi(attribute_table["size"].value.c_str()), + attribute_table["name"].value); + } +} + +/** Given that a \c blob tag has just been read, extract and save the CID + included in the element. */ +void DDXParser::process_blob(const xmlChar **attrs, int nb_attributes) +{ + transfer_xml_attrs(attrs, nb_attributes); + if (check_required_attribute(string("href"))) { + set_state(inside_blob_href); + *blob_href = attribute_table["href"].value; + } +} + +/** Check to see if the current tag is either an \c Attribute or an \c Alias + start tag. This method is a glorified macro... + + @param name The start tag name + @param attrs The tag's XML attributes + @return True if the tag was an \c Attribute or \c Alias tag */ +inline bool +DDXParser::is_attribute_or_alias(const char *name, const xmlChar **attrs, + int nb_attributes) +{ + if (strcmp(name, "Attribute") == 0) { + process_attribute_element(attrs, nb_attributes); + // next state: inside_attribtue or inside_attribute_container + return true; + } + else if (strcmp(name, "Alias") == 0) { + process_attribute_alias(attrs, nb_attributes); + // next state: inside_alias + return true; + } + + return false; +} + +/** Check to see if the current tag is the start of a variable declaration. + If so, process it. A glorified macro... + @param name The start tag name + @param attrs The tag's XML attributes + @return True if the tag was a variable tag */ +inline bool DDXParser::is_variable(const char *name, const xmlChar **attrs, + int nb_attributes) +{ + Type t = get_type(name); + //if ((t = is_simple_type(name)) != dods_null_c) { + if (is_simple_type(t)) { + process_variable(t, inside_simple_type, attrs, nb_attributes); + return true; + } + else if (strcmp(name, "Array") == 0) { + process_variable(dods_array_c, inside_array, attrs, nb_attributes); + return true; + } + else if (strcmp(name, "Structure") == 0) { + process_variable(dods_structure_c, inside_structure, attrs, nb_attributes); + return true; + } + else if (strcmp(name, "Sequence") == 0) { + process_variable(dods_sequence_c, inside_sequence, attrs, nb_attributes); + return true; + } + else if (strcmp(name, "Grid") == 0) { + process_variable(dods_grid_c, inside_grid, attrs, nb_attributes); + return true; + } + + return false; +} + +void DDXParser::finish_variable(const char *tag, Type t, const char *expected) +{ + if (strcmp(tag, expected) != 0) { + DDXParser::ddx_fatal_error(this, + "Expected an end tag for a %s; found '%s' instead.", + expected, tag); + return; + } + + pop_state(); + + BaseType *btp = bt_stack.top(); + + bt_stack.pop(); + at_stack.pop(); + + if (btp->type() != t) { + DDXParser::ddx_fatal_error(this, + "Internal error: Expected a %s variable.", + expected); + delete btp; + return; + } + // Once libxml2 validates, this can go away. 05/30/03 jhrg + if (t == dods_array_c + && static_cast(btp)->dimensions() == 0) { + DDXParser::ddx_fatal_error(this, + "No dimension element included in the Array '%s'.", + btp->name().c_str()); + delete btp; + return; + } + + BaseType *parent = bt_stack.top(); + + if (!(parent->is_vector_type() || parent->is_constructor_type())) { + DDXParser::ddx_fatal_error(this, + "Tried to add the array variable '%s' to a non-constructor type (%s %s).", + tag, + bt_stack.top()->type_name().c_str(), + bt_stack.top()->name().c_str()); + delete btp; + return; + } + + parent->add_var_nocopy(btp); +} + +/** @name SAX Parser Callbacks + + These methods are declared static in the class header. This gives them C + linkage which allows them to be used as callbacks by the SAX parser + engine. */ +//@{ + +/** Initialize the SAX parser state object. This object is passed to each + callback as a void pointer. The initial state is parser_start. + + @param p The SAX parser */ +void DDXParser::ddx_start_document(void * p) +{ + DDXParser *parser = static_cast(p); + parser->error_msg = ""; + parser->char_data = ""; + + // init attr table stack. + parser->at_stack.push(&parser->dds->get_attr_table()); + + // Trick; DDS *should* be a child of Structure. To simplify parsing, + // stuff a Structure on the bt_stack and dump the top level variables + // there. Once we're done, transfer the variables to the DDS. + parser->bt_stack.push(new Structure("dummy_dds")); + + parser->set_state(parser_start); + + DBG2(cerr << "Parser state: " << states[parser->get_state()] << endl); +} + +/** Clean up after finishing a parse. + @param p The SAX parser */ +void DDXParser::ddx_end_document(void * p) +{ + DDXParser *parser = static_cast(p); + DBG2(cerr << "Ending state == " << states[parser->get_state()] << + endl); + + if (parser->get_state() != parser_start) + DDXParser::ddx_fatal_error(parser, "The document contained unbalanced tags."); + + // If we've found any sort of error, don't make the DDX; intern() will + // take care of the error. + if (parser->get_state() == parser_error) { + return; + } + + // Pop the temporary Structure off the stack and transfer its variables + // to the DDS. + Constructor *cp = dynamic_cast < Constructor * >(parser->bt_stack.top()); + if (!cp) { + delete parser->bt_stack.top(); + parser->bt_stack.pop(); + ddx_fatal_error(parser, "Parse error: Expected a Structure, Sequence or Grid variable."); + return; + } + + for (Constructor::Vars_iter i = cp->var_begin(); i != cp->var_end(); ++i) { + (*i)->set_parent(0); // top-level vars have no parents + parser->dds->add_var(*i); + } + + delete parser->bt_stack.top(); + parser->bt_stack.pop(); +} + +void DDXParser::ddx_sax2_start_element(void *p, + const xmlChar *l, const xmlChar *prefix, const xmlChar *URI, + int nb_namespaces, const xmlChar **namespaces, + int nb_attributes, int /*nb_defaulted*/, const xmlChar **attributes) +{ + DDXParser *parser = static_cast(p); + const char *localname = (const char *)l; + + DBG2(cerr << "start element: " << localname << ", states: " + << states[parser->get_state()]); + + switch (parser->get_state()) { + case parser_start: + if (strcmp(localname, "Dataset") == 0) { + parser->set_state(inside_dataset); + parser->root_ns = URI != 0 ? (const char *)URI: ""; + parser->transfer_xml_attrs(attributes, nb_attributes); + + if (parser->check_required_attribute(string("name"))) + parser->dds->set_dataset_name(parser->attribute_table["name"].value); + + if (parser->check_attribute("dapVersion")) + parser->dds->set_dap_version(parser->attribute_table["dapVersion"].value); + } + else + DDXParser::ddx_fatal_error(parser, + "Expected response to start with a Dataset element; found '%s' instead.", + localname); + break; + + case inside_dataset: + if (parser->is_attribute_or_alias(localname, attributes, nb_attributes)) + break; + else if (parser->is_variable(localname, attributes, nb_attributes)) + break; + else if (strcmp(localname, "blob") == 0 || strcmp(localname, "dataBLOB") == 0) { + parser->process_blob(attributes, nb_attributes); + // next state: inside_data_blob + } + else + DDXParser::ddx_fatal_error(parser, + "Expected an Attribute, Alias or variable element; found '%s' instead.", + localname); + break; + + case inside_attribute_container: + if (parser->is_attribute_or_alias(localname, attributes, nb_attributes)) + break; + else + DDXParser::ddx_fatal_error(parser, + "Expected an Attribute or Alias element; found '%s' instead.", + localname); + break; + + case inside_attribute: + if (parser->is_attribute_or_alias(localname, attributes, nb_attributes)) + break; + else if (strcmp(localname, "value") == 0) + parser->set_state(inside_attribute_value); + else + ddx_fatal_error(parser, + "Expected an 'Attribute', 'Alias' or 'value' element; found '%s' instead.", + localname); + break; + + case inside_attribute_value: + ddx_fatal_error(parser, + "Internal parser error; unexpected state, inside value while processing element '%s'.", + localname); + break; + + case inside_other_xml_attribute: + DBGN(cerr << endl << "\t inside_other_xml_attribute: " << localname << endl); + + parser->other_xml_depth++; + + // Accumulate the elements here + + parser->other_xml.append("<"); + if (prefix) { + parser->other_xml.append((const char *)prefix); + parser->other_xml.append(":"); + } + parser->other_xml.append(localname); + + if (nb_namespaces != 0) { + parser->transfer_xml_ns(namespaces, nb_namespaces); + + for (map::iterator i = parser->namespace_table.begin(); + i != parser->namespace_table.end(); + ++i) { + parser->other_xml.append(" xmlns"); + if (!i->first.empty()) { + parser->other_xml.append(":"); + parser->other_xml.append(i->first); + } + parser->other_xml.append("=\""); + parser->other_xml.append(i->second); + parser->other_xml.append("\""); + } + } + + if (nb_attributes != 0) { + parser->transfer_xml_attrs(attributes, nb_attributes); + for (XMLAttrMap::iterator i = parser->attr_table_begin(); + i != parser->attr_table_end(); + ++i) { + parser->other_xml.append(" "); + if (!i->second.prefix.empty()) { + parser->other_xml.append(i->second.prefix); + parser->other_xml.append(":"); + } + parser->other_xml.append(i->first); + parser->other_xml.append("=\""); + parser->other_xml.append(i->second.value); + parser->other_xml.append("\""); + } + } + + parser->other_xml.append(">"); + break; + + case inside_alias: + ddx_fatal_error(parser, + "Internal parser error; unexpected state, inside alias while processing element '%s'.", + localname); + break; + + case inside_simple_type: + if (parser->is_attribute_or_alias(localname, attributes, nb_attributes)) + break; + else + ddx_fatal_error(parser, + "Expected an 'Attribute' or 'Alias' element; found '%s' instead.", + localname); + break; + + case inside_array: + if (parser->is_attribute_or_alias(localname, attributes, nb_attributes)) + break; + else if (is_not(localname, "Array") + && parser->is_variable(localname, attributes, nb_attributes)) + break; + else if (strcmp(localname, "dimension") == 0) { + parser->process_dimension(attributes, nb_attributes); + // next state: inside_dimension + } + else + ddx_fatal_error(parser, + "Expected an 'Attribute' or 'Alias' element; found '%s' instead.", + localname); + break; + + case inside_dimension: + ddx_fatal_error(parser, + "Internal parser error; unexpected state, inside dimension while processing element '%s'.", + localname); + break; + + case inside_structure: + if (parser->is_attribute_or_alias(localname, attributes, nb_attributes)) + break; + else if (parser->is_variable(localname, attributes, nb_attributes)) + break; + else + DDXParser::ddx_fatal_error(parser, + "Expected an Attribute, Alias or variable element; found '%s' instead.", + localname); + break; + + case inside_sequence: + if (parser->is_attribute_or_alias(localname, attributes, nb_attributes)) + break; + else if (parser->is_variable(localname, attributes, nb_attributes)) + break; + else + DDXParser::ddx_fatal_error(parser, + "Expected an Attribute, Alias or variable element; found '%s' instead.", + localname); + break; + + case inside_grid: + if (parser->is_attribute_or_alias(localname, attributes, nb_attributes)) + break; + else if (strcmp(localname, "Array") == 0) + parser->process_variable(dods_array_c, inside_array, attributes, nb_attributes); + else if (strcmp(localname, "Map") == 0) + parser->process_variable(dods_array_c, inside_map, attributes, nb_attributes); + else + DDXParser::ddx_fatal_error(parser, + "Expected an Attribute, Alias or variable element; found '%s' instead.", + localname); + break; + + case inside_map: + if (parser->is_attribute_or_alias(localname, attributes, nb_attributes)) + break; + else if (is_not(localname, "Array") && is_not(localname, "Sequence") + && is_not(localname, "Grid") + && parser->is_variable(localname, attributes, nb_attributes)) + break; + else if (strcmp(localname, "dimension") == 0) { + parser->process_dimension(attributes, nb_attributes); + // next state: inside_dimension + } + else + ddx_fatal_error(parser, + "Expected an 'Attribute', 'Alias', variable or 'dimension' element; found '%s' instead.", + localname); + break; + + case inside_blob_href: + ddx_fatal_error(parser, + "Internal parser error; unexpected state, inside blob href while processing element '%s'.", + localname); + break; + + case parser_unknown: + // *** Never used? If so remove/error + parser->set_state(parser_unknown); + break; + + case parser_error: + break; + } + + DBGN(cerr << " ... " << states[parser->get_state()] << endl); +} + +void DDXParser::ddx_sax2_end_element(void *p, const xmlChar *l, + const xmlChar *prefix, const xmlChar *URI) +{ + DDXParser *parser = static_cast(p); + const char *localname = (const char *)l; + + DBG2(cerr << "End element " << localname << " (state " + << states[parser->get_state()] << ")" << endl); + + switch (parser->get_state()) { + case parser_start: + ddx_fatal_error(parser, + "Internal parser error; unexpected state, inside start state while processing element '%s'.", + localname); + break; + + case inside_dataset: + if (strcmp(localname, "Dataset") == 0) + parser->pop_state(); + else + DDXParser::ddx_fatal_error(parser, + "Expected an end Dataset tag; found '%s' instead.", + localname); + break; + + case inside_attribute_container: + if (strcmp(localname, "Attribute") == 0) { + parser->pop_state(); + parser->at_stack.pop(); // pop when leaving a container. + } + else + DDXParser::ddx_fatal_error(parser, + "Expected an end Attribute tag; found '%s' instead.", + localname); + break; + + case inside_attribute: + if (strcmp(localname, "Attribute") == 0) + parser->pop_state(); + else + DDXParser::ddx_fatal_error(parser, + "Expected an end Attribute tag; found '%s' instead.", + localname); + break; + + case inside_attribute_value: + if (strcmp(localname, "value") == 0) { + parser->pop_state(); + AttrTable *atp = parser->at_stack.top(); + atp->append_attr(parser->dods_attr_name, + parser->dods_attr_type, parser->char_data); + parser->char_data = ""; // Null this after use. + } + else + DDXParser::ddx_fatal_error(parser, + "Expected an end value tag; found '%s' instead.", + localname); + + break; + + case inside_other_xml_attribute: { + if (strcmp(localname, "Attribute") == 0 + && parser->root_ns == (const char *)URI) { + + DBGN(cerr << endl << "\t Popping the 'inside_other_xml_attribute' state" + << endl); + + parser->pop_state(); + + AttrTable *atp = parser->at_stack.top(); + atp->append_attr(parser->dods_attr_name, + parser->dods_attr_type, parser->other_xml); + + parser->other_xml = ""; // Null this after use. + } + else { + DBGN(cerr << endl << "\t inside_other_xml_attribute: " << localname + << ", depth: " << parser->other_xml_depth << endl); + if (parser->other_xml_depth == 0) + DDXParser::ddx_fatal_error(parser, + "Expected an OtherXML attribute to end! Instead I found '%s'", + localname); + parser->other_xml_depth--; + + parser->other_xml.append("other_xml.append((const char *)prefix); + parser->other_xml.append(":"); + } + parser->other_xml.append(localname); + parser->other_xml.append(">"); + } + break; + } + // Alias is busted in libdap++ 05/29/03 jhrg + case inside_alias: + parser->pop_state(); + break; + + case inside_simple_type: { + Type t = get_type(localname); + if (is_simple_type(t)) { + parser->pop_state(); + BaseType *btp = parser->bt_stack.top(); + parser->bt_stack.pop(); + parser->at_stack.pop(); + + BaseType *parent = parser->bt_stack.top(); + + if (parent->is_vector_type() || parent->is_constructor_type()) { + parent->add_var(btp); + delete btp; + } + else { + DDXParser::ddx_fatal_error(parser, + "Tried to add the simple-type variable '%s' to a non-constructor type (%s %s).", + localname, + parser->bt_stack.top()-> + type_name().c_str(), + parser->bt_stack.top()->name(). + c_str()); + delete btp; + } + } + else { + DDXParser::ddx_fatal_error(parser, + "Expected an end tag for a simple type; found '%s' instead.", + localname); + } + break; + } + + case inside_array: + parser->finish_variable(localname, dods_array_c, "Array"); + break; + + case inside_dimension: + if (strcmp(localname, "dimension") == 0) + parser->pop_state(); + else + DDXParser::ddx_fatal_error(parser, + "Expected an end dimension tag; found '%s' instead.", + localname); + break; + + case inside_structure: + parser->finish_variable(localname, dods_structure_c, "Structure"); + break; + + case inside_sequence: + parser->finish_variable(localname, dods_sequence_c, "Sequence"); + break; + + case inside_grid: + parser->finish_variable(localname, dods_grid_c, "Grid"); + break; + + case inside_map: + parser->finish_variable(localname, dods_array_c, "Map"); + break; + + case inside_blob_href: + if (strcmp(localname, "blob") == 0 || strcmp(localname, "dataBLOB") == 0) + parser->pop_state(); + else + DDXParser::ddx_fatal_error(parser, + "Expected an end dataBLOB/blob tag; found '%s' instead.", + localname); + break; + + case parser_unknown: + parser->pop_state(); + break; + + case parser_error: + break; + } + + + DBGN(cerr << " ... " << states[parser->get_state()] << endl); +} + +/** Process/accumulate character data. This may be called more than once for + one logical clump of data. Only save character data when processing + 'value' elements; throw away all other characters. */ +void DDXParser::ddx_get_characters(void * p, const xmlChar * ch, int len) +{ + DDXParser *parser = static_cast(p); + + switch (parser->get_state()) { + case inside_attribute_value: + parser->char_data.append((const char *)(ch), len); + DBG2(cerr << "Characters: '" << parser->char_data << "'" << endl); + break; + + case inside_other_xml_attribute: + parser->other_xml.append((const char *)(ch), len); + DBG2(cerr << "Other XML Characters: '" << parser->other_xml << "'" << endl); + break; + + default: + break; + } +} + +/** Read whitespace that's not really important for content. This is used + only for the OtherXML attribute type to preserve formating of the XML. + Doing so makes the attribute value far easier to read. + */ +void DDXParser::ddx_ignoreable_whitespace(void *p, const xmlChar *ch, + int len) +{ + DDXParser *parser = static_cast(p); + + switch (parser->get_state()) { + case inside_other_xml_attribute: + parser->other_xml.append((const char *)(ch), len); + break; + + default: + break; + } +} + +/** Get characters in a cdata block. DAP does not use CData, but XML in an + OtherXML attribute (the value of that DAP attribute) might use it. This + callback also allows CData when the parser is in the 'parser_unknown' + state since some future DAP element might use it. + */ +void DDXParser::ddx_get_cdata(void *p, const xmlChar *value, int len) +{ + DDXParser *parser = static_cast(p); + + switch (parser->get_state()) { + case inside_other_xml_attribute: + parser->other_xml.append((const char *)(value), len); + break; + + case parser_unknown: + break; + + default: + DDXParser::ddx_fatal_error(parser, + "Found a CData block but none are allowed by DAP."); + + break; + } +} + +/** Handle the standard XML entities. + + @param parser The SAX parser + @param name The XML entity. */ +xmlEntityPtr DDXParser::ddx_get_entity(void *, const xmlChar * name) +{ + return xmlGetPredefinedEntity(name); +} + +/** Process an XML fatal error. Note that SAX provides for warnings, errors + and fatal errors. This code treats them all as fatal errors since there's + typically no way to tell a user about the error since there's often no + user interface for this software. + + @param p The SAX parser + @param msg A printf-style format string. */ +void DDXParser::ddx_fatal_error(void * p, const char *msg, ...) +{ + va_list args; + DDXParser *parser = static_cast(p); + + parser->set_state(parser_error); + + va_start(args, msg); + char str[1024]; + vsnprintf(str, 1024, msg, args); + va_end(args); + + int line = xmlSAX2GetLineNumber(parser->ctxt); + + parser->error_msg += "At line " + long_to_string(line) + ": "; + parser->error_msg += string(str) + string("\n"); +} + +//@} + +void DDXParser::cleanup_parse(xmlParserCtxtPtr & context) +{ + bool wellFormed = context->wellFormed; + bool valid = context->valid; + + context->sax = NULL; + xmlFreeParserCtxt(context); + + // If there's an error, there may still be items on the stack at the + // end of the parse. + while (!bt_stack.empty()) { + delete bt_stack.top(); + bt_stack.pop(); + } + + if (!wellFormed) { + throw DDXParseFailed(string("The DDX is not a well formed XML document.\n") + error_msg); + } + + if (!valid) { + throw DDXParseFailed(string("The DDX is not a valid document.\n") + error_msg); + } + + if (get_state() == parser_error) { + throw DDXParseFailed(string("Error parsing DDX response.\n") + error_msg); + } +} + +/** Read a DDX from a C++ input stream and populate a DDS object. + * + * @param in + * @param dds + * @param cid + * @param boundary + */ +void DDXParser::intern_stream(istream &in, DDS *dest_dds, string &cid, const string &boundary) +{ + // Code example from libxml2 docs re: read from a stream. + if (!in || in.eof()) + throw InternalErr(__FILE__, __LINE__, "Input stream not open or read error"); + + const int size = 1024; + char chars[size + 1]; + + // int res = fread(chars, 1, 4, in); + in.read(chars, 4); + int res = in.gcount(); + if (res > 0) { + chars[4]='\0'; + xmlParserCtxtPtr context = xmlCreatePushParserCtxt(NULL, NULL, chars, res, "stream"); + + if (!context) + throw DDXParseFailed("Error parsing DDX response: Input does not look like XML"); + + ctxt = context; // need ctxt for error messages + dds = dest_dds; // dump values here + blob_href = &cid; // cid goes here + + xmlSAXHandler ddx_sax_parser; + memset( &ddx_sax_parser, 0, sizeof(xmlSAXHandler) ); + + ddx_sax_parser.getEntity = &DDXParser::ddx_get_entity; + ddx_sax_parser.startDocument = &DDXParser::ddx_start_document; + ddx_sax_parser.endDocument = &DDXParser::ddx_end_document; + ddx_sax_parser.characters = &DDXParser::ddx_get_characters; + ddx_sax_parser.ignorableWhitespace = &DDXParser::ddx_ignoreable_whitespace; + ddx_sax_parser.cdataBlock = &DDXParser::ddx_get_cdata; + ddx_sax_parser.warning = &DDXParser::ddx_fatal_error; + ddx_sax_parser.error = &DDXParser::ddx_fatal_error; + ddx_sax_parser.fatalError = &DDXParser::ddx_fatal_error; + ddx_sax_parser.initialized = XML_SAX2_MAGIC; + ddx_sax_parser.startElementNs = &DDXParser::ddx_sax2_start_element; + ddx_sax_parser.endElementNs = &DDXParser::ddx_sax2_end_element; + + context->sax = &ddx_sax_parser; + context->userData = this; + context->validate = true; + + in.getline(chars, size); // chars has size+1 elements + res = in.gcount(); + chars[res-1] = '\n'; // libxml needs the newline; w/o it the parse will fail + chars[res] = '\0'; + while (res > 0 && !is_boundary(chars, boundary)) { + DBG(cerr << "line (" << res << "): " << chars << endl); + xmlParseChunk(ctxt, chars, res, 0); + + in.getline(chars, size); // chars has size+1 elements + res = in.gcount(); + if (res > 0) { + chars[res-1] = '\n'; + chars[res] = '\0'; + } + } + + // This call ends the parse: The fourth argument of xmlParseChunk is + // the bool 'terminate.' + xmlParseChunk(ctxt, chars, 0, 1); + + cleanup_parse(context); + } + else { + throw DDXParseFailed("Error parsing DDX response: Could not read from input stream."); + } +} + +/** @brief Read the DDX from a stream instead of a file. + @see DDXParser::intern(). */ +void DDXParser::intern_stream(FILE *in, DDS *dest_dds, string &cid, const string &boundary) +{ + // Code example from libxml2 docs re: read from a stream. + if (!in || feof(in) || ferror(in)) + throw InternalErr(__FILE__, __LINE__, "Input stream not open or read error"); + + const int size = 1024; + char chars[size]; + + int res = fread(chars, 1, 4, in); + if (res > 0) { + chars[4]='\0'; + xmlParserCtxtPtr context = xmlCreatePushParserCtxt(NULL, NULL, chars, res, "stream"); + + if (!context) + throw DDXParseFailed("Error parsing DDX response: Input does not look like XML"); + + ctxt = context; // need ctxt for error messages + dds = dest_dds; // dump values here + blob_href = &cid; // cid goes here + + xmlSAXHandler ddx_sax_parser; + memset( &ddx_sax_parser, 0, sizeof(xmlSAXHandler) ); + + ddx_sax_parser.getEntity = &DDXParser::ddx_get_entity; + ddx_sax_parser.startDocument = &DDXParser::ddx_start_document; + ddx_sax_parser.endDocument = &DDXParser::ddx_end_document; + ddx_sax_parser.characters = &DDXParser::ddx_get_characters; + ddx_sax_parser.ignorableWhitespace = &DDXParser::ddx_ignoreable_whitespace; + ddx_sax_parser.cdataBlock = &DDXParser::ddx_get_cdata; + ddx_sax_parser.warning = &DDXParser::ddx_fatal_error; + ddx_sax_parser.error = &DDXParser::ddx_fatal_error; + ddx_sax_parser.fatalError = &DDXParser::ddx_fatal_error; + ddx_sax_parser.initialized = XML_SAX2_MAGIC; + ddx_sax_parser.startElementNs = &DDXParser::ddx_sax2_start_element; + ddx_sax_parser.endElementNs = &DDXParser::ddx_sax2_end_element; + + context->sax = &ddx_sax_parser; + context->userData = this; + context->validate = true; + + + while ((fgets(chars, size, in) != 0) && !is_boundary(chars, boundary)) { + DBG(cerr << "line (" << strlen(chars) << "): " << chars << endl); + xmlParseChunk(ctxt, chars, strlen(chars), 0); + } + // This call ends the parse: The fourth argument of xmlParseChunk is + // the bool 'terminate.' + xmlParseChunk(ctxt, chars, 0, 1); + + cleanup_parse(context); + } + else { + throw DDXParseFailed("Error parsing DDX response: Could not read from input file."); + } +} + + +/** Parse a DDX document stored in a file. The XML in the document is parsed + and a binary DDX is built. This implementation stores the result in a DDS + object where each instance of BaseType can hold an AttrTable object. + + @param document Read the DDX from this file. + @param dest_dds Value/result parameter; dumps the information to this DDS + instance. + @param cid Value/result parameter; puts the href which references the \c + CID. + @exception DDXParseFailed Thrown if the XML document could not be + read or parsed. */ +void DDXParser::intern(const string & document, DDS * dest_dds, string &cid) +{ + // Create the context pointer explicitly so that we can store a pointer + // to it in the DDXParser instance. This provides a way to generate our + // own error messages *with* line numbers. The messages are pretty + // meaningless otherwise. This means that we use an interface from the + // 'parser internals' header, and not the 'parser' header. However, this + // interface is also used in one of the documented examples, so it's + // probably pretty stable. 06/02/03 jhrg + xmlParserCtxtPtr context = xmlCreateFileParserCtxt(document.c_str()); + if (!context) + throw + DDXParseFailed(string + ("Could not initialize the parser with the file: '") + + document + string("'.")); + + dds = dest_dds; // dump values here + blob_href = &cid; + ctxt = context; // need ctxt for error messages + + xmlSAXHandler ddx_sax_parser; + memset( &ddx_sax_parser, 0, sizeof(xmlSAXHandler) ); + + ddx_sax_parser.getEntity = &DDXParser::ddx_get_entity; + ddx_sax_parser.startDocument = &DDXParser::ddx_start_document; + ddx_sax_parser.endDocument = &DDXParser::ddx_end_document; + ddx_sax_parser.characters = &DDXParser::ddx_get_characters; + ddx_sax_parser.ignorableWhitespace = &DDXParser::ddx_ignoreable_whitespace; + ddx_sax_parser.cdataBlock = &DDXParser::ddx_get_cdata; + ddx_sax_parser.warning = &DDXParser::ddx_fatal_error; + ddx_sax_parser.error = &DDXParser::ddx_fatal_error; + ddx_sax_parser.fatalError = &DDXParser::ddx_fatal_error; + ddx_sax_parser.initialized = XML_SAX2_MAGIC; + ddx_sax_parser.startElementNs = &DDXParser::ddx_sax2_start_element; + ddx_sax_parser.endElementNs = &DDXParser::ddx_sax2_end_element; + + context->sax = &ddx_sax_parser; + context->userData = this; + context->validate = false; + + xmlParseDocument(context); + + cleanup_parse(context); +} + +} // namespace libdap diff --git a/DDXParserSAX2.h b/DDXParserSAX2.h new file mode 100644 index 0000000..933bebf --- /dev/null +++ b/DDXParserSAX2.h @@ -0,0 +1,266 @@ + +// -*- mode: c++; c-basic-offset:4 -*- + +// This file is part of libdap, A C++ implementation of the OPeNDAP Data +// Access Protocol. + +// Copyright (c) 2003 OPeNDAP, Inc. +// Author: James Gallagher +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// +// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112. + +#ifndef ddx_parser_h +#define ddx_parser_h + +#include +#include +#include + +#include + +#ifndef ddx_exceptions_h +#include "DDXExceptions.h" +#endif + +#ifndef _dds_h +#include "DDS.h" +#endif + +#ifndef _basetype_h +#include "BaseType.h" +#endif + +#ifndef base_type_factory_h +#include "BaseTypeFactory.h" +#endif + +namespace libdap +{ + +/** Parse the XML text which encodes the network/persistent representation of + the DDX object. In the current implementation, the DDX is held by an + instance of the class DDS which in turn holds variables which include + attributes. That is, the binary \e implementation of a DDX uses the old + DDS, BaseType and AttrTable classes, albeit arranged in a slightly new + way. + + This parser for the DDX \e document uses the SAX interface of \c libxml2. + Static methods are used as callbacks for the SAX parser. These static + methods are public because making them private complicates compilation. + They should not be called by anything other than the \e intern method. + They do not throw exceptions because exceptions from within callbacks are + not reliable or portable. To signal errors, the methods record + information in the DDXParser object. Once the error handler is called, + construction of an DDX/DDS object ends even though the SAX parser still + calls the various callback functions. The parser treats \e warnings, \e + errors and \e fatal_errors the same way; when any are found parsing + stops. The \e intern method throws an DDXParseFailed exception if an + error was found. + + Note that this class uses the C++-supplied default definitions for the + default and copy constructors as well as the destructor and assignment + operator. + + @see DDS */ +class DDXParser +{ +private: + /** States used by DDXParserState. These are the states of the SAX parser + state-machine. */ + enum ParseState { + parser_start, + + inside_dataset, + + inside_attribute_container, + inside_attribute, + inside_attribute_value, + inside_other_xml_attribute, + + inside_alias, + + // This covers Byte, ..., Url. + inside_simple_type, + + inside_array, + inside_dimension, + + inside_grid, + inside_map, + + inside_structure, + inside_sequence, + + inside_blob_href, + + parser_unknown, + parser_error + }; + + BaseTypeFactory *d_factory; + + // These stacks hold the state of the parse as it progresses. + stack s; // Current parse state + stack bt_stack; // current variable(s) + stack at_stack; // current attribute table + + // Accumulate stuff inside an 'OtherXML' DAP attribute here + string other_xml; + + // When we're parsing unknown XML, how deeply is it nested? This is used + // for the OtherXML DAP attributes. + unsigned int other_xml_depth; + unsigned int unknown_depth; + + // These are used for processing errors. + string error_msg; // Error message(s), if any. + xmlParserCtxtPtr ctxt; // used for error message line numbers + + // The results of the parse operation are stored in these fields. + DDS *dds; // dump DDX here + string *blob_href; // put href to blob here + + // These hold temporary values read during the parse. + string dods_attr_name; // DAP2 attributes, not XML attributes + string dods_attr_type; // ... not XML ... + string char_data; // char data in value elements; null after use + string root_ns; // What is the namespace of the root node (Dataset) + + class XMLAttribute { + public: + string prefix; + string nsURI; + string value; + + void clone(const XMLAttribute &src) { + prefix = src.prefix; + nsURI = src.nsURI; + value = src.value; + } + + XMLAttribute() : prefix(""), nsURI(""), value("") {} + XMLAttribute(const string &p, const string &ns, const string &v) + : prefix(p), nsURI(ns), value(v) {} + // 'attributes' as passed from libxml2 is a five element array but this + // ctor gets the back four elements. + XMLAttribute(const xmlChar **attributes/*[4]*/) { + prefix = attributes[0] != 0 ? (const char *)attributes[0]: ""; + nsURI = attributes[1] != 0 ? (const char *)attributes[1]: ""; + value = string((const char *)attributes[2], (const char *)attributes[3]); + } + XMLAttribute(const XMLAttribute &rhs) { + clone(rhs); + } + XMLAttribute &operator=(const XMLAttribute &rhs) { + if (this == &rhs) + return *this; + clone(rhs); + return *this; + } + }; + + typedef map XMLAttrMap; + XMLAttrMap attribute_table; // dump XML attributes here + + XMLAttrMap::iterator attr_table_begin() { + return attribute_table.begin(); + } + + XMLAttrMap::iterator attr_table_end() { + return attribute_table.end(); + } + + map namespace_table; + + // These are kind of silly... + void set_state(DDXParser::ParseState state); + DDXParser::ParseState get_state() const; + void pop_state(); + + // Glue for the BaseTypeFactory class. + BaseType *factory(Type t, const string &name); + + // Common cleanup code for intern() and intern_stream() + void cleanup_parse(xmlParserCtxtPtr &context); + + /** @name Parser Actions + + These methods are the 'actions' carried out by the start_element and + end_element callbacks. Most of what takes place in those has been + factored out to this set of functions. */ + //@{ + void transfer_xml_attrs(const xmlChar **attrs, int nb_attributes); + void transfer_xml_ns(const xmlChar **namespaces, int nb_namespaces); + bool check_required_attribute(const string &attr); + bool check_attribute(const string & attr); + + void process_attribute_element(const xmlChar **attrs, int nb_attrs); + void process_attribute_alias(const xmlChar **attrs, int nb_attrs); + + void process_variable(Type t, ParseState s, const xmlChar **attrs, + int nb_attributes); + + void process_dimension(const xmlChar **attrs, int nb_attrs); + void process_blob(const xmlChar **attrs, int nb_attrs); + + bool is_attribute_or_alias(const char *name, const xmlChar **attrs, + int nb_attributes); + bool is_variable(const char *name, const xmlChar **attrs, int nb_attributes); + + void finish_variable(const char *tag, Type t, const char *expected); + //@} + + /// Declare the default ctor here to prevent its use. + DDXParser(); + + friend class DDXParserTest; + +public: + DDXParser(BaseTypeFactory *factory) + : d_factory(factory), + other_xml(""), other_xml_depth(0), unknown_depth(0), + error_msg(""), ctxt(0), dds(0), blob_href(0), + dods_attr_name(""), dods_attr_type(""), + char_data(""), root_ns("") + {} + + void intern(const string &document, DDS *dest_dds, string &cid); + void intern_stream(FILE *in, DDS *dds, string &cid, const string &boundary = ""); + void intern_stream(istream &in, DDS *dds, string &cid, const string &boundary = ""); + + static void ddx_start_document(void *parser); + static void ddx_end_document(void *parser); + + static void ddx_sax2_start_element(void *parser, + const xmlChar *localname, const xmlChar *prefix, const xmlChar *URI, + int nb_namespaces, const xmlChar **namespaces, int nb_attributes, + int nb_defaulted, const xmlChar **attributes); + static void ddx_sax2_end_element(void *parser, const xmlChar *localname, + const xmlChar *prefix, const xmlChar *URI); + + static void ddx_get_characters(void *parser, const xmlChar *ch, int len); + static void ddx_ignoreable_whitespace(void *parser, + const xmlChar * ch, int len); + static void ddx_get_cdata(void *parser, const xmlChar *value, int len); + + static xmlEntityPtr ddx_get_entity(void *parser, const xmlChar *name); + static void ddx_fatal_error(void *parser, const char *msg, ...); +}; + +} // namespace libdap + +#endif // ddx_parser_h diff --git a/DMR.cc b/DMR.cc new file mode 100644 index 0000000..1d4e09e --- /dev/null +++ b/DMR.cc @@ -0,0 +1,498 @@ +// -*- mode: c++; c-basic-offset:4 -*- + +// This file is part of libdap, A C++ implementation of the OPeNDAP Data +// Access Protocol. + +// Copyright (c) 2013 OPeNDAP, Inc. +// Author: James Gallagher +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112. + +#include "config.h" + +#ifdef WIN32 +#include +#include +#include +#else +#include // for alarm and dup +#include +#endif + +#include + +#include +#include + +//#define DODS_DEBUG +//#define DODS_DEBUG2 + +#include "D4Group.h" +#include "BaseType.h" +#include "Array.h" +#include "Grid.h" +#include "DMR.h" +#include "XMLWriter.h" +#include "D4BaseTypeFactory.h" +#include "D4Attributes.h" + +#include "DDS.h" // Included so DMRs can be built using a DDS for 'legacy' handlers + +#include "debug.h" + +/** + * DapXmlNamespaces + * + * TODO Replace all uses of the following variables with calls to DapXmlNamespaces + */ +const string c_xml_xsi = "http://www.w3.org/2001/XMLSchema-instance"; +const string c_xml_namespace = "http://www.w3.org/XML/1998/namespace"; + +const string c_default_dap40_schema_location = "http://xml.opendap.org/dap/dap4.0.xsd"; + +const string c_dap40_namespace = "http://xml.opendap.org/ns/DAP/4.0#"; + +const string c_dap_40_n_sl = c_dap40_namespace + " " + c_default_dap40_schema_location; + +using namespace std; + +namespace libdap { + +void +DMR::m_duplicate(const DMR &dmr) +{ + // This is needed because we use the factory to make a new instance of the root group + assert(dmr.OK()); + + d_factory = dmr.d_factory; // Shallow copy here + + d_name = dmr.d_name; + d_filename = dmr.d_filename; + + d_dap_major = dmr.d_dap_major; + d_dap_minor = dmr.d_dap_minor; + d_dap_version = dmr.d_dap_version; // String version of the protocol + + d_dmr_version = dmr.d_dmr_version; + + d_request_xml_base = dmr.d_request_xml_base; + + d_namespace = dmr.d_namespace; + + d_max_response_size = dmr.d_max_response_size; + + // Deep copy, using ptr_duplicate() + // d_root can only be a D4Group, so the thing returned by ptr_duplicate() must be a D4Group. + d_root = static_cast(dmr.d_root->ptr_duplicate()); + DBG(cerr << "dmr.d_root: " << dmr.d_root << endl); + DBG(cerr << "d_root (from ptr_dup(): " << d_root << endl); + + //d_root = static_cast(dmr.d_factory->NewVariable(dods_group_c, dmr.d_root->name())); +} + +/** + * Make a DMR which uses the given BaseTypeFactory to create variables. + * + * @note The default DAP version is 4.0 - use the DDS class to make DAP2 + * things. The default DMR version is 1.0 + * + * @param factory The D4BaseTypeFactory to use when creating instances of + * DAP4 variables. The caller must ensure the factory's lifetime is at least + * that of the DMR instance. + * @param name The name of the DMR - usually derived from the name of the + * pathname or table name of the dataset. + */ +DMR::DMR(D4BaseTypeFactory *factory, const string &name) + : d_factory(factory), d_name(name), d_filename(""), + d_dap_major(4), d_dap_minor(0), + d_dmr_version("1.0"), d_request_xml_base(""), + d_namespace(c_dap40_namespace), d_max_response_size(0), d_root(0) +{ + // sets d_dap_version string and the two integer fields too + set_dap_version("4.0"); +} + +/** @brief Build a DMR using a DAP2 DDS. + * + * Given a DDS from code written for DAP2, build a DAP4 DMR object. This + * works because DAP4 subsumes DAP2, but there are a few quirks... For + * each variable in the DDS, transform it to the equivalent DAP4 variable + * type and then copy the variable's attributes. Most types convert easily. + * Types that need special treatment are: + * Array: DAP2 array dimensions must be morphed to DAP4 + * Sequence: Make a D4Sequence + * Grid: Make a coverage; assume Grids with the same dimension names + * have 'shared dimensions' and that maps with the same names are shared too. + * + * @note Assume that a DDS has only a root group. This is not actually + * true for a DDS from the HDF5 handler, because it has Groups encoded + * into the variable names. jhrg 3/18/14 + * + * @param factory Factory class used to make new variables + * @param dds Get the variables to convert from this DAP2 DDS. + * @see BaseType::transform_to_dap4() + */ +DMR::DMR(D4BaseTypeFactory *factory, DDS &dds) + : d_factory(factory), d_name(dds.get_dataset_name()), + d_filename(dds.filename()), d_dap_major(4), d_dap_minor(0), + d_dmr_version("1.0"), d_request_xml_base(""), + d_namespace(c_dap40_namespace), d_max_response_size(0), d_root(0) +{ + // sets d_dap_version string and the two integer fields too + set_dap_version("4.0"); + + build_using_dds(dds); +#if 0 + for (DDS::Vars_iter i = dds.var_begin(), e = dds.var_end(); i != e; ++i) { + BaseType *new_var = (*i)->transform_to_dap4(root() /*group*/, root() /*container*/); + // If the variable being transformed is a Grid, + // then Grid::transform_to_dap4() will add all the arrays to the + // container (root() in this case) and return null, indicating that + // this code does not need to do anything to add the transformed variable. + if (new_var) + root()->add_var_nocopy(new_var); + } + + // Now copy the global attributes + root()->attributes()->transform_to_dap4(dds.get_attr_table()); +#endif +} + +/** + * Make a DMR which uses the given BaseTypeFactory to create variables. + * + * @note The default DAP version is 4.0 - use the DDS class to make DAP2 + * things. The default DMR version is 1.0 + */ +DMR::DMR() + : d_factory(0), d_name(""), d_filename(""), d_dap_major(4), d_dap_minor(0), + d_dap_version("4.0"), d_dmr_version("1.0"), d_request_xml_base(""), + d_namespace(c_dap40_namespace), d_max_response_size(0), d_root(0) +{ + // sets d_dap_version string and the two integer fields too + set_dap_version("4.0"); +} + +/** The DMR copy constructor. */ +DMR::DMR(const DMR &rhs) : DapObj() +{ + m_duplicate(rhs); +} + +/** Delete a DMR. The BaseType factory is not freed, while the contained + * group is. + */ +DMR::~DMR() +{ +#if 1 + delete d_root; +#endif +} + +DMR & +DMR::operator=(const DMR &rhs) +{ + if (this == &rhs) + return *this; + + m_duplicate(rhs); + + return *this; +} + +/** + * If we have a DDS that includes Attributes, use it to build the DMR. This + * will copy all of the variables in the DDS into the DMR using BaseType::transform_to_dap4(), + * so the actual types added can be controlled by code that specializes + * the various type classes. + * + * @param dds Read variables and Attributes from this DDS + */ +void DMR::build_using_dds(DDS &dds) +{ + set_name(dds.get_dataset_name()); + set_filename(dds.filename()); + + D4Group *root_grp = root(); + for (DDS::Vars_iter i = dds.var_begin(), e = dds.var_end(); i != e; ++i) { + BaseType *d4_var = root()->var((*i)->name()); + // Don't add duplicate variables. We have to make this check + // because some of the child variables may add arrays + // to the root object. For example, this happens in + // Grid with the Map Arrays - ndp - 05/08/17 + if(!d4_var){ + // no variable of this name is in the root group at this point. Add it. + DBG(cerr << __func__ << "() - Transforming top level variable: " << + " (" << (*i)->type_name() << ":'" << (*i)->name() << "':"<<(void *)(*i) << + ") (root:"<< root_grp << ")"<< endl; ); + (*i)->transform_to_dap4(root_grp, root_grp); + DBG(cerr << __func__ << "() - top level variable: '" << + (*i)->name() << "' (type:" << (*i)->type_name() << ") Transformed"<< endl; ); + } + else { + DBG(cerr << __func__ << "() - Skipping variable: " << + d4_var->type_name() << " " << d4_var->name() << " because a variable with" << + " this name already exists in the root group." << endl; ); + } + } + + // Now copy the global attributes + root()->attributes()->transform_to_dap4(dds.get_attr_table()); +} +#if 1 +/** + * If we have a DMR that includes Attributes, use it to build the DDS. This + * will copy all of the variables in the DMR into the DDS using + * BaseType::transform_to_dap2(), so the actual types added can be + * controlled by code that specializes the various type classes. + * + * @param dds Read variables and Attributes from this DDS + */ +DDS *DMR::getDDS(DMR &dmr) +{ + DBG( cerr << __func__ << "() - BEGIN" << endl;); + D4Group *root = dmr.root(); + + BaseTypeFactory *btf = new BaseTypeFactory(); + DDS *dds = new DDS(btf,dmr.name()); + dds->filename(dmr.filename()); + AttrTable *dds_at = &(dds->get_attr_table()); + + // Now copy the global attributes + // D4Attributes::load_AttrTable(dds_at,root->attributes()); + + vector *top_vars = root->transform_to_dap2(dds_at,true); + + vector::iterator vIter = top_vars->begin(); + vector::iterator vEnd = top_vars->end(); + for( ; vIter!=vEnd ; vIter++){ + dds->add_var(*vIter); + } + +#if 0 + set shared_dim_candidates; + + vector dropped_vars; + for (D4Group::Vars_iter i = root->var_begin(), e = root->var_end(); i != e; ++i) + { + DBG( cerr << __func__ << "() - Processing top level variable '"<< (*i)->type_name() << " " << (*i)->name() << "' to DDS." << endl; ); + vector *new_vars = (*i)->transform_to_dap2(&(dds->get_attr_table())); + if(new_vars!=0){ + vector::iterator vIter = new_vars->begin(); + vector::iterator end = new_vars->end(); + for( ; vIter!=end ; vIter++ ){ + BaseType *new_var = (*vIter); + DBG( cerr << __func__ << "() - Adding variable name: '"<< new_var->name() << "' " << + "type: " << new_var->type() << " " << + "type_name: " << new_var->type_name() << " to DDS." << endl; ); + dds->add_var_nocopy(new_var); + Grid *grid = dynamic_cast (new_var); + if(grid){ + Grid::Map_iter m = grid->map_begin(); + for( ; m != grid->map_end() ; m++){ + shared_dim_candidates.insert((*m)->name()); + } + } + (*vIter) = 0; + } + delete new_vars; + } + else { + DBG( cerr << __func__ << "Adding variable '"<< (*i)->type_name() << " " << (*i)->name() << "' to drop list." << endl; ); + dropped_vars.push_back((*i)); + } + } + AttrTable *dv_table = Constructor::make_dropped_vars_attr_table(&dropped_vars); + if(dv_table){ + DBG( cerr << __func__ << "() - Adding dropped variable AttrTable." << endl;); + dds_at->append_container(dv_table,dv_table->get_name()); + } + + // Get all the child groups. + D4Group::groupsIter gIter = root->grp_begin(); + D4Group::groupsIter gEnd = root->grp_end(); + for( ; gIter!=gEnd ; gIter++){ + D4Group *grp = *gIter; + DBG( cerr << __func__ << "() - Processing D4Group " << grp->name() << endl;); + vector *d2_vars = grp->transform_to_dap2(dds_at); + if(d2_vars){ + DBG( cerr << __func__ << "() - Processing " << grp->name() << " Member Variables." << endl;); + vector::iterator vIter = d2_vars->begin(); + vector::iterator vEnd = d2_vars->end(); + for( ; vIter!=vEnd; vIter++){ + DBG( cerr << __func__ << "() - Processing " << grp->name() << " Member Variable: " << (*vIter)->name() << endl;); + dds->add_var(*vIter); + } + } + } +#endif + + + DBG( cerr << __func__ << "() - END" << endl;); + return dds; +} + + +DDS *DMR::getDDS() +{ + return DMR::getDDS(*this); +} + + + +#endif + +D4Group * +DMR::root() +{ + if (!d_root) d_root = static_cast(d_factory->NewVariable(dods_group_c, "/")); + return d_root; +} + +/** + * Given the DAP protocol version, parse that string and set the DMR fields. + * + * @param v The version string. + */ +void +DMR::set_dap_version(const string &v) +{ + istringstream iss(v); + + int major = -1, minor = -1; + char dot; + if (!iss.eof() && !iss.fail()) + iss >> major; + if (!iss.eof() && !iss.fail()) + iss >> dot; + if (!iss.eof() && !iss.fail()) + iss >> minor; + + if (major == -1 || minor == -1 or dot != '.') + throw InternalErr(__FILE__, __LINE__, "Could not parse dap version. Value given: " + v); + + d_dap_version = v; + + d_dap_major = major; + d_dap_minor = minor; + + // Now set the related XML constants. These might be overwritten if + // the DMR instance is being built from a document parse, but if it's + // being constructed by a server the code to generate the XML document + // needs these values to match the DAP version information. + switch (d_dap_major) { + case 4: + d_namespace = c_dap40_namespace; + break; + default: + d_namespace = ""; + break; + } +} + +/** Get the size of a response, in kilobytes. This method looks at the + * variables in the DMR a computes the number of bytes in the response. + * + * @note This version of the method does a poor job with Arrays that + * have varying dimensions. + * + * @param constrained Should the size of the whole DMR be used or should the + * current constraint be taken into account? + * @return The size of the request in kilobytes + */ +long +DMR::request_size(bool constrained) +{ + return d_root->request_size(constrained); +} + +/** + * Print the DAP4 DMR object. + * + * @param xml use this XMLWriter to build the XML. + * @param constrained Should the DMR be subject to a constraint? Defaults to + * False + */ +void +DMR::print_dap4(XMLWriter &xml, bool constrained) +{ + if (xmlTextWriterStartElement(xml.get_writer(), (const xmlChar*) "Dataset") < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write Dataset element"); + +#if 0 + // Reintroduce these if they are really useful. jhrg 4/15/13 + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "xmlns:xml", + (const xmlChar*) c_xml_namespace.c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute for xmlns:xml"); + + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "xmlns:xsi", (const xmlChar*) c_xml_xsi.c_str()) + < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute for xmlns:xsi"); + + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "xsi:schemaLocation", + (const xmlChar*) c_dap_40_n_sl.c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute for xmlns:schemaLocation"); +#endif + + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "xmlns", (const xmlChar*) get_namespace().c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute for xmlns"); + + if (!request_xml_base().empty()) { + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "xml:base", + (const xmlChar*)request_xml_base().c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute for xml:base"); + } + + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "dapVersion", (const xmlChar*)dap_version().c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute for dapVersion"); + + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "dmrVersion", (const xmlChar*)dmr_version().c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute for dapVersion"); + + if (xmlTextWriterWriteAttribute(xml.get_writer(), (const xmlChar*) "name", (const xmlChar*)name().c_str()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not write attribute for name"); + + root()->print_dap4(xml, constrained); + + if (xmlTextWriterEndElement(xml.get_writer()) < 0) + throw InternalErr(__FILE__, __LINE__, "Could not end the top-level Group element"); +} + + +/** @brief dumps information about this object + * + * Displays the pointer value of this instance and then calls parent dump + * + * @param strm C++ i/o stream to dump the information to + * @return void + */ +void +DMR::dump(ostream &strm) const +{ + strm << DapIndent::LMarg << "DMR::dump - (" + << (void *)this << ")" << endl ; + DapIndent::Indent() ; + strm << DapIndent::LMarg << "factory: " << (void *)d_factory << endl ; + strm << DapIndent::LMarg << "name: " << d_name << endl ; + strm << DapIndent::LMarg << "filename: " << d_filename << endl ; + strm << DapIndent::LMarg << "protocol major: " << d_dap_major << endl; + strm << DapIndent::LMarg << "protocol minor: " << d_dap_minor << endl; + + DapIndent::UnIndent() ; +} + +} // namespace libdap diff --git a/DMR.h b/DMR.h new file mode 100644 index 0000000..fad1200 --- /dev/null +++ b/DMR.h @@ -0,0 +1,189 @@ +// -*- mode: c++; c-basic-offset:4 -*- + +// This file is part of libdap, A C++ implementation of the OPeNDAP Data +// Access Protocol. + +// Copyright (c) 2013 OPeNDAP, Inc. +// Author: James Gallagher +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112. + +#ifndef _dmr_h +#define _dmr_h 1 + +#include + +#include +#include +#include + +//#include "D4Group.h" +//#include "XMLWriter.h" +#include "DapObj.h" + +namespace libdap +{ + +class D4Group; +class D4BaseTypeFactory; +class XMLWriter; + +class DDS; + +/** DMR is root object for a DAP4 dataset. It holds a D4Group and other + * information about the dataset (DAP protocol number, DMR version, etc.). + * + * @note This class holds the dataset name and filename (which might + * actually be a database name, but it's usually a filename). The variables + * of a DAP4 dataset are held by the D4Group instance (which is a child + * of Constructor). + */ +class DMR : public DapObj +{ +private: + D4BaseTypeFactory *d_factory; + + /// The name of the dataset. This should not be the pathname to a file + string d_name; + /// The pathname or other system identifier for the dataset + string d_filename; + + /// DAP protocol major version number. Should be '4' + int d_dap_major; + /// DAP protocol minor version number. + int d_dap_minor; + /// String version of the DAP protocol number + string d_dap_version; + + /// The version of the DMR document + string d_dmr_version; + + /// The URL for the request base + string d_request_xml_base; + + /// The namespace to use when printing the XML serialization + string d_namespace; + + /// The maximum response size (in Kilo bytes) + long d_max_response_size; + + /// The root group; holds dimensions, enums, variables, groups, ... + D4Group *d_root; + + friend class DMRTest; + +protected: + void m_duplicate(const DMR &dmr); + +public: + DMR(); + DMR(const DMR &dmr); + DMR(D4BaseTypeFactory *factory, const string &name = ""); + + DMR(D4BaseTypeFactory *factory, DDS &dds); + + virtual ~DMR(); + + DMR &operator=(const DMR &rhs); + + virtual void build_using_dds(DDS &dds); + + /** + * Class invariant. If true, any method can be used. + * @return True if the instance is OK to use, false otherwise. + */ + bool OK() const { return (d_factory && d_root && !d_dap_version.empty()); } + + /** Get and set the DMR's name. This is effectively the 'dataset' name. + * It should not be used to reference the dataset's data store + * (e.g., it should not be a pathname to a file). This will be used in + * error messages. + */ + //@{ + string name() const { return d_name; } + void set_name(const string &n) { d_name = n; } + //@} + + /** Get/set the factory which makes instances of the variables. + Specialize D4BaseTypeFactory so that a DMR will be + populated with your client or server's specialized types.*/ + //@{ + virtual D4BaseTypeFactory *factory() { return d_factory; } + virtual void set_factory(D4BaseTypeFactory *f) { d_factory = f; } + //@} + + /** get/set the dataset's 'filename.' The filename is a string that can + * be used to access the dataset's actual data store (it's usually a + * pathname to a file, but it might be a database key. + */ + //@{ + string filename() const { return d_filename; } + void set_filename(const string &fn) { d_filename = fn;} + //@} + + string dap_version() const { return d_dap_version; } + void set_dap_version(const string &version_string); + int dap_major() const { return d_dap_major; } + int dap_minor() const { return d_dap_minor; } + + string dmr_version() const { return d_dmr_version; } + void set_dmr_version(const string &v) { d_dmr_version = v; } + + /// Get the URL that will return this DMR/DDX/DataThing + string request_xml_base() const { return d_request_xml_base; } + + /// @see get_request_xml_base + void set_request_xml_base(const string &xb) { d_request_xml_base = xb; } + + /// Get the namespace associated with the DDS - likely set only by DDX responses + string get_namespace() const { return d_namespace; } + + /// Set the namespace for this DDS/DDX object/response + void set_namespace(const string &ns) { d_namespace = ns; } + + // TODO Move the response_limit methods to D4ResponseBuilder? jhrg 5/1/13 + /// Get the maximum response size, in KB. Zero indicates no limit. + long response_limit() { return d_max_response_size; } + + /** Set the maximum response size. Zero is the default value. The size + is given in kilobytes. + @param size The maximum size of the response in kilobytes. */ + void set_response_limit(long size) { d_max_response_size = size; } + + /// Get the estimated response size, in kilo bytes + long request_size(bool constrained); + + /** Return the root group of this Dataset. If no root group has been + * set, use the D4BaseType factory to make it. + * @return The root group of the dataset. + */ + D4Group *root(); + + static DDS *getDDS(DMR &dmr); + virtual DDS *getDDS(); + + + + + void print_dap4(XMLWriter &xml, bool constrained = false); + + virtual void dump(ostream &strm) const ; +}; + +} // namespace libdap + +#endif // _dmr_h diff --git a/DODSFilter.cc b/DODSFilter.cc new file mode 100644 index 0000000..3d5f5d5 --- /dev/null +++ b/DODSFilter.cc @@ -0,0 +1,1190 @@ + +// -*- mode: c++; c-basic-offset:4 -*- + +// This file is part of libdap, A C++ implementation of the OPeNDAP Data +// Access Protocol. + +// Copyright (c) 2002,2003 OPeNDAP, Inc. +// Author: James Gallagher +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// +// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112. + +// (c) COPYRIGHT URI/MIT 1997-1999 +// Please read the full copyright statement in the file COPYRIGHT_URI. +// +// Authors: +// jhrg,jimg James Gallagher + +// Implementation of the DODSFilter class. This class is used to build dods +// filter programs which, along with a CGI program, comprise OPeNDAP servers. +// jhrg 8/26/97 + + +#include "config.h" + +#include + +#ifndef WIN32 +#include // for getopt +#include +#else +#include +#include +#include +#endif + +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_UUID_UUID_H +#include // used to build CID header value for data ddx +#elif defined(HAVE_UUID_H) +#include +#else +#error "Could not find UUID library header" +#endif + +#include + +#include "DAS.h" +#include "DDS.h" +#include "debug.h" +#include "mime_util.h" +#include "Ancillary.h" +#include "util.h" +#include "escaping.h" +#include "DODSFilter.h" +#include "XDRStreamMarshaller.h" +#include "InternalErr.h" + +#ifndef WIN32 +#include "SignalHandler.h" +#include "EventHandler.h" +#include "AlarmHandler.h" +#endif + +#define CRLF "\r\n" // Change here, expr-test.cc and DODSFilter.cc + +using namespace std; + +namespace libdap { + +const string usage = + "Usage: -o -u [options ...] [data set]\n\ + \n\ + options: -o : DAS, DDS, DataDDS, DDX, BLOB or Version (Required)\n\ + -u : The complete URL minus the CE (required for DDX)\n\ + -c: Compress the response using the deflate algorithm.\n\ + -e : When returning a DataDDS, use as the constraint.\n\ + -v : Use as the version number\n\ + -d : Look for ancillary file in (deprecated).\n\ + -f : Look for ancillary data in (deprecated).\n\ + -r : Use as a cache directory\n\ + -l