Skip to content

Commit 0d1b15c

Browse files
authored
Merge branch 'master' into IOUtils-closewitherrors
2 parents c2751e6 + d790873 commit 0d1b15c

File tree

16 files changed

+319
-331
lines changed

16 files changed

+319
-331
lines changed

src/changes/changes.xml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,12 @@ The <action> type attribute can be add,update,fix,remove.
120120
<action dev="ggregory" type="fix" due-to="Arturo Bernal">
121121
Minor changes #287.
122122
</action>
123+
<action issue="IO-756" dev="ggregory" type="fix" due-to="wodencafe, Gary Gregory, Bruno P. Kinoshita">
124+
Update FileWriterWithEncoding to extend ProxyWriter #296.
125+
</action>
126+
<action dev="ggregory" type="fix" due-to="Gary Gregory">
127+
Initialize the message of an IOExceptionList to a default if null.
128+
</action>
123129
<!-- ADD -->
124130
<action dev="ggregory" type="add" due-to="Gary Gregory">
125131
Add BrokenReader.INSTANCE.
@@ -275,6 +281,15 @@ The <action> type attribute can be add,update,fix,remove.
275281
<action dev="ggregory" type="add" due-to="Gary Gregory">
276282
Add PathUtils.readAttributes(Path, Class, LinkOption...). #290
277283
</action>
284+
<action dev="ggregory" type="add" due-to="Gary Gregory">
285+
Add IOExceptionList.checkEmpty(List, Object).
286+
</action>
287+
<action dev="ggregory" type="add" due-to="Gary Gregory">
288+
Add IOBiConsumer.
289+
</action>
290+
<action dev="ggregory" type="add" due-to="Gary Gregory">
291+
Add and reuse IOConsumer.forEach(T[], IOConsumer) and forEachIndexed(Stream, IOConsumer).
292+
</action>
278293
<!-- UPDATE -->
279294
<action dev="ggregory" type="add" due-to="Gary Gregory">
280295
Update FileEntry to use FileTime instead of long for file time stamps.

src/main/java/org/apache/commons/io/FileUtils.java

Lines changed: 3 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@
7676
import org.apache.commons.io.filefilter.IOFileFilter;
7777
import org.apache.commons.io.filefilter.SuffixFileFilter;
7878
import org.apache.commons.io.filefilter.TrueFileFilter;
79+
import org.apache.commons.io.function.IOConsumer;
7980

8081
/**
8182
* General file manipulation utilities.
@@ -340,20 +341,7 @@ public static long checksumCRC32(final File file) throws IOException {
340341
* @see #forceDelete(File)
341342
*/
342343
public static void cleanDirectory(final File directory) throws IOException {
343-
final File[] files = listFiles(directory, null);
344-
345-
final List<Exception> causeList = new ArrayList<>();
346-
for (final File file : files) {
347-
try {
348-
forceDelete(file);
349-
} catch (final IOException ioe) {
350-
causeList.add(ioe);
351-
}
352-
}
353-
354-
if (!causeList.isEmpty()) {
355-
throw new IOExceptionList(directory.toString(), causeList);
356-
}
344+
IOConsumer.forEach(listFiles(directory, null), file -> forceDelete(file));
357345
}
358346

359347
/**
@@ -366,20 +354,7 @@ public static void cleanDirectory(final File directory) throws IOException {
366354
* @see #forceDeleteOnExit(File)
367355
*/
368356
private static void cleanDirectoryOnExit(final File directory) throws IOException {
369-
final File[] files = listFiles(directory, null);
370-
371-
final List<Exception> causeList = new ArrayList<>();
372-
for (final File file : files) {
373-
try {
374-
forceDeleteOnExit(file);
375-
} catch (final IOException ioe) {
376-
causeList.add(ioe);
377-
}
378-
}
379-
380-
if (!causeList.isEmpty()) {
381-
throw new IOExceptionList(causeList);
382-
}
357+
IOConsumer.forEach(listFiles(directory, null), file -> forceDeleteOnExit(file));
383358
}
384359

385360
/**

src/main/java/org/apache/commons/io/IOExceptionList.java

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,10 @@
2020
import java.io.IOException;
2121
import java.util.Collections;
2222
import java.util.List;
23+
import java.util.Objects;
2324

2425
/**
25-
* A IOException based on a list of Throwable causes.
26+
* An IOException based on a list of Throwable causes.
2627
* <p>
2728
* The first exception in the list is used as this exception's cause and is accessible with the usual
2829
* {@link #getCause()} while the complete list is accessible with {@link #getCauseList()}.
@@ -33,6 +34,29 @@
3334
public class IOExceptionList extends IOException {
3435

3536
private static final long serialVersionUID = 1L;
37+
38+
/**
39+
* Throws this exception if the list is not null or empty.
40+
*
41+
* @param causeList The list to test.
42+
* @param message The detail message, see {@link #getMessage()}.
43+
* @throws IOExceptionList if the list is not null or empty.
44+
* @since 2.12.0
45+
*/
46+
public static void checkEmpty(final List<? extends Throwable> causeList, final Object message) throws IOExceptionList {
47+
if (!isEmpty(causeList)) {
48+
throw new IOExceptionList(Objects.toString(message, null), causeList);
49+
}
50+
}
51+
52+
private static boolean isEmpty(final List<? extends Throwable> causeList) {
53+
return causeList == null || causeList.isEmpty();
54+
}
55+
56+
private static String toMessage(final List<? extends Throwable> causeList) {
57+
return String.format("%,d exception(s): %s", causeList == null ? 0 : causeList.size(), causeList);
58+
}
59+
3660
private final List<? extends Throwable> causeList;
3761

3862
/**
@@ -41,7 +65,7 @@ public class IOExceptionList extends IOException {
4165
* @param causeList a list of cause exceptions.
4266
*/
4367
public IOExceptionList(final List<? extends Throwable> causeList) {
44-
this(String.format("%,d exceptions: %s", causeList == null ? 0 : causeList.size(), causeList), causeList);
68+
this(toMessage(causeList), causeList);
4569
}
4670

4771
/**
@@ -52,7 +76,7 @@ public IOExceptionList(final List<? extends Throwable> causeList) {
5276
* @since 2.9.0
5377
*/
5478
public IOExceptionList(final String message, final List<? extends Throwable> causeList) {
55-
super(message, causeList == null || causeList.isEmpty() ? null : causeList.get(0));
79+
super(message != null ? message : toMessage(causeList), isEmpty(causeList) ? null : causeList.get(0));
5680
this.causeList = causeList == null ? Collections.emptyList() : causeList;
5781
}
5882

@@ -76,7 +100,7 @@ public <T extends Throwable> T getCause(final int index) {
76100
* @return The list of causes.
77101
*/
78102
public <T extends Throwable> T getCause(final int index, final Class<T> clazz) {
79-
return clazz.cast(causeList.get(index));
103+
return clazz.cast(getCause(index));
80104
}
81105

82106
/**

src/main/java/org/apache/commons/io/IOUtils.java

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -384,29 +384,14 @@ public static void close(final Closeable closeable) throws IOException {
384384
}
385385

386386
/**
387-
* Closes the given {@link Closeable} as a null-safe operation.
387+
* Closes the given {@link Closeable}s as null-safe operations.
388388
*
389389
* @param closeables The resource(s) to close, may be null.
390390
* @throws IOException if an I/O error occurs.
391391
* @since 2.8.0
392392
*/
393393
public static void close(final Closeable... closeables) throws IOException {
394-
if (closeables != null) {
395-
List<IOException> exceptions = null;
396-
for (final Closeable closeable : closeables) {
397-
try {
398-
close(closeable);
399-
} catch (IOException ex) {
400-
if (exceptions == null) {
401-
exceptions = new ArrayList<>();
402-
}
403-
exceptions.add(ex);
404-
}
405-
}
406-
if (exceptions != null) {
407-
throw new IOExceptionList(exceptions);
408-
}
409-
}
394+
IOConsumer.forEach(closeables, IOUtils::close);
410395
}
411396

412397
/**
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package org.apache.commons.io.function;
19+
20+
import java.io.IOException;
21+
import java.util.Objects;
22+
import java.util.function.BiConsumer;
23+
24+
/**
25+
* Like {@link BiConsumer} but throws {@link IOException}.
26+
*
27+
* @param <T> the type of the first argument to the operation
28+
* @param <U> the type of the second argument to the operation
29+
*
30+
* @see BiConsumer
31+
* @since 1.8
32+
*/
33+
@FunctionalInterface
34+
public interface IOBiConsumer<T, U> {
35+
36+
/**
37+
* Performs this operation on the given arguments.
38+
*
39+
* @param t the first input argument
40+
* @param u the second input argument
41+
* @throws IOException if an I/O error occurs.
42+
*/
43+
void accept(T t, U u) throws IOException;
44+
45+
/**
46+
* Returns a composed {@code BiConsumer} that performs, in sequence, this operation followed by the {@code after}
47+
* operation. If performing either operation throws an exception, it is relayed to the caller of the composed operation.
48+
* If performing this operation throws an exception, the {@code after} operation will not be performed.
49+
*
50+
* @param after the operation to perform after this operation
51+
* @return a composed {@code BiConsumer} that performs in sequence this operation followed by the {@code after}
52+
* operation
53+
* @throws NullPointerException if {@code after} is null
54+
*/
55+
default IOBiConsumer<T, U> andThen(IOBiConsumer<? super T, ? super U> after) {
56+
Objects.requireNonNull(after);
57+
58+
return (l, r) -> {
59+
accept(l, r);
60+
after.accept(l, r);
61+
};
62+
}
63+
}

src/main/java/org/apache/commons/io/function/IOConsumer.java

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@
2020
import java.io.IOException;
2121
import java.util.Objects;
2222
import java.util.function.Consumer;
23+
import java.util.stream.Stream;
24+
25+
import org.apache.commons.io.IOExceptionList;
26+
import org.apache.commons.io.IOIndexedException;
2327

2428
/**
2529
* Like {@link Consumer} but throws {@link IOException}.
@@ -35,6 +39,32 @@ public interface IOConsumer<T> {
3539
*/
3640
IOConsumer<?> NOOP_IO_CONSUMER = t -> {/* noop */};
3741

42+
/**
43+
* Performs an action for each element of this stream.
44+
*
45+
* @param <T> The element type.
46+
* @param array The input to stream.
47+
* @param action The action to apply to each input element.
48+
* @throws IOException if an I/O error occurs.
49+
* @since 2.12.0
50+
*/
51+
static <T> void forEach(final T[] array, final IOConsumer<T> action) throws IOException {
52+
IOStreams.forEach(IOStreams.of(array), action);
53+
}
54+
55+
/**
56+
* Performs an action for each element of this stream.
57+
*
58+
* @param <T> The element type.
59+
* @param stream The input to stream.
60+
* @param action The action to apply to each input element.
61+
* @throws IOExceptionList if an I/O error occurs.
62+
* @since 2.12.0
63+
*/
64+
static <T> void forEachIndexed(final Stream<T> stream, final IOConsumer<T> action) throws IOExceptionList {
65+
IOStreams.forEachIndexed(stream, action, IOIndexedException::new);
66+
}
67+
3868
/**
3969
* Returns a constant NOOP consumer.
4070
*
@@ -56,13 +86,12 @@ static <T> IOConsumer<T> noop() {
5686
void accept(T t) throws IOException;
5787

5888
/**
59-
* Returns a composed {@code IoConsumer} that performs, in sequence, this operation followed by the {@code after}
60-
* operation. If performing either operation throws an exception, it is relayed to the caller of the composed
61-
* operation. If performing this operation throws an exception, the {@code after} operation will not be performed.
89+
* Returns a composed {@code IOConsumer} that performs, in sequence, this operation followed by the {@code after}
90+
* operation. If performing either operation throws an exception, it is relayed to the caller of the composed operation.
91+
* If performing this operation throws an exception, the {@code after} operation will not be performed.
6292
*
6393
* @param after the operation to perform after this operation
64-
* @return a composed {@code Consumer} that performs in sequence this operation followed by the {@code after}
65-
* operation
94+
* @return a composed {@code Consumer} that performs in sequence this operation followed by the {@code after} operation
6695
* @throws NullPointerException if {@code after} is null
6796
*/
6897
default IOConsumer<T> andThen(final IOConsumer<? super T> after) {
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package org.apache.commons.io.function;
19+
20+
import java.io.IOException;
21+
import java.util.ArrayList;
22+
import java.util.List;
23+
import java.util.concurrent.atomic.AtomicInteger;
24+
import java.util.concurrent.atomic.AtomicReference;
25+
import java.util.function.BiFunction;
26+
import java.util.stream.Stream;
27+
28+
import org.apache.commons.io.IOExceptionList;
29+
30+
/**
31+
* Keeps code package private for now.
32+
*/
33+
class IOStreams {
34+
35+
/**
36+
* Null-safe version of {@link Stream#of(Object[])}.
37+
*
38+
* Copied from Apache Commons Lang.
39+
*
40+
* @param <T> the type of stream elements.
41+
* @param values the elements of the new stream, may be {@code null}.
42+
* @return the new stream on {@code values} or {@link Stream#empty()}.
43+
*/
44+
@SafeVarargs // Creating a stream from an array is safe
45+
static <T> Stream<T> of(final T... values) {
46+
return values == null ? Stream.empty() : Stream.of(values);
47+
}
48+
49+
static <T> void forEach(final Stream<T> stream, final IOConsumer<T> action) throws IOException {
50+
forEachIndexed(stream, action, (i, e) -> e);
51+
}
52+
53+
static <T> void forEachIndexed(final Stream<T> stream, final IOConsumer<T> action, final BiFunction<Integer, IOException, IOException> exSupplier)
54+
throws IOExceptionList {
55+
final AtomicReference<List<IOException>> causeList = new AtomicReference<>();
56+
final AtomicInteger index = new AtomicInteger();
57+
stream.forEach(e -> {
58+
try {
59+
action.accept(e);
60+
} catch (final IOException ioex) {
61+
if (causeList.get() == null) {
62+
causeList.set(new ArrayList<>());
63+
}
64+
causeList.get().add(exSupplier.apply(index.get(), ioex));
65+
}
66+
index.incrementAndGet();
67+
});
68+
IOExceptionList.checkEmpty(causeList.get(), "forEach");
69+
}
70+
71+
}

0 commit comments

Comments
 (0)