Home | History | Annotate | Download | only in reflect
      1 /*
      2  * Copyright (C) 2012 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 
     17 package com.google.common.reflect;
     18 
     19 import static com.google.common.base.Preconditions.checkNotNull;
     20 
     21 import com.google.common.annotations.Beta;
     22 import com.google.common.collect.ImmutableList;
     23 
     24 import java.lang.annotation.Annotation;
     25 import java.lang.reflect.AccessibleObject;
     26 import java.lang.reflect.Constructor;
     27 import java.lang.reflect.GenericDeclaration;
     28 import java.lang.reflect.InvocationTargetException;
     29 import java.lang.reflect.Member;
     30 import java.lang.reflect.Method;
     31 import java.lang.reflect.Modifier;
     32 import java.lang.reflect.Type;
     33 import java.lang.reflect.TypeVariable;
     34 import java.util.Arrays;
     35 
     36 import javax.annotation.Nullable;
     37 
     38 /**
     39  * Wrapper around either a {@link Method} or a {@link Constructor}.
     40  * Convenience API is provided to make common reflective operation easier to deal with,
     41  * such as {@link #isPublic}, {@link #getParameters} etc.
     42  *
     43  * <p>In addition to convenience methods, {@link TypeToken#method} and {@link
     44  * TypeToken#constructor} will resolve the type parameters of the method or constructor in the
     45  * context of the owner type, which may be a subtype of the declaring class. For example:
     46  *
     47  * <pre>   {@code
     48  *   Method getMethod = List.class.getMethod("get", int.class);
     49  *   Invokable<List<String>, ?> invokable = new TypeToken<List<String>>() {}.method(getMethod);
     50  *   assertEquals(TypeToken.of(String.class), invokable.getReturnType()); // Not Object.class!
     51  *   assertEquals(new TypeToken<List<String>>() {}, invokable.getOwnerType());}</pre>
     52  *
     53  * @param <T> the type that owns this method or constructor.
     54  * @param <R> the return type of (or supertype thereof) the method or the declaring type of the
     55  *            constructor.
     56  * @author Ben Yu
     57  * @since 14.0
     58  */
     59 @Beta
     60 public abstract class Invokable<T, R> extends Element implements GenericDeclaration {
     61 
     62   <M extends AccessibleObject & Member> Invokable(M member) {
     63     super(member);
     64   }
     65 
     66   /** Returns {@link Invokable} of {@code method}. */
     67   public static Invokable<?, Object> from(Method method) {
     68     return new MethodInvokable<Object>(method);
     69   }
     70 
     71   /** Returns {@link Invokable} of {@code constructor}. */
     72   public static <T> Invokable<T, T> from(Constructor<T> constructor) {
     73     return new ConstructorInvokable<T>(constructor);
     74   }
     75 
     76   /**
     77    * Returns {@code true} if this is an overridable method. Constructors, private, static or final
     78    * methods, or methods declared by final classes are not overridable.
     79    */
     80   public abstract boolean isOverridable();
     81 
     82   /** Returns {@code true} if this was declared to take a variable number of arguments. */
     83   public abstract boolean isVarArgs();
     84 
     85   /**
     86    * Invokes with {@code receiver} as 'this' and {@code args} passed to the underlying method
     87    * and returns the return value; or calls the underlying constructor with {@code args} and returns
     88    * the constructed instance.
     89    *
     90    * @throws IllegalAccessException if this {@code Constructor} object enforces Java language
     91    *         access control and the underlying method or constructor is inaccessible.
     92    * @throws IllegalArgumentException if the number of actual and formal parameters differ;
     93    *         if an unwrapping conversion for primitive arguments fails; or if, after possible
     94    *         unwrapping, a parameter value cannot be converted to the corresponding formal
     95    *         parameter type by a method invocation conversion.
     96    * @throws InvocationTargetException if the underlying method or constructor throws an exception.
     97    */
     98   // All subclasses are owned by us and we'll make sure to get the R type right.
     99   @SuppressWarnings("unchecked")
    100   public final R invoke(@Nullable T receiver, Object... args)
    101       throws InvocationTargetException, IllegalAccessException {
    102     return (R) invokeInternal(receiver, checkNotNull(args));
    103   }
    104 
    105   /** Returns the return type of this {@code Invokable}. */
    106   // All subclasses are owned by us and we'll make sure to get the R type right.
    107   @SuppressWarnings("unchecked")
    108   public final TypeToken<? extends R> getReturnType() {
    109     return (TypeToken<? extends R>) TypeToken.of(getGenericReturnType());
    110   }
    111 
    112   /**
    113    * Returns all declared parameters of this {@code Invokable}. Note that if this is a constructor
    114    * of a non-static inner class, unlike {@link Constructor#getParameterTypes}, the hidden
    115    * {@code this} parameter of the enclosing class is excluded from the returned parameters.
    116    */
    117   public final ImmutableList<Parameter> getParameters() {
    118     Type[] parameterTypes = getGenericParameterTypes();
    119     Annotation[][] annotations = getParameterAnnotations();
    120     ImmutableList.Builder<Parameter> builder = ImmutableList.builder();
    121     for (int i = 0; i < parameterTypes.length; i++) {
    122       builder.add(new Parameter(
    123           this, i, TypeToken.of(parameterTypes[i]), annotations[i]));
    124     }
    125     return builder.build();
    126   }
    127 
    128   /** Returns all declared exception types of this {@code Invokable}. */
    129   public final ImmutableList<TypeToken<? extends Throwable>> getExceptionTypes() {
    130     ImmutableList.Builder<TypeToken<? extends Throwable>> builder = ImmutableList.builder();
    131     for (Type type : getGenericExceptionTypes()) {
    132        // getGenericExceptionTypes() will never return a type that's not exception
    133       @SuppressWarnings("unchecked")
    134       TypeToken<? extends Throwable> exceptionType = (TypeToken<? extends Throwable>)
    135           TypeToken.of(type);
    136       builder.add(exceptionType);
    137     }
    138     return builder.build();
    139   }
    140 
    141   /**
    142    * Explicitly specifies the return type of this {@code Invokable}. For example:
    143    * <pre>   {@code
    144    *   Method factoryMethod = Person.class.getMethod("create");
    145    *   Invokable<?, Person> factory = Invokable.of(getNameMethod).returning(Person.class);}</pre>
    146    */
    147   public final <R1 extends R> Invokable<T, R1> returning(Class<R1> returnType) {
    148     return returning(TypeToken.of(returnType));
    149   }
    150 
    151   /** Explicitly specifies the return type of this {@code Invokable}. */
    152   public final <R1 extends R> Invokable<T, R1> returning(TypeToken<R1> returnType) {
    153     if (!returnType.isAssignableFrom(getReturnType())) {
    154       throw new IllegalArgumentException(
    155           "Invokable is known to return " + getReturnType() + ", not " + returnType);
    156     }
    157     @SuppressWarnings("unchecked") // guarded by previous check
    158     Invokable<T, R1> specialized = (Invokable<T, R1>) this;
    159     return specialized;
    160   }
    161 
    162   @SuppressWarnings("unchecked") // The declaring class is T's raw class, or one of its supertypes.
    163   @Override public final Class<? super T> getDeclaringClass() {
    164     return (Class<? super T>) super.getDeclaringClass();
    165   }
    166 
    167   /** Returns the type of {@code T}. */
    168   // Overridden in TypeToken#method() and TypeToken#constructor()
    169   @SuppressWarnings("unchecked") // The declaring class is T.
    170   @Override public TypeToken<T> getOwnerType() {
    171     return (TypeToken<T>) TypeToken.of(getDeclaringClass());
    172   }
    173 
    174   abstract Object invokeInternal(@Nullable Object receiver, Object[] args)
    175       throws InvocationTargetException, IllegalAccessException;
    176 
    177   abstract Type[] getGenericParameterTypes();
    178 
    179   /** This should never return a type that's not a subtype of Throwable. */
    180   abstract Type[] getGenericExceptionTypes();
    181 
    182   abstract Annotation[][] getParameterAnnotations();
    183 
    184   abstract Type getGenericReturnType();
    185 
    186   static class MethodInvokable<T> extends Invokable<T, Object> {
    187 
    188     final Method method;
    189 
    190     MethodInvokable(Method method) {
    191       super(method);
    192       this.method = method;
    193     }
    194 
    195     @Override final Object invokeInternal(@Nullable Object receiver, Object[] args)
    196         throws InvocationTargetException, IllegalAccessException {
    197       return method.invoke(receiver, args);
    198     }
    199 
    200     @Override Type getGenericReturnType() {
    201       return method.getGenericReturnType();
    202     }
    203 
    204     @Override Type[] getGenericParameterTypes() {
    205       return method.getGenericParameterTypes();
    206     }
    207 
    208     @Override Type[] getGenericExceptionTypes() {
    209       return method.getGenericExceptionTypes();
    210     }
    211 
    212     @Override final Annotation[][] getParameterAnnotations() {
    213       return method.getParameterAnnotations();
    214     }
    215 
    216     @Override public final TypeVariable<?>[] getTypeParameters() {
    217       return method.getTypeParameters();
    218     }
    219 
    220     @Override public final boolean isOverridable() {
    221       return  !(isFinal() || isPrivate() || isStatic()
    222           || Modifier.isFinal(getDeclaringClass().getModifiers()));
    223     }
    224 
    225     @Override public final boolean isVarArgs() {
    226       return method.isVarArgs();
    227     }
    228   }
    229 
    230   static class ConstructorInvokable<T> extends Invokable<T, T> {
    231 
    232     final Constructor<?> constructor;
    233 
    234     ConstructorInvokable(Constructor<?> constructor) {
    235       super(constructor);
    236       this.constructor = constructor;
    237     }
    238 
    239     @Override final Object invokeInternal(@Nullable Object receiver, Object[] args)
    240         throws InvocationTargetException, IllegalAccessException {
    241       try {
    242         return constructor.newInstance(args);
    243       } catch (InstantiationException e) {
    244         throw new RuntimeException(constructor + " failed.", e);
    245       }
    246     }
    247 
    248     /** If the class is parameterized, such as ArrayList, this returns ArrayList<E>. */
    249     @Override Type getGenericReturnType() {
    250       Class<?> declaringClass = getDeclaringClass();
    251       TypeVariable<?>[] typeParams = declaringClass.getTypeParameters();
    252       if (typeParams.length > 0) {
    253         return Types.newParameterizedType(declaringClass, typeParams);
    254       } else {
    255         return declaringClass;
    256       }
    257     }
    258 
    259     @Override Type[] getGenericParameterTypes() {
    260       Type[] types = constructor.getGenericParameterTypes();
    261       if (types.length > 0 && mayNeedHiddenThis()) {
    262         Class<?>[] rawParamTypes = constructor.getParameterTypes();
    263         if (types.length == rawParamTypes.length
    264             && rawParamTypes[0] == getDeclaringClass().getEnclosingClass()) {
    265           // first parameter is the hidden 'this'
    266           return Arrays.copyOfRange(types, 1, types.length);
    267         }
    268       }
    269       return types;
    270     }
    271 
    272     @Override Type[] getGenericExceptionTypes() {
    273       return constructor.getGenericExceptionTypes();
    274     }
    275 
    276     @Override final Annotation[][] getParameterAnnotations() {
    277       return constructor.getParameterAnnotations();
    278     }
    279 
    280     /**
    281      * {@inheritDoc}
    282      *
    283      * {@code [<E>]} will be returned for ArrayList's constructor. When both the class and the
    284      * constructor have type parameters, the class parameters are prepended before those of the
    285      * constructor's. This is an arbitrary rule since no existing language spec mandates one way or
    286      * the other. From the declaration syntax, the class type parameter appears first, but the
    287      * call syntax may show up in opposite order such as {@code new <A>Foo<B>()}.
    288      */
    289     @Override public final TypeVariable<?>[] getTypeParameters() {
    290       TypeVariable<?>[] declaredByClass = getDeclaringClass().getTypeParameters();
    291       TypeVariable<?>[] declaredByConstructor = constructor.getTypeParameters();
    292       TypeVariable<?>[] result =
    293           new TypeVariable<?>[declaredByClass.length + declaredByConstructor.length];
    294       System.arraycopy(declaredByClass, 0, result, 0, declaredByClass.length);
    295       System.arraycopy(
    296           declaredByConstructor, 0,
    297           result, declaredByClass.length,
    298           declaredByConstructor.length);
    299       return result;
    300     }
    301 
    302     @Override public final boolean isOverridable() {
    303       return false;
    304     }
    305 
    306     @Override public final boolean isVarArgs() {
    307       return constructor.isVarArgs();
    308     }
    309 
    310     private boolean mayNeedHiddenThis() {
    311       Class<?> declaringClass = constructor.getDeclaringClass();
    312       if (declaringClass.getEnclosingConstructor() != null) {
    313         // Enclosed in a constructor, needs hidden this
    314         return true;
    315       }
    316       Method enclosingMethod = declaringClass.getEnclosingMethod();
    317       if (enclosingMethod != null) {
    318         // Enclosed in a method, if it's not static, must need hidden this.
    319         return !Modifier.isStatic(enclosingMethod.getModifiers());
    320       } else {
    321         // Strictly, this doesn't necessarily indicate a hidden 'this' in the case of
    322         // static initializer. But there seems no way to tell in that case. :(
    323         // This may cause issues when an anonymous class is created inside a static initializer,
    324         // and the class's constructor's first parameter happens to be the enclosing class.
    325         // In such case, we may mistakenly think that the class is within a non-static context
    326         // and the first parameter is the hidden 'this'.
    327         return declaringClass.getEnclosingClass() != null
    328             && !Modifier.isStatic(declaringClass.getModifiers());
    329       }
    330     }
    331   }
    332 }
    333