/**
* 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.WeakReference;
import java.util.Collection;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import com.google.common.collect.MapMaker;
/**
* Implementation of {@link Map2} backed by a standard {@link ConcurrentHashMap}.
*
* @param <K1> key 1 type
* @param <K2> key 2 type
* @param <V> value type
*/
public class HashMap2<K1, K2, V> implements Map2<K1, K2, V> {
/**
* Strategy pattern for dealing with the K1 key.
*/
public abstract static class KeyStrategy {
protected abstract <K1, K2, V> ConcurrentMap<K1, ConcurrentMap<K2, V>> createBaseMap();
protected abstract <K> K getKey(final Object reference);
protected abstract <K> Object createReference(final K key);
}
/**
* Use to hold key1 with strong references.
*/
public static final KeyStrategy STRONG_KEYS = new KeyStrategy() {
@Override
protected <K1, K2, V> ConcurrentMap<K1, ConcurrentMap<K2, V>> createBaseMap() {
return new ConcurrentHashMap<K1, ConcurrentMap<K2, V>>();
}
@Override
@SuppressWarnings("unchecked")
protected <K> K getKey(final Object reference) {
return (K) reference;
}
@Override
protected <K> Object createReference(final K key) {
return key;
}
};
/**
* Use to hold key1 with weak references.
*/
public static final KeyStrategy WEAK_KEYS = new KeyStrategy() {
@Override
protected <K1, K2, V> ConcurrentMap<K1, ConcurrentMap<K2, V>> createBaseMap() {
return new MapMaker().weakKeys().makeMap();
}
@SuppressWarnings("unchecked")
@Override
protected <K> K getKey(final Object reference) {
return ((Reference<K>) reference).get();
}
@Override
protected <K> Object createReference(final K key) {
return new WeakReference<K>(key);
}
};
private final KeyStrategy _key1Strategy;
private final ConcurrentMap<K1, ConcurrentMap<K2, V>> _values;
public HashMap2(final KeyStrategy key1Strategy) {
_key1Strategy = key1Strategy;
_values = key1Strategy.createBaseMap();
}
protected ConcurrentMap<K2, V> newSubMap(final K1 key1) {
return new MapMaker().makeMap();
}
protected Object createKey1Reference(final K1 key1) {
return _key1Strategy.createReference(key1);
}
protected K1 getKey1(final Object opaqueReference) {
return _key1Strategy.getKey(opaqueReference);
}
protected ConcurrentMap<K2, V> createSubMap(final K1 key1, final K2 key2, final V value) {
final ConcurrentMap<K2, V> map = newSubMap(key1);
map.put(key2, value);
return map;
}
protected void housekeep() {
}
// Map2
@Override
public int size() {
int size = 0;
for (final ConcurrentMap<K2, V> map : _values.values()) {
size += map.size();
}
return size;
}
@Override
public boolean isEmpty() {
return _values.isEmpty();
}
@Override
public V get(final K1 key1, final K2 key2) {
final ConcurrentMap<K2, V> values = _values.get(key1);
if (values != null) {
return values.get(key2);
} else {
return null;
}
}
@Override
public V put(final K1 key1, final K2 key2, final V value) {
V result;
do {
ConcurrentMap<K2, V> values = _values.get(key1);
if (values == null) {
values = createSubMap(key1, key2, value);
final ConcurrentMap<K2, V> existing = _values.putIfAbsent(key1, values);
if (existing == null) {
result = null;
break;
}
values = existing;
}
synchronized (values) {
if (!values.isEmpty()) {
result = values.put(key2, value);
break;
}
}
} while (true);
housekeep();
return result;
}
@Override
public boolean containsKey(final K1 key1, final K2 key2) {
final ConcurrentMap<K2, V> values = _values.get(key1);
if (values != null) {
return values.containsKey(key2);
} else {
return false;
}
}
@Override
public V remove(final K1 key1, final K2 key2) {
housekeep();
do {
final ConcurrentMap<K2, V> values = _values.get(key1);
if (values == null) {
return null;
}
synchronized (values) {
if (!values.isEmpty()) {
final V result = values.remove(key2);
if (values.isEmpty()) {
_values.remove(key1, values);
}
return result;
}
}
} while (true);
}
protected void removeAllKey1NoHousekeep(final K1 key1) {
_values.remove(key1);
}
@Override
public void removeAllKey1(final K1 key1) {
removeAllKey1NoHousekeep(key1);
housekeep();
}
@Override
public void retainAllKey1(final Collection<K1> key1) {
_values.keySet().retainAll(key1);
housekeep();
}
@Override
public V putIfAbsent(final K1 key1, final K2 key2, final V value) {
V result;
do {
ConcurrentMap<K2, V> values = _values.get(key1);
if (values == null) {
values = createSubMap(key1, key2, value);
final ConcurrentMap<K2, V> existing = _values.putIfAbsent(key1, values);
if (existing == null) {
result = null;
break;
}
values = existing;
}
synchronized (values) {
if (!values.isEmpty()) {
result = values.putIfAbsent(key2, value);
break;
}
}
} while (true);
housekeep();
return result;
}
@Override
public void clear() {
_values.clear();
housekeep();
}
}