org.iherus.shiro.util.Utils Maven / Gradle / Ivy
/**
* Copyright (c) 2016-2019, Bosco.Liao ([email protected]).
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.iherus.shiro.util;
import java.lang.ref.SoftReference;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.UndeclaredThrowableException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;
/**
* 工具类
*
* @author Bosco.Liao
* @since 2.0.0
*/
public abstract class Utils {
private static final int INITIAL_HASH = 7;
private static final int MULTIPLIER = 31;
private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
private static final String[] EMPTY_STRING_ARRAY = {};
private static final Field[] EMPTY_FIELD_ARRAY = new Field[0];
private static final Method[] EMPTY_METHOD_ARRAY = new Method[0];
private static final Map, SoftReference> LOCAL_FIELDS = new ConcurrentHashMap<>(128);
private static final Map, SoftReference> LOCAL_METHODS = new ConcurrentHashMap<>(128);
private static final Charset UTF_8 = Charset.forName("UTF-8");
@SuppressWarnings("unchecked")
public static MutableArray newMutableArray(T... elements) {
return new MutableArray(elements);
}
/************************ Assert about***********************/
/**
* Assert that the given String contains valid text content; that is, it must not
* be {@code null} and must contain at least one non-whitespace character.
* Utils.assertNotBlank(name, "'name' must not be empty");
*/
public static void assertNotBlank(String text, String message) {
if (!isNotBlank(text)) {
throw new IllegalArgumentException(message);
}
}
/**
* Assert whether the object is {@code null}.
*/
public static void assertNotNull(Object object, String message) {
if (object == null) {
throw new IllegalArgumentException(message);
}
}
/**
* Assert a boolean expression, throwing an {@code IllegalArgumentException}
* if the expression evaluates to {@code false}.
*
* Utils.assertIsTrue(i > 0, () -> "The value '" + i + "' must be greater than zero");
*
*/
public static void assertIsTrue(boolean expression, Supplier messageSupplier) {
if (!expression) {
throw new IllegalArgumentException(nullSafeGet(messageSupplier));
}
}
private static String nullSafeGet(Supplier messageSupplier) {
return (messageSupplier != null ? messageSupplier.get() : null);
}
/**
* Whether the CharSequence is empty.
*/
public static boolean isEmpty(final CharSequence cs) {
return cs == null || cs.length() == 0;
}
/**
* Whether the CharSequence is not empty.
*/
public static boolean isNotEmpty(final CharSequence cs) {
return !isEmpty(cs);
}
/**
* Whether the CharSequence is blank.
*/
public static boolean isBlank(final CharSequence cs) {
int strLen;
if (cs == null || (strLen = cs.length()) == 0) {
return true;
}
for (int i = 0; i < strLen; i++) {
if (Character.isWhitespace(cs.charAt(i)) == false) {
return false;
}
}
return true;
}
/**
* Whether the CharSequence is not blank.
*/
public static boolean isNotBlank(final CharSequence cs) {
return !isBlank(cs);
}
/**
* Split a {@code String} at the first occurrence of the delimiter.
* Does not include the delimiter in the result.
* @param toSplit the string to split (potentially {@code null} or empty)
* @param delimiter to split the string up with (potentially {@code null} or empty)
* @return a two element array with index 0 being before the delimiter, and
* index 1 being after the delimiter (neither element includes the delimiter);
* or {@code null} if the delimiter wasn't found in the given input {@code String}
*/
public static String[] split(String toSplit, String delimiter) {
if (!hasLength(toSplit) || !hasLength(delimiter)) {
return null;
}
int offset = toSplit.indexOf(delimiter);
if (offset < 0) {
return null;
}
String beforeDelimiter = toSplit.substring(0, offset);
String afterDelimiter = toSplit.substring(offset + delimiter.length());
return new String[] { beforeDelimiter, afterDelimiter };
}
/**
* Convert a comma delimited list (e.g., a row from a CSV file) into a set.
* Note that this will suppress duplicates, and as of 4.2, the elements in
* the returned set will preserve the original order in a {@link LinkedHashSet}.
* @param str the input {@code String} (potentially {@code null} or empty)
* @return a set of {@code String} entries in the list
*/
public static Set commaDelimitedListToSet(String str) {
String[] tokens = commaDelimitedListToStringArray(str);
return new LinkedHashSet<>(Arrays.asList(tokens));
}
/**
* Convert a comma delimited list (e.g., a row from a CSV file) into an
* array of strings.
* @param str the input {@code String} (potentially {@code null} or empty)
* @return an array of strings, or the empty array in case of empty input
*/
public static String[] commaDelimitedListToStringArray(String str) {
return delimitedListToStringArray(str, ",");
}
/**
* Take a {@code String} that is a delimited list and convert it into a
* {@code String} array.
*/
public static String[] delimitedListToStringArray(String str, String delimiter) {
return delimitedListToStringArray(str, delimiter, null);
}
/**
* Take a {@code String} that is a delimited list and convert it into
* a {@code String} array.
*/
public static String[] delimitedListToStringArray(String str, String delimiter, String charsToDelete) {
if (str == null) {
return EMPTY_STRING_ARRAY;
}
if (delimiter == null) {
return new String[] { str };
}
List result = new ArrayList<>();
if (delimiter.isEmpty()) {
for (int i = 0; i < str.length(); i++) {
result.add(deleteAny(str.substring(i, i + 1), charsToDelete));
}
} else {
int pos = 0;
int delPos;
while ((delPos = str.indexOf(delimiter, pos)) != -1) {
result.add(deleteAny(str.substring(pos, delPos), charsToDelete));
pos = delPos + delimiter.length();
}
if (str.length() > 0 && pos <= str.length()) {
// Add rest of String, but not in case of empty input.
result.add(deleteAny(str.substring(pos), charsToDelete));
}
}
return toStringArray(result);
}
/**
* Copy the given {@link Collection} into a {@code String} array.
* The {@code Collection} must contain {@code String} elements only.
* @param collection the {@code Collection} to copy
* (potentially {@code null} or empty)
* @return the resulting {@code String} array
*/
public static String[] toStringArray(Collection collection) {
return (!isEmpty(collection) ? collection.toArray(EMPTY_STRING_ARRAY) : EMPTY_STRING_ARRAY);
}
/**
* Return {@code true} if the supplied Collection is {@code null} or empty.
* Otherwise, return {@code false}.
* @param collection the Collection to check
* @return whether the given Collection is empty
*/
public static boolean isEmpty(Collection> collection) {
return (collection == null || collection.isEmpty());
}
/**
* Delete any character in a given {@code String}.
* @param inString the original {@code String}
* @param charsToDelete a set of characters to delete.
* E.g. "az\n" will delete 'a's, 'z's and new lines.
* @return the resulting {@code String}
*/
public static String deleteAny(String inString, String charsToDelete) {
if (!hasLength(inString) || !hasLength(charsToDelete)) {
return inString;
}
StringBuilder sb = new StringBuilder(inString.length());
for (int i = 0; i < inString.length(); i++) {
char c = inString.charAt(i);
if (charsToDelete.indexOf(c) == -1) {
sb.append(c);
}
}
return sb.toString();
}
/**
* Check that the given {@code String} is neither {@code null} nor of length 0.
* Note: this method returns {@code true} for a {@code String} that
* purely consists of whitespace.
* @param str the {@code String} to check (may be {@code null})
* @return {@code true} if the {@code String} is not {@code null} and has length
*/
public static boolean hasLength(String str) {
return (str != null && !str.isEmpty());
}
/************************ Byte[] about ***********************/
public static String bytesToText(byte[] bytes) {
return new String(bytes, UTF_8);
}
/**
* numeric {@code long} to byte array.
*/
public static byte[] intToBytes(int number) {
return String.valueOf(number).getBytes(UTF_8);
}
public static byte[] longToBytes(long number) {
return String.valueOf(number).getBytes(UTF_8);
}
/**
* Clones an array returning a typecast result and handling {@code null}.
*/
public static byte[] clone(final byte[] array) {
if (array == null) {
return null;
}
return array.clone();
}
/**
* Whether the byte array is empty.
*/
public static boolean isEmpty(final byte[] array) {
return (array == null || array.length == 0);
}
/**
* Whether the byte[] array is empty.
*/
public static boolean isEmpty(final byte[][] array) {
return (array == null || array.length == 0);
}
/**
* Merges all the elements of the given arrays into a new array.
*/
public static byte[] mergeAll(final byte[] array1, final byte... array2) {
if (array1 == null) {
return clone(array2);
} else if (array2 == null) {
return clone(array1);
}
final byte[] joinedArray = new byte[array1.length + array2.length];
System.arraycopy(array1, 0, joinedArray, 0, array1.length);
System.arraycopy(array2, 0, joinedArray, array1.length, array2.length);
return joinedArray;
}
/**
* Produces a new {@code byte} array containing the elements between the start
* and end indices.
*/
public static byte[] subarray(final byte[] array, int startIndexInclusive, int endIndexExclusive) {
if (array == null) {
return null;
}
if (startIndexInclusive < 0) {
startIndexInclusive = 0;
}
if (endIndexExclusive > array.length) {
endIndexExclusive = array.length;
}
final int newSize = endIndexExclusive - startIndexInclusive;
if (newSize <= 0) {
return EMPTY_BYTE_ARRAY;
}
final byte[] subarray = new byte[newSize];
System.arraycopy(array, startIndexInclusive, subarray, 0, newSize);
return subarray;
}
/************************ Hashcode about***********************/
/**
* Return as hash code for the given object; typically the value of
* {@code Object#hashCode()}}. If the object is an array,
* this method will delegate to any of the {@code nullSafeHashCode}
* methods for arrays in this class. If the object is {@code null},
* this method returns 0.
* @see Object#hashCode()
* @see #nullSafeHashCode(Object[])
* @see #nullSafeHashCode(boolean[])
* @see #nullSafeHashCode(byte[])
* @see #nullSafeHashCode(char[])
* @see #nullSafeHashCode(double[])
* @see #nullSafeHashCode(float[])
* @see #nullSafeHashCode(int[])
* @see #nullSafeHashCode(long[])
* @see #nullSafeHashCode(short[])
*/
public static int nullSafeHashCode(Object obj) {
if (obj == null) {
return 0;
}
if (obj.getClass().isArray()) {
if (obj instanceof Object[]) {
return nullSafeHashCode((Object[]) obj);
}
if (obj instanceof boolean[]) {
return nullSafeHashCode((boolean[]) obj);
}
if (obj instanceof byte[]) {
return nullSafeHashCode((byte[]) obj);
}
if (obj instanceof char[]) {
return nullSafeHashCode((char[]) obj);
}
if (obj instanceof double[]) {
return nullSafeHashCode((double[]) obj);
}
if (obj instanceof float[]) {
return nullSafeHashCode((float[]) obj);
}
if (obj instanceof int[]) {
return nullSafeHashCode((int[]) obj);
}
if (obj instanceof long[]) {
return nullSafeHashCode((long[]) obj);
}
if (obj instanceof short[]) {
return nullSafeHashCode((short[]) obj);
}
}
return obj.hashCode();
}
/**
* Return a hash code based on the contents of the specified array.
* If {@code array} is {@code null}, this method returns 0.
*/
public static int nullSafeHashCode(Object[] array) {
if (array == null) {
return 0;
}
int hash = INITIAL_HASH;
for (Object element : array) {
hash = MULTIPLIER * hash + nullSafeHashCode(element);
}
return hash;
}
/**
* Return a hash code based on the contents of the specified array.
* If {@code array} is {@code null}, this method returns 0.
*/
public static int nullSafeHashCode(boolean[] array) {
if (array == null) {
return 0;
}
int hash = INITIAL_HASH;
for (boolean element : array) {
hash = MULTIPLIER * hash + Boolean.hashCode(element);
}
return hash;
}
/**
* Return a hash code based on the contents of the specified array.
* If {@code array} is {@code null}, this method returns 0.
*/
public static int nullSafeHashCode(byte[] array) {
if (array == null) {
return 0;
}
int hash = INITIAL_HASH;
for (byte element : array) {
hash = MULTIPLIER * hash + element;
}
return hash;
}
/**
* Return a hash code based on the contents of the specified array.
* If {@code array} is {@code null}, this method returns 0.
*/
public static int nullSafeHashCode(char[] array) {
if (array == null) {
return 0;
}
int hash = INITIAL_HASH;
for (char element : array) {
hash = MULTIPLIER * hash + element;
}
return hash;
}
/**
* Return a hash code based on the contents of the specified array.
* If {@code array} is {@code null}, this method returns 0.
*/
public static int nullSafeHashCode(double[] array) {
if (array == null) {
return 0;
}
int hash = INITIAL_HASH;
for (double element : array) {
hash = MULTIPLIER * hash + Double.hashCode(element);
}
return hash;
}
/**
* Return a hash code based on the contents of the specified array.
* If {@code array} is {@code null}, this method returns 0.
*/
public static int nullSafeHashCode(float[] array) {
if (array == null) {
return 0;
}
int hash = INITIAL_HASH;
for (float element : array) {
hash = MULTIPLIER * hash + Float.hashCode(element);
}
return hash;
}
/**
* Return a hash code based on the contents of the specified array.
* If {@code array} is {@code null}, this method returns 0.
*/
public static int nullSafeHashCode(int[] array) {
if (array == null) {
return 0;
}
int hash = INITIAL_HASH;
for (int element : array) {
hash = MULTIPLIER * hash + element;
}
return hash;
}
/**
* Return a hash code based on the contents of the specified array.
* If {@code array} is {@code null}, this method returns 0.
*/
public static int nullSafeHashCode(long[] array) {
if (array == null) {
return 0;
}
int hash = INITIAL_HASH;
for (long element : array) {
hash = MULTIPLIER * hash + Long.hashCode(element);
}
return hash;
}
/**
* Return a hash code based on the contents of the specified array.
* If {@code array} is {@code null}, this method returns 0.
*/
public static int nullSafeHashCode(short[] array) {
if (array == null) {
return 0;
}
int hash = INITIAL_HASH;
for (short element : array) {
hash = MULTIPLIER * hash + element;
}
return hash;
}
/**
* Determine if the given objects are equal, returning {@code true} if
* both are {@code null} or {@code false} if only one is {@code null}.
*
Compares arrays with {@code Arrays.equals}, performing an equality
* check based on the array elements rather than the array reference.
* @param o1 first Object to compare
* @param o2 second Object to compare
* @return whether the given objects are equal
* @see Object#equals(Object)
* @see java.util.Arrays#equals
*/
public static boolean nullSafeEquals(Object o1, Object o2) {
if (o1 == o2) {
return true;
}
if (o1 == null || o2 == null) {
return false;
}
if (o1.equals(o2)) {
return true;
}
if (o1.getClass().isArray() && o2.getClass().isArray()) {
return arrayEquals(o1, o2);
}
return false;
}
/**
* Compare the given arrays with {@code Arrays.equals}, performing an equality
* check based on the array elements rather than the array reference.
* @param o1 first array to compare
* @param o2 second array to compare
* @return whether the given objects are equal
* @see #nullSafeEquals(Object, Object)
* @see java.util.Arrays#equals
*/
private static boolean arrayEquals(Object o1, Object o2) {
if (o1 instanceof Object[] && o2 instanceof Object[]) {
return Arrays.equals((Object[]) o1, (Object[]) o2);
}
if (o1 instanceof boolean[] && o2 instanceof boolean[]) {
return Arrays.equals((boolean[]) o1, (boolean[]) o2);
}
if (o1 instanceof byte[] && o2 instanceof byte[]) {
return Arrays.equals((byte[]) o1, (byte[]) o2);
}
if (o1 instanceof char[] && o2 instanceof char[]) {
return Arrays.equals((char[]) o1, (char[]) o2);
}
if (o1 instanceof double[] && o2 instanceof double[]) {
return Arrays.equals((double[]) o1, (double[]) o2);
}
if (o1 instanceof float[] && o2 instanceof float[]) {
return Arrays.equals((float[]) o1, (float[]) o2);
}
if (o1 instanceof int[] && o2 instanceof int[]) {
return Arrays.equals((int[]) o1, (int[]) o2);
}
if (o1 instanceof long[] && o2 instanceof long[]) {
return Arrays.equals((long[]) o1, (long[]) o2);
}
if (o1 instanceof short[] && o2 instanceof short[]) {
return Arrays.equals((short[]) o1, (short[]) o2);
}
return false;
}
/************************ Field about***********************/
/**
* Attempt to find a {@link Field field} on the supplied {@link Class} with the
* supplied {@code name}. Searches all super-classes up to {@link Object}.
*
* @param clazz the class to introspect
* @param name the name of the field
* @return the corresponding Field object, or {@code null} if not found
*/
public static Field findField(Class> clazz, String name) {
return findField(clazz, name, null);
}
/**
* Get the field represented by the supplied {@link Field field object} on the
* specified {@link Object target object}. In accordance with {@link Field#get(Object)}
* semantics, the returned value is automatically wrapped if the underlying field
* has a primitive type.
* @param
*
* @param name the field name to get
* @param target the target object from which to get the field
* @return the field's current value
*/
public static T getFieldValue(Object target, String name, Class type) {
Field field = findField(target.getClass(), name);
if (null == field) {
return null;
}
try {
makeAccessible(field);
return type.cast(field.get(target));
} catch (IllegalAccessException ex) {
handleReflectionException(ex);
}
throw new IllegalStateException("Should never get here");
}
public static void setFieldValue(Object target, String name, Class type, Object value) {
Field field = findField(target.getClass(), name);
if (null == field) {
return;
}
try {
makeAccessible(field);
field.set(target, value);
return;
} catch (IllegalAccessException ex) {
handleReflectionException(ex);
}
throw new IllegalStateException("Should never get here");
}
/**
* 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);
}
}
/**
* Attempt to find a {@link Field field} on the supplied {@link Class} with the
* supplied {@code name} and/or {@link Class type}. Searches all super-classes up to {@link Object}.
*
* @param clazz the class to introspect
* @param name the name of the field (may be {@code null} if type is specified)
* @param type the type of the field (may be {@code null} if name is specified)
* @return the corresponding Field object, or {@code null} if not found
*/
public static Field findField(Class> clazz, String name, Class> type) {
assertNotNull(clazz, "Class must not be null");
assertIsTrue(name != null || type != null, () -> "Either name or type of the field must be specified");
Class> searchType = clazz;
while (Object.class != searchType && searchType != null) {
Field[] fields = getDeclaredFields(searchType);
for (Field field : fields) {
if ((name == null || name.equals(field.getName())) && (type == null || type.equals(field.getType()))) {
return field;
}
}
searchType = searchType.getSuperclass();
}
return null;
}
private static Field[] getDeclaredFields(Class> clazz) {
assertNotNull(clazz, "Class must not be null");
SoftReference fieldsRef = LOCAL_FIELDS.get(clazz);
if (fieldsRef == null) {
try {
Field[] declaredFields = clazz.getDeclaredFields();
fieldsRef = new SoftReference<>((declaredFields.length == 0 ? EMPTY_FIELD_ARRAY : declaredFields));
LOCAL_FIELDS.put(clazz, fieldsRef);
} catch (Throwable ex) {
throw new IllegalStateException("Failed to introspect Class [" + clazz.getName()
+ "] from ClassLoader [" + clazz.getClassLoader() + "]", ex);
}
}
return fieldsRef.get();
}
/************************ Method about***********************/
/**
* Attempt to find a {@link Method} on the supplied class with the supplied name
* and no parameters. Searches all superclasses up to {@code Object}.
* Returns {@code null} if no {@link Method} can be found.
* @param clazz the class to introspect
* @param name the name of the method
* @return the Method object, or {@code null} if none found
*/
public static Method findMethod(Class> clazz, String name) {
return findMethod(clazz, name, new Class>[0]);
}
/**
* Attempt to find a {@link Method} on the supplied class with the supplied name
* and parameter types. Searches all superclasses up to {@code Object}.
*
Returns {@code null} if no {@link Method} can be found.
* @param clazz the class to introspect
* @param name the name of the method
* @param argTypes the parameter types of the method
* (may be {@code null} to indicate any signature)
* @return the Method object, or {@code null} if none found
*/
public static Method findMethod(Class> clazz, String name, Class>... argTypes) {
assertNotNull(clazz, "Class must not be null");
assertNotNull(name, "Method name must not be null");
Class> searchType = clazz;
while (searchType != null) {
Method[] methods = (searchType.isInterface() ? searchType.getMethods() : getDeclaredMethods(searchType));
for (Method method : methods) {
if (name.equals(method.getName())
&& (argTypes == null || Arrays.equals(argTypes, method.getParameterTypes()))) {
return method;
}
}
searchType = searchType.getSuperclass();
}
return null;
}
/**
* This variant retrieves {@link Class#getDeclaredMethods()} from a local cache
* in order to avoid the JVM's SecurityManager check and defensive array copying.
* In addition, it also includes Java 8 default methods from locally implemented
* interfaces, since those are effectively to be treated just like declared methods.
* @param clazz the class to introspect
* @return the cached array of methods
* @throws IllegalStateException if introspection fails
* @see Class#getDeclaredMethods()
*/
private static Method[] getDeclaredMethods(Class> clazz) {
assertNotNull(clazz, "Class must not be null");
SoftReference methodRef = LOCAL_METHODS.get(clazz);
if (methodRef == null) {
try {
Method[] declaredMethods = clazz.getDeclaredMethods();
List defaultMethods = findConcreteMethodsOnInterfaces(clazz);
Method[] result = declaredMethods;
if (defaultMethods != null) {
result = new Method[declaredMethods.length + defaultMethods.size()];
System.arraycopy(declaredMethods, 0, result, 0, declaredMethods.length);
int index = declaredMethods.length;
for (Method defaultMethod : defaultMethods) {
result[index] = defaultMethod;
index++;
}
}
methodRef = new SoftReference((result.length == 0 ? EMPTY_METHOD_ARRAY : result));
LOCAL_METHODS.put(clazz, methodRef);
} catch (Throwable ex) {
throw new IllegalStateException("Failed to introspect Class [" + clazz.getName()
+ "] from ClassLoader [" + clazz.getClassLoader() + "]", ex);
}
}
return methodRef.get();
}
private static List findConcreteMethodsOnInterfaces(Class> clazz) {
List result = null;
for (Class> ifc : clazz.getInterfaces()) {
for (Method ifcMethod : ifc.getMethods()) {
if (!Modifier.isAbstract(ifcMethod.getModifiers())) {
if (result == null) {
result = new ArrayList<>();
}
result.add(ifcMethod);
}
}
}
return result;
}
public static void makeAccessible(Method method) {
if ((!Modifier.isPublic(method.getModifiers()) ||
!Modifier.isPublic(method.getDeclaringClass().getModifiers())) && !method.isAccessible()) {
method.setAccessible(true);
}
}
public static Object invokeMethod(Object target, String methodName) {
return invokeMethod(target, methodName, new Class[] {});
}
public static Object invokeSuperMethod(Object target, String methodName) {
return invokeSuperMethod(target, methodName, new Class[]{});
}
public static Object invokeMethod(Object target, String methodName, Class> argType, Object arg) {
return invokeMethod(target, methodName, new Class[] { argType }, ((Object) arg));
}
public static Object invokeMethod(Object target, String methodName, Class>[] argTypes, Object... args) {
assertNotNull(target, "Object must not be null");
Method method = findMethod(target.getClass(), methodName, argTypes);
if (method == null) {
return null;
}
try {
makeAccessible(method);
return method.invoke(target, args);
} catch (Exception ex) {
handleReflectionException(ex);
}
throw new IllegalStateException("Should never get here");
}
public static Object invokeSuperMethod(Object target, String methodName, Class>[] argTypes, Object... args) {
assertNotNull(target, "Object must not be null");
Method method = findMethod(target.getClass().getSuperclass(), methodName, argTypes);
if (method == null) {
return null;
}
try {
makeAccessible(method);
return method.invoke(target, args);
} catch (Exception ex) {
handleReflectionException(ex);
}
throw new IllegalStateException("Should never get here");
}
private 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 or field: " + ex.getMessage());
}
if (ex instanceof InvocationTargetException) {
rethrowRuntimeException(((InvocationTargetException) ex).getTargetException());
}
if (ex instanceof RuntimeException) {
throw (RuntimeException) ex;
}
throw new UndeclaredThrowableException(ex);
}
private static void rethrowRuntimeException(Throwable ex) {
if (ex instanceof RuntimeException) {
throw (RuntimeException) ex;
}
if (ex instanceof Error) {
throw (Error) ex;
}
throw new UndeclaredThrowableException(ex);
}
public static class MutableArray {
private final ArrayList list;
@SuppressWarnings("unchecked")
MutableArray(T... initialElements) {
this.list = new ArrayList(Arrays.asList(initialElements));
}
public MutableArray add(boolean executed, Supplier supplier) {
if (executed) {
this.list.add(supplier.get());
}
return this;
}
public MutableArray replace(int index, T e) {
this.list.set(index, e);
return this;
}
public Object[] toArray() {
return this.list.toArray();
}
}
}