/* * 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; } }