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

ch.inftec.ju.util.ReflectUtils Maven / Gradle / Ivy

package ch.inftec.ju.util;

import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import org.apache.commons.lang3.reflect.FieldUtils;

/**
 * Class containing reflection related utility methods.
 * 
 * @author tgdmemae
 * 
 */
public final class ReflectUtils {
	/**
	 * Don't instantiate.
	 */
	private ReflectUtils() {
		throw new AssertionError("use only statically");
	}
	
	/**
	 * Get the underlying class for a type, or null if the type is a variable
	 * type.
	 * 
	 * @param type
	 *            the type
	 * @return the underlying class
	 */
	public static Class getClass(Type type) {
		if (type instanceof Class) {
			return (Class) type;
		} else if (type instanceof ParameterizedType) {
			return getClass(((ParameterizedType) type).getRawType());
		} else if (type instanceof GenericArrayType) {
			Type componentType = ((GenericArrayType) type)
					.getGenericComponentType();
			Class componentClass = getClass(componentType);
			if (componentClass != null) {
				return Array.newInstance(componentClass, 0).getClass();
			} else {
				return null;
			}
		} else {
			return null;
		}
	}
	
	/**
	 * Gets the inner class of the specified class with the specified name.
	 * 

* If the class does not exist, null is returned. *

* Note that the class (or interface) has to be public. * @param clazz Enclosing class * @param innerClassName Public inner class or interface * @return Inner class or null if no such class exists */ public static Class getInnerClass(Class clazz, String innerClassName) { for (Class innerClass : clazz.getClasses()) { if (innerClass.getSimpleName().equals(innerClassName)) { return innerClass; } } return null; } /** * Get the actual type arguments a child class has used to extend a generic * base class. * * @param baseClass * the base class * @param childClass * the child class * @return a list of the raw classes for the actual type arguments. */ public static List> getTypeArguments(Class baseClass, Class childClass) { Map resolvedTypes = new HashMap(); Type type = childClass; // start walking up the inheritance hierarchy until we hit baseClass while (!getClass(type).equals(baseClass)) { if (type instanceof Class) { // there is no useful information for us in raw types, so just // keep going. type = ((Class) type).getGenericSuperclass(); } else { ParameterizedType parameterizedType = (ParameterizedType) type; Class rawType = (Class) parameterizedType.getRawType(); Type[] actualTypeArguments = parameterizedType .getActualTypeArguments(); TypeVariable[] typeParameters = rawType.getTypeParameters(); for (int i = 0; i < actualTypeArguments.length; i++) { resolvedTypes .put(typeParameters[i], actualTypeArguments[i]); } if (!rawType.equals(baseClass)) { type = rawType.getGenericSuperclass(); } } } // finally, for each actual type argument provided to baseClass, // determine (if possible) // the raw class for that type argument. Type[] actualTypeArguments; if (type instanceof Class) { actualTypeArguments = ((Class) type).getTypeParameters(); } else { actualTypeArguments = ((ParameterizedType) type) .getActualTypeArguments(); } List> typeArgumentsAsClasses = new ArrayList>(); // resolve types by chasing down type variables. for (Type baseType : actualTypeArguments) { while (resolvedTypes.containsKey(baseType)) { baseType = resolvedTypes.get(baseType); } typeArgumentsAsClasses.add(getClass(baseType)); } return typeArgumentsAsClasses; } /** * Gets the class whose method called the current method. * @return Class that called the current method */ public static Class getCallingClass() { try { StackTraceElement stackTrace[] = Thread.currentThread().getStackTrace(); // The first element in the stack trace will be the getStackTrace method, // the second this method, the third the called method - i.e. the method // of the calling class is the 4th element. return Class.forName(stackTrace[3].getClassName()); } catch (ClassNotFoundException ex) { throw new RuntimeException("Couldn't get calling class", ex); } } /** * Gets an array of types for the specified objects. If an object is null, * it is assumed to be of type Object. * @param objs Array ob objects * @return Array of corresponding class types for the objects */ public static Class[] getTypes(Object objs[]) { ArrayList> types = new ArrayList>(); for (Object obj : objs) { types.add(obj == null ? Object.class : obj.getClass()); } return (Class[])types.toArray(new Class[0]); } /** * Gets the method of the specified class by its name. *

* If no such method can be found, null is returned. *

* This uses the Java Class.getMethod function internally. * @param clazz Clazz containing the method * @param name Name of the method * @param paramTypes Parameter types of the method * @return Method instance of null if no such method exists */ public static Method getMethod(Class clazz, String name, Class paramTypes[]) { try { Method method = clazz.getMethod(name, paramTypes); return method; } catch (NoSuchMethodException ex) { return null; } } /** * Gets the first declared method that matches the specified name and parameter types. In contrast * to the Java reflection method, this method will try to find a method that only matches super types * of the specified parameters instead of the exact type. *
* If multiple methods match, the first returned by the Class.getMethods method will be returned * @param clazz Class instance * @param paramTypes Parameter types * @return Method of the class with the specified parameter types or null if no such * method exists */ public static Method getDeclaredMethod(Class clazz, String name, Class paramTypes[]) { // Try to get the exact method using the Java built in method try { return clazz.getDeclaredMethod(name, paramTypes); } catch (NoSuchMethodException ex) { // Ignore this exception and try to find a method that matches // super types of the parameter types } for (Method m : clazz.getDeclaredMethods()) { if (m.getName().equals(name) && m.getParameterTypes().length == paramTypes.length) { boolean mismatch = false; for (int i = 0; i < paramTypes.length; i++) { if (!m.getParameterTypes()[i].isAssignableFrom(paramTypes[i])) { mismatch = true; break; } } if (!mismatch) return m; } } return null; } /** * Gets the first declared method that matches the specified name and parameter types. In contrast * to the Java reflection method, this method will try to find a method that only matches super types * of the specified parameters instead of the exact type.
* If multiple methods match, the first returned by the Class.getMethods method will be returned *

* This method will also include declared methods of parent classes. * * @param clazz * Class instance * @param paramTypes * Parameter types * @return Method of the class with the specified parameter types or null if no such * method exists */ public static Method getDeclaredMethodInherited(Class clazz, String name, Class paramTypes[]) { Class c = clazz; while (c != null) { Method m = ReflectUtils.getDeclaredMethod(c, name, paramTypes); if (m != null) { return m; } else { c = c.getSuperclass(); } } return null; } /** * Gets the value of a static field. * * @param clazz * Clazz to get static field from * @param fieldName * Name of the field * @param defaultValue * Default value that is returned if the field doesn't exist or has the value null. * @return Value of the field or the specified return value if the field either doesn't exist or is null * @throws JuRuntimeException * If the field cannot be read */ public static Object getStaticFieldValue(Class clazz, String fieldName, Object defaultValue) { try { Object fieldValue = FieldUtils.readStaticField(clazz, fieldName, true); return fieldValue != null ? fieldValue : defaultValue; } catch (IllegalArgumentException ex) { return defaultValue; } catch (Exception ex) { throw new JuRuntimeException(String.format("Couldn't read static field %s from class %s", fieldName, clazz), ex); } } /** * Gets the value of the specified field in the specified object. *

* If the field is not accessible, the method tries to overwrite the * accessibility. * @param obj Object to get field value of * @param field Field to get * @return */ public static Object getFieldValue(Object obj, Field field) { try { if (!field.isAccessible()) field.setAccessible(true); return field.get(obj); } catch (Exception ex) { throw new JuRuntimeException("Couldn't access field", ex); } } /** * Creates an instance of the specified class, forcing access to the default constructor if necessary. * @param clazz Clazz * @param forceAccess If true, access to a private constructor is forces * @param parameters Optional list of parameters to pass to the constructor * @return New instance of the specified class * @throws JuRuntimeException if the instance cannot be created using the default constructor */ public static T newInstance(Class clazz, boolean forceAccess, Object... parameters) { try { if (!forceAccess && parameters.length == 0) { return clazz.newInstance(); } else { Class parameterTypes[] = new Class[parameters.length]; for (int i = 0; i < parameterTypes.length; i++) { AssertUtil.assertNotNull("Null parameters not supported yet", parameters[i]); parameterTypes[i] = parameters[i].getClass(); } Constructor constructor = clazz.getDeclaredConstructor(parameterTypes); if (forceAccess) { constructor.setAccessible(true); } return constructor.newInstance(parameters); } } catch (Exception ex) { throw new JuRuntimeException("Couldn't create instance using default constructor for class " + clazz, ex); } } /** * Gets all declared Methods of the specified class that have the specified annotation. *

* This will not return any inherited methods of base classes. *

* Annotations need to have retention=RUNTIME to be found at runtime * @param clazz Class of the object to find methods of * @param annotationClass Class of the annotations the methods need to have * @return List of declared methods of the class (regardless of accessibility) that have the annotation assigned. Will be ordered by name. */ public static List getDeclaredMethodsByAnnotation(Class clazz, Class annotationClass) { Map methods = new TreeMap<>(); for (Method method : clazz.getDeclaredMethods()) { Annotation a = method.getAnnotation(annotationClass); if (a != null) methods.put(method.getName(), method); } return new ArrayList<>(methods.values()); } /** * Gets all declared fields of the specified class that have the specified annotation. *

* This will not returned any inherited fields of base classes. *

* Annotations need to have retention=RUNTIME to be found at runtime * @param clazz Class of the object to find fields of * @param annotationClass Class of the annotation the field needs to have * @return All declared fields of the class (regardless of accessibility) that have the annotation assigned. * Fields will be sorted by name */ public static List getDeclaredFieldsByAnnotation(Class clazz, Class annotationClass) { Map fields = new TreeMap<>(); for (Field field : clazz.getDeclaredFields()) { Annotation a = field.getAnnotation(annotationClass); if (a != null) fields.put(field.getName(), field); } return new ArrayList<>(fields.values()); } /** * Sets the static field of a class to a new value. * @param clazz Class name * @param fieldName Static field name * @param val New value * @param force If true, accessibility is forced (used to set private fields */ public static void setStaticField(Class clazz, String fieldName, Object val, boolean force) { try { Field field = clazz.getDeclaredField(fieldName); if (force && !field.isAccessible()) { field.setAccessible(true); } field.set(null, val); } catch (Exception ex) { throw new JuRuntimeException("Couldn't set static field %s.%s to value %s", ex, clazz, fieldName, val); } } /** * Gets the value if the (alphabetically) first declared field of the specified object that has the * specified annotation assigned. *

* If no such field can be found, null is returned. * @param obj Object to find field of * @param annotationClass Class of the annotation the field needs to have * @param forceAccess If true, access is forced even if the field is private * @return Value of the (alphabetically) first declared field with the specified annotation or null if none exists */ public static Object getDeclaredFieldValueByAnnotation(Object obj, Class annotationClass, boolean forceAccess) { List fields = ReflectUtils.getDeclaredFieldsByAnnotation(obj.getClass(), annotationClass); if (fields.isEmpty()) return null; Field field = fields.get(0); try { if (forceAccess) field.setAccessible(true); return field.get(obj); } catch (IllegalAccessException ex) { throw new JuRuntimeException("Couldn't access field " + field.getName(), ex); } } private static List toAnnotationList(List> list) { List annos = new ArrayList<>(); for (AnnotationInfo annoInfo : list) { annos.add(annoInfo.getAnnotation()); } return annos; } /** * Gets all annotations of the specified type for the specified class, not including annotation declaration info. *

* If no annotation is found, an empty list is returned. *

* If includeSuperClassesAnnotations is true, all super classes of the class are searched for the * specified annotation. The annotations are returned in order class (first) to super classes (Object last). * @param clazz Class to search for annotations * @param annotationClass Type of annotation * @param includeSuperClassesAnnotations If true, super classes are searched as well. If false, we can only get 0 or 1 results * @return List of annotations, in order of clazz (first) to super class (Object is last) */ public static List getAnnotations( Class clazz, Class annotationClass, boolean includeSuperClassesAnnotations) { return toAnnotationList(getAnnotationsWithInfo(clazz, annotationClass, includeSuperClassesAnnotations)); } /** * Gets all annotations of the specified type for the specified class. *

* If no annotation is found, an empty list is returned. *

* If includeSuperClassesAnnotations is true, all super classes of the class are searched for the * specified annotation. The annotations are returned in order class (first) to super classes (Object last). * @param clazz Class to search for annotations * @param annotationClass Type of annotation * @param includeSuperClassesAnnotations If true, super classes are searched as well. If false, we can only get 0 or 1 results * @return List of annotations, in order of clazz (first) to super class (Object is last). Includes infos on where the annotation * was defined (on which class / method). */ public static List> getAnnotationsWithInfo( Class clazz, Class annotationClass, boolean includeSuperClassesAnnotations) { List> annos = new ArrayList<>(); Class c = clazz; do { A anno = c.getAnnotation(annotationClass); if (anno != null) annos.add(new AnnotationInfo(anno, annotationClass, c, null)); c = c.getSuperclass(); } while (includeSuperClassesAnnotations && c != null); return annos; } /** * Gets annotations of the specified type for the specified method. Doesn't include annotation declaration info. *

* If no annotation is found, an empty list is returned. *

* If includeOverriddenMethods and includeClassAnnotations are both false, we can get either 1 * or 2 results. *

* Annotations are returned in the following order: *

    *
  1. method
  2. *
  3. overridden method(s)
  4. *
  5. declaring class
  6. *
  7. super classes of declaring class
  8. *
* @param method Method to get annotations for * @param annotationClass Class of the annotation to get * @param includeOverriddenMethods If true, overridden methods of super classes are also searched * @param includeClassAnnotations If true, the declaring class of the method is also searched * @param includeSuperClassesAnnotations If true, super classes of the declaring class are also searched. This * parameter is ignored if includeClassAnnotations is false. * @return List of annotations. If no annotations were found, an empty list is returned. */ public static List getAnnotations( Method method, Class annotationClass, boolean includeOverriddenMethods, boolean includeClassAnnotations, boolean includeSuperClassesAnnotations) { return toAnnotationList(getAnnotationsWithInfo(method, annotationClass , includeOverriddenMethods, includeClassAnnotations, includeSuperClassesAnnotations)); } /** * Gets annotations of the specified type for the specified method. *

* If no annotation is found, an empty list is returned. *

* If includeOverriddenMethods and includeClassAnnotations are both false, we can get either 1 * or 2 results. *

* Annotations are returned in the following order: *

    *
  1. method
  2. *
  3. overridden method(s)
  4. *
  5. declaring class
  6. *
  7. super classes of declaring class
  8. *
* @param method Method to get annotations for * @param annotationClass Class of the annotation to get * @param includeOverriddenMethods If true, overridden methods of super classes are also searched * @param includeClassAnnotations If true, the declaring class of the method is also searched * @param includeSuperClassesAnnotations If true, super classes of the declaring class are also searched. This * parameter is ignored if includeClassAnnotations is false. * @return List of annotations. If no annotations were found, an empty list is returned. Includes information on where the annotation * was declared (on which class / method). */ public static
List> getAnnotationsWithInfo( Method method, Class annotationClass, boolean includeOverriddenMethods, boolean includeClassAnnotations, boolean includeSuperClassesAnnotations) { List> annos = new ArrayList<>(); Method m = method; do { A anno = m.getAnnotation(annotationClass); if (anno != null) annos.add(new AnnotationInfo(anno, annotationClass, m.getDeclaringClass(), m)); // Check if the method is overriding a method in the super class Class superClass = m.getDeclaringClass().getSuperclass(); if (superClass != null) { try { m = superClass.getMethod(m.getName(), m.getParameterTypes()); } catch (NoSuchMethodException ex) { m = null; } } else { m = null; } } while (includeOverriddenMethods && m != null); if (includeClassAnnotations) { List> classAnnos = ReflectUtils.getAnnotationsWithInfo(method.getDeclaringClass(), annotationClass, includeSuperClassesAnnotations); annos.addAll(classAnnos); } return annos; } /** * Helper class that encapsulates an Annotation and information on where it was declared (on which class / method). *

* Overrides toString that will return the Annotation type along with the declaring class and (if any) method * @author [email protected] * * @param Annotation Type */ public static class AnnotationInfo implements Serializable { private A annotation; private String annotationClassName; private String className; private String methodName; private AnnotationInfo(A annotation, Class annotationClass, Class clazz, Method method) { this.annotation = annotation; this.annotationClassName = annotationClass.getName(); this.className = clazz.getName(); this.methodName = method == null ? null : method.getName(); } /** * Gets the actual annotation. * @return Annotation */ public A getAnnotation() { return this.annotation; } /** * Gets the name of the class the annotation is declared on. If it's a method annotation, * the class of the method is returned. * @return Name of the declaring class */ public String getDeclaringClassName() { return this.className; } /** * Gets the name of the method the annotation is declared on. If it's a class annotation, null * is returned. * @return Name of the declaring method or null for class annotations */ public String getDeclaringMethodName() { return this.methodName; } @Override public String toString() { String s = String.format("%s (%s", this.annotationClassName, this.className); if (this.methodName == null) { s += ")"; } else { s += String.format(".%s())", this.methodName); } return s; } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy