/** * 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 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 com.google.common.collect.ImmutableList; import com.google.inject.util.Types; import junit.framework.TestCase; 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; /** * 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 newHasGenericConstructor; Constructor 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.>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> extends Comparable {} static class MyInteger implements MyComparable { 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 { public List 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 { @SuppressWarnings("UnusedDeclaration") public 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 { 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[] getArray(); Set 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 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 { List listS(); List listT(); List> 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", 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", 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", e.getMessage()); } try { resolver.getSupertype(String.class); fail(); } catch (Exception e) { assertEquals("class java.lang.String is not a supertype of " + "java.util.ArrayList", 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", 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", e.getMessage()); } } public void testResolve() { TypeLiteral typeResolver = TypeLiteral.get(StringIntegerMap.class); assertEquals(String.class, typeResolver.resolveType(mapK)); typeResolver = new TypeLiteral>() {}; assertEquals(String.class, typeResolver.resolveType(mapK)); assertEquals(Types.mapOf(String.class, Integer.class), typeResolver.getSupertype(Map.class).getType()); typeResolver = new TypeLiteral>() {}; assertEquals(String.class, typeResolver.resolveType(mapK)); typeResolver = new TypeLiteral>() {}; 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 {} interface BetterMap extends Map {} interface BestMap extends BetterMap {} static class StringIntegerHashMap extends HashMap {} public void testGetSupertype() { TypeLiteral> listOfString = new TypeLiteral>() {}; 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 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> ofString = new TypeLiteral>() {}; assertEquals(new TypeLiteral>() {}.getType(), ofString.getFieldType(Parameterized.class.getField("t")).getType()); assertEquals(new TypeLiteral>() {}.getType(), ofString.getFieldType(Parameterized.class.getField("extendsT")).getType()); assertEquals(new TypeLiteral>() {}.getType(), ofString.getFieldType(Parameterized.class.getField("superT")).getType()); } static class Parameterized { public List t; public List extendsT; public List superT; } // TODO(jessewilson): tests for tricky bounded types like 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)); } }