Add and reuse IOConsumer.forEach(*) and forEachIndexed(*).
Use forEach and streams.
MODIFY
src/changes/changes.xml
src/changes/changes.xml
@@ -357,7 +357,7 @@ The <action> type attribute can be add,update,fix,remove.
Add IOBiConsumer.
</action>
<action dev="ggregory" type="add" due-to="Gary Gregory">
- Add and reuse IOConsumer.forEach(T[], IOConsumer) and forEachIndexed(Stream, IOConsumer).
+ Add and reuse IOConsumer.forEach(*) and forEachIndexed(*).
</action>
<action dev="ggregory" type="add" due-to="Gary Gregory">
Add CharsetEncoders.

MODIFY
src/main/java/org/apache/commons/io/FileSystemUtils.java
src/main/java/org/apache/commons/io/FileSystemUtils.java
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.time.Duration;
-import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.StringTokenizer;
+import java.util.stream.Collectors;
/**
* General File System utilities.
@@ -487,7 +487,7 @@ long parseDir(final String line, final String path) throws IOException {
// however, it's still not perfect as the JDK support is so poor
// (see commons-exec or Ant for a better multithreaded multi-os solution)
- final List<String> lines = new ArrayList<>(20);
+ final List<String> lines;
Process proc = null;
InputStream in = null;
OutputStream out = null;
@@ -503,12 +503,8 @@ long parseDir(final String line, final String path) throws IOException {
err = proc.getErrorStream();
// default charset is most likely appropriate here
inr = new BufferedReader(new InputStreamReader(in, Charset.defaultCharset()));
- String line = inr.readLine();
- while (line != null && lines.size() < max) {
- line = line.toLowerCase(Locale.ENGLISH).trim();
- lines.add(line);
- line = inr.readLine();
- }
+
+ lines = inr.lines().limit(max).map(line -> line.toLowerCase(Locale.ENGLISH).trim()).collect(Collectors.toList());
proc.waitFor();
@@ -516,15 +512,11 @@ long parseDir(final String line, final String path) throws IOException {
if (proc.exitValue() != 0) {
// OS command problem, throw exception
- throw new IOException(
- "Command line returned OS error code '" + proc.exitValue() +
- "' for command " + Arrays.asList(cmdAttribs));
+ throw new IOException("Command line returned OS error code '" + proc.exitValue() + "' for command " + Arrays.asList(cmdAttribs));
}
if (lines.isEmpty()) {
// unknown problem, throw exception
- throw new IOException(
- "Command line did not return any info " +
- "for command " + Arrays.asList(cmdAttribs));
+ throw new IOException("Command line did not return any info " + "for command " + Arrays.asList(cmdAttribs));
}
inr.close();

MODIFY
src/main/java/org/apache/commons/io/FilenameUtils.java
src/main/java/org/apache/commons/io/FilenameUtils.java
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import java.util.stream.Stream;
/**
* General file name and file path manipulation utilities.
@@ -1054,12 +1055,7 @@ public static boolean isExtension(final String fileName, final String... extensi
return indexOfExtension(fileName) == NOT_FOUND;
}
final String fileExt = getExtension(fileName);
- for (final String extension : extensions) {
- if (fileExt.equals(extension)) {
- return true;
- }
- }
- return false;
+ return Stream.of(extensions).anyMatch(fileExt::equals);
}
/**

MODIFY
src/main/java/org/apache/commons/io/IOUtils.java
src/main/java/org/apache/commons/io/IOUtils.java
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
+import java.io.UncheckedIOException;
import java.io.Writer;
import java.net.HttpURLConnection;
import java.net.ServerSocket;
import java.nio.channels.Selector;
import java.nio.charset.Charset;
import java.nio.file.Files;
-import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;
+import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.commons.io.function.IOConsumer;
@@ -428,6 +430,15 @@ public static void close(final URLConnection conn) {
}
}
+ /**
+ * Avoids the need to type cast.
+ *
+ * @param closeable the object to close, may be null
+ */
+ private static void closeQ(final Closeable closeable) {
+ closeQuietly(closeable, null);
+ }
+
/**
* Closes a {@link Closeable} unconditionally.
*
@@ -473,15 +484,6 @@ public static void closeQuietly(final Closeable closeable) {
closeQuietly(closeable, null);
}
- /**
- * Avoids the need to type cast.
- *
- * @param closeable the object to close, may be null
- */
- private static void closeQ(final Closeable closeable) {
- closeQuietly(closeable, null);
- }
-
/**
* Closes a {@link Closeable} unconditionally.
* <p>
@@ -602,22 +604,6 @@ public static void closeQuietly(final Iterable<Closeable> closeables) {
}
}
- /**
- * Closes a stream of {@link Closeable} unconditionally.
- * <p>
- * Equivalent calling {@link Closeable#close()} on each element, except any exceptions will be ignored.
- * </p>
- *
- * @param closeables the objects to close, may be null or already closed
- * @see #closeQuietly(Closeable)
- * @since 2.12.0
- */
- public static void closeQuietly(final Stream<Closeable> closeables) {
- if (closeables != null) {
- closeables.forEach(IOUtils::closeQuietly);
- }
- }
-
/**
* Closes an {@link OutputStream} unconditionally.
* <p>
@@ -784,6 +770,22 @@ public static void closeQuietly(final Socket socket) {
closeQ(socket);
}
+ /**
+ * Closes a stream of {@link Closeable} unconditionally.
+ * <p>
+ * Equivalent calling {@link Closeable#close()} on each element, except any exceptions will be ignored.
+ * </p>
+ *
+ * @param closeables the objects to close, may be null or already closed
+ * @see #closeQuietly(Closeable)
+ * @since 2.12.0
+ */
+ public static void closeQuietly(final Stream<Closeable> closeables) {
+ if (closeables != null) {
+ closeables.forEach(IOUtils::closeQuietly);
+ }
+ }
+
/**
* Closes an {@link Writer} unconditionally.
* <p>
@@ -895,6 +897,19 @@ public static boolean contentEquals(final InputStream input1, final InputStream
}
}
+ // TODO Consider making public
+ private static boolean contentEquals(final Iterator<?> iterator1, final Iterator<?> iterator2) {
+ while (iterator1.hasNext()) {
+ if (!iterator2.hasNext()) {
+ return false;
+ }
+ if (!Objects.equals(iterator1.next(), iterator2.next())) {
+ return false;
+ }
+ }
+ return !iterator2.hasNext();
+ }
+
/**
* Compares the contents of two Readers to determine if they are equal or not.
* <p>
@@ -953,6 +968,28 @@ public static boolean contentEquals(final Reader input1, final Reader input2) th
}
}
+ // TODO Consider making public
+ private static boolean contentEquals(final Stream<?> stream1, final Stream<?> stream2) {
+ if (stream1 == stream2) {
+ return true;
+ }
+ if (stream1 == null || stream2 == null) {
+ return false;
+ }
+ return contentEquals(stream1.iterator(), stream2.iterator());
+ }
+
+ // TODO Consider making public
+ private static boolean contentEqualsIgnoreEOL(final BufferedReader reader1, final BufferedReader reader2) {
+ if (reader1 == reader2) {
+ return true;
+ }
+ if (reader1 == null || reader2 == null) {
+ return false;
+ }
+ return contentEquals(reader1.lines(), reader2.lines());
+ }
+
/**
* Compares the contents of two Readers to determine if they are equal or
* not, ignoring EOL characters.
@@ -965,28 +1002,18 @@ public static boolean contentEquals(final Reader input1, final Reader input2) th
* @param reader2 the second reader
* @return true if the content of the readers are equal (ignoring EOL differences), false otherwise
* @throws NullPointerException if either input is null
- * @throws IOException if an I/O error occurs
+ * @throws UncheckedIOException if an I/O error occurs
* @since 2.2
*/
@SuppressWarnings("resource")
- public static boolean contentEqualsIgnoreEOL(final Reader reader1, final Reader reader2)
- throws IOException {
+ public static boolean contentEqualsIgnoreEOL(final Reader reader1, final Reader reader2) throws UncheckedIOException {
if (reader1 == reader2) {
return true;
}
- if (reader1 == null ^ reader2 == null) {
+ if (reader1 == null || reader2 == null) {
return false;
}
- final BufferedReader br1 = toBufferedReader(reader1);
- final BufferedReader br2 = toBufferedReader(reader2);
-
- String line1 = br1.readLine();
- String line2 = br2.readLine();
- while (line1 != null && line1.equals(line2)) {
- line1 = br1.readLine();
- line2 = br2.readLine();
- }
- return Objects.equals(line1, line2);
+ return contentEqualsIgnoreEOL(toBufferedReader(reader1), toBufferedReader(reader2));
}
/**
@@ -2042,12 +2069,12 @@ public static void readFully(final Reader reader, final char[] buffer, final int
* @param input the {@link InputStream} to read from, not null
* @return the list of Strings, never null
* @throws NullPointerException if the input is null
- * @throws IOException if an I/O error occurs
+ * @throws UncheckedIOException if an I/O error occurs
* @since 1.1
* @deprecated 2.5 use {@link #readLines(InputStream, Charset)} instead
*/
@Deprecated
- public static List<String> readLines(final InputStream input) throws IOException {
+ public static List<String> readLines(final InputStream input) throws UncheckedIOException {
return readLines(input, Charset.defaultCharset());
}
@@ -2063,12 +2090,11 @@ public static void readFully(final Reader reader, final char[] buffer, final int
* @param charset the charset to use, null means platform default
* @return the list of Strings, never null
* @throws NullPointerException if the input is null
- * @throws IOException if an I/O error occurs
+ * @throws UncheckedIOException if an I/O error occurs
* @since 2.3
*/
- public static List<String> readLines(final InputStream input, final Charset charset) throws IOException {
- final InputStreamReader reader = new InputStreamReader(input, Charsets.toCharset(charset));
- return readLines(reader);
+ public static List<String> readLines(final InputStream input, final Charset charset) throws UncheckedIOException {
+ return readLines(new InputStreamReader(input, Charsets.toCharset(charset)));
}
/**
@@ -2087,13 +2113,13 @@ public static void readFully(final Reader reader, final char[] buffer, final int
* @param charsetName the name of the requested charset, null means platform default
* @return the list of Strings, never null
* @throws NullPointerException if the input is null
- * @throws IOException if an I/O error occurs
+ * @throws UncheckedIOException if an I/O error occurs
* @throws java.nio.charset.UnsupportedCharsetException thrown instead of {@link java.io
* .UnsupportedEncodingException} in version 2.2 if the
* encoding is not supported.
* @since 1.1
*/
- public static List<String> readLines(final InputStream input, final String charsetName) throws IOException {
+ public static List<String> readLines(final InputStream input, final String charsetName) throws UncheckedIOException {
return readLines(input, Charsets.toCharset(charsetName));
}
@@ -2108,18 +2134,12 @@ public static void readFully(final Reader reader, final char[] buffer, final int
* @param reader the {@link Reader} to read from, not null
* @return the list of Strings, never null
* @throws NullPointerException if the input is null
- * @throws IOException if an I/O error occurs
+ * @throws UncheckedIOException if an I/O error occurs
* @since 1.1
*/
@SuppressWarnings("resource") // reader wraps input and is the responsibility of the caller.
- public static List<String> readLines(final Reader reader) throws IOException {
- final BufferedReader bufReader = toBufferedReader(reader);
- final List<String> list = new ArrayList<>();
- String line;
- while ((line = bufReader.readLine()) != null) {
- list.add(line);
- }
- return list;
+ public static List<String> readLines(final Reader reader) throws UncheckedIOException {
+ return toBufferedReader(reader).lines().collect(Collectors.toList());
}
/**

MODIFY
src/main/java/org/apache/commons/io/file/StandardDeleteOption.java
src/main/java/org/apache/commons/io/file/StandardDeleteOption.java
package org.apache.commons.io.file;
+import java.util.stream.Stream;
+
import org.apache.commons.io.IOUtils;
/**
@@ -43,12 +45,7 @@ public static boolean overrideReadOnly(final DeleteOption[] options) {
if (IOUtils.length(options) == 0) {
return false;
}
- for (final DeleteOption deleteOption : options) {
- if (deleteOption == StandardDeleteOption.OVERRIDE_READ_ONLY) {
- return true;
- }
- }
- return false;
+ return Stream.of(options).anyMatch(e -> StandardDeleteOption.OVERRIDE_READ_ONLY == e);
}
}

MODIFY
src/main/java/org/apache/commons/io/filefilter/AndFileFilter.java
src/main/java/org/apache/commons/io/filefilter/AndFileFilter.java
import java.util.Collections;
import java.util.List;
import java.util.Objects;
+import java.util.stream.Stream;
/**
* A {@link java.io.FileFilter} providing conditional AND logic across a list of
@@ -148,9 +149,7 @@ public void addFileFilter(final IOFileFilter fileFilter) {
* @since 2.9.0
*/
public void addFileFilter(final IOFileFilter... fileFilters) {
- for (final IOFileFilter fileFilter : Objects.requireNonNull(fileFilters, "fileFilters")) {
- addFileFilter(fileFilter);
- }
+ Stream.of(Objects.requireNonNull(fileFilters, "fileFilters")).forEach(this::addFileFilter);
}
/**

MODIFY
src/main/java/org/apache/commons/io/filefilter/FileFilterUtils.java
src/main/java/org/apache/commons/io/filefilter/FileFilterUtils.java
import java.io.File;
import java.io.FileFilter;
import java.io.FilenameFilter;
-import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
@@ -723,12 +722,7 @@ public static IOFileFilter suffixFileFilter(final String suffix, final IOCase io
* @since 2.0
*/
public static List<IOFileFilter> toList(final IOFileFilter... filters) {
- Objects.requireNonNull(filters, "filters");
- final List<IOFileFilter> list = new ArrayList<>(filters.length);
- for (final IOFileFilter filter : filters) {
- list.add(Objects.requireNonNull(filter, "filters[i]"));
- }
- return list;
+ return Stream.of(Objects.requireNonNull(filters, "filters")).map(Objects::requireNonNull).collect(Collectors.toList());
}
/**

MODIFY
src/main/java/org/apache/commons/io/filefilter/OrFileFilter.java
src/main/java/org/apache/commons/io/filefilter/OrFileFilter.java
import java.util.Collections;
import java.util.List;
import java.util.Objects;
+import java.util.stream.Stream;
/**
* A {@link java.io.FileFilter} providing conditional OR logic across a list of file filters. This filter returns
@@ -141,9 +142,7 @@ public void addFileFilter(final IOFileFilter fileFilter) {
* @since 2.9.0
*/
public void addFileFilter(final IOFileFilter... fileFilters) {
- for (final IOFileFilter fileFilter : Objects.requireNonNull(fileFilters, "fileFilters")) {
- addFileFilter(fileFilter);
- }
+ Stream.of(Objects.requireNonNull(fileFilters, "fileFilters")).forEach(this::addFileFilter);
}
/**

MODIFY
src/main/java/org/apache/commons/io/function/IOConsumer.java
src/main/java/org/apache/commons/io/function/IOConsumer.java
package org.apache.commons.io.function;
import java.io.IOException;
+import java.util.Collection;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.stream.Stream;
IOConsumer<?> NOOP_IO_CONSUMER = t -> {/* noop */};
/**
- * Performs an action for each element of this stream.
+ * Performs an action for each element of the collection.
+ *
+ * @param <T> The element type.
+ * @param collection 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 Collection<T> collection, final IOConsumer<T> action) throws IOException {
+ IOStreams.forEach(IOStreams.of(collection), action);
+ }
+
+ /**
+ * Performs an action for each element of the stream.
+ *
+ * @param <T> The element type.
+ * @param stream 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 Stream<T> stream, final IOConsumer<T> action) throws IOException {
+ IOStreams.forEach(stream, action);
+ }
+
+ /**
+ * Performs an action for each element of this array.
*
* @param <T> The element type.
* @param array The input to stream.
}
/**
- * Performs an action for each element of this stream.
+ * Performs an action for each element of the stream.
*
* @param <T> The element type.
* @param stream The input to stream.

MODIFY
src/main/java/org/apache/commons/io/function/IOStreams.java
src/main/java/org/apache/commons/io/function/IOStreams.java
import java.io.IOException;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
throws IOExceptionList {
final AtomicReference<List<IOException>> causeList = new AtomicReference<>();
final AtomicInteger index = new AtomicInteger();
+ final IOConsumer<T> actualAction = action != null ? action : IOConsumer.noop();
+ if (stream != null) {
stream.forEach(e -> {
try {
- action.accept(e);
+ actualAction.accept(e);
} catch (final IOException ioex) {
if (causeList.get() == null) {
causeList.set(new ArrayList<>());
}
+ if (exSupplier != null) {
causeList.get().add(exSupplier.apply(index.get(), ioex));
}
+ }
index.incrementAndGet();
});
IOExceptionList.checkEmpty(causeList.get(), null);
+ }}
+
+ /**
+ * Null-safe version of {@link Collection#stream()}.
+ *
+ * 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()}.
+ */
+ static <T> Stream<T> of(final Collection<T> values) {
+ return values == null ? Stream.empty() : values.stream();
}
/**

MODIFY
src/main/java/org/apache/commons/io/input/ObservableInputStream.java
src/main/java/org/apache/commons/io/input/ObservableInputStream.java
@@ -175,10 +175,7 @@ public void consume() throws IOException {
}
private void forEachObserver(final IOConsumer<Observer> action) throws IOException {
- Objects.requireNonNull(action);
- for (final Observer t : observers) {
- action.accept(t);
- }
+ IOConsumer.forEach(observers, Objects.requireNonNull(action));
}
/**

MODIFY
src/main/java/org/apache/commons/io/input/XmlStreamReader.java
src/main/java/org/apache/commons/io/input/XmlStreamReader.java
import org.apache.commons.io.ByteOrderMark;
import org.apache.commons.io.IOUtils;
+import org.apache.commons.io.function.IOConsumer;
/**
* Character stream that handles all the necessary Voodoo to figure out the
@@ -214,13 +215,9 @@ private static String getXmlProlog(final InputStream inputStream, final String g
final int bytesRead = offset;
if (bytesRead > 0) {
inputStream.reset();
- final BufferedReader bReader = new BufferedReader(new StringReader(
- xmlProlog.substring(0, firstGT + 1)));
+ final BufferedReader bReader = new BufferedReader(new StringReader(xmlProlog.substring(0, firstGT + 1)));
final StringBuilder prolog = new StringBuilder();
- String line;
- while ((line = bReader.readLine()) != null) {
- prolog.append(line);
- }
+ IOConsumer.forEach(bReader.lines(), prolog::append);
final Matcher m = ENCODING_PATTERN.matcher(prolog);
if (m.find()) {
encoding = m.group(1).toUpperCase(Locale.ROOT);

MODIFY
src/main/java/org/apache/commons/io/monitor/FileAlterationMonitor.java
src/main/java/org/apache/commons/io/monitor/FileAlterationMonitor.java
import java.util.Optional;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ThreadFactory;
+import java.util.stream.Stream;
/**
* A runnable that spawns a monitoring thread triggering any
@@ -86,9 +87,7 @@ public FileAlterationMonitor(final long interval, final Collection<FileAlteratio
public FileAlterationMonitor(final long interval, final FileAlterationObserver... observers) {
this(interval);
if (observers != null) {
- for (final FileAlterationObserver observer : observers) {
- addObserver(observer);
- }
+ Stream.of(observers).forEach(this::addObserver);
}
}

MODIFY
src/main/java/org/apache/commons/io/monitor/FileAlterationObserver.java
src/main/java/org/apache/commons/io/monitor/FileAlterationObserver.java
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.stream.Stream;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOCase;
@@ -315,9 +316,7 @@ private void doCreate(final FileEntry entry) {
listener.onFileCreate(entry.getFile());
}
});
- for (final FileEntry aChildren : entry.getChildren()) {
- doCreate(aChildren);
- }
+ Stream.of(entry.getChildren()).forEach(this::doCreate);
}
/**

MODIFY
src/main/java/org/apache/commons/io/serialization/ValidatingObjectInputStream.java
src/main/java/org/apache/commons/io/serialization/ValidatingObjectInputStream.java
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
+import java.util.stream.Stream;
/**
* An {@link ObjectInputStream} that's restricted to deserialize
@@ -67,9 +68,7 @@ public ValidatingObjectInputStream(final InputStream input) throws IOException {
* @return this object
*/
public ValidatingObjectInputStream accept(final Class<?>... classes) {
- for (final Class<?> c : classes) {
- acceptMatchers.add(new FullClassNameMatcher(c.getName()));
- }
+ Stream.of(classes).map(c -> new FullClassNameMatcher(c.getName())).forEach(acceptMatchers::add);
return this;
}
@@ -106,9 +105,7 @@ public ValidatingObjectInputStream accept(final Pattern pattern) {
* @return this object
*/
public ValidatingObjectInputStream accept(final String... patterns) {
- for (final String pattern : patterns) {
- acceptMatchers.add(new WildcardClassNameMatcher(pattern));
- }
+ Stream.of(patterns).map(WildcardClassNameMatcher::new).forEach(acceptMatchers::add);
return this;
}
@@ -132,9 +129,7 @@ protected void invalidClassNameFound(final String className) throws InvalidClass
* @return this object
*/
public ValidatingObjectInputStream reject(final Class<?>... classes) {
- for (final Class<?> c : classes) {
- rejectMatchers.add(new FullClassNameMatcher(c.getName()));
- }
+ Stream.of(classes).map(c -> new FullClassNameMatcher(c.getName())).forEach(rejectMatchers::add);
return this;
}
@@ -171,9 +166,7 @@ public ValidatingObjectInputStream reject(final Pattern pattern) {
* @return this object
*/
public ValidatingObjectInputStream reject(final String... patterns) {
- for (final String pattern : patterns) {
- rejectMatchers.add(new WildcardClassNameMatcher(pattern));
- }
+ Stream.of(patterns).map(WildcardClassNameMatcher::new).forEach(rejectMatchers::add);
return this;
}

Add and reuse IOConsumer.forEach(*) and forEachIndexed(*).

Use forEach and streams.