/*
* Copyright (c) 2005-2016 Substance Kirill Grouchnikov. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* o Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* o Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* o Neither the name of Substance Kirill Grouchnikov nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.pushingpixels.substance.internal.utils;
import java.io.Serializable;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.util.AbstractMap;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
/**
* The original implementation is taken from <a
* href="http://www.javaspecialists.co.za/archive/Issue098.html">The Java
* Specialists' Newsletter [Issue 098]</a> with permission of the original
* author. Tweaked code by Endre Stolsvik was <a href=
* "https://substance.dev.java.net/servlets/ReadMsg?list=users&msgNo=1396"
* >contributed</a> in December 2009.
*
* @author Dr. Heinz M. Kabutz
* @author Endre Stolsvik
*/
class SoftHashMap<K, V> extends AbstractMap<K, V> implements Serializable {
/** The internal HashMap that will hold the SoftReference. */
private final Map<K, KeySoftReference<K, V>> hash = new HashMap<K, KeySoftReference<K, V>>();
/** Reference queue for cleared SoftReference objects. */
private final ReferenceQueue<V> queue = new ReferenceQueue<V>();
public static class KeySoftReference<K, V> extends SoftReference<V> {
final K key;
public KeySoftReference(K key, V referent, ReferenceQueue<V> q) {
super(referent, q);
this.key = key;
}
}
@Override
public V get(Object key) {
expungeStaleEntries();
V result = null;
// We get the SoftReference represented by that key
KeySoftReference<K, V> soft_ref = hash.get(key);
if (soft_ref != null) {
// From the SoftReference we get the value, which can be
// null if it has been garbage collected
result = soft_ref.get();
if (result == null) {
// If the value has been garbage collected, remove the
// entry from the HashMap.
hash.remove(key);
}
}
return result;
}
@SuppressWarnings("unchecked")
private void expungeStaleEntries() {
Reference<? extends V> ref;
while ((ref = queue.poll()) != null) {
KeySoftReference keyRef = (KeySoftReference<K, V>) ref;
hash.remove(keyRef.key);
}
}
@Override
public V put(K key, V value) {
expungeStaleEntries();
KeySoftReference<K, V> keyRef = new KeySoftReference<K, V>(key, value,
queue);
SoftReference<V> result = hash.put(key, keyRef);
if (result == null)
return null;
return result.get();
}
@Override
public V remove(Object key) {
expungeStaleEntries();
SoftReference<V> result = hash.remove(key);
if (result == null)
return null;
return result.get();
}
@Override
public void clear() {
hash.clear();
}
@Override
public int size() {
expungeStaleEntries();
return hash.size();
}
@Override
public boolean containsKey(Object key) {
expungeStaleEntries();
SoftReference<V> keyRef = hash.get(key);
if (keyRef != null) {
// From the SoftReference we get the value, which can be
// null if it has been garbage collected
V result = keyRef.get();
if (result != null) {
return true;
}
// If the value has been garbage collected, remove the
// entry from the HashMap.
hash.remove(key);
}
return false;
}
/**
* Returns a copy of the key/values in the map at the point of calling.
* However, setValue still sets the value in the actual SoftHashMap.
*/
@Override
public Set<Entry<K, V>> entrySet() {
expungeStaleEntries();
Set<Entry<K, V>> result = new LinkedHashSet<Entry<K, V>>();
for (final Entry<K, KeySoftReference<K, V>> entry : hash.entrySet()) {
final V value = entry.getValue().get();
if (value != null) {
result.add(new Entry<K, V>() {
public K getKey() {
return entry.getKey();
}
public V getValue() {
return value;
}
public V setValue(V v) {
entry.setValue(new KeySoftReference<K, V>(entry
.getKey(), v, queue));
return value;
}
});
}
}
return result;
}
}