All Downloads are FREE. Search and download functionalities are using the official Maven repository.

acscommons.com.google.common.reflect.Invokable Maven / Gradle / Ivy

There is a newer version: 6.9.4
Show newest version
/*
 * Copyright (C) 2012 The Guava Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */

package acscommons.com.google.common.reflect;

import static acscommons.com.google.common.base.Preconditions.checkNotNull;

import acscommons.com.google.common.annotations.Beta;
import acscommons.com.google.common.collect.ImmutableList;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import java.lang.annotation.Annotation;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.AnnotatedType;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.Arrays;
import javax.annotation.CheckForNull;
import org.checkerframework.checker.nullness.qual.Nullable;

/**
 * Wrapper around either a {@link Method} or a {@link Constructor}. Convenience API is provided to
 * make common reflective operation easier to deal with, such as {@link #isPublic}, {@link
 * #getParameters} etc.
 *
 * 

In addition to convenience methods, {@link TypeToken#method} and {@link TypeToken#constructor} * will resolve the type parameters of the method or constructor in the context of the owner type, * which may be a subtype of the declaring class. For example: * *

{@code
 * Method getMethod = List.class.getMethod("get", int.class);
 * Invokable, ?> invokable = new TypeToken>() {}.method(getMethod);
 * assertEquals(TypeToken.of(String.class), invokable.getReturnType()); // Not Object.class!
 * assertEquals(new TypeToken>() {}, invokable.getOwnerType());
 * }
* *

Note: earlier versions of this class inherited from {@link * java.lang.reflect.AccessibleObject AccessibleObject} and {@link * java.lang.reflect.GenericDeclaration GenericDeclaration}. Since version 31.0 that is no longer * the case. However, most methods from those types are present with the same signature in this * class. * * @param the type that owns this method or constructor. * @param the return type of (or supertype thereof) the method or the declaring type of the * constructor. * @author Ben Yu * @since 14.0 (no longer implements {@link AccessibleObject} or {@code GenericDeclaration} since * 31.0) */ @Beta @ElementTypesAreNonnullByDefault public abstract class Invokable implements AnnotatedElement, Member { private final AccessibleObject accessibleObject; private final Member member; Invokable(M member) { checkNotNull(member); this.accessibleObject = member; this.member = member; } /** Returns {@link Invokable} of {@code method}. */ public static Invokable from(Method method) { return new MethodInvokable<>(method); } /** Returns {@link Invokable} of {@code constructor}. */ public static Invokable from(Constructor constructor) { return new ConstructorInvokable(constructor); } @Override public final boolean isAnnotationPresent(Class annotationClass) { return accessibleObject.isAnnotationPresent(annotationClass); } @Override @CheckForNull public final A getAnnotation(Class annotationClass) { return accessibleObject.getAnnotation(annotationClass); } @Override public final Annotation[] getAnnotations() { return accessibleObject.getAnnotations(); } @Override public final Annotation[] getDeclaredAnnotations() { return accessibleObject.getDeclaredAnnotations(); } // We ought to be able to implement GenericDeclaration instead its parent AnnotatedElement. // That would give us this method declaration. But for some reason, implementing // GenericDeclaration leads to weird errors in Android tests: // IncompatibleClassChangeError: interface not implemented /** See {@link java.lang.reflect.GenericDeclaration#getTypeParameters()}. */ public abstract TypeVariable[] getTypeParameters(); /** See {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)}. */ public final void setAccessible(boolean flag) { accessibleObject.setAccessible(flag); } /** See {@link java.lang.reflect.AccessibleObject#trySetAccessible()}. */ public final boolean trySetAccessible() { // We can't call accessibleObject.trySetAccessible since that was added in Java 9 and this code // should work on Java 8. So we emulate it this way. try { accessibleObject.setAccessible(true); return true; } catch (RuntimeException e) { return false; } } /** See {@link java.lang.reflect.AccessibleObject#isAccessible()}. */ public final boolean isAccessible() { return accessibleObject.isAccessible(); } @Override public final String getName() { return member.getName(); } @Override public final int getModifiers() { return member.getModifiers(); } @Override public final boolean isSynthetic() { return member.isSynthetic(); } /** Returns true if the element is public. */ public final boolean isPublic() { return Modifier.isPublic(getModifiers()); } /** Returns true if the element is protected. */ public final boolean isProtected() { return Modifier.isProtected(getModifiers()); } /** Returns true if the element is package-private. */ public final boolean isPackagePrivate() { return !isPrivate() && !isPublic() && !isProtected(); } /** Returns true if the element is private. */ public final boolean isPrivate() { return Modifier.isPrivate(getModifiers()); } /** Returns true if the element is static. */ public final boolean isStatic() { return Modifier.isStatic(getModifiers()); } /** * Returns {@code true} if this method is final, per {@code Modifier.isFinal(getModifiers())}. * *

Note that a method may still be effectively "final", or non-overridable when it has no * {@code final} keyword. For example, it could be private, or it could be declared by a final * class. To tell whether a method is overridable, use {@link Invokable#isOverridable}. */ public final boolean isFinal() { return Modifier.isFinal(getModifiers()); } /** Returns true if the method is abstract. */ public final boolean isAbstract() { return Modifier.isAbstract(getModifiers()); } /** Returns true if the element is native. */ public final boolean isNative() { return Modifier.isNative(getModifiers()); } /** Returns true if the method is synchronized. */ public final boolean isSynchronized() { return Modifier.isSynchronized(getModifiers()); } /** Returns true if the field is volatile. */ final boolean isVolatile() { return Modifier.isVolatile(getModifiers()); } /** Returns true if the field is transient. */ final boolean isTransient() { return Modifier.isTransient(getModifiers()); } @Override public boolean equals(@CheckForNull Object obj) { if (obj instanceof Invokable) { Invokable that = (Invokable) obj; return getOwnerType().equals(that.getOwnerType()) && member.equals(that.member); } return false; } @Override public int hashCode() { return member.hashCode(); } @Override public String toString() { return member.toString(); } /** * Returns {@code true} if this is an overridable method. Constructors, private, static or final * methods, or methods declared by final classes are not overridable. */ public abstract boolean isOverridable(); /** Returns {@code true} if this was declared to take a variable number of arguments. */ public abstract boolean isVarArgs(); /** * Invokes with {@code receiver} as 'this' and {@code args} passed to the underlying method and * returns the return value; or calls the underlying constructor with {@code args} and returns the * constructed instance. * * @throws IllegalAccessException if this {@code Constructor} object enforces Java language access * control and the underlying method or constructor is inaccessible. * @throws IllegalArgumentException if the number of actual and formal parameters differ; if an * unwrapping conversion for primitive arguments fails; or if, after possible unwrapping, a * parameter value cannot be converted to the corresponding formal parameter type by a method * invocation conversion. * @throws InvocationTargetException if the underlying method or constructor throws an exception. */ // All subclasses are owned by us and we'll make sure to get the R type right, including nullness. @SuppressWarnings({"unchecked", "nullness"}) @CanIgnoreReturnValue @CheckForNull public final R invoke(@CheckForNull T receiver, @Nullable Object... args) throws InvocationTargetException, IllegalAccessException { return (R) invokeInternal(receiver, checkNotNull(args)); } /** Returns the return type of this {@code Invokable}. */ // All subclasses are owned by us and we'll make sure to get the R type right. @SuppressWarnings("unchecked") public final TypeToken getReturnType() { return (TypeToken) TypeToken.of(getGenericReturnType()); } /** * Returns all declared parameters of this {@code Invokable}. Note that if this is a constructor * of a non-static inner class, unlike {@link Constructor#getParameterTypes}, the hidden {@code * this} parameter of the enclosing class is excluded from the returned parameters. */ public final ImmutableList getParameters() { Type[] parameterTypes = getGenericParameterTypes(); Annotation[][] annotations = getParameterAnnotations(); AnnotatedType[] annotatedTypes = getAnnotatedParameterTypes(); ImmutableList.Builder builder = ImmutableList.builder(); for (int i = 0; i < parameterTypes.length; i++) { builder.add( new Parameter( this, i, TypeToken.of(parameterTypes[i]), annotations[i], annotatedTypes[i])); } return builder.build(); } /** Returns all declared exception types of this {@code Invokable}. */ public final ImmutableList> getExceptionTypes() { ImmutableList.Builder> builder = ImmutableList.builder(); for (Type type : getGenericExceptionTypes()) { // getGenericExceptionTypes() will never return a type that's not exception @SuppressWarnings("unchecked") TypeToken exceptionType = (TypeToken) TypeToken.of(type); builder.add(exceptionType); } return builder.build(); } /** * Explicitly specifies the return type of this {@code Invokable}. For example: * *

{@code
   * Method factoryMethod = Person.class.getMethod("create");
   * Invokable factory = Invokable.of(getNameMethod).returning(Person.class);
   * }
*/ public final Invokable returning(Class returnType) { return returning(TypeToken.of(returnType)); } /** Explicitly specifies the return type of this {@code Invokable}. */ public final Invokable returning(TypeToken returnType) { if (!returnType.isSupertypeOf(getReturnType())) { throw new IllegalArgumentException( "Invokable is known to return " + getReturnType() + ", not " + returnType); } @SuppressWarnings("unchecked") // guarded by previous check Invokable specialized = (Invokable) this; return specialized; } @SuppressWarnings("unchecked") // The declaring class is T's raw class, or one of its supertypes. @Override public final Class getDeclaringClass() { return (Class) member.getDeclaringClass(); } /** Returns the type of {@code T}. */ // Overridden in TypeToken#method() and TypeToken#constructor() @SuppressWarnings("unchecked") // The declaring class is T. public TypeToken getOwnerType() { return (TypeToken) TypeToken.of(getDeclaringClass()); } @CheckForNull abstract Object invokeInternal(@CheckForNull Object receiver, @Nullable Object[] args) throws InvocationTargetException, IllegalAccessException; abstract Type[] getGenericParameterTypes(); abstract AnnotatedType[] getAnnotatedParameterTypes(); /** This should never return a type that's not a subtype of Throwable. */ abstract Type[] getGenericExceptionTypes(); abstract Annotation[][] getParameterAnnotations(); abstract Type getGenericReturnType(); public abstract AnnotatedType getAnnotatedReturnType(); static class MethodInvokable extends Invokable { final Method method; MethodInvokable(Method method) { super(method); this.method = method; } @Override @CheckForNull final Object invokeInternal(@CheckForNull Object receiver, @Nullable Object[] args) throws InvocationTargetException, IllegalAccessException { return method.invoke(receiver, args); } @Override Type getGenericReturnType() { return method.getGenericReturnType(); } @Override Type[] getGenericParameterTypes() { return method.getGenericParameterTypes(); } @Override AnnotatedType[] getAnnotatedParameterTypes() { return method.getAnnotatedParameterTypes(); } @Override public AnnotatedType getAnnotatedReturnType() { return method.getAnnotatedReturnType(); } @Override Type[] getGenericExceptionTypes() { return method.getGenericExceptionTypes(); } @Override final Annotation[][] getParameterAnnotations() { return method.getParameterAnnotations(); } @Override public final TypeVariable[] getTypeParameters() { return method.getTypeParameters(); } @Override public final boolean isOverridable() { return !(isFinal() || isPrivate() || isStatic() || Modifier.isFinal(getDeclaringClass().getModifiers())); } @Override public final boolean isVarArgs() { return method.isVarArgs(); } } static class ConstructorInvokable extends Invokable { final Constructor constructor; ConstructorInvokable(Constructor constructor) { super(constructor); this.constructor = constructor; } @Override final Object invokeInternal(@CheckForNull Object receiver, @Nullable Object[] args) throws InvocationTargetException, IllegalAccessException { try { return constructor.newInstance(args); } catch (InstantiationException e) { throw new RuntimeException(constructor + " failed.", e); } } /** * If the class is parameterized, such as {@link java.util.ArrayList ArrayList}, this returns * {@code ArrayList}. */ @Override Type getGenericReturnType() { Class declaringClass = getDeclaringClass(); TypeVariable[] typeParams = declaringClass.getTypeParameters(); if (typeParams.length > 0) { return Types.newParameterizedType(declaringClass, typeParams); } else { return declaringClass; } } @Override Type[] getGenericParameterTypes() { Type[] types = constructor.getGenericParameterTypes(); if (types.length > 0 && mayNeedHiddenThis()) { Class[] rawParamTypes = constructor.getParameterTypes(); if (types.length == rawParamTypes.length && rawParamTypes[0] == getDeclaringClass().getEnclosingClass()) { // first parameter is the hidden 'this' return Arrays.copyOfRange(types, 1, types.length); } } return types; } @Override AnnotatedType[] getAnnotatedParameterTypes() { return constructor.getAnnotatedParameterTypes(); } @Override public AnnotatedType getAnnotatedReturnType() { return constructor.getAnnotatedReturnType(); } @Override Type[] getGenericExceptionTypes() { return constructor.getGenericExceptionTypes(); } @Override final Annotation[][] getParameterAnnotations() { return constructor.getParameterAnnotations(); } /** * {@inheritDoc} * *

{@code []} will be returned for ArrayList's constructor. When both the class and the * constructor have type parameters, the class parameters are prepended before those of the * constructor's. This is an arbitrary rule since no existing language spec mandates one way or * the other. From the declaration syntax, the class type parameter appears first, but the call * syntax may show up in opposite order such as {@code new Foo()}. */ @Override public final TypeVariable[] getTypeParameters() { TypeVariable[] declaredByClass = getDeclaringClass().getTypeParameters(); TypeVariable[] declaredByConstructor = constructor.getTypeParameters(); TypeVariable[] result = new TypeVariable[declaredByClass.length + declaredByConstructor.length]; System.arraycopy(declaredByClass, 0, result, 0, declaredByClass.length); System.arraycopy( declaredByConstructor, 0, result, declaredByClass.length, declaredByConstructor.length); return result; } @Override public final boolean isOverridable() { return false; } @Override public final boolean isVarArgs() { return constructor.isVarArgs(); } private boolean mayNeedHiddenThis() { Class declaringClass = constructor.getDeclaringClass(); if (declaringClass.getEnclosingConstructor() != null) { // Enclosed in a constructor, needs hidden this return true; } Method enclosingMethod = declaringClass.getEnclosingMethod(); if (enclosingMethod != null) { // Enclosed in a method, if it's not static, must need hidden this. return !Modifier.isStatic(enclosingMethod.getModifiers()); } else { // Strictly, this doesn't necessarily indicate a hidden 'this' in the case of // static initializer. But there seems no way to tell in that case. :( // This may cause issues when an anonymous class is created inside a static initializer, // and the class's constructor's first parameter happens to be the enclosing class. // In such case, we may mistakenly think that the class is within a non-static context // and the first parameter is the hidden 'this'. return declaringClass.getEnclosingClass() != null && !Modifier.isStatic(declaringClass.getModifiers()); } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy