org.keycloak.common.util.reflections.Reflections Maven / Gradle / Ivy
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* 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.keycloak.common.util.reflections;
import java.beans.Introspector;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
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.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Utility class for working with JDK Reflection and also CDI's {link Annotated} metadata.
*/
public class Reflections {
/**
* An empty array of type {@link java.lang.annotation.Annotation}, useful converting lists to arrays.
*/
public static final Annotation[] EMPTY_ANNOTATION_ARRAY = new Annotation[0];
/**
* An empty array of type {@link Object}, useful for converting lists to arrays.
*/
public static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
public static final Type[] EMPTY_TYPES = {};
public static final Class>[] EMPTY_CLASSES = new Class>[0];
private Reflections() {
}
/**
* Perform a runtime cast. Similar to {@link Class#cast(Object)}, but useful when you do not have a {@link
* Class} object for type you wish to cast to.
{@link Class#cast(Object)} should be used if possible
*
*
* @param the type to cast to
* @param obj the object to perform the cast on
*
* @return the casted object
*
* @throws ClassCastException if the type T is not a subtype of the object
* @see Class#cast(Object)
*/
@SuppressWarnings("unchecked")
public static T cast(Object obj) {
return (T) obj;
}
/**
* Get all the declared fields on the class hierarchy. This will return overridden fields.
*
* @param clazz The class to search
*
* @return the set of all declared fields or an empty set if there are none
*/
public static Set getAllDeclaredFields(Class> clazz) {
HashSet fields = new HashSet();
for (Class> c = clazz; c != null && c != Object.class; c = c.getSuperclass()) {
for (Field a : c.getDeclaredFields()) {
fields.add(a);
}
}
return fields;
}
/**
* Search the class hierarchy for a field with the given name. Will return the nearest match, starting with the
* class specified and searching up the hierarchy.
*
* @param clazz The class to search
* @param name The name of the field to search for
*
* @return The field found, or null if no field is found
*/
public static Field findDeclaredField(Class> clazz, String name) {
for (Class> c = clazz; c != null && c != Object.class; c = c.getSuperclass()) {
try {
return c.getDeclaredField(name);
} catch (NoSuchFieldException e) {
// No-op, we continue looking up the class hierarchy
}
}
return null;
}
/**
* Search for annotations with the specified meta annotation type
*
* @param annotations The annotation set to search
* @param metaAnnotationType The type of the meta annotation to search for
*
* @return The set of annotations with the specified meta annotation, or an empty set if none are found
*/
public static Set getAnnotationsWithMetaAnnotation(
Set annotations, Class extends Annotation> metaAnnotationType) {
Set set = new HashSet();
for (Annotation annotation : annotations) {
if (annotation.annotationType().isAnnotationPresent(metaAnnotationType)) {
set.add(annotation);
}
}
return set;
}
/**
* Determine if a method exists in a specified class hierarchy
*
* @param clazz The class to search
* @param name The name of the method
*
* @return true if a method is found, otherwise false
*/
public static boolean methodExists(Class> clazz, String name) {
for (Class> c = clazz; c != null && c != Object.class; c = c.getSuperclass()) {
for (Method m : c.getDeclaredMethods()) {
if (m.getName().equals(name)) {
return true;
}
}
}
return false;
}
/**
* Get all the declared methods on the class hierarchy. This will return overridden methods.
*
* @param clazz The class to search
*
* @return the set of all declared methods or an empty set if there are none
*/
public static Set getAllDeclaredMethods(Class> clazz) {
HashSet methods = new HashSet();
for (Class> c = clazz; c != null && c != Object.class; c = c.getSuperclass()) {
for (Method a : c.getDeclaredMethods()) {
methods.add(a);
}
}
return methods;
}
/**
* Search the class hierarchy for a method with the given name and arguments. Will return the nearest match,
* starting with the class specified and searching up the hierarchy.
*
* @param clazz The class to search
* @param name The name of the method to search for
* @param args The arguments of the method to search for
*
* @return The method found, or null if no method is found
*/
public static Method findDeclaredMethod(Class> clazz, String name, Class>... args) {
for (Class> c = clazz; c != null && c != Object.class; c = c.getSuperclass()) {
try {
return c.getDeclaredMethod(name, args);
} catch (NoSuchMethodException e) {
// No-op, continue the search
}
}
return null;
}
/**
* Search the class hierarchy for a constructor with the given arguments. Will return the nearest match, starting
* with the class specified and searching up the hierarchy.
*
* @param clazz The class to search
* @param args The arguments of the constructor to search for
*
* @return The constructor found, or null if no constructor is found
*/
public static Constructor> findDeclaredConstructor(Class> clazz, Class>... args) {
for (Class> c = clazz; c != null && c != Object.class; c = c.getSuperclass()) {
try {
return c.getDeclaredConstructor(args);
} catch (NoSuchMethodException e) {
// No-op, continue the search
}
}
return null;
}
/**
* Get all the declared constructors on the class hierarchy. This will return overridden constructors.
*
* @param clazz The class to search
*
* @return the set of all declared constructors or an empty set if there are none
*/
public static Set> getAllDeclaredConstructors(Class> clazz) {
HashSet> constructors = new HashSet>();
for (Class> c = clazz; c != null && c != Object.class; c = c.getSuperclass()) {
for (Constructor> constructor : c.getDeclaredConstructors()) {
constructors.add(constructor);
}
}
return constructors;
}
/**
* Get the type of the member
*
* @param member The member
*
* @return The type of the member
*
* @throws UnsupportedOperationException if the member is not a field, method, or constructor
*/
public static Class> getMemberType(Member member) {
if (member instanceof Field) {
return ((Field) member).getType();
} else if (member instanceof Method) {
return ((Method) member).getReturnType();
} else if (member instanceof Constructor>) {
return ((Constructor>) member).getDeclaringClass();
} else {
throw new UnsupportedOperationException("Cannot operate on a member of type " + member.getClass());
}
}
/**
* Loads and initializes a class for the given name.
If the Thread Context Class Loader is
* available, it will be used, otherwise the classloader used to load {@link Reflections} will be used
* It is also possible to specify additional classloaders to attempt to load the class with. If the first attempt
* fails, then these additional loaders are tried in order.
*
* @param name the name of the class to load
* @param loaders additional classloaders to use to attempt to load the class
*
* @return the class object
*
* @throws ClassNotFoundException if the class cannot be found
*/
public static Class classForName(String name, ClassLoader... loaders) throws ClassNotFoundException {
try {
if (Thread.currentThread().getContextClassLoader() != null) {
return (Class) Class.forName(name, true, Thread.currentThread().getContextClassLoader());
} else {
return (Class) Class.forName(name);
}
} catch (ClassNotFoundException e) {
for (ClassLoader l : loaders) {
try {
return (Class) Class.forName(name, true, l);
} catch (ClassNotFoundException ex) {
}
}
}
if (Thread.currentThread().getContextClassLoader() != null) {
throw new ClassNotFoundException("Could not load class " + name +
" with the context class loader " + Thread.currentThread().getContextClassLoader().toString() +
" or any of the additional ClassLoaders: " + Arrays.toString(loaders));
} else {
throw new ClassNotFoundException("Could not load class " + name +
" using Class.forName or using any of the additional ClassLoaders: " +
Arrays.toString(loaders));
}
}
private static String buildInvokeMethodErrorMessage(Method method, Object obj, Object... args) {
StringBuilder message = new StringBuilder(
String.format("Exception invoking method [%s] on object [%s], using arguments [",
method.getName(), obj));
if (args != null) {
for (int i = 0; i < args.length; i++) {
message.append((i > 0 ? "," : "") + args[i]);
}
}
message.append("]");
return message.toString();
}
/**
* Invoke the specified method on the provided instance, passing any additional arguments included in this
* method as arguments to the specified method.
This method provides the same functionality and throws
* the same exceptions as {@link Reflections#invokeMethod(boolean, Method, Class, Object, Object...)}, with the
* expected return type set to {@link Object} and no change to the method's accessibility.
*
* @see Reflections#invokeMethod(boolean, Method, Class, Object, Object...)
* @see Method#invoke(Object, Object...)
*/
public static Object invokeMethod(Method method, Object instance, Object... args) {
return invokeMethod(false, method, Object.class, instance, args);
}
/**
* Invoke the specified method on the provided instance, passing any additional arguments included in this
* method as arguments to the specified method.
This method attempts to set the accessible flag of the
* method in a {link PrivilegedAction} before invoking the method if the first argument is true.
This
* method provides the same functionality and throws the same exceptions as {@link Reflections#invokeMethod(boolean,
* Method, Class, Object, Object...)}, with the expected return type set to {@link Object}.
*
* @see Reflections#invokeMethod(boolean, Method, Class, Object, Object...)
* @see Method#invoke(Object, Object...)
*/
public static Object invokeMethod(boolean setAccessible, Method method, Object instance, Object... args) {
return invokeMethod(setAccessible, method, Object.class, instance, args);
}
/**
* Invoke the specified method on the provided instance, passing any additional arguments included in this
* method as arguments to the specified method.
This method provides the same functionality and throws
* the same exceptions as {@link Reflections#invokeMethod(boolean, Method, Class, Object, Object...)}, with the
* expected return type set to {@link Object} and honoring the accessibility of the method.
*
* @see Reflections#invokeMethod(boolean, Method, Class, Object, Object...)
* @see Method#invoke(Object, Object...)
*/
public static T invokeMethod(Method method, Class expectedReturnType, Object instance, Object... args) {
return invokeMethod(false, method, expectedReturnType, instance, args);
}
/**
* Invoke the method on the instance, with any arguments specified, casting the result of invoking the method to
* the expected return type.
This method wraps {@link Method#invoke(Object, Object...)}, converting
* the checked exceptions that {@link Method#invoke(Object, Object...)} specifies to runtime exceptions.
* If instructed, this method attempts to set the accessible flag of the method in a {link PrivilegedAction}
* before invoking the method.
*
* @param setAccessible flag indicating whether method should first be set as accessible
* @param method the method to invoke
* @param instance the instance to invoke the method
* @param args the arguments to the method
*
* @return the result of invoking the method, or null if the method's return type is void
*
* @throws RuntimeException if this Method
object enforces Java language access control and the
* underlying method is inaccessible or if the underlying method throws an exception or if the initialization
* provoked by this method fails.
* @throws IllegalArgumentException if the method is an instance method and the specified instance
* argument is not an instance of the class or interface declaring the underlying method (or of a subclass or
* implementor thereof); 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 NullPointerException if the specified instance
is null and the method is an instance
* method.
* @throws ClassCastException if the result of invoking the method cannot be cast to the expectedReturnType
* @throws ExceptionInInitializerError if the initialization provoked by this method fails.
* @see Method#invoke(Object, Object...)
*/
public static T invokeMethod(boolean setAccessible, Method method,
Class expectedReturnType, Object instance, Object... args) {
if (setAccessible && !method.isAccessible()) {
setAccessible(method);
}
try {
return expectedReturnType.cast(method.invoke(instance, args));
} catch (IllegalAccessException ex) {
throw new RuntimeException(buildInvokeMethodErrorMessage(method, instance, args), ex);
} catch (IllegalArgumentException ex) {
throw new IllegalArgumentException(buildInvokeMethodErrorMessage(method, instance, args), ex);
} catch (InvocationTargetException ex) {
throw new RuntimeException(buildInvokeMethodErrorMessage(method, instance, args), ex.getCause());
} catch (NullPointerException ex) {
NullPointerException ex2 = new NullPointerException(buildInvokeMethodErrorMessage(method, instance, args));
ex2.initCause(ex.getCause());
throw ex2;
} catch (ExceptionInInitializerError e) {
ExceptionInInitializerError e2 = new ExceptionInInitializerError(
buildInvokeMethodErrorMessage(method, instance, args));
e2.initCause(e.getCause());
throw e2;
}
}
/**
* Set the accessibility flag on the {@link AccessibleObject} as described in {@link
* AccessibleObject#setAccessible(boolean)}.
*
* @param member the accessible object type
* @param member the accessible object
*
* @return the accessible object after the accessible flag has been altered
*/
public static A setAccessible(A member) {
member.setAccessible(true);
return member;
}
/**
* Set the accessibility flag on the {@link AccessibleObject} to false as described in {@link
* AccessibleObject#setAccessible(boolean)}.
*
* @param member the accessible object type
* @param member the accessible object
*
* @return the accessible object after the accessible flag has been altered
*/
public static A unsetAccessible(A member) {
member.setAccessible(false);
return member;
}
private static String buildSetFieldValueErrorMessage(Field field, Object obj, Object value) {
return String.format("Exception setting [%s] field on object [%s] to value [%s]", field.getName(), obj, value);
}
private static String buildGetFieldValueErrorMessage(Field field, Object obj) {
return String.format("Exception reading [%s] field from object [%s].", field.getName(), obj);
}
public static Object getFieldValue(Field field, Object instance) {
return getFieldValue(field, instance, Object.class);
}
/**
* Get the value of the field, on the specified instance, casting the value of the field to the expected type.
*
This method wraps {@link Field#get(Object)}, converting the checked exceptions that {@link
* Field#get(Object)} specifies to runtime exceptions.
*
* @param the type of the field's value
* @param field the field to operate on
* @param instance the instance from which to retrieve the value
* @param expectedType the expected type of the field's value
*
* @return the value of the field
*
* @throws RuntimeException if the underlying field is inaccessible.
* @throws IllegalArgumentException if the specified instance
is not an instance of the class or
* interface declaring the underlying field (or a subclass or implementor thereof).
* @throws NullPointerException if the specified instance
is null and the field is an instance field.
* @throws ExceptionInInitializerError if the initialization provoked by this method fails.
*/
public static T getFieldValue(Field field, Object instance, Class expectedType) {
try {
return Reflections.cast(field.get(instance));
} catch (IllegalAccessException e) {
throw new RuntimeException(buildGetFieldValueErrorMessage(field, instance), e);
} catch (NullPointerException ex) {
NullPointerException ex2 = new NullPointerException(buildGetFieldValueErrorMessage(field, instance));
ex2.initCause(ex.getCause());
throw ex2;
} catch (ExceptionInInitializerError e) {
ExceptionInInitializerError e2 = new ExceptionInInitializerError(
buildGetFieldValueErrorMessage(field, instance));
e2.initCause(e.getCause());
throw e2;
}
}
/**
* Extract the raw type, given a type.
*
* @param the type
* @param type the type to extract the raw type from
*
* @return the raw type, or null if the raw type cannot be determined.
*/
@SuppressWarnings("unchecked")
public static Class getRawType(Type type) {
if (type instanceof Class>) {
return (Class) type;
} else if (type instanceof ParameterizedType) {
if (((ParameterizedType) type).getRawType() instanceof Class>) {
return (Class) ((ParameterizedType) type).getRawType();
}
}
return null;
}
/**
* Check if a class is serializable.
*
* @param clazz The class to check
*
* @return true if the class implements serializable or is a primitive
*/
public static boolean isSerializable(Class> clazz) {
return clazz.isPrimitive() || Serializable.class.isAssignableFrom(clazz);
}
public static Map, Type> buildTypeMap(Set types) {
Map, Type> map = new HashMap, Type>();
for (Type type : types) {
if (type instanceof Class>) {
map.put((Class>) type, type);
} else if (type instanceof ParameterizedType) {
if (((ParameterizedType) type).getRawType() instanceof Class>) {
map.put((Class>) ((ParameterizedType) type).getRawType(), type);
}
} else if (type instanceof TypeVariable>) {
}
}
return map;
}
public static boolean isCacheable(Set annotations) {
for (Annotation qualifier : annotations) {
Class> clazz = qualifier.getClass();
if (clazz.isAnonymousClass() || (clazz.isMemberClass() && isStatic(clazz))) {
return false;
}
}
return true;
}
public static boolean isCacheable(Annotation[] annotations) {
for (Annotation qualifier : annotations) {
Class> clazz = qualifier.getClass();
if (clazz.isAnonymousClass() || (clazz.isMemberClass() && isStatic(clazz))) {
return false;
}
}
return true;
}
/**
* 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();
if (methodName.matches("^(get).*")) {
return Introspector.decapitalize(methodName.substring(3));
} else if (methodName.matches("^(is).*")) {
return Introspector.decapitalize(methodName.substring(2));
} 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 getNonPrivateFinalMethodOrType(type) != null;
}
public static Object getNonPrivateFinalMethodOrType(Class> type) {
if (isFinal(type)) {
return type;
}
for (Method method : type.getDeclaredMethods()) {
if (isFinal(method) && !isPrivate(method)) {
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
*
* @return
*/
public static boolean isAbstract(Method method) {
return Modifier.isAbstract(method.getModifiers());
}
/**
* 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 isParamerterizedTypeWithWildcard(Class> type) {
if (isParameterizedType(type)) {
return containsWildcards(type.getTypeParameters());
} else {
return false;
}
}
public static boolean containsWildcards(Type[] types) {
for (Type type : types) {
if (type instanceof WildcardType) {
return true;
}
}
return false;
}
/**
* Check the assignability of one type to another, taking into account the actual type arguments
*
* @param rawType1 the raw type of the class to check
* @param actualTypeArguments1 the actual type arguments to check, or an empty array if not a parameterized type
* @param rawType2 the raw type of the class to check
* @param actualTypeArguments2 the actual type arguments to check, or an empty array if not a parameterized type
*
* @return
*/
public static boolean isAssignableFrom(Class> rawType1, Type[] actualTypeArguments1,
Class> rawType2, Type[] actualTypeArguments2) {
return Types.boxedClass(rawType1).isAssignableFrom(Types.boxedClass(rawType2)) &&
isAssignableFrom(actualTypeArguments1, actualTypeArguments2);
}
public static boolean matches(Class> rawType1, Type[] actualTypeArguments1,
Class> rawType2, Type[] actualTypeArguments2) {
return Types.boxedClass(rawType1).equals(Types.boxedClass(rawType2)) &&
isAssignableFrom(actualTypeArguments1, actualTypeArguments2);
}
public static boolean isAssignableFrom(Type[] actualTypeArguments1, Type[] actualTypeArguments2) {
for (int i = 0; i < actualTypeArguments1.length; i++) {
Type type1 = actualTypeArguments1[i];
Type type2 = Object.class;
if (actualTypeArguments2.length > i) {
type2 = actualTypeArguments2[i];
}
if (!isAssignableFrom(type1, type2)) {
return false;
}
}
return true;
}
public static boolean isAssignableFrom(Type type1, Set extends Type> types2) {
for (Type type2 : types2) {
if (isAssignableFrom(type1, type2)) {
return true;
}
}
return false;
}
public static boolean matches(Type type1, Set extends Type> types2) {
for (Type type2 : types2) {
if (matches(type1, type2)) {
return true;
}
}
return false;
}
public static boolean isAssignableFrom(Type type1, Type[] types2) {
for (Type type2 : types2) {
if (isAssignableFrom(type1, type2)) {
return true;
}
}
return false;
}
public static boolean isAssignableFrom(Type type1, Type type2) {
if (type1 instanceof Class>) {
Class> clazz = (Class>) type1;
if (isAssignableFrom(clazz, EMPTY_TYPES, type2)) {
return true;
}
}
if (type1 instanceof ParameterizedType) {
ParameterizedType parameterizedType1 = (ParameterizedType) type1;
if (parameterizedType1.getRawType() instanceof Class>) {
if (isAssignableFrom((Class>) parameterizedType1.getRawType(),
parameterizedType1.getActualTypeArguments(), type2)) {
return true;
}
}
}
if (type1 instanceof WildcardType) {
WildcardType wildcardType = (WildcardType) type1;
if (isTypeBounded(type2, wildcardType.getLowerBounds(), wildcardType.getUpperBounds())) {
return true;
}
}
if (type2 instanceof WildcardType) {
WildcardType wildcardType = (WildcardType) type2;
if (isTypeBounded(type1, wildcardType.getUpperBounds(), wildcardType.getLowerBounds())) {
return true;
}
}
if (type1 instanceof TypeVariable>) {
TypeVariable> typeVariable = (TypeVariable>) type1;
if (isTypeBounded(type2, EMPTY_TYPES, typeVariable.getBounds())) {
return true;
}
}
if (type2 instanceof TypeVariable>) {
TypeVariable> typeVariable = (TypeVariable>) type2;
if (isTypeBounded(type1, typeVariable.getBounds(), EMPTY_TYPES)) {
return true;
}
}
return false;
}
public static boolean matches(Type type1, Type type2) {
if (type1 instanceof Class>) {
Class> clazz = (Class>) type1;
if (matches(clazz, EMPTY_TYPES, type2)) {
return true;
}
}
if (type1 instanceof ParameterizedType) {
ParameterizedType parameterizedType1 = (ParameterizedType) type1;
if (parameterizedType1.getRawType() instanceof Class>) {
if (matches((Class>) parameterizedType1.getRawType(),
parameterizedType1.getActualTypeArguments(), type2)) {
return true;
}
}
}
if (type1 instanceof WildcardType) {
WildcardType wildcardType = (WildcardType) type1;
if (isTypeBounded(type2, wildcardType.getLowerBounds(), wildcardType.getUpperBounds())) {
return true;
}
}
if (type2 instanceof WildcardType) {
WildcardType wildcardType = (WildcardType) type2;
if (isTypeBounded(type1, wildcardType.getUpperBounds(), wildcardType.getLowerBounds())) {
return true;
}
}
if (type1 instanceof TypeVariable>) {
TypeVariable> typeVariable = (TypeVariable>) type1;
if (isTypeBounded(type2, EMPTY_TYPES, typeVariable.getBounds())) {
return true;
}
}
if (type2 instanceof TypeVariable>) {
TypeVariable> typeVariable = (TypeVariable>) type2;
if (isTypeBounded(type1, typeVariable.getBounds(), EMPTY_TYPES)) {
return true;
}
}
return false;
}
public static boolean isTypeBounded(Type type, Type[] lowerBounds, Type[] upperBounds) {
if (lowerBounds.length > 0) {
if (!isAssignableFrom(type, lowerBounds)) {
return false;
}
}
if (upperBounds.length > 0) {
if (!isAssignableFrom(upperBounds, type)) {
return false;
}
}
return true;
}
public static boolean isAssignableFrom(Class> rawType1, Type[] actualTypeArguments1, Type type2) {
if (type2 instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) type2;
if (parameterizedType.getRawType() instanceof Class>) {
if (isAssignableFrom(rawType1, actualTypeArguments1, (Class>) parameterizedType.getRawType(),
parameterizedType.getActualTypeArguments())) {
return true;
}
}
} else if (type2 instanceof Class>) {
Class> clazz = (Class>) type2;
if (isAssignableFrom(rawType1, actualTypeArguments1, clazz, EMPTY_TYPES)) {
return true;
}
} else if (type2 instanceof TypeVariable>) {
TypeVariable> typeVariable = (TypeVariable>) type2;
if (isTypeBounded(rawType1, actualTypeArguments1, typeVariable.getBounds())) {
return true;
}
}
return false;
}
public static boolean matches(Class> rawType1, Type[] actualTypeArguments1, Type type2) {
if (type2 instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) type2;
if (parameterizedType.getRawType() instanceof Class>) {
if (matches(rawType1, actualTypeArguments1, (Class>) parameterizedType.getRawType(),
parameterizedType.getActualTypeArguments())) {
return true;
}
}
} else if (type2 instanceof Class>) {
Class> clazz = (Class>) type2;
if (matches(rawType1, actualTypeArguments1, clazz, EMPTY_TYPES)) {
return true;
}
}
return false;
}
/**
* Check the assiginability of a set of flattened types. This algorithm will check whether any of the types1
* matches a type in types2
*
* @param types1
* @param types2
*
* @return
*/
public static boolean isAssignableFrom(Set types1, Set types2) {
for (Type type : types1) {
if (isAssignableFrom(type, types2)) {
return true;
}
}
return false;
}
/**
* Check whether whether any of the types1 matches a type in types2
*
* @param types1
* @param types2
*
* @return
*/
public static boolean matches(Set types1, Set types2) {
for (Type type : types1) {
if (matches(type, types2)) {
return true;
}
}
return false;
}
/**
* Check the assignability of a set of flattened types. This algorithm will check whether any of the types1
* matches a type in types2
*
* @param types1
* @param type2
*
* @return
*/
public static boolean isAssignableFrom(Set types1, Type type2) {
for (Type type : types1) {
if (isAssignableFrom(type, type2)) {
return true;
}
}
return false;
}
public static boolean isAssignableFrom(Type[] types1, Type type2) {
for (Type type : types1) {
if (isAssignableFrom(type, type2)) {
return true;
}
}
return false;
}
public static boolean isPrimitive(Type type) {
Class> rawType = getRawType(type);
return rawType == null ? false : rawType.isPrimitive();
}
/**
* Creates a new instance of a class.
*
* This method will use the same class loader of the given class to create the new instance.
*
* @param fromClass The class from where the instance should be created.
*
* @return A newly allocated instance of the class.
*
* @throws ClassNotFoundException
* @throws IllegalAccessException
* @throws InstantiationException
* @deprecated for removal in Keycloak 27
*/
@Deprecated
public static T newInstance(final Class fromClass) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
return newInstance(fromClass, fromClass.getName());
}
/**
* Creates a new instance of a class given its fullQualifiedName
.
*
* This method will use the same class loader of type
to create the new instance.
*
* @param type The class that will be used to get the class loader from.
* @param fullQualifiedName The full qualified name of the class from which the instance will be created.
*
* @return A newly allocated instance of the class.
*
* @throws ClassNotFoundException
* @throws IllegalAccessException
* @throws InstantiationException
* @deprecated for removal in Keycloak 27
*/
@Deprecated
public static T newInstance(final Class> type, final String fullQualifiedName) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
return (T) classForName(fullQualifiedName, type.getClassLoader()).newInstance();
}
/**
* Resolves the type of items for a {@link Field} declared as a {@link List}.
*
*
This method will first try to check the parametrized type of the field type. If none is defined, it will try to infer
* the type of items by looking at the value of the field for the given {@code instance}.
*
*
Make sure the field is accessible before invoking this method.
*
* @param field the field declared as {@link List}
* @param instance the instance that should be used to obtain infer the type in case no parametrized type is found in the field.
* @return if the field is not a {@link List}, it returns null. Otherwise the type of items of the list. If the type for items can not be inferred, the {@link Object} type is returned.
* @throws IllegalAccessException in case it fails to obtain the value of the field from the {@code instance}
*/
public static Class> resolveListType(Field field, Object instance) throws IllegalAccessException {
if (!List.class.isAssignableFrom(field.getType())) {
return null;
}
Type genericType = field.getGenericType();
if (genericType instanceof ParameterizedType) {
Type[] typeArguments = ParameterizedType.class.cast(genericType)
.getActualTypeArguments();
if (typeArguments[0] instanceof Class) {
return (Class>) typeArguments[0];
}
} else if (instance != null) {
// just in case the field is not parametrized
List item = List.class.cast(field.get(instance));
if (!item.isEmpty()) {
return item.get(0).getClass();
}
}
return Object.class;
}
}