/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- 
 * $Id: OP.java,v 1.8 2003/09/23 16:19:24 metlov Exp $
 *
 * This file is part of the Java Expressions Library (JEL).
 *   For more information about JEL visit :
 *    http://galaxy.fzu.cz/JEL/
 *
 * (c) 1998 -- 2000 by Konstantin Metlov(metlov@fzu.cz);
 *
 * JEL is Distributed under the terms of GNU General Public License.
 *    This code comes with ABSOLUTELY NO WARRANTY.
 *  For license details see COPYING file in this directory.
 */

package gnu.jel;

import gnu.jel.debug.Debug;
import java.util.Stack;

/**
 * A tree node, representing an operation.
 */
public abstract class OP {

  /** Holds references to children of this node */
  public OP[] chi=null;

  /** Holds type ID of the result of this OP */
  public int resID;
  
  /** Holds type of the result of this OP */
  public Class resType;

  /**
   * Called to evaluate this node and all its sub-nodes.
   * <P>Upon success this node is to be replaced by the constant node
   * holding the returned object.
   * @return an object to which this node evaluates
   * @exception if can't evaluate, in this case the sub-nodes
   */
  public abstract Object eval() throws Exception;

  /**
   * Called to generate the code implementing this OP.
   * @param cf class file with a new open method to write the code into.
   */
  public abstract void compile(ClassFile cf);

  //======================================================
  // Utility methods for dealing with types, etc...
  //======================================================

  /**
   *  Classes of the special types by ID.
   *  <P> The frequently used types (those on which many operations are
   *  defined) are identified by an integer number. The advantage is 
   *  the possibility to have almost completely table driven code generator.
   *  <P> So, the special types are only special in the fact that except
   *  of the reference to their class object they are also identified by an
   *  integer number.
   */
  public final static Class[] specialTypes; // defined in gnu.jel.TablesKeeper

  /**
   * Unwraps the type ID.
   * <P>That is all special types which are references are translated into 8.
   */
  public final static byte[] unwrapType;

  static {
    specialTypes=(Class[])TableKeeper.getTable("specialTypes");
    unwrapType=(byte[]) TableKeeper.getTable("unwrapType");
  };

  /**
   * Identifies the primitive type of the given class.
   * @param c class to identify.
   * @return id of the corresponding primitive type.
   */
  public static final int typeID(Class c) {

    final int NUM_SPECIAL_PRIMITIVE_TYPES=10;
    // ^^^ it is the number of primitive types out of special ones

    if (c==null) return 8;
    if (c.isPrimitive()) {
      int i;
      for(i=0;(i<NUM_SPECIAL_PRIMITIVE_TYPES) && (specialTypes[i]!=c);i++);
      if (Debug.enabled)
        Debug.check(i<NUM_SPECIAL_PRIMITIVE_TYPES,
                     "You didn't put _ALL_ primitive types"+
                     " into primitiveTypes array.");
      return i;
    };

    int i;
    for(i=NUM_SPECIAL_PRIMITIVE_TYPES+1; // TSB is excluded
        (i<specialTypes.length) && (!specialTypes[i].isAssignableFrom(c));
        i++);
    
    if (i<specialTypes.length) return i; 
    return 8; // just a generic reference
  };


  /**
   * Identify the primitive type corresponding to the given reflection object.
   * @param o object to identify.
   * @return id of the corresponding primitive type.
   */
  public static final int typeIDObject(Object o) {
    if (o instanceof java.lang.Boolean) return 0;
    if (o instanceof java.lang.Byte) return 1;
    if (o instanceof java.lang.Character) return 2;
    if (o instanceof java.lang.Short) return 3;
    if (o instanceof java.lang.Integer) return 4;
    if (o instanceof java.lang.Long) return 5;
    if (o instanceof java.lang.Float) return 6;
    if (o instanceof java.lang.Double) return 7;
    if (o instanceof java.lang.String) return 11;
    return 8;
  };

  // bitmap of allowed widening type conversions. 
  private final static int[] cvt_wide;
  static {
    cvt_wide=(int[])TableKeeper.getTable("cvt_wide");
  };

  /**
   * Used to find out if the conversion t1->t2 is widening.
   * @param id1 type ID to convert from
   * @param c1 class to convert from (used if id1==8)
   * @param id2 type ID to convert to
   * @param c2 class to convert to (used if id2==8)
   * @return true if the given conversion is widening (can be done
   *         automatically)
   */
  public static boolean isWidening(int id1, Class c1, int id2, Class c2) {
    if ((id2<=11) && (id2!=8)) {
      // converting into primitive
      id1=unwrapType[id1];
      if (Debug.enabled)
        Debug.check(id1<cvt_wide.length);
      
      return (cvt_wide[id2] & (0x800 >> id1)) >0;
    };

    // converting into object
    if (id1>=8) {
      // converting object into object
      if (c1==c2) return true; // for nulls also
      if (c1==null) return true; // "null" can be widened to any reference
      if (c2==null) return false; // assignment to "null" is narrowing
      return c2.isAssignableFrom(c1); // can assign references
    };

    // primitive into object is certainly not widening
    return false;
  };

  /**
   * Used to find out if the conversion t1->t2 is widening.
   * @param c1 class to convert from (used if id1==8)
   * @param c2 class to convert to (used if id2==8)
   * @return true if the given conversion is widening (can be done
   *         automatically)
   */
  public static boolean isWidening(Class c1, Class c2) {
    return isWidening(typeID(c1),c1,typeID(c2),c2);
  };

  /**
   * Makes widest possible representation of a value of Java primitive type.
   * @param o reflection object, containing value to represent.
   * @param clsID ID of a type of this reflection object (to save lookup).
   * @return Number, representing the given value.
   */
  protected static Number widen(Object o, int clsID) {
    switch (clsID) {
    case 0: // Z
      if (((Boolean)o).booleanValue()) 
        return new Long(1L); 
      else 
        return new Long(0L);
    case 1: // B
      return (Number)o;
    case 2: // C
      return new Long((long)((Character)o).charValue());
    case 3: // S
    case 4: // I
    case 5: // J
    case 6: // F
    case 7: // D
      return (Number)o;
    default:
      if (Debug.enabled)
        Debug.println("Attempt to widen wrong primitive ("+clsID+").");
      return new Long(0L);
    }
  };

  /**
   * Narrows the value back to desired primitiva type.
   * @param o reflection object, containing value to narrow.
   * @param clsID ID of a type to narrow the given object into.
   * @return narrowed reflection object.
   */
  protected static Object narrow(Number val, int clsID) {
    switch (clsID) {
    case 0: // Z
      if (val.longValue()!=0) return Boolean.TRUE; else return Boolean.FALSE;
    case 1: // B
      return new Byte(val.byteValue());
    case 2: // C
      return new Character((char)val.longValue());
    case 3: // S
      return new Short(val.shortValue());
    case 4: // I
      return new Integer(val.intValue());
    case 5: // J
      return new Long(val.longValue());
    case 6:
      return new Float(val.floatValue());
    case 7:
      return new Double(val.doubleValue());
    default:
      if (Debug.enabled)
        Debug.println("Attempt to narrow wrong primitive ("+clsID+").");
      return null;
    }
  };
};
