org.mockito.internal.stubbing.defaultanswers.RetrieveGenericsForDefaultAnswers Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of mockito-core Show documentation
Show all versions of mockito-core Show documentation
Mockito mock objects library core API and implementation
/*
* Copyright (c) 2007 Mockito contributors
* This program is made available under the terms of the MIT License.
*/
package org.mockito.internal.stubbing.defaultanswers;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import org.mockito.internal.util.MockUtil;
import org.mockito.internal.util.reflection.GenericMetadataSupport;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.mock.MockCreationSettings;
final class RetrieveGenericsForDefaultAnswers {
static Object returnTypeForMockWithCorrectGenerics(
InvocationOnMock invocation, AnswerCallback answerCallback) {
Class type = invocation.getMethod().getReturnType();
final Type returnType = invocation.getMethod().getGenericReturnType();
Object defaultReturnValue = null;
if (returnType instanceof TypeVariable) {
type = findTypeFromGeneric(invocation, (TypeVariable) returnType);
if (type != null) {
defaultReturnValue = delegateChains(type);
}
}
if (defaultReturnValue != null) {
return defaultReturnValue;
}
if (type != null) {
final MockCreationSettings mockSettings =
MockUtil.getMockSettings(invocation.getMock());
if (!MockUtil.typeMockabilityOf(type, mockSettings.getMockMaker()).mockable()) {
return null;
}
return answerCallback.apply(type);
}
return answerCallback.apply(null);
}
/**
* Try to resolve the result value using {@link ReturnsEmptyValues} and {@link ReturnsMoreEmptyValues}.
*
* This will try to use all parent class (superclass and interfaces) to retrieve the value..
*
* @param type the return type of the method
* @return a non-null instance if the type has been resolve. Null otherwise.
*/
private static Object delegateChains(final Class type) {
final ReturnsEmptyValues returnsEmptyValues = new ReturnsEmptyValues();
Object result = returnsEmptyValues.returnValueFor(type);
if (result == null) {
Class emptyValueForClass = type;
while (emptyValueForClass != null && result == null) {
final Class[] classes = emptyValueForClass.getInterfaces();
for (Class clazz : classes) {
result = returnsEmptyValues.returnValueFor(clazz);
if (result != null) {
break;
}
}
emptyValueForClass = emptyValueForClass.getSuperclass();
}
}
if (result == null) {
result = new ReturnsMoreEmptyValues().returnValueFor(type);
}
return result;
}
/**
* Retrieve the expected type when it came from a primitive. If the type cannot be retrieve, return null.
*
* @param invocation the current invocation
* @param returnType the expected return type
* @return the type or null if not found
*/
private static Class findTypeFromGeneric(
final InvocationOnMock invocation, final TypeVariable returnType) {
// Class level
final MockCreationSettings mockSettings =
MockUtil.getMockHandler(invocation.getMock()).getMockSettings();
final GenericMetadataSupport returnTypeSupport =
GenericMetadataSupport.inferFrom(mockSettings.getTypeToMock())
.resolveGenericReturnType(invocation.getMethod());
final Class rawType = returnTypeSupport.rawType();
// Method level
if (rawType == Object.class) {
return findTypeFromGenericInArguments(invocation, returnType);
}
return rawType;
}
/**
* Find a return type using generic arguments provided by the calling method.
*
* @param invocation the current invocation
* @param returnType the expected return type
* @return the return type or null if the return type cannot be found
*/
private static Class findTypeFromGenericInArguments(
final InvocationOnMock invocation, final TypeVariable returnType) {
final Type[] parameterTypes = invocation.getMethod().getGenericParameterTypes();
for (int i = 0; i < parameterTypes.length; i++) {
Type argType = parameterTypes[i];
if (returnType.equals(argType)) {
Object argument = invocation.getArgument(i);
if (argument == null) {
return null;
}
return argument.getClass();
}
if (argType instanceof GenericArrayType) {
argType = ((GenericArrayType) argType).getGenericComponentType();
if (returnType.equals(argType)) {
return invocation.getArgument(i).getClass();
}
}
}
return null;
}
interface AnswerCallback {
Object apply(Class type);
}
private RetrieveGenericsForDefaultAnswers() {}
}