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<Integer> intToken = new TypeReference<Integer>() {{ }}; 37 * 38 * // using named classes 39 * class IntTypeReference extends TypeReference<Integer> {...} 40 * TypeReference<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