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