src/test/java/fr/xephi/authme/ReflectionTestUtils.java
package fr.xephi.authme;
import ch.jalu.injector.handlers.postconstruct.PostConstructMethodInvoker;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import static java.lang.String.format;
/**
* Offers reflection functionality to set up tests. Use only when absolutely necessary.
*/
public final class ReflectionTestUtils {
private ReflectionTestUtils() {
// Util class
}
/**
* Sets the field of a given object to a new value with reflection.
*
* @param clazz the class declaring the field
* @param instance the instance to modify (pass null for static fields)
* @param fieldName the field name
* @param value the value to set the field to
* @param <T> the instance type
*/
public static <T> void setField(Class<? super T> clazz, T instance, String fieldName, Object value) {
try {
Field field = getField(clazz, fieldName);
field.set(instance, value);
} catch (IllegalAccessException e) {
throw new IllegalStateException(
format("Could not set value to field '%s' for instance '%s' of class '%s'",
fieldName, instance, clazz.getName()), e);
}
}
/**
* Sets the field on the given instance to the new value.
*
* @param instance the instance to modify
* @param fieldName the field name
* @param value the value to set the field to
*/
@SuppressWarnings("unchecked")
public static void setField(Object instance, String fieldName, Object value) {
setField((Class) instance.getClass(), instance, fieldName, value);
}
private static <T> Field getField(Class<T> clazz, String fieldName) {
try {
Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
return field;
} catch (NoSuchFieldException e) {
throw new IllegalStateException(format("Could not get field '%s' from class '%s'",
fieldName, clazz.getName()), e);
}
}
@SuppressWarnings("unchecked")
public static <T, V> V getFieldValue(Class<T> clazz, T instance, String fieldName) {
Field field = getField(clazz, fieldName);
return getFieldValue(field, instance);
}
/**
* Returns the value of the field on the given instance. Wraps exceptions into a runtime exception.
*
* @param field the field to read
* @param instance the instance to get the value from, null if field is static
* @param <V> type of the field
* @return value of the field
*/
@SuppressWarnings("unchecked")
public static <V> V getFieldValue(Field field, Object instance) {
field.setAccessible(true);
try {
return (V) field.get(instance);
} catch (IllegalAccessException e) {
throw new IllegalStateException("Could not get value of field '" + field.getName() + "'", e);
}
}
/**
* Returns the method on the given class with the supplied parameter types.
*
* @param clazz the class to retrieve a method from
* @param methodName the name of the method
* @param parameterTypes the parameter types the method to retrieve has
* @return the method of the class, set to be accessible
*/
public static Method getMethod(Class<?> clazz, String methodName, Class<?>... parameterTypes) {
try {
Method method = clazz.getDeclaredMethod(methodName, parameterTypes);
method.setAccessible(true);
return method;
} catch (NoSuchMethodException e) {
throw new IllegalStateException("Could not retrieve method '" + methodName + "' from class '"
+ clazz.getName() + "'");
}
}
/**
* Invokes the given method on the provided instance with the given parameters.
*
* @param method the method to invoke
* @param instance the instance to invoke the method on (null for static methods)
* @param parameters the parameters to pass to the method
* @param <V> return value of the method
* @return method return value
*/
@SuppressWarnings("unchecked")
public static <V> V invokeMethod(Method method, Object instance, Object... parameters) {
method.setAccessible(true);
try {
return (V) method.invoke(instance, parameters);
} catch (InvocationTargetException | IllegalAccessException e) {
throw new IllegalStateException("Could not invoke method '" + method + "'", e);
}
}
/**
* Runs all methods annotated with {@link javax.annotation.PostConstruct} on the given instance
* (including such methods on superclasses).
*
* @param instance the instance to process
*/
public static void invokePostConstructMethods(Object instance) {
// Use the implementation of the injector to invoke all @PostConstruct methods the same way
new PostConstructMethodInvoker().postProcess(instance, null, null);
}
/**
* Creates a new instance of the given class, using a no-args constructor (which may be hidden).
*
* @param clazz the class to instantiate
* @param <T> the class' type
* @return the created instance
*/
public static <T> T newInstance(Class<T> clazz) {
try {
Constructor<T> constructor = clazz.getDeclaredConstructor();
constructor.setAccessible(true);
return constructor.newInstance();
} catch (ReflectiveOperationException e) {
throw new IllegalStateException("Could not invoke no-args constructor of class " + clazz, e);
}
}
}