1/*
2 * Copyright (C) 2011 The Guava Authors
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 com.google.common.reflect;
18
19import static com.google.common.base.Preconditions.checkArgument;
20import static com.google.common.base.Preconditions.checkNotNull;
21import static com.google.common.collect.Iterables.transform;
22
23import com.google.common.annotations.VisibleForTesting;
24import com.google.common.base.Function;
25import com.google.common.base.Joiner;
26import com.google.common.base.Objects;
27import com.google.common.base.Predicates;
28import com.google.common.collect.ImmutableList;
29import com.google.common.collect.ImmutableMap;
30import com.google.common.collect.Iterables;
31
32import java.io.Serializable;
33import java.lang.reflect.AnnotatedElement;
34import java.lang.reflect.Array;
35import java.lang.reflect.GenericArrayType;
36import java.lang.reflect.GenericDeclaration;
37import java.lang.reflect.InvocationHandler;
38import java.lang.reflect.InvocationTargetException;
39import java.lang.reflect.Method;
40import java.lang.reflect.ParameterizedType;
41import java.lang.reflect.Proxy;
42import java.lang.reflect.Type;
43import java.lang.reflect.TypeVariable;
44import java.lang.reflect.WildcardType;
45import java.security.AccessControlException;
46import java.util.Arrays;
47import java.util.Collection;
48import java.util.concurrent.atomic.AtomicReference;
49
50import javax.annotation.Nullable;
51
52/**
53 * Utilities for working with {@link Type}.
54 *
55 * @author Ben Yu
56 */
57final class Types {
58
59  /** Class#toString without the "class " and "interface " prefixes */
60  private static final Function<Type, String> TYPE_NAME =
61      new Function<Type, String>() {
62        @Override public String apply(Type from) {
63          return JavaVersion.CURRENT.typeName(from);
64        }
65      };
66
67  private static final Joiner COMMA_JOINER = Joiner.on(", ").useForNull("null");
68
69  /** Returns the array type of {@code componentType}. */
70  static Type newArrayType(Type componentType) {
71    if (componentType instanceof WildcardType) {
72      WildcardType wildcard = (WildcardType) componentType;
73      Type[] lowerBounds = wildcard.getLowerBounds();
74      checkArgument(lowerBounds.length <= 1, "Wildcard cannot have more than one lower bounds.");
75      if (lowerBounds.length == 1) {
76        return supertypeOf(newArrayType(lowerBounds[0]));
77      } else {
78        Type[] upperBounds = wildcard.getUpperBounds();
79        checkArgument(upperBounds.length == 1, "Wildcard should have only one upper bound.");
80        return subtypeOf(newArrayType(upperBounds[0]));
81      }
82    }
83    return JavaVersion.CURRENT.newArrayType(componentType);
84  }
85
86  /**
87   * Returns a type where {@code rawType} is parameterized by
88   * {@code arguments} and is owned by {@code ownerType}.
89   */
90  static ParameterizedType newParameterizedTypeWithOwner(
91      @Nullable Type ownerType, Class<?> rawType, Type... arguments) {
92    if (ownerType == null) {
93      return newParameterizedType(rawType, arguments);
94    }
95    // ParameterizedTypeImpl constructor already checks, but we want to throw NPE before IAE
96    checkNotNull(arguments);
97    checkArgument(rawType.getEnclosingClass() != null, "Owner type for unenclosed %s", rawType);
98    return new ParameterizedTypeImpl(ownerType, rawType, arguments);
99  }
100
101  /**
102   * Returns a type where {@code rawType} is parameterized by
103   * {@code arguments}.
104   */
105  static ParameterizedType newParameterizedType(Class<?> rawType, Type... arguments) {
106      return new ParameterizedTypeImpl(
107          ClassOwnership.JVM_BEHAVIOR.getOwnerType(rawType), rawType, arguments);
108  }
109
110  /** Decides what owner type to use for constructing {@link ParameterizedType} from a raw class. */
111  private enum ClassOwnership {
112
113    OWNED_BY_ENCLOSING_CLASS {
114      @Nullable
115      @Override
116      Class<?> getOwnerType(Class<?> rawType) {
117        return rawType.getEnclosingClass();
118      }
119    },
120    LOCAL_CLASS_HAS_NO_OWNER {
121      @Nullable
122      @Override
123      Class<?> getOwnerType(Class<?> rawType) {
124        if (rawType.isLocalClass()) {
125          return null;
126        } else {
127          return rawType.getEnclosingClass();
128        }
129      }
130    };
131
132    @Nullable abstract Class<?> getOwnerType(Class<?> rawType);
133
134    static final ClassOwnership JVM_BEHAVIOR = detectJvmBehavior();
135
136    private static ClassOwnership detectJvmBehavior() {
137      class LocalClass<T> {}
138      Class<?> subclass = new LocalClass<String>() {}.getClass();
139      ParameterizedType parameterizedType = (ParameterizedType)
140          subclass.getGenericSuperclass();
141      for (ClassOwnership behavior : ClassOwnership.values()) {
142        if (behavior.getOwnerType(LocalClass.class) == parameterizedType.getOwnerType()) {
143          return behavior;
144        }
145      }
146      throw new AssertionError();
147    }
148  }
149
150  /**
151   * Returns a new {@link TypeVariable} that belongs to {@code declaration} with
152   * {@code name} and {@code bounds}.
153   */
154  static <D extends GenericDeclaration> TypeVariable<D> newArtificialTypeVariable(
155      D declaration, String name, Type... bounds) {
156    return newTypeVariableImpl(
157        declaration,
158        name,
159        (bounds.length == 0)
160            ? new Type[] { Object.class }
161            : bounds);
162  }
163
164  /** Returns a new {@link WildcardType} with {@code upperBound}. */
165  @VisibleForTesting static WildcardType subtypeOf(Type upperBound) {
166    return new WildcardTypeImpl(new Type[0], new Type[] { upperBound });
167  }
168
169  /** Returns a new {@link WildcardType} with {@code lowerBound}. */
170  @VisibleForTesting static WildcardType supertypeOf(Type lowerBound) {
171    return new WildcardTypeImpl(new Type[] { lowerBound }, new Type[] { Object.class });
172  }
173
174  /**
175   * Returns human readable string representation of {@code type}.
176   * <ul>
177   * <li> For array type {@code Foo[]}, {@code "com.mypackage.Foo[]"} are
178   * returned.
179   * <li> For any class, {@code theClass.getName()} are returned.
180   * <li> For all other types, {@code type.toString()} are returned.
181   * </ul>
182   */
183  static String toString(Type type) {
184    return (type instanceof Class)
185        ? ((Class<?>) type).getName()
186        : type.toString();
187  }
188
189  @Nullable static Type getComponentType(Type type) {
190    checkNotNull(type);
191    final AtomicReference<Type> result = new AtomicReference<Type>();
192    new TypeVisitor() {
193      @Override void visitTypeVariable(TypeVariable<?> t) {
194        result.set(subtypeOfComponentType(t.getBounds()));
195      }
196      @Override void visitWildcardType(WildcardType t) {
197        result.set(subtypeOfComponentType(t.getUpperBounds()));
198      }
199      @Override void visitGenericArrayType(GenericArrayType t) {
200        result.set(t.getGenericComponentType());
201      }
202      @Override void visitClass(Class<?> t) {
203        result.set(t.getComponentType());
204      }
205    }.visit(type);
206    return result.get();
207  }
208
209  /**
210   * Returns {@code ? extends X} if any of {@code bounds} is a subtype of {@code X[]}; or null
211   * otherwise.
212   */
213  @Nullable private static Type subtypeOfComponentType(Type[] bounds) {
214    for (Type bound : bounds) {
215      Type componentType = getComponentType(bound);
216      if (componentType != null) {
217        // Only the first bound can be a class or array.
218        // Bounds after the first can only be interfaces.
219        if (componentType instanceof Class) {
220          Class<?> componentClass = (Class<?>) componentType;
221          if (componentClass.isPrimitive()) {
222            return componentClass;
223          }
224        }
225        return subtypeOf(componentType);
226      }
227    }
228    return null;
229  }
230
231  private static final class GenericArrayTypeImpl
232      implements GenericArrayType, Serializable {
233
234    private final Type componentType;
235
236    GenericArrayTypeImpl(Type componentType) {
237      this.componentType = JavaVersion.CURRENT.usedInGenericType(componentType);
238    }
239
240    @Override public Type getGenericComponentType() {
241      return componentType;
242    }
243
244    @Override public String toString() {
245      return Types.toString(componentType) + "[]";
246    }
247
248    @Override public int hashCode() {
249      return componentType.hashCode();
250    }
251
252    @Override public boolean equals(Object obj) {
253      if (obj instanceof GenericArrayType) {
254        GenericArrayType that = (GenericArrayType) obj;
255        return Objects.equal(
256            getGenericComponentType(), that.getGenericComponentType());
257      }
258      return false;
259    }
260
261    private static final long serialVersionUID = 0;
262  }
263
264  private static final class ParameterizedTypeImpl
265      implements ParameterizedType, Serializable {
266
267    private final Type ownerType;
268    private final ImmutableList<Type> argumentsList;
269    private final Class<?> rawType;
270
271    ParameterizedTypeImpl(
272        @Nullable Type ownerType, Class<?> rawType, Type[] typeArguments) {
273      checkNotNull(rawType);
274      checkArgument(typeArguments.length == rawType.getTypeParameters().length);
275      disallowPrimitiveType(typeArguments, "type parameter");
276      this.ownerType = ownerType;
277      this.rawType = rawType;
278      this.argumentsList = JavaVersion.CURRENT.usedInGenericType(typeArguments);
279    }
280
281    @Override public Type[] getActualTypeArguments() {
282      return toArray(argumentsList);
283    }
284
285    @Override public Type getRawType() {
286      return rawType;
287    }
288
289    @Override public Type getOwnerType() {
290      return ownerType;
291    }
292
293    @Override public String toString() {
294      StringBuilder builder = new StringBuilder();
295      if (ownerType != null) {
296        builder.append(JavaVersion.CURRENT.typeName(ownerType)).append('.');
297      }
298      builder.append(rawType.getName())
299          .append('<')
300          .append(COMMA_JOINER.join(transform(argumentsList, TYPE_NAME)))
301          .append('>');
302      return builder.toString();
303    }
304
305    @Override public int hashCode() {
306      return (ownerType == null ? 0 : ownerType.hashCode())
307          ^ argumentsList.hashCode() ^ rawType.hashCode();
308    }
309
310    @Override public boolean equals(Object other) {
311      if (!(other instanceof ParameterizedType)) {
312        return false;
313      }
314      ParameterizedType that = (ParameterizedType) other;
315      return getRawType().equals(that.getRawType())
316          && Objects.equal(getOwnerType(), that.getOwnerType())
317          && Arrays.equals(
318              getActualTypeArguments(), that.getActualTypeArguments());
319    }
320
321    private static final long serialVersionUID = 0;
322  }
323
324  private static <D extends GenericDeclaration> TypeVariable<D> newTypeVariableImpl(
325      D genericDeclaration, String name, Type[] bounds) {
326    TypeVariableImpl<D> typeVariableImpl =
327        new TypeVariableImpl<D>(genericDeclaration, name, bounds);
328    @SuppressWarnings("unchecked")
329    TypeVariable<D> typeVariable = Reflection.newProxy(
330        TypeVariable.class, new TypeVariableInvocationHandler(typeVariableImpl));
331    return typeVariable;
332  }
333
334  /**
335   * Invocation handler to work around a compatibility problem between Java 7 and Java 8.
336   *
337   * <p>Java 8 introduced a new method {@code getAnnotatedBounds()} in the {@link TypeVariable}
338   * interface, whose return type {@code AnnotatedType[]} is also new in Java 8. That means that we
339   * cannot implement that interface in source code in a way that will compile on both Java 7 and
340   * Java 8. If we include the {@code getAnnotatedBounds()} method then its return type means
341   * it won't compile on Java 7, while if we don't include the method then the compiler will
342   * complain that an abstract method is unimplemented. So instead we use a dynamic proxy to
343   * get an implementation. If the method being called on the {@code TypeVariable} instance has
344   * the same name as one of the public methods of {@link TypeVariableImpl}, the proxy calls
345   * the same method on its instance of {@code TypeVariableImpl}. Otherwise it throws {@link
346   * UnsupportedOperationException}; this should only apply to {@code getAnnotatedBounds()}. This
347   * does mean that users on Java 8 who obtain an instance of {@code TypeVariable} from {@link
348   * TypeResolver#resolveType} will not be able to call {@code getAnnotatedBounds()} on it, but that
349   * should hopefully be rare.
350   *
351   * <p>This workaround should be removed at a distant future time when we no longer support Java
352   * versions earlier than 8.
353   */
354  private static final class TypeVariableInvocationHandler implements InvocationHandler {
355    private static final ImmutableMap<String, Method> typeVariableMethods;
356    static {
357      ImmutableMap.Builder<String, Method> builder = ImmutableMap.builder();
358      for (Method method : TypeVariableImpl.class.getMethods()) {
359        if (method.getDeclaringClass().equals(TypeVariableImpl.class)) {
360          try {
361            method.setAccessible(true);
362          } catch (AccessControlException e) {
363            // OK: the method is accessible to us anyway. The setAccessible call is only for
364            // unusual execution environments where that might not be true.
365          }
366          builder.put(method.getName(), method);
367        }
368      }
369      typeVariableMethods = builder.build();
370    }
371
372    private final TypeVariableImpl<?> typeVariableImpl;
373
374    TypeVariableInvocationHandler(TypeVariableImpl<?> typeVariableImpl) {
375      this.typeVariableImpl = typeVariableImpl;
376    }
377
378    @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
379      String methodName = method.getName();
380      Method typeVariableMethod = typeVariableMethods.get(methodName);
381      if (typeVariableMethod == null) {
382        throw new UnsupportedOperationException(methodName);
383      } else {
384        try {
385          return typeVariableMethod.invoke(typeVariableImpl, args);
386        } catch (InvocationTargetException e) {
387          throw e.getCause();
388        }
389      }
390    }
391  }
392
393  private static final class TypeVariableImpl<D extends GenericDeclaration> {
394
395    private final D genericDeclaration;
396    private final String name;
397    private final ImmutableList<Type> bounds;
398
399    TypeVariableImpl(D genericDeclaration, String name, Type[] bounds) {
400      disallowPrimitiveType(bounds, "bound for type variable");
401      this.genericDeclaration = checkNotNull(genericDeclaration);
402      this.name = checkNotNull(name);
403      this.bounds = ImmutableList.copyOf(bounds);
404    }
405
406    public Type[] getBounds() {
407      return toArray(bounds);
408    }
409
410    public D getGenericDeclaration() {
411      return genericDeclaration;
412    }
413
414    public String getName() {
415      return name;
416    }
417
418    public String getTypeName() {
419      return name;
420    }
421
422    @Override public String toString() {
423      return name;
424    }
425
426    @Override public int hashCode() {
427      return genericDeclaration.hashCode() ^ name.hashCode();
428    }
429
430    @Override public boolean equals(Object obj) {
431      if (NativeTypeVariableEquals.NATIVE_TYPE_VARIABLE_ONLY) {
432        // equal only to our TypeVariable implementation with identical bounds
433        if (obj != null
434            && Proxy.isProxyClass(obj.getClass())
435            && Proxy.getInvocationHandler(obj) instanceof TypeVariableInvocationHandler) {
436          TypeVariableInvocationHandler typeVariableInvocationHandler =
437              (TypeVariableInvocationHandler) Proxy.getInvocationHandler(obj);
438          TypeVariableImpl<?> that = typeVariableInvocationHandler.typeVariableImpl;
439          return name.equals(that.getName())
440              && genericDeclaration.equals(that.getGenericDeclaration())
441              && bounds.equals(that.bounds);
442        }
443        return false;
444      } else {
445        // equal to any TypeVariable implementation regardless of bounds
446        if (obj instanceof TypeVariable) {
447          TypeVariable<?> that = (TypeVariable<?>) obj;
448          return name.equals(that.getName())
449              && genericDeclaration.equals(that.getGenericDeclaration());
450        }
451        return false;
452      }
453    }
454  }
455
456  static final class WildcardTypeImpl implements WildcardType, Serializable {
457
458    private final ImmutableList<Type> lowerBounds;
459    private final ImmutableList<Type> upperBounds;
460
461    WildcardTypeImpl(Type[] lowerBounds, Type[] upperBounds) {
462      disallowPrimitiveType(lowerBounds, "lower bound for wildcard");
463      disallowPrimitiveType(upperBounds, "upper bound for wildcard");
464      this.lowerBounds = JavaVersion.CURRENT.usedInGenericType(lowerBounds);
465      this.upperBounds = JavaVersion.CURRENT.usedInGenericType(upperBounds);
466    }
467
468    @Override public Type[] getLowerBounds() {
469      return toArray(lowerBounds);
470    }
471
472    @Override public Type[] getUpperBounds() {
473      return toArray(upperBounds);
474    }
475
476    @Override public boolean equals(Object obj) {
477      if (obj instanceof WildcardType) {
478        WildcardType that = (WildcardType) obj;
479        return lowerBounds.equals(Arrays.asList(that.getLowerBounds()))
480            && upperBounds.equals(Arrays.asList(that.getUpperBounds()));
481      }
482      return false;
483    }
484
485    @Override public int hashCode() {
486      return lowerBounds.hashCode() ^ upperBounds.hashCode();
487    }
488
489    @Override public String toString() {
490      StringBuilder builder = new StringBuilder("?");
491      for (Type lowerBound : lowerBounds) {
492        builder.append(" super ").append(JavaVersion.CURRENT.typeName(lowerBound));
493      }
494      for (Type upperBound : filterUpperBounds(upperBounds)) {
495        builder.append(" extends ").append(JavaVersion.CURRENT.typeName(upperBound));
496      }
497      return builder.toString();
498    }
499
500    private static final long serialVersionUID = 0;
501  }
502
503  private static Type[] toArray(Collection<Type> types) {
504    return types.toArray(new Type[types.size()]);
505  }
506
507  private static Iterable<Type> filterUpperBounds(Iterable<Type> bounds) {
508    return Iterables.filter(
509        bounds, Predicates.not(Predicates.<Type>equalTo(Object.class)));
510  }
511
512  private static void disallowPrimitiveType(Type[] types, String usedAs) {
513    for (Type type : types) {
514      if (type instanceof Class) {
515        Class<?> cls = (Class<?>) type;
516        checkArgument(!cls.isPrimitive(),
517            "Primitive type '%s' used as %s", cls, usedAs);
518      }
519    }
520  }
521
522  /** Returns the {@code Class} object of arrays with {@code componentType}. */
523  static Class<?> getArrayClass(Class<?> componentType) {
524    // TODO(user): This is not the most efficient way to handle generic
525    // arrays, but is there another way to extract the array class in a
526    // non-hacky way (i.e. using String value class names- "[L...")?
527    return Array.newInstance(componentType, 0).getClass();
528  }
529
530  // TODO(benyu): Once we are on Java 8, delete this abstraction
531  enum JavaVersion {
532
533    JAVA6 {
534      @Override GenericArrayType newArrayType(Type componentType) {
535        return new GenericArrayTypeImpl(componentType);
536      }
537      @Override Type usedInGenericType(Type type) {
538        checkNotNull(type);
539        if (type instanceof Class) {
540          Class<?> cls = (Class<?>) type;
541          if (cls.isArray()) {
542            return new GenericArrayTypeImpl(cls.getComponentType());
543          }
544        }
545        return type;
546      }
547    },
548    JAVA7 {
549      @Override Type newArrayType(Type componentType) {
550        if (componentType instanceof Class) {
551          return getArrayClass((Class<?>) componentType);
552        } else {
553          return new GenericArrayTypeImpl(componentType);
554        }
555      }
556      @Override Type usedInGenericType(Type type) {
557        return checkNotNull(type);
558      }
559    },
560    JAVA8 {
561      @Override Type newArrayType(Type componentType) {
562        return JAVA7.newArrayType(componentType);
563      }
564      @Override Type usedInGenericType(Type type) {
565        return JAVA7.usedInGenericType(type);
566      }
567      @Override String typeName(Type type) {
568        try {
569          Method getTypeName = Type.class.getMethod("getTypeName");
570          return (String) getTypeName.invoke(type);
571        } catch (NoSuchMethodException e) {
572          throw new AssertionError("Type.getTypeName should be available in Java 8");
573        } catch (InvocationTargetException e) {
574          throw new RuntimeException(e);
575        } catch (IllegalAccessException e) {
576          throw new RuntimeException(e);
577        }
578      }
579    }
580    ;
581
582    static final JavaVersion CURRENT;
583    static {
584      if (AnnotatedElement.class.isAssignableFrom(TypeVariable.class)) {
585        CURRENT = JAVA8;
586      } else if (new TypeCapture<int[]>() {}.capture() instanceof Class) {
587        CURRENT = JAVA7;
588      } else {
589        CURRENT = JAVA6;
590      }
591    }
592
593    abstract Type newArrayType(Type componentType);
594    abstract Type usedInGenericType(Type type);
595    String typeName(Type type) {
596      return Types.toString(type);
597    }
598
599    final ImmutableList<Type> usedInGenericType(Type[] types) {
600      ImmutableList.Builder<Type> builder = ImmutableList.builder();
601      for (Type type : types) {
602        builder.add(usedInGenericType(type));
603      }
604      return builder.build();
605    }
606  }
607
608  /**
609   * Per https://code.google.com/p/guava-libraries/issues/detail?id=1635,
610   * In JDK 1.7.0_51-b13, TypeVariableImpl.equals() is changed to no longer be equal to custom
611   * TypeVariable implementations. As a result, we need to make sure our TypeVariable implementation
612   * respects symmetry.
613   * Moreover, we don't want to reconstruct a native type variable <A> using our implementation
614   * unless some of its bounds have changed in resolution. This avoids creating unequal TypeVariable
615   * implementation unnecessarily. When the bounds do change, however, it's fine for the synthetic
616   * TypeVariable to be unequal to any native TypeVariable anyway.
617   */
618  static final class NativeTypeVariableEquals<X> {
619    static final boolean NATIVE_TYPE_VARIABLE_ONLY =
620        !NativeTypeVariableEquals.class.getTypeParameters()[0].equals(
621            newArtificialTypeVariable(NativeTypeVariableEquals.class, "X"));
622  }
623
624  private Types() {}
625}
626