cassava.csv.core.utils.ReflectionUtils Maven / Gradle / Ivy
Show all versions of cassava Show documentation
package cassava.csv.core.utils;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.lang.reflect.UndeclaredThrowableException;
import java.util.ArrayList;
import java.util.List;
/**
* @author Andrew Vella
* @since 16/12/15.
*/
public final class ReflectionUtils {
private ReflectionUtils() {
}
/**
* Make the given field accessible, explicitly setting it accessible if
* necessary. The {@code setAccessible(true)} method is only called
* when actually necessary, to avoid unnecessary conflicts with a JVM
* SecurityManager (if active).
*
* @param field the field to make accessible
* @see java.lang.reflect.Field#setAccessible
*/
public static void makeAccessible(Field field) {
if ((!Modifier.isPublic(field.getModifiers()) ||
!Modifier.isPublic(field.getDeclaringClass().getModifiers()) ||
Modifier.isFinal(field.getModifiers())) && !field.isAccessible()) {
field.setAccessible(true);
}
}
/**
* Set the field represented by the supplied {@link Field field object} on the
* specified {@link Object target object} to the specified {@code value}.
* In accordance with {@link Field#set(Object, Object)} semantics, the new value
* is automatically unwrapped if the underlying field has a primitive type.
* Thrown exceptions are handled via a call to {@link #handleReflectionException(Exception)}.
*
* @param field the field to set
* @param target the target object on which to set the field
* @param value the value to set (may be {@code null})
*/
public static void setField(Field field, Object target, Object value) {
try {
field.set(target, value);
} catch (IllegalAccessException ex) {
handleReflectionException(ex);
throw new IllegalStateException(
"Unexpected reflection exception - " + ex.getClass().getName() + ": " + ex.getMessage());
}
}
/**
* Handle the given invocation target exception. Should only be called if no
* checked exception is expected to be thrown by the target method.
*
Throws the underlying RuntimeException or Error in case of such a root
* cause. Throws an IllegalStateException else.
*
* @param ex the invocation target exception to handle
*/
public static void handleInvocationTargetException(InvocationTargetException ex) {
rethrowRuntimeException(ex.getTargetException());
}
/**
* Rethrow the given {@link Throwable exception}, which is presumably the
* target exception of an {@link InvocationTargetException}.
* Should only be called if no checked exception is expected to be thrown
* by the target method.
*
Rethrows the underlying exception cast to an {@link RuntimeException} or
* {@link Error} if appropriate; otherwise, throws an {@link IllegalStateException}.
*
* @param ex the exception to rethrow
* @throws RuntimeException the rethrown exception
*/
public static void rethrowRuntimeException(Throwable ex) {
if (ex instanceof RuntimeException) {
throw (RuntimeException) ex;
}
if (ex instanceof Error) {
throw (Error) ex;
}
throw new UndeclaredThrowableException(ex);
}
/**
* Handle the given reflection exception. Should only be called if no
* checked exception is expected to be thrown by the target method.
*
Throws the underlying RuntimeException or Error in case of an
* InvocationTargetException with such a root cause. Throws an
* IllegalStateException with an appropriate message else.
*
* @param ex the reflection exception to handle
*/
public static void handleReflectionException(Exception ex) {
if (ex instanceof NoSuchMethodException) {
throw new IllegalStateException("Method not found: " + ex.getMessage());
}
if (ex instanceof IllegalAccessException) {
throw new IllegalStateException("Could not access method: " + ex.getMessage());
}
if (ex instanceof InvocationTargetException) {
handleInvocationTargetException((InvocationTargetException) ex);
}
if (ex instanceof RuntimeException) {
throw (RuntimeException) ex;
}
throw new UndeclaredThrowableException(ex);
}
/**
* Gets all fields of the given class and its parents (if any) that are annotated with the given annotation.
*
* @param cls the {@link Class} to query
* @param annotationCls the {@link Annotation} that must be present on a field to be matched
* @return a list of Fields (possibly empty).
* @throws IllegalArgumentException if the class or annotation are {@code null}
* @since 3.4
*/
public static List getFieldsListWithAnnotation(final Class> cls, final Class extends Annotation> annotationCls) {
final List allFields = getAllFieldsList(cls);
final List annotatedFields = new ArrayList<>();
for (final Field field : allFields) {
if (field.getAnnotation(annotationCls) != null) {
annotatedFields.add(field);
}
}
return annotatedFields;
}
/**
* Gets all fields of the given class and its parents (if any).
*
* @param cls the {@link Class} to query
* @return an array of Fields (possibly empty).
* @throws IllegalArgumentException if the class is {@code null}
* @since 3.2
*/
public static List getAllFieldsList(final Class> cls) {
final List allFields = new ArrayList<>();
Class> currentClass = cls;
while (currentClass != null) {
final Field[] declaredFields = currentClass.getDeclaredFields();
for (final Field field : declaredFields) {
allFields.add(field);
}
currentClass = currentClass.getSuperclass();
}
return allFields;
}
}