001/*
002 * Copyright (C) 2012 The Guava Authors
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
005 * in compliance with the License. You may obtain a copy of the License at
006 *
007 * http://www.apache.org/licenses/LICENSE-2.0
008 *
009 * Unless required by applicable law or agreed to in writing, software distributed under the License
010 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
011 * or implied. See the License for the specific language governing permissions and limitations under
012 * the License.
013 */
014
015package com.google.common.reflect;
016
017import static com.google.common.base.Preconditions.checkNotNull;
018
019import com.google.common.annotations.Beta;
020import com.google.common.collect.ImmutableList;
021import com.google.errorprone.annotations.CanIgnoreReturnValue;
022import java.lang.annotation.Annotation;
023import java.lang.reflect.AccessibleObject;
024import java.lang.reflect.Constructor;
025import java.lang.reflect.GenericDeclaration;
026import java.lang.reflect.InvocationTargetException;
027import java.lang.reflect.Member;
028import java.lang.reflect.Method;
029import java.lang.reflect.Modifier;
030import java.lang.reflect.Type;
031import java.lang.reflect.TypeVariable;
032import java.util.Arrays;
033import org.checkerframework.checker.nullness.compatqual.NullableDecl;
034
035/**
036 * Wrapper around either a {@link Method} or a {@link Constructor}. Convenience API is provided to
037 * make common reflective operation easier to deal with, such as {@link #isPublic}, {@link
038 * #getParameters} etc.
039 *
040 * <p>In addition to convenience methods, {@link TypeToken#method} and {@link TypeToken#constructor}
041 * will resolve the type parameters of the method or constructor in the context of the owner type,
042 * which may be a subtype of the declaring class. For example:
043 *
044 * <pre>{@code
045 * Method getMethod = List.class.getMethod("get", int.class);
046 * Invokable<List<String>, ?> invokable = new TypeToken<List<String>>() {}.method(getMethod);
047 * assertEquals(TypeToken.of(String.class), invokable.getReturnType()); // Not Object.class!
048 * assertEquals(new TypeToken<List<String>>() {}, invokable.getOwnerType());
049 * }</pre>
050 *
051 * @param <T> the type that owns this method or constructor.
052 * @param <R> the return type of (or supertype thereof) the method or the declaring type of the
053 *     constructor.
054 * @author Ben Yu
055 * @since 14.0
056 */
057@Beta
058public abstract class Invokable<T, R> extends Element implements GenericDeclaration {
059
060  <M extends AccessibleObject & Member> Invokable(M member) {
061    super(member);
062  }
063
064  /** Returns {@link Invokable} of {@code method}. */
065  public static Invokable<?, Object> from(Method method) {
066    return new MethodInvokable<>(method);
067  }
068
069  /** Returns {@link Invokable} of {@code constructor}. */
070  public static <T> Invokable<T, T> from(Constructor<T> constructor) {
071    return new ConstructorInvokable<T>(constructor);
072  }
073
074  /**
075   * Returns {@code true} if this is an overridable method. Constructors, private, static or final
076   * methods, or methods declared by final classes are not overridable.
077   */
078  public abstract boolean isOverridable();
079
080  /** Returns {@code true} if this was declared to take a variable number of arguments. */
081  public abstract boolean isVarArgs();
082
083  /**
084   * Invokes with {@code receiver} as 'this' and {@code args} passed to the underlying method and
085   * returns the return value; or calls the underlying constructor with {@code args} and returns the
086   * constructed instance.
087   *
088   * @throws IllegalAccessException if this {@code Constructor} object enforces Java language access
089   *     control and the underlying method or constructor is inaccessible.
090   * @throws IllegalArgumentException if the number of actual and formal parameters differ; if an
091   *     unwrapping conversion for primitive arguments fails; or if, after possible unwrapping, a
092   *     parameter value cannot be converted to the corresponding formal parameter type by a method
093   *     invocation conversion.
094   * @throws InvocationTargetException if the underlying method or constructor throws an exception.
095   */
096  // All subclasses are owned by us and we'll make sure to get the R type right.
097  @SuppressWarnings("unchecked")
098  @CanIgnoreReturnValue
099  public final R invoke(@NullableDecl T receiver, Object... args)
100      throws InvocationTargetException, IllegalAccessException {
101    return (R) invokeInternal(receiver, checkNotNull(args));
102  }
103
104  /** Returns the return type of this {@code Invokable}. */
105  // All subclasses are owned by us and we'll make sure to get the R type right.
106  @SuppressWarnings("unchecked")
107  public final TypeToken<? extends R> getReturnType() {
108    return (TypeToken<? extends R>) TypeToken.of(getGenericReturnType());
109  }
110
111  /**
112   * Returns all declared parameters of this {@code Invokable}. Note that if this is a constructor
113   * of a non-static inner class, unlike {@link Constructor#getParameterTypes}, the hidden {@code
114   * this} parameter of the enclosing class is excluded from the returned parameters.
115   */
116  public final ImmutableList<Parameter> getParameters() {
117    Type[] parameterTypes = getGenericParameterTypes();
118    Annotation[][] annotations = getParameterAnnotations();
119    ImmutableList.Builder<Parameter> builder = ImmutableList.builder();
120    for (int i = 0; i < parameterTypes.length; i++) {
121      builder.add(new Parameter(this, i, TypeToken.of(parameterTypes[i]), annotations[i]));
122    }
123    return builder.build();
124  }
125
126  /** Returns all declared exception types of this {@code Invokable}. */
127  public final ImmutableList<TypeToken<? extends Throwable>> getExceptionTypes() {
128    ImmutableList.Builder<TypeToken<? extends Throwable>> builder = ImmutableList.builder();
129    for (Type type : getGenericExceptionTypes()) {
130      // getGenericExceptionTypes() will never return a type that's not exception
131      @SuppressWarnings("unchecked")
132      TypeToken<? extends Throwable> exceptionType =
133          (TypeToken<? extends Throwable>) TypeToken.of(type);
134      builder.add(exceptionType);
135    }
136    return builder.build();
137  }
138
139  /**
140   * Explicitly specifies the return type of this {@code Invokable}. For example:
141   *
142   * <pre>{@code
143   * Method factoryMethod = Person.class.getMethod("create");
144   * Invokable<?, Person> factory = Invokable.of(getNameMethod).returning(Person.class);
145   * }</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.isSupertypeOf(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
164  public final Class<? super T> getDeclaringClass() {
165    return (Class<? super T>) super.getDeclaringClass();
166  }
167
168  /** Returns the type of {@code T}. */
169  // Overridden in TypeToken#method() and TypeToken#constructor()
170  @SuppressWarnings("unchecked") // The declaring class is T.
171  @Override
172  public TypeToken<T> getOwnerType() {
173    return (TypeToken<T>) TypeToken.of(getDeclaringClass());
174  }
175
176  abstract Object invokeInternal(@NullableDecl Object receiver, Object[] args)
177      throws InvocationTargetException, IllegalAccessException;
178
179  abstract Type[] getGenericParameterTypes();
180
181  /** This should never return a type that's not a subtype of Throwable. */
182  abstract Type[] getGenericExceptionTypes();
183
184  abstract Annotation[][] getParameterAnnotations();
185
186  abstract Type getGenericReturnType();
187
188  static class MethodInvokable<T> extends Invokable<T, Object> {
189
190    final Method method;
191
192    MethodInvokable(Method method) {
193      super(method);
194      this.method = method;
195    }
196
197    @Override
198    final Object invokeInternal(@NullableDecl Object receiver, Object[] args)
199        throws InvocationTargetException, IllegalAccessException {
200      return method.invoke(receiver, args);
201    }
202
203    @Override
204    Type getGenericReturnType() {
205      return method.getGenericReturnType();
206    }
207
208    @Override
209    Type[] getGenericParameterTypes() {
210      return method.getGenericParameterTypes();
211    }
212
213    @Override
214    Type[] getGenericExceptionTypes() {
215      return method.getGenericExceptionTypes();
216    }
217
218    @Override
219    final Annotation[][] getParameterAnnotations() {
220      return method.getParameterAnnotations();
221    }
222
223    @Override
224    public final TypeVariable<?>[] getTypeParameters() {
225      return method.getTypeParameters();
226    }
227
228    @Override
229    public final boolean isOverridable() {
230      return !(isFinal()
231          || isPrivate()
232          || isStatic()
233          || Modifier.isFinal(getDeclaringClass().getModifiers()));
234    }
235
236    @Override
237    public final boolean isVarArgs() {
238      return method.isVarArgs();
239    }
240  }
241
242  static class ConstructorInvokable<T> extends Invokable<T, T> {
243
244    final Constructor<?> constructor;
245
246    ConstructorInvokable(Constructor<?> constructor) {
247      super(constructor);
248      this.constructor = constructor;
249    }
250
251    @Override
252    final Object invokeInternal(@NullableDecl Object receiver, Object[] args)
253        throws InvocationTargetException, IllegalAccessException {
254      try {
255        return constructor.newInstance(args);
256      } catch (InstantiationException e) {
257        throw new RuntimeException(constructor + " failed.", e);
258      }
259    }
260
261    /**
262     * If the class is parameterized, such as {@link java.util.ArrayList ArrayList}, this returns
263     * {@code ArrayList<E>}.
264     */
265    @Override
266    Type getGenericReturnType() {
267      Class<?> declaringClass = getDeclaringClass();
268      TypeVariable<?>[] typeParams = declaringClass.getTypeParameters();
269      if (typeParams.length > 0) {
270        return Types.newParameterizedType(declaringClass, typeParams);
271      } else {
272        return declaringClass;
273      }
274    }
275
276    @Override
277    Type[] getGenericParameterTypes() {
278      Type[] types = constructor.getGenericParameterTypes();
279      if (types.length > 0 && mayNeedHiddenThis()) {
280        Class<?>[] rawParamTypes = constructor.getParameterTypes();
281        if (types.length == rawParamTypes.length
282            && rawParamTypes[0] == getDeclaringClass().getEnclosingClass()) {
283          // first parameter is the hidden 'this'
284          return Arrays.copyOfRange(types, 1, types.length);
285        }
286      }
287      return types;
288    }
289
290    @Override
291    Type[] getGenericExceptionTypes() {
292      return constructor.getGenericExceptionTypes();
293    }
294
295    @Override
296    final Annotation[][] getParameterAnnotations() {
297      return constructor.getParameterAnnotations();
298    }
299
300    /**
301     * {@inheritDoc}
302     *
303     * <p>{@code [<E>]} will be returned for ArrayList's constructor. When both the class and the
304     * constructor have type parameters, the class parameters are prepended before those of the
305     * constructor's. This is an arbitrary rule since no existing language spec mandates one way or
306     * the other. From the declaration syntax, the class type parameter appears first, but the call
307     * syntax may show up in opposite order such as {@code new <A>Foo<B>()}.
308     */
309    @Override
310    public final TypeVariable<?>[] getTypeParameters() {
311      TypeVariable<?>[] declaredByClass = getDeclaringClass().getTypeParameters();
312      TypeVariable<?>[] declaredByConstructor = constructor.getTypeParameters();
313      TypeVariable<?>[] result =
314          new TypeVariable<?>[declaredByClass.length + declaredByConstructor.length];
315      System.arraycopy(declaredByClass, 0, result, 0, declaredByClass.length);
316      System.arraycopy(
317          declaredByConstructor, 0, result, declaredByClass.length, declaredByConstructor.length);
318      return result;
319    }
320
321    @Override
322    public final boolean isOverridable() {
323      return false;
324    }
325
326    @Override
327    public final boolean isVarArgs() {
328      return constructor.isVarArgs();
329    }
330
331    private boolean mayNeedHiddenThis() {
332      Class<?> declaringClass = constructor.getDeclaringClass();
333      if (declaringClass.getEnclosingConstructor() != null) {
334        // Enclosed in a constructor, needs hidden this
335        return true;
336      }
337      Method enclosingMethod = declaringClass.getEnclosingMethod();
338      if (enclosingMethod != null) {
339        // Enclosed in a method, if it's not static, must need hidden this.
340        return !Modifier.isStatic(enclosingMethod.getModifiers());
341      } else {
342        // Strictly, this doesn't necessarily indicate a hidden 'this' in the case of
343        // static initializer. But there seems no way to tell in that case. :(
344        // This may cause issues when an anonymous class is created inside a static initializer,
345        // and the class's constructor's first parameter happens to be the enclosing class.
346        // In such case, we may mistakenly think that the class is within a non-static context
347        // and the first parameter is the hidden 'this'.
348        return declaringClass.getEnclosingClass() != null
349            && !Modifier.isStatic(declaringClass.getModifiers());
350      }
351    }
352  }
353}