org.infinispan.commons.util.Closeables Maven / Gradle / Ivy
package org.infinispan.commons.util;
import java.util.Iterator;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.function.Consumer;
import java.util.stream.BaseStream;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import org.reactivestreams.Publisher;
import io.reactivex.rxjava3.core.Flowable;
import io.reactivex.rxjava3.disposables.Disposable;
/**
* This class consists exclusively of static methods that operate on or return closeable interfaces. This is helpful
* when wanting to change a given interface to an appropriate closeable interface.
* @since 8.0
*/
public class Closeables {
private Closeables() { }
/**
* Takes a provided closeable iterator and wraps it appropriately so it can be used as a closeable spliterator that
* will close the iterator when the spliterator is closed.
* @param iterator The closeable iterator to change to a spliterator
* @param size The approximate size of the iterator. If no size is known {@link Long#MAX_VALUE} should be passed
* @param characteristics The characteristics of the given spliterator as defined on the {@link Spliterator}
* interface
* @param The type that is the same between the iterator and spliterator
* @return A spliterator that when closed will close the provided iterator
*/
public static CloseableSpliterator spliterator(CloseableIterator extends E> iterator, long size,
int characteristics) {
return new CloseableIteratorAsCloseableSpliterator<>(iterator, size, characteristics);
}
/**
* Creates a closeable spliterator from the given spliterator that does nothing when close is called.
* @param spliterator The spliterator to wrap to allow it to become a closeable spliterator.
* @param The type of the spliterators
* @return A spliterator that does nothing when closed
*/
public static CloseableSpliterator spliterator(Spliterator spliterator) {
if (spliterator instanceof CloseableSpliterator) {
return (CloseableSpliterator) spliterator;
}
return new SpliteratorAsCloseableSpliterator<>(spliterator);
}
/**
* Creates a closeable spliterator that when closed will close the underlying stream as well
* @param stream The stream to change into a closeable spliterator
* @param The type of the stream
* @return A spliterator that when closed will also close the underlying stream
*/
public static CloseableSpliterator spliterator(BaseStream> stream) {
Spliterator spliterator = stream.spliterator();
if (spliterator instanceof CloseableSpliterator) {
return (CloseableSpliterator) spliterator;
}
return new StreamToCloseableSpliterator<>(stream, spliterator);
}
/**
* Creates a closeable iterator that when closed will close the underlying stream as well
* @param stream The stream to change into a closeable iterator
* @param The type of the stream
* @return An iterator that when closed will also close the underlying stream
*/
public static CloseableIterator iterator(BaseStream> stream) {
Iterator iterator = stream.iterator();
if (iterator instanceof CloseableIterator) {
return (CloseableIterator) iterator;
}
return new StreamToCloseableIterator<>(stream, iterator);
}
/**
* Creates a closeable iterator from the given iterator that does nothing when close is called.
* @param iterator The iterator to wrap to allow it to become a closeable iterator
* @param The type of the iterators
* @return An iterator that does nothing when closed
*/
public static CloseableIterator iterator(Iterator extends E> iterator) {
if (iterator instanceof CloseableIterator) {
return (CloseableIterator) iterator;
}
return new IteratorAsCloseableIterator<>(iterator);
}
/**
* Creates a stream that when closed will also close the underlying spliterator
* @param spliterator spliterator to back the stream and subsequently close
* @param parallel whether or not the returned stream is parallel or not
* @param the type of the stream
* @return the stream to use
*/
public static Stream stream(CloseableSpliterator spliterator, boolean parallel) {
Stream stream = StreamSupport.stream(spliterator, parallel);
stream.onClose(spliterator::close);
return stream;
}
/**
* Creates a stream that when closed will also close the underlying iterator
* @param iterator iterator to back the stream and subsequently close
* @param parallel whether or not the returned stream is parallel or not
* @param size the size of the iterator if known, otherwise {@link Long#MAX_VALUE} should be passed.
* @param characteristics the characteristics of the iterator to be used
* @param the type of the stream
* @return the stream to use
*/
public static Stream stream(CloseableIterator iterator, boolean parallel, long size, int characteristics) {
Stream stream = StreamSupport.stream(Spliterators.spliterator(iterator, size, characteristics), parallel);
stream.onClose(iterator::close);
return stream;
}
private static class IteratorAsCloseableIterator implements CloseableIterator {
private final Iterator extends E> iterator;
public IteratorAsCloseableIterator(Iterator extends E> iterator) {
this.iterator = iterator;
}
@Override
public void close() {
// This does nothing
}
@Override
public boolean hasNext() {
return iterator.hasNext();
}
@Override
public E next() {
return iterator.next();
}
@Override
public void remove() {
iterator.remove();
}
}
private static class SpliteratorAsCloseableSpliterator implements CloseableSpliterator {
private final Spliterator spliterator;
public SpliteratorAsCloseableSpliterator(Spliterator spliterator) {
this.spliterator = spliterator;
}
@Override
public void close() {
}
@Override
public boolean tryAdvance(Consumer super T> action) {
return spliterator.tryAdvance(action);
}
@Override
public Spliterator trySplit() {
return spliterator.trySplit();
}
@Override
public long estimateSize() {
return spliterator.estimateSize();
}
@Override
public int characteristics() {
return spliterator.characteristics();
}
}
private static class CloseableIteratorAsCloseableSpliterator extends SpliteratorAsCloseableSpliterator {
private final CloseableIterator extends E> iterator;
CloseableIteratorAsCloseableSpliterator(CloseableIterator extends E> iterator, long size, int characteristics) {
super(Spliterators.spliterator(iterator, size, characteristics));
this.iterator = iterator;
}
@Override
public void close() {
this.iterator.close();
}
}
private static class StreamToCloseableIterator extends IteratorAsCloseableIterator {
private final BaseStream> stream;
public StreamToCloseableIterator(BaseStream> stream, Iterator iterator) {
super(iterator);
this.stream = stream;
}
@Override
public void close() {
stream.close();
}
}
private static class StreamToCloseableSpliterator extends SpliteratorAsCloseableSpliterator {
private final BaseStream> stream;
public StreamToCloseableSpliterator(BaseStream> stream, Spliterator spliterator) {
super(spliterator);
this.stream = stream;
}
@Override
public void close() {
stream.close();
}
}
/**
* Converts a {@link Publisher} to a {@link CloseableIterator} by utilizing items fetched into an array and
* refetched as they are consumed from the iterator. The iterator when closed will also close the underlying
* {@link org.reactivestreams.Subscription} when subscribed to the publisher.
* @param publisher the publisher to convert
* @param fetchSize how many entries to hold in memory at once in preparation for the iterators consumption
* @param value type
* @return an iterator that when closed will cancel the subscription
*/
public static CloseableIterator iterator(Publisher publisher, int fetchSize) {
// This iterator from rxjava3 implements Disposable akin to Closeable
Flowable flowable = Flowable.fromPublisher(publisher);
@SuppressWarnings("checkstyle:forbiddenmethod")
Iterable iterable = flowable.blockingIterable(fetchSize);
Iterator iterator = iterable.iterator();
return new CloseableIterator() {
@Override
public void close() {
((Disposable) iterator).dispose();
}
@Override
public boolean hasNext() {
return iterator.hasNext();
}
@Override
public E next() {
return iterator.next();
}
};
}
}