/* * Copyright 2011 Blazebit */ package com.blazebit.reflection; import java.lang.annotation.Annotation; import java.lang.reflect.Array; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.GenericArrayType; import java.lang.reflect.GenericDeclaration; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.lang.reflect.WildcardType; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Queue; import java.util.Set; import java.util.TreeSet; /** * 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 * TODO: Maybe compare to org.springframework.core.GenericTypeResolver */ public final class ReflectionUtils { private static final Map<String, Class<?>> PRIMITIVE_NAME_TO_TYPE; private static final Map<Class<?>, Class<?>> PRIMITIVE_TO_WRAPPER; private static final Map<Class<?>, Class<?>> WRAPPER_TO_PRIMITIVE; static { Map<String, Class<?>> primitiveNameToType = new HashMap<String, Class<?>>(); 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<?>, Class<?>> primitiveToWrapper = new HashMap<Class<?>, 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<?>, Class<?>> wrapperToPrimitive = new HashMap<Class<?>, 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: * * <code> * ReflectionUtil.getClass("void").equals(void.class) * ReflectionUtil.getClass("int").equals(int.class) * </code> * * @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<Class<?>> getSuperTypes(Class<?> clazz) { return getSuperTypes(clazz, Object.class); } public static Set<Class<?>> getSuperTypes(Class<?> clazz, Class<?> commonSuperType) { Set<Class<?>> list = new LinkedHashSet<Class<?>>(); addSuperTypes(list, clazz, commonSuperType); return list; } private static void addSuperTypes(Set<Class<?>> 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; } private 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) { Type componentType = ((GenericArrayType) type).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((Class<?>) componentClass, 0); return (Class<?>) o.getClass(); } 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<?> getClassThatContainsTypeVariable(TypeVariable<?> typeVariable) { if (isSubtype(typeVariable.getGenericDeclaration().getClass(), Class.class)) { return (Class<?>) typeVariable .getGenericDeclaration(); } else if (isSubtype(typeVariable.getGenericDeclaration().getClass(), Method.class)) { return ((Method) typeVariable .getGenericDeclaration()).getDeclaringClass(); } else if (isSubtype(typeVariable.getGenericDeclaration().getClass(), Constructor.class)) { return ((Constructor<?>) typeVariable .getGenericDeclaration()).getDeclaringClass(); } return null; } /** * Tries to resolve the type variable againts 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 or null if the type can not be * resolved. * @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) { Class<?> classThatContainsTypeVariable = getClassThatContainsTypeVariable(typeVariable); 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<Class<?>> superTypes = getSuperTypes(concreteClass, classThatContainsTypeVariable); List<Class<?>> classStack = new ArrayList<Class<?>>(); 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 for (Class<?> superType : superTypes) { classStack.add(superType); } // 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<Type> typesToInspect = new ArrayList<Type>(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 position = getTypeVariablePosition(classToInspect, (TypeVariable<?>) resolvedType); classThatContainsTypeVariable = getClassThatContainsTypeVariable((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; } } } if (resolvedType instanceof Class<?>) { return (Class<?>) resolvedType; } else if (resolvedType instanceof TypeVariable<?>) { Type boundType = ((TypeVariable<?>) resolvedType).getBounds()[0]; if (boundType instanceof Class<?>) { return (Class<?>) boundType; } else if (boundType instanceof ParameterizedType) { return (Class<?>) ((ParameterizedType) boundType).getRawType(); } } else if (resolvedType instanceof ParameterizedType) { return (Class<?>) ((ParameterizedType) resolvedType).getRawType(); } else if (resolvedType instanceof WildcardType) { WildcardType wildcardType = ((WildcardType) resolvedType); if (wildcardType.getLowerBounds().length > 0) { return resolveType(concreteClass, wildcardType.getLowerBounds()[0]); } else { return resolveType(concreteClass, wildcardType.getUpperBounds()[0]); } } throw new IllegalArgumentException("Could not resolve the type variable '" + typeVariable + "' for the concrete class " + concreteClass.getName() + ". The resolved type is unknown: " + 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> FIELD_NAME_AND_DECLARING_CLASS_COMPARATOR = new Comparator<Field>() { @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<Field> fields = new TreeSet<Field>( FIELD_NAME_AND_DECLARING_CLASS_COMPARATOR); traverseHierarchy(clazz, new TraverseTask<Field>() { @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<Field> fields = new TreeSet<Field>( FIELD_NAME_AND_DECLARING_CLASS_COMPARATOR); traverseHierarchy(clazz, new TraverseTask<Field>() { @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<Field>() { @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(java.lang.Class, java.lang.String) * } 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(java.lang.Class, java.lang.String) */ 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<Method>() { @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<T> { public T run(Class<?> clazz); } private static <T> T traverseHierarchy(Class<?> clazz, TraverseTask<T> task) { Queue<Class<?>> classQueue = new LinkedList<Class<?>>(); 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<? extends Annotation> annotation) { return traverseHierarchy(clazz, new TraverseTask<Method>() { @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<Method> getMethods(Class<?> clazz, final Class<? extends Annotation> annotation) { final List<Method> methods = new ArrayList<Method>(); traverseHierarchy(clazz, new TraverseTask<Method>() { @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<Method>() { @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<Method>() { @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; } }