| 1 | /* | 
| 2 | *  jDTAUS Core RI Client Container | 
| 3 | *  Copyright (C) 2005 Christian Schulte | 
| 4 | *  <cs@schulte.it> | 
| 5 | * | 
| 6 | *  This library is free software; you can redistribute it and/or | 
| 7 | *  modify it under the terms of the GNU Lesser General Public | 
| 8 | *  License as published by the Free Software Foundation; either | 
| 9 | *  version 2.1 of the License, or any later version. | 
| 10 | * | 
| 11 | *  This library is distributed in the hope that it will be useful, | 
| 12 | *  but WITHOUT ANY WARRANTY; without even the implied warranty of | 
| 13 | *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
| 14 | *  Lesser General Public License for more details. | 
| 15 | * | 
| 16 | *  You should have received a copy of the GNU Lesser General Public | 
| 17 | *  License along with this library; if not, write to the Free Software | 
| 18 | *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA | 
| 19 | * | 
| 20 | */ | 
| 21 | package org.jdtaus.core.container.ri.client; | 
| 22 |  | 
| 23 | import java.lang.ref.ReferenceQueue; | 
| 24 | import java.lang.ref.WeakReference; | 
| 25 | import java.util.AbstractCollection; | 
| 26 | import java.util.AbstractSet; | 
| 27 | import java.util.Collection; | 
| 28 | import java.util.Collections; | 
| 29 | import java.util.ConcurrentModificationException; | 
| 30 | import java.util.HashMap; | 
| 31 | import java.util.IdentityHashMap; | 
| 32 | import java.util.Iterator; | 
| 33 | import java.util.Map; | 
| 34 | import java.util.NoSuchElementException; | 
| 35 | import java.util.Set; | 
| 36 | import java.util.WeakHashMap; | 
| 37 |  | 
| 38 | /** | 
| 39 | * Hash-table based {@code Map} implementation with weak keys, using | 
| 40 | * object-identity in place of object-equality when comparing keys. | 
| 41 | * | 
| 42 | * <p>In a {@code WeakIdentityHashMap} two keys {@code k1} and {@code k2} are | 
| 43 | * considered equal if and only if {@code k1==k2} evaluates to {@code true}. | 
| 44 | * An entry will automatically be removed when its key is no longer in ordinary | 
| 45 | * use. More precisely, the presence of a mapping for a given key will not | 
| 46 | * prevent the key from being discarded by the garbage collector, that is, made | 
| 47 | * finalizable, finalized, and then reclaimed. When a key has been discarded its | 
| 48 | * entry is effectively removed from the map, so this class behaves somewhat | 
| 49 | * differently from other {@code Map} implementations and is not a | 
| 50 | * general-purpose {@code Map} implementation! Although it implements the | 
| 51 | * {@code Map} interface, it intentionally violates {@code Map}'s general | 
| 52 | * contract, which mandates the use of the {@code equals} method when comparing | 
| 53 | * objects.</p> | 
| 54 | * | 
| 55 | * <p>This class has performance characteristics similar to those of the | 
| 56 | * {@code HashMap} class, and has the same efficiency parameters of | 
| 57 | * {@code initialCapacity} and {@code loadFactor}. All of the optional map | 
| 58 | * operations are provided. It does not support {@code null} values and the | 
| 59 | * {@code null} key. All methods taking a key or value will throw a | 
| 60 | * {@code NullPointerException} when given a {@code null} reference. No | 
| 61 | * guarantees are made as to the order of the map; in particular, there is no | 
| 62 | * guarantee that the order will remain constant over time. Like most collection | 
| 63 | * classes, this class is not synchronized. A synchronized | 
| 64 | * {@code WeakIdentityHashMap} may be constructed using the | 
| 65 | * {@code Collections.synchronizedMap} method.</p> | 
| 66 | * | 
| 67 | * <p>The behavior of the {@code WeakIdentityHashMap} class depends in part upon | 
| 68 | * the actions of the garbage collector, so several {@code Map} invariants do | 
| 69 | * not hold for this class. Because the garbage collector may discard keys at | 
| 70 | * any time, a {@code WeakIdentityHashMap} may behave as though an unknown | 
| 71 | * thread is silently removing entries. In particular, even if synchronizing on | 
| 72 | * a {@code WeakIdentityHashMap} instance and invoking none of its mutator | 
| 73 | * methods, it is possible for the {@code size} method to return smaller values | 
| 74 | * over time, for the {@code isEmpty} method to return {@code false} and then | 
| 75 | * {@code true}, for the {@code containsKey} method to return {@code true} and | 
| 76 | * later {@code false} for a given key, for the {@code get} method to return a | 
| 77 | * value for a given key but later return {@code null}, for the {@code put} | 
| 78 | * method to return {@code null} and the {@code remove} method to return | 
| 79 | * {@code false} for a key that previously appeared to be in the map, and | 
| 80 | * for successive examinations of the key set, the value collection, and | 
| 81 | * the entry set to yield successively smaller numbers of elements. To protect | 
| 82 | * against such situations all {@code Iterator}s and {@code Entry}s created by | 
| 83 | * this class throw an {@code IllegalStateException} when a key becomes | 
| 84 | * {@code null} or a mapping got removed.</p> | 
| 85 | * | 
| 86 | * <p>The iterators returned by the {@code iterator} method of the collections | 
| 87 | * returned by all of this class's "collection view methods" are | 
| 88 | * fail-fast: if the map is structurally modified at any time after the iterator | 
| 89 | * is created, in any way except through the iterator's own {@code remove} | 
| 90 | * method, the iterator will throw a {@code ConcurrentModificationException}. | 
| 91 | * Note that the fail-fast behavior of an iterator cannot be guaranteed as it | 
| 92 | * is, generally speaking, impossible to make any hard guarantees in the | 
| 93 | * presence of unsynchronized concurrent modification. Fail-fast iterators throw | 
| 94 | * {@code ConcurrentModificationException}s on a best-effort basis. Therefore, | 
| 95 | * it would be wrong to write a program that depends on this exception for its | 
| 96 | * correctness: <i>the fail-fast behavior of iterators should be used only to | 
| 97 | * detect bugs.</i></p> | 
| 98 | * | 
| 99 | * @author <a href="mailto:cs@schulte.it">Christian Schulte</a> | 
| 100 | * @version $JDTAUS: WeakIdentityHashMap.java 8853 2014-01-10 13:50:00Z schulte $ | 
| 101 | * | 
| 102 | * @see HashMap | 
| 103 | * @see WeakHashMap | 
| 104 | * @see IdentityHashMap | 
| 105 | * @see Collections#synchronizedMap | 
| 106 | */ | 
| 107 | public final class WeakIdentityHashMap implements Map | 
| 108 | { | 
| 109 |  | 
| 110 | /** Maximum capacity of the hash-table backing the implementation (2^30). */ | 
| 111 | private static final int MAXIMUM_CAPACITY = 0x40000000; | 
| 112 |  | 
| 113 | /** Default initial capacity (2^4). */ | 
| 114 | private static final int DEFAULT_INITIAL_CAPACITY = 16; | 
| 115 |  | 
| 116 | /** Default load factor (3/4). */ | 
| 117 | private static final float DEFAULT_LOAD_FACTOR = 0.75f; | 
| 118 |  | 
| 119 | /** The number of times the map got structurally modified. */ | 
| 120 | private int modifications; | 
| 121 |  | 
| 122 | /** The number of mappings held by the map. */ | 
| 123 | private int size; | 
| 124 |  | 
| 125 | /** The size value at which the hash table needs resizing. */ | 
| 126 | private int resizeThreshold; | 
| 127 |  | 
| 128 | /** The hash-table backing the map. */ | 
| 129 | private Entry[] hashTable; | 
| 130 |  | 
| 131 | /** Queue, to which weak keys are appended. */ | 
| 132 | private final ReferenceQueue referenceQueue = new ReferenceQueue(); | 
| 133 |  | 
| 134 | /** The key set view of the map. */ | 
| 135 | private transient Set keySet; | 
| 136 |  | 
| 137 | /** The entry set view of the map. */ | 
| 138 | private transient Set entrySet; | 
| 139 |  | 
| 140 | /** The value collection view of the map. */ | 
| 141 | private transient Collection valueCollection; | 
| 142 |  | 
| 143 | /** The initial capacity of the hash table. */ | 
| 144 | private final int initialCapacity; | 
| 145 |  | 
| 146 | /** The load factor for the hash table. */ | 
| 147 | private final float loadFactor; | 
| 148 |  | 
| 149 | /** Null value returned by method {@link #getEntry(Object)}. */ | 
| 150 | private final Entry NULL_ENTRY = new Entry( null, null, 0 ); | 
| 151 |  | 
| 152 | /** | 
| 153 | * Constructs a new, empty {@code WeakIdentityHashMap} with the default | 
| 154 | * initial capacity ({@code 16}) and load factor ({@code 0.75}). | 
| 155 | */ | 
| 156 | public WeakIdentityHashMap() | 
| 157 | { | 
| 158 | this( DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR ); | 
| 159 | } | 
| 160 |  | 
| 161 | /** | 
| 162 | * Constructs a new, empty {@code WeakIdentityHashMap} with the given | 
| 163 | * initial capacity and the default load factor ({@code 0.75}). | 
| 164 | * | 
| 165 | * @param  initialCapacity The initial capacity of the | 
| 166 | * {@code WeakIdentityHashMap}. | 
| 167 | * | 
| 168 | * @throws IllegalArgumentException if {@code initialCapacity} is negative | 
| 169 | * or greater than the maximum supported capacity ({@code 2^30}). | 
| 170 | */ | 
| 171 | public WeakIdentityHashMap( final int initialCapacity ) | 
| 172 | { | 
| 173 | this( initialCapacity, DEFAULT_LOAD_FACTOR ); | 
| 174 | } | 
| 175 |  | 
| 176 | /** | 
| 177 | * Constructs a new, empty {@code WeakIdentityHashMap} with the default | 
| 178 | * initial capacity ({@code 16}) and the given load factor. | 
| 179 | * | 
| 180 | * @param loadFactor The load factor of the {@code WeakIdentityHashMap}. | 
| 181 | * | 
| 182 | * @throws IllegalArgumentException if {@code loadFactor} is nonpositive. | 
| 183 | */ | 
| 184 | public WeakIdentityHashMap( final float loadFactor ) | 
| 185 | { | 
| 186 | this( DEFAULT_INITIAL_CAPACITY, loadFactor ); | 
| 187 | } | 
| 188 |  | 
| 189 | /** | 
| 190 | * Constructs a new, empty {@code WeakIdentityHashMap} with the given | 
| 191 | * initial capacity and the given load factor. | 
| 192 | * | 
| 193 | * @param initialCapacity The initial capacity of the | 
| 194 | * {@code WeakIdentityHashMap}. | 
| 195 | * @param loadFactor The load factor of the {@code WeakIdentityHashMap}. | 
| 196 | * | 
| 197 | * @throws IllegalArgumentException if {@code initialCapacity} is negative | 
| 198 | * or greater than the maximum supported capacity ({@code 2^30}), or if | 
| 199 | * {@code loadFactor} is nonpositive. | 
| 200 | */ | 
| 201 | public WeakIdentityHashMap( final int initialCapacity, | 
| 202 | final float loadFactor ) | 
| 203 | { | 
| 204 | if ( initialCapacity < 0 || initialCapacity > MAXIMUM_CAPACITY ) | 
| 205 | { | 
| 206 | throw new IllegalArgumentException( | 
| 207 | Integer.toString( initialCapacity ) ); | 
| 208 |  | 
| 209 | } | 
| 210 | if ( loadFactor <= 0 || Float.isNaN( loadFactor ) ) | 
| 211 | { | 
| 212 | throw new IllegalArgumentException( Float.toString( loadFactor ) ); | 
| 213 | } | 
| 214 |  | 
| 215 | this.initialCapacity = initialCapacity; | 
| 216 | this.loadFactor = loadFactor; | 
| 217 | this.resizeThreshold = initialCapacity; | 
| 218 | this.size = 0; | 
| 219 | this.hashTable = new Entry[ initialCapacity ]; | 
| 220 | } | 
| 221 |  | 
| 222 | /** | 
| 223 | * Gets the number of key-value mappings in this map. | 
| 224 | * <p>If the map contains more than {@code Integer.MAX_VALUE} elements, | 
| 225 | * returns {@code Integer.MAX_VALUE}.</p> | 
| 226 | * | 
| 227 | * @return The number of key-value mappings in this map. | 
| 228 | */ | 
| 229 | public int size() | 
| 230 | { | 
| 231 | if ( this.size > 0 ) | 
| 232 | { | 
| 233 | this.purge(); | 
| 234 | } | 
| 235 |  | 
| 236 | return this.size; | 
| 237 | } | 
| 238 |  | 
| 239 | /** | 
| 240 | * Gets a flag indicating if this map is empty. | 
| 241 | * | 
| 242 | * @return {@code true} if this map contains no key-value mappings; | 
| 243 | * {@code false} if this map contains at least one mapping. | 
| 244 | */ | 
| 245 | public boolean isEmpty() | 
| 246 | { | 
| 247 | return this.size() == 0; | 
| 248 | } | 
| 249 |  | 
| 250 | /** | 
| 251 | * Gets a flag indicating if this map contains a mapping for a given key. | 
| 252 | * <p>More formally, returns {@code true} if and only if this map contains a | 
| 253 | * mapping for a key {@code k} such that {@code key==k}. There can be | 
| 254 | * at most one such mapping.</p> | 
| 255 | * | 
| 256 | * @param key The key whose presence in this map is to be tested. | 
| 257 | * | 
| 258 | * @return {@code true} if this map contains a mapping for {@code key}; | 
| 259 | * {@code false} if this map does not contain a mapping for {@code key}. | 
| 260 | * | 
| 261 | * @throws NullPointerException if {@code key} is {@code null}. | 
| 262 | */ | 
| 263 | public boolean containsKey( final Object key ) | 
| 264 | { | 
| 265 | if ( key == null ) | 
| 266 | { | 
| 267 | throw new NullPointerException( "key" ); | 
| 268 | } | 
| 269 |  | 
| 270 | return this.getEntry( key ).value != null; | 
| 271 | } | 
| 272 |  | 
| 273 | /** | 
| 274 | * Gets a flag indicating if this map maps one or more keys to the specified | 
| 275 | * value. | 
| 276 | * <p>More formally, this method returns {@code true} if and only if this | 
| 277 | * map contains at least one mapping to a value {@code v} such that | 
| 278 | * {@code value.equals(v)}. This operation requires time linear in the | 
| 279 | * map size.</p> | 
| 280 | * | 
| 281 | * @param value The value whose presence in this map is to be tested. | 
| 282 | * | 
| 283 | * @return {@code true} if this map maps one or more keys to {@code value}; | 
| 284 | * {@code false} if this map does not map any key to {@code value}. | 
| 285 | * | 
| 286 | * @throws NullPointerException if {@code value} is {@code null}. | 
| 287 | */ | 
| 288 | public boolean containsValue( final Object value ) | 
| 289 | { | 
| 290 | if ( value == null ) | 
| 291 | { | 
| 292 | throw new NullPointerException( "value" ); | 
| 293 | } | 
| 294 |  | 
| 295 | final Entry[] table = this.getHashTable(); | 
| 296 |  | 
| 297 | for ( int i = table.length - 1; i >= 0; i-- ) | 
| 298 | { | 
| 299 | for ( Entry e = table[i]; e != null; e = e.next ) | 
| 300 | { | 
| 301 | if ( value.equals( e.value ) ) | 
| 302 | { | 
| 303 | return true; | 
| 304 | } | 
| 305 | } | 
| 306 | } | 
| 307 |  | 
| 308 | return false; | 
| 309 | } | 
| 310 |  | 
| 311 | /** | 
| 312 | * Gets the value to which a given key is mapped, or {@code null} if | 
| 313 | * this map contains no mapping for that key. | 
| 314 | * <p>More formally, if this map contains a mapping from a key | 
| 315 | * {@code k} to a value {@code v} such that {@code key==k}, then this | 
| 316 | * method returns {@code v}; otherwise it returns {@code null}. There can | 
| 317 | * be at most one such mapping.</p> | 
| 318 | * | 
| 319 | * @param key The key whose associated value is to be returned. | 
| 320 | * | 
| 321 | * @return the value to which {@code key} is mapped, or {@code null} if this | 
| 322 | * map contains no mapping for {@code key}. | 
| 323 | * | 
| 324 | * @throws NullPointerException if {@code key} is {@code null}. | 
| 325 | */ | 
| 326 | public Object get( final Object key ) | 
| 327 | { | 
| 328 | if ( key == null ) | 
| 329 | { | 
| 330 | throw new NullPointerException( "key" ); | 
| 331 | } | 
| 332 |  | 
| 333 | return this.getEntry( key ).value; | 
| 334 | } | 
| 335 |  | 
| 336 | /** | 
| 337 | * Associates a given value with a given key in this map. | 
| 338 | * <p>If the map previously contained a mapping for that key, the old value | 
| 339 | * is replaced by the given value.</p> | 
| 340 | * | 
| 341 | * @param key The key with which {@code value} is to be associated. | 
| 342 | * @param value The value to be associated with {@code key}. | 
| 343 | * | 
| 344 | * @return the value previously associated with {@code key}, or {@code null} | 
| 345 | * if there was no mapping for {@code key}. | 
| 346 | * | 
| 347 | * @throws NullPointerException if {@code key} or {@code value} is | 
| 348 | * {@code null}. | 
| 349 | */ | 
| 350 | public Object put( final Object key, final Object value ) | 
| 351 | { | 
| 352 | if ( key == null ) | 
| 353 | { | 
| 354 | throw new NullPointerException( "key" ); | 
| 355 | } | 
| 356 | if ( value == null ) | 
| 357 | { | 
| 358 | throw new NullPointerException( "value" ); | 
| 359 | } | 
| 360 |  | 
| 361 | final int hashCode = getHashCode( key ); | 
| 362 | final Entry[] table = this.getHashTable(); | 
| 363 | final int index = getHashTableIndex( hashCode, table.length ); | 
| 364 |  | 
| 365 | for ( Entry e = table[index]; e != null; e = e.next ) | 
| 366 | { | 
| 367 | if ( e.hashCode == hashCode && e.get() == key ) | 
| 368 | { | 
| 369 | final Object oldValue = e.value; | 
| 370 | e.value = value; | 
| 371 | return oldValue; | 
| 372 | } | 
| 373 | } | 
| 374 |  | 
| 375 | final Entry entry = new Entry( key, value, hashCode ); | 
| 376 | entry.next = table[index]; | 
| 377 | table[index] = entry; | 
| 378 |  | 
| 379 | this.increaseSize(); | 
| 380 |  | 
| 381 | return null; | 
| 382 | } | 
| 383 |  | 
| 384 | /** | 
| 385 | * Removes the mapping for a given key from this map if it is present. | 
| 386 | * <p>More formally, if this map contains a mapping from key {@code k} to | 
| 387 | * value {@code v} such that {@code key==k}, that mapping is removed. | 
| 388 | * The map can contain at most one such mapping. The map will not contain a | 
| 389 | * mapping for the given key once the call returns.</p> | 
| 390 | * | 
| 391 | * @param key The key whose mapping is to be removed from the map. | 
| 392 | * | 
| 393 | * @return the value previously associated with {@code key}, or {@code null} | 
| 394 | * if there was no mapping for {@code key}. | 
| 395 | * | 
| 396 | * @throws NullPointerException if {@code key} is {@code null}. | 
| 397 | */ | 
| 398 | public Object remove( final Object key ) | 
| 399 | { | 
| 400 | if ( key == null ) | 
| 401 | { | 
| 402 | throw new NullPointerException( "key" ); | 
| 403 | } | 
| 404 |  | 
| 405 | final Entry[] table = this.getHashTable(); | 
| 406 | final int hashCode = getHashCode( key ); | 
| 407 | final int index = getHashTableIndex( hashCode, table.length ); | 
| 408 |  | 
| 409 | for ( Entry e = table[index], pre = null; e != null; | 
| 410 | pre = e, e = e.next ) | 
| 411 | { | 
| 412 | if ( e.hashCode == hashCode && e.get() == key ) | 
| 413 | { | 
| 414 | if ( pre != null ) | 
| 415 | { | 
| 416 | pre.next = e.next; | 
| 417 | } | 
| 418 | else | 
| 419 | { | 
| 420 | table[index] = e.next; | 
| 421 | } | 
| 422 |  | 
| 423 | this.decreaseSize(); | 
| 424 | this.modifications++; | 
| 425 |  | 
| 426 | final Object removed = e.value; | 
| 427 | e.removed = true; | 
| 428 | e.value = null; | 
| 429 | e.next = null; | 
| 430 | return removed; | 
| 431 | } | 
| 432 | } | 
| 433 |  | 
| 434 | return null; | 
| 435 | } | 
| 436 |  | 
| 437 | /** | 
| 438 | * Copies all of the mappings from a given map to this map. | 
| 439 | * <p>The effect of this call is equivalent to that of calling | 
| 440 | * {@link #put(Object,Object) put(k, v)} on this map once for each mapping | 
| 441 | * from key {@code k} to value {@code v} in the given map. The behavior of | 
| 442 | * this operation is undefined if the given map is modified while the | 
| 443 | * operation is in progress.</p> | 
| 444 | * | 
| 445 | * @param m The mappings to be stored in this map. | 
| 446 | * | 
| 447 | * @throws NullPointerException if {@code map} is {@code null}, or if | 
| 448 | * {@code map} contains {@code null} keys or values. | 
| 449 | */ | 
| 450 | public void putAll( final Map m ) | 
| 451 | { | 
| 452 | if ( m == null ) | 
| 453 | { | 
| 454 | throw new NullPointerException( "m" ); | 
| 455 | } | 
| 456 |  | 
| 457 | for ( final Iterator it = m.entrySet().iterator(); it.hasNext(); ) | 
| 458 | { | 
| 459 | final Map.Entry entry = (Map.Entry) it.next(); | 
| 460 | this.put( entry.getKey(), entry.getValue() ); | 
| 461 | } | 
| 462 | } | 
| 463 |  | 
| 464 | /** | 
| 465 | * Removes all of the mappings from this map so that the map will be empty | 
| 466 | * after this call returns. | 
| 467 | */ | 
| 468 | public void clear() | 
| 469 | { | 
| 470 | this.purge(); | 
| 471 | this.hashTable = new Entry[ this.initialCapacity ]; | 
| 472 | this.size = 0; | 
| 473 | this.resizeThreshold = this.initialCapacity; | 
| 474 | this.modifications++; | 
| 475 | while ( this.referenceQueue.poll() != null ); | 
| 476 | } | 
| 477 |  | 
| 478 | /** | 
| 479 | * Gets a {@code Set} view of the keys contained in this map. | 
| 480 | * <p>The set is backed by the map, so changes to the map are reflected in | 
| 481 | * the set, and vice-versa. If the map is modified while an iteration over | 
| 482 | * the set is in progress (except through the iterator's own {@code remove} | 
| 483 | * operation), the results of the iteration are undefined, that is, the | 
| 484 | * iterator may throw an {@code IllegalStateException}. The set supports | 
| 485 | * element removal, which removes the corresponding mapping from the map, | 
| 486 | * via the {@code Iterator.remove}, {@code Set.remove}, {@code removeAll}, | 
| 487 | * {@code retainAll}, and {@code clear} operations. It does not support the | 
| 488 | * {@code add} or {@code addAll} operations.</p> | 
| 489 | * | 
| 490 | * @return a set view of the keys contained in this map. | 
| 491 | */ | 
| 492 | public Set keySet() | 
| 493 | { | 
| 494 | if ( this.keySet == null ) | 
| 495 | { | 
| 496 | this.keySet = new AbstractSet() | 
| 497 | { | 
| 498 |  | 
| 499 | public Iterator iterator() | 
| 500 | { | 
| 501 | return new EntryIterator() | 
| 502 | { | 
| 503 |  | 
| 504 | public Object next() | 
| 505 | { | 
| 506 | return ( (Entry) super.next() ).getKey(); | 
| 507 | } | 
| 508 |  | 
| 509 | }; | 
| 510 | } | 
| 511 |  | 
| 512 | public int size() | 
| 513 | { | 
| 514 | return WeakIdentityHashMap.this.size(); | 
| 515 | } | 
| 516 |  | 
| 517 | }; | 
| 518 |  | 
| 519 | } | 
| 520 |  | 
| 521 | return this.keySet; | 
| 522 | } | 
| 523 |  | 
| 524 | /** | 
| 525 | * Gets a {@code Collection} view of the values contained in this map. | 
| 526 | * <p>The collection is backed by the map, so changes to the map are | 
| 527 | * reflected in the collection, and vice-versa. If the map is modified while | 
| 528 | * an iteration over the collection is in progress (except through the | 
| 529 | * iterator's own {@code remove} operation), the results of the iteration | 
| 530 | * are undefined, that is, the iterator may throw an | 
| 531 | * {@code IllegalStateException}. The collection supports element removal, | 
| 532 | * which removes the corresponding mapping from the map, via the | 
| 533 | * {@code Iterator.remove}, {@code Collection.remove}, {@code removeAll}, | 
| 534 | * {@code retainAll} and {@code clear} operations. It does not support the | 
| 535 | * {@code add} or {@code addAll} operations.</p> | 
| 536 | * | 
| 537 | * @return a collection view of the values contained in this map. | 
| 538 | */ | 
| 539 | public Collection values() | 
| 540 | { | 
| 541 | if ( this.valueCollection == null ) | 
| 542 | { | 
| 543 | this.valueCollection = new AbstractCollection() | 
| 544 | { | 
| 545 |  | 
| 546 | public Iterator iterator() | 
| 547 | { | 
| 548 | return new EntryIterator() | 
| 549 | { | 
| 550 |  | 
| 551 | public Object next() | 
| 552 | { | 
| 553 | return ( (Entry) super.next() ).getValue(); | 
| 554 | } | 
| 555 |  | 
| 556 | }; | 
| 557 | } | 
| 558 |  | 
| 559 | public int size() | 
| 560 | { | 
| 561 | return WeakIdentityHashMap.this.size(); | 
| 562 | } | 
| 563 |  | 
| 564 | }; | 
| 565 | } | 
| 566 |  | 
| 567 | return this.valueCollection; | 
| 568 | } | 
| 569 |  | 
| 570 | /** | 
| 571 | * Gets a {@code Set} view of the mappings contained in this map. | 
| 572 | * <p>The set is backed by the map, so changes to the map are reflected in | 
| 573 | * the set, and vice-versa. If the map is modified while an iteration over | 
| 574 | * the set is in progress (except through the iterator's own {@code remove} | 
| 575 | * operation, or through the {@code setValue} operation on a map entry | 
| 576 | * returned by the iterator) the results of the iteration are undefined, | 
| 577 | * that is, the iterator may throw an {@code IllegalStateException}. | 
| 578 | * The set supports element removal, which removes the corresponding mapping | 
| 579 | * from the map, via the {@code Iterator.remove}, {@code Set.remove}, | 
| 580 | * {@code removeAll}, {@code retainAll} and {@code clear} operations. It | 
| 581 | * does not support the {@code add} or {@code addAll} operations.</p> | 
| 582 | * | 
| 583 | * @return a set view of the mappings contained in this map. | 
| 584 | */ | 
| 585 | public Set entrySet() | 
| 586 | { | 
| 587 | if ( this.entrySet == null ) | 
| 588 | { | 
| 589 | this.entrySet = new AbstractSet() | 
| 590 | { | 
| 591 |  | 
| 592 | public Iterator iterator() | 
| 593 | { | 
| 594 | return new EntryIterator(); | 
| 595 | } | 
| 596 |  | 
| 597 | public int size() | 
| 598 | { | 
| 599 | return WeakIdentityHashMap.this.size(); | 
| 600 | } | 
| 601 |  | 
| 602 | }; | 
| 603 | } | 
| 604 |  | 
| 605 | return this.entrySet; | 
| 606 | } | 
| 607 |  | 
| 608 | /** | 
| 609 | * Returns a string representation of the object. | 
| 610 | * | 
| 611 | * @return a string representation of the object. | 
| 612 | */ | 
| 613 | public String toString() | 
| 614 | { | 
| 615 | return super.toString() + this.internalString(); | 
| 616 | } | 
| 617 |  | 
| 618 | /** | 
| 619 | * Compares the specified object with this map for equality. | 
| 620 | * <p>Returns {@code true} if the given object is also a map and the two | 
| 621 | * maps represent the same mappings. More formally, two maps {@code m1} and | 
| 622 | * {@code m2} represent the same mappings if | 
| 623 | * {@code m1.entrySet().equals(m2.entrySet())}.</p> | 
| 624 | * | 
| 625 | * @param o The object to be compared for equality with this map. | 
| 626 | * | 
| 627 | * @return {@code true} if {@code o} is equal to this map; {@code false} | 
| 628 | * if {@code o} is not equal to this map. | 
| 629 | */ | 
| 630 | public boolean equals( final Object o ) | 
| 631 | { | 
| 632 | boolean equal = this == o; | 
| 633 |  | 
| 634 | if ( !equal && o instanceof Map ) | 
| 635 | { | 
| 636 | final Map that = (Map) o; | 
| 637 | equal = this.entrySet().equals( that.entrySet() ); | 
| 638 | } | 
| 639 |  | 
| 640 | return equal; | 
| 641 | } | 
| 642 |  | 
| 643 | /** | 
| 644 | * Gets the hash code value for this map. | 
| 645 | * <p>The hash code of a map is defined to be the sum of the hash codes of | 
| 646 | * each entry in the map's {@code entrySet()} view.</p> | 
| 647 | * | 
| 648 | * @return the hash code value for this map. | 
| 649 | */ | 
| 650 | public int hashCode() | 
| 651 | { | 
| 652 | return this.entrySet().hashCode(); | 
| 653 | } | 
| 654 |  | 
| 655 | /** | 
| 656 | * Creates a string representing the mappings of the instance. | 
| 657 | * | 
| 658 | * @return a string representing the mappings of the instance. | 
| 659 | */ | 
| 660 | private String internalString() | 
| 661 | { | 
| 662 | final StringBuffer buf = | 
| 663 | new StringBuffer( 12 * this.size() ).append( '{' ); | 
| 664 |  | 
| 665 | final Entry[] table = this.getHashTable(); | 
| 666 | for ( int i = table.length - 1, index = 0; i >= 0; i-- ) | 
| 667 | { | 
| 668 | for ( Entry e = table[i]; e != null; e = e.next ) | 
| 669 | { | 
| 670 | if ( buf.length() > 1 ) | 
| 671 | { | 
| 672 | buf.append( ", " ); | 
| 673 | } | 
| 674 |  | 
| 675 | buf.append( '[' ).append( index++ ).append( "]=" ).append( e ); | 
| 676 | } | 
| 677 | } | 
| 678 |  | 
| 679 | return buf.append( '}' ).toString(); | 
| 680 | } | 
| 681 |  | 
| 682 | /** | 
| 683 | * Gets a hash-code value for a given key. | 
| 684 | * | 
| 685 | * @param key The key to get a hash-code value for. | 
| 686 | */ | 
| 687 | private static int getHashCode( final Object key ) | 
| 688 | { | 
| 689 | return System.identityHashCode( key ); | 
| 690 | } | 
| 691 |  | 
| 692 | /** | 
| 693 | * Gets the index of a hash code value. | 
| 694 | * | 
| 695 | * @param hashCode The hash code value to return the index of. | 
| 696 | * @param capacity The capacity to return an index for. | 
| 697 | * | 
| 698 | * @return the index of {@code hashCode} for {@code capacity}. | 
| 699 | */ | 
| 700 | private static int getHashTableIndex( final int hashCode, | 
| 701 | final int capacity ) | 
| 702 | { | 
| 703 | return hashCode & ( capacity - 1 ); | 
| 704 | } | 
| 705 |  | 
| 706 | /** | 
| 707 | * Gets the hash-table backing the instance. | 
| 708 | * <p>This method creates a new hash-table and re-hashes any mappings | 
| 709 | * whenever the size of the map gets greater than the capacity of the | 
| 710 | * internal hash-table times the load factor value.</p> | 
| 711 | * | 
| 712 | * @return the hash-table backing the instance. | 
| 713 | */ | 
| 714 | private Entry[] getHashTable() | 
| 715 | { | 
| 716 | if ( this.hashTable.length < this.resizeThreshold ) | 
| 717 | { | 
| 718 | final Entry[] table = new Entry[ this.calculateCapacity() ]; | 
| 719 |  | 
| 720 | for ( int i = this.hashTable.length - 1; i >= 0; i-- ) | 
| 721 | { | 
| 722 | Entry entry = this.hashTable[i]; | 
| 723 |  | 
| 724 | while ( entry != null ) | 
| 725 | { | 
| 726 | final Entry next = entry.next; | 
| 727 | final int index = | 
| 728 | getHashTableIndex( entry.hashCode, table.length ); | 
| 729 |  | 
| 730 | entry.next = table[index]; | 
| 731 | table[index] = entry; | 
| 732 | entry = next; | 
| 733 | } | 
| 734 | } | 
| 735 |  | 
| 736 | this.hashTable = table; | 
| 737 | this.modifications++; | 
| 738 | } | 
| 739 |  | 
| 740 | this.purge(); | 
| 741 | return this.hashTable; | 
| 742 | } | 
| 743 |  | 
| 744 | /** Removes any garbage collected entries. */ | 
| 745 | private void purge() | 
| 746 | { | 
| 747 | Entry purge; | 
| 748 |  | 
| 749 | while ( ( purge = (Entry) this.referenceQueue.poll() ) != null ) | 
| 750 | { | 
| 751 | final int index = | 
| 752 | getHashTableIndex( purge.hashCode, this.hashTable.length ); | 
| 753 |  | 
| 754 | for ( Entry e = this.hashTable[index], pre = null; e != null; | 
| 755 | pre = e, e = e.next ) | 
| 756 | { | 
| 757 | if ( e == purge ) | 
| 758 | { | 
| 759 | if ( pre != null ) | 
| 760 | { | 
| 761 | pre.next = purge.next; | 
| 762 | } | 
| 763 | else | 
| 764 | { | 
| 765 | this.hashTable[index] = purge.next; | 
| 766 | } | 
| 767 |  | 
| 768 | purge.removed = true; | 
| 769 | purge.next = null; | 
| 770 | purge.value = null; | 
| 771 |  | 
| 772 | this.decreaseSize(); | 
| 773 |  | 
| 774 | break; | 
| 775 | } | 
| 776 | } | 
| 777 | } | 
| 778 | } | 
| 779 |  | 
| 780 | private void increaseSize() | 
| 781 | { | 
| 782 | if ( this.size < Integer.MAX_VALUE ) | 
| 783 | { | 
| 784 | this.size++; | 
| 785 | this.resizeThreshold = (int) ( this.size * this.loadFactor ); | 
| 786 | } | 
| 787 |  | 
| 788 | this.modifications++; | 
| 789 | } | 
| 790 |  | 
| 791 | private void decreaseSize() | 
| 792 | { | 
| 793 | if ( this.size > 0 ) | 
| 794 | { | 
| 795 | this.size--; | 
| 796 | } | 
| 797 | } | 
| 798 |  | 
| 799 | private int calculateCapacity() | 
| 800 | { | 
| 801 | int maxCapacity = this.initialCapacity; | 
| 802 | if ( maxCapacity < this.resizeThreshold ) | 
| 803 | { | 
| 804 | maxCapacity = this.resizeThreshold > MAXIMUM_CAPACITY | 
| 805 | ? MAXIMUM_CAPACITY | 
| 806 | : this.resizeThreshold; | 
| 807 |  | 
| 808 | } | 
| 809 |  | 
| 810 | int capacity = 1; | 
| 811 | while ( capacity < maxCapacity ) | 
| 812 | { | 
| 813 | capacity <<= 1; | 
| 814 | } | 
| 815 |  | 
| 816 | return capacity; | 
| 817 | } | 
| 818 |  | 
| 819 | private Entry getEntry( final Object key ) | 
| 820 | { | 
| 821 | final int hashCode = getHashCode( key ); | 
| 822 | for ( Entry e = this.hashTable[getHashTableIndex( hashCode, | 
| 823 | this.hashTable.length )]; e != null; e = e.next ) | 
| 824 | { | 
| 825 | if ( e.hashCode == hashCode && e.get() == key ) | 
| 826 | { | 
| 827 | return e; | 
| 828 | } | 
| 829 | } | 
| 830 |  | 
| 831 | return NULL_ENTRY; | 
| 832 | } | 
| 833 |  | 
| 834 | /** | 
| 835 | * A map entry (key-value pair) with weakly referenced key. | 
| 836 | * <p>The {@code WeakIdentityHashMap.entrySet} method returns a | 
| 837 | * collection-view of the map, whose elements are of this class. | 
| 838 | * The only way to obtain a reference to a map entry is from the iterator of | 
| 839 | * this collection-view. These {@code Map.Entry} objects are valid only for | 
| 840 | * the duration of the iteration; more formally, the behavior of a map entry | 
| 841 | * is undefined if the backing map has been modified after the entry was | 
| 842 | * returned by the iterator, except through the {@code setValue} operation | 
| 843 | * on the map entry.</p> | 
| 844 | * | 
| 845 | * @see WeakIdentityHashMap#entrySet() | 
| 846 | */ | 
| 847 | private class Entry extends WeakReference implements Map.Entry | 
| 848 | { | 
| 849 |  | 
| 850 | /** The value of the entry. */ | 
| 851 | private Object value; | 
| 852 |  | 
| 853 | /** The next entry in the bucket. */ | 
| 854 | private Entry next; | 
| 855 |  | 
| 856 | /** Flag indicating that this entry got removed from the map. */ | 
| 857 | private boolean removed; | 
| 858 |  | 
| 859 | /** The hash code value of the key. */ | 
| 860 | private final int hashCode; | 
| 861 |  | 
| 862 | Entry( final Object key, final Object value, final int hashCode ) | 
| 863 | { | 
| 864 | super( key, WeakIdentityHashMap.this.referenceQueue ); | 
| 865 | this.hashCode = hashCode; | 
| 866 | this.value = value; | 
| 867 | } | 
| 868 |  | 
| 869 | /** | 
| 870 | * Gets the key corresponding to this entry. | 
| 871 | * | 
| 872 | * @return The key corresponding to this entry. | 
| 873 | * | 
| 874 | * @throws IllegalStateException if the entry got removed from the | 
| 875 | * backing map (either due to an iterator's {@code remove} operation or | 
| 876 | * due to the key having been garbage collected). | 
| 877 | */ | 
| 878 | public Object getKey() | 
| 879 | { | 
| 880 | final Object key = this.get(); | 
| 881 |  | 
| 882 | if ( key == null || this.removed ) | 
| 883 | { | 
| 884 | throw new IllegalStateException(); | 
| 885 | } | 
| 886 |  | 
| 887 | return key; | 
| 888 | } | 
| 889 |  | 
| 890 | /** | 
| 891 | * Gets the value corresponding to this entry. | 
| 892 | * | 
| 893 | * @return the value corresponding to this entry. | 
| 894 | * | 
| 895 | * @throws IllegalStateException if the entry got removed from the | 
| 896 | * backing map (either due to an iterator's {@code remove} operation or | 
| 897 | * due to the key having been garbage collected). | 
| 898 | */ | 
| 899 | public Object getValue() | 
| 900 | { | 
| 901 | if ( this.get() == null || this.removed ) | 
| 902 | { | 
| 903 | throw new IllegalStateException(); | 
| 904 | } | 
| 905 |  | 
| 906 | return this.value; | 
| 907 | } | 
| 908 |  | 
| 909 | /** | 
| 910 | * Replaces the value corresponding to this entry with the specified | 
| 911 | * value. | 
| 912 | * | 
| 913 | * @param value The new value to be stored in this entry. | 
| 914 | * | 
| 915 | * @return old value corresponding to the entry. | 
| 916 | * | 
| 917 | * @throws NullPointerException if {@code value} is {@code null}. | 
| 918 | * @throws IllegalStateException if the entry got removed from the | 
| 919 | * backing map (either due to an iterator's {@code remove} operation or | 
| 920 | * due to the key having been garbage collected). | 
| 921 | */ | 
| 922 | public Object setValue( final Object value ) | 
| 923 | { | 
| 924 | if ( value == null ) | 
| 925 | { | 
| 926 | throw new NullPointerException( "value" ); | 
| 927 | } | 
| 928 | if ( this.get() == null || this.removed ) | 
| 929 | { | 
| 930 | throw new IllegalStateException(); | 
| 931 | } | 
| 932 |  | 
| 933 | final Object oldValue = this.getValue(); | 
| 934 |  | 
| 935 | if ( value != oldValue && !value.equals( oldValue ) ) | 
| 936 | { | 
| 937 | this.value = value; | 
| 938 | } | 
| 939 |  | 
| 940 | return oldValue; | 
| 941 | } | 
| 942 |  | 
| 943 | /** | 
| 944 | * Returns a string representation of the object. | 
| 945 | * | 
| 946 | * @return a string representation of the object. | 
| 947 | */ | 
| 948 | public String toString() | 
| 949 | { | 
| 950 | return super.toString() + this.internalString(); | 
| 951 | } | 
| 952 |  | 
| 953 | /** | 
| 954 | * Compares a given object with this entry for equality. | 
| 955 | * <p>Returns {@code true} if the given object is also a map entry and | 
| 956 | * the two entries represent the same mapping. More formally, two | 
| 957 | * entries {@code e1} and {@code e2} represent the same mapping if | 
| 958 | * <pre><blockquote> | 
| 959 | * ( e1.getKey() == e2.getKey() )  && | 
| 960 | * ( e1.getValue().equals( e2.getValue() ) ) | 
| 961 | * </blockquote></pre></p> | 
| 962 | * | 
| 963 | * @param o The object to be compared for equality with this map entry. | 
| 964 | * | 
| 965 | * @return {@code true} if {@code o} is equal to this map entry; | 
| 966 | * {@code false} if {@code o} is not equal to this map entry. | 
| 967 | */ | 
| 968 | public boolean equals( final Object o ) | 
| 969 | { | 
| 970 | boolean equal = this == o; | 
| 971 |  | 
| 972 | if ( !equal && o instanceof Map.Entry ) | 
| 973 | { | 
| 974 | final Map.Entry that = (Map.Entry) o; | 
| 975 | equal = this.getKey() == that.getKey() && | 
| 976 | this.getValue().equals( that.getValue() ); | 
| 977 |  | 
| 978 | } | 
| 979 |  | 
| 980 | return equal; | 
| 981 | } | 
| 982 |  | 
| 983 | /** | 
| 984 | * Gets the hash code value for this map entry. | 
| 985 | * <p>The hash code of a map entry {@code e} is defined to be: | 
| 986 | * <pre><blockquote> | 
| 987 | * ( e.getKey() == null ? 0 : e.getKey().hashCode() ) ^ | 
| 988 | * ( e.getValue() == null ? 0 : e.getValue().hashCode() ) | 
| 989 | * </blockquote></pre></p> | 
| 990 | * | 
| 991 | * @return the hash code value for this map entry. | 
| 992 | */ | 
| 993 | public int hashCode() | 
| 994 | { | 
| 995 | return ( this.hashCode ) ^ ( this.getValue().hashCode() ); | 
| 996 | } | 
| 997 |  | 
| 998 | /** | 
| 999 | * Creates a string representing the properties of the instance. | 
| 1000 | * | 
| 1001 | * @return a string representing the properties of the instance. | 
| 1002 | */ | 
| 1003 | private String internalString() | 
| 1004 | { | 
| 1005 | final StringBuffer buf = new StringBuffer( 50 ).append( '{' ); | 
| 1006 | buf.append( "key=" ).append( this.getKey() ). | 
| 1007 | append( ", value=" ).append( this.getValue() ); | 
| 1008 |  | 
| 1009 | return buf.append( '}' ).toString(); | 
| 1010 | } | 
| 1011 |  | 
| 1012 | } | 
| 1013 |  | 
| 1014 | /** An iterator over the hash-table backing the implementation. */ | 
| 1015 | private class EntryIterator implements Iterator | 
| 1016 | { | 
| 1017 |  | 
| 1018 | /** The next element in the iteration. */ | 
| 1019 | private Entry next; | 
| 1020 |  | 
| 1021 | /** The current element in the iteration. */ | 
| 1022 | private Entry current; | 
| 1023 |  | 
| 1024 | /** The current index into the hash-table. */ | 
| 1025 | private int index; | 
| 1026 |  | 
| 1027 | /** The number of modifications when this iterator got created. */ | 
| 1028 | private int modifications; | 
| 1029 |  | 
| 1030 | /** Creates a new {@code EntryIterator} instance. */ | 
| 1031 | EntryIterator() | 
| 1032 | { | 
| 1033 | final Entry[] table = getHashTable(); | 
| 1034 | for ( this.index = table.length - 1; this.index >= 0; this.index-- ) | 
| 1035 | { | 
| 1036 | if ( table[this.index] != null ) | 
| 1037 | { | 
| 1038 | this.next = table[this.index--]; | 
| 1039 | break; | 
| 1040 | } | 
| 1041 | } | 
| 1042 |  | 
| 1043 | this.modifications = WeakIdentityHashMap.this.modifications; | 
| 1044 | } | 
| 1045 |  | 
| 1046 | /** | 
| 1047 | * Gets a flag indicating that the iteration has more elements. | 
| 1048 | * | 
| 1049 | * @return {@code true} if the iterator has more elements; {@code false} | 
| 1050 | * if the iterator does not have more elements. | 
| 1051 | */ | 
| 1052 | public boolean hasNext() | 
| 1053 | { | 
| 1054 | if ( this.modifications != WeakIdentityHashMap.this.modifications ) | 
| 1055 | { | 
| 1056 | throw new ConcurrentModificationException( | 
| 1057 | Thread.currentThread().getName() ); | 
| 1058 |  | 
| 1059 | } | 
| 1060 |  | 
| 1061 | return this.next != null; | 
| 1062 | } | 
| 1063 |  | 
| 1064 | /** | 
| 1065 | * Gets the next element in the iteration. | 
| 1066 | * | 
| 1067 | * @return the next element in the iteration. | 
| 1068 | * | 
| 1069 | * @throws NoSuchElementException if the iterator does not have more | 
| 1070 | * elements. | 
| 1071 | */ | 
| 1072 | public Object next() | 
| 1073 | { | 
| 1074 | if ( this.modifications != WeakIdentityHashMap.this.modifications ) | 
| 1075 | { | 
| 1076 | throw new ConcurrentModificationException( | 
| 1077 | Thread.currentThread().getName() ); | 
| 1078 |  | 
| 1079 | } | 
| 1080 | if ( this.next == null ) | 
| 1081 | { | 
| 1082 | throw new NoSuchElementException(); | 
| 1083 | } | 
| 1084 |  | 
| 1085 | this.current = this.next; | 
| 1086 |  | 
| 1087 | if ( this.next.next != null ) | 
| 1088 | { | 
| 1089 | this.next = this.next.next; | 
| 1090 | } | 
| 1091 | else | 
| 1092 | { | 
| 1093 | this.next = null; | 
| 1094 | final Entry[] table = getHashTable(); | 
| 1095 | for ( ; this.index >= 0; this.index-- ) | 
| 1096 | { | 
| 1097 | if ( table[this.index] != null ) | 
| 1098 | { | 
| 1099 | this.next = table[this.index--]; | 
| 1100 | break; | 
| 1101 | } | 
| 1102 | } | 
| 1103 | } | 
| 1104 |  | 
| 1105 | return this.current; | 
| 1106 | } | 
| 1107 |  | 
| 1108 | /** | 
| 1109 | * Removes from the underlying hash-table the last element returned by | 
| 1110 | * the iterator. | 
| 1111 | * | 
| 1112 | * @throws IllegalStateException if the {@code next} method has not yet | 
| 1113 | * been called, or the {@code remove} method has already been called | 
| 1114 | * after the last call to the {@code next} method. | 
| 1115 | */ | 
| 1116 | public void remove() | 
| 1117 | { | 
| 1118 | if ( this.modifications != WeakIdentityHashMap.this.modifications ) | 
| 1119 | { | 
| 1120 | throw new ConcurrentModificationException( | 
| 1121 | Thread.currentThread().getName() ); | 
| 1122 |  | 
| 1123 | } | 
| 1124 | if ( this.current == null ) | 
| 1125 | { | 
| 1126 | throw new IllegalStateException(); | 
| 1127 | } | 
| 1128 |  | 
| 1129 | final Object key = this.current.getKey(); | 
| 1130 |  | 
| 1131 | if ( key == null ) | 
| 1132 | { | 
| 1133 | throw new IllegalStateException(); | 
| 1134 | } | 
| 1135 |  | 
| 1136 | WeakIdentityHashMap.this.remove( key ); | 
| 1137 | this.modifications = WeakIdentityHashMap.this.modifications; | 
| 1138 | this.current = null; | 
| 1139 | } | 
| 1140 |  | 
| 1141 | } | 
| 1142 |  | 
| 1143 | } |