1 /*
2  * Copyright (C) 2014 The Android Open Source Project
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 android.hardware.camera2.utils;
18 
19 import java.lang.reflect.Array;
20 import java.lang.reflect.GenericArrayType;
21 import java.lang.reflect.ParameterizedType;
22 import java.lang.reflect.Type;
23 import java.lang.reflect.TypeVariable;
24 import java.lang.reflect.WildcardType;
25 
26 import static com.android.internal.util.Preconditions.*;
27 
28 /**
29  * Super type token; allows capturing generic types at runtime by forcing them to be reified.
30  *
31  * <p>Usage example: <pre>{@code
32  *      // using anonymous classes (preferred)
33  *      TypeReference&lt;Integer> intToken = new TypeReference&lt;Integer>() {{ }};
34  *
35  *      // using named classes
36  *      class IntTypeReference extends TypeReference&lt;Integer> {...}
37  *      TypeReference&lt;Integer> intToken = new IntTypeReference();
38  * }</p></pre>
39  *
40  * <p>Unlike the reference implementation, this bans nested TypeVariables; that is all
41  * dynamic types must equal to the static types.</p>
42  *
43  * <p>See <a href="http://gafter.blogspot.com/2007/05/limitation-of-super-type-tokens.html">
44  * http://gafter.blogspot.com/2007/05/limitation-of-super-type-tokens.html</a>
45  * for more details.</p>
46  */
47 public abstract class TypeReference<T> {
48     private final Type mType;
49     private final int mHash;
50 
51     /**
52      * Create a new type reference for {@code T}.
53      *
54      * @throws IllegalArgumentException if {@code T}'s actual type contains a type variable
55      *
56      * @see TypeReference
57      */
TypeReference()58     protected TypeReference() {
59         ParameterizedType thisType = (ParameterizedType)getClass().getGenericSuperclass();
60 
61         // extract the "T" from TypeReference<T>
62         mType = thisType.getActualTypeArguments()[0];
63 
64         /*
65          * Prohibit type references with type variables such as
66          *
67          *    class GenericListToken<T> extends TypeReference<List<T>>
68          *
69          * Since the "T" there is not known without an instance of T, type equality would
70          * consider *all* Lists equal regardless of T. Allowing this would defeat
71          * some of the type safety of a type reference.
72          */
73         if (containsTypeVariable(mType)) {
74             throw new IllegalArgumentException(
75                     "Including a type variable in a type reference is not allowed");
76         }
77         mHash = mType.hashCode();
78     }
79 
80     /**
81      * Return the dynamic {@link Type} corresponding to the captured type {@code T}.
82      */
getType()83     public Type getType() {
84         return mType;
85     }
86 
TypeReference(Type type)87     private TypeReference(Type type) {
88         mType = type;
89         if (containsTypeVariable(mType)) {
90             throw new IllegalArgumentException(
91                     "Including a type variable in a type reference is not allowed");
92         }
93         mHash = mType.hashCode();
94     }
95 
96     private static class SpecializedTypeReference<T> extends TypeReference<T> {
SpecializedTypeReference(Class<T> klass)97         public SpecializedTypeReference(Class<T> klass) {
98             super(klass);
99         }
100     }
101 
102     @SuppressWarnings("rawtypes")
103     private static class SpecializedBaseTypeReference extends TypeReference {
SpecializedBaseTypeReference(Type type)104         public SpecializedBaseTypeReference(Type type) {
105             super(type);
106         }
107     }
108 
109     /**
110      * Create a specialized type reference from a dynamic class instance,
111      * bypassing the standard compile-time checks.
112      *
113      * <p>As with a regular type reference, the {@code klass} must not contain
114      * any type variables.</p>
115      *
116      * @param klass a non-{@code null} {@link Class} instance
117      *
118      * @return a type reference which captures {@code T} at runtime
119      *
120      * @throws IllegalArgumentException if {@code T} had any type variables
121      */
createSpecializedTypeReference(Class<T> klass)122     public static <T> TypeReference<T> createSpecializedTypeReference(Class<T> klass) {
123         return new SpecializedTypeReference<T>(klass);
124     }
125 
126     /**
127      * Create a specialized type reference from a dynamic {@link Type} instance,
128      * bypassing the standard compile-time checks.
129      *
130      * <p>As with a regular type reference, the {@code type} must not contain
131      * any type variables.</p>
132      *
133      * @param type a non-{@code null} {@link Type} instance
134      *
135      * @return a type reference which captures {@code T} at runtime
136      *
137      * @throws IllegalArgumentException if {@code type} had any type variables
138      */
createSpecializedTypeReference(Type type)139     public static TypeReference<?> createSpecializedTypeReference(Type type) {
140         return new SpecializedBaseTypeReference(type);
141     }
142 
143     /**
144      * Returns the raw type of T.
145      *
146      * <p><ul>
147      * <li>If T is a Class itself, T itself is returned.
148      * <li>If T is a ParameterizedType, the raw type of the parameterized type is returned.
149      * <li>If T is a GenericArrayType, the returned type is the corresponding array class.
150      * For example: {@code List<Integer>[]} => {@code List[]}.
151      * <li>If T is a type variable or a wildcard type, the raw type of the first upper bound is
152      * returned. For example: {@code <X extends Foo>} => {@code Foo}.
153      * </ul>
154      *
155      * @return the raw type of {@code T}
156      */
157     @SuppressWarnings("unchecked")
getRawType()158     public final Class<? super T> getRawType() {
159         return (Class<? super T>)getRawType(mType);
160     }
161 
getRawType(Type type)162     private static final Class<?> getRawType(Type type) {
163         if (type == null) {
164             throw new NullPointerException("type must not be null");
165         }
166 
167         if (type instanceof Class<?>) {
168             return (Class<?>)type;
169         } else if (type instanceof ParameterizedType) {
170             return (Class<?>)(((ParameterizedType)type).getRawType());
171         } else if (type instanceof GenericArrayType) {
172             return getArrayClass(getRawType(((GenericArrayType)type).getGenericComponentType()));
173         } else if (type instanceof WildcardType) {
174             // Should be at most 1 upper bound, but treat it like an array for simplicity
175             return getRawType(((WildcardType) type).getUpperBounds());
176         } else if (type instanceof TypeVariable) {
177             throw new AssertionError("Type variables are not allowed in type references");
178         } else {
179             // Impossible
180             throw new AssertionError("Unhandled branch to get raw type for type " + type);
181         }
182     }
183 
getRawType(Type[] types)184     private static final Class<?> getRawType(Type[] types) {
185         if (types == null) {
186             return null;
187         }
188 
189         for (Type type : types) {
190             Class<?> klass = getRawType(type);
191             if (klass !=  null) {
192                 return klass;
193             }
194         }
195 
196         return null;
197     }
198 
getArrayClass(Class<?> componentType)199     private static final Class<?> getArrayClass(Class<?> componentType) {
200         return Array.newInstance(componentType, 0).getClass();
201     }
202 
203     /**
204      * Get the component type, e.g. {@code T} from {@code T[]}.
205      *
206      * @return component type, or {@code null} if {@code T} is not an array
207      */
getComponentType()208     public TypeReference<?> getComponentType() {
209         Type componentType = getComponentType(mType);
210 
211         return (componentType != null) ?
212                 createSpecializedTypeReference(componentType) :
213                 null;
214     }
215 
getComponentType(Type type)216     private static Type getComponentType(Type type) {
217         checkNotNull(type, "type must not be null");
218 
219         if (type instanceof Class<?>) {
220             return ((Class<?>) type).getComponentType();
221         } else if (type instanceof ParameterizedType) {
222             return null;
223         } else if (type instanceof GenericArrayType) {
224             return ((GenericArrayType)type).getGenericComponentType();
225         } else if (type instanceof WildcardType) {
226             // Should be at most 1 upper bound, but treat it like an array for simplicity
227             throw new UnsupportedOperationException("TODO: support wild card components");
228         } else if (type instanceof TypeVariable) {
229             throw new AssertionError("Type variables are not allowed in type references");
230         } else {
231             // Impossible
232             throw new AssertionError("Unhandled branch to get component type for type " + type);
233         }
234     }
235 
236     /**
237      * Compare two objects for equality.
238      *
239      * <p>A TypeReference is only equal to another TypeReference if their captured type {@code T}
240      * is also equal.</p>
241      */
242     @Override
equals(Object o)243     public boolean equals(Object o) {
244         // Note that this comparison could inaccurately return true when comparing types
245         // with nested type variables; therefore we ban type variables in the constructor.
246         return o instanceof TypeReference<?> && mType.equals(((TypeReference<?>)o).mType);
247     }
248 
249     /**
250      * {@inheritDoc}
251      */
252     @Override
hashCode()253     public int hashCode() {
254         return mHash;
255     }
256 
257     /**
258      * Check if the {@code type} contains a {@link TypeVariable} recursively.
259      *
260      * <p>Intuitively, a type variable is a type in a type expression that refers to a generic
261      * type which is not known at the definition of the expression (commonly seen when
262      * type parameters are used, e.g. {@code class Foo<T>}).</p>
263      *
264      * <p>See <a href="http://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.4">
265      * http://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.4</a>
266      * for a more formal definition of a type variable</p>.
267      *
268      * @param type a type object ({@code null} is allowed)
269      * @return {@code true} if there were nested type variables; {@code false} otherwise
270      */
containsTypeVariable(Type type)271     public static boolean containsTypeVariable(Type type) {
272         if (type == null) {
273             // Trivially false
274             return false;
275         } else if (type instanceof TypeVariable<?>) {
276             /*
277              * T -> trivially true
278              */
279             return true;
280         } else if (type instanceof Class<?>) {
281             /*
282              * class Foo -> no type variable
283              * class Foo<T> - has a type variable
284              *
285              * This also covers the case of class Foo<T> extends ... / implements ...
286              * since everything on the right hand side would either include a type variable T
287              * or have no type variables.
288              */
289             Class<?> klass = (Class<?>)type;
290 
291             // Empty array => class is not generic
292             if (klass.getTypeParameters().length != 0) {
293                 return true;
294             } else {
295                 // Does the outer class(es) contain any type variables?
296 
297                 /*
298                  * class Outer<T> {
299                  *   class Inner {
300                  *      T field;
301                  *   }
302                  * }
303                  *
304                  * In this case 'Inner' has no type parameters itself, but it still has a type
305                  * variable as part of the type definition.
306                  */
307                 return containsTypeVariable(klass.getDeclaringClass());
308             }
309         } else if (type instanceof ParameterizedType) {
310             /*
311              * This is the "Foo<T1, T2, T3, ... Tn>" in the scope of a
312              *
313              *      // no type variables here, T1-Tn are known at this definition
314              *      class X extends Foo<T1, T2, T3, ... Tn>
315              *
316              *      // T1 is a type variable, T2-Tn are known at this definition
317              *      class X<T1> extends Foo<T1, T2, T3, ... Tn>
318              */
319             ParameterizedType p = (ParameterizedType) type;
320 
321             // This needs to be recursively checked
322             for (Type arg : p.getActualTypeArguments()) {
323                 if (containsTypeVariable(arg)) {
324                     return true;
325                 }
326             }
327 
328             return false;
329         } else if (type instanceof WildcardType) {
330             WildcardType wild = (WildcardType) type;
331 
332             /*
333              * This is is the "?" inside of a
334              *
335              *       Foo<?> --> unbounded; trivially no type variables
336              *       Foo<? super T> --> lower bound; does T have a type variable?
337              *       Foo<? extends T> --> upper bound; does T have a type variable?
338              */
339 
340             /*
341              *  According to JLS 4.5.1
342              *  (http://java.sun.com/docs/books/jls/third_edition/html/typesValues.html#4.5.1):
343              *
344              *  - More than 1 lower/upper bound is illegal
345              *  - Both a lower and upper bound is illegal
346              *
347              *  However, we use this 'array OR array' approach for readability
348              */
349             return containsTypeVariable(wild.getLowerBounds()) ||
350                     containsTypeVariable(wild.getUpperBounds());
351         }
352 
353         return false;
354     }
355 
356     /**
357      * {@inheritDoc}
358      */
359     @Override
toString()360     public String toString() {
361         StringBuilder builder = new StringBuilder();
362         builder.append("TypeReference<");
363         toString(getType(), builder);
364         builder.append(">");
365 
366         return builder.toString();
367     }
368 
toString(Type type, StringBuilder out)369     private static void toString(Type type, StringBuilder out) {
370         if (type == null) {
371             return;
372         } else if (type instanceof TypeVariable<?>) {
373             // T
374             out.append(((TypeVariable<?>)type).getName());
375         } else if (type instanceof Class<?>) {
376             Class<?> klass = (Class<?>)type;
377 
378             out.append(klass.getName());
379             toString(klass.getTypeParameters(), out);
380         } else if (type instanceof ParameterizedType) {
381              // "Foo<T1, T2, T3, ... Tn>"
382             ParameterizedType p = (ParameterizedType) type;
383 
384             out.append(((Class<?>)p.getRawType()).getName());
385             toString(p.getActualTypeArguments(), out);
386         } else if (type instanceof GenericArrayType) {
387             GenericArrayType gat = (GenericArrayType)type;
388 
389             toString(gat.getGenericComponentType(), out);
390             out.append("[]");
391         } else { // WildcardType, BoundedType
392             // TODO:
393             out.append(type.toString());
394         }
395     }
396 
toString(Type[] types, StringBuilder out)397     private static void toString(Type[] types, StringBuilder out) {
398         if (types == null) {
399             return;
400         } else if (types.length == 0) {
401             return;
402         }
403 
404         out.append("<");
405 
406         for (int i = 0; i < types.length; ++i) {
407             toString(types[i], out);
408             if (i != types.length - 1) {
409                 out.append(", ");
410             }
411         }
412 
413         out.append(">");
414     }
415 
416     /**
417      * Check if any of the elements in this array contained a type variable.
418      *
419      * <p>Empty and null arrays trivially have no type variables.</p>
420      *
421      * @param typeArray an array ({@code null} is ok) of types
422      * @return true if any elements contained a type variable; false otherwise
423      */
containsTypeVariable(Type[] typeArray)424     private static boolean containsTypeVariable(Type[] typeArray) {
425         if (typeArray == null) {
426             return false;
427         }
428 
429         for (Type type : typeArray) {
430             if (containsTypeVariable(type)) {
431                 return true;
432             }
433         }
434 
435         return false;
436     }
437 }
438