/* * Copyright (c) 2006-2013 Rogério Liesenfeld * This file is subject to the terms of the MIT license (see LICENSE.txt). */ package mockit.internal.util; import java.lang.reflect.*; import static java.lang.reflect.Modifier.*; import static mockit.internal.util.ParameterReflection.*; public final class MethodReflection { public static <T> T invoke( Class<?> theClass, Object targetInstance, String methodName, Class<?>[] paramTypes, Object... methodArgs) { Method method = findSpecifiedMethod(theClass, methodName, paramTypes); T result = invoke(targetInstance, method, methodArgs); return result; } private static Method findSpecifiedMethod(Class<?> theClass, String methodName, Class<?>[] paramTypes) { for (Method declaredMethod : theClass.getDeclaredMethods()) { if (declaredMethod.getName().equals(methodName)) { Class<?>[] declaredParameterTypes = declaredMethod.getParameterTypes(); int firstRealParameter = indexOfFirstRealParameter(declaredParameterTypes, paramTypes); if ( firstRealParameter >= 0 && matchesParameterTypes(declaredMethod.getParameterTypes(), paramTypes, firstRealParameter) ) { return declaredMethod; } } } Class<?> superClass = theClass.getSuperclass(); if (superClass != null && superClass != Object.class) { return findSpecifiedMethod(superClass, methodName, paramTypes); } String paramTypesDesc = getParameterTypesDescription(paramTypes); throw new IllegalArgumentException("Specified method not found: " + methodName + paramTypesDesc); } public static <T> T invokeWithCheckedThrows( Class<?> theClass, Object targetInstance, String methodName, Class<?>[] paramTypes, Object... methodArgs) throws Throwable { Method method = findSpecifiedMethod(theClass, methodName, paramTypes); T result = invokeWithCheckedThrows(targetInstance, method, methodArgs); return result; } public static <T> T invoke(Object targetInstance, Method method, Object... methodArgs) { Utilities.ensureThatMemberIsAccessible(method); try { //noinspection unchecked return (T) method.invoke(targetInstance, methodArgs); } catch (IllegalAccessException e) { throw new RuntimeException(e); } catch (IllegalArgumentException e) { StackTrace.filterStackTrace(e); throw new IllegalArgumentException("Failure to invoke method: " + method, e); } catch (InvocationTargetException e) { Throwable cause = e.getCause(); if (cause instanceof Error) { throw (Error) cause; } else if (cause instanceof RuntimeException) { throw (RuntimeException) cause; } else { ThrowOfCheckedException.doThrow((Exception) cause); return null; } } } public static <T> T invokeWithCheckedThrows(Object targetInstance, Method method, Object... methodArgs) throws Throwable { Utilities.ensureThatMemberIsAccessible(method); try { //noinspection unchecked return (T) method.invoke(targetInstance, methodArgs); } catch (IllegalArgumentException e) { StackTrace.filterStackTrace(e); throw new IllegalArgumentException("Failure to invoke method: " + method, e); } catch (InvocationTargetException e) { throw e.getCause(); } } public static <T> T invoke(Class<?> theClass, Object targetInstance, String methodName, Object... methodArgs) { boolean staticMethod = targetInstance == null; Class<?>[] argTypes = getArgumentTypesFromArgumentValues(methodArgs); Method method = staticMethod ? findCompatibleStaticMethod(theClass, methodName, argTypes) : findCompatibleMethod(theClass, methodName, argTypes); if (staticMethod && !isStatic(method.getModifiers())) { throw new IllegalArgumentException( "Attempted to invoke non-static method without an instance to invoke it on"); } T result = invoke(targetInstance, method, methodArgs); return result; } private static Method findCompatibleStaticMethod(Class<?> theClass, String methodName, Class<?>[] argTypes) { Method methodFound = findCompatibleMethodInClass(theClass, methodName, argTypes); if (methodFound != null) { return methodFound; } String argTypesDesc = getParameterTypesDescription(argTypes); throw new IllegalArgumentException("No compatible static method found: " + methodName + argTypesDesc); } public static Method findCompatibleMethod(Class<?> theClass, String methodName, Class<?>[] argTypes) { Method methodFound = null; while (true) { Method compatibleMethod = findCompatibleMethodInClass(theClass, methodName, argTypes); if ( compatibleMethod != null && (methodFound == null || hasMoreSpecificTypes(compatibleMethod.getParameterTypes(), methodFound.getParameterTypes())) ) { methodFound = compatibleMethod; } Class<?> superClass = theClass.getSuperclass(); if (superClass == null || superClass == Object.class) { break; } //noinspection AssignmentToMethodParameter theClass = superClass; } if (methodFound != null) { return methodFound; } String argTypesDesc = getParameterTypesDescription(argTypes); throw new IllegalArgumentException("No compatible method found: " + methodName + argTypesDesc); } private static Method findCompatibleMethodInClass(Class<?> theClass, String methodName, Class<?>[] argTypes) { Method found = null; Class<?>[] foundParamTypes = null; for (Method declaredMethod : theClass.getDeclaredMethods()) { if (declaredMethod.getName().equals(methodName)) { Class<?>[] declaredParamTypes = declaredMethod.getParameterTypes(); int firstRealParameter = indexOfFirstRealParameter(declaredParamTypes, argTypes); if ( firstRealParameter >= 0 && (matchesParameterTypes(declaredParamTypes, argTypes, firstRealParameter) || acceptsArgumentTypes(declaredParamTypes, argTypes, firstRealParameter)) && (found == null || hasMoreSpecificTypes(declaredParamTypes, foundParamTypes)) ) { found = declaredMethod; foundParamTypes = declaredParamTypes; } } } return found; } public static Method findNonPrivateHandlerMethod(Object handler) { Class<?> handlerClass = handler.getClass(); Method nonPrivateMethod; do { nonPrivateMethod = findNonPrivateHandlerMethod(handlerClass); if (nonPrivateMethod != null) { break; } handlerClass = handlerClass.getSuperclass(); } while (handlerClass != null && handlerClass != Object.class); if (nonPrivateMethod == null) { throw new IllegalArgumentException("No non-private invocation handler method found"); } return nonPrivateMethod; } private static Method findNonPrivateHandlerMethod(Class<?> handlerClass) { Method[] declaredMethods = handlerClass.getDeclaredMethods(); Method found = null; for (Method declaredMethod : declaredMethods) { if (!isPrivate(declaredMethod.getModifiers())) { if (found != null) { throw new IllegalArgumentException("More than one non-private invocation handler method found"); } found = declaredMethod; } } return found; } }