Browse Source
- Support for serialization - Allow programmatic creation of an array from a given component type - Allow programmatic creation with given generics - Extract generics from Class types using Class.getTypeParameters() - Move TypeVariableResolver to an inner class (and make method private) - Refine 'resolve()' algorithm Issue: SPR-10973pull/388/head
6 changed files with 901 additions and 200 deletions
@ -0,0 +1,330 @@
@@ -0,0 +1,330 @@
|
||||
/* |
||||
* Copyright 2002-2013 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.core; |
||||
|
||||
import java.io.IOException; |
||||
import java.io.ObjectInputStream; |
||||
import java.io.Serializable; |
||||
import java.lang.reflect.Field; |
||||
import java.lang.reflect.GenericArrayType; |
||||
import java.lang.reflect.InvocationHandler; |
||||
import java.lang.reflect.Method; |
||||
import java.lang.reflect.ParameterizedType; |
||||
import java.lang.reflect.Proxy; |
||||
import java.lang.reflect.Type; |
||||
import java.lang.reflect.TypeVariable; |
||||
import java.lang.reflect.WildcardType; |
||||
|
||||
import org.springframework.util.Assert; |
||||
import org.springframework.util.ReflectionUtils; |
||||
|
||||
/** |
||||
* Internal utility class that can be used to obtain wrapped {@link Serializable} variants |
||||
* of {@link java.lang.reflect.Type}s. |
||||
* |
||||
* <p>{@link #forField(Field) Fields} or {@link #forMethodParameter(MethodParameter) |
||||
* MethodParameters} can be used as the root source for a serializable type. Alternatively |
||||
* the {@link #forGenericSuperclass(Class) superclass}, |
||||
* {@link #forGenericInterfaces(Class) interfaces} or {@link #forTypeParameters(Class) |
||||
* type parameters} or a regular {@link Class} can also be used as source. |
||||
* |
||||
* <p>The returned type will either be a {@link Class} or a serializable proxy of |
||||
* {@link GenericArrayType}, {@link ParameterizedType}, {@link TypeVariable} or |
||||
* {@link WildcardType}. With the exception of {@link Class} (which is final) calls to |
||||
* methods that return further {@link Type}s (for example |
||||
* {@link GenericArrayType#getGenericComponentType()}) will be automatically wrapped. |
||||
* |
||||
* @author Phillip Webb |
||||
* @since 4.0 |
||||
*/ |
||||
abstract class SerializableTypeWrapper { |
||||
|
||||
private static final Class<?>[] SUPPORTED_SERIALAZABLE_TYPES = { GenericArrayType.class, |
||||
ParameterizedType.class, TypeVariable.class, WildcardType.class }; |
||||
|
||||
|
||||
/** |
||||
* Return a {@link Serializable} variant of {@link Field#getGenericType()}. |
||||
*/ |
||||
public static Type forField(Field field) { |
||||
Assert.notNull(field, "Field must not be null"); |
||||
return forTypeProvider(new FieldTypeProvider(field)); |
||||
} |
||||
|
||||
/** |
||||
* Return a {@link Serializable} variant of |
||||
* {@link MethodParameter#getGenericParameterType()}. |
||||
*/ |
||||
public static Type forMethodParameter(MethodParameter methodParameter) { |
||||
return forTypeProvider(new MethodParameterTypeProvider(methodParameter)); |
||||
} |
||||
|
||||
/** |
||||
* Return a {@link Serializable} variant of {@link Class#getGenericSuperclass()}. |
||||
*/ |
||||
public static Type forGenericSuperclass(final Class<?> type) { |
||||
return forTypeProvider(new TypeProvider() { |
||||
@Override |
||||
public Type getType() { |
||||
return type.getGenericSuperclass(); |
||||
} |
||||
}); |
||||
} |
||||
|
||||
/** |
||||
* Return a {@link Serializable} variant of {@link Class#getGenericInterfaces()}. |
||||
*/ |
||||
public static Type[] forGenericInterfaces(final Class<?> type) { |
||||
Type[] result = new Type[type.getGenericInterfaces().length]; |
||||
for (int i = 0; i < result.length; i++) { |
||||
final int index = i; |
||||
result[i] = forTypeProvider(new TypeProvider() { |
||||
@Override |
||||
public Type getType() { |
||||
return type.getGenericInterfaces()[index]; |
||||
} |
||||
}); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
/** |
||||
* Return a {@link Serializable} variant of {@link Class#getTypeParameters()}. |
||||
*/ |
||||
public static Type[] forTypeParameters(final Class<?> type) { |
||||
Type[] result = new Type[type.getTypeParameters().length]; |
||||
for (int i = 0; i < result.length; i++) { |
||||
final int index = i; |
||||
result[i] = forTypeProvider(new TypeProvider() { |
||||
@Override |
||||
public Type getType() { |
||||
return type.getTypeParameters()[index]; |
||||
} |
||||
}); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Return a {@link Serializable} {@link Type} backed by a {@link TypeProvider} . |
||||
*/ |
||||
private static Type forTypeProvider(final TypeProvider provider) { |
||||
Assert.notNull(provider, "Provider must not be null"); |
||||
if (provider.getType() instanceof Serializable || provider.getType() == null) { |
||||
return provider.getType(); |
||||
} |
||||
for (Class<?> type : SUPPORTED_SERIALAZABLE_TYPES) { |
||||
if (type.isAssignableFrom(provider.getType().getClass())) { |
||||
ClassLoader classLoader = provider.getClass().getClassLoader(); |
||||
Class<?>[] interfaces = new Class<?>[] { type, Serializable.class }; |
||||
InvocationHandler handler = new TypeProxyInvocationHandler(provider); |
||||
return (Type) Proxy.newProxyInstance(classLoader, interfaces, handler); |
||||
} |
||||
} |
||||
throw new IllegalArgumentException("Unsupported Type class " |
||||
+ provider.getType().getClass().getName()); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* A {@link Serializable} interface providing access to a {@link Type}. |
||||
*/ |
||||
private static interface TypeProvider extends Serializable { |
||||
|
||||
/** |
||||
* Return the (possibly non {@link Serializable}) {@link Type}. |
||||
*/ |
||||
Type getType(); |
||||
|
||||
} |
||||
|
||||
|
||||
/** |
||||
* {@link Serializable} {@link InvocationHandler} used by the Proxied {@link Type}. |
||||
* Provides serialization support and enhances any methods that return {@code Type} |
||||
* or {@code Type[]}. |
||||
*/ |
||||
private static class TypeProxyInvocationHandler implements InvocationHandler, |
||||
Serializable { |
||||
|
||||
private final TypeProvider provider; |
||||
|
||||
|
||||
public TypeProxyInvocationHandler(TypeProvider provider) { |
||||
this.provider = provider; |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { |
||||
if (Type.class.equals(method.getReturnType()) && args == null) { |
||||
return forTypeProvider(new MethodInvokeTypeProvider(this.provider, method, -1)); |
||||
} |
||||
if (Type[].class.equals(method.getReturnType()) && args == null) { |
||||
Type[] result = new Type[((Type[]) method.invoke(this.provider.getType(), args)).length]; |
||||
for (int i = 0; i < result.length; i++) { |
||||
result[i] = forTypeProvider(new MethodInvokeTypeProvider(this.provider, method, i)); |
||||
} |
||||
return result; |
||||
} |
||||
return method.invoke(this.provider.getType(), args); |
||||
} |
||||
|
||||
} |
||||
|
||||
|
||||
|
||||
/** |
||||
* {@link TypeProvider} for {@link Type}s obtained from a {@link Field}. |
||||
*/ |
||||
private static class FieldTypeProvider implements TypeProvider { |
||||
|
||||
private final String fieldName; |
||||
|
||||
private final Class<?> declaringClass; |
||||
|
||||
private transient Field field; |
||||
|
||||
|
||||
public FieldTypeProvider(Field field) { |
||||
this.fieldName = field.getName(); |
||||
this.declaringClass = field.getDeclaringClass(); |
||||
this.field = field; |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public Type getType() { |
||||
return this.field.getGenericType(); |
||||
} |
||||
|
||||
private void readObject(ObjectInputStream inputStream) throws IOException, |
||||
ClassNotFoundException { |
||||
inputStream.defaultReadObject(); |
||||
try { |
||||
this.field = this.declaringClass.getDeclaredField(this.fieldName); |
||||
} |
||||
catch (Throwable ex) { |
||||
throw new IllegalStateException( |
||||
"Could not find original class structure", ex); |
||||
} |
||||
} |
||||
|
||||
} |
||||
|
||||
|
||||
/** |
||||
* {@link TypeProvider} for {@link Type}s obtained from a {@link MethodParameter}. |
||||
*/ |
||||
private static class MethodParameterTypeProvider implements TypeProvider { |
||||
|
||||
private final String methodName; |
||||
|
||||
private final Class<?>[] parameterTypes; |
||||
|
||||
private final Class<?> declaringClass; |
||||
|
||||
private final int parameterIndex; |
||||
|
||||
private transient MethodParameter methodParameter; |
||||
|
||||
|
||||
public MethodParameterTypeProvider(MethodParameter methodParameter) { |
||||
if (methodParameter.getMethod() != null) { |
||||
this.methodName = methodParameter.getMethod().getName(); |
||||
this.parameterTypes = methodParameter.getMethod().getParameterTypes(); |
||||
} |
||||
else { |
||||
this.methodName = null; |
||||
this.parameterTypes = methodParameter.getConstructor().getParameterTypes(); |
||||
} |
||||
this.declaringClass = methodParameter.getDeclaringClass(); |
||||
this.parameterIndex = methodParameter.getParameterIndex(); |
||||
this.methodParameter = methodParameter; |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public Type getType() { |
||||
return this.methodParameter.getGenericParameterType(); |
||||
} |
||||
|
||||
private void readObject(ObjectInputStream inputStream) throws IOException, |
||||
ClassNotFoundException { |
||||
inputStream.defaultReadObject(); |
||||
try { |
||||
if (this.methodName != null) { |
||||
this.methodParameter = new MethodParameter( |
||||
this.declaringClass.getDeclaredMethod(this.methodName, |
||||
this.parameterTypes), this.parameterIndex); |
||||
} |
||||
else { |
||||
this.methodParameter = new MethodParameter( |
||||
this.declaringClass.getDeclaredConstructor(this.parameterTypes), |
||||
this.parameterIndex); |
||||
} |
||||
} |
||||
catch (Throwable ex) { |
||||
throw new IllegalStateException( |
||||
"Could not find original class structure", ex); |
||||
} |
||||
} |
||||
|
||||
} |
||||
|
||||
|
||||
/** |
||||
* {@link TypeProvider} for {@link Type}s obtained by invoking a no-arg method. |
||||
*/ |
||||
private static class MethodInvokeTypeProvider implements TypeProvider { |
||||
|
||||
private final TypeProvider provider; |
||||
|
||||
private final String methodName; |
||||
|
||||
private final int index; |
||||
|
||||
private transient Object result; |
||||
|
||||
|
||||
public MethodInvokeTypeProvider(TypeProvider provider, Method method, int index) { |
||||
this.provider = provider; |
||||
this.methodName = method.getName(); |
||||
this.index = index; |
||||
this.result = ReflectionUtils.invokeMethod(method, provider.getType()); |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public Type getType() { |
||||
if (this.result instanceof Type || this.result == null) { |
||||
return (Type) this.result; |
||||
} |
||||
return ((Type[])this.result)[this.index]; |
||||
} |
||||
|
||||
private void readObject(ObjectInputStream inputStream) throws IOException, |
||||
ClassNotFoundException { |
||||
inputStream.defaultReadObject(); |
||||
Method method = ReflectionUtils.findMethod( |
||||
this.provider.getType().getClass(), this.methodName); |
||||
this.result = ReflectionUtils.invokeMethod(method, this.provider.getType()); |
||||
} |
||||
|
||||
} |
||||
} |
||||
@ -1,40 +0,0 @@
@@ -1,40 +0,0 @@
|
||||
/* |
||||
* Copyright 2002-2013 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.core; |
||||
|
||||
import java.lang.reflect.Type; |
||||
import java.lang.reflect.TypeVariable; |
||||
|
||||
/** |
||||
* Strategy interface that can be used to resolve {@link java.lang.reflect.TypeVariable}s. |
||||
* |
||||
* @author Phillip Webb |
||||
* @since 4.0 |
||||
* @see ResolvableType |
||||
* @see GenericTypeResolver |
||||
*/ |
||||
interface TypeVariableResolver { |
||||
|
||||
/** |
||||
* Resolve the specified type variable. |
||||
* @param typeVariable the type variable to resolve (must not be {@code null}) |
||||
* @return the resolved {@link java.lang.reflect.Type} for the variable or |
||||
* {@code null} if the variable cannot be resolved. |
||||
*/ |
||||
Type resolveVariable(TypeVariable<?> typeVariable); |
||||
|
||||
} |
||||
@ -0,0 +1,171 @@
@@ -0,0 +1,171 @@
|
||||
/* |
||||
* Copyright 2002-2013 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.core; |
||||
|
||||
import java.io.ByteArrayInputStream; |
||||
import java.io.ByteArrayOutputStream; |
||||
import java.io.ObjectInputStream; |
||||
import java.io.ObjectOutputStream; |
||||
import java.lang.reflect.Constructor; |
||||
import java.lang.reflect.GenericArrayType; |
||||
import java.lang.reflect.Method; |
||||
import java.lang.reflect.ParameterizedType; |
||||
import java.lang.reflect.Type; |
||||
import java.lang.reflect.TypeVariable; |
||||
import java.lang.reflect.WildcardType; |
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
|
||||
import org.junit.Test; |
||||
|
||||
import static org.hamcrest.Matchers.*; |
||||
import static org.junit.Assert.*; |
||||
|
||||
|
||||
/** |
||||
* Tests for {@link SerializableTypeWrapper}. |
||||
* |
||||
* @author Phillip Webb |
||||
*/ |
||||
public class SerializableTypeWrapperTests { |
||||
|
||||
@Test |
||||
public void forField() throws Exception { |
||||
Type type = SerializableTypeWrapper.forField(Fields.class.getField("parameterizedType")); |
||||
assertThat(type.toString(), equalTo("java.util.List<java.lang.String>")); |
||||
assertSerialzable(type); |
||||
} |
||||
|
||||
@Test |
||||
public void forMethodParameter() throws Exception { |
||||
Method method = Methods.class.getDeclaredMethod("method", Class.class, Object.class); |
||||
Type type = SerializableTypeWrapper.forMethodParameter(MethodParameter.forMethodOrConstructor(method, 0)); |
||||
assertThat(type.toString(), equalTo("java.lang.Class<T>")); |
||||
assertSerialzable(type); |
||||
} |
||||
|
||||
@Test |
||||
public void forConstructor() throws Exception { |
||||
Constructor<?> constructor = Constructors.class.getDeclaredConstructor(List.class); |
||||
Type type = SerializableTypeWrapper.forMethodParameter(MethodParameter.forMethodOrConstructor(constructor, 0)); |
||||
assertThat(type.toString(), equalTo("java.util.List<java.lang.String>")); |
||||
assertSerialzable(type); |
||||
} |
||||
|
||||
@Test |
||||
public void forGenericSuperClass() throws Exception { |
||||
Type type = SerializableTypeWrapper.forGenericSuperclass(ArrayList.class); |
||||
assertThat(type.toString(), equalTo("java.util.AbstractList<E>")); |
||||
assertSerialzable(type); |
||||
} |
||||
|
||||
@Test |
||||
public void forGenericInterfaces() throws Exception { |
||||
Type type = SerializableTypeWrapper.forGenericInterfaces(List.class)[0]; |
||||
assertThat(type.toString(), equalTo("java.util.Collection<E>")); |
||||
assertSerialzable(type); |
||||
} |
||||
|
||||
@Test |
||||
public void forTypeParamters() throws Exception { |
||||
Type type = SerializableTypeWrapper.forTypeParameters(List.class)[0]; |
||||
assertThat(type.toString(), equalTo("E")); |
||||
assertSerialzable(type); |
||||
} |
||||
|
||||
@Test |
||||
public void classType() throws Exception { |
||||
Type type = SerializableTypeWrapper.forField(Fields.class.getField("classType")); |
||||
assertThat(type.toString(), equalTo("class java.lang.String")); |
||||
assertSerialzable(type); |
||||
} |
||||
|
||||
@Test |
||||
public void genericArrayType() throws Exception { |
||||
GenericArrayType type = (GenericArrayType) SerializableTypeWrapper.forField(Fields.class.getField("genericArrayType")); |
||||
assertThat(type.toString(), equalTo("java.util.List<java.lang.String>[]")); |
||||
assertSerialzable(type); |
||||
assertSerialzable(type.getGenericComponentType()); |
||||
} |
||||
|
||||
@Test |
||||
public void parameterizedType() throws Exception { |
||||
ParameterizedType type = (ParameterizedType) SerializableTypeWrapper.forField(Fields.class.getField("parameterizedType")); |
||||
assertThat(type.toString(), equalTo("java.util.List<java.lang.String>")); |
||||
assertSerialzable(type); |
||||
assertSerialzable(type.getOwnerType()); |
||||
assertSerialzable(type.getRawType()); |
||||
assertSerialzable(type.getActualTypeArguments()); |
||||
assertSerialzable(type.getActualTypeArguments()[0]); |
||||
} |
||||
|
||||
@Test |
||||
public void typeVariableType() throws Exception { |
||||
TypeVariable<?> type = (TypeVariable<?>) SerializableTypeWrapper.forField(Fields.class.getField("typeVariableType")); |
||||
assertThat(type.toString(), equalTo("T")); |
||||
assertSerialzable(type); |
||||
assertSerialzable(type.getBounds()); |
||||
} |
||||
|
||||
@Test |
||||
public void wildcardType() throws Exception { |
||||
ParameterizedType typeSource = (ParameterizedType) SerializableTypeWrapper.forField(Fields.class.getField("wildcardType")); |
||||
WildcardType type = (WildcardType) typeSource.getActualTypeArguments()[0]; |
||||
assertThat(type.toString(), equalTo("? extends java.lang.CharSequence")); |
||||
assertSerialzable(type); |
||||
assertSerialzable(type.getLowerBounds()); |
||||
assertSerialzable(type.getUpperBounds()); |
||||
} |
||||
|
||||
|
||||
private void assertSerialzable(Object source) throws Exception { |
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream(); |
||||
ObjectOutputStream oos = new ObjectOutputStream(bos); |
||||
oos.writeObject(source); |
||||
oos.close(); |
||||
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray())); |
||||
assertThat(ois.readObject(), equalTo(source)); |
||||
} |
||||
|
||||
|
||||
static class Fields<T> { |
||||
|
||||
public String classType; |
||||
|
||||
public List<String>[] genericArrayType; |
||||
|
||||
public List<String> parameterizedType; |
||||
|
||||
public T typeVariableType; |
||||
|
||||
public List<? extends CharSequence> wildcardType; |
||||
|
||||
} |
||||
|
||||
static interface Methods { |
||||
|
||||
<T> List<T> method(Class<T> p1, T p2); |
||||
|
||||
} |
||||
|
||||
static class Constructors { |
||||
|
||||
public Constructors(List<String> p) { |
||||
} |
||||
|
||||
} |
||||
} |
||||
Loading…
Reference in new issue