package railo.runtime.reflection;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import railo.commons.lang.StringUtil;
import railo.runtime.exp.PageException;
import railo.runtime.op.Caster;
import railo.runtime.reflection.pairs.ConstructorParameterPair;
import railo.runtime.reflection.pairs.MethodParameterPair;
import railo.runtime.type.ObjectWrap;
/**
* To invoke a Object on different ways
*/
public final class Invoker {
private static Method[] lastMethods;
private static Class lastClass;
/**
* @param clazz
* @param parameters
* @return new Instance
* @throws NoSuchMethodException
* @throws IllegalArgumentException
* @throws InstantiationException
* @throws IllegalAccessException
* @throws InvocationTargetException
*/
public static Object newInstance(Class clazz, Object[] parameters) throws NoSuchMethodException, IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException {
ConstructorParameterPair pair=
getConstructorParameterPairIgnoreCase(clazz, parameters);
return pair.getConstructor().newInstance(pair.getParameters());
}
/**
* search the matching constructor to defined parameter list, also translate parameters for matching
* @param clazz class to get constructo from
* @param parameters parameter for the constructor
* @return Constructor parameter pair
* @throws NoSuchMethodException
*/
public static ConstructorParameterPair getConstructorParameterPairIgnoreCase(Class clazz, Object[] parameters) throws NoSuchMethodException {
// set all values
//Class objectClass=object.getClass();
if(parameters==null)parameters=new Object[0];
// set parameter classes
Class[] parameterClasses=new Class[parameters.length];
for(int i=0;i<parameters.length;i++) {
parameterClasses[i]=parameters[i].getClass();
}
// search right method
Constructor[] constructor=clazz.getConstructors();
for(int mode=0;mode<2;mode++) {
outer: for(int i=0;i<constructor.length;i++) {
Constructor c=constructor[i];
Class[] paramTrg=c.getParameterTypes();
// Same Parameter count
if(parameterClasses.length==paramTrg.length) {
for(int y=0;y<parameterClasses.length;y++) {
if(mode==0 && parameterClasses[y]!=primitiveToWrapperType(paramTrg[y])) {
continue outer;
}
else if(mode==1) {
Object o=compareClasses(parameters[y], paramTrg[y]);
if(o==null)continue outer;
parameters[y]=o;
parameterClasses[y]=o.getClass();
}
}
return new ConstructorParameterPair(c,parameters);
}
}
}
// Exeception
String parameter="";
for(int i=0;i<parameterClasses.length;i++) {
if(i!=0) parameter+=", ";
parameter+=parameterClasses[i].getName();
}
throw new NoSuchMethodException("class constructor "+clazz.getName()+"("+parameter+") doesn't exist");
}
/**
* call of a method from given object
* @param object object to call method from
* @param methodName name of the method to call
* @param parameters parameter for method
* @return return value of the method
* @throws SecurityException
* @throws NoSuchMethodException
* @throws IllegalArgumentException
* @throws IllegalAccessException
* @throws InvocationTargetException
*/
public static Object callMethod(Object object, String methodName, Object[] parameters) throws NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException {
MethodParameterPair pair=
getMethodParameterPairIgnoreCase(object.getClass(), methodName, parameters);
return pair.getMethod().invoke(object,pair.getParameters());
}
/**
* search the matching method to defined Method Name, also translate parameters for matching
* @param objectClass class object where searching method from
* @param methodName name of the method to search
* @param parameters whished parameter list
* @return pair with method matching and parameterlist matching
* @throws NoSuchMethodException
*/
public static MethodParameterPair getMethodParameterPairIgnoreCase(Class objectClass, String methodName, Object[] parameters) throws NoSuchMethodException {
// set all values
if(parameters==null)parameters=new Object[0];
// set parameter classes
Class[] parameterClasses=new Class[parameters.length];
for(int i=0;i<parameters.length;i++) {
parameterClasses[i]=parameters[i].getClass();
}
// search right method
Method[] methods=null;
if(lastClass!=null && lastClass.equals(objectClass)) {
methods=lastMethods;
}
else {
methods=objectClass.getDeclaredMethods();
}
lastClass=objectClass;
lastMethods=methods;
//Method[] methods=objectClass.getMethods();
//Method[] methods=objectClass.getDeclaredMethods();
//methods=objectClass.getDeclaredMethods();
for(int mode=0;mode<2;mode++) {
outer: for(int i=0;i<methods.length;i++) {
Method method=methods[i];
// Same Name
if(method.getName().equalsIgnoreCase(methodName)) {
Class[] paramTrg=method.getParameterTypes();
// Same Parameter count
if(parameterClasses.length==paramTrg.length) {
//if(parameterClasses.length==0)return m;
for(int y=0;y<parameterClasses.length;y++) {
if(mode==0 && parameterClasses[y]!=primitiveToWrapperType(paramTrg[y])) {
continue outer;
}
else if(mode==1) {
Object o=compareClasses(parameters[y], paramTrg[y]);
if(o==null) {
continue outer;
}
parameters[y]=o;
parameterClasses[y]=o.getClass();
}
//if(parameterClasses.length-1==y) return m;
}
return new MethodParameterPair(method,parameters);
}
}
}
}
// Exeception
String parameter="";
for(int i=0;i<parameterClasses.length;i++) {
if(i!=0) parameter+=", ";
parameter+=parameterClasses[i].getName();
}
throw new NoSuchMethodException("method "+methodName+"("+parameter+") doesn't exist in class "+objectClass.getName());
}
/**
* compare parameter with whished parameter class and convert parameter to whished type
* @param parameter parameter to compare
* @param trgClass whished type of the parameter
* @return converted parameter (to whished type) or null
*/
private static Object compareClasses(Object parameter, Class trgClass) {
Class srcClass=parameter.getClass();
trgClass=primitiveToWrapperType(trgClass);
try {
if(parameter instanceof ObjectWrap)
parameter=((ObjectWrap)parameter).getEmbededObject();
// parameter is already ok
if(srcClass==trgClass) return parameter;
else if (instaceOf(srcClass,trgClass)) {
return parameter;
}
else if (trgClass.getName().equals("java.lang.String")) {
return Caster.toString(parameter);
}
else if (trgClass.getName().equals("java.lang.Boolean")) {
return Caster.toBoolean(parameter);
}
else if (trgClass.getName().equals("java.lang.Byte")) {
return new Byte( Caster.toString(parameter));
}
else if (trgClass.getName().equals("java.lang.Character")) {
String str = Caster.toString(parameter);
if(str.length()==1) return new Character(str.toCharArray()[0]);
return null;
}
else if (trgClass.getName().equals("java.lang.Short")) {
return Short.valueOf((short) Caster.toIntValue(parameter));
}
else if (trgClass.getName().equals("java.lang.Integer")) {
return Integer.valueOf(Caster.toIntValue(parameter));
}
else if (trgClass.getName().equals("java.lang.Long")) {
return Long.valueOf((long)Caster.toDoubleValue(parameter));
}
else if (trgClass.getName().equals("java.lang.Float")) {
return Float.valueOf((float)Caster.toDoubleValue(parameter));
}
else if (trgClass.getName().equals("java.lang.Double")) {
return Caster.toDouble(parameter);
}
}
catch (PageException e) {
return null;
}
return null;
}
/**
* @param srcClass
* @param trgClass
* @return is instance of or not
*/
private static boolean instaceOf(Class srcClass, Class trgClass) {
while(srcClass!=null) {
if(srcClass==trgClass) return true;
srcClass=primitiveToWrapperType(srcClass.getSuperclass());
}
return false;
}
/**
* cast a primitive type class definition to his object reference type
* @param clazz class object to check and convert if it is of primitive type
* @return object reference class object
*/
private static Class primitiveToWrapperType(Class clazz) {
// boolean, byte, char, short, int, long, float, and double
if(clazz==null) return null;
else if(clazz.isPrimitive()) {
if(clazz.getName().equals("boolean"))return Boolean.class;
else if(clazz.getName().equals("byte"))return Byte.class;
else if(clazz.getName().equals("char"))return Character.class;
else if(clazz.getName().equals("short"))return Short.class;
else if(clazz.getName().equals("int"))return Integer.class;
else if(clazz.getName().equals("long"))return Long.class;
else if(clazz.getName().equals("float"))return Float.class;
else if(clazz.getName().equals("double"))return Double.class;
}
return clazz;
}
/**
* to invoke a getter Method of a Object
* @param o Object to invoke method from
* @param prop Name of the Method without get
* @return return Value of the getter Method
* @throws SecurityException
* @throws NoSuchMethodException
* @throws IllegalArgumentException
* @throws IllegalAccessException
* @throws InvocationTargetException
*/
public static Object callGetter(Object o, String prop) throws SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException {
prop="get"+prop;
Class c=o.getClass();
Method m=getMethodParameterPairIgnoreCase(c, prop, null).getMethod();
//Method m=getMethodIgnoreCase(c,prop,null);
if(m.getReturnType().getName().equals("void"))
throw new NoSuchMethodException("invalid return Type, method ["+m.getName()+"] can't have return type void");
return m.invoke(o,null);
}
/**
* to invoke a setter Method of a Object
* @param o Object to invoke method from
* @param prop Name of the Method without get
* @param value Value to set to the Method
* @throws SecurityException
* @throws NoSuchMethodException
* @throws IllegalArgumentException
* @throws IllegalAccessException
* @throws InvocationTargetException
*/
public static void callSetter(Object o, String prop,Object value) throws SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException {
prop="set"+StringUtil.ucFirst(prop);
Class c=o.getClass();
//Class[] cArg=new Class[]{value.getClass()};
Object[] oArg=new Object[]{value};
MethodParameterPair mp = getMethodParameterPairIgnoreCase(c, prop, oArg);
//Method m=getMethodIgnoreCase(c,prop,cArg);
if(!mp.getMethod().getReturnType().getName().equals("void"))
throw new NoSuchMethodException("invalid return Type, method ["+mp.getMethod().getName()+"] must have return type void, now ["+mp.getMethod().getReturnType().getName()+"]");
mp.getMethod().invoke(o,mp.getParameters());
}
/**
* to get a visible Property of a object
* @param o Object to invoke
* @param prop property to call
* @return property value
* @throws NoSuchFieldException
* @throws IllegalArgumentException
* @throws IllegalAccessException
*/
public static Object getProperty(Object o, String prop) throws NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
Field f=getFieldIgnoreCase(o.getClass(),prop);
return f.get(o);
}
/**
* assign a value to a visible property of a object
* @param o Object to assign value to his property
* @param prop name of property
* @param value Value to assign
* @throws IllegalArgumentException
* @throws IllegalAccessException
* @throws NoSuchFieldException
*/
public static void setProperty(Object o, String prop,Object value) throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException {
getFieldIgnoreCase(o.getClass(),prop).set(o,value);
}
/**
* same like method getField from Class but ignore case from field name
* @param c class to search the field
* @param name name to search
* @return Matching Field
* @throws NoSuchFieldException
*/
public static Field getFieldIgnoreCase(Class c, String name) throws NoSuchFieldException {
Field[] fields=c.getFields();
for(int i=0;i<fields.length;i++) {
Field f=fields[i];
// Same Name
if(f.getName().equalsIgnoreCase(name)) {
return f;
}
}
throw new NoSuchFieldException("Field doesn't exist");
}
/**
* call of a static method of a Class
* @param staticClass class how contains method to invoke
* @param methodName method name to invoke
* @param values Arguments for the Method
* @return return value from method
* @throws PageException
*/
public static Object callStaticMethod(Class staticClass, String methodName, Object[] values) throws PageException {
if(values==null)values=new Object[0];
MethodParameterPair mp;
try {
mp = getMethodParameterPairIgnoreCase(staticClass, methodName, values);
}
catch (NoSuchMethodException e) {
throw Caster.toPageException(e);
}
try {
return mp.getMethod().invoke(null,mp.getParameters());
}
catch (InvocationTargetException e) {
Throwable target = e.getTargetException();
if(target instanceof PageException) throw (PageException)target;
throw Caster.toPageException(e.getTargetException());
}
catch (Exception e) {
throw Caster.toPageException(e);
}
}
}