Home | History | Annotate | Download | only in utils
      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      */
     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