/* * 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.SimpleImmutableEntry; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; /** * This Map will remove entries when the value in the map has been cleaned from * garbage collection * * @version $Revision: 1.4 $ * @author Bill Burke */ public class SoftValueHashMap implements Map { private static class SoftValueRef extends SoftReference { public K key; private SoftValueRef(K key, V val, ReferenceQueue q) { super(val, q); this.key = key; } private static SoftValueRef create( K key, V 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. */ @Override public Set> entrySet() { processQueue(); Set> ret = new HashSet>(); for (Entry> e:hash.entrySet()) ret.add(new SimpleImmutableEntry ( e.getKey(), e.getValue().get())); return ret; } /* 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() { Object ref; if (!hash.isEmpty()) while ((ref = queue.poll()) != null) if (ref instanceof SoftValueRef) { @SuppressWarnings("rawtypes") SoftValueRef que =(SoftValueRef) ref; if (ref == hash.get(que.key)) // only remove if it is the *exact* same SoftValueRef hash.remove(que.key); } } /* -- Constructors -- */ /** * Constructs a new, empty WeakHashMap with the given initial * capacity and the given load factor. * * @param initialCapacity * The initial capacity of the WeakHashMap * * @param loadFactor * The load factor of the WeakHashMap * * @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 ConcurrentHashMap>(initialCapacity, loadFactor); } /** * Constructs a new, empty WeakHashMap with the given initial * capacity and the default load factor, which is 0.75. * * @param initialCapacity * The initial capacity of the WeakHashMap * * @throws IllegalArgumentException * If the initial capacity is less than zero */ public SoftValueHashMap(int initialCapacity) { hash = new ConcurrentHashMap>(initialCapacity); } /** * Constructs a new, empty WeakHashMap with the default * initial capacity and the default load factor, which is 0.75. */ public SoftValueHashMap() { hash = new ConcurrentHashMap>(); } /** * Constructs a new WeakHashMap with the same mappings as the * specified Map. The WeakHashMap 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 * 0.75. * * @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. Note: * In contrast with most implementations of the * Map interface, the time required by this operation is * linear in the size of the map. */ @Override public int size() { processQueue(); return hash.size(); } /** * Returns true if this map contains no key-value mappings. */ @Override public boolean isEmpty() { processQueue(); return hash.isEmpty(); } /** * Returns true if this map contains a mapping for the * specified key. * * @param key * The key whose presence in this map is to be tested. */ @Override public boolean containsKey(Object key) { processQueue(); return hash.containsKey(key); } /* -- Lookup and modification operations -- */ /** * Returns the value to which this map maps the specified key. * If this map does not contain a value for this key, then return * null. * * @param key * The key whose associated value, if any, is to be returned. */ @Override public V get(Object key) { processQueue(); return valueOrNull(hash.get(key)); } /** * Updates this map so that the given key maps to the given * value. If the map previously contained a mapping for * key then that mapping is replaced and the previous value * is returned. * * @param key * The key that is to be mapped to the given value * @param value * The value to which the given key is to be * mapped * * @return The previous value to which this key was mapped, or * null if if there was no mapping for the key */ @Override public V put(K key, V value) { processQueue(); return valueOrNull(hash.put(key, SoftValueRef.create(key, value, queue))); } /** * Removes the mapping for the given key 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 null if * there was no mapping for the key. */ @Override public V remove(Object key) { processQueue(); return valueOrNull(hash.remove(key)); } /** * Removes all mappings from this map. */ @Override public void clear() { processQueue(); hash.clear(); } /* * Check whether the supplied value exists. * @param Object the value to compare. * @return true if it was found or null. */ @Override public boolean containsValue(Object arg0) { processQueue(); if (null == arg0) return false; for (SoftValueRef e:hash.values()) if (null != e && arg0.equals(e.get())) return true; return false; } /* {@inheritDoc} */ @Override public Set keySet() { processQueue(); return hash.keySet(); } /* {@inheritDoc} */ @Override public void putAll(Map arg0) { processQueue(); for (K key:arg0.keySet()) put(key, arg0.get(key)); } /* {@inheritDoc} */ @Override public Collection values() { processQueue(); List ret = new ArrayList(); for (SoftValueRef e:hash.values()) ret.add(e.get()); return ret; } private V valueOrNull(SoftValueRef rtn) { if (null == rtn) return null; return rtn.get(); } }