/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*   This file is part of the Java Expressions Library (JEL).
*   For more information about JEL visit : http://fti.dn.ua/JEL/
*
*   Copyright (C) 1998, 1999, 2000, 2001, 2003, 2006, 2007, 2009 Konstantin L. Metlov
*
*   This program is free software: you can redistribute it and/or modify
*   it under the terms of the GNU General Public License as published by
*   the Free Software Foundation, either version 3 of the License, or
*   (at your option) any later version.
*
*   This program 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 General Public License for more details.
*
*   You should have received a copy of the GNU General Public License
*   along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

package gnu.jel;

import gnu.jel.debug.Debug;

/**
 * This abstract class is a superclass of every JEL-compiled expression,
 * each of which overrides some of the abstract methods below.
 * <p> Most methods of this class accept a reference to the array of objects. 
 * This reference is a pointer to the dynamic object library. As you know,
 * JEL allows to call virtual methods of Java classes, but, as you also know,
 * virtual methods require a reference to an object instance
 * (<tt>this</tt>) to be
 * passed along with parameters. The array <tt>dl</tt> (dynamic 
 * library) serves just this purpose. It should contain references
 * to objects of all classes, whose 
 * <i>virtual</i> methods were put into the Library of callable functions.
 * Objects
 * in the <tt>dl</tt> array should correspond one-to-one to classes
 * in the array, passed as a second argument of <TT>gnu.jel.Library</TT>
 * constructor.
 * <p>
 * <p> There are two ways of evaluating the compiled expressions allowing
 * to compromise between raw performance and simplicity:
 * <p> The <i>first method</i> (simplest one) is based on the call to the
 * <TT> evaluate</TT> method, which will
 * return an object even if the result of computation was of a primitive type.
 * The primitive types will be automatically converted into the 
 * corresponding reflection 
 * objects. There is certain overhead, associated with object creation, it 
 * takes CPU cycles and also produces load on the garbage collector later..
 * <P>For massive (thousand times) evaluations of functions, producing 
 * results of primitive Java types, the <I>second 
 * method</I> can be more suitable. It is based on : first, determining the 
 * type of the result, and, then, subsequent call to the
 * <U>corresponding</U> evaluateXXX method.
 * <P> The type of the resulting expression can be determined by call to the
 * getType() method. It will return an integer number, indentifying the type,
 * <U>proper</U> evaluateXXX method can be determined, 
 * based on the following table:
 * <PRE>
 * getType()   |  method to call
 * ------------+----------------------
 * 0           | evaluate_boolean(...)
 * 1           | evaluate_byte(...)
 * 2           | evaluate_char(...)
 * 3           | evaluate_short(...)
 * 4           | evaluate_int(...)
 * 5           | evaluate_long(...)
 * 6           | evaluate_float(...)
 * 7           | evaluate_double(...)
 * 8           | evaluate(...)         <- result is Object (universal method)
 * 9           | evaluate_void(...)
 * -----------------------------------
 * </PRE>
 * <P> Note: If a wrong evaluateXXX() method is called, it will return zero,
 * and, in debug version of JEL (jel_g.jar) only, a warning will be 
 * printed to stderr.
 * <P> There is a possibility to enforce resulting type of the expression at
 * compile time (see <tt>gnu.jel.Evaluator</tt>). 
 * Use it to avoid unnecesary type checks.
 * @see gnu.jel.Library
 * @see gnu.jel.Evaluator
 */
public abstract class CompiledExpression {
  
  /**
   * Returns type of the expression result.
   * <P> The type is encoded in integer. Following table could help
   * in determining what it is :
   * <PRE>
   * getType()   |  method to call
   * ------------+-----------------
   * 0           | boolean
   * 1           | byte
   * 2           | char
   * 3           | short
   * 4           | int
   * 5           | long
   * 6           | float
   * 7           | double
   * 8           | Object
   * 9           | void
   * </PRE>
   * <P> The reason not to introduce the family of, say, TYPE_XXX constants
   * is to save space. There are so many things connected with these
   * particular numbers in code generator that it could be a pain to change 
   * their meaning. Also, this shoul never be necessary because these 
   * are ALL Java 1.X primitive types.
   * @return the type of the expression, encoded in integer.
   */
  public abstract int getType();

  /**
   * Returns the type of the expression result.
   * <p>If the result has primitive type the corresponding wrapper
   * class is returned (please note that it is just a wrapper class like
   * java.lang.Integer instead of exact primitive type class 
   * java.lang.Integer.TYPE). This corresponds to the wrapping done
   * in non-specialized evaluate() method.
   * <p>When the result is an object, the returned value is the most 
   * specific reference to the class of that object (which is always
   * a subclass of the returned class) available at compile time.
   * <p>The precision of determining the result type this way mainly
   * depends on the design of the user's function namespace (defined
   * by gnu.jel.Library class). It is possible to design a library in
   * such a way that the best approximation to the result type obtainable
   * at compile time would be the java.lang.Object class, which will
   * be returned by this method, thus, providing no useful information
   * about the actual type.
   * <p>Please note again that the guaranteed exact type of the result can't be
   * determined at compile time and can be quiered only after evaluating
   * the expression (directly from resulting object). 
   * <p> The only guarantee
   * this method provides is that a variable of the returned expression type
   * can be assigned by a reference, resulting from a call to evaluate.
   */
  public abstract Class<?> getTypeC();
  
  /**
   * Evaluates the expression, representing result as an object.
   * <P> If the result of evaluation is Java primitive type it gets wrapped
   * into corresponding reflection object , i.e. 
   * <TT>int</TT> -> <TT>java.lang.Integer</TT>,
   * <TT>boolean</TT> -> <TT>java.lang.Boolean</TT>,...
   * @param dl Array of the instance references to the objects in dynamic
   *  library. See description of this class above for more details.
   * @return the result fo computation.
   * @exception Throwable if any runtime error have occured (i.e. division
   *          by 0)
   * @see gnu.jel.Evaluator#compile
   * @see gnu.jel.Library
   */
  public Object evaluate(Object[] dl) throws Throwable {
    int type=getType();
    Object res=null;
    switch (type) {
    case 0: res=new Boolean(evaluate_boolean(dl)); break;
    case 1: res=new Byte(evaluate_byte(dl)); break;
    case 2: res=new Character(evaluate_char(dl)); break;
    case 3: res=new Short(evaluate_short(dl)); break;
    case 4: res=new Integer(evaluate_int(dl)); break;
    case 5: res=new Long(evaluate_long(dl)); break;
    case 6: res=new Float(evaluate_float(dl)); break;
    case 7: res=new Double(evaluate_double(dl)); break;
    case 9: evaluate_void(dl); break;
    default:
      if (Debug.enabled)
	Debug.check(false,"WrongTypeReturned from "+
		     "CompiledExpression.getType().");
    };
    return res;
  };

  /**
   * Evaluates the expression whose result has type <TT>boolean</TT>.
   * <P> If the type of the result is not a <TT>boolean</TT> this function 
   * returns always <TT>false</TT>, debug version will print a warning to stderr.
   * @param dl Array of the instance references to the objects in dynamic
   *  library. See description of this class above for more details.
   * @return the result fo computation.
   * @exception Throwable if any runtime error have occured (i.e. division
   *          by 0)
   * @see gnu.jel.Evaluator#compile
   * @see gnu.jel.Library
   */
  public boolean evaluate_boolean(Object[] dl) throws Throwable {
    if (Debug.enabled)
      Debug.println("Wrong evaluateXXXX() method called,"+
		    " check value of getType().");
    return false;
  };

  /**
   * Evaluates the expression whose result has type <TT>byte</TT>.
   * <P> If the type of the result is not a <TT>byte</TT> this function returns
   * always <TT>0</TT>, debug version will print a warning to stderr.
   * @param dl Array of the instance references to the objects in dynamic
   *  library. See description of this class above for more details.
   * @return the result fo computation.
   * @exception Throwable if any runtime error have occured (i.e. division
   *          by 0)
   * @see gnu.jel.Evaluator#compile
   * @see gnu.jel.Library
   */
  public byte evaluate_byte(Object[] dl) throws Throwable {
    if (Debug.enabled)
      Debug.println("Wrong evaluateXXXX() method called,"+
		    " check value of getType().");
    return 0;
  };

  /**
   * Evaluates the expression whose result has type <TT>short</TT>.
   * <P>If the type of the result is not a <TT>short</TT> this function returns
   * always <TT>0</TT>, debug version will print a warning to stderr.
   * @param dl Array of the instance references to the objects in dynamic
   *  library. See description of this class above for more details.
   * @return the result fo computation.
   * @exception Throwable if any runtime error have occured (i.e. division
   *          by 0)
   * @see gnu.jel.Evaluator#compile
   * @see gnu.jel.Library
   */
  public short evaluate_short(Object[] dl) throws Throwable {
    if (Debug.enabled)
      Debug.println("Wrong evaluateXXXX() method called,"+
		    " check value of getType().");
    return 0;
  };

  /**
   * Evaluates the expression whose result has type <TT>char</TT>.
   * <P>If the type of the result is not a <TT>char</TT> this function returns
   * always <TT>'?'</TT>, debug version will print a warning to stderr.
   * @param dl Array of the instance references to the objects in dynamic
   *  library. See description of this class above for more details.
   * @return the result fo computation.
   * @exception Throwable if any runtime error have occured (i.e. division
   *          by 0)
   * @see gnu.jel.Evaluator#compile
   * @see gnu.jel.Library
   */
  public char evaluate_char(Object[] dl) throws Throwable {
    if (Debug.enabled)
      Debug.println("Wrong evaluateXXXX() method called,"+
		    " check value of getType().");
    return '?';
  };

  /**
   * Evaluates the expression whose result has type <TT>int</TT>.
   * <P>If the type of the result is not a <TT>int</TT> this function returns
   * always <TT>0</TT>, debug version will print a warning to stderr.
   * @param dl Array of the instance references to the objects in dynamic
   *  library. See description of this class above for more details.
   * @return the result fo computation.
   * @exception Throwable if any runtime error have occured (i.e. division
   *          by 0)
   * @see gnu.jel.Evaluator#compile
   * @see gnu.jel.Library
   */
  public int evaluate_int(Object[] dl) throws Throwable {
    if (Debug.enabled)
      Debug.println("Wrong evaluateXXXX() method called,"+
		    " check value of getType().");
    return 0;
  };

  /**
   * Evaluates the expression whose result has type <TT>long</TT>.
   * <P>If the type of the result is not a <TT>long</TT> this function returns
   * always <TT>0</TT>, debug version will print a warning to stderr.
   * @param dl Array of the instance references to the objects in dynamic
   *  library. See description of this class above for more details.
   * @return the result fo computation.
   * @exception Throwable if any runtime error have occured (i.e. division
   *          by 0)
   * @see gnu.jel.Evaluator#compile
   * @see gnu.jel.Library
   */
  public long evaluate_long(Object[] dl) throws Throwable {
    if (Debug.enabled)
      Debug.println("Wrong evaluateXXXX() method called,"+
		    " check value of getType().");
    return 0L;
  };

  /**
   * Evaluates the expression whose result has type <TT>float</TT>.
   * <P>If the type of the result is not a <TT>float</TT> this function returns
   * always <TT>0.0</TT>, debug version will print a warning to stderr.
   * @param dl Array of the instance references to the objects in dynamic
   *  library. See description of this class above for more details.
   * @return the result fo computation.
   * @exception Throwable if any runtime error have occured (i.e. division
   *          by 0)
   * @see gnu.jel.Evaluator#compile
   * @see gnu.jel.Library
   */
  public float evaluate_float(Object[] dl) throws Throwable {
    if (Debug.enabled)
      Debug.println("Wrong evaluateXXXX() method called,"+
		    " check value of getType().");
    return 0.0F;
  };

  /**
   * Evaluates the expression whose result has type <TT>double</TT>.
   * <P>If the type of the result is not a <TT>double</TT> this function 
   * returns always <TT>0.0</TT>, debug version will print a warning to stderr.
   * @param dl Array of the instance references to the objects in dynamic
   *  library. See description of this class above for more details.
   * @return the result fo computation.
   * @exception Throwable if any runtime error have occured (i.e. division
   *          by 0)
   * @see gnu.jel.Evaluator#compile
   * @see gnu.jel.Library
   */
  public double evaluate_double(Object[] dl) throws Throwable {
    if (Debug.enabled)
      Debug.println("Wrong evaluateXXXX() method called,"+
		    " check value of getType().");
    return 0.0D;
  };

  /**
   * Evaluates the expression whose result has type <TT>void</TT>.
   * <P>If the type of the result is not a <TT>void</TT> debug version
   * will print a warning to stderr.
   * @param dl Array of the instance references to the objects in dynamic
   *  library. See description of this class above for more details.
   * @exception Throwable if any runtime error have occured (i.e. division
   *          by 0)
   * @see gnu.jel.Evaluator#compile
   * @see gnu.jel.Library
   */
  public void evaluate_void(Object[] dl) throws Throwable {
    if (Debug.enabled)
      Debug.println("Wrong evaluateXXXX() method called,"+
                    " check value of getType().");
    return;
  };

  

  // String and object comparisons
  private static java.text.Collator collator=null;
  
  public static int compare(String s1,String s2) {
    if (collator==null) 
      collator = java.text.Collator.getInstance();
    return collator.compare(s1,s2);
  };

};






