1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.commons.logging.impl;
19
20 import java.lang.ref.ReferenceQueue;
21 import java.lang.ref.WeakReference;
22 import java.util.ArrayList;
23 import java.util.Collection;
24 import java.util.Enumeration;
25 import java.util.HashSet;
26 import java.util.Hashtable;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.Objects;
30 import java.util.Set;
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120 @Deprecated
121 public final class WeakHashtable extends Hashtable {
122
123
124 private final static class Entry implements Map.Entry {
125
126 private final Object key;
127 private final Object value;
128
129 private Entry(final Object key, final Object value) {
130 this.key = key;
131 this.value = value;
132 }
133
134 @Override
135 public boolean equals(final Object o) {
136 boolean result = false;
137 if (o instanceof Map.Entry) {
138 final Map.Entry entry = (Map.Entry) o;
139 result = (getKey()==null ?
140 entry.getKey() == null :
141 getKey().equals(entry.getKey())) &&
142 (getValue()==null ?
143 entry.getValue() == null :
144 getValue().equals(entry.getValue()));
145 }
146 return result;
147 }
148
149 @Override
150 public Object getKey() {
151 return key;
152 }
153
154 @Override
155 public Object getValue() {
156 return value;
157 }
158
159 @Override
160 public int hashCode() {
161 return (getKey()==null ? 0 : getKey().hashCode()) ^
162 (getValue()==null ? 0 : getValue().hashCode());
163 }
164
165 @Override
166 public Object setValue(final Object value) {
167 throw new UnsupportedOperationException("Entry.setValue is not supported.");
168 }
169 }
170
171
172 private final static class Referenced {
173
174 private final WeakReference reference;
175 private final int hashCode;
176
177
178
179
180
181 private Referenced(final Object referant) {
182 reference = new WeakReference(referant);
183
184
185 hashCode = referant.hashCode();
186 }
187
188
189
190
191
192 private Referenced(final Object key, final ReferenceQueue queue) {
193 reference = new WeakKey(key, queue, this);
194
195
196 hashCode = key.hashCode();
197
198 }
199
200 @Override
201 public boolean equals(final Object o) {
202 boolean result = false;
203 if (o instanceof Referenced) {
204 final Referenced otherKey = (Referenced) o;
205 final Object thisKeyValue = getValue();
206 final Object otherKeyValue = otherKey.getValue();
207 if (thisKeyValue == null) {
208 result = otherKeyValue == null;
209
210
211
212
213
214
215
216 result = result && hashCode() == otherKey.hashCode();
217
218
219
220
221 }
222 else
223 {
224 result = thisKeyValue.equals(otherKeyValue);
225 }
226 }
227 return result;
228 }
229
230 private Object getValue() {
231 return reference.get();
232 }
233
234 @Override
235 public int hashCode() {
236 return hashCode;
237 }
238 }
239
240
241
242
243
244
245 private final static class WeakKey extends WeakReference {
246
247 private final Referenced referenced;
248
249 private WeakKey(final Object key,
250 final ReferenceQueue queue,
251 final Referenced referenced) {
252 super(key, queue);
253 this.referenced = referenced;
254 }
255
256 private Referenced getReferenced() {
257 return referenced;
258 }
259 }
260
261
262 private static final long serialVersionUID = -1546036869799732453L;
263
264
265
266
267
268 private static final int MAX_CHANGES_BEFORE_PURGE = 100;
269
270
271
272
273
274 private static final int PARTIAL_PURGE_COUNT = 10;
275
276
277 private final transient ReferenceQueue queue = new ReferenceQueue();
278
279
280 private int changeCount;
281
282
283
284
285
286 public WeakHashtable() {}
287
288
289
290
291 @Override
292 public boolean containsKey(final Object key) {
293
294 final Referenced referenced = new Referenced(key);
295 return super.containsKey(referenced);
296 }
297
298
299
300
301 @Override
302 public Enumeration elements() {
303 purge();
304 return super.elements();
305 }
306
307
308
309
310 @Override
311 public Set entrySet() {
312 purge();
313 final Set referencedEntries = super.entrySet();
314 final Set unreferencedEntries = new HashSet();
315 for (final Object referencedEntry : referencedEntries) {
316 final Map.Entry entry = (Map.Entry) referencedEntry;
317 final Referenced referencedKey = (Referenced) entry.getKey();
318 final Object key = referencedKey.getValue();
319 final Object value = entry.getValue();
320 if (key != null) {
321 final Entry dereferencedEntry = new Entry(key, value);
322 unreferencedEntries.add(dereferencedEntry);
323 }
324 }
325 return unreferencedEntries;
326 }
327
328
329
330
331 @Override
332 public Object get(final Object key) {
333
334 final Referenced referenceKey = new Referenced(key);
335 return super.get(referenceKey);
336 }
337
338
339
340
341 @Override
342 public boolean isEmpty() {
343 purge();
344 return super.isEmpty();
345 }
346
347
348
349
350 @Override
351 public Enumeration keys() {
352 purge();
353 final Enumeration enumer = super.keys();
354 return new Enumeration() {
355 @Override
356 public boolean hasMoreElements() {
357 return enumer.hasMoreElements();
358 }
359 @Override
360 public Object nextElement() {
361 final Referenced nextReference = (Referenced) enumer.nextElement();
362 return nextReference.getValue();
363 }
364 };
365 }
366
367
368
369
370 @Override
371 public Set keySet() {
372 purge();
373 final Set referencedKeys = super.keySet();
374 final Set unreferencedKeys = new HashSet();
375 for (final Object referencedKey : referencedKeys) {
376 final Referenced referenceKey = (Referenced) referencedKey;
377 final Object keyValue = referenceKey.getValue();
378 if (keyValue != null) {
379 unreferencedKeys.add(keyValue);
380 }
381 }
382 return unreferencedKeys;
383 }
384
385
386
387
388
389 private void purge() {
390 final List toRemove = new ArrayList();
391 synchronized (queue) {
392 WeakKey key;
393 while ((key = (WeakKey) queue.poll()) != null) {
394 toRemove.add(key.getReferenced());
395 }
396 }
397
398
399
400
401 final int size = toRemove.size();
402 for (int i = 0; i < size; i++) {
403 super.remove(toRemove.get(i));
404 }
405 }
406
407
408
409
410
411 private void purgeOne() {
412 synchronized (queue) {
413 final WeakKey key = (WeakKey) queue.poll();
414 if (key != null) {
415 super.remove(key.getReferenced());
416 }
417 }
418 }
419
420
421
422
423 @Override
424 public synchronized Object put(final Object key, final Object value) {
425
426 Objects.requireNonNull(key, "key");
427 Objects.requireNonNull(value, "value");
428
429
430
431 if (changeCount++ > MAX_CHANGES_BEFORE_PURGE) {
432 purge();
433 changeCount = 0;
434 }
435
436 else if (changeCount % PARTIAL_PURGE_COUNT == 0) {
437 purgeOne();
438 }
439
440 final Referenced keyRef = new Referenced(key, queue);
441 return super.put(keyRef, value);
442 }
443
444
445
446
447 @Override
448 public void putAll(final Map t) {
449 if (t != null) {
450 final Set entrySet = t.entrySet();
451 for (final Object element : entrySet) {
452 final Map.Entry entry = (Map.Entry) element;
453 put(entry.getKey(), entry.getValue());
454 }
455 }
456 }
457
458
459
460
461 @Override
462 protected void rehash() {
463
464 purge();
465 super.rehash();
466 }
467
468
469
470
471 @Override
472 public synchronized Object remove(final Object key) {
473
474
475 if (changeCount++ > MAX_CHANGES_BEFORE_PURGE) {
476 purge();
477 changeCount = 0;
478 }
479
480 else if (changeCount % PARTIAL_PURGE_COUNT == 0) {
481 purgeOne();
482 }
483 return super.remove(new Referenced(key));
484 }
485
486
487
488
489 @Override
490 public int size() {
491 purge();
492 return super.size();
493 }
494
495
496
497
498 @Override
499 public String toString() {
500 purge();
501 return super.toString();
502 }
503
504
505
506
507 @Override
508 public Collection values() {
509 purge();
510 return super.values();
511 }
512 }