|
Packit |
a4aae4 |
// -*- mode: c++; c-basic-offset:4 -*-
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
// This file is part of libdap, A C++ implementation of the OPeNDAP Data
|
|
Packit |
a4aae4 |
// Access Protocol.
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
// Copyright (c) 2002,2003 OPeNDAP, Inc.
|
|
Packit |
a4aae4 |
// Author: James Gallagher <jgallagher@opendap.org>
|
|
Packit |
a4aae4 |
//
|
|
Packit |
a4aae4 |
// This library is free software; you can redistribute it and/or
|
|
Packit |
a4aae4 |
// modify it under the terms of the GNU Lesser General Public
|
|
Packit |
a4aae4 |
// License as published by the Free Software Foundation; either
|
|
Packit |
a4aae4 |
// version 2.1 of the License, or (at your option) any later version.
|
|
Packit |
a4aae4 |
//
|
|
Packit |
a4aae4 |
// This library is distributed in the hope that it will be useful,
|
|
Packit |
a4aae4 |
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
a4aae4 |
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Packit |
a4aae4 |
// Lesser General Public License for more details.
|
|
Packit |
a4aae4 |
//
|
|
Packit |
a4aae4 |
// You should have received a copy of the GNU Lesser General Public
|
|
Packit |
a4aae4 |
// License along with this library; if not, write to the Free Software
|
|
Packit |
a4aae4 |
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
Packit |
a4aae4 |
//
|
|
Packit |
a4aae4 |
// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
#include <string>
|
|
Packit |
a4aae4 |
#include <sstream>
|
|
Packit |
a4aae4 |
#include <iterator>
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
//#define DODS_DEBUG
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
#include "D4CEScanner.h"
|
|
Packit |
a4aae4 |
#include "D4ConstraintEvaluator.h"
|
|
Packit |
a4aae4 |
#include "d4_ce_parser.tab.hh"
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
#include "DMR.h"
|
|
Packit |
a4aae4 |
#include "D4Group.h"
|
|
Packit |
a4aae4 |
#include "D4Dimensions.h"
|
|
Packit |
a4aae4 |
#include "D4Maps.h"
|
|
Packit |
a4aae4 |
#include "BaseType.h"
|
|
Packit |
a4aae4 |
#include "Array.h"
|
|
Packit |
a4aae4 |
#include "Constructor.h"
|
|
Packit |
a4aae4 |
#include "D4Sequence.h"
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
#include "D4RValue.h"
|
|
Packit |
a4aae4 |
#include "D4FilterClause.h"
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
#include "escaping.h"
|
|
Packit |
a4aae4 |
#include "parser.h" // for get_ull()
|
|
Packit |
a4aae4 |
#include "debug.h"
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
namespace libdap {
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
bool D4ConstraintEvaluator::parse(const std::string &expr)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
d_expr = expr; // set for error messages. See the %initial-action section of .yy
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
std::istringstream iss(expr);
|
|
Packit |
a4aae4 |
D4CEScanner scanner(iss);
|
|
Packit |
a4aae4 |
D4CEParser parser(scanner, *this /* driver */);
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
if (trace_parsing()) {
|
|
Packit |
a4aae4 |
parser.set_debug_level(1);
|
|
Packit |
a4aae4 |
parser.set_debug_stream(std::cerr);
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
return parser.parse() == 0;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
void
|
|
Packit |
a4aae4 |
D4ConstraintEvaluator::throw_not_found(const string &id, const string &ident)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
throw Error(no_such_variable, d_expr + ": The variable " + id + " was not found in the dataset (" + ident + ").");
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
void
|
|
Packit |
a4aae4 |
D4ConstraintEvaluator::throw_not_array(const string &id, const string &ident)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
throw Error(no_such_variable, d_expr + ": The variable '" + id + "' is not an Array variable (" + ident + ").");
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
void
|
|
Packit |
a4aae4 |
D4ConstraintEvaluator::search_for_and_mark_arrays(BaseType *btp)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
DBG(cerr << "Entering D4ConstraintEvaluator::search_for_and_mark_arrays...(" << btp->name() << ")" << endl);
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
assert(btp->is_constructor_type());
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
Constructor *ctor = static_cast<Constructor*>(btp);
|
|
Packit |
a4aae4 |
for (Constructor::Vars_iter i = ctor->var_begin(), e = ctor->var_end(); i != e; ++i) {
|
|
Packit |
a4aae4 |
switch ((*i)->type()) {
|
|
Packit |
a4aae4 |
case dods_array_c:
|
|
Packit |
a4aae4 |
DBG(cerr << "Found an array: " << (*i)->name() << endl);
|
|
Packit |
a4aae4 |
mark_array_variable(*i);
|
|
Packit |
a4aae4 |
break;
|
|
Packit |
a4aae4 |
case dods_structure_c:
|
|
Packit |
a4aae4 |
case dods_sequence_c:
|
|
Packit |
a4aae4 |
DBG(cerr << "Found a ctor: " << (*i)->name() << endl);
|
|
Packit |
a4aae4 |
search_for_and_mark_arrays(*i);
|
|
Packit |
a4aae4 |
break;
|
|
Packit |
a4aae4 |
default:
|
|
Packit |
a4aae4 |
break;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
/**
|
|
Packit |
a4aae4 |
* When an identifier is used in a CE, is becomes part of the 'current projection,'
|
|
Packit |
a4aae4 |
* which means it is part of the set of variables to be sent back to the client. This
|
|
Packit |
a4aae4 |
* method sets a flag in the variable (send_p; pronounced 'send predicate') indicating that.
|
|
Packit |
a4aae4 |
*
|
|
Packit |
a4aae4 |
* @note This will check if the variable is an array and set it's slices accordingly
|
|
Packit |
a4aae4 |
* @param btp BaseType pointer to the variable. Must be non-null
|
|
Packit |
a4aae4 |
* @return The BaseType* to the variable; the send_p flag is set as a side effect.
|
|
Packit |
a4aae4 |
*/
|
|
Packit |
a4aae4 |
BaseType *
|
|
Packit |
a4aae4 |
D4ConstraintEvaluator::mark_variable(BaseType *btp)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
assert(btp);
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
DBG(cerr << "In D4ConstraintEvaluator::mark_variable... (" << btp->name() << "; " << btp->type_name() << ")" << endl);
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
btp->set_send_p(true);
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
if (btp->type() == dods_array_c ) {
|
|
Packit |
a4aae4 |
mark_array_variable(btp);
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
// Test for Constructors and marks arrays they contain
|
|
Packit |
a4aae4 |
if (btp->is_constructor_type()) {
|
|
Packit |
a4aae4 |
search_for_and_mark_arrays(btp);
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
else if (btp->type() == dods_array_c && btp->var() && btp->var()->is_constructor_type()) {
|
|
Packit |
a4aae4 |
search_for_and_mark_arrays(btp->var());
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
// Now set the parent variables
|
|
Packit |
a4aae4 |
BaseType *parent = btp->get_parent();
|
|
Packit |
a4aae4 |
while (parent) {
|
|
Packit |
a4aae4 |
parent->BaseType::set_send_p(true); // Just set the parent using BaseType's impl.
|
|
Packit |
a4aae4 |
parent = parent->get_parent();
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
return btp;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
static bool
|
|
Packit |
a4aae4 |
array_uses_shared_dimension(Array *map, D4Dimension *source_dim)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
for (Array::Dim_iter d = map->dim_begin(), e = map->dim_end(); d != e; ++d) {
|
|
Packit |
a4aae4 |
if (source_dim->name() == (*d).name)
|
|
Packit |
a4aae4 |
return true;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
return false;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
/**
|
|
Packit |
a4aae4 |
* Add an array to the current projection with slicing. Calling this method will result
|
|
Packit |
a4aae4 |
* in the array being returned with anonymous dimensions.
|
|
Packit |
a4aae4 |
*
|
|
Packit |
a4aae4 |
* @note If btp is an array that has shared dimensions and uses '[]' where a shared dimension
|
|
Packit |
a4aae4 |
* is found and if that shared dimension has been sliced, then the slice is used as the array's
|
|
Packit |
a4aae4 |
* slice for that dimension (there must be an easier way to explain that...)
|
|
Packit |
a4aae4 |
*
|
|
Packit |
a4aae4 |
* @param btp
|
|
Packit |
a4aae4 |
* @return The BaseType* to the Array variable; the send_p and slicing information is
|
|
Packit |
a4aae4 |
* set as a side effect.
|
|
Packit |
a4aae4 |
*/
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
// Note: If a Map is not part of the current projection, do not include mention of it
|
|
Packit |
a4aae4 |
// in the response DMR (CDMR)
|
|
Packit |
a4aae4 |
BaseType *
|
|
Packit |
a4aae4 |
D4ConstraintEvaluator::mark_array_variable(BaseType *btp)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
assert(btp->type() == dods_array_c);
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
Array *a = static_cast<Array*>(btp);
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
// If an array appears in a CE without the slicing operators ([]) we still have to
|
|
Packit |
a4aae4 |
// call add_constraint(...) for all of it's sdims for them to appear in
|
|
Packit |
a4aae4 |
// the Constrained DMR.
|
|
Packit |
a4aae4 |
if (d_indexes.empty()) {
|
|
Packit |
a4aae4 |
for (Array::Dim_iter d = a->dim_begin(), de = a->dim_end(); d != de; ++d) {
|
|
Packit |
a4aae4 |
D4Dimension *dim = a->dimension_D4dim(d);
|
|
Packit |
a4aae4 |
if (dim) {
|
|
Packit |
a4aae4 |
a->add_constraint(d, dim);
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
else {
|
|
Packit |
a4aae4 |
// Test that the indexes and dimensions match in number
|
|
Packit |
a4aae4 |
if (d_indexes.size() != a->dimensions())
|
|
Packit |
a4aae4 |
throw Error(malformed_expr, "The index constraint for '" + btp->name() + "' does not match its rank.");
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
Array::Dim_iter d = a->dim_begin();
|
|
Packit |
a4aae4 |
for (vector<index>::iterator i = d_indexes.begin(), e = d_indexes.end(); i != e; ++i) {
|
|
Packit |
a4aae4 |
if ((*i).stride > (unsigned long long) (a->dimension_stop(d, false) - a->dimension_start(d, false)) + 1)
|
|
Packit |
a4aae4 |
throw Error(malformed_expr, "For '" + btp->name() + "', the index stride value is greater than the number of elements in the Array");
|
|
Packit |
a4aae4 |
if (!(*i).rest && ((*i).stop) > (unsigned long long) (a->dimension_stop(d, false) - a->dimension_start(d, false)) + 1)
|
|
Packit |
a4aae4 |
throw Error(malformed_expr, "For '" + btp->name() + "', the index stop value is greater than the number of elements in the Array");
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
D4Dimension *dim = a->dimension_D4dim(d);
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
// In a DAP4 CE, specifying '[]' as an array dimension slice has two meanings.
|
|
Packit |
a4aae4 |
// It can mean 'all the elements' of the dimension or 'apply the slicing inherited
|
|
Packit |
a4aae4 |
// from the shared dimension'. The latter might be provide 'all the elements'
|
|
Packit |
a4aae4 |
// but regardless, the Array object must record the CE correctly.
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
if (dim && (*i).empty) {
|
|
Packit |
a4aae4 |
// This case corresponds to a CE that uses the '[]' notation for a
|
|
Packit |
a4aae4 |
// particular dimension - meaning, use the Shared Dimension size for
|
|
Packit |
a4aae4 |
// this dimension's 'slice'.
|
|
Packit |
a4aae4 |
a->add_constraint(d, dim); // calls set_used_by_projected_var(true) + more
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
else {
|
|
Packit |
a4aae4 |
// This case corresponds to a 'local dimension slice' (See sections 8.6.2 and
|
|
Packit |
a4aae4 |
// 8.7 of the spec as of 4/12/16). When a local dimension slice is used, drop
|
|
Packit |
a4aae4 |
// the Map(s) that include that dimension. This enables people to constrain
|
|
Packit |
a4aae4 |
// an Array when some of the Array's dimensions don't use Shared Dimensions
|
|
Packit |
a4aae4 |
// but others do.
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
// First apply the constraint to the Array's dimension
|
|
Packit |
a4aae4 |
a->add_constraint(d, (*i).start, (*i).stride, (*i).rest ? -1 : (*i).stop);
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
// Then, if the Array has Maps, scan those Maps for any that use dimensions
|
|
Packit |
a4aae4 |
// that match the name of this particular dimension. If any such Maps are found
|
|
Packit |
a4aae4 |
// remove them. This ensure that the Array can be constrained using the 'local
|
|
Packit |
a4aae4 |
// dimension slice' without the constrained DMR containing references to Maps
|
|
Packit |
a4aae4 |
// that don't exist (or are otherwise nonsensical).
|
|
Packit |
a4aae4 |
//
|
|
Packit |
a4aae4 |
// This code came about as a fix for problems discovered during testing of
|
|
Packit |
a4aae4 |
// local dimension slices. See https://opendap.atlassian.net/browse/HYRAX-98
|
|
Packit |
a4aae4 |
// jhrg 4/12/16
|
|
Packit |
a4aae4 |
if (!a->maps()->empty()) {
|
|
Packit |
a4aae4 |
for(D4Maps::D4MapsIter m = a->maps()->map_begin(), e = a->maps()->map_end(); m != e; ++m) {
|
|
Packit |
a4aae4 |
if ((*m)->array() == 0)
|
|
Packit |
a4aae4 |
throw Error(malformed_expr, "An array with Maps was found, but one of the Maps was not defined correctly.");
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
Array *map = const_cast<Array*>((*m)->array()); // Array lacks const iterator support
|
|
Packit |
a4aae4 |
// Added a test to ensure 'dim' is not null. This could be the case if
|
|
Packit |
a4aae4 |
// execution gets here and the index *i was not empty. jhrg 4/18/17
|
|
Packit |
a4aae4 |
if (dim && array_uses_shared_dimension(map, dim)) {
|
|
Packit |
a4aae4 |
D4Map *map_to_be_removed = *m;
|
|
Packit |
a4aae4 |
a->maps()->remove_map(map_to_be_removed); // Invalidates the iterator
|
|
Packit |
a4aae4 |
delete map_to_be_removed; // removed from container; delete
|
|
Packit |
a4aae4 |
break; // must leave the for loop because 'm' is now invalid
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
++d;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
d_indexes.clear(); // Clear the info so the next slice expression can be parsed.
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
return btp;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
/**
|
|
Packit |
a4aae4 |
* Add an array to the current projection with slicing. Calling this method will result
|
|
Packit |
a4aae4 |
* in the array being returned with anonymous dimensions.
|
|
Packit |
a4aae4 |
*
|
|
Packit |
a4aae4 |
* @param id
|
|
Packit |
a4aae4 |
* @return The BaseType* to the Array variable; the send_p and slicing information is
|
|
Packit |
a4aae4 |
* set as a side effect.
|
|
Packit |
a4aae4 |
*/
|
|
Packit |
a4aae4 |
D4Dimension *
|
|
Packit |
a4aae4 |
D4ConstraintEvaluator::slice_dimension(const std::string &id, const index &i)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
D4Dimension *dim = dmr()->root()->find_dim(id);
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
if (i.stride > dim->size())
|
|
Packit |
a4aae4 |
throw Error(malformed_expr, "For '" + id + "', the index stride value is greater than the size of the dimension");
|
|
Packit |
a4aae4 |
if (!i.rest && (i.stop > dim->size() - 1))
|
|
Packit |
a4aae4 |
throw Error(malformed_expr, "For '" + id + "', the index stop value is greater than the size of the dimension");
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
dim->set_constraint(i.start, i.stride, i.rest ? dim->size() - 1: i.stop);
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
return dim;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
D4ConstraintEvaluator::index
|
|
Packit |
a4aae4 |
D4ConstraintEvaluator::make_index(const std::string &i)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
unsigned long long v = get_int64(i.c_str());
|
|
Packit |
a4aae4 |
return index(v, 1, v, false, false /*empty*/, "");
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
D4ConstraintEvaluator::index
|
|
Packit |
a4aae4 |
D4ConstraintEvaluator::make_index(const std::string &i, const std::string &s, const std::string &e)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
return index(get_int64(i.c_str()), get_int64(s.c_str()), get_int64(e.c_str()), false, false /*empty*/, "");
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
D4ConstraintEvaluator::index
|
|
Packit |
a4aae4 |
D4ConstraintEvaluator::make_index(const std::string &i, unsigned long long s, const std::string &e)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
return index(get_int64(i.c_str()), s, get_int64(e.c_str()), false, false /*empty*/, "");
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
D4ConstraintEvaluator::index
|
|
Packit |
a4aae4 |
D4ConstraintEvaluator::make_index(const std::string &i, const std::string &s)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
return index(get_int64(i.c_str()), get_int64(s.c_str()), 0, true, false /*empty*/, "");
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
D4ConstraintEvaluator::index
|
|
Packit |
a4aae4 |
D4ConstraintEvaluator::make_index(const std::string &i, unsigned long long s)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
return index(get_uint64(i.c_str()), s, 0, true, false /*empty*/, "");
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
static string
|
|
Packit |
a4aae4 |
expr_msg(const std::string &op, const std::string &arg1, const std::string &arg2)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
return "(" + arg1 + " " + op + " " + arg2 + ").";
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
/**
|
|
Packit |
a4aae4 |
* @brief Return the D4FilterClause constant for an operator
|
|
Packit |
a4aae4 |
*
|
|
Packit |
a4aae4 |
* Here are the strings returned by the parser:
|
|
Packit |
a4aae4 |
* GREATER ">"
|
|
Packit |
a4aae4 |
* LESS_EQUAL "<="
|
|
Packit |
a4aae4 |
* GREATER_EQUAL ">="
|
|
Packit |
a4aae4 |
* EQUAL "=="
|
|
Packit |
a4aae4 |
* NOT_EQUAL "!="
|
|
Packit |
a4aae4 |
* REGEX_MATCH "~="
|
|
Packit |
a4aae4 |
*
|
|
Packit |
a4aae4 |
* LESS_BBOX "<<"
|
|
Packit |
a4aae4 |
* GREATER_BBOX ">>"
|
|
Packit |
a4aae4 |
*
|
|
Packit |
a4aae4 |
* MASK "@="
|
|
Packit |
a4aae4 |
* ND "ND"
|
|
Packit |
a4aae4 |
*/
|
|
Packit |
a4aae4 |
static D4FilterClause::ops
|
|
Packit |
a4aae4 |
get_op_code(const std::string &op)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
DBGN(cerr << "Entering " << __PRETTY_FUNCTION__ << endl << "op: " << op << endl);
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
if (op == "<")
|
|
Packit |
a4aae4 |
return D4FilterClause::less;
|
|
Packit |
a4aae4 |
else if (op == ">")
|
|
Packit |
a4aae4 |
return D4FilterClause::greater;
|
|
Packit |
a4aae4 |
else if (op == "<=")
|
|
Packit |
a4aae4 |
return D4FilterClause::less_equal;
|
|
Packit |
a4aae4 |
else if (op == ">=")
|
|
Packit |
a4aae4 |
return D4FilterClause::greater_equal;
|
|
Packit |
a4aae4 |
else if (op == "==")
|
|
Packit |
a4aae4 |
return D4FilterClause::equal;
|
|
Packit |
a4aae4 |
else if (op == "!=")
|
|
Packit |
a4aae4 |
return D4FilterClause::not_equal;
|
|
Packit |
a4aae4 |
else if (op == "~=")
|
|
Packit |
a4aae4 |
return D4FilterClause::match;
|
|
Packit |
a4aae4 |
else
|
|
Packit |
a4aae4 |
throw Error(malformed_expr, "The opertator '" + op + "' is not supported.");
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
/**
|
|
Packit |
a4aae4 |
* @brief Add a D4FilterClause
|
|
Packit |
a4aae4 |
*
|
|
Packit |
a4aae4 |
* This method adds a filter clause to the D4Sequence that is on the top of the
|
|
Packit |
a4aae4 |
* parser's stack. If there is not a D4Sequence on the stack, an exception is
|
|
Packit |
a4aae4 |
* thrown. Similarly, if the filter clause parameters are not valid, then an
|
|
Packit |
a4aae4 |
* exception is thrown.
|
|
Packit |
a4aae4 |
*
|
|
Packit |
a4aae4 |
* Filter clause rules: One of the parameters must be a variable in a D4Sequence
|
|
Packit |
a4aae4 |
* and the other must be a constant. The operator must be one of the valid relops.
|
|
Packit |
a4aae4 |
* Note that the D4FilterClause objects use the same numerical codes as the DAP2
|
|
Packit |
a4aae4 |
* parser/evaluator.
|
|
Packit |
a4aae4 |
*
|
|
Packit |
a4aae4 |
* @note The parser will have pushed the Sequence onto the BaseType stack during
|
|
Packit |
a4aae4 |
* the parse, so variables can be looked up using the top_basetype() (which
|
|
Packit |
a4aae4 |
* must be a D4Sequence).
|
|
Packit |
a4aae4 |
*
|
|
Packit |
a4aae4 |
* @param arg1 The first argument; a D4Sequence variable or a constant.
|
|
Packit |
a4aae4 |
* @param arg2 The second argument; a D4Sequence variable or a constant.
|
|
Packit |
a4aae4 |
* @param op The infix relop
|
|
Packit |
a4aae4 |
*/
|
|
Packit |
a4aae4 |
void
|
|
Packit |
a4aae4 |
D4ConstraintEvaluator::add_filter_clause(const std::string &op, const std::string &arg1, const std::string &arg2)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
DBG(cerr << "Entering: " << __PRETTY_FUNCTION__ << endl);
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
// Check that there really is a D4Sequence associated with this filter clause.
|
|
Packit |
a4aae4 |
D4Sequence *s = dynamic_cast<D4Sequence*>(top_basetype());
|
|
Packit |
a4aae4 |
if (!s)
|
|
Packit |
a4aae4 |
throw Error(malformed_expr,
|
|
Packit |
a4aae4 |
"When a filter expression is used, it must be bound to a Sequence variable: " + expr_msg(op, arg1, arg2));
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
DBG(cerr << "s->name(): " << s->name() << endl);
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
// Check that arg1 and 2 are valid
|
|
Packit |
a4aae4 |
BaseType *a1 = s->var(arg1);
|
|
Packit |
a4aae4 |
BaseType *a2 = s->var(arg2);
|
|
Packit |
a4aae4 |
DBG(cerr << "a1: " << a1 << ", a2: " << a2 << endl);
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
if (a1 && a2)
|
|
Packit |
a4aae4 |
throw Error(malformed_expr,
|
|
Packit |
a4aae4 |
"One of the arguments in a filter expression must be a constant: " + expr_msg(op, arg1, arg2));
|
|
Packit |
a4aae4 |
if (!(a1 || a2))
|
|
Packit |
a4aae4 |
throw Error(malformed_expr,
|
|
Packit |
a4aae4 |
"One of the arguments in a filter expression must be a variable in a Sequence: " + expr_msg(op, arg1, arg2));
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
// Now we know a1 XOR a2 is true
|
|
Packit |
a4aae4 |
if (a1) {
|
|
Packit |
a4aae4 |
s->clauses().add_clause(new D4FilterClause(get_op_code(op), new D4RValue(a1), D4RValueFactory(arg2)));
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
else {
|
|
Packit |
a4aae4 |
s->clauses().add_clause(new D4FilterClause(get_op_code(op), D4RValueFactory(arg1), new D4RValue(a2)));
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
/**
|
|
Packit |
a4aae4 |
* @brief If the string has surrounding quotes, remove them.
|
|
Packit |
a4aae4 |
*
|
|
Packit |
a4aae4 |
* @param src The source string, passed by reference and modified in place
|
|
Packit |
a4aae4 |
* @return A reference to the sting parameter.
|
|
Packit |
a4aae4 |
*/
|
|
Packit |
a4aae4 |
string &
|
|
Packit |
a4aae4 |
D4ConstraintEvaluator::remove_quotes(string &s)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
if (*s.begin() == '\"' && *(s.end() - 1) == '\"') {
|
|
Packit |
a4aae4 |
s.erase(s.begin());
|
|
Packit |
a4aae4 |
s.erase(s.end() - 1);
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
return s;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
// This method is called from the parser (see d4_ce_parser.yy, down in the code
|
|
Packit |
a4aae4 |
// section). This will be called during the call to D4CEParser::parse(), that
|
|
Packit |
a4aae4 |
// is inside D4ConstraintEvaluator::parse(...)
|
|
Packit |
a4aae4 |
void
|
|
Packit |
a4aae4 |
D4ConstraintEvaluator::error(const libdap::location &l, const std::string &m)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
ostringstream oss;
|
|
Packit |
a4aae4 |
oss << l << ": " << m << ends;
|
|
Packit |
a4aae4 |
throw Error(malformed_expr, oss.str());
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
} /* namespace libdap */
|