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.ConstructorNotFoundException;
import org.powermock.reflect.exceptions.FieldNotFoundException;
import org.powermock.reflect.exceptions.MethodInvocationException;
import org.powermock.reflect.exceptions.MethodNotFoundException;
import org.powermock.reflect.exceptions.TooManyConstructorsFoundException;
import org.powermock.reflect.exceptions.TooManyFieldsFoundException;
import org.powermock.reflect.exceptions.TooManyMethodsFoundException;
import org.powermock.reflect.internal.comparator.ComparatorFactory;
import org.powermock.reflect.internal.matcherstrategies.AllFieldsMatcherStrategy;
import org.powermock.reflect.internal.matcherstrategies.AssignableFromFieldTypeMatcherStrategy;
import org.powermock.reflect.internal.matcherstrategies.AssignableToFieldTypeMatcherStrategy;
import org.powermock.reflect.internal.matcherstrategies.FieldAnnotationMatcherStrategy;
import org.powermock.reflect.internal.matcherstrategies.FieldMatcherStrategy;
import org.powermock.reflect.internal.matcherstrategies.FieldNameMatcherStrategy;
import org.powermock.reflect.internal.primitivesupport.BoxedWrapper;
import org.powermock.reflect.internal.primitivesupport.PrimitiveWrapper;
import org.powermock.reflect.internal.proxy.ProxyFrameworks;
import org.powermock.reflect.internal.proxy.UnproxiedType;
import org.powermock.reflect.matching.FieldMatchingStrategy;

import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

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

    /**
     * The proxy framework.
     */
    private static ProxyFrameworks proxyFrameworks = new ProxyFrameworks();

    /**
     * "Strong" map prevent class and method objects from being GCed and unloaded.
     * TODO replace with ClassValue when Powermock drops Java 6 support.
     */
    private static ConcurrentMap allClassMethodsCache = new ConcurrentHashMap();

    /**
     * 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 {@code 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 "
                                                      + getOriginalUnmockedType(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 {@code 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()) {
            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 {@code PowerMockRunner} or
     * {@code 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() {
                        @Override
                        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
     *                       {@code null}).
     * @return A .
     */
    public static Constructor getConstructor(Class type, Class... parameterTypes) {
        Class unmockedType = WhiteboxImpl.getOriginalUnmockedType(type);
        try {
            final Constructor constructor = unmockedType.getDeclaredConstructor(parameterTypes);
            constructor.setAccessible(true);
            return constructor;
        } catch (RuntimeException e) {
            throw e;
        } catch (Error e) {
            throw 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) {
                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
     * {@code 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
     *                        {@code null}).
     * @param methodToExecute The method name. If {@code 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 MethodNotFoundException if no method was found.
     * @throws TooManyMethodsFoundException if too methods matched.
     * @throws IllegalArgumentException if {@code tested} is null.
     */
    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 {@code types}.
     *
     * @param firstMethodCandidate  The first method candidate
     * @param secondMethodCandidate The second method candidate
     * @return The method that most closely matches the provided types or {@code 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 {@code true} if the {@code expectedTypes} must match
     *                                the parameter types must match exactly, {@code false} if
     *                                the {@code 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, ComparatorFactory.createMethodComparator());
            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 ConstructorNotFoundException if too many constructors was found.
     */
    public static Constructor findDefaultConstructorOrThrowException(Class type) throws
            ConstructorNotFoundException {
        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 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[]
     */
    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 ConstructorNotFoundException if no constructor was found.
     * @throws TooManyConstructorsFoundException if too constructors matched.
     * @throws IllegalArgumentException if {@code type} is null.
     */
    public static Constructor findUniqueConstructorOrThrowException(Class type, Object... arguments) {
        return new ConstructorFinder(type, arguments).findConstructor();
    }

    /**
     * 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 " + getOriginalUnmockedType(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 "
                                                     + getOriginalUnmockedType(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 '" + getOriginalUnmockedType(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
     */
    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" : getUnproxyType(argumentArray[j]).getName(),
                                    argumentArray);
                        }
                        return argumentsAsString.toString();
                    } else {
                        argumentName = noParameters;
                    }
                } else if (argument == null) {
                    argumentName = "null";
                } else {
                    argumentName = getUnproxyType(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 = getBestCandidateConstructor(classThatContainsTheConstructorToTest, argumentTypes, arguments); return createInstance(constructor, arguments); } private static Constructor getBestCandidateConstructor(Class classThatContainsTheConstructorToTest, Class[] argumentTypes, Object[] arguments) { Constructor constructor; Constructor potentialConstructorWrapped = getPotentialConstructorWrapped(classThatContainsTheConstructorToTest, argumentTypes); Constructor potentialConstructorPrimitive = getPotentialConstructorPrimitive(classThatContainsTheConstructorToTest, argumentTypes); if (potentialConstructorPrimitive == null && potentialConstructorWrapped == 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 (potentialConstructorPrimitive == null) { constructor = potentialConstructorWrapped; } else if (potentialConstructorWrapped == null) { constructor = potentialConstructorPrimitive; } else if (arguments == null || arguments.length == 0 && potentialConstructorPrimitive != null) { constructor = potentialConstructorPrimitive; } else { throw new TooManyConstructorsFoundException( "Could not determine which constructor to execute. Please specify the parameter types by hand."); } return constructor; } private static Constructor getPotentialConstructorWrapped(Class classThatContainsTheConstructorToTest, Class[] argumentTypes) { return new CandidateConstructorSearcher(classThatContainsTheConstructorToTest, argumentTypes) .findConstructor(); } private static Constructor getPotentialConstructorPrimitive(Class classThatContainsTheConstructorToTest, Class[] argumentTypes) { Constructor potentialConstructorPrimitive = null; try { Class[] primitiveType = PrimitiveWrapper.toPrimitiveType(argumentTypes); if (!argumentTypesEqualsPrimitiveTypes(argumentTypes, primitiveType)) { potentialConstructorPrimitive = new CandidateConstructorSearcher(classThatContainsTheConstructorToTest, primitiveType) .findConstructor(); } } catch (Exception e) { // Do nothing } return potentialConstructorPrimitive; } private static boolean argumentTypesEqualsPrimitiveTypes(Class[] argumentTypes, Class[] primitiveType) { for (int index = 0; index < argumentTypes.length; index++) { if (!argumentTypes[index].equals(primitiveType[index])) { return false; } } return true; } /** * 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]; System.arraycopy(arguments, 0, completeArgumentList, 0, varArgsIndex); 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 * {@code 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) { Method[] allMethods = allClassMethodsCache.get(clazz); if (allMethods == null) { // Allows a race between concurrent threads coming for clazz's methods at the same time, // but the race seems to be harmless. allMethods = doGetAllMethods(clazz); allClassMethodsCache.put(clazz, allMethods); } return allMethods; } private static Method[] doGetAllMethods(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() { @Override 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 * {@code klass}. * * @param klass The class where the constructor is located. {@code null} * ). * @return A . */ public static Constructor getFirstParentConstructor(Class klass) { try { return getOriginalUnmockedType(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 * {@code parameterTypes} are present the method will return the first * method with name {@code methodNameToMock}. If no method was found, * {@code null} will be returned. If no {@code 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; } /** * Gets the unmocked type. * * @param the generic type * @param type the type * @return the unmocked type */ public static Class getOriginalUnmockedType(Class type) { return getUnproxiedType(type).getOriginalType(); } public static UnproxiedType getUnproxiedType(Class type) { return proxyFrameworks.getUnproxiedType(type); } /** * 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, (Object[]) 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 {@code 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 {@code true} if the {@code expectedTypes} must match * the parameter types must match exactly, {@code false} if * the {@code 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]; System.arraycopy(arguments, 0, completeArgumentList, 0, varArgsIndex); 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[] getAllMethodsExcept(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(getOriginalUnmockedType(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. */ 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; } 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; } /** * Gets the type. * * @param object the object * @return The type of the of an object. */ public static Class getUnproxyType(Object object) { Class type = null; if (isClass(object)) { type = (Class) object; } else if (object != null) { type = object.getClass(); } return type == null ? null : getOriginalUnmockedType(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 {@code declaringClass} you must pass * in {@code 1} if you want to get the type for the first * one or {@code 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 * {@code declaringClass} you must pass in {@code 1} if * you want to get the type for the first one or {@code 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 {@code 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, getUnproxyType(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) { return new ParameterTypesMatcher(isVarArgs, expectedParameterTypes, actualParameterTypes).match(); } /** * 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 { int fieldModifiersMask = foundField.getModifiers(); removeFinalModifierIfPresent(foundField); foundField.set(object, value); restoreModifiersToFieldIfChanged(fieldModifiersMask, foundField); } catch (IllegalAccessException e) { throw new RuntimeException("Internal error: Failed to set field in method setInternalState.", e); } } private static void removeFinalModifierIfPresent(Field fieldToRemoveFinalFrom) throws IllegalAccessException { int fieldModifiersMask = fieldToRemoveFinalFrom.getModifiers(); boolean isFinalModifierPresent = (fieldModifiersMask & Modifier.FINAL) == Modifier.FINAL; if (isFinalModifierPresent) { checkIfCanSetNewValue(fieldToRemoveFinalFrom); int fieldModifiersMaskWithoutFinal = fieldModifiersMask & ~Modifier.FINAL; sedModifiersToField(fieldToRemoveFinalFrom, fieldModifiersMaskWithoutFinal); } } private static void checkIfCanSetNewValue(Field fieldToSetNewValueTo) { int fieldModifiersMask = fieldToSetNewValueTo.getModifiers(); boolean isFinalModifierPresent = (fieldModifiersMask & Modifier.FINAL) == Modifier.FINAL; boolean isStaticModifierPresent = (fieldModifiersMask & Modifier.STATIC) == Modifier.STATIC; if(isFinalModifierPresent && isStaticModifierPresent){ boolean fieldTypeIsPrimitive = fieldToSetNewValueTo.getType().isPrimitive(); if (fieldTypeIsPrimitive) { throw new IllegalArgumentException("You are trying to set a private static final primitive. Try using an object like Integer instead of int!"); } boolean fieldTypeIsString = fieldToSetNewValueTo.getType().equals(String.class); if (fieldTypeIsString) { throw new IllegalArgumentException("You are trying to set a private static final String. Cannot set such fields!"); } } } private static void restoreModifiersToFieldIfChanged(int initialFieldModifiersMask, Field fieldToRestoreModifiersTo) throws IllegalAccessException { int newFieldModifiersMask = fieldToRestoreModifiersTo.getModifiers(); if(initialFieldModifiersMask != newFieldModifiersMask){ sedModifiersToField(fieldToRestoreModifiersTo, initialFieldModifiersMask); } } private static void sedModifiersToField(Field fieldToRemoveFinalFrom, int fieldModifiersMaskWithoutFinal) throws IllegalAccessException { try { Field modifiersField = Field.class.getDeclaredField("modifiers"); boolean accessibleBeforeSet = modifiersField.isAccessible(); modifiersField.setAccessible(true); modifiersField.setInt(fieldToRemoveFinalFrom, fieldModifiersMaskWithoutFinal); modifiersField.setAccessible(accessibleBeforeSet); } catch (NoSuchFieldException e) { throw new RuntimeException("Internal error: Failed to find the \"modifiers\" 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); } /** * 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 */ 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 * {@code java.lang.Integer} this method will return * {@code int.class}. * * @param object The object whose type to get. * @return the type as primitive if wrapped */ 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 * {@code 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 {@code myInstance} to the values * specified in {@code 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 * {@code 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 {@code MyInstance} to the values * specified in {@code 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; } }