package org.robobinding; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.Arrays; import org.robobinding.util.ClassUtils; /** * @since 1.0 * @author Cheng Wei * */ class ReflectionUtils { public static <T> void setField(Object target, String fieldName, Class<T> fieldType, T value) { Field field = findField(target.getClass(), fieldName, fieldType); if(field == null) { throw new RuntimeException("unknown given field '"+fieldName+"'"); } makeAccessible(field); try { field.set(target, value); } catch (IllegalArgumentException e) { throw new RuntimeException(e); } catch (IllegalAccessException e) { throw new RuntimeException(e); } } public static Field findField(Class<?> clazz, String name, Class<?> type) { Class<?> searchType = clazz; while (!Object.class.equals(searchType) && searchType != null) { Field[] fields = searchType.getDeclaredFields(); for (Field field : fields) { if ((name == null || name.equals(field.getName())) && (type == null || type.equals(field.getType()))) { return field; } } searchType = searchType.getSuperclass(); } return null; } private static void makeAccessible(Field field) { if ((!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers()) || Modifier.isFinal(field.getModifiers())) && !field.isAccessible()) { field.setAccessible(true); } } @SuppressWarnings("unchecked") public static <T> T getField(Object target, String fieldName, Class<T> fieldType) { Field field = findField(target.getClass(), fieldName, fieldType); if(field == null) { throw new RuntimeException("unknown given field '"+fieldName+"'"); } makeAccessible(field); try { return (T)field.get(target); } catch (IllegalAccessException ex) { throw new IllegalStateException( "Unexpected reflection exception - " + ex.getClass().getName() + ": " + ex.getMessage(), ex); } } @SuppressWarnings("unchecked") public static <T> T tryToGetCompatibleField(Object target, String fieldName, Class<T> compatibleFieldType) { Field field = findCompatibleField(target.getClass(), fieldName, compatibleFieldType); if(field == null) { return null; } makeAccessible(field); try { return (T)field.get(target); } catch (IllegalAccessException ex) { throw new IllegalStateException( "Unexpected reflection exception - " + ex.getClass().getName() + ": " + ex.getMessage(), ex); } } private static Field findCompatibleField(Class<?> clazz, String name, Class<?> compatibleType) { Class<?> searchType = clazz; while (!Object.class.equals(searchType) && searchType != null) { Field[] fields = searchType.getDeclaredFields(); for (Field field : fields) { if ((name == null || name.equals(field.getName())) && (compatibleType == null || compatibleType.isAssignableFrom(field.getType()))) { return field; } } searchType = searchType.getSuperclass(); } return null; } @SuppressWarnings("unchecked") public static <T> T tryToInvokeMethod(Object target, String name, Object... args) { final Class<?>[] parameterTypes = ClassUtils.toClass(args); Method method = findMethod(target.getClass(), name, parameterTypes); if(method != null) { makeAccessible(method); try { return (T)method.invoke(target, args); } catch (IllegalAccessException e) { throw new RuntimeException(e); } catch (InvocationTargetException e) { throw new RuntimeException(e); } } return null; } private static Method findMethod(Class<?> clazz, String name, Class<?>... paramTypes) { Class<?> searchType = clazz; while (searchType != null) { Method[] methods = (searchType.isInterface() ? searchType.getMethods() : searchType.getDeclaredMethods()); for (Method method : methods) { if (name.equals(method.getName()) && (paramTypes == null || Arrays.equals(paramTypes, method.getParameterTypes()))) { return method; } } searchType = searchType.getSuperclass(); } return null; } private static void makeAccessible(Method method) { if ((!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers())) && !method.isAccessible()) { method.setAccessible(true); } } }