package ModSQL;
import java.sql.*;
 
/* $Id: Operator_Logical.java,v 1.9 2003/07/09 06:54:49 cvs Exp $
 *
 * Copyright (c) 2003 Chris Studholme <chris.studholme@utoronto.ca>
 *
 * May be copied or modified under the terms of the GNU General Public
 * License.  See COPYING for more information.
 */

/**
 * <p>SQL operators AND and OR.
 *
 * @author chris.studholme@utoronto.ca
 */
final class Operator_Logical extends Operator {

  /** True for AND, false for OR. */
  protected boolean and;
  /** Result of operator applied to all constant parameters. */
  protected Boolean constant_value;

  /**
   * Constructor.
   *
   * @param and true for AND, false for OR
   */
  public Operator_Logical(boolean and) {
    this.and = and;
  }

  /**
   * Adds a function to the list of parameters maintained by this operator.
   * If the function is of type Operator_Not and has double NOT, the double
   * NOT operators will be removed.  If the function is of type
   * Operator_Logical, or Operator_Not followed by Operator_Logical, the
   * functions parameters (conjuncts/disjuncts) may be added instead of the
   * function (with NOT operators applied as needed).
   *
   * @param function function to add
   * @throws SQLException if a database error occurs
   */
  public void addParameter(Function function) throws SQLException {
    function = removeDoubleNot(function);
    if (function instanceof Operator_Logical && 
	((Operator_Logical)function).and==and)
      addAllParameters(function);
    else if (function instanceof Operator_Not &&
	     function.getParameterCount()==1 &&
	     function.getParameter(0) instanceof Operator_Logical &&
	     ((Operator_Logical)function.getParameter(0)).and!=and)
      addAllParametersNot(function.getParameter(0));
    else
      super.addParameter(function);
  }


  /**
   * <p>Prepare the function for use.  This method will check the parameters
   * and apply the operator to all constant parameters.
   *
   * @throws SQLException if the parameters are invalid
   */
  public void optimize() throws SQLException {
    super.optimize();
    if (parameters.length<=0) 
      throw new SQLException("operator "+functionName()+
			     " requires at least one parameter");
    for (int i=0; i<parameters.length; ++i) 
      switch (parameters[i].getSQLType()) {
      case Types.NULL:
      case Types.BIT:
	break;
      default:
	throw new SQLException("invalid parameter type in operator "+
			       functionName());
      }
    
    // compute constant value
    evaluateConstantParameters();
    constant_value = new Boolean(and);
    for (int i=0; i<parameter_constant.length; ++i) {
      if (parameter_constant[i]) {
	if (parameter_value[i]==null)
	  constant_value = null;
	else if (((Boolean)parameter_value[i]).booleanValue()!=and) {
	  constant_value = new Boolean(!and);
	  break;  // boolean short circuit
	}
      }
    }
  }

  /**
   * Returns the name of this function for use by toString() method.
   *
   * @return name of function 
   */
  public String functionName() {
    return and ? "#AND" : "#OR";
  }


  /****************  result meta-data  ****************/
  

  /**
   * Returns Types.BIT.
   *
   * @return SQL type of data to be returned
   */
  public int getSQLType() {
    return Types.BIT;
  }

  /**
   * Returns -1 as this operator will never return a String object.
   *
   * @return maximum size of String returned or -1 if unknown
   */
  public int getMaxResultSize() {
    return -1;
  }


  /****************  evaluation methods  ****************/


  /**
   * Evaluate parameters and compute the function.  
   *
   * @param aggregate passed to parameters
   * @return result object
   * @throws SQLException if a database-access error occurs
   * @throws EndOfTable if thrown by a parameter
   */
  public Object evaluate(boolean aggregate) throws SQLException, EndOfTable {
    boolean nullfound = false;
    if (constant_value==null)
      nullfound=true;
    else if (constant_value.booleanValue()!=and) 
      return constant_value;
    for (int i=0; i<evaluate_order.length; ++i) {
      if (!parameter_constant[evaluate_order[i]]) {
	Object term = parameters[evaluate_order[i]].evaluate(aggregate);
	if (term==null)
	  nullfound = true;
	else if (((Boolean)term).booleanValue()!=and)
	  return term;  // boolean short circuit
      }
    }
    return nullfound ? null : new Boolean(and);
  }

  /**
   * Evaluate parameters and compute the function.  
   *
   * @param match_op how the value should be matched
   * @param match_value desired value
   * @return result object
   * @throws SQLException if a database-access error occurs
   * @throws EndOfTable if thrown by a parameter
   */
  public Object evaluate(int match_op, Object match_value)
    throws SQLException, EndOfTable {

    Boolean match = (Boolean)match_value;
    boolean nullfound = false;

    // process constant value
    if (constant_value==null) {
      nullfound=true;
      // check if we can return early
      if (match_op==MATCH_EQU && match!=null && match.booleanValue()==and)
	return null;
    }
    else if (constant_value.booleanValue()!=and) 
      return constant_value;  // boolean short circuit

    // figure out match_value and match_op for evaluating parameters
    Object mv = null;
    int mf = -1;
    if (match_value==null) {
      if (match_op==MATCH_EQU) {
	mv = new Boolean(!and);
	mf = MATCH_NE;
      }
    }
    else if (match.booleanValue()==and) {
      if (match_op==MATCH_EQU) {
	mv = match_value;
	mf = match_op;
      }
    }
    else { // match_value!=and
      if (match_op==MATCH_NE) {
	mv = match_value;
	mf = match_op;
      }
    }

    // process non-constant parameters
    for (int i=0; i<evaluate_order.length; ++i) {
      if (!parameter_constant[evaluate_order[i]]) {
	if (i==evaluate_order.length-1) {
	  // last term, maybe we can get an (mf,mv) afterall
	  if (match_value!=null && match_op==MATCH_EQU && mv==null) {
	    mv = match_value;
	    mf = match_op;
	  }
	}
	Object term = mv!=null ?
	  parameters[evaluate_order[i]].evaluate(mf,mv) :
	  parameters[evaluate_order[i]].evaluate(false);
	if (term==null) {
	  nullfound = true;
	  // check if we can return early
	  if (match_op==MATCH_EQU && 
	      match!=null && match.booleanValue()==and)
	    return null;
	}
	else if (((Boolean)term).booleanValue()!=and)
	  return term;  // boolean short circuit
      }
    }
    return nullfound ? null : new Boolean(and);
  }


};
