From 0f196f59c6a4cb76ab8409da62ff1f35505f94a8 Mon Sep 17 00:00:00 2001 From: Dico Karssiens Date: Sun, 11 Nov 2018 14:06:45 +0000 Subject: Changes I made before breaking my local repository. Hoping this works. --- .../src/main/java/io/dico/dicore/Reflection.java | 1560 ++++++++++---------- 1 file changed, 780 insertions(+), 780 deletions(-) (limited to 'dicore3/core/src/main/java/io/dico/dicore/Reflection.java') diff --git a/dicore3/core/src/main/java/io/dico/dicore/Reflection.java b/dicore3/core/src/main/java/io/dico/dicore/Reflection.java index b7b1ed4..2452d75 100644 --- a/dicore3/core/src/main/java/io/dico/dicore/Reflection.java +++ b/dicore3/core/src/main/java/io/dico/dicore/Reflection.java @@ -1,780 +1,780 @@ -package io.dico.dicore; - -import io.dico.dicore.exceptions.ExceptionHandler; - -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.util.Objects; -import java.util.function.Consumer; - -/** - * Reflective utilities - */ -@SuppressWarnings("unchecked") -public class Reflection { - private static final ExceptionHandler exceptionHandler; - private static final Field fieldModifiersField = restrictedSearchField(Field.class, "modifiers"); - private static Consumer errorTarget; - - private Reflection() { - - } - - static { - exceptionHandler = new ExceptionHandler() { - @Override - public void handle(Throwable ex) { - handleGenericException(ex); - } - - @Override - public Object handleGenericException(Throwable ex, Object... args) { - String action = args.length == 0 || !(args[0] instanceof String) ? "executing a reflective operation" : (String) args[0]; - ExceptionHandler.log(errorTarget, action, ex); - return null; - } - }; - - // don't use method reference here: the current reference in System.out would be cached. - setErrorTarget(msg -> System.out.println(msg)); - } - - /** - * Sets the output where ReflectiveOperationException's and similar are sent. - * This defaults to {@link System#out}. - * - * @param target The new output - * @throws NullPointerException if target is null - */ - public static void setErrorTarget(Consumer target) { - errorTarget = Objects.requireNonNull(target); - } - - /** - * This search modifier tells the implementation that it should subsequently search superclasses for the field/method. - * Using this modifier means a call to {@link #deepSearchField(Class, String)} will be used instead of {@link #restrictedSearchField(Class, String)} - * and a call to {@link #deepSearchMethod(Class, String, Class[])} will be used instead of {@link #restrictedSearchMethod(Class, String, Class[])} - */ - public static final int DEEP_SEARCH = 0x1; - - /** - * This search modifier applies only to fields, and tells the implementation that a final modifier might be present on a found field, and that it should be removed. - */ - public static final int REMOVE_FINAL = 0x2; - - /** - * This search modifier applies only to methods, and tells the implementation that it should completely ignore parameter types and return the first method with a matching name - * The implementation uses {@link Class#getDeclaredMethods()} instead of {@link Class#getDeclaredMethod(String, Class[])} if this modifier is set. - */ - public static final int IGNORE_PARAMS = 0x2; - - /* - ### FIELD METHODS ### - */ - - /** - * Search a field of any accessibility within the class or any of its superclasses. - * The first field with the given name that is found will be returned. - *

- * If a field is found and it is not accessible, this method attempts to make it accessible. - * If a {@link SecurityException} is thrown in the process, that is ignored and the field will be returned nonetheless. - *

- * This method throws IllegalArgumentException if the Field is not found, because, in most cases, that should never happen, - * and it should simplify debugging. In some cases, if you want to know if the field exists, you'll have to use try/catch for that. - * - * @param clazz The lowest class in the ladder to start searching from - * @param fieldName The name of the field - * //@param fieldType the type of the field, or null if it can be any. - * @return The field - * @throws NullPointerException if clazz is null or fieldName is null - * @throws IllegalArgumentException if the field doesn't exist - * @see #restrictedSearchField(Class, String) - */ - public static Field deepSearchField(Class clazz, String fieldName/*, Class fieldType*/) { - Class currentClass = clazz; - Field result; - do { - // throws NPE if class or fieldName is null - result = internalSearchField(clazz, fieldName); - if (result != null) { - return result; - } - currentClass = currentClass.getSuperclass(); - } while (currentClass != null); - - throw new IllegalArgumentException("field not found in " + clazz.getCanonicalName() + " and superclasses: " + fieldName); - } - - /** - * Search a field of any accessibility within the class, but not its superclasses. - *

- * If a field is found and it is not accessible, this method attempts to make it accessible. - * If a {@link SecurityException} is thrown in the process, that is ignored and the field will be returned nonetheless. - *

- * This method throws IllegalArgumentException if the Field is not found, because, in most cases, that should never happen, - * and it should simplify debugging. In some cases, if you want to know if the field exists, you'll have to use try/catch for that. - * - * @param clazz The only class to search for the field - * @param fieldName The name of the field - * @return The field - * @throws NullPointerException if clazz or fieldName is null - * @throws IllegalArgumentException if the field does not exist - */ - public static Field restrictedSearchField(Class clazz, String fieldName) { - Field result = internalSearchField(clazz, fieldName); - if (result == null) { - throw new IllegalArgumentException("field not found in " + clazz.getCanonicalName() + ": " + fieldName); - } - return result; - } - - /** - * Searches for a field using the given search method. - * - * @param modifiers The modifiers for field search. Can have {@link #DEEP_SEARCH} and {@link #REMOVE_FINAL} - * @param clazz The class to search in/from - * @param fieldName Name of the field - * @return The field - * @throws NullPointerException if clazz or fieldName is null - * @throws IllegalArgumentException if the field is not found - */ - public static Field searchField(int modifiers, Class clazz, String fieldName) { - Field result; - if ((modifiers & DEEP_SEARCH) != 0) { - result = deepSearchField(clazz, fieldName); - } else { - result = restrictedSearchField(clazz, fieldName); - } - if ((modifiers & REMOVE_FINAL) != 0) { - removeFinalModifier(result); - } - return result; - } - - /** - * @return The same as {@link #restrictedSearchField(Class, String)}, but returns null instead of throwing IllegalArgumentException - * @see #restrictedSearchField(Class, String) - */ - private static Field internalSearchField(Class clazz, String fieldName) { - Field result; - try { - // throws NullPointerException if either clazz or fieldName are null. - result = clazz.getDeclaredField(fieldName); - } catch (NoSuchFieldException | SecurityException ex) { - return null; - } - - if (!result.isAccessible()) try { - result.setAccessible(true); - } catch (SecurityException ignored) { - - } - - return result; - } - - /** - * Attempts to remove existing final modifier of the given field - * This method should always return true. - * - * @param field The field whose final modifier to remove - * @return true if the field most definitely has no final modifier after this call - * @throws NullPointerException if field is null - */ - public static boolean removeFinalModifier(Field field) { - Objects.requireNonNull(field); - try { - int modifiers = (int) fieldModifiersField.get(field); - if (modifiers != (modifiers &= ~Modifier.FINAL)) { - fieldModifiersField.set(field, modifiers); - } - return true; - } catch (Exception ex) { - return false; - } - } - - /** - * Gets field value of the field named fieldName and the given instance - * To find the field, {@link #deepSearchField(Class, String)} is used (DEEP search method). - *

- * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} - * - * @param instance The instance whose field value to get - * @param fieldName the name of the field - * @param The expected/known field type - * @return The field value - * @throws IllegalArgumentException if the field doesn't exist - * @see #deepSearchField(Class, String) - * @see #getFieldValue(Class, String, Object) - */ - public static T getFieldValue(Object instance, String fieldName) { - return getFieldValue(deepSearchField(instance.getClass(), fieldName), instance); - } - - /** - * Gets field value of the field named fieldName and the given instance - * To find the field, {@link #restrictedSearchField(Class, String)} is used (RESTRICTED search method). - *

- * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} - * - * @param clazz The class to search for the field - * @param instance The instance whose field value to get - * @param fieldName the name of the field - * @param The expected/known field type - * @return The field value - * @throws IllegalArgumentException if the field doesn't exist - * @see #restrictedSearchField(Class, String) - * @see #getFieldValue(Field, Object) - */ - public static T getFieldValue(Class clazz, String fieldName, Object instance) { - return getFieldValue(restrictedSearchField(clazz, fieldName), instance); - } - - /** - * Gets field value of the field named fieldName and the given instance - * To find the field, {@link #searchField(int, Class, String)} is used. - *

- * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} - * - * @param modifiers The modifiers for field search. Can have {@link #DEEP_SEARCH} and {@link #REMOVE_FINAL} - * @param clazz The class to search for the field - * @param instance The instance whose field value to get - * @param fieldName the name of the field - * @param The expected/known field type - * @return The field value - * @throws IllegalArgumentException if the field doesn't exist - * @see #searchField(int, Class, String) - * @see #getFieldValue(Field, Object) - */ - public static T getFieldValue(int modifiers, Class clazz, String fieldName, Object instance) { - return getFieldValue(searchField(modifiers, clazz, fieldName), instance); - } - - /** - * Gets field value of the given field and the given instance - *

- * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} - * - * @param field the field - * @param instance The instance whose field value to get - * @param The expected/known field type - * @return The field value - */ - public static T getFieldValue(Field field, Object instance) { - return exceptionHandler.supplySafe(() -> (T) field.get(instance)); - } - - /** - * Gets static field value of the field named fieldName - * To find the field, {@link #restrictedSearchField(Class, String)} is used (RESTRICTED search method). - *

- * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} - * - * @param clazz The class to search for the field - * @param fieldName the name of the field - * @param The expected/known field type - * @return The field value - * @throws IllegalArgumentException if the field doesn't exist - * @see #restrictedSearchField(Class, String) - * @see #getStaticFieldValue(Field) - */ - public static T getStaticFieldValue(Class clazz, String fieldName) { - return getStaticFieldValue(restrictedSearchField(clazz, fieldName)); - } - - /** - * Gets static field value of the field named fieldName - * To find the field, {@link #searchField(int, Class, String)} is used. - *

- * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} - * - * @param modifiers The modifiers for field search. Can have {@link #DEEP_SEARCH} and {@link #REMOVE_FINAL} - * @param clazz The class to search for the field - * @param fieldName the name of the field - * @param The expected/known field type - * @return The field value - * @throws IllegalArgumentException if the field doesn't exist - * @see #deepSearchField(Class, String) - * @see #getStaticFieldValue(Field) - */ - public static T getStaticFieldValue(int modifiers, Class clazz, String fieldName) { - return getStaticFieldValue(searchField(modifiers, clazz, fieldName)); - } - - /** - * Gets static field value - *

- * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} - *

- * Equivalent to the call {@code getFieldValue(field, (Object) null)} - * - * @param field the field - * @param The expected/known field type - * @return The field value - * @see #getFieldValue(Field, Object) - */ - public static T getStaticFieldValue(Field field) { - return getFieldValue(field, (Object) null); - } - - /** - * Sets field value of the field named fieldName and the given instance - * To find the field, {@link #deepSearchField(Class, String)} is used (DEEP search method). - *

- * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} - * - * @param instance The instance whose field value to set - * @param fieldName the name of the field - * @param newValue the new field value - * @throws IllegalArgumentException if the field doesn't exist - * @see #deepSearchField(Class, String) - * @see #setFieldValue(Class, String, Object, Object) - */ - public static void setFieldValue(Object instance, String fieldName, Object newValue) { - setFieldValue(deepSearchField(instance.getClass(), fieldName), instance, newValue); - } - - /** - * Sets field value of the field named fieldName and the given instance - * To find the field, {@link #restrictedSearchField(Class, String)} is used (RESTRICTED search method). - *

- * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} - * - * @param clazz The class to search for the field - * @param fieldName the name of the field - * @param instance The field owner - * @param newValue The new field value - * @throws IllegalArgumentException if the field doesn't exist - * @see #restrictedSearchField(Class, String) - * @see #setFieldValue(Field, Object, Object) - */ - public static void setFieldValue(Class clazz, String fieldName, Object instance, Object newValue) { - setFieldValue(restrictedSearchField(clazz, fieldName), instance, newValue); - } - - /** - * Sets field value of the field named fieldName and the given instance - * To find the field, {@link #searchField(int, Class, String)} is used. - *

- * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} - * - * @param modifiers The modifiers for field search. Can have {@link #DEEP_SEARCH} and {@link #REMOVE_FINAL} - * @param clazz The class to search for the field - * @param instance The instance whose field value to set - * @param fieldName the name of the field - * @param newValue The new field value - * @throws IllegalArgumentException if the field doesn't exist - * @see #searchField(int, Class, String) - * @see #setFieldValue(Field, Object, Object) - */ - public static void setFieldValue(int modifiers, Class clazz, String fieldName, Object instance, Object newValue) { - setFieldValue(searchField(modifiers, clazz, fieldName), instance, newValue); - } - - /** - * Sets a field value - *

- * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} - * - * @param field The field - * @param instance The field owner - * @param newValue The new field value - */ - public static void setFieldValue(Field field, Object instance, Object newValue) { - exceptionHandler.runSafe(() -> field.set(instance, newValue)); - } - - /** - * Sets static field value of the field name fieldName - * To find the field, {@link #restrictedSearchField(Class, String)} is used (RESTRICTED search method). - *

- * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} - * - * @param clazz The class to search for the field - * @param fieldName the name of the field - * @param newValue The new field value - * @throws IllegalArgumentException if the field doesn't exist - * @see #restrictedSearchField(Class, String) - * @see #setStaticFieldValue(Field, Object) - */ - public static void setStaticFieldValue(Class clazz, String fieldName, Object newValue) { - setStaticFieldValue(restrictedSearchField(clazz, fieldName), newValue); - } - - /** - * Sets static field value of the field named fieldName - * To find the field, {@link #searchField(int, Class, String)} is used. - *

- * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} - * - * @param modifiers The modifiers for field search. Can have {@link #DEEP_SEARCH} and {@link #REMOVE_FINAL} - * @param clazz The class to search for the field - * @param fieldName the name of the field - * @param newValue The new field value - * @throws IllegalArgumentException if the field doesn't exist - * @see #searchField(int, Class, String) - * @see #setStaticFieldValue(Field, Object) - */ - public static void setStaticFieldValue(int modifiers, Class clazz, String fieldName, Object newValue) { - setStaticFieldValue(searchField(modifiers, clazz, fieldName), newValue); - } - - /** - * Sets a static field value - *

- * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} - * - * @param field The field - * @param newValue The new field value - */ - public static void setStaticFieldValue(Field field, Object newValue) { - setFieldValue(field, (Object) null, newValue); - } - - /* - ### METHOD METHODS ### - */ - - /** - * Search a method of any accessibility within the class or any of its superclasses. - * The first method with the given name that is found will be returned. - *

- * If a method is found and it is not accessible, this method attempts to make it accessible. - * If a {@link SecurityException} is thrown in the process, that is ignored and the method will be returned nonetheless. - *

- * This method throws IllegalArgumentException if the Method is not found, because, in most cases, that should never happen, - * and it should simplify debugging. In some cases, if you want to know if the method exists, you'll have to use try/catch for that. - * - * @param clazz The lowest class in the ladder to start searching from - * @param methodName The name of the method - * @param parameterTypes the parameter types of the sought method. - * @return The method - * @throws NullPointerException if clazz is null or methodName is null - * @throws IllegalArgumentException if the method doesn't exist - * @see #restrictedSearchMethod(Class, String, Class[]) - */ - public static Method deepSearchMethod(Class clazz, String methodName, Class... parameterTypes) { - return deepSearchMethod(0, clazz, methodName, parameterTypes); - } - - /** - * Search a method of any accessibility within the class or any of its superclasses. - * The first method with the given name that is found will be returned. - *

- * If a method is found and it is not accessible, this method attempts to make it accessible. - * If a {@link SecurityException} is thrown in the process, that is ignored and the method will be returned nonetheless. - *

- * This method throws IllegalArgumentException if the Method is not found, because, in most cases, that should never happen, - * and it should simplify debugging. In some cases, if you want to know if the method exists, you'll have to use try/catch for that. - * - * @param modifiers The modifiers for method search. Can have {@link #IGNORE_PARAMS} - * @param clazz The lowest class in the ladder to start searching from - * @param methodName The name of the method - * @param parameterTypes the parameter types of the sought method. - * @return The method - * @throws NullPointerException if clazz is null or methodName is null - * @throws IllegalArgumentException if the method doesn't exist - * @see #restrictedSearchMethod(Class, String, Class[]) - */ - public static Method deepSearchMethod(int modifiers, Class clazz, String methodName, Class... parameterTypes) { - Class currentClass = clazz; - Method result; - do { - // throws NPE if class or methodName is null - result = internalSearchMethod(modifiers, currentClass, methodName, parameterTypes); - if (result != null) { - return result; - } - currentClass = currentClass.getSuperclass(); - } while (currentClass != null); - - throw new IllegalArgumentException("method not found in " + clazz.getCanonicalName() + " and superclasses: " + methodName); - } - - /** - * Search a method of any accessibility within the class, but not its superclasses. - *

- * If a method is found and it is not accessible, this method attempts to make it accessible. - * If a {@link SecurityException} is thrown in the process, that is ignored and the method will be returned nonetheless. - *

- * This method throws IllegalArgumentException if the Method is not found, because, in most cases, that should never happen, - * and it should simplify debugging. In some cases, if you want to know if the method exists, you'll have to use try/catch for that. - * - * @param clazz The only class to search for the method - * @param methodName The name of the method - * @param parameterTypes the parameter types of the sought method. - * @return The method - * @throws NullPointerException if clazz or methodName is null - * @throws IllegalArgumentException if the method does not exist - */ - public static Method restrictedSearchMethod(Class clazz, String methodName, Class... parameterTypes) { - return restrictedSearchMethod(0, clazz, methodName, parameterTypes); - } - - /** - * Search a method of any accessibility within the class, but not its superclasses. - *

- * If a method is found and it is not accessible, this method attempts to make it accessible. - * If a {@link SecurityException} is thrown in the process, that is ignored and the method will be returned nonetheless. - *

- * This method throws IllegalArgumentException if the Method is not found, because, in most cases, that should never happen, - * and it should simplify debugging. In some cases, if you want to know if the method exists, you'll have to use try/catch for that. - * - * @param modifiers The modifiers for method search. Can have {@link #IGNORE_PARAMS} - * @param clazz The only class to search for the method - * @param methodName The name of the method - * @param parameterTypes the parameter types of the sought method. - * @return The method - * @throws NullPointerException if clazz or methodName is null - * @throws IllegalArgumentException if the method does not exist - */ - public static Method restrictedSearchMethod(int modifiers, Class clazz, String methodName, Class... parameterTypes) { - Method result = internalSearchMethod(modifiers, clazz, methodName, parameterTypes); - if (result == null) { - throw new IllegalArgumentException("method not found in " + clazz.getCanonicalName() + ": " + methodName); - } - return result; - } - - /** - * Searches for a method using the given search method. - *

- * If a method is found and it is not accessible, this method attempts to make it accessible. - * If a {@link SecurityException} is thrown in the process, that is ignored and the method will be returned nonetheless. - *

- * This method throws IllegalArgumentException if the Method is not found, because, in most cases, that should never happen, - * and it should simplify debugging. In some cases, if you want to know if the method exists, you'll have to use try/catch for that. - * - * @param modifiers The modifiers for method search. Can have {@link #DEEP_SEARCH} and {@link #IGNORE_PARAMS} - * @param clazz The class to search in/from - * @param methodName Name of the method - * @param parameterTypes the parameter types of the sought method. - * @return The method - * @throws NullPointerException if clazz or methodName is null - * @throws IllegalArgumentException if the method is not found - */ - public static Method searchMethod(int modifiers, Class clazz, String methodName, Class... parameterTypes) { - if ((modifiers & DEEP_SEARCH) != 0) { - return deepSearchMethod(modifiers, clazz, methodName, parameterTypes); - } else { - return restrictedSearchMethod(modifiers, clazz, methodName, parameterTypes); - } - } - - /** - * @return The same as {@link #restrictedSearchMethod(Class, String, Class[]) }, but returns null instead of throwing IllegalArgumentException - * @see #restrictedSearchMethod(Class, String, Class[]) - */ - private static Method internalSearchMethod(int modifiers, Class clazz, String methodName, Class... parameterTypes) { - Method result = null; - - if ((modifiers & IGNORE_PARAMS) != 0) { - - // throws NullPointerException if either clazz or methodName are null. - methodName = methodName.intern(); - for (Method method : clazz.getDeclaredMethods()) { - // all method names are interned. Identity comparison is much faster. - if (method.getName() == methodName) { - result = method; - break; - } - } - - if (result == null) { - return null; - } - - } else { - - try { - // throws NullPointerException if either clazz or methodName are null. - result = clazz.getDeclaredMethod(methodName, parameterTypes); - } catch (NoSuchMethodException | SecurityException ex) { - return null; - } - - } - - if (!result.isAccessible()) try { - result.setAccessible(true); - } catch (SecurityException ignored) { - - } - - return result; - } - - /** - * Invokes the method named methodName with the given instance and arguments - * To find the method, {@link #searchMethod(int, Class, String, Class[])} is used with no type parameters, - * modifiers {@link #DEEP_SEARCH} and {@link #IGNORE_PARAMS}, and the class {@link Object#getClass() instance.getClass()} - *

- * To search the method with type parameters, you should search the method using {@link #searchMethod(int, Class, String, Class[])} or similar, - * and call {@link #invokeMethod(Method, Object, Object...)} - *

- * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} - * - * @param methodName Name of the method - * @param instance The instance to invoke the method on - * @param args The arguments to use in the method call - * @param The expected/known method return type - * @return The result of calling the method - * @throws NullPointerException if instance or methodName is null - * @throws IllegalArgumentException if the method is not found - * @see #invokeMethod(Method, Object, Object...) - */ - public static T invokeMethod(Object instance, String methodName, Object... args) { - return invokeMethod(searchMethod(DEEP_SEARCH | IGNORE_PARAMS, instance.getClass(), methodName), instance, args); - } - - /** - * Invokes the method named methodName with the given instance and arguments - * To find the method, {@link #searchMethod(int, Class, String, Class[])} is used with no type parameters, - * as well as the modifier {@link #IGNORE_PARAMS} - *

- * To search the method with type parameters, you should search the method using {@link #searchMethod(int, Class, String, Class[])} or similar, - * and call {@link #invokeMethod(Method, Object, Object...)} - *

- * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} - * - * @param clazz The class to search in/from - * @param methodName Name of the method - * @param instance The instance to invoke the method on - * @param args The arguments to use in the method call - * @param The expected/known method return type - * @return The result of calling the method - * @throws NullPointerException if clazz or methodName is null - * @throws IllegalArgumentException if the method is not found - * @see #invokeMethod(Method, Object, Object...) - */ - public static T invokeMethod(Class clazz, String methodName, Object instance, Object... args) { - return invokeMethod(searchMethod(IGNORE_PARAMS, clazz, methodName), instance, args); - } - - /** - * Invokes the method named methodName with the given instance and arguments - * To find the method, {@link #searchMethod(int, Class, String, Class[])} is used with no type parameters. - * For this search, the result of calling {@link Object#getClass() instance.getClass()} is used. - *

- * To search the method with type parameters, you should search the method using {@link #searchMethod(int, Class, String, Class[])} or similar, - * and call {@link #invokeMethod(Method, Object, Object...)} - *

- * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} - * - * @param modifiers The modifiers for method search. Can have {@link #DEEP_SEARCH} and {@link #IGNORE_PARAMS} - * @param methodName Name of the method - * @param instance The instance to invoke the method on - * @param args The arguments to use in the method call - * @param The expected/known method return type - * @return The result of calling the method - * @throws NullPointerException if instance or methodName is null - * @throws IllegalArgumentException if the method is not found - * @see #invokeMethod(Method, Object, Object...) - */ - public static T invokeMethod(int modifiers, Object instance, String methodName, Object... args) { - return invokeMethod(searchMethod(modifiers, instance.getClass(), methodName), instance, args); - } - - /** - * Invokes the method named methodName with the given instance and arguments - * To find the method, {@link #searchMethod(int, Class, String, Class[])} is used with no type parameters. - *

- * To search the method with type parameters, you should search the method using {@link #searchMethod(int, Class, String, Class[])} or similar, - * and call {@link #invokeMethod(Method, Object, Object...)} - *

- * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} - * - * @param modifiers The modifiers for method search. Can have {@link #DEEP_SEARCH} and {@link #IGNORE_PARAMS} - * @param clazz The class to search in/from - * @param methodName Name of the method - * @param instance The instance to invoke the method on - * @param args The arguments to use in the method call - * @param The expected/known method return type - * @return The result of calling the method - * @throws NullPointerException if clazz or methodName is null - * @throws IllegalArgumentException if the method is not found - * @see #invokeMethod(Method, Object, Object...) - */ - public static T invokeMethod(int modifiers, Class clazz, String methodName, Object instance, Object... args) { - return invokeMethod(searchMethod(modifiers, clazz, methodName), instance, args); - } - - /** - * Invokes the method with the given instance and arguments - *

- * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} - * - * @param method The method to invoke - * @param instance The instance to invoke the method on - * @param args The arguments to use in the method call - * @param The expected/known method return type - * @return The result of calling the method - */ - public static T invokeMethod(Method method, Object instance, Object... args) { - return exceptionHandler.supplySafe(() -> (T) method.invoke(instance, args)); - } - - /** - * Invokes the static method named methodName with the given arguments - * To find the method, {@link #searchMethod(int, Class, String, Class[])} is used with no type parameters, - * as well as the modifier {@link #IGNORE_PARAMS} - *

- * To search the method with type parameters, you should search the method using {@link #searchMethod(int, Class, String, Class[])} or similar, - * and call {@link #invokeMethod(Method, Object, Object...)} - *

- * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} - * - * @param clazz The class to search in/from - * @param methodName Name of the method - * @param args The arguments to use in the method call - * @param The expected/known method return type - * @return The result of calling the method - * @throws NullPointerException if clazz or methodName is null - * @throws IllegalArgumentException if the method is not found - * @see #invokeStaticMethod(Method, Object...) - */ - public static T invokeStaticMethod(Class clazz, String methodName, Object... args) { - return invokeStaticMethod(searchMethod(IGNORE_PARAMS, clazz, methodName), args); - } - - /** - * Invokes the static method named methodName with the given arguments - * To find the method, {@link #searchMethod(int, Class, String, Class[])} is used with no type parameters. - *

- * To search the method with type parameters, you should search the method using {@link #searchMethod(int, Class, String, Class[])} or similar, - * and call {@link #invokeMethod(Method, Object, Object...)} - *

- * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} - * - * @param modifiers The modifiers for method search. Can have {@link #DEEP_SEARCH} and {@link #IGNORE_PARAMS} - * @param clazz The class to search in/from - * @param methodName Name of the method - * @param args The arguments to use in the method call - * @param The expected/known method return type - * @return The result of calling the method - * @throws NullPointerException if clazz or methodName is null - * @throws IllegalArgumentException if the method is not found - * @see #invokeStaticMethod(Method, Object...) - */ - public static T invokeStaticMethod(int modifiers, Class clazz, String methodName, Object... args) { - return invokeStaticMethod(searchMethod(modifiers, clazz, methodName), args); - } - - /** - * Invokes the static method with the given arguments - *

- * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} - * - * @param method The method to invoke - * @param args The arguments to use in the method call - * @param The expected/known method return type - * @return The result of calling the method - * @see #invokeMethod(Method, Object, Object...) - */ - public static T invokeStaticMethod(Method method, Object... args) { - return invokeMethod(method, (Object) null, args); - } - -} +package io.dico.dicore; + +import io.dico.dicore.exceptions.ExceptionHandler; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.Objects; +import java.util.function.Consumer; + +/** + * Reflective utilities + */ +@SuppressWarnings("unchecked") +public class Reflection { + private static final ExceptionHandler exceptionHandler; + private static final Field fieldModifiersField = restrictedSearchField(Field.class, "modifiers"); + private static Consumer errorTarget; + + private Reflection() { + + } + + static { + exceptionHandler = new ExceptionHandler() { + @Override + public void handle(Throwable ex) { + handleGenericException(ex); + } + + @Override + public Object handleGenericException(Throwable ex, Object... args) { + String action = args.length == 0 || !(args[0] instanceof String) ? "executing a reflective operation" : (String) args[0]; + ExceptionHandler.log(errorTarget, action, ex); + return null; + } + }; + + // don't use method reference here: the current reference in System.out would be cached. + setErrorTarget(msg -> System.out.println(msg)); + } + + /** + * Sets the output where ReflectiveOperationException's and similar are sent. + * This defaults to {@link System#out}. + * + * @param target The new output + * @throws NullPointerException if target is null + */ + public static void setErrorTarget(Consumer target) { + errorTarget = Objects.requireNonNull(target); + } + + /** + * This search modifier tells the implementation that it should subsequently search superclasses for the field/method. + * Using this modifier means a call to {@link #deepSearchField(Class, String)} will be used instead of {@link #restrictedSearchField(Class, String)} + * and a call to {@link #deepSearchMethod(Class, String, Class[])} will be used instead of {@link #restrictedSearchMethod(Class, String, Class[])} + */ + public static final int DEEP_SEARCH = 0x1; + + /** + * This search modifier applies only to fields, and tells the implementation that a final modifier might be present on a found field, and that it should be removed. + */ + public static final int REMOVE_FINAL = 0x2; + + /** + * This search modifier applies only to methods, and tells the implementation that it should completely ignore parameter types and return the first method with a matching name + * The implementation uses {@link Class#getDeclaredMethods()} instead of {@link Class#getDeclaredMethod(String, Class[])} if this modifier is set. + */ + public static final int IGNORE_PARAMS = 0x2; + + /* + ### FIELD METHODS ### + */ + + /** + * Search a field of any accessibility within the class or any of its superclasses. + * The first field with the given name that is found will be returned. + *

+ * If a field is found and it is not accessible, this method attempts to make it accessible. + * If a {@link SecurityException} is thrown in the process, that is ignored and the field will be returned nonetheless. + *

+ * This method throws IllegalArgumentException if the Field is not found, because, in most cases, that should never happen, + * and it should simplify debugging. In some cases, if you want to know if the field exists, you'll have to use try/catch for that. + * + * @param clazz The lowest class in the ladder to start searching from + * @param fieldName The name of the field + * //@param fieldType the type of the field, or null if it can be any. + * @return The field + * @throws NullPointerException if clazz is null or fieldName is null + * @throws IllegalArgumentException if the field doesn't exist + * @see #restrictedSearchField(Class, String) + */ + public static Field deepSearchField(Class clazz, String fieldName/*, Class fieldType*/) { + Class currentClass = clazz; + Field result; + do { + // throws NPE if class or fieldName is null + result = internalSearchField(clazz, fieldName); + if (result != null) { + return result; + } + currentClass = currentClass.getSuperclass(); + } while (currentClass != null); + + throw new IllegalArgumentException("field not found in " + clazz.getCanonicalName() + " and superclasses: " + fieldName); + } + + /** + * Search a field of any accessibility within the class, but not its superclasses. + *

+ * If a field is found and it is not accessible, this method attempts to make it accessible. + * If a {@link SecurityException} is thrown in the process, that is ignored and the field will be returned nonetheless. + *

+ * This method throws IllegalArgumentException if the Field is not found, because, in most cases, that should never happen, + * and it should simplify debugging. In some cases, if you want to know if the field exists, you'll have to use try/catch for that. + * + * @param clazz The only class to search for the field + * @param fieldName The name of the field + * @return The field + * @throws NullPointerException if clazz or fieldName is null + * @throws IllegalArgumentException if the field does not exist + */ + public static Field restrictedSearchField(Class clazz, String fieldName) { + Field result = internalSearchField(clazz, fieldName); + if (result == null) { + throw new IllegalArgumentException("field not found in " + clazz.getCanonicalName() + ": " + fieldName); + } + return result; + } + + /** + * Searches for a field using the given search method. + * + * @param modifiers The modifiers for field search. Can have {@link #DEEP_SEARCH} and {@link #REMOVE_FINAL} + * @param clazz The class to search in/from + * @param fieldName Name of the field + * @return The field + * @throws NullPointerException if clazz or fieldName is null + * @throws IllegalArgumentException if the field is not found + */ + public static Field searchField(int modifiers, Class clazz, String fieldName) { + Field result; + if ((modifiers & DEEP_SEARCH) != 0) { + result = deepSearchField(clazz, fieldName); + } else { + result = restrictedSearchField(clazz, fieldName); + } + if ((modifiers & REMOVE_FINAL) != 0) { + removeFinalModifier(result); + } + return result; + } + + /** + * @return The same as {@link #restrictedSearchField(Class, String)}, but returns null instead of throwing IllegalArgumentException + * @see #restrictedSearchField(Class, String) + */ + private static Field internalSearchField(Class clazz, String fieldName) { + Field result; + try { + // throws NullPointerException if either clazz or fieldName are null. + result = clazz.getDeclaredField(fieldName); + } catch (NoSuchFieldException | SecurityException ex) { + return null; + } + + if (!result.isAccessible()) try { + result.setAccessible(true); + } catch (SecurityException ignored) { + + } + + return result; + } + + /** + * Attempts to remove existing final modifier of the given field + * This method should always return true. + * + * @param field The field whose final modifier to remove + * @return true if the field most definitely has no final modifier after this call + * @throws NullPointerException if field is null + */ + public static boolean removeFinalModifier(Field field) { + Objects.requireNonNull(field); + try { + int modifiers = (int) fieldModifiersField.get(field); + if (modifiers != (modifiers &= ~Modifier.FINAL)) { + fieldModifiersField.set(field, modifiers); + } + return true; + } catch (Exception ex) { + return false; + } + } + + /** + * Gets field value of the field named fieldName and the given instance + * To find the field, {@link #deepSearchField(Class, String)} is used (DEEP search method). + *

+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} + * + * @param instance The instance whose field value to get + * @param fieldName the name of the field + * @param The expected/known field type + * @return The field value + * @throws IllegalArgumentException if the field doesn't exist + * @see #deepSearchField(Class, String) + * @see #getFieldValue(Class, String, Object) + */ + public static T getFieldValue(Object instance, String fieldName) { + return getFieldValue(deepSearchField(instance.getClass(), fieldName), instance); + } + + /** + * Gets field value of the field named fieldName and the given instance + * To find the field, {@link #restrictedSearchField(Class, String)} is used (RESTRICTED search method). + *

+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} + * + * @param clazz The class to search for the field + * @param instance The instance whose field value to get + * @param fieldName the name of the field + * @param The expected/known field type + * @return The field value + * @throws IllegalArgumentException if the field doesn't exist + * @see #restrictedSearchField(Class, String) + * @see #getFieldValue(Field, Object) + */ + public static T getFieldValue(Class clazz, String fieldName, Object instance) { + return getFieldValue(restrictedSearchField(clazz, fieldName), instance); + } + + /** + * Gets field value of the field named fieldName and the given instance + * To find the field, {@link #searchField(int, Class, String)} is used. + *

+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} + * + * @param modifiers The modifiers for field search. Can have {@link #DEEP_SEARCH} and {@link #REMOVE_FINAL} + * @param clazz The class to search for the field + * @param instance The instance whose field value to get + * @param fieldName the name of the field + * @param The expected/known field type + * @return The field value + * @throws IllegalArgumentException if the field doesn't exist + * @see #searchField(int, Class, String) + * @see #getFieldValue(Field, Object) + */ + public static T getFieldValue(int modifiers, Class clazz, String fieldName, Object instance) { + return getFieldValue(searchField(modifiers, clazz, fieldName), instance); + } + + /** + * Gets field value of the given field and the given instance + *

+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} + * + * @param field the field + * @param instance The instance whose field value to get + * @param The expected/known field type + * @return The field value + */ + public static T getFieldValue(Field field, Object instance) { + return exceptionHandler.supplySafe(() -> (T) field.get(instance)); + } + + /** + * Gets static field value of the field named fieldName + * To find the field, {@link #restrictedSearchField(Class, String)} is used (RESTRICTED search method). + *

+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} + * + * @param clazz The class to search for the field + * @param fieldName the name of the field + * @param The expected/known field type + * @return The field value + * @throws IllegalArgumentException if the field doesn't exist + * @see #restrictedSearchField(Class, String) + * @see #getStaticFieldValue(Field) + */ + public static T getStaticFieldValue(Class clazz, String fieldName) { + return getStaticFieldValue(restrictedSearchField(clazz, fieldName)); + } + + /** + * Gets static field value of the field named fieldName + * To find the field, {@link #searchField(int, Class, String)} is used. + *

+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} + * + * @param modifiers The modifiers for field search. Can have {@link #DEEP_SEARCH} and {@link #REMOVE_FINAL} + * @param clazz The class to search for the field + * @param fieldName the name of the field + * @param The expected/known field type + * @return The field value + * @throws IllegalArgumentException if the field doesn't exist + * @see #deepSearchField(Class, String) + * @see #getStaticFieldValue(Field) + */ + public static T getStaticFieldValue(int modifiers, Class clazz, String fieldName) { + return getStaticFieldValue(searchField(modifiers, clazz, fieldName)); + } + + /** + * Gets static field value + *

+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} + *

+ * Equivalent to the call {@code getFieldValue(field, (Object) null)} + * + * @param field the field + * @param The expected/known field type + * @return The field value + * @see #getFieldValue(Field, Object) + */ + public static T getStaticFieldValue(Field field) { + return getFieldValue(field, (Object) null); + } + + /** + * Sets field value of the field named fieldName and the given instance + * To find the field, {@link #deepSearchField(Class, String)} is used (DEEP search method). + *

+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} + * + * @param instance The instance whose field value to set + * @param fieldName the name of the field + * @param newValue the new field value + * @throws IllegalArgumentException if the field doesn't exist + * @see #deepSearchField(Class, String) + * @see #setFieldValue(Class, String, Object, Object) + */ + public static void setFieldValue(Object instance, String fieldName, Object newValue) { + setFieldValue(deepSearchField(instance.getClass(), fieldName), instance, newValue); + } + + /** + * Sets field value of the field named fieldName and the given instance + * To find the field, {@link #restrictedSearchField(Class, String)} is used (RESTRICTED search method). + *

+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} + * + * @param clazz The class to search for the field + * @param fieldName the name of the field + * @param instance The field owner + * @param newValue The new field value + * @throws IllegalArgumentException if the field doesn't exist + * @see #restrictedSearchField(Class, String) + * @see #setFieldValue(Field, Object, Object) + */ + public static void setFieldValue(Class clazz, String fieldName, Object instance, Object newValue) { + setFieldValue(restrictedSearchField(clazz, fieldName), instance, newValue); + } + + /** + * Sets field value of the field named fieldName and the given instance + * To find the field, {@link #searchField(int, Class, String)} is used. + *

+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} + * + * @param modifiers The modifiers for field search. Can have {@link #DEEP_SEARCH} and {@link #REMOVE_FINAL} + * @param clazz The class to search for the field + * @param instance The instance whose field value to set + * @param fieldName the name of the field + * @param newValue The new field value + * @throws IllegalArgumentException if the field doesn't exist + * @see #searchField(int, Class, String) + * @see #setFieldValue(Field, Object, Object) + */ + public static void setFieldValue(int modifiers, Class clazz, String fieldName, Object instance, Object newValue) { + setFieldValue(searchField(modifiers, clazz, fieldName), instance, newValue); + } + + /** + * Sets a field value + *

+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} + * + * @param field The field + * @param instance The field owner + * @param newValue The new field value + */ + public static void setFieldValue(Field field, Object instance, Object newValue) { + exceptionHandler.runSafe(() -> field.set(instance, newValue)); + } + + /** + * Sets static field value of the field name fieldName + * To find the field, {@link #restrictedSearchField(Class, String)} is used (RESTRICTED search method). + *

+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} + * + * @param clazz The class to search for the field + * @param fieldName the name of the field + * @param newValue The new field value + * @throws IllegalArgumentException if the field doesn't exist + * @see #restrictedSearchField(Class, String) + * @see #setStaticFieldValue(Field, Object) + */ + public static void setStaticFieldValue(Class clazz, String fieldName, Object newValue) { + setStaticFieldValue(restrictedSearchField(clazz, fieldName), newValue); + } + + /** + * Sets static field value of the field named fieldName + * To find the field, {@link #searchField(int, Class, String)} is used. + *

+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} + * + * @param modifiers The modifiers for field search. Can have {@link #DEEP_SEARCH} and {@link #REMOVE_FINAL} + * @param clazz The class to search for the field + * @param fieldName the name of the field + * @param newValue The new field value + * @throws IllegalArgumentException if the field doesn't exist + * @see #searchField(int, Class, String) + * @see #setStaticFieldValue(Field, Object) + */ + public static void setStaticFieldValue(int modifiers, Class clazz, String fieldName, Object newValue) { + setStaticFieldValue(searchField(modifiers, clazz, fieldName), newValue); + } + + /** + * Sets a static field value + *

+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} + * + * @param field The field + * @param newValue The new field value + */ + public static void setStaticFieldValue(Field field, Object newValue) { + setFieldValue(field, (Object) null, newValue); + } + + /* + ### METHOD METHODS ### + */ + + /** + * Search a method of any accessibility within the class or any of its superclasses. + * The first method with the given name that is found will be returned. + *

+ * If a method is found and it is not accessible, this method attempts to make it accessible. + * If a {@link SecurityException} is thrown in the process, that is ignored and the method will be returned nonetheless. + *

+ * This method throws IllegalArgumentException if the Method is not found, because, in most cases, that should never happen, + * and it should simplify debugging. In some cases, if you want to know if the method exists, you'll have to use try/catch for that. + * + * @param clazz The lowest class in the ladder to start searching from + * @param methodName The name of the method + * @param parameterTypes the parameter types of the sought method. + * @return The method + * @throws NullPointerException if clazz is null or methodName is null + * @throws IllegalArgumentException if the method doesn't exist + * @see #restrictedSearchMethod(Class, String, Class[]) + */ + public static Method deepSearchMethod(Class clazz, String methodName, Class... parameterTypes) { + return deepSearchMethod(0, clazz, methodName, parameterTypes); + } + + /** + * Search a method of any accessibility within the class or any of its superclasses. + * The first method with the given name that is found will be returned. + *

+ * If a method is found and it is not accessible, this method attempts to make it accessible. + * If a {@link SecurityException} is thrown in the process, that is ignored and the method will be returned nonetheless. + *

+ * This method throws IllegalArgumentException if the Method is not found, because, in most cases, that should never happen, + * and it should simplify debugging. In some cases, if you want to know if the method exists, you'll have to use try/catch for that. + * + * @param modifiers The modifiers for method search. Can have {@link #IGNORE_PARAMS} + * @param clazz The lowest class in the ladder to start searching from + * @param methodName The name of the method + * @param parameterTypes the parameter types of the sought method. + * @return The method + * @throws NullPointerException if clazz is null or methodName is null + * @throws IllegalArgumentException if the method doesn't exist + * @see #restrictedSearchMethod(Class, String, Class[]) + */ + public static Method deepSearchMethod(int modifiers, Class clazz, String methodName, Class... parameterTypes) { + Class currentClass = clazz; + Method result; + do { + // throws NPE if class or methodName is null + result = internalSearchMethod(modifiers, currentClass, methodName, parameterTypes); + if (result != null) { + return result; + } + currentClass = currentClass.getSuperclass(); + } while (currentClass != null); + + throw new IllegalArgumentException("method not found in " + clazz.getCanonicalName() + " and superclasses: " + methodName); + } + + /** + * Search a method of any accessibility within the class, but not its superclasses. + *

+ * If a method is found and it is not accessible, this method attempts to make it accessible. + * If a {@link SecurityException} is thrown in the process, that is ignored and the method will be returned nonetheless. + *

+ * This method throws IllegalArgumentException if the Method is not found, because, in most cases, that should never happen, + * and it should simplify debugging. In some cases, if you want to know if the method exists, you'll have to use try/catch for that. + * + * @param clazz The only class to search for the method + * @param methodName The name of the method + * @param parameterTypes the parameter types of the sought method. + * @return The method + * @throws NullPointerException if clazz or methodName is null + * @throws IllegalArgumentException if the method does not exist + */ + public static Method restrictedSearchMethod(Class clazz, String methodName, Class... parameterTypes) { + return restrictedSearchMethod(0, clazz, methodName, parameterTypes); + } + + /** + * Search a method of any accessibility within the class, but not its superclasses. + *

+ * If a method is found and it is not accessible, this method attempts to make it accessible. + * If a {@link SecurityException} is thrown in the process, that is ignored and the method will be returned nonetheless. + *

+ * This method throws IllegalArgumentException if the Method is not found, because, in most cases, that should never happen, + * and it should simplify debugging. In some cases, if you want to know if the method exists, you'll have to use try/catch for that. + * + * @param modifiers The modifiers for method search. Can have {@link #IGNORE_PARAMS} + * @param clazz The only class to search for the method + * @param methodName The name of the method + * @param parameterTypes the parameter types of the sought method. + * @return The method + * @throws NullPointerException if clazz or methodName is null + * @throws IllegalArgumentException if the method does not exist + */ + public static Method restrictedSearchMethod(int modifiers, Class clazz, String methodName, Class... parameterTypes) { + Method result = internalSearchMethod(modifiers, clazz, methodName, parameterTypes); + if (result == null) { + throw new IllegalArgumentException("method not found in " + clazz.getCanonicalName() + ": " + methodName); + } + return result; + } + + /** + * Searches for a method using the given search method. + *

+ * If a method is found and it is not accessible, this method attempts to make it accessible. + * If a {@link SecurityException} is thrown in the process, that is ignored and the method will be returned nonetheless. + *

+ * This method throws IllegalArgumentException if the Method is not found, because, in most cases, that should never happen, + * and it should simplify debugging. In some cases, if you want to know if the method exists, you'll have to use try/catch for that. + * + * @param modifiers The modifiers for method search. Can have {@link #DEEP_SEARCH} and {@link #IGNORE_PARAMS} + * @param clazz The class to search in/from + * @param methodName Name of the method + * @param parameterTypes the parameter types of the sought method. + * @return The method + * @throws NullPointerException if clazz or methodName is null + * @throws IllegalArgumentException if the method is not found + */ + public static Method searchMethod(int modifiers, Class clazz, String methodName, Class... parameterTypes) { + if ((modifiers & DEEP_SEARCH) != 0) { + return deepSearchMethod(modifiers, clazz, methodName, parameterTypes); + } else { + return restrictedSearchMethod(modifiers, clazz, methodName, parameterTypes); + } + } + + /** + * @return The same as {@link #restrictedSearchMethod(Class, String, Class[]) }, but returns null instead of throwing IllegalArgumentException + * @see #restrictedSearchMethod(Class, String, Class[]) + */ + private static Method internalSearchMethod(int modifiers, Class clazz, String methodName, Class... parameterTypes) { + Method result = null; + + if ((modifiers & IGNORE_PARAMS) != 0) { + + // throws NullPointerException if either clazz or methodName are null. + methodName = methodName.intern(); + for (Method method : clazz.getDeclaredMethods()) { + // all method names are interned. Identity comparison is much faster. + if (method.getName() == methodName) { + result = method; + break; + } + } + + if (result == null) { + return null; + } + + } else { + + try { + // throws NullPointerException if either clazz or methodName are null. + result = clazz.getDeclaredMethod(methodName, parameterTypes); + } catch (NoSuchMethodException | SecurityException ex) { + return null; + } + + } + + if (!result.isAccessible()) try { + result.setAccessible(true); + } catch (SecurityException ignored) { + + } + + return result; + } + + /** + * Invokes the method named methodName with the given instance and arguments + * To find the method, {@link #searchMethod(int, Class, String, Class[])} is used with no type parameters, + * modifiers {@link #DEEP_SEARCH} and {@link #IGNORE_PARAMS}, and the class {@link Object#getClass() instance.getClass()} + *

+ * To search the method with type parameters, you should search the method using {@link #searchMethod(int, Class, String, Class[])} or similar, + * and call {@link #invokeMethod(Method, Object, Object...)} + *

+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} + * + * @param methodName Name of the method + * @param instance The instance to invoke the method on + * @param args The arguments to use in the method call + * @param The expected/known method return type + * @return The result of calling the method + * @throws NullPointerException if instance or methodName is null + * @throws IllegalArgumentException if the method is not found + * @see #invokeMethod(Method, Object, Object...) + */ + public static T invokeMethod(Object instance, String methodName, Object... args) { + return invokeMethod(searchMethod(DEEP_SEARCH | IGNORE_PARAMS, instance.getClass(), methodName), instance, args); + } + + /** + * Invokes the method named methodName with the given instance and arguments + * To find the method, {@link #searchMethod(int, Class, String, Class[])} is used with no type parameters, + * as well as the modifier {@link #IGNORE_PARAMS} + *

+ * To search the method with type parameters, you should search the method using {@link #searchMethod(int, Class, String, Class[])} or similar, + * and call {@link #invokeMethod(Method, Object, Object...)} + *

+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} + * + * @param clazz The class to search in/from + * @param methodName Name of the method + * @param instance The instance to invoke the method on + * @param args The arguments to use in the method call + * @param The expected/known method return type + * @return The result of calling the method + * @throws NullPointerException if clazz or methodName is null + * @throws IllegalArgumentException if the method is not found + * @see #invokeMethod(Method, Object, Object...) + */ + public static T invokeMethod(Class clazz, String methodName, Object instance, Object... args) { + return invokeMethod(searchMethod(IGNORE_PARAMS, clazz, methodName), instance, args); + } + + /** + * Invokes the method named methodName with the given instance and arguments + * To find the method, {@link #searchMethod(int, Class, String, Class[])} is used with no type parameters. + * For this search, the result of calling {@link Object#getClass() instance.getClass()} is used. + *

+ * To search the method with type parameters, you should search the method using {@link #searchMethod(int, Class, String, Class[])} or similar, + * and call {@link #invokeMethod(Method, Object, Object...)} + *

+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} + * + * @param modifiers The modifiers for method search. Can have {@link #DEEP_SEARCH} and {@link #IGNORE_PARAMS} + * @param methodName Name of the method + * @param instance The instance to invoke the method on + * @param args The arguments to use in the method call + * @param The expected/known method return type + * @return The result of calling the method + * @throws NullPointerException if instance or methodName is null + * @throws IllegalArgumentException if the method is not found + * @see #invokeMethod(Method, Object, Object...) + */ + public static T invokeMethod(int modifiers, Object instance, String methodName, Object... args) { + return invokeMethod(searchMethod(modifiers, instance.getClass(), methodName), instance, args); + } + + /** + * Invokes the method named methodName with the given instance and arguments + * To find the method, {@link #searchMethod(int, Class, String, Class[])} is used with no type parameters. + *

+ * To search the method with type parameters, you should search the method using {@link #searchMethod(int, Class, String, Class[])} or similar, + * and call {@link #invokeMethod(Method, Object, Object...)} + *

+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} + * + * @param modifiers The modifiers for method search. Can have {@link #DEEP_SEARCH} and {@link #IGNORE_PARAMS} + * @param clazz The class to search in/from + * @param methodName Name of the method + * @param instance The instance to invoke the method on + * @param args The arguments to use in the method call + * @param The expected/known method return type + * @return The result of calling the method + * @throws NullPointerException if clazz or methodName is null + * @throws IllegalArgumentException if the method is not found + * @see #invokeMethod(Method, Object, Object...) + */ + public static T invokeMethod(int modifiers, Class clazz, String methodName, Object instance, Object... args) { + return invokeMethod(searchMethod(modifiers, clazz, methodName), instance, args); + } + + /** + * Invokes the method with the given instance and arguments + *

+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} + * + * @param method The method to invoke + * @param instance The instance to invoke the method on + * @param args The arguments to use in the method call + * @param The expected/known method return type + * @return The result of calling the method + */ + public static T invokeMethod(Method method, Object instance, Object... args) { + return exceptionHandler.supplySafe(() -> (T) method.invoke(instance, args)); + } + + /** + * Invokes the static method named methodName with the given arguments + * To find the method, {@link #searchMethod(int, Class, String, Class[])} is used with no type parameters, + * as well as the modifier {@link #IGNORE_PARAMS} + *

+ * To search the method with type parameters, you should search the method using {@link #searchMethod(int, Class, String, Class[])} or similar, + * and call {@link #invokeMethod(Method, Object, Object...)} + *

+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} + * + * @param clazz The class to search in/from + * @param methodName Name of the method + * @param args The arguments to use in the method call + * @param The expected/known method return type + * @return The result of calling the method + * @throws NullPointerException if clazz or methodName is null + * @throws IllegalArgumentException if the method is not found + * @see #invokeStaticMethod(Method, Object...) + */ + public static T invokeStaticMethod(Class clazz, String methodName, Object... args) { + return invokeStaticMethod(searchMethod(IGNORE_PARAMS, clazz, methodName), args); + } + + /** + * Invokes the static method named methodName with the given arguments + * To find the method, {@link #searchMethod(int, Class, String, Class[])} is used with no type parameters. + *

+ * To search the method with type parameters, you should search the method using {@link #searchMethod(int, Class, String, Class[])} or similar, + * and call {@link #invokeMethod(Method, Object, Object...)} + *

+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} + * + * @param modifiers The modifiers for method search. Can have {@link #DEEP_SEARCH} and {@link #IGNORE_PARAMS} + * @param clazz The class to search in/from + * @param methodName Name of the method + * @param args The arguments to use in the method call + * @param The expected/known method return type + * @return The result of calling the method + * @throws NullPointerException if clazz or methodName is null + * @throws IllegalArgumentException if the method is not found + * @see #invokeStaticMethod(Method, Object...) + */ + public static T invokeStaticMethod(int modifiers, Class clazz, String methodName, Object... args) { + return invokeStaticMethod(searchMethod(modifiers, clazz, methodName), args); + } + + /** + * Invokes the static method with the given arguments + *

+ * If a {@link ReflectiveOperationException} occurs, this is printed to {@link System#out} + * + * @param method The method to invoke + * @param args The arguments to use in the method call + * @param The expected/known method return type + * @return The result of calling the method + * @see #invokeMethod(Method, Object, Object...) + */ + public static T invokeStaticMethod(Method method, Object... args) { + return invokeMethod(method, (Object) null, args); + } + +} -- cgit v1.2.3