001: /**********************************************************************
002: Copyright (c) 2002 Mike Martin (TJDO) and others. All rights reserved.
003: Licensed under the Apache License, Version 2.0 (the "License");
004: you may not use this file except in compliance with the License.
005: You may obtain a copy of the License at
006:
007: http://www.apache.org/licenses/LICENSE-2.0
008:
009: Unless required by applicable law or agreed to in writing, software
010: distributed under the License is distributed on an "AS IS" BASIS,
011: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012: See the License for the specific language governing permissions and
013: limitations under the License.
014:
015:
016: Contributors:
017: 2002 Kelly Grizzle (TJDO)
018: 2003 Andy Jefferson - commented
019: ...
020: **********************************************************************/package org.jpox.util;
021:
022: import java.lang.ref.Reference;
023: import java.lang.ref.ReferenceQueue;
024: import java.util.ArrayList;
025: import java.util.Collection;
026: import java.util.Collections;
027: import java.util.HashMap;
028: import java.util.Iterator;
029: import java.util.Map;
030: import java.util.Set;
031:
032: /**
033: * A <code>java.util.Map</code> implementation using reference values.
034: *
035: * <p>The values are stored in the map as references. If the garbage collector
036: * clears the reference, the corresponding key is automatically removed from the
037: * map.
038: *
039: * @see java.lang.ref.Reference
040: * @version $Revision: 1.3 $
041: */
042: public abstract class ReferenceValueMap implements Map, Cloneable {
043: private HashMap map;
044: private ReferenceQueue reaped = new ReferenceQueue();
045:
046: /**
047: * Default Constructor.
048: **/
049: public ReferenceValueMap() {
050: map = new HashMap();
051: }
052:
053: /**
054: * Constructor taking initial capacity.
055: * @param initial_capacity Initial Capacity of HashMap
056: **/
057: public ReferenceValueMap(int initial_capacity) {
058: map = new HashMap(initial_capacity);
059: }
060:
061: /**
062: * Constructor taking initial capacity and load factor.
063: * @param initial_capacity Initial Capacity of HashMap
064: * @param load_factor Load Factor of HashMap
065: **/
066: public ReferenceValueMap(int initial_capacity, float load_factor) {
067: map = new HashMap(initial_capacity, load_factor);
068: }
069:
070: /**
071: * Constructor taking initial Map.
072: * @param m Map to initial with.
073: **/
074: public ReferenceValueMap(Map m) {
075: map = new HashMap();
076: putAll(m);
077: }
078:
079: /**
080: * Clone method.
081: * @return Clone of this object.
082: **/
083: public Object clone() {
084: reap();
085:
086: ReferenceValueMap rvm = null;
087:
088: try {
089: rvm = (ReferenceValueMap) super .clone();
090: } catch (CloneNotSupportedException e) {
091: // Do nothing
092: }
093:
094: rvm.map = (HashMap) map.clone(); // to preserve initialCapacity, loadFactor
095: rvm.map.clear();
096: rvm.reaped = new ReferenceQueue();
097: rvm.putAll(entrySet());
098:
099: return rvm;
100: }
101:
102: /**
103: * References returned by <code>newValueReference</code> must implement
104: * this interface to provide the corresponding map key for the value.
105: */
106: public interface ValueReference {
107: /**
108: * Returns the key associated with the value referenced by this
109: * <code>Reference</code> object.
110: * @return The Key
111: */
112: Object getKey();
113: }
114:
115: /**
116: * Returns a new <code>Reference</code> object to be inserted into the map.
117: * Subclasses must implement this method to construct <code>Reference</code>
118: * objects of the desired type (e.g. <code>SoftReference</code>, etc.).
119: *
120: * @param key The key that will be inserted.
121: * @param value The associated value to be referenced.
122: * @param queue The <code>ReferenceQueue</code> with which to register the
123: * new <code>Reference</code> object.
124: * @return The new ValueReference
125: */
126: protected abstract ValueReference newValueReference(Object key,
127: Object value, ReferenceQueue queue);
128:
129: /**
130: * Method to add an object to the Map.
131: * @param key Key for object
132: * @param value Value of object
133: * @return The Object.
134: **/
135: public Object put(Object key, Object value) {
136: reap();
137: return map.put(key, newValueReference(key, value, reaped));
138: }
139:
140: /**
141: * Method to add the contents of a Map.
142: * @param m Map
143: **/
144: public void putAll(Map m) {
145: putAll(m.entrySet());
146: }
147:
148: /**
149: * Method to add the contents of a Set.
150: * @param entrySet The Set
151: **/
152: private void putAll(Set entrySet) {
153: Iterator i = entrySet.iterator();
154:
155: while (i.hasNext()) {
156: Map.Entry entry = (Map.Entry) i.next();
157: put(entry.getKey(), entry.getValue());
158: }
159: }
160:
161: /**
162: * Method to get a value for a key.
163: * @param key The Key
164: * @return The Value
165: **/
166: public Object get(Object key) {
167: reap();
168: Reference ref = (Reference) map.get(key);
169:
170: Object value = ref == null ? null : ref.get();
171:
172: return value;
173: }
174:
175: /**
176: * Method to empty the HashMap.
177: **/
178: public void clear() {
179: reap();
180: map.clear();
181: }
182:
183: /**
184: * Accessor for the size of the HashMap.
185: * @return The size
186: **/
187: public int size() {
188: reap();
189: return map.size();
190: }
191:
192: /**
193: * Accessor for whether the Map contains the specified Key
194: * @param obj The key
195: * @return Whether the key exists
196: **/
197: public boolean containsKey(Object obj) {
198: reap();
199: return map.containsKey(obj);
200: }
201:
202: /**
203: * Accessor for whether the Map contains the specified value.
204: * @param obj The value
205: * @return Whether the Map contains the value.
206: **/
207: public boolean containsValue(Object obj) {
208: reap();
209:
210: if (obj != null) {
211: Iterator i = map.values().iterator();
212:
213: while (i.hasNext()) {
214: Reference ref = (Reference) i.next();
215:
216: if (obj.equals(ref.get())) {
217: return true;
218: }
219: }
220: }
221:
222: return false;
223: }
224:
225: /**
226: * Accessor for whether the Map is empty.
227: * @return Whether the Map is empty.
228: **/
229: public boolean isEmpty() {
230: reap();
231: return map.isEmpty();
232: }
233:
234: /**
235: * Accessor for the Set of keys in the Map.
236: * @return The Set of keys
237: **/
238: public Set keySet() {
239: reap();
240: return map.keySet();
241: }
242:
243: /**
244: * Accessor for the values from the Map.
245: * @return The Values.
246: **/
247: public Collection values() {
248: reap();
249:
250: Collection c = map.values();
251: Iterator i = c.iterator();
252: ArrayList l = new ArrayList(c.size());
253:
254: while (i.hasNext()) {
255: Reference ref = (Reference) i.next();
256: Object obj = ref.get();
257:
258: if (obj != null) {
259: l.add(obj);
260: }
261: }
262:
263: return Collections.unmodifiableList(l);
264: }
265:
266: /**
267: * Accessor for the entry set.
268: * @return The Set.
269: **/
270: public Set entrySet() {
271: reap();
272:
273: Set s = map.entrySet();
274: Iterator i = s.iterator();
275: HashMap m = new HashMap(s.size());
276:
277: while (i.hasNext()) {
278: Map.Entry entry = (Map.Entry) i.next();
279: Reference ref = (Reference) entry.getValue();
280: Object obj = ref.get();
281:
282: if (obj != null) {
283: m.put(entry.getKey(), obj);
284: }
285: }
286:
287: return Collections.unmodifiableSet(m.entrySet());
288: }
289:
290: /**
291: * Method to remove an object for the specified key.
292: * @param key The Key
293: * @return The Object removed
294: **/
295: public Object remove(Object key) {
296: reap();
297: return map.remove(key);
298: }
299:
300: /**
301: * Hashcode generator for this object.
302: * @return The Hashcode
303: **/
304: public int hashCode() {
305: reap();
306: return map.hashCode();
307: }
308:
309: /**
310: * Equality operator.
311: * @param o THe object to compare against.
312: * @return Whether it is equal.
313: **/
314: public boolean equals(Object o) {
315: reap();
316: return map.equals(o);
317: }
318:
319: /**
320: * Utility method to reap objects.
321: **/
322: public void reap() {
323: ValueReference ref;
324:
325: while ((ref = (ValueReference) reaped.poll()) != null) {
326: map.remove(ref.getKey());
327: }
328: }
329: }
|