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