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