/*******************************************************************************
* 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);
}
}
}
}