package org.infinispan.stream.impl.local; import java.util.Collection; import java.util.HashSet; import java.util.Set; import java.util.Spliterator; import java.util.stream.Stream; import org.infinispan.Cache; import org.infinispan.CacheCollection; import org.infinispan.CacheSet; import org.infinispan.CacheStream; import org.infinispan.commands.read.AbstractCloseableIteratorCollection; import org.infinispan.commons.util.CloseableIterator; import org.infinispan.commons.util.CloseableIteratorMapper; import org.infinispan.commons.util.CloseableSpliterator; import org.infinispan.commons.util.Closeables; import org.infinispan.container.entries.CacheEntry; import org.infinispan.stream.StreamMarshalling; /** * CacheCollection that can be used for the values method of a cache. Backs all the calls to the cacheSet version * allowing for key filtering still to be applied. * @param <K> key type of the cache * @param <V> value type of the cache */ public class ValueCacheCollection<K, V> extends AbstractCloseableIteratorCollection<V, K, V> implements CacheCollection<V> { private final CacheSet<CacheEntry<K, V>> cacheSet; public ValueCacheCollection(Cache<K, V> cache, CacheSet<CacheEntry<K, V>> cacheSet) { super(cache); this.cacheSet = cacheSet; } @Override public CloseableIterator<V> iterator() { return new CloseableIteratorMapper<>(cacheSet.iterator(), CacheEntry::getValue); } @Override public CloseableSpliterator<V> spliterator() { return Closeables.spliterator(iterator(), cache.getAdvancedCache().getDataContainer().sizeIncludingExpired(), Spliterator.CONCURRENT | Spliterator.NONNULL); } @Override public boolean contains(Object o) { // We don't support null values if (o == null) { throw new NullPointerException(); } try (CloseableIterator<V> it = iterator()) { while (it.hasNext()) if (o.equals(it.next())) return true; return false; } } @Override public boolean containsAll(Collection<?> c) { // The AbstractCollection implementation calls contains for each element. Instead we want to call the iterator // only once so we have a special implementation. if (c.size() > 0) { Set<?> set = new HashSet<>(c); try (CloseableIterator<V> it = iterator()) { while (!set.isEmpty() && it.hasNext()) { set.remove(it.next()); } } return set.isEmpty(); } return true; } @Override public boolean remove(Object o) { try (CloseableIterator<V> it = iterator()) { while (it.hasNext()) { V next = it.next(); if (o.equals(next)) { it.remove(); return true; } } return false; } } @Override public CacheStream<V> stream() { Stream<CacheEntry<K, V>> stream = cacheSet.stream(); return (CacheStream<V>) stream.map(StreamMarshalling.entryToValueFunction()); } @Override public CacheStream<V> parallelStream() { Stream<CacheEntry<K, V>> stream = cacheSet.parallelStream(); return (CacheStream<V>) stream.map(StreamMarshalling.entryToValueFunction()); } }