/*
 * 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.api.easymock;

import net.sf.cglib.proxy.Enhancer;
import org.easymock.ConstructorArgs;
import org.easymock.IExpectationSetters;
import org.easymock.IMocksControl;
import org.easymock.internal.LastControl;
import org.easymock.internal.MockInvocationHandler;
import org.easymock.internal.MocksControl;
import org.powermock.api.easymock.internal.invocationcontrol.EasyMockMethodInvocationControl;
import org.powermock.api.easymock.internal.invocationcontrol.NewInvocationControlAssertionError;
import org.powermock.api.easymock.internal.invocationcontrol.NewInvocationControlImpl;
import org.powermock.api.easymock.internal.mockstrategy.MockStrategy;
import org.powermock.api.easymock.internal.mockstrategy.impl.DefaultMockStrategy;
import org.powermock.api.easymock.internal.mockstrategy.impl.NiceMockStrategy;
import org.powermock.api.easymock.internal.mockstrategy.impl.StrictMockStrategy;
import org.powermock.api.support.SuppressCode;
import org.powermock.api.support.membermodification.MemberModifier;
import org.powermock.core.ClassReplicaCreator;
import org.powermock.core.DefaultFieldValueGenerator;
import org.powermock.core.MockGateway;
import org.powermock.core.MockRepository;
import org.powermock.core.classloader.MockClassLoader;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.core.classloader.annotations.PrepareOnlyThisForTest;
import org.powermock.core.classloader.annotations.SuppressStaticInitializationFor;
import org.powermock.core.spi.MethodInvocationControl;
import org.powermock.core.spi.NewInvocationControl;
import org.powermock.core.spi.support.InvocationSubstitute;
import org.powermock.reflect.Whitebox;
import org.powermock.reflect.internal.WhiteboxImpl;

import java.lang.reflect.*;
import java.util.*;

/**
 * PowerMock extends EasyMock functionality with several new features such as
 * mocking static and private methods, mocking new instances and more. Use
 * PowerMock instead of EasyMock where applicable.
 */
public class PowerMock extends MemberModifier {

    private static final String NICE_REPLAY_AND_VERIFY_KEY = "PowerMock.niceReplayAndVerify";

    static {
        MockGateway.MOCK_STANDARD_METHODS = false;
        MockGateway.MOCK_GET_CLASS_METHOD = false;
    }

    /**
     * Creates a mock object that supports mocking of final and native methods.
     *
     * @param <T>     the type of the mock object
     * @param type    the type of the mock object
     * @param methods optionally what methods to mock
     * @return the mock object.
     */
    public static synchronized <T> T createMock(Class<T> type, Method... methods) {
        return doMock(type, false, new DefaultMockStrategy(), null, methods);
    }

    /**
     * Creates a mock object that supports mocking of final and native methods.
     *
     * @param <T>  the type of the mock object
     * @param type the type of the mock object
     * @return the mock object.
     */
    public static synchronized <T> T createMock(Class<T> type) {
        return doMock(type, false, new DefaultMockStrategy(), null, (Method[]) null);
    }

    /**
     * Creates a mock object that supports mocking of final and native methods
     * and invokes a specific constructor.
     *
     * @param <T>             the type of the mock object
     * @param type            the type of the mock object
     * @param constructorArgs The constructor arguments that will be used to invoke a
     *                        special constructor.
     * @param methods         optionally what methods to mock
     * @return the mock object.
     */
    public static <T> T createMock(Class<T> type, ConstructorArgs constructorArgs, Method... methods) {
        return doMock(type, false, new DefaultMockStrategy(), constructorArgs, methods);
    }

    /**
     * Creates a mock object that supports mocking of final and native methods
     * and invokes a specific constructor based on the supplied argument values.
     *
     * @param <T>                  the type of the mock object
     * @param type                 the type of the mock object
     * @param constructorArguments The constructor arguments that will be used to invoke a
     *                             certain constructor.
     * @return the mock object.
     */
    public static <T> T createMock(Class<T> type, Object... constructorArguments) {
        Constructor<?> constructor = WhiteboxImpl.findUniqueConstructorOrThrowException(type, constructorArguments);
        ConstructorArgs constructorArgs = new ConstructorArgs(constructor, constructorArguments);
        return doMock(type, false, new DefaultMockStrategy(), constructorArgs, (Method[]) null);
    }

    /**
     * Creates a strict mock object that supports mocking of final and native
     * methods.
     *
     * @param <T>     the type of the mock object
     * @param type    the type of the mock object
     * @param methods optionally what methods to mock
     * @return the mock object.
     */
    public static synchronized <T> T createStrictMock(Class<T> type, Method... methods) {
        return doMock(type, false, new StrictMockStrategy(), null, methods);
    }

    /**
     * Creates a strict mock object that supports mocking of final and native
     * methods.
     *
     * @param <T>  the type of the mock object
     * @param type the type of the mock object
     * @return the mock object.
     */
    public static synchronized <T> T createStrictMock(Class<T> type) {
        return doMock(type, false, new StrictMockStrategy(), null, (Method[]) null);
    }

    /**
     * Creates a nice mock object that supports mocking of final and native
     * methods.
     *
     * @param <T>     the type of the mock object
     * @param type    the type of the mock object
     * @param methods optionally what methods to mock
     * @return the mock object.
     */
    public static synchronized <T> T createNiceMock(Class<T> type, Method... methods) {
        return doMock(type, false, new NiceMockStrategy(), null, methods);
    }

    /**
     * Creates a nice mock object that supports mocking of final and native
     * methods.
     *
     * @param <T>  the type of the mock object
     * @param type the type of the mock object
     * @return the mock object.
     */
    public static synchronized <T> T createNiceMock(Class<T> type) {
        return doMock(type, false, new NiceMockStrategy(), null, (Method[]) null);
    }

    /**
     * Creates a strict mock object that supports mocking of final and native
     * methods and invokes a specific constructor.
     *
     * @param <T>             the type of the mock object
     * @param type            the type of the mock object
     * @param constructorArgs The constructor arguments that will be used to invoke a
     *                        special constructor.
     * @param methods         optionally what methods to mock
     * @return the mock object.
     */
    public static <T> T createStrictMock(Class<T> type, ConstructorArgs constructorArgs, Method... methods) {
        return doMock(type, false, new StrictMockStrategy(), constructorArgs, methods);
    }

    /**
     * Creates a nice mock object that supports mocking of final and native
     * methods and invokes a specific constructor.
     *
     * @param <T>             the type of the mock object
     * @param type            the type of the mock object
     * @param constructorArgs The constructor arguments that will be used to invoke a
     *                        special constructor.
     * @param methods         optionally what methods to mock
     * @return the mock object.
     */
    public static <T> T createNiceMock(Class<T> type, ConstructorArgs constructorArgs, Method... methods) {
        return doMock(type, false, new NiceMockStrategy(), constructorArgs, methods);
    }

    /**
     * Creates a strict mock object that supports mocking of final and native
     * methods and invokes a specific constructor based on the supplied argument
     * values.
     *
     * @param <T>                  the type of the mock object
     * @param type                 the type of the mock object
     * @param constructorArguments The constructor arguments that will be used to invoke a
     *                             certain constructor.
     * @return the mock object.
     */
    public static <T> T createStrictMock(Class<T> type, Object... constructorArguments) {
        Constructor<?> constructor = WhiteboxImpl.findUniqueConstructorOrThrowException(type, constructorArguments);
        ConstructorArgs constructorArgs = new ConstructorArgs(constructor, constructorArguments);
        return doMock(type, false, new StrictMockStrategy(), constructorArgs, (Method[]) null);
    }

    /**
     * Creates a nice mock object that supports mocking of final and native
     * methods and invokes a specific constructor based on the supplied argument
     * values.
     *
     * @param <T>                  the type of the mock object
     * @param type                 the type of the mock object
     * @param constructorArguments The constructor arguments that will be used to invoke a
     *                             certain constructor.
     * @return the mock object.
     */
    public static <T> T createNiceMock(Class<T> type, Object... constructorArguments) {
        Constructor<?> constructor = WhiteboxImpl.findUniqueConstructorOrThrowException(type, constructorArguments);
        ConstructorArgs constructorArgs = new ConstructorArgs(constructor, constructorArguments);
        return doMock(type, false, new NiceMockStrategy(), constructorArgs, (Method[]) null);
    }

    /**
     * Enable static mocking for a class.
     *
     * @param type    the class to enable static mocking
     * @param methods optionally what methods to mock
     */
    public static synchronized void mockStatic(Class<?> type, Method... methods) {
        doMock(type, true, new DefaultMockStrategy(), null, methods);
    }

    /**
     * Enable static mocking for a class.
     *
     * @param type the class to enable static mocking
     */
    public static synchronized void mockStatic(Class<?> type) {
        doMock(type, true, new DefaultMockStrategy(), null, (Method[]) null);
    }

    /**
     * Enable strict static mocking for a class.
     *
     * @param type    the class to enable static mocking
     * @param methods optionally what methods to mock
     */
    public static synchronized void mockStaticStrict(Class<?> type, Method... methods) {
        doMock(type, true, new StrictMockStrategy(), null, methods);
    }

    /**
     * Enable strict static mocking for a class.
     *
     * @param type the class to enable static mocking
     */
    public static synchronized void mockStaticStrict(Class<?> type) {
        doMock(type, true, new StrictMockStrategy(), null, (Method[]) null);
    }

    /**
     * Enable nice static mocking for a class.
     *
     * @param type    the class to enable static mocking
     * @param methods optionally what methods to mock
     */
    public static synchronized void mockStaticNice(Class<?> type, Method... methods) {
        doMock(type, true, new NiceMockStrategy(), null, methods);
    }

    /**
     * Enable nice static mocking for a class.
     *
     * @param type the class to enable static mocking
     */
    public static synchronized void mockStaticNice(Class<?> type) {
        doMock(type, true, new NiceMockStrategy(), null, (Method[]) null);
    }

    /**
     * A utility method that may be used to specify several methods that should
     * <i>not</i> be mocked in an easy manner (by just passing in the method
     * names of the method you wish <i>not</i> to mock). Note that you cannot
     * uniquely specify a method to exclude using this method if there are
     * several methods with the same name in <code>type</code>. This method will
     * mock ALL methods that doesn't match the supplied name(s) regardless of
     * parameter types and signature. If this is not the case you should
     * fall-back on using the {@link #createMock(Class, Method...)} method
     * instead.
     *
     * @param <T>         The type of the mock.
     * @param type        The type that'll be used to create a mock instance.
     * @param methodNames The names of the methods that should be mocked. If
     *                    <code>null</code>, then this method will have the same effect
     *                    as just calling {@link #createMock(Class, Method...)} with the
     *                    second parameter as <code>new Method[0]</code> (i.e. all
     *                    methods in that class will be mocked).
     * @return A mock object of type <T>.
     */
    public static synchronized <T> T createPartialMockForAllMethodsExcept(Class<T> type, String... methodNames) {
        if (methodNames != null && methodNames.length == 0) {
            return createMock(type);
        }

        return createMock(type, WhiteboxImpl.getAllMethodExcept(type, methodNames));
    }

    /**
     * A utility method that may be used to specify several methods that should
     * <i>not</i> be nicely mocked in an easy manner (by just passing in the
     * method names of the method you wish <i>not</i> to mock). Note that you
     * cannot uniquely specify a method to exclude using this method if there
     * are several methods with the same name in <code>type</code>. This method
     * will mock ALL methods that doesn't match the supplied name(s) regardless
     * of parameter types and signature. If this is not the case you should
     * fall-back on using the {@link #createMock(Class, Method...)} method
     * instead.
     *
     * @param <T>         The type of the mock.
     * @param type        The type that'll be used to create a mock instance.
     * @param methodNames The names of the methods that should be mocked. If
     *                    <code>null</code>, then this method will have the same effect
     *                    as just calling {@link #createMock(Class, Method...)} with the
     *                    second parameter as <code>new Method[0]</code> (i.e. all
     *                    methods in that class will be mocked).
     * @return A mock object of type <T>.
     */
    public static synchronized <T> T createNicePartialMockForAllMethodsExcept(Class<T> type, String... methodNames) {
        if (methodNames != null && methodNames.length == 0) {
            return createNiceMock(type);
        }

        return createNiceMock(type, WhiteboxImpl.getAllMethodExcept(type, methodNames));
    }

    /**
     * A utility method that may be used to specify several methods that should
     * <i>not</i> be strictly mocked in an easy manner (by just passing in the
     * method names of the method you wish <i>not</i> to mock). Note that you
     * cannot uniquely specify a method to exclude using this method if there
     * are several methods with the same name in <code>type</code>. This method
     * will mock ALL methods that doesn't match the supplied name(s) regardless
     * of parameter types and signature. If this is not the case you should
     * fall-back on using the {@link #createMock(Class, Method...)} method
     * instead.
     *
     * @param <T>         The type of the mock.
     * @param type        The type that'll be used to create a mock instance.
     * @param methodNames The names of the methods that should be mocked. If
     *                    <code>null</code>, then this method will have the same effect
     *                    as just calling {@link #createMock(Class, Method...)} with the
     *                    second parameter as <code>new Method[0]</code> (i.e. all
     *                    methods in that class will be mocked).
     * @return A mock object of type <T>.
     */
    public static synchronized <T> T createStrictPartialMockForAllMethodsExcept(Class<T> type, String... methodNames) {
        if (methodNames != null && methodNames.length == 0) {
            return createStrictMock(type);
        }

        return createStrictMock(type, WhiteboxImpl.getAllMethodExcept(type, methodNames));
    }

    /**
     * Mock all methods of a class except for a specific one. Use this method
     * only if you have several overloaded methods.
     *
     * @param <T>                 The type of the mock.
     * @param type                The type that'll be used to create a mock instance.
     * @param methodNameToExclude The name of the method not to mock.
     * @param firstArgumentType   The type of the first parameter of the method not to mock
     * @param moreTypes           Optionally more parameter types that defines the method. Note
     *                            that this is only needed to separate overloaded methods.
     * @return A mock object of type <T>.
     */
    public static synchronized <T> T createPartialMockForAllMethodsExcept(Class<T> type, String methodNameToExclude,
                                                                          Class<?> firstArgumentType, Class<?>... moreTypes) {
        /*
           * The reason why we've split the first and "additional types" is
           * because it should not intervene with the mockAllExcept(type,
           * String...methodNames) method.
           */
        final Class<?>[] argumentTypes = mergeArgumentTypes(firstArgumentType, moreTypes);

        return createMock(type, WhiteboxImpl.getAllMetodsExcept(type, methodNameToExclude, argumentTypes));
    }

    /**
     * Mock all methods of a class except for a specific one nicely. Use this
     * method only if you have several overloaded methods.
     *
     * @param <T>                 The type of the mock.
     * @param type                The type that'll be used to create a mock instance.
     * @param methodNameToExclude The name of the method not to mock.
     * @param firstArgumentType   The type of the first parameter of the method not to mock
     * @param moreTypes           Optionally more parameter types that defines the method. Note
     *                            that this is only needed to separate overloaded methods.
     * @return A mock object of type <T>.
     */
    public static synchronized <T> T createNicePartialMockForAllMethodsExcept(Class<T> type,
                                                                              String methodNameToExclude, Class<?> firstArgumentType, Class<?>... moreTypes) {
        /*
           * The reason why we've split the first and "additional types" is
           * because it should not intervene with the mockAllExcept(type,
           * String...methodNames) method.
           */
        final Class<?>[] argumentTypes = mergeArgumentTypes(firstArgumentType, moreTypes);

        return createNiceMock(type, WhiteboxImpl.getAllMetodsExcept(type, methodNameToExclude, argumentTypes));
    }

    /**
     * Mock all methods of a class except for a specific one strictly. Use this
     * method only if you have several overloaded methods.
     *
     * @param <T>                 The type of the mock.
     * @param type                The type that'll be used to create a mock instance.
     * @param methodNameToExclude The name of the method not to mock.
     * @param firstArgumentType   The type of the first parameter of the method not to mock
     * @param moreTypes           Optionally more parameter types that defines the method. Note
     *                            that this is only needed to separate overloaded methods.
     * @return A mock object of type <T>.
     */
    public static synchronized <T> T createStrictPartialMockForAllMethodsExcept(Class<T> type,
                                                                                String methodNameToExclude, Class<?> firstArgumentType, Class<?>... moreTypes) {
        /*
           * The reason why we've split the first and "additional types" is
           * because it should not intervene with the mockAllExcept(type,
           * String...methodNames) method.
           */
        final Class<?>[] argumentTypes = mergeArgumentTypes(firstArgumentType, moreTypes);

        return createStrictMock(type, WhiteboxImpl.getAllMetodsExcept(type, methodNameToExclude, argumentTypes));
    }

    /**
     * Mock a single specific method. Use this to handle overloaded methods.
     *
     * @param <T>                     The type of the mock.
     * @param type                    The type that'll be used to create a mock instance.
     * @param methodNameToMock        The name of the method to mock
     * @param firstArgumentType       The type of the first parameter of the method to mock
     * @param additionalArgumentTypes Optionally more parameter types that defines the method. Note
     *                                that this is only needed to separate overloaded methods.
     * @return A mock object of type <T>.
     */
    public static synchronized <T> T createPartialMock(Class<T> type, String methodNameToMock,
                                                       Class<?> firstArgumentType, Class<?>... additionalArgumentTypes) {
        return doMockSpecific(type, new DefaultMockStrategy(), new String[]{methodNameToMock}, null,
                mergeArgumentTypes(firstArgumentType, additionalArgumentTypes));
    }

    /**
     * Strictly mock a single specific method. Use this to handle overloaded
     * methods.
     *
     * @param <T>                     The type of the mock.
     * @param type                    The type that'll be used to create a mock instance.
     * @param methodNameToMock        The name of the method to mock
     * @param firstArgumentType       The type of the first parameter of the method to mock
     * @param additionalArgumentTypes Optionally more parameter types that defines the method. Note
     *                                that this is only needed to separate overloaded methods.
     * @return A mock object of type <T>.
     */
    public static synchronized <T> T createStrictPartialMock(Class<T> type, String methodNameToMock,
                                                             Class<?> firstArgumentType, Class<?>... additionalArgumentTypes) {
        return doMockSpecific(type, new StrictMockStrategy(), new String[]{methodNameToMock}, null,
                mergeArgumentTypes(firstArgumentType, additionalArgumentTypes));
    }

    /**
     * Nicely mock a single specific method. Use this to handle overloaded
     * methods.
     *
     * @param <T>                     The type of the mock.
     * @param type                    The type that'll be used to create a mock instance.
     * @param methodNameToMock        The name of the method to mock
     * @param firstArgumentType       The type of the first parameter of the method to mock
     * @param additionalArgumentTypes Optionally more parameter types that defines the method. Note
     *                                that this is only needed to separate overloaded methods.
     * @return A mock object of type <T>.
     */
    public static synchronized <T> T createNicePartialMock(Class<T> type, String methodNameToMock,
                                                           Class<?> firstArgumentType, Class<?>... additionalArgumentTypes) {
        return doMockSpecific(type, new NiceMockStrategy(), new String[]{methodNameToMock}, null,
                mergeArgumentTypes(firstArgumentType, additionalArgumentTypes));
    }

    /**
     * Mock a single static method.
     *
     * @param clazz                   The class where the method is specified in.
     * @param methodNameToMock        The first argument
     * @param firstArgumentType       The first argument type.
     * @param additionalArgumentTypes Optional additional argument types.
     */
    public static synchronized void mockStaticPartial(Class<?> clazz, String methodNameToMock,
                                                      Class<?> firstArgumentType, Class<?>... additionalArgumentTypes) {
        doMockSpecific(clazz, new DefaultMockStrategy(), new String[]{methodNameToMock}, null,
                mergeArgumentTypes(firstArgumentType, additionalArgumentTypes));
    }

    /**
     * Mock a single static method (strict).
     *
     * @param clazz                   The class where the method is specified in.
     * @param methodNameToMock        The first argument
     * @param firstArgumentType       The first argument type.
     * @param additionalArgumentTypes Optional additional argument types.
     */
    public static synchronized void mockStaticPartialStrict(Class<?> clazz, String methodNameToMock,
                                                            Class<?> firstArgumentType, Class<?>... additionalArgumentTypes) {
        doMockSpecific(clazz, new StrictMockStrategy(), new String[]{methodNameToMock}, null,
                mergeArgumentTypes(firstArgumentType, additionalArgumentTypes));
    }

    /**
     * Mock a single static method (nice).
     *
     * @param clazz                   The class where the method is specified in.
     * @param methodNameToMock        The first argument
     * @param firstArgumentType       The first argument type.
     * @param additionalArgumentTypes Optional additional argument types.
     */
    public static synchronized void mockStaticPartialNice(Class<?> clazz, String methodNameToMock,
                                                          Class<?> firstArgumentType, Class<?>... additionalArgumentTypes) {
        doMockSpecific(clazz, new NiceMockStrategy(), new String[]{methodNameToMock}, null,
                mergeArgumentTypes(firstArgumentType, additionalArgumentTypes));
    }

    /**
     * A utility method that may be used to mock several <b>static</b> methods
     * in an easy way (by just passing in the method names of the method you
     * wish to mock). Note that you cannot uniquely specify a method to mock
     * using this method if there are several methods with the same name in
     * <code>type</code>. This method will mock ALL methods that match the
     * supplied name regardless of parameter types and signature. If this is the
     * case you should fall-back on using the
     * {@link #mockStatic(Class, Method...)} method instead.
     *
     * @param clazz       The class that contains the static methods that should be
     *                    mocked.
     * @param methodNames The names of the methods that should be mocked. If
     *                    <code>null</code>, then this method will have the same effect
     *                    as just calling {@link #mockStatic(Class, Method...)} with the
     *                    second parameter as <code>new Method[0]</code> (i.e. all
     *                    methods in that class will be mocked).
     */
    public static synchronized void mockStaticPartial(Class<?> clazz, String... methodNames) {
        mockStatic(clazz, Whitebox.getMethods(clazz, methodNames));
    }

    /**
     * A utility method that may be used to mock several <b>static</b> methods
     * (strict) in an easy way (by just passing in the method names of the
     * method you wish to mock). Note that you cannot uniquely specify a method
     * to mock using this method if there are several methods with the same name
     * in <code>type</code>. This method will mock ALL methods that match the
     * supplied name regardless of parameter types and signature. If this is the
     * case you should fall-back on using the
     * {@link #mockStaticStrict(Class, Method...)} method instead.
     *
     * @param clazz       The class that contains the static methods that should be
     *                    mocked.
     * @param methodNames The names of the methods that should be mocked. If
     *                    <code>null</code>, then this method will have the same effect
     *                    as just calling {@link #mockStatic(Class, Method...)} with the
     *                    second parameter as <code>new Method[0]</code> (i.e. all
     *                    methods in that class will be mocked).
     */
    public static synchronized void mockStaticPartialStrict(Class<?> clazz, String... methodNames) {
        mockStaticStrict(clazz, Whitebox.getMethods(clazz, methodNames));
    }

    /**
     * A utility method that may be used to mock several <b>static</b> methods
     * (nice) in an easy way (by just passing in the method names of the method
     * you wish to mock). Note that you cannot uniquely specify a method to mock
     * using this method if there are several methods with the same name in
     * <code>type</code>. This method will mock ALL methods that match the
     * supplied name regardless of parameter types and signature. If this is the
     * case you should fall-back on using the
     * {@link #mockStaticStrict(Class, Method...)} method instead.
     *
     * @param clazz       The class that contains the static methods that should be
     *                    mocked.
     * @param methodNames The names of the methods that should be mocked. If
     *                    <code>null</code>, then this method will have the same effect
     *                    as just calling {@link #mockStatic(Class, Method...)} with the
     *                    second parameter as <code>new Method[0]</code> (i.e. all
     *                    methods in that class will be mocked).
     */
    public static synchronized void mockStaticPartialNice(Class<?> clazz, String... methodNames) {
        mockStaticNice(clazz, Whitebox.getMethods(clazz, methodNames));
    }

    static <T> T doMockSpecific(Class<T> type, MockStrategy mockStrategy, String[] methodNamesToMock,
                                ConstructorArgs constructorArgs, Class<?>... argumentTypes) {
        List<Method> methods = new LinkedList<Method>();
        for (String methodName : methodNamesToMock) {
            methods.add(WhiteboxImpl.findMethodOrThrowException(type, methodName, argumentTypes));
        }

        final Method[] methodArray = methods.toArray(new Method[0]);
        if (WhiteboxImpl.areAllMethodsStatic(methodArray)) {
            if (mockStrategy instanceof DefaultMockStrategy) {
                mockStatic(type, methodArray);
            } else if (mockStrategy instanceof StrictMockStrategy) {
                mockStaticStrict(type, methodArray);
            } else {
                mockStaticNice(type, methodArray);
            }
            return null;
        }

        T mock = null;
        if (mockStrategy instanceof DefaultMockStrategy) {
            mock = createMock(type, constructorArgs, methodArray);
        } else if (mockStrategy instanceof StrictMockStrategy) {
            mock = createStrictMock(type, constructorArgs, methodArray);
        } else {
            mock = createNiceMock(type, constructorArgs, methodArray);
        }

        return mock;
    }

    /**
     * A utility method that may be used to mock several methods in an easy way
     * (by just passing in the method names of the method you wish to mock).
     * Note that you cannot uniquely specify a method to mock using this method
     * if there are several methods with the same name in <code>type</code>.
     * This method will mock ALL methods that match the supplied name regardless
     * of parameter types and signature. If this is the case you should
     * fall-back on using the {@link #createMock(Class, Method...)} method
     * instead.
     *
     * @param <T>         The type of the mock.
     * @param type        The type that'll be used to create a mock instance.
     * @param methodNames The names of the methods that should be mocked. If
     *                    <code>null</code>, then this method will have the same effect
     *                    as just calling {@link #createMock(Class, Method...)} with the
     *                    second parameter as <code>new Method[0]</code> (i.e. all
     *                    methods in that class will be mocked).
     * @return A mock object of type <T>.
     */
    public static synchronized <T> T createPartialMock(Class<T> type, String... methodNames) {
        return createMock(type, Whitebox.getMethods(type, methodNames));
    }

    /**
     * A utility method that may be used to mock several methods in an easy way
     * (by just passing in the method names of the method you wish to mock).
     * Note that you cannot uniquely specify a method to mock using this method
     * if there are several methods with the same name in <code>type</code>.
     * This method will mock ALL methods that match the supplied name regardless
     * of parameter types and signature. If this is the case you should
     * fall-back on using the {@link #createMock(Class, Method...)} method
     * instead.
     * <p/>
     * With this method you can specify where the class hierarchy the methods
     * are located. This is useful in, for example, situations where class A
     * extends B and both have a method called "mockMe" (A overrides B's mockMe
     * method) and you like to specify the only the "mockMe" method in B should
     * be mocked. "mockMe" in A should be left intact. In this case you should
     * do:
     * <p/>
     * <pre>
     * A tested = createPartialMock(A.class, B.class, &quot;mockMe&quot;);
     * </pre>
     *
     * @param <T>         The type of the mock.
     * @param type        The type that'll be used to create a mock instance.
     * @param where       Where in the class hierarchy the methods resides.
     * @param methodNames The names of the methods that should be mocked. If
     *                    <code>null</code>, then this method will have the same effect
     *                    as just calling {@link #createMock(Class, Method...)} with the
     *                    second parameter as <code>new Method[0]</code> (i.e. all
     *                    methods in that class will be mocked).
     * @return A mock object of type <T>.
     */
    public static synchronized <T> T createPartialMock(Class<T> type, Class<? super T> where, String... methodNames) {
        return createMock(type, Whitebox.getMethods(where, methodNames));
    }

    /**
     * A utility method that may be used to strictly mock several methods in an
     * easy way (by just passing in the method names of the method you wish to
     * mock). Note that you cannot uniquely specify a method to mock using this
     * method if there are several methods with the same name in
     * <code>type</code>. This method will mock ALL methods that match the
     * supplied name regardless of parameter types and signature. If this is the
     * case you should fall-back on using the
     * {@link #createMock(Class, Method...)} method instead.
     *
     * @param <T>         The type of the mock.
     * @param type        The type that'll be used to create a mock instance.
     * @param methodNames The names of the methods that should be mocked. If
     *                    <code>null</code>, then this method will have the same effect
     *                    as just calling {@link #createMock(Class, Method...)} with the
     *                    second parameter as <code>new Method[0]</code> (i.e. all
     *                    methods in that class will be mocked).
     * @return A mock object of type <T>.
     */
    public static synchronized <T> T createStrictPartialMock(Class<T> type, String... methodNames) {
        return createStrictMock(type, Whitebox.getMethods(type, methodNames));
    }

    /**
     * A utility method that may be used to strictly mock several methods in an
     * easy way (by just passing in the method names of the method you wish to
     * mock). Note that you cannot uniquely specify a method to mock using this
     * method if there are several methods with the same name in
     * <code>type</code>. This method will mock ALL methods that match the
     * supplied name regardless of parameter types and signature. If this is the
     * case you should fall-back on using the
     * {@link #createMock(Class, Method...)} method instead.
     * <p/>
     * With this method you can specify where the class hierarchy the methods
     * are located. This is useful in, for example, situations where class A
     * extends B and both have a method called "mockMe" (A overrides B's mockMe
     * method) and you like to specify the only the "mockMe" method in B should
     * be mocked. "mockMe" in A should be left intact. In this case you should
     * do:
     * <p/>
     * <pre>
     * A tested = createPartialMockStrict(A.class, B.class, &quot;mockMe&quot;);
     * </pre>
     *
     * @param <T>         The type of the mock.
     * @param type        The type that'll be used to create a mock instance.
     * @param where       Where in the class hierarchy the methods resides.
     * @param methodNames The names of the methods that should be mocked. If
     *                    <code>null</code>, then this method will have the same effect
     *                    as just calling {@link #createMock(Class, Method...)} with the
     *                    second parameter as <code>new Method[0]</code> (i.e. all
     *                    methods in that class will be mocked).
     * @return A mock object of type <T>.
     */
    public static synchronized <T> T createStrictPartialMock(Class<T> type, Class<? super T> where,
                                                             String... methodNames) {
        return createStrictMock(type, Whitebox.getMethods(where, methodNames));
    }

    /**
     * A utility method that may be used to nicely mock several methods in an
     * easy way (by just passing in the method names of the method you wish to
     * mock). Note that you cannot uniquely specify a method to mock using this
     * method if there are several methods with the same name in
     * <code>type</code>. This method will mock ALL methods that match the
     * supplied name regardless of parameter types and signature. If this is the
     * case you should fall-back on using the
     * {@link #createMock(Class, Method...)} method instead.
     *
     * @param <T>         The type of the mock.
     * @param type        The type that'll be used to create a mock instance.
     * @param methodNames The names of the methods that should be mocked. If
     *                    <code>null</code>, then this method will have the same effect
     *                    as just calling {@link #createMock(Class, Method...)} with the
     *                    second parameter as <code>new Method[0]</code> (i.e. all
     *                    methods in that class will be mocked).
     * @return A mock object of type <T>.
     */
    public static synchronized <T> T createNicePartialMock(Class<T> type, String... methodNames) {
        return createNiceMock(type, Whitebox.getMethods(type, methodNames));
    }

    /**
     * A utility method that may be used to nicely mock several methods in an
     * easy way (by just passing in the method names of the method you wish to
     * mock). Note that you cannot uniquely specify a method to mock using this
     * method if there are several methods with the same name in
     * <code>type</code>. This method will mock ALL methods that match the
     * supplied name regardless of parameter types and signature. If this is the
     * case you should fall-back on using the
     * {@link #createMock(Class, Method...)} method instead.
     * <p/>
     * With this method you can specify where the class hierarchy the methods
     * are located. This is useful in, for example, situations where class A
     * extends B and both have a method called "mockMe" (A overrides B's mockMe
     * method) and you like to specify the only the "mockMe" method in B should
     * be mocked. "mockMe" in A should be left intact. In this case you should
     * do:
     * <p/>
     * <pre>
     * A tested = createPartialMockNice(A.class, B.class, &quot;mockMe&quot;);
     * </pre>
     *
     * @param <T>         The type of the mock.
     * @param type        The type that'll be used to create a mock instance.
     * @param where       Where in the class hierarchy the methods resides.
     * @param methodNames The names of the methods that should be mocked. If
     *                    <code>null</code>, then this method will have the same effect
     *                    as just calling {@link #createMock(Class, Method...)} with the
     *                    second parameter as <code>new Method[0]</code> (i.e. all
     *                    methods in that class will be mocked).
     * @return A mock object of type <T>.
     */
    public static synchronized <T> T createNicePartialMock(Class<T> type, Class<? super T> where, String... methodNames) {
        return createNiceMock(type, Whitebox.getMethods(where, methodNames));
    }

    /**
     * A utility method that may be used to mock several methods in an easy way
     * (by just passing in the method names of the method you wish to mock). The
     * mock object created will support mocking of final methods and invokes the
     * default constructor (even if it's private).
     *
     * @param <T>         the type of the mock object
     * @param type        the type of the mock object
     * @param methodNames The names of the methods that should be mocked. If
     *                    <code>null</code>, then this method will have the same effect
     *                    as just calling {@link #createMock(Class, Method...)} with the
     *                    second parameter as <code>new Method[0]</code> (i.e. all
     *                    methods in that class will be mocked).
     * @return the mock object.
     */
    public static <T> T createPartialMockAndInvokeDefaultConstructor(Class<T> type, String... methodNames)
            throws Exception {
        return createMock(type, new ConstructorArgs(Whitebox.getConstructor(type)),
                Whitebox.getMethods(type, methodNames));
    }

    /**
     * A utility method that may be used to nicely mock several methods in an
     * easy way (by just passing in the method names of the method you wish to
     * mock). The mock object created will support mocking of final methods and
     * invokes the default constructor (even if it's private).
     *
     * @param <T>         the type of the mock object
     * @param type        the type of the mock object
     * @param methodNames The names of the methods that should be mocked. If
     *                    <code>null</code>, then this method will have the same effect
     *                    as just calling {@link #createMock(Class, Method...)} with the
     *                    second parameter as <code>new Method[0]</code> (i.e. all
     *                    methods in that class will be mocked).
     * @return the mock object.
     */
    public static <T> T createNicePartialMockAndInvokeDefaultConstructor(Class<T> type, String... methodNames)
            throws Exception {
        return createNiceMock(type, new ConstructorArgs(Whitebox.getConstructor(type)),
                Whitebox.getMethods(type, methodNames));
    }

    /**
     * A utility method that may be used to strictly mock several methods in an
     * easy way (by just passing in the method names of the method you wish to
     * mock). The mock object created will support mocking of final methods and
     * invokes the default constructor (even if it's private).
     *
     * @param <T>         the type of the mock object
     * @param type        the type of the mock object
     * @param methodNames The names of the methods that should be mocked. If
     *                    <code>null</code>, then this method will have the same effect
     *                    as just calling {@link #createMock(Class, Method...)} with the
     *                    second parameter as <code>new Method[0]</code> (i.e. all
     *                    methods in that class will be mocked).
     * @return the mock object.
     */
    public static <T> T createStrictPartialMockAndInvokeDefaultConstructor(Class<T> type, String... methodNames)
            throws Exception {
        return createStrictMock(type, new ConstructorArgs(Whitebox.getConstructor(type)),
                Whitebox.getMethods(type, methodNames));
    }

    /**
     * A utility method that may be used to mock several methods in an easy way
     * (by just passing in the method names of the method you wish to mock). The
     * mock object created will support mocking of final and native methods and
     * invokes a specific constructor based on the supplied argument values.
     *
     * @param <T>                  the type of the mock object
     * @param type                 the type of the mock object
     * @param methodNames          The names of the methods that should be mocked. If
     *                             <code>null</code>, then this method will have the same effect
     *                             as just calling {@link #createMock(Class, Method...)} with the
     *                             second parameter as <code>new Method[0]</code> (i.e. all
     *                             methods in that class will be mocked).
     * @param constructorArguments The constructor arguments that will be used to invoke a
     *                             certain constructor. (optional)
     * @return the mock object.
     */
    public static <T> T createPartialMock(Class<T> type, String[] methodNames, Object... constructorArguments) {
        Constructor<?> constructor = WhiteboxImpl.findUniqueConstructorOrThrowException(type, constructorArguments);
        ConstructorArgs constructorArgs = new ConstructorArgs(constructor, constructorArguments);
        return doMock(type, false, new DefaultMockStrategy(), constructorArgs, Whitebox.getMethods(type, methodNames));
    }

    /**
     * * A utility method that may be used to strictly mock several methods in
     * an easy way (by just passing in the method names of the method you wish
     * to mock). The mock object created will support mocking of final and
     * native methods and invokes a specific constructor based on the supplied
     * argument values.
     *
     * @param <T>                  the type of the mock object
     * @param type                 the type of the mock object
     * @param methodNames          The names of the methods that should be mocked. If
     *                             <code>null</code>, then this method will have the same effect
     *                             as just calling {@link #createMock(Class, Method...)} with the
     *                             second parameter as <code>new Method[0]</code> (i.e. all
     *                             methods in that class will be mocked).
     * @param constructorArguments The constructor arguments that will be used to invoke a
     *                             certain constructor. (optional)
     * @return the mock object.
     */
    public static <T> T createStrictPartialMock(Class<T> type, String[] methodNames, Object... constructorArguments) {
        Constructor<?> constructor = WhiteboxImpl.findUniqueConstructorOrThrowException(type, constructorArguments);
        ConstructorArgs constructorArgs = new ConstructorArgs(constructor, constructorArguments);
        return doMock(type, false, new StrictMockStrategy(), constructorArgs, Whitebox.getMethods(type, methodNames));
    }

    /**
     * * A utility method that may be used to nicely mock several methods in an
     * easy way (by just passing in the method names of the method you wish to
     * mock). The mock object created will support mocking of final and native
     * methods and invokes a specific constructor based on the supplied argument
     * values.
     *
     * @param <T>                  the type of the mock object
     * @param type                 the type of the mock object
     * @param methodNames          The names of the methods that should be mocked. If
     *                             <code>null</code>, then this method will have the same effect
     *                             as just calling {@link #createMock(Class, Method...)} with the
     *                             second parameter as <code>new Method[0]</code> (i.e. all
     *                             methods in that class will be mocked).
     * @param constructorArguments The constructor arguments that will be used to invoke a
     *                             certain constructor. (optional)
     * @return the mock object.
     */
    public static <T> T createNicePartialMock(Class<T> type, String[] methodNames, Object... constructorArguments) {
        Constructor<?> constructor = WhiteboxImpl.findUniqueConstructorOrThrowException(type, constructorArguments);
        ConstructorArgs constructorArgs = new ConstructorArgs(constructor, constructorArguments);
        return doMock(type, false, new NiceMockStrategy(), constructorArgs, Whitebox.getMethods(type, methodNames));
    }

    /**
     * A utility method that may be used to mock several methods in an easy way
     * (by just passing in the method names of the method you wish to mock). Use
     * this to handle overloaded methods. The mock object created will support
     * mocking of final and native methods and invokes a specific constructor
     * based on the supplied argument values.
     *
     * @param <T>                  the type of the mock object
     * @param type                 the type of the mock object
     * @param methodName           The names of the methods that should be mocked. If
     *                             <code>null</code>, then this method will have the same effect
     *                             as just calling {@link #createMock(Class, Method...)} with the
     *                             second parameter as <code>new Method[0]</code> (i.e. all
     *                             methods in that class will be mocked).
     * @param methodParameterTypes Parameter types that defines the method. Note that this is
     *                             only needed to separate overloaded methods.
     * @param constructorArguments The constructor arguments that will be used to invoke a
     *                             certain constructor. (optional)
     * @return the mock object.
     */
    public static <T> T createPartialMock(Class<T> type, String methodName, Class<?>[] methodParameterTypes,
                                          Object... constructorArguments) {
        Constructor<?> constructor = WhiteboxImpl.findUniqueConstructorOrThrowException(type, constructorArguments);
        ConstructorArgs constructorArgs = new ConstructorArgs(constructor, constructorArguments);
        return doMockSpecific(type, new DefaultMockStrategy(), new String[]{methodName}, constructorArgs,
                methodParameterTypes);
    }

    /**
     * A utility method that may be used to strictly mock several methods in an
     * easy way (by just passing in the method names of the method you wish to
     * mock). Use this to handle overloaded methods. The mock object created
     * will support mocking of final and native methods and invokes a specific
     * constructor based on the supplied argument values.
     *
     * @param <T>                  the type of the mock object
     * @param type                 the type of the mock object
     * @param methodName           The names of the methods that should be mocked. If
     *                             <code>null</code>, then this method will have the same effect
     *                             as just calling {@link #createMock(Class, Method...)} with the
     *                             second parameter as <code>new Method[0]</code> (i.e. all
     *                             methods in that class will be mocked).
     * @param methodParameterTypes Parameter types that defines the method. Note that this is
     *                             only needed to separate overloaded methods.
     * @param constructorArguments The constructor arguments that will be used to invoke a
     *                             certain constructor. (optional)
     * @return the mock object.
     */
    public static <T> T createStrictPartialMock(Class<T> type, String methodName, Class<?>[] methodParameterTypes,
                                                Object... constructorArguments) {
        Constructor<?> constructor = WhiteboxImpl.findUniqueConstructorOrThrowException(type, constructorArguments);
        ConstructorArgs constructorArgs = new ConstructorArgs(constructor, constructorArguments);
        return doMockSpecific(type, new StrictMockStrategy(), new String[]{methodName}, constructorArgs,
                methodParameterTypes);
    }

    /**
     * A utility method that may be used to nicely mock several methods in an
     * easy way (by just passing in the method names of the method you wish to
     * mock). Use this to handle overloaded methods. The mock object created
     * will support mocking of final and native methods and invokes a specific
     * constructor based on the supplied argument values.
     *
     * @param <T>                  the type of the mock object
     * @param type                 the type of the mock object
     * @param methodName           The names of the methods that should be mocked. If
     *                             <code>null</code>, then this method will have the same effect
     *                             as just calling {@link #createMock(Class, Method...)} with the
     *                             second parameter as <code>new Method[0]</code> (i.e. all
     *                             methods in that class will be mocked).
     * @param methodParameterTypes Parameter types that defines the method. Note that this is
     *                             only needed to separate overloaded methods.
     * @param constructorArguments The constructor arguments that will be used to invoke a
     *                             certain constructor. (optional)
     * @return the mock object.
     */
    public static <T> T createNicePartialMock(Class<T> type, String methodName, Class<?>[] methodParameterTypes,
                                              Object... constructorArguments) {
        Constructor<?> constructor = WhiteboxImpl.findUniqueConstructorOrThrowException(type, constructorArguments);
        ConstructorArgs constructorArgs = new ConstructorArgs(constructor, constructorArguments);
        return doMockSpecific(type, new NiceMockStrategy(), new String[]{methodName}, constructorArgs,
                methodParameterTypes);
    }

    /**
     * A utility method that may be used to mock several methods in an easy way
     * (by just passing in the method names of the method you wish to mock). Use
     * this to handle overloaded methods <i>and</i> overloaded constructors. The
     * mock object created will support mocking of final and native methods and
     * invokes a specific constructor based on the supplied argument values.
     *
     * @param <T>                       the type of the mock object
     * @param type                      the type of the mock object
     * @param methodName                The names of the methods that should be mocked. If
     *                                  <code>null</code>, then this method will have the same effect
     *                                  as just calling {@link #createMock(Class, Method...)} with the
     *                                  second parameter as <code>new Method[0]</code> (i.e. all
     *                                  methods in that class will be mocked).
     * @param methodParameterTypes      Parameter types that defines the method. Note that this is
     *                                  only needed to separate overloaded methods.
     * @param constructorArguments      The constructor arguments that will be used to invoke a
     *                                  certain constructor.
     * @param constructorParameterTypes Parameter types that defines the constructor that should be
     *                                  invoked. Note that this is only needed to separate overloaded
     *                                  constructors.
     * @return the mock object.
     */
    public static <T> T createPartialMock(Class<T> type, String methodName, Class<?>[] methodParameterTypes,
                                          Object[] constructorArguments, Class<?>[] constructorParameterTypes) {
        ConstructorArgs constructorArgs = new ConstructorArgs(Whitebox.getConstructor(type, constructorParameterTypes),
                constructorArguments);
        return doMockSpecific(type, new DefaultMockStrategy(), new String[]{methodName}, constructorArgs,
                methodParameterTypes);
    }

    /**
     * A utility method that may be used to strictly mock several methods in an
     * easy way (by just passing in the method names of the method you wish to
     * mock). Use this to handle overloaded methods <i>and</i> overloaded
     * constructors. The mock object created will support mocking of final and
     * native methods and invokes a specific constructor based on the supplied
     * argument values.
     *
     * @param <T>                       the type of the mock object
     * @param type                      the type of the mock object
     * @param methodName                The names of the methods that should be mocked. If
     *                                  <code>null</code>, then this method will have the same effect
     *                                  as just calling {@link #createMock(Class, Method...)} with the
     *                                  second parameter as <code>new Method[0]</code> (i.e. all
     *                                  methods in that class will be mocked).
     * @param methodParameterTypes      Parameter types that defines the method. Note that this is
     *                                  only needed to separate overloaded methods.
     * @param constructorArguments      The constructor arguments that will be used to invoke a
     *                                  certain constructor.
     * @param constructorParameterTypes Parameter types that defines the constructor that should be
     *                                  invoked. Note that this is only needed to separate overloaded
     *                                  constructors.
     * @return the mock object.
     */
    public static <T> T createStrictPartialMock(Class<T> type, String methodName, Class<?>[] methodParameterTypes,
                                                Object[] constructorArguments, Class<?>[] constructorParameterTypes) {
        ConstructorArgs constructorArgs = new ConstructorArgs(Whitebox.getConstructor(type, constructorParameterTypes),
                constructorArguments);
        return doMockSpecific(type, new StrictMockStrategy(), new String[]{methodName}, constructorArgs,
                methodParameterTypes);
    }

    /**
     * A utility method that may be used to nicely mock several methods in an
     * easy way (by just passing in the method names of the method you wish to
     * mock). Use this to handle overloaded methods <i>and</i> overloaded
     * constructors. The mock object created will support mocking of final and
     * native methods and invokes a specific constructor based on the supplied
     * argument values.
     *
     * @param <T>                       the type of the mock object
     * @param type                      the type of the mock object
     * @param methodName                The names of the methods that should be mocked. If
     *                                  <code>null</code>, then this method will have the same effect
     *                                  as just calling {@link #createMock(Class, Method...)} with the
     *                                  second parameter as <code>new Method[0]</code> (i.e. all
     *                                  methods in that class will be mocked).
     * @param methodParameterTypes      Parameter types that defines the method. Note that this is
     *                                  only needed to separate overloaded methods.
     * @param constructorArguments      The constructor arguments that will be used to invoke a
     *                                  certain constructor.
     * @param constructorParameterTypes Parameter types that defines the constructor that should be
     *                                  invoked. Note that this is only needed to separate overloaded
     *                                  constructors.
     * @return the mock object.
     */
    public static <T> T createNicePartialMock(Class<T> type, String methodName, Class<?>[] methodParameterTypes,
                                              Object[] constructorArguments, Class<?>[] constructorParameterTypes) {
        ConstructorArgs constructorArgs = new ConstructorArgs(Whitebox.getConstructor(type, constructorParameterTypes),
                constructorArguments);
        return doMockSpecific(type, new NiceMockStrategy(), new String[]{methodName}, constructorArgs,
                methodParameterTypes);
    }

    /**
     * Used to specify expectations on private static methods. If possible use
     * variant with only method name.
     */
    public static synchronized <T> IExpectationSetters<T> expectPrivate(Class<?> clazz, Method method,
                                                                        Object... arguments) throws Exception {
        return doExpectPrivate(clazz, method, arguments);
    }

    /**
     * Used to specify expectations on private methods. If possible use variant
     * with only method name.
     */
    public static synchronized <T> IExpectationSetters<T> expectPrivate(Object instance, Method method,
                                                                        Object... arguments) throws Exception {
        return doExpectPrivate(instance, method, arguments);
    }

    /**
     * Used to specify expectations on private methods. Use this method to
     * handle overloaded methods.
     */
    @SuppressWarnings("all")
    public static synchronized <T> IExpectationSetters<T> expectPrivate(Object instance, String methodName,
                                                                        Class<?>[] parameterTypes, Object... arguments) throws Exception {

        if (arguments == null) {
            arguments = new Object[0];
        }

        if (instance == null) {
            throw new IllegalArgumentException("instance cannot be null.");
        } else if (arguments.length != parameterTypes.length) {
            throw new IllegalArgumentException(
                    "The length of the arguments must be equal to the number of parameter types.");
        }

        Method foundMethod = Whitebox.getMethod(instance.getClass(), methodName, parameterTypes);

        WhiteboxImpl.throwExceptionIfMethodWasNotFound(instance.getClass(), methodName, foundMethod, parameterTypes);

        return doExpectPrivate(instance, foundMethod, arguments);
    }

    /**
     * Used to specify expectations on methods using the method name. Works on
     * for example private or package private methods.
     */
    public static synchronized <T> IExpectationSetters<T> expectPrivate(Object instance, String methodName,
                                                                        Object... arguments) throws Exception {
        if (instance == null) {
            throw new IllegalArgumentException("Instance or class cannot be null.");
        }

        return expectPrivate(instance, methodName, Whitebox.getType(instance), arguments);
    }

    /**
     * Used to specify expectations on methods without specifying a method name.
     * Works on for example private or package private methods. PowerMock tries
     * to find a unique method to expect based on the argument parameters. If
     * PowerMock is unable to locate a unique method you need to revert to using
     * {@link #expectPrivate(Object, String, Object...)}.
     */
    public static synchronized <T> IExpectationSetters<T> expectPrivate(Object instance, Object... arguments)
            throws Exception {
        return expectPrivate(instance, null, Whitebox.getType(instance), arguments);
    }

    /**
     * Used to specify expectations on methods using the method name at a
     * specific place in the class hierarchy (specified by the
     * <code>where</code> parameter). Works on for example private or package
     * private methods.
     * <p/>
     * Use this for overloaded methods.
     */
    public static synchronized <T> IExpectationSetters<T> expectPrivate(Object instance, String methodName,
                                                                        Class<?> where, Class<?>[] parameterTypes, Object... arguments) throws Exception {
        if (instance == null) {
            throw new IllegalArgumentException("Instance or class to expect cannot be null.");
        }
        Method[] methods = null;
        if (methodName != null) {
            if (parameterTypes == null) {
                methods = Whitebox.getMethods(where, methodName);
            } else {
                methods = new Method[]{Whitebox.getMethod(where, methodName, parameterTypes)};
            }
        }
        Method methodToExpect;
        if (methods != null && methods.length == 1) {
            methodToExpect = methods[0];
        } else {
            methodToExpect = WhiteboxImpl.findMethodOrThrowException(instance, null, methodName, arguments);
        }

        return doExpectPrivate(instance, methodToExpect, arguments);
    }

    /**
     * Used to specify expectations on methods using the method name at a
     * specific place in the class hierarchy (specified by the
     * <code>where</code> parameter). Works on for example private or package
     * private methods.
     */
    public static synchronized <T> IExpectationSetters<T> expectPrivate(Object instance, String methodName,
                                                                        Class<?> where, Object... arguments) throws Exception {
        return expectPrivate(instance, methodName, where, null, arguments);
    }

    /**
     * This method just delegates to EasyMock class extensions
     * {@link org.easymock.EasyMock#expectLastCall()} method.
     *
     * @return The expectation setter.
     * @see org.easymock.EasyMock#expectLastCall()
     */
    public static synchronized IExpectationSetters<Object> expectLastCall() {
        return org.easymock.EasyMock.expectLastCall();
    }

    /**
     * Sometimes it is useful to allow replay and verify on non-mocks. For
     * example when using partial mocking in some tests and no mocking in other
     * test-methods, but using the same setUp and tearDown.
     */
    public static synchronized void niceReplayAndVerify() {
        MockRepository.putAdditionalState(NICE_REPLAY_AND_VERIFY_KEY, true);
    }

    /**
     * Test if a object is a mock created by EasyMock or not.
     */
    private static boolean isEasyMocked(Object mock) {
        return Enhancer.isEnhanced(mock.getClass()) || Proxy.isProxyClass(mock.getClass());
    }

    /**
     * Replay all classes and mock objects known by PowerMock. This includes all
     * classes that are prepared for test using the {@link PrepareForTest} or
     * {@link PrepareOnlyThisForTest} annotations and all classes that have had
     * their static initializers removed by using the
     * {@link SuppressStaticInitializationFor} annotation. It also includes all
     * mock instances created by PowerMock such as those created or used by
     * {@link #createMock(Class, Method...)},
     * {@link #mockStatic(Class, Method...)},
     * {@link #expectNew(Class, Object...)},
     * {@link #createPartialMock(Class, String...)} etc.
     * <p/>
     * To make it easy to pass in additional mocks <i>not</i> created by the
     * PowerMock API you can optionally specify them as <tt>additionalMocks</tt>
     * . These are typically those mock objects you have created using pure
     * EasyMock or EasyMock class extensions. No additional mocks needs to be
     * specified if you're only using PowerMock API methods.
     * <p/>
     * Note that the <tt>additionalMocks</tt> are also automatically verified
     * when invoking the {@link #verifyAll()} method.
     *
     * @param additionalMocks Mocks not created by the PowerMock API. These are typically
     *                        those mock objects you have created using pure EasyMock or
     *                        EasyMock class extensions.
     */
    public static synchronized void replayAll(Object... additionalMocks) {
        MockRepository.addObjectsToAutomaticallyReplayAndVerify(additionalMocks);

        for (Object classToReplayOrVerify : MockRepository.getObjectsToAutomaticallyReplayAndVerify()) {
            replay(classToReplayOrVerify);
        }
    }

    /**
     * Reset all classes and mock objects known by PowerMock. This includes all
     * classes that are prepared for test using the {@link PrepareForTest} or
     * {@link PrepareOnlyThisForTest} annotations and all classes that have had
     * their static initializers removed by using the
     * {@link SuppressStaticInitializationFor} annotation. It also includes all
     * mock instances created by PowerMock such as those created or used by
     * {@link #createMock(Class, Method...)},
     * {@link #mockStatic(Class, Method...)},
     * {@link #expectNew(Class, Object...)},
     * {@link #createPartialMock(Class, String...)} etc.
     * <p/>
     * To make it easy to pass in additional mocks <i>not</i> created by the
     * PowerMock API you can optionally specify them as <tt>additionalMocks</tt>
     * . These are typically those mock objects you have created using pure
     * EasyMock or EasyMock class extensions. No additional mocks needs to be
     * specified if you're only using PowerMock API methods.
     *
     * @param additionalMocks Mocks not created by the PowerMock API. These are typically
     *                        those mock objects you have created using pure EasyMock or
     *                        EasyMock class extensions.
     */
    public static synchronized void resetAll(Object... additionalMocks) {
        MockRepository.addObjectsToAutomaticallyReplayAndVerify(additionalMocks);

        for (Object classToReplayOrVerify : MockRepository.getObjectsToAutomaticallyReplayAndVerify()) {
            reset(classToReplayOrVerify);
        }
    }

    /**
     * Reset a list of class mocks.
     */
    public static synchronized void reset(Class<?>... classMocks) {
        for (Class<?> type : classMocks) {
            final MethodInvocationControl invocationHandler = MockRepository.getStaticMethodInvocationControl(type);
            if (invocationHandler != null) {
                invocationHandler.reset();
            }
            NewInvocationControl<?> newInvocationControl = MockRepository.getNewInstanceControl(type);
            if (newInvocationControl != null) {
                try {
                    newInvocationControl.reset();
                } catch (AssertionError e) {
                    NewInvocationControlAssertionError.throwAssertionErrorForNewSubstitutionFailure(e, type);
                }
            }
        }
    }

    /**
     * Reset a list of mock objects or classes.
     */
    public static synchronized void reset(Object... mocks) {
        try {
            for (Object mock : mocks) {
                if (mock instanceof Class<?>) {
                    reset((Class<?>) mock);
                } else {
                    MethodInvocationControl invocationControl = MockRepository.getInstanceMethodInvocationControl(mock);
                    if (invocationControl != null) {
                        invocationControl.reset();
                    } else {
                        if (isNiceReplayAndVerifyMode() && !isEasyMocked(mock)) {
                            // ignore non-mock
                        } else {
                            /*
                                    * Delegate to easy mock class extension if we have
                                    * no handler registered for this object.
                                    */
                            try {
                                org.easymock.EasyMock.reset(mock);
                            } catch (RuntimeException e) {
                                throw new RuntimeException(mock + " is not a mock object", e);
                            }
                        }
                    }
                }
            }
        } catch (Throwable t) {
            MockRepository.putAdditionalState(NICE_REPLAY_AND_VERIFY_KEY, false);
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else if (t instanceof Error) {
                throw (Error) t;
            }
            throw new RuntimeException(t);
        }
    }

    /**
     * Verify all classes and mock objects known by PowerMock. This includes all
     * classes that are prepared for test using the {@link PrepareForTest} or
     * {@link PrepareOnlyThisForTest} annotations and all classes that have had
     * their static initializers removed by using the
     * {@link SuppressStaticInitializationFor} annotation. It also includes all
     * mock instances created by PowerMock such as those created or used by
     * {@link #createMock(Class, Method...)},
     * {@link #mockStatic(Class, Method...)},
     * {@link #expectNew(Class, Object...)},
     * {@link #createPartialMock(Class, String...)} etc.
     * <p/>
     * Note that all <tt>additionalMocks</tt> passed to the
     * {@link #replayAll(Object...)} method are also verified here
     * automatically.
     */
    public static synchronized void verifyAll() {
        for (Object classToReplayOrVerify : MockRepository.getObjectsToAutomaticallyReplayAndVerify()) {
            verify(classToReplayOrVerify);
        }
    }

    /**
     * Switches the mocks or classes to replay mode. Note that you must use this
     * method when using PowerMock!
     *
     * @param mocks mock objects or classes loaded by PowerMock.
     * @throws Exception If something unexpected goes wrong.
     */
    public static synchronized void replay(Object... mocks) {
        try {
            for (Object mock : mocks) {
                if (mock instanceof Class<?>) {
                    replay((Class<?>) mock);
                } else {
                    MethodInvocationControl invocationControl = MockRepository.getInstanceMethodInvocationControl(mock);
                    if (invocationControl != null) {
                        invocationControl.replay();
                    } else {
                        if (isNiceReplayAndVerifyMode() && !isEasyMocked(mock)) {
                            // ignore non-mock
                        } else {
                            /*
                                    * Delegate to easy mock class extension if we have
                                    * no handler registered for this object.
                                    */
                            try {
                                org.easymock.EasyMock.replay(mock);
                            } catch (RuntimeException e) {
                                throw new RuntimeException(mock + " is not a mock object", e);
                            }
                        }
                    }
                }
            }
        } catch (Throwable t) {
            MockRepository.putAdditionalState(NICE_REPLAY_AND_VERIFY_KEY, false);
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else if (t instanceof Error) {
                throw (Error) t;
            }
            throw new RuntimeException(t);
        }
    }

    /**
     * Switches the mocks or classes to verify mode. Note that you must use this
     * method when using PowerMock!
     *
     * @param objects mock objects or classes loaded by PowerMock.
     */
    public static synchronized void verify(Object... objects) {
        for (Object mock : objects) {
            if (mock instanceof Class<?>) {
                verifyClass((Class<?>) mock);
            } else {
                MethodInvocationControl invocationControl = MockRepository.getInstanceMethodInvocationControl(mock);
                if (invocationControl != null) {
                    invocationControl.verify();
                } else {
                    if (isNiceReplayAndVerifyMode() && !isEasyMocked(mock)) {
                        // ignore non-mock
                    } else {
                        /*
                               * Delegate to easy mock class extension if we have no
                               * handler registered for this object.
                               */
                        try {
                            org.easymock.EasyMock.verify(mock);
                        } catch (RuntimeException e) {
                            throw new RuntimeException(mock + " is not a mock object", e);
                        }
                    }
                }
            }
        }
    }

    /**
     * Convenience method for createMock followed by expectNew.
     *
     * @param type      The class that should be mocked.
     * @param arguments The constructor arguments.
     * @return A mock object of the same type as the mock.
     * @throws Exception
     */
    public static synchronized <T> T createMockAndExpectNew(Class<T> type, Object... arguments) throws Exception {
        T mock = createMock(type);
        expectNew(type, arguments).andReturn(mock);
        return mock;
    }

    /**
     * Convenience method for createMock followed by expectNew when PowerMock
     * cannot determine which constructor to use automatically. This happens
     * when you have one constructor taking a primitive type and another one
     * taking the wrapper type of the primitive. For example <code>int</code>
     * and <code>Integer</code>.
     *
     * @param type           The class that should be mocked.
     * @param parameterTypes The constructor parameter types.
     * @param arguments      The constructor arguments.
     * @return A mock object of the same type as the mock.
     * @throws Exception
     */
    public static synchronized <T> T createMockAndExpectNew(Class<T> type, Class<?>[] parameterTypes,
                                                            Object... arguments) throws Exception {
        T mock = createMock(type);
        expectNew(type, parameterTypes, arguments).andReturn(mock);
        return mock;
    }

    /**
     * Convenience method for createNiceMock followed by expectNew.
     *
     * @param type      The class that should be mocked.
     * @param arguments The constructor arguments.
     * @return A mock object of the same type as the mock.
     * @throws Exception
     */
    public static synchronized <T> T createNiceMockAndExpectNew(Class<T> type, Object... arguments) throws Exception {
        T mock = createNiceMock(type);
        IExpectationSetters<T> expectationSetters = expectNiceNew(type, arguments);
        if (expectationSetters != null) {
            expectationSetters.andReturn(mock);
        }
        return mock;
    }

    /**
     * Convenience method for createNiceMock followed by expectNew when
     * PowerMock cannot determine which constructor to use automatically. This
     * happens when you have one constructor taking a primitive type and another
     * one taking the wrapper type of the primitive. For example
     * <code>int</code> and <code>Integer</code>.
     *
     * @param type           The class that should be mocked.
     * @param parameterTypes The constructor parameter types.
     * @param arguments      The constructor arguments.
     * @return A mock object of the same type as the mock.
     * @throws Exception
     */
    public static synchronized <T> T createNiceMockAndExpectNew(Class<T> type, Class<?>[] parameterTypes,
                                                                Object... arguments) throws Exception {
        T mock = createNiceMock(type);
        IExpectationSetters<T> expectationSetters = expectNiceNew(type, parameterTypes, arguments);
        if (expectationSetters != null) {
            expectationSetters.andReturn(mock);
        }
        return mock;
    }

    /**
     * Convenience method for createStrictMock followed by expectNew.
     *
     * @param type      The class that should be mocked.
     * @param arguments The constructor arguments.
     * @return A mock object of the same type as the mock.
     * @throws Exception
     */
    public static synchronized <T> T createStrictMockAndExpectNew(Class<T> type, Object... arguments) throws Exception {
        T mock = createStrictMock(type);
        expectStrictNew(type, arguments).andReturn(mock);
        return mock;
    }

    /**
     * Convenience method for createStrictMock followed by expectNew when
     * PowerMock cannot determine which constructor to use automatically. This
     * happens when you have one constructor taking a primitive type and another
     * one taking the wrapper type of the primitive. For example
     * <code>int</code> and <code>Integer</code>.
     *
     * @param type           The class that should be mocked.
     * @param parameterTypes The constructor parameter types.
     * @param arguments      The constructor arguments.
     * @return A mock object of the same type as the mock.
     * @throws Exception
     */
    public static synchronized <T> T createStrictMockAndExpectNew(Class<T> type, Class<?>[] parameterTypes,
                                                                  Object... arguments) throws Exception {
        T mock = createStrictMock(type);
        expectStrictNew(type, parameterTypes, arguments).andReturn(mock);
        return mock;
    }

    /**
     * Allows specifying expectations on new invocations. For example you might
     * want to throw an exception or return a mock. Note that you must replay
     * the class when using this method since this behavior is part of the class
     * mock.
     * <p/>
     * Use this method when you need to specify parameter types for the
     * constructor when PowerMock cannot determine which constructor to use
     * automatically. In most cases you should use
     * {@link #expectNew(Class, Object...)} instead.
     */
    public static synchronized <T> IExpectationSetters<T> expectNew(Class<T> type, Class<?>[] parameterTypes,
                                                                    Object... arguments) throws Exception {
        return doExpectNew(type, new DefaultMockStrategy(), parameterTypes, arguments);
    }

    @SuppressWarnings("unchecked")
    private static <T> IExpectationSetters<T> doExpectNew(Class<T> type, MockStrategy mockStrategy,
                                                          Class<?>[] parameterTypes, Object... arguments) throws Exception {
        if (type == null) {
            throw new IllegalArgumentException("type cannot be null");
        } else if (mockStrategy == null) {
            throw new IllegalArgumentException("Internal error: Mock strategy cannot be null");
        }

        final boolean isNiceMock = mockStrategy instanceof NiceMockStrategy;

        final Class<T> unmockedType = (Class<T>) WhiteboxImpl.getUnmockedType(type);
        if (!isNiceMock) {
            if (parameterTypes == null) {
                WhiteboxImpl.findUniqueConstructorOrThrowException(type, arguments);
            } else {
                WhiteboxImpl.getConstructor(unmockedType, parameterTypes);
            }
        }

        /*
           * Check if this type has been mocked before
           */
        NewInvocationControl<IExpectationSetters<T>> newInvocationControl = (NewInvocationControl<IExpectationSetters<T>>) MockRepository
                .getNewInstanceControl(unmockedType);
        if (newInvocationControl == null) {
            InvocationSubstitute<T> mock = doMock(InvocationSubstitute.class, false, mockStrategy, null,
                    (Method[]) null);
            newInvocationControl = new NewInvocationControlImpl<T>(mock, type);
            MockRepository.putNewInstanceControl(type, newInvocationControl);
            MockRepository.addObjectsToAutomaticallyReplayAndVerify(WhiteboxImpl.getUnmockedType(type));
        }

        if (isNiceMock && (arguments == null || arguments.length == 0)) {
            return null;
        }
        return newInvocationControl.expectSubstitutionLogic(arguments);
    }

    /**
     * Allows specifying expectations on new invocations. For example you might
     * want to throw an exception or return a mock. Note that you must replay
     * the class when using this method since this behavior is part of the class
     * mock.
     */
    public static synchronized <T> IExpectationSetters<T> expectNew(Class<T> type, Object... arguments)
            throws Exception {
        return doExpectNew(type, new DefaultMockStrategy(), null, arguments);
    }

    /**
     * Allows specifying expectations on new invocations for private member
     * (inner) classes, local or anonymous classes. For example you might want
     * to throw an exception or return a mock. Note that you must replay the
     * class when using this method since this behavior is part of the class
     * mock.
     *
     * @param fullyQualifiedName The fully-qualified name of the inner/local/anonymous type to
     *                           expect.
     * @param arguments          Optional number of arguments.
     */
    @SuppressWarnings("unchecked")
    public static synchronized <T> IExpectationSetters<T> expectNew(String fullyQualifiedName, Object... arguments)
            throws Exception {
        final Class<?> forName = Class.forName(fullyQualifiedName);
        return (IExpectationSetters<T>) doExpectNew(forName, new DefaultMockStrategy(), null, arguments);
    }

    /**
     * Allows specifying expectations on new invocations. For example you might
     * want to throw an exception or return a mock.
     * <p/>
     * This method checks the order of constructor invocations.
     * <p/>
     * Note that you must replay the class when using this method since this
     * behavior is part of the class mock.
     */
    public static synchronized <T> IExpectationSetters<T> expectStrictNew(Class<T> type, Object... arguments)
            throws Exception {
        return doExpectNew(type, new StrictMockStrategy(), null, arguments);
    }

    /**
     * Allows specifying expectations on new invocations. For example you might
     * want to throw an exception or return a mock. Note that you must replay
     * the class when using this method since this behavior is part of the class
     * mock.
     * <p/>
     * This method checks the order of constructor invocations.
     * <p/>
     * Use this method when you need to specify parameter types for the
     * constructor when PowerMock cannot determine which constructor to use
     * automatically. In most cases you should use
     * {@link #expectNew(Class, Object...)} instead.
     */
    public static synchronized <T> IExpectationSetters<T> expectStrictNew(Class<T> type, Class<?>[] parameterTypes,
                                                                          Object... arguments) throws Exception {
        return doExpectNew(type, new StrictMockStrategy(), parameterTypes, arguments);
    }

    /**
     * Allows specifying expectations on new invocations. For example you might
     * want to throw an exception or return a mock.
     * <p/>
     * This method allows any number of calls to a new constructor without
     * throwing an exception.
     * <p/>
     * Note that you must replay the class when using this method since this
     * behavior is part of the class mock.
     */
    public static synchronized <T> IExpectationSetters<T> expectNiceNew(Class<T> type, Object... arguments)
            throws Exception {
        return doExpectNew(type, new NiceMockStrategy(), null, arguments);
    }

    /**
     * Allows specifying expectations on new invocations. For example you might
     * want to throw an exception or return a mock. Note that you must replay
     * the class when using this method since this behavior is part of the class
     * mock.
     * <p/>
     * This method allows any number of calls to a new constructor without
     * throwing an exception.
     * <p/>
     * Use this method when you need to specify parameter types for the
     * constructor when PowerMock cannot determine which constructor to use
     * automatically. In most cases you should use
     * {@link #expectNew(Class, Object...)} instead.
     */
    public static synchronized <T> IExpectationSetters<T> expectNiceNew(Class<T> type, Class<?>[] parameterTypes,
                                                                        Object... arguments) throws Exception {
        return doExpectNew(type, new NiceMockStrategy(), parameterTypes, arguments);
    }

    /**
     * Suppress constructor calls on specific constructors only.
     *
     * @deprecated Use {@link #suppress(Constructor[])} instead.
     */
    public static synchronized void suppressConstructor(Constructor<?>... constructors) {
        SuppressCode.suppressConstructor(constructors);
    }

    /**
     * This method can be used to suppress the code in a specific constructor.
     *
     * @param clazz          The class where the constructor is located.
     * @param parameterTypes The parameter types of the constructor to suppress.
     * @deprecated Use {@link #suppress(Constructor)} instead.
     */
    public static synchronized void suppressSpecificConstructor(Class<?> clazz, Class<?>... parameterTypes) {
        SuppressCode.suppressSpecificConstructor(clazz, parameterTypes);
    }

    /**
     * Suppress all constructors in the given class and it's super classes.
     *
     * @param classes The classes whose constructors will be suppressed.
     * @deprecated Use {@link #suppress(Constructor[])} instead.
     */
    public static synchronized void suppressConstructor(Class<?>... classes) {
        SuppressCode.suppressConstructor(classes);
    }

    /**
     * Suppress all constructors in the given class.
     *
     * @param clazz                      The classes whose constructors will be suppressed.
     * @param excludePrivateConstructors optionally keep code in private constructors
     * @deprecated Use {@link #suppress(Constructor[])} instead.
     */
    public static synchronized void suppressConstructor(Class<?> clazz, boolean excludePrivateConstructors) {
        SuppressCode.suppressConstructor(clazz, excludePrivateConstructors);
    }

    /**
     * Suppress specific fields. This works on both instance methods and static
     * methods. Note that replay and verify are not needed as this is not part
     * of a mock behavior.
     *
     * @deprecated Use {@link #suppress(Field[])} instead.
     */
    public static synchronized void suppressField(Field... fields) {
        SuppressCode.suppressField(fields);
    }

    /**
     * Suppress all fields for these classes.
     *
     * @deprecated Use {@link #suppress(Field[])} instead.
     */
    public static synchronized void suppressField(Class<?>[] classes) {
        SuppressCode.suppressField(classes);
    }

    /**
     * Suppress multiple methods for a class.
     *
     * @param clazz      The class whose methods will be suppressed.
     * @param fieldNames The names of the methods that'll be suppressed. If field names
     *                   are empty, <i>all</i> fields in the supplied class will be
     *                   suppressed.
     * @deprecated Use {@link #suppress(Field)} instead.
     */
    public static synchronized void suppressField(Class<?> clazz, String... fieldNames) {
        SuppressCode.suppressField(clazz, fieldNames);
    }

    /**
     * Suppress specific method calls on all types containing this method. This
     * works on both instance methods and static methods. Note that replay and
     * verify are not needed as this is not part of a mock behavior.
     *
     * @deprecated Use {@link #suppress(Method[])} instead.
     */
    public static synchronized void suppressMethod(Method... methods) {
        SuppressCode.suppressMethod(methods);
    }

    /**
     * Suppress all methods for these classes.
     *
     * @param cls               The first class whose methods will be suppressed.
     * @param additionalClasses Additional classes whose methods will be suppressed.
     * @deprecated Use {@link #suppress(Method[])} instead.
     */
    public static synchronized void suppressMethod(Class<?> cls, Class<?>... additionalClasses) {
        SuppressCode.suppressMethod(cls, additionalClasses);
    }

    /**
     * Suppress all methods for these classes.
     *
     * @param classes Classes whose methods will be suppressed.
     * @deprecated Use {@link #suppress(Method[])} instead.
     */
    public static synchronized void suppressMethod(Class<?>[] classes) {
        SuppressCode.suppressMethod(classes);
    }

    /**
     * Suppress multiple methods for a class.
     *
     * @param clazz                 The class whose methods will be suppressed.
     * @param methodName            The first method to be suppress in class <code>clazz</code>.
     * @param additionalMethodNames Additional methods to suppress in class <code>clazz</code>.
     * @deprecated Use {@link #suppress(Method[])} instead.
     */
    public static synchronized void suppressMethod(Class<?> clazz, String methodName, String... additionalMethodNames) {
        SuppressCode.suppressMethod(clazz, methodName, additionalMethodNames);
    }

    /**
     * Suppress multiple methods for a class.
     *
     * @param clazz       The class whose methods will be suppressed.
     * @param methodNames Methods to suppress in class <code>clazz</code>.
     * @deprecated Use {@link #suppress(Method[])} instead.
     */
    public static synchronized void suppressMethod(Class<?> clazz, String[] methodNames) {
        SuppressCode.suppressMethod(clazz, methodNames);
    }

    /**
     * Suppress all methods for this class.
     *
     * @param clazz                 The class which methods will be suppressed.
     * @param excludePrivateMethods optionally not suppress private methods
     * @deprecated Use {@link #suppress(Method[])} instead.
     */
    public static synchronized void suppressMethod(Class<?> clazz, boolean excludePrivateMethods) {
        SuppressCode.suppressMethod(clazz, excludePrivateMethods);
    }

    /**
     * Suppress a specific method call. Use this for overloaded methods.
     *
     * @deprecated Use {@link #suppress(Method)} instead.
     */
    public static synchronized void suppressMethod(Class<?> clazz, String methodName, Class<?>[] parameterTypes) {
        SuppressCode.suppressMethod(clazz, methodName, parameterTypes);
    }

    @SuppressWarnings("unchecked")
    private static <T> T doMock(Class<T> type, boolean isStatic, MockStrategy mockStrategy,
                                ConstructorArgs constructorArgs, Method... methods) {
        if (type == null) {
            throw new IllegalArgumentException("The class to mock cannot be null");
        }

        /*
         * Clear the EasyMock state after the test method is executed.
         */
        MockRepository.addAfterMethodRunner(new Runnable() {
            @Override
            public void run() {
                LastControl.reportLastControl(null);
            }
        });

        IMocksControl control = mockStrategy.createMockControl(type);
        MockRepository.addAfterMethodRunner(new EasyMockStateCleaner());
        T mock;
        if (type.isInterface()) {
            mock = control.createMock(type);
        } else if (type.getName().startsWith("java.") && Modifier.isFinal(type.getModifiers())) {
            Class<?> replicaType = createReplicaType(type, isStatic, constructorArgs);
            final Object replica = doCreateMock(replicaType, constructorArgs, control, methods);
            control = mockStrategy.createMockControl(replicaType);
            MockInvocationHandler h = new MockInvocationHandler((MocksControl) control);
            final Set<Method> methodsToMock = toSet(methods);
            if (isStatic) {
                MockRepository.putStaticMethodInvocationControl(type, new EasyMockMethodInvocationControl<Object>(h,
                        methodsToMock, replica));
                MockRepository.addObjectsToAutomaticallyReplayAndVerify(type);
                return null;
            } else {
                final T newInstance;
                if (constructorArgs == null) {
                    newInstance = Whitebox.newInstance(type);
                    DefaultFieldValueGenerator.fillWithDefaultValues(newInstance);
                } else {
                    try {
                        newInstance = (T) constructorArgs.getConstructor().newInstance(constructorArgs.getInitArgs());
                    } catch (Exception e) {
                        throw new RuntimeException("Internal error", e);
                    }
                }
                MockRepository.putInstanceMethodInvocationControl(newInstance,
                        new EasyMockMethodInvocationControl<Object>(h, methodsToMock, replica));
                if (newInstance instanceof InvocationSubstitute<?> == false) {
                    MockRepository.addObjectsToAutomaticallyReplayAndVerify(newInstance);
                }
                return newInstance;
            }
        } else {
            mock = doCreateMock(type, constructorArgs, control, methods);
        }
        MockInvocationHandler h = new MockInvocationHandler((MocksControl) control);
        final Set<Method> methodsToMock = toSet(methods);
        if (isStatic) {
            MockRepository.putStaticMethodInvocationControl(type, new EasyMockMethodInvocationControl<T>(h,
                    methodsToMock, mock));
            MockRepository.addObjectsToAutomaticallyReplayAndVerify(type);
        } else {
            MockRepository.putInstanceMethodInvocationControl(mock, new EasyMockMethodInvocationControl<T>(h,
                    methodsToMock));
            if (mock instanceof InvocationSubstitute<?> == false) {
                MockRepository.addObjectsToAutomaticallyReplayAndVerify(mock);
            }
        }
        ClassLoader classLoader = mock.getClass().getClassLoader();
        if (classLoader instanceof MockClassLoader) {
            ((MockClassLoader) classLoader).cache(mock.getClass());
        }
        return mock;
    }

    private static <T> Class<?> createReplicaType(Class<T> type, boolean isStatic, ConstructorArgs constructorArgs) {
        ClassReplicaCreator classReplicaCreator = new ClassReplicaCreator();
        Class<?> replicaType = null;
        if (isStatic || constructorArgs == null) {
            replicaType = classReplicaCreator.createClassReplica(type);
        } else {
            try {
                replicaType = classReplicaCreator.createInstanceReplica(constructorArgs.getConstructor().newInstance(
                        constructorArgs.getInitArgs()));
            } catch (RuntimeException e) {
                throw e;
            } catch (InvocationTargetException e) {
                Throwable targetException = e.getTargetException();
                if (targetException instanceof RuntimeException) {
                    throw (RuntimeException) targetException;
                }
                throw new RuntimeException(e);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }

        }
        return replicaType;
    }

    private static <T> T doCreateMock(Class<T> type, ConstructorArgs constructorArgs, final IMocksControl control,
                                      Method... methods) {
        T mock;
        MocksControl mocksControl = ((MocksControl) control);
        if (constructorArgs == null) {
            if (methods == null) {
                mock = mocksControl.createMock(type);
            } else {
                mock = mocksControl.createMock(null, type, null, methods);
            }
        } else {
            if (methods == null) {
                mock = mocksControl.createMock(null, type, constructorArgs);
            } else {
                mock = mocksControl.createMock(null, type, constructorArgs, methods);
            }
        }
        return mock;
    }

    private static Set<Method> toSet(Method[] methods) {
        return methods == null ? null : new HashSet<Method>(Arrays.asList(methods));
    }

    private static Class<?>[] mergeArgumentTypes(Class<?> firstArgumentType, Class<?>... additionalArgumentTypes) {
        if (firstArgumentType == null) {
            return additionalArgumentTypes == null ? new Class<?>[0] : additionalArgumentTypes;
        } else if (additionalArgumentTypes == null) {
            additionalArgumentTypes = new Class<?>[0];
        }
        final Class<?>[] argumentTypes = new Class[additionalArgumentTypes.length + 1];
        argumentTypes[0] = firstArgumentType;
        if (additionalArgumentTypes.length != 0) {
            System.arraycopy(additionalArgumentTypes, 0, argumentTypes, 1, additionalArgumentTypes.length);
        }
        return argumentTypes;
    }

    @SuppressWarnings("unchecked")
    private static <T> IExpectationSetters<T> doExpectPrivate(Object instance, Method methodToExpect,
                                                              Object... arguments) throws Exception {
        WhiteboxImpl.performMethodInvocation(instance, methodToExpect, arguments);
        return (IExpectationSetters<T>) org.easymock.EasyMock.expectLastCall();
    }

    private static synchronized void replay(Class<?>... types) {
        for (Class<?> type : types) {
            final MethodInvocationControl invocationHandler = MockRepository.getStaticMethodInvocationControl(type);
            if (invocationHandler != null) {
                invocationHandler.replay();
            }

            NewInvocationControl<?> newInvocationControl = MockRepository.getNewInstanceControl(type);
            if (newInvocationControl != null) {
                newInvocationControl.replay();
            }
        }
    }

    /**
     * Note: doesn't clear PowerMock state.
     */
    private static synchronized void verifyClass(Class<?>... types) {
        for (Class<?> type : types) {
            final MethodInvocationControl invocationHandler = MockRepository.getStaticMethodInvocationControl(type);
            if (invocationHandler != null) {
                invocationHandler.verify();
            }
            NewInvocationControl<?> newInvocationControl = MockRepository.getNewInstanceControl(type);
            if (newInvocationControl != null) {
                try {
                    newInvocationControl.verify();
                } catch (AssertionError e) {
                    NewInvocationControlAssertionError.throwAssertionErrorForNewSubstitutionFailure(e, type);
                }
            }
        }
    }

    private static boolean isNiceReplayAndVerifyMode() {
        final Boolean mode = (Boolean) MockRepository.getAdditionalState(NICE_REPLAY_AND_VERIFY_KEY);
        return mode != null && mode;
    }

    /**
     * Clears the state in LastControl that deals with MocksControl.
     */
    private static class EasyMockStateCleaner implements Runnable {

        @Override
        public void run() {
            LastControl.reportLastControl(null);
            clearStateFromOtherClassLoaders();
        }

        private void clearStateFromOtherClassLoaders() {
            for (ClassLoader cl : classloadersToClear()) {
                try {
                    final Class<?> lastControlClassByCL = Class.forName(LastControl.class.getName(), false, cl);
                    final Class<?> mocksControlClassByCL = Class.forName(MocksControl.class.getName(), false, cl);
                    final Method reportLastControl = lastControlClassByCL.getMethod("reportLastControl", mocksControlClassByCL);
                    reportLastControl.invoke(lastControlClassByCL, new Object[]{null});
                } catch (Exception e) {
                    // Should never happen
                    throw new RuntimeException("Failed to clean up state", e);

                }
            }
        }

        private Iterable<ClassLoader> classloadersToClear() {
            List<ClassLoader> loaders = new ArrayList<ClassLoader>();
            final ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
            final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
            if (classFoundInClassloader(LastControl.class, systemClassLoader)) {
                loaders.add(systemClassLoader);
            }
            if (classFoundInClassloader(LastControl.class, contextClassLoader)) {
                loaders.add(contextClassLoader);
            }
            return loaders;
        }

        private boolean classFoundInClassloader(Class<?> cls, ClassLoader classLoader) {
            try {
                Class.forName(cls.getName(), false, classLoader);
                return true;
            } catch (ClassNotFoundException e) {
                return false;
            }
        }
    }
}
