1/*
2 * Javassist, a Java-bytecode translator toolkit.
3 * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
4 *
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License.  Alternatively, the contents of this file may be used under
8 * the terms of the GNU Lesser General Public License Version 2.1 or later.
9 *
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
14 */
15
16package javassist.scopedpool;
17
18import java.lang.ref.ReferenceQueue;
19import java.lang.ref.SoftReference;
20import java.util.AbstractMap;
21import java.util.HashMap;
22import java.util.Map;
23import java.util.Set;
24
25/**
26 * This Map will remove entries when the value in the map has been cleaned from
27 * garbage collection
28 *
29 * @version <tt>$Revision: 1.4 $</tt>
30 * @author <a href="mailto:bill@jboss.org">Bill Burke</a>
31 */
32public class SoftValueHashMap extends AbstractMap implements Map {
33    private static class SoftValueRef extends SoftReference {
34        public Object key;
35
36        private SoftValueRef(Object key, Object val, ReferenceQueue q) {
37            super(val, q);
38            this.key = key;
39        }
40
41        private static SoftValueRef create(Object key, Object val,
42                ReferenceQueue q) {
43            if (val == null)
44                return null;
45            else
46                return new SoftValueRef(key, val, q);
47        }
48
49    }
50
51    /**
52     * Returns a set of the mappings contained in this hash table.
53     */
54    public Set entrySet() {
55        processQueue();
56        return hash.entrySet();
57    }
58
59    /* Hash table mapping WeakKeys to values */
60    private Map hash;
61
62    /* Reference queue for cleared WeakKeys */
63    private ReferenceQueue queue = new ReferenceQueue();
64
65    /*
66     * Remove all invalidated entries from the map, that is, remove all entries
67     * whose values have been discarded.
68     */
69    private void processQueue() {
70        SoftValueRef ref;
71        while ((ref = (SoftValueRef)queue.poll()) != null) {
72            if (ref == (SoftValueRef)hash.get(ref.key)) {
73                // only remove if it is the *exact* same WeakValueRef
74                //
75                hash.remove(ref.key);
76            }
77        }
78    }
79
80    /* -- Constructors -- */
81
82    /**
83     * Constructs a new, empty <code>WeakHashMap</code> with the given initial
84     * capacity and the given load factor.
85     *
86     * @param initialCapacity
87     *            The initial capacity of the <code>WeakHashMap</code>
88     *
89     * @param loadFactor
90     *            The load factor of the <code>WeakHashMap</code>
91     *
92     * @throws IllegalArgumentException
93     *             If the initial capacity is less than zero, or if the load
94     *             factor is nonpositive
95     */
96    public SoftValueHashMap(int initialCapacity, float loadFactor) {
97        hash = new HashMap(initialCapacity, loadFactor);
98    }
99
100    /**
101     * Constructs a new, empty <code>WeakHashMap</code> with the given initial
102     * capacity and the default load factor, which is <code>0.75</code>.
103     *
104     * @param initialCapacity
105     *            The initial capacity of the <code>WeakHashMap</code>
106     *
107     * @throws IllegalArgumentException
108     *             If the initial capacity is less than zero
109     */
110    public SoftValueHashMap(int initialCapacity) {
111        hash = new HashMap(initialCapacity);
112    }
113
114    /**
115     * Constructs a new, empty <code>WeakHashMap</code> with the default
116     * initial capacity and the default load factor, which is <code>0.75</code>.
117     */
118    public SoftValueHashMap() {
119        hash = new HashMap();
120    }
121
122    /**
123     * Constructs a new <code>WeakHashMap</code> with the same mappings as the
124     * specified <tt>Map</tt>. The <code>WeakHashMap</code> is created with
125     * an initial capacity of twice the number of mappings in the specified map
126     * or 11 (whichever is greater), and a default load factor, which is
127     * <tt>0.75</tt>.
128     *
129     * @param t     the map whose mappings are to be placed in this map.
130     */
131    public SoftValueHashMap(Map t) {
132        this(Math.max(2 * t.size(), 11), 0.75f);
133        putAll(t);
134    }
135
136    /* -- Simple queries -- */
137
138    /**
139     * Returns the number of key-value mappings in this map. <strong>Note:</strong>
140     * <em>In contrast with most implementations of the
141     * <code>Map</code> interface, the time required by this operation is
142     * linear in the size of the map.</em>
143     */
144    public int size() {
145        processQueue();
146        return hash.size();
147    }
148
149    /**
150     * Returns <code>true</code> if this map contains no key-value mappings.
151     */
152    public boolean isEmpty() {
153        processQueue();
154        return hash.isEmpty();
155    }
156
157    /**
158     * Returns <code>true</code> if this map contains a mapping for the
159     * specified key.
160     *
161     * @param key
162     *            The key whose presence in this map is to be tested.
163     */
164    public boolean containsKey(Object key) {
165        processQueue();
166        return hash.containsKey(key);
167    }
168
169    /* -- Lookup and modification operations -- */
170
171    /**
172     * Returns the value to which this map maps the specified <code>key</code>.
173     * If this map does not contain a value for this key, then return
174     * <code>null</code>.
175     *
176     * @param key
177     *            The key whose associated value, if any, is to be returned.
178     */
179    public Object get(Object key) {
180        processQueue();
181        SoftReference ref = (SoftReference)hash.get(key);
182        if (ref != null)
183            return ref.get();
184        return null;
185    }
186
187    /**
188     * Updates this map so that the given <code>key</code> maps to the given
189     * <code>value</code>. If the map previously contained a mapping for
190     * <code>key</code> then that mapping is replaced and the previous value
191     * is returned.
192     *
193     * @param key
194     *            The key that is to be mapped to the given <code>value</code>
195     * @param value
196     *            The value to which the given <code>key</code> is to be
197     *            mapped
198     *
199     * @return The previous value to which this key was mapped, or
200     *         <code>null</code> if if there was no mapping for the key
201     */
202    public Object put(Object key, Object value) {
203        processQueue();
204        Object rtn = hash.put(key, SoftValueRef.create(key, value, queue));
205        if (rtn != null)
206            rtn = ((SoftReference)rtn).get();
207        return rtn;
208    }
209
210    /**
211     * Removes the mapping for the given <code>key</code> from this map, if
212     * present.
213     *
214     * @param key
215     *            The key whose mapping is to be removed.
216     *
217     * @return The value to which this key was mapped, or <code>null</code> if
218     *         there was no mapping for the key.
219     */
220    public Object remove(Object key) {
221        processQueue();
222        return hash.remove(key);
223    }
224
225    /**
226     * Removes all mappings from this map.
227     */
228    public void clear() {
229        processQueue();
230        hash.clear();
231    }
232}
233