| /** |
| * Copyright (C) 2008 Google Inc. |
| * |
| * 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 com.google.inject; |
| |
| import static com.google.inject.Asserts.assertEqualsBothWays; |
| import static com.google.inject.Asserts.assertNotSerializable; |
| import com.google.inject.internal.util.ImmutableList; |
| import com.google.inject.util.Types; |
| import static com.google.inject.util.Types.arrayOf; |
| import static com.google.inject.util.Types.listOf; |
| import static com.google.inject.util.Types.newParameterizedType; |
| import static com.google.inject.util.Types.newParameterizedTypeWithOwner; |
| import static com.google.inject.util.Types.setOf; |
| import java.io.IOException; |
| import java.lang.reflect.Constructor; |
| import java.lang.reflect.Field; |
| import java.lang.reflect.Method; |
| import java.lang.reflect.Type; |
| import java.util.AbstractCollection; |
| import java.util.AbstractList; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import junit.framework.TestCase; |
| |
| /** |
| * This test checks that TypeLiteral can perform type resolution on its members. |
| * |
| * @author jessewilson@google.com (Jesse Wilson) |
| */ |
| public class TypeLiteralTypeResolutionTest extends TestCase { |
| Type arrayListOfString = newParameterizedType(ArrayList.class, String.class); |
| Type hasGenericFieldsOfShort = newParameterizedTypeWithOwner( |
| getClass(), HasGenericFields.class, Short.class); |
| Type hasGenericConstructorOfShort = newParameterizedTypeWithOwner( |
| getClass(), GenericConstructor.class, Short.class); |
| Type throwerOfNpe = newParameterizedTypeWithOwner( |
| getClass(), Thrower.class, NullPointerException.class); |
| Type hasArrayOfShort = newParameterizedTypeWithOwner(getClass(), HasArray.class, Short.class); |
| Type hasRelatedOfString = newParameterizedTypeWithOwner( |
| getClass(), HasRelated.class, String.class, String.class); |
| Type mapK = Map.class.getTypeParameters()[0]; |
| Type hashMapK = HashMap.class.getTypeParameters()[0]; |
| Type setEntryKV; |
| Type entryStringInteger = setOf(newParameterizedTypeWithOwner( |
| Map.class, Map.Entry.class, String.class, Integer.class)); |
| Field list; |
| Field instance; |
| Constructor<GenericConstructor> newHasGenericConstructor; |
| Constructor<Thrower> newThrower; |
| Constructor newString; |
| Method stringIndexOf; |
| Method comparableCompareTo; |
| Method getArray; |
| Method getSetOfArray; |
| Method echo; |
| Method throwS; |
| |
| @Override protected void setUp() throws Exception { |
| super.setUp(); |
| |
| list = HasGenericFields.class.getField("list"); |
| instance = HasGenericFields.class.getField("instance"); |
| newHasGenericConstructor = GenericConstructor.class.getConstructor(Object.class, Object.class); |
| newThrower = Thrower.class.getConstructor(); |
| stringIndexOf = String.class.getMethod("indexOf", String.class); |
| newString = String.class.getConstructor(String.class); |
| comparableCompareTo = Comparable.class.getMethod("compareTo", Object.class); |
| getArray = HasArray.class.getMethod("getArray"); |
| getSetOfArray = HasArray.class.getMethod("getSetOfArray"); |
| echo = HasRelated.class.getMethod("echo", Object.class); |
| throwS = Thrower.class.getMethod("throwS"); |
| setEntryKV = HashMap.class.getMethod("entrySet").getGenericReturnType(); |
| } |
| |
| public void testDirectInheritance() throws NoSuchMethodException { |
| TypeLiteral<?> resolver = TypeLiteral.get(arrayListOfString); |
| assertEquals(listOf(String.class), |
| resolver.getReturnType(List.class.getMethod("subList", int.class, int.class)).getType()); |
| assertEquals(ImmutableList.<TypeLiteral<?>>of(TypeLiteral.get(String.class)), |
| resolver.getParameterTypes(Collection.class.getMethod("add", Object.class))); |
| } |
| |
| public void testGenericSupertype() { |
| TypeLiteral<?> resolver = TypeLiteral.get(arrayListOfString); |
| assertEquals(newParameterizedType(Collection.class, String.class), |
| resolver.getSupertype(Collection.class).getType()); |
| assertEquals(newParameterizedType(Iterable.class, String.class), |
| resolver.getSupertype(Iterable.class).getType()); |
| assertEquals(newParameterizedType(AbstractList.class, String.class), |
| resolver.getSupertype(AbstractList.class).getType()); |
| assertEquals(Object.class, resolver.getSupertype(Object.class).getType()); |
| } |
| |
| public void testRecursiveTypeVariable() { |
| TypeLiteral<?> resolver = TypeLiteral.get(MyInteger.class); |
| assertEquals(MyInteger.class, resolver.getParameterTypes(comparableCompareTo).get(0).getType()); |
| } |
| |
| interface MyComparable<E extends MyComparable<E>> extends Comparable<E> {} |
| |
| static class MyInteger implements MyComparable<MyInteger> { |
| int value; |
| public int compareTo(MyInteger o) { |
| return value - o.value; |
| } |
| } |
| |
| public void testFields() { |
| TypeLiteral<?> resolver = TypeLiteral.get(hasGenericFieldsOfShort); |
| assertEquals(listOf(Short.class), resolver.getFieldType(list).getType()); |
| assertEquals(Short.class, resolver.getFieldType(instance).getType()); |
| } |
| |
| static class HasGenericFields<T> { |
| public List<T> list; |
| public T instance; |
| } |
| |
| public void testGenericConstructor() throws NoSuchMethodException { |
| TypeLiteral<?> resolver = TypeLiteral.get(hasGenericConstructorOfShort); |
| assertEquals(Short.class, |
| resolver.getParameterTypes(newHasGenericConstructor).get(0).getType()); |
| } |
| |
| static class GenericConstructor<S> { |
| @SuppressWarnings("UnusedDeclaration") |
| public <T> GenericConstructor(S s, T t) {} |
| } |
| |
| public void testThrowsExceptions() { |
| TypeLiteral<?> type = TypeLiteral.get(throwerOfNpe); |
| assertEquals(NullPointerException.class, type.getExceptionTypes(newThrower).get(0).getType()); |
| assertEquals(NullPointerException.class, type.getExceptionTypes(throwS).get(0).getType()); |
| } |
| |
| static class Thrower<S extends Exception> { |
| public Thrower() throws S {} |
| public void throwS() throws S {} |
| } |
| |
| public void testArrays() { |
| TypeLiteral<?> resolver = TypeLiteral.get(hasArrayOfShort); |
| assertEquals(arrayOf(Short.class), resolver.getReturnType(getArray).getType()); |
| assertEquals(setOf(arrayOf(Short.class)), resolver.getReturnType(getSetOfArray).getType()); |
| } |
| |
| static interface HasArray<T extends Number> { |
| T[] getArray(); |
| Set<T[]> getSetOfArray(); |
| } |
| |
| public void testRelatedTypeVariables() { |
| TypeLiteral<?> resolver = TypeLiteral.get(hasRelatedOfString); |
| assertEquals(String.class, resolver.getParameterTypes(echo).get(0).getType()); |
| assertEquals(String.class, resolver.getReturnType(echo).getType()); |
| } |
| |
| interface HasRelated<T, R extends T> { |
| T echo(R r); |
| } |
| |
| /** Ensure the cache doesn't cache too much */ |
| public void testCachingAndReindexing() throws NoSuchMethodException { |
| TypeLiteral<?> resolver = TypeLiteral.get( |
| newParameterizedTypeWithOwner(getClass(), HasLists.class, String.class, Short.class)); |
| assertEquals(listOf(String.class), |
| resolver.getReturnType(HasLists.class.getMethod("listS")).getType()); |
| assertEquals(listOf(Short.class), |
| resolver.getReturnType(HasLists.class.getMethod("listT")).getType()); |
| } |
| |
| interface HasLists<S, T> { |
| List<S> listS(); |
| List<T> listT(); |
| List<Map.Entry<S, T>> listEntries(); |
| } |
| |
| public void testUnsupportedQueries() throws NoSuchMethodException { |
| TypeLiteral<?> resolver = TypeLiteral.get(arrayListOfString); |
| |
| try { |
| resolver.getExceptionTypes(stringIndexOf); |
| fail(); |
| } catch (IllegalArgumentException e) { |
| assertEquals("public int java.lang.String.indexOf(java.lang.String) is not defined by a " |
| + "supertype of java.util.ArrayList<java.lang.String>", e.getMessage()); |
| } |
| try { |
| resolver.getParameterTypes(stringIndexOf); |
| fail(); |
| } catch (Exception e) { |
| assertEquals("public int java.lang.String.indexOf(java.lang.String) is not defined by a " |
| + "supertype of java.util.ArrayList<java.lang.String>", e.getMessage()); |
| } |
| try { |
| resolver.getReturnType(stringIndexOf); |
| fail(); |
| } catch (Exception e) { |
| assertEquals("public int java.lang.String.indexOf(java.lang.String) is not defined by a " |
| + "supertype of java.util.ArrayList<java.lang.String>", e.getMessage()); |
| } |
| try { |
| resolver.getSupertype(String.class); |
| fail(); |
| } catch (Exception e) { |
| assertEquals("class java.lang.String is not a supertype of " |
| + "java.util.ArrayList<java.lang.String>", e.getMessage()); |
| } |
| try { |
| resolver.getExceptionTypes(newString); |
| fail(); |
| } catch (Exception e) { |
| assertEquals("public java.lang.String(java.lang.String) does not construct " |
| + "a supertype of java.util.ArrayList<java.lang.String>", e.getMessage()); |
| } |
| try { |
| resolver.getParameterTypes(newString); |
| fail(); |
| } catch (Exception e) { |
| assertEquals("public java.lang.String(java.lang.String) does not construct " |
| + "a supertype of java.util.ArrayList<java.lang.String>", e.getMessage()); |
| } |
| } |
| |
| public void testResolve() { |
| TypeLiteral<?> typeResolver = TypeLiteral.get(StringIntegerMap.class); |
| assertEquals(String.class, typeResolver.resolveType(mapK)); |
| |
| typeResolver = new TypeLiteral<Map<String, Integer>>() {}; |
| assertEquals(String.class, typeResolver.resolveType(mapK)); |
| assertEquals(Types.mapOf(String.class, Integer.class), |
| typeResolver.getSupertype(Map.class).getType()); |
| |
| typeResolver = new TypeLiteral<BetterMap<String, Integer>>() {}; |
| assertEquals(String.class, typeResolver.resolveType(mapK)); |
| |
| typeResolver = new TypeLiteral<BestMap<String, Integer>>() {}; |
| assertEquals(String.class, typeResolver.resolveType(mapK)); |
| |
| typeResolver = TypeLiteral.get(StringIntegerHashMap.class); |
| assertEquals(String.class, typeResolver.resolveType(mapK)); |
| assertEquals(String.class, typeResolver.resolveType(hashMapK)); |
| assertEquals(entryStringInteger, typeResolver.resolveType(setEntryKV)); |
| assertEquals(Object.class, typeResolver.getSupertype(Object.class).getType()); |
| } |
| |
| public void testOnObject() { |
| TypeLiteral<?> typeResolver = TypeLiteral.get(Object.class); |
| assertEquals(Object.class, typeResolver.getSupertype(Object.class).getType()); |
| assertEquals(Object.class, typeResolver.getRawType()); |
| |
| // interfaces also resolve Object |
| typeResolver = TypeLiteral.get(Types.setOf(Integer.class)); |
| assertEquals(Object.class, typeResolver.getSupertype(Object.class).getType()); |
| } |
| |
| interface StringIntegerMap extends Map<String, Integer> {} |
| interface BetterMap<K1, V1> extends Map<K1, V1> {} |
| interface BestMap<K2, V2> extends BetterMap<K2, V2> {} |
| static class StringIntegerHashMap extends HashMap<String, Integer> {} |
| |
| public void testGetSupertype() { |
| TypeLiteral<AbstractList<String>> listOfString = new TypeLiteral<AbstractList<String>>() {}; |
| assertEquals(Types.newParameterizedType(AbstractCollection.class, String.class), |
| listOfString.getSupertype(AbstractCollection.class).getType()); |
| |
| TypeLiteral arrayListOfE = TypeLiteral.get(newParameterizedType( |
| ArrayList.class, ArrayList.class.getTypeParameters())); |
| assertEquals( |
| newParameterizedType(AbstractCollection.class, ArrayList.class.getTypeParameters()), |
| arrayListOfE.getSupertype(AbstractCollection.class).getType()); |
| } |
| |
| public void testGetSupertypeForArraysAsList() { |
| Class<? extends List> arraysAsListClass = Arrays.asList().getClass(); |
| Type anotherE = arraysAsListClass.getTypeParameters()[0]; |
| TypeLiteral type = TypeLiteral.get(newParameterizedType(AbstractList.class, anotherE)); |
| assertEquals(newParameterizedType(AbstractCollection.class, anotherE), |
| type.getSupertype(AbstractCollection.class).getType()); |
| } |
| |
| public void testWildcards() throws NoSuchFieldException { |
| TypeLiteral<Parameterized<String>> ofString = new TypeLiteral<Parameterized<String>>() {}; |
| |
| assertEquals(new TypeLiteral<List<String>>() {}.getType(), |
| ofString.getFieldType(Parameterized.class.getField("t")).getType()); |
| assertEquals(new TypeLiteral<List<? extends String>>() {}.getType(), |
| ofString.getFieldType(Parameterized.class.getField("extendsT")).getType()); |
| assertEquals(new TypeLiteral<List<? super String>>() {}.getType(), |
| ofString.getFieldType(Parameterized.class.getField("superT")).getType()); |
| } |
| |
| static class Parameterized<T> { |
| public List<T> t; |
| public List<? extends T> extendsT; |
| public List<? super T> superT; |
| } |
| |
| // TODO(jessewilson): tests for tricky bounded types like <T extends Collection, Serializable> |
| |
| public void testEqualsAndHashCode() throws IOException { |
| TypeLiteral<?> a1 = TypeLiteral.get(arrayListOfString); |
| TypeLiteral<?> a2 = TypeLiteral.get(arrayListOfString); |
| TypeLiteral<?> b = TypeLiteral.get(listOf(String.class)); |
| assertEqualsBothWays(a1, a2); |
| assertNotSerializable(a1); |
| assertFalse(a1.equals(b)); |
| } |
| } |