libdap Updated for version 3.18.1
D4ConstraintEvaluator.cc
1// -*- mode: c++; c-basic-offset:4 -*-
2
3// This file is part of libdap, A C++ implementation of the OPeNDAP Data
4// Access Protocol.
5
6// Copyright (c) 2002,2003 OPeNDAP, Inc.
7// Author: James Gallagher <jgallagher@opendap.org>
8//
9// This library is free software; you can redistribute it and/or
10// modify it under the terms of the GNU Lesser General Public
11// License as published by the Free Software Foundation; either
12// version 2.1 of the License, or (at your option) any later version.
13//
14// This library is distributed in the hope that it will be useful,
15// but WITHOUT ANY WARRANTY; without even the implied warranty of
16// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17// Lesser General Public License for more details.
18//
19// You should have received a copy of the GNU Lesser General Public
20// License along with this library; if not, write to the Free Software
21// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22//
23// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
24
25#include <string>
26#include <sstream>
27#include <iterator>
28
29//#define DODS_DEBUG
30
31#include "D4CEScanner.h"
32#include "D4ConstraintEvaluator.h"
33#include "d4_ce_parser.tab.hh"
34
35#include "DMR.h"
36#include "D4Group.h"
37#include "D4Dimensions.h"
38#include "D4Maps.h"
39#include "BaseType.h"
40#include "Array.h"
41#include "Constructor.h"
42#include "D4Sequence.h"
43
44#include "D4RValue.h"
45#include "D4FilterClause.h"
46
47#include "parser.h" // for get_ull()
48#include "debug.h"
49
50namespace libdap {
51
52bool D4ConstraintEvaluator::parse(const std::string &expr)
53{
54 d_expr = expr; // set for error messages. See the %initial-action section of .yy
55
56 std::istringstream iss(expr);
57 D4CEScanner scanner(iss);
58 D4CEParser parser(scanner, *this /* driver */);
59
60 if (trace_parsing()) {
61 parser.set_debug_level(1);
62 parser.set_debug_stream(std::cerr);
63 }
64
65 return parser.parse() == 0;
66}
67
68void
69D4ConstraintEvaluator::throw_not_found(const string &id, const string &ident)
70{
71 throw Error(no_such_variable, d_expr + ": The variable " + id + " was not found in the dataset (" + ident + ").");
72}
73
74void
75D4ConstraintEvaluator::throw_not_array(const string &id, const string &ident)
76{
77 throw Error(no_such_variable, d_expr + ": The variable '" + id + "' is not an Array variable (" + ident + ").");
78}
79
80void
81D4ConstraintEvaluator::search_for_and_mark_arrays(BaseType *btp)
82{
83 DBG(cerr << "Entering D4ConstraintEvaluator::search_for_and_mark_arrays...(" << btp->name() << ")" << endl);
84
85 assert(btp->is_constructor_type());
86
87 Constructor *ctor = static_cast<Constructor*>(btp);
88 for (Constructor::Vars_iter i = ctor->var_begin(), e = ctor->var_end(); i != e; ++i) {
89 switch ((*i)->type()) {
90 case dods_array_c:
91 DBG(cerr << "Found an array: " << (*i)->name() << endl);
92 mark_array_variable(*i);
93 break;
94 case dods_structure_c:
95 case dods_sequence_c:
96 DBG(cerr << "Found a ctor: " << (*i)->name() << endl);
97 search_for_and_mark_arrays(*i);
98 break;
99 default:
100 break;
101 }
102 }
103}
104
114BaseType *
115D4ConstraintEvaluator::mark_variable(BaseType *btp)
116{
117 assert(btp);
118
119 DBG(cerr << "In D4ConstraintEvaluator::mark_variable... (" << btp->name() << "; " << btp->type_name() << ")" << endl);
120
121 btp->set_send_p(true);
122
123 if (btp->type() == dods_array_c ) {
124 mark_array_variable(btp);
125 }
126
127 // Test for Constructors and marks arrays they contain
128 if (btp->is_constructor_type()) {
129 search_for_and_mark_arrays(btp);
130 }
131 else if (btp->type() == dods_array_c && btp->var() && btp->var()->is_constructor_type()) {
132 search_for_and_mark_arrays(btp->var());
133 }
134
135 // Now set the parent variables
136 BaseType *parent = btp->get_parent();
137 while (parent) {
138 parent->BaseType::set_send_p(true); // Just set the parent using BaseType's impl.
139 parent = parent->get_parent();
140 }
141
142 return btp;
143}
144
145static bool
146array_uses_shared_dimension(Array *map, D4Dimension *source_dim)
147{
148 for (Array::Dim_iter d = map->dim_begin(), e = map->dim_end(); d != e; ++d) {
149 if (source_dim->name() == (*d).name)
150 return true;
151 }
152
153 return false;
154}
155
169// Note: If a Map is not part of the current projection, do not include mention of it
170// in the response DMR (CDMR)
171BaseType *
172D4ConstraintEvaluator::mark_array_variable(BaseType *btp)
173{
174 assert(btp->type() == dods_array_c);
175
176 Array *a = static_cast<Array*>(btp);
177
178 // If an array appears in a CE without the slicing operators ([]) we still have to
179 // call add_constraint(...) for all of it's sdims for them to appear in
180 // the Constrained DMR.
181 if (d_indexes.empty()) {
182 for (Array::Dim_iter d = a->dim_begin(), de = a->dim_end(); d != de; ++d) {
183 D4Dimension *dim = a->dimension_D4dim(d);
184 if (dim) {
185 a->add_constraint(d, dim);
186 }
187 }
188 }
189 else {
190 // Test that the indexes and dimensions match in number
191 if (d_indexes.size() != a->dimensions())
192 throw Error(malformed_expr, "The index constraint for '" + btp->name() + "' does not match its rank.");
193
194 Array::Dim_iter d = a->dim_begin();
195 for (vector<index>::iterator i = d_indexes.begin(), e = d_indexes.end(); i != e; ++i) {
196 if ((*i).stride > (unsigned long long) (a->dimension_stop(d, false) - a->dimension_start(d, false)) + 1)
197 throw Error(malformed_expr, "For '" + btp->name() + "', the index stride value is greater than the number of elements in the Array");
198 if (!(*i).rest && ((*i).stop) > (unsigned long long) (a->dimension_stop(d, false) - a->dimension_start(d, false)) + 1)
199 throw Error(malformed_expr, "For '" + btp->name() + "', the index stop value is greater than the number of elements in the Array");
200
201 D4Dimension *dim = a->dimension_D4dim(d);
202
203 // In a DAP4 CE, specifying '[]' as an array dimension slice has two meanings.
204 // It can mean 'all the elements' of the dimension or 'apply the slicing inherited
205 // from the shared dimension'. The latter might be provide 'all the elements'
206 // but regardless, the Array object must record the CE correctly.
207
208 if (dim && (*i).empty) {
209 // This case corresponds to a CE that uses the '[]' notation for a
210 // particular dimension - meaning, use the Shared Dimension size for
211 // this dimension's 'slice'.
212 a->add_constraint(d, dim); // calls set_used_by_projected_var(true) + more
213 }
214 else {
215 // This case corresponds to a 'local dimension slice' (See sections 8.6.2 and
216 // 8.7 of the spec as of 4/12/16). When a local dimension slice is used, drop
217 // the Map(s) that include that dimension. This enables people to constrain
218 // an Array when some of the Array's dimensions don't use Shared Dimensions
219 // but others do.
220
221 // First apply the constraint to the Array's dimension
222 a->add_constraint(d, (*i).start, (*i).stride, (*i).rest ? -1 : (*i).stop);
223
224 // Then, if the Array has Maps, scan those Maps for any that use dimensions
225 // that match the name of this particular dimension. If any such Maps are found
226 // remove them. This ensure that the Array can be constrained using the 'local
227 // dimension slice' without the constrained DMR containing references to Maps
228 // that don't exist (or are otherwise nonsensical).
229 //
230 // This code came about as a fix for problems discovered during testing of
231 // local dimension slices. See https://opendap.atlassian.net/browse/HYRAX-98
232 // jhrg 4/12/16
233 if (!a->maps()->empty()) {
234 for(D4Maps::D4MapsIter m = a->maps()->map_begin(), e = a->maps()->map_end(); m != e; ++m) {
235 if ((*m)->array() == 0)
236 throw Error(malformed_expr, "An array with Maps was found, but one of the Maps was not defined correctly.");
237
238 Array *map = const_cast<Array*>((*m)->array()); // Array lacks const iterator support
239 if (array_uses_shared_dimension(map, dim)) {
240 D4Map *map_to_be_removed = *m;
241 a->maps()->remove_map(map_to_be_removed); // Invalidates the iterator
242 delete map_to_be_removed; // removed from container; delete
243 break; // must leave the for loop because 'm' is now invalid
244 }
245 }
246 }
247 }
248
249 ++d;
250 }
251 }
252
253 d_indexes.clear(); // Clear the info so the next slice expression can be parsed.
254
255 return btp;
256}
257
266D4Dimension *
267D4ConstraintEvaluator::slice_dimension(const std::string &id, const index &i)
268{
269 D4Dimension *dim = dmr()->root()->find_dim(id);
270
271 if (i.stride > dim->size())
272 throw Error(malformed_expr, "For '" + id + "', the index stride value is greater than the size of the dimension");
273 if (!i.rest && (i.stop > dim->size() - 1))
274 throw Error(malformed_expr, "For '" + id + "', the index stop value is greater than the size of the dimension");
275
276 dim->set_constraint(i.start, i.stride, i.rest ? dim->size() - 1: i.stop);
277
278 return dim;
279}
280
281D4ConstraintEvaluator::index
282D4ConstraintEvaluator::make_index(const std::string &i)
283{
284 unsigned long long v = get_int64(i.c_str());
285 return index(v, 1, v, false, false /*empty*/, "");
286}
287
288D4ConstraintEvaluator::index
289D4ConstraintEvaluator::make_index(const std::string &i, const std::string &s, const std::string &e)
290{
291 return index(get_int64(i.c_str()), get_int64(s.c_str()), get_int64(e.c_str()), false, false /*empty*/, "");
292}
293
294D4ConstraintEvaluator::index
295D4ConstraintEvaluator::make_index(const std::string &i, unsigned long long s, const std::string &e)
296{
297 return index(get_int64(i.c_str()), s, get_int64(e.c_str()), false, false /*empty*/, "");
298}
299
300D4ConstraintEvaluator::index
301D4ConstraintEvaluator::make_index(const std::string &i, const std::string &s)
302{
303 return index(get_int64(i.c_str()), get_int64(s.c_str()), 0, true, false /*empty*/, "");
304}
305
306D4ConstraintEvaluator::index
307D4ConstraintEvaluator::make_index(const std::string &i, unsigned long long s)
308{
309 return index(get_uint64(i.c_str()), s, 0, true, false /*empty*/, "");
310}
311
312static string
313expr_msg(const std::string &op, const std::string &arg1, const std::string &arg2)
314{
315 return "(" + arg1 + " " + op + " " + arg2 + ").";
316}
317
335static D4FilterClause::ops
336get_op_code(const std::string &op)
337{
338 DBGN(cerr << "Entering " << __PRETTY_FUNCTION__ << endl << "op: " << op << endl);
339
340 if (op == "<")
341 return D4FilterClause::less;
342 else if (op == ">")
343 return D4FilterClause::greater;
344 else if (op == "<=")
345 return D4FilterClause::less_equal;
346 else if (op == ">=")
347 return D4FilterClause::greater_equal;
348 else if (op == "==")
349 return D4FilterClause::equal;
350 else if (op == "!=")
351 return D4FilterClause::not_equal;
352 else if (op == "~=")
353 return D4FilterClause::match;
354 else
355 throw Error(malformed_expr, "The opertator '" + op + "' is not supported.");
356}
357
379void
380D4ConstraintEvaluator::add_filter_clause(const std::string &op, const std::string &arg1, const std::string &arg2)
381{
382 DBG(cerr << "Entering: " << __PRETTY_FUNCTION__ << endl);
383
384 // Check that there really is a D4Sequence associated with this filter clause.
385 D4Sequence *s = dynamic_cast<D4Sequence*>(top_basetype());
386 if (!s)
387 throw Error(malformed_expr,
388 "When a filter expression is used, it must be bound to a Sequence variable: " + expr_msg(op, arg1, arg2));
389
390 DBG(cerr << "s->name(): " << s->name() << endl);
391
392 // Check that arg1 and 2 are valid
393 BaseType *a1 = s->var(arg1);
394 BaseType *a2 = s->var(arg2);
395 DBG(cerr << "a1: " << a1 << ", a2: " << a2 << endl);
396
397 if (a1 && a2)
398 throw Error(malformed_expr,
399 "One of the arguments in a filter expression must be a constant: " + expr_msg(op, arg1, arg2));
400 if (!(a1 || a2))
401 throw Error(malformed_expr,
402 "One of the arguments in a filter expression must be a variable in a Sequence: " + expr_msg(op, arg1, arg2));
403
404 // Now we know a1 XOR a2 is true
405 if (a1) {
406 s->clauses().add_clause(new D4FilterClause(get_op_code(op), new D4RValue(a1), D4RValueFactory(arg2)));
407 }
408 else {
409 s->clauses().add_clause(new D4FilterClause(get_op_code(op), D4RValueFactory(arg1), new D4RValue(a2)));
410 }
411}
412
413// This method is called from the parser (see d4_ce_parser.yy, down in the code
414// section). This will be called during the call to D4CEParser::parse(), that
415// is inside D4ConstraintEvaluator::parse(...)
416void
417D4ConstraintEvaluator::error(const libdap::location &l, const std::string &m)
418{
419 ostringstream oss;
420 oss << l << ": " << m << ends;
421 throw Error(malformed_expr, oss.str());
422}
423
424} /* namespace libdap */
std::vector< dimension >::iterator Dim_iter
Definition: Array.h:204
D4Dimension * find_dim(const string &path)
Find the dimension using a path. Using the DAP4 name syntax, lookup a dimension. The dimension must b...
Definition: D4Group.cc:268
D4Group * root()
Definition: DMR.cc:242
D4RValue * D4RValueFactory(std::string cpps)
Build an appropriate RValue.
Definition: D4RValue.cc:218