• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2008 Google Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.google.inject;
18 
19 import static com.google.inject.Asserts.assertEqualsBothWays;
20 import static com.google.inject.Asserts.assertNotSerializable;
21 import static com.google.inject.util.Types.arrayOf;
22 import static com.google.inject.util.Types.listOf;
23 import static com.google.inject.util.Types.newParameterizedType;
24 import static com.google.inject.util.Types.newParameterizedTypeWithOwner;
25 import static com.google.inject.util.Types.setOf;
26 
27 import com.google.common.collect.ImmutableList;
28 import com.google.inject.util.Types;
29 import java.io.IOException;
30 import java.lang.reflect.Constructor;
31 import java.lang.reflect.Field;
32 import java.lang.reflect.Method;
33 import java.lang.reflect.Type;
34 import java.util.AbstractCollection;
35 import java.util.AbstractList;
36 import java.util.ArrayList;
37 import java.util.Arrays;
38 import java.util.Collection;
39 import java.util.HashMap;
40 import java.util.List;
41 import java.util.Map;
42 import java.util.Set;
43 import junit.framework.TestCase;
44 
45 /**
46  * This test checks that TypeLiteral can perform type resolution on its members.
47  *
48  * @author jessewilson@google.com (Jesse Wilson)
49  */
50 public class TypeLiteralTypeResolutionTest extends TestCase {
51   Type arrayListOfString = newParameterizedType(ArrayList.class, String.class);
52   Type hasGenericFieldsOfShort =
53       newParameterizedTypeWithOwner(getClass(), HasGenericFields.class, Short.class);
54   Type hasGenericConstructorOfShort =
55       newParameterizedTypeWithOwner(getClass(), GenericConstructor.class, Short.class);
56   Type throwerOfNpe =
57       newParameterizedTypeWithOwner(getClass(), Thrower.class, NullPointerException.class);
58   Type hasArrayOfShort = newParameterizedTypeWithOwner(getClass(), HasArray.class, Short.class);
59   Type hasRelatedOfString =
60       newParameterizedTypeWithOwner(getClass(), HasRelated.class, String.class, String.class);
61   Type mapK = Map.class.getTypeParameters()[0];
62   Type hashMapK = HashMap.class.getTypeParameters()[0];
63   Type setEntryKV;
64   Type entryStringInteger =
65       setOf(newParameterizedTypeWithOwner(Map.class, Map.Entry.class, String.class, Integer.class));
66   Field list;
67   Field instance;
68   Constructor<GenericConstructor> newHasGenericConstructor;
69   Constructor<Thrower> newThrower;
70   Constructor newString;
71   Method stringIndexOf;
72   Method comparableCompareTo;
73   Method getArray;
74   Method getSetOfArray;
75   Method echo;
76   Method throwS;
77 
78   @Override
setUp()79   protected void setUp() throws Exception {
80     super.setUp();
81 
82     list = HasGenericFields.class.getField("list");
83     instance = HasGenericFields.class.getField("instance");
84     newHasGenericConstructor = GenericConstructor.class.getConstructor(Object.class, Object.class);
85     newThrower = Thrower.class.getConstructor();
86     stringIndexOf = String.class.getMethod("indexOf", String.class);
87     newString = String.class.getConstructor(String.class);
88     comparableCompareTo = Comparable.class.getMethod("compareTo", Object.class);
89     getArray = HasArray.class.getMethod("getArray");
90     getSetOfArray = HasArray.class.getMethod("getSetOfArray");
91     echo = HasRelated.class.getMethod("echo", Object.class);
92     throwS = Thrower.class.getMethod("throwS");
93     setEntryKV = HashMap.class.getMethod("entrySet").getGenericReturnType();
94   }
95 
testDirectInheritance()96   public void testDirectInheritance() throws NoSuchMethodException {
97     TypeLiteral<?> resolver = TypeLiteral.get(arrayListOfString);
98     assertEquals(
99         listOf(String.class),
100         resolver.getReturnType(List.class.getMethod("subList", int.class, int.class)).getType());
101     assertEquals(
102         ImmutableList.<TypeLiteral<?>>of(TypeLiteral.get(String.class)),
103         resolver.getParameterTypes(Collection.class.getMethod("add", Object.class)));
104   }
105 
testGenericSupertype()106   public void testGenericSupertype() {
107     TypeLiteral<?> resolver = TypeLiteral.get(arrayListOfString);
108     assertEquals(
109         newParameterizedType(Collection.class, String.class),
110         resolver.getSupertype(Collection.class).getType());
111     assertEquals(
112         newParameterizedType(Iterable.class, String.class),
113         resolver.getSupertype(Iterable.class).getType());
114     assertEquals(
115         newParameterizedType(AbstractList.class, String.class),
116         resolver.getSupertype(AbstractList.class).getType());
117     assertEquals(Object.class, resolver.getSupertype(Object.class).getType());
118   }
119 
testRecursiveTypeVariable()120   public void testRecursiveTypeVariable() {
121     TypeLiteral<?> resolver = TypeLiteral.get(MyInteger.class);
122     assertEquals(MyInteger.class, resolver.getParameterTypes(comparableCompareTo).get(0).getType());
123   }
124 
125   interface MyComparable<E extends MyComparable<E>> extends Comparable<E> {}
126 
127   static class MyInteger implements MyComparable<MyInteger> {
128     int value;
129 
130     @Override
compareTo(MyInteger o)131     public int compareTo(MyInteger o) {
132       return value - o.value;
133     }
134   }
135 
testFields()136   public void testFields() {
137     TypeLiteral<?> resolver = TypeLiteral.get(hasGenericFieldsOfShort);
138     assertEquals(listOf(Short.class), resolver.getFieldType(list).getType());
139     assertEquals(Short.class, resolver.getFieldType(instance).getType());
140   }
141 
142   static class HasGenericFields<T> {
143     public List<T> list;
144     public T instance;
145   }
146 
testGenericConstructor()147   public void testGenericConstructor() throws NoSuchMethodException {
148     TypeLiteral<?> resolver = TypeLiteral.get(hasGenericConstructorOfShort);
149     assertEquals(
150         Short.class, resolver.getParameterTypes(newHasGenericConstructor).get(0).getType());
151   }
152 
153   static class GenericConstructor<S> {
154     @SuppressWarnings("UnusedDeclaration")
GenericConstructor(S s, T t)155     public <T> GenericConstructor(S s, T t) {}
156   }
157 
testThrowsExceptions()158   public void testThrowsExceptions() {
159     TypeLiteral<?> type = TypeLiteral.get(throwerOfNpe);
160     assertEquals(NullPointerException.class, type.getExceptionTypes(newThrower).get(0).getType());
161     assertEquals(NullPointerException.class, type.getExceptionTypes(throwS).get(0).getType());
162   }
163 
164   static class Thrower<S extends Exception> {
Thrower()165     public Thrower() throws S {}
166 
throwS()167     public void throwS() throws S {}
168   }
169 
testArrays()170   public void testArrays() {
171     TypeLiteral<?> resolver = TypeLiteral.get(hasArrayOfShort);
172     assertEquals(arrayOf(Short.class), resolver.getReturnType(getArray).getType());
173     assertEquals(setOf(arrayOf(Short.class)), resolver.getReturnType(getSetOfArray).getType());
174   }
175 
176   static interface HasArray<T extends Number> {
getArray()177     T[] getArray();
178 
getSetOfArray()179     Set<T[]> getSetOfArray();
180   }
181 
testRelatedTypeVariables()182   public void testRelatedTypeVariables() {
183     TypeLiteral<?> resolver = TypeLiteral.get(hasRelatedOfString);
184     assertEquals(String.class, resolver.getParameterTypes(echo).get(0).getType());
185     assertEquals(String.class, resolver.getReturnType(echo).getType());
186   }
187 
188   interface HasRelated<T, R extends T> {
echo(R r)189     T echo(R r);
190   }
191 
192   /** Ensure the cache doesn't cache too much */
testCachingAndReindexing()193   public void testCachingAndReindexing() throws NoSuchMethodException {
194     TypeLiteral<?> resolver =
195         TypeLiteral.get(
196             newParameterizedTypeWithOwner(getClass(), HasLists.class, String.class, Short.class));
197     assertEquals(
198         listOf(String.class), resolver.getReturnType(HasLists.class.getMethod("listS")).getType());
199     assertEquals(
200         listOf(Short.class), resolver.getReturnType(HasLists.class.getMethod("listT")).getType());
201   }
202 
203   interface HasLists<S, T> {
listS()204     List<S> listS();
205 
listT()206     List<T> listT();
207 
listEntries()208     List<Map.Entry<S, T>> listEntries();
209   }
210 
testUnsupportedQueries()211   public void testUnsupportedQueries() throws NoSuchMethodException {
212     TypeLiteral<?> resolver = TypeLiteral.get(arrayListOfString);
213 
214     try {
215       resolver.getExceptionTypes(stringIndexOf);
216       fail();
217     } catch (IllegalArgumentException e) {
218       assertEquals(
219           "public int java.lang.String.indexOf(java.lang.String) is not defined by a "
220               + "supertype of java.util.ArrayList<java.lang.String>",
221           e.getMessage());
222     }
223     try {
224       resolver.getParameterTypes(stringIndexOf);
225       fail();
226     } catch (Exception e) {
227       assertEquals(
228           "public int java.lang.String.indexOf(java.lang.String) is not defined by a "
229               + "supertype of java.util.ArrayList<java.lang.String>",
230           e.getMessage());
231     }
232     try {
233       resolver.getReturnType(stringIndexOf);
234       fail();
235     } catch (Exception e) {
236       assertEquals(
237           "public int java.lang.String.indexOf(java.lang.String) is not defined by a "
238               + "supertype of java.util.ArrayList<java.lang.String>",
239           e.getMessage());
240     }
241     try {
242       resolver.getSupertype(String.class);
243       fail();
244     } catch (Exception e) {
245       assertEquals(
246           "class java.lang.String is not a supertype of " + "java.util.ArrayList<java.lang.String>",
247           e.getMessage());
248     }
249     try {
250       resolver.getExceptionTypes(newString);
251       fail();
252     } catch (Exception e) {
253       assertEquals(
254           "public java.lang.String(java.lang.String) does not construct "
255               + "a supertype of java.util.ArrayList<java.lang.String>",
256           e.getMessage());
257     }
258     try {
259       resolver.getParameterTypes(newString);
260       fail();
261     } catch (Exception e) {
262       assertEquals(
263           "public java.lang.String(java.lang.String) does not construct "
264               + "a supertype of java.util.ArrayList<java.lang.String>",
265           e.getMessage());
266     }
267   }
268 
testResolve()269   public void testResolve() {
270     TypeLiteral<?> typeResolver = TypeLiteral.get(StringIntegerMap.class);
271     assertEquals(String.class, typeResolver.resolveType(mapK));
272 
273     typeResolver = new TypeLiteral<Map<String, Integer>>() {};
274     assertEquals(String.class, typeResolver.resolveType(mapK));
275     assertEquals(
276         Types.mapOf(String.class, Integer.class), typeResolver.getSupertype(Map.class).getType());
277 
278     typeResolver = new TypeLiteral<BetterMap<String, Integer>>() {};
279     assertEquals(String.class, typeResolver.resolveType(mapK));
280 
281     typeResolver = new TypeLiteral<BestMap<String, Integer>>() {};
282     assertEquals(String.class, typeResolver.resolveType(mapK));
283 
284     typeResolver = TypeLiteral.get(StringIntegerHashMap.class);
285     assertEquals(String.class, typeResolver.resolveType(mapK));
286     assertEquals(String.class, typeResolver.resolveType(hashMapK));
287     assertEquals(entryStringInteger, typeResolver.resolveType(setEntryKV));
288     assertEquals(Object.class, typeResolver.getSupertype(Object.class).getType());
289   }
290 
testOnObject()291   public void testOnObject() {
292     TypeLiteral<?> typeResolver = TypeLiteral.get(Object.class);
293     assertEquals(Object.class, typeResolver.getSupertype(Object.class).getType());
294     assertEquals(Object.class, typeResolver.getRawType());
295 
296     // interfaces also resolve Object
297     typeResolver = TypeLiteral.get(Types.setOf(Integer.class));
298     assertEquals(Object.class, typeResolver.getSupertype(Object.class).getType());
299   }
300 
301   interface StringIntegerMap extends Map<String, Integer> {}
302 
303   interface BetterMap<K1, V1> extends Map<K1, V1> {}
304 
305   interface BestMap<K2, V2> extends BetterMap<K2, V2> {}
306 
307   static class StringIntegerHashMap extends HashMap<String, Integer> {}
308 
testGetSupertype()309   public void testGetSupertype() {
310     TypeLiteral<AbstractList<String>> listOfString = new TypeLiteral<AbstractList<String>>() {};
311     assertEquals(
312         Types.newParameterizedType(AbstractCollection.class, String.class),
313         listOfString.getSupertype(AbstractCollection.class).getType());
314 
315     TypeLiteral arrayListOfE =
316         TypeLiteral.get(newParameterizedType(ArrayList.class, ArrayList.class.getTypeParameters()));
317     assertEquals(
318         newParameterizedType(AbstractCollection.class, ArrayList.class.getTypeParameters()),
319         arrayListOfE.getSupertype(AbstractCollection.class).getType());
320   }
321 
testGetSupertypeForArraysAsList()322   public void testGetSupertypeForArraysAsList() {
323     Class<? extends List> arraysAsListClass = Arrays.asList().getClass();
324     Type anotherE = arraysAsListClass.getTypeParameters()[0];
325     TypeLiteral type = TypeLiteral.get(newParameterizedType(AbstractList.class, anotherE));
326     assertEquals(
327         newParameterizedType(AbstractCollection.class, anotherE),
328         type.getSupertype(AbstractCollection.class).getType());
329   }
330 
testWildcards()331   public void testWildcards() throws NoSuchFieldException {
332     TypeLiteral<Parameterized<String>> ofString = new TypeLiteral<Parameterized<String>>() {};
333 
334     assertEquals(
335         new TypeLiteral<List<String>>() {}.getType(),
336         ofString.getFieldType(Parameterized.class.getField("t")).getType());
337     assertEquals(
338         new TypeLiteral<List<? extends String>>() {}.getType(),
339         ofString.getFieldType(Parameterized.class.getField("extendsT")).getType());
340     assertEquals(
341         new TypeLiteral<List<? super String>>() {}.getType(),
342         ofString.getFieldType(Parameterized.class.getField("superT")).getType());
343   }
344 
345   static class Parameterized<T> {
346     public List<T> t;
347     public List<? extends T> extendsT;
348     public List<? super T> superT;
349   }
350 
351   // TODO(jessewilson): tests for tricky bounded types like <T extends Collection, Serializable>
352 
testEqualsAndHashCode()353   public void testEqualsAndHashCode() throws IOException {
354     TypeLiteral<?> a1 = TypeLiteral.get(arrayListOfString);
355     TypeLiteral<?> a2 = TypeLiteral.get(arrayListOfString);
356     TypeLiteral<?> b = TypeLiteral.get(listOf(String.class));
357     assertEqualsBothWays(a1, a2);
358     assertNotSerializable(a1);
359     assertFalse(a1.equals(b));
360   }
361 }
362