/* * Beanfabrics Framework Copyright (C) by Michael Karneim, beanfabrics.org * Use is subject to license terms. See license.txt. */ // TODO javadoc - remove this comment only when the class and all non-public // methods and fields are documented package org.beanfabrics.util; import java.lang.annotation.Annotation; 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.util.Arrays; import java.util.Collection; import java.util.LinkedList; import java.util.List; import java.util.Properties; import org.beanfabrics.log.Logger; import org.beanfabrics.log.LoggerFactory; /** * The <code>ReflectionUtil</code> is a utility class for doing reflection. * * @author Michael Karneim */ public class ReflectionUtil { private final static Logger LOG = LoggerFactory.getLogger(ReflectionUtil.class); private static int callCount = 0; public interface FieldFilter { boolean accept(Field field); } public interface MethodFilter { boolean accept(Method method); } /** * Returns all declared fields of the given {@link Class} and it's * superclasses. */ public static List<Field> getAllFields(Class cls) { LinkedList<Field> result = new LinkedList<Field>(); findAllFields(cls, result); return result; } /** * Returns all declared fields of the given {@link Class} and it's * superclasses that have the given name and are assignable to the given * type. * * @param cls the class that owns the returned fields * @param fieldName name of the returned fields or <code>null</code> for no * name filtering * @param fieldType the generic type of the returned fields or * <code>null</code> for no type filtering * @return all declared fields that match the given filter arguments */ public static List<Field> getAllFields(Class cls, final String fieldName, final Class fieldType) { LinkedList<Field> result = new LinkedList<Field>(); findAllFields(cls, result, new FieldFilter() { public boolean accept(Field field) { return (fieldName == null || fieldName.equals(field.getName())) && (fieldType == null || fieldType.isAssignableFrom(field.getType())); } }); return result; } private static void findAllFields(Class cls, Collection<? super Field> result) { findAllFields(cls, result, null); } private static void findAllFields(Class cls, Collection<? super Field> result, FieldFilter filter) { Field[] declFields = cls.getDeclaredFields(); for (Field f : declFields) { if (filter == null || filter.accept(f)) { result.add(f); } } Class superCls = cls.getSuperclass(); if (superCls != null) { findAllFields(superCls, result, filter); } // Ugh! We don't have (non static) fields in interfaces, have we?? // Class[] interfaces = cls.getInterfaces(); // for (Class i : interfaces) { // findAllFields( i, result, filter); // } } public static List<Field> getAllFields(Class cls, final Class annoType) { LinkedList<Field> result = new LinkedList<Field>(); findAllFields(cls, result, new FieldFilter() { public boolean accept(Field field) { Annotation anno = field.getAnnotation(annoType); return (anno != null); } }); return result; } /** * Returns all declared members of the given {@link Class} and it's * superclasses. * * @param cls the class of which to search the members */ public static List<Member> getAllMembers(Class cls) { LinkedList<Member> result = new LinkedList<Member>(); findAllMethods(cls, result); findAllFields(cls, result); return result; } /** * Returns all declared methods of the given {@link Class} and it's * superclasses. * * @param cls the class of which to search the methods */ public static List<Method> getAllMethods(Class cls) { LinkedList<Method> result = new LinkedList<Method>(); findAllMethods(cls, result); return result; } private static void findAllMethods(Class cls, Collection<? super Method> result) { Method[] declMethods = cls.getDeclaredMethods(); for (Method m : declMethods) { if (result.contains(m) == false) { result.add(m); } } Class superCls = cls.getSuperclass(); if (superCls != null) { findAllMethods(superCls, result); } Class[] interfaces = cls.getInterfaces(); for (Class i : interfaces) { findAllMethods(i, result); } } /** * Returns all declared methods of the given {@link Class} and it's * superclasses that have the given name and have a return type assignable * to the given type. * * @param cls the class that owns the returned methods * @param methodName name of the returned methods or <code>null</code> for * no name filtering * @param parameterTypes the parameter types of the returned methods or * <code>null</code> for no parameter types filtering * @param returnType the generic type of the returned methods or * <code>null</code> for no type filtering * @return all declared methods that match the given filter arguments */ public static Collection<Method> getAllMethods(Class cls, final String methodName, final Class[] parameterTypes, final Class returnType) { LinkedList<Method> result = new LinkedList<Method>(); findAllMethods(cls, result, new MethodFilter() { public boolean accept(Method method) { return (methodName == null || methodName.equals(method.getName())) && (returnType == null || returnType.isAssignableFrom(method.getReturnType())) && (parameterTypes == null || Arrays.equals(parameterTypes, method.getParameterTypes())); } }); return result; } /** * Returns all Methods of the given {@link Class} and it's superclasses * which are annotated with the given <code>annotationType</code>. * * @param cls the class of which to search the methods * @param annotationType the annotation type which mark methods to be * contained in the result */ public static List<Method> getAllMethods(Class cls, Class annotationType) { LinkedList<Method> result = new LinkedList<Method>(); findAllMethods(cls, annotationType, result); return result; } private static void findAllMethods(Class cls, Collection<? super Method> result, MethodFilter filter) { Method[] declMethods = cls.getDeclaredMethods(); for (Method f : declMethods) { if (filter == null || filter.accept(f)) { result.add(f); } } Class superCls = cls.getSuperclass(); if (superCls != null) { findAllMethods(superCls, result, filter); } Class[] interfaces = cls.getInterfaces(); for (Class i : interfaces) { findAllMethods(i, result, filter); } } private static void findAllMethods(Class cls, Class annoType, Collection<? super Method> result) { Method[] declMethods = cls.getDeclaredMethods(); for (Method m : declMethods) { Annotation anno = m.getAnnotation(annoType); if (anno != null && result.contains(m) == false) { result.add(m); } } Class superCls = cls.getSuperclass(); if (superCls != null) { findAllMethods(superCls, annoType, result); } Class[] interfaces = cls.getInterfaces(); for (Class i : interfaces) { findAllMethods(i, annoType, result); } } /** * Returns a {@link List} of all superclasses and all interfaces of the * given baseclass, including the baseclass. * * @param baseClass * @return List */ public static List<Class> getAllClasses(Class baseClass) { LinkedList<Class> result = new LinkedList<Class>(); findAllClasses(baseClass, result); return result; } private static void findAllClasses(Class baseClass, LinkedList<Class> result) { if (result.contains(baseClass) == false) { result.add(baseClass); Class superCls = baseClass.getSuperclass(); if (superCls != null) { findAllClasses(superCls, result); } Class[] interfaces = baseClass.getInterfaces(); for (Class i : interfaces) { findAllClasses(i, result); } } } /** * Calls all methods of <code>target</code> that are annotated with the * specified <code>annotationType</code>. * * @param target the <code>Object</code> to call the methods from * @param annotationType the type of the annotation marking the methods to * call */ public static void callAnnotatedMethods(Object target, Class annotationType) { Collection<Method> methods = getAllMethods(target.getClass()); for (Method m : methods) { Annotation anno = m.getAnnotation(annotationType); if (anno != null) { try { if (!m.isAccessible()) { m.setAccessible(true); } m.invoke(target, (Object[])null); } catch (IllegalAccessException ex) { throw new RuntimeException(ex); } catch (InvocationTargetException ex) { throw new RuntimeException(ex); } } } } public static void setProperties(Object bean, Properties props) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, SecurityException, NoSuchMethodException { Class beanClass = bean.getClass(); for (Object key : props.keySet()) { if (key instanceof String) { String name = (String)key; String value = props.getProperty(name); String setterMethodName = "set" + firstCharToUpperCase(name); Method m = beanClass.getMethod(setterMethodName, new Class[] { String.class }); m.invoke(bean, new Object[] { value }); } } } private static String firstCharToUpperCase(String text) { String result = Character.toUpperCase(text.charAt(0)) + text.substring(1); return result; } public static void setFieldValue(Object owner, Field field, Object value) throws IllegalArgumentException, IllegalAccessException { if (!field.isAccessible()) { field.setAccessible(true); } field.set(owner, value); } public static Object getFieldValue(Object owner, Field f) throws IllegalArgumentException, IllegalAccessException { if (!f.isAccessible()) { f.setAccessible(true); } Object result = f.get(owner); return result; } public static Object invokeMethod(Object owner, Method m, Object... args) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException { callCount++; if (!m.isAccessible()) { m.setAccessible(true); } if (LOG.isDebugEnabled()) { if (m.getParameterTypes().length == 0) { LOG.debug("Calling " + format(m) + " on " + owner); } else if (args == null) { LOG.debug("Calling " + format(m) + " on " + owner + " with no arguments"); } else { LOG.debug("Calling " + format(m) + " on " + owner + " with arguments: " + format(args)); } } Object result = m.invoke(owner, args); return result; } private static String format(Object[] args) { if (args == null) { throw new IllegalArgumentException("args==null"); } StringBuilder sb = new StringBuilder(); for (int i = 0; i < args.length; ++i) { if (i > 0) { sb.append(", "); } sb.append(args[i]); } return sb.toString(); } private static String format(Method m) { StringBuilder sb = new StringBuilder(); sb.append(m.getName()); sb.append("("); int count = 0; for (Class paramType : m.getParameterTypes()) { if (count > 0) { sb.append(", "); } sb.append(paramType.getName()); count++; } sb.append(")"); return sb.toString(); } public static <T> T newInstance(Class<T> cls) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, SecurityException, NoSuchMethodException, InstantiationException { Constructor<T> constr = cls.getConstructor((Class[])null); if (!constr.isAccessible()) { constr.setAccessible(true); } T result = constr.newInstance((Object[])null); return result; } }