From bfb0ec40020121c68c9a257c19284f60ec4a03e2 Mon Sep 17 00:00:00 2001
From: tdurieux
Date: Tue, 7 Mar 2017 13:27:33 +0100
Subject: [PATCH] buggy files form Mockito #7
---
.../reflection/GenericMetadataSupport.java | 626 ++++++++++++++++++
1 file changed, 626 insertions(+)
create mode 100644 projects/Mockito/7/org/mockito/internal/util/reflection/GenericMetadataSupport.java
diff --git a/projects/Mockito/7/org/mockito/internal/util/reflection/GenericMetadataSupport.java b/projects/Mockito/7/org/mockito/internal/util/reflection/GenericMetadataSupport.java
new file mode 100644
index 0000000..2d6c07b
--- /dev/null
+++ b/projects/Mockito/7/org/mockito/internal/util/reflection/GenericMetadataSupport.java
@@ -0,0 +1,626 @@
+/*
+ * 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.
+ *
+ *
+ * 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.
+ *
+ *
+ *
+ * Hence :
+ *
+ * - A new instance representing the metadata is created using the {@link #inferFrom(Type)} method from a real
+ *
Class
or from a ParameterizedType
, other types are not yet supported.
+ *
+ * - Then from this metadata, we can extract meta-data for a generic return type of a method, using
+ * {@link #resolveGenericReturnType(Method)}.
+ *
+ *
+ *
+ *
+ * For now this code support the following kind of generic declarations :
+ *
+ * interface GenericsNest<K extends Comparable<K> & Cloneable> extends Map<K, Set<Number>> {
+ * Set<Number> remove(Object key); // override with fixed ParameterizedType
+ * List<? super Integer> returning_wildcard_with_class_lower_bound();
+ * List<? super K> returning_wildcard_with_typeVar_lower_bound();
+ * List<? extends K> returning_wildcard_with_typeVar_upper_bound();
+ * K returningK();
+ * <O extends K> List<O> paramType_with_type_params();
+ * <S, T extends S> T two_type_params();
+ * <O extends K> O typeVar_with_type_params();
+ * Number returningNonGeneric();
+ * }
+ *
+ *
+ * @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 contextualActualTypeParameters = new HashMap();
+
+
+ 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 & super MyInterface>)
+ * - Multiple bounds are not supported (for instance, this is not allowed: extends List & 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 if relevant, otherwise empty List.
+ */
+ public List extraInterfaces() {
+ return Collections.emptyList();
+ }
+
+ /**
+ * @return Returns an array with the raw types of {@link #extraInterfaces()} if relevant.
+ */
+ public Class>[] rawExtraInterfaces() {
+ return new Class[0];
+ }
+
+ /**
+ * @return Returns true if metadata knows about extra-interfaces {@link #extraInterfaces()} if relevant.
+ */
+ 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 actualTypeArguments() {
+ TypeVariable[] typeParameters = rawType().getTypeParameters();
+ LinkedHashMap actualTypeArguments = new LinkedHashMap();
+
+ 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}.
+ *
+ *
+ * At the moment type
can only be a {@link Class} or a {@link ParameterizedType}, otherwise
+ * it'll throw a {@link MockitoException}.
+ *
+ *
+ * @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);
+ }
+ 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.
+ */
+ return extractRawTypeOf(contextualActualTypeParameters.get(type));
+ }
+ throw new MockitoException("Raw extraction not supported for : '" + type + "'");
+ }
+
+ @Override
+ public List 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 extraInterfaces = extraInterfaces();
+ List> rawExtraInterfaces = new ArrayList>();
+ 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.
+ */
+ 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 http://docs.oracle.com/javase/specs/jls/se5.0/html/typesValues.html#4.4
+ * @see WildCardBoundedType
+ * @see http://docs.oracle.com/javase/specs/jls/se5.0/html/typesValues.html#4.5.1
+ */
+ public interface BoundedType extends Type {
+ Type firstBound();
+
+ Type[] interfaceBounds();
+ }
+
+ /**
+ * Type representing bounds of a type variable, allows to keep all bounds information.
+ *
+ * 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).
+ *
+ * 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. SomeClass
.
+ *
+ * interface UpperBoundedTypeWithClass & Cloneable> {
+ * E get();
+ * }
+ * // will return Comparable type
+ *
+ *
+ *
+ * @see http://docs.oracle.com/javase/specs/jls/se5.0/html/typesValues.html#4.4
+ */
+ 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.
+ *
+ * The JLS says that lower bound and upper bound are mutually exclusive, and that multiple bounds
+ * are not allowed.
+ *
+ * @see http://docs.oracle.com/javase/specs/jls/se5.0/html/typesValues.html#4.4
+ */
+ 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;
+ }
+ }
+
+}
+
+