diff --git a/pom.xml b/pom.xml index 0ae407d7f..e9727c6e9 100644 --- a/pom.xml +++ b/pom.xml @@ -505,6 +505,9 @@ Mark Dacek + + Peter Verhas + diff --git a/src/changes/changes.xml b/src/changes/changes.xml index d22ea6b01..4535fa979 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -60,6 +60,7 @@ The type attribute can be add,update,fix,remove. commons.japicmp.version 0.13.1 -> 0.14.1. checkstyle.version 8.18 -> 8.23. junit-jupiter 5.5.0 -> 5.5.1. + Added Functions.as*, and tests thereof, as suggested by Peter Verhas diff --git a/src/main/java/org/apache/commons/lang3/Functions.java b/src/main/java/org/apache/commons/lang3/Functions.java index ec4db67ae..5c7d03254 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 { + /** + * 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 Consumer asConsumer(FailableConsumer 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 Callable asCallable(FailableCallable pCallable) { + return () -> { + try { + return pCallable.call(); + } catch (Throwable t) { + throw rethrow(t); + } + }; + } + + /** + * Converts the given {@link FailableBiConsumer} into a standard {@link BiConsumer}. + */ + public static BiConsumer asBiConsumer(FailableBiConsumer 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 Function asFunction(FailableFunction 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 BiFunction asBiFunction(FailableBiFunction 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 Predicate asPredicate(FailablePredicate 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 BiPredicate asBiPredicate(FailableBiPredicate 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 Supplier asSupplier(FailableSupplier pSupplier) { + return () -> { + try { + return pSupplier.get(); + } catch (Throwable t) { + throw rethrow(t); + } + }; + } /** * Runs a runnable and rethrows any exception as a {@link RuntimeException}. @@ -255,6 +389,22 @@ public class Functions { } } + /** + * Invokes the supplier, and returns the result. + * @param pSupplier The supplier to invoke. + * @param The suppliers output type. + * @param The type of checked exception, which the supplier can throw. + * @return The object, which has been created by the supplier + */ + public static O get(FailableSupplier 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 diff --git a/src/test/java/org/apache/commons/lang3/FunctionsTest.java b/src/test/java/org/apache/commons/lang3/FunctionsTest.java index 0808b8069..d70c675bf 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 { @@ -127,6 +139,20 @@ class FunctionsTest { Functions.run(FailureOnOddInvocations::new); } + @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; @@ -139,6 +165,25 @@ class FunctionsTest { assertNotNull(instance); } + @Test + void testAsCallable() { + FailureOnOddInvocations.invocation = 0; + final FailableCallable failableCallable = () -> { return new FailureOnOddInvocations(); }; + final Callable 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(); @@ -162,6 +207,30 @@ class FunctionsTest { Functions.accept(Testable::test, testable); } + @Test + void testAsConsumer() { + final IllegalStateException ise = new IllegalStateException(); + final Testable testable = new Testable(ise); + final Consumer 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(); @@ -184,6 +253,29 @@ class FunctionsTest { Functions.accept(Testable::test, testable, (Throwable) null); } + @Test + void testAsBiConsumer() { + final IllegalStateException ise = new IllegalStateException(); + final Testable testable = new Testable(null); + final FailableBiConsumer failableBiConsumer = (t, th) -> { t.setThrowable(th); t.test(); }; + final BiConsumer 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(); @@ -209,6 +301,33 @@ class FunctionsTest { assertEquals(0, i.intValue()); } + @Test + public void testAsFunction() { + final IllegalStateException ise = new IllegalStateException(); + final Testable testable = new Testable(ise); + final FailableFunction failableFunction = (th) -> { + testable.setThrowable(th); + return Integer.valueOf(testable.testInt()); + }; + final Function 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(); @@ -231,6 +350,59 @@ class FunctionsTest { assertEquals(0, i.intValue()); } + @Test + public void testAsBiFunction() { + final IllegalStateException ise = new IllegalStateException(); + final Testable testable = new Testable(ise); + final FailableBiFunction failableBiFunction = (t, th) -> { + t.setThrowable(th); + return Integer.valueOf(t.testInt()); + }; + final BiFunction 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 failableSupplier = () -> { return new FailureOnOddInvocations(); }; + final Supplier 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();