
org.jboss.weld.util.reflection.Reflections Maven / Gradle / Ivy
Show all versions of weld-se-shaded Show documentation
/*
* JBoss, Home of Professional Open Source
* Copyright 2008, Red Hat, Inc., and individual contributors
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* 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 org.jboss.weld.util.reflection;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.jboss.weld.exceptions.WeldException;
import org.jboss.weld.logging.ReflectionLogger;
import org.jboss.weld.resources.spi.ResourceLoader;
import org.jboss.weld.resources.spi.ResourceLoadingException;
import org.jboss.weld.util.Types;
/**
* Utility class for static reflection-type operations
*
* @author Pete Muir
* @author Ales Justin
* @author Marko Luksa
*/
public class Reflections {
public static final Type[] EMPTY_TYPES = {};
public static final Annotation[] EMPTY_ANNOTATIONS = {};
public static final Class>[] EMPTY_CLASSES = new Class>[0];
private Reflections() {
}
public static Map, Type> buildTypeMap(Set types) {
Map, Type> map = new HashMap, Type>();
for (Type type : types) {
Class> clazz = getRawType(type);
if (clazz != null) {
map.put(clazz, type);
}
}
return map;
}
public static boolean isCacheable(Collection annotations) {
for (Annotation qualifier : annotations) {
Class> clazz = qualifier.getClass();
if (!isTopLevelOrStaticNestedClass(clazz)) {
return false;
}
}
return true;
}
public static boolean isCacheable(Annotation[] annotations) {
for (Annotation qualifier : annotations) {
Class> clazz = qualifier.getClass();
if (!isTopLevelOrStaticNestedClass(clazz)) {
return false;
}
}
return true;
}
@SuppressWarnings("unchecked")
public static T cast(Object obj) {
return (T) obj;
}
/**
* Gets the property name from a getter method.
*
* We extend JavaBean conventions, allowing the getter method to have parameters
*
* @param method The getter method
* @return The name of the property. Returns null if method wasn't JavaBean
* getter-styled
*/
public static String getPropertyName(Method method) {
String methodName = method.getName();
final String get = "get";
final String is = "is";
if (methodName.startsWith(get)) {
return decapitalize(methodName.substring(get.length()));
} else if (methodName.startsWith(is)) {
return decapitalize(methodName.substring(is.length()));
} else {
return null;
}
}
/**
* Checks if class is final
*
* @param clazz The class to check
* @return True if final, false otherwise
*/
public static boolean isFinal(Class> clazz) {
return Modifier.isFinal(clazz.getModifiers());
}
public static int getNesting(Class> clazz) {
if (clazz.isMemberClass() && !isStatic(clazz)) {
return 1 + getNesting(clazz.getDeclaringClass());
} else {
return 0;
}
}
/**
* Checks if member is final
*
* @param member The member to check
* @return True if final, false otherwise
*/
public static boolean isFinal(Member member) {
return Modifier.isFinal(member.getModifiers());
}
/**
* Checks if member is private
*
* @param member The member to check
* @return True if final, false otherwise
*/
public static boolean isPrivate(Member member) {
return Modifier.isPrivate(member.getModifiers());
}
/**
* Checks if type or member is final
*
* @param type Type or member
* @return True if final, false otherwise
*/
public static boolean isTypeOrAnyMethodFinal(Class> type) {
return isFinal(type) || getNonPrivateNonStaticFinalMethod(type) != null;
}
public static Method getNonPrivateNonStaticFinalMethod(Class> type) {
for (Class> clazz = type; clazz != null && clazz != Object.class; clazz = clazz.getSuperclass()) {
for (Method method : clazz.getDeclaredMethods()) {
if (isFinal(method) && !isPrivate(method) && !isStatic(method) && !method.isSynthetic()) {
return method;
}
}
}
return null;
}
public static boolean isPackagePrivate(int mod) {
return !(Modifier.isPrivate(mod) || Modifier.isProtected(mod) || Modifier.isPublic(mod));
}
/**
* Checks if type is static
*
* @param type Type to check
* @return True if static, false otherwise
*/
public static boolean isStatic(Class> type) {
return Modifier.isStatic(type.getModifiers());
}
/**
* Checks if member is static
*
* @param member Member to check
* @return True if static, false otherwise
*/
public static boolean isStatic(Member member) {
return Modifier.isStatic(member.getModifiers());
}
public static boolean isTransient(Member member) {
return Modifier.isTransient(member.getModifiers());
}
/**
* Checks if a method is abstract
*
* @param method the method
* @return true if abstract
*/
public static boolean isAbstract(Method method) {
return Modifier.isAbstract(method.getModifiers());
}
public static boolean isAbstract(Class> clazz) {
return Modifier.isAbstract(clazz.getModifiers());
}
/**
* Gets the actual type arguments of a class
*
* @param clazz The class to examine
* @return The type arguments
*/
public static Type[] getActualTypeArguments(Class> clazz) {
Type type = Types.getCanonicalType(clazz);
if (type instanceof ParameterizedType) {
return ((ParameterizedType) type).getActualTypeArguments();
} else {
return EMPTY_TYPES;
}
}
/**
* Gets the actual type arguments of a Type
*
* @param type The type to examine
* @return The type arguments
*/
public static Type[] getActualTypeArguments(Type type) {
Type resolvedType = Types.getCanonicalType(type);
if (resolvedType instanceof ParameterizedType) {
return ((ParameterizedType) resolvedType).getActualTypeArguments();
} else {
return EMPTY_TYPES;
}
}
/**
* Checks if raw type is array type
*
* @param rawType The raw type to check
* @return True if array, false otherwise
*/
public static boolean isArrayType(Class> rawType) {
return rawType.isArray();
}
/**
* Checks if type is parameterized type
*
* @param type The type to check
* @return True if parameterized, false otherwise
*/
public static boolean isParameterizedType(Class> type) {
return type.getTypeParameters().length > 0;
}
public static boolean isParameterizedTypeWithWildcard(Class> type) {
return isParameterizedType(type) && containsWildcards(type.getTypeParameters());
}
public static boolean containsWildcards(Type[] types) {
for (Type type : types) {
if (type instanceof WildcardType) {
return true;
}
}
return false;
}
public static boolean isSerializable(Class> clazz) {
return clazz.isPrimitive() || Serializable.class.isAssignableFrom(clazz);
}
public static boolean isPrimitive(Type type) {
Class> rawType = getRawType(type);
return rawType != null && rawType.isPrimitive();
}
@SuppressWarnings("unchecked")
public static Class getRawType(Type type) {
if (type instanceof Class>) {
return (Class) type;
}
if (type instanceof ParameterizedType) {
if (((ParameterizedType) type).getRawType() instanceof Class>) {
return (Class) ((ParameterizedType) type).getRawType();
}
}
if (type instanceof TypeVariable>) {
TypeVariable> variable = (TypeVariable>) type;
Type[] bounds = variable.getBounds();
return getBound(bounds);
}
if (type instanceof WildcardType) {
WildcardType wildcard = (WildcardType) type;
return getBound(wildcard.getUpperBounds());
}
if (type instanceof GenericArrayType) {
GenericArrayType genericArrayType = (GenericArrayType) type;
Class> rawType = getRawType(genericArrayType.getGenericComponentType());
if (rawType != null) {
return (Class) Array.newInstance(rawType, 0).getClass();
}
}
return null;
}
@SuppressWarnings("unchecked")
private static Class getBound(Type[] bounds) {
if (bounds.length == 0) {
return (Class) Object.class;
} else {
return getRawType(bounds[0]);
}
}
public static boolean isClassLoadable(String className, ResourceLoader resourceLoader) {
return loadClass(className, resourceLoader) != null;
}
/**
* Tries to load a class using the specified ResourceLoader. Returns null if the class is not found.
*
* @param className
* @param resourceLoader
* @return the loaded class or null if the given class cannot be loaded
*/
public static Class loadClass(String className, ResourceLoader resourceLoader) {
try {
return cast(resourceLoader.classForName(className));
} catch (ResourceLoadingException e) {
return null;
} catch (SecurityException e) {
return null;
}
}
public static boolean isUnboundedWildcard(Type type) {
if (type instanceof WildcardType) {
WildcardType wildcard = (WildcardType) type;
return isEmptyBoundArray(wildcard.getUpperBounds()) && isEmptyBoundArray(wildcard.getLowerBounds());
}
return false;
}
public static boolean isUnboundedTypeVariable(Type type) {
if (type instanceof TypeVariable>) {
TypeVariable> typeVariable = (TypeVariable>) type;
return isEmptyBoundArray(typeVariable.getBounds());
}
return false;
}
static boolean isEmptyBoundArray(Type[] bounds) {
return bounds == null || bounds.length == 0 || (bounds.length == 1 && Object.class.equals(bounds[0]));
}
/**
*
* @param javaClass
* @return true
if the given class is a static nested class, false
otherwise
*/
public static boolean isStaticNestedClass(Class> javaClass) {
if (javaClass.getEnclosingConstructor() != null || javaClass.getEnclosingMethod() != null) {
// Local or anonymous class
return false;
}
if (javaClass.getEnclosingClass() != null) {
// Extra check for anonymous class - http://bugs.java.com/bugdatabase/view_bug.do?bug_id=8034044
if (javaClass.isAnonymousClass()) {
return false;
}
return Reflections.isStatic(javaClass);
}
return false;
}
/**
*
* @param javaClass
* @return true
if the given class is a top-level or static nested class, false
otherwise
*/
public static boolean isTopLevelOrStaticNestedClass(Class> javaClass) {
return javaClass.getEnclosingClass() == null || isStaticNestedClass(javaClass);
}
/**
* Invokes the method on a given instance passing in given parameters. If the invocation yields
* {@link InvocationTargetException}, the exception is unwrapped.
*
* It is a responsibility of the caller to make sure that the method is accessible to the caller.
*/
public static T invokeAndUnwrap(final Object instance, final Method method, final Object... parameters)
throws Throwable {
try {
return cast(method.invoke(instance, parameters));
} catch (IllegalArgumentException e) {
throw ReflectionLogger.LOG.illegalArgumentExceptionOnReflectionInvocation(instance.getClass(), instance, method,
Arrays.toString(parameters), e);
} catch (IllegalAccessException e) {
throw new WeldException(e);
} catch (InvocationTargetException e) {
throw e.getCause();
}
}
/**
* Triggers loading of declaring class (if any) of the given class recursively.
* If the class cannot be loaded, the underlying {@link LinkageError} is propagated.
*
* @param class the given class
* @throws LinkageError or its subclass if a declaring class cannot be loaded
*/
public static void checkDeclaringClassLoadable(Class> c) {
for (Class> clazz = c; clazz != null; clazz = clazz.getDeclaringClass()) {
// noop, the loop triggers loading of the declaring classes
}
}
/**
* Searches for a declared method with a given name. If the class declares multiple methods with the given name,
* there is no guarantee as of which methods is returned. Null is returned if the class does not declare a method
* with the given name.
*
* @param clazz the given class
* @param methodName the given method name
* @return method with the given name declared by the given class or null if no such method exists
*/
public static Method findDeclaredMethodByName(Class> clazz, String methodName) {
for (Method method : clazz.getDeclaredMethods()) {
if (methodName.equals(method.getName())) {
return method;
}
}
return null;
}
/**
* Unwraps the given {@link InvocationTargetException}. {@link Error} and {@link Exception} are unwrapped right away,
* {@link Throwable} is wrapped
* within {@link WeldException}. This method never returns - it always throws the unwrapped cause instead. The return type
* matches the throws part of
* the signature just to simplify usage.
*
* @param the given exception
* @return
* @throws Exception unwrapped cause of {@link InvocationTargetException}
*/
public static Exception unwrapInvocationTargetException(InvocationTargetException e) throws Exception {
Throwable cause = e.getCause();
if (cause instanceof Error) {
throw (Error) cause;
} else if (cause instanceof Exception) {
throw (Exception) cause;
} else {
throw new WeldException(cause);
}
}
public static Set> getInterfaceClosure(Class> clazz) {
Set> result = new HashSet<>();
for (Class> classToDiscover = clazz; classToDiscover != null; classToDiscover = classToDiscover.getSuperclass()) {
addInterfaces(classToDiscover, result);
}
return result;
}
private static void addInterfaces(Class> clazz, Set> result) {
Class>[] interfaces = clazz.getInterfaces();
if (interfaces.length == 0) {
return;
}
Collections.addAll(result, interfaces);
for (Class> interfac : interfaces) {
addInterfaces(interfac, result);
}
}
public static boolean isDefault(Method method) {
// Default methods are public non-abstract instance methods declared in an interface
// Backported from JDK8
return ((method.getModifiers() & (Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC)) == Modifier.PUBLIC)
&& method.getDeclaringClass().isInterface();
}
/**
* Copy of java.beans.Introspector#decapitalize(name) to reduce java.desktop
* module dependency.
*
* Utility method to take a string and convert it to normal Java variable name
* capitalization. This normally means converting the first character from upper
* case to lower case, but in the (unusual) special case when there is more than
* one character and both the first and second characters are upper case, we
* leave it alone.
*
* Thus "FooBah" becomes "fooBah" and "X" becomes "x", but "URL" stays as "URL".
*
* @param name
* The string to be decapitalized.
* @return The decapitalized version of the string.
*/
public static String decapitalize(String name) {
if (name == null || name.length() == 0) {
return name;
}
if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) && Character.isUpperCase(name.charAt(0))) {
return name;
}
char[] chars = name.toCharArray();
chars[0] = Character.toLowerCase(chars[0]);
return new String(chars);
}
/**
* Checks if given Java class contains a declared field with provided name.
*
* @param javaClass java class
* @param name name of the field
* @return true if the class declares a field with provided name; false otherwise
*/
public static boolean hasDeclaredField(Class> javaClass, String name) {
Field[] fields = javaClass.getDeclaredFields();
for (Field field : fields) {
if (field.getName().equals(name)) {
return true;
}
}
return false;
}
/**
* Set the {@code accessible} flag for this accessible object.
* Uses {@link AccessibleObject#isAccessible()} to check accessibility.
*
* @param accessibleObject
*/
public static void ensureAccessible(AccessibleObject accessibleObject) {
ensureAccessible(accessibleObject, null);
}
/**
* Set the {@code accessible} flag for this accessible object.
* Uses {@link AccessibleObject#canAccess(Object)} to check accessibility.
*
* @param accessibleObject
* @param instance instance of the accessible object you are trying to access
*/
public static void ensureAccessible(AccessibleObject accessibleObject, Object instance) {
if (accessibleObject != null) {
if (instance != null) {
if (!accessibleObject.canAccess(instance)) {
accessibleObject.setAccessible(true);
}
} else {
if (!accessibleObject.isAccessible()) {
accessibleObject.setAccessible(true);
}
}
}
}
/**
* Creates a copy of the given Java member (field, method, constructor) and makes it accessible.
*
* @param member
* @return
* @param
*/
public static T getAccessibleCopyOfMember(T member) {
T copy = copyMember(member);
copy.setAccessible(true);
return copy;
}
private static T copyMember(T originalMember) {
final String unableToObtainAccessibleCopyOf = "Unable to obtain an accessible copy of ";
Class> declaringClass = originalMember.getDeclaringClass();
try {
if (originalMember instanceof Field) {
return (T) copyField((Field) originalMember, declaringClass);
}
if (originalMember instanceof Constructor>) {
return (T) copyConstructor((Constructor>) originalMember, declaringClass);
}
if (originalMember instanceof Method) {
return (T) copyMethod((Method) originalMember, declaringClass);
}
} catch (Exception e) {
throw new IllegalArgumentException(unableToObtainAccessibleCopyOf + originalMember, e);
}
throw new IllegalArgumentException(unableToObtainAccessibleCopyOf + originalMember);
}
private static Field copyField(Field field, Class> declaringClass) throws NoSuchFieldException {
return declaringClass.getDeclaredField(field.getName());
}
private static Method copyMethod(Method method, Class> declaringClass) throws NoSuchMethodException {
return declaringClass.getDeclaredMethod(method.getName(), method.getParameterTypes());
}
private static Constructor> copyConstructor(Constructor> constructor, Class> declaringClass)
throws NoSuchMethodException {
return declaringClass.getDeclaredConstructor(constructor.getParameterTypes());
}
/**
* Looks up a field with given name within the class and any of its superclasses.
*
* @param javaClass
* @param fieldName
* @return
* @throws NoSuchMethodException
*/
public static Field lookupField(Class> javaClass, String fieldName) throws NoSuchFieldException {
for (Class> inspectedClass = javaClass; inspectedClass != null; inspectedClass = inspectedClass.getSuperclass()) {
for (Class> inspectedInterface : inspectedClass.getInterfaces()) {
try {
return lookupField(inspectedInterface, fieldName);
} catch (NoSuchFieldException e) {
// Expected, nothing to see here.
}
}
try {
return inspectedClass.getDeclaredField(fieldName);
} catch (NoSuchFieldException nsme) {
// Expected, nothing to see here.
}
}
throw new NoSuchFieldException();
}
/**
* Looks up a method with given name and parameters within the class and any of its superclasses.
*
* @param javaClass
* @param methodName
* @param parameterTypes
* @return
* @throws NoSuchMethodException
*/
public static Method lookupMethod(Class> javaClass, String methodName, Class>[] parameterTypes)
throws NoSuchMethodException {
for (Class> inspectedClass = javaClass; inspectedClass != null; inspectedClass = inspectedClass.getSuperclass()) {
for (Class> inspectedInterface : inspectedClass.getInterfaces()) {
try {
return lookupMethod(inspectedInterface, methodName, parameterTypes);
} catch (NoSuchMethodException e) {
// Expected, nothing to see here.
}
}
try {
return inspectedClass.getDeclaredMethod(methodName, parameterTypes);
} catch (NoSuchMethodException nsme) {
// Expected, nothing to see here.
}
}
throw new NoSuchMethodException(
javaClass + ", method: " + methodName + ", paramTypes: " + Arrays.toString(parameterTypes));
}
/**
* Attempts to look up a method based on provided name and parameter types within given class.
* If the method exists, it is returned; otherwise, the exception throws is wrapped in
* {@link ReflectionLogger#noSuchMethodWrapper(NoSuchMethodException, String)}
*
* @param javaClass class that should contain the method
* @param methodName method name
* @param parameterTypes method parameter types
* @return
*/
public static Method wrapException(Class> javaClass, String methodName, Class>... parameterTypes) {
try {
return javaClass.getDeclaredMethod(methodName, parameterTypes);
} catch (NoSuchMethodException e) {
throw ReflectionLogger.LOG.noSuchMethodWrapper(e, e.getMessage());
}
}
}