summaryrefslogtreecommitdiff
path: root/src/main/java/org/mockito/internal/util
diff options
context:
space:
mode:
authorPaul Duffin <paulduffin@google.com>2017-03-03 10:59:02 +0000
committerPaul Duffin <paulduffin@google.com>2017-03-07 13:42:56 +0000
commitd174d376bccaf52c3273473bcc34bdb11b50bd67 (patch)
tree1fc35d29b265d03aec96fc5c74f050365a2695b5 /src/main/java/org/mockito/internal/util
parent134e857e0377f553c0be5f0344238ade69c5de53 (diff)
downloadmockito-d174d376bccaf52c3273473bcc34bdb11b50bd67.tar.gz
Restructure files to match upstream mockito
Makes the file structure match upstream mockito so that it is simpler to update in future. Moving the files as a separate step to upgrading makes it easier for git to track renames and simplifies future diffs and reviews. Bug: 32912773 Test: make checkbuild Change-Id: I884ba2cc2856fd19d31bdb658fa058c47d078982
Diffstat (limited to 'src/main/java/org/mockito/internal/util')
-rw-r--r--src/main/java/org/mockito/internal/util/Checks.java27
-rw-r--r--src/main/java/org/mockito/internal/util/ConsoleMockitoLogger.java15
-rw-r--r--src/main/java/org/mockito/internal/util/Decamelizer.java42
-rw-r--r--src/main/java/org/mockito/internal/util/DefaultMockingDetails.java45
-rw-r--r--src/main/java/org/mockito/internal/util/MockCreationValidator.java73
-rw-r--r--src/main/java/org/mockito/internal/util/MockNameImpl.java49
-rw-r--r--src/main/java/org/mockito/internal/util/MockUtil.java92
-rw-r--r--src/main/java/org/mockito/internal/util/MockitoLogger.java11
-rw-r--r--src/main/java/org/mockito/internal/util/ObjectMethodsGuru.java44
-rw-r--r--src/main/java/org/mockito/internal/util/Primitives.java87
-rw-r--r--src/main/java/org/mockito/internal/util/RemoveFirstLine.java16
-rw-r--r--src/main/java/org/mockito/internal/util/SimpleMockitoLogger.java20
-rw-r--r--src/main/java/org/mockito/internal/util/StringJoiner.java22
-rw-r--r--src/main/java/org/mockito/internal/util/Timer.java26
-rw-r--r--src/main/java/org/mockito/internal/util/collections/ArrayUtils.java14
-rw-r--r--src/main/java/org/mockito/internal/util/collections/HashCodeAndEqualsMockWrapper.java68
-rw-r--r--src/main/java/org/mockito/internal/util/collections/HashCodeAndEqualsSafeSet.java158
-rw-r--r--src/main/java/org/mockito/internal/util/collections/IdentitySet.java26
-rw-r--r--src/main/java/org/mockito/internal/util/collections/Iterables.java22
-rw-r--r--src/main/java/org/mockito/internal/util/collections/ListUtil.java26
-rw-r--r--src/main/java/org/mockito/internal/util/collections/Sets.java33
-rw-r--r--src/main/java/org/mockito/internal/util/io/IOUtil.java71
-rw-r--r--src/main/java/org/mockito/internal/util/junit/JUnitFailureHacker.java31
-rw-r--r--src/main/java/org/mockito/internal/util/package.html8
-rw-r--r--src/main/java/org/mockito/internal/util/reflection/AccessibilityChanger.java32
-rw-r--r--src/main/java/org/mockito/internal/util/reflection/BeanPropertySetter.java98
-rw-r--r--src/main/java/org/mockito/internal/util/reflection/Constructors.java20
-rw-r--r--src/main/java/org/mockito/internal/util/reflection/FieldCopier.java15
-rw-r--r--src/main/java/org/mockito/internal/util/reflection/FieldInitializationReport.java58
-rw-r--r--src/main/java/org/mockito/internal/util/reflection/FieldInitializer.java291
-rw-r--r--src/main/java/org/mockito/internal/util/reflection/FieldReader.java34
-rw-r--r--src/main/java/org/mockito/internal/util/reflection/FieldSetter.java32
-rw-r--r--src/main/java/org/mockito/internal/util/reflection/Fields.java130
-rw-r--r--src/main/java/org/mockito/internal/util/reflection/GenericMaster.java34
-rw-r--r--src/main/java/org/mockito/internal/util/reflection/GenericMetadataSupport.java627
-rw-r--r--src/main/java/org/mockito/internal/util/reflection/InstanceField.java124
-rw-r--r--src/main/java/org/mockito/internal/util/reflection/LenientCopyTool.java50
-rw-r--r--src/main/java/org/mockito/internal/util/reflection/Whitebox.java55
-rw-r--r--src/main/java/org/mockito/internal/util/reflection/package.html8
39 files changed, 2634 insertions, 0 deletions
diff --git a/src/main/java/org/mockito/internal/util/Checks.java b/src/main/java/org/mockito/internal/util/Checks.java
new file mode 100644
index 0000000..00ed8a8
--- /dev/null
+++ b/src/main/java/org/mockito/internal/util/Checks.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2007 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+
+package org.mockito.internal.util;
+
+/**
+ * Pre-made preconditions
+ */
+public class Checks {
+
+ public static <T> T checkNotNull(T value, String checkedValue) {
+ if(value == null) {
+ throw new NullPointerException(checkedValue + " should not be null");
+ }
+ return value;
+ }
+
+ public static <T extends Iterable> T checkItemsNotNull(T iterable, String checkedIterable) {
+ checkNotNull(iterable, checkedIterable);
+ for (Object item : iterable) {
+ checkNotNull(item, "item in " + checkedIterable);
+ }
+ return iterable;
+ }
+}
diff --git a/src/main/java/org/mockito/internal/util/ConsoleMockitoLogger.java b/src/main/java/org/mockito/internal/util/ConsoleMockitoLogger.java
new file mode 100644
index 0000000..80cac5c
--- /dev/null
+++ b/src/main/java/org/mockito/internal/util/ConsoleMockitoLogger.java
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2007 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+package org.mockito.internal.util;
+
+public class ConsoleMockitoLogger implements MockitoLogger {
+
+ /* (non-Javadoc)
+ * @see org.mockito.internal.util.Logger#print(java.lang.Object)
+ */
+ public void log(Object what) {
+ System.out.println(what);
+ }
+}
diff --git a/src/main/java/org/mockito/internal/util/Decamelizer.java b/src/main/java/org/mockito/internal/util/Decamelizer.java
new file mode 100644
index 0000000..9bc34be
--- /dev/null
+++ b/src/main/java/org/mockito/internal/util/Decamelizer.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2007 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+
+package org.mockito.internal.util;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class Decamelizer {
+
+private static final Pattern CAPS = Pattern.compile("([A-Z\\d][^A-Z\\d]*)");
+
+ public static String decamelizeMatcher(String className) {
+ if (className.length() == 0) {
+ return "<custom argument matcher>";
+ }
+
+ String decamelized = decamelizeClassName(className);
+
+ if (decamelized.length() == 0) {
+ return "<" + className + ">";
+ }
+
+ return "<" + decamelized + ">";
+ }
+
+ private static String decamelizeClassName(String className) {
+ Matcher match = CAPS.matcher(className);
+ StringBuilder deCameled = new StringBuilder();
+ while(match.find()) {
+ if (deCameled.length() == 0) {
+ deCameled.append(match.group());
+ } else {
+ deCameled.append(" ");
+ deCameled.append(match.group().toLowerCase());
+ }
+ }
+ return deCameled.toString();
+ }
+}
diff --git a/src/main/java/org/mockito/internal/util/DefaultMockingDetails.java b/src/main/java/org/mockito/internal/util/DefaultMockingDetails.java
new file mode 100644
index 0000000..e361c6e
--- /dev/null
+++ b/src/main/java/org/mockito/internal/util/DefaultMockingDetails.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2007 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+package org.mockito.internal.util;
+
+import org.mockito.MockingDetails;
+import org.mockito.invocation.Invocation;
+
+import java.util.Collection;
+
+/**
+ * Class to inspect any object, and identify whether a particular object is either a mock or a spy. This is
+ * a wrapper for {@link org.mockito.internal.util.MockUtil}.
+ */
+public class DefaultMockingDetails implements MockingDetails {
+
+ private final Object toInspect;
+ private final MockUtil delegate;
+
+ public DefaultMockingDetails(Object toInspect, MockUtil delegate){
+ this.toInspect = toInspect;
+ this.delegate = delegate;
+ }
+ /**
+ * Find out whether the object is a mock.
+ * @return true if the object is a mock or a spy.
+ */
+ public boolean isMock(){
+ return delegate.isMock( toInspect );
+ }
+
+ /**
+ * Find out whether the object is a spy.
+ * @return true if the object is a spy.
+ */
+ public boolean isSpy(){
+ return delegate.isSpy( toInspect );
+ }
+
+ public Collection<Invocation> getInvocations() {
+ return delegate.getMockHandler(toInspect).getInvocationContainer().getInvocations();
+ }
+}
+
diff --git a/src/main/java/org/mockito/internal/util/MockCreationValidator.java b/src/main/java/org/mockito/internal/util/MockCreationValidator.java
new file mode 100644
index 0000000..35d99d9
--- /dev/null
+++ b/src/main/java/org/mockito/internal/util/MockCreationValidator.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2007 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+package org.mockito.internal.util;
+
+import org.mockito.exceptions.Reporter;
+import org.mockito.internal.util.reflection.Constructors;
+import org.mockito.mock.SerializableMode;
+
+import java.io.Serializable;
+import java.util.Collection;
+
+@SuppressWarnings("unchecked")
+public class MockCreationValidator {
+
+ private final MockUtil mockUtil = new MockUtil();
+
+ public void validateType(Class classToMock) {
+ if (!mockUtil.isTypeMockable(classToMock)) {
+ new Reporter().cannotMockFinalClass(classToMock);
+ }
+ }
+
+ public void validateExtraInterfaces(Class classToMock, Collection<Class> extraInterfaces) {
+ if (extraInterfaces == null) {
+ return;
+ }
+
+ for (Class i : extraInterfaces) {
+ if (classToMock == i) {
+ new Reporter().extraInterfacesCannotContainMockedType(classToMock);
+ }
+ }
+ }
+
+ public void validateMockedType(Class classToMock, Object spiedInstance) {
+ if (classToMock == null || spiedInstance == null) {
+ return;
+ }
+ if (!classToMock.equals(spiedInstance.getClass())) {
+ new Reporter().mockedTypeIsInconsistentWithSpiedInstanceType(classToMock, spiedInstance);
+ }
+ }
+
+ public void validateDelegatedInstance(Class classToMock, Object delegatedInstance) {
+ if (classToMock == null || delegatedInstance == null) {
+ return;
+ }
+ if (delegatedInstance.getClass().isAssignableFrom(classToMock)) {
+ new Reporter().mockedTypeIsInconsistentWithDelegatedInstanceType(classToMock, delegatedInstance);
+ }
+ }
+
+ public void validateSerializable(Class classToMock, boolean serializable) {
+ // We can't catch all the errors with this piece of code
+ // Having a **superclass that do not implements Serializable** might fail as well when serialized
+ // Though it might prevent issues when mockito is mocking a class without superclass.
+ if(serializable
+ && !classToMock.isInterface()
+ && !(Serializable.class.isAssignableFrom(classToMock))
+ && Constructors.noArgConstructorOf(classToMock) == null
+ ) {
+ new Reporter().serializableWontWorkForObjectsThatDontImplementSerializable(classToMock);
+ }
+ }
+
+ public void validateConstructorUse(boolean usingConstructor, SerializableMode mode) {
+ if (usingConstructor && mode == SerializableMode.ACROSS_CLASSLOADERS) {
+ new Reporter().usingConstructorWithFancySerializable(mode);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/mockito/internal/util/MockNameImpl.java b/src/main/java/org/mockito/internal/util/MockNameImpl.java
new file mode 100644
index 0000000..d8654f3
--- /dev/null
+++ b/src/main/java/org/mockito/internal/util/MockNameImpl.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2007 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+package org.mockito.internal.util;
+
+import org.mockito.mock.MockName;
+
+import java.io.Serializable;
+
+public class MockNameImpl implements MockName, Serializable {
+
+ private static final long serialVersionUID = 8014974700844306925L;
+ private final String mockName;
+ private boolean defaultName;
+
+ @SuppressWarnings("unchecked")
+ public MockNameImpl(String mockName, Class classToMock) {
+ if (mockName == null) {
+ this.mockName = toInstanceName(classToMock);
+ this.defaultName = true;
+ } else {
+ this.mockName = mockName;
+ }
+ }
+
+ public MockNameImpl(String mockName) {
+ this.mockName = mockName;
+ }
+
+ private static String toInstanceName(Class<?> clazz) {
+ String className = clazz.getSimpleName();
+ if (className.length() == 0) {
+ //it's an anonymous class, let's get name from the parent
+ className = clazz.getSuperclass().getSimpleName();
+ }
+ //lower case first letter
+ return className.substring(0, 1).toLowerCase() + className.substring(1);
+ }
+
+ public boolean isDefault() {
+ return defaultName;
+ }
+
+ @Override
+ public String toString() {
+ return mockName;
+ }
+}
diff --git a/src/main/java/org/mockito/internal/util/MockUtil.java b/src/main/java/org/mockito/internal/util/MockUtil.java
new file mode 100644
index 0000000..433be78
--- /dev/null
+++ b/src/main/java/org/mockito/internal/util/MockUtil.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2007 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+package org.mockito.internal.util;
+
+import org.mockito.Mockito;
+import org.mockito.exceptions.misusing.NotAMockException;
+import org.mockito.internal.InternalMockHandler;
+import org.mockito.internal.configuration.plugins.Plugins;
+import org.mockito.internal.creation.settings.CreationSettings;
+import org.mockito.internal.handler.MockHandlerFactory;
+import org.mockito.internal.util.reflection.LenientCopyTool;
+import org.mockito.invocation.MockHandler;
+import org.mockito.mock.MockCreationSettings;
+import org.mockito.mock.MockName;
+import org.mockito.plugins.MockMaker;
+
+import java.lang.reflect.Modifier;
+
+@SuppressWarnings("unchecked")
+public class MockUtil {
+
+ private static final MockMaker mockMaker = Plugins.getMockMaker();
+
+ public boolean isTypeMockable(Class<?> type) {
+ return !type.isPrimitive() && !Modifier.isFinal(type.getModifiers());
+ }
+
+ public <T> T createMock(MockCreationSettings<T> settings) {
+ MockHandler mockHandler = new MockHandlerFactory().create(settings);
+
+ T mock = mockMaker.createMock(settings, mockHandler);
+
+ Object spiedInstance = settings.getSpiedInstance();
+ if (spiedInstance != null) {
+ new LenientCopyTool().copyToMock(spiedInstance, mock);
+ }
+
+ return mock;
+ }
+
+ public <T> void resetMock(T mock) {
+ InternalMockHandler oldHandler = (InternalMockHandler) getMockHandler(mock);
+ MockCreationSettings settings = oldHandler.getMockSettings();
+ MockHandler newHandler = new MockHandlerFactory().create(settings);
+
+ mockMaker.resetMock(mock, newHandler, settings);
+ }
+
+ public <T> InternalMockHandler<T> getMockHandler(T mock) {
+ if (mock == null) {
+ throw new NotAMockException("Argument should be a mock, but is null!");
+ }
+
+ if (isMockitoMock(mock)) {
+ MockHandler handler = mockMaker.getHandler(mock);
+ return (InternalMockHandler) handler;
+ } else {
+ throw new NotAMockException("Argument should be a mock, but is: " + mock.getClass());
+ }
+ }
+
+ public boolean isMock(Object mock) {
+ // double check to avoid classes that have the same interfaces, could be great to have a custom mockito field in the proxy instead of relying on instance fields
+ return isMockitoMock(mock);
+ }
+
+ public boolean isSpy(Object mock) {
+ return isMockitoMock(mock) && getMockSettings(mock).getDefaultAnswer() == Mockito.CALLS_REAL_METHODS;
+ }
+
+ private <T> boolean isMockitoMock(T mock) {
+ return mockMaker.getHandler(mock) != null;
+ }
+
+ public MockName getMockName(Object mock) {
+ return getMockHandler(mock).getMockSettings().getMockName();
+ }
+
+ public void maybeRedefineMockName(Object mock, String newName) {
+ MockName mockName = getMockName(mock);
+ //TODO SF hacky...
+ if (mockName.isDefault() && getMockHandler(mock).getMockSettings() instanceof CreationSettings) {
+ ((CreationSettings) getMockHandler(mock).getMockSettings()).setMockName(new MockNameImpl(newName));
+ }
+ }
+
+ public MockCreationSettings getMockSettings(Object mock) {
+ return getMockHandler(mock).getMockSettings();
+ }
+}
diff --git a/src/main/java/org/mockito/internal/util/MockitoLogger.java b/src/main/java/org/mockito/internal/util/MockitoLogger.java
new file mode 100644
index 0000000..31117d8
--- /dev/null
+++ b/src/main/java/org/mockito/internal/util/MockitoLogger.java
@@ -0,0 +1,11 @@
+/*
+ * Copyright (c) 2007 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+package org.mockito.internal.util;
+
+public interface MockitoLogger {
+
+ void log(Object what);
+
+} \ No newline at end of file
diff --git a/src/main/java/org/mockito/internal/util/ObjectMethodsGuru.java b/src/main/java/org/mockito/internal/util/ObjectMethodsGuru.java
new file mode 100644
index 0000000..4e78249
--- /dev/null
+++ b/src/main/java/org/mockito/internal/util/ObjectMethodsGuru.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2007 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+package org.mockito.internal.util;
+
+import org.mockito.internal.creation.DelegatingMethod;
+import org.mockito.internal.invocation.MockitoMethod;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+
+public class ObjectMethodsGuru implements Serializable {
+
+ private static final long serialVersionUID = -1286718569065470494L;
+
+ public boolean isToString(Method method) {
+ return isToString(new DelegatingMethod(method));
+ }
+
+ public boolean isToString(MockitoMethod method) {
+ return method.getReturnType() == String.class
+ && method.getParameterTypes().length == 0
+ && method.getName().equals("toString");
+ }
+
+ public boolean isEqualsMethod(Method method) {
+ return method.getName().equals("equals")
+ && method.getParameterTypes().length == 1
+ && method.getParameterTypes()[0] == Object.class;
+ }
+
+ public boolean isHashCodeMethod(Method method) {
+ return method.getName().equals("hashCode")
+ && method.getParameterTypes().length == 0;
+ }
+
+ public boolean isCompareToMethod(Method method) {
+ return Comparable.class.isAssignableFrom(method.getDeclaringClass())
+ && method.getName().equals("compareTo")
+ && method.getParameterTypes().length == 1
+ && method.getParameterTypes()[0] == method.getDeclaringClass();
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/mockito/internal/util/Primitives.java b/src/main/java/org/mockito/internal/util/Primitives.java
new file mode 100644
index 0000000..4dcc07e
--- /dev/null
+++ b/src/main/java/org/mockito/internal/util/Primitives.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2007 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+package org.mockito.internal.util;
+
+import java.util.HashMap;
+import java.util.Map;
+
+@SuppressWarnings("unchecked")
+public class Primitives {
+
+ private static final Map<Class<?>, Class<?>> PRIMITIVE_TYPES = new HashMap<Class<?>, Class<?>>();
+ private static final Map<Class<?>, Object> PRIMITIVE_OR_WRAPPER_DEFAULT_VALUES = new HashMap<Class<?>, Object>();
+
+
+ /**
+ * Returns the primitive type of the given class.
+ * <p/>
+ * The passed class can be any class : <code>boolean.class</code>, <code>Integer.class</code>
+ * in witch case this method will return <code>boolean.class</code>, even <code>SomeObject.class</code>
+ * in which case <code>null</code> will be returned.
+ *
+ * @param clazz The class from which primitive type has to be retrieved
+ * @param <T> The type
+ * @return The primitive type if relevant, otherwise <code>null</code>
+ */
+ public static <T> Class<T> primitiveTypeOf(Class<T> clazz) {
+ if (clazz.isPrimitive()) {
+ return clazz;
+ }
+ return (Class<T>) PRIMITIVE_TYPES.get(clazz);
+ }
+
+ /**
+ * Indicates if the given class is primitive type or a primitive wrapper.
+ *
+ * @param type The type to check
+ * @return <code>true</code> if primitive or wrapper, <code>false</code> otherwise.
+ */
+ public static boolean isPrimitiveOrWrapper(Class<?> type) {
+ return PRIMITIVE_OR_WRAPPER_DEFAULT_VALUES.containsKey(type);
+ }
+
+ /**
+ * Returns the boxed default value for a primitive or a primitive wrapper.
+ *
+ * @param primitiveOrWrapperType The type to lookup the default value
+ * @return The boxed default values as defined in Java Language Specification,
+ * <code>null</code> if the type is neither a primitive nor a wrapper
+ */
+ public static <T> T defaultValueForPrimitiveOrWrapper(Class<T> primitiveOrWrapperType) {
+ return (T) PRIMITIVE_OR_WRAPPER_DEFAULT_VALUES.get(primitiveOrWrapperType);
+ }
+
+
+ static {
+ PRIMITIVE_TYPES.put(Boolean.class, Boolean.TYPE);
+ PRIMITIVE_TYPES.put(Character.class, Character.TYPE);
+ PRIMITIVE_TYPES.put(Byte.class, Byte.TYPE);
+ PRIMITIVE_TYPES.put(Short.class, Short.TYPE);
+ PRIMITIVE_TYPES.put(Integer.class, Integer.TYPE);
+ PRIMITIVE_TYPES.put(Long.class, Long.TYPE);
+ PRIMITIVE_TYPES.put(Float.class, Float.TYPE);
+ PRIMITIVE_TYPES.put(Double.class, Double.TYPE);
+ }
+
+ static {
+ PRIMITIVE_OR_WRAPPER_DEFAULT_VALUES.put(Boolean.class, false);
+ PRIMITIVE_OR_WRAPPER_DEFAULT_VALUES.put(Character.class, '\u0000');
+ PRIMITIVE_OR_WRAPPER_DEFAULT_VALUES.put(Byte.class, (byte) 0);
+ PRIMITIVE_OR_WRAPPER_DEFAULT_VALUES.put(Short.class, (short) 0);
+ PRIMITIVE_OR_WRAPPER_DEFAULT_VALUES.put(Integer.class, 0);
+ PRIMITIVE_OR_WRAPPER_DEFAULT_VALUES.put(Long.class, 0L);
+ PRIMITIVE_OR_WRAPPER_DEFAULT_VALUES.put(Float.class, 0F);
+ PRIMITIVE_OR_WRAPPER_DEFAULT_VALUES.put(Double.class, 0D);
+
+ PRIMITIVE_OR_WRAPPER_DEFAULT_VALUES.put(boolean.class, false);
+ PRIMITIVE_OR_WRAPPER_DEFAULT_VALUES.put(char.class, '\u0000');
+ PRIMITIVE_OR_WRAPPER_DEFAULT_VALUES.put(byte.class, (byte) 0);
+ PRIMITIVE_OR_WRAPPER_DEFAULT_VALUES.put(short.class, (short) 0);
+ PRIMITIVE_OR_WRAPPER_DEFAULT_VALUES.put(int.class, 0);
+ PRIMITIVE_OR_WRAPPER_DEFAULT_VALUES.put(long.class, 0L);
+ PRIMITIVE_OR_WRAPPER_DEFAULT_VALUES.put(float.class, 0F);
+ PRIMITIVE_OR_WRAPPER_DEFAULT_VALUES.put(double.class, 0D);
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/mockito/internal/util/RemoveFirstLine.java b/src/main/java/org/mockito/internal/util/RemoveFirstLine.java
new file mode 100644
index 0000000..517f88f
--- /dev/null
+++ b/src/main/java/org/mockito/internal/util/RemoveFirstLine.java
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2007 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+package org.mockito.internal.util;
+
+public class RemoveFirstLine {
+
+ /**
+ * @param text to have the first line removed
+ * @return less first line
+ */
+ public String of(String text) {
+ return text.replaceFirst(".*?\n", "");
+ }
+}
diff --git a/src/main/java/org/mockito/internal/util/SimpleMockitoLogger.java b/src/main/java/org/mockito/internal/util/SimpleMockitoLogger.java
new file mode 100644
index 0000000..6ee40ea
--- /dev/null
+++ b/src/main/java/org/mockito/internal/util/SimpleMockitoLogger.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2007 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+package org.mockito.internal.util;
+
+public class SimpleMockitoLogger extends ConsoleMockitoLogger {
+
+ StringBuilder loggedInfo = new StringBuilder();
+
+ public void log(Object what) {
+// can be uncommented when debugging this test
+// super.log(what);
+ loggedInfo.append(what);
+ }
+
+ public String getLoggedInfo() {
+ return loggedInfo.toString();
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/mockito/internal/util/StringJoiner.java b/src/main/java/org/mockito/internal/util/StringJoiner.java
new file mode 100644
index 0000000..2cbab0c
--- /dev/null
+++ b/src/main/java/org/mockito/internal/util/StringJoiner.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2007 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+
+package org.mockito.internal.util;
+
+public class StringJoiner {
+
+ public static String join(Object ... linesToBreak) {
+ StringBuilder out = new StringBuilder("\n");
+ return join(out, linesToBreak);
+ }
+
+ private static String join(StringBuilder out, Object[] linesToBreak) {
+ for (Object line : linesToBreak) {
+ out.append(line.toString()).append("\n");
+ }
+ int lastBreak = out.lastIndexOf("\n");
+ return out.replace(lastBreak, lastBreak+1, "").toString();
+ }
+}
diff --git a/src/main/java/org/mockito/internal/util/Timer.java b/src/main/java/org/mockito/internal/util/Timer.java
new file mode 100644
index 0000000..b71e4fd
--- /dev/null
+++ b/src/main/java/org/mockito/internal/util/Timer.java
@@ -0,0 +1,26 @@
+package org.mockito.internal.util;
+
+public class Timer {
+
+ private final long durationMillis;
+ private long startTime = -1;
+
+ public Timer(long durationMillis) {
+ this.durationMillis = durationMillis;
+ }
+
+ /**
+ * Informs whether the timer is still counting down.
+ */
+ public boolean isCounting() {
+ assert startTime != -1;
+ return System.currentTimeMillis() - startTime <= durationMillis;
+ }
+
+ /**
+ * Starts the timer count down.
+ */
+ public void start() {
+ startTime = System.currentTimeMillis();
+ }
+}
diff --git a/src/main/java/org/mockito/internal/util/collections/ArrayUtils.java b/src/main/java/org/mockito/internal/util/collections/ArrayUtils.java
new file mode 100644
index 0000000..1b3859c
--- /dev/null
+++ b/src/main/java/org/mockito/internal/util/collections/ArrayUtils.java
@@ -0,0 +1,14 @@
+/*
+ * Copyright (c) 2007 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+package org.mockito.internal.util.collections;
+
+@SuppressWarnings("unchecked")
+public class ArrayUtils {
+
+ public <T> boolean isEmpty(T[] array) {
+ return array == null || array.length == 0;
+ }
+
+} \ No newline at end of file
diff --git a/src/main/java/org/mockito/internal/util/collections/HashCodeAndEqualsMockWrapper.java b/src/main/java/org/mockito/internal/util/collections/HashCodeAndEqualsMockWrapper.java
new file mode 100644
index 0000000..22c9d1b
--- /dev/null
+++ b/src/main/java/org/mockito/internal/util/collections/HashCodeAndEqualsMockWrapper.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2007 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+package org.mockito.internal.util.collections;
+
+import org.mockito.internal.util.MockUtil;
+
+/**
+ * hashCode and equals safe mock wrapper.
+ *
+ * <p>
+ * It doesn't use the actual mock {@link Object#hashCode} and {@link Object#equals} method as they might
+ * throw an NPE if those method cannot be stubbed <em>even internally</em>.
+ * </p>
+ *
+ * <p>
+ * Instead the strategy is :
+ * <ul>
+ * <li>For hashCode : <strong>use {@link System#identityHashCode}</strong></li>
+ * <li>For equals : <strong>use the object reference equality</strong></li>
+ * </ul>
+ * </p>
+ *
+ * @see HashCodeAndEqualsSafeSet
+ */
+public class HashCodeAndEqualsMockWrapper {
+
+ private final Object mockInstance;
+
+ public HashCodeAndEqualsMockWrapper(Object mockInstance) {
+ this.mockInstance = mockInstance;
+ }
+
+ public Object get() {
+ return mockInstance;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof HashCodeAndEqualsMockWrapper)) return false;
+
+ HashCodeAndEqualsMockWrapper that = (HashCodeAndEqualsMockWrapper) o;
+
+ return mockInstance == that.mockInstance;
+ }
+
+ @Override
+ public int hashCode() {
+ return System.identityHashCode(mockInstance);
+ }
+
+ public static HashCodeAndEqualsMockWrapper of(Object mock) {
+ return new HashCodeAndEqualsMockWrapper(mock);
+ }
+
+ @Override public String toString() {
+ MockUtil mockUtil = new MockUtil();
+ return "HashCodeAndEqualsMockWrapper{" +
+ "mockInstance=" + (mockUtil.isMock(mockInstance) ? mockUtil.getMockName(mockInstance) : typeInstanceString()) +
+ '}';
+ }
+
+ private String typeInstanceString() {
+ return mockInstance.getClass().getSimpleName() + "(" + System.identityHashCode(mockInstance) + ")";
+ }
+}
diff --git a/src/main/java/org/mockito/internal/util/collections/HashCodeAndEqualsSafeSet.java b/src/main/java/org/mockito/internal/util/collections/HashCodeAndEqualsSafeSet.java
new file mode 100644
index 0000000..466c11a
--- /dev/null
+++ b/src/main/java/org/mockito/internal/util/collections/HashCodeAndEqualsSafeSet.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 2007 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+package org.mockito.internal.util.collections;
+
+import org.mockito.internal.util.Checks;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+import static java.lang.reflect.Array.*;
+
+/**
+ * hashCode and equals safe hash based set.
+ *
+ * <p>
+ * Useful for holding mocks that have un-stubbable hashCode or equals method,
+ * meaning that in this scenario the real code is always called and will most probably
+ * cause an {@link NullPointerException}.
+ * </p>
+ * <p>
+ * This collection wraps the mock in an augmented type {@link HashCodeAndEqualsMockWrapper}
+ * that have his own implementation.
+ * </p>
+ *
+ * @see HashCodeAndEqualsMockWrapper
+ */
+public class HashCodeAndEqualsSafeSet implements Set<Object> {
+
+ private final HashSet<HashCodeAndEqualsMockWrapper> backingHashSet = new HashSet<HashCodeAndEqualsMockWrapper>();
+
+ public Iterator<Object> iterator() {
+ return new Iterator<Object>() {
+ private final Iterator<HashCodeAndEqualsMockWrapper> iterator = backingHashSet.iterator();
+
+ public boolean hasNext() {
+ return iterator.hasNext();
+ }
+
+ public Object next() {
+ return iterator.next().get();
+ }
+
+ public void remove() {
+ iterator.remove();
+ }
+ };
+ }
+
+ public int size() {
+ return backingHashSet.size();
+ }
+
+ public boolean isEmpty() {
+ return backingHashSet.isEmpty();
+ }
+
+ public boolean contains(Object mock) {
+ return backingHashSet.contains(HashCodeAndEqualsMockWrapper.of(mock));
+ }
+
+ public boolean add(Object mock) {
+ return backingHashSet.add(HashCodeAndEqualsMockWrapper.of(mock));
+ }
+
+ public boolean remove(Object mock) {
+ return backingHashSet.remove(HashCodeAndEqualsMockWrapper.of(mock));
+ }
+
+ public void clear() {
+ backingHashSet.clear();
+ }
+
+ @Override public Object clone() throws CloneNotSupportedException {
+ throw new CloneNotSupportedException();
+ }
+
+ @Override public boolean equals(Object o) {
+ if (!(o instanceof HashCodeAndEqualsSafeSet)) {
+ return false;
+ }
+ HashCodeAndEqualsSafeSet that = (HashCodeAndEqualsSafeSet) o;
+ return backingHashSet.equals(that.backingHashSet);
+ }
+
+ @Override public int hashCode() {
+ return backingHashSet.hashCode();
+ }
+
+ public Object[] toArray() {
+ return unwrapTo(new Object[size()]);
+ }
+
+ private <T> T[] unwrapTo(T[] array) {
+ Iterator<Object> iterator = iterator();
+ for (int i = 0, objectsLength = array.length; i < objectsLength; i++) {
+ if (iterator.hasNext()) {
+ array[i] = (T) iterator.next();
+ }
+ }
+ return array;
+ }
+
+
+ public <T> T[] toArray(T[] typedArray) {
+ T[] array = typedArray.length >= size() ? typedArray :
+ (T[]) newInstance(typedArray.getClass().getComponentType(), size());
+ return unwrapTo(array);
+ }
+
+ public boolean removeAll(Collection<?> mocks) {
+ return backingHashSet.removeAll(asWrappedMocks(mocks));
+ }
+
+ public boolean containsAll(Collection<?> mocks) {
+ return backingHashSet.containsAll(asWrappedMocks(mocks));
+ }
+
+ public boolean addAll(Collection<?> mocks) {
+ return backingHashSet.addAll(asWrappedMocks(mocks));
+ }
+
+ public boolean retainAll(Collection<?> mocks) {
+ return backingHashSet.retainAll(asWrappedMocks(mocks));
+ }
+
+ private HashSet<HashCodeAndEqualsMockWrapper> asWrappedMocks(Collection<?> mocks) {
+ Checks.checkNotNull(mocks, "Passed collection should notify() be null");
+ HashSet<HashCodeAndEqualsMockWrapper> hashSet = new HashSet<HashCodeAndEqualsMockWrapper>();
+ for (Object mock : mocks) {
+ assert ! (mock instanceof HashCodeAndEqualsMockWrapper) : "WRONG";
+ hashSet.add(HashCodeAndEqualsMockWrapper.of(mock));
+ }
+ return hashSet;
+ }
+
+ @Override public String toString() {
+ return backingHashSet.toString();
+ }
+
+ public static HashCodeAndEqualsSafeSet of(Object... mocks) {
+ return of(Arrays.asList(mocks));
+ }
+
+ public static HashCodeAndEqualsSafeSet of(Iterable<Object> objects) {
+ HashCodeAndEqualsSafeSet hashCodeAndEqualsSafeSet = new HashCodeAndEqualsSafeSet();
+ if (objects != null) {
+ for (Object mock : objects) {
+ hashCodeAndEqualsSafeSet.add(mock);
+ }
+ }
+ return hashCodeAndEqualsSafeSet;
+ }
+}
diff --git a/src/main/java/org/mockito/internal/util/collections/IdentitySet.java b/src/main/java/org/mockito/internal/util/collections/IdentitySet.java
new file mode 100644
index 0000000..661d609
--- /dev/null
+++ b/src/main/java/org/mockito/internal/util/collections/IdentitySet.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2007 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+package org.mockito.internal.util.collections;
+
+import java.util.LinkedList;
+
+@SuppressWarnings("unchecked")
+public class IdentitySet {
+
+ LinkedList list = new LinkedList();
+
+ public boolean contains(Object o) {
+ for(Object existing:list) {
+ if (existing == o) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public void add(Object o) {
+ list.add(o);
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/mockito/internal/util/collections/Iterables.java b/src/main/java/org/mockito/internal/util/collections/Iterables.java
new file mode 100644
index 0000000..9aba368
--- /dev/null
+++ b/src/main/java/org/mockito/internal/util/collections/Iterables.java
@@ -0,0 +1,22 @@
+package org.mockito.internal.util.collections;
+
+import java.util.Enumeration;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * Utilities for Iterables
+ */
+public class Iterables {
+
+ /**
+ * Converts enumeration into iterable
+ */
+ public static <T> Iterable<T> toIterable(Enumeration<T> in) {
+ List<T> out = new LinkedList<T>();
+ while(in.hasMoreElements()) {
+ out.add(in.nextElement());
+ }
+ return out;
+ }
+}
diff --git a/src/main/java/org/mockito/internal/util/collections/ListUtil.java b/src/main/java/org/mockito/internal/util/collections/ListUtil.java
new file mode 100644
index 0000000..b426bb2
--- /dev/null
+++ b/src/main/java/org/mockito/internal/util/collections/ListUtil.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2007 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+
+package org.mockito.internal.util.collections;
+
+import java.util.Collection;
+import java.util.LinkedList;
+
+public class ListUtil {
+
+ public static <T> LinkedList<T> filter(Collection<T> collection, Filter<T> filter) {
+ LinkedList<T> filtered = new LinkedList<T>();
+ for (T t : collection) {
+ if (!filter.isOut(t)) {
+ filtered.add(t);
+ }
+ }
+ return filtered;
+ }
+
+ public interface Filter<T> {
+ boolean isOut(T object);
+ }
+}
diff --git a/src/main/java/org/mockito/internal/util/collections/Sets.java b/src/main/java/org/mockito/internal/util/collections/Sets.java
new file mode 100644
index 0000000..6095549
--- /dev/null
+++ b/src/main/java/org/mockito/internal/util/collections/Sets.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2007 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+package org.mockito.internal.util.collections;
+
+
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+import static java.util.Arrays.asList;
+
+public abstract class Sets {
+ public static Set<Object> newMockSafeHashSet(Iterable<Object> mocks) {
+ return HashCodeAndEqualsSafeSet.of(mocks);
+ }
+
+ public static Set<Object> newMockSafeHashSet(Object... mocks) {
+ return HashCodeAndEqualsSafeSet.of(mocks);
+ }
+
+ public static IdentitySet newIdentitySet() {
+ return new IdentitySet();
+ }
+
+ public static <T> Set<T> newSet(T ... elements) {
+ if (elements == null) {
+ throw new IllegalArgumentException("Expected an array of elements (or empty array) but received a null.");
+ }
+ return new LinkedHashSet<T>(asList(elements));
+ }
+}
diff --git a/src/main/java/org/mockito/internal/util/io/IOUtil.java b/src/main/java/org/mockito/internal/util/io/IOUtil.java
new file mode 100644
index 0000000..2b90c2c
--- /dev/null
+++ b/src/main/java/org/mockito/internal/util/io/IOUtil.java
@@ -0,0 +1,71 @@
+package org.mockito.internal.util.io;
+
+import org.mockito.exceptions.base.MockitoException;
+
+import java.io.*;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * IO utils. A bit of reinventing the wheel but we don't want extra dependencies at this stage and we want to be java.
+ */
+public class IOUtil {
+
+ /**
+ * Writes text to file
+ */
+ public static void writeText(String text, File output) {
+ PrintWriter pw = null;
+ try {
+ pw = new PrintWriter(new FileWriter(output));
+ pw.write(text);
+ } catch (Exception e) {
+ throw new MockitoException("Problems writing text to file: " + output, e);
+ } finally {
+ close(pw);
+ }
+ }
+
+ public static Collection<String> readLines(InputStream is) {
+ List<String> out = new LinkedList<String>();
+ BufferedReader r = new BufferedReader(new InputStreamReader(is));
+ String line;
+ try {
+ while((line = r.readLine()) != null) {
+ out.add(line);
+ }
+ } catch (IOException e) {
+ throw new MockitoException("Problems reading from: " + is, e);
+ }
+ return out;
+ }
+
+ /**
+ * Closes the target. Does nothing when target is null. Is silent.
+ *
+ * @param closeable the target, may be null
+ */
+ public static void closeQuietly(Closeable closeable) {
+ try {
+ close(closeable);
+ } catch (MockitoException ignored) {
+ //ignore, for backwards compatibility
+ }
+ }
+
+ /**
+ * Closes the target. Does nothing when target is null. Is not silent and exceptions are rethrown.
+ *
+ * @param closeable the target, may be null
+ */
+ public static void close(Closeable closeable) {
+ if (closeable != null) {
+ try {
+ closeable.close();
+ } catch (IOException e) {
+ throw new MockitoException("Problems closing stream: " + closeable, e);
+ }
+ }
+ }
+}
diff --git a/src/main/java/org/mockito/internal/util/junit/JUnitFailureHacker.java b/src/main/java/org/mockito/internal/util/junit/JUnitFailureHacker.java
new file mode 100644
index 0000000..debc871
--- /dev/null
+++ b/src/main/java/org/mockito/internal/util/junit/JUnitFailureHacker.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2007 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+package org.mockito.internal.util.junit;
+
+import org.junit.runner.notification.Failure;
+import org.mockito.internal.exceptions.ExceptionIncludingMockitoWarnings;
+import org.mockito.internal.util.reflection.Whitebox;
+
+public class JUnitFailureHacker {
+
+ public void appendWarnings(Failure failure, String warnings) {
+ if (isEmpty(warnings)) {
+ return;
+ }
+ //TODO: this has to protect the use in case jUnit changes and this internal state logic fails
+ Throwable throwable = (Throwable) Whitebox.getInternalState(failure, "fThrownException");
+
+ String newMessage = "contains both: actual test failure *and* Mockito warnings.\n" +
+ warnings + "\n *** The actual failure is because of: ***\n";
+
+ ExceptionIncludingMockitoWarnings e = new ExceptionIncludingMockitoWarnings(newMessage, throwable);
+ e.setStackTrace(throwable.getStackTrace());
+ Whitebox.setInternalState(failure, "fThrownException", e);
+ }
+
+ private boolean isEmpty(String warnings) {
+ return warnings == null || "".equals(warnings); // isEmpty() is in JDK 6+
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/mockito/internal/util/package.html b/src/main/java/org/mockito/internal/util/package.html
new file mode 100644
index 0000000..b86ca1c
--- /dev/null
+++ b/src/main/java/org/mockito/internal/util/package.html
@@ -0,0 +1,8 @@
+<!--
+ ~ Copyright (c) 2007 Mockito contributors
+ ~ This program is made available under the terms of the MIT License.
+ -->
+
+<body>
+Static utils
+</body> \ No newline at end of file
diff --git a/src/main/java/org/mockito/internal/util/reflection/AccessibilityChanger.java b/src/main/java/org/mockito/internal/util/reflection/AccessibilityChanger.java
new file mode 100644
index 0000000..a1d59c3
--- /dev/null
+++ b/src/main/java/org/mockito/internal/util/reflection/AccessibilityChanger.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2007 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+package org.mockito.internal.util.reflection;
+
+import java.lang.reflect.AccessibleObject;
+
+public class AccessibilityChanger {
+
+ private Boolean wasAccessible = null;
+
+ /**
+ * safely disables access
+ */
+ public void safelyDisableAccess(AccessibleObject accessibleObject) {
+ assert wasAccessible != null : "accessibility info shall not be null";
+ try {
+ accessibleObject.setAccessible(wasAccessible);
+ } catch (Throwable t) {
+ //ignore
+ }
+ }
+
+ /**
+ * changes the accessibleObject accessibility and returns true if accessibility was changed
+ */
+ public void enableAccess(AccessibleObject accessibleObject) {
+ wasAccessible = accessibleObject.isAccessible();
+ accessibleObject.setAccessible(true);
+ }
+}
diff --git a/src/main/java/org/mockito/internal/util/reflection/BeanPropertySetter.java b/src/main/java/org/mockito/internal/util/reflection/BeanPropertySetter.java
new file mode 100644
index 0000000..082231d
--- /dev/null
+++ b/src/main/java/org/mockito/internal/util/reflection/BeanPropertySetter.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2007 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+package org.mockito.internal.util.reflection;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Locale;
+
+/**
+ * This utility class will call the setter of the property to inject a new value.
+ */
+public class BeanPropertySetter {
+
+ private static final String SET_PREFIX = "set";
+
+ private final Object target;
+ private final boolean reportNoSetterFound;
+ private final Field field;
+
+ /**
+ * New BeanPropertySetter
+ * @param target The target on which the setter must be invoked
+ * @param propertyField The field that should be accessed with the setter
+ * @param reportNoSetterFound Allow the set method to raise an Exception if the setter cannot be found
+ */
+ public BeanPropertySetter(final Object target, final Field propertyField, boolean reportNoSetterFound) {
+ this.field = propertyField;
+ this.target = target;
+ this.reportNoSetterFound = reportNoSetterFound;
+ }
+
+ /**
+ * New BeanPropertySetter that don't report failure
+ * @param target The target on which the setter must be invoked
+ * @param propertyField The propertyField that must be accessed through a setter
+ */
+ public BeanPropertySetter(final Object target, final Field propertyField) {
+ this(target, propertyField, false);
+ }
+
+ /**
+ * Set the value to the property represented by this {@link BeanPropertySetter}
+ * @param value the new value to pass to the property setter
+ * @return <code>true</code> if the value has been injected, <code>false</code> otherwise
+ * @throws RuntimeException Can be thrown if the setter threw an exception, if the setter is not accessible
+ * or, if <code>reportNoSetterFound</code> and setter could not be found.
+ */
+ public boolean set(final Object value) {
+
+ AccessibilityChanger changer = new AccessibilityChanger();
+ Method writeMethod = null;
+ try {
+ writeMethod = target.getClass().getMethod(setterName(field.getName()), field.getType());
+
+ changer.enableAccess(writeMethod);
+ writeMethod.invoke(target, value);
+ return true;
+ } catch (InvocationTargetException e) {
+ throw new RuntimeException("Setter '" + writeMethod + "' of '" + target + "' with value '" + value + "' threw exception : '" + e.getTargetException() + "'", e);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException("Access not authorized on field '" + field + "' of object '" + target + "' with value: '" + value + "'", e);
+ } catch (NoSuchMethodException e) {
+ reportNoSetterFound();
+ } finally {
+ if(writeMethod != null) {
+ changer.safelyDisableAccess(writeMethod);
+ }
+ }
+
+ reportNoSetterFound();
+ return false;
+ }
+
+ /**
+ * Retrieve the setter name from the field name.
+ *
+ * <p>Implementation is based on the code of {@link java.beans.Introspector}.</p>
+ *
+ * @param fieldName the Field name
+ * @return Setter name.
+ */
+ private String setterName(String fieldName) {
+ return new StringBuilder(SET_PREFIX)
+ .append(fieldName.substring(0, 1).toUpperCase(Locale.ENGLISH))
+ .append(fieldName.substring(1))
+ .toString();
+ }
+
+ private void reportNoSetterFound() {
+ if(reportNoSetterFound) {
+ throw new RuntimeException("Problems setting value on object: [" + target + "] for property : [" + field.getName() + "], setter not found");
+ }
+ }
+
+}
diff --git a/src/main/java/org/mockito/internal/util/reflection/Constructors.java b/src/main/java/org/mockito/internal/util/reflection/Constructors.java
new file mode 100644
index 0000000..909de8c
--- /dev/null
+++ b/src/main/java/org/mockito/internal/util/reflection/Constructors.java
@@ -0,0 +1,20 @@
+package org.mockito.internal.util.reflection;
+
+import java.lang.reflect.Constructor;
+
+public abstract class Constructors {
+
+ /**
+ * Returns the no arg constructor of the type if any.
+ *
+ * @param classToMock The type to look for a no-arg constructor
+ * @return The no-arg constructor or null if none is declared.
+ */
+ public static Constructor<?> noArgConstructorOf(Class<?> classToMock) {
+ try {
+ return classToMock.getDeclaredConstructor();
+ } catch (NoSuchMethodException e) {
+ return null;
+ }
+ }
+}
diff --git a/src/main/java/org/mockito/internal/util/reflection/FieldCopier.java b/src/main/java/org/mockito/internal/util/reflection/FieldCopier.java
new file mode 100644
index 0000000..bc6bcff
--- /dev/null
+++ b/src/main/java/org/mockito/internal/util/reflection/FieldCopier.java
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2007 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+package org.mockito.internal.util.reflection;
+
+import java.lang.reflect.Field;
+
+public class FieldCopier {
+
+ public <T> void copyValue(T from, T to, Field field) throws IllegalAccessException {
+ Object value = field.get(from);
+ field.set(to, value);
+ }
+}
diff --git a/src/main/java/org/mockito/internal/util/reflection/FieldInitializationReport.java b/src/main/java/org/mockito/internal/util/reflection/FieldInitializationReport.java
new file mode 100644
index 0000000..9a0b778
--- /dev/null
+++ b/src/main/java/org/mockito/internal/util/reflection/FieldInitializationReport.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2007 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+
+package org.mockito.internal.util.reflection;
+
+/**
+ * Report on field initialization
+ */
+public class FieldInitializationReport {
+ private final Object fieldInstance;
+ private final boolean wasInitialized;
+ private final boolean wasInitializedUsingConstructorArgs;
+
+ public FieldInitializationReport(Object fieldInstance, boolean wasInitialized, boolean wasInitializedUsingConstructorArgs) {
+ this.fieldInstance = fieldInstance;
+ this.wasInitialized = wasInitialized;
+ this.wasInitializedUsingConstructorArgs = wasInitializedUsingConstructorArgs;
+ }
+
+ /**
+ * Returns the actual field instance.
+ *
+ * @return the actual instance
+ */
+ public Object fieldInstance() {
+ return fieldInstance;
+ }
+
+ /**
+ * Indicate wether the field was created during the process or not.
+ *
+ * @return <code>true</code> if created, <code>false</code> if the field did already hold an instance.
+ */
+ public boolean fieldWasInitialized() {
+ return wasInitialized;
+ }
+
+ /**
+ * Indicate wether the field was created using constructor args.
+ *
+ * @return <code>true</code> if field was created using constructor parameters.
+ */
+ public boolean fieldWasInitializedUsingContructorArgs() {
+ return wasInitializedUsingConstructorArgs;
+ }
+
+ /**
+ * Returns the class of the actual instance in the field.
+ *
+ * @return Class of the instance
+ */
+ public Class<?> fieldClass() {
+ return fieldInstance != null ? fieldInstance.getClass() : null;
+ }
+}
+
diff --git a/src/main/java/org/mockito/internal/util/reflection/FieldInitializer.java b/src/main/java/org/mockito/internal/util/reflection/FieldInitializer.java
new file mode 100644
index 0000000..ec4b68e
--- /dev/null
+++ b/src/main/java/org/mockito/internal/util/reflection/FieldInitializer.java
@@ -0,0 +1,291 @@
+/*
+ * Copyright (c) 2007 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+package org.mockito.internal.util.reflection;
+
+import org.mockito.exceptions.base.MockitoException;
+import org.mockito.internal.util.MockUtil;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Modifier;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * Initialize a field with type instance if a default constructor can be found.
+ *
+ * <p>
+ * If the given field is already initialized, then <strong>the actual instance is returned</strong>.
+ * This initializer doesn't work with inner classes, local classes, interfaces or abstract types.
+ * </p>
+ *
+ */
+public class FieldInitializer {
+
+ private final Object fieldOwner;
+ private final Field field;
+ private final ConstructorInstantiator instantiator;
+
+
+ /**
+ * Prepare initializer with the given field on the given instance.
+ *
+ * <p>
+ * This constructor fail fast if the field type cannot be handled.
+ * </p>
+ *
+ * @param fieldOwner Instance of the test.
+ * @param field Field to be initialize.
+ */
+ public FieldInitializer(Object fieldOwner, Field field) {
+ this(fieldOwner, field, new NoArgConstructorInstantiator(fieldOwner, field));
+ }
+
+ /**
+ * Prepare initializer with the given field on the given instance.
+ *
+ * <p>
+ * This constructor fail fast if the field type cannot be handled.
+ * </p>
+ *
+ * @param fieldOwner Instance of the test.
+ * @param field Field to be initialize.
+ * @param argResolver Constructor parameters resolver
+ */
+ public FieldInitializer(Object fieldOwner, Field field, ConstructorArgumentResolver argResolver) {
+ this(fieldOwner, field, new ParameterizedConstructorInstantiator(fieldOwner, field, argResolver));
+ }
+
+ private FieldInitializer(Object fieldOwner, Field field, ConstructorInstantiator instantiator) {
+ if(new FieldReader(fieldOwner, field).isNull()) {
+ checkNotLocal(field);
+ checkNotInner(field);
+ checkNotInterface(field);
+ checkNotAbstract(field);
+ }
+ this.fieldOwner = fieldOwner;
+ this.field = field;
+ this.instantiator = instantiator;
+ }
+
+ /**
+ * Initialize field if not initialized and return the actual instance.
+ *
+ * @return Actual field instance.
+ */
+ public FieldInitializationReport initialize() {
+ final AccessibilityChanger changer = new AccessibilityChanger();
+ changer.enableAccess(field);
+
+ try {
+ return acquireFieldInstance();
+ } catch(IllegalAccessException e) {
+ throw new MockitoException("Problems initializing field '" + field.getName() + "' of type '" + field.getType().getSimpleName() + "'", e);
+ } finally {
+ changer.safelyDisableAccess(field);
+ }
+ }
+
+ private void checkNotLocal(Field field) {
+ if(field.getType().isLocalClass()) {
+ throw new MockitoException("the type '" + field.getType().getSimpleName() + "' is a local class.");
+ }
+ }
+
+ private void checkNotInner(Field field) {
+ if(field.getType().isMemberClass() && !Modifier.isStatic(field.getType().getModifiers())) {
+ throw new MockitoException("the type '" + field.getType().getSimpleName() + "' is an inner class.");
+ }
+ }
+
+ private void checkNotInterface(Field field) {
+ if(field.getType().isInterface()) {
+ throw new MockitoException("the type '" + field.getType().getSimpleName() + "' is an interface.");
+ }
+ }
+
+ private void checkNotAbstract(Field field) {
+ if(Modifier.isAbstract(field.getType().getModifiers())) {
+ throw new MockitoException("the type '" + field.getType().getSimpleName() + " is an abstract class.");
+ }
+ }
+
+ private FieldInitializationReport acquireFieldInstance() throws IllegalAccessException {
+ Object fieldInstance = field.get(fieldOwner);
+ if(fieldInstance != null) {
+ return new FieldInitializationReport(fieldInstance, false, false);
+ }
+
+ return instantiator.instantiate();
+ }
+
+ /**
+ * Represents the strategy used to resolve actual instances
+ * to be given to a constructor given the argument types.
+ */
+ public interface ConstructorArgumentResolver {
+
+ /**
+ * Try to resolve instances from types.
+ *
+ * <p>
+ * Checks on the real argument type or on the correct argument number
+ * will happen during the field initialization {@link FieldInitializer#initialize()}.
+ * I.e the only responsibility of this method, is to provide instances <strong>if possible</strong>.
+ * </p>
+ *
+ * @param argTypes Constructor argument types, should not be null.
+ * @return The argument instances to be given to the constructor, should not be null.
+ */
+ Object[] resolveTypeInstances(Class<?>... argTypes);
+ }
+
+ private interface ConstructorInstantiator {
+ FieldInitializationReport instantiate();
+ }
+
+ /**
+ * Constructor instantiating strategy for no-arg constructor.
+ *
+ * <p>
+ * If a no-arg constructor can be found then the instance is created using
+ * this constructor.
+ * Otherwise a technical MockitoException is thrown.
+ * </p>
+ */
+ static class NoArgConstructorInstantiator implements ConstructorInstantiator {
+ private final Object testClass;
+ private final Field field;
+
+ /**
+ * Internal, checks are done by FieldInitializer.
+ * Fields are assumed to be accessible.
+ */
+ NoArgConstructorInstantiator(Object testClass, Field field) {
+ this.testClass = testClass;
+ this.field = field;
+ }
+
+ public FieldInitializationReport instantiate() {
+ final AccessibilityChanger changer = new AccessibilityChanger();
+ Constructor<?> constructor = null;
+ try {
+ constructor = field.getType().getDeclaredConstructor();
+ changer.enableAccess(constructor);
+
+ final Object[] noArg = new Object[0];
+ Object newFieldInstance = constructor.newInstance(noArg);
+ new FieldSetter(testClass, field).set(newFieldInstance);
+
+ return new FieldInitializationReport(field.get(testClass), true, false);
+ } catch (NoSuchMethodException e) {
+ throw new MockitoException("the type '" + field.getType().getSimpleName() + "' has no default constructor", e);
+ } catch (InvocationTargetException e) {
+ throw new MockitoException("the default constructor of type '" + field.getType().getSimpleName() + "' has raised an exception (see the stack trace for cause): " + e.getTargetException().toString(), e);
+ } catch (InstantiationException e) {
+ throw new MockitoException("InstantiationException (see the stack trace for cause): " + e.toString(), e);
+ } catch (IllegalAccessException e) {
+ throw new MockitoException("IllegalAccessException (see the stack trace for cause): " + e.toString(), e);
+ } finally {
+ if(constructor != null) {
+ changer.safelyDisableAccess(constructor);
+ }
+ }
+ }
+ }
+
+ /**
+ * Constructor instantiating strategy for parameterized constructors.
+ *
+ * <p>
+ * Choose the constructor with the highest number of parameters, then
+ * call the ConstructorArgResolver to get actual argument instances.
+ * If the argResolver fail, then a technical MockitoException is thrown is thrown.
+ * Otherwise the instance is created with the resolved arguments.
+ * </p>
+ */
+ static class ParameterizedConstructorInstantiator implements ConstructorInstantiator {
+ private final Object testClass;
+ private final Field field;
+ private final ConstructorArgumentResolver argResolver;
+ private final MockUtil mockUtil = new MockUtil();
+ private final Comparator<Constructor<?>> byParameterNumber = new Comparator<Constructor<?>>() {
+ public int compare(Constructor<?> constructorA, Constructor<?> constructorB) {
+ int argLengths = constructorB.getParameterTypes().length - constructorA.getParameterTypes().length;
+ if (argLengths == 0) {
+ int constructorAMockableParamsSize = countMockableParams(constructorA);
+ int constructorBMockableParamsSize = countMockableParams(constructorB);
+ return constructorBMockableParamsSize - constructorAMockableParamsSize;
+ }
+ return argLengths;
+ }
+
+ private int countMockableParams(Constructor<?> constructor) {
+ int constructorMockableParamsSize = 0;
+ for (Class<?> aClass : constructor.getParameterTypes()) {
+ if(mockUtil.isTypeMockable(aClass)){
+ constructorMockableParamsSize++;
+ }
+ }
+ return constructorMockableParamsSize;
+ }
+ };
+
+ /**
+ * Internal, checks are done by FieldInitializer.
+ * Fields are assumed to be accessible.
+ */
+ ParameterizedConstructorInstantiator(Object testClass, Field field, ConstructorArgumentResolver argumentResolver) {
+ this.testClass = testClass;
+ this.field = field;
+ this.argResolver = argumentResolver;
+ }
+
+ public FieldInitializationReport instantiate() {
+ final AccessibilityChanger changer = new AccessibilityChanger();
+ Constructor<?> constructor = null;
+ try {
+ constructor = biggestConstructor(field.getType());
+ changer.enableAccess(constructor);
+
+ final Object[] args = argResolver.resolveTypeInstances(constructor.getParameterTypes());
+ Object newFieldInstance = constructor.newInstance(args);
+ new FieldSetter(testClass, field).set(newFieldInstance);
+
+ return new FieldInitializationReport(field.get(testClass), false, true);
+ } catch (IllegalArgumentException e) {
+ throw new MockitoException("internal error : argResolver provided incorrect types for constructor " + constructor + " of type " + field.getType().getSimpleName(), e);
+ } catch (InvocationTargetException e) {
+ throw new MockitoException("the constructor of type '" + field.getType().getSimpleName() + "' has raised an exception (see the stack trace for cause): " + e.getTargetException().toString(), e);
+ } catch (InstantiationException e) {
+ throw new MockitoException("InstantiationException (see the stack trace for cause): " + e.toString(), e);
+ } catch (IllegalAccessException e) {
+ throw new MockitoException("IllegalAccessException (see the stack trace for cause): " + e.toString(), e);
+ } finally {
+ if(constructor != null) {
+ changer.safelyDisableAccess(constructor);
+ }
+ }
+ }
+
+ private void checkParameterized(Constructor<?> constructor, Field field) {
+ if(constructor.getParameterTypes().length == 0) {
+ throw new MockitoException("the field " + field.getName() + " of type " + field.getType() + " has no parameterized constructor");
+ }
+ }
+
+ private Constructor<?> biggestConstructor(Class<?> clazz) {
+ final List<Constructor<?>> constructors = Arrays.asList(clazz.getDeclaredConstructors());
+ Collections.sort(constructors, byParameterNumber);
+
+ Constructor<?> constructor = constructors.get(0);
+ checkParameterized(constructor, field);
+ return constructor;
+ }
+ }
+}
diff --git a/src/main/java/org/mockito/internal/util/reflection/FieldReader.java b/src/main/java/org/mockito/internal/util/reflection/FieldReader.java
new file mode 100644
index 0000000..49a62e7
--- /dev/null
+++ b/src/main/java/org/mockito/internal/util/reflection/FieldReader.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2007 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+package org.mockito.internal.util.reflection;
+
+import org.mockito.exceptions.base.MockitoException;
+
+import java.lang.reflect.Field;
+
+public class FieldReader {
+
+ final Object target;
+ final Field field;
+ final AccessibilityChanger changer = new AccessibilityChanger();
+
+ public FieldReader(Object target, Field field) {
+ this.target = target;
+ this.field = field;
+ changer.enableAccess(field);
+ }
+
+ public boolean isNull() {
+ return read() == null;
+ }
+
+ public Object read() {
+ try {
+ return field.get(target);
+ } catch (Exception e) {
+ throw new MockitoException("Cannot read state from field: " + field + ", on instance: " + target);
+ }
+ }
+}
diff --git a/src/main/java/org/mockito/internal/util/reflection/FieldSetter.java b/src/main/java/org/mockito/internal/util/reflection/FieldSetter.java
new file mode 100644
index 0000000..5972767
--- /dev/null
+++ b/src/main/java/org/mockito/internal/util/reflection/FieldSetter.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2007 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+package org.mockito.internal.util.reflection;
+
+import java.lang.reflect.Field;
+
+public class FieldSetter {
+
+ private final Object target;
+ private final Field field;
+
+ public FieldSetter(Object target, Field field) {
+ this.target = target;
+ this.field = field;
+ }
+
+ public void set(Object value) {
+ AccessibilityChanger changer = new AccessibilityChanger();
+ changer.enableAccess(field);
+ try {
+ field.set(target, value);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException("Access not authorized on field '" + field + "' of object '" + target + "' with value: '" + value + "'", e);
+ } catch (IllegalArgumentException e) {
+ throw new RuntimeException("Wrong argument on field '" + field + "' of object '" + target + "' with value: '" + value + "', \n" +
+ "reason : " + e.getMessage(), e);
+ }
+ changer.safelyDisableAccess(field);
+ }
+}
diff --git a/src/main/java/org/mockito/internal/util/reflection/Fields.java b/src/main/java/org/mockito/internal/util/reflection/Fields.java
new file mode 100644
index 0000000..327dac0
--- /dev/null
+++ b/src/main/java/org/mockito/internal/util/reflection/Fields.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2007 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+package org.mockito.internal.util.reflection;
+
+import org.mockito.internal.util.Checks;
+import org.mockito.internal.util.collections.ListUtil;
+import org.mockito.internal.util.collections.ListUtil.Filter;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Small fluent reflection tools to work with fields.
+ *
+ * Code is very new and might need rework.
+ */
+public abstract class Fields {
+
+ /**
+ * Instance fields declared in the class and superclasses of the given instance.
+ *
+ * @param instance Instance from which declared fields will be retrieved.
+ * @return InstanceFields of this object instance.
+ */
+ public static InstanceFields allDeclaredFieldsOf(Object instance) {
+ List<InstanceField> instanceFields = new ArrayList<InstanceField>();
+ for (Class<?> clazz = instance.getClass(); clazz != Object.class; clazz = clazz.getSuperclass()) {
+ instanceFields.addAll(instanceFieldsIn(instance, clazz.getDeclaredFields()));
+ }
+ return new InstanceFields(instance, instanceFields);
+ }
+
+ /**
+ * Instance fields declared in the class of the given instance.
+ *
+ * @param instance Instance from which declared fields will be retrieved.
+ * @return InstanceFields of this object instance.
+ */
+ public static InstanceFields declaredFieldsOf(Object instance) {
+ List<InstanceField> instanceFields = new ArrayList<InstanceField>();
+ instanceFields.addAll(instanceFieldsIn(instance, instance.getClass().getDeclaredFields()));
+ return new InstanceFields(instance, instanceFields);
+ }
+
+ private static List<InstanceField> instanceFieldsIn(Object instance, Field[] fields) {
+ List<InstanceField> instanceDeclaredFields = new ArrayList<InstanceField>();
+ for (Field field : fields) {
+ InstanceField instanceField = new InstanceField(field, instance);
+ instanceDeclaredFields.add(instanceField);
+ }
+ return instanceDeclaredFields;
+ }
+
+ /**
+ * Accept fields annotated by the given annotations.
+ *
+ * @param annotations Annotation types to check.
+ * @return The filter.
+ */
+ public static Filter<InstanceField> annotatedBy(final Class<? extends Annotation>... annotations) {
+ return new Filter<InstanceField>() {
+ public boolean isOut(InstanceField instanceField) {
+ Checks.checkNotNull(annotations, "Provide at least one annotation class");
+
+ for (Class<? extends Annotation> annotation : annotations) {
+ if(instanceField.isAnnotatedBy(annotation)) {
+ return false;
+ }
+ }
+ return true;
+ }
+ };
+ }
+
+ /**
+ * Accept fields with non null value.
+ *
+ * @return The filter.
+ */
+ private static Filter<InstanceField> nullField() {
+ return new Filter<InstanceField>() {
+ public boolean isOut(InstanceField instanceField) {
+ return instanceField.isNull();
+ }
+ };
+ }
+
+ public static class InstanceFields {
+ private final Object instance;
+
+ private final List<InstanceField> instanceFields;
+
+ public InstanceFields(Object instance, List<InstanceField> instanceFields) {
+ this.instance = instance;
+ this.instanceFields = instanceFields;
+ }
+
+ public InstanceFields filter(Filter<InstanceField> withFilter) {
+ return new InstanceFields(instance, ListUtil.filter(instanceFields, withFilter));
+ }
+
+ public InstanceFields notNull() {
+ return filter(nullField());
+ }
+
+ public List<InstanceField> instanceFields() {
+ return new ArrayList<InstanceField>(instanceFields);
+ }
+
+ public List<Object> assignedValues() {
+ List<Object> values = new ArrayList<Object>(instanceFields.size());
+ for (InstanceField instanceField : instanceFields) {
+ values.add(instanceField.read());
+ }
+ return values;
+ }
+
+ public List<String> names() {
+ List<String> fieldNames = new ArrayList<String>(instanceFields.size());
+ for (InstanceField instanceField : instanceFields) {
+ fieldNames.add(instanceField.name());
+ }
+ return fieldNames;
+ }
+ }
+}
diff --git a/src/main/java/org/mockito/internal/util/reflection/GenericMaster.java b/src/main/java/org/mockito/internal/util/reflection/GenericMaster.java
new file mode 100644
index 0000000..2b07930
--- /dev/null
+++ b/src/main/java/org/mockito/internal/util/reflection/GenericMaster.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2007 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+package org.mockito.internal.util.reflection;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+
+@SuppressWarnings("unchecked")
+public class GenericMaster {
+
+ /**
+ * Finds the generic type (parametrized type) of the field. If the field is not generic it returns Object.class.
+ *
+ * @param field
+ */
+ public Class getGenericType(Field field) {
+ Type generic = field.getGenericType();
+ if (generic instanceof ParameterizedType) {
+ Type actual = ((ParameterizedType) generic).getActualTypeArguments()[0];
+ if (actual instanceof Class) {
+ return (Class) actual;
+ } else if (actual instanceof ParameterizedType) {
+ //in case of nested generics we don't go deep
+ return (Class) ((ParameterizedType) actual).getRawType();
+ }
+ }
+
+ return Object.class;
+ }
+
+}
diff --git a/src/main/java/org/mockito/internal/util/reflection/GenericMetadataSupport.java b/src/main/java/org/mockito/internal/util/reflection/GenericMetadataSupport.java
new file mode 100644
index 0000000..80bd908
--- /dev/null
+++ b/src/main/java/org/mockito/internal/util/reflection/GenericMetadataSupport.java
@@ -0,0 +1,627 @@
+/*
+ * Copyright (c) 2007 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+package org.mockito.internal.util.reflection;
+
+
+import org.mockito.exceptions.base.MockitoException;
+import org.mockito.internal.util.Checks;
+
+import java.lang.reflect.*;
+import java.util.*;
+
+
+/**
+ * This class can retrieve generic meta-data that the compiler stores on classes
+ * and accessible members.
+ *
+ * <p>
+ * The main idea of this code is to create a Map that will help to resolve return types.
+ * In order to actually work with nested generics, this map will have to be passed along new instances
+ * as a type context.
+ * </p>
+ *
+ * <p>
+ * Hence :
+ * <ul>
+ * <li>A new instance representing the metadata is created using the {@link #inferFrom(Type)} method from a real
+ * <code>Class</code> or from a <code>ParameterizedType</code>, other types are not yet supported.</li>
+ *
+ * <li>Then from this metadata, we can extract meta-data for a generic return type of a method, using
+ * {@link #resolveGenericReturnType(Method)}.</li>
+ * </ul>
+ * </p>
+ *
+ * <p>
+ * For now this code support the following kind of generic declarations :
+ * <pre class="code"><code class="java">
+ * interface GenericsNest&lt;K extends Comparable&lt;K&gt; & Cloneable&gt; extends Map&lt;K, Set&lt;Number&gt;&gt; {
+ * Set&lt;Number&gt; remove(Object key); // override with fixed ParameterizedType
+ * List&lt;? super Integer&gt; returning_wildcard_with_class_lower_bound();
+ * List&lt;? super K&gt; returning_wildcard_with_typeVar_lower_bound();
+ * List&lt;? extends K&gt; returning_wildcard_with_typeVar_upper_bound();
+ * K returningK();
+ * &lt;O extends K&gt; List&lt;O&gt; paramType_with_type_params();
+ * &lt;S, T extends S&gt; T two_type_params();
+ * &lt;O extends K&gt; O typeVar_with_type_params();
+ * Number returningNonGeneric();
+ * }
+ * </code></pre>
+ *
+ * @see #inferFrom(Type)
+ * @see #resolveGenericReturnType(Method)
+ * @see org.mockito.internal.stubbing.defaultanswers.ReturnsDeepStubs
+ */
+public abstract class GenericMetadataSupport {
+
+ // public static MockitoLogger logger = new ConsoleMockitoLogger();
+
+ /**
+ * Represents actual type variables resolved for current class.
+ */
+ protected Map<TypeVariable, Type> contextualActualTypeParameters = new HashMap<TypeVariable, Type>();
+
+
+ protected void registerTypeVariablesOn(Type classType) {
+ if (!(classType instanceof ParameterizedType)) {
+ return;
+ }
+ ParameterizedType parameterizedType = (ParameterizedType) classType;
+ TypeVariable[] typeParameters = ((Class<?>) parameterizedType.getRawType()).getTypeParameters();
+ Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
+ for (int i = 0; i < actualTypeArguments.length; i++) {
+ TypeVariable typeParameter = typeParameters[i];
+ Type actualTypeArgument = actualTypeArguments[i];
+
+ if (actualTypeArgument instanceof WildcardType) {
+ contextualActualTypeParameters.put(typeParameter, boundsOf((WildcardType) actualTypeArgument));
+ } else if (typeParameter != actualTypeArgument) {
+ contextualActualTypeParameters.put(typeParameter, actualTypeArgument);
+ }
+ // logger.log("For '" + parameterizedType + "' found type variable : { '" + typeParameter + "(" + System.identityHashCode(typeParameter) + ")" + "' : '" + actualTypeArgument + "(" + System.identityHashCode(typeParameter) + ")" + "' }");
+ }
+ }
+
+ protected void registerTypeParametersOn(TypeVariable[] typeParameters) {
+ for (TypeVariable type : typeParameters) {
+ registerTypeVariableIfNotPresent(type);
+ }
+ }
+
+ private void registerTypeVariableIfNotPresent(TypeVariable typeVariable) {
+ if (!contextualActualTypeParameters.containsKey(typeVariable)) {
+ contextualActualTypeParameters.put(typeVariable, boundsOf(typeVariable));
+ // logger.log("For '" + typeVariable.getGenericDeclaration() + "' found type variable : { '" + typeVariable + "(" + System.identityHashCode(typeVariable) + ")" + "' : '" + boundsOf(typeVariable) + "' }");
+ }
+ }
+
+ /**
+ * @param typeParameter The TypeVariable parameter
+ * @return A {@link BoundedType} for easy bound information, if first bound is a TypeVariable
+ * then retrieve BoundedType of this TypeVariable
+ */
+ private BoundedType boundsOf(TypeVariable typeParameter) {
+ if (typeParameter.getBounds()[0] instanceof TypeVariable) {
+ return boundsOf((TypeVariable) typeParameter.getBounds()[0]);
+ }
+ return new TypeVarBoundedType(typeParameter);
+ }
+
+ /**
+ * @param wildCard The WildCard type
+ * @return A {@link BoundedType} for easy bound information, if first bound is a TypeVariable
+ * then retrieve BoundedType of this TypeVariable
+ */
+ private BoundedType boundsOf(WildcardType wildCard) {
+ /*
+ * According to JLS(http://docs.oracle.com/javase/specs/jls/se5.0/html/typesValues.html#4.5.1):
+ * - Lower and upper can't coexist: (for instance, this is not allowed: <? extends List<String> & super MyInterface>)
+ * - Multiple bounds are not supported (for instance, this is not allowed: <? extends List<String> & MyInterface>)
+ */
+
+ WildCardBoundedType wildCardBoundedType = new WildCardBoundedType(wildCard);
+ if (wildCardBoundedType.firstBound() instanceof TypeVariable) {
+ return boundsOf((TypeVariable) wildCardBoundedType.firstBound());
+ }
+
+ return wildCardBoundedType;
+ }
+
+
+
+ /**
+ * @return Raw type of the current instance.
+ */
+ public abstract Class<?> rawType();
+
+
+
+ /**
+ * @return Returns extra interfaces <strong>if relevant</strong>, otherwise empty List.
+ */
+ public List<Type> extraInterfaces() {
+ return Collections.emptyList();
+ }
+
+ /**
+ * @return Returns an array with the raw types of {@link #extraInterfaces()} <strong>if relevant</strong>.
+ */
+ public Class<?>[] rawExtraInterfaces() {
+ return new Class[0];
+ }
+
+ /**
+ * @return Returns true if metadata knows about extra-interfaces {@link #extraInterfaces()} <strong>if relevant</strong>.
+ */
+ public boolean hasRawExtraInterfaces() {
+ return rawExtraInterfaces().length > 0;
+ }
+
+
+
+ /**
+ * @return Actual type arguments matching the type variables of the raw type represented by this {@link GenericMetadataSupport} instance.
+ */
+ public Map<TypeVariable, Type> actualTypeArguments() {
+ TypeVariable[] typeParameters = rawType().getTypeParameters();
+ LinkedHashMap<TypeVariable, Type> actualTypeArguments = new LinkedHashMap<TypeVariable, Type>();
+
+ for (TypeVariable typeParameter : typeParameters) {
+
+ Type actualType = getActualTypeArgumentFor(typeParameter);
+
+ actualTypeArguments.put(typeParameter, actualType);
+ // logger.log("For '" + rawType().getCanonicalName() + "' returning explicit TypeVariable : { '" + typeParameter + "(" + System.identityHashCode(typeParameter) + ")" + "' : '" + actualType +"' }");
+ }
+
+ return actualTypeArguments;
+ }
+
+ protected Type getActualTypeArgumentFor(TypeVariable typeParameter) {
+ Type type = this.contextualActualTypeParameters.get(typeParameter);
+ if (type instanceof TypeVariable) {
+ TypeVariable typeVariable = (TypeVariable) type;
+ return getActualTypeArgumentFor(typeVariable);
+ }
+
+ return type;
+ }
+
+
+
+ /**
+ * Resolve current method generic return type to a {@link GenericMetadataSupport}.
+ *
+ * @param method Method to resolve the return type.
+ * @return {@link GenericMetadataSupport} representing this generic return type.
+ */
+ public GenericMetadataSupport resolveGenericReturnType(Method method) {
+ Type genericReturnType = method.getGenericReturnType();
+ // logger.log("Method '" + method.toGenericString() + "' has return type : " + genericReturnType.getClass().getInterfaces()[0].getSimpleName() + " : " + genericReturnType);
+
+ if (genericReturnType instanceof Class) {
+ return new NotGenericReturnTypeSupport(genericReturnType);
+ }
+ if (genericReturnType instanceof ParameterizedType) {
+ return new ParameterizedReturnType(this, method.getTypeParameters(), (ParameterizedType) method.getGenericReturnType());
+ }
+ if (genericReturnType instanceof TypeVariable) {
+ return new TypeVariableReturnType(this, method.getTypeParameters(), (TypeVariable) genericReturnType);
+ }
+
+ throw new MockitoException("Ouch, it shouldn't happen, type '" + genericReturnType.getClass().getCanonicalName() + "' on method : '" + method.toGenericString() + "' is not supported : " + genericReturnType);
+ }
+
+ /**
+ * Create an new instance of {@link GenericMetadataSupport} inferred from a {@link Type}.
+ *
+ * <p>
+ * At the moment <code>type</code> can only be a {@link Class} or a {@link ParameterizedType}, otherwise
+ * it'll throw a {@link MockitoException}.
+ * </p>
+ *
+ * @param type The class from which the {@link GenericMetadataSupport} should be built.
+ * @return The new {@link GenericMetadataSupport}.
+ * @throws MockitoException Raised if type is not a {@link Class} or a {@link ParameterizedType}.
+ */
+ public static GenericMetadataSupport inferFrom(Type type) {
+ Checks.checkNotNull(type, "type");
+ if (type instanceof Class) {
+ return new FromClassGenericMetadataSupport((Class<?>) type);
+ }
+ if (type instanceof ParameterizedType) {
+ return new FromParameterizedTypeGenericMetadataSupport((ParameterizedType) type);
+ }
+
+ throw new MockitoException("Type meta-data for this Type (" + type.getClass().getCanonicalName() + ") is not supported : " + type);
+ }
+
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ //// Below are specializations of GenericMetadataSupport that could handle retrieval of possible Types
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Generic metadata implementation for {@link Class}.
+ *
+ * Offer support to retrieve generic metadata on a {@link Class} by reading type parameters and type variables on
+ * the class and its ancestors and interfaces.
+ */
+ private static class FromClassGenericMetadataSupport extends GenericMetadataSupport {
+ private final Class<?> clazz;
+
+ public FromClassGenericMetadataSupport(Class<?> clazz) {
+ this.clazz = clazz;
+
+ for (Class currentExploredClass = clazz;
+ currentExploredClass != null && currentExploredClass != Object.class;
+ currentExploredClass = superClassOf(currentExploredClass)
+ ) {
+ readActualTypeParametersOnDeclaringClass(currentExploredClass);
+ }
+ }
+
+ private Class superClassOf(Class currentExploredClass) {
+ Type genericSuperclass = currentExploredClass.getGenericSuperclass();
+ if (genericSuperclass instanceof ParameterizedType) {
+ Type rawType = ((ParameterizedType) genericSuperclass).getRawType();
+ return (Class) rawType;
+ }
+ return (Class) genericSuperclass;
+ }
+
+ private void readActualTypeParametersOnDeclaringClass(Class<?> clazz) {
+ registerTypeParametersOn(clazz.getTypeParameters());
+ registerTypeVariablesOn(clazz.getGenericSuperclass());
+ for (Type genericInterface : clazz.getGenericInterfaces()) {
+ registerTypeVariablesOn(genericInterface);
+ }
+ }
+
+ @Override
+ public Class<?> rawType() {
+ return clazz;
+ }
+ }
+
+
+ /**
+ * Generic metadata implementation for "standalone" {@link ParameterizedType}.
+ *
+ * Offer support to retrieve generic metadata on a {@link ParameterizedType} by reading type variables of
+ * the related raw type and declared type variable of this parameterized type.
+ *
+ * This class is not designed to work on ParameterizedType returned by {@link Method#getGenericReturnType()}, as
+ * the ParameterizedType instance return in these cases could have Type Variables that refer to type declaration(s).
+ * That's what meant the "standalone" word at the beginning of the Javadoc.
+ * Instead use {@link ParameterizedReturnType}.
+ */
+ private static class FromParameterizedTypeGenericMetadataSupport extends GenericMetadataSupport {
+ private final ParameterizedType parameterizedType;
+
+ public FromParameterizedTypeGenericMetadataSupport(ParameterizedType parameterizedType) {
+ this.parameterizedType = parameterizedType;
+ readActualTypeParameters();
+ }
+
+ private void readActualTypeParameters() {
+ registerTypeVariablesOn(parameterizedType.getRawType());
+ registerTypeVariablesOn(parameterizedType);
+ }
+
+ @Override
+ public Class<?> rawType() {
+ return (Class<?>) parameterizedType.getRawType();
+ }
+ }
+
+
+ /**
+ * Generic metadata specific to {@link ParameterizedType} returned via {@link Method#getGenericReturnType()}.
+ */
+ private static class ParameterizedReturnType extends GenericMetadataSupport {
+ private final ParameterizedType parameterizedType;
+ private final TypeVariable[] typeParameters;
+
+ public ParameterizedReturnType(GenericMetadataSupport source, TypeVariable[] typeParameters, ParameterizedType parameterizedType) {
+ this.parameterizedType = parameterizedType;
+ this.typeParameters = typeParameters;
+ this.contextualActualTypeParameters = source.contextualActualTypeParameters;
+
+ readTypeParameters();
+ readTypeVariables();
+ }
+
+ private void readTypeParameters() {
+ registerTypeParametersOn(typeParameters);
+ }
+
+ private void readTypeVariables() {
+ registerTypeVariablesOn(parameterizedType);
+ }
+
+ @Override
+ public Class<?> rawType() {
+ return (Class<?>) parameterizedType.getRawType();
+ }
+
+ }
+
+
+ /**
+ * Generic metadata for {@link TypeVariable} returned via {@link Method#getGenericReturnType()}.
+ */
+ private static class TypeVariableReturnType extends GenericMetadataSupport {
+ private final TypeVariable typeVariable;
+ private final TypeVariable[] typeParameters;
+ private Class<?> rawType;
+
+
+
+ public TypeVariableReturnType(GenericMetadataSupport source, TypeVariable[] typeParameters, TypeVariable typeVariable) {
+ this.typeParameters = typeParameters;
+ this.typeVariable = typeVariable;
+ this.contextualActualTypeParameters = source.contextualActualTypeParameters;
+
+ readTypeParameters();
+ readTypeVariables();
+ }
+
+ private void readTypeParameters() {
+ registerTypeParametersOn(typeParameters);
+ }
+
+ private void readTypeVariables() {
+ for (Type type : typeVariable.getBounds()) {
+ registerTypeVariablesOn(type);
+ }
+ registerTypeParametersOn(new TypeVariable[] { typeVariable });
+ registerTypeVariablesOn(getActualTypeArgumentFor(typeVariable));
+ }
+
+ @Override
+ public Class<?> rawType() {
+ if (rawType == null) {
+ rawType = extractRawTypeOf(typeVariable);
+ }
+ return rawType;
+ }
+
+ private Class<?> extractRawTypeOf(Type type) {
+ if (type instanceof Class) {
+ return (Class<?>) type;
+ }
+ if (type instanceof ParameterizedType) {
+ return (Class<?>) ((ParameterizedType) type).getRawType();
+ }
+ if (type instanceof BoundedType) {
+ return extractRawTypeOf(((BoundedType) type).firstBound());
+ }
+ if (type instanceof TypeVariable) {
+ /*
+ * If type is a TypeVariable, then it is needed to gather data elsewhere. Usually TypeVariables are declared
+ * on the class definition, such as such as List<E>.
+ */
+ return extractRawTypeOf(contextualActualTypeParameters.get(type));
+ }
+ throw new MockitoException("Raw extraction not supported for : '" + type + "'");
+ }
+
+ @Override
+ public List<Type> extraInterfaces() {
+ Type type = extractActualBoundedTypeOf(typeVariable);
+ if (type instanceof BoundedType) {
+ return Arrays.asList(((BoundedType) type).interfaceBounds());
+ }
+ if (type instanceof ParameterizedType) {
+ return Collections.singletonList(type);
+ }
+ if (type instanceof Class) {
+ return Collections.emptyList();
+ }
+ throw new MockitoException("Cannot extract extra-interfaces from '" + typeVariable + "' : '" + type + "'");
+ }
+
+ /**
+ * @return Returns an array with the extracted raw types of {@link #extraInterfaces()}.
+ * @see #extractRawTypeOf(java.lang.reflect.Type)
+ */
+ public Class<?>[] rawExtraInterfaces() {
+ List<Type> extraInterfaces = extraInterfaces();
+ List<Class<?>> rawExtraInterfaces = new ArrayList<Class<?>>();
+ for (Type extraInterface : extraInterfaces) {
+ Class<?> rawInterface = extractRawTypeOf(extraInterface);
+ // avoid interface collision with actual raw type (with typevariables, resolution ca be quite aggressive)
+ if(!rawType().equals(rawInterface)) {
+ rawExtraInterfaces.add(rawInterface);
+ }
+ }
+ return rawExtraInterfaces.toArray(new Class[rawExtraInterfaces.size()]);
+ }
+
+ private Type extractActualBoundedTypeOf(Type type) {
+ if (type instanceof TypeVariable) {
+ /*
+ If type is a TypeVariable, then it is needed to gather data elsewhere. Usually TypeVariables are declared
+ on the class definition, such as such as List<E>.
+ */
+ return extractActualBoundedTypeOf(contextualActualTypeParameters.get(type));
+ }
+ if (type instanceof BoundedType) {
+ Type actualFirstBound = extractActualBoundedTypeOf(((BoundedType) type).firstBound());
+ if (!(actualFirstBound instanceof BoundedType)) {
+ return type; // avoid going one step further, ie avoid : O(TypeVar) -> K(TypeVar) -> Some ParamType
+ }
+ return actualFirstBound;
+ }
+ return type; // irrelevant, we don't manage other types as they are not bounded.
+ }
+ }
+
+
+
+ /**
+ * Non-Generic metadata for {@link Class} returned via {@link Method#getGenericReturnType()}.
+ */
+ private static class NotGenericReturnTypeSupport extends GenericMetadataSupport {
+ private final Class<?> returnType;
+
+ public NotGenericReturnTypeSupport(Type genericReturnType) {
+ returnType = (Class<?>) genericReturnType;
+ }
+
+ @Override
+ public Class<?> rawType() {
+ return returnType;
+ }
+ }
+
+
+
+ /**
+ * Type representing bounds of a type
+ *
+ * @see TypeVarBoundedType
+ * @see <a href="http://docs.oracle.com/javase/specs/jls/se5.0/html/typesValues.html#4.4">http://docs.oracle.com/javase/specs/jls/se5.0/html/typesValues.html#4.4</a>
+ * @see WildCardBoundedType
+ * @see <a href="http://docs.oracle.com/javase/specs/jls/se5.0/html/typesValues.html#4.5.1">http://docs.oracle.com/javase/specs/jls/se5.0/html/typesValues.html#4.5.1</a>
+ */
+ public interface BoundedType extends Type {
+ Type firstBound();
+
+ Type[] interfaceBounds();
+ }
+
+ /**
+ * Type representing bounds of a type variable, allows to keep all bounds information.
+ *
+ * <p>It uses the first bound in the array, as this array is never null and always contains at least
+ * one element (Object is always here if no bounds are declared).</p>
+ *
+ * <p>If upper bounds are declared with SomeClass and additional interfaces, then firstBound will be SomeClass and
+ * interfacesBound will be an array of the additional interfaces.
+ *
+ * i.e. <code>SomeClass</code>.
+ * <pre class="code"><code class="java">
+ * interface UpperBoundedTypeWithClass<E extends Comparable<E> & Cloneable> {
+ * E get();
+ * }
+ * // will return Comparable type
+ * </code></pre>
+ * </p>
+ *
+ * @see <a href="http://docs.oracle.com/javase/specs/jls/se5.0/html/typesValues.html#4.4">http://docs.oracle.com/javase/specs/jls/se5.0/html/typesValues.html#4.4</a>
+ */
+ public static class TypeVarBoundedType implements BoundedType {
+ private final TypeVariable typeVariable;
+
+
+ public TypeVarBoundedType(TypeVariable typeVariable) {
+ this.typeVariable = typeVariable;
+ }
+
+ /**
+ * @return either a class or an interface (parameterized or not), if no bounds declared Object is returned.
+ */
+ public Type firstBound() {
+ return typeVariable.getBounds()[0]; //
+ }
+
+ /**
+ * On a Type Variable (typeVar extends C_0 & I_1 & I_2 & etc), will return an array
+ * containing I_1 and I_2.
+ *
+ * @return other bounds for this type, these bounds can only be only interfaces as the JLS says,
+ * empty array if no other bound declared.
+ */
+ public Type[] interfaceBounds() {
+ Type[] interfaceBounds = new Type[typeVariable.getBounds().length - 1];
+ System.arraycopy(typeVariable.getBounds(), 1, interfaceBounds, 0, typeVariable.getBounds().length - 1);
+ return interfaceBounds;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ return typeVariable.equals(((TypeVarBoundedType) o).typeVariable);
+
+ }
+
+ @Override
+ public int hashCode() {
+ return typeVariable.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return "{firstBound=" + firstBound() + ", interfaceBounds=" + Arrays.deepToString(interfaceBounds()) + '}';
+ }
+
+ public TypeVariable typeVariable() {
+ return typeVariable;
+ }
+ }
+
+ /**
+ * Type representing bounds of a wildcard, allows to keep all bounds information.
+ *
+ * <p>The JLS says that lower bound and upper bound are mutually exclusive, and that multiple bounds
+ * are not allowed.
+ *
+ * @see <a href="http://docs.oracle.com/javase/specs/jls/se5.0/html/typesValues.html#4.4">http://docs.oracle.com/javase/specs/jls/se5.0/html/typesValues.html#4.4</a>
+ */
+ public static class WildCardBoundedType implements BoundedType {
+ private final WildcardType wildcard;
+
+
+ public WildCardBoundedType(WildcardType wildcard) {
+ this.wildcard = wildcard;
+ }
+
+ /**
+ * @return The first bound, either a type or a reference to a TypeVariable
+ */
+ public Type firstBound() {
+ Type[] lowerBounds = wildcard.getLowerBounds();
+ Type[] upperBounds = wildcard.getUpperBounds();
+
+ return lowerBounds.length != 0 ? lowerBounds[0] : upperBounds[0];
+ }
+
+ /**
+ * @return An empty array as, wildcard don't support multiple bounds.
+ */
+ public Type[] interfaceBounds() {
+ return new Type[0];
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ return wildcard.equals(((TypeVarBoundedType) o).typeVariable);
+
+ }
+
+ @Override
+ public int hashCode() {
+ return wildcard.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return "{firstBound=" + firstBound() + ", interfaceBounds=[]}";
+ }
+
+ public WildcardType wildCard() {
+ return wildcard;
+ }
+ }
+
+}
+
+
diff --git a/src/main/java/org/mockito/internal/util/reflection/InstanceField.java b/src/main/java/org/mockito/internal/util/reflection/InstanceField.java
new file mode 100644
index 0000000..c3f019d
--- /dev/null
+++ b/src/main/java/org/mockito/internal/util/reflection/InstanceField.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2007 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+package org.mockito.internal.util.reflection;
+
+import org.mockito.internal.util.Checks;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+
+/**
+ * Represents an accessible instance field.
+ *
+ * Contains the instance reference on which the field can be read adn write.
+ */
+public class InstanceField {
+ private final Field field;
+ private final Object instance;
+ private FieldReader fieldReader;
+
+ /**
+ * Create a new InstanceField.
+ *
+ * @param field The field that should be accessed, note that no checks are performed to ensure
+ * the field belong to this instance class.
+ * @param instance The instance from which the field shall be accessed.
+ */
+ public InstanceField(Field field, Object instance) {
+ this.field = Checks.checkNotNull(field, "field");
+ this.instance = Checks.checkNotNull(instance, "instance");
+ }
+
+ /**
+ * Safely read the field.
+ *
+ * @return the field value.
+ * @see FieldReader
+ */
+ public Object read() {
+ return reader().read();
+ }
+
+ /**
+ * Set the given value to the field of this instance.
+ *
+ * @param value The value that should be written to the field.
+ * @see FieldSetter
+ */
+ public void set(Object value) {
+ new FieldSetter(instance, field).set(value);
+ }
+
+ /**
+ * Check that the field is not null.
+ *
+ * @return <code>true</code> if <code>null</code>, else <code>false</code>.
+ */
+ public boolean isNull() {
+ return reader().isNull();
+ }
+
+ /**
+ * Check if the field is annotated by the given annotation.
+ *
+ * @param annotationClass The annotation type to check.
+ * @return <code>true</code> if the field is annotated by this annotation, else <code>false</code>.
+ */
+ public boolean isAnnotatedBy(Class<? extends Annotation> annotationClass) {
+ return field.isAnnotationPresent(annotationClass);
+ }
+
+ /**
+ * Returns the annotation instance for the given annotation type.
+ *
+ * @param annotationClass Tha annotation type to retrieve.
+ * @param <A> Type of the annotation.
+ * @return The annotation instance.
+ */
+ public <A extends Annotation> A annotation(Class<A> annotationClass) {
+ return field.getAnnotation(annotationClass);
+ }
+
+ /**
+ * Returns the JDK {@link Field} instance.
+ *
+ * @return The actual {@link Field} instance.
+ */
+ public Field jdkField() {
+ return field;
+ }
+
+ private FieldReader reader() {
+ if (fieldReader == null) {
+ fieldReader = new FieldReader(instance, field);
+ }
+ return fieldReader;
+ }
+
+ /**
+ * Returns the name of the field.
+ *
+ * @return Name of the field.
+ */
+ public String name() {
+ return field.getName();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ InstanceField that = (InstanceField) o;
+ return field.equals(that.field) && instance.equals(that.instance);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = field.hashCode();
+ result = 31 * result + instance.hashCode();
+ return result;
+ }
+}
diff --git a/src/main/java/org/mockito/internal/util/reflection/LenientCopyTool.java b/src/main/java/org/mockito/internal/util/reflection/LenientCopyTool.java
new file mode 100644
index 0000000..2418ced
--- /dev/null
+++ b/src/main/java/org/mockito/internal/util/reflection/LenientCopyTool.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2007 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+package org.mockito.internal.util.reflection;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+
+@SuppressWarnings("unchecked")
+public class LenientCopyTool {
+
+ FieldCopier fieldCopier = new FieldCopier();
+
+ public <T> void copyToMock(T from, T mock) {
+ copy(from, mock, from.getClass(), mock.getClass().getSuperclass());
+ }
+
+ public <T> void copyToRealObject(T from, T to) {
+ copy(from, to, from.getClass(), to.getClass());
+ }
+
+ private <T> void copy(T from, T to, Class fromClazz, Class toClass) {
+ while (fromClazz != Object.class) {
+ copyValues(from, to, fromClazz);
+ fromClazz = fromClazz.getSuperclass();
+ }
+ }
+
+ private <T> void copyValues(T from, T mock, Class classFrom) {
+ Field[] fields = classFrom.getDeclaredFields();
+
+ for (int i = 0; i < fields.length; i++) {
+ // ignore static fields
+ Field field = fields[i];
+ if (Modifier.isStatic(field.getModifiers())) {
+ continue;
+ }
+ AccessibilityChanger accessibilityChanger = new AccessibilityChanger();
+ try {
+ accessibilityChanger.enableAccess(field);
+ fieldCopier.copyValue(from, mock, field);
+ } catch (Throwable t) {
+ //Ignore - be lenient - if some field cannot be copied then let's be it
+ } finally {
+ accessibilityChanger.safelyDisableAccess(field);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/mockito/internal/util/reflection/Whitebox.java b/src/main/java/org/mockito/internal/util/reflection/Whitebox.java
new file mode 100644
index 0000000..54a7cc6
--- /dev/null
+++ b/src/main/java/org/mockito/internal/util/reflection/Whitebox.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2007 Mockito contributors
+ * This program is made available under the terms of the MIT License.
+ */
+package org.mockito.internal.util.reflection;
+
+import java.lang.reflect.Field;
+
+public class Whitebox {
+
+ public static Object getInternalState(Object target, String field) {
+ Class<?> c = target.getClass();
+ try {
+ Field f = getFieldFromHierarchy(c, field);
+ f.setAccessible(true);
+ return f.get(target);
+ } catch (Exception e) {
+ throw new RuntimeException("Unable to get internal state on a private field. Please report to mockito mailing list.", e);
+ }
+ }
+
+ public static void setInternalState(Object target, String field, Object value) {
+ Class<?> c = target.getClass();
+ try {
+ Field f = getFieldFromHierarchy(c, field);
+ f.setAccessible(true);
+ f.set(target, value);
+ } catch (Exception e) {
+ throw new RuntimeException("Unable to set internal state on a private field. Please report to mockito mailing list.", e);
+ }
+ }
+
+ private static Field getFieldFromHierarchy(Class<?> clazz, String field) {
+ Field f = getField(clazz, field);
+ while (f == null && clazz != Object.class) {
+ clazz = clazz.getSuperclass();
+ f = getField(clazz, field);
+ }
+ if (f == null) {
+ throw new RuntimeException(
+ "You want me to get this field: '" + field +
+ "' on this class: '" + clazz.getSimpleName() +
+ "' but this field is not declared withing hierarchy of this class!");
+ }
+ return f;
+ }
+
+ private static Field getField(Class<?> clazz, String field) {
+ try {
+ return clazz.getDeclaredField(field);
+ } catch (NoSuchFieldException e) {
+ return null;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/mockito/internal/util/reflection/package.html b/src/main/java/org/mockito/internal/util/reflection/package.html
new file mode 100644
index 0000000..ac2eca0
--- /dev/null
+++ b/src/main/java/org/mockito/internal/util/reflection/package.html
@@ -0,0 +1,8 @@
+<!--
+ ~ Copyright (c) 2007 Mockito contributors
+ ~ This program is made available under the terms of the MIT License.
+ -->
+
+<body>
+reflection utilities
+</body> \ No newline at end of file