
org.nuiton.util.ReflectUtil Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of nuiton-utils Show documentation
Show all versions of nuiton-utils Show documentation
Library of usefull class to be used in any project.
/*
* #%L
* Nuiton Utils
* %%
* Copyright (C) 2004 - 2010 CodeLutin
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Lesser Public License for more details.
*
* You should have received a copy of the GNU General Lesser Public
* License along with this program. If not, see
* .
* #L%
*/
package org.nuiton.util;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static org.nuiton.i18n.I18n.t;
/**
* Introspection sur une classe. Détermine si un attribut est constant,
* recherche de constantes d'un type donné, conversion d'une classe en
* Enum...
*
* Created: 30 décembre 2007
*
* @author Tony Chemit - [email protected]
*/
public class ReflectUtil {
/** Logger */
private static final Log log = LogFactory.getLog(ReflectUtil.class);
/**
* Pour déterminer si un champ d'une classe est une constante
* (modifiers sont static, final et public)
*
* @param field le champs à tester
* @return {@code true} si les modifiers sont final, static et public
*/
public static boolean isConstantField(Field field) {
int modifiers = field.getModifiers();
return Modifier.isPublic(modifiers) &&
Modifier.isStatic(modifiers) &&
Modifier.isFinal(modifiers);
}
/**
* Recherche dans une classe donnée {@code klazz}, les constantes d'un
* certain type {@code searchingClass} et les retourne.
*
* L'algorithme parcourt aussi les superclasses.
*
* @param enumeration's type
* @param klass la classe contenant les constantes
* @param searchingClass le type des champs constants à récupérer
* @return la liste des champs du type requis dans
* @throws RuntimeException si problème lors de la récupération
*/
@SuppressWarnings({"unchecked"})
public static List getConstants(Class> klass,
Class searchingClass) {
List result = new ArrayList();
for (Field field : klass.getDeclaredFields()) {
if (!field.isAccessible()) {
field.setAccessible(true);
}
if (searchingClass.isAssignableFrom(field.getType()) &&
isConstantField(field)) {
try {
result.add((T) field.get(null));
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
Class> superClass = klass.getSuperclass();
if (superClass != null) {
result.addAll(getConstants(superClass, searchingClass));
}
return result;
}
/**
* @param enumeration's type
* @param klass the required class
* @param fieldName the required constant name
* @return the constant value
*/
@SuppressWarnings({"unchecked"})
public static T getConstant(Class> klass, String fieldName) {
try {
T result = null;
Field f = klass.getDeclaredField(fieldName);
if (isConstantField(f)) {
f.setAccessible(true);
result = (T) f.get(null);
}
return result;
} catch (NoSuchFieldException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
/**
* Convertit une classe non typée, en une classe d'enum
*
* @param enumeration's type
* @param type la classe a typer
* @return la classe typee
* @throws IllegalArgumentException si le type est null ou non une extension
* de la classe Enum.
*/
@SuppressWarnings({"unchecked"})
public static > Class getEnumClass(Class> type) throws IllegalArgumentException {
if (type == null) {
throw new IllegalArgumentException(t("nuitonutil.error.null.parameter", "type"));
}
if (!type.isEnum()) {
throw new IllegalArgumentException(t("nuitonutil.error.not.an.enum", type));
}
return (Class) type;
}
/**
* Cherche une methode selon son nom et ses paramètres d'invocation.
*
* @param klass la classe dans laquelle rechercher la méthode
* @param methodName le nom de la méthode recherchée
* @param strict un drapeau pour déclancher une exception si la méthode
* n'est pas trouvée
* @param arguments les arguments d'invocation de la méthode
* @return la méthode trouvée
* @throws IllegalArgumentException si la méthode n'est pas trouvée et que
* le drapeau {@code strict} est à {@code true}
* @since 1.3.1
*/
public static Method getDeclaredMethod(Class> klass,
String methodName,
boolean strict,
Object... arguments) throws IllegalArgumentException {
HashSet> classes = new HashSet>();
Method method;
try {
method = getDeclaredMethod(klass, methodName, classes, arguments);
} finally {
if (log.isDebugEnabled()) {
log.debug("Inspected classes : " + classes);
}
classes.clear();
}
if (method == null && strict) {
throw new IllegalArgumentException(
"could not find method " + methodName + " on type " +
klass.getName());
}
return method;
}
/**
* Obtain the boxed type of any incoming type.
*
* If incoming type is not a primitive type, then just returns himself.
*
* @param type the type to box
* @return the boxed type
* @see Class#isPrimitive()
* @since 1.3.1
*/
public static Class> boxType(Class> type) {
if (!type.isPrimitive()) {
return type;
}
if (boolean.class.equals(type)) {
return Boolean.class;
}
if (char.class.equals(type)) {
return Character.class;
}
if (byte.class.equals(type)) {
return Byte.class;
}
if (short.class.equals(type)) {
return Short.class;
}
if (int.class.equals(type)) {
return Integer.class;
}
if (long.class.equals(type)) {
return Long.class;
}
if (float.class.equals(type)) {
return Float.class;
}
if (double.class.equals(type)) {
return Double.class;
}
if (void.class.equals(type)) {
return Void.class;
}
// should never come here...
return type;
}
/**
* Obtain the unboxed type of any incoming type.
*
* If incoming type is a primitive type, then just returns himself.
*
* @param type the type to unbox
* @return the unboxed type
* @see Class#isPrimitive()
* @since 1.3.1
*/
public static Class> unboxType(Class> type) {
if (type.isPrimitive()) {
return type;
}
if (Boolean.class.equals(type)) {
return boolean.class;
}
if (Character.class.equals(type)) {
return char.class;
}
if (Byte.class.equals(type)) {
return byte.class;
}
if (Short.class.equals(type)) {
return short.class;
}
if (Integer.class.equals(type)) {
return int.class;
}
if (Long.class.equals(type)) {
return long.class;
}
if (Float.class.equals(type)) {
return float.class;
}
if (Double.class.equals(type)) {
return double.class;
}
if (Void.class.equals(type)) {
return void.class;
}
// not a primitive type
return type;
}
/**
* Obtain all the declared fields of the given {@code objectClass} with a
* deep scan inside super classes and interfaces.
*
* Note: The type {@link Object} will not be scanned.
*
* @param objectClass the object class to scan
* @return the set of all declared fields found
* @since 2.0
*/
public static Set getAllDeclaredFields(Class> objectClass) {
Set result = new HashSet();
Set> visitedClasses = new HashSet>();
try {
getAllDeclaredFields(objectClass, visitedClasses, result);
} finally {
visitedClasses.clear();
}
return result;
}
/**
* Obtain all the declared methods of the given {@code objectClass} with a
* deep scan inside super classes and interfaces.
*
* Note: The type {@link Object} will not be scanned.
*
* @param objectClass the object class to scan
* @return the set of all declared methods found
* @since 2.0
*/
public static Set getAllDeclaredMethods(Class> objectClass) {
Set result = new HashSet();
Set> visitedClasses = new HashSet>();
try {
getAllDeclaredMethods(objectClass, visitedClasses, result);
} finally {
visitedClasses.clear();
}
return result;
}
/**
* Obtain all the fields with the given annotation type.
*
* Note: This method will not scan deeply the given type
* (no scan of super-classes nor interfaces).
*
* @param objectClass the type to scan
* @param annotationClass the type of annotation to scan
* @param type of annotation to scan
* @return the dictionnary of fields with the given annotation
* @since 2.0
*/
public static Map getFieldAnnotation(Class> objectClass,
Class annotationClass) {
Map result = getFieldAnnotation(objectClass,
annotationClass,
false
);
return result;
}
/**
* Obtain all the fields with the given annotation type.
*
* Note: This method will scan deeply the given type
* if parameter {@code deepVisit} is setted to {@code true}.
*
* Note: The type {@link Object} will not be scanned.
*
* @param objectClass the type to scan
* @param annotationClass the type of annotation to scan
* @param deepVisit flag to visit deeply the class (if set to
* {@code true}, will also scan super classes
* and interfaces)
* @param type of annotation to scan
* @return the dictionnary of fields with the given annotation
* @since 2.0
*/
public static Map getFieldAnnotation(Class> objectClass,
Class annotationClass,
boolean deepVisit) {
Map result = new HashMap();
Set> visitedClasses = new HashSet>();
try {
getFieldAnnotation(objectClass,
annotationClass,
deepVisit,
visitedClasses,
result
);
} finally {
visitedClasses.clear();
}
return result;
}
/**
* Obtain all the methods with the given annotation type.
*
* Note: This method will not scan deeply the given type
* (no scan of super-classes nor interfaces).
*
* @param objectClass the type to scan
* @param annotationClass the type of annotation to scan
* @param type of annotation to scan
* @return the dictionnary of methods with the given annotation
* @since 2.0
*/
public static Map getMethodAnnotation(Class> objectClass,
Class annotationClass) {
Map result = getMethodAnnotation(objectClass,
annotationClass,
false
);
return result;
}
/**
* Obtain all the methods with the given annotation type.
*
* Note: This method will scan deeply the given type
* if parameter {@code deepVisit} is setted to {@code true}.
*
* Note: The type {@link Object} will not be scanned.
*
* @param objectClass the type to scan
* @param annotationClass the type of annotation to scan
* @param type of annotation to scan
* @param deepVisit flag to visit deeply the class (if set to
* {@code true}, will also scan super classes
* and interfaces)
* @return the dictionnary of methods with the given annotation
* @since 2.0
*/
public static Map getMethodAnnotation(Class> objectClass,
Class annotationClass,
boolean deepVisit) {
Map result = new HashMap();
Set> visitedClasses = new HashSet>();
try {
getMethodAnnotation(objectClass,
annotationClass,
deepVisit,
visitedClasses,
result
);
} finally {
visitedClasses.clear();
}
return result;
}
protected static Method getDeclaredMethod(Class> klass,
String methodName,
Set> visitedClasses,
Object... arguments) {
if (visitedClasses.contains(klass)) {
// this means class was already unsucessfull visited
return null;
}
visitedClasses.add(klass);
Method method = null;
for (Method m : klass.getDeclaredMethods()) {
if (!methodName.equals(m.getName())) {
continue;
}
// same method name
Class>[] types = m.getParameterTypes();
if (arguments.length != types.length) {
continue;
}
// same number arguments
Class>[] prototype = m.getParameterTypes();
if (log.isDebugEnabled()) {
log.debug("Found a method with same parameters size : " +
m.getName() + " : " + Arrays.toString(prototype));
}
int index = 0;
boolean parametersMatches = true;
for (Object argument : arguments) {
Class> type = prototype[index++];
if (argument == null) {
// can not say anything, let says it is ok...
continue;
}
Class> runtimeType = argument.getClass();
if (log.isDebugEnabled()) {
log.debug("Test parameter [" + (index - 1) + "] : " +
type + " vs " + runtimeType);
}
type = boxType(type);
runtimeType = boxType(runtimeType);
if (!type.equals(runtimeType) &&
!type.isAssignableFrom(runtimeType)) {
// not same type
parametersMatches = false;
if (log.isDebugEnabled()) {
log.debug("Types are not matching.");
}
break;
}
}
if (parametersMatches) {
// same parameters types, this is a match
method = m;
}
break;
}
if (method == null) {
// try on super class
if (klass.getSuperclass() != null) {
method = getDeclaredMethod(klass.getSuperclass(),
methodName,
visitedClasses,
arguments
);
}
}
if (method == null) {
// try on interfaces
Class>[] interfaces = klass.getInterfaces();
for (Class> anInterface : interfaces) {
method = getDeclaredMethod(anInterface,
methodName,
visitedClasses,
arguments
);
if (method != null) {
break;
}
}
}
return method;
}
protected static void getAllDeclaredFields(Class> objectClass,
Set> visitedClasses,
Set result) {
if (visitedClasses.contains(objectClass) ||
Object.class.equals(objectClass)) {
// already scanned
// or arrives to Object.class
return;
}
// mark as scanned
visitedClasses.add(objectClass);
Field[] declaredFields = objectClass.getDeclaredFields();
result.addAll(Arrays.asList(declaredFields));
Class> superclass = objectClass.getSuperclass();
if (superclass != null) {
// scan also the superclass
getAllDeclaredFields(superclass,
visitedClasses,
result
);
}
Class>[] interfaces = objectClass.getInterfaces();
for (Class> anInterface : interfaces) {
// scan also the interface
getAllDeclaredFields(anInterface,
visitedClasses,
result
);
}
}
protected static void getAllDeclaredMethods(Class> objectClass,
Set> visitedClasses,
Set result) {
if (visitedClasses.contains(objectClass) ||
Object.class.equals(objectClass)) {
// already scanned
// or arrives to Object.class
return;
}
// mark as scanned
visitedClasses.add(objectClass);
Method[] declaredFields = objectClass.getDeclaredMethods();
result.addAll(Arrays.asList(declaredFields));
Class> superclass = objectClass.getSuperclass();
if (superclass != null) {
// scan also the superclass
getAllDeclaredMethods(superclass,
visitedClasses,
result
);
}
Class>[] interfaces = objectClass.getInterfaces();
for (Class> anInterface : interfaces) {
// scan also the interface
getAllDeclaredMethods(anInterface,
visitedClasses,
result
);
}
}
protected static void getFieldAnnotation(Class> objectClass,
Class annotationClass,
boolean deepVisit,
Set> visitedClasses,
Map result) {
if (visitedClasses.contains(objectClass) ||
Object.class.equals(objectClass)) {
// already scanned
// or arrives to Object.class
return;
}
// mark as scanned
visitedClasses.add(objectClass);
for (Field field : objectClass.getDeclaredFields()) {
A annotation = field.getAnnotation(annotationClass);
if (annotation != null) {
result.put(field, annotation);
}
}
if (deepVisit) {
Class> superclass = objectClass.getSuperclass();
if (superclass != null) {
// scan also the superclass
getFieldAnnotation(superclass,
annotationClass,
deepVisit,
visitedClasses,
result
);
}
Class>[] interfaces = objectClass.getInterfaces();
for (Class> anInterface : interfaces) {
// scan also the interface
getFieldAnnotation(anInterface,
annotationClass,
deepVisit,
visitedClasses,
result
);
}
}
}
protected static void getMethodAnnotation(Class> objectClass,
Class annotationClass,
boolean deepVisit,
Set> visitedClasses,
Map result) {
if (visitedClasses.contains(objectClass) ||
Object.class.equals(objectClass)) {
// already scanned
// or arrives to Object.class
return;
}
// mark as scanned
visitedClasses.add(objectClass);
for (Method method : objectClass.getDeclaredMethods()) {
A annotation = method.getAnnotation(annotationClass);
if (annotation != null) {
result.put(method, annotation);
}
}
if (deepVisit) {
Class> superclass = objectClass.getSuperclass();
if (superclass != null) {
// scan also the superclass
getMethodAnnotation(superclass,
annotationClass,
deepVisit,
visitedClasses,
result
);
}
Class>[] interfaces = objectClass.getInterfaces();
for (Class> anInterface : interfaces) {
// scan also the interface
getMethodAnnotation(anInterface,
annotationClass,
deepVisit,
visitedClasses,
result
);
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy