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
17package android.hardware.camera2.utils;
18
19import java.lang.reflect.Array;
20import java.lang.reflect.GenericArrayType;
21import java.lang.reflect.ParameterizedType;
22import java.lang.reflect.Type;
23import java.lang.reflect.TypeVariable;
24import java.lang.reflect.WildcardType;
25
26import 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 */
47public 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     */
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     */
83    public Type getType() {
84        return mType;
85    }
86
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> {
97        public SpecializedTypeReference(Class<T> klass) {
98            super(klass);
99        }
100    }
101
102    @SuppressWarnings("rawtypes")
103    private static class SpecializedBaseTypeReference extends TypeReference {
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     */
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     */
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")
158    public final Class<? super T> getRawType() {
159        return (Class<? super T>)getRawType(mType);
160    }
161
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
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
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     */
208    public TypeReference<?> getComponentType() {
209        Type componentType = getComponentType(mType);
210
211        return (componentType != null) ?
212                createSpecializedTypeReference(componentType) :
213                null;
214    }
215
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
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
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     */
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
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
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
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     */
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