/**
* Copyright (C) 2012 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.util.map;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.util.AbstractSet;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* Refinement of {@link HashMap2} that holds references.
*
* @param <K1> key 1 type
* @param <K2> key 2 type
* @param <V> value type
*/
/* package */abstract class ReferenceHashMap2<K1, K2, V> extends HashMap2<K1, K2, V> {
private final ReferenceQueue<V> _garbage = new ReferenceQueue<V>();
/* package */final class ReferenceMap implements ConcurrentMap<K2, V> {
private final Object _key1Reference;
private final ConcurrentMap<K2, Reference<? extends V>> _underlying;
private ReferenceMap(final K1 key1, final ConcurrentMap<K2, Reference<? extends V>> underlying) {
_key1Reference = createKey1Reference(key1);
_underlying = underlying;
}
@Override
public int size() {
return _underlying.size();
}
@Override
public boolean isEmpty() {
return _underlying.isEmpty();
}
@Override
public boolean containsKey(final Object key) {
throw new UnsupportedOperationException();
}
@Override
public boolean containsValue(final Object value) {
throw new UnsupportedOperationException();
}
@Override
public V get(final Object key) {
final Reference<? extends V> reference = _underlying.get(key);
if (reference != null) {
return reference.get();
}
return null;
}
@Override
public V put(final K2 key, final V value) {
final Reference<? extends V> reference = _underlying.put(key, createReference(this, key, value));
if (reference != null) {
return reference.get();
}
return null;
}
@Override
public V remove(final Object key) {
final Reference<? extends V> reference = _underlying.remove(key);
if (reference != null) {
return reference.get();
}
return null;
}
@Override
public void putAll(final Map<? extends K2, ? extends V> m) {
throw new UnsupportedOperationException();
}
@Override
public void clear() {
throw new UnsupportedOperationException();
}
@Override
public Set<K2> keySet() {
throw new UnsupportedOperationException();
}
@Override
public Collection<V> values() {
throw new UnsupportedOperationException();
}
@Override
public Set<Map.Entry<K2, V>> entrySet() {
return new AbstractSet<Map.Entry<K2, V>>() {
@Override
public Iterator<Map.Entry<K2, V>> iterator() {
final Iterator<Map.Entry<K2, Reference<? extends V>>> itr = _underlying.entrySet().iterator();
return new Iterator<Map.Entry<K2, V>>() {
@Override
public boolean hasNext() {
return itr.hasNext();
}
@Override
public Map.Entry<K2, V> next() {
final Map.Entry<K2, Reference<? extends V>> e = itr.next();
return new Map.Entry<K2, V>() {
@Override
public K2 getKey() {
return e.getKey();
}
@Override
public V getValue() {
return e.getValue().get();
}
@Override
public V setValue(final V value) {
throw new UnsupportedOperationException();
}
};
}
@Override
public void remove() {
itr.remove();
}
};
}
@Override
public int size() {
return _underlying.size();
}
};
}
@Override
public V putIfAbsent(final K2 key, final V value) {
final Reference<? extends V> newValue = createReference(this, key, value);
final Reference<? extends V> reference = _underlying.putIfAbsent(key, newValue);
if (reference != null) {
final V result = reference.get();
if (result == null) {
if (_underlying.replace(key, reference, newValue)) {
return null;
}
} else {
return result;
}
}
return null;
}
@Override
public boolean remove(final Object key, final Object value) {
return _underlying.remove(key, value);
}
@Override
public boolean replace(final K2 key, final V oldValue, final V newValue) {
throw new UnsupportedOperationException();
}
@Override
public V replace(final K2 key, final V value) {
throw new UnsupportedOperationException();
}
/* package */void housekeep(final K2 key, final Reference<? extends V> value) {
final K1 key1 = getKey1(_key1Reference);
if (key1 != null) {
synchronized (this) {
if (!remove(key, value) || !isEmpty()) {
return;
}
}
removeAllKey1NoHousekeep(key1);
}
}
}
public ReferenceHashMap2(final KeyStrategy key1Strategy) {
super(key1Strategy);
}
@Override
protected ConcurrentMap<K2, V> newSubMap(final K1 key1) {
return new ReferenceMap(key1, new ConcurrentHashMap<K2, Reference<? extends V>>());
}
@Override
protected void housekeep() {
Reference<? extends V> garbage = getGarbageQueue().poll();
while (garbage != null) {
housekeep(garbage);
garbage = getGarbageQueue().poll();
}
}
protected ReferenceQueue<V> getGarbageQueue() {
return _garbage;
}
protected abstract Reference<? extends V> createReference(ReferenceMap map, K2 key, V value);
protected abstract void housekeep(Reference<? extends V> ref);
}