/*
* Copyright 2002-2017 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.io.Serializable;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
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.AbstractCollection;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.concurrent.Callable;
import org.hamcrest.Matchers;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.core.ResolvableType.VariableResolver;
import org.springframework.util.MultiValueMap;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import static org.mockito.BDDMockito.*;
/**
* Tests for {@link ResolvableType}.
*
* @author Phillip Webb
* @author Juergen Hoeller
*/
@SuppressWarnings("rawtypes")
@RunWith(MockitoJUnitRunner.class)
public class ResolvableTypeTests {
@Rule
public ExpectedException thrown = ExpectedException.none();
@Captor
private ArgumentCaptor<TypeVariable<?>> typeVariableCaptor;
@Test
public void noneReturnValues() throws Exception {
ResolvableType none = ResolvableType.NONE;
assertThat(none.as(Object.class), equalTo(ResolvableType.NONE));
assertThat(none.asCollection(), equalTo(ResolvableType.NONE));
assertThat(none.asMap(), equalTo(ResolvableType.NONE));
assertThat(none.getComponentType(), equalTo(ResolvableType.NONE));
assertThat(none.getGeneric(0), equalTo(ResolvableType.NONE));
assertThat(none.getGenerics().length, equalTo(0));
assertThat(none.getInterfaces().length, equalTo(0));
assertThat(none.getSuperType(), equalTo(ResolvableType.NONE));
assertThat(none.getType(), nullValue());
assertThat(none.hasGenerics(), equalTo(false));
assertThat(none.isArray(), equalTo(false));
assertThat(none.resolve(), nullValue());
assertThat(none.resolve(String.class), equalTo((Class) String.class));
assertThat(none.resolveGeneric(0), nullValue());
assertThat(none.resolveGenerics().length, equalTo(0));
assertThat(none.toString(), equalTo("?"));
assertThat(none.hasUnresolvableGenerics(), equalTo(false));
assertThat(none.isAssignableFrom(ResolvableType.forClass(Object.class)), equalTo(false));
}
@Test
public void forClass() throws Exception {
ResolvableType type = ResolvableType.forClass(ExtendsList.class);
assertThat(type.getType(), equalTo((Type) ExtendsList.class));
assertThat(type.getRawClass(), equalTo(ExtendsList.class));
assertTrue(type.isAssignableFrom(ExtendsList.class));
assertFalse(type.isAssignableFrom(ArrayList.class));
}
@Test
public void forClassWithNull() throws Exception {
ResolvableType type = ResolvableType.forClass(null);
assertThat(type.getType(), equalTo((Type) Object.class));
assertThat(type.getRawClass(), equalTo(Object.class));
assertTrue(type.isAssignableFrom(Object.class));
assertTrue(type.isAssignableFrom(String.class));
}
@Test
public void forRawClass() throws Exception {
ResolvableType type = ResolvableType.forRawClass(ExtendsList.class);
assertThat(type.getType(), equalTo((Type) ExtendsList.class));
assertThat(type.getRawClass(), equalTo(ExtendsList.class));
assertTrue(type.isAssignableFrom(ExtendsList.class));
assertFalse(type.isAssignableFrom(ArrayList.class));
}
@Test
public void forRawClassWithNull() throws Exception {
ResolvableType type = ResolvableType.forRawClass(null);
assertThat(type.getType(), equalTo((Type) Object.class));
assertThat(type.getRawClass(), equalTo(Object.class));
assertTrue(type.isAssignableFrom(Object.class));
assertTrue(type.isAssignableFrom(String.class));
}
@Test
public void forInstanceMustNotBeNull() {
this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("Instance must not be null");
ResolvableType.forInstance(null);
}
@Test
public void forInstanceNoProvider() {
ResolvableType type = ResolvableType.forInstance(new Object());
assertThat(type.getType(), equalTo(Object.class));
assertThat(type.resolve(), equalTo(Object.class));
}
@Test
public void forInstanceProvider() {
ResolvableType type = ResolvableType.forInstance(new MyGenericInterfaceType<>(String.class));
assertThat(type.getRawClass(), equalTo(MyGenericInterfaceType.class));
assertThat(type.getGeneric().resolve(), equalTo(String.class));
}
@Test
public void forInstanceProviderNull() {
ResolvableType type = ResolvableType.forInstance(new MyGenericInterfaceType<String>(null));
assertThat(type.getType(), equalTo(MyGenericInterfaceType.class));
assertThat(type.resolve(), equalTo(MyGenericInterfaceType.class));
}
@Test
public void forField() throws Exception {
Field field = Fields.class.getField("charSequenceList");
ResolvableType type = ResolvableType.forField(field);
assertThat(type.getType(), equalTo(field.getGenericType()));
}
@Test
public void forPrivateField() throws Exception {
Field field = Fields.class.getDeclaredField("privateField");
ResolvableType type = ResolvableType.forField(field);
assertThat(type.getType(), equalTo(field.getGenericType()));
assertThat(type.resolve(), equalTo((Class) List.class));
Field field2 = Fields.class.getDeclaredField("otherPrivateField");
ResolvableType type2 = ResolvableType.forField(field2);
assertThat(type2.getType(), equalTo(field2.getGenericType()));
assertThat(type2.resolve(), equalTo((Class) List.class));
assertEquals(type, type2);
assertEquals(type.hashCode(), type2.hashCode());
}
@Test
public void forFieldMustNotBeNull() throws Exception {
this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("Field must not be null");
ResolvableType.forField(null);
}
@Test
public void forConstructorParameter() throws Exception {
Constructor<Constructors> constructor = Constructors.class.getConstructor(List.class);
ResolvableType type = ResolvableType.forConstructorParameter(constructor, 0);
assertThat(type.getType(), equalTo(constructor.getGenericParameterTypes()[0]));
}
@Test
public void forConstructorParameterMustNotBeNull() throws Exception {
this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("Constructor must not be null");
ResolvableType.forConstructorParameter(null, 0);
}
@Test
public void forMethodParameterByIndex() throws Exception {
Method method = Methods.class.getMethod("charSequenceParameter", List.class);
ResolvableType type = ResolvableType.forMethodParameter(method, 0);
assertThat(type.getType(), equalTo(method.getGenericParameterTypes()[0]));
}
@Test
public void forMethodParameterByIndexMustNotBeNull() throws Exception {
this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("Method must not be null");
ResolvableType.forMethodParameter(null, 0);
}
@Test
public void forMethodParameter() throws Exception {
Method method = Methods.class.getMethod("charSequenceParameter", List.class);
MethodParameter methodParameter = MethodParameter.forExecutable(method, 0);
ResolvableType type = ResolvableType.forMethodParameter(methodParameter);
assertThat(type.getType(), equalTo(method.getGenericParameterTypes()[0]));
}
@Test
public void forMethodParameterWithNesting() throws Exception {
Method method = Methods.class.getMethod("nested", Map.class);
MethodParameter methodParameter = MethodParameter.forExecutable(method, 0);
methodParameter.increaseNestingLevel();
ResolvableType type = ResolvableType.forMethodParameter(methodParameter);
assertThat(type.resolve(), equalTo((Class) Map.class));
assertThat(type.getGeneric(0).resolve(), equalTo((Class) Byte.class));
assertThat(type.getGeneric(1).resolve(), equalTo((Class) Long.class));
}
@Test
public void forMethodParameterWithNestingAndLevels() throws Exception {
Method method = Methods.class.getMethod("nested", Map.class);
MethodParameter methodParameter = MethodParameter.forExecutable(method, 0);
methodParameter.increaseNestingLevel();
methodParameter.setTypeIndexForCurrentLevel(0);
ResolvableType type = ResolvableType.forMethodParameter(methodParameter);
assertThat(type.resolve(), equalTo((Class) Map.class));
assertThat(type.getGeneric(0).resolve(), equalTo((Class) String.class));
assertThat(type.getGeneric(1).resolve(), equalTo((Class) Integer.class));
}
@Test
public void forMethodParameterMustNotBeNull() throws Exception {
this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("MethodParameter must not be null");
ResolvableType.forMethodParameter(null);
}
@Test
public void forMethodReturn() throws Exception {
Method method = Methods.class.getMethod("charSequenceReturn");
ResolvableType type = ResolvableType.forMethodReturnType(method);
assertThat(type.getType(), equalTo(method.getGenericReturnType()));
}
@Test
public void forMethodReturnMustNotBeNull() throws Exception {
this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("Method must not be null");
ResolvableType.forMethodReturnType(null);
}
@Test
public void classType() throws Exception {
ResolvableType type = ResolvableType.forField(Fields.class.getField("classType"));
assertThat(type.getType().getClass(), equalTo((Class) Class.class));
}
@Test
public void paramaterizedType() throws Exception {
ResolvableType type = ResolvableType.forField(Fields.class.getField("parameterizedType"));
assertThat(type.getType(), instanceOf(ParameterizedType.class));
}
@Test
public void arrayClassType() throws Exception {
ResolvableType type = ResolvableType.forField(Fields.class.getField("arrayClassType"));
assertThat(type.getType(), instanceOf(Class.class));
assertThat(((Class) type.getType()).isArray(), equalTo(true));
}
@Test
public void genericArrayType() throws Exception {
ResolvableType type = ResolvableType.forField(Fields.class.getField("genericArrayType"));
assertThat(type.getType(), instanceOf(GenericArrayType.class));
}
@Test
public void wildcardType() throws Exception {
ResolvableType type = ResolvableType.forField(Fields.class.getField("wildcardType"));
assertThat(type.getType(), instanceOf(ParameterizedType.class));
assertThat(type.getGeneric().getType(), instanceOf(WildcardType.class));
}
@Test
public void typeVariableType() throws Exception {
ResolvableType type = ResolvableType.forField(Fields.class.getField("typeVariableType"));
assertThat(type.getType(), instanceOf(TypeVariable.class));
}
@Test
public void getComponentTypeForClassArray() throws Exception {
Field field = Fields.class.getField("arrayClassType");
ResolvableType type = ResolvableType.forField(field);
assertThat(type.isArray(), equalTo(true));
assertThat(type.getComponentType().getType(),
equalTo((Type) ((Class) field.getGenericType()).getComponentType()));
}
@Test
public void getComponentTypeForGenericArrayType() throws Exception {
ResolvableType type = ResolvableType.forField(Fields.class.getField("genericArrayType"));
assertThat(type.isArray(), equalTo(true));
assertThat(type.getComponentType().getType(),
equalTo(((GenericArrayType) type.getType()).getGenericComponentType()));
}
@Test
public void getComponentTypeForVariableThatResolvesToGenericArray() throws Exception {
ResolvableType type = ResolvableType.forClass(ListOfGenericArray.class).asCollection().getGeneric();
assertThat(type.isArray(), equalTo(true));
assertThat(type.getType(), instanceOf(TypeVariable.class));
assertThat(type.getComponentType().getType().toString(),
equalTo("java.util.List<java.lang.String>"));
}
@Test
public void getComponentTypeForNonArray() throws Exception {
ResolvableType type = ResolvableType.forClass(String.class);
assertThat(type.isArray(), equalTo(false));
assertThat(type.getComponentType(), equalTo(ResolvableType.NONE));
}
@Test
public void asCollection() throws Exception {
ResolvableType type = ResolvableType.forClass(ExtendsList.class).asCollection();
assertThat(type.resolve(), equalTo((Class) Collection.class));
assertThat(type.resolveGeneric(), equalTo((Class) CharSequence.class));
}
@Test
public void asMap() throws Exception {
ResolvableType type = ResolvableType.forClass(ExtendsMap.class).asMap();
assertThat(type.resolve(), equalTo((Class) Map.class));
assertThat(type.resolveGeneric(0), equalTo((Class) String.class));
assertThat(type.resolveGeneric(1), equalTo((Class) Integer.class));
}
@Test
public void asFromInterface() throws Exception {
ResolvableType type = ResolvableType.forClass(ExtendsList.class).as(List.class);
assertThat(type.getType().toString(), equalTo("java.util.List<E>"));
}
@Test
public void asFromInheritedInterface() throws Exception {
ResolvableType type = ResolvableType.forClass(ExtendsList.class).as(Collection.class);
assertThat(type.getType().toString(), equalTo("java.util.Collection<E>"));
}
@Test
public void asFromSuperType() throws Exception {
ResolvableType type = ResolvableType.forClass(ExtendsList.class).as(ArrayList.class);
assertThat(type.getType().toString(), equalTo("java.util.ArrayList<java.lang.CharSequence>"));
}
@Test
public void asFromInheritedSuperType() throws Exception {
ResolvableType type = ResolvableType.forClass(ExtendsList.class).as(List.class);
assertThat(type.getType().toString(), equalTo("java.util.List<E>"));
}
@Test
public void asNotFound() throws Exception {
ResolvableType type = ResolvableType.forClass(ExtendsList.class).as(Map.class);
assertThat(type, sameInstance(ResolvableType.NONE));
}
@Test
public void asSelf() throws Exception {
ResolvableType type = ResolvableType.forClass(ExtendsList.class);
assertThat(type.as(ExtendsList.class), equalTo(type));
}
@Test
public void getSuperType() throws Exception {
ResolvableType type = ResolvableType.forClass(ExtendsList.class).getSuperType();
assertThat(type.resolve(), equalTo((Class) ArrayList.class));
type = type.getSuperType();
assertThat(type.resolve(), equalTo((Class) AbstractList.class));
type = type.getSuperType();
assertThat(type.resolve(), equalTo((Class) AbstractCollection.class));
type = type.getSuperType();
assertThat(type.resolve(), equalTo((Class) Object.class));
}
@Test
public void getInterfaces() throws Exception {
ResolvableType type = ResolvableType.forClass(ExtendsList.class);
assertThat(type.getInterfaces().length, equalTo(0));
SortedSet<String> interfaces = new TreeSet<>();
for (ResolvableType interfaceType : type.getSuperType().getInterfaces()) {
interfaces.add(interfaceType.toString());
}
assertThat(interfaces.toString(), equalTo(
"["
+ "java.io.Serializable, "
+ "java.lang.Cloneable, "
+ "java.util.List<java.lang.CharSequence>, "
+ "java.util.RandomAccess"
+ "]"));
}
@Test
public void noSuperType() throws Exception {
assertThat(ResolvableType.forClass(Object.class).getSuperType(),
equalTo(ResolvableType.NONE));
}
@Test
public void noInterfaces() throws Exception {
assertThat(ResolvableType.forClass(Object.class).getInterfaces().length,
equalTo(0));
}
@Test
public void nested() throws Exception {
ResolvableType type = ResolvableType.forField(Fields.class.getField("nested"));
type = type.getNested(2);
assertThat(type.resolve(), equalTo((Class) Map.class));
assertThat(type.getGeneric(0).resolve(), equalTo((Class) Byte.class));
assertThat(type.getGeneric(1).resolve(), equalTo((Class) Long.class));
}
@Test
public void nestedWithIndexes() throws Exception {
ResolvableType type = ResolvableType.forField(Fields.class.getField("nested"));
type = type.getNested(2, Collections.singletonMap(2, 0));
assertThat(type.resolve(), equalTo((Class) Map.class));
assertThat(type.getGeneric(0).resolve(), equalTo((Class) String.class));
assertThat(type.getGeneric(1).resolve(), equalTo((Class) Integer.class));
}
@Test
public void nestedWithArray() throws Exception {
ResolvableType type = ResolvableType.forField(Fields.class.getField("genericArrayType"));
type = type.getNested(2);
assertThat(type.resolve(), equalTo((Class) List.class));
assertThat(type.resolveGeneric(), equalTo((Class) String.class));
}
@Test
public void getGeneric() throws Exception {
ResolvableType type = ResolvableType.forField(Fields.class.getField("stringList"));
assertThat(type.getGeneric().getType(), equalTo((Type) String.class));
}
@Test
public void getGenericByIndex() throws Exception {
ResolvableType type = ResolvableType.forField(Fields.class.getField("stringIntegerMultiValueMap"));
assertThat(type.getGeneric(0).getType(), equalTo((Type) String.class));
assertThat(type.getGeneric(1).getType(), equalTo((Type) Integer.class));
}
@Test
public void getGenericOfGeneric() throws Exception {
ResolvableType type = ResolvableType.forField(Fields.class.getField("stringListList"));
assertThat(type.getGeneric().getType().toString(), equalTo("java.util.List<java.lang.String>"));
assertThat(type.getGeneric().getGeneric().getType(), equalTo((Type) String.class));
}
@Test
public void genericOfGenericWithAs() throws Exception {
ResolvableType type = ResolvableType.forField(Fields.class.getField("stringListList")).asCollection();
assertThat(type.toString(), equalTo("java.util.Collection<java.util.List<java.lang.String>>"));
assertThat(type.getGeneric().asCollection().toString(), equalTo("java.util.Collection<java.lang.String>"));
}
@Test
public void getGenericOfGenericByIndexes() throws Exception {
ResolvableType type = ResolvableType.forField(Fields.class.getField("stringListList"));
assertThat(type.getGeneric(0, 0).getType(), equalTo((Type) String.class));
}
@Test
public void getGenericOutOfBounds() throws Exception {
ResolvableType type = ResolvableType.forClass(List.class, ExtendsList.class);
assertThat(type.getGeneric(0), not(equalTo(ResolvableType.NONE)));
assertThat(type.getGeneric(1), equalTo(ResolvableType.NONE));
assertThat(type.getGeneric(0, 1), equalTo(ResolvableType.NONE));
}
@Test
public void hasGenerics() throws Exception {
ResolvableType type = ResolvableType.forClass(ExtendsList.class);
assertThat(type.hasGenerics(), equalTo(false));
assertThat(type.asCollection().hasGenerics(), equalTo(true));
}
@Test
public void getGenericsFromParameterizedType() throws Exception {
ResolvableType type = ResolvableType.forClass(List.class, ExtendsList.class);
ResolvableType[] generics = type.getGenerics();
assertThat(generics.length, equalTo(1));
assertThat(generics[0].resolve(), equalTo((Class) CharSequence.class));
}
@Test
public void getGenericsFromClass() throws Exception {
ResolvableType type = ResolvableType.forClass(List.class);
ResolvableType[] generics = type.getGenerics();
assertThat(generics.length, equalTo(1));
assertThat(generics[0].getType().toString(), equalTo("E"));
}
@Test
public void noGetGenerics() throws Exception {
ResolvableType type = ResolvableType.forClass(ExtendsList.class);
ResolvableType[] generics = type.getGenerics();
assertThat(generics.length, equalTo(0));
}
@Test
public void getResolvedGenerics() throws Exception {
ResolvableType type = ResolvableType.forClass(List.class, ExtendsList.class);
Class<?>[] generics = type.resolveGenerics();
assertThat(generics.length, equalTo(1));
assertThat(generics[0], equalTo((Class) CharSequence.class));
}
@Test
public void resolveClassType() throws Exception {
ResolvableType type = ResolvableType.forField(Fields.class.getField("classType"));
assertThat(type.resolve(), equalTo((Class) List.class));
}
@Test
public void resolveParameterizedType() throws Exception {
ResolvableType type = ResolvableType.forField(Fields.class.getField("parameterizedType"));
assertThat(type.resolve(), equalTo((Class) List.class));
}
@Test
public void resolveArrayClassType() throws Exception {
ResolvableType type = ResolvableType.forField(Fields.class.getField("arrayClassType"));
assertThat(type.resolve(), equalTo((Class) List[].class));
}
@Test
public void resolveGenericArrayType() throws Exception {
ResolvableType type = ResolvableType.forField(Fields.class.getField("genericArrayType"));
assertThat(type.resolve(), equalTo((Class) List[].class));
assertThat(type.getComponentType().resolve(), equalTo((Class) List.class));
assertThat(type.getComponentType().getGeneric().resolve(), equalTo((Class) String.class));
}
@Test
public void resolveGenericMultiArrayType() throws Exception {
ResolvableType type = ResolvableType.forField(Fields.class.getField("genericMultiArrayType"));
assertThat(type.resolve(), equalTo((Class) List[][][].class));
assertThat(type.getComponentType().resolve(), equalTo((Class) List[][].class));
}
@Test
public void resolveGenericArrayFromGeneric() throws Exception {
ResolvableType type = ResolvableType.forField(Fields.class.getField("stringArrayList"));
ResolvableType generic = type.asCollection().getGeneric();
assertThat(generic.getType().toString(), equalTo("E"));
assertThat(generic.isArray(), equalTo(true));
assertThat(generic.resolve(), equalTo((Class) String[].class));
}
@Test
public void resolveVariableGenericArray() throws Exception {
ResolvableType type = ResolvableType.forField(Fields.class.getField("variableTypeGenericArray"), TypedFields.class);
assertThat(type.getType().toString(), equalTo("T[]"));
assertThat(type.isArray(), equalTo(true));
assertThat(type.resolve(), equalTo((Class) String[].class));
}
@Test
public void resolveVariableGenericArrayUnknown() throws Exception {
ResolvableType type = ResolvableType.forField(Fields.class.getField("variableTypeGenericArray"));
assertThat(type.getType().toString(), equalTo("T[]"));
assertThat(type.isArray(), equalTo(true));
assertThat(type.resolve(), nullValue());
}
@Test
public void resolveVariableGenericArrayUnknownWithFallback() throws Exception {
ResolvableType type = ResolvableType.forField(Fields.class.getField("variableTypeGenericArray"));
assertThat(type.getType().toString(), equalTo("T[]"));
assertThat(type.isArray(), equalTo(true));
assertThat(type.resolve(Object.class), equalTo((Class) Object.class));
}
@Test
public void resolveWildcardTypeUpperBounds() throws Exception {
ResolvableType type = ResolvableType.forField(Fields.class.getField("wildcardType"));
assertThat(type.getGeneric().resolve(), equalTo((Class) Number.class));
}
@Test
public void resolveWildcardLowerBounds() throws Exception {
ResolvableType type = ResolvableType.forField(Fields.class.getField("wildcardSuperType"));
assertThat(type.getGeneric().resolve(), equalTo((Class) Number.class));
}
@Test
public void resolveVariableFromFieldType() throws Exception {
ResolvableType type = ResolvableType.forField(Fields.class.getField("stringList"));
assertThat(type.resolve(), equalTo((Class) List.class));
assertThat(type.getGeneric().resolve(), equalTo((Class) String.class));
}
@Test
public void resolveVariableFromFieldTypeUnknown() throws Exception {
ResolvableType type = ResolvableType.forField(Fields.class.getField("parameterizedType"));
assertThat(type.resolve(), equalTo((Class) List.class));
assertThat(type.getGeneric().resolve(), nullValue());
}
@Test
public void resolveVariableFromInheritedField() throws Exception {
ResolvableType type = ResolvableType.forField(
Fields.class.getField("stringIntegerMultiValueMap")).as(Map.class);
assertThat(type.getGeneric(0).resolve(), equalTo((Class) String.class));
assertThat(type.getGeneric(1).resolve(), equalTo((Class) List.class));
assertThat(type.getGeneric(1, 0).resolve(), equalTo((Class) Integer.class));
}
@Test
public void resolveVariableFromInheritedFieldSwitched() throws Exception {
ResolvableType type = ResolvableType.forField(
Fields.class.getField("stringIntegerMultiValueMapSwitched")).as(Map.class);
assertThat(type.getGeneric(0).resolve(), equalTo((Class) String.class));
assertThat(type.getGeneric(1).resolve(), equalTo((Class) List.class));
assertThat(type.getGeneric(1, 0).resolve(), equalTo((Class) Integer.class));
}
@Test
public void doesResolveFromOuterOwner() throws Exception {
ResolvableType type = ResolvableType.forField(
Fields.class.getField("listOfListOfUnknown")).as(Collection.class);
assertThat(type.getGeneric(0).resolve(), equalTo((Class) List.class));
assertThat(type.getGeneric(0).as(Collection.class).getGeneric(0).as(Collection.class).resolve(), nullValue());
}
@Test
public void resolveBoundedTypeVariableResult() throws Exception {
ResolvableType type = ResolvableType.forMethodReturnType(Methods.class.getMethod("boundedTypeVaraibleResult"));
assertThat(type.resolve(), equalTo((Class) CharSequence.class));
}
@Test
public void resolveVariableNotFound() throws Exception {
ResolvableType type = ResolvableType.forMethodReturnType(Methods.class.getMethod("typedReturn"));
assertThat(type.resolve(), nullValue());
}
@Test
public void resolveTypeVaraibleFromMethodReturn() throws Exception {
ResolvableType type = ResolvableType.forMethodReturnType(Methods.class.getMethod("typedReturn"));
assertThat(type.resolve(), nullValue());
}
@Test
public void resolveTypeVaraibleFromMethodReturnWithInstanceClass() throws Exception {
ResolvableType type = ResolvableType.forMethodReturnType(
Methods.class.getMethod("typedReturn"), TypedMethods.class);
assertThat(type.resolve(), equalTo((Class) String.class));
}
@Test
public void resolveTypeVaraibleFromSimpleInterfaceType() {
ResolvableType type = ResolvableType.forClass(
MySimpleInterfaceType.class).as(MyInterfaceType.class);
assertThat(type.resolveGeneric(), equalTo((Class) String.class));
}
@Test
public void resolveTypeVaraibleFromSimpleCollectionInterfaceType() {
ResolvableType type = ResolvableType.forClass(
MyCollectionInterfaceType.class).as(MyInterfaceType.class);
assertThat(type.resolveGeneric(), equalTo((Class) Collection.class));
assertThat(type.resolveGeneric(0, 0), equalTo((Class) String.class));
}
@Test
public void resolveTypeVaraibleFromSimpleSuperclassType() {
ResolvableType type = ResolvableType.forClass(
MySimpleSuperclassType.class).as(MySuperclassType.class);
assertThat(type.resolveGeneric(), equalTo((Class) String.class));
}
@Test
public void resolveTypeVaraibleFromSimpleCollectionSuperclassType() {
ResolvableType type = ResolvableType.forClass(
MyCollectionSuperclassType.class).as(MySuperclassType.class);
assertThat(type.resolveGeneric(), equalTo((Class) Collection.class));
assertThat(type.resolveGeneric(0, 0), equalTo((Class) String.class));
}
@Test
public void resolveTypeVariableFromFieldTypeWithImplementsClass() throws Exception {
ResolvableType type = ResolvableType.forField(
Fields.class.getField("parameterizedType"), TypedFields.class);
assertThat(type.resolve(), equalTo((Class) List.class));
assertThat(type.getGeneric().resolve(), equalTo((Class) String.class));
}
@Test
public void resolveTypeVariableFromFieldTypeWithImplementsType() throws Exception {
ResolvableType implementationType = ResolvableType.forClassWithGenerics(
Fields.class, Integer.class);
ResolvableType type = ResolvableType.forField(
Fields.class.getField("parameterizedType"), implementationType);
assertThat(type.resolve(), equalTo((Class) List.class));
assertThat(type.getGeneric().resolve(), equalTo((Class) Integer.class));
}
@Test
public void resolveTypeVariableFromSuperType() throws Exception {
ResolvableType type = ResolvableType.forClass(ExtendsList.class);
assertThat(type.resolve(), equalTo((Class) ExtendsList.class));
assertThat(type.asCollection().resolveGeneric(),
equalTo((Class) CharSequence.class));
}
@Test
public void resolveTypeVariableFromClassWithImplementsClass() throws Exception {
ResolvableType type = ResolvableType.forClass(
MySuperclassType.class, MyCollectionSuperclassType.class);
assertThat(type.resolveGeneric(), equalTo((Class) Collection.class));
assertThat(type.resolveGeneric(0, 0), equalTo((Class) String.class));
}
@Test
public void resolveTypeVariableFromConstructorParameter() throws Exception {
Constructor<?> constructor = Constructors.class.getConstructor(List.class);
ResolvableType type = ResolvableType.forConstructorParameter(constructor, 0);
assertThat(type.resolve(), equalTo((Class) List.class));
assertThat(type.resolveGeneric(0), equalTo((Class) CharSequence.class));
}
@Test
public void resolveUnknownTypeVariableFromConstructorParameter() throws Exception {
Constructor<?> constructor = Constructors.class.getConstructor(Map.class);
ResolvableType type = ResolvableType.forConstructorParameter(constructor, 0);
assertThat(type.resolve(), equalTo((Class) Map.class));
assertThat(type.resolveGeneric(0), nullValue());
}
@Test
public void resolveTypeVariableFromConstructorParameterWithImplementsClass() throws Exception {
Constructor<?> constructor = Constructors.class.getConstructor(Map.class);
ResolvableType type = ResolvableType.forConstructorParameter(
constructor, 0, TypedConstructors.class);
assertThat(type.resolve(), equalTo((Class) Map.class));
assertThat(type.resolveGeneric(0), equalTo((Class) String.class));
}
@Test
public void resolveTypeVariableFromMethodParameter() throws Exception {
Method method = Methods.class.getMethod("typedParameter", Object.class);
ResolvableType type = ResolvableType.forMethodParameter(method, 0);
assertThat(type.resolve(), nullValue());
assertThat(type.getType().toString(), equalTo("T"));
}
@Test
public void resolveTypeVariableFromMethodParameterWithImplementsClass() throws Exception {
Method method = Methods.class.getMethod("typedParameter", Object.class);
ResolvableType type = ResolvableType.forMethodParameter(method, 0, TypedMethods.class);
assertThat(type.resolve(), equalTo((Class) String.class));
assertThat(type.getType().toString(), equalTo("T"));
}
@Test
public void resolveTypeVariableFromMethodParameterType() throws Exception {
Method method = Methods.class.getMethod("typedParameter", Object.class);
MethodParameter methodParameter = MethodParameter.forExecutable(method, 0);
ResolvableType type = ResolvableType.forMethodParameter(methodParameter);
assertThat(type.resolve(), nullValue());
assertThat(type.getType().toString(), equalTo("T"));
}
@Test
public void resolveTypeVariableFromMethodParameterTypeWithImplementsClass() throws Exception {
Method method = Methods.class.getMethod("typedParameter", Object.class);
MethodParameter methodParameter = MethodParameter.forExecutable(method, 0);
methodParameter.setContainingClass(TypedMethods.class);
ResolvableType type = ResolvableType.forMethodParameter(methodParameter);
assertThat(type.resolve(), equalTo((Class) String.class));
assertThat(type.getType().toString(), equalTo("T"));
}
@Test
public void resolveTypeVariableFromMethodParameterTypeWithImplementsType() throws Exception {
Method method = Methods.class.getMethod("typedParameter", Object.class);
MethodParameter methodParameter = MethodParameter.forExecutable(method, 0);
ResolvableType implementationType = ResolvableType.forClassWithGenerics(Methods.class, Integer.class);
ResolvableType type = ResolvableType.forMethodParameter(methodParameter, implementationType);
assertThat(type.resolve(), equalTo((Class) Integer.class));
assertThat(type.getType().toString(), equalTo("T"));
}
@Test
public void resolveTypeVariableFromMethodReturn() throws Exception {
Method method = Methods.class.getMethod("typedReturn");
ResolvableType type = ResolvableType.forMethodReturnType(method);
assertThat(type.resolve(), nullValue());
assertThat(type.getType().toString(), equalTo("T"));
}
@Test
public void resolveTypeVariableFromMethodReturnWithImplementsClass() throws Exception {
Method method = Methods.class.getMethod("typedReturn");
ResolvableType type = ResolvableType.forMethodReturnType(method, TypedMethods.class);
assertThat(type.resolve(), equalTo((Class) String.class));
assertThat(type.getType().toString(), equalTo("T"));
}
@Test
public void resolveTypeVariableFromType() throws Exception {
Type sourceType = Methods.class.getMethod("typedReturn").getGenericReturnType();
ResolvableType type = ResolvableType.forType(sourceType);
assertThat(type.resolve(), nullValue());
assertThat(type.getType().toString(), equalTo("T"));
}
@Test
public void resolveTypeVariableFromTypeWithVariableResolver() throws Exception {
Type sourceType = Methods.class.getMethod("typedReturn").getGenericReturnType();
ResolvableType type = ResolvableType.forType(
sourceType, ResolvableType.forClass(TypedMethods.class).as(Methods.class).asVariableResolver());
assertThat(type.resolve(), equalTo((Class) String.class));
assertThat(type.getType().toString(), equalTo("T"));
}
@Test
public void resolveTypeWithCustomVariableResolver() throws Exception {
VariableResolver variableResolver = mock(VariableResolver.class);
given(variableResolver.getSource()).willReturn(this);
ResolvableType longType = ResolvableType.forClass(Long.class);
given(variableResolver.resolveVariable(any())).willReturn(longType);
ResolvableType variable = ResolvableType.forType(
Fields.class.getField("typeVariableType").getGenericType(), variableResolver);
ResolvableType parameterized = ResolvableType.forType(
Fields.class.getField("parameterizedType").getGenericType(), variableResolver);
assertThat(variable.resolve(), equalTo((Class) Long.class));
assertThat(parameterized.resolve(), equalTo((Class) List.class));
assertThat(parameterized.resolveGeneric(), equalTo((Class) Long.class));
verify(variableResolver, atLeastOnce()).resolveVariable(this.typeVariableCaptor.capture());
assertThat(this.typeVariableCaptor.getValue().getName(), equalTo("T"));
}
@Test
public void toStrings() throws Exception {
assertThat(ResolvableType.NONE.toString(), equalTo("?"));
assertFieldToStringValue("classType", "java.util.List<?>");
assertFieldToStringValue("typeVariableType", "?");
assertFieldToStringValue("parameterizedType", "java.util.List<?>");
assertFieldToStringValue("arrayClassType", "java.util.List<?>[]");
assertFieldToStringValue("genericArrayType", "java.util.List<java.lang.String>[]");
assertFieldToStringValue("genericMultiArrayType", "java.util.List<java.lang.String>[][][]");
assertFieldToStringValue("wildcardType", "java.util.List<java.lang.Number>");
assertFieldToStringValue("wildcardSuperType", "java.util.List<java.lang.Number>");
assertFieldToStringValue("charSequenceList", "java.util.List<java.lang.CharSequence>");
assertFieldToStringValue("stringList", "java.util.List<java.lang.String>");
assertFieldToStringValue("stringListList", "java.util.List<java.util.List<java.lang.String>>");
assertFieldToStringValue("stringArrayList", "java.util.List<java.lang.String[]>");
assertFieldToStringValue("stringIntegerMultiValueMap", "org.springframework.util.MultiValueMap<java.lang.String, java.lang.Integer>");
assertFieldToStringValue("stringIntegerMultiValueMapSwitched", VariableNameSwitch.class.getName() + "<java.lang.Integer, java.lang.String>");
assertFieldToStringValue("listOfListOfUnknown", "java.util.List<java.util.List<?>>");
assertTypedFieldToStringValue("typeVariableType", "java.lang.String");
assertTypedFieldToStringValue("parameterizedType", "java.util.List<java.lang.String>");
assertThat(ResolvableType.forClass(ListOfGenericArray.class).toString(), equalTo(ListOfGenericArray.class.getName()));
assertThat(ResolvableType.forClass(List.class, ListOfGenericArray.class).toString(), equalTo("java.util.List<java.util.List<java.lang.String>[]>"));
}
@Test
public void getSource() throws Exception {
Class<?> classType = MySimpleInterfaceType.class;
Field basicField = Fields.class.getField("classType");
Field field = Fields.class.getField("charSequenceList");
Method method = Methods.class.getMethod("charSequenceParameter", List.class);
MethodParameter methodParameter = MethodParameter.forExecutable(method, 0);
assertThat(ResolvableType.forField(basicField).getSource(), equalTo((Object) basicField));
assertThat(ResolvableType.forField(field).getSource(), equalTo((Object) field));
assertThat(ResolvableType.forMethodParameter(methodParameter).getSource(), equalTo((Object) methodParameter));
assertThat(ResolvableType.forMethodParameter(method, 0).getSource(), equalTo((Object) methodParameter));
assertThat(ResolvableType.forClass(classType).getSource(), equalTo((Object) classType));
assertThat(ResolvableType.forClass(classType).getSuperType().getSource(), equalTo((Object) classType.getGenericSuperclass()));
}
private void assertFieldToStringValue(String field, String expected) throws Exception {
ResolvableType type = ResolvableType.forField(Fields.class.getField(field));
assertThat("field " + field + " toString", type.toString(), equalTo(expected));
}
private void assertTypedFieldToStringValue(String field, String expected) throws Exception {
ResolvableType type = ResolvableType.forField(Fields.class.getField(field), TypedFields.class);
assertThat("field " + field + " toString", type.toString(), equalTo(expected));
}
@Test
public void resolveFromOuterClass() throws Exception {
Field field = EnclosedInParameterizedType.InnerTyped.class.getField("field");
ResolvableType type = ResolvableType.forField(field, TypedEnclosedInParameterizedType.TypedInnerTyped.class);
assertThat(type.resolve(), equalTo((Type) Integer.class));
}
@Test
public void resolveFromClassWithGenerics() throws Exception {
ResolvableType type = ResolvableType.forClassWithGenerics(List.class, ResolvableType.forClassWithGenerics(List.class, String.class));
assertThat(type.asCollection().toString(), equalTo("java.util.Collection<java.util.List<java.lang.String>>"));
assertThat(type.asCollection().getGeneric().toString(), equalTo("java.util.List<java.lang.String>"));
assertThat(type.asCollection().getGeneric().asCollection().toString(), equalTo("java.util.Collection<java.lang.String>"));
assertThat(type.toString(), equalTo("java.util.List<java.util.List<java.lang.String>>"));
assertThat(type.asCollection().getGeneric().getGeneric().resolve(), equalTo((Type) String.class));
}
@Test
public void isAssignableFromMustNotBeNull() throws Exception {
this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("Type must not be null");
ResolvableType.forClass(Object.class).isAssignableFrom((ResolvableType) null);
}
@Test
public void isAssignableFromForNone() throws Exception {
ResolvableType objectType = ResolvableType.forClass(Object.class);
assertThat(objectType.isAssignableFrom(ResolvableType.NONE), equalTo(false));
assertThat(ResolvableType.NONE.isAssignableFrom(objectType), equalTo(false));
}
@Test
public void isAssignableFromForClassAndClass() throws Exception {
ResolvableType objectType = ResolvableType.forClass(Object.class);
ResolvableType charSequenceType = ResolvableType.forClass(CharSequence.class);
ResolvableType stringType = ResolvableType.forClass(String.class);
assertAssignable(objectType, objectType, charSequenceType, stringType).equalTo(true, true, true);
assertAssignable(charSequenceType, objectType, charSequenceType, stringType).equalTo(false, true, true);
assertAssignable(stringType, objectType, charSequenceType, stringType).equalTo(false, false, true);
assertTrue(objectType.isAssignableFrom(String.class));
assertTrue(objectType.isAssignableFrom(StringBuilder.class));
assertTrue(charSequenceType.isAssignableFrom(String.class));
assertTrue(charSequenceType.isAssignableFrom(StringBuilder.class));
assertTrue(stringType.isAssignableFrom(String.class));
assertFalse(stringType.isAssignableFrom(StringBuilder.class));
assertTrue(objectType.isInstance("a String"));
assertTrue(objectType.isInstance(new StringBuilder("a StringBuilder")));
assertTrue(charSequenceType.isInstance("a String"));
assertTrue(charSequenceType.isInstance(new StringBuilder("a StringBuilder")));
assertTrue(stringType.isInstance("a String"));
assertFalse(stringType.isInstance(new StringBuilder("a StringBuilder")));
}
@Test
public void isAssignableFromCannotBeResolved() throws Exception {
ResolvableType objectType = ResolvableType.forClass(Object.class);
ResolvableType unresolvableVariable = ResolvableType.forField(AssignmentBase.class.getField("o"));
assertThat(unresolvableVariable.resolve(), nullValue());
assertAssignable(objectType, unresolvableVariable).equalTo(true);
assertAssignable(unresolvableVariable, objectType).equalTo(true);
}
@Test
public void isAssignableFromForClassAndSimpleVariable() throws Exception {
ResolvableType objectType = ResolvableType.forClass(Object.class);
ResolvableType charSequenceType = ResolvableType.forClass(CharSequence.class);
ResolvableType stringType = ResolvableType.forClass(String.class);
ResolvableType objectVariable = ResolvableType.forField(AssignmentBase.class.getField("o"), Assignment.class);
ResolvableType charSequenceVariable = ResolvableType.forField(AssignmentBase.class.getField("c"), Assignment.class);
ResolvableType stringVariable = ResolvableType.forField(AssignmentBase.class.getField("s"), Assignment.class);
assertAssignable(objectType, objectVariable, charSequenceVariable, stringVariable).equalTo(true, true, true);
assertAssignable(charSequenceType, objectVariable, charSequenceVariable, stringVariable).equalTo(false, true, true);
assertAssignable(stringType, objectVariable, charSequenceVariable, stringVariable).equalTo(false, false, true);
assertAssignable(objectVariable, objectType, charSequenceType, stringType).equalTo(true, true, true);
assertAssignable(charSequenceVariable, objectType, charSequenceType, stringType).equalTo(false, true, true);
assertAssignable(stringVariable, objectType, charSequenceType, stringType).equalTo(false, false, true);
assertAssignable(objectVariable, objectVariable, charSequenceVariable, stringVariable).equalTo(true, true, true);
assertAssignable(charSequenceVariable, objectVariable, charSequenceVariable, stringVariable).equalTo(false, true, true);
assertAssignable(stringVariable, objectVariable, charSequenceVariable, stringVariable).equalTo(false, false, true);
}
@Test
public void isAssignableFromForSameClassNonExtendsGenerics() throws Exception {
ResolvableType objectList = ResolvableType.forField(AssignmentBase.class.getField("listo"), Assignment.class);
ResolvableType stringList = ResolvableType.forField(AssignmentBase.class.getField("lists"), Assignment.class);
assertAssignable(stringList, objectList).equalTo(false);
assertAssignable(objectList, stringList).equalTo(false);
assertAssignable(stringList, stringList).equalTo(true);
}
@Test
public void isAssignableFromForSameClassExtendsGenerics() throws Exception {
// Generic assignment can be a little confusing, given:
//
// List<CharSequence> c1, List<? extends CharSequence> c2, List<String> s;
//
// c2 = s; is allowed and is often used for argument input, for example
// see List.addAll(). You can get items from c2 but you cannot add items without
// getting a generic type 'is not applicable for the arguments' error. This makes
// sense since if you added a StringBuffer to c2 it would break the rules on s.
//
// c1 = s; not allowed. Since there is no '? extends' to cause the generic
// 'is not applicable for the arguments' error when adding (which would pollute
// s).
ResolvableType objectList = ResolvableType.forField(AssignmentBase.class.getField("listo"), Assignment.class);
ResolvableType charSequenceList = ResolvableType.forField(AssignmentBase.class.getField("listc"), Assignment.class);
ResolvableType stringList = ResolvableType.forField(AssignmentBase.class.getField("lists"), Assignment.class);
ResolvableType extendsObjectList = ResolvableType.forField(AssignmentBase.class.getField("listxo"), Assignment.class);
ResolvableType extendsCharSequenceList = ResolvableType.forField(AssignmentBase.class.getField("listxc"), Assignment.class);
ResolvableType extendsStringList = ResolvableType.forField(AssignmentBase.class.getField("listxs"), Assignment.class);
assertAssignable(objectList, extendsObjectList, extendsCharSequenceList, extendsStringList).equalTo(false, false, false);
assertAssignable(charSequenceList, extendsObjectList, extendsCharSequenceList, extendsStringList).equalTo(false, false, false);
assertAssignable(stringList, extendsObjectList, extendsCharSequenceList, extendsStringList).equalTo(false, false, false);
assertAssignable(extendsObjectList, objectList, charSequenceList, stringList).equalTo(true, true, true);
assertAssignable(extendsObjectList, extendsObjectList, extendsCharSequenceList, extendsStringList).equalTo(true, true, true);
assertAssignable(extendsCharSequenceList, extendsObjectList, extendsCharSequenceList, extendsStringList).equalTo(false, true, true);
assertAssignable(extendsCharSequenceList, objectList, charSequenceList, stringList).equalTo(false, true, true);
assertAssignable(extendsStringList, extendsObjectList, extendsCharSequenceList, extendsStringList).equalTo(false, false, true);
assertAssignable(extendsStringList, objectList, charSequenceList, stringList).equalTo(false, false, true);
}
@Test
public void isAssignableFromForDifferentClassesWithGenerics() throws Exception {
ResolvableType extendsCharSequenceCollection = ResolvableType.forField(AssignmentBase.class.getField("collectionxc"), Assignment.class);
ResolvableType charSequenceCollection = ResolvableType.forField(AssignmentBase.class.getField("collectionc"), Assignment.class);
ResolvableType charSequenceList = ResolvableType.forField(AssignmentBase.class.getField("listc"), Assignment.class);
ResolvableType extendsCharSequenceList = ResolvableType.forField(AssignmentBase.class.getField("listxc"), Assignment.class);
ResolvableType extendsStringList = ResolvableType.forField(AssignmentBase.class.getField("listxs"), Assignment.class);
assertAssignable(extendsCharSequenceCollection, charSequenceCollection, charSequenceList, extendsCharSequenceList, extendsStringList)
.equalTo(true, true, true, true);
assertAssignable(charSequenceCollection, charSequenceList, extendsCharSequenceList, extendsStringList)
.equalTo(true, false, false);
assertAssignable(charSequenceList, extendsCharSequenceCollection, charSequenceCollection)
.equalTo(false, false);
assertAssignable(extendsCharSequenceList, extendsCharSequenceCollection, charSequenceCollection)
.equalTo(false, false);
assertAssignable(extendsStringList, charSequenceCollection, charSequenceList, extendsCharSequenceList)
.equalTo(false, false, false);
}
@Test
public void isAssignableFromForArrays() throws Exception {
ResolvableType object = ResolvableType.forField(AssignmentBase.class.getField("o"), Assignment.class);
ResolvableType objectArray = ResolvableType.forField(AssignmentBase.class.getField("oarray"), Assignment.class);
ResolvableType charSequenceArray = ResolvableType.forField(AssignmentBase.class.getField("carray"), Assignment.class);
ResolvableType stringArray = ResolvableType.forField(AssignmentBase.class.getField("sarray"), Assignment.class);
assertAssignable(object, objectArray, charSequenceArray, stringArray).
equalTo(true, true, true);
assertAssignable(objectArray, object, objectArray, charSequenceArray, stringArray).
equalTo(false, true, true, true);
assertAssignable(charSequenceArray, object, objectArray, charSequenceArray, stringArray).
equalTo(false, false, true, true);
assertAssignable(stringArray, object, objectArray, charSequenceArray, stringArray).
equalTo(false, false, false, true);
}
@Test
public void isAssignableFromForWildcards() throws Exception {
ResolvableType object = ResolvableType.forClass(Object.class);
ResolvableType charSequence = ResolvableType.forClass(CharSequence.class);
ResolvableType string = ResolvableType.forClass(String.class);
ResolvableType extendsAnon = ResolvableType.forField(AssignmentBase.class.getField("listAnon"), Assignment.class).getGeneric();
ResolvableType extendsObject = ResolvableType.forField(AssignmentBase.class.getField("listxo"), Assignment.class).getGeneric();
ResolvableType extendsCharSequence = ResolvableType.forField(AssignmentBase.class.getField("listxc"), Assignment.class).getGeneric();
ResolvableType extendsString = ResolvableType.forField(AssignmentBase.class.getField("listxs"), Assignment.class).getGeneric();
ResolvableType superObject = ResolvableType.forField(AssignmentBase.class.getField("listso"), Assignment.class).getGeneric();
ResolvableType superCharSequence = ResolvableType.forField(AssignmentBase.class.getField("listsc"), Assignment.class).getGeneric();
ResolvableType superString = ResolvableType.forField(AssignmentBase.class.getField("listss"), Assignment.class).getGeneric();
// Language Spec 4.5.1. Type Arguments and Wildcards
// ? extends T <= ? extends S if T <: S
assertAssignable(extendsCharSequence, extendsObject, extendsCharSequence, extendsString).
equalTo(false, true, true);
assertAssignable(extendsCharSequence, object, charSequence, string).
equalTo(false, true, true);
// ? super T <= ? super S if S <: T
assertAssignable(superCharSequence, superObject, superCharSequence, superString).
equalTo(true, true, false);
assertAssignable(superCharSequence, object, charSequence, string).
equalTo(true, true, false);
// [Implied] super / extends cannot be mixed
assertAssignable(superCharSequence, extendsObject, extendsCharSequence, extendsString).
equalTo(false, false, false);
assertAssignable(extendsCharSequence, superObject, superCharSequence, superString).
equalTo(false, false, false);
// T <= T
assertAssignable(charSequence, object, charSequence, string).
equalTo(false, true, true);
// T <= ? extends T
assertAssignable(extendsCharSequence, object, charSequence, string).
equalTo(false, true, true);
assertAssignable(charSequence, extendsObject, extendsCharSequence, extendsString).
equalTo(false, false, false);
assertAssignable(extendsAnon, object, charSequence, string).
equalTo(true, true, true);
// T <= ? super T
assertAssignable(superCharSequence, object, charSequence, string).
equalTo(true, true, false);
assertAssignable(charSequence, superObject, superCharSequence, superString).
equalTo(false, false, false);
}
@Test
public void isAssignableFromForComplexWildcards() throws Exception {
ResolvableType complex1 = ResolvableType.forField(AssignmentBase.class.getField("complexWildcard1"));
ResolvableType complex2 = ResolvableType.forField(AssignmentBase.class.getField("complexWildcard2"));
ResolvableType complex3 = ResolvableType.forField(AssignmentBase.class.getField("complexWildcard3"));
ResolvableType complex4 = ResolvableType.forField(AssignmentBase.class.getField("complexWildcard4"));
assertAssignable(complex1, complex2).equalTo(true);
assertAssignable(complex2, complex1).equalTo(false);
assertAssignable(complex3, complex4).equalTo(true);
assertAssignable(complex4, complex3).equalTo(false);
}
@Test
public void hashCodeAndEquals() throws Exception {
ResolvableType forClass = ResolvableType.forClass(List.class);
ResolvableType forFieldDirect = ResolvableType.forField(Fields.class.getDeclaredField("stringList"));
ResolvableType forFieldViaType = ResolvableType.forType(Fields.class.getDeclaredField("stringList").getGenericType(), (VariableResolver) null);
ResolvableType forFieldWithImplementation = ResolvableType.forField(Fields.class.getDeclaredField("stringList"), TypedFields.class);
assertThat(forClass, equalTo(forClass));
assertThat(forClass.hashCode(), equalTo(forClass.hashCode()));
assertThat(forClass, not(equalTo(forFieldDirect)));
assertThat(forClass, not(equalTo(forFieldWithImplementation)));
assertThat(forFieldDirect, equalTo(forFieldDirect));
assertThat(forFieldDirect, not(equalTo(forFieldViaType)));
assertThat(forFieldDirect, not(equalTo(forFieldWithImplementation)));
}
@SuppressWarnings("unused")
private HashMap<Integer, List<String>> myMap;
@Test
public void javaDocSample() throws Exception {
ResolvableType t = ResolvableType.forField(getClass().getDeclaredField("myMap"));
assertThat(t.getSuperType().toString(), equalTo("java.util.AbstractMap<java.lang.Integer, java.util.List<java.lang.String>>"));
assertThat(t.asMap().toString(), equalTo("java.util.Map<java.lang.Integer, java.util.List<java.lang.String>>"));
assertThat(t.getGeneric(0).resolve(), equalTo((Class)Integer.class));
assertThat(t.getGeneric(1).resolve(), equalTo((Class)List.class));
assertThat(t.getGeneric(1).toString(), equalTo("java.util.List<java.lang.String>"));
assertThat(t.resolveGeneric(1, 0), equalTo((Class) String.class));
}
@Test
public void forClassWithGenerics() throws Exception {
ResolvableType elementType = ResolvableType.forClassWithGenerics(Map.class, Integer.class, String.class);
ResolvableType listType = ResolvableType.forClassWithGenerics(List.class, elementType);
assertThat(listType.toString(), equalTo("java.util.List<java.util.Map<java.lang.Integer, java.lang.String>>"));
}
@Test
public void classWithGenericsAs() throws Exception {
ResolvableType type = ResolvableType.forClassWithGenerics(MultiValueMap.class, Integer.class, String.class);
assertThat(type.asMap().toString(), equalTo("java.util.Map<java.lang.Integer, java.util.List<java.lang.String>>"));
}
@Test
public void forClassWithMismatchedGenerics() throws Exception {
thrown.expect(IllegalArgumentException.class);
thrown.expectMessage("Mismatched number of generics specified");
ResolvableType.forClassWithGenerics(Map.class, Integer.class);
}
@Test
public void forArrayComponent() throws Exception {
ResolvableType elementType = ResolvableType.forField(Fields.class.getField("stringList"));
ResolvableType type = ResolvableType.forArrayComponent(elementType);
assertThat(type.toString(), equalTo("java.util.List<java.lang.String>[]"));
assertThat(type.resolve(), equalTo((Class) List[].class));
}
@Test
public void serialize() throws Exception {
testSerialization(ResolvableType.forClass(List.class));
testSerialization(ResolvableType.forField(Fields.class.getField("charSequenceList")));
testSerialization(ResolvableType.forMethodParameter(Methods.class.getMethod("charSequenceParameter", List.class), 0));
testSerialization(ResolvableType.forMethodReturnType(Methods.class.getMethod("charSequenceReturn")));
testSerialization(ResolvableType.forConstructorParameter(Constructors.class.getConstructor(List.class), 0));
testSerialization(ResolvableType.forField(Fields.class.getField("charSequenceList")).getGeneric());
testSerialization(ResolvableType.forField(Fields.class.getField("charSequenceList")).asCollection());
testSerialization(ResolvableType.forClass(ExtendsMap.class).getSuperType());
ResolvableType deserializedNone = testSerialization(ResolvableType.NONE);
assertThat(deserializedNone, sameInstance(ResolvableType.NONE));
}
@Test
public void canResolveVoid() throws Exception {
ResolvableType type = ResolvableType.forClass(void.class);
assertThat(type.resolve(), equalTo((Class) void.class));
}
@Test
public void narrow() throws Exception {
ResolvableType type = ResolvableType.forField(Fields.class.getField("stringList"));
ResolvableType narrow = ResolvableType.forType(ArrayList.class, type);
assertThat(narrow.getGeneric().resolve(), equalTo((Class) String.class));
}
@Test
public void hasUnresolvableGenerics() throws Exception {
ResolvableType type = ResolvableType.forField(Fields.class.getField("stringList"));
assertThat(type.hasUnresolvableGenerics(), equalTo(false));
}
@Test
public void hasUnresolvableGenericsBasedOnOwnGenerics() throws Exception {
ResolvableType type = ResolvableType.forClass(List.class);
assertThat(type.hasUnresolvableGenerics(), equalTo(true));
}
@Test
public void hasUnresolvableGenericsWhenSelfNotResolvable() throws Exception {
ResolvableType type = ResolvableType.forClass(List.class).getGeneric();
assertThat(type.hasUnresolvableGenerics(), equalTo(false));
}
@Test
public void hasUnresolvableGenericsWhenImplementesRawInterface() throws Exception {
ResolvableType type = ResolvableType.forClass(MySimpleInterfaceTypeWithImplementsRaw.class);
for (ResolvableType generic : type.getGenerics()) {
assertThat(generic.resolve(), not(nullValue()));
}
assertThat(type.hasUnresolvableGenerics(), equalTo(true));
}
@Test
public void hasUnresolvableGenericsWhenExtends() throws Exception {
ResolvableType type = ResolvableType.forClass(ExtendsMySimpleInterfaceTypeWithImplementsRaw.class);
for (ResolvableType generic : type.getGenerics()) {
assertThat(generic.resolve(), not(nullValue()));
}
assertThat(type.hasUnresolvableGenerics(), equalTo(true));
}
@Test
public void testSpr11219() throws Exception {
ResolvableType type = ResolvableType.forField(BaseProvider.class.getField("stuff"), BaseProvider.class);
assertTrue(type.getNested(2).isAssignableFrom(ResolvableType.forClass(BaseImplementation.class)));
assertEquals("java.util.Collection<org.springframework.core.ResolvableTypeTests$IBase<?>>", type.toString());
}
@Test
public void testSpr12701() throws Exception {
ResolvableType resolvableType = ResolvableType.forClassWithGenerics(Callable.class, String.class);
Type type = resolvableType.getType();
assertThat(type, is(instanceOf(ParameterizedType.class)));
assertThat(((ParameterizedType) type).getRawType(), is(equalTo(Callable.class)));
assertThat(((ParameterizedType) type).getActualTypeArguments().length, is(equalTo(1)));
assertThat(((ParameterizedType) type).getActualTypeArguments()[0], is(equalTo(String.class)));
}
@Test
public void testSpr14648() throws Exception {
ResolvableType collectionClass = ResolvableType.forRawClass(Collection.class);
ResolvableType setClass = ResolvableType.forRawClass(Set.class);
ResolvableType fromReturnType = ResolvableType.forMethodReturnType(Methods.class.getMethod("wildcardSet"));
assertTrue(collectionClass.isAssignableFrom(fromReturnType));
assertTrue(setClass.isAssignableFrom(fromReturnType));
}
private ResolvableType testSerialization(ResolvableType type) throws Exception {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(type);
oos.close();
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
ResolvableType read = (ResolvableType) ois.readObject();
assertThat(read, equalTo(type));
assertThat(read.getType(), equalTo(type.getType()));
assertThat(read.resolve(), equalTo((Class) type.resolve()));
return read;
}
private static AssertAssignbleMatcher assertAssignable(final ResolvableType type, final ResolvableType... fromTypes) {
return new AssertAssignbleMatcher() {
@Override
public void equalTo(boolean... values) {
for (int i = 0; i < fromTypes.length; i++) {
assertThat(stringDesc(type) + " isAssignableFrom " + stringDesc(fromTypes[i]),
type.isAssignableFrom(fromTypes[i]), Matchers.equalTo(values[i]));
}
}
};
}
private static String stringDesc(ResolvableType type) {
if (type == ResolvableType.NONE) {
return "NONE";
}
if (type.getType().getClass().equals(Class.class)) {
return type.toString();
}
return type.getType() + ":" + type;
}
private static interface AssertAssignbleMatcher {
void equalTo(boolean... values);
}
@SuppressWarnings("serial")
static class ExtendsList extends ArrayList<CharSequence> {
}
@SuppressWarnings("serial")
static class ExtendsMap extends HashMap<String, Integer> {
}
static class Fields<T> {
public List classType;
public T typeVariableType;
public List<T> parameterizedType;
public List[] arrayClassType;
public List<String>[] genericArrayType;
public List<String>[][][] genericMultiArrayType;
public List<? extends Number> wildcardType;
public List<? super Number> wildcardSuperType = new ArrayList<Object>();
public List<CharSequence> charSequenceList;
public List<String> stringList;
public List<List<String>> stringListList;
public List<String[]> stringArrayList;
public MultiValueMap<String, Integer> stringIntegerMultiValueMap;
public VariableNameSwitch<Integer, String> stringIntegerMultiValueMapSwitched;
public List<List> listOfListOfUnknown;
@SuppressWarnings("unused")
private List<String> privateField;
@SuppressWarnings("unused")
private List<String> otherPrivateField;
public Map<Map<String, Integer>, Map<Byte, Long>> nested;
public T[] variableTypeGenericArray;
}
static class TypedFields extends Fields<String> {
}
interface Methods<T> {
List<CharSequence> charSequenceReturn();
void charSequenceParameter(List<CharSequence> cs);
<R extends CharSequence & Serializable> R boundedTypeVaraibleResult();
void nested(Map<Map<String, Integer>, Map<Byte, Long>> p);
void typedParameter(T p);
T typedReturn();
Set<?> wildcardSet();
}
static class AssignmentBase<O, C, S> {
public O o;
public C c;
public S s;
public List<O> listo;
public List<C> listc;
public List<S> lists;
public List<?> listAnon;
public List<? extends O> listxo;
public List<? extends C> listxc;
public List<? extends S> listxs;
public List<? super O> listso;
public List<? super C> listsc;
public List<? super S> listss;
public O[] oarray;
public C[] carray;
public S[] sarray;
public Collection<C> collectionc;
public Collection<? extends C> collectionxc;
public Map<? super Integer, List<String>> complexWildcard1;
public MultiValueMap<Number, String> complexWildcard2;
public Collection<? extends Collection<? extends CharSequence>> complexWildcard3;
public List<List<String>> complexWildcard4;
}
static class Assignment extends AssignmentBase<Object, CharSequence, String> {
}
interface TypedMethods extends Methods<String> {
}
static class Constructors<T> {
public Constructors(List<CharSequence> p) {
}
public Constructors(Map<T, Long> p) {
}
}
static class TypedConstructors extends Constructors<String> {
public TypedConstructors(List<CharSequence> p) {
super(p);
}
public TypedConstructors(Map<String, Long> p) {
super(p);
}
}
public interface MyInterfaceType<T> {
}
public class MyGenericInterfaceType<T> implements MyInterfaceType<T>, ResolvableTypeProvider {
private final Class<T> type;
public MyGenericInterfaceType(Class<T> type) {
this.type = type;
}
@Override
public ResolvableType getResolvableType() {
if (this.type == null) {
return null;
}
return ResolvableType.forClassWithGenerics(getClass(), this.type);
}
}
public class MySimpleInterfaceType implements MyInterfaceType<String> {
}
public abstract class MySimpleInterfaceTypeWithImplementsRaw implements MyInterfaceType<String>, List {
}
public abstract class ExtendsMySimpleInterfaceTypeWithImplementsRaw extends MySimpleInterfaceTypeWithImplementsRaw {
}
public class MyCollectionInterfaceType implements MyInterfaceType<Collection<String>> {
}
public abstract class MySuperclassType<T> {
}
public class MySimpleSuperclassType extends MySuperclassType<String> {
}
public class MyCollectionSuperclassType extends MySuperclassType<Collection<String>> {
}
interface Wildcard<T extends Number> extends List<T> {
}
interface RawExtendsWildcard extends Wildcard {
}
interface VariableNameSwitch<V, K> extends MultiValueMap<K, V> {
}
interface ListOfGenericArray extends List<List<String>[]> {
}
static class EnclosedInParameterizedType<T> {
static class InnerRaw {
}
class InnerTyped<Y> {
public T field;
}
}
static class TypedEnclosedInParameterizedType extends EnclosedInParameterizedType<Integer> {
class TypedInnerTyped extends InnerTyped<Long> {
}
}
public interface IProvider<P> {
}
public interface IBase<BT extends IBase<BT>> {
}
public abstract class AbstractBase<BT extends IBase<BT>> implements IBase<BT> {
}
public class BaseImplementation extends AbstractBase<BaseImplementation> {
}
public class BaseProvider<BT extends IBase<BT>> implements IProvider<IBase<BT>> {
public Collection<IBase<BT>> stuff;
}
}