package org.infinispan.stream.impl.local;
import java.util.Comparator;
import java.util.Optional;
import java.util.Set;
import java.util.Spliterator;
import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.BinaryOperator;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.IntFunction;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.function.ToDoubleFunction;
import java.util.function.ToIntFunction;
import java.util.function.ToLongFunction;
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 org.infinispan.Cache;
import org.infinispan.CacheStream;
import org.infinispan.commons.util.CloseableIterator;
import org.infinispan.commons.util.Closeables;
import org.infinispan.factories.ComponentRegistry;
import org.infinispan.stream.CacheAware;
import org.infinispan.stream.impl.intops.IntermediateOperation;
import org.infinispan.stream.impl.intops.object.DistinctOperation;
import org.infinispan.stream.impl.intops.object.FilterOperation;
import org.infinispan.stream.impl.intops.object.FlatMapOperation;
import org.infinispan.stream.impl.intops.object.FlatMapToDoubleOperation;
import org.infinispan.stream.impl.intops.object.FlatMapToIntOperation;
import org.infinispan.stream.impl.intops.object.FlatMapToLongOperation;
import org.infinispan.stream.impl.intops.object.LimitOperation;
import org.infinispan.stream.impl.intops.object.MapOperation;
import org.infinispan.stream.impl.intops.object.MapToDoubleOperation;
import org.infinispan.stream.impl.intops.object.MapToIntOperation;
import org.infinispan.stream.impl.intops.object.MapToLongOperation;
import org.infinispan.stream.impl.intops.object.PeekOperation;
import org.infinispan.stream.impl.intops.object.SkipOperation;
import org.infinispan.stream.impl.intops.object.SortedComparatorOperation;
import org.infinispan.stream.impl.intops.object.SortedOperation;
import org.infinispan.util.function.RemovableFunction;
import org.infinispan.util.function.SerializableBiConsumer;
import org.infinispan.util.function.SerializableBiFunction;
import org.infinispan.util.function.SerializableBinaryOperator;
import org.infinispan.util.function.SerializableComparator;
import org.infinispan.util.function.SerializableConsumer;
import org.infinispan.util.function.SerializableFunction;
import org.infinispan.util.function.SerializableIntFunction;
import org.infinispan.util.function.SerializablePredicate;
import org.infinispan.util.function.SerializableSupplier;
import org.infinispan.util.function.SerializableToDoubleFunction;
import org.infinispan.util.function.SerializableToIntFunction;
import org.infinispan.util.function.SerializableToLongFunction;
/**
* CacheStream that is to be used locally. This allows for full functionality of a regular stream but also has options
* to filter by keys and other functionality.
* @param <R> type of the stream
*/
public class LocalCacheStream<R> extends AbstractLocalCacheStream<R, Stream<R>, CacheStream<R>> implements CacheStream<R> {
public LocalCacheStream(StreamSupplier<R, Stream<R>> streamSupplier, boolean parallel, ComponentRegistry registry) {
super(streamSupplier, parallel, registry);
}
public LocalCacheStream(AbstractLocalCacheStream<?, ?, ?> other) {
super(other);
}
@Override
public LocalCacheStream<R> sequentialDistribution() {
return this;
}
@Override
public LocalCacheStream<R> parallelDistribution() {
return this;
}
@Override
public LocalCacheStream<R> filterKeySegments(Set<Integer> segments) {
segmentsToFilter = segments;
return this;
}
@Override
public LocalCacheStream<R> filterKeys(Set<?> keys) {
keysToFilter = keys;
return this;
}
@Override
public LocalCacheStream<R> distributedBatchSize(int batchSize) {
// TODO: Does this change cache loader?
return this;
}
@Override
public LocalCacheStream<R> segmentCompletionListener(SegmentCompletionListener listener) {
// All segments are completed when the getStream() is completed so we don't track them
return this;
}
@Override
public LocalCacheStream<R> disableRehashAware() {
// Local stream doesn't matter for rehash
return this;
}
@Override
public LocalCacheStream<R> filter(Predicate<? super R> predicate) {
registry.wireDependencies(predicate);
intermediateOperations.add(new FilterOperation<>(predicate));
return this;
}
@Override
public LocalCacheStream<R> filter(SerializablePredicate<? super R> predicate) {
return filter((Predicate<? super R>) predicate);
}
@Override
public <R1> LocalCacheStream<R1> map(Function<? super R, ? extends R1> mapper) {
registry.wireDependencies(mapper);
intermediateOperations.add(new MapOperation<>(mapper));
return (LocalCacheStream<R1>) this;
}
@Override
public <R1> LocalCacheStream<R1> map(SerializableFunction<? super R, ? extends R1> mapper) {
return map((Function<? super R, ? extends R1>) mapper);
}
@Override
public LocalIntCacheStream mapToInt(ToIntFunction<? super R> mapper) {
intermediateOperations.add(new MapToIntOperation<>(mapper));
return new LocalIntCacheStream(this);
}
@Override
public LocalIntCacheStream mapToInt(SerializableToIntFunction<? super R> mapper) {
return mapToInt((ToIntFunction<? super R>) mapper);
}
@Override
public LocalLongCacheStream mapToLong(ToLongFunction<? super R> mapper) {
intermediateOperations.add(new MapToLongOperation<>(mapper));
return new LocalLongCacheStream(this);
}
@Override
public LocalLongCacheStream mapToLong(SerializableToLongFunction<? super R> mapper) {
return mapToLong((ToLongFunction<? super R>) mapper);
}
@Override
public LocalDoubleCacheStream mapToDouble(ToDoubleFunction<? super R> mapper) {
intermediateOperations.add(new MapToDoubleOperation<>(mapper));
return new LocalDoubleCacheStream(this);
}
@Override
public LocalDoubleCacheStream mapToDouble(SerializableToDoubleFunction<? super R> mapper) {
return mapToDouble((ToDoubleFunction<? super R>) mapper);
}
@Override
public <R1> LocalCacheStream<R1> flatMap(Function<? super R, ? extends Stream<? extends R1>> mapper) {
intermediateOperations.add(new FlatMapOperation<>(mapper));
return (LocalCacheStream<R1>) this;
}
@Override
public <R1> LocalCacheStream<R1> flatMap(SerializableFunction<? super R, ? extends Stream<? extends R1>> mapper) {
return flatMap((Function<? super R, ? extends Stream<? extends R1>>) mapper);
}
@Override
public LocalIntCacheStream flatMapToInt(Function<? super R, ? extends IntStream> mapper) {
intermediateOperations.add(new FlatMapToIntOperation<>(mapper));
return new LocalIntCacheStream(this);
}
@Override
public LocalIntCacheStream flatMapToInt(SerializableFunction<? super R, ? extends IntStream> mapper) {
return flatMapToInt((Function<? super R, ? extends IntStream>) mapper);
}
@Override
public LocalLongCacheStream flatMapToLong(Function<? super R, ? extends LongStream> mapper) {
intermediateOperations.add(new FlatMapToLongOperation<>(mapper));
return new LocalLongCacheStream(this);
}
@Override
public LocalLongCacheStream flatMapToLong(SerializableFunction<? super R, ? extends LongStream> mapper) {
return flatMapToLong((Function<? super R, ? extends LongStream>) mapper);
}
@Override
public LocalDoubleCacheStream flatMapToDouble(Function<? super R, ? extends DoubleStream> mapper) {
intermediateOperations.add(new FlatMapToDoubleOperation<>(mapper));
return new LocalDoubleCacheStream(this);
}
@Override
public LocalDoubleCacheStream flatMapToDouble(SerializableFunction<? super R, ? extends DoubleStream> mapper) {
return flatMapToDouble((Function<? super R, ? extends DoubleStream>) mapper);
}
@Override
public LocalCacheStream<R> distinct() {
intermediateOperations.add(DistinctOperation.getInstance());
return this;
}
@Override
public LocalCacheStream<R> sorted() {
intermediateOperations.add(SortedOperation.getInstance());
return this;
}
@Override
public LocalCacheStream<R> sorted(Comparator<? super R> comparator) {
intermediateOperations.add(new SortedComparatorOperation<>(comparator));
return this;
}
@Override
public LocalCacheStream<R> sorted(SerializableComparator<? super R> comparator) {
return sorted((Comparator<? super R>) comparator);
}
@Override
public LocalCacheStream<R> peek(Consumer<? super R> action) {
intermediateOperations.add(new PeekOperation<>(action));
return this;
}
@Override
public LocalCacheStream<R> peek(SerializableConsumer<? super R> action) {
return peek((Consumer<? super R>) action);
}
@Override
public LocalCacheStream<R> limit(long maxSize) {
intermediateOperations.add(new LimitOperation<>(maxSize));
return this;
}
@Override
public LocalCacheStream<R> skip(long n) {
intermediateOperations.add(new SkipOperation<>(n));
return this;
}
@Override
public void forEach(Consumer<? super R> action) {
injectCache(action);
createStream().forEach(action);
}
@Override
public void forEach(SerializableConsumer<? super R> action) {
forEach((Consumer<? super R>) action);
}
@Override
public <K, V> void forEach(BiConsumer<Cache<K, V>, ? super R> action) {
Cache<K, V> cache = registry.getComponent(Cache.class);
createStream().forEach(e -> action.accept(cache, e));
}
@Override
public <K, V> void forEach(SerializableBiConsumer<Cache<K, V>, ? super R> action) {
forEach((BiConsumer<Cache<K, V>, ? super R>) action);
}
@Override
public void forEachOrdered(Consumer<? super R> action) {
injectCache(action);
createStream().forEachOrdered(action);
}
/**
* Method to inject a cache into a consumer. Note we only support this for the consumer at this
* time.
* @param cacheAware the instance that may be a {@link CacheAware}
*/
private void injectCache(Consumer<? super R> cacheAware) {
if (cacheAware instanceof CacheAware) {
((CacheAware) cacheAware).injectCache(registry.getComponent(Cache.class));
}
}
@Override
public Object[] toArray() {
return createStream().toArray();
}
@Override
public <A> A[] toArray(IntFunction<A[]> generator) {
return createStream().toArray(generator);
}
@Override
public <A> A[] toArray(SerializableIntFunction<A[]> generator) {
return toArray((IntFunction<A[]>) generator);
}
@Override
public R reduce(R identity, BinaryOperator<R> accumulator) {
return createStream().reduce(identity, accumulator);
}
@Override
public R reduce(R identity, SerializableBinaryOperator<R> accumulator) {
return reduce(identity, (BinaryOperator<R>) accumulator);
}
@Override
public Optional<R> reduce(BinaryOperator<R> accumulator) {
return createStream().reduce(accumulator);
}
@Override
public Optional<R> reduce(SerializableBinaryOperator<R> accumulator) {
return reduce((BinaryOperator<R>) accumulator);
}
@Override
public <U> U reduce(U identity, BiFunction<U, ? super R, U> accumulator, BinaryOperator<U> combiner) {
return createStream().reduce(identity, accumulator, combiner);
}
@Override
public <U> U reduce(U identity, SerializableBiFunction<U, ? super R, U> accumulator, SerializableBinaryOperator<U> combiner) {
return reduce(identity, (BiFunction<U, ? super R, U>) accumulator, combiner);
}
@Override
public <R1> R1 collect(Supplier<R1> supplier, BiConsumer<R1, ? super R> accumulator, BiConsumer<R1, R1> combiner) {
return createStream().collect(supplier, accumulator, combiner);
}
@Override
public <R1> R1 collect(SerializableSupplier<R1> supplier, SerializableBiConsumer<R1, ? super R> accumulator, SerializableBiConsumer<R1, R1> combiner) {
return collect((Supplier<R1>) supplier, accumulator, combiner);
}
@Override
public <R1, A> R1 collect(Collector<? super R, A, R1> collector) {
return createStream().collect(collector);
}
@Override
public Optional<R> min(Comparator<? super R> comparator) {
return createStream().min(comparator);
}
@Override
public Optional<R> min(SerializableComparator<? super R> comparator) {
return min((Comparator<? super R>) comparator);
}
@Override
public Optional<R> max(Comparator<? super R> comparator) {
return createStream().max(comparator);
}
@Override
public Optional<R> max(SerializableComparator<? super R> comparator) {
return max((Comparator<? super R>) comparator);
}
@Override
public long count() {
return createStream().count();
}
@Override
public boolean anyMatch(Predicate<? super R> predicate) {
return createStream().anyMatch(predicate);
}
@Override
public boolean anyMatch(SerializablePredicate<? super R> predicate) {
return anyMatch((Predicate<? super R>) predicate);
}
@Override
public boolean allMatch(Predicate<? super R> predicate) {
return createStream().allMatch(predicate);
}
@Override
public boolean allMatch(SerializablePredicate<? super R> predicate) {
return allMatch((Predicate<? super R>) predicate);
}
@Override
public boolean noneMatch(Predicate<? super R> predicate) {
return createStream().noneMatch(predicate);
}
@Override
public boolean noneMatch(SerializablePredicate<? super R> predicate) {
return noneMatch((Predicate<? super R>) predicate);
}
@Override
public Optional<R> findFirst() {
return createStream().findFirst();
}
@Override
public Optional<R> findAny() {
return createStream().findAny();
}
@Override
public CloseableIterator<R> iterator() {
int size = intermediateOperations.size();
if (size == 0) {
// If no intermediate operations we can support remove
return streamSupplier.removableIterator(Closeables.iterator(createStream()));
}
else if (size == 1) {
IntermediateOperation intOp = intermediateOperations.peek();
if (intOp instanceof MapOperation) {
MapOperation map = (MapOperation) intOp;
if (map.getFunction() instanceof RemovableFunction) {
// If function was removable means we can just use remove as is
return streamSupplier.removableIterator(Closeables.iterator(createStream()));
}
}
}
return Closeables.iterator(createStream());
}
@Override
public Spliterator<R> spliterator() {
return createStream().spliterator();
}
@Override
public LocalCacheStream<R> timeout(long timeout, TimeUnit unit) {
// Timeout does nothing for a local cache stream
return this;
}
}