001/*
002 * Copyright (C) 2012 The Guava Authors
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package com.google.common.reflect;
018
019import static com.google.common.base.Preconditions.checkNotNull;
020
021import com.google.common.annotations.Beta;
022import com.google.common.collect.ImmutableList;
023
024import java.lang.annotation.Annotation;
025import java.lang.reflect.AccessibleObject;
026import java.lang.reflect.Constructor;
027import java.lang.reflect.GenericDeclaration;
028import java.lang.reflect.InvocationTargetException;
029import java.lang.reflect.Member;
030import java.lang.reflect.Method;
031import java.lang.reflect.Modifier;
032import java.lang.reflect.Type;
033import java.lang.reflect.TypeVariable;
034import java.util.Arrays;
035
036import javax.annotation.Nullable;
037
038/**
039 * Wrapper around either a {@link Method} or a {@link Constructor}.
040 * Convenience API is provided to make common reflective operation easier to deal with,
041 * such as {@link #isPublic}, {@link #getParameters} etc.
042 *
043 * <p>In addition to convenience methods, {@link TypeToken#method} and {@link
044 * TypeToken#constructor} will resolve the type parameters of the method or constructor in the
045 * context of the owner type, which may be a subtype of the declaring class. For example:
046 *
047 * <pre>   {@code
048 *   Method getMethod = List.class.getMethod("get", int.class);
049 *   Invokable<List<String>, ?> invokable = new TypeToken<List<String>>() {}.method(getMethod);
050 *   assertEquals(TypeToken.of(String.class), invokable.getReturnType()); // Not Object.class!
051 *   assertEquals(new TypeToken<List<String>>() {}, invokable.getOwnerType());}</pre>
052 * 
053 * @param <T> the type that owns this method or constructor.
054 * @param <R> the return type of (or supertype thereof) the method or the declaring type of the
055 *            constructor.
056 * @author Ben Yu
057 * @since 14.0
058 */
059@Beta
060public abstract class Invokable<T, R> extends Element implements GenericDeclaration {
061
062  <M extends AccessibleObject & Member> Invokable(M member) {
063    super(member);
064  }
065
066  /** Returns {@link Invokable} of {@code method}. */
067  public static Invokable<?, Object> from(Method method) {
068    return new MethodInvokable<Object>(method);
069  }
070
071  /** Returns {@link Invokable} of {@code constructor}. */
072  public static <T> Invokable<T, T> from(Constructor<T> constructor) {
073    return new ConstructorInvokable<T>(constructor);
074  }
075
076  /**
077   * Returns {@code true} if this is an overridable method. Constructors, private, static or final
078   * methods, or methods declared by final classes are not overridable.
079   */
080  public abstract boolean isOverridable();
081
082  /** Returns {@code true} if this was declared to take a variable number of arguments. */
083  public abstract boolean isVarArgs();
084
085  /**
086   * Invokes with {@code receiver} as 'this' and {@code args} passed to the underlying method
087   * and returns the return value; or calls the underlying constructor with {@code args} and returns
088   * the constructed instance.
089   *
090   * @throws IllegalAccessException if this {@code Constructor} object enforces Java language
091   *         access control and the underlying method or constructor is inaccessible.
092   * @throws IllegalArgumentException if the number of actual and formal parameters differ;
093   *         if an unwrapping conversion for primitive arguments fails; or if, after possible
094   *         unwrapping, a parameter value cannot be converted to the corresponding formal
095   *         parameter type by a method invocation conversion.
096   * @throws InvocationTargetException if the underlying method or constructor throws an exception.
097   */
098  // All subclasses are owned by us and we'll make sure to get the R type right.
099  @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}