package sample.vector;

import java.io.IOException;
import javassist.*;
import sample.preproc.Assistant;

/**
 * This is a Javassist program which produce a new class representing
 * vectors of a given type.  For example,
 *
 * <ul>import java.util.Vector by sample.vector.VectorAssistant(int)</ul>
 *
 * <p>requests the Javassist preprocessor to substitute the following
 * lines for the original import declaration:
 *
 * <ul><pre>
 * import java.util.Vector;
 * import sample.vector.intVector;
 * </pre></ul>
 *
 * <p>The Javassist preprocessor calls <code>VectorAssistant.assist()</code>
 * and produces class <code>intVector</code> equivalent to:
 *
 * <ul><pre>
 * package sample.vector;
 *
 * public class intVector extends Vector {
 *   pubilc void add(int value) {
 *     addElement(new Integer(value));
 *   }
 *
 *   public int at(int index) {
 *     return elementAt(index).intValue();
 *   }
 * }
 * </pre></ul>
 *
 * <p><code>VectorAssistant.assist()</code> uses
 * <code>sample.vector.Sample</code> and <code>sample.vector.Sample2</code>
 * as a template to produce the methods <code>add()</code> and
 * <code>at()</code>.
 */
public class VectorAssistant implements Assistant {
    public final String packageName = "sample.vector.";

    /**
     * Calls <code>makeSubclass()</code> and produces a new vector class.
     * This method is called by a <code>sample.preproc.Compiler</code>.
     *
     * @see sample.preproc.Compiler
     */
    public CtClass[] assist(ClassPool pool, String vec, String[] args)
	throws CannotCompileException
    {
	if (args.length != 1)
	    throw new CannotCompileException(
			"VectorAssistant receives a single argument.");

	try {
	    CtClass subclass;
	    CtClass elementType = pool.get(args[0]);
	    if (elementType.isPrimitive())
		subclass = makeSubclass2(pool, elementType);
	    else
		subclass = makeSubclass(pool, elementType);

	    CtClass[] results = { subclass, pool.get(vec) };
	    return results;
	}
	catch (NotFoundException e) {
	    throw new CannotCompileException(e);
	}
	catch (IOException e) {
	    throw new CannotCompileException(e);
	}
    }

    /**
     * Produces a new vector class.  This method does not work if
     * the element type is a primitive type.
     *
     * @param type	the type of elements
     */
    public CtClass makeSubclass(ClassPool pool, CtClass type)
	throws CannotCompileException, NotFoundException, IOException
    {
	CtClass vec = pool.makeClass(makeClassName(type));
	vec.setSuperclass(pool.get("java.util.Vector"));

	CtClass c = pool.get("sample.vector.Sample");
	CtMethod addmethod = c.getDeclaredMethod("add");
	CtMethod atmethod = c.getDeclaredMethod("at");

	ClassMap map = new ClassMap();
	map.put("sample.vector.X", type.getName());

	vec.addMethod(CtNewMethod.copy(addmethod, "add", vec, map));
	vec.addMethod(CtNewMethod.copy(atmethod, "at", vec, map));
	vec.writeFile();
	return vec;
    }

    /**
     * Produces a new vector class.  This uses wrapped methods so that
     * the element type can be a primitive type.
     *
     * @param type	the type of elements
     */
    public CtClass makeSubclass2(ClassPool pool, CtClass type)
	throws CannotCompileException, NotFoundException, IOException
    {
	CtClass vec = pool.makeClass(makeClassName(type));
	vec.setSuperclass(pool.get("java.util.Vector"));

	CtClass c = pool.get("sample.vector.Sample2");
	CtMethod addmethod = c.getDeclaredMethod("add");
	CtMethod atmethod = c.getDeclaredMethod("at");

	CtClass[] args1 = { type };
	CtClass[] args2 = { CtClass.intType };
	CtMethod m
	    = CtNewMethod.wrapped(CtClass.voidType, "add", args1,
				  null, addmethod, null, vec);
	vec.addMethod(m);
	m = CtNewMethod.wrapped(type, "at", args2,
				null, atmethod, null, vec);
	vec.addMethod(m);
	vec.writeFile();
	return vec;
    }

    private String makeClassName(CtClass type) {
	return packageName + type.getSimpleName() + "Vector";
    }
}