You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by gg...@apache.org on 2020/04/16 01:39:54 UTC
[commons-io] branch master updated: Further functional interfaces
(#110)
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-io.git
The following commit(s) were added to refs/heads/master by this push:
new b739ce7 Further functional interfaces (#110)
b739ce7 is described below
commit b739ce7ccdaac07a7aa7bd7bbecf90baf3e340d5
Author: Adam Retter <ad...@googlemail.com>
AuthorDate: Thu Apr 16 03:39:47 2020 +0200
Further functional interfaces (#110)
* Fix a warning over generics
* Extract IOFunction from test case to be usable elsewhere. Complements IOConsumer
* Add IOSupplier to complement IOFunction and IOConsumer
---
.../org/apache/commons/io/function/IOFunction.java | 207 +++++++++++++++++++++
.../org/apache/commons/io/function/IOSupplier.java | 40 ++++
.../apache/commons/io/function/IOFunctionTest.java | 164 ++++++++++++++++
.../io/output/ByteArrayOutputStreamTestCase.java | 22 +--
4 files changed, 420 insertions(+), 13 deletions(-)
diff --git a/src/main/java/org/apache/commons/io/function/IOFunction.java b/src/main/java/org/apache/commons/io/function/IOFunction.java
new file mode 100644
index 0000000..332e346
--- /dev/null
+++ b/src/main/java/org/apache/commons/io/function/IOFunction.java
@@ -0,0 +1,207 @@
+/*
+ * 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.io.function;
+
+import java.io.IOException;
+import java.util.Objects;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+/**
+ * Like {@link Function} but throws {@link IOException}.
+ *
+ * @param <T> the type of the input to the operations.
+ * @param <R> the return type of the operations.
+ * @since 2.7
+ */
+@FunctionalInterface
+public interface IOFunction<T, R> {
+
+ /**
+ * Applies this function to the given argument.
+ *
+ * @param t the function argument
+ * @return the function result
+ *
+ * @throws IOException if the function throws an IOException
+ */
+ R apply(final T t) throws IOException;
+
+ /**
+ * Returns a composed {@link IOFunction} that first applies the {@code before}
+ * function to its input, and then applies this function to the result.
+ * If evaluation of either function throws an exception, it is relayed to
+ * the caller of the composed function.
+ *
+ * @param <V> the type of input to the {@code before} function, and to the
+ * composed function
+ * @param before the function to apply before this function is applied
+ * @return a composed function that first applies the {@code before}
+ * function and then applies this function
+ * @throws NullPointerException if before is null
+ *
+ * @see #andThen(IOFunction)
+ */
+ default <V> IOFunction<V, R> compose(final IOFunction<? super V, ? extends T> before) {
+ Objects.requireNonNull(before);
+ return (V v) -> apply(before.apply(v));
+ }
+
+ /**
+ * Returns a composed {@link IOFunction} that first applies the {@code before}
+ * function to its input, and then applies this function to the result.
+ * If evaluation of either function throws an exception, it is relayed to
+ * the caller of the composed function.
+ *
+ * @param <V> the type of input to the {@code before} function, and to the
+ * composed function
+ * @param before the function to apply before this function is applied
+ * @return a composed function that first applies the {@code before}
+ * function and then applies this function
+ * @throws NullPointerException if before is null
+ *
+ * @see #andThen(IOFunction)
+ */
+ default <V> IOFunction<V, R> compose(final Function<? super V, ? extends T> before) {
+ Objects.requireNonNull(before);
+ return (V v) -> apply(before.apply(v));
+ }
+
+ /**
+ * Returns a composed {@link IOFunction} that first applies the {@code before}
+ * function to its input, and then applies this function to the result.
+ * If evaluation of either function throws an exception, it is relayed to
+ * the caller of the composed function.
+ *
+ * @param before the supplier which feeds the application of this function
+ * @return a composed function that first applies the {@code before}
+ * function and then applies this function
+ * @throws NullPointerException if before is null
+ *
+ * @see #andThen(IOFunction)
+ */
+ default IOSupplier<R> compose(final IOSupplier<? extends T> before) {
+ Objects.requireNonNull(before);
+ return () -> apply(before.get());
+ }
+
+ /**
+ * Returns a composed {@link IOFunction} that first applies the {@code before}
+ * function to its input, and then applies this function to the result.
+ * If evaluation of either function throws an exception, it is relayed to
+ * the caller of the composed function.
+ *
+ * @param before the supplier which feeds the application of this function
+ * @return a composed function that first applies the {@code before}
+ * function and then applies this function
+ * @throws NullPointerException if before is null
+ *
+ * @see #andThen(IOFunction)
+ */
+ default IOSupplier<R> compose(final Supplier<? extends T> before) {
+ Objects.requireNonNull(before);
+ return () -> apply(before.get());
+ }
+
+ /**
+ * Returns a composed {@link IOFunction} that first applies this function to
+ * its input, and then applies the {@code after} function to the result.
+ * If evaluation of either function throws an exception, it is relayed to
+ * the caller of the composed function.
+ *
+ * @param <V> the type of output of the {@code after} function, and of the
+ * composed function
+ * @param after the function to apply after this function is applied
+ * @return a composed function that first applies this function and then
+ * applies the {@code after} function
+ * @throws NullPointerException if after is null
+ *
+ * @see #compose(IOFunction)
+ */
+ default <V> IOFunction<T, V> andThen(IOFunction<? super R, ? extends V> after) {
+ Objects.requireNonNull(after);
+ return (T t) -> after.apply(apply(t));
+ }
+
+ /**
+ * Returns a composed {@link IOFunction} that first applies this function to
+ * its input, and then applies the {@code after} function to the result.
+ * If evaluation of either function throws an exception, it is relayed to
+ * the caller of the composed function.
+ *
+ * @param <V> the type of output of the {@code after} function, and of the
+ * composed function
+ * @param after the function to apply after this function is applied
+ * @return a composed function that first applies this function and then
+ * applies the {@code after} function
+ * @throws NullPointerException if after is null
+ *
+ * @see #compose(IOFunction)
+ */
+ default <V> IOFunction<T, V> andThen(Function<? super R, ? extends V> after) {
+ Objects.requireNonNull(after);
+ return (T t) -> after.apply(apply(t));
+ }
+
+ /**
+ * Returns a composed {@link IOFunction} that first applies this function to
+ * its input, and then applies the {@code after} consumer to the result.
+ * If evaluation of either function throws an exception, it is relayed to
+ * the caller of the composed function.
+ *
+ * @param after the consumer to apply after this function is applied
+ * @return a composed function that first applies this function and then
+ * applies the {@code after} consumer
+ * @throws NullPointerException if after is null
+ *
+ * @see #compose(IOFunction)
+ */
+ default IOConsumer<T> andThen(IOConsumer<? super R> after) {
+ Objects.requireNonNull(after);
+ return (T t) -> after.accept(apply(t));
+ }
+
+ /**
+ * Returns a composed {@link IOFunction} that first applies this function to
+ * its input, and then applies the {@code after} consumer to the result.
+ * If evaluation of either function throws an exception, it is relayed to
+ * the caller of the composed function.
+ *
+ * @param after the consumer to apply after this function is applied
+ * @return a composed function that first applies this function and then
+ * applies the {@code after} consumer
+ * @throws NullPointerException if after is null
+ *
+ * @see #compose(IOFunction)
+ */
+ default IOConsumer<T> andThen(Consumer<? super R> after) {
+ Objects.requireNonNull(after);
+ return (T t) -> after.accept(apply(t));
+ }
+
+ /**
+ * Returns a {@link IOFunction} that always returns its input argument.
+ *
+ * @param <T> the type of the input and output objects to the function
+ * @return a function that always returns its input argument
+ */
+ static <T> IOFunction<T, T> identity() {
+ return t -> t;
+ }
+}
diff --git a/src/main/java/org/apache/commons/io/function/IOSupplier.java b/src/main/java/org/apache/commons/io/function/IOSupplier.java
new file mode 100644
index 0000000..5dc7dcb
--- /dev/null
+++ b/src/main/java/org/apache/commons/io/function/IOSupplier.java
@@ -0,0 +1,40 @@
+/*
+ * 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.io.function;
+
+import java.io.IOException;
+import java.util.function.Supplier;
+
+/**
+ * Like {@link Supplier} but throws {@link IOException}.
+ *
+ * @param <T> the return type of the operations.
+ * @since 2.7
+ */
+@FunctionalInterface
+public interface IOSupplier<T> {
+
+ /**
+ * Gets a result.
+ *
+ * @return a result
+ *
+ * @throws IOException if an IO error occurs whilst supplying the value.
+ */
+ T get() throws IOException;
+}
diff --git a/src/test/java/org/apache/commons/io/function/IOFunctionTest.java b/src/test/java/org/apache/commons/io/function/IOFunctionTest.java
new file mode 100644
index 0000000..5050566
--- /dev/null
+++ b/src/test/java/org/apache/commons/io/function/IOFunctionTest.java
@@ -0,0 +1,164 @@
+/*
+ * 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.io.function;
+
+import org.junit.jupiter.api.Test;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+public class IOFunctionTest {
+
+ @Test
+ public void testApply() throws IOException {
+ final IOFunction<InputStream, Integer> readByte = is -> is.read();
+ final InputStream is = new ByteArrayInputStream(new byte[] { (byte)0xa, (byte)0xb, (byte)0xc});
+ assertEquals(0xa, readByte.apply(is));
+ assertEquals(0xb, readByte.apply(is));
+ assertEquals(0xc, readByte.apply(is));
+ assertEquals(-1, readByte.apply(is));
+ }
+
+ @Test
+ public void testApplyRaisesException() {
+ final IOFunction<InputStream, Integer> raiseException = is -> {
+ throw new IOException("Boom!");
+ };
+ final InputStream is = new ByteArrayInputStream(new byte[] { (byte)0xa, (byte)0xb, (byte)0xc});
+
+ assertThrows(IOException.class, () -> {
+ raiseException.apply(is);
+ });
+ }
+
+ @Test
+ public void testComposeIOFunction() throws IOException {
+ final IOFunction<InputStream, Integer> readByte = is -> is.read();
+ final IOFunction<Integer, Integer> squareInteger = i -> i * i;
+ final IOFunction<InputStream, Integer> productFunction = squareInteger.compose(readByte);
+
+ final InputStream is = new ByteArrayInputStream(new byte[] {2, 3});
+ assertEquals(4, productFunction.apply(is));
+ assertEquals(9, productFunction.apply(is));
+ }
+
+ @Test
+ public void testComposeFunction() throws IOException {
+ final Function<InputStream, Integer> alwaysSeven = is -> 7;
+ final IOFunction<Integer, Integer> squareInteger = i -> i * i;
+ final IOFunction<InputStream, Integer> productFunction = squareInteger.compose(alwaysSeven);
+
+ final InputStream is = new ByteArrayInputStream(new byte[] {2, 3});
+ assertEquals(49, productFunction.apply(is));
+ assertEquals(49, productFunction.apply(is));
+ }
+
+ @Test
+ public void testComposeIOSupplier() throws IOException {
+ final InputStream is = new ByteArrayInputStream(new byte[] {2, 3});
+
+ final IOSupplier<Integer> readByte = () -> is.read();
+ final IOFunction<Integer, Integer> squareInteger = i -> i * i;
+ final IOSupplier<Integer> productFunction = squareInteger.compose(readByte);
+
+ assertEquals(4, productFunction.get());
+ assertEquals(9, productFunction.get());
+ }
+
+ @Test
+ public void testComposeSupplier() throws IOException {
+ final Supplier<Integer> alwaysNine = () -> 9;
+ final IOFunction<Integer, Integer> squareInteger = i -> i * i;
+ final IOSupplier<Integer> productFunction = squareInteger.compose(alwaysNine);
+
+ assertEquals(81, productFunction.get());
+ assertEquals(81, productFunction.get());
+ }
+
+ @Test
+ public void testAndThenIOFunction() throws IOException {
+ final IOFunction<InputStream, Integer> readByte = is -> is.read();
+ final IOFunction<Integer, Integer> squareInteger = i -> i * i;
+ final IOFunction<InputStream, Integer> productFunction = readByte.andThen(squareInteger);
+
+ final InputStream is = new ByteArrayInputStream(new byte[] {2, 3});
+ assertEquals(4, productFunction.apply(is));
+ assertEquals(9, productFunction.apply(is));
+ }
+
+ @Test
+ public void testAndThenFunction() throws IOException {
+ final IOFunction<InputStream, Integer> readByte = is -> is.read();
+ final Function<Integer, Integer> squareInteger = i -> i * i;
+ final IOFunction<InputStream, Integer> productFunction = readByte.andThen(squareInteger);
+
+ final InputStream is = new ByteArrayInputStream(new byte[] {2, 3});
+ assertEquals(4, productFunction.apply(is));
+ assertEquals(9, productFunction.apply(is));
+ }
+
+ @Test
+ public void testAndThenIOConsumer() throws IOException {
+ final Holder<Integer> holder = new Holder<>();
+ final IOFunction<InputStream, Integer> readByte = is -> is.read();
+ final IOConsumer<Integer> sinkInteger = i -> {
+ holder.value = i * i;
+ };
+ final IOConsumer<InputStream> productFunction = readByte.andThen(sinkInteger);
+
+ final InputStream is = new ByteArrayInputStream(new byte[] {2, 3});
+ productFunction.accept(is);
+ assertEquals(4, holder.value);
+ productFunction.accept(is);
+ assertEquals(9, holder.value);
+ }
+
+ @Test
+ public void testAndThenConsumer() throws IOException {
+ final Holder<Integer> holder = new Holder<>();
+ final IOFunction<InputStream, Integer> readByte = is -> is.read();
+ final Consumer<Integer> sinkInteger = i -> {
+ holder.value = i * i;
+ };
+ final IOConsumer<InputStream> productFunction = readByte.andThen(sinkInteger);
+
+ final InputStream is = new ByteArrayInputStream(new byte[] {2, 3});
+ productFunction.accept(is);
+ assertEquals(4, holder.value);
+ productFunction.accept(is);
+ assertEquals(9, holder.value);
+ }
+
+ @Test
+ public void testIdentity() throws IOException {
+ final IOFunction<InputStream, InputStream> identityFunction = IOFunction.identity();
+ final InputStream is = new ByteArrayInputStream(new byte[] { (byte)0xa, (byte)0xb, (byte)0xc});
+ assertEquals(is, identityFunction.apply(is));
+ }
+
+ private static class Holder<T> {
+ T value;
+ }
+}
diff --git a/src/test/java/org/apache/commons/io/output/ByteArrayOutputStreamTestCase.java b/src/test/java/org/apache/commons/io/output/ByteArrayOutputStreamTestCase.java
index 917a993..cda2539 100644
--- a/src/test/java/org/apache/commons/io/output/ByteArrayOutputStreamTestCase.java
+++ b/src/test/java/org/apache/commons/io/output/ByteArrayOutputStreamTestCase.java
@@ -22,6 +22,7 @@ import java.io.InputStream;
import java.util.stream.Stream;
import org.apache.commons.io.IOUtils;
+import org.apache.commons.io.function.IOFunction;
import org.apache.commons.io.input.ClosedInputStream;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
@@ -341,33 +342,33 @@ public class ByteArrayOutputStreamTestCase {
);
}
- private static class ByteArrayOutputStreamFactory implements BAOSFactory {
+ private static class ByteArrayOutputStreamFactory implements BAOSFactory<ByteArrayOutputStream> {
@Override
- public AbstractByteArrayOutputStream instance() {
+ public ByteArrayOutputStream instance() {
return new ByteArrayOutputStream();
}
@Override
- public AbstractByteArrayOutputStream instance(final int size) {
+ public ByteArrayOutputStream instance(final int size) {
return new ByteArrayOutputStream(size);
}
}
- private static class UnsynchronizedByteArrayOutputStreamFactory implements BAOSFactory {
+ private static class UnsynchronizedByteArrayOutputStreamFactory implements BAOSFactory<UnsynchronizedByteArrayOutputStream> {
@Override
- public AbstractByteArrayOutputStream instance() {
+ public UnsynchronizedByteArrayOutputStream instance() {
return new UnsynchronizedByteArrayOutputStream();
}
@Override
- public AbstractByteArrayOutputStream instance(final int size) {
+ public UnsynchronizedByteArrayOutputStream instance(final int size) {
return new UnsynchronizedByteArrayOutputStream(size);
}
}
private interface BAOSFactory<T extends AbstractByteArrayOutputStream> {
- AbstractByteArrayOutputStream instance();
- AbstractByteArrayOutputStream instance(final int size);
+ T instance();
+ T instance(final int size);
}
private static Stream<Arguments> toBufferedInputStreamFunctionFactories() {
@@ -383,10 +384,5 @@ public class ByteArrayOutputStreamTestCase {
Arguments.of("UnsynchronizedByteArrayOutputStream.toBufferedInputStream(InputStream, int)", unSyncBaosToBufferedInputStreamWithSize)
);
}
-
- @FunctionalInterface
- private interface IOFunction<T, R> {
- R apply(final T t) throws IOException;
- }
}