package org.infinispan.interceptors.compat;
import java.util.Iterator;
import java.util.Spliterator;
import org.infinispan.CacheStream;
import org.infinispan.commons.util.IteratorMapper;
import org.infinispan.compat.TypeConverter;
import org.infinispan.container.InternalEntryFactory;
import org.infinispan.container.entries.CacheEntry;
import org.infinispan.stream.impl.spliterators.IteratorAsSpliterator;
import org.infinispan.util.AbstractDelegatingCacheStream;
/**
* Delegating stream that converts elements or CacheEntries. Note this stream specifically doesn't specify generics as
* it can have intermediate operations performed upon it that will change the type without changing the instance. Also
* depending on if CacheEntry values are returned we have to unbox those properly.
*/
public class TypeConverterStream extends AbstractDelegatingCacheStream {
private final TypeConverter<Object, Object, Object, Object> converter;
private final InternalEntryFactory entryFactory;
public TypeConverterStream(CacheStream<?> stream, TypeConverter<Object, Object, Object, Object> converter,
InternalEntryFactory entryFactory) {
super(stream);
this.converter = converter;
this.entryFactory = entryFactory;
}
@Override
public Iterator<Object> iterator() {
// Note that a transformation could have been applied intermediately which means this
// may not be a cache entry instance at all - so we have to define it as Object first
return new IteratorMapper<>(super.iterator(), (java.lang.Object e) -> {
if (e instanceof CacheEntry) {
return convert((CacheEntry<?, ?>) e, converter, entryFactory);
} else {
// If it isn't a CacheEntry just unbox as is
return converter.unboxValue(e);
}
});
}
@Override
public Spliterator<Object> spliterator() {
// We rely on our iterator to do unwrapping.
return new IteratorAsSpliterator.Builder<>(iterator())
.setEstimateRemaining(super.spliterator().estimateSize())
.setCharacteristics(Spliterator.CONCURRENT | Spliterator.DISTINCT | Spliterator.NONNULL)
.get();
}
private static <K, V> CacheEntry<K, V> convert(CacheEntry<K, V> entry,
TypeConverter<Object, Object, Object, Object> converter, InternalEntryFactory entryFactory) {
K newKey = (K) converter.unboxKey(entry.getKey());
V newValue = (V) converter.unboxValue(entry.getValue());
// If either value changed then make a copy
if (newKey != entry.getKey() || newValue != entry.getValue()) {
return entryFactory.create(newKey, newValue, entry.getMetadata());
}
return entry;
}
}