All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.powermock.reflect.internal.WhiteboxImpl Maven / Gradle / Ivy

There is a newer version: 2.0.9
Show newest version
/*
 * Copyright 2008 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.powermock.reflect.internal;

import org.objenesis.Objenesis;
import org.objenesis.ObjenesisStd;
import org.objenesis.instantiator.ObjectInstantiator;
import org.powermock.reflect.exceptions.*;
import org.powermock.reflect.internal.matcherstrategies.*;
import org.powermock.reflect.internal.primitivesupport.BoxedWrapper;
import org.powermock.reflect.internal.primitivesupport.PrimitiveWrapper;
import org.powermock.reflect.matching.FieldMatchingStrategy;
import org.powermock.reflect.spi.ProxyFramework;

import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.*;

/**
 * Various utilities for accessing internals of a class. Basically a simplified
 * reflection utility intended for tests.
 */
public class WhiteboxImpl {

    /**
     * The proxy framework.
     */
    private static ProxyFramework proxyFramework = null;

    /**
     * Convenience method to get a method from a class type without having to
     * catch the checked exceptions otherwise required. These exceptions are
     * wrapped as runtime exceptions.
     * 

* The method will first try to look for a declared method in the same * class. If the method is not declared in this class it will look for the * method in the super class. This will continue throughout the whole class * hierarchy. If the method is not found an {@link MethodNotFoundException} * is thrown. Since the method name is not specified an * * @param type The type of the class where the method is located. * @param parameterTypes All parameter types of the method (may be null). * @return A . {@link TooManyMethodsFoundException} is thrown if two or more * methods matches the same parameter types in the same class. */ public static Method getMethod(Class type, Class... parameterTypes) { Class thisType = type; if (parameterTypes == null) { parameterTypes = new Class[0]; } List foundMethods = new LinkedList(); while (thisType != null) { Method[] methodsToTraverse = null; if (thisType.isInterface()) { // Interfaces only contain public (and abstract) methods, no // need to traverse the hierarchy. methodsToTraverse = getAllPublicMethods(thisType); } else { methodsToTraverse = thisType.getDeclaredMethods(); } for (Method method : methodsToTraverse) { if (checkIfParameterTypesAreSame(method.isVarArgs(), parameterTypes, method.getParameterTypes())) { foundMethods.add(method); if (foundMethods.size() == 1) { method.setAccessible(true); } } } if (foundMethods.size() == 1) { return foundMethods.get(0); } else if (foundMethods.size() > 1) { break; } thisType = thisType.getSuperclass(); } if (foundMethods.isEmpty()) { throw new MethodNotFoundException("No method was found with parameter types: [ " + getArgumentTypesAsString((Object[]) parameterTypes) + " ] in class " + getUnmockedType(type).getName() + "."); } else { throwExceptionWhenMultipleMethodMatchesFound("method name", foundMethods.toArray(new Method[foundMethods.size()])); } // Will never happen return null; } /** * Convenience method to get a method from a class type without having to * catch the checked exceptions otherwise required. These exceptions are * wrapped as runtime exceptions. *

* The method will first try to look for a declared method in the same * class. If the method is not declared in this class it will look for the * method in the super class. This will continue throughout the whole class * hierarchy. If the method is not found an {@link IllegalArgumentException} * is thrown. * * @param type The type of the class where the method is located. * @param methodName The method names. * @param parameterTypes All parameter types of the method (may be null). * @return A . */ public static Method getMethod(Class type, String methodName, Class... parameterTypes) { Class thisType = type; if (parameterTypes == null) { parameterTypes = new Class[0]; } while (thisType != null) { Method[] methodsToTraverse = null; if (thisType.isInterface()) { // Interfaces only contain public (and abstract) methods, no // need to traverse the hierarchy. methodsToTraverse = getAllPublicMethods(thisType); } else { methodsToTraverse = thisType.getDeclaredMethods(); } for (Method method : methodsToTraverse) { if (methodName.equals(method.getName()) && checkIfParameterTypesAreSame(method.isVarArgs(), parameterTypes, method.getParameterTypes())) { method.setAccessible(true); return method; } } thisType = thisType.getSuperclass(); } throwExceptionIfMethodWasNotFound(type, methodName, null, new Object[]{parameterTypes}); return null; } /** * Convenience method to get a field from a class type. *

* The method will first try to look for a declared field in the same class. * If the method is not declared in this class it will look for the field in * the super class. This will continue throughout the whole class hierarchy. * If the field is not found an {@link IllegalArgumentException} is thrown. * * @param type The type of the class where the method is located. * @param fieldName The method names. * @return A . */ @SuppressWarnings({"unchecked", "rawtypes"}) public static Field getField(Class type, String fieldName) { LinkedList> examine = new LinkedList>(); examine.add(type); Set> done = new HashSet>(); while (examine.isEmpty() == false) { Class thisType = examine.removeFirst(); done.add(thisType); final Field[] declaredField = thisType.getDeclaredFields(); for (Field field : declaredField) { if (fieldName.equals(field.getName())) { field.setAccessible(true); return field; } } Set> potential = new HashSet>(); final Class clazz = thisType.getSuperclass(); if (clazz != null) { potential.add(thisType.getSuperclass()); } potential.addAll((Collection) Arrays.asList(thisType.getInterfaces())); potential.removeAll(done); examine.addAll(potential); } throwExceptionIfFieldWasNotFound(type, fieldName, null); return null; } /** * Create a new instance of a class without invoking its constructor. *

* No byte-code manipulation is needed to perform this operation and thus * it's not necessary use the PowerMockRunner or * PrepareForTest annotation to use this functionality. * * @param The type of the instance to create. * @param classToInstantiate The type of the instance to create. * @return A new instance of type T, created without invoking the * constructor. */ @SuppressWarnings("unchecked") public static T newInstance(Class classToInstantiate) { int modifiers = classToInstantiate.getModifiers(); final Object object; if (Modifier.isInterface(modifiers)) { object = Proxy.newProxyInstance(WhiteboxImpl.class.getClassLoader(), new Class[]{classToInstantiate}, new InvocationHandler() { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return TypeUtils.getDefaultValue(method.getReturnType()); } }); } else if (classToInstantiate.isArray()) { object = Array.newInstance(classToInstantiate.getComponentType(), 0); } else if (Modifier.isAbstract(modifiers)) { throw new IllegalArgumentException( "Cannot instantiate an abstract class. Please use the ConcreteClassGenerator in PowerMock support to generate a concrete class first."); } else { Objenesis objenesis = new ObjenesisStd(); ObjectInstantiator thingyInstantiator = objenesis.getInstantiatorOf(classToInstantiate); object = thingyInstantiator.newInstance(); } return (T) object; } /** * Convenience method to get a (declared) constructor from a class type * without having to catch the checked exceptions otherwise required. These * exceptions are wrapped as runtime exceptions. The constructor is also set * to accessible. * * @param type The type of the class where the constructor is located. * @param parameterTypes All parameter types of the constructor (may be * null). * @return A . */ public static Constructor getConstructor(Class type, Class... parameterTypes) { Class unmockedType = WhiteboxImpl.getUnmockedType(type); try { final Constructor constructor = unmockedType.getDeclaredConstructor(parameterTypes); constructor.setAccessible(true); return constructor; } catch (RuntimeException e) { throw (RuntimeException) e; } catch (Error e) { throw (Error) e; } catch (Throwable e) { throw new ConstructorNotFoundException(String.format( "Failed to lookup constructor with parameter types [ %s ] in class %s.", getArgumentTypesAsString((Object[]) parameterTypes), unmockedType.getName()), e); } } /** * Set the value of a field using reflection. This method will traverse the * super class hierarchy until a field with name fieldName is * found. * * @param object the object whose field to modify * @param fieldName the name of the field * @param value the new value of the field */ public static void setInternalState(Object object, String fieldName, Object value) { Field foundField = findFieldInHierarchy(object, fieldName); setField(object, value, foundField); } /** * Set the value of a field using reflection. This method will traverse the * super class hierarchy until a field with name fieldName is * found. * * @param object the object to modify * @param fieldName the name of the field * @param value the new value of the field */ public static void setInternalState(Object object, String fieldName, Object[] value) { setInternalState(object, fieldName, (Object) value); } /** * Set the value of a field using reflection. This method will traverse the * super class hierarchy until the first field of type fieldType is * found. The value will then be assigned to this field. * * @param object the object to modify * @param fieldType the type of the field * @param value the new value of the field */ public static void setInternalState(Object object, Class fieldType, Object value) { setField(object, value, findFieldInHierarchy(object, new AssignableFromFieldTypeMatcherStrategy(fieldType))); } /** * Set the value of a field using reflection. This method will traverse the * super class hierarchy until the first field assignable to the * value type is found. The value (or * additionaValues if present) will then be assigned to this field. * * @param object the object to modify * @param value the new value of the field * @param additionalValues Additional values to set on the object */ public static void setInternalState(Object object, Object value, Object... additionalValues) { setField(object, value, findFieldInHierarchy(object, new AssignableFromFieldTypeMatcherStrategy(getType(value)))); if (additionalValues != null && additionalValues.length > 0) { for (Object additionalValue : additionalValues) { setField( object, additionalValue, findFieldInHierarchy(object, new AssignableFromFieldTypeMatcherStrategy( getType(additionalValue)))); } } } /** * Set the value of a field using reflection at at specific place in the * class hierarchy (where). This first field assignable to * object will then be set to value. * * @param object the object to modify * @param value the new value of the field * @param where the class in the hierarchy where the field is defined */ public static void setInternalState(Object object, Object value, Class where) { setField(object, value, findField(object, new AssignableFromFieldTypeMatcherStrategy(getType(value)), where)); } /** * Set the value of a field using reflection at a specific location ( * where) in the class hierarchy. The value will then be * assigned to this field. * * @param object the object to modify * @param fieldType the type of the field the should be set. * @param value the new value of the field * @param where which class in the hierarchy defining the field */ public static void setInternalState(Object object, Class fieldType, Object value, Class where) { if (fieldType == null || where == null) { throw new IllegalArgumentException("fieldType and where cannot be null"); } setField(object, value, findFieldOrThrowException(fieldType, where)); } /** * Set the value of a field using reflection. Use this method when you need * to specify in which class the field is declared. This is useful if you * have two fields in a class hierarchy that has the same name but you like * to modify the latter. * * @param object the object to modify * @param fieldName the name of the field * @param value the new value of the field * @param where which class the field is defined */ public static void setInternalState(Object object, String fieldName, Object value, Class where) { if (object == null || fieldName == null || fieldName.equals("") || fieldName.startsWith(" ")) { throw new IllegalArgumentException("object, field name, and \"where\" must not be empty or null."); } final Field field = getField(fieldName, where); try { field.set(object, value); } catch (Exception e) { throw new RuntimeException("Internal Error: Failed to set field in method setInternalState.", e); } } /** * Get the value of a field using reflection. This method will iterate * through the entire class hierarchy and return the value of the first * field named fieldName. If you want to get a specific field value * at specific place in the class hierarchy please refer to * * @param the generic type * @param object the object to modify * @param fieldName the name of the field * @return the internal state * {@link #getInternalState(Object, String, Class)}. */ @SuppressWarnings("unchecked") public static T getInternalState(Object object, String fieldName) { Field foundField = findFieldInHierarchy(object, fieldName); try { return (T) foundField.get(object); } catch (IllegalAccessException e) { throw new RuntimeException("Internal error: Failed to get field in method getInternalState.", e); } } /** * Find field in hierarchy. * * @param object the object * @param fieldName the field name * @return the field */ private static Field findFieldInHierarchy(Object object, String fieldName) { return findFieldInHierarchy(object, new FieldNameMatcherStrategy(fieldName)); } /** * Find field in hierarchy. * * @param object the object * @param strategy the strategy * @return the field */ private static Field findFieldInHierarchy(Object object, FieldMatcherStrategy strategy) { assertObjectInGetInternalStateIsNotNull(object); return findSingleFieldUsingStrategy(strategy, object, true, getType(object)); } /** * Find field. * * @param object the object * @param strategy the strategy * @param where the where * @return the field */ private static Field findField(Object object, FieldMatcherStrategy strategy, Class where) { return findSingleFieldUsingStrategy(strategy, object, false, where); } /** * Find single field using strategy. * * @param strategy the strategy * @param object the object * @param checkHierarchy the check hierarchy * @param startClass the start class * @return the field */ private static Field findSingleFieldUsingStrategy(FieldMatcherStrategy strategy, Object object, boolean checkHierarchy, Class startClass) { assertObjectInGetInternalStateIsNotNull(object); Field foundField = null; final Class originalStartClass = startClass; while (startClass != null) { final Field[] declaredFields = startClass.getDeclaredFields(); for (Field field : declaredFields) { if (strategy.matches(field) && hasFieldProperModifier(object, field)) { if (foundField != null) { throw new TooManyFieldsFoundException("Two or more fields matching " + strategy + "."); } foundField = field; } } if (foundField != null) { break; } else if (checkHierarchy == false) { break; } startClass = startClass.getSuperclass(); } if (foundField == null) { strategy.notFound(originalStartClass, !isClass(object)); } foundField.setAccessible(true); return foundField; } /** * Find all fields using strategy. * * @param strategy the strategy * @param object the object * @param checkHierarchy the check hierarchy * @param startClass the start class * @return the set */ private static Set findAllFieldsUsingStrategy(FieldMatcherStrategy strategy, Object object, boolean checkHierarchy, Class startClass) { assertObjectInGetInternalStateIsNotNull(object); final Set foundFields = new LinkedHashSet(); while (startClass != null) { final Field[] declaredFields = startClass.getDeclaredFields(); for (Field field : declaredFields) { if (strategy.matches(field) && hasFieldProperModifier(object, field)) { field.setAccessible(true); foundFields.add(field); } } if (!checkHierarchy) { break; } startClass = startClass.getSuperclass(); } return Collections.unmodifiableSet(foundFields); } /** * Checks for field proper modifier. * * @param object the object * @param field the field * @return true, if successful */ private static boolean hasFieldProperModifier(Object object, Field field) { return ((object instanceof Class && Modifier.isStatic(field.getModifiers())) || ((object instanceof Class == false && Modifier .isStatic(field.getModifiers()) == false))); } /** * Get the value of a field using reflection. This method will traverse the * super class hierarchy until the first field of type fieldType is * found. The value of this field will be returned. * * @param the generic type * @param object the object to modify * @param fieldType the type of the field * @return the internal state */ @SuppressWarnings("unchecked") public static T getInternalState(Object object, Class fieldType) { Field foundField = findFieldInHierarchy(object, new AssignableToFieldTypeMatcherStrategy(fieldType)); try { return (T) foundField.get(object); } catch (IllegalAccessException e) { throw new RuntimeException("Internal error: Failed to get field in method getInternalState.", e); } } /** * Get the value of a field using reflection. Use this method when you need * to specify in which class the field is declared. The first field matching * the fieldType in where will is the field whose value * will be returned. * * @param the expected type of the field * @param object the object to modify * @param fieldType the type of the field * @param where which class the field is defined * @return the internal state */ @SuppressWarnings("unchecked") public static T getInternalState(Object object, Class fieldType, Class where) { if (object == null) { throw new IllegalArgumentException("object and type are not allowed to be null"); } try { return (T) findFieldOrThrowException(fieldType, where).get(object); } catch (IllegalAccessException e) { throw new RuntimeException("Internal error: Failed to get field in method getInternalState.", e); } } /** * Get the value of a field using reflection. Use this method when you need * to specify in which class the field is declared. This might be useful * when you have mocked the instance you are trying to access. Use this * method to avoid casting. * * @param the expected type of the field * @param object the object to modify * @param fieldName the name of the field * @param where which class the field is defined * @return the internal state */ @SuppressWarnings("unchecked") public static T getInternalState(Object object, String fieldName, Class where) { if (object == null || fieldName == null || fieldName.equals("") || fieldName.startsWith(" ")) { throw new IllegalArgumentException("object, field name, and \"where\" must not be empty or null."); } Field field = null; try { field = where.getDeclaredField(fieldName); field.setAccessible(true); return (T) field.get(object); } catch (NoSuchFieldException e) { throw new FieldNotFoundException("Field '" + fieldName + "' was not found in class " + where.getName() + "."); } catch (Exception e) { throw new RuntimeException("Internal error: Failed to get field in method getInternalState.", e); } } /** * Invoke a private or inner class method without the need to specify the * method name. This is thus a more refactor friendly version of the * * @param the generic type * @param tested the tested * @param arguments the arguments * @return the t * @throws Exception the exception * {@link #invokeMethod(Object, String, Object...)} method and * is recommend over this method for that reason. This method * might be useful to test private methods. */ @SuppressWarnings("unchecked") public static synchronized T invokeMethod(Object tested, Object... arguments) throws Exception { return (T) doInvokeMethod(tested, null, null, arguments); } /** * Invoke a private or inner class method without the need to specify the * method name. This is thus a more refactor friendly version of the * * @param the generic type * @param tested the tested * @param arguments the arguments * @return the t * @throws Exception the exception * {@link #invokeMethod(Object, String, Object...)} method and * is recommend over this method for that reason. This method * might be useful to test private methods. */ @SuppressWarnings("unchecked") public static synchronized T invokeMethod(Class tested, Object... arguments) throws Exception { return (T) doInvokeMethod(tested, null, null, arguments); } /** * Invoke a private or inner class method. This might be useful to test * private methods. * * @param the generic type * @param tested the tested * @param methodToExecute the method to execute * @param arguments the arguments * @return the t * @throws Exception the exception */ @SuppressWarnings("unchecked") public static synchronized T invokeMethod(Object tested, String methodToExecute, Object... arguments) throws Exception { return (T) doInvokeMethod(tested, null, methodToExecute, arguments); } /** * Invoke a private or inner class method in cases where power mock cannot * automatically determine the type of the parameters, for example when * mixing primitive types and wrapper types in the same method. For most * situations use {@link #invokeMethod(Class, String, Object...)} instead. * * @param the generic type * @param tested the tested * @param methodToExecute the method to execute * @param argumentTypes the argument types * @param arguments the arguments * @return the t * @throws Exception Exception that may occur when invoking this method. */ @SuppressWarnings("unchecked") public static synchronized T invokeMethod(Object tested, String methodToExecute, Class[] argumentTypes, Object... arguments) throws Exception { final Class unmockedType = getType(tested); Method method = getMethod(unmockedType, methodToExecute, argumentTypes); if (method == null) { throwExceptionIfMethodWasNotFound(unmockedType, methodToExecute, null, arguments); } return (T) performMethodInvocation(tested, method, arguments); } /** * Invoke a private or inner class method in a subclass (defined by * definedIn) in cases where power mock cannot automatically * determine the type of the parameters, for example when mixing primitive * types and wrapper types in the same method. For most situations use * * @param the generic type * @param tested the tested * @param methodToExecute the method to execute * @param definedIn the defined in * @param argumentTypes the argument types * @param arguments the arguments * @return the t * @throws Exception Exception that may occur when invoking this method. * {@link #invokeMethod(Class, String, Object...)} instead. */ @SuppressWarnings("unchecked") public static synchronized T invokeMethod(Object tested, String methodToExecute, Class definedIn, Class[] argumentTypes, Object... arguments) throws Exception { Method method = getMethod(definedIn, methodToExecute, argumentTypes); if (method == null) { throwExceptionIfMethodWasNotFound(definedIn, methodToExecute, null, arguments); } return (T) performMethodInvocation(tested, method, arguments); } /** * Invoke a private or inner class method in that is located in a subclass * of the tested instance. This might be useful to test private methods. * * @param the generic type * @param tested the tested * @param declaringClass the declaring class * @param methodToExecute the method to execute * @param arguments the arguments * @return the t * @throws Exception Exception that may occur when invoking this method. */ @SuppressWarnings("unchecked") public static synchronized T invokeMethod(Object tested, Class declaringClass, String methodToExecute, Object... arguments) throws Exception { return (T) doInvokeMethod(tested, declaringClass, methodToExecute, arguments); } /** * Invoke a private method in that is located in a subclass of an instance. * This might be useful to test overloaded private methods. *

* Use this for overloaded methods only, if possible use * * @param the generic type * @param object the object * @param declaringClass the declaring class * @param methodToExecute the method to execute * @param parameterTypes the parameter types * @param arguments the arguments * @return the t * @throws Exception Exception that may occur when invoking this method. * {@link #invokeMethod(Object, Object...)} or * {@link #invokeMethod(Object, String, Object...)} instead. */ @SuppressWarnings("unchecked") public static synchronized T invokeMethod(Object object, Class declaringClass, String methodToExecute, Class[] parameterTypes, Object... arguments) throws Exception { if (object == null) { throw new IllegalArgumentException("object cannot be null"); } final Method methodToInvoke = getMethod(declaringClass, methodToExecute, parameterTypes); // Invoke method return (T) performMethodInvocation(object, methodToInvoke, arguments); } /** * Invoke a private or inner class method. This might be useful to test * private methods. * * @param the generic type * @param clazz the clazz * @param methodToExecute the method to execute * @param arguments the arguments * @return the t * @throws Exception the exception */ @SuppressWarnings("unchecked") public static synchronized T invokeMethod(Class clazz, String methodToExecute, Object... arguments) throws Exception { return (T) doInvokeMethod(clazz, null, methodToExecute, arguments); } /** * Do invoke method. * * @param the generic type * @param tested the tested * @param declaringClass the declaring class * @param methodToExecute the method to execute * @param arguments the arguments * @return the t * @throws Exception the exception */ @SuppressWarnings("unchecked") private static T doInvokeMethod(Object tested, Class declaringClass, String methodToExecute, Object... arguments) throws Exception { Method methodToInvoke = findMethodOrThrowException(tested, declaringClass, methodToExecute, arguments); // Invoke test return (T) performMethodInvocation(tested, methodToInvoke, arguments); } /** * Finds and returns a certain method. If the method couldn't be found this * method delegates to * * @param tested The instance or class containing the method. * @param declaringClass The class where the method is supposed to be declared (may be * null). * @param methodToExecute The method name. If null then method will be * looked up based on the argument types only. * @param arguments The arguments of the methods. * @return A single method. * @throws {@link org.powermock.reflect.exceptions.MethodNotFoundException} if no method was found * @throws {@link org.powermock.reflect.exceptions.TooManyMethodsFoundException} if too methods matched */ public static Method findMethodOrThrowException(Object tested, Class declaringClass, String methodToExecute, Object[] arguments) { if (tested == null) { throw new IllegalArgumentException("The object to perform the operation on cannot be null."); } /* * Get methods from the type if it's not mocked or from the super type * if the tested object is mocked. */ Class testedType = null; if (isClass(tested)) { testedType = (Class) tested; } else { testedType = tested.getClass(); } Method[] methods = null; if (declaringClass == null) { methods = getAllMethods(testedType); } else { methods = declaringClass.getDeclaredMethods(); } Method potentialMethodToInvoke = null; for (Method method : methods) { if (methodToExecute == null || method.getName().equals(methodToExecute)) { Class[] paramTypes = method.getParameterTypes(); if ((arguments != null && (paramTypes.length == arguments.length))) { if (paramTypes.length == 0) { potentialMethodToInvoke = method; break; } boolean methodFound = checkArgumentTypesMatchParameterTypes(method.isVarArgs(), paramTypes, arguments); if (methodFound) { if (potentialMethodToInvoke == null) { potentialMethodToInvoke = method; } else if (potentialMethodToInvoke.getName().equals(method.getName())) { if (areAllArgumentsOfSameType(arguments) && potentialMethodToInvoke.getDeclaringClass() != method.getDeclaringClass()) { // We've already found the method which means that "potentialMethodToInvoke" overrides "method". return potentialMethodToInvoke; } else { // We've found an overloaded method return getBestMethodCandidate(getType(tested), method.getName(), getTypes(arguments), false); } } else { // A special case to be backward compatible Method bestCandidateMethod = getMethodWithMostSpecificParameterTypes(method, potentialMethodToInvoke); if (bestCandidateMethod != null) { potentialMethodToInvoke = bestCandidateMethod; continue; } /* * We've already found a method match before, this * means that PowerMock cannot determine which * method to expect since there are two methods with * the same name and the same number of arguments * but one is using wrapper types. */ throwExceptionWhenMultipleMethodMatchesFound("argument parameter types", new Method[]{ potentialMethodToInvoke, method}); } } } else if (isPotentialVarArgsMethod(method, arguments)) { if (potentialMethodToInvoke == null) { potentialMethodToInvoke = method; } else { /* * We've already found a method match before, this means * that PowerMock cannot determine which method to * expect since there are two methods with the same name * and the same number of arguments but one is using * wrapper types. */ throwExceptionWhenMultipleMethodMatchesFound("argument parameter types", new Method[]{ potentialMethodToInvoke, method}); } break; } else if (arguments != null && (paramTypes.length != arguments.length)) { continue; } else if (arguments == null && paramTypes.length == 1 && !paramTypes[0].isPrimitive()) { potentialMethodToInvoke = method; } } } WhiteboxImpl.throwExceptionIfMethodWasNotFound(getType(tested), methodToExecute, potentialMethodToInvoke, arguments); return potentialMethodToInvoke; } /** * Find the method whose parameter types most closely matches the types. * * @param firstMethodCandidate The first method candidate * @param secondMethodCandidate The second method candidate * @return The method that most closely matches the provided types or null if no method match. */ private static Method getMethodWithMostSpecificParameterTypes(Method firstMethodCandidate, Method secondMethodCandidate) { Class[] firstMethodCandidateParameterTypes = firstMethodCandidate.getParameterTypes(); Class[] secondMethodCandidateParameterTypes = secondMethodCandidate.getParameterTypes(); Method bestMatch = null; for (int i = 0; i < firstMethodCandidateParameterTypes.length; i++) { Class candidateType1 = toBoxedIfPrimitive(firstMethodCandidateParameterTypes[i]); Class candidateType2 = toBoxedIfPrimitive(secondMethodCandidateParameterTypes[i]); if (!candidateType1.equals(candidateType2)) { Method potentialMatch = null; if (candidateType1.isAssignableFrom(candidateType2)) { potentialMatch = secondMethodCandidate; } else if (candidateType2.isAssignableFrom(candidateType1)) { potentialMatch = firstMethodCandidate; } if (potentialMatch != null) { if (bestMatch != null && !potentialMatch.equals(bestMatch)) { /* * We cannot determine which method is the most specific because one parameter of the first candidate * was more specific and another parameter of the second candidate was more specific. */ return null; } else { bestMatch = potentialMatch; } } } } return bestMatch; } private static Class toBoxedIfPrimitive(Class type) { return type.isPrimitive() ? BoxedWrapper.getBoxedFromPrimitiveType(type) : type; } /** * Gets the types. * * @param arguments the arguments * @return the types */ private static Class[] getTypes(Object[] arguments) { Class[] classes = new Class[arguments.length]; for (int i = 0; i < arguments.length; i++) { classes[i] = getType(arguments[i]); } return classes; } /** * Gets the best method candidate. * * @param cls the cls * @param methodName the method name * @param signature the signature * @param exactParameterTypeMatch true if the expectedTypes must match * the parameter types must match exactly, false if * the expectedTypes are allowed to be converted * into primitive types if they are of a wrapped type and still * match. * @return the best method candidate */ public static Method getBestMethodCandidate(Class cls, String methodName, Class[] signature, boolean exactParameterTypeMatch) { final Method foundMethod; final Method[] methods = getMethods(cls, methodName, signature, exactParameterTypeMatch); if (methods.length == 1) { foundMethod = methods[0]; } else { /* * We've found overloaded methods, we need to find the best one to * invoke. */ Arrays.sort(methods, new Comparator() { public int compare(Method m1, Method m2) { final Class[] typesMethod1 = m1.getParameterTypes(); final Class[] typesMethod2 = m2.getParameterTypes(); final int size = typesMethod1.length; for (int i = 0; i < size; i++) { Class type1 = typesMethod1[i]; Class type2 = typesMethod2[i]; if (!type1.equals(type2)) { if (type1.isAssignableFrom(type2)) { if (!type1.isArray() && type2.isArray()) { return -1; } return 1; } else { if (type1.isArray() && !type2.isArray()) { return 1; } return -1; } } } return 0; } }); foundMethod = methods[0]; } return foundMethod; } /** * Finds and returns the default constructor. If the constructor couldn't be * found this method delegates to {@link #throwExceptionWhenMultipleConstructorMatchesFound(java.lang.reflect.Constructor[])}. * * @param type The type where the constructor should be located. * @return The found constructor. * @throws {@link org.powermock.reflect.exceptions.TooManyConstructorsFoundException} if too many constructors was found. */ public static Constructor findDefaultConstructorOrThrowException(Class type) { if (type == null) { throw new IllegalArgumentException("type cannot be null"); } final Constructor declaredConstructor; try { declaredConstructor = type.getDeclaredConstructor(); } catch (NoSuchMethodException e) { throw new ConstructorNotFoundException(String.format("Couldn't find a default constructor in %s.", type.getName())); } return declaredConstructor; } /** * Finds and returns any constructor. If the constructor couldn't be * found this method delegates to {@link #throwExceptionWhenMultipleConstructorMatchesFound(java.lang.reflect.Constructor[])}. * * @param type The type where the constructor should be located. * @return The found constructor. * @throws {@link org.powermock.reflect.exceptions.TooManyConstructorsFoundException} if too many constructors was found. */ public static Constructor findConstructorOrThrowException(Class type) { final Constructor[] declaredConstructors = filterPowerMockConstructor(type.getDeclaredConstructors()); if (declaredConstructors.length > 1) { throwExceptionWhenMultipleConstructorMatchesFound(declaredConstructors); } return declaredConstructors[0]; } /** * Filter power mock constructor. * * @param declaredConstructors the declared constructors * @return the constructor[] */ private static Constructor[] filterPowerMockConstructor(Constructor[] declaredConstructors) { Set> constructors = new HashSet>(); for (Constructor constructor : declaredConstructors) { final Class[] parameterTypes = constructor.getParameterTypes(); if (parameterTypes.length >= 1 && parameterTypes[parameterTypes.length - 1].getName().equals( "org.powermock.core.IndicateReloadClass")) { continue; } else { constructors.add(constructor); } } return constructors.toArray(new Constructor[constructors.size()]); } /** * Finds and returns a certain constructor. If the constructor couldn't be * found this method delegates to * * @param type The type where the constructor should be located. * @param arguments The arguments passed to the constructor. * @return The found constructor. * @throws {@link org.powermock.reflect.exceptions.ConstructorNotFoundException} if no constructor was found * @throws {@link org.powermock.reflect.exceptions.TooManyConstructorsFoundException} if too constructors matched */ public static Constructor findUniqueConstructorOrThrowException(Class type, Object... arguments) { if (type == null) { throw new IllegalArgumentException("Class type cannot be null."); } Class unmockedType = getUnmockedType(type); if ((unmockedType.isLocalClass() || unmockedType.isAnonymousClass() || unmockedType.isMemberClass()) && !Modifier.isStatic(unmockedType.getModifiers()) && arguments != null) { Object[] argumentsForLocalClass = new Object[arguments.length + 1]; argumentsForLocalClass[0] = unmockedType.getEnclosingClass(); System.arraycopy(arguments, 0, argumentsForLocalClass, 1, arguments.length); arguments = argumentsForLocalClass; } Constructor[] constructors = filterPowerMockConstructor(unmockedType.getDeclaredConstructors()); Constructor potentialConstructor = null; for (Constructor constructor : constructors) { Class[] paramTypes = constructor.getParameterTypes(); if ((arguments != null && (paramTypes.length == arguments.length))) { if (paramTypes.length == 0) { potentialConstructor = constructor; break; } boolean constructorFound = checkArgumentTypesMatchParameterTypes(constructor.isVarArgs(), paramTypes, arguments); if (constructorFound) { if (potentialConstructor == null) { potentialConstructor = constructor; } else { /* * We've already found a constructor match before, this * means that PowerMock cannot determine which method to * expect since there are two methods with the same name * and the same number of arguments but one is using * wrapper types. */ throwExceptionWhenMultipleConstructorMatchesFound(new Constructor[]{potentialConstructor, constructor}); } } } else if (isPotentialVarArgsConstructor(constructor, arguments)) { if (potentialConstructor == null) { potentialConstructor = constructor; } else { /* * We've already found a constructor match before, this * means that PowerMock cannot determine which method to * expect since there are two methods with the same name and * the same number of arguments but one is using wrapper * types. */ throwExceptionWhenMultipleConstructorMatchesFound(new Constructor[]{potentialConstructor, constructor}); } break; } else if (arguments != null && (paramTypes.length != arguments.length)) { continue; } } WhiteboxImpl.throwExceptionIfConstructorWasNotFound(type, potentialConstructor, arguments); return potentialConstructor; } /** * Convert argument types to primitive. * * @param paramTypes the param types * @param arguments the arguments * @return the class[] */ private static Class[] convertArgumentTypesToPrimitive(Class[] paramTypes, Object[] arguments) { Class[] types = new Class[arguments.length]; for (int i = 0; i < arguments.length; i++) { Class argumentType = null; if (arguments[i] == null) { argumentType = paramTypes[i]; } else { argumentType = getType(arguments[i]); } Class primitiveWrapperType = PrimitiveWrapper.getPrimitiveFromWrapperType(argumentType); if (primitiveWrapperType == null) { types[i] = argumentType; } else { types[i] = primitiveWrapperType; } } return types; } /** * Throw exception if method was not found. * * @param type the type * @param methodName the method name * @param methodToMock the method to mock * @param arguments the arguments */ public static void throwExceptionIfMethodWasNotFound(Class type, String methodName, Method methodToMock, Object... arguments) { if (methodToMock == null) { String methodNameData = ""; if (methodName != null) { methodNameData = "with name '" + methodName + "' "; } throw new MethodNotFoundException("No method found " + methodNameData + "with parameter types: [ " + getArgumentTypesAsString(arguments) + " ] in class " + getUnmockedType(type).getName() + "."); } } /** * Throw exception if field was not found. * * @param type the type * @param fieldName the field name * @param field the field */ public static void throwExceptionIfFieldWasNotFound(Class type, String fieldName, Field field) { if (field == null) { throw new FieldNotFoundException("No field was found with name '" + fieldName + "' in class " + getUnmockedType(type).getName() + "."); } } /** * Throw exception if constructor was not found. * * @param type the type * @param potentialConstructor the potential constructor * @param arguments the arguments */ static void throwExceptionIfConstructorWasNotFound(Class type, Constructor potentialConstructor, Object... arguments) { if (potentialConstructor == null) { String message = "No constructor found in class '" + getUnmockedType(type).getName() + "' with " + "parameter types: [ " + getArgumentTypesAsString(arguments) + " ]."; throw new ConstructorNotFoundException(message); } } /** * Gets the argument types as string. * * @param arguments the arguments * @return the argument types as string */ private static String getArgumentTypesAsString(Object... arguments) { StringBuilder argumentsAsString = new StringBuilder(); final String noParameters = ""; if (arguments != null && arguments.length != 0) { for (int i = 0; i < arguments.length; i++) { String argumentName = null; Object argument = arguments[i]; if (argument instanceof Class) { argumentName = ((Class) argument).getName(); } else if (argument instanceof Class[] && arguments.length == 1) { Class[] argumentArray = (Class[]) argument; if (argumentArray.length > 0) { for (int j = 0; j < argumentArray.length; j++) { appendArgument(argumentsAsString, j, argumentArray[j] == null ? "null" : getType(argumentArray[j]).getName(), argumentArray); } return argumentsAsString.toString(); } else { argumentName = noParameters; } } else if (argument == null) { argumentName = "null"; } else { argumentName = getType(argument).getName(); } appendArgument(argumentsAsString, i, argumentName, arguments); } } else { argumentsAsString.append(""); } return argumentsAsString.toString(); } /** * Append argument. * * @param argumentsAsString the arguments as string * @param index the index * @param argumentName the argument name * @param arguments the arguments */ private static void appendArgument(StringBuilder argumentsAsString, int index, String argumentName, Object[] arguments) { argumentsAsString.append(argumentName); if (index != arguments.length - 1) { argumentsAsString.append(", "); } } /** * Invoke a constructor. Useful for testing classes with a private * constructor when PowerMock cannot determine which constructor to invoke. * This only happens if you have two constructors with the same number of * arguments where one is using primitive data types and the other is using * the wrapped counter part. For example: *

*

     * public class MyClass {
     * private MyClass(Integer i) {
     * ...
     * }
     *
     * private MyClass(int i) {
     * ...
     * }
     * 
*

* This ought to be a really rare case. So for most situation, use * * @param the generic type * @param classThatContainsTheConstructorToTest the class that contains the constructor to test * @param parameterTypes the parameter types * @param arguments the arguments * @return The object created after the constructor has been invoked. * @throws Exception If an exception occur when invoking the constructor. * {@link #invokeConstructor(Class, Object...)} instead. */ public static T invokeConstructor(Class classThatContainsTheConstructorToTest, Class[] parameterTypes, Object[] arguments) throws Exception { if (parameterTypes != null && arguments != null) { if (parameterTypes.length != arguments.length) { throw new IllegalArgumentException("parameterTypes and arguments must have the same length"); } } Constructor constructor = null; try { constructor = classThatContainsTheConstructorToTest.getDeclaredConstructor(parameterTypes); } catch (Exception e) { throw new ConstructorNotFoundException("Could not lookup the constructor", e); } return createInstance(constructor, arguments); } /** * Invoke a constructor. Useful for testing classes with a private * constructor. * * @param the generic type * @param classThatContainsTheConstructorToTest the class that contains the constructor to test * @param arguments the arguments * @return The object created after the constructor has been invoked. * @throws Exception If an exception occur when invoking the constructor. */ public static T invokeConstructor(Class classThatContainsTheConstructorToTest, Object... arguments) throws Exception { if (classThatContainsTheConstructorToTest == null) { throw new IllegalArgumentException("The class should contain the constructor cannot be null."); } Class[] argumentTypes = null; if (arguments == null) { argumentTypes = new Class[0]; } else { argumentTypes = new Class[arguments.length]; for (int i = 0; i < arguments.length; i++) { argumentTypes[i] = getType(arguments[i]); } } Constructor constructor = null; Constructor potentialContstructorWrapped = null; Constructor potentialContstructorPrimitive = null; try { potentialContstructorWrapped = classThatContainsTheConstructorToTest.getDeclaredConstructor(argumentTypes); } catch (Exception e) { // Do nothing, we'll try with primitive type next. } try { potentialContstructorPrimitive = classThatContainsTheConstructorToTest .getDeclaredConstructor(PrimitiveWrapper.toPrimitiveType(argumentTypes)); } catch (Exception e) { // Do nothing } if (potentialContstructorPrimitive == null && potentialContstructorWrapped == null) { // Check if we can find a matching var args constructor. constructor = getPotentialVarArgsConstructor(classThatContainsTheConstructorToTest, arguments); if (constructor == null) { throw new ConstructorNotFoundException("Failed to find a constructor with parameter types: [" + getArgumentTypesAsString(arguments) + "]"); } } else if (potentialContstructorPrimitive == null && potentialContstructorWrapped != null) { constructor = potentialContstructorWrapped; } else if (potentialContstructorPrimitive != null && potentialContstructorWrapped == null) { constructor = potentialContstructorPrimitive; } else if (arguments == null || arguments.length == 0 && potentialContstructorPrimitive != null) { constructor = potentialContstructorPrimitive; } else { throw new TooManyConstructorsFoundException( "Could not determine which constructor to execute. Please specify the parameter types by hand."); } return createInstance(constructor, arguments); } /** * Gets the potential var args constructor. * * @param the generic type * @param classThatContainsTheConstructorToTest the class that contains the constructor to test * @param arguments the arguments * @return the potential var args constructor */ @SuppressWarnings("unchecked") private static Constructor getPotentialVarArgsConstructor(Class classThatContainsTheConstructorToTest, Object... arguments) { Constructor[] declaredConstructors = (Constructor[]) classThatContainsTheConstructorToTest .getDeclaredConstructors(); for (Constructor possibleVarArgsConstructor : declaredConstructors) { if (possibleVarArgsConstructor.isVarArgs()) { if (arguments == null || arguments.length == 0) { return possibleVarArgsConstructor; } else { Class[] parameterTypes = possibleVarArgsConstructor.getParameterTypes(); if (parameterTypes[parameterTypes.length - 1].getComponentType().isAssignableFrom( getType(arguments[0]))) { return possibleVarArgsConstructor; } } } } return null; } /** * Creates the instance. * * @param the generic type * @param constructor the constructor * @param arguments the arguments * @return the t * @throws Exception the exception */ private static T createInstance(Constructor constructor, Object... arguments) throws Exception { if (constructor == null) { throw new IllegalArgumentException("Constructor cannot be null"); } constructor.setAccessible(true); T createdObject = null; try { if (constructor.isVarArgs()) { Class[] parameterTypes = constructor.getParameterTypes(); final int varArgsIndex = parameterTypes.length - 1; Class varArgsType = parameterTypes[varArgsIndex].getComponentType(); Object varArgsArrayInstance = createAndPopulateVarArgsArray(varArgsType, varArgsIndex, arguments); Object[] completeArgumentList = new Object[parameterTypes.length]; for (int i = 0; i < varArgsIndex; i++) { completeArgumentList[i] = arguments[i]; } completeArgumentList[completeArgumentList.length - 1] = varArgsArrayInstance; createdObject = constructor.newInstance(completeArgumentList); } else { createdObject = constructor.newInstance(arguments); } } catch (InvocationTargetException e) { Throwable cause = e.getCause(); if (cause instanceof Exception) { throw (Exception) cause; } else if (cause instanceof Error) { throw (Error) cause; } } return createdObject; } /** * Creates the and populate var args array. * * @param varArgsType the var args type * @param varArgsStartPosition the var args start position * @param arguments the arguments * @return the object */ private static Object createAndPopulateVarArgsArray(Class varArgsType, int varArgsStartPosition, Object... arguments) { Object arrayInstance = Array.newInstance(varArgsType, arguments.length - varArgsStartPosition); for (int i = varArgsStartPosition; i < arguments.length; i++) { Array.set(arrayInstance, i - varArgsStartPosition, arguments[i]); } return arrayInstance; } /** * Get all declared constructors in the class and set accessible to * true. * * @param clazz The class whose constructors to get. * @return All constructors declared in this class hierarchy. */ public static Constructor[] getAllConstructors(Class clazz) { Constructor[] declaredConstructors = clazz.getDeclaredConstructors(); for (Constructor constructor : declaredConstructors) { if (!constructor.isAccessible()) { constructor.setAccessible(true); } } return declaredConstructors; } /** * Get all methods in a class hierarchy! Both declared an non-declared (no * duplicates). * * @param clazz The class whose methods to get. * @return All methods declared in this class hierarchy. */ public static Method[] getAllMethods(Class clazz) { if (clazz == null) { throw new IllegalArgumentException("You must specify a class in order to get the methods."); } Set methods = new LinkedHashSet(); Class thisType = clazz; while (thisType != null) { final Class type = thisType; final Method[] declaredMethods = AccessController.doPrivileged(new PrivilegedAction() { public Method[] run() { return type.getDeclaredMethods(); } }); for (Method method : declaredMethods) { method.setAccessible(true); methods.add(method); } Collections.addAll(methods, type.getMethods()); thisType = thisType.getSuperclass(); } return methods.toArray(new Method[methods.size()]); } /** * Get all public methods for a class (no duplicates)! Note that the * class-hierarchy will not be traversed. * * @param clazz The class whose methods to get. * @return All public methods declared in class. */ private static Method[] getAllPublicMethods(Class clazz) { if (clazz == null) { throw new IllegalArgumentException("You must specify a class in order to get the methods."); } Set methods = new LinkedHashSet(); for (Method method : clazz.getMethods()) { method.setAccessible(true); methods.add(method); } return methods.toArray(new Method[0]); } /** * Get all fields in a class hierarchy! Both declared an non-declared (no * duplicates). * * @param clazz The class whose fields to get. * @return All fields declared in this class hierarchy. */ public static Field[] getAllFields(Class clazz) { if (clazz == null) { throw new IllegalArgumentException("You must specify the class that contains the fields"); } Set fields = new LinkedHashSet(); Class thisType = clazz; while (thisType != null) { final Field[] declaredFields = thisType.getDeclaredFields(); for (Field field : declaredFields) { field.setAccessible(true); fields.add(field); } thisType = thisType.getSuperclass(); } return fields.toArray(new Field[fields.size()]); } /** * Get the first parent constructor defined in a super class of * klass. * * @param klass The class where the constructor is located. null * ). * @return A . */ public static Constructor getFirstParentConstructor(Class klass) { try { return getUnmockedType(klass).getSuperclass().getDeclaredConstructors()[0]; } catch (Exception e) { throw new ConstructorNotFoundException("Failed to lookup constructor.", e); } } /** * Finds and returns a method based on the input parameters. If no * parameterTypes are present the method will return the first * method with name methodNameToMock. If no method was found, * null will be returned. If no methodName is * specified the method will be found based on the parameter types. If * neither method name nor parameters are specified an * * @param the generic type * @param type the type * @param methodName the method name * @param parameterTypes the parameter types * @return the method {@link IllegalArgumentException} will be thrown. */ public static Method findMethod(Class type, String methodName, Class... parameterTypes) { if (methodName == null && parameterTypes == null) { throw new IllegalArgumentException("You must specify a method name or parameter types."); } List matchingMethodsList = new LinkedList(); for (Method method : getAllMethods(type)) { if (methodName == null || method.getName().equals(methodName)) { if (parameterTypes != null && parameterTypes.length > 0) { // If argument types was supplied, make sure that they // match. Class[] paramTypes = method.getParameterTypes(); if (!checkIfParameterTypesAreSame(method.isVarArgs(), parameterTypes, paramTypes)) { continue; } } // Add the method to the matching methods list. matchingMethodsList.add(method); } } Method methodToMock = null; if (matchingMethodsList.size() > 0) { if (matchingMethodsList.size() == 1) { // We've found a unique method match. methodToMock = matchingMethodsList.get(0); } else if ((parameterTypes != null ? parameterTypes.length : 0) == 0) { /* * If we've found several matches and we've supplied no * parameter types, go through the list of found methods and see * if we have a method with no parameters. In that case return * that method. */ for (Method method : matchingMethodsList) { if (method.getParameterTypes().length == 0) { methodToMock = method; break; } } if (methodToMock == null) { WhiteboxImpl.throwExceptionWhenMultipleMethodMatchesFound("argument parameter types", matchingMethodsList.toArray(new Method[matchingMethodsList.size()])); } } else { // We've found several matching methods. WhiteboxImpl.throwExceptionWhenMultipleMethodMatchesFound("argument parameter types", matchingMethodsList.toArray(new Method[matchingMethodsList.size()])); } } return methodToMock; } /** * Checks if is proxy. * * @param type the type * @return true, if is proxy */ public static boolean isProxy(Class type) { return proxyFramework.isProxy(type); } /** * Gets the unmocked type. * * @param the generic type * @param type the type * @return the unmocked type */ public static Class getUnmockedType(Class type) { if (type == null) { throw new IllegalArgumentException("type cannot be null"); } Class unmockedType = null; if (proxyFramework != null && proxyFramework.isProxy(type)) { unmockedType = proxyFramework.getUnproxiedType(type); } else if (Proxy.isProxyClass(type)) { unmockedType = type.getInterfaces()[0]; } else { unmockedType = type; } return unmockedType; } /** * Throw exception when multiple method matches found. * * @param helpInfo the help info * @param methods the methods */ static void throwExceptionWhenMultipleMethodMatchesFound(String helpInfo, Method[] methods) { if (methods == null || methods.length < 2) { throw new IllegalArgumentException( "Internal error: throwExceptionWhenMultipleMethodMatchesFound needs at least two methods."); } StringBuilder sb = new StringBuilder(); sb.append("Several matching methods found, please specify the "); sb.append(helpInfo); sb.append(" so that PowerMock can determine which method you're referring to.\n"); sb.append("Matching methods in class ").append(methods[0].getDeclaringClass().getName()).append(" were:\n"); for (Method method : methods) { sb.append(method.getReturnType().getName()).append(" "); sb.append(method.getName()).append("( "); final Class[] parameterTypes = method.getParameterTypes(); for (Class paramType : parameterTypes) { sb.append(paramType.getName()).append(".class "); } sb.append(")\n"); } throw new TooManyMethodsFoundException(sb.toString()); } /** * Throw exception when multiple constructor matches found. * * @param constructors the constructors */ static void throwExceptionWhenMultipleConstructorMatchesFound(Constructor[] constructors) { if (constructors == null || constructors.length < 2) { throw new IllegalArgumentException( "Internal error: throwExceptionWhenMultipleConstructorMatchesFound needs at least two constructors."); } StringBuilder sb = new StringBuilder(); sb.append("Several matching constructors found, please specify the argument parameter types so that PowerMock can determine which method you're referring to.\n"); sb.append("Matching constructors in class ").append(constructors[0].getDeclaringClass().getName()) .append(" were:\n"); for (Constructor constructor : constructors) { sb.append(constructor.getName()).append("( "); final Class[] parameterTypes = constructor.getParameterTypes(); for (Class paramType : parameterTypes) { sb.append(paramType.getName()).append(".class "); } sb.append(")\n"); } throw new TooManyConstructorsFoundException(sb.toString()); } /** * Find method or throw exception. * * @param type the type * @param methodName the method name * @param parameterTypes the parameter types * @return the method */ @SuppressWarnings("all") public static Method findMethodOrThrowException(Class type, String methodName, Class... parameterTypes) { Method methodToMock = findMethod(type, methodName, parameterTypes); throwExceptionIfMethodWasNotFound(type, methodName, methodToMock, parameterTypes); return methodToMock; } /** * Get an array of {@link Method}'s that matches the supplied list of method * names. Both instance and static methods are taken into account. * * @param clazz The class that should contain the methods. * @param methodNames Names of the methods that will be returned. * @return An array of Method's. */ public static Method[] getMethods(Class clazz, String... methodNames) { if (methodNames == null || methodNames.length == 0) { throw new IllegalArgumentException("You must supply at least one method name."); } final List methodsToMock = new LinkedList(); Method[] allMethods = null; if (clazz.isInterface()) { allMethods = getAllPublicMethods(clazz); } else { allMethods = getAllMethods(clazz); } for (Method method : allMethods) { for (String methodName : methodNames) { if (method.getName().equals(methodName)) { method.setAccessible(true); methodsToMock.add(method); } } } final Method[] methodArray = methodsToMock.toArray(new Method[0]); if (methodArray.length == 0) { throw new MethodNotFoundException(String.format( "No methods matching the name(s) %s were found in the class hierarchy of %s.", concatenateStrings(methodNames), getType(clazz))); } return methodArray; } /** * Get an array of {@link Method}'s that matches the method name and whose * argument types are assignable from expectedTypes. Both * instance and static methods are taken into account. * * @param clazz The class that should contain the methods. * @param methodName Names of the methods that will be returned. * @param expectedTypes The methods must match * @param exactParameterTypeMatch true if the expectedTypes must match * the parameter types must match exactly, false if * the expectedTypes are allowed to be converted * into primitive types if they are of a wrapped type and still * match. * @return An array of Method's. */ public static Method[] getMethods(Class clazz, String methodName, Class[] expectedTypes, boolean exactParameterTypeMatch) { List matchingArgumentTypes = new LinkedList(); Method[] methods = getMethods(clazz, methodName); for (Method method : methods) { final Class[] parameterTypes = method.getParameterTypes(); if (checkIfParameterTypesAreSame(method.isVarArgs(), expectedTypes, parameterTypes) || (!exactParameterTypeMatch && checkIfParameterTypesAreSame(method.isVarArgs(), convertParameterTypesToPrimitive(expectedTypes), parameterTypes))) { matchingArgumentTypes.add(method); } } final Method[] methodArray = matchingArgumentTypes.toArray(new Method[0]); if (methodArray.length == 0) { throw new MethodNotFoundException(String.format( "No methods matching the name(s) %s were found in the class hierarchy of %s.", concatenateStrings(methodName), getType(clazz))); } return matchingArgumentTypes.toArray(new Method[matchingArgumentTypes.size()]); } /** * Get an array of {@link Field}'s that matches the supplied list of field * names. Both instance and static fields are taken into account. * * @param clazz The class that should contain the fields. * @param fieldNames Names of the fields that will be returned. * @return An array of Field's. May be of length 0 but not . */ public static Field[] getFields(Class clazz, String... fieldNames) { final List fields = new LinkedList(); for (Field field : getAllFields(clazz)) { for (String fieldName : fieldNames) { if (field.getName().equals(fieldName)) { fields.add(field); } } } final Field[] fieldArray = fields.toArray(new Field[fields.size()]); if (fieldArray.length == 0) { throw new FieldNotFoundException(String.format( "No fields matching the name(s) %s were found in the class hierarchy of %s.", concatenateStrings(fieldNames), getType(clazz))); } return fieldArray; } /** * Perform method invocation. * * @param the generic type * @param tested the tested * @param methodToInvoke the method to invoke * @param arguments the arguments * @return the t * @throws Exception the exception */ @SuppressWarnings("unchecked") public static T performMethodInvocation(Object tested, Method methodToInvoke, Object... arguments) throws Exception { final boolean accessible = methodToInvoke.isAccessible(); if (!accessible) { methodToInvoke.setAccessible(true); } try { if (isPotentialVarArgsMethod(methodToInvoke, arguments)) { Class[] parameterTypes = methodToInvoke.getParameterTypes(); final int varArgsIndex = parameterTypes.length - 1; Class varArgsType = parameterTypes[varArgsIndex].getComponentType(); Object varArgsArrayInstance = createAndPopulateVarArgsArray(varArgsType, varArgsIndex, arguments); Object[] completeArgumentList = new Object[parameterTypes.length]; for (int i = 0; i < varArgsIndex; i++) { completeArgumentList[i] = arguments[i]; } completeArgumentList[completeArgumentList.length - 1] = varArgsArrayInstance; return (T) methodToInvoke.invoke(tested, completeArgumentList); } else { return (T) methodToInvoke.invoke(tested, arguments == null ? new Object[]{arguments} : arguments); } } catch (InvocationTargetException e) { Throwable cause = e.getCause(); if (cause instanceof Exception) { throw (Exception) cause; } else if (cause instanceof Error) { throw (Error) cause; } else { throw new MethodInvocationException(cause); } } finally { if (!accessible) { methodToInvoke.setAccessible(false); } } } /** * Gets the all method except. * * @param the generic type * @param type the type * @param methodNames the method names * @return the all method except */ public static Method[] getAllMethodExcept(Class type, String... methodNames) { List methodsToMock = new LinkedList(); Method[] methods = getAllMethods(type); iterateMethods: for (Method method : methods) { for (String methodName : methodNames) { if (method.getName().equals(methodName)) { continue iterateMethods; } } methodsToMock.add(method); } return methodsToMock.toArray(new Method[0]); } /** * Gets the all metods except. * * @param the generic type * @param type the type * @param methodNameToExclude the method name to exclude * @param argumentTypes the argument types * @return the all metods except */ public static Method[] getAllMetodsExcept(Class type, String methodNameToExclude, Class[] argumentTypes) { Method[] methods = getAllMethods(type); List methodList = new ArrayList(); outer: for (Method method : methods) { if (method.getName().equals(methodNameToExclude)) { if (argumentTypes != null && argumentTypes.length > 0) { final Class[] args = method.getParameterTypes(); if (args != null && args.length == argumentTypes.length) { for (int i = 0; i < args.length; i++) { if (args[i].isAssignableFrom(getUnmockedType(argumentTypes[i]))) { /* * Method was not found thus it should not be * mocked. Continue to investigate the next * method. */ continue outer; } } } } else { continue; } } methodList.add(method); } return methodList.toArray(new Method[0]); } /** * Are all methods static. * * @param methods the methods * @return true, if successful */ public static boolean areAllMethodsStatic(Method... methods) { for (Method method : methods) { if (!Modifier.isStatic(method.getModifiers())) { return false; } } return true; } /** * Check if all arguments are of the same type. * * @param arguments the arguments * @return true, if successful */ static boolean areAllArgumentsOfSameType(Object[] arguments) { if (arguments == null || arguments.length <= 1) { return true; } // Handle null values int index = 0; Object object = null; while (object == null && index < arguments.length) { object = arguments[index++]; } if (object == null) { return true; } // End of handling null values final Class firstArgumentType = getType(object); for (int i = index; i < arguments.length; i++) { final Object argument = arguments[i]; if (argument != null && !getType(argument).isAssignableFrom(firstArgumentType)) { return false; } } return true; } /** * Check argument types match parameter types. * * @param isVarArgs If the last parameter is a var args. * @param parameterTypes the parameter types * @param arguments the arguments * @return if all actual parameter types are assignable from the expected * arguments, otherwise. */ private static boolean checkArgumentTypesMatchParameterTypes(boolean isVarArgs, Class[] parameterTypes, Object[] arguments) { if (parameterTypes == null) { throw new IllegalArgumentException("parameter types cannot be null"); } else if (!isVarArgs && arguments.length != parameterTypes.length) { return false; } for (int i = 0; i < arguments.length; i++) { Object argument = arguments[i]; if (argument == null) { final int index; if (i >= parameterTypes.length) { index = parameterTypes.length - 1; } else { index = i; } final Class type = parameterTypes[index]; if (type.isPrimitive()) { // Primitives cannot be null return false; } else { continue; } } else if (i >= parameterTypes.length) { if (isAssignableFrom(parameterTypes[parameterTypes.length - 1], getType(argument))) { continue; } else { return false; } } else { boolean assignableFrom = isAssignableFrom(parameterTypes[i], getType(argument)); final boolean isClass = parameterTypes[i].equals(Class.class) && isClass(argument); if (!assignableFrom && !isClass) { return false; } } } return true; } private static boolean isAssignableFrom(Class type, Class from) { boolean assignableFrom; Class theType = getComponentType(type); Class theFrom = getComponentType(from); assignableFrom = theType.isAssignableFrom(theFrom); if (!assignableFrom && PrimitiveWrapper.hasPrimitiveCounterPart(theFrom)) { final Class primitiveFromWrapperType = PrimitiveWrapper.getPrimitiveFromWrapperType(theFrom); if (primitiveFromWrapperType != null) { assignableFrom = theType.isAssignableFrom(primitiveFromWrapperType); } } return assignableFrom; } private static Class getComponentType(Class type) { Class theType = type; while (theType.isArray()) { theType = theType.getComponentType(); } return theType; } /** * Gets the type. * * @param object the object * @return The type of the of an object. */ public static Class getType(Object object) { Class type = null; if (isClass(object)) { type = (Class) object; } else if (object != null) { type = object.getClass(); } return type == null ? null : getUnmockedType(type); } /** * Get an inner class type. * * @param declaringClass The class in which the inner class is declared. * @param name The unqualified name (simple name) of the inner class. * @return The type. * @throws ClassNotFoundException the class not found exception */ @SuppressWarnings("unchecked") public static Class getInnerClassType(Class declaringClass, String name) throws ClassNotFoundException { return (Class) Class.forName(declaringClass.getName() + "$" + name); } /** * Get the type of a local inner class. * * @param declaringClass The class in which the local inner class is declared. * @param occurrence The occurrence of the local class. For example if you have two * local classes in the declaringClass you must pass * in 1 if you want to get the type for the first * one or 2 if you want the second one. * @param name The unqualified name (simple name) of the local class. * @return The type. * @throws ClassNotFoundException the class not found exception */ @SuppressWarnings("unchecked") public static Class getLocalClassType(Class declaringClass, int occurrence, String name) throws ClassNotFoundException { return (Class) Class.forName(declaringClass.getName() + "$" + occurrence + name); } /** * Get the type of an anonymous inner class. * * @param declaringClass The class in which the anonymous inner class is declared. * @param occurrence The occurrence of the anonymous inner class. For example if * you have two anonymous inner classes classes in the * declaringClass you must pass in 1 if * you want to get the type for the first one or 2 * if you want the second one. * @return The type. * @throws ClassNotFoundException the class not found exception */ @SuppressWarnings("unchecked") public static Class getAnonymousInnerClassType(Class declaringClass, int occurrence) throws ClassNotFoundException { return (Class) Class.forName(declaringClass.getName() + "$" + occurrence); } /** * Get all fields annotated with a particular annotation. This method * traverses the class hierarchy when checking for the annotation. * * @param object The object to look for annotations. Note that if're you're * passing an object only instance fields are checked, passing a * class will only check static fields. * @param annotation The annotation type to look for. * @param additionalAnnotations Optionally more annotations to look for. If any of the * annotations are associated with a particular field it will be * added to the resulting Set. * @return A set of all fields containing the particular annotation. */ @SuppressWarnings("unchecked") public static Set getFieldsAnnotatedWith(Object object, Class annotation, Class... additionalAnnotations) { Class[] annotations = null; if (additionalAnnotations == null || additionalAnnotations.length == 0) { annotations = (Class[]) new Class[]{annotation}; } else { annotations = (Class[]) new Class[additionalAnnotations.length + 1]; annotations[0] = annotation; System.arraycopy(additionalAnnotations, 0, annotations, 1, additionalAnnotations.length); } return getFieldsAnnotatedWith(object, annotations); } /** * Get all fields annotated with a particular annotation. This method * traverses the class hierarchy when checking for the annotation. * * @param object The object to look for annotations. Note that if're you're * passing an object only instance fields are checked, passing a * class will only check static fields. * @param annotationTypes The annotation types to look for * @return A set of all fields containing the particular annotation(s). * @since 1.3 */ public static Set getFieldsAnnotatedWith(Object object, Class[] annotationTypes) { return findAllFieldsUsingStrategy(new FieldAnnotationMatcherStrategy(annotationTypes), object, true, getType(object)); } /** * Get all fields assignable from a particular type. This method traverses * the class hierarchy when checking for the type. * * @param object The object to look for type. Note that if're you're passing an * object only instance fields are checked, passing a class will * only check static fields. * @param type The type to look for. * @return A set of all fields of the particular type. */ public static Set getFieldsOfType(Object object, Class type) { return findAllFieldsUsingStrategy(new AssignableFromFieldTypeMatcherStrategy(type), object, true, getType(object)); } /** * Get all instance fields for a particular object. It returns all fields * regardless of the field modifier and regardless of where in the class * hierarchy a field is located. * * @param object The object whose instance fields to get. * @return All instance fields in the hierarchy. All fields are set to * accessible */ public static Set getAllInstanceFields(Object object) { return findAllFieldsUsingStrategy(new AllFieldsMatcherStrategy(), object, true, getType(object)); } /** * Get all static fields for a particular type. * * @param type The class whose static fields to get. * @return All static fields in . All fields are set to accessible. */ public static Set getAllStaticFields(Class type) { final Set fields = new LinkedHashSet(); final Field[] declaredFields = type.getDeclaredFields(); for (Field field : declaredFields) { if (Modifier.isStatic(field.getModifiers())) { field.setAccessible(true); fields.add(field); } } return fields; } /** * Checks if is class. * * @param argument the argument * @return true, if is class */ public static boolean isClass(Object argument) { return argument instanceof Class; } /** * Check if parameter types are same. * * @param isVarArgs Whether or not the method or constructor contains var args. * @param expectedParameterTypes the expected parameter types * @param actualParameterTypes the actual parameter types * @return if all actual parameter types are assignable from the expected * parameter types, otherwise. */ public static boolean checkIfParameterTypesAreSame(boolean isVarArgs, Class[] expectedParameterTypes, Class[] actualParameterTypes) { if (expectedParameterTypes == null || actualParameterTypes == null) { throw new IllegalArgumentException("parameter types cannot be null"); } else if (expectedParameterTypes.length != actualParameterTypes.length) { return false; } else { for (int i = 0; i < expectedParameterTypes.length; i++) { final Class actualParameterType = getType(actualParameterTypes[i]); if (isVarArgs && i == expectedParameterTypes.length - 1 && actualParameterType.getComponentType().isAssignableFrom(expectedParameterTypes[i])) { return true; } else if (!actualParameterType.isAssignableFrom(expectedParameterTypes[i])) { return false; } } } return true; } /** * Gets the field. * * @param fieldName the field name * @param where the where * @return the field */ private static Field getField(String fieldName, Class where) { if (where == null) { throw new IllegalArgumentException("where cannot be null"); } Field field = null; try { field = where.getDeclaredField(fieldName); field.setAccessible(true); } catch (NoSuchFieldException e) { throw new FieldNotFoundException("Field '" + fieldName + "' was not found in class " + where.getName() + "."); } return field; } /** * Find field or throw exception. * * @param fieldType the field type * @param where the where * @return the field */ private static Field findFieldOrThrowException(Class fieldType, Class where) { if (fieldType == null || where == null) { throw new IllegalArgumentException("fieldType and where cannot be null"); } Field field = null; for (Field currentField : where.getDeclaredFields()) { currentField.setAccessible(true); if (currentField.getType().equals(fieldType)) { field = currentField; break; } } if (field == null) { throw new FieldNotFoundException("Cannot find a field of type " + fieldType + "in where."); } return field; } /** * Sets the field. * * @param object the object * @param value the value * @param foundField the found field */ private static void setField(Object object, Object value, Field foundField) { foundField.setAccessible(true); try { foundField.set(object, value); } catch (IllegalAccessException e) { throw new RuntimeException("Internal error: Failed to set field in method setInternalState.", e); } } /** * Concatenate strings. * * @param stringsToConcatenate the strings to concatenate * @return the string */ private static String concatenateStrings(String... stringsToConcatenate) { StringBuilder builder = new StringBuilder(); final int stringsLength = stringsToConcatenate.length; for (int i = 0; i < stringsLength; i++) { if (i == stringsLength - 1 && stringsLength != 1) { builder.append(" or "); } else if (i != 0) { builder.append(", "); } builder.append(stringsToConcatenate[i]); } return builder.toString(); } /** * Checks if is potential var args method. * * @param method the method * @param arguments the arguments * @return true, if is potential var args method */ private static boolean isPotentialVarArgsMethod(Method method, Object[] arguments) { return doesParameterTypesMatchForVarArgsInvocation(method.isVarArgs(), method.getParameterTypes(), arguments); } /** * Checks if is potential var args constructor. * * @param constructor the constructor * @param arguments the arguments * @return true, if is potential var args constructor */ private static boolean isPotentialVarArgsConstructor(Constructor constructor, Object[] arguments) { final Class[] parameterTypes = constructor.getParameterTypes(); return doesParameterTypesMatchForVarArgsInvocation(constructor.isVarArgs(), parameterTypes, arguments); } /** * Does parameter types match for var args invocation. * * @param isVarArgs the is var args * @param parameterTypes the parameter types * @param arguments the arguments * @return true, if successful */ private static boolean doesParameterTypesMatchForVarArgsInvocation(boolean isVarArgs, Class[] parameterTypes, Object[] arguments) { if (isVarArgs && arguments != null && arguments.length >= 1 && parameterTypes != null && parameterTypes.length >= 1) { final Class componentType = parameterTypes[parameterTypes.length - 1].getComponentType(); final Object lastArgument = arguments[arguments.length - 1]; if (lastArgument != null) { final Class lastArgumentTypeAsPrimitive = getTypeAsPrimitiveIfWrapped(lastArgument); final Class varArgsParameterTypeAsPrimitive = getTypeAsPrimitiveIfWrapped(componentType); isVarArgs = varArgsParameterTypeAsPrimitive.isAssignableFrom(lastArgumentTypeAsPrimitive); } } return isVarArgs && checkArgumentTypesMatchParameterTypes(isVarArgs, parameterTypes, arguments); } /** * Get the type of an object and convert it to primitive if the type has a * primitive counter-part. E.g. if object is an instance of * java.lang.Integer this method will return * int.class. * * @param object The object whose type to get. * @return the type as primitive if wrapped */ private static Class getTypeAsPrimitiveIfWrapped(Object object) { if (object != null) { final Class firstArgumentType = getType(object); final Class firstArgumentTypeAsPrimitive = PrimitiveWrapper.hasPrimitiveCounterPart(firstArgumentType) ? PrimitiveWrapper .getPrimitiveFromWrapperType(firstArgumentType) : firstArgumentType; return firstArgumentTypeAsPrimitive; } return null; } /** * Set the values of multiple instance fields defined in a context using * reflection. The values in the context will be assigned to values on the * instance. This method will traverse the class hierarchy when * searching for the fields. Example usage: *

* Given: *

*

     * public class MyContext {
     * 	private String myString = "myString";
     * 	protected int myInt = 9;
     * }
     *
     * public class MyInstance {
     * 	private String myInstanceString;
     * 	private int myInstanceInt;
     *
     * }
     * 
*

* then *

*

     * Whitebox.setInternalStateFromContext(new MyInstance(), new MyContext());
     * 
*

* will set the instance variables of myInstance to the values * specified in MyContext. * * @param object the object * @param context The context where the fields are defined. * @param additionalContexts Optionally more additional contexts. */ public static void setInternalStateFromContext(Object object, Object context, Object[] additionalContexts) { setInternalStateFromContext(object, context, FieldMatchingStrategy.MATCHING); if (additionalContexts != null && additionalContexts.length > 0) { for (Object additionContext : additionalContexts) { setInternalStateFromContext(object, additionContext, FieldMatchingStrategy.MATCHING); } } } public static void setInternalStateFromContext(Object object, Object context, FieldMatchingStrategy strategy) { if (isClass(context)) { copyState(object, getType(context), strategy); } else { copyState(object, context, strategy); } } /** * Set the values of multiple static fields defined in a context using * reflection. The values in the context will be assigned to values on the * classOrInstance. This method will traverse the class * hierarchy when searching for the fields. Example usage: *

* Given: *

*

     * public class MyContext {
     * 	private static String myString = "myString";
     * 	protected static int myInt = 9;
     * }
     *
     * public class MyInstance {
     * 	private static String myInstanceString;
     * 	private static int myInstanceInt;
     *
     * }
     * 
*

* then *

*

     * Whitebox.setInternalStateFromContext(MyInstance.class, MyContext.class);
     * 
*

* will set the static variables of MyInstance to the values * specified in MyContext. * * @param object the object * @param context The context where the fields are defined. * @param additionalContexts Optionally more additional contexts. */ public static void setInternalStateFromContext(Object object, Class context, Class[] additionalContexts) { setInternalStateFromContext(object, context, FieldMatchingStrategy.MATCHING); if (additionalContexts != null && additionalContexts.length > 0) { for (Class additionContext : additionalContexts) { setInternalStateFromContext(object, additionContext, FieldMatchingStrategy.MATCHING); } } } /** * Copy state. * * @param object the object * @param context the context * @param strategy The field matching strategy. */ static void copyState(Object object, Object context, FieldMatchingStrategy strategy) { if (object == null) { throw new IllegalArgumentException("object to set state cannot be null"); } else if (context == null) { throw new IllegalArgumentException("context cannot be null"); } else if (strategy == null) { throw new IllegalArgumentException("strategy cannot be null"); } Set allFields = isClass(context) ? getAllStaticFields(getType(context)) : getAllInstanceFields(context); for (Field field : allFields) { try { final boolean isStaticField = Modifier.isStatic(field.getModifiers()); setInternalState(isStaticField ? getType(object) : object, field.getType(), field.get(context)); } catch (FieldNotFoundException e) { if (strategy == FieldMatchingStrategy.STRICT) { throw e; } } catch (IllegalAccessException e) { // Should never happen throw new RuntimeException( "Internal Error: Failed to get the field value in method setInternalStateFromContext.", e); } } } /** * Assert object in get internal state is not null. * * @param object the object */ private static void assertObjectInGetInternalStateIsNotNull(Object object) { if (object == null) { throw new IllegalArgumentException("The object containing the field cannot be null"); } } /** * Convert parameter types to primitive. * * @param parameterTypes the parameter types * @return the class[] */ private static Class[] convertParameterTypesToPrimitive(Class[] parameterTypes) { Class[] converted = new Class[parameterTypes.length]; for (int i = 0; i < parameterTypes.length; i++) { Class primitiveWrapperType = PrimitiveWrapper.getPrimitiveFromWrapperType(parameterTypes[i]); if (primitiveWrapperType == null) { converted[i] = parameterTypes[i]; } else { converted[i] = primitiveWrapperType; } } return converted; } }