Blame ConstraintEvaluator.cc

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 "config.h"
Packit a4aae4
Packit a4aae4
//#define DODS_DEBUG
Packit a4aae4
Packit a4aae4
#include "ServerFunctionsList.h"
Packit a4aae4
#include "ConstraintEvaluator.h"
Packit a4aae4
#include "Clause.h"
Packit a4aae4
#include "DataDDS.h"
Packit a4aae4
Packit a4aae4
#include "ce_parser.h"
Packit a4aae4
#include "debug.h"
Packit a4aae4
#include "parser.h"
Packit a4aae4
#include "expr.h"
Packit a4aae4
Packit a4aae4
struct yy_buffer_state;
Packit a4aae4
Packit a4aae4
int ce_exprparse(libdap::ce_parser_arg *arg);
Packit a4aae4
Packit a4aae4
// Glue routines declared in expr.lex
Packit a4aae4
void ce_expr_switch_to_buffer(void *new_buffer);
Packit a4aae4
void ce_expr_delete_buffer(void * buffer);
Packit a4aae4
void *ce_expr_string(const char *yy_str);
Packit a4aae4
Packit a4aae4
extern int ce_exprdebug;
Packit a4aae4
Packit a4aae4
namespace libdap {
Packit a4aae4
Packit a4aae4
ConstraintEvaluator::ConstraintEvaluator()
Packit a4aae4
{
Packit a4aae4
    // Functions are now held in BES modules. jhrg 1/30/13
Packit a4aae4
Packit a4aae4
    // modules load functions to this list; this class searches the list
Packit a4aae4
    // instead of having it's own copy. This is very similar to the BES'
Packit a4aae4
    // various List classes, but this one is part of libdap and not the
Packit a4aae4
    // BES. The List class is a singleton, so each function module can
Packit a4aae4
    // register it's functions to the list object.
Packit a4aae4
    d_functions_list = ServerFunctionsList::TheList();
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
ConstraintEvaluator::~ConstraintEvaluator()
Packit a4aae4
{
Packit a4aae4
    // delete all the constants created by the parser for CE evaluation
Packit a4aae4
    for (Constants_iter j = constants.begin(); j != constants.end(); j++) {
Packit a4aae4
        BaseType *btp = *j;
Packit a4aae4
        delete btp;
Packit a4aae4
        btp = 0;
Packit a4aae4
    }
Packit a4aae4
Packit a4aae4
    for (Clause_iter k = expr.begin(); k != expr.end(); k++) {
Packit a4aae4
        Clause *cp = *k;
Packit a4aae4
        delete cp;
Packit a4aae4
        cp = 0;
Packit a4aae4
    }
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** Returns the first clause in a parsed constraint expression. */
Packit a4aae4
ConstraintEvaluator::Clause_iter ConstraintEvaluator::clause_begin()
Packit a4aae4
{
Packit a4aae4
    return expr.begin();
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** Returns a reference to the end of the list of clauses in a parsed
Packit a4aae4
 constraint expression. It does not reference the last clause */
Packit a4aae4
ConstraintEvaluator::Clause_iter ConstraintEvaluator::clause_end()
Packit a4aae4
{
Packit a4aae4
    return expr.end();
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** Returns the value of the indicated clause of a constraint
Packit a4aae4
 expression. */
Packit a4aae4
bool ConstraintEvaluator::clause_value(Clause_iter &iter, DDS &dds/*, const string &***/)
Packit a4aae4
{
Packit a4aae4
    if (expr.empty())
Packit a4aae4
        throw InternalErr(__FILE__, __LINE__, "There are no CE clauses for *this* DDS object.");
Packit a4aae4
Packit a4aae4
    return (*iter)->value(dds);
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** @brief Add a clause to a constraint expression.
Packit a4aae4
Packit a4aae4
 This function adds an operator clause to the constraint
Packit a4aae4
 expression.
Packit a4aae4
Packit a4aae4
 @param op An integer indicating the operator in use.  These
Packit a4aae4
 values are generated by \c bison.
Packit a4aae4
 @param arg1 A pointer to the argument on the left side of the
Packit a4aae4
 operator.
Packit a4aae4
 @param arg2 A pointer to a list of the arguments on the right
Packit a4aae4
 side of the operator.
Packit a4aae4
 */
Packit a4aae4
void ConstraintEvaluator::append_clause(int op, rvalue *arg1, rvalue_list *arg2)
Packit a4aae4
{
Packit a4aae4
    Clause *clause = new Clause(op, arg1, arg2);
Packit a4aae4
Packit a4aae4
    expr.push_back(clause);
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** @brief Add a clause to a constraint expression.
Packit a4aae4
Packit a4aae4
 This function adds a boolean function clause to the constraint
Packit a4aae4
 expression.
Packit a4aae4
Packit a4aae4
 @param func A pointer to a boolean function from the list of
Packit a4aae4
 supported functions.
Packit a4aae4
 @param args A list of arguments to that function.
Packit a4aae4
 */
Packit a4aae4
void ConstraintEvaluator::append_clause(bool_func func, rvalue_list *args)
Packit a4aae4
{
Packit a4aae4
    Clause *clause = new Clause(func, args);
Packit a4aae4
Packit a4aae4
    expr.push_back(clause);
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** @brief Add a clause to a constraint expression.
Packit a4aae4
Packit a4aae4
 This function adds a real-valued (BaseType) function clause to
Packit a4aae4
 the constraint expression.
Packit a4aae4
Packit a4aae4
 @param func A pointer to a BaseType function from the list of
Packit a4aae4
 supported functions.
Packit a4aae4
 @param args A list of arguments to that function.
Packit a4aae4
 */
Packit a4aae4
void ConstraintEvaluator::append_clause(btp_func func, rvalue_list *args)
Packit a4aae4
{
Packit a4aae4
    Clause *clause = new Clause(func, args);
Packit a4aae4
Packit a4aae4
    expr.push_back(clause);
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** The Constraint Evaluator maintains a list of BaseType pointers for all the
Packit a4aae4
 constants that the constraint expression parser generates. These objects
Packit a4aae4
 are deleted by the Constraint Evaluator destructor. Note that there are no
Packit a4aae4
 list accessors; these constants are never accessed from the list. The list
Packit a4aae4
 is simply a convenient way to make sure the constants are disposed of
Packit a4aae4
 properly.
Packit a4aae4
 */
Packit a4aae4
void ConstraintEvaluator::append_constant(BaseType *btp)
Packit a4aae4
{
Packit a4aae4
    constants.push_back(btp);
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** @brief Find a Boolean function with a given name in the function list. */
Packit a4aae4
bool ConstraintEvaluator::find_function(const string &name, bool_func *f) const
Packit a4aae4
{
Packit a4aae4
    return d_functions_list->find_function(name, f);
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** @brief Find a BaseType function with a given name in the function list. */
Packit a4aae4
bool ConstraintEvaluator::find_function(const string &name, btp_func *f) const
Packit a4aae4
{
Packit a4aae4
    return d_functions_list->find_function(name, f);
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** @brief Find a projection function with a given name in the function list. */
Packit a4aae4
bool ConstraintEvaluator::find_function(const string &name, proj_func *f) const
Packit a4aae4
{
Packit a4aae4
    return d_functions_list->find_function(name, f);
Packit a4aae4
}
Packit a4aae4
//@}
Packit a4aae4
Packit a4aae4
/** @brief Does the current constraint expression return a BaseType
Packit a4aae4
 pointer?
Packit a4aae4
 This method does not evaluate the clause, it provides information to the
Packit a4aae4
 evaluator regarding _how_ to evaluate the clause.
Packit a4aae4
 @return True if the clause is a function that returns a BaseType* and
Packit a4aae4
 false otherwise
Packit a4aae4
 @deprecated
Packit a4aae4
 */
Packit a4aae4
bool ConstraintEvaluator::functional_expression()
Packit a4aae4
{
Packit a4aae4
    if (expr.empty())
Packit a4aae4
        return false;
Packit a4aae4
Packit a4aae4
    Clause *cp = expr[0];
Packit a4aae4
    return cp->value_clause();
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** @brief Evaluate a function-valued constraint expression.
Packit a4aae4
 * @deprecated
Packit a4aae4
 * */
Packit a4aae4
BaseType *
Packit a4aae4
ConstraintEvaluator::eval_function(DDS &dds, const string &)
Packit a4aae4
{
Packit a4aae4
    if (expr.size() != 1)
Packit a4aae4
        throw InternalErr(__FILE__, __LINE__, "The length of the list of CE clauses is not 1.");
Packit a4aae4
Packit a4aae4
    Clause *cp = expr[0];
Packit a4aae4
    BaseType *result;
Packit a4aae4
    if (cp->value(dds, &result))
Packit a4aae4
        return result;
Packit a4aae4
    else
Packit a4aae4
        return NULL;
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** @brief Does the current constraint expression contain function clauses
Packit a4aae4
Packit a4aae4
 This method does not evaluate the clauses, it provides information to the
Packit a4aae4
 evaluator regarding _how_ to evaluate the clause.
Packit a4aae4
Packit a4aae4
 @note Added for libdap 3.11
Packit a4aae4
Packit a4aae4
 @return True if the current constraint contains function clauses,
Packit a4aae4
 otherwise returns False */
Packit a4aae4
bool ConstraintEvaluator::function_clauses()
Packit a4aae4
{
Packit a4aae4
    if (expr.empty())
Packit a4aae4
        return false;
Packit a4aae4
Packit a4aae4
    for (unsigned int i = 0; i < expr.size(); ++i) {
Packit a4aae4
        Clause *cp = expr[i];
Packit a4aae4
        if (!cp->value_clause())
Packit a4aae4
            return false;
Packit a4aae4
    }
Packit a4aae4
Packit a4aae4
    return true;
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** @brief Evaluate a function-valued constraint expression that contains
Packit a4aae4
 several function calls.
Packit a4aae4
Packit a4aae4
 This method can be called for any function-valued constraint expression.
Packit a4aae4
 Unlike eval_function(), it will package the return value in a new DDS
Packit a4aae4
 object. The server should free this object once it has been serialized
Packit a4aae4
 and sent.
Packit a4aae4
Packit a4aae4
 @note While there is another type of function that can appear in a CE (a
Packit a4aae4
 'projection function') those are evaluated by the ce parser - they are used
Packit a4aae4
 to insert new variables into the DDS as a side effect of CE evaluation.
Packit a4aae4
 That kind of function can never appear here; these are all functions that
Packit a4aae4
 return BaseType pointers.
Packit a4aae4
Packit a4aae4
 @note Added for libdap 3.11 */
Packit a4aae4
DDS *
Packit a4aae4
ConstraintEvaluator::eval_function_clauses(DDS &dds)
Packit a4aae4
{
Packit a4aae4
    if (expr.empty())
Packit a4aae4
        throw InternalErr(__FILE__, __LINE__, "The constraint expression is empty.");
Packit a4aae4
Packit a4aae4
    DDS *fdds = new DDS(dds.get_factory(), "function_result_" + dds.get_dataset_name());
Packit a4aae4
    for (unsigned int i = 0; i < expr.size(); ++i) {
Packit a4aae4
        Clause *cp = expr[i];
Packit a4aae4
        BaseType *result;
Packit a4aae4
        if (cp->value(dds, &result)) {
Packit a4aae4
            // This is correct: The function must allocate the memory for the result
Packit a4aae4
            // variable. 11/30/12 jhrg
Packit a4aae4
            fdds->add_var_nocopy(result);
Packit a4aae4
        }
Packit a4aae4
        else {
Packit a4aae4
            delete fdds;
Packit a4aae4
            throw Error(internal_error, "A function was called but failed to return a value.");
Packit a4aae4
        }
Packit a4aae4
    }
Packit a4aae4
Packit a4aae4
    return fdds;
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** @brief Evaluate a function-valued constraint expression that contains
Packit a4aae4
 several function calls. Takes and returns a DataDDS.
Packit a4aae4
Packit a4aae4
 @see ConstraintEvaluator::eval_function_clauses(DataDDS &dds)
Packit a4aae4
 @note Added for libdap 3.11 */
Packit a4aae4
DataDDS *
Packit a4aae4
ConstraintEvaluator::eval_function_clauses(DataDDS &dds)
Packit a4aae4
{
Packit a4aae4
    if (expr.empty())
Packit a4aae4
        throw InternalErr(__FILE__, __LINE__, "The constraint expression is empty.");
Packit a4aae4
Packit a4aae4
    DataDDS *fdds = new DataDDS(dds.get_factory(), "function_result_" + dds.get_dataset_name(), dds.get_version(),
Packit a4aae4
            dds.get_protocol());
Packit a4aae4
Packit a4aae4
    for (unsigned int i = 0; i < expr.size(); ++i) {
Packit a4aae4
        Clause *cp = expr[i];
Packit a4aae4
        BaseType *result;
Packit a4aae4
        if (cp->value(dds, &result)) {
Packit a4aae4
            fdds->add_var_nocopy(result);
Packit a4aae4
        }
Packit a4aae4
        else {
Packit a4aae4
            delete fdds;
Packit a4aae4
            throw Error(internal_error, "A function was called but failed to return a value.");
Packit a4aae4
        }
Packit a4aae4
    }
Packit a4aae4
Packit a4aae4
    return fdds;
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** @brief Does the current constraint expression return a boolean value? */
Packit a4aae4
bool ConstraintEvaluator::boolean_expression()
Packit a4aae4
{
Packit a4aae4
    if (expr.empty())
Packit a4aae4
        return false;
Packit a4aae4
Packit a4aae4
    bool boolean = true;
Packit a4aae4
    for (Clause_iter i = expr.begin(); i != expr.end(); i++) {
Packit a4aae4
        boolean = boolean && (*i)->boolean_clause();
Packit a4aae4
    }
Packit a4aae4
Packit a4aae4
    return boolean;
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** @brief Evaluate a boolean-valued constraint expression.
Packit a4aae4
 This is main method for the evaluator and is called by the
Packit a4aae4
 BaseType::serialize() methods.
Packit a4aae4
Packit a4aae4
 @param dds Use these variables when evaluating the expressions.
Packit a4aae4
 @param dataset This string is passed to the read() methods.
Packit a4aae4
 @return True if the expression is true, false otherwise. */
Packit a4aae4
bool ConstraintEvaluator::eval_selection(DDS &dds, const string &)
Packit a4aae4
{
Packit a4aae4
    if (expr.empty()) {
Packit a4aae4
        DBG(cerr << "No selection recorded" << endl);
Packit a4aae4
        return true;
Packit a4aae4
    }
Packit a4aae4
Packit a4aae4
    DBG(cerr << "Eval selection" << endl);
Packit a4aae4
Packit a4aae4
    // A CE is made up of zero or more clauses, each of which has a boolean
Packit a4aae4
    // value. The value of the CE is the logical AND of the clause
Packit a4aae4
    // values. See ConstraintEvaluator::clause::value(...) for information on logical ORs in
Packit a4aae4
    // CEs.
Packit a4aae4
    bool result = true;
Packit a4aae4
    for (Clause_iter i = expr.begin(); i != expr.end() && result; i++) {
Packit a4aae4
        // A selection expression *must* contain only boolean clauses!
Packit a4aae4
        if (!((*i)->boolean_clause()))
Packit a4aae4
            throw InternalErr(__FILE__, __LINE__, "A selection expression must contain only boolean clauses.");
Packit a4aae4
        result = result && (*i)->value(dds);
Packit a4aae4
    }
Packit a4aae4
Packit a4aae4
    return result;
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
/** @brief Parse the constraint expression given the current DDS.
Packit a4aae4
Packit a4aae4
 Evaluate the constraint expression; return the value of the expression.
Packit a4aae4
 As a side effect, mark the DDS so that BaseType's mfuncs can be used to
Packit a4aae4
 correctly read the variable's value and send it to the client.
Packit a4aae4
Packit a4aae4
 @param constraint A string containing the constraint expression.
Packit a4aae4
 @param dds The DDS that provides the environment within which the
Packit a4aae4
 constraint is evaluated.
Packit a4aae4
 @exception Throws Error if the constraint does not parse. */
Packit a4aae4
void ConstraintEvaluator::parse_constraint(const string &constraint, DDS &dds)
Packit a4aae4
{
Packit a4aae4
    void *buffer = ce_expr_string(constraint.c_str());
Packit a4aae4
Packit a4aae4
    // Toggle this to debug the parser. A last resort...
Packit a4aae4
    ce_exprdebug = false;
Packit a4aae4
Packit a4aae4
    ce_expr_switch_to_buffer(buffer);
Packit a4aae4
Packit a4aae4
    ce_parser_arg arg(this, &dds;;
Packit a4aae4
Packit a4aae4
    // For all errors, exprparse will throw Error.
Packit a4aae4
    try {
Packit a4aae4
    	ce_exprparse(&arg;;
Packit a4aae4
    	ce_expr_delete_buffer(buffer);
Packit a4aae4
    }
Packit a4aae4
    catch (...) {
Packit a4aae4
    	// Make sure to remove the buffer when there's an error
Packit a4aae4
    	ce_expr_delete_buffer(buffer);
Packit a4aae4
    	throw;
Packit a4aae4
    }
Packit a4aae4
}
Packit a4aae4
Packit a4aae4
} // namespace libdap