/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later, * or the Apache License Version 2.0. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.scopedpool; import java.lang.ref.ReferenceQueue; import java.lang.ref.SoftReference; import java.util.AbstractMap; import java.util.HashMap; import java.util.Map; import java.util.Set; /** * This Map will remove entries when the value in the map has been cleaned from * garbage collection * * @version <tt>$Revision: 1.4 $</tt> * @author <a href="mailto:bill@jboss.org">Bill Burke</a> */ public class SoftValueHashMap extends AbstractMap implements Map { private static class SoftValueRef extends SoftReference { public Object key; private SoftValueRef(Object key, Object val, ReferenceQueue q) { super(val, q); this.key = key; } private static SoftValueRef create(Object key, Object val, ReferenceQueue q) { if (val == null) return null; else return new SoftValueRef(key, val, q); } } /** * Returns a set of the mappings contained in this hash table. */ public Set entrySet() { processQueue(); return hash.entrySet(); } /* Hash table mapping WeakKeys to values */ private Map hash; /* Reference queue for cleared WeakKeys */ private ReferenceQueue queue = new ReferenceQueue(); /* * Remove all invalidated entries from the map, that is, remove all entries * whose values have been discarded. */ private void processQueue() { SoftValueRef ref; while ((ref = (SoftValueRef)queue.poll()) != null) { if (ref == (SoftValueRef)hash.get(ref.key)) { // only remove if it is the *exact* same WeakValueRef // hash.remove(ref.key); } } } /* -- Constructors -- */ /** * Constructs a new, empty <code>WeakHashMap</code> with the given initial * capacity and the given load factor. * * @param initialCapacity * The initial capacity of the <code>WeakHashMap</code> * * @param loadFactor * The load factor of the <code>WeakHashMap</code> * * @throws IllegalArgumentException * If the initial capacity is less than zero, or if the load * factor is nonpositive */ public SoftValueHashMap(int initialCapacity, float loadFactor) { hash = new HashMap(initialCapacity, loadFactor); } /** * Constructs a new, empty <code>WeakHashMap</code> with the given initial * capacity and the default load factor, which is <code>0.75</code>. * * @param initialCapacity * The initial capacity of the <code>WeakHashMap</code> * * @throws IllegalArgumentException * If the initial capacity is less than zero */ public SoftValueHashMap(int initialCapacity) { hash = new HashMap(initialCapacity); } /** * Constructs a new, empty <code>WeakHashMap</code> with the default * initial capacity and the default load factor, which is <code>0.75</code>. */ public SoftValueHashMap() { hash = new HashMap(); } /** * Constructs a new <code>WeakHashMap</code> with the same mappings as the * specified <tt>Map</tt>. The <code>WeakHashMap</code> is created with * an initial capacity of twice the number of mappings in the specified map * or 11 (whichever is greater), and a default load factor, which is * <tt>0.75</tt>. * * @param t the map whose mappings are to be placed in this map. */ public SoftValueHashMap(Map t) { this(Math.max(2 * t.size(), 11), 0.75f); putAll(t); } /* -- Simple queries -- */ /** * Returns the number of key-value mappings in this map. <strong>Note:</strong> * <em>In contrast with most implementations of the * <code>Map</code> interface, the time required by this operation is * linear in the size of the map.</em> */ public int size() { processQueue(); return hash.size(); } /** * Returns <code>true</code> if this map contains no key-value mappings. */ public boolean isEmpty() { processQueue(); return hash.isEmpty(); } /** * Returns <code>true</code> if this map contains a mapping for the * specified key. * * @param key * The key whose presence in this map is to be tested. */ public boolean containsKey(Object key) { processQueue(); return hash.containsKey(key); } /* -- Lookup and modification operations -- */ /** * Returns the value to which this map maps the specified <code>key</code>. * If this map does not contain a value for this key, then return * <code>null</code>. * * @param key * The key whose associated value, if any, is to be returned. */ public Object get(Object key) { processQueue(); SoftReference ref = (SoftReference)hash.get(key); if (ref != null) return ref.get(); return null; } /** * Updates this map so that the given <code>key</code> maps to the given * <code>value</code>. If the map previously contained a mapping for * <code>key</code> then that mapping is replaced and the previous value * is returned. * * @param key * The key that is to be mapped to the given <code>value</code> * @param value * The value to which the given <code>key</code> is to be * mapped * * @return The previous value to which this key was mapped, or * <code>null</code> if if there was no mapping for the key */ public Object put(Object key, Object value) { processQueue(); Object rtn = hash.put(key, SoftValueRef.create(key, value, queue)); if (rtn != null) rtn = ((SoftReference)rtn).get(); return rtn; } /** * Removes the mapping for the given <code>key</code> from this map, if * present. * * @param key * The key whose mapping is to be removed. * * @return The value to which this key was mapped, or <code>null</code> if * there was no mapping for the key. */ public Object remove(Object key) { processQueue(); return hash.remove(key); } /** * Removes all mappings from this map. */ public void clear() { processQueue(); hash.clear(); } }