From b3efdf3c2b5b55624164d184d237795be6e7b3b5 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Wed, 13 Apr 2022 16:43:31 -0700 Subject: [PATCH] Add Throwable functional interfaces Add 'Throwable' variants of the `Consumer`, `Function`, `BiFunction` and `Supplier` interfaces that wrap checked exceptions or allow calls to be made that throw them. Closes gh-28417 --- .../util/function/ThrowingBiFunction.java | 135 ++++++++++++++++++ .../util/function/ThrowingConsumer.java | 121 ++++++++++++++++ .../util/function/ThrowingFunction.java | 126 ++++++++++++++++ .../util/function/ThrowingSupplier.java | 120 ++++++++++++++++ .../function/ThrowingBiFunctionTests.java | 87 +++++++++++ .../util/function/ThrowingConsumerTests.java | 87 +++++++++++ .../util/function/ThrowingFunctionTests.java | 87 +++++++++++ .../util/function/ThrowingSupplierTests.java | 88 ++++++++++++ 8 files changed, 851 insertions(+) create mode 100644 spring-core/src/main/java/org/springframework/util/function/ThrowingBiFunction.java create mode 100644 spring-core/src/main/java/org/springframework/util/function/ThrowingConsumer.java create mode 100644 spring-core/src/main/java/org/springframework/util/function/ThrowingFunction.java create mode 100644 spring-core/src/main/java/org/springframework/util/function/ThrowingSupplier.java create mode 100644 spring-core/src/test/java/org/springframework/util/function/ThrowingBiFunctionTests.java create mode 100644 spring-core/src/test/java/org/springframework/util/function/ThrowingConsumerTests.java create mode 100644 spring-core/src/test/java/org/springframework/util/function/ThrowingFunctionTests.java create mode 100644 spring-core/src/test/java/org/springframework/util/function/ThrowingSupplierTests.java diff --git a/spring-core/src/main/java/org/springframework/util/function/ThrowingBiFunction.java b/spring-core/src/main/java/org/springframework/util/function/ThrowingBiFunction.java new file mode 100644 index 00000000000..9f5f4ef5646 --- /dev/null +++ b/spring-core/src/main/java/org/springframework/util/function/ThrowingBiFunction.java @@ -0,0 +1,135 @@ +/* + * Copyright 2002-2022 the original author or authors. + * + * Licensed 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 + * + * https://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.springframework.util.function; + +import java.util.function.BiFunction; + +/** + * A {@link BiFunction} that allows invocation of code that throws a checked + * exception. + * + * @author Stephane Nicoll + * @author Phillip Webb + * @since 6.0 + * @param the type of the first argument to the function + * @param the type of the second argument to the function + * @param the type of the result of the function + */ +public interface ThrowingBiFunction extends BiFunction { + + /** + * Applies this function to the given argument, possibly throwing a checked + * exception. + * @param t the first function argument + * @param u the second function argument + * @return the function result + * @throws Exception on error + */ + R applyWithException(T t, U u) throws Exception; + + /** + * Default {@link BiFunction#apply(Object, Object)} that wraps any thrown + * checked exceptions (by default in a {@link RuntimeException}). + * @param t the first function argument + * @param u the second function argument + * @return the function result + * @see java.util.function.BiFunction#apply(Object, Object) + */ + @Override + default R apply(T t, U u) { + return apply(t, u, RuntimeException::new); + } + + /** + * Applies this function to the given argument, wrapping any thrown checked + * exceptions using the given {@code exceptionWrapper}. + * @param t the first function argument + * @param u the second function argument + * @param exceptionWrapper {@link BiFunction} that wraps the given message + * and checked exception into a runtime exception + * @return a result + */ + default R apply(T t, U u, BiFunction exceptionWrapper) { + try { + return applyWithException(t, u); + } + catch (RuntimeException ex) { + throw ex; + } + catch (Exception ex) { + throw exceptionWrapper.apply(ex.getMessage(), ex); + } + } + + /** + * Return a new {@link ThrowingBiFunction} where the + * {@link #apply(Object, 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 ThrowingBiFunction} instance + */ + default ThrowingBiFunction throwing(BiFunction exceptionWrapper) { + return new ThrowingBiFunction<>() { + + @Override + public R applyWithException(T t, U u) throws Exception { + return ThrowingBiFunction.this.applyWithException(t, u); + } + + @Override + public R apply(T t, U u) { + return apply(t, u, exceptionWrapper); + } + + }; + } + + /** + * Lambda friendly convenience method that can be used to create + * {@link ThrowingBiFunction} where the {@link #apply(Object, Object)} + * method wraps any thrown checked exceptions using the given + * {@code exceptionWrapper}. + * @param the type of the first argument to the function + * @param the type of the second argument to the function + * @param the type of the result of the function + * @param function the source function + * @return a new {@link ThrowingFunction} instance + */ + static ThrowingBiFunction of(ThrowingBiFunction function) { + return function; + } + + /** + * Lambda friendly convenience method that can be used to create + * {@link ThrowingBiFunction} where the {@link #apply(Object, Object)} + * method wraps any thrown checked exceptions using the given + * {@code exceptionWrapper}. + * @param the type of the first argument to the function + * @param the type of the second argument to the function + * @param 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 ThrowingBiFunction of(ThrowingBiFunction function, + BiFunction exceptionWrapper) { + + return function.throwing(exceptionWrapper); + } + +} diff --git a/spring-core/src/main/java/org/springframework/util/function/ThrowingConsumer.java b/spring-core/src/main/java/org/springframework/util/function/ThrowingConsumer.java new file mode 100644 index 00000000000..a8c4f4778dd --- /dev/null +++ b/spring-core/src/main/java/org/springframework/util/function/ThrowingConsumer.java @@ -0,0 +1,121 @@ +/* + * Copyright 2002-2022 the original author or authors. + * + * Licensed 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 + * + * https://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.springframework.util.function; + +import java.util.function.BiFunction; +import java.util.function.Consumer; + +/** + * A {@link Consumer} that allows invocation of code that throws a checked + * exception. + * + * @author Stephane Nicoll + * @author Phillip Webb + * @since 6.0 + * @param the type of the input to the operation + */ +@FunctionalInterface +public interface ThrowingConsumer extends Consumer { + + /** + * 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(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(T t,BiFunction 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 throwing(BiFunction exceptionWrapper) { + return new ThrowingConsumer<>() { + + @Override + public void acceptWithException(T t) throws Exception { + ThrowingConsumer.this.acceptWithException(t); + } + + @Override + public void accept(T t) { + accept(t, exceptionWrapper); + } + + }; + } + + /** + * Lambda friendly convenience method that can be used to create + * {@link ThrowingConsumer} where the {@link #accept(Object)} method wraps + * any thrown checked exceptions using the given {@code exceptionWrapper}. + * @param the type of the input to the operation + * @param consumer the source consumer + * @return a new {@link ThrowingConsumer} instance + */ + static ThrowingConsumer of(ThrowingConsumer consumer) { + return consumer; + } + + /** + * Lambda friendly convenience method that can be used to create + * {@link ThrowingConsumer} where the {@link #accept(Object)} method wraps + * any thrown checked exceptions using the given {@code exceptionWrapper}. + * @param 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 ThrowingConsumer of(ThrowingConsumer consumer, + BiFunction exceptionWrapper) { + + return consumer.throwing(exceptionWrapper); + } + +} diff --git a/spring-core/src/main/java/org/springframework/util/function/ThrowingFunction.java b/spring-core/src/main/java/org/springframework/util/function/ThrowingFunction.java new file mode 100644 index 00000000000..a0baadb81fe --- /dev/null +++ b/spring-core/src/main/java/org/springframework/util/function/ThrowingFunction.java @@ -0,0 +1,126 @@ +/* + * Copyright 2002-2022 the original author or authors. + * + * Licensed 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 + * + * https://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.springframework.util.function; + +import java.util.function.BiFunction; +import java.util.function.Function; + +/** + * A {@link Function} that allows invocation of code that throws a checked + * exception. + * + * @author Stephane Nicoll + * @author Phillip Webb + * @since 6.0 + * @param the type of the input to the function + * @param the type of the result of the function + */ +@FunctionalInterface +public interface ThrowingFunction extends Function { + + /** + * 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(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(T t, BiFunction 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 throwing(BiFunction exceptionWrapper) { + return new ThrowingFunction<>() { + + @Override + public R applyWithException(T t) throws Exception { + return ThrowingFunction.this.applyWithException(t); + } + + @Override + public R apply(T t) { + return apply(t, exceptionWrapper); + } + + }; + } + + /** + * Lambda friendly convenience method that can be used to create + * {@link ThrowingFunction} where the {@link #apply(Object)} method wraps + * any thrown checked exceptions using the given {@code exceptionWrapper}. + * @param the type of the input to the function + * @param the type of the result of the function + * @param function the source function + * @return a new {@link ThrowingFunction} instance + */ + static ThrowingFunction of(ThrowingFunction function) { + return function; + } + + /** + * Lambda friendly convenience method that can be used to create + * {@link ThrowingFunction} where the {@link #apply(Object)} method wraps + * any thrown checked exceptions using the given {@code exceptionWrapper}. + * @param the type of the input to the function + * @param 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 ThrowingFunction of(ThrowingFunction function, + BiFunction exceptionWrapper) { + + return function.throwing(exceptionWrapper); + } + +} diff --git a/spring-core/src/main/java/org/springframework/util/function/ThrowingSupplier.java b/spring-core/src/main/java/org/springframework/util/function/ThrowingSupplier.java new file mode 100644 index 00000000000..f2b1af87de8 --- /dev/null +++ b/spring-core/src/main/java/org/springframework/util/function/ThrowingSupplier.java @@ -0,0 +1,120 @@ +/* + * Copyright 2002-2022 the original author or authors. + * + * Licensed 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 + * + * https://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.springframework.util.function; + +import java.util.function.BiFunction; +import java.util.function.Supplier; + +/** + * A {@link Supplier} that allows invocation of code that throws a checked + * exception. + * + * @author Stephane Nicoll + * @author Phillip Webb + * @since 6.0 + * @param the type of results supplied by this supplier + */ +public interface ThrowingSupplier extends Supplier { + + /** + * Gets a result, possibly throwing a checked exception. + * @return a result + * @throws Exception on error + */ + T getWithException() throws Exception; + + /** + * Default {@link Supplier#get()} that wraps any thrown checked exceptions + * (by default in a {@link RuntimeException}). + * @see java.util.function.Supplier#get() + */ + @Override + default T get() { + return get(RuntimeException::new); + } + + /** + * Gets a result, 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 T get(BiFunction exceptionWrapper) { + try { + return getWithException(); + } + catch (RuntimeException ex) { + throw ex; + } + catch (Exception ex) { + throw exceptionWrapper.apply(ex.getMessage(), ex); + } + } + + /** + * Return a new {@link ThrowingSupplier} where the {@link #get()} 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 ThrowingSupplier} instance + */ + default ThrowingSupplier throwing(BiFunction exceptionWrapper) { + return new ThrowingSupplier<>() { + + @Override + public T getWithException() throws Exception { + return ThrowingSupplier.this.getWithException(); + } + + @Override + public T get() { + return get(exceptionWrapper); + } + + }; + } + + /** + * Lambda friendly convenience method that can be used to create + * {@link ThrowingSupplier} where the {@link #get()} method wraps any + * thrown checked exceptions. + * @param the type of results supplied by this supplier + * @param supplier the source supplier + * @return a new {@link ThrowingSupplier} instance + */ + static ThrowingSupplier of(ThrowingSupplier supplier) { + return supplier; + } + + /** + * Lambda friendly convenience method that can be used to create + * {@link ThrowingSupplier} where the {@link #get()} method wraps any + * thrown checked exceptions using the given {@code exceptionWrapper}. + * @param the type of results supplied by this supplier + * @param supplier the source supplier + * @param exceptionWrapper the exception wrapper to use + * @return a new {@link ThrowingSupplier} instance + */ + static ThrowingSupplier of(ThrowingSupplier supplier, + BiFunction exceptionWrapper) { + + return supplier.throwing(exceptionWrapper); + } + +} diff --git a/spring-core/src/test/java/org/springframework/util/function/ThrowingBiFunctionTests.java b/spring-core/src/test/java/org/springframework/util/function/ThrowingBiFunctionTests.java new file mode 100644 index 00000000000..93d4a55a2bf --- /dev/null +++ b/spring-core/src/test/java/org/springframework/util/function/ThrowingBiFunctionTests.java @@ -0,0 +1,87 @@ +/* + * Copyright 2002-2022 the original author or authors. + * + * Licensed 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 + * + * https://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.springframework.util.function; + +import java.io.IOException; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.assertj.core.api.Assertions.assertThatIllegalStateException; + +/** + * Tests for {@link ThrowingBiFunction}. + * + * @author Phillip Webb + * @since 6.0 + */ +class ThrowingBiFunctionTests { + + @Test + void applyWhenThrowingUncheckedExceptionThrowsOriginal() { + ThrowingBiFunction function = this::throwIllegalArgumentException; + assertThatIllegalArgumentException().isThrownBy(() -> function.apply(this, this)); + } + + @Test + void applyWhenThrowingCheckedExceptionThrowsWrapperRuntimeException() { + ThrowingBiFunction function = this::throwIOException; + assertThatExceptionOfType(RuntimeException.class).isThrownBy( + () -> function.apply(this, this)).withCauseInstanceOf(IOException.class); + } + + @Test + void applyWithExceptionWrapperWhenThrowingUncheckedExceptionThrowsOriginal() { + ThrowingBiFunction function = this::throwIllegalArgumentException; + assertThatIllegalArgumentException().isThrownBy( + () -> function.apply(this, this, IllegalStateException::new)); + } + + @Test + void applyWithExceptionWrapperWhenThrowingCheckedExceptionThrowsWrapper() { + ThrowingBiFunction function = this::throwIOException; + assertThatIllegalStateException().isThrownBy(() -> function.apply(this, this, + IllegalStateException::new)).withCauseInstanceOf(IOException.class); + } + + @Test + void throwingModifiesThrownException() { + ThrowingBiFunction function = this::throwIOException; + ThrowingBiFunction modified = function.throwing( + IllegalStateException::new); + assertThatIllegalStateException().isThrownBy( + () -> modified.apply(this, this)).withCauseInstanceOf(IOException.class); + } + + @Test + void ofModifiesThrowException() { + ThrowingBiFunction function = ThrowingBiFunction.of( + this::throwIOException, IllegalStateException::new); + assertThatIllegalStateException().isThrownBy( + () -> function.apply(this, this)).withCauseInstanceOf(IOException.class); + } + + private Object throwIOException(Object o, Object u) throws IOException { + throw new IOException(); + } + + private Object throwIllegalArgumentException(Object o, Object u) throws IOException { + throw new IllegalArgumentException(); + } + +} diff --git a/spring-core/src/test/java/org/springframework/util/function/ThrowingConsumerTests.java b/spring-core/src/test/java/org/springframework/util/function/ThrowingConsumerTests.java new file mode 100644 index 00000000000..536c5b86c7e --- /dev/null +++ b/spring-core/src/test/java/org/springframework/util/function/ThrowingConsumerTests.java @@ -0,0 +1,87 @@ +/* + * Copyright 2002-2022 the original author or authors. + * + * Licensed 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 + * + * https://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.springframework.util.function; + +import java.io.IOException; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.assertj.core.api.Assertions.assertThatIllegalStateException; + +/** + * Tests for {@link ThrowingConsumer}. + * + * @author Phillip Webb + * @since 6.0 + */ +class ThrowingConsumerTests { + + @Test + void applyWhenThrowingUncheckedExceptionThrowsOriginal() { + ThrowingConsumer consumer = this::throwIllegalArgumentException; + assertThatIllegalArgumentException().isThrownBy(() -> consumer.accept(this)); + } + + @Test + void applyWhenThrowingCheckedExceptionThrowsWrapperRuntimeException() { + ThrowingConsumer consumer = this::throwIOException; + assertThatExceptionOfType(RuntimeException.class).isThrownBy( + () -> consumer.accept(this)).withCauseInstanceOf(IOException.class); + } + + @Test + void applyWithExceptionWrapperWhenThrowingUncheckedExceptionThrowsOriginal() { + ThrowingConsumer consumer = this::throwIllegalArgumentException; + assertThatIllegalArgumentException().isThrownBy( + () -> consumer.accept(this, IllegalStateException::new)); + } + + @Test + void applyWithExceptionWrapperWhenThrowingCheckedExceptionThrowsWrapper() { + ThrowingConsumer consumer = this::throwIOException; + assertThatIllegalStateException().isThrownBy(() -> consumer.accept(this, + IllegalStateException::new)).withCauseInstanceOf(IOException.class); + } + + @Test + void throwingModifiesThrownException() { + ThrowingConsumer consumer = this::throwIOException; + ThrowingConsumer modified = consumer.throwing( + IllegalStateException::new); + assertThatIllegalStateException().isThrownBy( + () -> modified.accept(this)).withCauseInstanceOf(IOException.class); + } + + @Test + void ofModifiesThrowException() { + ThrowingConsumer consumer = ThrowingConsumer.of(this::throwIOException, + IllegalStateException::new); + assertThatIllegalStateException().isThrownBy( + () -> consumer.accept(this)).withCauseInstanceOf(IOException.class); + } + + private void throwIOException(Object o) throws IOException { + throw new IOException(); + } + + private void throwIllegalArgumentException(Object o) throws IOException { + throw new IllegalArgumentException(); + } + +} diff --git a/spring-core/src/test/java/org/springframework/util/function/ThrowingFunctionTests.java b/spring-core/src/test/java/org/springframework/util/function/ThrowingFunctionTests.java new file mode 100644 index 00000000000..c985c23954a --- /dev/null +++ b/spring-core/src/test/java/org/springframework/util/function/ThrowingFunctionTests.java @@ -0,0 +1,87 @@ +/* + * Copyright 2002-2022 the original author or authors. + * + * Licensed 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 + * + * https://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.springframework.util.function; + +import java.io.IOException; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.assertj.core.api.Assertions.assertThatIllegalStateException; + +/** + * Tests for {@link ThrowingFunction}. + * + * @author Phillip Webb + * @since 6.0 + */ +class ThrowingFunctionTests { + + @Test + void applyWhenThrowingUncheckedExceptionThrowsOriginal() { + ThrowingFunction function = this::throwIllegalArgumentException; + assertThatIllegalArgumentException().isThrownBy(() -> function.apply(this)); + } + + @Test + void applyWhenThrowingCheckedExceptionThrowsWrapperRuntimeException() { + ThrowingFunction function = this::throwIOException; + assertThatExceptionOfType(RuntimeException.class).isThrownBy( + () -> function.apply(this)).withCauseInstanceOf(IOException.class); + } + + @Test + void applyWithExceptionWrapperWhenThrowingUncheckedExceptionThrowsOriginal() { + ThrowingFunction function = this::throwIllegalArgumentException; + assertThatIllegalArgumentException().isThrownBy( + () -> function.apply(this, IllegalStateException::new)); + } + + @Test + void applyWithExceptionWrapperWhenThrowingCheckedExceptionThrowsWrapper() { + ThrowingFunction function = this::throwIOException; + assertThatIllegalStateException().isThrownBy(() -> function.apply(this, + IllegalStateException::new)).withCauseInstanceOf(IOException.class); + } + + @Test + void throwingModifiesThrownException() { + ThrowingFunction function = this::throwIOException; + ThrowingFunction modified = function.throwing( + IllegalStateException::new); + assertThatIllegalStateException().isThrownBy( + () -> modified.apply(this)).withCauseInstanceOf(IOException.class); + } + + @Test + void ofModifiesThrowException() { + ThrowingFunction function = ThrowingFunction.of( + this::throwIOException, IllegalStateException::new); + assertThatIllegalStateException().isThrownBy( + () -> function.apply(this)).withCauseInstanceOf(IOException.class); + } + + private Object throwIOException(Object o) throws IOException { + throw new IOException(); + } + + private Object throwIllegalArgumentException(Object o) throws IOException { + throw new IllegalArgumentException(); + } + +} diff --git a/spring-core/src/test/java/org/springframework/util/function/ThrowingSupplierTests.java b/spring-core/src/test/java/org/springframework/util/function/ThrowingSupplierTests.java new file mode 100644 index 00000000000..7e9ded1255b --- /dev/null +++ b/spring-core/src/test/java/org/springframework/util/function/ThrowingSupplierTests.java @@ -0,0 +1,88 @@ +/* + * Copyright 2002-2022 the original author or authors. + * + * Licensed 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 + * + * https://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.springframework.util.function; + +import java.io.IOException; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.assertj.core.api.Assertions.assertThatIllegalStateException; + +/** + * Tests for {@link ThrowingSupplier}. + * + * @author Phillip Webb + * @since 6.0 + */ +class ThrowingSupplierTests { + + @Test + void getWhenThrowingUncheckedExceptionThrowsOriginal() { + ThrowingSupplier supplier = this::throwIllegalArgumentException; + assertThatIllegalArgumentException().isThrownBy(supplier::get); + } + + @Test + void getWhenThrowingCheckedExceptionThrowsWrapperRuntimeException() { + ThrowingSupplier supplier = this::throwIOException; + assertThatExceptionOfType(RuntimeException.class).isThrownBy( + supplier::get).withCauseInstanceOf(IOException.class); + } + + @Test + void getWithExceptionWrapperWhenThrowingUncheckedExceptionThrowsOriginal() { + ThrowingSupplier supplier = this::throwIllegalArgumentException; + assertThatIllegalArgumentException().isThrownBy( + () -> supplier.get(IllegalStateException::new)); + } + + @Test + void getWithExceptionWrapperWhenThrowingCheckedExceptionThrowsWrapper() { + ThrowingSupplier supplier = this::throwIOException; + assertThatIllegalStateException().isThrownBy( + () -> supplier.get(IllegalStateException::new)).withCauseInstanceOf( + IOException.class); + } + + @Test + void throwingModifiesThrownException() { + ThrowingSupplier supplier = this::throwIOException; + ThrowingSupplier modified = supplier.throwing( + IllegalStateException::new); + assertThatIllegalStateException().isThrownBy( + () -> modified.get()).withCauseInstanceOf(IOException.class); + } + + @Test + void ofModifiesThrowException() { + ThrowingSupplier supplier = ThrowingSupplier.of( + this::throwIOException, IllegalStateException::new); + assertThatIllegalStateException().isThrownBy( + () -> supplier.get()).withCauseInstanceOf(IOException.class); + } + + private Object throwIOException() throws IOException { + throw new IOException(); + } + + private Object throwIllegalArgumentException() throws IOException { + throw new IllegalArgumentException(); + } + +}