/*
* Copyright 2005-2006 the original author or authors.
*
* Licensed 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.strecks.util;
import java.beans.PropertyDescriptor;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import org.apache.commons.beanutils.PropertyUtils;
import org.strecks.exceptions.ApplicationRuntimeException;
/**
* Class with reflection-related helper methods
* @author Phil Zoio
*/
public class ReflectHelper
{
/**
* Returns the property name associated with a setter method with the given name
*/
public static String getPropertyName(String setterName)
{
Character firstChar = setterName.charAt(3);
String propertyName = setterName.substring(4);
propertyName = Character.toLowerCase(firstChar) + propertyName;
return propertyName;
}
/**
* Gets getter corresponding with setter method
*/
public static Method getGetter(Method setterMethod)
{
PropertyDescriptor property = BeanUtils.findPropertyForMethod(setterMethod);
if (property == null)
{
return null;
}
else
{
return property.getReadMethod();
}
}
/**
* Gets setter corresponding with getter method
*/
public static Method getSetter(Method getterMethod)
{
PropertyDescriptor property = BeanUtils.findPropertyForMethod(getterMethod);
if (property == null)
{
return null;
}
else
{
return property.getWriteMethod();
}
}
/**
* Checks whether a particular method begins with "set"
*/
public static String checkSetterMethodName(Method m)
{
String methodName = m.getName();
if (!methodName.startsWith("set"))
{
throw new ApplicationRuntimeException("Method " + m + " declared in " + m.getDeclaringClass()
+ " is not a setter method");
}
return methodName;
}
/**
* Checks that a method has exactly the specified number of parameters
*/
public static void checkParameterTypeLength(Method m, int parameterCount)
{
Class<?>[] parameterTypes = m.getParameterTypes();
if (parameterTypes.length != parameterCount)
{
throw new ApplicationRuntimeException("Method " + m.getName() + " in class " + m.getDeclaringClass()
+ " must have " + parameterCount + " parameter(s)");
}
}
public static boolean isGetter(Method method)
{
PropertyDescriptor property = BeanUtils.findPropertyForMethod(method);
if (property == null)
return false;
return property.getReadMethod().equals(method);
}
public static boolean hasReturnValue(Method method)
{
Class<?> returnType = method.getReturnType();
boolean equals = returnType.equals(Void.TYPE);
return (!equals);
}
public static boolean isSetter(Method method)
{
boolean isSetter = method.getName().startsWith("set") && method.getParameterTypes() != null
&& method.getParameterTypes().length == 1;
return isSetter;
}
/**
* Invokes the specified method on the specified object
* @return
*/
public static <T extends Object> T invokeMethod(Object o, String methodName, Class<T> returnType)
{
Method method = getMethod(o, methodName);
return invokeMethod(o, method, returnType);
}
/**
* Invokes the specified method on the specified object
* @return
*/
public static Object invokeMethod(Object o, String methodName)
{
Method method = getMethod(o, methodName);
return invokeMethod(o, method);
}
public static Method getMethod(Object o, String methodName)
{
Assert.notNull(o);
Assert.notNull(methodName);
Class<? extends Object> clazz = o.getClass();
return getMethod(methodName, clazz);
}
public static Method getMethod(String methodName, Class<? extends Object> clazz)
{
Assert.notNull(clazz);
Assert.notNull(methodName);
Method method = null;
try
{
method = clazz.getMethod(methodName);
}
catch (IllegalArgumentException e)
{
throw new ApplicationRuntimeException("Unable to retrieve method " + methodName + "() of class "
+ clazz.getName(), e);
}
catch (SecurityException e)
{
throw new ApplicationRuntimeException(
"Security violation " + methodName + "() of class " + clazz.getName(), e);
}
catch (NoSuchMethodException e)
{
throw new ApplicationRuntimeException("No method " + methodName + "() of class " + clazz.getName(), e);
}
return method;
}
public static Object invokeMethod(Object o, Method method)
{
return invokeMethod(o, method, null);
}
@SuppressWarnings("unchecked")
public static <T extends Object> T invokeMethod(Object o, Method method, Class<T> returnType)
{
Assert.notNull(o);
Assert.notNull(method);
try
{
Object invoke = method.invoke(o);
if (returnType != null && invoke != null && !invoke.getClass().isAssignableFrom(returnType))
{
throw new ApplicationRuntimeException("Unable to cast result " + invoke + " of method "
+ method.getName() + "() of class " + o.getClass().getName() + " to return type " + returnType);
}
return (T) invoke;
}
catch (IllegalArgumentException e)
{
throw new ApplicationRuntimeException("Unable to invoke method " + method.getName() + " of class "
+ o.getClass(), e);
}
catch (SecurityException e)
{
throw new ApplicationRuntimeException("Security violation " + method.getName() + " of class "
+ o.getClass(), e);
}
catch (IllegalAccessException e)
{
throw new ApplicationRuntimeException("Security violation " + method.getName() + " of class "
+ o.getClass(), e);
}
catch (InvocationTargetException e)
{
Throwable targetException = e.getTargetException();
Throwable nested = null;
if (targetException instanceof Exception)
nested = targetException;
else
nested = e;
throw new ApplicationRuntimeException("Invocation target exception for " + method.getName() + " of class "
+ o.getClass(), nested);
}
}
public static <T extends Object> T createInstance(String className, Class<T> type)
{
Assert.notNull(className);
Class c = null;
try
{
c = Class.forName(className, true, ReflectHelper.class.getClassLoader());
}
catch (ClassNotFoundException e)
{
throw new ApplicationRuntimeException("Unable to create new instance of " + className
+ " as class cannot be located", e);
}
return createInstance(c, type);
}
@SuppressWarnings("unchecked")
public static <T> T createInstance(Class clazz, Class<T> type)
{
try
{
Object newInstance = clazz.newInstance();
if (!(type.isAssignableFrom(newInstance.getClass())))
{
throw new ApplicationRuntimeException("Class " + clazz.getName()
+ " was instantiated but was not an instance of the type " + type);
}
return (T) newInstance;
}
catch (InstantiationException e)
{
throw new ApplicationRuntimeException("Unable to create new instance of " + clazz
+ " as class cannot be instantiated", e);
}
catch (IllegalAccessException e)
{
throw new ApplicationRuntimeException("Illegal access in attempting to create instance of " + clazz, e);
}
}
/**
* Checks that the input type is parameterized by the class expectedType
* @param inputClass
* the class being tested
* @param genericInterface
* is the interface that the inputClass implements
* @param specificType
* the expected parameterization type
* @return false if the expectedType is not assignable from the actual parameterized type
*/
@SuppressWarnings("unchecked")
public static boolean checkGenericType(Class inputClass, Class genericInterface, Class specificType)
{
Class c = inputClass;
Type[] genericInterfaces = c.getGenericInterfaces();
for (Type type : genericInterfaces)
{
if (type instanceof ParameterizedType)
{
ParameterizedType t = (ParameterizedType) type;
Type rawType = t.getRawType();
if (rawType instanceof Class && rawType.equals(genericInterface))
{
Type[] actualTypeArguments = t.getActualTypeArguments();
if (actualTypeArguments.length > 0)
{
Type arg = actualTypeArguments[0];
if (arg instanceof Class)
{
Class<Object> expectedType = (Class<Object>) arg;
if (!expectedType.isAssignableFrom(specificType))
{
return false;
}
}
}
}
}
}
return true;
}
/**
* Returns the (single) generic parameterization type fo the input class, for a given interface,
* if this can be determined
* @return the parameterization class, if this can be determined, otherwise null
*/
@SuppressWarnings("unchecked")
public static Class getGenericType(Class c, Class genericInterface)
{
Type[] actualTypeArguments = getGenericTypes(c, genericInterface);
if (actualTypeArguments != null && actualTypeArguments.length > 0)
{
Type arg = actualTypeArguments[0];
if (arg instanceof Class)
{
Class<Object> specificType = (Class<Object>) arg;
return specificType;
}
}
return null;
}
/**
* Returns the (single) generic parameterization type fo the input class, for a given interface,
* if this can be determined
* @return the parameterization class, if this can be determined, otherwise null
*/
@SuppressWarnings("unchecked")
public static Type[] getGenericTypes(Class c, Class genericInterface)
{
Class inputClass = c;
ParameterizedType genericType = null;
do
{
genericType = getParameterizedType(genericInterface, inputClass);
}
while (genericType == null && ((inputClass = inputClass.getSuperclass()) != null));
if (genericType != null)
{
Type[] actualTypeArguments = genericType.getActualTypeArguments();
return actualTypeArguments;
}
return null;
}
static ParameterizedType getParameterizedType(Class genericInterface, Class inputClass)
{
Type[] genericInterfaces = inputClass.getGenericInterfaces();
ParameterizedType genericType = null;
for (Type type : genericInterfaces)
{
if (type instanceof ParameterizedType)
{
ParameterizedType t = (ParameterizedType) type;
Type rawType = t.getRawType();
if (rawType instanceof Class && rawType.equals(genericInterface))
{
genericType = t;
break;
}
}
}
return genericType;
}
/**
* Provides basic information on a particular type
*/
public static String getTypeDescription(Type type)
{
Assert.notNull(type);
if (type instanceof Class)
{
return type.toString();
}
else if (type instanceof ParameterizedType)
{
ParameterizedType pType = (ParameterizedType) type;
String typeDescription = "parameterized " + getTypeDescription(pType.getRawType());
Type[] args = pType.getActualTypeArguments();
String typesDescription = getTypeArrayDescription(args);
typeDescription = typeDescription + " " + typesDescription;
return typeDescription;
}
else if (type instanceof GenericArrayType)
{
GenericArrayType gType = (GenericArrayType) type;
String typeDescription = "array of " + getTypeDescription(gType.getGenericComponentType());
return typeDescription;
}
else if (type instanceof WildcardType)
{
WildcardType wType = (WildcardType) type;
String typeDescription = "wildcard generic type";
String upperDesc = getTypeArrayDescription(wType.getUpperBounds());
String lowerDesc = getTypeArrayDescription(wType.getLowerBounds());
if (StringUtils.notBlankOrNull(upperDesc))
typeDescription = typeDescription + " with upper bound " + upperDesc;
if (StringUtils.notBlankOrNull(lowerDesc))
typeDescription = typeDescription + " and lower bound " + lowerDesc;
return typeDescription;
}
else if (type instanceof TypeVariable)
{
TypeVariable tType = (TypeVariable) type;
String typeDescription = "type variable named " + tType.getName();
typeDescription = typeDescription + " bounded by " + getTypeArrayDescription(tType.getBounds());
return typeDescription;
}
return "Unknown";
}
public static String getTypeArrayDescription(Type[] types)
{
Assert.notNull(types);
if (types.length == 0)
return "";
StringBuffer buffer = new StringBuffer("(");
for (Type arg : types)
{
buffer.append(getTypeDescription(arg)).append(", ");
}
StringUtils.shorten(buffer, 2);
buffer.append(")");
String typesDescription = buffer.toString();
return typesDescription;
}
/**
* Fnd the type of a the property of <code>containingBean</code> identified by
* <code>beanPropertyName</code>
* @return the type of the bean property as a class <code>Class</code> instance
*/
public static Class<?> getBeanPropertyType(Object containingBean, String beanPropertyName)
{
Class<?> propertyType = null;
try
{
PropertyDescriptor propertyDescriptor = PropertyUtils.getPropertyDescriptor(containingBean,
beanPropertyName);
propertyType = propertyDescriptor.getPropertyType();
}
catch (Exception e)
{
throw new ApplicationRuntimeException("Unable to read property descriptor for bean "
+ containingBean.getClass().getName() + ", property " + beanPropertyName, e);
}
return propertyType;
}
}