/******************************************************************************* * Copyright (c) 2010 Bruno Medeiros and other Contributors. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Bruno Medeiros - initial API and implementation *******************************************************************************/ package melnorme.utilbox.misc; import static melnorme.utilbox.core.Assert.AssertNamespace.assertNotNull; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; /** * Miscellaneous very simple utility methods for using reflection. * Some of these methods have very low type safety, and as such may not be suitable for production code. */ public class ReflectionUtils { /** Creates a new instance of class with given klassName. * Returns null if the instance could not be created */ public static Object newInstanceSafe(final String klassName){ final Class<?> klass = ReflectionUtils.loadClassSafe(klassName); return klass == null ? null : ReflectionUtils.newInstanceSafe(klass); } /** Creates a new instance of given klass. * Returns null if the instance could not be created. */ public static Object newInstanceSafe(final Class<?> klass) { try { return klass.newInstance(); } catch (InstantiationException e) { return null; } catch (IllegalAccessException e) { return null; } } /** Loads a class with given klassName. * Returns null if the class could not be loaded. */ public static Class<?> loadClassSafe(final String klassName) { try { return Class.forName(klassName); } catch (ClassNotFoundException e) { return null; } } /* ---------------------------------------------------------------- */ /** Same as {@link Class#getMethod(String, Class...)} but unchecks the exceptions. */ public static Method uncheckedGetMethod(Class<?> klass, String methodName, Class<?>... parameterTypes) { try { return klass.getMethod(methodName, parameterTypes); } catch (NoSuchMethodException e) { throw melnorme.utilbox.core.ExceptionAdapter.unchecked(e); } catch (SecurityException e) { throw melnorme.utilbox.core.ExceptionAdapter.unchecked(e); } } /** Same as {@link Method#invoke(Object, Object...)} but unchecks the exceptions. */ public static <T> T uncheckedInvoke(Object obj, Method method, Object... args) { try { @SuppressWarnings("unchecked") T result = (T) method.invoke(obj, args); return result; } catch (IllegalArgumentException e) { throw melnorme.utilbox.core.ExceptionAdapter.unchecked(e); } catch (IllegalAccessException e) { throw melnorme.utilbox.core.ExceptionAdapter.unchecked(e); } catch (InvocationTargetException e) { throw melnorme.utilbox.core.ExceptionAdapter.unchecked(e.getTargetException()); } } /** Invoke method with given methodName on given obj receiver, using given args */ public static <T> T invokeMethod(Object obj, String methodName, Object... args) { try { Class<?>[] paramTypes = new Class<?>[args.length]; for(int i = 0; i < paramTypes.length; i++) { paramTypes[i] = args[i].getClass(); } Method method = getAvailableMethod(obj.getClass(), methodName, paramTypes); @SuppressWarnings("unchecked") T result = (T) method.invoke(obj, args); return result; } catch (IllegalArgumentException e) { throw melnorme.utilbox.core.ExceptionAdapter.unchecked(e); } catch (IllegalAccessException e) { throw melnorme.utilbox.core.ExceptionAdapter.unchecked(e); } catch (InvocationTargetException e) { throw melnorme.utilbox.core.ExceptionAdapter.unchecked(e.getTargetException()); } } /** Reads the method with given methodName and given parameterTypes in given klass. */ public static Method getAvailableMethod(Class<?> klass, String methodName, Class<?>... paramTypes) { try { return klass.getMethod(methodName, paramTypes); } catch (NoSuchMethodException e) { return getDeclaredMethodInHierarchy(klass, methodName, paramTypes); } } private static Method getDeclaredMethodInHierarchy(Class<?> klass, String methodName, Class<?>... paramTypes) { try { Method field = klass.getDeclaredMethod(methodName, paramTypes); field.setAccessible(true); return field; } catch (NoSuchMethodException e) { klass = klass.getSuperclass(); if(klass == null) { return null; } else { return getAvailableMethod(klass, methodName); } } } /* ---------------------------------------------------------------- */ /** Reads the field with given fieldName in given object. */ @SuppressWarnings("unchecked") public static <R> R readField(Object object, String fieldName) throws NoSuchFieldException { return readAvailableField((Class<Object>)object.getClass(), object, fieldName); } /** Reads the static field with given fieldName in given klass. */ public static <T, R> R readStaticField(Class<T> klass, String fieldName) throws NoSuchFieldException { return readAvailableField(klass, null, fieldName); } @SuppressWarnings("unchecked") private static <T, R> R readAvailableField(Class<? super T> klass, T object, String fieldName) throws NoSuchFieldException { Field field = getAvailableField(klass, fieldName); if (field == null) { throw new NoSuchFieldException(); } try { return (R) field.get(object); } catch (IllegalArgumentException e) { throw melnorme.utilbox.core.ExceptionAdapter.unchecked(e); } catch (IllegalAccessException e) { throw melnorme.utilbox.core.ExceptionAdapter.unchecked(e); } } /** Write the field with given fieldName in given object to given value. */ public static <T> void writeField(Object object, String fieldName, Object value) throws NoSuchFieldException, IllegalFieldValue { writeAvailableField(object.getClass(), object, fieldName, value); } /** Write the static field with given fieldName in given klass to given value. */ public static void writeStaticField(Class<?> klass, String fieldName, Object value) throws NoSuchFieldException, IllegalFieldValue { writeAvailableField(klass, null, fieldName, value); } private static <T> void writeAvailableField(Class<?> klass, T object, String fieldName, T value) throws NoSuchFieldException, IllegalFieldValue { Field field = getAvailableField(klass, fieldName); if (field == null) throw new NoSuchFieldException(); try { field.set(object, value); } catch (IllegalArgumentException e) { throw new IllegalFieldValue(e.getMessage()); } catch (IllegalAccessException e) { // Should not happen, because we set field.setAccessible(true); throw melnorme.utilbox.core.ExceptionAdapter.unchecked(e); } } @SuppressWarnings("serial") public static class IllegalFieldValue extends Exception { public IllegalFieldValue(String message) { super(assertNotNull(message)); } @Override public String getMessage() { return super.getMessage(); } } private static Field getAvailableField(Class<?> klass, String fieldName) { try { return klass.getField(fieldName); } catch (NoSuchFieldException e) { return getDeclaredFieldInHierarchy(klass, fieldName); } } private static Field getDeclaredFieldInHierarchy(Class<?> klass, String fieldName) { try { Field field = klass.getDeclaredField(fieldName); field.setAccessible(true); return field; } catch (NoSuchFieldException e) { klass = klass.getSuperclass(); if(klass == null) { return null; } else { return getAvailableField(klass, fieldName); } } } }