DATACMNS-783 - DefaultTypeMapper now specializes raw generic types.

If the type lookup from the store source returns a raw generic type (e.g. resolving the value of a generic property against a value of that generic type - i.e. not a more concrete type binding the generic information) in the context of a generic property, we previously did not apply the generics information of the contextual instance to that very raw type.

We now expose a TypeInformation.specialize(ClassTypeInformation) which applies the current generics context to the given raw type and basically creates a synthetic parameterized TypeInformation instance.

DefaultTypeMapper applies this specialization by default now with ClassTypeInformation simply returning the given type as is so that we don't create any resolution overhead in case no generics are involved in the first place.
This commit is contained in:
Oliver Gierke
2015-11-13 16:52:09 +01:00
parent 0d6476c642
commit d2737983c3
6 changed files with 184 additions and 14 deletions

View File

@@ -147,10 +147,16 @@ public class DefaultTypeMapper<S> implements TypeMapper<S> {
Class<T> rawType = basicType == null ? null : basicType.getType();
boolean isMoreConcreteCustomType = rawType == null ? true : rawType.isAssignableFrom(documentsTargetType)
&& !rawType.equals(documentsTargetType);
return isMoreConcreteCustomType ? (TypeInformation<? extends T>) ClassTypeInformation.from(documentsTargetType)
: basicType;
boolean isMoreConcreteCustomType = rawType == null ? true
: rawType.isAssignableFrom(documentsTargetType) && !rawType.equals(documentsTargetType);
if (!isMoreConcreteCustomType) {
return basicType;
}
ClassTypeInformation<?> targetType = ClassTypeInformation.from(documentsTargetType);
return (TypeInformation<? extends T>) (basicType != null ? basicType.specialize(targetType) : targetType);
}
/**

View File

@@ -167,6 +167,15 @@ public class ClassTypeInformation<S> extends TypeDiscoverer<S> {
return getType().isAssignableFrom(target.getType());
}
/*
* (non-Javadoc)
* @see org.springframework.data.util.TypeDiscoverer#specialize(org.springframework.data.util.ClassTypeInformation)
*/
@Override
public TypeInformation<?> specialize(ClassTypeInformation<?> type) {
return type;
}
/*
* (non-Javadoc)
* @see java.lang.Object#toString()

View File

@@ -493,6 +493,20 @@ class TypeDiscoverer<S> implements TypeInformation<S> {
return target.getSuperTypeInformation(getType()).equals(this);
}
/*
* (non-Javadoc)
* @see org.springframework.data.util.TypeInformation#specialize(org.springframework.data.util.ClassTypeInformation)
*/
@Override
public TypeInformation<?> specialize(ClassTypeInformation<?> type) {
Assert.isTrue(getType().isAssignableFrom(type.getType()));
List<TypeInformation<?>> arguments = getTypeArguments();
return arguments.isEmpty() ? type : createInfo(new SyntheticParamterizedType(type, arguments));
}
private TypeInformation<?> getTypeArgument(Class<?> bound, int index) {
Class<?>[] arguments = GenericTypeResolver.resolveTypeArguments(getType(), bound);
@@ -537,4 +551,63 @@ class TypeDiscoverer<S> implements TypeInformation<S> {
public int hashCode() {
return hashCode;
}
/**
* A synthetic {@link ParameterizedType}.
*
* @author Oliver Gierke
* @since 1.11
*/
private static class SyntheticParamterizedType implements ParameterizedType {
private final ClassTypeInformation<?> typeInformation;
private final List<TypeInformation<?>> typeParameters;
/**
* @param typeInformation must not be {@literal null}.
* @param typeParameters must not be {@literal null}.
*/
public SyntheticParamterizedType(ClassTypeInformation<?> typeInformation, List<TypeInformation<?>> typeParameters) {
Assert.notNull(typeInformation, "Type must not be null!");
Assert.notNull(typeParameters, "Type parameters must not be null!");
this.typeInformation = typeInformation;
this.typeParameters = typeParameters;
}
/*
* (non-Javadoc)
* @see java.lang.reflect.ParameterizedType#getRawType()
*/
@Override
public Type getRawType() {
return typeInformation.getType();
}
/*
* (non-Javadoc)
* @see java.lang.reflect.ParameterizedType#getOwnerType()
*/
@Override
public Type getOwnerType() {
return null;
}
/*
* (non-Javadoc)
* @see java.lang.reflect.ParameterizedType#getActualTypeArguments()
*/
@Override
public Type[] getActualTypeArguments() {
Type[] result = new Type[typeParameters.size()];
for (int i = 0; i < typeParameters.size(); i++) {
result[i] = typeParameters.get(0).getType();
}
return result;
}
}
}

View File

@@ -140,4 +140,14 @@ public interface TypeInformation<S> {
* @return
*/
List<TypeInformation<?>> getTypeArguments();
/**
* Specializes the given (raw) {@link ClassTypeInformation} using the context of the current potentially parameterized
* type, basically turning the given raw type into a parameterized one. Will return the given type as is if no
* generics are involved.
*
* @param type must not be {@literal null}.
* @return will never be {@literal null}.
*/
TypeInformation<?> specialize(ClassTypeInformation<?> type);
}