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