EMMA Coverage Report (generated Tue Jan 14 02:29:45 CET 2014)
[all classes][org.jdtaus.core.container.ri.client]

COVERAGE SUMMARY FOR SOURCE FILE [WeakIdentityHashMap.java]

nameclass, %method, %block, %line, %
WeakIdentityHashMap.java100% (8/8)87%  (46/53)68%  (758/1120)67%  (162.7/243)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class WeakIdentityHashMap$Entry100% (1/1)75%  (6/8)51%  (76/148)50%  (15/30)
equals (Object): boolean 0%   (0/1)0%   (0/32)0%   (0/5)
setValue (Object): Object 0%   (0/1)0%   (0/32)0%   (0/8)
getValue (): Object 100% (1/1)69%  (9/13)67%  (2/3)
getKey (): Object 100% (1/1)71%  (10/14)75%  (3/4)
WeakIdentityHashMap$Entry (WeakIdentityHashMap, Object, Object, int): void 100% (1/1)100% (15/15)100% (5/5)
hashCode (): int 100% (1/1)100% (7/7)100% (1/1)
internalString (): String 100% (1/1)100% (24/24)100% (3/3)
toString (): String 100% (1/1)100% (11/11)100% (1/1)
     
class WeakIdentityHashMap$2$1100% (1/1)50%  (1/2)62%  (8/13)50%  (1/2)
next (): Object 0%   (0/1)0%   (0/5)0%   (0/1)
WeakIdentityHashMap$2$1 (WeakIdentityHashMap$2): void 100% (1/1)100% (8/8)100% (1/1)
     
class WeakIdentityHashMap100% (1/1)89%  (25/28)65%  (471/722)65%  (106.7/164)
WeakIdentityHashMap (float): void 0%   (0/1)0%   (0/5)0%   (0/2)
calculateCapacity (): int 0%   (0/1)0%   (0/28)0%   (0/7)
putAll (Map): void 0%   (0/1)0%   (0/27)0%   (0/7)
purge (): void 100% (1/1)13%  (8/63)13%  (2/15)
getHashTable (): WeakIdentityHashMap$Entry [] 100% (1/1)17%  (11/63)20%  (3/15)
put (Object, Object): Object 100% (1/1)58%  (43/74)64%  (10.8/17)
get (Object): Object 100% (1/1)58%  (7/12)67%  (2/3)
containsKey (Object): boolean 100% (1/1)69%  (11/16)67%  (2/3)
remove (Object): Object 100% (1/1)76%  (61/80)79%  (15/19)
WeakIdentityHashMap (int, float): void 100% (1/1)79%  (45/57)85%  (11/13)
containsValue (Object): boolean 100% (1/1)87%  (33/38)88%  (7/8)
getEntry (Object): WeakIdentityHashMap$Entry 100% (1/1)87%  (27/31)89%  (5.3/6)
equals (Object): boolean 100% (1/1)91%  (21/23)95%  (4.8/5)
clear (): void 100% (1/1)96%  (25/26)97%  (6.8/7)
WeakIdentityHashMap (): void 100% (1/1)100% (5/5)100% (2/2)
WeakIdentityHashMap (int): void 100% (1/1)100% (5/5)100% (2/2)
decreaseSize (): void 100% (1/1)100% (10/10)100% (3/3)
entrySet (): Set 100% (1/1)100% (12/12)100% (3/3)
getHashCode (Object): int 100% (1/1)100% (3/3)100% (1/1)
getHashTableIndex (int, int): int 100% (1/1)100% (6/6)100% (1/1)
hashCode (): int 100% (1/1)100% (4/4)100% (1/1)
increaseSize (): void 100% (1/1)100% (26/26)100% (5/5)
internalString (): String 100% (1/1)100% (58/58)100% (8/8)
isEmpty (): boolean 100% (1/1)100% (7/7)100% (1/1)
keySet (): Set 100% (1/1)100% (12/12)100% (3/3)
size (): int 100% (1/1)100% (8/8)100% (3/3)
toString (): String 100% (1/1)100% (11/11)100% (1/1)
values (): Collection 100% (1/1)100% (12/12)100% (3/3)
     
class WeakIdentityHashMap$2100% (1/1)67%  (2/3)73%  (11/15)67%  (2/3)
size (): int 0%   (0/1)0%   (0/4)0%   (0/1)
WeakIdentityHashMap$2 (WeakIdentityHashMap): void 100% (1/1)100% (6/6)100% (1/1)
iterator (): Iterator 100% (1/1)100% (5/5)100% (1/1)
     
class WeakIdentityHashMap$EntryIterator100% (1/1)100% (4/4)83%  (148/178)83%  (30/36)
remove (): void 100% (1/1)67%  (29/43)73%  (8/11)
next (): Object 100% (1/1)77%  (54/70)79%  (11/14)
WeakIdentityHashMap$EntryIterator (WeakIdentityHashMap): void 100% (1/1)100% (46/46)100% (8/8)
hasNext (): boolean 100% (1/1)100% (19/19)100% (3/3)
     
class WeakIdentityHashMap$1100% (1/1)100% (3/3)100% (15/15)100% (3/3)
WeakIdentityHashMap$1 (WeakIdentityHashMap): void 100% (1/1)100% (6/6)100% (1/1)
iterator (): Iterator 100% (1/1)100% (5/5)100% (1/1)
size (): int 100% (1/1)100% (4/4)100% (1/1)
     
class WeakIdentityHashMap$1$1100% (1/1)100% (2/2)100% (13/13)100% (2/2)
WeakIdentityHashMap$1$1 (WeakIdentityHashMap$1): void 100% (1/1)100% (8/8)100% (1/1)
next (): Object 100% (1/1)100% (5/5)100% (1/1)
     
class WeakIdentityHashMap$3100% (1/1)100% (3/3)100% (16/16)100% (3/3)
WeakIdentityHashMap$3 (WeakIdentityHashMap): void 100% (1/1)100% (6/6)100% (1/1)
iterator (): Iterator 100% (1/1)100% (6/6)100% (1/1)
size (): int 100% (1/1)100% (4/4)100% (1/1)

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 */
21package org.jdtaus.core.container.ri.client;
22 
23import java.lang.ref.ReferenceQueue;
24import java.lang.ref.WeakReference;
25import java.util.AbstractCollection;
26import java.util.AbstractSet;
27import java.util.Collection;
28import java.util.Collections;
29import java.util.ConcurrentModificationException;
30import java.util.HashMap;
31import java.util.IdentityHashMap;
32import java.util.Iterator;
33import java.util.Map;
34import java.util.NoSuchElementException;
35import java.util.Set;
36import 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 */
107public 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() )  &amp;&amp;
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}

[all classes][org.jdtaus.core.container.ri.client]
EMMA 2.1.5320 (stable) (C) Vladimir Roubtsov