import java.io.IOException;

import org.apache.bcel.Constants;
import org.apache.bcel.generic.Type;
import org.apache.bcel.generic.MethodGen;
import org.apache.bcel.generic.ClassGen;
import org.apache.bcel.generic.InstructionFactory;
import org.apache.bcel.generic.InstructionList;

/**
 * Un generatore di una classe di Java bytecode con un unico metodo che
 * esegue la funzione parziale ricorsiva che e' stata compilata.
 */

public abstract class Generator extends ClassGen {

    /**
     * Costruisce una classe di Java bytecode che scrive sul file system.
     *
     * @param className il nome che si vuole dare alla classe (senza .class)
     * @param pars il numero di argomenti della funzione parziale ricorsiva
     *             che e' stata compilata
     * @throws IOException se si verifica un problema nella scrittura del
     *                     file .class su disco
     */

    protected Generator(String className, int pars) throws IOException {
	super(className,           // il nome della classe
	      "java.lang.Object",  // il nome della sua superclasse
	      "generated by ??",   // il codice sorgente da cui e' compilata
	      Constants.ACC_PUBLIC,// il modificatore d`accesso della classe
	      new String[] {}      // la lista delle sue interfacce
	      );

	// creaiamo un array di pars Type.INT
	Type[] args = new Type[pars];
	for (int pos = 0; pos < pars; pos++) args[pos] = Type.INT;

	// aggiungiamo un metodo:
	//
	// protected static int function(int p_1,...,int p_args)

	MethodGen methodGen = new MethodGen
	    (Constants.ACC_PROTECTED | Constants.ACC_STATIC, // protected static
	     Type.INT,                                // return type
	     args,                                    // parameters types
	     null,                     // parameters names: irrilevante
	     "function",                              // nome del metodo
	     className,                               // nome della classe

	     // creiamo una factory relativa al pool di costanti della classe
	     // che generiamo
	     bytecode(new InstructionFactory(getConstantPool())),
	            // bytecode del metodo

	     getConstantPool());                      // constant pool

	// questi metodi stabiliscono quante variabili locali ed elementi di
	// stack sono necessari al massimo per il metodo
	methodGen.setMaxStack();
	methodGen.setMaxLocals();

	// aggiungiamo il metodo alla classe che stiamo generando
	addMethod(methodGen.getMethod());

	// generiamo sul file system una classe Java bytecode
	// ottenuta a partire da questo generatore
	getJavaClass().dump(className + ".class");
    }

    /**
     * Ritorna il codice per la funzione che e' stata compilata.
     *
     * @param factory la factory che puo' essere usata per generare
     *                qualche bytecode complesso
     * @return il codice
     */

    protected abstract InstructionList bytecode(InstructionFactory factory);
}

