com.blazebit.reflection.ReflectionUtils Maven / Gradle / Ivy
/*
* Copyright 2011 Blazebit
*/
package com.blazebit.reflection;
import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import java.util.*;
/**
* Utillity class for reflection specific actions. This class only uses basic
* reflection mechanisms provided by the Reflection API. It provides methods
* that are missing in the standard API and Apache Commons Utils.
*
* @author Christian Beikov
* @since 0.1.2
*/
public final class ReflectionUtils {
private static final Map> PRIMITIVE_NAME_TO_TYPE;
private static final Map, Class> PRIMITIVE_TO_WRAPPER;
private static final Map, Class> WRAPPER_TO_PRIMITIVE;
static {
Map> primitiveNameToType = new HashMap>();
primitiveNameToType.put("int", Integer.TYPE);
primitiveNameToType.put("long", Long.TYPE);
primitiveNameToType.put("double", Double.TYPE);
primitiveNameToType.put("float", Float.TYPE);
primitiveNameToType.put("boolean", Boolean.TYPE);
primitiveNameToType.put("char", Character.TYPE);
primitiveNameToType.put("byte", Byte.TYPE);
primitiveNameToType.put("void", Void.TYPE);
primitiveNameToType.put("short", Short.TYPE);
PRIMITIVE_NAME_TO_TYPE = Collections.unmodifiableMap(primitiveNameToType);
Map, Class> primitiveToWrapper = new HashMap, Class>();
primitiveToWrapper.put(int.class, Integer.class);
primitiveToWrapper.put(long.class, Long.class);
primitiveToWrapper.put(double.class, Double.class);
primitiveToWrapper.put(float.class, Float.class);
primitiveToWrapper.put(boolean.class, Boolean.class);
primitiveToWrapper.put(char.class, Character.class);
primitiveToWrapper.put(byte.class, Byte.class);
primitiveToWrapper.put(void.class, Void.class);
primitiveToWrapper.put(short.class, Short.class);
PRIMITIVE_TO_WRAPPER = Collections.unmodifiableMap(primitiveToWrapper);
Map, Class> wrapperToPrimitive = new HashMap, Class>();
wrapperToPrimitive.put(Integer.class, int.class);
wrapperToPrimitive.put(Long.class, long.class);
wrapperToPrimitive.put(Double.class, double.class);
wrapperToPrimitive.put(Float.class, float.class);
wrapperToPrimitive.put(Boolean.class, boolean.class);
wrapperToPrimitive.put(Character.class, char.class);
wrapperToPrimitive.put(Byte.class, byte.class);
wrapperToPrimitive.put(Void.class, void.class);
wrapperToPrimitive.put(Short.class, short.class);
WRAPPER_TO_PRIMITIVE = Collections.unmodifiableMap(wrapperToPrimitive);
}
private ReflectionUtils() {
}
/**
* Returns the class object for the specified qualified class name. Calling
* this method is equal to #{@link Class#forName(java.lang.String)} except
* that also primitive types can be get via this method. The names for the
* primitive types needed for this method are equal to the type literals
* used in the java language.
*
* Example:
*
*
* ReflectionUtil.getClass("void").equals(void.class)
* ReflectionUtil.getClass("int").equals(int.class)
*
*
* @param className
* @return
* @throws ClassNotFoundException
*/
public static Class getClass(String className)
throws ClassNotFoundException {
Class clazz = PRIMITIVE_NAME_TO_TYPE.get(className);
if (clazz == null) {
clazz = Class.forName(className);
}
return clazz;
}
/**
* Returns the wrapper class of the given primitive class or the given class.
*
* @param primitive The primitive class
* @return The wrapper class or the given class
*/
public static Class getObjectClassOfPrimitve(Class primitive) {
Class objectClass = PRIMITIVE_TO_WRAPPER.get(primitive);
if (objectClass != null) {
return objectClass;
}
return primitive;
}
/**
* Returns the wrapper class of the given primitive class or null.
*
* @param primitive The primitive class
* @return The wrapper class or null
*/
public static Class getWrapperClassOfPrimitve(Class primitive) {
return PRIMITIVE_TO_WRAPPER.get(primitive);
}
/**
* Returns the primitive class of the given wrapper class or null.
*
* @param wrapperClass The wrapper class
* @return The primitive class or null
*/
public static Class getPrimitiveClassOfWrapper(Class wrapperClass) {
return WRAPPER_TO_PRIMITIVE.get(wrapperClass);
}
/**
* Checks if the target class is a subtype of the supertype or not.
* Basically this method calls @link{java.lang.Class#isAssignableFrom(Class)}
* and therefore only acts as an alias.
*
* @param targetClazz the class to check wether it is a subtype of the supertype or
* not
* @param superType the supertype class
* @return true if targetClazz is subtype of superType or targetClazz equals
* superType, otherwise false
*/
public static boolean isSubtype(Class targetClazz, Class superType) {
return superType.isAssignableFrom(targetClazz);
}
/**
* Retrieves all super types of the given class type. Super types are all
* types the given class extends or implements. The given class type is also
* included in the set. The iteration order of the set has to be from most
* concrete to most general.
*
* @param clazz The class from which the super types should be retrieved
* @return The super types of the given class
*/
public static Set> getSuperTypes(Class clazz) {
return getSuperTypes(clazz, Object.class);
}
public static Set> getSuperTypes(Class clazz, Class commonSuperType) {
Set> list = new LinkedHashSet>();
addSuperTypes(list, clazz, commonSuperType);
return list;
}
private static void addSuperTypes(Set> superTypes, Class clazz, Class commonSuperType) {
Class traverseClass = clazz;
do {
if (isSubtype(traverseClass, commonSuperType)) {
superTypes.add(traverseClass);
}
for (Class interfaceClass : traverseClass.getInterfaces()) {
if (isSubtype(interfaceClass, commonSuperType)) {
superTypes.add(interfaceClass);
}
}
for (Class interfaceClass : traverseClass.getInterfaces()) {
if (isSubtype(interfaceClass, commonSuperType)) {
addSuperTypes(superTypes, interfaceClass, commonSuperType);
}
}
traverseClass = traverseClass.getSuperclass();
} while (traverseClass != null);
}
/**
* Returns the type of a field if it exists within the class. Calling this
* method is equal to calling #
* {@link ReflectionUtils#getField(java.lang.Class, java.lang.String)
* } with
* a null check and finally return the type via getType().
*
* @param clazz The class within to look for the field with the given field
* name
* @param fieldName The name of the field to be returned
* @return The type of the field if it can be found, otherwise null
* @see ReflectionUtils#getField(java.lang.Class, java.lang.String)
*/
public static Class getFieldType(Class clazz, String fieldName) {
Field f = getField(clazz, fieldName);
if (f == null) {
return null;
}
return f.getType();
}
public static Class getResolvedFieldType(Class clazz, String fieldName) {
return getResolvedFieldType(clazz, getField(clazz, fieldName));
}
public static Class getResolvedFieldType(Class clazz, Field f) {
if (f == null) {
return null;
}
if (f.getGenericType() instanceof TypeVariable) {
return resolveTypeVariable(clazz,
(TypeVariable) f.getGenericType());
}
return f.getType();
}
public static Class[] getResolvedFieldTypeArguments(Class clazz, String fieldName) {
return getResolvedFieldTypeArguments(clazz, getField(clazz, fieldName));
}
public static Class[] getResolvedFieldTypeArguments(Class clazz, Field f) {
if (f == null) {
return null;
}
return resolveTypeArguments(clazz, f.getGenericType());
}
public static Class[] resolveTypeArguments(Class concreteClass, Type type) {
if (type instanceof ParameterizedType) {
return resolveTypeArguments(concreteClass, (ParameterizedType) type);
}
return new Class[0];
}
public static Class[] resolveTypeArguments(Class concreteClass, ParameterizedType parameterizedType) {
if (parameterizedType == null) {
return null;
}
Type[] argumentTypes = parameterizedType.getActualTypeArguments();
Class[] resolvedClasses = new Class[argumentTypes.length];
for (int i = 0; i < argumentTypes.length; i++) {
resolvedClasses[i] = resolveType(concreteClass, argumentTypes[i]);
}
return resolvedClasses;
}
public static Type resolve(Class concreteClass, Type type) {
if (type instanceof TypeVariable) {
return resolveTypeVariableType(concreteClass, (TypeVariable) type);
} else if (type instanceof GenericArrayType) {
return getArrayClass((GenericArrayType) type);
} else if (type instanceof WildcardType) {
WildcardType wildcardType = ((WildcardType) type);
if (wildcardType.getLowerBounds().length > 0) {
return resolve(concreteClass, wildcardType.getLowerBounds()[0]);
} else {
return resolve(concreteClass, wildcardType.getUpperBounds()[0]);
}
} else {
// We assume here that only class types, type variables and parameterized types are
// possible as argument types for the parameterized type
return type;
}
}
public static Class resolveType(Class concreteClass, Type type) {
if (type instanceof TypeVariable) {
return resolveTypeVariable(concreteClass, (TypeVariable) type);
} else if (type instanceof ParameterizedType) {
return (Class) ((ParameterizedType) type).getRawType();
} else if (type instanceof GenericArrayType) {
return getArrayClass((GenericArrayType) type);
} else if (type instanceof WildcardType) {
WildcardType wildcardType = ((WildcardType) type);
if (wildcardType.getLowerBounds().length > 0) {
return resolveType(concreteClass, wildcardType.getLowerBounds()[0]);
} else {
return resolveType(concreteClass, wildcardType.getUpperBounds()[0]);
}
} else {
// We assume here that only class types, type variables and parameterized types are
// possible as argument types for the parameterized type
return (Class) type;
}
}
private static Class getArrayClass(GenericArrayType genericArrayType) {
Type componentType = genericArrayType.getGenericComponentType();
Class componentClass;
if (componentType instanceof Class) {
componentClass = (Class) componentType;
} else if (componentType instanceof ParameterizedType) {
componentClass = (Class) ((ParameterizedType) componentType).getRawType();
} else {
throw new IllegalArgumentException("Unsupported array component type: " + componentType);
}
Object o = Array.newInstance(componentClass, 0);
return o.getClass();
}
private static Class getClassThatContainsTypeVariable(TypeVariable typeVariable) {
if (typeVariable.getGenericDeclaration().getClass() == Class.class) {
return (Class) typeVariable.getGenericDeclaration();
}
return null;
}
/**
* Tries to resolve the type variable against the concrete class. The
* concrete class has to be a subtype of the type in which the type variable
* has been declared. This method tries to resolve the given type variable
* by inspecting the subclasses of the class in which the type variable was
* declared and as soon as the resolved type is instance of java.lang.Class
* it stops and returns that class.
*
* @param concreteClass The class which is used to resolve the type. The type for the
* type variable must be bound in this class or a superclass.
* @param typeVariable The type variable to resolve.
* @return The resolved type
* @throws IllegalArgumentException Is thrown when the concrete class is not a subtype of the
* class in which the type variable has been declared.
*/
public static Type resolveTypeVariableType(Class concreteClass, TypeVariable typeVariable) {
Class classThatContainsTypeVariable = getClassThatContainsTypeVariable(typeVariable);
// If the type variable is defined in the concrete class, we can only use the bounds
if (classThatContainsTypeVariable == null || concreteClass == classThatContainsTypeVariable) {
return resolve(concreteClass, typeVariable.getBounds()[0]);
}
if (!isSubtype(concreteClass, classThatContainsTypeVariable)) {
throw new IllegalArgumentException(
"The given concrete class is not a subtype of the class that contain the type variable!");
}
int position = getTypeVariablePosition(typeVariable);
if (position == -1) {
// Should never happen
throw new IllegalArgumentException(
"Type variable not found in its container class!");
}
Set> superTypes = getSuperTypes(concreteClass, classThatContainsTypeVariable);
List> classStack = new ArrayList<>(superTypes.size());
Type resolvedType = typeVariable;
// The class that contains the type variable mustn't be considered
superTypes.remove(classThatContainsTypeVariable);
// Build a stack of the class hierarchy to be able to resolve the type
classStack.addAll(superTypes);
// Resolve every type variable in every class level
// We need to do this here because the type variables of subclasses
// of the container class of the type variable could move their
// type variables to different positions
while (!classStack.isEmpty() && !(resolvedType instanceof Class)) {
// Start at most general type and go down the hierarchy until
// we reach concreteClass. Resolve the current type variable
// to every level of the hierarchy and stop as soon as the
// type is instance of java.lang.Class
Class classToInspect = classStack.remove(classStack.size() - 1);
Type[] genericInterfaces = classToInspect.getGenericInterfaces();
List typesToInspect = new ArrayList(genericInterfaces.length + 1);
typesToInspect.add(classToInspect.getGenericSuperclass());
Collections.addAll(typesToInspect, genericInterfaces);
for (Type classToInspectType : typesToInspect) {
// Since the resolvedType is not yet instance of class, the
// classToInspectType has to be a ParameterizedType
// otherwise we can not resolve the type variable
if (!(classToInspectType instanceof ParameterizedType)) {
continue;
}
ParameterizedType parameterizedClassToInspect = (ParameterizedType) classToInspectType;
// The found parameterized type is not the one we are looking for
if (!classThatContainsTypeVariable.equals(parameterizedClassToInspect.getRawType())) {
continue;
}
// This should be fulfilled, anyway we check it
if (parameterizedClassToInspect.getActualTypeArguments().length < position + 1) {
throw new IllegalArgumentException("Could not resolve type");
}
// Set the type of the type arguments at the needed position
// as the resolvedType
resolvedType = parameterizedClassToInspect.getActualTypeArguments()[position];
if (resolvedType instanceof TypeVariable) {
// If the currently available resolvedType is still a type
// variable
// retrieve the position of the type variable within the type
// variables of the current class, so we can look in the next
// subclass for the concrete type
classThatContainsTypeVariable = getClassThatContainsTypeVariable((TypeVariable) resolvedType);
if (classThatContainsTypeVariable == null) {
return resolve(concreteClass, ((TypeVariable) resolvedType).getBounds()[0]);
}
position = getTypeVariablePosition(classToInspect, (TypeVariable) resolvedType);
break;
} else if (resolvedType instanceof ParameterizedType) {
// Since we only want a class object, we don't
// care about type arguments of the parameterized type
// and just set the raw type of it as the resolved
// type
resolvedType = ((ParameterizedType) resolvedType).getRawType();
break;
} else if (resolvedType instanceof WildcardType) {
WildcardType wildcardType = ((WildcardType) resolvedType);
if (wildcardType.getLowerBounds().length > 0) {
resolvedType = resolveType(concreteClass, wildcardType.getLowerBounds()[0]);
} else {
resolvedType = resolveType(concreteClass, wildcardType.getUpperBounds()[0]);
}
break;
} else if (resolvedType instanceof Class) {
break;
}
}
}
return resolvedType;
}
/**
* Tries to resolve the type variable against the concrete class. The
* concrete class has to be a subtype of the type in which the type variable
* has been declared. This method tries to resolve the given type variable
* by inspecting the subclasses of the class in which the type variable was
* declared and as soon as the resolved type is instance of java.lang.Class
* it stops and returns that class.
*
* @param concreteClass The class which is used to resolve the type. The type for the
* type variable must be bound in this class or a superclass.
* @param typeVariable The type variable to resolve.
* @return The resolved type as class
* @throws IllegalArgumentException Is thrown when the concrete class is not a subtype of the
* class in which the type variable has been declared.
*/
public static Class resolveTypeVariable(Class concreteClass, TypeVariable typeVariable) {
Type resolvedType = resolveTypeVariableType(concreteClass, typeVariable);
return resolveType(concreteClass, resolvedType);
}
/**
* Returns the position of the type variable for the class in which it is
* declared.
*
* @param typeVariable The type variable for which the position should be retrieved
* @return The position of the type variable within the class in which it is
* declared.
*/
public static int getTypeVariablePosition(TypeVariable typeVariable) {
return getTypeVariablePosition(typeVariable.getGenericDeclaration(),
typeVariable);
}
/**
* Tries to find the position of the given type variable in the type
* parameters of the given class. This method iterates through the type
* parameters of the given class and tries to find the given type variable
* within the type parameters. When the type variable is found, the position
* is returned, otherwise -1.
*
* @param genericDeclartion The generic declartion type in which to look for the type
* variable
* @param typeVariable The type variable to look for in the given class type
* parameters
* @return The position of the given type variable within the type
* parameters of the given class if found, otherwise -1
*/
public static int getTypeVariablePosition(GenericDeclaration genericDeclartion, TypeVariable typeVariable) {
int position = -1;
TypeVariable[] typeVariableDeclarationParameters = genericDeclartion
.getTypeParameters();
// Try to find the position of the type variable in the class
for (int i = 0; i < typeVariableDeclarationParameters.length; i++) {
if (typeVariableDeclarationParameters[i].equals(typeVariable)) {
position = i;
break;
}
}
return position;
}
/**
* Returns the static field objects that are declared in the given class or
* any of it's super types. Calling this method is equivalent to a call to
* {@link ReflectionUtils#getNonMatchingFields(Class, int)} with the
* modifiers {@link Modifier#STATIC}.
*
* @param clazz The class within to look for the fields with the given
* modifiers
* @return The array of static fields that are within the type hierarchy of
* the given class
*/
public static Field[] getInstanceFields(Class clazz) {
return getNonMatchingFields(clazz, Modifier.STATIC);
}
/**
* Returns the static field objects that are declared in the given class or
* any of it's super types. Calling this method is equivalent to a call to
* {@link ReflectionUtils#getMatchingFields(Class, int)} with the modifiers
* {@link Modifier#STATIC}.
*
* @param clazz The class within to look for the fields with the given
* modifiers
* @return The array of static fields that are within the type hierarchy of
* the given class
*/
public static Field[] getStaticFields(Class clazz) {
return getMatchingFields(clazz, Modifier.STATIC);
}
private static final Comparator FIELD_NAME_AND_DECLARING_CLASS_COMPARATOR = new Comparator() {
@Override
public int compare(Field o1, Field o2) {
int result = o1.getName().compareTo(o2.getName());
return result == 0 ? o1.getDeclaringClass().getName()
.compareTo(o2.getDeclaringClass().getName()) : result;
}
};
/**
* Returns the field objects that are declared in the given class or any of
* it's super types that have any of the given modifiers. The type hierarchy
* is traversed upwards and all declared fields that match the given
* modifiers are added to the result array. The elements in the array are
* sorted by their names and declaring classes.
*
* @param clazz The class within to look for the fields with the given
* modifiers
* @param modifiers The OR-ed together modifiers that a field must match to be
* included into the result
* @return The array of fields that match the modifiers and are within the
* type hierarchy of the given class
*/
public static Field[] getMatchingFields(Class clazz, final int modifiers) {
final Set fields = new TreeSet(
FIELD_NAME_AND_DECLARING_CLASS_COMPARATOR);
traverseHierarchy(clazz, new TraverseTask() {
@Override
public Field run(Class clazz) {
Field[] fieldArray = clazz.getDeclaredFields();
for (int i = 0; i < fieldArray.length; i++) {
if ((modifiers & fieldArray[i].getModifiers()) != 0) {
fields.add(fieldArray[i]);
}
}
return null;
}
});
return fields.toArray(new Field[fields.size()]);
}
/**
* Returns the field objects that are declared in the given class or any of
* it's super types that have none of the given modifiers. The type
* hierarchy is traversed upwards and all declared fields that do not match
* the given modifiers are added to the result array. The elements in the
* array are sorted by their names and declaring classes.
*
* @param clazz The class within to look for the fields with the given
* modifiers
* @param modifiers The OR-ed together modifiers that a field must not match to be
* included into the result
* @return The array of fields that do not match the modifiers and are
* within the type hierarchy of the given class
*/
public static Field[] getNonMatchingFields(Class clazz, final int modifiers) {
final Set fields = new TreeSet(
FIELD_NAME_AND_DECLARING_CLASS_COMPARATOR);
traverseHierarchy(clazz, new TraverseTask() {
@Override
public Field run(Class clazz) {
Field[] fieldArray = clazz.getDeclaredFields();
for (int i = 0; i < fieldArray.length; i++) {
if ((modifiers & fieldArray[i].getModifiers()) == 0) {
fields.add(fieldArray[i]);
}
}
return null;
}
});
return fields.toArray(new Field[fields.size()]);
}
/**
* Returns the field object found for the given field name in the given
* class. This method traverses through the super classes of the given class
* and tries to find the field as declared field within these classes. When
* the object class is reached the traversing stops. If the field can not be
* found, null is returned.
*
* @param clazz The class within to look for the field with the given field
* name
* @param fieldName The name of the field to be returned
* @return The field object with the given field name if the field can be
* found, otherwise null
*/
public static Field getField(Class clazz, String fieldName) {
final String internedName = fieldName.intern();
return traverseHierarchy(clazz, new TraverseTask() {
@Override
public Field run(Class clazz) {
Field[] fields = clazz.getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
if (fields[i].getName() == internedName) {
return fields[i];
}
}
return null;
}
});
}
/**
* Returns the return type of a method if it exists within the class.
* Calling this method is equal to calling {@link ReflectionUtils#getMethod(Class, String, Class[])} with
* a null check and finally return the type via getReturnType().
*
* @param clazz The class within to look for the method with the given method
* name
* @param methodName The name of the method to be returned
* @param parameterTypes The accepting parameter types of the method
* @return The return type of the method if it can be found, otherwise null
* @see ReflectionUtils#getMethod(Class, String, Class[])
*/
public static Class getMethodReturnType(Class clazz, String methodName, Class... parameterTypes) {
Method m = getMethod(clazz, methodName, parameterTypes);
if (m == null) {
return null;
}
return m.getReturnType();
}
public static Class[] getMethodParameterTypes(Class clazz, String methodName, Class... parameterTypes) {
Method m = getMethod(clazz, methodName, parameterTypes);
if (m == null) {
return null;
}
return m.getParameterTypes();
}
public static Class[] getMethodExceptionTypes(Class clazz, String methodName, Class... parameterTypes) {
Method m = getMethod(clazz, methodName, parameterTypes);
if (m == null) {
return null;
}
return m.getExceptionTypes();
}
public static Class getResolvedMethodReturnType(Class clazz, String methodName, Class... parameterTypes) {
return getResolvedMethodReturnType(clazz,
getMethod(clazz, methodName, parameterTypes));
}
public static Class getResolvedMethodReturnType(Class clazz, Method m) {
if (m == null) {
return null;
}
if (m.getGenericReturnType() instanceof TypeVariable) {
return resolveTypeVariable(clazz,
(TypeVariable) m.getGenericReturnType());
}
return m.getReturnType();
}
public static Class[] getResolvedMethodReturnTypeArguments(Class clazz, String methodName, Class... parameterTypes) {
return getResolvedMethodReturnTypeArguments(clazz,
getMethod(clazz, methodName, parameterTypes));
}
public static Class[] getResolvedMethodReturnTypeArguments(Class clazz, Method m) {
if (m == null) {
return null;
}
return resolveTypeArguments(clazz, m.getGenericReturnType());
}
public static Class[] getResolvedMethodParameterTypes(Class clazz, String methodName, Class... parameterTypes) {
return getResolvedMethodParameterTypes(clazz,
getMethod(clazz, methodName, parameterTypes));
}
public static Class[] getResolvedMethodParameterTypes(Class clazz, Method m) {
if (m == null) {
return null;
}
Type[] genericParameterTypes = m.getGenericParameterTypes();
Class[] parameterTypes = new Class[genericParameterTypes.length];
for (int i = 0; i < genericParameterTypes.length; i++) {
if (genericParameterTypes[i] instanceof TypeVariable) {
parameterTypes[i] = resolveTypeVariable(clazz,
(TypeVariable) genericParameterTypes[i]);
} else if (genericParameterTypes[i] instanceof ParameterizedType) {
parameterTypes[i] = (Class) ((ParameterizedType) genericParameterTypes[i]).getRawType();
} else {
parameterTypes[i] = (Class) genericParameterTypes[i];
}
}
return parameterTypes;
}
public static Class[][] getResolvedMethodParameterTypesArguments(Class clazz, String methodName, Class... parameterTypes) {
return getResolvedMethodParameterTypesArguments(clazz, getMethod(clazz, methodName, parameterTypes));
}
public static Class[][] getResolvedMethodParameterTypesArguments(Class clazz, Method method) {
int parameterCount = method.getParameterTypes().length;
Class[][] parameterTypeArguments = new Class[parameterCount][];
Type[] genericParameterTypes = method.getGenericParameterTypes();
for (int i = 0; i < parameterCount; i++) {
parameterTypeArguments[i] = resolveTypeArguments(clazz, genericParameterTypes[i]);
}
return parameterTypeArguments;
}
public static Class[] getResolvedMethodExceptionTypes(Class clazz, String methodName, Class... parameterTypes) {
return getResolvedMethodExceptionTypes(clazz,
getMethod(clazz, methodName, parameterTypes));
}
public static Class[] getResolvedMethodExceptionTypes(Class clazz, Method m) {
if (m == null) {
return null;
}
Type[] genericExceptionTypes = m.getGenericExceptionTypes();
Class[] exceptionTypes = new Class[genericExceptionTypes.length];
for (int i = 0; i < genericExceptionTypes.length; i++) {
if (genericExceptionTypes[i] instanceof TypeVariable) {
exceptionTypes[i] = resolveTypeVariable(clazz,
(TypeVariable) genericExceptionTypes[i]);
} else if (genericExceptionTypes[i] instanceof ParameterizedType) {
exceptionTypes[i] = (Class) ((ParameterizedType) genericExceptionTypes[i]).getRawType();
} else {
exceptionTypes[i] = (Class) genericExceptionTypes[i];
}
}
return exceptionTypes;
}
public static MethodParameter[] getMethodParameters(Class clazz, String methodName, Class... parameterTypes) {
return getMethodParameters(clazz,
getMethod(clazz, methodName, parameterTypes));
}
public static MethodParameter[] getMethodParameters(Class clazz, Method m) {
if (m == null) {
return null;
}
Type[] exceptionTypes = m.getGenericExceptionTypes();
MethodParameter[] methodParameters = new MethodParameter[exceptionTypes.length];
for (int i = 0; i < exceptionTypes.length; i++) {
methodParameters[i] = new MethodParameter(m, i);
}
return methodParameters;
}
public static MethodException[] getMethodExceptions(Class clazz, String methodName, Class... parameterTypes) {
return getMethodExceptions(clazz,
getMethod(clazz, methodName, parameterTypes));
}
public static MethodException[] getMethodExceptions(Class clazz, Method m) {
if (m == null) {
return null;
}
Type[] exceptionTypes = m.getGenericExceptionTypes();
MethodException[] methodExceptions = new MethodException[exceptionTypes.length];
for (int i = 0; i < exceptionTypes.length; i++) {
methodExceptions[i] = new MethodException(m, i);
}
return methodExceptions;
}
/**
* Returns the method object found for the given method name in the given
* class. This method traverses through the super classes of the given class
* and tries to find the method as declared method within these classes.
* When the object class is reached the traversing stops. If the method can
* not be found, null is returned.
*
* @param clazz The class within to look for the method with the given method
* name
* @param methodName The name of the method to be returned
* @param parameterTypes The accepting parameter types of the method
* @return The method object with the given method name if the method can be
* found, otherwise null
*/
public static Method getMethod(Class clazz, final String methodName, final Class... parameterTypes) {
final String internedName = methodName.intern();
return traverseHierarchy(clazz, new TraverseTask() {
@Override
public Method run(Class clazz) {
Method[] methods = clazz.getDeclaredMethods();
Method res = null;
for (int i = 0; i < methods.length; i++) {
Method m = methods[i];
if (m.getName() == internedName
&& arrayContentsEq(parameterTypes, m.getParameterTypes())
&& (res == null
|| res.getReturnType().isAssignableFrom(m.getReturnType()))) {
res = m;
}
}
return res;
}
});
}
private static interface TraverseTask {
public T run(Class clazz);
}
private static T traverseHierarchy(Class clazz, TraverseTask task) {
Queue> classQueue = new LinkedList>();
Class traverseClass;
classQueue.add(clazz);
while (!classQueue.isEmpty()) {
traverseClass = classQueue.remove();
T result = task.run(traverseClass);
if (result != null) {
return result;
}
if (traverseClass.getSuperclass() != null) {
classQueue.add(traverseClass.getSuperclass());
}
for (Class interfaceClass : traverseClass.getInterfaces()) {
classQueue.add(interfaceClass);
}
}
return null;
}
private static boolean arrayContentsEq(Object[] a1, Object[] a2) {
if (a1 == null) {
return a2 == null || a2.length == 0;
}
if (a2 == null) {
return a1.length == 0;
}
if (a1.length != a2.length) {
return false;
}
for (int i = 0; i < a1.length; i++) {
if (a1[i] != a2[i]) {
return false;
}
}
return true;
}
/**
* Returns the method object for a method which is annotated with the
* given annotation of the given class. This method traverses through
* the super classes of the given class and tries to find the method as
* declared method within these classes that is annotated with an
* annotation of the given annotation type.
* When the object class is reached the traversing stops. If the method can
* not be found, null is returned.
* This methods immediatelly returns the first method found that is
* annotated with an annotation of the given type.
* To retrieve all methods annotated with the given annotation type see
* {@link ReflectionUtils#getMethods(java.lang.Class, java.lang.Class)}
*
* @param clazz The class within to look for the method
* @param annotation The annotation type a method must be annotated with to be
* returned
* @return The method object for the method annotated with the given
* annotation type if the method can be found, otherwise null
*/
public static Method getMethod(Class clazz, final Class annotation) {
return traverseHierarchy(clazz, new TraverseTask() {
@Override
public Method run(Class clazz) {
Method[] methods = clazz.getDeclaredMethods();
for (int i = 0; i < methods.length; i++) {
Method m = methods[i];
if (m.getAnnotation(annotation) != null) {
return m;
}
}
return null;
}
});
}
/**
* Returns the method objects for methods which are annotated with the
* given annotation of the given class. This method traverses through
* the super classes of the given class and tries to find methods as
* declared methods within these classes which are annotated with an
* annotation of the given annotation type.
* When the object class is reached the traversing stops. If no methods
* can be found, an empty list is returned.
* The order of the methods is random.
*
* @param clazz The class within to look for the methods
* @param annotation The annotation type a method must be annotated with to be
* included in the list
* @return A list of method objects for methods annotated with the given
* annotation type or an emtpy list
*/
public static List getMethods(Class clazz, final Class annotation) {
final List methods = new ArrayList();
traverseHierarchy(clazz, new TraverseTask() {
@Override
public Method run(Class clazz) {
Method[] methodArray = clazz.getDeclaredMethods();
for (int i = 0; i < methodArray.length; i++) {
Method m = methodArray[i];
if (m.getAnnotation(annotation) != null) {
methods.add(m);
}
}
return null;
}
});
return methods;
}
/**
* Retrieves the getter method of the given class for the specified field
* name. The method first tries to find the getFieldName method of the class
* and if it can not find that method it looks for the isFieldName method.
* If this method also can not be found, null is returned.
*
* This method uses #
* {@link ReflectionUtils#getMethodReturnType(Class, String, Class...)} to
* retrieve the getter.
*
* A getter must not have any parameters and must have a return type that is
* different from void.
*
* @param clazz The class within to look for the getter method
* @param fieldName The field name for which to find the getter method
* @return The getter method for the given fieldName if it can be found,
* otherwise null
*/
public static Method getGetter(Class clazz, String fieldName) {
StringBuilder sb = new StringBuilder("get").append(
Character.toUpperCase(fieldName.charAt(0))).append(fieldName,
1, fieldName.length());
final String internedGetName = sb.toString().intern();
final String internedIsName = sb.replace(0, 3, "is").toString().intern();
return traverseHierarchy(clazz, new TraverseTask() {
@Override
public Method run(Class clazz) {
Method[] methods = clazz.getDeclaredMethods();
Method res = null;
for (int i = 0; i < methods.length; i++) {
Method m = methods[i];
if (isGetterSignature(m)) {
if (m.getName() == internedGetName
&& (res == null
|| res.getReturnType().isAssignableFrom(m.getReturnType()))) {
res = m;
}
if (m.getName() == internedIsName
&& (res == null
|| res.getReturnType().isAssignableFrom(m.getReturnType()))) {
res = m;
}
}
}
return res;
}
});
}
private static boolean isGetterSignature(Method m) {
return m != null && !void.class.equals(m.getReturnType())
&& m.getParameterTypes().length == 0;
}
public static boolean isGetter(Method m) {
return m != null && (m.getName().startsWith("get") || m.getName().startsWith("is"))
&& !void.class.equals(m.getReturnType()) && m.getParameterTypes().length == 0;
}
/**
* Retrieves the setter method of the given class for the specified field
* name. The method traverses through all methods of all types that are in
* the inheritance hierarchie of the given class and tries to find a setter
* method with the return type void and exactly one parameter. If that
* method can not be found, null is returned.
*
* A setter must have void return type and accept exactly one parameter.
*
* @param clazz The class within to look for the setter method
* @param fieldName The field name for which to find the setter method
* @return The setter method for the given fieldName if it can be found,
* otherwise null
*/
public static Method getSetter(Class clazz, String fieldName) {
StringBuilder sb = new StringBuilder("set").append(
Character.toUpperCase(fieldName.charAt(0))).append(fieldName,
1, fieldName.length());
final String internedName = sb.toString().intern();
return traverseHierarchy(clazz, new TraverseTask() {
@Override
public Method run(Class clazz) {
Method[] methods = clazz.getDeclaredMethods();
Method res = null;
for (int i = 0; i < methods.length; i++) {
Method m = methods[i];
if (isSetterSignature(m)) {
if (m.getName() == internedName
&& (res == null
|| res.getParameterTypes()[0].isAssignableFrom(m.getParameterTypes()[0]))) {
res = m;
}
}
}
return res;
}
});
}
private static boolean isSetterSignature(Method m) {
return m != null && m.getReturnType().equals(void.class)
&& m.getParameterTypes().length == 1;
}
public static boolean isSetter(Method m) {
return m != null && m.getName().startsWith("set") && m.getReturnType().equals(void.class)
&& m.getParameterTypes().length == 1;
}
}