package com.googlecode.totallylazy.collections;
import com.googlecode.totallylazy.Atomic;
import com.googlecode.totallylazy.Maps;
import com.googlecode.totallylazy.Option;
import com.googlecode.totallylazy.Pair;
import com.googlecode.totallylazy.Segment;
import com.googlecode.totallylazy.Unchecked;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentMap;
import static com.googlecode.totallylazy.Atomic.constructors.atomic;
import static com.googlecode.totallylazy.Pair.pair;
import static com.googlecode.totallylazy.Sequences.sequence;
import static com.googlecode.totallylazy.Unchecked.cast;
public class AtomicMap<K, V> implements ConcurrentMap<K, V> {
private final Atomic<PersistentMap<K, V>> atomic;
private AtomicMap(Atomic<PersistentMap<K, V>> atomic) {
this.atomic = atomic;
}
public static <K, V> AtomicMap<K, V> atomicMap(PersistentMap<K, V> map) {return new AtomicMap<K, V>(atomic(map));}
private PersistentMap<K, V> map() {return atomic.value();}
private K key(Object key) {return cast(key);}
@Override
public int size() {
return map().size();
}
@Override
public boolean isEmpty() {
return map().isEmpty();
}
@Override
public boolean containsKey(Object key) {
return map().contains(key(key));
}
@Override
public boolean containsValue(Object value) {
return values().contains(value);
}
@Override
public V get(Object key) {
return map().lookup(key(key)).getOrNull();
}
@Override
public V put(final K key, final V value) {
return atomic.modifyReturn(map -> AtomicMap.this.put(map, key, value));
}
private Pair<PersistentMap<K, V>, V> put(PersistentMap<K, V> map, K key, V value) {
return PersistentMap.methods.put(map, key, value).
second(Option.functions.<V>getOrNull());
}
@Override
public void putAll(final Map<? extends K, ? extends V> m) {
atomic.modify(map -> Maps.pairs(m).<Pair<K, V>>unsafeCast().fold(map, Segment.functions.<Pair<K, V>, PersistentMap<K, V>>cons()));
}
@Override
public V remove(final Object key) {
return atomic.modifyReturn(map -> PersistentMap.methods.remove(map, key(key)).
second(Option.functions.<V>getOrNull()));
}
@Override
public void clear() {
atomic.modify(PersistentMap<K, V>::empty);
}
@Override
public Set<K> keySet() {
return map().keys().toSet();
}
@Override
public Collection<V> values() {
return map().values().toList();
}
@Override
public Set<Entry<K, V>> entrySet() {
return Maps.entrySet(map());
}
@Override
public V putIfAbsent(final K key, final V value) {
return atomic.modifyReturn(map -> {
if (!map.contains(key)) return put(map, key, value);
return pair(map, map.lookup(key).getOrNull());
});
}
@Override
public boolean remove(final Object rawKey, final Object value) {
return atomic.modifyReturn(map -> {
K key = key(rawKey);
if (map.lookup(key).contains(Unchecked.<V>cast(value))) return pair(map.delete(key), true);
return pair(map, false);
});
}
@Override
public boolean replace(final K rawKey, final V oldValue, final V newValue) {
return atomic.modifyReturn(map -> {
K key = key(rawKey);
if (map.lookup(key).contains(Unchecked.<V>cast(oldValue))) return pair(map.insert(key, newValue), true);
return pair(map, false);
});
}
@Override
public V replace(final K key, final V value) {
return atomic.modifyReturn(map -> {
if (map.contains(key)) return AtomicMap.this.put(map, key, value);
return pair(map, null);
});
}
}