package de.invesdwin.util.collections.loadingcache.guava.internal; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ExecutionException; import javax.annotation.concurrent.ThreadSafe; import com.google.common.base.Optional; import com.google.common.cache.CacheStats; import com.google.common.cache.LoadingCache; import com.google.common.collect.ImmutableMap; /** * This is a workaround to make googles ComputingMap work with null values. * * @see <a href="http://code.google.com/p/google-collections/issues/detail?id=166">Source</a> * */ @ThreadSafe public class OptionalValueWrapperLoadingCache<K, V> implements LoadingCache<K, V> { private final LoadingCache<K, Optional<V>> delegate; public OptionalValueWrapperLoadingCache(final LoadingCache<K, Optional<V>> delegate) { this.delegate = delegate; } @SuppressWarnings("unchecked") @Override public V getIfPresent(final Object key) { return maybeGet((K) key, delegate.getIfPresent(key)); } @Override public V get(final K key, final Callable<? extends V> valueLoader) throws ExecutionException { return maybeGet(key, delegate.get(key, new Callable<Optional<V>>() { @SuppressWarnings("unchecked") @Override public Optional<V> call() throws Exception { return (Optional<V>) Optional.fromNullable(valueLoader.call()); } })); } @Override public ImmutableMap<K, V> getAllPresent(final Iterable<?> keys) { final ImmutableMap<K, Optional<V>> allPresent = delegate.getAllPresent(keys); final Map<K, V> nonnullAllPresent = new HashMap<K, V>(); for (final Entry<K, Optional<V>> e : allPresent.entrySet()) { final V value = maybeGet(e.getKey(), e.getValue()); if (value != null) { nonnullAllPresent.put(e.getKey(), value); } } return ImmutableMap.copyOf(nonnullAllPresent); } @Override public void put(final K key, final V value) { delegate.put(key, Optional.fromNullable(value)); } @Override public void putAll(final Map<? extends K, ? extends V> m) { final Map<K, Optional<V>> newMap = new HashMap<K, Optional<V>>(); for (final Entry<? extends K, ? extends V> e : m.entrySet()) { final K key = e.getKey(); final V value = e.getValue(); newMap.put(key, Optional.fromNullable(value)); } delegate.putAll(newMap); } @Override public void invalidate(final Object key) { delegate.invalidate(key); } @Override public void invalidateAll(final Iterable<?> keys) { delegate.invalidateAll(keys); } @Override public void invalidateAll() { delegate.invalidateAll(); } @Override public long size() { return delegate.size(); } @Override public CacheStats stats() { return delegate.stats(); } @Override public void cleanUp() { delegate.cleanUp(); } @Override public V get(final K key) throws ExecutionException { return maybeGet(key, delegate.get(key)); } @Override public V getUnchecked(final K key) { return maybeGet(key, delegate.getUnchecked(key)); } @Override public ImmutableMap<K, V> getAll(final Iterable<? extends K> keys) throws ExecutionException { final ImmutableMap<K, Optional<V>> all = delegate.getAll(keys); final Map<K, V> nonnullAll = new HashMap<K, V>(); for (final Entry<K, Optional<V>> e : all.entrySet()) { final V value = maybeGet(e.getKey(), e.getValue()); if (value != null) { nonnullAll.put(e.getKey(), value); } } return ImmutableMap.copyOf(nonnullAll); } @Override public V apply(final K key) { return maybeGet(key, delegate.apply(key)); } @Override public void refresh(final K key) { delegate.refresh(key); } @Override public ConcurrentMap<K, V> asMap() { return new OptionalValueWrapperConcurrentMap<K, V>(delegate.asMap()); } protected final V maybeGet(final K key, final Optional<V> value) { if (value != null) { final V v = value.orNull(); if (!isPutAllowed(key, v)) { invalidate(key); } return v; } else { return (V) null; } } protected boolean isPutAllowed(final K key, final V value) { return true; } }