Extended TypeInformation classes to support generic arrays as well.
Additionally added support for resolving component types (for collections and map keys) as well as map value types.
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
package org.springframework.data.util;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.GenericArrayType;
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
/**
|
||||
* Special {@link TypeDiscoverer} handling {@link GenericArrayType}s.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class ArrayTypeDiscoverer extends TypeDiscoverer {
|
||||
|
||||
private GenericArrayType type;
|
||||
|
||||
/**
|
||||
* @param type
|
||||
* @param typeVariableMap
|
||||
* @param parent
|
||||
*/
|
||||
protected ArrayTypeDiscoverer(GenericArrayType type, TypeDiscoverer parent) {
|
||||
super(type, null, parent);
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.util.TypeDiscoverer#getType()
|
||||
*/
|
||||
@Override
|
||||
public Class<?> getType() {
|
||||
|
||||
return Array.newInstance(resolveType(type.getGenericComponentType()), 0).getClass();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.util.TypeDiscoverer#getComponentType()
|
||||
*/
|
||||
@Override
|
||||
public TypeInformation getComponentType() {
|
||||
|
||||
Type componentType = type.getGenericComponentType();
|
||||
return createInfo(componentType);
|
||||
}
|
||||
}
|
||||
@@ -12,68 +12,79 @@ import java.util.Set;
|
||||
*/
|
||||
public class ClassTypeInformation extends TypeDiscoverer {
|
||||
|
||||
private final Class<?> type;
|
||||
private final Class<?> type;
|
||||
|
||||
/**
|
||||
* Creates {@link ClassTypeInformation} for the given type.
|
||||
* @param type
|
||||
*/
|
||||
public ClassTypeInformation(Class<?> type) {
|
||||
this(type, GenericTypeResolver.getTypeVariableMap(type), null, null);
|
||||
}
|
||||
/**
|
||||
* Creates {@link ClassTypeInformation} for the given type.
|
||||
*
|
||||
* @param type
|
||||
*/
|
||||
public ClassTypeInformation(Class<?> type) {
|
||||
this(type, GenericTypeResolver.getTypeVariableMap(type), null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates {@link ClassTypeInformation} for the given type and the given basic types. Handing over a basic type
|
||||
* will prevent it's nested fields to be traversed for further {@link TypeInformation}.
|
||||
*
|
||||
* @param type
|
||||
* @param basicTypes
|
||||
*/
|
||||
public ClassTypeInformation(Class<?> type, Set<Class<?>> basicTypes) {
|
||||
this(type, GenericTypeResolver.getTypeVariableMap(type), basicTypes, null);
|
||||
}
|
||||
/**
|
||||
* Creates {@link ClassTypeInformation} for the given type and the given basic types. Handing over a basic type will
|
||||
* prevent it's nested fields to be traversed for further {@link TypeInformation}.
|
||||
*
|
||||
* @param type
|
||||
* @param basicTypes
|
||||
*/
|
||||
public ClassTypeInformation(Class<?> type, Set<Class<?>> basicTypes) {
|
||||
this(type, GenericTypeResolver.getTypeVariableMap(type), basicTypes, null);
|
||||
}
|
||||
|
||||
ClassTypeInformation(Class<?> type, TypeDiscoverer parent) {
|
||||
this(type, null, null, parent);
|
||||
}
|
||||
ClassTypeInformation(Class<?> type, TypeDiscoverer parent) {
|
||||
this(type, null, null, parent);
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
ClassTypeInformation(Class<?> type, Map<TypeVariable, Type> typeVariableMap, Set<Class<?>> basicTypes,
|
||||
TypeDiscoverer parent) {
|
||||
super(type, typeVariableMap, parent);
|
||||
this.type = type;
|
||||
}
|
||||
@SuppressWarnings("rawtypes")
|
||||
ClassTypeInformation(Class<?> type, Map<TypeVariable, Type> typeVariableMap, Set<Class<?>> basicTypes,
|
||||
TypeDiscoverer parent) {
|
||||
super(type, typeVariableMap, parent);
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.data.document.mongodb.TypeDiscovererTest.FieldInformation#getType()
|
||||
*/
|
||||
@Override
|
||||
public Class<?> getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.util.TypeDiscoverer#equals(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
|
||||
if (!super.equals(obj)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ClassTypeInformation that = (ClassTypeInformation) obj;
|
||||
return this.type.equals(that.type);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.util.TypeDiscoverer#hashCode()
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = super.hashCode();
|
||||
return result += 31 * type.hashCode();
|
||||
}
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.data.document.mongodb.TypeDiscovererTest.FieldInformation#getType()
|
||||
*/
|
||||
@Override
|
||||
public Class<?> getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.util.TypeDiscoverer#getComponentType()
|
||||
*/
|
||||
@Override
|
||||
public TypeInformation getComponentType() {
|
||||
|
||||
TypeVariable<?>[] typeParameters = type.getTypeParameters();
|
||||
return typeParameters.length > 0 ? new TypeVariableTypeInformation(typeParameters[0], this.getType(), this) : null;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.util.TypeDiscoverer#equals(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
|
||||
if (!super.equals(obj)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ClassTypeInformation that = (ClassTypeInformation) obj;
|
||||
return this.type.equals(that.type);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.util.TypeDiscoverer#hashCode()
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = super.hashCode();
|
||||
return result += 31 * type.hashCode();
|
||||
}
|
||||
}
|
||||
@@ -2,9 +2,11 @@ package org.springframework.data.util;
|
||||
|
||||
import static org.springframework.util.ObjectUtils.*;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.GenericArrayType;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.lang.reflect.TypeVariable;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
@@ -60,7 +62,7 @@ class TypeDiscoverer implements TypeInformation {
|
||||
* @param fieldType
|
||||
* @return
|
||||
*/
|
||||
private TypeInformation createInfo(Type fieldType) {
|
||||
protected TypeInformation createInfo(Type fieldType) {
|
||||
|
||||
if (fieldType instanceof ParameterizedType) {
|
||||
ParameterizedType parameterizedType = (ParameterizedType) fieldType;
|
||||
@@ -75,6 +77,10 @@ class TypeDiscoverer implements TypeInformation {
|
||||
if (fieldType instanceof Class) {
|
||||
return new ClassTypeInformation((Class<?>) fieldType, this);
|
||||
}
|
||||
|
||||
if (fieldType instanceof GenericArrayType) {
|
||||
return new ArrayTypeDiscoverer((GenericArrayType) fieldType, this);
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
@@ -142,14 +148,28 @@ class TypeDiscoverer implements TypeInformation {
|
||||
* @see org.springframework.data.util.TypeInformation#getMapValueType()
|
||||
*/
|
||||
@Override
|
||||
public Class<?> getMapValueType() {
|
||||
public TypeInformation getMapValueType() {
|
||||
|
||||
if (!Map.class.isAssignableFrom(getType())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ParameterizedType parameterizedType = (ParameterizedType) type;
|
||||
return createInfo(parameterizedType.getActualTypeArguments()[1]).getType();
|
||||
return createInfo(parameterizedType.getActualTypeArguments()[1]);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.util.TypeInformation#getComponentType()
|
||||
*/
|
||||
@Override
|
||||
public TypeInformation getComponentType() {
|
||||
|
||||
if (!(Map.class.isAssignableFrom(getType()) || Collection.class.isAssignableFrom(getType()))) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ParameterizedType parameterizedType = (ParameterizedType) type;
|
||||
return createInfo(parameterizedType.getActualTypeArguments()[0]);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.springframework.data.util;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
@@ -20,13 +21,19 @@ public interface TypeInformation {
|
||||
*/
|
||||
TypeInformation getProperty(String fieldname);
|
||||
|
||||
/**
|
||||
* Returns the component type for {@link Collection}s or the key type for {@link Map}s.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
TypeInformation getComponentType();
|
||||
|
||||
/**
|
||||
* Will return the type of the value in case the underlying type is a {@link Map}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Class<?> getMapValueType();
|
||||
TypeInformation getMapValueType();
|
||||
|
||||
/**
|
||||
* Returns the type of the property. Will resolve generics and the generic
|
||||
|
||||
@@ -4,7 +4,9 @@ import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.data.mapping.Person;
|
||||
@@ -69,11 +71,25 @@ public class ClassTypeInformationUnitTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void discoversArrays() {
|
||||
ClassTypeInformation information = new ClassTypeInformation(CollectionContainer.class);
|
||||
Class<?> type = information.getProperty("array").getType();
|
||||
public void discoversArraysAndCollections() {
|
||||
ClassTypeInformation information = new ClassTypeInformation(StringCollectionContainer.class);
|
||||
|
||||
TypeInformation property = information.getProperty("array");
|
||||
assertEquals(property.getComponentType().getType(), String.class);
|
||||
|
||||
Class<?> type = property.getType();
|
||||
assertEquals(String[].class, type);
|
||||
assertThat(type.isArray(), is(true));
|
||||
|
||||
property = information.getProperty("foo");
|
||||
assertEquals(Collection[].class, property.getType());
|
||||
assertEquals(Collection.class, property.getComponentType().getType());
|
||||
assertEquals(String.class, property.getComponentType().getComponentType().getType());
|
||||
|
||||
property = information.getProperty("rawSet");
|
||||
assertEquals(Set.class, property.getType());
|
||||
assertEquals(Object.class, property.getComponentType().getType());
|
||||
assertNull(property.getMapValueType());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -82,11 +98,11 @@ public class ClassTypeInformationUnitTests {
|
||||
ClassTypeInformation information = new ClassTypeInformation(StringMapContainer.class);
|
||||
TypeInformation genericMap = information.getProperty("genericMap");
|
||||
assertEquals(Map.class, genericMap.getType());
|
||||
assertEquals(String.class, genericMap.getMapValueType());
|
||||
assertEquals(String.class, genericMap.getMapValueType().getType());
|
||||
|
||||
TypeInformation map = information.getProperty("map");
|
||||
assertEquals(Map.class, map.getType());
|
||||
assertEquals(Calendar.class, map.getMapValueType());
|
||||
assertEquals(Calendar.class, map.getMapValueType().getType());
|
||||
}
|
||||
|
||||
private class StringMapContainer extends MapContainer<String> {
|
||||
@@ -97,10 +113,17 @@ public class ClassTypeInformationUnitTests {
|
||||
Map<String, T> genericMap;
|
||||
Map<String, Calendar> map;
|
||||
}
|
||||
|
||||
private class CollectionContainer {
|
||||
|
||||
private class StringCollectionContainer extends CollectionContainer<String> {
|
||||
|
||||
String[] array;
|
||||
}
|
||||
|
||||
private class CollectionContainer<T> {
|
||||
|
||||
T[] array;
|
||||
Collection<T>[] foo;
|
||||
Set<String> set;
|
||||
Set rawSet;
|
||||
}
|
||||
|
||||
private class GenericTypeWithBound<T extends Person> {
|
||||
|
||||
Reference in New Issue
Block a user