org.nuiton.jaxx.runtime.util.ReflectUtil Maven / Gradle / Ivy
package org.nuiton.jaxx.runtime.util;
/*-
* #%L
* JAXX :: Runtime
* %%
* Copyright (C) 2008 - 2024 Code Lutin, Ultreia.io
* %%
* 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%
*/
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
/**
* Created by tchemit on 12/07/17.
*
* @author Tony Chemit - [email protected]
*/
public class ReflectUtil {
/** Logger. */
private static final Logger log = LogManager.getLogger(ReflectUtil.class);
/**
* 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;
}
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;
}
/**
* 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 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 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 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
);
}
}
}
}