package ModSQL;
import java.sql.*;
 
/* $Id: Operator_Is.java,v 1.6 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 operator IS. 
 *
 * @author chris.studholme@utoronto.ca
 */
final class Operator_Is extends Operator {

  /** True of IS NOT. */
  private boolean is_not;
  /** Row in case of row-constructor on LHS. */
  private RowConstructor row;
  /** Single column row for use when we don't have a row-constructor. */
  private Object[] single_object = new Object[1];


  /**
   * <p>Prepare the function for use.  
   *
   * <p>If the RHS parameter is a Function of type Operator_Not, it is
   * changed to the parameter of that operator and is_not is set to true.
   *
   * @throws SQLException if the parameters are invalid
   */
  public void optimize() throws SQLException {
    super.optimize();
    if (parameters.length!=2) 
      throw new SQLException("IS operator requires exactly two parameters");
    // pull out null
    is_not = parameters[1] instanceof Operator_Not;
    if (is_not)
      parameters[1] = parameters[1].getParameter(0);
    // check for row constructor
    row = parameters[0] instanceof RowConstructor ?
      (RowConstructor)parameters[0] : null;
    // check parameter types
    switch (parameters[1].getSQLType()) {
    case Types.NULL:
      break;
    case Types.BIT:
      if (row!=null) {
	for (int i=0; i<row.getColumnCount(); ++i)
	  switch (row.getSQLType(i)) {
	  case Types.BIT:
	  case Types.NULL:
	    break;
	  default:
	    throw new SQLException("invalid parameter type in IS operator");
	  }
      }
      else
	switch (parameters[0].getSQLType()) {
	case Types.BIT:
	case Types.NULL:
	  break;
	default:
	  throw new SQLException("invalid parameter type in IS operator");
	}
      break;
    default:
      throw new SQLException("invalid parameter type in IS operator");
    }
  }
  
  /**
   * Returns "#IS".
   *
   * @return name of function 
   */
  public String functionName() {
    return "#IS";
  }


  /****************  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 target = (Boolean)parameters[1].evaluate(aggregate);
    Object[] check;
    if (row!=null)
      check = row.evaluateRow(aggregate);
    else {
      check = single_object;
      check[0] = parameters[0].evaluate(aggregate);
    }
    if (target==null) {
      // IS NULL or IS NOT NULL
      for (int i=0; i<check.length; ++i)
	if ((check[i]==null)==is_not)
	  return new Boolean(false);
      return new Boolean(true);
    }
    else if (is_not) {
      // IS NOT TRUE/FALSE
      boolean b = target.booleanValue();
      for (int i=0; i<check.length; ++i)
	if (check[i]!=null && b==((Boolean)check[i]).booleanValue())
	  return new Boolean(false);
      return new Boolean(true);
    }
    else {
      // IS TRUE/FALSE
      boolean b = target.booleanValue();
      for (int i=0; i<check.length; ++i)
	if (check[i]==null || b!=((Boolean)check[i]).booleanValue())
	  return new Boolean(false);
      return new Boolean(true);
    }
  }

  /**
   * 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 {
    // should do something better here
    return evaluate(false);
  }
};
