package ModSQL;
import java.sql.*;

/* $Id: Aggregate_MinMax.java,v 1.3 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.
 */

/**
 * Implementation of SQL aggregate functions MIN() and MAX().
 *
 * @author chris.studholme@utoronto.ca
 */
final class Aggregate_MinMax extends AbstractAggregate {

  /** Function type: true for MAX(), false for MIN(). */
  private boolean max;
  /** Current minimum/maximum value. */
  private Object minmax=null;

  /**
   * Returns a new instance of this class if name is "min" or "max".  The
   * instance is initialized as needed to represent the desired function.
   *
   * @param name name of function to find
   * @return instance of this class or null if name does not match
   */
  public static Function forName(String name) {
    if (name.equals("min"))
      return new Aggregate_MinMax(false);
    if (name.equals("max"))
      return new Aggregate_MinMax(true);
    return null;
  }

  /**
   * Constructor.
   *
   * @param max true for MAX(), false for MIN()
   */
  public Aggregate_MinMax(boolean max) {
    this.max=max;
  }

  /**
   * <p>Prepare the function for use.  MIN/MAX accepts at most one parameter
   * and requires a parameter if DISTINCT is requested.  
   *
   * @throws SQLException if the parameters are invalid
   */
  public void optimize() throws SQLException {
    super.optimize();
    if (parameters.length>1)
      throw new SQLException("too many parameters in "+functionName());
    if (distinct && parameters.length==0)
      throw new SQLException("DISTINCT requires a parameter");
  }

  /**
   * Returns "MIN" or "MAX" depending on how this instances was initialized.
   *
   * @return name of function
   */
  public String functionName() {
    return max ? "MAX" : "MIN";
  }

  /**
   * This method returns the SQL type of the parameter, or NULL if there is
   * no parameter.
   *
   * @return SQL type of data to be returned
   * @throws SQLException if a database-access error occurs
   */
  public int getSQLType() throws SQLException {
    return parameters.length>0 ? parameters[0].getSQLType() : Types.NULL;
  }

  /**
   * Return value is the same as for the parameter, or -1 if there is
   * no parameter.
   *
   * @return maximum size of String returned or -1 if unknown
   * @throws SQLException if a database-access error occurs
   */
  public int getMaxResultSize() throws SQLException {
    return parameters.length>0 ? parameters[0].getMaxResultSize() : -1;
  }

  /**
   * Reset the minimum/maximum object to NULL.
   *
   * @throws SQLException if a database error occurs
   */
  public void reset() throws SQLException {
    super.reset();
    minmax=null;
  }

  /**
   * <p>Evaluate parameter and check for new minimum/maximum.  This method
   * always returns the current/new minimum/maximum value, regardless of
   * aggregate.  If aggregate is true, the parameter is not evaluated.
   *
   * @param aggregate true to return final aggregate value
   * @return current minimum/maximum object
   * @throws SQLException if a database-access error occurs
   * @throws EndOfTable if thrown by a parameter
   */
  public Object evaluate(boolean aggregate) throws SQLException, EndOfTable {
    if (!aggregate && parameters.length>0) {
      // evaluate parameter
      Object value = parameters[0].evaluate(false);
      if (value!=null) {
	if (minmax==null)
	  minmax=value;
	else {
	  if (!(value instanceof Comparable))
	    throw new SQLException(functionName()+
				   " requires comparable objects");
	  int c = ((Comparable)value).compareTo(minmax);
	  if (max) c = -c;
	  if (c<0) minmax = value;
	}
      }
    }
    return minmax;
  }
};

