001: /*
002: * Copyright 2002 (C) TJDO.
003: * All rights reserved.
004: *
005: * This software is distributed under the terms of the TJDO License version 1.0.
006: * See the terms of the TJDO License in the documentation provided with this software.
007: *
008: * $Id: ReferenceValueMap.java,v 1.4 2002/11/08 05:06:27 jackknifebarber Exp $
009: */
010:
011: package com.triactive.jdo.util;
012:
013: import java.lang.ref.Reference;
014: import java.lang.ref.ReferenceQueue;
015: import java.util.ArrayList;
016: import java.util.Iterator;
017: import java.util.Map;
018: import java.util.HashMap;
019: import java.util.Set;
020: import java.util.Collection;
021: import java.util.Collections;
022: import org.apache.log4j.Category;
023:
024: /**
025: * A <code>java.util.Map</code> implementation using reference values.
026: *
027: * <p>The values are stored in the map as references. If the garbage collector
028: * clears the reference, the corresponding key is automatically removed from the
029: * map.
030: *
031: * @author <a href="mailto:mmartin5@austin.rr.com">Mike Martin</a>
032: * @version $Revision: 1.4 $
033: *
034: * @see java.lang.ref.Reference
035: */
036:
037: public abstract class ReferenceValueMap implements Map, Cloneable {
038: private static final Category LOG = Category
039: .getInstance(ReferenceValueMap.class);
040:
041: private HashMap map;
042: private ReferenceQueue reaped = new ReferenceQueue();
043: private long lastLogTime = 0;
044: private int hits = 0;
045: private int misses = 0;
046: private int cleared = 0;
047:
048: public ReferenceValueMap() {
049: map = new HashMap();
050: }
051:
052: public ReferenceValueMap(int initialCapacity) {
053: map = new HashMap(initialCapacity);
054: }
055:
056: public ReferenceValueMap(int initialCapacity, float loadFactor) {
057: map = new HashMap(initialCapacity, loadFactor);
058: }
059:
060: public ReferenceValueMap(Map m) {
061: map = new HashMap();
062: putAll(m);
063: }
064:
065: public Object clone() {
066: reap();
067:
068: ReferenceValueMap rvm = null;
069:
070: try {
071: rvm = (ReferenceValueMap) super .clone();
072: } catch (CloneNotSupportedException e) {
073: }
074:
075: rvm.map = (HashMap) map.clone(); /* to preserve initialCapacity, loadFactor */
076: rvm.map.clear();
077: rvm.reaped = new ReferenceQueue();
078: rvm.putAll(entrySet());
079:
080: return rvm;
081: }
082:
083: /**
084: * References returned by <code>newValueReference</code> must implement
085: * this interface to provide the corresponding map key for the value.
086: */
087:
088: public interface ValueReference {
089: /**
090: * Returns the key associated with the value referenced by this
091: * <code>Reference</code> object.
092: */
093:
094: Object getKey();
095: }
096:
097: /**
098: * Returns a new <code>Reference</code> object to be inserted into the map.
099: * Subclasses must implement this method to construct <code>Reference</code>
100: * objects of the desired type (e.g. <code>SoftReference</code>, etc.).
101: *
102: * @param key The key that will be inserted.
103: * @param value The associated value to be referenced.
104: * @param queue The <code>ReferenceQueue</code> with which to register the
105: * new <code>Reference</code> object.
106: */
107:
108: protected abstract ValueReference newValueReference(Object key,
109: Object value, ReferenceQueue queue);
110:
111: public Object put(Object key, Object value) {
112: reap();
113: return map.put(key, newValueReference(key, value, reaped));
114: }
115:
116: public void putAll(Map m) {
117: putAll(m.entrySet());
118: }
119:
120: private void putAll(Set entrySet) {
121: Iterator i = entrySet.iterator();
122:
123: while (i.hasNext()) {
124: Map.Entry entry = (Map.Entry) i.next();
125: put(entry.getKey(), entry.getValue());
126: }
127: }
128:
129: public Object get(Object key) {
130: reap();
131: Reference ref = (Reference) map.get(key);
132:
133: Object value = ref == null ? null : ref.get();
134:
135: if (value == null)
136: ++misses;
137: else
138: ++hits;
139:
140: if (LOG.isDebugEnabled()) {
141: if ((System.currentTimeMillis() - lastLogTime) > 1000) {
142: LOG.debug(getClass().getName() + "("
143: + System.identityHashCode(this ) + ") size = "
144: + size() + ", hits = " + hits + ", misses = "
145: + misses + ", cleared = " + cleared);
146: lastLogTime = System.currentTimeMillis();
147: }
148: }
149:
150: return value;
151: }
152:
153: public void clear() {
154: reap();
155: map.clear();
156: }
157:
158: public int size() {
159: reap();
160: return map.size();
161: }
162:
163: public boolean containsKey(Object obj) {
164: reap();
165: return map.containsKey(obj);
166: }
167:
168: public boolean containsValue(Object obj) {
169: reap();
170:
171: if (obj != null) {
172: Iterator i = map.values().iterator();
173:
174: while (i.hasNext()) {
175: Reference ref = (Reference) i.next();
176:
177: if (obj.equals(ref.get()))
178: return true;
179: }
180: }
181:
182: return false;
183: }
184:
185: public boolean isEmpty() {
186: reap();
187: return map.isEmpty();
188: }
189:
190: public Set keySet() {
191: reap();
192: return map.keySet();
193: }
194:
195: public Collection values() {
196: reap();
197:
198: Collection c = map.values();
199: Iterator i = c.iterator();
200: ArrayList l = new ArrayList(c.size());
201:
202: while (i.hasNext()) {
203: Reference ref = (Reference) i.next();
204: Object obj = ref.get();
205:
206: if (obj != null)
207: l.add(obj);
208: }
209:
210: return Collections.unmodifiableList(l);
211: }
212:
213: public Set entrySet() {
214: reap();
215:
216: Set s = map.entrySet();
217: Iterator i = s.iterator();
218: HashMap m = new HashMap(s.size());
219:
220: while (i.hasNext()) {
221: Map.Entry entry = (Map.Entry) i.next();
222: Reference ref = (Reference) entry.getValue();
223: Object obj = ref.get();
224:
225: if (obj != null)
226: m.put(entry.getKey(), obj);
227: }
228:
229: return Collections.unmodifiableSet(m.entrySet());
230: }
231:
232: public Object remove(Object key) {
233: reap();
234: return map.remove(key);
235: }
236:
237: public int hashCode() {
238: reap();
239: return map.hashCode();
240: }
241:
242: public boolean equals(Object o) {
243: reap();
244: return map.equals(o);
245: }
246:
247: public void reap() {
248: ValueReference ref;
249:
250: while ((ref = (ValueReference) reaped.poll()) != null) {
251: map.remove(ref.getKey());
252: ++cleared;
253: }
254: }
255: }
|