// -*- 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.
#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