/*
* Copyright (C) 2001 Mika Riekkinen, Joni Suominen
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package alt.jiapi.instrumentor;
import java.lang.reflect.Modifier;
import org.apache.log4j.Category;
import alt.jiapi.Runtime;
import alt.jiapi.reflect.InstructionFactory;
import alt.jiapi.reflect.InstructionList;
import alt.jiapi.reflect.JiapiClass;
import alt.jiapi.reflect.JiapiMethod;
import alt.jiapi.reflect.Signature;
import alt.jiapi.reflect.MethodExistsException;
/**
* An Instrumentor which creates a new method for a class.
*
* @author Mika Riekkinen
* @author Joni Suominen
* @version $Revision: 1.12 $ $Date: 2004/03/15 14:47:53 $
*/
public class CreateMethodInstrumentor extends AbstractInstrumentor {
private static Category log = Runtime.getLogCategory(CreateMethodInstrumentor.class);
public static int FORWARD_ORIGINAL = 1;
public static int FORWARD_NEW = 2;
public static int FORWARD_BOTH = 3;
private static int DEFAULT = FORWARD_NEW;
private int modifiers;
private String returnType;
private String methodName;
private String[] parameterNames;
private int forwardMode = DEFAULT;
private boolean createReturn = true;
/**
* A constructor which can be used to create a method with
* empty argument list and void return value.
*
* @param modifiers modifier flags
* @param methodName name of a method
*/
public CreateMethodInstrumentor(int modifiers, String methodName) {
this(modifiers, "void", methodName, new String[0]);
}
/**
* A constructor which can be used to create a method with
* empty argument list and void return value.
*
* @param modifiers modifier flags
* @param methodName name of a method
*/
public CreateMethodInstrumentor(int modifiers, String returnType,
String methodName) {
this(modifiers, returnType, methodName, new String[0]);
}
/**
* A constructor which can be used to create a method with
* with a given signature.
*
* @param modifiers modifier flags
* @param methodName name of a method
* @param parameterNames signature of a method
*/
public CreateMethodInstrumentor(int modifiers, String methodName,
String[] parameterNames) {
this(modifiers, "void", methodName, parameterNames);
}
/**
* A constructor which can be used to create a method using
* a given method as a template.
*
* @param method a method to be used as a template
*/
public CreateMethodInstrumentor(JiapiMethod method) {
this(method.getModifiers(), method.getReturnType(), method.getName(),
method.getParameterTypeNames());
}
/**
* A constructor which can be used to create a method with
* with a given signature.
*
* @param modifiers modifier flags
* @param returnType modifier flags
* @param methodName name of a method
* @param parameterNames signature of a method
*/
public CreateMethodInstrumentor(int modifiers, String returnType,
String methodName,
String[] parameterNames) {
this.methodName = methodName;
this.modifiers = modifiers;
this.parameterNames = parameterNames;
this.returnType = returnType;
}
/**
* Sets the forward mode for the instrumentor. Forward mode
* specifies which InstructionList is sent forward in the chain.
* Possible options are:
* <li>
* <ul>FORWARD_ORIGINAL</ul>
* Forwards the original InstructionList, i.e the InstructionList
* which was given in instrument-method.
* <ul>FORWARD_NEW</ul>
* Forwards the just created method's InstructionList.
* <ul>FORWARD_BOTH</ul>
* Forwards the both InstructionLists
* </li>
*/
public void setForwardMode(int forwardMode) {
this.forwardMode = forwardMode;
}
/**
* Set whether a default body should be created for a method.
* If true, an instructions which will return either null, 0, false
* or 0.0 will be created. Depending on method's return type.
* If set to false, the method body will be left empty.
*
* @param b set to true if default method body should be created
*/
public void setCreateReturn(boolean b) {
createReturn = b;
}
/**
* Instrument a JiapiClass.
*
* @param clazz A Class to be instrumented
*/
public void instrument(InstructionList il) {
JiapiClass clazz = getCurrentClass();
// If the class is interface, then the modifiers must be public.
// Interface can have only public methods.
if (Modifier.isInterface(clazz.getModifiers())) {
if (!Modifier.isPublic(modifiers)) {
log.info("Cannot create non public method " +
methodName + " to interface " + clazz.getName());
// NOTE! Throw an exception if mode is FORWARD_NEW (or BOTH)
forward(il);
return;
}
}
log.info("Creating method " + clazz.getName() + "." + methodName);
JiapiMethod method = null;
try {
Signature signature = new Signature(returnType, parameterNames);
method = clazz.addMethod((short)modifiers, methodName, signature);
}
catch (MethodExistsException mee) {
log.info("method " + clazz.getName() + "." + methodName +
" already exists");
method = mee.getMethod();
}
if (forwardMode == FORWARD_ORIGINAL) {
forward(il);
}
else if (forwardMode == FORWARD_NEW) {
forward(method.getInstructionList());
}
else {
// Forward both
forward(il);
forward(method.getInstructionList());
}
if (createReturn) {
InstructionFactory factory = method.getInstructionFactory();
method.getInstructionList().add(factory.returnMethod(method));
}
}
}