package com.revolsys.collection.map;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.AbstractMap;
import java.util.AbstractSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
final class WeakValueMap<K, V> extends AbstractMap<K, V> {
private final class Entry extends SimpleEntry<K, V> {
private static final long serialVersionUID = 1L;
Entry(final K key, final V value) {
super(key, value);
}
@Override
public V setValue(final V value) {
put(getKey(), value);
return super.setValue(value);
}
}
private final class EntrySet extends AbstractSet<Map.Entry<K, V>> {
final class EntryIterator implements Iterator<Map.Entry<K, V>> {
final Iterator<Map.Entry<K, V>> setIterator;
Map.Entry<K, V> currentEntry = null;
EntryIterator(final Iterator<Map.Entry<K, V>> setIterator) {
this.setIterator = setIterator;
}
@Override
public boolean hasNext() {
return this.setIterator.hasNext();
}
@Override
public Map.Entry<K, V> next() {
this.currentEntry = this.setIterator.next();
return this.currentEntry;
}
@Override
public void remove() {
this.setIterator.remove();
EntrySet.this.remove(this.currentEntry);
}
}
@Override
public boolean contains(final Object o) {
if (o instanceof Map.Entry<?, ?>) {
final Map.Entry<?, ?> entry = (Map.Entry<?, ?>)o;
return entry.getValue().equals(get(entry.getKey()));
} else {
return false;
}
}
@Override
public Iterator<Map.Entry<K, V>> iterator() {
final Set<Map.Entry<K, V>> iterationSet = new HashSet<>();
processQueue();
for (final Map.Entry<K, ValueReference<K, V>> i : WeakValueMap.this.backingMap.entrySet()) {
final K key = i.getKey();
final V value = i.getValue().get();
if (value != null) {
iterationSet.add(new Entry(key, value));
}
}
final Iterator<Map.Entry<K, V>> setIterator = iterationSet.iterator();
return new EntryIterator(setIterator);
}
@Override
public boolean remove(final Object o) {
if (o instanceof Map.Entry<?, ?>) {
final Map.Entry<?, ?> entry = (Map.Entry<?, ?>)o;
return WeakValueMap.this.remove(entry.getKey(), entry.getValue());
} else {
return false;
}
}
@Override
public int size() {
return WeakValueMap.this.size();
}
}
private static final class ValueReference<K, V> extends WeakReference<V> {
final K key;
ValueReference(final ReferenceQueue<V> referenceQueue, final K key, final V value) {
super(value, referenceQueue);
this.key = key;
}
}
private final Map<K, ValueReference<K, V>> backingMap = new HashMap<>();
private final ReferenceQueue<V> referenceQueue = new ReferenceQueue<>();
private final Set<Map.Entry<K, V>> entrySet = new EntrySet();
@Override
public Set<Map.Entry<K, V>> entrySet() {
return this.entrySet;
}
@Override
public V get(final Object key) {
Objects.requireNonNull(key);
processQueue();
return resolveReference(this.backingMap.get(key));
}
private void processQueue() {
while (true) {
final ValueReference<?, ?> reference = (ValueReference<?, ?>)this.referenceQueue.poll();
if (reference == null) {
break;
}
this.backingMap.remove(reference.key);
}
}
@Override
public V put(final K key, final V value) {
Objects.requireNonNull(key);
Objects.requireNonNull(value);
processQueue();
return resolveReference(
this.backingMap.put(key, new ValueReference<>(this.referenceQueue, key, value)));
}
@Override
public V remove(final Object key) {
Objects.requireNonNull(key);
processQueue();
return resolveReference(this.backingMap.remove(key));
}
private V resolveReference(final Reference<V> reference) {
if (reference == null) {
return null;
} else {
return reference.get();
}
}
@Override
public int size() {
processQueue();
return this.backingMap.size();
}
}