blob: 6a215176ac39138953ed1f4c30a78c2460f63a2a [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.myfaces.extensions.scripting.core.common.util;
import org.apache.myfaces.extensions.scripting.core.api.WeavingContext;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* @author Werner Punz (latest modification by $Author$)
* @version $Revision$ $Date$
*/
public class ReflectUtil
{
static final Logger _logger = Logger.getLogger(ReflectUtil.class.getName());
public static Object instantiate(String clazz, Object... varargs)
{
return instantiate(ClassUtils.forName(clazz), varargs);
}
/**
* A simplified instantiation over reflection
*
* @param clazz the class to be instantiated
* @param varargs the instantiation parameters
* @return the instantiated object
*/
public static Object instantiate(Class clazz, Object... varargs)
{
Class[] classes = new Class[varargs.length];
for (int cnt = 0; cnt < varargs.length; cnt++)
{
if (varargs[cnt] instanceof Cast)
{
classes[cnt] = ((Cast) varargs[cnt]).getClazz();
varargs[cnt] = ((Cast) varargs[cnt]).getValue();
} else
{
classes[cnt] = varargs[cnt].getClass();
}
}
try
{
Constructor constr = clazz.getConstructor(classes);
return constr.newInstance(varargs);
}
catch (NoSuchMethodException e)
{
throw new RuntimeException(e);
}
catch (InvocationTargetException e)
{
throw new RuntimeException(e);
}
catch (IllegalAccessException e)
{
throw new RuntimeException(e);
}
catch (InstantiationException e)
{
throw new RuntimeException(e);
}
}/*this is mostly just a helper to bypass a groovy bug in a more
* complex delegation environment. Groovy throws a classcast
* exception wrongly, delegating the instantiation code to java
* fixes that
* */
public static Object newObject(Class clazz) throws IllegalAccessException, InstantiationException
{
return clazz.newInstance();
}
/**
* Generic execute method which simplifies the reflection api
* down to a usable system
*
* @param obj the target object the method has to be executed upon
* @param methodName the method name
* @param varargs the arguments which have to be passed to the method
* @return the return value of the method
*/
public static Object executeStaticMethod(Class obj, String methodName, Object... varargs)
{
Collection<Method> methods = getMethods(obj, methodName, varargs.length);
Object retVal = handleStaticMethod(obj, methodName, methods, varargs);
if (!methodNotFound(retVal))
{
return retVal;
}
methods = getAllMethods(obj, methodName, varargs.length);
retVal = handleStaticMethod(obj, methodName, methods, varargs);
if (!methodNotFound(retVal))
{
return retVal;
}
throw new RuntimeException("Static Method of :" + methodName + " from class " + obj.getClass().getName() + " not found");
}
public static void setField(Object obj, String fieldName, Object value, boolean protectedField)
{
try
{
Field f = null;
try
{
f = obj.getClass().getDeclaredField(fieldName);
}
catch (NoSuchFieldException e)
{
f = obj.getClass().getField(fieldName);
}
f.setAccessible(protectedField);
f.set(obj, value);
}
catch (NoSuchFieldException e)
{
throw new RuntimeException(e);
}
catch (IllegalAccessException e)
{
throw new RuntimeException(e);
}
}
public static Object getField(Object obj, String fieldName, boolean protectedField)
{
try
{
Field f = null;
try
{
f = obj.getClass().getDeclaredField(fieldName);
}
catch (NoSuchFieldException e)
{
f = obj.getClass().getField(fieldName);
}
f.setAccessible(protectedField);
return f.get(obj);
}
catch (NoSuchFieldException e)
{
throw new RuntimeException(e);
}
catch (IllegalAccessException e)
{
throw new RuntimeException(e);
}
}
public static Collection<Method> getAllMethods(Class clazz, String methodName, int varargLength)
{
ArrayList<Method> retVal = new ArrayList<Method>(30);
while (clazz != null)
{
for (Method m : clazz.getDeclaredMethods())
{
if (m.getParameterTypes().length == varargLength && m.getName().equals(methodName))
{
retVal.add(m);
}
}
clazz = clazz.getSuperclass();
}
return retVal;
}
public static Collection<Method> getMethods(Class clazz, String methodName, int varargLength)
{
ArrayList<Method> retVal = new ArrayList<Method>(30);
for (Method m : clazz.getDeclaredMethods())
{
if (m.getParameterTypes().length == varargLength && m.getName().equals(methodName))
{
retVal.add(m);
}
}
return retVal;
}
/**
* Generic execute method which simplifies the reflection api
* down to a usable system
*
* @param obj the target object the method has to be executed upon
* @param methodName the method name
* @param varargs the arguments which have to be passed to the method
* @return the return value of the method
* @throws RuntimeException a generic runtime exception in case of a failure
* we use unmanaged exceptions here to get a behavior similar to scripting
* language execution where failures can happen but method executions
* should not enforce exception handling
*/
public static Object executeMethod(Object obj, String methodName, Object... varargs)
{
Collection<Method> methods;
//if we have an invocationHandler here we
//can work over the generic invoke interface
//That way we can cover more dynamic stuff
//our reload invocation handler is treated differently here
if (obj instanceof InvocationHandler)
{
InvocationHandler objToInvoke = (InvocationHandler) obj;
Object realTarget = WeavingContext.getInstance().getDelegateFromProxy(objToInvoke);
//first we try only the public because they are the most likely ones
//to be accessed
methods = getMethods(realTarget.getClass(), methodName, varargs.length);
Object retVal = handleInvHandlerMethod(objToInvoke, methodName, methods, varargs);
if (!methodNotFound(retVal))
{
return retVal;
}
//if not we try all of them until we have a match
methods = getAllMethods(realTarget.getClass(), methodName, varargs.length);
retVal = handleInvHandlerMethod(objToInvoke, methodName, methods, varargs);
if (!(methodNotFound(retVal)))
{
return retVal;
}
throw new RuntimeException("Method of :" + methodName + " from class " + obj.getClass().getName() + " not found");
}
Class clazz = obj.getClass();
//first we try only the public because they are the most likely ones
//to be accessed
methods = getMethods(clazz, methodName, varargs.length);
Object retVal = handleObjMethod(obj, methodName, methods, varargs);
if (!methodNotFound(retVal))
{
return retVal;
}
//if not we try all of them until we have a match
methods = getAllMethods(clazz, methodName, varargs.length);
retVal = handleObjMethod(obj, methodName, methods, varargs);
if (!methodNotFound(retVal))
{
return retVal;
}
throw new RuntimeException("Method of :" + methodName + " from class " + obj.getClass().getName() + " not found");
}
/**
* special marker class which is a special return value indicating
* that not method has been found which can be executed
*/
static class _MethodNotFound
{
}
/**
* check if the return value is a method not found return val which
* indicates we have to follow the next workflow step
*
* @param retVal the retVal which has to be investigated
* @return true if the retVal is instance of _MethodNotFound false otherwise
*/
private static boolean methodNotFound(Object retVal)
{
return retVal instanceof _MethodNotFound;
}
/**
* executes a method in an invocation handler with a set of
* methods which are canidates for execution
*
* @param objToInvoke the invokee object
* @param methodName the method name
* @param methods the methods which are under investigation for invoking
* @param varargs the list of varargs to be passed to the method
* @return the result of the invocation, or an object of type _MethodNotFound otherwise
*/
static private Object handleInvHandlerMethod(InvocationHandler objToInvoke, String methodName, Collection<Method> methods, Object... varargs)
{
for (Method m : methods)
{
if (!m.getName().equals(methodName) || m.getParameterTypes().length != varargs.length)
{
continue;
}
try
{
return objToInvoke.invoke(objToInvoke, m, varargs);
}
catch (Throwable e)
{
handleException(e);
}
}
return new _MethodNotFound();
}
/**
* executes a method on an object
*
* @param objToInvoke the invokee object
* @param methodName the method name
* @param methods the methods which are under investigation for invoking
* @param varargs the list of varargs to be passed to the method
* @return the result of the invocation, or an object of type _MethodNotFound otherwise
*/
static private Object handleObjMethod(Object objToInvoke, String methodName, Collection<Method> methods, Object... varargs)
{
for (Method m : methods)
{
if (!m.getName().equals(methodName) || m.getParameterTypes().length != varargs.length)
{
continue;
}
try
{
return m.invoke(objToInvoke, varargs);
}
catch (Throwable e)
{
handleException(e);
}
}
return new _MethodNotFound();
}
/**
* executes a static method on a class
*
* @param objToInvoke the invokee object
* @param methodName the method name
* @param methods the methods which are under investigation for invoking
* @param varargs the list of varargs to be passed to the method
* @return the result of the invocation, or an object of type _MethodNotFound otherwise
*/
static private Object handleStaticMethod(Class objToInvoke, String methodName, Collection<Method> methods, Object... varargs)
{
for (Method m : methods)
{
if (!m.getName().equals(methodName) || m.getParameterTypes().length != varargs.length)
{
continue;
}
try
{
return m.invoke(objToInvoke, varargs);
}
catch (Throwable e)
{
handleException(e);
}
}
return new _MethodNotFound();
}
private static void handleException(Throwable e)
{
if (e instanceof IllegalAccessException)
{
if (_logger.isLoggable(Level.FINEST))
{
_logger.log(Level.FINEST, "", e);
}
} else if (e instanceof IllegalArgumentException)
{
if (_logger.isLoggable(Level.FINEST))
{
_logger.log(Level.FINEST, "", e);
}
} else
{
throw new RuntimeException(e);
}
}
/**
* executes a function method on a target object
*
* @param obj the target object
* @param methodName the method name
* @param varargs a list of objects casts or nulls defining the parameter classes and its values
* if something occurs on introspection level an unmanaged exception is throw, just like
* it would happen in a scripting class
* @return the result object for the Method(method) call
* @throws RuntimeException an unmanaged runtime exception in case of an introspection error
*/
public static Object fastExecuteMethod(Object obj, String methodName, Object... varargs)
{
Class[] classes = new Class[varargs.length];
for (int cnt = 0; cnt < varargs.length; cnt++)
{
if (varargs[cnt] instanceof Cast)
{
classes[cnt] = ((Cast) varargs[cnt]).getClazz();
varargs[cnt] = ((Cast) varargs[cnt]).getValue();
} else
{
classes[cnt] = varargs[cnt].getClass();
}
}
try
{
Method m = fastGetMethod(obj, methodName, classes);
return m.invoke(obj, varargs);
}
catch (NoSuchMethodException e)
{
throw new RuntimeException(e);
}
catch (InvocationTargetException e)
{
throw new RuntimeException(e);
}
catch (IllegalAccessException e)
{
throw new RuntimeException(e);
}
}
/**
* faster reflection call
* if we know the data types exactly we can
* trigger a direct call instead of walking through all methods
* note this method only allows to trigger against directly declared methods
* it ignores the inheritance hierarchy for faster access
*
* @param obj the invokee object
* @param methodName the metod name
* @param classes the parameter type classes
* @return the method if found
* @throws NoSuchMethodException in case it could not be found
*/
public static Method fastGetMethod(Object obj, String methodName, Class[] classes) throws NoSuchMethodException
{
Method m;
try
{
m = obj.getClass().getDeclaredMethod(methodName, classes);
}
catch (NoSuchMethodException e)
{
m = obj.getClass().getMethod(methodName, classes);
}
return m;
}
/**
* executes a function method on a target object
*
* @param obj the target object
* @param methodName the method name
* @param varargs a list of objects casts or nulls defining the parameter classes and its values
* if something occurs on introspection level an unmanaged exception is throw, just like
* it would happen in a scripting class
* @return the result object for the Method(method) call
* @throws RuntimeException an unmanaged runtime exception in case of an introspection error
*/
public static Object fastExecuteStaticMethod(Class obj, String methodName, Object... varargs)
{
Class[] classes = new Class[varargs.length];
for (int cnt = 0; cnt < varargs.length; cnt++)
{
if (varargs[cnt] instanceof Cast)
{
classes[cnt] = ((Cast) varargs[cnt]).getClazz();
varargs[cnt] = ((Cast) varargs[cnt]).getValue();
} else
{
classes[cnt] = varargs[cnt].getClass();
}
}
try
{
Method m = fastGetStaticMethod(obj, methodName, classes);
return m.invoke(obj, varargs);
}
catch (NoSuchMethodException e)
{
throw new RuntimeException(e);
}
catch (InvocationTargetException e)
{
throw new RuntimeException(e);
}
catch (IllegalAccessException e)
{
throw new RuntimeException(e);
}
}
public static Method fastGetStaticMethod(Class obj, String methodName, Class[] classes) throws NoSuchMethodException
{
Method m;
try
{
m = obj.getDeclaredMethod(methodName, classes);
}
catch (NoSuchMethodException e)
{
m = obj.getMethod(methodName, classes);
}
return m;
}
/**
* convenience method which makes the code a little bit more readable
* use it in conjunction with static imports
*
* @param clazz the cast target for the method call
* @param value the value object to be used as param
* @return a Cast object of the parameters
*/
public static Cast cast(Class clazz, Object value)
{
return new Cast(clazz, value);
}
/**
* convenience method which makes the code a little bit more readable
* use it in conjunction with static imports
*
* @param clazz the cast target for the method call
* @return a null value Cast object of the parameters
*/
public static Null nullCast(Class clazz)
{
return new Null(clazz);
}
}