DATACMNS-1196 - Fixed generics lookup for nested generics in ParameterizedTypeInformation.

We now eagerly resolve a generics declaration chain, which we previously - erroneously - expected GenericTypeResolver to do for us. Simplified TypeVariableTypeInformation implementation. Renamed ParameterizedTypeUnitTests to ParameterizedTypeInformationUnitTests.
This commit is contained in:
Oliver Gierke
2017-10-12 18:04:32 +02:00
parent 9c8aaac0c4
commit 8ce79c1810
4 changed files with 73 additions and 70 deletions

View File

@@ -59,31 +59,6 @@ class ParameterizedTypeInformation<T> extends ParentTypeAwareTypeInformation<T>
this.resolved = Lazy.of(() -> isResolvedCompletely());
}
/**
* Resolves the type variables to be used. Uses the parent's type variable map but overwrites variables locally
* declared.
*
* @param type must not be {@literal null}.
* @param resolvedType must not be {@literal null}.
* @param parent must not be {@literal null}.
* @return
*/
private static Map<TypeVariable<?>, Type> calculateTypeVariables(ParameterizedType type, Class<?> resolvedType,
TypeDiscoverer<?> parent) {
TypeVariable<?>[] typeParameters = resolvedType.getTypeParameters();
Type[] arguments = type.getActualTypeArguments();
Map<TypeVariable<?>, Type> localTypeVariables = new HashMap<>(parent.getTypeVariableMap());
IntStream.range(0, typeParameters.length) //
.mapToObj(it -> Pair.of(typeParameters[it], arguments[it])) //
.filter(it -> !(it.getSecond() instanceof TypeVariable)) //
.forEach(it -> localTypeVariables.put(it.getFirst(), it.getSecond()));
return localTypeVariables;
}
/*
* (non-Javadoc)
* @see org.springframework.data.util.TypeDiscoverer#doGetMapValueType()
@@ -267,4 +242,47 @@ class ParameterizedTypeInformation<T> extends ParentTypeAwareTypeInformation<T>
return true;
}
/**
* Resolves the type variables to be used. Uses the parent's type variable map but overwrites variables locally
* declared.
*
* @param type must not be {@literal null}.
* @param resolvedType must not be {@literal null}.
* @param parent must not be {@literal null}.
* @return will never be {@literal null}.
*/
private static Map<TypeVariable<?>, Type> calculateTypeVariables(ParameterizedType type, Class<?> resolvedType,
TypeDiscoverer<?> parent) {
TypeVariable<?>[] typeParameters = resolvedType.getTypeParameters();
Type[] arguments = type.getActualTypeArguments();
Map<TypeVariable<?>, Type> localTypeVariables = new HashMap<>(parent.getTypeVariableMap());
IntStream.range(0, typeParameters.length) //
.mapToObj(it -> Pair.of(typeParameters[it], flattenTypeVariable(arguments[it], localTypeVariables))) //
.forEach(it -> localTypeVariables.put(it.getFirst(), it.getSecond()));
return localTypeVariables;
}
/**
* Recursively resolves the type bound to the given {@link Type} in case it's a {@link TypeVariable} and there's an
* entry in the given type variables.
*
* @param source must not be {@literal null}.
* @param variables must not be {@literal null}.
* @return will never be {@literal null}.
*/
private static Type flattenTypeVariable(Type source, Map<TypeVariable<?>, Type> variables) {
if (!(source instanceof TypeVariable)) {
return source;
}
Type value = variables.get(source);
return value == null ? source : flattenTypeVariable(value, variables);
}
}

View File

@@ -140,7 +140,7 @@ class TypeDiscoverer<S> implements TypeInformation<S> {
if (fieldType instanceof TypeVariable) {
TypeVariable<?> variable = (TypeVariable<?>) fieldType;
return new TypeVariableTypeInformation(variable, type, this);
return new TypeVariableTypeInformation(variable, this);
}
if (fieldType instanceof GenericArrayType) {

View File

@@ -17,7 +17,6 @@ package org.springframework.data.util;
import static org.springframework.util.ObjectUtils.*;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
@@ -33,61 +32,22 @@ import org.springframework.util.Assert;
class TypeVariableTypeInformation<T> extends ParentTypeAwareTypeInformation<T> {
private final TypeVariable<?> variable;
private final Type owningType;
/**
* Creates a bew {@link TypeVariableTypeInformation} for the given {@link TypeVariable} owning {@link Type} and parent
* Creates a new {@link TypeVariableTypeInformation} for the given {@link TypeVariable} owning {@link Type} and parent
* {@link TypeDiscoverer}.
*
* @param variable must not be {@literal null}
* @param owningType must not be {@literal null}
* @param parent
*/
public TypeVariableTypeInformation(TypeVariable<?> variable, Type owningType, TypeDiscoverer<?> parent) {
public TypeVariableTypeInformation(TypeVariable<?> variable, TypeDiscoverer<?> parent) {
super(variable, parent);
Assert.notNull(variable, "TypeVariable must not be null!");
this.variable = variable;
this.owningType = owningType;
}
/*
* (non-Javadoc)
* @see org.springframework.data.util.TypeDiscoverer#getType()
*/
@Override
public Class<T> getType() {
int index = getIndex(variable);
if (owningType instanceof ParameterizedType && index != -1) {
Type fieldType = ((ParameterizedType) owningType).getActualTypeArguments()[index];
return resolveType(fieldType);
}
return resolveType(variable);
}
/**
* Returns the index of the type parameter binding the given {@link TypeVariable}.
*
* @param variable
* @return
*/
private int getIndex(TypeVariable<?> variable) {
Class<?> rawType = resolveType(owningType);
TypeVariable<?>[] typeParameters = rawType.getTypeParameters();
for (int i = 0; i < typeParameters.length; i++) {
if (variable.equals(typeParameters[i])) {
return i;
}
}
return -1;
}
/*