001: /*
002: * Copyright (c) 2005-2008 Substance Kirill Grouchnikov. All Rights Reserved.
003: *
004: * Redistribution and use in source and binary forms, with or without
005: * modification, are permitted provided that the following conditions are met:
006: *
007: * o Redistributions of source code must retain the above copyright notice,
008: * this list of conditions and the following disclaimer.
009: *
010: * o Redistributions in binary form must reproduce the above copyright notice,
011: * this list of conditions and the following disclaimer in the documentation
012: * and/or other materials provided with the distribution.
013: *
014: * o Neither the name of Substance Kirill Grouchnikov nor the names of
015: * its contributors may be used to endorse or promote products derived
016: * from this software without specific prior written permission.
017: *
018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
020: * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
021: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
022: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
023: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
024: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
025: * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
026: * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
027: * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
028: * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029: */
030: package org.jvnet.substance.utils;
031:
032: import java.lang.ref.*;
033: import java.util.*;
034: import java.io.Serializable;
035:
036: /**
037: * This implementation is taken from
038: * <a href="http://www.javaspecialists.co.za/archive/Issue098.html">The
039: * Java Specialists' Newsletter [Issue 098]</a> with permission of the original
040: * author.
041: *
042: * @author Dr. Heinz M. Kabutz
043: */
044: public class SoftHashMap<K, V> extends AbstractMap<K, V> implements
045: Serializable {
046: /** The internal HashMap that will hold the SoftReference. */
047: private final Map<K, SoftReference<V>> hash = new HashMap<K, SoftReference<V>>();
048:
049: private final Map<SoftReference<V>, K> reverseLookup = new HashMap<SoftReference<V>, K>();
050:
051: /** Reference queue for cleared SoftReference objects. */
052: private final ReferenceQueue<V> queue = new ReferenceQueue<V>();
053:
054: public V get(Object key) {
055: expungeStaleEntries();
056: V result = null;
057: // We get the SoftReference represented by that key
058: SoftReference<V> soft_ref = hash.get(key);
059: if (soft_ref != null) {
060: // From the SoftReference we get the value, which can be
061: // null if it has been garbage collected
062: result = soft_ref.get();
063: if (result == null) {
064: // If the value has been garbage collected, remove the
065: // entry from the HashMap.
066: hash.remove(key);
067: reverseLookup.remove(soft_ref);
068: }
069: }
070: return result;
071: }
072:
073: private void expungeStaleEntries() {
074: Reference<? extends V> sv;
075: while ((sv = queue.poll()) != null) {
076: hash.remove(reverseLookup.remove(sv.get()));
077: }
078: }
079:
080: public V put(K key, V value) {
081: expungeStaleEntries();
082: SoftReference<V> soft_ref = new SoftReference<V>(value, queue);
083: reverseLookup.put(soft_ref, key);
084: SoftReference<V> result = hash.put(key, soft_ref);
085: if (result == null)
086: return null;
087: return result.get();
088: }
089:
090: public V remove(Object key) {
091: expungeStaleEntries();
092: SoftReference<V> result = hash.remove(key);
093: if (result == null)
094: return null;
095: return result.get();
096: }
097:
098: public void clear() {
099: hash.clear();
100: reverseLookup.clear();
101: }
102:
103: public int size() {
104: expungeStaleEntries();
105: return hash.size();
106: }
107:
108: /**
109: * Returns a copy of the key/values in the map at the point of
110: * calling. However, setValue still sets the value in the
111: * actual SoftHashMap.
112: */
113: public Set<Entry<K, V>> entrySet() {
114: expungeStaleEntries();
115: Set<Entry<K, V>> result = new LinkedHashSet<Entry<K, V>>();
116: for (final Entry<K, SoftReference<V>> entry : hash.entrySet()) {
117: final V value = entry.getValue().get();
118: if (value != null) {
119: result.add(new Entry<K, V>() {
120: public K getKey() {
121: return entry.getKey();
122: }
123:
124: public V getValue() {
125: return value;
126: }
127:
128: public V setValue(V v) {
129: entry.setValue(new SoftReference<V>(v, queue));
130: return value;
131: }
132: });
133: }
134: }
135: return result;
136: }
137: }
|