You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@causeway.apache.org by ah...@apache.org on 2023/02/26 05:41:53 UTC

[causeway] branch master updated: CAUSEWAY-3304: introduces Spring clones of ThrowingFunction and ThrowingConsumer (to be removed in v3)

This is an automated email from the ASF dual-hosted git repository.

ahuber pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/causeway.git


The following commit(s) were added to refs/heads/master by this push:
     new d57c6fa778 CAUSEWAY-3304: introduces Spring clones of ThrowingFunction and ThrowingConsumer (to be removed in v3)
d57c6fa778 is described below

commit d57c6fa778d95250e280c41c636031508f8929a9
Author: Andi Huber <ah...@apache.org>
AuthorDate: Sun Feb 26 06:41:47 2023 +0100

    CAUSEWAY-3304: introduces Spring clones of ThrowingFunction and
    ThrowingConsumer (to be removed in v3)
---
 .../commons/functional/ThrowingConsumer.java       | 140 ++++++++++++++++++++
 .../commons/functional/ThrowingFunction.java       | 144 +++++++++++++++++++++
 .../commons/functional/ThrowingRunnable.java       |   3 +-
 .../org/apache/causeway/commons/io/FileUtils.java  |   8 +-
 4 files changed, 290 insertions(+), 5 deletions(-)

diff --git a/commons/src/main/java/org/apache/causeway/commons/functional/ThrowingConsumer.java b/commons/src/main/java/org/apache/causeway/commons/functional/ThrowingConsumer.java
new file mode 100644
index 0000000000..2bc517efc1
--- /dev/null
+++ b/commons/src/main/java/org/apache/causeway/commons/functional/ThrowingConsumer.java
@@ -0,0 +1,140 @@
+/*
+ *  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.causeway.commons.functional;
+
+import java.util.function.BiFunction;
+import java.util.function.Consumer;
+
+/**
+ * A {@link Consumer} that allows invocation of code that throws a checked
+ * {@link Exception}.
+ *
+ * @since 2.x [@index}
+ * @param <T> the type of the input to the operation
+ * @apiNote this is a clone from <i>Spring's</i>
+ *     org.springframework.util.function.ThrowingConsumer (as was introduced with <i>Spring Framework v6</i>),
+ *     with version 3, the latter is used as a replacement and this interface is removed
+ */
+@FunctionalInterface
+public interface ThrowingConsumer<T> extends Consumer<T> {
+
+    /**
+     * Performs this operation on the given argument, possibly throwing a
+     * checked exception.
+     * @param t the input argument
+     * @throws Exception on error
+     */
+    void acceptWithException(T t) throws Exception;
+
+    /**
+     * Default {@link Consumer#accept(Object)} that wraps any thrown checked
+     * exceptions (by default in a {@link RuntimeException}).
+     * @see java.util.function.Consumer#accept(Object)
+     */
+    @Override
+    default void accept(final T t) {
+        accept(t, RuntimeException::new);
+    }
+
+    /**
+     * Performs this operation on the given argument, wrapping any thrown
+     * checked exceptions using the given {@code exceptionWrapper}.
+     * @param exceptionWrapper {@link BiFunction} that wraps the given message
+     * and checked exception into a runtime exception
+     */
+    default void accept(final T t, final BiFunction<String, Exception, RuntimeException> exceptionWrapper) {
+        try {
+            acceptWithException(t);
+        }
+        catch (RuntimeException ex) {
+            throw ex;
+        }
+        catch (Exception ex) {
+            throw exceptionWrapper.apply(ex.getMessage(), ex);
+        }
+    }
+
+    /**
+     * Return a new {@link ThrowingConsumer} where the {@link #accept(Object)}
+     * method wraps any thrown checked exceptions using the given
+     * {@code exceptionWrapper}.
+     * @param exceptionWrapper {@link BiFunction} that wraps the given message
+     * and checked exception into a runtime exception
+     * @return the replacement {@link ThrowingConsumer} instance
+     */
+    default ThrowingConsumer<T> throwing(final BiFunction<String, Exception, RuntimeException> exceptionWrapper) {
+        return new ThrowingConsumer<>() {
+
+            @Override
+            public void acceptWithException(final T t) throws Exception {
+                ThrowingConsumer.this.acceptWithException(t);
+            }
+
+            @Override
+            public void accept(final T t) {
+                accept(t, exceptionWrapper);
+            }
+
+        };
+    }
+
+    /**
+     * Lambda friendly convenience method that can be used to create a
+     * {@link ThrowingConsumer} where the {@link #accept(Object)} method wraps
+     * any checked exception thrown by the supplied lambda expression or method
+     * reference.
+     * <p>This method can be especially useful when working with method references.
+     * It allows you to easily convert a method that throws a checked exception
+     * into an instance compatible with a regular {@link Consumer}.
+     * <p>For example:
+     * <pre class="code">
+     * list.forEach(ThrowingConsumer.of(Example::methodThatCanThrowCheckedException));
+     * </pre>
+     * @param <T> the type of the input to the operation
+     * @param consumer the source consumer
+     * @return a new {@link ThrowingConsumer} instance
+     */
+    static <T> ThrowingConsumer<T> of(final ThrowingConsumer<T> consumer) {
+        return consumer;
+    }
+
+    /**
+     * Lambda friendly convenience method that can be used to create a
+     * {@link ThrowingConsumer} where the {@link #accept(Object)} method wraps
+     * any thrown checked exceptions using the given {@code exceptionWrapper}.
+     * <p>This method can be especially useful when working with method references.
+     * It allows you to easily convert a method that throws a checked exception
+     * into an instance compatible with a regular {@link Consumer}.
+     * <p>For example:
+     * <pre class="code">
+     * list.forEach(ThrowingConsumer.of(Example::methodThatCanThrowCheckedException, IllegalStateException::new));
+     * </pre>
+     * @param <T> the type of the input to the operation
+     * @param consumer the source consumer
+     * @param exceptionWrapper the exception wrapper to use
+     * @return a new {@link ThrowingConsumer} instance
+     */
+    static <T> ThrowingConsumer<T> of(final ThrowingConsumer<T> consumer,
+            final BiFunction<String, Exception, RuntimeException> exceptionWrapper) {
+
+        return consumer.throwing(exceptionWrapper);
+    }
+
+}
+
diff --git a/commons/src/main/java/org/apache/causeway/commons/functional/ThrowingFunction.java b/commons/src/main/java/org/apache/causeway/commons/functional/ThrowingFunction.java
new file mode 100644
index 0000000000..a25c5f87f0
--- /dev/null
+++ b/commons/src/main/java/org/apache/causeway/commons/functional/ThrowingFunction.java
@@ -0,0 +1,144 @@
+/*
+ *  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.causeway.commons.functional;
+
+import java.util.function.BiFunction;
+import java.util.function.Function;
+
+/**
+ * A {@link Function} that allows invocation of code that throws a checked
+ * {@link Exception}.
+ *
+ * @since 2.x {index}
+ * @param <T> the type of the input to the function
+ * @param <R> the type of the result of the function
+ * @apiNote this is a clone from <i>Spring's</i>
+ *     org.springframework.util.function.ThrowingFunction (as was introduced with <i>Spring Framework v6</i>);
+ *     with version 3, the latter is used as a replacement and this interface is removed
+ */
+@FunctionalInterface
+public interface ThrowingFunction<T, R> extends Function<T, R> {
+
+    /**
+     * Applies this function to the given argument, possibly throwing a checked
+     * exception.
+     * @param t the function argument
+     * @return the function result
+     * @throws Exception on error
+     */
+    R applyWithException(T t) throws Exception;
+
+    /**
+     * Default {@link Function#apply(Object)} that wraps any thrown checked
+     * exceptions (by default in a {@link RuntimeException}).
+     * @see java.util.function.Function#apply(java.lang.Object)
+     */
+    @Override
+    default R apply(final T t) {
+        return apply(t, RuntimeException::new);
+    }
+
+    /**
+     * Applies this function to the given argument, wrapping any thrown checked
+     * exceptions using the given {@code exceptionWrapper}.
+     * @param exceptionWrapper {@link BiFunction} that wraps the given message
+     * and checked exception into a runtime exception
+     * @return a result
+     */
+    default R apply(final T t, final BiFunction<String, Exception, RuntimeException> exceptionWrapper) {
+        try {
+            return applyWithException(t);
+        }
+        catch (RuntimeException ex) {
+            throw ex;
+        }
+        catch (Exception ex) {
+            throw exceptionWrapper.apply(ex.getMessage(), ex);
+        }
+    }
+
+    /**
+     * Return a new {@link ThrowingFunction} where the {@link #apply(Object)}
+     * method wraps any thrown checked exceptions using the given
+     * {@code exceptionWrapper}.
+     * @param exceptionWrapper {@link BiFunction} that wraps the given message
+     * and checked exception into a runtime exception
+     * @return the replacement {@link ThrowingFunction} instance
+     */
+    default ThrowingFunction<T, R> throwing(final BiFunction<String, Exception, RuntimeException> exceptionWrapper) {
+        return new ThrowingFunction<>() {
+
+            @Override
+            public R applyWithException(final T t) throws Exception {
+                return ThrowingFunction.this.applyWithException(t);
+            }
+
+            @Override
+            public R apply(final T t) {
+                return apply(t, exceptionWrapper);
+            }
+
+        };
+    }
+
+    /**
+     * Lambda friendly convenience method that can be used to create a
+     * {@link ThrowingFunction} where the {@link #apply(Object)} method wraps
+     * any checked exception thrown by the supplied lambda expression or method
+     * reference.
+     * <p>This method can be especially useful when working with method references.
+     * It allows you to easily convert a method that throws a checked exception
+     * into an instance compatible with a regular {@link Function}.
+     * <p>For example:
+     * <pre class="code">
+     * stream.map(ThrowingFunction.of(Example::methodThatCanThrowCheckedException));
+     * </pre>
+     * @param <T> the type of the input to the function
+     * @param <R> the type of the result of the function
+     * @param function the source function
+     * @return a new {@link ThrowingFunction} instance
+     */
+    static <T, R> ThrowingFunction<T, R> of(final ThrowingFunction<T, R> function) {
+        return function;
+    }
+
+    /**
+     * Lambda friendly convenience method that can be used to create a
+     * {@link ThrowingFunction} where the {@link #apply(Object)} method wraps
+     * any thrown checked exceptions using the given {@code exceptionWrapper}.
+     * <p>This method can be especially useful when working with method references.
+     * It allows you to easily convert a method that throws a checked exception
+     * into an instance compatible with a regular {@link Function}.
+     * <p>For example:
+     * <pre class="code">
+     * stream.map(ThrowingFunction.of(Example::methodThatCanThrowCheckedException, IllegalStateException::new));
+     * </pre>
+     * @param <T> the type of the input to the function
+     * @param <R> the type of the result of the function
+     * @param function the source function
+     * @param exceptionWrapper the exception wrapper to use
+     * @return a new {@link ThrowingFunction} instance
+     */
+    static <T, R> ThrowingFunction<T, R> of(final ThrowingFunction<T, R> function,
+            final BiFunction<String, Exception, RuntimeException> exceptionWrapper) {
+
+        return function.throwing(exceptionWrapper);
+    }
+
+}
diff --git a/commons/src/main/java/org/apache/causeway/commons/functional/ThrowingRunnable.java b/commons/src/main/java/org/apache/causeway/commons/functional/ThrowingRunnable.java
index a5f3ea2e4d..0b378566bb 100644
--- a/commons/src/main/java/org/apache/causeway/commons/functional/ThrowingRunnable.java
+++ b/commons/src/main/java/org/apache/causeway/commons/functional/ThrowingRunnable.java
@@ -23,14 +23,13 @@ import java.util.concurrent.Callable;
 import lombok.NonNull;
 
 /**
- * Similar to a {@link Runnable}, except that it can also throw exceptions.
+ * Similar to a {@link Runnable}, except that it can also throw a checked {@link Exception}.
  *
  * @since 2.x [@index}
  */
 @FunctionalInterface
 public interface ThrowingRunnable {
 
-
     // -- INTERFACE
 
     void run() throws Exception;
diff --git a/commons/src/main/java/org/apache/causeway/commons/io/FileUtils.java b/commons/src/main/java/org/apache/causeway/commons/io/FileUtils.java
index 77675e0823..9c4cc02910 100644
--- a/commons/src/main/java/org/apache/causeway/commons/io/FileUtils.java
+++ b/commons/src/main/java/org/apache/causeway/commons/io/FileUtils.java
@@ -37,6 +37,8 @@ import java.util.function.Predicate;
 
 import org.springframework.lang.Nullable;
 
+import org.apache.causeway.commons.functional.ThrowingConsumer;
+import org.apache.causeway.commons.functional.ThrowingFunction;
 import org.apache.causeway.commons.functional.Try;
 import org.apache.causeway.commons.internal.exceptions._Exceptions;
 
@@ -60,7 +62,7 @@ public class FileUtils {
      * @return either a successful or failed {@link Try} (non-null);
      *     if the file is null or not readable, the failure may hold a {@link NoSuchFileException} or other i/o related exceptions
      */
-    public Try<Void> tryRead(final @Nullable File file, final @NonNull Consumer<InputStream> inputStreamConsumer) {
+    public Try<Void> tryReadAndAccept(final @Nullable File file, final @NonNull ThrowingConsumer<InputStream> inputStreamConsumer) {
         return Try.run(()->{
             try(val inputStream = new FileInputStream(existingFileElseFail(file))){
                 inputStreamConsumer.accept(inputStream);
@@ -70,13 +72,13 @@ public class FileUtils {
 
     /**
      * Opens an {@link InputStream} for give {@link File}
-     * and passes it to given {@link Function} for applying (mapping),
+     * and passes it to given {@link Function} for applying,
      * then finally closes it.
      * @return either a successful or failed {@link Try} (non-null),
      *      where in the success case, the Try is holding the returned value from the given {@link Function inputStreamMapper};
      *      if the file is null or not readable, the failure may hold a {@link NoSuchFileException} or other i/o related exceptions
      */
-    public <T> Try<T> tryMap(final @Nullable File file, final @NonNull Function<InputStream, T> inputStreamMapper) {
+    public <T> Try<T> tryReadAndApply(final @Nullable File file, final @NonNull ThrowingFunction<InputStream, T> inputStreamMapper) {
         return Try.call(()->{
             try(val inputStream = new FileInputStream(existingFileElseFail(file))){
                 return inputStreamMapper.apply(inputStream);