1 16 17 package com.google.inject.util; 18 19 import static com.google.inject.util.ReferenceType.STRONG; 20 21 import java.io.IOException ; 22 import java.io.ObjectInputStream ; 23 import java.io.ObjectOutputStream ; 24 import java.io.Serializable ; 25 import java.lang.ref.Reference ; 26 import java.util.ArrayList ; 27 import java.util.Collection ; 28 import java.util.Collections ; 29 import java.util.HashSet ; 30 import java.util.Map ; 31 import java.util.Set ; 32 import java.util.concurrent.ConcurrentHashMap ; 33 import java.util.concurrent.ConcurrentMap ; 34 35 59 @SuppressWarnings ("unchecked") 60 public class ReferenceMap<K, V> implements Map <K, V>, Serializable { 61 62 private static final long serialVersionUID = 0; 63 64 transient ConcurrentMap <Object , Object > delegate; 65 66 final ReferenceType keyReferenceType; 67 final ReferenceType valueReferenceType; 68 69 76 public ReferenceMap(ReferenceType keyReferenceType, 77 ReferenceType valueReferenceType) { 78 ensureNotNull(keyReferenceType, valueReferenceType); 79 80 if (keyReferenceType == ReferenceType.PHANTOM 81 || valueReferenceType == ReferenceType.PHANTOM) { 82 throw new IllegalArgumentException ("Phantom references not supported."); 83 } 84 85 this.delegate = new ConcurrentHashMap <Object , Object >(); 86 this.keyReferenceType = keyReferenceType; 87 this.valueReferenceType = valueReferenceType; 88 } 89 90 V internalGet(K key) { 91 Object valueReference = delegate.get(makeKeyReferenceAware(key)); 92 return valueReference == null 93 ? null 94 : (V) dereferenceValue(valueReference); 95 } 96 97 public V get(final Object key) { 98 ensureNotNull(key); 99 return internalGet((K) key); 100 } 101 102 V execute(Strategy strategy, K key, V value) { 103 ensureNotNull(key, value); 104 Object keyReference = referenceKey(key); 105 Object valueReference = strategy.execute( 106 this, 107 keyReference, 108 referenceValue(keyReference, value) 109 ); 110 return valueReference == null ? null 111 : (V) dereferenceValue(valueReference); 112 } 113 114 public V put(K key, V value) { 115 return execute(putStrategy(), key, value); 116 } 117 118 public V remove(Object key) { 119 ensureNotNull(key); 120 Object referenceAwareKey = makeKeyReferenceAware(key); 121 Object valueReference = delegate.remove(referenceAwareKey); 122 return valueReference == null ? null 123 : (V) dereferenceValue(valueReference); 124 } 125 126 public int size() { 127 return delegate.size(); 128 } 129 130 public boolean isEmpty() { 131 return delegate.isEmpty(); 132 } 133 134 public boolean containsKey(Object key) { 135 ensureNotNull(key); 136 Object referenceAwareKey = makeKeyReferenceAware(key); 137 return delegate.containsKey(referenceAwareKey); 138 } 139 140 public boolean containsValue(Object value) { 141 ensureNotNull(value); 142 for (Object valueReference : delegate.values()) { 143 if (value.equals(dereferenceValue(valueReference))) { 144 return true; 145 } 146 } 147 return false; 148 } 149 150 public void putAll(Map <? extends K, ? extends V> t) { 151 for (Map.Entry <? extends K, ? extends V> entry : t.entrySet()) { 152 put(entry.getKey(), entry.getValue()); 153 } 154 } 155 156 public void clear() { 157 delegate.clear(); 158 } 159 160 164 public Set <K> keySet() { 165 return Collections.unmodifiableSet( 166 dereferenceKeySet(delegate.keySet())); 167 } 168 169 173 public Collection <V> values() { 174 return Collections.unmodifiableCollection( 175 dereferenceValues(delegate.values())); 176 } 177 178 public V putIfAbsent(K key, V value) { 179 return execute(putIfAbsentStrategy(), key, value); 182 } 183 184 public boolean remove(Object key, Object value) { 185 ensureNotNull(key, value); 186 Object referenceAwareKey = makeKeyReferenceAware(key); 187 Object referenceAwareValue = makeValueReferenceAware(value); 188 return delegate.remove(referenceAwareKey, referenceAwareValue); 189 } 190 191 public boolean replace(K key, V oldValue, V newValue) { 192 ensureNotNull(key, oldValue, newValue); 193 Object keyReference = referenceKey(key); 194 195 Object referenceAwareOldValue = makeValueReferenceAware(oldValue); 196 return delegate.replace( 197 keyReference, 198 referenceAwareOldValue, 199 referenceValue(keyReference, newValue) 200 ); 201 } 202 203 public V replace(K key, V value) { 204 return execute(replaceStrategy(), key, value); 207 } 208 209 213 public Set <Map.Entry <K, V>> entrySet() { 214 Set <Map.Entry <K, V>> entrySet = new HashSet <Map.Entry <K, V>>(); 215 for (Map.Entry <Object , Object > entry : delegate.entrySet()) { 216 Map.Entry <K, V> dereferenced = dereferenceEntry(entry); 217 if (dereferenced != null) { 218 entrySet.add(dereferenced); 219 } 220 } 221 return Collections.unmodifiableSet(entrySet); 222 } 223 224 227 Entry dereferenceEntry(Map.Entry <Object , Object > entry) { 228 K key = dereferenceKey(entry.getKey()); 229 V value = dereferenceValue(entry.getValue()); 230 return (key == null || value == null) 231 ? null 232 : new Entry(key, value); 233 } 234 235 238 Object referenceKey(K key) { 239 switch (keyReferenceType) { 240 case STRONG: return key; 241 case SOFT: return new SoftKeyReference(key); 242 case WEAK: return new WeakKeyReference(key); 243 default: throw new AssertionError (); 244 } 245 } 246 247 250 K dereferenceKey(Object o) { 251 return (K) dereference(keyReferenceType, o); 252 } 253 254 257 V dereferenceValue(Object o) { 258 return (V) dereference(valueReferenceType, o); 259 } 260 261 264 Object dereference(ReferenceType referenceType, Object reference) { 265 return referenceType == STRONG ? reference : ((Reference ) reference).get(); 266 } 267 268 271 Object referenceValue(Object keyReference, Object value) { 272 switch (valueReferenceType) { 273 case STRONG: return value; 274 case SOFT: return new SoftValueReference(keyReference, value); 275 case WEAK: return new WeakValueReference(keyReference, value); 276 default: throw new AssertionError (); 277 } 278 } 279 280 283 Set <K> dereferenceKeySet(Set keyReferences) { 284 return keyReferenceType == STRONG 285 ? keyReferences 286 : dereferenceCollection(keyReferenceType, keyReferences, new HashSet ()); 287 } 288 289 292 Collection <V> dereferenceValues(Collection valueReferences) { 293 return valueReferenceType == STRONG 294 ? valueReferences 295 : dereferenceCollection(valueReferenceType, valueReferences, 296 new ArrayList (valueReferences.size())); 297 } 298 299 302 Object makeKeyReferenceAware(Object o) { 303 return keyReferenceType == STRONG ? o : new KeyReferenceAwareWrapper(o); 304 } 305 306 309 Object makeValueReferenceAware(Object o) { 310 return valueReferenceType == STRONG ? o : new ReferenceAwareWrapper(o); 311 } 312 313 318 <T extends Collection <Object >> T dereferenceCollection( 319 ReferenceType referenceType, T in, T out) { 320 for (Object reference : in) { 321 out.add(dereference(referenceType, reference)); 322 } 323 return out; 324 } 325 326 329 interface InternalReference {} 330 331 static int keyHashCode(Object key) { 332 return System.identityHashCode(key); 333 } 334 335 342 static boolean referenceEquals(Reference r, Object o) { 343 if (o instanceof InternalReference) { 345 if (o == r) { 347 return true; 348 } 349 350 Object referent = ((Reference ) o).get(); 352 return referent != null && referent == r.get(); 353 } 354 355 return ((ReferenceAwareWrapper) o).unwrap() == r.get(); 357 } 358 359 363 static class ReferenceAwareWrapper { 364 365 final Object wrapped; 366 367 ReferenceAwareWrapper(Object wrapped) { 368 this.wrapped = wrapped; 369 } 370 371 Object unwrap() { 372 return wrapped; 373 } 374 375 public int hashCode() { 376 return wrapped.hashCode(); 377 } 378 379 public boolean equals(Object obj) { 380 return obj.equals(this); 382 } 383 } 384 385 388 static class KeyReferenceAwareWrapper extends ReferenceAwareWrapper { 389 390 public KeyReferenceAwareWrapper(Object wrapped) { 391 super(wrapped); 392 } 393 394 public int hashCode() { 395 return System.identityHashCode(wrapped); 396 } 397 } 398 399 class SoftKeyReference extends FinalizableSoftReference<Object > 400 implements InternalReference { 401 402 final int hashCode; 403 404 public SoftKeyReference(Object key) { 405 super(key); 406 this.hashCode = keyHashCode(key); 407 } 408 409 public void finalizeReferent() { 410 delegate.remove(this); 411 } 412 413 @Override public int hashCode() { 414 return this.hashCode; 415 } 416 417 @Override public boolean equals(Object o) { 418 return referenceEquals(this, o); 419 } 420 } 421 422 class WeakKeyReference extends FinalizableWeakReference<Object > 423 implements InternalReference { 424 425 final int hashCode; 426 427 public WeakKeyReference(Object key) { 428 super(key); 429 this.hashCode = keyHashCode(key); 430 } 431 432 public void finalizeReferent() { 433 delegate.remove(this); 434 } 435 436 @Override public int hashCode() { 437 return this.hashCode; 438 } 439 440 @Override public boolean equals(Object o) { 441 return referenceEquals(this, o); 442 } 443 } 444 445 class SoftValueReference extends FinalizableSoftReference<Object > 446 implements InternalReference { 447 448 final Object keyReference; 449 450 public SoftValueReference(Object keyReference, Object value) { 451 super(value); 452 this.keyReference = keyReference; 453 } 454 455 public void finalizeReferent() { 456 delegate.remove(keyReference, this); 457 } 458 459 @Override public boolean equals(Object obj) { 460 return referenceEquals(this, obj); 461 } 462 } 463 464 class WeakValueReference extends FinalizableWeakReference<Object > 465 implements InternalReference { 466 467 final Object keyReference; 468 469 public WeakValueReference(Object keyReference, Object value) { 470 super(value); 471 this.keyReference = keyReference; 472 } 473 474 public void finalizeReferent() { 475 delegate.remove(keyReference, this); 476 } 477 478 @Override public boolean equals(Object obj) { 479 return referenceEquals(this, obj); 480 } 481 } 482 483 protected interface Strategy { 484 public Object execute(ReferenceMap map, Object keyReference, 485 Object valueReference); 486 } 487 488 protected Strategy putStrategy() { 489 return PutStrategy.PUT; 490 } 491 492 protected Strategy putIfAbsentStrategy() { 493 return PutStrategy.PUT_IF_ABSENT; 494 } 495 496 protected Strategy replaceStrategy() { 497 return PutStrategy.REPLACE; 498 } 499 500 protected enum PutStrategy implements Strategy { 501 PUT { 502 public Object execute(ReferenceMap map, Object keyReference, 503 Object valueReference) { 504 return map.delegate.put(keyReference, valueReference); 505 } 506 }, 507 508 REPLACE { 509 public Object execute(ReferenceMap map, Object keyReference, 510 Object valueReference) { 511 return map.delegate.replace(keyReference, valueReference); 512 } 513 }, 514 515 PUT_IF_ABSENT { 516 public Object execute(ReferenceMap map, Object keyReference, 517 Object valueReference) { 518 return map.delegate.putIfAbsent(keyReference, valueReference); 519 } 520 }; 521 }; 522 523 private static PutStrategy defaultPutStrategy; 524 525 protected PutStrategy getPutStrategy() { 526 return defaultPutStrategy; 527 } 528 529 530 class Entry implements Map.Entry <K, V> { 531 532 final K key; 533 final V value; 534 535 public Entry(K key, V value) { 536 this.key = key; 537 this.value = value; 538 } 539 540 public K getKey() { 541 return this.key; 542 } 543 544 public V getValue() { 545 return this.value; 546 } 547 548 public V setValue(V value) { 549 return put(key, value); 550 } 551 552 public int hashCode() { 553 return key.hashCode() * 31 + value.hashCode(); 554 } 555 556 public boolean equals(Object o) { 557 if (!(o instanceof ReferenceMap.Entry)) { 558 return false; 559 } 560 561 Entry entry = (Entry) o; 562 return key.equals(entry.key) && value.equals(entry.value); 563 } 564 565 public String toString() { 566 return key + "=" + value; 567 } 568 } 569 570 static void ensureNotNull(Object o) { 571 if (o == null) { 572 throw new NullPointerException (); 573 } 574 } 575 576 static void ensureNotNull(Object ... array) { 577 for (int i = 0; i < array.length; i++) { 578 if (array[i] == null) { 579 throw new NullPointerException ("Argument #" + i + " is null."); 580 } 581 } 582 } 583 584 private void writeObject(ObjectOutputStream out) throws IOException { 585 out.defaultWriteObject(); 586 out.writeInt(size()); 587 for (Map.Entry <Object , Object > entry : delegate.entrySet()) { 588 Object key = dereferenceKey(entry.getKey()); 589 Object value = dereferenceValue(entry.getValue()); 590 591 if (key != null && value != null) { 593 out.writeObject(key); 594 out.writeObject(value); 595 } 596 } 597 out.writeObject(null); 598 } 599 600 private void readObject(ObjectInputStream in) throws IOException , 601 ClassNotFoundException { 602 in.defaultReadObject(); 603 int size = in.readInt(); 604 this.delegate = new ConcurrentHashMap <Object , Object >(size); 605 while (true) { 606 K key = (K) in.readObject(); 607 if (key == null) { 608 break; 609 } 610 V value = (V) in.readObject(); 611 put(key, value); 612 } 613 } 614 615 } 616 | Popular Tags |