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;
-    }
 }