/**
* junit-rules: JUnit Rules Library
*
* Copyright (c) 2009-2011 by Alistair A. Israel.
* This software is made available under the terms of the MIT License.
*
* Created Oct 15, 2009
*/
package junit.rules.util;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Iterator;
/**
* <p>
* Provides a set of utility methods (and classes) for reflection.
* </p>
* <p>
* Basically, a clever way to be able to write:
* </p>
*
* <pre>
* Reflection.set(field).of(target).to(value);
* </pre>
*
* @author Alistair A. Israel
* @since 0.3.1
*/
public final class Reflection {
/**
* Utility classes should not have a public or default constructor.
*/
private Reflection() {
// noop
}
/**
* @param field
* the Field to set
* @return {@link FieldWrapper}
*/
public static FieldWrapper set(final Field field) {
return new FieldWrapper(field);
}
/**
* <p>
* Lets us write <code>ReflectionUtils.set(field)</code>.
* </p>
*/
public static class FieldWrapper {
private final Field field;
/**
* @param field
* {@link Field}
*/
public FieldWrapper(final Field field) {
this.field = field;
}
/**
* @param target
* the object whose field we're setting
* @return Setter
*/
public final Setter of(final Object target) {
return new Setter(target);
}
/**
* <p>
* Lets us write <code>ReflectionUtils.set(field).of(target)</code>.
* </p>
*/
public class Setter {
private final Object target;
/**
* @param target
* the object to set the field value for
*/
public Setter(final Object target) {
this.target = target;
}
/**
* <p>
* Let's us write <code>ReflectionUtils.set(field).of(target).to(value)</code> .
* </p>
*
* @param value
* the value to set the field to
*/
public final void to(final Object value) {
quietlySetField(field, target, value);
}
}
}
/**
* @param field
* the field to set
* @param target
* the object whose field we're setting
* @param value
* the value to set it to
*/
public static void quietlySetField(final Field field, final Object target, final Object value) {
try {
final boolean accessible = field.isAccessible();
if (!accessible) {
field.setAccessible(true);
}
field.set(target, value);
if (!accessible) {
field.setAccessible(false);
}
} catch (final IllegalAccessException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
/**
* <p>
* Allows us to cleverly write:
* </p>
*
* <pre>
* Reflection.invoke(method).on(object);
* </pre>
*
* @author Alistair A. Israel
* @since 0.3.1
*/
public static class MethodInvoker {
private final Method method;
/**
* @param method
* the method to invoke
*/
public MethodInvoker(final Method method) {
this.method = method;
}
/**
* @param object
* the ojbect to invoke the method on
* @param params
* any parameters to the method invocation
* @return any return value
*/
public final Object on(final Object object, final Object... params) {
return quietlyInvokeMethod(object, method, params);
}
}
/**
* @param method
* the method to invoke
* @return {@link MethodInvoker}
*/
public static MethodInvoker invoke(final Method method) {
return new MethodInvoker(method);
}
/**
* @param clazz
* the {@link Class}
* @param methodName
* the name of the method we're interested in
* @param parameterTypes
* the parameter types
* @return the {@link Method} found, or {@code null} if no matching method is found
* @since 0.5.1
*/
public static Method quietlyGetMethod(final Class<?> clazz, final String methodName,
final Class<?>... parameterTypes) {
try {
return clazz.getMethod(methodName, parameterTypes);
} catch (final SecurityException e) {
throw new RuntimeException("SecurityException attempting to retrieve method \"" + methodName
+ "\" from class " + clazz.getName(), e);
} catch (final NoSuchMethodException e) {
return null;
}
}
/**
* @param object
* the object to invoke the method on
* @param method
* the method to invoke
* @param params
* any parameters to the method
* @return any return value
*/
public static Object quietlyInvokeMethod(final Object object, final Method method, final Object... params) {
try {
final boolean accessible = method.isAccessible();
if (!accessible) {
method.setAccessible(true);
}
try {
return method.invoke(object, params);
} finally {
if (!accessible) {
method.setAccessible(false);
}
}
} catch (final IllegalArgumentException e) {
throw new RuntimeException(e.getMessage(), e);
} catch (final IllegalAccessException e) {
throw new RuntimeException(e.getMessage(), e);
} catch (final InvocationTargetException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
/**
* @param clazz
* the class
* @return an iterable for the class hierarchy
*/
public static Iterable<Class<?>> iterateClassHierarchy(final Class<?> clazz) {
return new Iterable<Class<?>>() {
@Override
public Iterator<Class<?>> iterator() {
return new ReadOnlyIterator<Class<?>>() {
private Class<?> cl = clazz;
@Override
public boolean hasNext() {
return cl != null && cl != Class.class;
}
@Override
public Class<?> next() {
final Class<?> next = cl;
cl = cl.getSuperclass();
return next;
}
};
}
};
}
}