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


  /**
   * <p>Prepare the function for use.  
   *
   * @throws SQLException if the parameters are invalid
   */
  public void optimize() throws SQLException {
    super.optimize();
    if (parameters.length<2) 
      throw new SQLException("LIKE operator requires at least two parameters");
    if (parameters.length>3) 
      throw new SQLException("too many parameters for LIKE operator");
    for (int i=0; i<parameters.length; ++i)
      switch (parameters[i].getSQLType()) {
      case Types.NULL:
      case Types.CHAR:
      case Types.VARCHAR:
      case Types.LONGVARCHAR:
        break;
      default:
        throw new SQLException("invalid parameter type in LIKE operator");
      }
    evaluateConstantParameters();
  }
  
  /**
   * Returns "#LIKE".
   *
   * @return name of function 
   */
  public String functionName() {
    return "#LIKE";
  }


  /****************  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 compare.
   *
   * @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 {
    for (int i=0; i<evaluate_order.length; ++i) {
      if (!parameter_constant[evaluate_order[i]])
	parameter_value[evaluate_order[i]] =
	  parameters[evaluate_order[i]].evaluate(aggregate);
    }
    if (parameters.length==3)
      throw new SQLException("operator LIKE does not support ESCAPE");
    if (parameter_value[0]==null || parameter_value[1]==null)
      return null;
    return new Boolean(patternCompare((String)parameter_value[0],
				      (String)parameter_value[1]));
  }

  /**
   * Evaluate parameters and compare.
   *
   * @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);
  }


  /** 
   * Recursive LIKE pattern matching.
   * % is multi-character string,
   * _ is single character.
   *
   * @param str string to test
   * @param pattern pattern to match
   * @return true for match, false otherwise
   */
  public static boolean patternCompare(String str, String pattern) {
    for (int i=0; i<pattern.length(); ++i) {
      char c = pattern.charAt(i);
      if (c=='%') {
        // recursive check of all possible suffixes of str
        String new_pattern = pattern.substring(i+1);
        for (int j=i; j<=str.length(); ++j)
          if (patternCompare(str.substring(j),new_pattern))
            return true;
        return false;
      }
      else if (i>=str.length())
        return false;
      else if (c!='_' && c!=str.charAt(i))
        return false;
    }
    // more characters in string
    return str.length()==pattern.length();
  }

};
