package org.infinispan.distribution.util;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import org.infinispan.distribution.ch.ConsistentHash;
import org.infinispan.util.AbstractDelegatingCollection;
import org.infinispan.util.AbstractDelegatingMap;
/**
* Map implementation that shows a read only view of the provided entry by only allowing
* entries whose key maps to a given segment using the provided consistent hash.
* <p>
* Any operation that would modify this map will throw an {@link UnsupportedOperationException}
* <p>
* This map is useful when you don't want to copy an entire map but only need to see
* entries from the given segments.
* <p>
* Note many operations are not constant time when using this map. The
* {@link ReadOnlySegmentAwareMap#values} method is not supported as well. Please check\
* the method you are using to see if it will perform differently than normally expected.
* @author wburns
* @since 7.2
*/
public class ReadOnlySegmentAwareMap<K, V> extends AbstractDelegatingMap<K, V> {
protected final Map<K, V> map;
protected final ConsistentHash ch;
protected final Set<Integer> allowedSegments;
protected Set<K> segmentAwareKeySet;
protected Set<Map.Entry<K, V>> segmentAwareEntrySet;
public ReadOnlySegmentAwareMap(Map<K, V> map, ConsistentHash ch,
Set<Integer> allowedSegments) {
super();
this.map = Collections.unmodifiableMap(map);
this.ch = ch;
this.allowedSegments = allowedSegments;
}
@Override
protected Map<K, V> delegate() {
return map;
}
protected boolean keyAllowed(Object key) {
int segment = ch.getSegment(key);
return allowedSegments.contains(segment);
}
@Override
public boolean containsKey(Object key) {
if (keyAllowed(key)) {
return super.containsKey(key);
}
return false;
}
@Override
public boolean containsValue(Object value) {
for (Entry<K, V> entry : entrySet()) {
if (value.equals(entry.getValue())) {
return true;
}
}
return false;
}
@Override
public V get(Object key) {
if (keyAllowed(key)) {
return super.get(key);
}
return null;
}
@Override
public Set<java.util.Map.Entry<K, V>> entrySet() {
if (segmentAwareEntrySet == null) {
segmentAwareEntrySet = new CollectionAsSet<>(
new ReadOnlySegmentAwareEntryCollection<>(delegate().entrySet(), ch, allowedSegments));
}
return segmentAwareEntrySet;
}
/**
* Checks if the provided map is empty. This is done by iterating over all of the keys
* until it can find a key that maps to a given segment.
* <p>
* This method should always be preferred over checking the size to see if it is empty.
* <p>
* This time complexity for this method between O(1) to O(N).
*/
@Override
public boolean isEmpty() {
Set<K> keySet = keySet();
Iterator<K> iter = keySet.iterator();
return !iter.hasNext();
}
@Override
public Set<K> keySet() {
if (segmentAwareKeySet == null) {
segmentAwareKeySet = new CollectionAsSet<K>(new ReadOnlySegmentAwareCollection<>(super.keySet(), ch, allowedSegments));
}
return segmentAwareKeySet;
}
/**
* Returns the size of the read only map. This is done by iterating over all of the
* keys counting all that are in the segments.
* <p>
* If you are using this method to verify if the map is empty, you should instead use
* the {@link ReadOnlySegmentAwareEntryMap#isEmpty()} as it will perform better if the
* size is only used for this purpose.
* <p>
* This time complexity for this method is always O(N).
*/
@Override
public int size() {
Set<K> keySet = keySet();
Iterator<K> iter = keySet.iterator();
int count = 0;
while (iter.hasNext()) {
iter.next();
count++;
}
return count;
}
/**
* NOTE: this method is not supported. Due to the nature of this map, we don't want
* to copy the underlying value collection. Thus almost any operation will require
* O(N) and therefore this method is not provided.
*/
@Override
public Collection<V> values() {
throw new UnsupportedOperationException();
}
@Override
public String toString() {
return "ReadOnlySegmentAwareMap [map=" + map + ", ch=" + ch +
", allowedSegments=" + allowedSegments + "]";
}
private static class CollectionAsSet<T> extends AbstractDelegatingCollection<T> implements Set<T> {
private final AbstractDelegatingCollection<T> delegate;
public CollectionAsSet(AbstractDelegatingCollection<T> delegate) {
this.delegate = delegate;
}
@Override
protected Collection<T> delegate() {
return delegate;
}
}
}