package org.needle4j.reflection; import static java.lang.String.format; import java.lang.annotation.Annotation; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.needle4j.predicate.IsSupportedAnnotationPredicate; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public final class ReflectionUtil { private static final Logger LOG = LoggerFactory.getLogger(ReflectionUtil.class); private static final Map<Class<?>, Class<?>> PRIMITIVES = new HashMap<Class<?>, Class<?>>(); static { PRIMITIVES.put(int.class, Integer.class); PRIMITIVES.put(double.class, Double.class); PRIMITIVES.put(boolean.class, Boolean.class); PRIMITIVES.put(long.class, Long.class); PRIMITIVES.put(float.class, Float.class); PRIMITIVES.put(char.class, Character.class); PRIMITIVES.put(short.class, Short.class); PRIMITIVES.put(byte.class, Byte.class); } private ReflectionUtil() { super(); } public static List<Field> getAllFieldsWithSupportedAnnotation(final Class<?> clazz, final IsSupportedAnnotationPredicate isSupportedAnnotationPredicate) { final List<Field> result = new ArrayList<Field>(); new DerivedClassIterator(clazz) { @Override protected boolean handleClass(final Class<?> clazz) { for (final Field field : getDeclaredFields(clazz)) { if (isSupportedAnnotationPredicate.applyAny(field.getAnnotations())) { result.add(field); } } return true; } }.iterate(); return result; } public static List<Field> getAllFieldsWithAnnotation(final Class<?> clazz, final Class<? extends Annotation> annotation) { final List<Field> result = new ArrayList<Field>(); new DerivedClassIterator(clazz) { @Override protected boolean handleClass(final Class<?> clazz) { for (final Field field : getDeclaredFields(clazz)) { if (field.getAnnotation(annotation) != null) { result.add(field); } } return true; } }.iterate(); return result; } public static List<Method> getAllMethodsWithAnnotation(final Class<?> clazz, final Class<? extends Annotation> annotation) { final List<Method> result = new ArrayList<Method>(); new DerivedClassIterator(clazz) { @Override protected boolean handleClass(final Class<?> clazz) { for (final Method method : clazz.getDeclaredMethods()) { if (method.isAnnotationPresent(annotation)) { result.add(method); } } return true; } }.iterate(); return result; } public static Map<Class<? extends Annotation>, List<Field>> getAllAnnotatedFields(final Class<?> clazz) { final Map<Class<? extends Annotation>, List<Field>> result = new HashMap<Class<? extends Annotation>, List<Field>>(); final List<Field> fields = getAllFields(clazz); for (final Field field : fields) { final Annotation[] annotations = field.getAnnotations(); for (final Annotation annotation : annotations) { final Class<? extends Annotation> annotationType = annotation.annotationType(); List<Field> list = result.get(annotationType); if (list == null) { list = new ArrayList<Field>(); } list.add(field); result.put(annotationType, list); } } return result; } public static List<Field> getAllFieldsAssignableFrom(final Class<?> assignableType, final Class<?> clazz) { final List<Field> result = new ArrayList<Field>(); new DerivedClassIterator(clazz) { @Override protected boolean handleClass(final Class<?> clazz) { for (final Field field : getDeclaredFields(clazz)) { if (field.getType().isAssignableFrom(assignableType)) { result.add(field); } } return true; } }.iterate(); return result; } /** * @deprecated typo * @see #getAllFieldsAssignableFrom(Class, Class) */ @Deprecated public static List<Field> getAllFieldsAssinableFrom(final Class<?> assinableType, final Class<?> clazz) { return getAllFieldsAssignableFrom(assinableType, clazz); } public static List<Field> getAllFieldsWithAnnotation(final Object instance, final Class<? extends Annotation> annotation) { return getAllFieldsWithAnnotation(instance.getClass(), annotation); } public static List<Field> getAllFields(final Class<?> clazz) { final List<Field> result = new ArrayList<Field>(); new DerivedClassIterator(clazz) { @Override protected boolean handleClass(final Class<?> clazz) { final Field[] fields = getDeclaredFields(clazz); Collections.addAll(result, fields); return true; } }.iterate(); return result; } private static Field[] getDeclaredFields(Class<?> clazz) { return clazz.getDeclaredFields(); } /** * @param clazz * object * @return list of method objects * @see Class#getMethods() */ public static List<Method> getMethods(final Class<?> clazz) { return Arrays.asList(clazz.getMethods()); } /** * Changing the value of a given field. * * @param object * -- target object of injection * @param clazz * -- type of argument object * @param fieldName * -- name of field whose value is to be set * @param value * -- object that is injected */ public static void setFieldValue(final Object object, final Class<?> clazz, final String fieldName, final Object value) throws NoSuchFieldException { final Field field = clazz.getDeclaredField(fieldName); try { setField(field, object, value); } catch (final Exception e) { LOG.error(e.getMessage(), e); } } /** * Changing the value of a given field. * * @param object * -- target object of injection * @param fieldName * -- name of field whose value is to be set * @param value * -- object that is injected */ public static void setFieldValue(final Object object, final String fieldName, final Object value) { final boolean success = new DerivedClassIterator(object.getClass()) { @Override protected boolean handleClass(final Class<?> clazz) { try { setFieldValue(object, clazz, fieldName, value); return true; } catch (final NoSuchFieldException e) { LOG.debug("could not set field " + fieldName + " value " + value, e); } return false; } }.iterate(); if (!success) { LOG.warn("could not set field " + fieldName + " value " + value); } } /** * Get the value of a given field on a given object via reflection. * * @param object * -- target object of field access * @param clazz * -- type of argument object * @param fieldName * -- name of the field * @return -- the value of the represented field in object; primitive values * are wrapped in an appropriate object before being returned */ public static Object getFieldValue(final Object object, final Class<?> clazz, final String fieldName) { try { final Field field = clazz.getDeclaredField(fieldName); return getFieldValue(object, field); } catch (final Exception e) { throw new IllegalArgumentException("Could not get field value: " + fieldName, e); } } /** * Get the value of a given field on a given object via reflection. * * @param object * -- target object of field access * @param field * -- target field * @return -- the value of the represented field in object; primitive values * are wrapped in an appropriate object before being returned */ public static Object getFieldValue(final Object object, final Field field) { try { if (!field.isAccessible()) { field.setAccessible(true); } return field.get(object); } catch (final Exception e) { throw new IllegalArgumentException("Could not get field value: " + field, e); } } /** * Get the value of a given field on a given object via reflection. * * @param object * -- target object of field access * @param fieldName * -- name of the field * @return -- the value of the represented field in object; primitive values * are wrapped in an appropriate object before being returned */ public static Object getFieldValue(final Object object, final String fieldName) { return getFieldValue(object, object.getClass(), fieldName); } /** * Invoke a given method with given arguments on a given object via * reflection. * * @param object * -- target object of invocation * @param clazz * -- type of argument object * @param methodName * -- name of method to be invoked * @param arguments * -- arguments for method invocation * @return -- method object to which invocation is actually dispatched * @throws Exception * - operation exception */ public static Object invokeMethod(final Object object, final Class<?> clazz, final String methodName, final Object... arguments) throws Exception { Class<?> superClazz = clazz; while (superClazz != null) { for (final Method declaredMethod : superClazz.getDeclaredMethods()) { if (declaredMethod.getName().equals(methodName)) { final Class<?>[] parameterTypes = declaredMethod.getParameterTypes(); if (parameterTypes.length == arguments.length && checkArguments(parameterTypes, arguments)) { return invokeMethod(declaredMethod, object, arguments); } } } superClazz = superClazz.getSuperclass(); } throw new IllegalArgumentException("Method " + methodName + ":" + Arrays.toString(arguments) + " not found"); } public static Object invokeMethod(final Method method, final Object instance, final Object... arguments) throws Exception { try { if (!method.isAccessible()) { method.setAccessible(true); } return method.invoke(instance, arguments); } catch (final Exception exc) { LOG.warn("Error invoking method: " + method.getName(), exc); final Throwable cause = exc.getCause(); if (cause instanceof Exception) { throw (Exception) cause; } throw exc; } } public static Method getMethod(final Class<?> clazz, final String methodName, final Class<?>... parameterTypes) throws NoSuchMethodException { final List<Method> result = new ArrayList<Method>(); new DerivedClassIterator(clazz) { @Override protected boolean handleClass(final Class<?> clazz) { try { result.add(clazz.getDeclaredMethod(methodName, parameterTypes)); return true; } catch (final Exception e) { // do nothing } return false; } }.iterate(); if (result.isEmpty()) { throw new NoSuchMethodException(methodName); } return result.get(0); } private static boolean checkArguments(final Class<?>[] parameterTypes, final Object[] arguments) { boolean match = true; for (int i = 0; i < arguments.length; i++) { final Class<?> parameterClass = parameterTypes[i]; final Class<?> argumentClass = arguments[i].getClass(); if (!parameterClass.isAssignableFrom(argumentClass) && !checkPrimativeArguments(parameterClass, argumentClass)) { match = false; } } return match; } private static boolean checkPrimativeArguments(final Class<?> parameterClass, final Class<?> argumentClass) { boolean result = false; for (final Entry<Class<?>, Class<?>> entry : PRIMITIVES.entrySet()) { result = result || (parameterClass == entry.getKey()) && (argumentClass == entry.getValue()); } return result; } /** * Invoke a given method with given arguments on a given object via * reflection. * * @param object * -- target object of invocation * @param methodName * -- name of method to be invoked * @param arguments * -- arguments for method invocation * @return -- method object to which invocation is actually dispatched * @throws Exception */ public static Object invokeMethod(final Object object, final String methodName, final Object... arguments) throws Exception { return invokeMethod(object, object.getClass(), methodName, arguments); } public static Set<Class<?>> getClasses(final String... classNames) { final Set<Class<?>> classes = new HashSet<Class<?>>(); for (final String className : classNames) { final Class<?> classObject = forName(className); if (classObject != null) { classes.add(classObject); } } return classes; } /** * Returns the <code>Class</code> object associated with the class or * interface with the given string name. * * @param className * the fully qualified name of the desired class. * @return <code>Class</code> or null */ public static Class<?> forName(final String className) { try { return Class.forName(className); } catch (final ClassNotFoundException e) { return null; } } public static void setField(final Field field, final Object target, final Object value) throws Exception { if (!field.isAccessible()) { field.setAccessible(true); } field.set(target, value); } public static void setField(final String fieldName, final Object target, final Object value) throws Exception { final Field field = ReflectionUtil.getField(target.getClass(), fieldName); if (!field.isAccessible()) { field.setAccessible(true); } field.set(target, value); } public static Field getField(final Class<?> clazz, final String fieldName) { Field field = getFieldByName(clazz, fieldName); Class<?> superClazz = clazz.getSuperclass(); while (superClazz != null && field == null) { field = getFieldByName(superClazz, fieldName); superClazz = superClazz.getSuperclass(); } return field; } private static Field getFieldByName(final Class<?> clazz, final String fieldName) { try { return clazz.getDeclaredField(fieldName); } catch (final NoSuchFieldException e) { LOG.warn(format("No such field: '%s#%s'", clazz.getCanonicalName(), fieldName)); return null; } } public static <T> T createInstance(final Class<T> clazz, final Object... parameter) throws Exception { final Class<?>[] parameterTypes = new Class<?>[parameter.length]; for (int i = 0; i < parameter.length; i++) { parameterTypes[i] = parameter[i].getClass(); } final Constructor<T> constructor = clazz.getConstructor(parameterTypes); return constructor.newInstance(parameter); } /** * * @param type * - base class * @param className * - fully qualified class name * @return class object * @throws ClassNotFoundException */ @SuppressWarnings("unchecked") public static <T> Class<T> lookupClass(final Class<T> type, final String className) throws ClassNotFoundException { return (Class<T>) Class.forName(className); } }