1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.xbean.propertyeditor;
18
19 import java.lang.ref.Reference;
20 import java.lang.ref.WeakReference;
21 import java.lang.ref.ReferenceQueue;
22 import java.util.Collection;
23 import java.util.Map;
24 import java.util.Set;
25
26
27
28
29
30
31
32
33
34
35
36
37 public class ReferenceIdentityMap implements Map {
38
39
40 private static final int DEFAULT_CAPACITY = 16;
41
42 private static final float DEFAULT_LOAD_FACTOR = 0.75f;
43
44 private static final int MAXIMUM_CAPACITY = 1 << 30;
45
46
47 private float loadFactor;
48
49 private transient int size;
50
51 private transient ReferenceEntry[] data;
52
53 private transient int threshold;
54
55
56
57
58 private ReferenceQueue purgeQueue;
59
60 public ReferenceIdentityMap() {
61 this.loadFactor = DEFAULT_LOAD_FACTOR;
62 this.data = new ReferenceEntry[DEFAULT_CAPACITY];
63 this.threshold = calculateThreshold(DEFAULT_CAPACITY, loadFactor);
64 this.purgeQueue = new ReferenceQueue();
65 }
66
67
68
69
70
71
72 public int size() {
73 purge();
74 return size;
75 }
76
77
78
79
80
81
82 public boolean isEmpty() {
83 purge();
84 return (size == 0);
85 }
86
87
88
89
90
91
92
93 public boolean containsKey(Object key) {
94 purge();
95 ReferenceEntry entry = getEntry(key);
96 if (entry == null) {
97 return false;
98 }
99 return (entry.getValue() != null);
100 }
101
102
103
104
105
106
107
108 public boolean containsValue(Object value) {
109 purge();
110 if (value == null || size == 0) {
111 return false;
112 }
113 ReferenceEntry [] table = data;
114 for (int i = 0; i < table.length; i++) {
115 ReferenceEntry entry = table[i];
116 while (entry != null) {
117 if (value.equals(entry.getValue())) {
118 return true;
119 }
120 entry = entry.next;
121 }
122 }
123 return false;
124 }
125
126
127
128
129
130
131
132 public Object get(Object key) {
133 purge();
134 ReferenceEntry entry = getEntry(key);
135 if (entry == null) {
136 return null;
137 }
138 return entry.getValue();
139 }
140
141
142
143
144
145
146
147
148
149
150 public Object put(Object key, Object value) {
151 assert key != null: "key is null";
152 assert value != null: "value is null";
153
154 purge();
155
156 int hashCode = hash(key);
157 int index = hashIndex(hashCode, data.length);
158 ReferenceEntry entry = data[index];
159 while (entry != null) {
160 if (entry.hashCode == hashCode && key == entry.getKey()) {
161 return entry.setValue(value);
162 }
163 entry = entry.next;
164 }
165
166 createEntry(index, hashCode, key, value);
167 return null;
168 }
169
170
171
172
173
174
175
176 public Object remove(Object key) {
177 if (key == null) {
178 return null;
179 }
180 purge();
181 int hashCode = hash(key);
182 int index = hashIndex(hashCode, data.length);
183 ReferenceEntry entry = data[index];
184 ReferenceEntry previous = null;
185 while (entry != null) {
186 if (entry.hashCode == hashCode && (key == entry.getKey())) {
187 Object oldValue = entry.getValue();
188 removeEntry(entry, index, previous);
189 return oldValue;
190 }
191 previous = entry;
192 entry = entry.next;
193 }
194 return null;
195 }
196
197
198
199
200
201 public void clear() {
202 ReferenceEntry[] data = this.data;
203 for (int i = data.length - 1; i >= 0; i--) {
204 data[i] = null;
205 }
206 size = 0;
207 while (purgeQueue.poll() != null) {}
208 }
209
210 public Collection values() {
211 throw new UnsupportedOperationException();
212 }
213
214 public void putAll(Map t) {
215 throw new UnsupportedOperationException();
216 }
217
218 public Set entrySet() {
219 throw new UnsupportedOperationException();
220 }
221
222 public Set keySet() {
223 throw new UnsupportedOperationException();
224 }
225
226
227
228
229
230
231
232
233 private ReferenceEntry getEntry(Object key) {
234 if (key == null) {
235 return null;
236 }
237 int hashCode = hash(key);
238 ReferenceEntry entry = data[hashIndex(hashCode, data.length)];
239 while (entry != null) {
240 if (entry.hashCode == hashCode && (key == entry.getKey())) {
241 return entry;
242 }
243 entry = entry.next;
244 }
245 return null;
246 }
247
248
249
250
251
252
253
254
255
256
257 private ReferenceEntry createEntry(int index, int hashCode, Object key, Object value) {
258 ReferenceEntry newEntry = new ReferenceEntry(this, data[index], hashCode, key, value);
259 data[index] = newEntry;
260 size++;
261 checkCapacity();
262 return newEntry;
263 }
264
265
266
267
268
269
270
271
272
273
274
275 private void removeEntry(ReferenceEntry entry, int hashIndex, ReferenceEntry previous) {
276 if (previous == null) {
277 data[hashIndex] = entry.next;
278 } else {
279 previous.next = entry.next;
280 }
281 size--;
282 entry.next = null;
283 entry.clear();
284 entry.value = null;
285 }
286
287
288
289
290
291
292 private void checkCapacity() {
293 if (size >= threshold) {
294 int newCapacity = data.length * 2;
295 if (newCapacity <= MAXIMUM_CAPACITY) {
296 ensureCapacity(newCapacity);
297 }
298 }
299 }
300
301
302
303
304
305
306 private void ensureCapacity(int newCapacity) {
307 int oldCapacity = data.length;
308 if (newCapacity <= oldCapacity) {
309 return;
310 }
311
312 ReferenceEntry oldEntries[] = data;
313 ReferenceEntry newEntries[] = new ReferenceEntry[newCapacity];
314
315 for (int i = oldCapacity - 1; i >= 0; i--) {
316 ReferenceEntry entry = oldEntries[i];
317 if (entry != null) {
318 oldEntries[i] = null;
319 do {
320 ReferenceEntry next = entry.next;
321 int index = hashIndex(entry.hashCode, newCapacity);
322 entry.next = newEntries[index];
323 newEntries[index] = entry;
324 entry = next;
325 } while (entry != null);
326 }
327 }
328 threshold = calculateThreshold(newCapacity, loadFactor);
329 data = newEntries;
330 }
331
332
333
334
335
336
337
338
339
340 private int calculateThreshold(int newCapacity, float factor) {
341 return (int) (newCapacity * factor);
342 }
343
344
345
346
347
348
349
350
351
352 private int hash(Object key) {
353 return System.identityHashCode(key);
354 }
355
356
357
358
359
360
361
362
363
364 private int hashIndex(int hashCode, int dataSize) {
365 return hashCode & (dataSize - 1);
366 }
367
368
369
370
371
372
373
374
375
376
377
378
379 private void purge() {
380 Reference entryRef = purgeQueue.poll();
381 while (entryRef != null) {
382 purge(entryRef);
383 entryRef = purgeQueue.poll();
384 }
385 }
386
387
388
389
390
391
392 private void purge(Reference purgedEntry) {
393 int hash = ((ReferenceEntry)purgedEntry).hashCode;
394 int index = hashIndex(hash, data.length);
395 ReferenceEntry previous = null;
396 ReferenceEntry currentEntry = data[index];
397 while (currentEntry != null) {
398 if (currentEntry == purgedEntry) {
399 currentEntry.purged();
400 if (previous == null) {
401 data[index] = currentEntry.next;
402 } else {
403 previous.next = currentEntry.next;
404 }
405 this.size--;
406 return;
407 }
408 previous = currentEntry;
409 currentEntry = currentEntry.next;
410 }
411 }
412
413
414
415
416
417
418
419
420
421 private static class ReferenceEntry extends WeakReference {
422
423 private ReferenceEntry next;
424
425 private int hashCode;
426
427 private Object value;
428
429
430
431
432
433
434
435
436
437
438 private ReferenceEntry(ReferenceIdentityMap parent, ReferenceEntry next, int hashCode, Object key, Object value) {
439 super(key, parent.purgeQueue);
440 this.next = next;
441 this.hashCode = hashCode;
442 this.value = value;
443 }
444
445
446
447
448
449
450
451 private Object getKey() {
452 return this.get();
453 }
454
455
456
457
458
459
460
461 private Object getValue() {
462 return value;
463 }
464
465
466
467
468
469
470
471 private Object setValue(Object obj) {
472 Object old = getValue();
473 value = obj;
474 return old;
475 }
476
477
478
479
480 private void purged() {
481 this.clear();
482 value = null;
483 }
484 }
485 }