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 <gardgreg...@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()); } }