001: /*
002: * Distributed as part of c3p0 v.0.9.1.2
003: *
004: * Copyright (C) 2005 Machinery For Change, Inc.
005: *
006: * Author: Steve Waldman <swaldman@mchange.com>
007: *
008: * This library is free software; you can redistribute it and/or modify
009: * it under the terms of the GNU Lesser General Public License version 2.1, as
010: * published by the Free Software Foundation.
011: *
012: * This software is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
015: * GNU Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public License
018: * along with this software; see the file LICENSE. If not, write to the
019: * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
020: * Boston, MA 02111-1307, USA.
021: */
022:
023: package com.mchange.v2.util;
024:
025: import java.lang.ref.ReferenceQueue;
026: import java.lang.ref.WeakReference;
027: import java.util.AbstractSet;
028: import java.util.ArrayList;
029: import java.util.Collection;
030: import java.util.HashMap;
031: import java.util.HashSet;
032: import java.util.Iterator;
033: import java.util.Map;
034: import java.util.Set;
035: import java.util.Map.Entry;
036:
037: import com.mchange.v1.util.AbstractMapEntry;
038: import com.mchange.v1.util.WrapperIterator;
039:
040: //TODO -- ensure that cleanCleared() gets called only once, even in methods implemented
041: // as loops. (cleanCleared() is idempotent, so the repeated calls are okay,
042: // but they're wasteful.
043:
044: /**
045: * <p>This class is <u>not</u> Thread safe.
046: * Use in single threaded contexts, or contexts where
047: * single threaded-access can be guaranteed, or
048: * wrap with Collections.synchronizedMap().</p>
049: *
050: * <p>This class does not accept null keys or values.</p>
051: */
052: public class DoubleWeakHashMap implements Map {
053: HashMap inner;
054: ReferenceQueue keyQ = new ReferenceQueue();
055: ReferenceQueue valQ = new ReferenceQueue();
056:
057: CheckKeyHolder holder = new CheckKeyHolder();
058:
059: Set userKeySet = null;
060: Collection valuesCollection = null;
061:
062: public DoubleWeakHashMap() {
063: this .inner = new HashMap();
064: }
065:
066: public DoubleWeakHashMap(int initialCapacity) {
067: this .inner = new HashMap(initialCapacity);
068: }
069:
070: public DoubleWeakHashMap(int initialCapacity, float loadFactor) {
071: this .inner = new HashMap(initialCapacity, loadFactor);
072: }
073:
074: public DoubleWeakHashMap(Map m) {
075: this ();
076: putAll(m);
077: }
078:
079: public void cleanCleared() {
080: WKey wk;
081: while ((wk = (WKey) keyQ.poll()) != null)
082: inner.remove(wk);
083:
084: WVal wv;
085: while ((wv = (WVal) valQ.poll()) != null)
086: inner.remove(wv.getWKey());
087: }
088:
089: public void clear() {
090: cleanCleared();
091: inner.clear();
092: }
093:
094: public boolean containsKey(Object key) {
095: cleanCleared();
096: try {
097: return inner.containsKey(holder.set(key));
098: } finally {
099: holder.clear();
100: }
101: }
102:
103: public boolean containsValue(Object val) {
104: for (Iterator ii = inner.values().iterator(); ii.hasNext();) {
105: WVal wval = (WVal) ii.next();
106: if (val.equals(wval.get()))
107: return true;
108: }
109: return false;
110: }
111:
112: public Set entrySet() {
113: cleanCleared();
114: return new UserEntrySet();
115: }
116:
117: public Object get(Object key) {
118: try {
119: cleanCleared();
120: WVal wval = (WVal) inner.get(holder.set(key));
121: return (wval == null ? null : wval.get());
122: } finally {
123: holder.clear();
124: }
125: }
126:
127: public boolean isEmpty() {
128: cleanCleared();
129: return inner.isEmpty();
130: }
131:
132: public Set keySet() {
133: cleanCleared();
134: if (userKeySet == null)
135: userKeySet = new UserKeySet();
136: return userKeySet;
137: }
138:
139: public Object put(Object key, Object val) {
140: cleanCleared();
141: WVal wout = doPut(key, val);
142: if (wout != null)
143: return wout.get();
144: else
145: return null;
146: }
147:
148: private WVal doPut(Object key, Object val) {
149: WKey wk = new WKey(key, keyQ);
150: WVal wv = new WVal(wk, val, valQ);
151: return (WVal) inner.put(wk, wv);
152: }
153:
154: public void putAll(Map m) {
155: cleanCleared();
156: for (Iterator ii = m.entrySet().iterator(); ii.hasNext();) {
157: Map.Entry entry = (Map.Entry) ii.next();
158: this .doPut(entry.getKey(), entry.getValue());
159: }
160: }
161:
162: public Object remove(Object key) {
163: try {
164: cleanCleared();
165: WVal wv = (WVal) inner.remove(holder.set(key));
166: return (wv == null ? null : wv.get());
167: } finally {
168: holder.clear();
169: }
170: }
171:
172: public int size() {
173: cleanCleared();
174: return inner.size();
175: }
176:
177: public Collection values() {
178: if (valuesCollection == null)
179: this .valuesCollection = new ValuesCollection();
180: return valuesCollection;
181: }
182:
183: final static class CheckKeyHolder {
184: Object checkKey;
185:
186: public Object get() {
187: return checkKey;
188: }
189:
190: public CheckKeyHolder set(Object ck) {
191: assert this .checkKey == null : "Illegal concurrenct use of DoubleWeakHashMap!";
192:
193: this .checkKey = ck;
194: return this ;
195: }
196:
197: public void clear() {
198: checkKey = null;
199: }
200:
201: public int hashCode() {
202: return checkKey.hashCode();
203: }
204:
205: public boolean equals(Object o) {
206: assert this .get() != null : "CheckedKeyHolder should never do an equality check while its value is null.";
207:
208: if (this == o)
209: return true;
210: else if (o instanceof CheckKeyHolder)
211: return this .get().equals(((CheckKeyHolder) o).get());
212: else if (o instanceof WKey)
213: return this .get().equals(((WKey) o).get());
214: else
215: return false;
216: }
217: }
218:
219: final static class WKey extends WeakReference {
220: int cachedHash;
221:
222: WKey(Object keyObj, ReferenceQueue rq) {
223: super (keyObj, rq);
224: this .cachedHash = keyObj.hashCode();
225: }
226:
227: public int hashCode() {
228: return cachedHash;
229: }
230:
231: public boolean equals(Object o) {
232: if (this == o)
233: return true;
234: else if (o instanceof WKey) {
235: WKey oo = (WKey) o;
236: Object myVal = this .get();
237: Object ooVal = oo.get();
238: if (myVal == null || ooVal == null)
239: return false;
240: else
241: return myVal.equals(ooVal);
242: } else if (o instanceof CheckKeyHolder) {
243: CheckKeyHolder oo = (CheckKeyHolder) o;
244: Object myVal = this .get();
245: Object ooVal = oo.get();
246: if (myVal == null || ooVal == null)
247: return false;
248: else
249: return myVal.equals(ooVal);
250: } else
251: return false;
252: }
253: }
254:
255: final static class WVal extends WeakReference {
256: WKey key;
257:
258: WVal(WKey key, Object valObj, ReferenceQueue rq) {
259: super (valObj, rq);
260: this .key = key;
261: }
262:
263: public WKey getWKey() {
264: return key;
265: }
266: }
267:
268: private final class UserEntrySet extends AbstractSet {
269: private Set innerEntrySet() {
270: cleanCleared();
271: return inner.entrySet();
272: }
273:
274: public Iterator iterator() {
275: return new WrapperIterator(innerEntrySet().iterator(), true) {
276: protected Object transformObject(Object o) {
277: Entry innerEntry = (Entry) o;
278: Object key = ((WKey) innerEntry.getKey()).get();
279: Object val = ((WVal) innerEntry.getValue()).get();
280:
281: if (key == null || val == null)
282: return WrapperIterator.SKIP_TOKEN;
283: else
284: return new UserEntry(innerEntry, key, val);
285: }
286: };
287: }
288:
289: public int size() {
290: return innerEntrySet().size();
291: }
292: }
293:
294: class UserEntry extends AbstractMapEntry {
295: Entry innerEntry;
296: Object key;
297: Object val;
298:
299: UserEntry(Entry innerEntry, Object key, Object val) {
300: this .innerEntry = innerEntry;
301: this .key = key;
302: this .val = val;
303: }
304:
305: public final Object getKey() {
306: return key;
307: }
308:
309: public final Object getValue() {
310: return val;
311: }
312:
313: public final Object setValue(Object value) {
314: return innerEntry.setValue(new WVal((WKey) innerEntry
315: .getKey(), value, valQ));
316: }
317: }
318:
319: class UserKeySet implements Set {
320: public boolean add(Object o) {
321: cleanCleared();
322: throw new UnsupportedOperationException(
323: "You cannot add to a Map's key set.");
324: }
325:
326: public boolean addAll(Collection c) {
327: cleanCleared();
328: throw new UnsupportedOperationException(
329: "You cannot add to a Map's key set.");
330: }
331:
332: public void clear() {
333: DoubleWeakHashMap.this .clear();
334: }
335:
336: public boolean contains(Object o) {
337: return DoubleWeakHashMap.this .containsKey(o);
338: }
339:
340: public boolean containsAll(Collection c) {
341: for (Iterator ii = c.iterator(); ii.hasNext();)
342: if (!this .contains(ii.next()))
343: return false;
344: return true;
345: }
346:
347: public boolean isEmpty() {
348: return DoubleWeakHashMap.this .isEmpty();
349: }
350:
351: public Iterator iterator() {
352: cleanCleared();
353: return new WrapperIterator(DoubleWeakHashMap.this .inner
354: .keySet().iterator(), true) {
355: protected Object transformObject(Object o) {
356: Object key = ((WKey) o).get();
357:
358: if (key == null)
359: return WrapperIterator.SKIP_TOKEN;
360: else
361: return key;
362: }
363: };
364: }
365:
366: public boolean remove(Object o) {
367: return (DoubleWeakHashMap.this .remove(o) != null);
368: }
369:
370: public boolean removeAll(Collection c) {
371: boolean out = false;
372: for (Iterator ii = c.iterator(); ii.hasNext();)
373: out |= this .remove(ii.next());
374: return out;
375: }
376:
377: public boolean retainAll(Collection c) {
378: //we implicitly cleanCleared() by calling iterator()
379: boolean out = false;
380: for (Iterator ii = this .iterator(); ii.hasNext();) {
381: if (!c.contains(ii.next())) {
382: ii.remove();
383: out = true;
384: }
385: }
386: return out;
387: }
388:
389: public int size() {
390: return DoubleWeakHashMap.this .size();
391: }
392:
393: public Object[] toArray() {
394: cleanCleared();
395: return new HashSet(this ).toArray();
396: }
397:
398: public Object[] toArray(Object[] array) {
399: cleanCleared();
400: return new HashSet(this ).toArray(array);
401: }
402: }
403:
404: class ValuesCollection implements Collection {
405:
406: public boolean add(Object o) {
407: cleanCleared();
408: throw new UnsupportedOperationException(
409: "DoubleWeakHashMap does not support adding to its values Collection.");
410: }
411:
412: public boolean addAll(Collection c) {
413: cleanCleared();
414: throw new UnsupportedOperationException(
415: "DoubleWeakHashMap does not support adding to its values Collection.");
416: }
417:
418: public void clear() {
419: DoubleWeakHashMap.this .clear();
420: }
421:
422: public boolean contains(Object o) {
423: return DoubleWeakHashMap.this .containsValue(o);
424: }
425:
426: public boolean containsAll(Collection c) {
427: for (Iterator ii = c.iterator(); ii.hasNext();)
428: if (!this .contains(ii.next()))
429: return false;
430: return true;
431: }
432:
433: public boolean isEmpty() {
434: return DoubleWeakHashMap.this .isEmpty();
435: }
436:
437: public Iterator iterator() {
438: return new WrapperIterator(inner.values().iterator(), true) {
439: protected Object transformObject(Object o) {
440: Object val = ((WVal) o).get();
441:
442: if (val == null)
443: return WrapperIterator.SKIP_TOKEN;
444: else
445: return val;
446: }
447: };
448: }
449:
450: public boolean remove(Object o) {
451: cleanCleared();
452: return removeValue(o);
453: }
454:
455: public boolean removeAll(Collection c) {
456: cleanCleared();
457: boolean out = false;
458: for (Iterator ii = c.iterator(); ii.hasNext();)
459: out |= removeValue(ii.next());
460: return out;
461: }
462:
463: public boolean retainAll(Collection c) {
464: cleanCleared();
465: return retainValues(c);
466: }
467:
468: public int size() {
469: return DoubleWeakHashMap.this .size();
470: }
471:
472: public Object[] toArray() {
473: cleanCleared();
474: return new ArrayList(this ).toArray();
475: }
476:
477: public Object[] toArray(Object[] array) {
478: cleanCleared();
479: return new ArrayList(this ).toArray(array);
480: }
481:
482: private boolean removeValue(Object val) {
483: boolean out = false;
484: for (Iterator ii = inner.values().iterator(); ii.hasNext();) {
485: WVal wv = (WVal) ii.next();
486: if (val.equals(wv.get())) {
487: ii.remove();
488: out = true;
489: }
490: }
491: return out;
492: }
493:
494: private boolean retainValues(Collection c) {
495: boolean out = false;
496: for (Iterator ii = inner.values().iterator(); ii.hasNext();) {
497: WVal wv = (WVal) ii.next();
498: if (!c.contains(wv.get())) {
499: ii.remove();
500: out = true;
501: }
502: }
503: return out;
504: }
505: }
506:
507: /*
508: public static void main(String[] argv)
509: {
510: DoubleWeakHashMap m = new DoubleWeakHashMap();
511: //Set keySet = new HashSet();
512: //Set valSet = new HashSet();
513:
514: while (true)
515: {
516: System.err.println( m.inner.size() );
517:
518: //if (Math.random() < 0.1f)
519: // valSet.clear();
520:
521: Object key = new Object();
522: Object val = new long[100000];
523: //keySet.add(key);
524: //valSet.add(val);
525: m.put( key, val );
526: }
527: }
528: */
529: }
|