DDS.cc
// -*- 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 <jgallagher@opendap.org>
//
// 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 <jgallagher@gso.uri.edu>
//
// jhrg 9/7/94
#include "config.h"
#include <cstdio>
#include <cmath>
#include <climits>
//#include <cstdint>
//#include <sys/types.h>
#ifdef WIN32
#include <io.h>
#include <process.h>
#include <fstream>
#else
#include <unistd.h> // for alarm and dup
#include <sys/wait.h>
#endif
#include <iostream>
#include <sstream>
#include <algorithm>
#include <functional>
#include <memory>
// #define DODS_DEBUG
// #define DODS_DEBUG2
#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 "DAS.h"
#include "Clause.h"
#include "Error.h"
#include "InternalErr.h"
#include "escaping.h"
#include "parser.h"
#include "debug.h"
#include "util.h"
#include "DapIndent.h"
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;
const string TOP_LEVEL_ATTRS_CONTAINER_NAME = "DAP4_GLOBAL";
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... " <<endl);
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 &>(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_max_response_size_kb = dds.d_max_response_size_kb;
}
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_kb(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");
}
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_kb(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);
}
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)
{
if (this == &rhs)
return *this;
duplicate(rhs);
return *this;
}
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);
}
// Now we transfer all 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());
}
}
}
string
DDS::get_dataset_name() const
{
return d_name;
}
void
DDS::set_dataset_name(const string &n)
{
d_name = n;
}
AttrTable &
DDS::get_attr_table()
{
return d_attr;
}
string
DDS::filename() const
{
return d_filename;
}
void
DDS::filename(const string &fn)
{
d_filename = fn;
}
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();
}
}
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();
}
}
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.");
}
}
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());
}
string
DDS::container_name()
{
return d_container_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<Structure *>( 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<Structure *>( var( cn ) ) ;
}
}
d_container_name = cn;
}
Structure *
DDS::container()
{
return d_container ;
}
//[[deprecated("Use DDS::get_request_size_kb()")]]
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;
}
uint64_t DDS::get_request_size_kb(bool constrained)
{
uint64_t req_size = 0;
for (auto &btp: vars) {
if (constrained) {
if (btp->send_p())
req_size += btp->width_ll(constrained);
}
else {
req_size += btp->width_ll(constrained);
}
}
return req_size / 1024;
}
void DDS::add_var(BaseType *bt) {
if (!bt)
throw InternalErr(__FILE__, __LINE__, "Trying to add a BaseType object with a NULL pointer.");
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);
}
}
void
DDS::add_var_nocopy(BaseType *bt)
{
if (!bt)
throw InternalErr(__FILE__, __LINE__, "Trying to add a BaseType object with a NULL pointer.");
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);
}
}
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;
}
}
}
void
DDS::del_var(Vars_iter i)
{
if (i != vars.end()) {
BaseType *bt = *i ;
vars.erase(i) ;
delete bt ; bt = 0;
}
}
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) ;
}
BaseType *
DDS::var(const string &n, BaseType::btp_stack &s)
{
return var(n, &s);
}
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.
}
void
DDS::insert_var(Vars_iter i, BaseType *ptr)
{
vars.insert(i, ptr->ptr_duplicate());
}
void
DDS::insert_var_nocopy(Vars_iter i, BaseType *ptr)
{
vars.insert(i, ptr);
}
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;
}
void
DDS::tag_nested_sequences()
{
for (Vars_iter i = vars.begin(); i != vars.end(); i++) {
if ((*i)->type() == dods_sequence_c)
dynamic_cast<Sequence&>(**i).set_leaf_sequence();
else if ((*i)->type() == dods_structure_c)
dynamic_cast<Structure&>(**i).set_leaf_sequence();
}
}
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 ;
}
}
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 ;
}
}
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();
}
}
void
DDS::print(FILE *out)
{
ostringstream oss;
print(oss);
fwrite(oss.str().data(), sizeof(char), oss.str().length(), out);
}
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 ;
}
bool
has_dap2_attributes(AttrTable &a)
{
for (AttrTable::Attr_iter i = a.attr_begin(), e = a.attr_end(); i != e; ++i) {
if (a.get_attr_type(i) != Attr_container) {
return true;
}
else if (has_dap2_attributes(*a.get_attr_table(i))) {
return true;
}
}
return false;
}
bool
has_dap2_attributes(BaseType *btp)
{
if (btp->get_attr_table().get_size() && has_dap2_attributes(btp->get_attr_table())) {
return true;
}
Constructor *cons = dynamic_cast<Constructor *>(btp);
if (cons) {
Grid* grid = dynamic_cast<Grid*>(btp);
if(grid){
return has_dap2_attributes(grid->get_array());
}
else {
for (Constructor::Vars_iter i = cons->var_begin(), e = cons->var_end(); i != e; i++) {
if (has_dap2_attributes(*i)) return true;
}
}
}
return false;
}
static string four_spaces = " ";
void print_var_das(ostream &out, BaseType *bt, string indent = "") {
if (!has_dap2_attributes(bt))
return;
AttrTable attr_table = bt->get_attr_table();
out << indent << add_space_encoding(bt->name()) << " {" << endl;
Constructor *cnstrctr = dynamic_cast<Constructor *>(bt);
if (cnstrctr) {
Grid *grid = dynamic_cast<Grid *>(bt);
if (grid) {
Array *gridArray = grid->get_array();
AttrTable arrayAT = gridArray->get_attr_table();
if (has_dap2_attributes(gridArray))
gridArray->get_attr_table().print(out, indent + four_spaces);
#if 0
// I dropped this because we don't want the MAP vectors showing up in the DAS
// as children of a Grid (aka flatten the Grid bro) - ndp 5/25/18
for (Grid::Map_iter mIter = grid->map_begin();
mIter != grid->map_end(); ++mIter) {
BaseType *currentMap = *mIter;
if (has_dap2_attributes(currentMap))
print_var_das(out, currentMap, indent + four_spaces);
}
#endif
}
else {
attr_table.print(out, indent + four_spaces);
Constructor::Vars_iter i = cnstrctr->var_begin();
Constructor::Vars_iter e = cnstrctr->var_end();
for (; i != e; i++) {
// Only call print_var_das() if there really are attributes.
// This is made complicated because while there might be none
// for a particular var (*i), that var might be a ctor and its
// descendant might have an attribute. jhrg 3/18/18
if (has_dap2_attributes(*i))
print_var_das(out, *i, indent + four_spaces);
}
}
}
else {
attr_table.print(out, indent + four_spaces);
}
out << indent << "}" << endl;
}
void
DDS::print_das(ostream &out)
{
unique_ptr<DAS> das(get_das());
das->print(out);
}
DAS *
DDS::get_das()
{
DAS *das = new DAS();
get_das(das);
return das;
}
static string
get_unique_top_level_global_container_name(DAS *das)
{
// It's virtually certain that the TOP_LEVE... name will be unique. If so,
// return the name. The code tests for a table to see if the name _should not_ be used.
AttrTable *table = das->get_table(TOP_LEVEL_ATTRS_CONTAINER_NAME);
if (!table)
return TOP_LEVEL_ATTRS_CONTAINER_NAME;
// ... but the default name might already be used
unsigned int i = 0;
string name;
ostringstream oss;
while (table) {
oss.str(""); // reset to empty for the next suffix
oss << "_" << ++i;
if (!(i < UINT_MAX))
throw InternalErr(__FILE__, __LINE__, "Cannot add top-level attributes to the DAS");
name = TOP_LEVEL_ATTRS_CONTAINER_NAME + oss.str();
table = das->get_table(name);
}
return name;
}
void fillConstructorAttrTable(AttrTable *at, BaseType *bt){
Constructor *cons = dynamic_cast<Constructor *>(bt);
if (cons) {
Grid *grid = dynamic_cast<Grid *>(bt);
if(grid){
Array *gridArray = grid->get_array();
AttrTable arrayAT = gridArray->get_attr_table();
for( AttrTable::Attr_iter atIter = arrayAT.attr_begin(); atIter!=arrayAT.attr_end(); ++atIter){
AttrType type = arrayAT.get_attr_type(atIter);
string childName = arrayAT.get_name(atIter);
if (type == Attr_container){
at->append_container( new AttrTable(*arrayAT.get_attr_table(atIter)), childName);
}
else {
vector<string>* pAttrTokens = arrayAT.get_attr_vector(atIter);
// append_attr makes a copy of the vector, so we don't have to do so here.
at->append_attr(childName, AttrType_to_String(type), pAttrTokens,(*atIter)->is_utf8_str);
}
}
}
else {
for (Constructor::Vars_iter i = cons->var_begin(), e = cons->var_end(); i != e; i++) {
if (has_dap2_attributes(*i)) {
AttrTable *childAttrT = new AttrTable((*i)->get_attr_table());
fillConstructorAttrTable(childAttrT, *i);
at->append_container(childAttrT,(*i)->name());
}
}
}
}
}
void DDS::get_das(DAS *das)
{
for (Vars_citer i = vars.begin(); i != vars.end(); i++) {
if (has_dap2_attributes(*i)) {
AttrTable *childAttrT = new AttrTable((*i)->get_attr_table());
fillConstructorAttrTable(childAttrT, *i);
das->add_table((*i)->name(), childAttrT);
}
}
// Used in the rare case we have global attributes not in a table.
unique_ptr<AttrTable> global(new AttrTable);
for (AttrTable::Attr_iter i = d_attr.attr_begin(); i != d_attr.attr_end(); ++i) {
// It's possible, given the API and if the DDS was built from a DMR, that a
// global attribute might not be a container; check for that.
if (d_attr.get_attr_table(i)) {
das->add_table(d_attr.get_name(i), new AttrTable(*(d_attr.get_attr_table(i))));
}
else {
// This must be a top level attribute outside a container. jhrg 4/6/18
global->append_attr(d_attr.get_name(i), d_attr.get_type(i), d_attr.get_attr_vector(i),(*i)->is_utf8_str);
}
}
// if any attributes were added to 'global,' add it to the DAS and take control of the pointer.
if (global->get_size() > 0) {
das->add_table(get_unique_top_level_global_container_name(das), global.get()); // What if this name is not unique?
global.release();
}
}
void
DDS::print_constrained(FILE *out)
{
ostringstream oss;
print_constrained(oss);
fwrite(oss.str().data(), sizeof(char), oss.str().length(), out);
}
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;
}
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);
}
void
DDS::print_xml(ostream &out, bool constrained, const string &blob)
{
print_xml_writer(out, constrained, blob);
}
class VariablePrintXMLWriter : public unary_function<BaseType *, void>
{
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);
}
};
void
DDS::print_xml_writer(ostream &out, bool constrained, const string &blob)
{
XMLWriter xml(" ");
// This is the 'DAP 3.2' DDX response - now the only response libdap will return.
// jhrg 9/10/18
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");
}
// Print the global attributes
d_attr.print_xml_writer(xml);
// Print each variable
for_each(var_begin(), var_end(), VariablePrintXMLWriter(xml, constrained));
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");
if (xmlTextWriterEndElement(xml.get_writer()) < 0)
throw InternalErr(__FILE__, __LINE__, "Could not end Dataset element");
out << xml.get_doc();// << ends;// << endl;
}
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 (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.
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;
}
bool
DDS::mark(const string &n, bool state)
{
unique_ptr<BaseType::btp_stack> s(new BaseType::btp_stack);
DBG2(cerr << "DDS::mark: Looking for " << n << endl);
BaseType *variable = var(n, s.get());
if (!variable) {
throw Error(malformed_expr, "Could not find variable " + n);
}
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);
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);
s->pop();
}
return true;
}
void
DDS::mark_all(bool state)
{
for (Vars_iter i = vars.begin(); i != vars.end(); i++)
(*i)->set_send_p(state);
}
bool DDS::is_dap4_projected(std::vector<string> &inventory)
{
bool has_dap4 = d_attr.has_dap4_types("/", inventory);
for(const auto var : variables()){
has_dap4 |= var->is_dap4_projected(inventory);
}
return has_dap4;
}
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
libdap::Array
A multidimensional array of identical data types.
Definition Array.h:123
libdap::AttrTable
Contains the attributes for a dataset.
Definition AttrTable.h:154
libdap::AttrTable::append_container
virtual AttrTable * append_container(const string &name)
Add a container to the attribute table.
Definition AttrTable.cc:554
libdap::AttrTable::has_dap4_types
bool has_dap4_types(const std::string &path, std::vector< std::string > &inventory) const
Definition AttrTable.cc:1184
libdap::AttrTable::get_attr_table
virtual AttrTable * get_attr_table(const string &name)
Get an attribute container.
Definition AttrTable.cc:751
libdap::AttrTable::attr_end
virtual Attr_iter attr_end()
Definition AttrTable.cc:863
libdap::AttrTable::get_type
virtual string get_type(const string &name)
Get the type name of an attribute within this attribute table.
Definition AttrTable.cc:757
libdap::AttrTable::get_attr_vector
virtual vector< string > * get_attr_vector(const string &name)
Get a vector-valued attribute.
Definition AttrTable.cc:797
libdap::AttrTable::append_attr
virtual unsigned int append_attr(const string &name, const string &type, const string &value)
Add an attribute to the table.
Definition AttrTable.cc:335
libdap::AttrTable::attr_begin
virtual Attr_iter attr_begin()
Definition AttrTable.cc:855
libdap::AttrTable::get_name
virtual string get_name() const
Get the name of this attribute table.
Definition AttrTable.cc:266
libdap::AttrTable::print_xml_writer
void print_xml_writer(XMLWriter &xml)
Definition AttrTable.cc:1614
libdap::AttrTable::get_size
virtual unsigned int get_size() const
Get the number of entries in this attribute table.
Definition AttrTable.cc:259
libdap::AttrTable::dump
virtual void dump(ostream &strm) const
dumps information about this object
Definition AttrTable.cc:1715
libdap::AttrTable::get_attr_type
virtual AttrType get_attr_type(const string &name)
Get the type of an attribute.
Definition AttrTable.cc:765
libdap::BaseTypeFactory
Definition BaseTypeFactory.h:81
libdap::BaseType
The basic data type for the DODS DAP types.
Definition BaseType.h:120
libdap::BaseType::is_dap4_projected
virtual bool is_dap4_projected(std::vector< string > &projected_dap4_inventory)
Definition BaseType.cc:1323
libdap::BaseType::type_name
virtual string type_name() const
Returns the type of the class instance as a string.
Definition BaseType.cc:376
libdap::BaseType::get_attr_table
virtual AttrTable & get_attr_table()
Definition BaseType.cc:579
libdap::BaseType::name
virtual string name() const
Returns the name of the class instance.
Definition BaseType.cc:317
libdap::BaseType::is_vector_type
virtual bool is_vector_type() const
Returns true if the instance is a vector (i.e., array) type variable.
Definition BaseType.cc:399
libdap::BaseType::is_constructor_type
virtual bool is_constructor_type() const
Returns true if the instance is a constructor (i.e., Structure, Sequence or Grid) type variable.
Definition BaseType.cc:409
libdap::BaseType::set_send_p
virtual void set_send_p(bool state)
Definition BaseType.cc:565
libdap::BaseType::ptr_duplicate
virtual BaseType * ptr_duplicate()=0
libdap::BaseType::var
virtual BaseType * var(const string &name="", bool exact_match=true, btp_stack *s=nullptr)
Returns a pointer to a member of a constructor class.
Definition BaseType.cc:764
libdap::Constructor
Definition Constructor.h:44
libdap::Constructor::var
BaseType * var(const string &name, bool exact_match=true, btp_stack *s=nullptr) override
btp_stack no longer needed; use back pointers (BaseType::get_parent())
Definition Constructor.cc:210
libdap::Constructor::add_var
void add_var(BaseType *bt, Part part=nil) override
Definition Constructor.cc:375
libdap::Constructor::var_end
Vars_iter var_end()
Definition Constructor.cc:305
libdap::Constructor::add_var_nocopy
void add_var_nocopy(BaseType *bt, Part part=nil) override
Definition Constructor.cc:395
libdap::Constructor::width
unsigned int width(bool constrained=false) const override
Definition Constructor.cc:169
libdap::Constructor::var_begin
Vars_iter var_begin()
Definition Constructor.cc:297
libdap::Constructor::del_var
virtual void del_var(const string &name)
Remove an element from a Constructor.
Definition Constructor.cc:416
libdap::DAS
Hold attribute data for a DAP2 dataset.
Definition DAS.h:122
libdap::DAS::get_top_level_attributes
virtual AttrTable * get_top_level_attributes()
Returns the top most set of attributes.
Definition DAS.h:166
libdap::DAS::get_table
AttrTable * get_table(AttrTable::Attr_iter &i)
Returns the referenced variable attribute table.
Definition DAS.cc:177
libdap::DAS::container_name
virtual string container_name() const
Returns the name of the current attribute container when multiple files used to build this DAS.
Definition DAS.h:149
libdap::DDS
Definition DDS.h:178
libdap::DDS::set_dataset_name
void set_dataset_name(const string &n)
Definition DDS.cc:292
libdap::DDS::set_dap_major
void set_dap_major(int p)
Definition DDS.cc:333
libdap::DDS::mark_all
void mark_all(bool state)
Definition DDS.cc:1534
libdap::DDS::print_dmr
void print_dmr(ostream &out, bool constrained)
Print the DAP4 DMR object using a DDS.
Definition DDS.cc:1377
libdap::DDS::add_var_nocopy
void add_var_nocopy(BaseType *bt)
Adds the variable to the DDS.
Definition DDS.cc:568
libdap::DDS::check_semantics
bool check_semantics(bool all=false)
Check the semantics of each of the variables represented in the DDS.
Definition DDS.cc:1448
libdap::DDS::filename
string filename() const
Definition DDS.cc:316
libdap::DDS::get_attr_table
virtual AttrTable & get_attr_table()
Definition DDS.cc:301
libdap::DDS::get_request_size_kb
uint64_t get_request_size_kb(bool constrained)
Get the estimated response size in kilobytes.
Definition DDS.cc:522
libdap::DDS::transfer_attributes
virtual void transfer_attributes(DAS *das)
Definition DDS.cc:246
libdap::DDS::set_dap_minor
void set_dap_minor(int p)
Definition DDS.cc:352
libdap::DDS::get_namespace
string get_namespace() const
Get the namespace associated with the DDS - likely set only by DDX responses.
Definition DDS.h:279
libdap::DDS::num_var
int num_var()
Returns the number of variables in the DDS.
Definition DDS.cc:775
libdap::DDS::print
void print(FILE *out)
Print the entire DDS to the specified file.
Definition DDS.cc:925
libdap::DDS::get_request_size
int get_request_size(bool constrained)
Get the estimated response size in bytes.
Definition DDS.cc:495
libdap::DDS::is_dap4_projected
bool is_dap4_projected(std::vector< string > &inventory)
Definition DDS.cc:1545
libdap::DDS::get_dataset_name
string get_dataset_name() const
Definition DDS.cc:285
libdap::DDS::del_var
void del_var(const string &n)
Removes a variable from the DDS.
Definition DDS.cc:590
libdap::DDS::parse
void parse(string fname)
Parse a DDS from a file with the given d_name.
Definition DDS.cc:844
libdap::DDS::var
BaseType * var(const string &n, BaseType::btp_stack &s)
Definition DDS.cc:646
libdap::DDS::print_xml
void print_xml(FILE *out, bool constrained, const string &blob="")
Definition DDS.cc:1250
libdap::DDS::insert_var
void insert_var(Vars_iter i, BaseType *ptr)
Insert a variable before the referenced element.
Definition DDS.cc:755
libdap::DDS::mark
bool mark(const string &name, bool state)
Mark the send_p flag of the named variable to state.
Definition DDS.cc:1492
libdap::DDS::DDS
DDS(BaseTypeFactory *factory, const string &name="")
Definition DDS.cc:168
libdap::DDS::tag_nested_sequences
void tag_nested_sequences()
Traverse DDS, set Sequence leaf nodes.
Definition DDS.cc:832
libdap::DDS::get_das
DAS * get_das()
Get a DAS object.
Definition DDS.cc:1083
libdap::DDS::var_begin
Vars_iter var_begin()
Definition DDS.h:344
libdap::DDS::print_constrained
void print_constrained(FILE *out)
Print a constrained DDS to the specified file.
Definition DDS.cc:1204
libdap::DDS::container_name
string container_name()
Definition DDS.cc:440
libdap::DDS::insert_var_nocopy
void insert_var_nocopy(Vars_iter i, BaseType *ptr)
Definition DDS.cc:768
libdap::DDS::get_request_xml_base
string get_request_xml_base() const
Get the URL that will return this DDS/DDX/DataThing.
Definition DDS.h:273
libdap::DDS::var_end
Vars_iter var_end()
Return an iterator.
Definition DDS.h:349
libdap::DDS::get_dap_major
int get_dap_major() const
Get the DAP major version as sent by the client.
Definition DDS.h:257
libdap::DDS::variables
const vector< BaseType * > & variables() const
Definition DDS.h:339
libdap::DDS::set_dap_version
void set_dap_version(const string &version_string="2.0")
Definition DDS.cc:369
libdap::DDS::container
Structure * container()
Definition DDS.cc:476
libdap::DDS::add_var
void add_var(BaseType *bt)
Adds a copy of the variable to the DDS. Using the ptr_duplicate() method, perform a deep copy on the ...
Definition DDS.cc:544
libdap::DDS::print_xml_writer
void print_xml_writer(ostream &out, bool constrained, const string &blob="")
Definition DDS.cc:1305
libdap::DDS::print_das
void print_das(ostream &out)
write the DAS response given the attribute information in the DDS
Definition DDS.cc:1066
libdap::DDS::dump
virtual void dump(ostream &strm) const
dumps information about this object
Definition DDS.cc:1563
libdap::DapObj
libdap base object for common functionality of libdap objects
Definition DapObj.h:51
libdap::Error
A class for error processing.
Definition Error.h:94
libdap::Grid
Holds the Grid data type.
Definition Grid.h:123
libdap::Grid::get_array
Array * get_array()
Returns the Grid Array. This method returns the array using an Array*, so no cast is required.
Definition Grid.cc:498
libdap::InternalErr
A class for software fault reporting.
Definition InternalErr.h:65
libdap::Sequence
Holds a sequence.
Definition Sequence.h:163
libdap::Sequence::set_leaf_sequence
virtual void set_leaf_sequence(int lvl=1)
Mark the Sequence which holds the leaf elements.
Definition Sequence.cc:1240
libdap::Structure
Holds a structure (aggregate) type.
Definition Structure.h:84
libdap::Structure::set_leaf_sequence
virtual void set_leaf_sequence(int level=1)
Traverse Structure, set Sequence leaf nodes.
Definition Structure.cc:309
libdap::XMLWriter
Definition XMLWriter.h:39
libdap
top level DAP object to house generic methods
Definition AlarmHandler.h:36
libdap::has_dap2_attributes
bool has_dap2_attributes(AttrTable &a)
Definition DDS.cc:955
libdap::www2id
string www2id(const string &in, const string &escape, const string &except)
Definition escaping.cc:220
libdap::add_space_encoding
string add_space_encoding(const string &s)
Definition AttrTable.cc:78
libdap::AttrType_to_String
string AttrType_to_String(const AttrType at)
Definition AttrTable.cc:97
libdap::AttrType
AttrType
Definition AttrTable.h:81
libdap::fillConstructorAttrTable
void fillConstructorAttrTable(AttrTable *at, BaseType *bt)
Recursive helper function for Building DAS entries for Constructor types.
Definition DDS.cc:1127
libdap::id2www
string id2www(string in, const string &allowable)
Definition escaping.cc:153
libdap::parser_arg
Pass parameters by reference to a parser.
Definition parser.h:69