IOStream.java
/*
* 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.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Optional;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BiFunction;
import java.util.function.IntFunction;
import java.util.function.ToDoubleFunction;
import java.util.function.ToIntFunction;
import java.util.function.ToLongFunction;
import java.util.function.UnaryOperator;
import java.util.stream.Collector;
import java.util.stream.DoubleStream;
import java.util.stream.IntStream;
import java.util.stream.LongStream;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import org.apache.commons.io.IOExceptionList;
/**
* Like {@link Stream} but throws {@link IOException}.
*
* @param <T> the type of the stream elements.
* @since 2.12.0
*/
public interface IOStream<T> extends IOBaseStream<T, IOStream<T>, Stream<T>> {
/**
* Constructs a new IOStream for the given Stream.
*
* @param <T> the type of the stream elements.
* @param stream The stream to delegate.
* @return a new IOStream.
*/
static <T> IOStream<T> adapt(final Stream<T> stream) {
return IOStreamAdapter.adapt(stream);
}
/**
* This class' version of {@link Stream#empty()}.
*
* @param <T> the type of the stream elements
* @return an empty sequential {@code IOStreamImpl}.
* @see Stream#empty()
*/
static <T> IOStream<T> empty() {
return IOStreamAdapter.adapt(Stream.empty());
}
/**
* Like {@link Stream#iterate(Object, UnaryOperator)} but for IO.
*
* @param <T> the type of stream elements.
* @param seed the initial element.
* @param f a function to be applied to the previous element to produce a new element.
* @return a new sequential {@code IOStream}.
*/
static <T> IOStream<T> iterate(final T seed, final IOUnaryOperator<T> f) {
Objects.requireNonNull(f);
final Iterator<T> iterator = new Iterator<T>() {
@SuppressWarnings("unchecked")
T t = (T) IOStreams.NONE;
@Override
public boolean hasNext() {
return true;
}
@Override
public T next() throws NoSuchElementException {
try {
return t = t == IOStreams.NONE ? seed : f.apply(t);
} catch (final IOException e) {
final NoSuchElementException nsee = new NoSuchElementException();
nsee.initCause(e);
throw nsee;
}
}
};
return adapt(StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, Spliterator.ORDERED | Spliterator.IMMUTABLE), false));
}
/**
* Null-safe version of {@link StreamSupport#stream(java.util.Spliterator, boolean)}.
*
* 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> IOStream<T> of(final Iterable<T> values) {
return values == null ? empty() : adapt(StreamSupport.stream(values.spliterator(), false));
}
/**
* Null-safe version of {@link Stream#of(Object[])} for an IO stream.
*
* @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> IOStream<T> of(final T... values) {
return values == null || values.length == 0 ? empty() : adapt(Arrays.stream(values));
}
/**
* Returns a sequential {@code IOStreamImpl} containing a single element.
*
* @param t the single element
* @param <T> the type of stream elements
* @return a singleton sequential stream
*/
static <T> IOStream<T> of(final T t) {
return adapt(Stream.of(t));
}
/**
* Like {@link Stream#allMatch(java.util.function.Predicate)} but throws {@link IOException}.
*
* @param predicate {@link Stream#allMatch(java.util.function.Predicate)}.
* @return Like {@link Stream#allMatch(java.util.function.Predicate)}.
* @throws IOException if an I/O error occurs.
*/
@SuppressWarnings("unused") // thrown by Erase.
default boolean allMatch(final IOPredicate<? super T> predicate) throws IOException {
return unwrap().allMatch(t -> Erase.test(predicate, t));
}
/**
* Like {@link Stream#anyMatch(java.util.function.Predicate)} but throws {@link IOException}.
*
* @param predicate {@link Stream#anyMatch(java.util.function.Predicate)}.
* @return Like {@link Stream#anyMatch(java.util.function.Predicate)}.
* @throws IOException if an I/O error occurs.
*/
@SuppressWarnings("unused") // thrown by Erase.
default boolean anyMatch(final IOPredicate<? super T> predicate) throws IOException {
return unwrap().anyMatch(t -> Erase.test(predicate, t));
}
/**
* TODO Package-private for now, needs IOCollector?
*
* Adding this method now and an IO version later is an issue because call sites would have to type-cast to pick one. It
* would be ideal to have only one.
*
* Like {@link Stream#collect(Collector)}.
*
* Package private for now.
*
* @param <R> Like {@link Stream#collect(Collector)}.
* @param <A> Like {@link Stream#collect(Collector)}.
* @param collector Like {@link Stream#collect(Collector)}.
* @return Like {@link Stream#collect(Collector)}.
*/
default <R, A> R collect(final Collector<? super T, A, R> collector) {
return unwrap().collect(collector);
}
/**
* Like
* {@link Stream#collect(java.util.function.Supplier, java.util.function.BiConsumer, java.util.function.BiConsumer)}.
*
* @param <R> Like
* {@link Stream#collect(java.util.function.Supplier, java.util.function.BiConsumer, java.util.function.BiConsumer)}.
* @param supplier Like
* {@link Stream#collect(java.util.function.Supplier, java.util.function.BiConsumer, java.util.function.BiConsumer)}.
* @param accumulator Like
* {@link Stream#collect(java.util.function.Supplier, java.util.function.BiConsumer, java.util.function.BiConsumer)}.
* @param combiner Like
* {@link Stream#collect(java.util.function.Supplier, java.util.function.BiConsumer, java.util.function.BiConsumer)}.
* @return Like
* {@link Stream#collect(java.util.function.Supplier, java.util.function.BiConsumer, java.util.function.BiConsumer)}.
* @throws IOException if an I/O error occurs.
*/
@SuppressWarnings("unused") // thrown by Erase.
default <R> R collect(final IOSupplier<R> supplier, final IOBiConsumer<R, ? super T> accumulator, final IOBiConsumer<R, R> combiner) throws IOException {
return unwrap().collect(() -> Erase.get(supplier), (t, u) -> Erase.accept(accumulator, t, u), (t, u) -> Erase.accept(combiner, t, u));
}
/**
* Like {@link Stream#count()}.
*
* @return Like {@link Stream#count()}.
*/
default long count() {
return unwrap().count();
}
/**
* Like {@link Stream#distinct()}.
*
* @return Like {@link Stream#distinct()}.
*/
default IOStream<T> distinct() {
return adapt(unwrap().distinct());
}
/**
* Like {@link Stream#filter(java.util.function.Predicate)}.
*
* @param predicate Like {@link Stream#filter(java.util.function.Predicate)}.
* @return Like {@link Stream#filter(java.util.function.Predicate)}.
* @throws IOException if an I/O error occurs.
*/
@SuppressWarnings("unused") // thrown by Erase.
default IOStream<T> filter(final IOPredicate<? super T> predicate) throws IOException {
return adapt(unwrap().filter(t -> Erase.test(predicate, t)));
}
/**
* Like {@link Stream#findAny()}.
*
* @return Like {@link Stream#findAny()}.
*/
default Optional<T> findAny() {
return unwrap().findAny();
}
/**
* Like {@link Stream#findFirst()}.
*
* @return Like {@link Stream#findFirst()}.
*/
default Optional<T> findFirst() {
return unwrap().findFirst();
}
/**
* Like {@link Stream#flatMap(java.util.function.Function)}.
*
* @param <R> Like {@link Stream#flatMap(java.util.function.Function)}.
* @param mapper Like {@link Stream#flatMap(java.util.function.Function)}.
* @return Like {@link Stream#flatMap(java.util.function.Function)}.
* @throws IOException if an I/O error occurs.
*/
@SuppressWarnings({ "unused", "resource" }) // thrown by Erase; resource closed by caller.
default <R> IOStream<R> flatMap(final IOFunction<? super T, ? extends IOStream<? extends R>> mapper) throws IOException {
return adapt(unwrap().flatMap(t -> Erase.apply(mapper, t).unwrap()));
}
/**
* TODO Package-private for now, needs IODoubleStream?
*
* Adding this method now and an IO version later is an issue because call sites would have to type-cast to pick one. It
* would be ideal to have only one.
*
* Like {@link Stream#flatMapToDouble(java.util.function.Function)}.
*
* @param mapper Like {@link Stream#flatMapToDouble(java.util.function.Function)}.
* @return Like {@link Stream#flatMapToDouble(java.util.function.Function)}.
* @throws IOException if an I/O error occurs.
*/
@SuppressWarnings("unused") // thrown by Erase.
default DoubleStream flatMapToDouble(final IOFunction<? super T, ? extends DoubleStream> mapper) throws IOException {
return unwrap().flatMapToDouble(t -> Erase.apply(mapper, t));
}
/**
* TODO Package-private for now, needs IOIntStream?
*
* Adding this method now and an IO version later is an issue because call sites would have to type-cast to pick one. It
* would be ideal to have only one.
*
* Like {@link Stream#flatMapToInt(java.util.function.Function)}.
*
* @param mapper Like {@link Stream#flatMapToInt(java.util.function.Function)}.
* @return Like {@link Stream#flatMapToInt(java.util.function.Function)}.
* @throws IOException if an I/O error occurs.
*/
@SuppressWarnings("unused") // thrown by Erase.
default IntStream flatMapToInt(final IOFunction<? super T, ? extends IntStream> mapper) throws IOException {
return unwrap().flatMapToInt(t -> Erase.apply(mapper, t));
}
/**
* TODO Package-private for now, needs IOLongStream?
*
* Adding this method now and an IO version later is an issue because call sites would have to type-cast to pick one. It
* would be ideal to have only one.
*
* Like {@link Stream#flatMapToLong(java.util.function.Function)}.
*
* @param mapper Like {@link Stream#flatMapToLong(java.util.function.Function)}.
* @return Like {@link Stream#flatMapToLong(java.util.function.Function)}.
* @throws IOException if an I/O error occurs.
*/
@SuppressWarnings("unused") // thrown by Erase.
default LongStream flatMapToLong(final IOFunction<? super T, ? extends LongStream> mapper) throws IOException {
return unwrap().flatMapToLong(t -> Erase.apply(mapper, t));
}
/**
* Performs an action for each element gathering any exceptions.
*
* @param action The action to apply to each element.
* @throws IOExceptionList if any I/O errors occur.
*/
default void forAll(final IOConsumer<T> action) throws IOExceptionList {
forAll(action, (i, e) -> e);
}
/**
* Performs an action for each element gathering any exceptions.
*
* @param action The action to apply to each element.
* @param exSupplier The exception supplier.
* @throws IOExceptionList if any I/O errors occur.
*/
default void forAll(final IOConsumer<T> action, final BiFunction<Integer, IOException, IOException> exSupplier) throws IOExceptionList {
final AtomicReference<List<IOException>> causeList = new AtomicReference<>();
final AtomicInteger index = new AtomicInteger();
final IOConsumer<T> safeAction = IOStreams.toIOConsumer(action);
unwrap().forEach(e -> {
try {
safeAction.accept(e);
} catch (final IOException innerEx) {
if (causeList.get() == null) {
// Only allocate if required
causeList.set(new ArrayList<>());
}
if (exSupplier != null) {
causeList.get().add(exSupplier.apply(index.get(), innerEx));
}
}
index.incrementAndGet();
});
IOExceptionList.checkEmpty(causeList.get(), null);
}
/**
* Like {@link Stream#forEach(java.util.function.Consumer)} but throws {@link IOException}.
*
* @param action Like {@link Stream#forEach(java.util.function.Consumer)}.
* @throws IOException if an I/O error occurs.
*/
@SuppressWarnings("unused") // thrown by Erase.
default void forEach(final IOConsumer<? super T> action) throws IOException {
unwrap().forEach(e -> Erase.accept(action, e));
}
/**
* Like {@link Stream#forEachOrdered(java.util.function.Consumer)}.
*
* @param action Like {@link Stream#forEachOrdered(java.util.function.Consumer)}.
* @throws IOException if an I/O error occurs.
*/
@SuppressWarnings("unused") // thrown by Erase.
default void forEachOrdered(final IOConsumer<? super T> action) throws IOException {
unwrap().forEachOrdered(e -> Erase.accept(action, e));
}
/**
* Like {@link Stream#limit(long)}.
*
* @param maxSize Like {@link Stream#limit(long)}.
* @return Like {@link Stream#limit(long)}.
*/
default IOStream<T> limit(final long maxSize) {
return adapt(unwrap().limit(maxSize));
}
/**
* Like {@link Stream#map(java.util.function.Function)}.
*
* @param <R> Like {@link Stream#map(java.util.function.Function)}.
* @param mapper Like {@link Stream#map(java.util.function.Function)}.
* @return Like {@link Stream#map(java.util.function.Function)}.
* @throws IOException if an I/O error occurs.
*/
@SuppressWarnings("unused") // thrown by Erase.
default <R> IOStream<R> map(final IOFunction<? super T, ? extends R> mapper) throws IOException {
return adapt(unwrap().map(t -> Erase.apply(mapper, t)));
}
/**
* TODO Package-private for now, needs IOToDoubleFunction?
*
* Adding this method now and an IO version later is an issue because call sites would have to type-cast to pick one. It
* would be ideal to have only one.
*
* Like {@link Stream#mapToDouble(ToDoubleFunction)}.
*
* Package private for now.
*
* @param mapper Like {@link Stream#mapToDouble(ToDoubleFunction)}.
* @return Like {@link Stream#mapToDouble(ToDoubleFunction)}.
*/
default DoubleStream mapToDouble(final ToDoubleFunction<? super T> mapper) {
return unwrap().mapToDouble(mapper);
}
/**
* TODO Package-private for now, needs IOToIntFunction?
*
* Adding this method now and an IO version later is an issue because call sites would have to type-cast to pick one. It
* would be ideal to have only one.
*
* Like {@link Stream#mapToInt(ToIntFunction)}.
*
* Package private for now.
*
* @param mapper Like {@link Stream#mapToInt(ToIntFunction)}.
* @return Like {@link Stream#mapToInt(ToIntFunction)}.
*/
default IntStream mapToInt(final ToIntFunction<? super T> mapper) {
return unwrap().mapToInt(mapper);
}
/**
* TODO Package-private for now, needs IOToLongFunction?
*
* Adding this method now and an IO version later is an issue because call sites would have to type-cast to pick one. It
* would be ideal to have only one.
*
* Like {@link Stream#mapToLong(ToLongFunction)}.
*
* Package private for now.
*
* @param mapper Like {@link Stream#mapToLong(ToLongFunction)}.
* @return Like {@link Stream#mapToLong(ToLongFunction)}.
*/
default LongStream mapToLong(final ToLongFunction<? super T> mapper) {
return unwrap().mapToLong(mapper);
}
/**
* Like {@link Stream#max(java.util.Comparator)}.
*
* @param comparator Like {@link Stream#max(java.util.Comparator)}.
* @return Like {@link Stream#max(java.util.Comparator)}.
* @throws IOException if an I/O error occurs.
*/
@SuppressWarnings("unused") // thrown by Erase.
default Optional<T> max(final IOComparator<? super T> comparator) throws IOException {
return unwrap().max((t, u) -> Erase.compare(comparator, t, u));
}
/**
* Like {@link Stream#min(java.util.Comparator)}.
*
* @param comparator Like {@link Stream#min(java.util.Comparator)}.
* @return Like {@link Stream#min(java.util.Comparator)}.
* @throws IOException if an I/O error occurs.
*/
@SuppressWarnings("unused") // thrown by Erase.
default Optional<T> min(final IOComparator<? super T> comparator) throws IOException {
return unwrap().min((t, u) -> Erase.compare(comparator, t, u));
}
/**
* Like {@link Stream#noneMatch(java.util.function.Predicate)}.
*
* @param predicate Like {@link Stream#noneMatch(java.util.function.Predicate)}.
* @return Like {@link Stream#noneMatch(java.util.function.Predicate)}.
* @throws IOException if an I/O error occurs.
*/
@SuppressWarnings("unused") // thrown by Erase.
default boolean noneMatch(final IOPredicate<? super T> predicate) throws IOException {
return unwrap().noneMatch(t -> Erase.test(predicate, t));
}
/**
* Like {@link Stream#peek(java.util.function.Consumer)}.
*
* @param action Like {@link Stream#peek(java.util.function.Consumer)}.
* @return Like {@link Stream#peek(java.util.function.Consumer)}.
* @throws IOException if an I/O error occurs.
*/
@SuppressWarnings("unused") // thrown by Erase.
default IOStream<T> peek(final IOConsumer<? super T> action) throws IOException {
return adapt(unwrap().peek(t -> Erase.accept(action, t)));
}
/**
* Like {@link Stream#reduce(java.util.function.BinaryOperator)}.
*
* @param accumulator Like {@link Stream#reduce(java.util.function.BinaryOperator)}.
* @return Like {@link Stream#reduce(java.util.function.BinaryOperator)}.
* @throws IOException if an I/O error occurs.
*/
@SuppressWarnings("unused") // thrown by Erase.
default Optional<T> reduce(final IOBinaryOperator<T> accumulator) throws IOException {
return unwrap().reduce((t, u) -> Erase.apply(accumulator, t, u));
}
/**
* Like {@link Stream#reduce(Object, java.util.function.BinaryOperator)}.
*
* @param identity Like {@link Stream#reduce(Object, java.util.function.BinaryOperator)}.
* @param accumulator Like {@link Stream#reduce(Object, java.util.function.BinaryOperator)}.
* @return Like {@link Stream#reduce(Object, java.util.function.BinaryOperator)}.
* @throws IOException if an I/O error occurs.
*/
@SuppressWarnings("unused") // thrown by Erase.
default T reduce(final T identity, final IOBinaryOperator<T> accumulator) throws IOException {
return unwrap().reduce(identity, (t, u) -> Erase.apply(accumulator, t, u));
}
/**
* Like {@link Stream#reduce(Object, BiFunction, java.util.function.BinaryOperator)}.
*
* @param <U> Like {@link Stream#reduce(Object, BiFunction, java.util.function.BinaryOperator)}.
* @param identity Like {@link Stream#reduce(Object, BiFunction, java.util.function.BinaryOperator)}.
* @param accumulator Like {@link Stream#reduce(Object, BiFunction, java.util.function.BinaryOperator)}.
* @param combiner Like {@link Stream#reduce(Object, BiFunction, java.util.function.BinaryOperator)}.
* @return Like {@link Stream#reduce(Object, BiFunction, java.util.function.BinaryOperator)}.
* @throws IOException if an I/O error occurs.
*/
@SuppressWarnings("unused") // thrown by Erase.
default <U> U reduce(final U identity, final IOBiFunction<U, ? super T, U> accumulator, final IOBinaryOperator<U> combiner) throws IOException {
return unwrap().reduce(identity, (t, u) -> Erase.apply(accumulator, t, u), (t, u) -> Erase.apply(combiner, t, u));
}
/**
* Like {@link Stream#skip(long)}.
*
* @param n Like {@link Stream#skip(long)}.
* @return Like {@link Stream#skip(long)}.
*/
default IOStream<T> skip(final long n) {
return adapt(unwrap().skip(n));
}
/**
* Like {@link Stream#sorted()}.
*
* @return Like {@link Stream#sorted()}.
*/
default IOStream<T> sorted() {
return adapt(unwrap().sorted());
}
/**
* Like {@link Stream#sorted(java.util.Comparator)}.
*
* @param comparator Like {@link Stream#sorted(java.util.Comparator)}.
* @return Like {@link Stream#sorted(java.util.Comparator)}.
* @throws IOException if an I/O error occurs.
*/
@SuppressWarnings("unused") // thrown by Erase.
default IOStream<T> sorted(final IOComparator<? super T> comparator) throws IOException {
return adapt(unwrap().sorted((t, u) -> Erase.compare(comparator, t, u)));
}
/**
* Like {@link Stream#toArray()}.
*
* @return {@link Stream#toArray()}.
*/
default Object[] toArray() {
return unwrap().toArray();
}
/**
* TODO Package-private for now, needs IOIntFunction?
*
* Adding this method now and an IO version later is an issue because call sites would have to type-cast to pick one. It
* would be ideal to have only one.
*
* Like {@link Stream#toArray(IntFunction)}.
*
* Package private for now.
*
* @param <A> Like {@link Stream#toArray(IntFunction)}.
* @param generator Like {@link Stream#toArray(IntFunction)}.
* @return Like {@link Stream#toArray(IntFunction)}.
*/
default <A> A[] toArray(final IntFunction<A[]> generator) {
return unwrap().toArray(generator);
}
}