package gnu.jel;
/*
 * $Id: ExpressionBits.java,v 1.3 1998/11/07 19:30:27 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 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.
 */
import gnu.jel.debug.Debug;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;

/**
 * This class handles storage of compiled expressions, their instantiation
 * and naming.
 * <P>As You may notice this class is serializable. Serialization of 
 * <TT>ExpressionBits</TT> objects is preferred method of persistent storage of
 * compiled expressions. To restore compiled expression first deserialize
 * corresponding <TT>ExpressionBits</TT> object and then call its 
 * <TT>getExpression()</TT> method.
 * <P>The unique class name of the expression is generated automatically by
 * appending successive integer numbers (incremented for each instantiated
 * expression) to the "gnu.jel.generated.E_". The number 
 * is represented by long integer
 * allowing to instantiate close to 10^19 expressions during the program
 * lifetime. This means program, instantiating a new expression each second,
 * will fail with numeric overflow in about 300000000000 years, which is 
 * big enough for most applications.
 */
public class ExpressionBits implements java.io.Serializable {

  // Fix serialization version
  static final long serialVersionUID = 5432046992123704544L;
  
  // Number of loaded expressions in this session
  //
  private transient volatile static long expressionUID=0;

  // the chunk of bits going BEFORE the expression name
  private byte[] beforeName=null;

  // The chunk of bits going after the expression name
  private byte[] afterName=null;
  
  // array of the object-type constants (all of them MUST be Serializable)
  private Object[] objectConstants=null;

  // Prefix of the expression classname, determines the package
  // Dots should be replaced by slashes as dictated by Java history ;) 
  private final static String classNamePrefix="gnu/jel/generated/E_";
  
  // name of the expressions
  private String name=null;
  
  ExpressionBits(byte[] beforeName,byte[] afterName,Object[] objectConstants) {
    this.beforeName=beforeName;
    this.afterName=afterName;
    this.objectConstants=objectConstants;
  };
  
  /**
   * Constructs a new instance of the CompiledExpression subclass from these bits.
   * @return a CompiledExpression instance or null if there was a error.
   */
  public CompiledExpression getExpression() {
    byte[] image=getImage();
    
    try {
      // Loading
      ExpressionLoader el=new ExpressionLoader(name,image);
      Class cls=el.loadClass(name);
      
      java.lang.reflect.Constructor[] ca=cls.getConstructors();
      
      if (Debug.enabled)
	Debug.assert(ca.length==1,"There should be only one constructor of "+
		     "the compiled expression.");
      
      // Passing object type constants
      Object[] constrParams=new Object[1];
      constrParams[0]=objectConstants;
      
      // Instantiating
      return (CompiledExpression)ca[0].newInstance(constrParams);
      
    } catch(Exception e) {
      if (Debug.enabled)
	Debug.reportThrowable(e);
      return null;
    };
    
  };

  /**
   * Used to get the binary image of the class.
   * <P> This should be used for debugging only. JEL compiled expressions
   * are not represented only by Java bytecode (since constants of type
   * object are allowed) which means saving bytes into classfile and loading
   * it using JVM classloader will not always work.
   * @return a binary class representation.
   */
  public byte[] getImage() {
    // Approximate length of the class
    name=classNamePrefix+Long.toString(expressionUID++);
    int alen=beforeName.length+afterName.length+30+classNamePrefix.length();
    
    ByteArrayOutputStream baos=new ByteArrayOutputStream(alen);
    DataOutputStream bit_stream=new DataOutputStream(baos);
    try {
      bit_stream.write(beforeName);
      bit_stream.write(1);
      bit_stream.writeUTF(name);
      bit_stream.write(afterName);
      if (Debug.enabled) {
        java.io.FileOutputStream fos=new java.io.FileOutputStream("dump.class");
        baos.writeTo(fos);
        fos.close();
        };
    } catch (java.io.IOException e) { 
      if (Debug.enabled) Debug.reportThrowable(e);
    };
    return baos.toByteArray();
  };
  
};

// --------------------- ExpressionLoader ----------------------------
class ExpressionLoader extends ClassLoader {
  String name;
  byte[] bytes;
  Class c;
  ClassLoader parent;
  
  ExpressionLoader(String name, byte[] bytes) {
    this.name=name;
    this.bytes=bytes;
    this.c=null;
    this.parent=this.getClass().getClassLoader();
  };
  
  protected synchronized Class loadClass(String name, boolean resolve) throws
  java.lang.ClassNotFoundException
    {
      //   Debug.println("Asked to load "+name+" ("+resolve+")");
      if (!name.equals(this.name)) {
	if (parent!=null) 
	  return parent.loadClass(name);
	else
	  return findSystemClass(name);
      } else {
	//      Debug.println("Defining "+this.name);
	if(c==null) {
	  c = defineClass(name,bytes, 0, bytes.length);    
	  if (resolve) resolveClass(c);
	}
	return c;
      };
    };
  
};
