Author: tchemit Date: 2010-06-09 12:14:18 +0200 (Wed, 09 Jun 2010) New Revision: 1874 Url: http://nuiton.org/repositories/revision/nuiton-utils/1874 Log: add usefull methods in ReflectionUtil Modified: trunk/src/main/java/org/nuiton/util/ReflectUtil.java Modified: trunk/src/main/java/org/nuiton/util/ReflectUtil.java =================================================================== --- trunk/src/main/java/org/nuiton/util/ReflectUtil.java 2010-06-08 09:01:57 UTC (rev 1873) +++ trunk/src/main/java/org/nuiton/util/ReflectUtil.java 2010-06-09 10:14:18 UTC (rev 1874) @@ -25,13 +25,20 @@ package org.nuiton.util; -import static org.nuiton.i18n.I18n._; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; 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.HashSet; import java.util.List; +import java.util.Set; +import static org.nuiton.i18n.I18n._; + /** * Des méthodes utiles d'introspection * @@ -39,6 +46,10 @@ */ public class ReflectUtil { + + /** Logger */ + static private Log log = LogFactory.getLog(ReflectUtil.class); + /** * Pour déterminer si un champ d'une classe est une constante * (modifiers sont static, final et public) @@ -57,7 +68,7 @@ * <p/> * L'algorithme parcourt aussi les superclasses. * - * @param <T> enumeration's type + * @param <T> 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 @@ -86,7 +97,7 @@ } /** - * @param <T> enumeration's type + * @param <T> enumeration's type * @param klass the required class * @param fieldName the required constant name * @return the constant value @@ -111,7 +122,7 @@ /** * Convertit une classe non typée, en une classe d'enum * - * @param <T> enumeration's type + * @param <T> enumeration's type * @param type la classe a typer * @return la classe typee * @throws IllegalArgumentException si le type est null ou non une extension @@ -127,4 +138,218 @@ } return (Class<T>) 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 { + Method method = getDeclaredMethod(klass, methodName, new HashSet<Class<?>>(), arguments); + 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. + * <p/> + * 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. + * <p/> + * 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; + + } + + protected static Method getDeclaredMethod(Class<?> klass, + String methodName, + Set<Class<?>> 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; + 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; + + } }