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 2021/11/06 20:40:28 UTC

[commons-io] branch master updated: Add and reuse IOConsumer.forEach(T[], IOConsumer) and forEachIndexed(Stream, IOConsumer)

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 c525935  Add and reuse IOConsumer.forEach(T[], IOConsumer<T>) and forEachIndexed(Stream<T>, IOConsumer<T>)
c525935 is described below

commit c525935dec0f4e66ed264adadbe1530de66659c9
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Sat Nov 6 16:40:25 2021 -0400

    Add and reuse IOConsumer.forEach(T[], IOConsumer<T>) and
    forEachIndexed(Stream<T>, IOConsumer<T>)
---
 src/changes/changes.xml                            |   3 +
 src/main/java/org/apache/commons/io/FileUtils.java |  25 +--
 src/main/java/org/apache/commons/io/IOUtils.java   |   8 +-
 .../org/apache/commons/io/function/IOConsumer.java |  37 ++++-
 .../org/apache/commons/io/function/IOStreams.java  |  71 +++++++++
 .../commons/io/output/FilterCollectionWriter.java  | 174 +++------------------
 6 files changed, 137 insertions(+), 181 deletions(-)

diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 5c989d6..61bb9e5 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -287,6 +287,9 @@ The <action> type attribute can be add,update,fix,remove.
       <action dev="ggregory" type="add" due-to="Gary Gregory">
         Add IOBiConsumer.
       </action>
+      <action dev="ggregory" type="add" due-to="Gary Gregory">
+        Add and reuse IOConsumer.forEach(T[], IOConsumer) and forEachIndexed(Stream, IOConsumer).
+      </action>
       <!-- UPDATE -->
       <action dev="ggregory" type="add" due-to="Gary Gregory">
         Update FileEntry to use FileTime instead of long for file time stamps.
diff --git a/src/main/java/org/apache/commons/io/FileUtils.java b/src/main/java/org/apache/commons/io/FileUtils.java
index ced2eaa..84cbfbf 100644
--- a/src/main/java/org/apache/commons/io/FileUtils.java
+++ b/src/main/java/org/apache/commons/io/FileUtils.java
@@ -76,6 +76,7 @@ import org.apache.commons.io.filefilter.FileFileFilter;
 import org.apache.commons.io.filefilter.IOFileFilter;
 import org.apache.commons.io.filefilter.SuffixFileFilter;
 import org.apache.commons.io.filefilter.TrueFileFilter;
+import org.apache.commons.io.function.IOConsumer;
 
 /**
  * General file manipulation utilities.
@@ -340,17 +341,7 @@ public class FileUtils {
      * @see #forceDelete(File)
      */
     public static void cleanDirectory(final File directory) throws IOException {
-        final File[] files = listFiles(directory, null);
-
-        final List<IOException> causeList = new ArrayList<>();
-        for (final File file : files) {
-            try {
-                forceDelete(file);
-            } catch (final IOException ioe) {
-                causeList.add(ioe);
-            }
-        }
-        IOExceptionList.checkEmpty(causeList, directory);
+        IOConsumer.forEach(listFiles(directory, null), file -> forceDelete(file));
     }
 
     /**
@@ -363,17 +354,7 @@ public class FileUtils {
      * @see #forceDeleteOnExit(File)
      */
     private static void cleanDirectoryOnExit(final File directory) throws IOException {
-        final File[] files = listFiles(directory, null);
-
-        final List<IOException> causeList = new ArrayList<>();
-        for (final File file : files) {
-            try {
-                forceDeleteOnExit(file);
-            } catch (final IOException ioe) {
-                causeList.add(ioe);
-            }
-        }
-        IOExceptionList.checkEmpty(causeList, directory);
+        IOConsumer.forEach(listFiles(directory, null), file -> forceDeleteOnExit(file));
     }
 
     /**
diff --git a/src/main/java/org/apache/commons/io/IOUtils.java b/src/main/java/org/apache/commons/io/IOUtils.java
index be574e5..6c6a007 100644
--- a/src/main/java/org/apache/commons/io/IOUtils.java
+++ b/src/main/java/org/apache/commons/io/IOUtils.java
@@ -384,18 +384,14 @@ public class IOUtils {
     }
 
     /**
-     * Closes the given {@link Closeable} as a null-safe operation.
+     * Closes the given {@link Closeable}s as null-safe operations.
      *
      * @param closeables The resource(s) to close, may be null.
      * @throws IOException if an I/O error occurs.
      * @since 2.8.0
      */
     public static void close(final Closeable... closeables) throws IOException {
-        if (closeables != null) {
-            for (final Closeable closeable : closeables) {
-                close(closeable);
-            }
-        }
+        IOConsumer.forEach(closeables, IOUtils::close);
     }
 
     /**
diff --git a/src/main/java/org/apache/commons/io/function/IOConsumer.java b/src/main/java/org/apache/commons/io/function/IOConsumer.java
index d2da2b1..662cd34 100644
--- a/src/main/java/org/apache/commons/io/function/IOConsumer.java
+++ b/src/main/java/org/apache/commons/io/function/IOConsumer.java
@@ -20,6 +20,10 @@ package org.apache.commons.io.function;
 import java.io.IOException;
 import java.util.Objects;
 import java.util.function.Consumer;
+import java.util.stream.Stream;
+
+import org.apache.commons.io.IOExceptionList;
+import org.apache.commons.io.IOIndexedException;
 
 /**
  * Like {@link Consumer} but throws {@link IOException}.
@@ -36,6 +40,32 @@ public interface IOConsumer<T> {
     IOConsumer<?> NOOP_IO_CONSUMER = t -> {/* noop */};
 
     /**
+     * Performs an action for each element of this stream.
+     *
+     * @param <T> The element type.
+     * @param array The input to stream.
+     * @param action The action to apply to each input element.
+     * @throws IOException if an I/O error occurs.
+     * @since 2.12.0
+     */
+    static <T> void forEach(final T[] array, final IOConsumer<T> action) throws IOException {
+        IOStreams.forEach(IOStreams.of(array), action);
+    }
+
+    /**
+     * Performs an action for each element of this stream.
+     *
+     * @param <T> The element type.
+     * @param stream The input to stream.
+     * @param action The action to apply to each input element.
+     * @throws IOExceptionList if an I/O error occurs.
+     * @since 2.12.0
+     */
+    static <T> void forEachIndexed(final Stream<T> stream, final IOConsumer<T> action) throws IOExceptionList {
+        IOStreams.forEachIndexed(stream, action, IOIndexedException::new);
+    }
+
+    /**
      * Returns a constant NOOP consumer.
      *
      * @param <T> Type consumer type.
@@ -57,12 +87,11 @@ public interface IOConsumer<T> {
 
     /**
      * Returns a composed {@code IOConsumer} that performs, in sequence, this operation followed by the {@code after}
-     * operation. If performing either operation throws an exception, it is relayed to the caller of the composed
-     * operation. If performing this operation throws an exception, the {@code after} operation will not be performed.
+     * operation. If performing either operation throws an exception, it is relayed to the caller of the composed operation.
+     * If performing this operation throws an exception, the {@code after} operation will not be performed.
      *
      * @param after the operation to perform after this operation
-     * @return a composed {@code Consumer} that performs in sequence this operation followed by the {@code after}
-     *         operation
+     * @return a composed {@code Consumer} that performs in sequence this operation followed by the {@code after} operation
      * @throws NullPointerException if {@code after} is null
      */
     default IOConsumer<T> andThen(final IOConsumer<? super T> after) {
diff --git a/src/main/java/org/apache/commons/io/function/IOStreams.java b/src/main/java/org/apache/commons/io/function/IOStreams.java
new file mode 100644
index 0000000..52654cb
--- /dev/null
+++ b/src/main/java/org/apache/commons/io/function/IOStreams.java
@@ -0,0 +1,71 @@
+/*
+ * 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.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.BiFunction;
+import java.util.stream.Stream;
+
+import org.apache.commons.io.IOExceptionList;
+
+/**
+ * Keeps code package private for now.
+ */
+class IOStreams {
+
+    /**
+     * Null-safe version of {@link Stream#of(Object[])}.
+     *
+     * Copied from Apache Commons Lang.
+     *
+     * @param <T> the type of stream elements.
+     * @param values the elements of the new stream, may be {@code null}.
+     * @return the new stream on {@code values} or {@link Stream#empty()}.
+     */
+    @SafeVarargs // Creating a stream from an array is safe
+    static <T> Stream<T> of(final T... values) {
+        return values == null ? Stream.empty() : Stream.of(values);
+    }
+
+    static <T> void forEach(final Stream<T> stream, final IOConsumer<T> action) throws IOException {
+        forEachIndexed(stream, action, (i, e) -> e);
+    }
+
+    static <T> void forEachIndexed(final Stream<T> stream, final IOConsumer<T> action, final BiFunction<Integer, IOException, IOException> exSupplier)
+        throws IOExceptionList {
+        final AtomicReference<List<IOException>> causeList = new AtomicReference<>();
+        final AtomicInteger index = new AtomicInteger();
+        stream.forEach(e -> {
+            try {
+                action.accept(e);
+            } catch (final IOException ioex) {
+                if (causeList.get() == null) {
+                    causeList.set(new ArrayList<>());
+                }
+                causeList.get().add(exSupplier.apply(index.get(), ioex));
+            }
+            index.incrementAndGet();
+        });
+        IOExceptionList.checkEmpty(causeList.get(), "forEach");
+    }
+
+}
diff --git a/src/main/java/org/apache/commons/io/output/FilterCollectionWriter.java b/src/main/java/org/apache/commons/io/output/FilterCollectionWriter.java
index 58400b7..08e7980 100644
--- a/src/main/java/org/apache/commons/io/output/FilterCollectionWriter.java
+++ b/src/main/java/org/apache/commons/io/output/FilterCollectionWriter.java
@@ -20,14 +20,15 @@ package org.apache.commons.io.output;
 import java.io.FilterWriter;
 import java.io.IOException;
 import java.io.Writer;
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.List;
+import java.util.function.Predicate;
+import java.util.stream.Stream;
 
 import org.apache.commons.io.IOExceptionList;
 import org.apache.commons.io.IOIndexedException;
+import org.apache.commons.io.function.IOConsumer;
 
 /**
  * Abstract class for writing filtered character streams to a {@link Collection} of writers. This is in contrast to
@@ -45,6 +46,14 @@ import org.apache.commons.io.IOIndexedException;
  */
 public class FilterCollectionWriter extends Writer {
 
+    @SuppressWarnings("rawtypes")
+    private static final Predicate NOT_NULL = e -> e != null;
+
+    @SuppressWarnings("unchecked")
+    private static <T> Predicate<T> notNull() {
+        return NOT_NULL;
+    }
+
     /**
      * Empty and immutable collection of writers.
      */
@@ -73,92 +82,27 @@ public class FilterCollectionWriter extends Writer {
         this.writers = writers == null ? EMPTY_WRITERS : Arrays.asList(writers);
     }
 
-    /**
-     * Adds an indexed exception to the list.
-     *
-     * @param causeList The target list.
-     * @param i The index.
-     * @param e The cause.
-     * @return the given list or a new list on null input.
-     */
-    private List<IOException> add(List<IOException> causeList, final int i, final IOException e) {
-        if (causeList == null) {
-            causeList = new ArrayList<>();
-        }
-        causeList.add(new IOIndexedException(i, e));
-        return causeList;
-    }
-
     @Override
     public Writer append(final char c) throws IOException {
-        List<IOException> causeList = null;
-        int i = 0;
-        for (final Writer w : writers) {
-            if (w != null) {
-                try {
-                    w.append(c);
-                } catch (final IOException e) {
-                    causeList = add(causeList, i, e);
-                }
-            }
-            i++;
-        }
-        IOExceptionList.checkEmpty(causeList, "append(char)");
+        IOConsumer.forEachIndexed(writers(), w -> w.append(c));
         return this;
     }
 
     @Override
     public Writer append(final CharSequence csq) throws IOException {
-        List<IOException> causeList = null;
-        int i = 0;
-        for (final Writer w : writers) {
-            if (w != null) {
-                try {
-                    w.append(csq);
-                } catch (final IOException e) {
-                    causeList = add(causeList, i, e);
-                }
-            }
-            i++;
-        }
-        IOExceptionList.checkEmpty(causeList, "append(CharSequence)");
+        IOConsumer.forEachIndexed(writers(), w -> w.append(csq));
         return this;
     }
 
     @Override
     public Writer append(final CharSequence csq, final int start, final int end) throws IOException {
-
-        List<IOException> causeList = null;
-        int i = 0;
-        for (final Writer w : writers) {
-            if (w != null) {
-                try {
-                    w.append(csq, start, end);
-                } catch (final IOException e) {
-                    causeList = add(causeList, i, e);
-                }
-            }
-            i++;
-        }
-        IOExceptionList.checkEmpty(causeList, "append(CharSequence, int, int)");
+        IOConsumer.forEachIndexed(writers(), w -> w.append(csq, start, end));
         return this;
     }
 
     @Override
     public void close() throws IOException {
-        List<IOException> causeList = null;
-        int i = 0;
-        for (final Writer w : writers) {
-            if (w != null) {
-                try {
-                    w.close();
-                } catch (final IOException e) {
-                    causeList = add(causeList, i, e);
-                }
-            }
-            i++;
-        }
-        IOExceptionList.checkEmpty(causeList, "close()");
+        IOConsumer.forEachIndexed(writers(), Writer::close);
     }
 
     /**
@@ -168,36 +112,12 @@ public class FilterCollectionWriter extends Writer {
      */
     @Override
     public void flush() throws IOException {
-        List<IOException> causeList = null;
-        int i = 0;
-        for (final Writer w : writers) {
-            if (w != null) {
-                try {
-                    w.flush();
-                } catch (final IOException e) {
-                    causeList = add(causeList, i, e);
-                }
-            }
-            i++;
-        }
-        IOExceptionList.checkEmpty(causeList, "flush()");
+        IOConsumer.forEachIndexed(writers(), Writer::flush);
     }
 
     @Override
     public void write(final char[] cbuf) throws IOException {
-        List<IOException> causeList = null;
-        int i = 0;
-        for (final Writer w : writers) {
-            if (w != null) {
-                try {
-                    w.write(cbuf);
-                } catch (final IOException e) {
-                    causeList = add(causeList, i, e);
-                }
-            }
-            i++;
-        }
-        IOExceptionList.checkEmpty(causeList, "write(char[])");
+        IOConsumer.forEachIndexed(writers(), w -> w.write(cbuf));
     }
 
     /**
@@ -211,19 +131,7 @@ public class FilterCollectionWriter extends Writer {
      */
     @Override
     public void write(final char[] cbuf, final int off, final int len) throws IOException {
-        List<IOException> causeList = null;
-        int i = 0;
-        for (final Writer w : writers) {
-            if (w != null) {
-                try {
-                    w.write(cbuf, off, len);
-                } catch (final IOException e) {
-                    causeList = add(causeList, i, e);
-                }
-            }
-            i++;
-        }
-        IOExceptionList.checkEmpty(causeList, "write(char[], int, int)");
+        IOConsumer.forEachIndexed(writers(), w -> w.write(cbuf, off, len));
     }
 
     /**
@@ -233,36 +141,12 @@ public class FilterCollectionWriter extends Writer {
      */
     @Override
     public void write(final int c) throws IOException {
-        List<IOException> causeList = null;
-        int i = 0;
-        for (final Writer w : writers) {
-            if (w != null) {
-                try {
-                    w.write(c);
-                } catch (final IOException e) {
-                    causeList = add(causeList, i, e);
-                }
-            }
-            i++;
-        }
-        IOExceptionList.checkEmpty(causeList, "write(int)");
+        IOConsumer.forEachIndexed(writers(), w -> w.write(c));
     }
 
     @Override
     public void write(final String str) throws IOException {
-        List<IOException> causeList = null;
-        int i = 0;
-        for (final Writer w : writers) {
-            if (w != null) {
-                try {
-                    w.write(str);
-                } catch (final IOException e) {
-                    causeList = add(causeList, i, e);
-                }
-            }
-            i++;
-        }
-        IOExceptionList.checkEmpty(causeList, "write(String)");
+        IOConsumer.forEachIndexed(writers(), w -> w.write(str));
     }
 
     /**
@@ -276,19 +160,11 @@ public class FilterCollectionWriter extends Writer {
      */
     @Override
     public void write(final String str, final int off, final int len) throws IOException {
-        List<IOException> causeList = null;
-        int i = 0;
-        for (final Writer w : writers) {
-            if (w != null) {
-                try {
-                    w.write(str, off, len);
-                } catch (final IOException e) {
-                    causeList = add(causeList, i, e);
-                }
-            }
-            i++;
-        }
-        IOExceptionList.checkEmpty(causeList, "write(String, int, int)");
+        IOConsumer.forEachIndexed(writers(), w -> w.write(str, off, len));
+    }
+
+    private Stream<Writer> writers() {
+        return writers.stream().filter(notNull());
     }
 
 }