This is an automated email from the ASF dual-hosted git repository. jochen 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 bc16e01 PR: LANG-1447 bc16e01 is described below commit bc16e0109a30b819c50895e157830fe9b3f8e93f Author: Jochen Wiedmann <jochen.wiedm...@gmail.com> AuthorDate: Thu Aug 22 22:09:11 2019 +0200 PR: LANG-1447 Added Functions.as* methods. --- pom.xml | 3 + src/changes/changes.xml | 1 + .../java/org/apache/commons/lang3/Functions.java | 150 ++++++++++++++++++ .../org/apache/commons/lang3/FunctionsTest.java | 174 ++++++++++++++++++++- 4 files changed, 327 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0ae407d..e9727c6 100644 --- a/pom.xml +++ b/pom.xml @@ -505,6 +505,9 @@ <contributor> <name>Mark Dacek</name> </contributor> + <contributor> + <name>Peter Verhas</name> + </contributor> </contributors> <!-- Lang should depend on very little --> diff --git a/src/changes/changes.xml b/src/changes/changes.xml index d22ea6b..4535fa9 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -60,6 +60,7 @@ The <action> type attribute can be add,update,fix,remove. <action type="update" dev="ggregory">commons.japicmp.version 0.13.1 -> 0.14.1.</action> <action type="update" dev="ggregory">checkstyle.version 8.18 -> 8.23.</action> <action type="update" dev="ggregory">junit-jupiter 5.5.0 -> 5.5.1.</action> + <action issue="LANG-1477" type="add" dev="jochen">Added Functions.as*, and tests thereof, as suggested by Peter Verhas</action> </release> <release version="3.9" date="2019-04-09" description="New features and bug fixes. Requires Java 8, supports Java 9, 10, 11"> diff --git a/src/main/java/org/apache/commons/lang3/Functions.java b/src/main/java/org/apache/commons/lang3/Functions.java index ec4db67..5c7d032 100644 --- a/src/main/java/org/apache/commons/lang3/Functions.java +++ b/src/main/java/org/apache/commons/lang3/Functions.java @@ -19,6 +19,14 @@ package org.apache.commons.lang3; import java.io.IOException; import java.io.UncheckedIOException; import java.lang.reflect.UndeclaredThrowableException; +import java.util.concurrent.Callable; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.BiPredicate; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.Supplier; /** This class provides utility functions, and classes for working with the @@ -124,6 +132,132 @@ public class Functions { */ boolean test(O1 pObject1, O2 pObject2) throws T; } + @FunctionalInterface + public interface FailableSupplier<O, T extends Throwable> { + /** + * Supplies an object + * @return the suppliers result + * @throws T if the supplier fails + */ + O get() throws T; + } + + /** + * Converts the given {@link FailableRunnable} into a standard {@link Runnable}. + */ + public static Runnable asRunnable(FailableRunnable<?> pRunnable) { + return () -> { + try { + pRunnable.run(); + } catch (Throwable t) { + throw rethrow(t); + } + }; + } + + /** + * Converts the given {@link FailableConsumer} into a standard {@link Consumer}. + */ + public static <I> Consumer<I> asConsumer(FailableConsumer<I,?> pConsumer) { + return (pInput) -> { + try { + pConsumer.accept(pInput); + } catch (Throwable t) { + throw rethrow(t); + } + }; + } + + /** + * Converts the given {@link FailableCallable} into a standard {@link Callable}. + */ + public static <O> Callable<O> asCallable(FailableCallable<O,?> pCallable) { + return () -> { + try { + return pCallable.call(); + } catch (Throwable t) { + throw rethrow(t); + } + }; + } + + /** + * Converts the given {@link FailableBiConsumer} into a standard {@link BiConsumer}. + */ + public static <I1,I2> BiConsumer<I1,I2> asBiConsumer(FailableBiConsumer<I1,I2,?> pConsumer) { + return (pInput1, pInput2) -> { + try { + pConsumer.accept(pInput1, pInput2); + } catch (Throwable t) { + throw rethrow(t); + } + }; + } + + /** + * Converts the given {@link FailableFunction} into a standard {@link Function}. + */ + public static <I,O> Function<I,O> asFunction(FailableFunction<I,O,?> pFunction) { + return (pInput) -> { + try { + return pFunction.apply(pInput); + } catch (Throwable t) { + throw rethrow(t); + } + }; + } + + /** + * Converts the given {@link FailableBiFunction} into a standard {@link BiFunction}. + */ + public static <I1,I2,O> BiFunction<I1,I2,O> asBiFunction(FailableBiFunction<I1,I2,O,?> pFunction) { + return (pInput1, pInput2) -> { + try { + return pFunction.apply(pInput1, pInput2); + } catch (Throwable t) { + throw rethrow(t); + } + }; + } + + /** + * Converts the given {@link FailablePredicate} into a standard {@link Predicate}. + */ + public static <I> Predicate<I> asPredicate(FailablePredicate<I,?> pPredicate) { + return (pInput) -> { + try { + return pPredicate.test(pInput); + } catch (Throwable t) { + throw rethrow(t); + } + }; + } + + /** + * Converts the given {@link FailableBiPredicate} into a standard {@link BiPredicate}. + */ + public static <I1,I2> BiPredicate<I1,I2> asBiPredicate(FailableBiPredicate<I1,I2,?> pPredicate) { + return (pInput1, pInput2) -> { + try { + return pPredicate.test(pInput1, pInput2); + } catch (Throwable t) { + throw rethrow(t); + } + }; + } + + /** + * Converts the given {@link FailableSupplier} into a standard {@link Supplier}. + */ + public static <O> Supplier<O> asSupplier(FailableSupplier<O,?> pSupplier) { + return () -> { + try { + return pSupplier.get(); + } catch (Throwable t) { + throw rethrow(t); + } + }; + } /** * Runs a runnable and rethrows any exception as a {@link RuntimeException}. @@ -256,6 +390,22 @@ public class Functions { } /** + * Invokes the supplier, and returns the result. + * @param pSupplier The supplier to invoke. + * @param <O> The suppliers output type. + * @param <T> The type of checked exception, which the supplier can throw. + * @return The object, which has been created by the supplier + */ + public static <O,T extends Throwable> O get(FailableSupplier<O,T> pSupplier) { + try { + return pSupplier.get(); + } catch (Throwable t) { + throw rethrow(t); + } + } + + + /** * A simple try-with-resources implementation, that can be used, if your * objects do not implement the {@link AutoCloseable} interface. The method * executes the {@code pAction}. The method guarantees, that <em>all</em> diff --git a/src/test/java/org/apache/commons/lang3/FunctionsTest.java b/src/test/java/org/apache/commons/lang3/FunctionsTest.java index 0808b80..d70c675 100644 --- a/src/test/java/org/apache/commons/lang3/FunctionsTest.java +++ b/src/test/java/org/apache/commons/lang3/FunctionsTest.java @@ -19,8 +19,19 @@ package org.apache.commons.lang3; import java.io.IOException; import java.io.UncheckedIOException; import java.lang.reflect.UndeclaredThrowableException; - +import java.util.concurrent.Callable; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Supplier; + +import org.apache.commons.lang3.Functions.FailableBiConsumer; +import org.apache.commons.lang3.Functions.FailableBiFunction; +import org.apache.commons.lang3.Functions.FailableCallable; import org.apache.commons.lang3.Functions.FailableConsumer; +import org.apache.commons.lang3.Functions.FailableFunction; +import org.apache.commons.lang3.Functions.FailableSupplier; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -28,6 +39,7 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertFalse; class FunctionsTest { public static class SomeException extends Exception { @@ -128,6 +140,20 @@ class FunctionsTest { } @Test + void testAsRunnable() { + FailureOnOddInvocations.invocation = 0; + Runnable runnable = Functions.asRunnable(() -> new FailureOnOddInvocations()); + UndeclaredThrowableException e = assertThrows(UndeclaredThrowableException.class, () -> runnable.run()); + final Throwable cause = e.getCause(); + assertNotNull(cause); + assertTrue(cause instanceof SomeException); + assertEquals("Odd Invocation: 1", cause.getMessage()); + + // Even invocation, should not throw an exception + runnable.run(); + } + + @Test void testCallable() { FailureOnOddInvocations.invocation = 0; UndeclaredThrowableException e = assertThrows(UndeclaredThrowableException.class, () -> Functions.run(FailureOnOddInvocations::new)); @@ -140,6 +166,25 @@ class FunctionsTest { } @Test + void testAsCallable() { + FailureOnOddInvocations.invocation = 0; + final FailableCallable<FailureOnOddInvocations,SomeException> failableCallable = () -> { return new FailureOnOddInvocations(); }; + final Callable<FailureOnOddInvocations> callable = Functions.asCallable(failableCallable); + UndeclaredThrowableException e = assertThrows(UndeclaredThrowableException.class, () -> callable.call()); + final Throwable cause = e.getCause(); + assertNotNull(cause); + assertTrue(cause instanceof SomeException); + assertEquals("Odd Invocation: 1", cause.getMessage()); + final FailureOnOddInvocations instance; + try { + instance = callable.call(); + } catch (Exception ex) { + throw Functions.rethrow(ex); + } + assertNotNull(instance); + } + + @Test void testAcceptConsumer() { final IllegalStateException ise = new IllegalStateException(); final Testable testable = new Testable(ise); @@ -163,6 +208,30 @@ class FunctionsTest { } @Test + void testAsConsumer() { + final IllegalStateException ise = new IllegalStateException(); + final Testable testable = new Testable(ise); + final Consumer<Testable> consumer = Functions.asConsumer((t) -> t.test()); + Throwable e = assertThrows(IllegalStateException.class, () -> consumer.accept(testable)); + assertSame(ise, e); + + final Error error = new OutOfMemoryError(); + testable.setThrowable(error); + e = assertThrows(OutOfMemoryError.class, () -> consumer.accept(testable)); + assertSame(error, e); + + final IOException ioe = new IOException("Unknown I/O error"); + testable.setThrowable(ioe); + e = assertThrows(UncheckedIOException.class, () -> consumer.accept(testable)); + final Throwable t = e.getCause(); + assertNotNull(t); + assertSame(ioe, t); + + testable.setThrowable(null); + Functions.accept(Testable::test, testable); + } + + @Test void testAcceptBiConsumer() { final IllegalStateException ise = new IllegalStateException(); final Testable testable = new Testable(null); @@ -185,6 +254,29 @@ class FunctionsTest { } @Test + void testAsBiConsumer() { + final IllegalStateException ise = new IllegalStateException(); + final Testable testable = new Testable(null); + final FailableBiConsumer<Testable, Throwable, Throwable> failableBiConsumer = (t, th) -> { t.setThrowable(th); t.test(); }; + final BiConsumer<Testable, Throwable> consumer = Functions.asBiConsumer(failableBiConsumer); + Throwable e = assertThrows(IllegalStateException.class, () -> consumer.accept(testable, ise)); + assertSame(ise, e); + + final Error error = new OutOfMemoryError(); + e = assertThrows(OutOfMemoryError.class, () -> consumer.accept(testable, error)); + assertSame(error, e); + + final IOException ioe = new IOException("Unknown I/O error"); + testable.setThrowable(ioe); + e = assertThrows(UncheckedIOException.class, () -> consumer.accept(testable, ioe)); + final Throwable t = e.getCause(); + assertNotNull(t); + assertSame(ioe, t); + + consumer.accept(testable, null); + } + + @Test public void testApplyFunction() { final IllegalStateException ise = new IllegalStateException(); final Testable testable = new Testable(ise); @@ -210,6 +302,33 @@ class FunctionsTest { } @Test + public void testAsFunction() { + final IllegalStateException ise = new IllegalStateException(); + final Testable testable = new Testable(ise); + final FailableFunction<Throwable,Integer,Throwable> failableFunction = (th) -> { + testable.setThrowable(th); + return Integer.valueOf(testable.testInt()); + }; + final Function<Throwable,Integer> function = Functions.asFunction(failableFunction); + Throwable e = assertThrows(IllegalStateException.class, () -> function.apply(ise)); + assertSame(ise, e); + + final Error error = new OutOfMemoryError(); + testable.setThrowable(error); + e = assertThrows(OutOfMemoryError.class, () -> function.apply(error)); + assertSame(error, e); + + final IOException ioe = new IOException("Unknown I/O error"); + testable.setThrowable(ioe); + e = assertThrows(UncheckedIOException.class, () -> function.apply(ioe)); + final Throwable t = e.getCause(); + assertNotNull(t); + assertSame(ioe, t); + + assertEquals(0, function.apply(null).intValue()); + } + + @Test public void testApplyBiFunction() { final IllegalStateException ise = new IllegalStateException(); final Testable testable = new Testable(null); @@ -232,6 +351,59 @@ class FunctionsTest { } @Test + public void testAsBiFunction() { + final IllegalStateException ise = new IllegalStateException(); + final Testable testable = new Testable(ise); + final FailableBiFunction<Testable,Throwable,Integer,Throwable> failableBiFunction = (t, th) -> { + t.setThrowable(th); + return Integer.valueOf(t.testInt()); + }; + final BiFunction<Testable,Throwable,Integer> biFunction = Functions.asBiFunction(failableBiFunction); + Throwable e = assertThrows(IllegalStateException.class, () -> biFunction.apply(testable, ise)); + assertSame(ise, e); + + final Error error = new OutOfMemoryError(); + testable.setThrowable(error); + e = assertThrows(OutOfMemoryError.class, () -> biFunction.apply(testable, error)); + assertSame(error, e); + + final IOException ioe = new IOException("Unknown I/O error"); + testable.setThrowable(ioe); + e = assertThrows(UncheckedIOException.class, () -> biFunction.apply(testable, ioe)); + final Throwable t = e.getCause(); + assertNotNull(t); + assertSame(ioe, t); + + assertEquals(0, biFunction.apply(testable, null).intValue()); + } + + @Test + public void testGetFromSupplier() { + FailureOnOddInvocations.invocation = 0; + UndeclaredThrowableException e = assertThrows(UndeclaredThrowableException.class, () -> Functions.run(FailureOnOddInvocations::new)); + final Throwable cause = e.getCause(); + assertNotNull(cause); + assertTrue(cause instanceof SomeException); + assertEquals("Odd Invocation: 1", cause.getMessage()); + final FailureOnOddInvocations instance = Functions.call(FailureOnOddInvocations::new); + assertNotNull(instance); + } + + @Test + public void testAsSupplier() { + FailureOnOddInvocations.invocation = 0; + final FailableSupplier<FailureOnOddInvocations,Throwable> failableSupplier = () -> { return new FailureOnOddInvocations(); }; + final Supplier<FailureOnOddInvocations> supplier = Functions.asSupplier(failableSupplier); + UndeclaredThrowableException e = assertThrows(UndeclaredThrowableException.class, () -> supplier.get()); + final Throwable cause = e.getCause(); + assertNotNull(cause); + assertTrue(cause instanceof SomeException); + assertEquals("Odd Invocation: 1", cause.getMessage()); + final FailureOnOddInvocations instance = supplier.get(); + assertNotNull(instance); + } + + @Test public void testTryWithResources() { final CloseableObject co = new CloseableObject(); final FailableConsumer<Throwable, ? extends Throwable> consumer = co::run;