This is an automated email from the ASF dual-hosted git repository. ggregory pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/commons-lang.git
The following commit(s) were added to refs/heads/master by this push: new f75f5b9 Add MethodInvokers. f75f5b9 is described below commit f75f5b9373c0c9b8c3257642468e10a792afb6a4 Author: Gary Gregory <garydgreg...@gmail.com> AuthorDate: Wed Nov 17 00:42:26 2021 -0500 Add MethodInvokers. --- src/changes/changes.xml | 1 + .../commons/lang3/function/MethodInvokers.java | 259 +++++++++++++++++++++ .../lang3/exception/CustomCheckedException.java | 38 +++ .../lang3/exception/CustomUncheckedException.java | 38 +++ .../lang3/function/AnnotationTestFixture.java | 29 +++ .../commons/lang3/function/MethodFixtures.java | 222 ++++++++++++++++++ .../function/MethodInvokersBiConsumerTest.java | 56 +++++ .../function/MethodInvokersBiFunctionTest.java | 65 ++++++ .../MethodInvokersFailableBiConsumerTest.java | 62 +++++ .../MethodInvokersFailableBiFunctionTest.java | 69 ++++++ .../MethodInvokersFailableFunctionTest.java | 84 +++++++ .../MethodInvokersFailableSupplierTest.java | 44 ++++ .../lang3/function/MethodInvokersFunctionTest.java | 101 ++++++++ .../lang3/function/MethodInvokersSupplierTest.java | 48 ++++ 14 files changed, 1116 insertions(+) diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 0c5e9c5..f938262 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -95,6 +95,7 @@ The <action> type attribute can be add,update,fix,remove. <action type="add" dev="ggregory" due-to="Gary Gregory">Add UncheckedReflectiveOperationException.</action> <action type="add" dev="ggregory" due-to="Gary Gregory">Add and use ClassUtils.isPublic(Class).</action> <action type="add" dev="ggregory" due-to="Gary Gregory">Add UncheckedIllegalAccessException.</action> + <action type="add" dev="ggregory" due-to="Gary Gregory">Add MethodInvokers.</action> <!-- UPDATE --> <action type="update" dev="ggregory" due-to="Dependabot, Gary Gregory">Bump spotbugs-maven-plugin from 4.2.0 to 4.4.2.2 #735, #808, #822.</action> <action type="update" dev="ggregory" due-to="Dependabot, XenoAmess">Bump Bump actions/cache from v2.1.4 to v2.1.6 #742, #752, #764.</action> diff --git a/src/main/java/org/apache/commons/lang3/function/MethodInvokers.java b/src/main/java/org/apache/commons/lang3/function/MethodInvokers.java new file mode 100644 index 0000000..39f3b1e --- /dev/null +++ b/src/main/java/org/apache/commons/lang3/function/MethodInvokers.java @@ -0,0 +1,259 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.commons.lang3.function; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandleProxies; +import java.lang.invoke.MethodHandles; +import java.lang.reflect.Method; +import java.util.Objects; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.function.Supplier; + +import org.apache.commons.lang3.exception.UncheckedIllegalAccessException; + +/** + * Converts {@link Method} objects to lambdas. + * <p> + * More specifically, produces instances of single-method interfaces which redirect calls to methods; see + * {@link #asInterfaceInstance(Class, Method)}. + * </p> + * <h2>Calling supplier methods with no arguments</h2> + * <p> + * If the interface's single-method defines no arguments, use {@link #asFunction(Method)} and then apply the function + * passing in the object to receive the method call. + * </p> + * <p> + * For example to invoke {@link String#length()}: + * </p> + * + * <pre> + * final Method method = String.class.getMethod("length"); + * final Function<String, Integer> function = MethodInvokers.asFunction(method); + * assertEquals(3, function.apply("ABC")); + * </pre> + * + * <h2>Calling function methods with one argument</h2> + * <p> + * If the interface's single-method defines one argument, use {@link #asBiFunction(Method)} and then apply the function + * passing in the object to receive the method call. The second argument to the function is the only argument to the + * method. + * </p> + * <p> + * For example to invoke {@link String#charAt(int)}: + * </p> + * + * <pre> + * final Method method = String.class.getMethod("charAt", int.class); + * final BiFunction<String, Integer, Character> function = MethodInvokers.asBiFunction(method); + * assertEquals('C', function.apply("ABC", 2)); + * </pre> + * + * @since 3.13.0 + */ +public final class MethodInvokers { + + /** + * Produces a {@link BiConsumer} for a given <em>consumer</em> Method. For example, a classic setter method (as opposed + * to a fluent setter). You call the BiConsumer with two arguments: (1) the object receiving the method call, and (2) + * the method argument. + * + * @param <T> the type of the first argument to the operation: The type containing the Method. + * @param <U> the type of the second argument to the operation: The type of the method argument. + * @param method the method to invoke. + * @return a correctly-typed wrapper for the given target. + */ + @SuppressWarnings("unchecked") + public static <T, U> BiConsumer<T, U> asBiConsumer(final Method method) { + return asInterfaceInstance(BiConsumer.class, method); + } + + /** + * Produces a {@link BiFunction} for a given a <em>function</em> Method. You call the BiFunction with two arguments: (1) + * the object receiving the method call, and (2) the method argument. The BiFunction return type must match the method's + * return type. + * <p> + * For example to invoke {@link String#charAt(int)}: + * </p> + * + * <pre> + * final Method method = String.class.getMethod("charAt", int.class); + * final BiFunction<String, Integer, Character> function = MethodInvokers.asBiFunction(method); + * assertEquals('C', function.apply("ABC", 2)); + * </pre> + * + * @param <T> the type of the first argument to the function: The type containing the method. + * @param <U> the type of the second argument to the function: the method argument type. + * @param <R> the type of the result of the function: The method return type. + * @param method the method to invoke. + * @return a correctly-typed wrapper for the given target. + */ + @SuppressWarnings("unchecked") + public static <T, U, R> BiFunction<T, U, R> asBiFunction(final Method method) { + return asInterfaceInstance(BiFunction.class, method); + } + + /** + * Produces a {@link FailableBiConsumer} for a given <em>consumer</em> Method. For example, a classic setter method (as + * opposed to a fluent setter). You call the FailableBiConsumer with two arguments: (1) the object receiving the method + * call, and (2) the method argument. + * + * @param <T> the type of the first argument to the operation: The type containing the Method. + * @param <U> the type of the second argument to the operation: The type of the method argument. + * @param method the method to invoke. + * @return a correctly-typed wrapper for the given target. + */ + @SuppressWarnings("unchecked") + public static <T, U> FailableBiConsumer<T, U, Throwable> asFailableBiConsumer(final Method method) { + return asInterfaceInstance(FailableBiConsumer.class, method); + } + + /** + * Produces a {@link FailableBiFunction} for a given a <em>function</em> Method. You call the FailableBiFunction with + * two arguments: (1) the object receiving the method call, and (2) the method argument. The BiFunction return type must + * match the method's return type. + * + * @param <T> the type of the first argument to the function: The type containing the method. + * @param <U> the type of the second argument to the function: the method argument type. + * @param <R> the type of the result of the function: The method return type. + * @param method the method to invoke. + * @return a correctly-typed wrapper for the given target. + */ + @SuppressWarnings("unchecked") + public static <T, U, R> FailableBiFunction<T, U, R, Throwable> asFailableBiFunction(final Method method) { + return asInterfaceInstance(FailableBiFunction.class, method); + } + + /** + * Produces a {@link FailableFunction} for a given a <em>supplier</em> Method. You call the Function with one argument: + * the object receiving the method call. The FailableFunction return type must match the method's return type. + * + * @param <T> the type of the first argument to the function: The type containing the method. + * @param <R> the type of the result of the function: The method return type. + * @param method the method to invoke. + * @return a correctly-typed wrapper for the given target. + */ + @SuppressWarnings("unchecked") + public static <T, R> FailableFunction<T, R, Throwable> asFailableFunction(final Method method) { + return asInterfaceInstance(FailableFunction.class, method); + } + + /** + * Produces a {@link FailableSupplier} for a given a <em>supplier</em> Method. The FailableSupplier return type must + * match the method's return type. + * <p> + * Only works with static methods. + * </p> + * + * @param <R> The Method return type. + * @param method the method to invoke. + * @return a correctly-typed wrapper for the given target. + */ + @SuppressWarnings("unchecked") + public static <R> FailableSupplier<R, Throwable> asFailableSupplier(final Method method) { + return asInterfaceInstance(FailableSupplier.class, method); + } + + /** + * Produces a {@link Function} for a given a <em>supplier</em> Method. You call the Function with one argument: the + * object receiving the method call. The Function return type must match the method's return type. + * <p> + * For example to invoke {@link String#length()}: + * </p> + * + * <pre> + * final Method method = String.class.getMethod("length"); + * final Function<String, Integer> function = MethodInvokers.asFunction(method); + * assertEquals(3, function.apply("ABC")); + * </pre> + * + * @param <T> the type of the first argument to the function: The type containing the method. + * @param <R> the type of the result of the function: The method return type. + * @param method the method to invoke. + * @return a correctly-typed wrapper for the given target. + */ + @SuppressWarnings("unchecked") + public static <T, R> Function<T, R> asFunction(final Method method) { + return asInterfaceInstance(Function.class, method); + } + + /** + * Produces an instance of the given single-method interface which redirects its calls to the given method. + * <p> + * For the definition of "single-method", see {@linkplain MethodHandleProxies#asInterfaceInstance(Class, MethodHandle)}. + * </p> + * + * @param <T> The interface type. + * @param intfc The interface type. + * @param method the method to invoke. + * @return a correctly-typed wrapper for the given target. + * @see MethodHandleProxies#asInterfaceInstance(Class, MethodHandle) + */ + public static <T> T asInterfaceInstance(final Class<T> intfc, final Method method) { + return MethodHandleProxies.asInterfaceInstance(Objects.requireNonNull(intfc, "intfc"), unreflectUnchecked(method)); + } + + /** + * Produces a {@link Supplier} for a given a <em>supplier</em> Method. The Supplier return type must match the method's + * return type. + * <p> + * Only works with static methods. + * </p> + * + * @param <R> The Method return type. + * @param method the method to invoke. + * @return a correctly-typed wrapper for the given target. + */ + @SuppressWarnings("unchecked") + public static <R> Supplier<R> asSupplier(final Method method) { + return asInterfaceInstance(Supplier.class, method); + } + + /** + * Throws NullPointerException if {@code method} is {@code null}. + * + * @param method The method to test. + * @return The given method. + * @throws NullPointerException if {@code method} is {@code null}. + */ + private static Method requireMethod(final Method method) { + return Objects.requireNonNull(method, "method"); + } + + private static MethodHandle unreflect(final Method method) throws IllegalAccessException { + return MethodHandles.lookup().unreflect(requireMethod(method)); + } + + private static MethodHandle unreflectUnchecked(final Method method) { + try { + return unreflect(method); + } catch (final IllegalAccessException e) { + throw new UncheckedIllegalAccessException(e); + } + } + + /** + * No need to create instances. + */ + private MethodInvokers() { + // noop + } + +} diff --git a/src/test/java/org/apache/commons/lang3/exception/CustomCheckedException.java b/src/test/java/org/apache/commons/lang3/exception/CustomCheckedException.java new file mode 100644 index 0000000..9982d09 --- /dev/null +++ b/src/test/java/org/apache/commons/lang3/exception/CustomCheckedException.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.commons.lang3.exception; + +public class CustomCheckedException extends Exception { + + public CustomCheckedException() { + super(); + } + + public CustomCheckedException(String message) { + super(message); + } + + public CustomCheckedException(String message, Throwable cause) { + super(message, cause); + } + + public CustomCheckedException(Throwable cause) { + super(cause); + } + +} diff --git a/src/test/java/org/apache/commons/lang3/exception/CustomUncheckedException.java b/src/test/java/org/apache/commons/lang3/exception/CustomUncheckedException.java new file mode 100644 index 0000000..fc53005 --- /dev/null +++ b/src/test/java/org/apache/commons/lang3/exception/CustomUncheckedException.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.commons.lang3.exception; + +public class CustomUncheckedException extends RuntimeException { + + public CustomUncheckedException() { + super(); + } + + public CustomUncheckedException(String message) { + super(message); + } + + public CustomUncheckedException(String message, Throwable cause) { + super(message, cause); + } + + public CustomUncheckedException(Throwable cause) { + super(cause); + } + +} diff --git a/src/test/java/org/apache/commons/lang3/function/AnnotationTestFixture.java b/src/test/java/org/apache/commons/lang3/function/AnnotationTestFixture.java new file mode 100644 index 0000000..a179a1f --- /dev/null +++ b/src/test/java/org/apache/commons/lang3/function/AnnotationTestFixture.java @@ -0,0 +1,29 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.commons.lang3.function; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +@interface AnnotationTestFixture { + // empty +} diff --git a/src/test/java/org/apache/commons/lang3/function/MethodFixtures.java b/src/test/java/org/apache/commons/lang3/function/MethodFixtures.java new file mode 100644 index 0000000..a7530d1 --- /dev/null +++ b/src/test/java/org/apache/commons/lang3/function/MethodFixtures.java @@ -0,0 +1,222 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.commons.lang3.function; + +import java.io.IOException; +import java.lang.reflect.Method; + +import org.apache.commons.lang3.exception.CustomCheckedException; +import org.apache.commons.lang3.exception.CustomUncheckedException; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; + +class MethodFixtures { + + static MethodFixtures INSTANCE = new MethodFixtures(); + + static Method getDeclaredMethod(final String name, final Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException { + return MethodFixtures.class.getDeclaredMethod(name, parameterTypes); + } + + static Method getMethodForGetString() throws NoSuchMethodException, SecurityException { + return getDeclaredMethod("getString"); + } + + static Method getMethodForGetString1Arg() throws NoSuchMethodException, SecurityException { + return getDeclaredMethod("getString1Arg", String.class); + } + + static Method getMethodForGetString1ArgChecked() throws NoSuchMethodException, SecurityException { + return getDeclaredMethod("getString1ArgChecked", String.class); + } + + static Method getMethodForGetString1ArgThrowsChecked() throws NoSuchMethodException, SecurityException { + return getDeclaredMethod("getString1ArgThrowsChecked", String.class); + } + + static Method getMethodForGetString1ArgThrowsUnchecked() throws NoSuchMethodException, SecurityException { + return getDeclaredMethod("getString1ArgThrowsUnchecked", String.class); + } + + static Method getMethodForGetString2Arg() throws NoSuchMethodException, SecurityException { + return getDeclaredMethod("getString2Args", String.class, String.class); + } + + static Method getMethodForGetStringChecked() throws NoSuchMethodException, SecurityException { + return getDeclaredMethod("getStringChecked"); + } + + static Method getMethodForGetStringsVarArg() throws NoSuchMethodException, SecurityException { + return getDeclaredMethod("getStringArrayVarStringArgs", String[].class); + } + + static Method getMethodForGetStringThrowsChecked() throws NoSuchMethodException, SecurityException { + return getDeclaredMethod("getStringThrowsChecked"); + } + + static Method getMethodForGetStringThrowsUnchecked() throws NoSuchMethodException, SecurityException { + return getDeclaredMethod("getStringThrowsUnchecked"); + } + + static Method getMethodForGetStringVarStringArgs() throws NoSuchMethodException, SecurityException { + return getDeclaredMethod("geStringtVarStringArgs", String[].class); + } + + static Method getMethodForSetString1Arg() throws NoSuchMethodException, SecurityException { + return getDeclaredMethod("setValue1", String.class); + } + + static Method getMethodForSetString1ArgThrows() throws NoSuchMethodException, SecurityException { + return getDeclaredMethod("setValue1Throws", String.class); + } + + static Method getMethodForSetString1ArgThrowsChecked() throws NoSuchMethodException, SecurityException { + return getDeclaredMethod("setValue1ThrowsChecked", String.class); + } + + static Method getMethodForSetString1ArgThrowsUnchecked() throws NoSuchMethodException, SecurityException { + return getDeclaredMethod("setValue1ThrowsUnchecked", String.class); + } + + static Method getMethodForSetString2Args() throws NoSuchMethodException, SecurityException { + return getDeclaredMethod("setValue1And2", String.class, String.class); + } + + static Method getMethodForSetStringsVarArg() throws NoSuchMethodException, SecurityException { + return getDeclaredMethod("setValueArray", String[].class); + } + + static Method getMethodForStaticGetString() throws NoSuchMethodException, SecurityException { + return getDeclaredMethod("staticGetString"); + } + + static Method getMethodForVoidMethod() throws NoSuchMethodException, SecurityException { + return getDeclaredMethod("voidMethod"); + } + + public static String staticGetString() { + return "Static.ABC"; + } + + private String value1; + + private String value2; + + private String[] valueArray; + + @BeforeEach + @AfterEach + public void clear() { + value1 = null; + value1 = null; + valueArray = null; + } + + public String geStringtVarStringArgs(final String... strings) { + return "XYZ"; + } + + @AnnotationTestFixture + public String getString() { + return "ABC"; + } + + public String getString1Arg(final String value) { + return value; + } + + @SuppressWarnings("unused") // IOException is declared but never thrown. + public String getString1ArgChecked(final String value) throws IOException { + return value; + } + + public String getString1ArgThrowsChecked(final String value) throws CustomCheckedException { + throw new CustomCheckedException("getString1ArgThrowsChecked"); + } + + public String getString1ArgThrowsUnchecked(final String value) { + throw new CustomUncheckedException("getString1ArgThrowsUnchecked"); + } + + @AnnotationTestFixture + public String getString2() { + return "EFG"; + } + + public String getString2Args(final String value1, final String value2) { + return value1 + value2; + } + + public String[] getStringArrayVarStringArgs(final String... strings) { + return strings; + } + + public String getStringChecked() throws Exception { + return "ABC"; + } + + public String getStringThrowsChecked() throws CustomCheckedException { + throw new CustomCheckedException("getStringThrowsChecked"); + } + + public String getStringThrowsUnchecked() { + throw new CustomUncheckedException("getStringThrowsUnchecked"); + } + + String getValue1() { + return value1; + } + + String getValue2() { + return value2; + } + + String[] getValueArray() { + return valueArray; + } + + void setValue1(final String value1) throws Exception { + this.value1 = value1; + } + + void setValue1And2(final String value1, final String value2) throws Exception { + this.value1 = value1; + this.value2 = value2; + } + + void setValue1ThrowsChecked(final String value1) throws CustomCheckedException { + throw new CustomCheckedException("setValue1ThrowsChecked"); + } + + void setValue1ThrowsUnchecked(final String value1) { + throw new CustomUncheckedException("setValue1ThrowsUnchecked"); + } + + void setValue2(final String value2) { + this.value2 = value2; + } + + void setValueArray(final String... values) throws Exception { + this.valueArray = values; + } + + public void voidMethod() { + // noop + } + +} diff --git a/src/test/java/org/apache/commons/lang3/function/MethodInvokersBiConsumerTest.java b/src/test/java/org/apache/commons/lang3/function/MethodInvokersBiConsumerTest.java new file mode 100644 index 0000000..9d02c24 --- /dev/null +++ b/src/test/java/org/apache/commons/lang3/function/MethodInvokersBiConsumerTest.java @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.commons.lang3.function; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.lang.reflect.Method; +import java.util.function.BiConsumer; + +import org.junit.jupiter.api.Test; + +/** + * Tests {@link MethodInvokers#asBiConsumer(Method)}. + */ +public class MethodInvokersBiConsumerTest extends MethodFixtures { + + @Test + public void testApply1Arg() throws NoSuchMethodException, SecurityException { + final BiConsumer<Object, Object> biConsumer = MethodInvokers.asBiConsumer(getMethodForSetString1Arg()); + biConsumer.accept(INSTANCE, "A"); + assertEquals("A", INSTANCE.getValue1()); + biConsumer.accept(INSTANCE, "B"); + assertEquals("B", INSTANCE.getValue1()); + } + + @Test + public void testConstructorForNull() throws SecurityException { + assertThrows(NullPointerException.class, () -> MethodInvokers.asBiConsumer(null)); + } + + @Test + public void testToString() throws SecurityException, ReflectiveOperationException { + // Should not blow up and must return _something_ + final BiConsumer<Object, Object> biConsumer = MethodInvokers.asBiConsumer(getMethodForSetString1Arg()); + assertFalse(biConsumer.toString().isEmpty()); + assertFalse(biConsumer.toString().isEmpty()); + } + +} diff --git a/src/test/java/org/apache/commons/lang3/function/MethodInvokersBiFunctionTest.java b/src/test/java/org/apache/commons/lang3/function/MethodInvokersBiFunctionTest.java new file mode 100644 index 0000000..90c24ce --- /dev/null +++ b/src/test/java/org/apache/commons/lang3/function/MethodInvokersBiFunctionTest.java @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.commons.lang3.function; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.lang.reflect.Method; +import java.util.function.BiFunction; + +import org.apache.commons.lang3.exception.CustomUncheckedException; +import org.junit.jupiter.api.Test; + +/** + * Tests {@link MethodInvokers#asBiFunction(Method)}. + */ +public class MethodInvokersBiFunctionTest extends MethodFixtures { + + @Test + public void testApply1Arg() throws NoSuchMethodException, SecurityException { + final BiFunction<MethodFixtures, String, String> func = MethodInvokers.asBiFunction(getMethodForGetString1Arg()); + assertEquals(INSTANCE.getString1Arg("A"), func.apply(INSTANCE, "A")); + } + + @Test + public void testApply1ArgThrowsUnchecked() throws NoSuchMethodException, SecurityException { + final BiFunction<MethodFixtures, String, String> func = MethodInvokers.asBiFunction(getMethodForGetString1ArgThrowsUnchecked()); + assertThrows(CustomUncheckedException.class, () -> func.apply(INSTANCE, "A")); + } + + @Test + public void testConstructorForNull() throws SecurityException { + assertThrows(NullPointerException.class, () -> MethodInvokers.asBiFunction(null)); + } + + @Test + public void testFullExample() throws SecurityException, ReflectiveOperationException { + final Method method = String.class.getMethod("charAt", int.class); + final BiFunction<String, Integer, Character> function = MethodInvokers.asBiFunction(method); + assertEquals('C', function.apply("ABC", 2)); + } + + @Test + public void testToString() throws SecurityException, ReflectiveOperationException { + // Should not blow up and must return _something_ + assertFalse(MethodInvokers.asBiFunction(getMethodForGetString1Arg()).toString().isEmpty()); + } + +} diff --git a/src/test/java/org/apache/commons/lang3/function/MethodInvokersFailableBiConsumerTest.java b/src/test/java/org/apache/commons/lang3/function/MethodInvokersFailableBiConsumerTest.java new file mode 100644 index 0000000..4d369d9 --- /dev/null +++ b/src/test/java/org/apache/commons/lang3/function/MethodInvokersFailableBiConsumerTest.java @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.commons.lang3.function; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.lang.reflect.Method; + +import org.apache.commons.lang3.exception.CustomCheckedException; +import org.apache.commons.lang3.exception.CustomUncheckedException; +import org.junit.jupiter.api.Test; + +/** + * Tests {@link MethodInvokers#asFailableBiConsumer(Method)}. + */ +public class MethodInvokersFailableBiConsumerTest extends MethodFixtures { + + @Test + public void testApply1Arg() throws Throwable { + MethodInvokers.asFailableBiConsumer(getMethodForSetString1Arg()).accept(INSTANCE, "A"); + assertEquals("A", INSTANCE.getValue1()); + } + + @Test + public void testApply1ArgThrowsChecked() throws Exception { + assertThrows(CustomCheckedException.class, () -> MethodInvokers.asFailableBiConsumer(getMethodForSetString1ArgThrowsChecked()).accept(INSTANCE, "A")); + } + + @Test + public void testApply1ArgThrowsUnchecked() throws Exception { + assertThrows(CustomUncheckedException.class, () -> MethodInvokers.asFailableBiConsumer(getMethodForSetString1ArgThrowsUnchecked()).accept(INSTANCE, "A")); + } + + @Test + public void testConstructorForNull() throws Exception { + assertThrows(NullPointerException.class, () -> MethodInvokers.asFailableBiConsumer(null)); + } + + @Test + public void testToString() throws SecurityException, ReflectiveOperationException { + // Should not blow up and must return _something_ + assertFalse(MethodInvokers.asFailableBiConsumer(getMethodForSetString1Arg()).toString().isEmpty()); + } + +} diff --git a/src/test/java/org/apache/commons/lang3/function/MethodInvokersFailableBiFunctionTest.java b/src/test/java/org/apache/commons/lang3/function/MethodInvokersFailableBiFunctionTest.java new file mode 100644 index 0000000..4b768cb --- /dev/null +++ b/src/test/java/org/apache/commons/lang3/function/MethodInvokersFailableBiFunctionTest.java @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.commons.lang3.function; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.lang.reflect.Method; + +import org.apache.commons.lang3.exception.CustomCheckedException; +import org.apache.commons.lang3.exception.CustomUncheckedException; +import org.junit.jupiter.api.Test; + +/** + * Tests {@link MethodInvokers#asFailableBiFunction(Method)}. + */ +public class MethodInvokersFailableBiFunctionTest extends MethodFixtures { + + @Test + public void testApply1Arg() throws Throwable { + // Use a local variable typed to the interface to make sure we compile. + final FailableBiFunction<MethodFixtures, String, String[], Throwable> func = MethodInvokers.asFailableBiFunction(getMethodForGetString1ArgChecked()); + assertEquals(INSTANCE.getString1ArgChecked("A"), func.apply(INSTANCE, "A")); + } + + @Test + public void testApply1ArgThrowsChecked() throws NoSuchMethodException, SecurityException { + // Use a local variable typed to the interface to make sure we compile. + final FailableBiFunction<MethodFixtures, String, String[], Throwable> func = MethodInvokers + .asFailableBiFunction(getMethodForGetString1ArgThrowsChecked()); + assertThrows(CustomCheckedException.class, () -> func.apply(INSTANCE, "A")); + } + + @Test + public void testApply1ArgThrowsUnchecked() throws NoSuchMethodException, SecurityException { + // Use a local variable typed to the interface to make sure we compile. + final FailableBiFunction<MethodFixtures, String, String[], Throwable> func = MethodInvokers + .asFailableBiFunction(getMethodForGetString1ArgThrowsUnchecked()); + assertThrows(CustomUncheckedException.class, () -> func.apply(INSTANCE, "A")); + } + + @Test + public void testConstructorForNull() throws SecurityException { + assertThrows(NullPointerException.class, () -> MethodInvokers.asFailableBiFunction(null)); + } + + @Test + public void testToString() throws SecurityException, Throwable { + // Should not blow up and must return _something_ + assertFalse(MethodInvokers.asFailableBiFunction(getMethodForGetString1ArgChecked()).toString().isEmpty()); + } + +} diff --git a/src/test/java/org/apache/commons/lang3/function/MethodInvokersFailableFunctionTest.java b/src/test/java/org/apache/commons/lang3/function/MethodInvokersFailableFunctionTest.java new file mode 100644 index 0000000..8e3ddf2 --- /dev/null +++ b/src/test/java/org/apache/commons/lang3/function/MethodInvokersFailableFunctionTest.java @@ -0,0 +1,84 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.commons.lang3.function; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.apache.commons.lang3.exception.UncheckedException; +import org.junit.jupiter.api.Test; + +/** + * Tests {@link MethodInvokers#asFailableFunction(Method)}. + */ +public class MethodInvokersFailableFunctionTest extends MethodFixtures { + + @Test + public void testApply0Arg() throws Throwable { + assertEquals(INSTANCE.getString(), MethodInvokers.asFailableFunction(getMethodForGetString()).apply(INSTANCE)); + } + + @Test + public void testConstructorForNull() throws SecurityException { + assertThrows(NullPointerException.class, () -> MethodInvokers.asFailableFunction((Method) null)); + } + + @Test + public void testBuildVarArg() throws SecurityException, NoSuchMethodException { + MethodInvokers.asFailableFunction(getMethodForGetStringVarStringArgs()); + } + + @Test + public void testFindAndInvoke() throws SecurityException { + // Finding + final List<FailableFunction<Object, Object, Throwable>> invokers = Stream.of(MethodFixtures.class.getDeclaredMethods()) + .filter(m -> m.isAnnotationPresent(AnnotationTestFixture.class)).map(MethodInvokers::asFailableFunction).collect(Collectors.toList()); + assertEquals(2, invokers.size()); + // ... + // Invoking + final Set<Object> set = invokers.stream().map(i -> { + try { + return i.apply(MethodFixtures.INSTANCE); + } catch (final Throwable e) { + throw new UncheckedException(e); + } + }).collect(Collectors.toSet()); + assertEquals(new HashSet<>(Arrays.asList(INSTANCE.getString(), INSTANCE.getString2())), set); + } + + @Test + public void testThrowsChecked() throws Exception { + assertThrows(Exception.class, () -> MethodInvokers.asFailableFunction(getMethodForGetStringThrowsChecked()).apply(INSTANCE)); + } + + @Test + public void testToString() throws SecurityException, ReflectiveOperationException { + // Should not blow up and must return _something_ + assertFalse(MethodInvokers.asFailableFunction(getMethodForGetString()).toString().isEmpty()); + } + +} diff --git a/src/test/java/org/apache/commons/lang3/function/MethodInvokersFailableSupplierTest.java b/src/test/java/org/apache/commons/lang3/function/MethodInvokersFailableSupplierTest.java new file mode 100644 index 0000000..62ccc60 --- /dev/null +++ b/src/test/java/org/apache/commons/lang3/function/MethodInvokersFailableSupplierTest.java @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.commons.lang3.function; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; + +import java.lang.reflect.Method; + +import org.junit.jupiter.api.Test; + +/** + * Tests {@link MethodInvokers#asFailableSupplier(Method)}. + */ +public class MethodInvokersFailableSupplierTest extends MethodFixtures { + + @Test + public void testSupplierStatic() throws Throwable { + assertEquals(staticGetString(), MethodInvokers.asFailableSupplier(getMethodForStaticGetString()).get()); + assertEquals(staticGetString(), MethodInvokers.asFailableSupplier(getMethodForStaticGetString()).get()); + } + + @Test + public void testSupplierToString() throws SecurityException, ReflectiveOperationException { + // Should not blow up and must return _something_ + assertFalse(MethodInvokers.asFailableSupplier(getMethodForStaticGetString()).toString().isEmpty()); + } + +} diff --git a/src/test/java/org/apache/commons/lang3/function/MethodInvokersFunctionTest.java b/src/test/java/org/apache/commons/lang3/function/MethodInvokersFunctionTest.java new file mode 100644 index 0000000..eb556f9 --- /dev/null +++ b/src/test/java/org/apache/commons/lang3/function/MethodInvokersFunctionTest.java @@ -0,0 +1,101 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.commons.lang3.function; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.apache.commons.lang3.exception.CustomUncheckedException; +import org.junit.jupiter.api.Test; + +/** + * Tests {@link MethodInvokers#asFunction(Method)}. + */ +public class MethodInvokersFunctionTest extends MethodFixtures { + + @Test + public void testApply0Arg() throws NoSuchMethodException, SecurityException { + final Function<MethodFixtures, String> func = MethodInvokers.asFunction(getMethodForGetString()); + assertEquals(INSTANCE.getString(), func.apply(INSTANCE)); + } + + @Test + public void testApply0ArgThrowsUnchecked() throws NoSuchMethodException, SecurityException { + final Function<MethodFixtures, String> func = MethodInvokers.asFunction(getMethodForGetStringThrowsUnchecked()); + assertThrows(CustomUncheckedException.class, () -> func.apply(INSTANCE)); + } + + @Test + public void testBuildVarArg() throws SecurityException, NoSuchMethodException { + MethodInvokers.asFunction(getMethodForGetStringVarStringArgs()); + } + + @Test + public void testConstructorForNull() throws SecurityException { + assertThrows(NullPointerException.class, () -> MethodInvokers.asFunction(null)); + } + + @Test + public void testFindAndInvoke() throws SecurityException { + // Finding + final List<Function<Object, Object>> invokers = Stream.of(MethodFixtures.class.getDeclaredMethods()) + .filter(m -> m.isAnnotationPresent(AnnotationTestFixture.class)).map(MethodInvokers::asFunction).collect(Collectors.toList()); + assertEquals(2, invokers.size()); + // ... + // Invoking + final Set<Object> set1 = invokers.stream().map(i -> i.apply(MethodFixtures.INSTANCE)).collect(Collectors.toSet()); + assertEquals(new HashSet<>(Arrays.asList(INSTANCE.getString(), INSTANCE.getString2())), set1); + final Set<Object> set2 = Stream.of(INSTANCE).map(invokers.get(0)).collect(Collectors.toSet()); + final Set<Object> set3 = Stream.of(INSTANCE).map(invokers.get(1)).collect(Collectors.toSet()); + set2.addAll(set3); + assertEquals(new HashSet<>(Arrays.asList(INSTANCE.getString(), INSTANCE.getString2())), set2); + } + + @Test + public void testFullExample() throws SecurityException, ReflectiveOperationException { + final Method method = String.class.getMethod("length"); + final Function<String, Integer> function = MethodInvokers.asFunction(method); + assertEquals(3, function.apply("ABC")); + } + + @Test + public void testMapComputeIfAbsent() throws NoSuchMethodException, SecurityException { + final Map<MethodFixtures, String> map = new HashMap<>(); + map.computeIfAbsent(INSTANCE, MethodInvokers.asFunction(getMethodForGetString())); + assertEquals(INSTANCE.getString(), map.get(INSTANCE)); + } + + @Test + public void testToString() throws SecurityException, ReflectiveOperationException { + // Should not blow up and must return _something_ + assertFalse(MethodInvokers.asFunction(getMethodForGetString()).toString().isEmpty()); + } + +} diff --git a/src/test/java/org/apache/commons/lang3/function/MethodInvokersSupplierTest.java b/src/test/java/org/apache/commons/lang3/function/MethodInvokersSupplierTest.java new file mode 100644 index 0000000..99ba067 --- /dev/null +++ b/src/test/java/org/apache/commons/lang3/function/MethodInvokersSupplierTest.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.commons.lang3.function; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; + +import java.lang.reflect.Method; +import java.util.function.Supplier; + +import org.junit.jupiter.api.Test; + +/** + * Tests {@link MethodInvokers#asSupplier(Method)}. + */ +public class MethodInvokersSupplierTest extends MethodFixtures { + + @Test + public void testSupplierStaticGetMethod() throws NoSuchMethodException, SecurityException { + final Supplier<String> supplier = MethodInvokers.asSupplier(getMethodForStaticGetString()); + assertEquals(staticGetString(), supplier.get()); + assertEquals(staticGetString(), supplier.get()); + } + + @Test + public void testSupplierStaticGetMethodToString() throws SecurityException, ReflectiveOperationException { + // Should not blow up and must return _something_ + final Supplier<Object> supplier = MethodInvokers.asSupplier(getMethodForStaticGetString()); + assertFalse(supplier.toString().isEmpty()); + assertFalse(supplier.toString().isEmpty()); + } + +}