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 @Deprecated
112 public final class WeakHashtable extends Hashtable {
113
114
115 private final static class Entry implements Map.Entry {
116
117 private final Object key;
118 private final Object value;
119
120 private Entry(final Object key, final Object value) {
121 this.key = key;
122 this.value = value;
123 }
124
125 @Override
126 public boolean equals(final Object o) {
127 boolean result = false;
128 if (o instanceof Map.Entry) {
129 final Map.Entry entry = (Map.Entry) o;
130 result = (getKey()==null ?
131 entry.getKey() == null :
132 getKey().equals(entry.getKey())) &&
133 (getValue()==null ?
134 entry.getValue() == null :
135 getValue().equals(entry.getValue()));
136 }
137 return result;
138 }
139
140 @Override
141 public Object getKey() {
142 return key;
143 }
144
145 @Override
146 public Object getValue() {
147 return value;
148 }
149
150 @Override
151 public int hashCode() {
152 return (getKey()==null ? 0 : getKey().hashCode()) ^
153 (getValue()==null ? 0 : getValue().hashCode());
154 }
155
156 @Override
157 public Object setValue(final Object value) {
158 throw new UnsupportedOperationException("Entry.setValue is not supported.");
159 }
160 }
161
162
163 private final static class Referenced {
164
165 private final WeakReference reference;
166 private final int hashCode;
167
168
169
170
171
172 private Referenced(final Object referant) {
173 reference = new WeakReference(referant);
174
175
176 hashCode = referant.hashCode();
177 }
178
179
180
181
182
183 private Referenced(final Object key, final ReferenceQueue queue) {
184 reference = new WeakKey(key, queue, this);
185
186
187 hashCode = key.hashCode();
188
189 }
190
191 @Override
192 public boolean equals(final Object o) {
193 boolean result = false;
194 if (o instanceof Referenced) {
195 final Referenced otherKey = (Referenced) o;
196 final Object thisKeyValue = getValue();
197 final Object otherKeyValue = otherKey.getValue();
198 if (thisKeyValue == null) {
199 result = otherKeyValue == null;
200
201
202
203
204
205
206
207 result = result && this.hashCode() == otherKey.hashCode();
208
209
210
211
212 }
213 else
214 {
215 result = thisKeyValue.equals(otherKeyValue);
216 }
217 }
218 return result;
219 }
220
221 private Object getValue() {
222 return reference.get();
223 }
224
225 @Override
226 public int hashCode() {
227 return hashCode;
228 }
229 }
230
231
232
233
234
235
236 private final static class WeakKey extends WeakReference {
237
238 private final Referenced referenced;
239
240 private WeakKey(final Object key,
241 final ReferenceQueue queue,
242 final Referenced referenced) {
243 super(key, queue);
244 this.referenced = referenced;
245 }
246
247 private Referenced getReferenced() {
248 return referenced;
249 }
250 }
251
252
253 private static final long serialVersionUID = -1546036869799732453L;
254
255
256
257
258
259 private static final int MAX_CHANGES_BEFORE_PURGE = 100;
260
261
262
263
264
265 private static final int PARTIAL_PURGE_COUNT = 10;
266
267
268 private final transient ReferenceQueue queue = new ReferenceQueue();
269
270
271 private int changeCount;
272
273
274
275
276
277 public WeakHashtable() {}
278
279
280
281
282 @Override
283 public boolean containsKey(final Object key) {
284
285 final Referenced referenced = new Referenced(key);
286 return super.containsKey(referenced);
287 }
288
289
290
291
292 @Override
293 public Enumeration elements() {
294 purge();
295 return super.elements();
296 }
297
298
299
300
301 @Override
302 public Set entrySet() {
303 purge();
304 final Set referencedEntries = super.entrySet();
305 final Set unreferencedEntries = new HashSet();
306 for (final Object referencedEntry : referencedEntries) {
307 final Map.Entry entry = (Map.Entry) referencedEntry;
308 final Referenced referencedKey = (Referenced) entry.getKey();
309 final Object key = referencedKey.getValue();
310 final Object value = entry.getValue();
311 if (key != null) {
312 final Entry dereferencedEntry = new Entry(key, value);
313 unreferencedEntries.add(dereferencedEntry);
314 }
315 }
316 return unreferencedEntries;
317 }
318
319
320
321
322 @Override
323 public Object get(final Object key) {
324
325 final Referenced referenceKey = new Referenced(key);
326 return super.get(referenceKey);
327 }
328
329
330
331
332 @Override
333 public boolean isEmpty() {
334 purge();
335 return super.isEmpty();
336 }
337
338
339
340
341 @Override
342 public Enumeration keys() {
343 purge();
344 final Enumeration enumer = super.keys();
345 return new Enumeration() {
346 @Override
347 public boolean hasMoreElements() {
348 return enumer.hasMoreElements();
349 }
350 @Override
351 public Object nextElement() {
352 final Referenced nextReference = (Referenced) enumer.nextElement();
353 return nextReference.getValue();
354 }
355 };
356 }
357
358
359
360
361 @Override
362 public Set keySet() {
363 purge();
364 final Set referencedKeys = super.keySet();
365 final Set unreferencedKeys = new HashSet();
366 for (final Object referencedKey : referencedKeys) {
367 final Referenced referenceKey = (Referenced) referencedKey;
368 final Object keyValue = referenceKey.getValue();
369 if (keyValue != null) {
370 unreferencedKeys.add(keyValue);
371 }
372 }
373 return unreferencedKeys;
374 }
375
376
377
378
379
380 private void purge() {
381 final List toRemove = new ArrayList();
382 synchronized (queue) {
383 WeakKey key;
384 while ((key = (WeakKey) queue.poll()) != null) {
385 toRemove.add(key.getReferenced());
386 }
387 }
388
389
390
391
392 final int size = toRemove.size();
393 for (int i = 0; i < size; i++) {
394 super.remove(toRemove.get(i));
395 }
396 }
397
398
399
400
401
402 private void purgeOne() {
403 synchronized (queue) {
404 final WeakKey key = (WeakKey) queue.poll();
405 if (key != null) {
406 super.remove(key.getReferenced());
407 }
408 }
409 }
410
411
412
413
414 @Override
415 public synchronized Object put(final Object key, final Object value) {
416
417 Objects.requireNonNull(key, "key");
418 Objects.requireNonNull(value, "value");
419
420
421
422 if (changeCount++ > MAX_CHANGES_BEFORE_PURGE) {
423 purge();
424 changeCount = 0;
425 }
426
427 else if (changeCount % PARTIAL_PURGE_COUNT == 0) {
428 purgeOne();
429 }
430
431 final Referenced keyRef = new Referenced(key, queue);
432 return super.put(keyRef, value);
433 }
434
435
436
437
438 @Override
439 public void putAll(final Map t) {
440 if (t != null) {
441 final Set entrySet = t.entrySet();
442 for (final Object element : entrySet) {
443 final Map.Entry entry = (Map.Entry) element;
444 put(entry.getKey(), entry.getValue());
445 }
446 }
447 }
448
449
450
451
452 @Override
453 protected void rehash() {
454
455 purge();
456 super.rehash();
457 }
458
459
460
461
462 @Override
463 public synchronized Object remove(final Object key) {
464
465
466 if (changeCount++ > MAX_CHANGES_BEFORE_PURGE) {
467 purge();
468 changeCount = 0;
469 }
470
471 else if (changeCount % PARTIAL_PURGE_COUNT == 0) {
472 purgeOne();
473 }
474 return super.remove(new Referenced(key));
475 }
476
477
478
479
480 @Override
481 public int size() {
482 purge();
483 return super.size();
484 }
485
486
487
488
489 @Override
490 public String toString() {
491 purge();
492 return super.toString();
493 }
494
495
496
497
498 @Override
499 public Collection values() {
500 purge();
501 return super.values();
502 }
503 }