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<Integer> intToken = new TypeReference<Integer>() {{ }}; 34 * 35 * // using named classes 36 * class IntTypeReference extends TypeReference<Integer> {...} 37 * TypeReference<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 */ TypeReference()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 */ getType()83 public Type getType() { 84 return mType; 85 } 86 TypeReference(Type type)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> { SpecializedTypeReference(Class<T> klass)97 public SpecializedTypeReference(Class<T> klass) { 98 super(klass); 99 } 100 } 101 102 @SuppressWarnings("rawtypes") 103 private static class SpecializedBaseTypeReference extends TypeReference { SpecializedBaseTypeReference(Type type)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 */ createSpecializedTypeReference(Class<T> klass)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 */ createSpecializedTypeReference(Type type)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") getRawType()158 public final Class<? super T> getRawType() { 159 return (Class<? super T>)getRawType(mType); 160 } 161 getRawType(Type type)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 getRawType(Type[] types)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 getArrayClass(Class<?> componentType)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 */ getComponentType()208 public TypeReference<?> getComponentType() { 209 Type componentType = getComponentType(mType); 210 211 return (componentType != null) ? 212 createSpecializedTypeReference(componentType) : 213 null; 214 } 215 getComponentType(Type type)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 equals(Object o)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 hashCode()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 */ containsTypeVariable(Type type)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 toString()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 toString(Type type, StringBuilder out)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 toString(Type[] types, StringBuilder out)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 */ containsTypeVariable(Type[] typeArray)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