/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 * $Id: ImageLoader.java,v 1.2 2000/02/12 17:31:32 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.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;

/**
 * Loads the JEL generated classes into memory.
 * <P> Specifics of JEL generated classes is that the class name UTF8 is always
 * the first entry in the constant pool. This loader will not load 
 * other classes.
 */
public class ImageLoader extends ClassLoader {
  String name;
  byte[] bytes;
  Class c;
  ClassLoader parent;
  
  private ImageLoader(String name, byte[] bytes) {
    this.name=name;
    this.bytes=bytes;
    this.c=null;
    this.parent=this.getClass().getClassLoader();
  };
  

  /**
   * Loads given JEL-generated image under its own name.
   * @param image to load
   * @return the class object for the new class or <TT>null</TT> 
   *         if unsuccessful.
   */
  public static Class load(byte[] image) {
    try {
      // determine the class name
      ByteArrayInputStream bais=new ByteArrayInputStream(image);
      DataInputStream dis=new DataInputStream(bais);
      dis.skip(10+(Debug.enabled?0:1));
      
      if (Debug.enabled)
        Debug.assert(dis.readUnsignedByte()==1,
                     "Attempt to load non-JEL generated class file");
      
      String name=dis.readUTF();

      // load class
      ImageLoader il=new ImageLoader(name,image);
      return il.loadClass(name);
    } catch(Exception e) {
      if (Debug.enabled)
        Debug.reportThrowable(e);
    };
    return null;    
  };


  // Number of loaded expressions in this session
  private transient volatile static long expressionUID=0;

  /**
   * Prefix of the expression classname, should be fully qualified with
   * dots replaced by slashes as dictated by Java history ;) 
   * <P>Example: "gnu/jel/generated/E_"
   */
  public static String classNamePrefix="gnu/jel/generated/E_";

  /**
   * Loads given JEL-generated image under unique name.
   * <P>The unique name is generated by appending a steadily incremented
   * number to the <TT>classNamePrefix</TT>.
   * @param image to load
   * @return the class object for the new class or <TT>null</TT> 
   *         if unsuccessful.
   * @see gnu.jel.ImageLoader#classNamePrefix
   */
  public static Class loadRenamed(byte[] image) {
    try {
      // determine the class name
      ByteArrayInputStream bais=new ByteArrayInputStream(image);
      DataInputStream dis=new DataInputStream(bais);
      ByteArrayOutputStream baos=new ByteArrayOutputStream();
      DataOutputStream dos=new DataOutputStream(baos);

      byte[] buf=new byte[15];
      int rc=dis.read(buf,0,11);
      if (Debug.enabled)
        Debug.assert((rc==11) && (buf[10]==1),
                     "Attempt to load non-JEL generated class file");
      String name=dis.readUTF();
      dos.write(buf,0,11);
      
      // generate and write out a new uniqe name
      dos.writeUTF(classNamePrefix+Long.toString(expressionUID++));

      // write the rest of the image
      while ((rc=dis.read(buf))>0) dos.write(buf,0,rc);

      // load class
      ImageLoader il=new ImageLoader(name,baos.toByteArray());
      
      return il.loadClass(name);
    } catch(Exception e) {
      if (Debug.enabled)
        Debug.reportThrowable(e);
    };
    return null;
  };

  protected synchronized Class loadClass(String name, boolean resolve) throws
  java.lang.ClassNotFoundException  {
    if (!name.equals(this.name)) {
      if (parent!=null) 
        return parent.loadClass(name);
      else
        return findSystemClass(name);
    } else {
      if(c==null) {
        c = defineClass(name,bytes, 0, bytes.length);    
        if (resolve) resolveClass(c);
      }
      return c;
    };
  };  
};

