Add findMethod(:Class, :String, :Object[]):Optional<Method> and supporting private Predicate creation methods.
findMethod(..) matches a Method based on the Object's Class type, the Method name and whether the arguments passed can be used to invoek a (potentially) overloaded method by the same name. Add a resolveInvocationTarget(:Object, :Method) to resolve the target of the Method invocation. The target of the Method invocation is determined by whether the Method is static; if the Method is static, then the resolved target is null, otherwise it is the target Object.
This commit is contained in:
@@ -16,17 +16,22 @@
|
||||
|
||||
package org.springframework.geode.core.util;
|
||||
|
||||
import static org.springframework.data.gemfire.util.ArrayUtils.nullSafeArray;
|
||||
import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newIllegalArgumentException;
|
||||
import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newIllegalStateException;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.Arrays;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
@@ -118,6 +123,56 @@ public abstract class ObjectUtils extends org.springframework.util.ObjectUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a {@link Method} with the given {@link Method#getName() name} on {@link Class type} which can be invoked
|
||||
* with the given {@link Object arguments}.
|
||||
*
|
||||
* @param type {@link Class} type to evaluate for the {@link Method}.
|
||||
* @param methodName {@link String} containing the name of the {@link Method} to find.
|
||||
* @param args {@link Object array of arguments} used when invoking the method.
|
||||
* @return an {@link Optional} {@link Method} on {@link Class type} potentially matching
|
||||
* the {@link Object arguments} of the invocation.
|
||||
* @see java.lang.Class
|
||||
* @see java.lang.reflect.Method
|
||||
* @see java.util.Optional
|
||||
*/
|
||||
public static Optional<Method> findMethod(@NonNull Class<?> type, @NonNull String methodName, Object... args) {
|
||||
|
||||
return Arrays.stream(nullSafeArray(type.getDeclaredMethods(), Method.class))
|
||||
.filter(methodNameMatchesPredicate(methodName))
|
||||
.filter(argumentsMatchParameterTypesPredicate(args))
|
||||
.findFirst();
|
||||
}
|
||||
|
||||
private static Predicate<Method> argumentsMatchParameterTypesPredicate(Object... args) {
|
||||
|
||||
return method -> {
|
||||
|
||||
Class<?>[] parameterTypes = nullSafeArray(method.getParameterTypes(), Class.class);
|
||||
|
||||
Object[] arguments = nullSafeArray(args, Object.class);
|
||||
|
||||
if (arguments.length != parameterTypes.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int index = 0; index < parameterTypes.length; index++) {
|
||||
|
||||
Object argument = arguments[index];
|
||||
|
||||
if (argument != null && !parameterTypes[index].isInstance(argument)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
private static Predicate<Method> methodNameMatchesPredicate(String methodName) {
|
||||
return method -> method.getName().equals(methodName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link Object value} of the given {@link String named} {@link Field} on the given {@link Object}.
|
||||
*
|
||||
@@ -184,17 +239,17 @@ public abstract class ObjectUtils extends org.springframework.util.ObjectUtils {
|
||||
methodName, org.springframework.util.ObjectUtils.nullSafeClassName(obj)));
|
||||
}
|
||||
|
||||
private static Constructor makeAccessible(Constructor<?> constructor) {
|
||||
public static Constructor makeAccessible(Constructor<?> constructor) {
|
||||
ReflectionUtils.makeAccessible(constructor);
|
||||
return constructor;
|
||||
}
|
||||
|
||||
private static Field makeAccessible(Field field) {
|
||||
public static Field makeAccessible(Field field) {
|
||||
ReflectionUtils.makeAccessible(field);
|
||||
return field;
|
||||
}
|
||||
|
||||
private static Method makeAccessible(Method method) {
|
||||
public static Method makeAccessible(Method method) {
|
||||
ReflectionUtils.makeAccessible(method);
|
||||
return method;
|
||||
}
|
||||
@@ -232,6 +287,23 @@ public abstract class ObjectUtils extends org.springframework.util.ObjectUtils {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the {@link Object invocation target} for the given {@link Method}.
|
||||
*
|
||||
* If the {@link Method} is {@link Modifier#STATIC} then {@literl null} is returned,
|
||||
* otherwise {@link Object target} will be returned.
|
||||
*
|
||||
* @param <T> {@lin Class type} of the {@link Object target}.
|
||||
* @param target {@link Object} on which the {@link Method} will be invoked.
|
||||
* @param method {@link Method} to invoke on the {@link Object}.
|
||||
* @return the resolved {@link Object invocation method}.
|
||||
* @see java.lang.Object
|
||||
* @see java.lang.reflect.Method
|
||||
*/
|
||||
public static <T> T resolveInvocationTarget(T target, Method method) {
|
||||
return Modifier.isStatic(method.getModifiers()) ? null : target;
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface ExceptionThrowingOperation<T> {
|
||||
T doExceptionThrowingOperation() throws Exception;
|
||||
|
||||
@@ -20,6 +20,8 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newRuntimeException;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
@@ -61,6 +63,50 @@ public class ObjectUtilsUnitTests {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findMethodUsingExactArguments() {
|
||||
|
||||
Optional<Method> method =
|
||||
ObjectUtils.findMethod(TestObject.class, "exactArgumentMethod", true);
|
||||
|
||||
assertThat(method.orElse(null)).isNotNull();
|
||||
assertThat(method.map(Method::getName).orElse(null)).isEqualTo("exactArgumentMethod");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findMethodUsingGenericArguments() {
|
||||
|
||||
Optional<Method> method =
|
||||
ObjectUtils.findMethod(TestObject.class, "genericArgumentMethod", "test", 2L);
|
||||
|
||||
assertThat(method.orElse(null)).isNotNull();
|
||||
assertThat(method.map(Method::getName).orElse(null)).isEqualTo("genericArgumentMethod");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findMethodWithTooFewArguments() {
|
||||
|
||||
Optional<Method> method = ObjectUtils.findMethod(TestObject.class, "exactArgumentMethod");
|
||||
|
||||
assertThat(method.isPresent()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findMethodWithTooManyArguments() {
|
||||
|
||||
Optional<Method> method = ObjectUtils.findMethod(TestObject.class, "testMethod", 'X');
|
||||
|
||||
assertThat(method.isPresent()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findNonExistingMethod() {
|
||||
|
||||
Optional<Method> method = ObjectUtils.findMethod(TestObject.class, "nonExistingMethod");
|
||||
|
||||
assertThat(method.isPresent()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getFieldValueIsSuccessful() throws Exception {
|
||||
|
||||
@@ -74,7 +120,7 @@ public class ObjectUtilsUnitTests {
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testGetFieldWithNullObjectThrowsIllegalArgumentException() throws Exception {
|
||||
public void getFieldWithNullObjectThrowsIllegalArgumentException() throws Exception {
|
||||
|
||||
try {
|
||||
ObjectUtils.get(null, TestObject.class.getDeclaredField("existingField"));
|
||||
@@ -89,7 +135,7 @@ public class ObjectUtilsUnitTests {
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testGetFieldWithNullFieldThrowsIllegalArgumentException() throws Exception {
|
||||
public void getFieldWithNullFieldThrowsIllegalArgumentException() throws Exception {
|
||||
|
||||
try {
|
||||
ObjectUtils.get(new TestObject(), (Field) null);
|
||||
@@ -160,6 +206,22 @@ public class ObjectUtilsUnitTests {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveInvocationTargetOnClassMethodReturnsNull() throws Exception {
|
||||
|
||||
Method staticTestMethod = TestObject.class.getDeclaredMethod("staticTestMethod");
|
||||
|
||||
assertThat(ObjectUtils.resolveInvocationTarget(TestObject.INSTANCE, staticTestMethod)).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveInvocationTargetOnObjectMethodReturnsObject() throws Exception {
|
||||
|
||||
Method testMethod = TestObject.class.getDeclaredMethod("testMethod");
|
||||
|
||||
assertThat(ObjectUtils.resolveInvocationTarget(TestObject.INSTANCE, testMethod)).isEqualTo(TestObject.INSTANCE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void returnValueThrowOnNullWithNonNullValueReturnsValue() {
|
||||
assertThat(ObjectUtils.returnValueThrowOnNull("test")).isEqualTo("test");
|
||||
@@ -183,8 +245,22 @@ public class ObjectUtilsUnitTests {
|
||||
@SuppressWarnings("unused")
|
||||
public static class TestObject {
|
||||
|
||||
public static final TestObject INSTANCE = new TestObject();
|
||||
|
||||
private Object existingField = "MOCK";
|
||||
|
||||
public static Object staticTestMethod() {
|
||||
return "STATIC";
|
||||
}
|
||||
|
||||
public Object exactArgumentMethod(Boolean condition) {
|
||||
return "exactArgumentMethod";
|
||||
}
|
||||
|
||||
public Object genericArgumentMethod(String name, Number value) {
|
||||
return "genericArgumentMethod";
|
||||
}
|
||||
|
||||
public String testMethod() {
|
||||
return "TEST";
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user