KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > ojb > broker > util > ReferenceMap


1 package org.apache.ojb.broker.util;
2
3 /* Copyright 2004-2005 The Apache Software Foundation
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */

17
18 import java.io.IOException JavaDoc;
19 import java.io.ObjectInputStream JavaDoc;
20 import java.io.ObjectOutputStream JavaDoc;
21 import java.lang.ref.Reference JavaDoc;
22 import java.lang.ref.ReferenceQueue JavaDoc;
23 import java.lang.ref.SoftReference JavaDoc;
24 import java.lang.ref.WeakReference JavaDoc;
25 import java.util.AbstractCollection JavaDoc;
26 import java.util.AbstractMap JavaDoc;
27 import java.util.AbstractSet JavaDoc;
28 import java.util.ArrayList JavaDoc;
29 import java.util.Arrays JavaDoc;
30 import java.util.Collection JavaDoc;
31 import java.util.ConcurrentModificationException JavaDoc;
32 import java.util.Iterator JavaDoc;
33 import java.util.Map JavaDoc;
34 import java.util.NoSuchElementException JavaDoc;
35 import java.util.Set JavaDoc;
36
37 /**
38  * <p>
39  * Modified version of {@link org.apache.commons.collections.ReferenceMap}, using
40  * object identity for key comparison (). This class
41  * simply extended {@link org.apache.commons.collections.ReferenceMap} with an extra field
42  * "useSystemIdentity" which is initialized in constructor and is used every time we
43  * want to compare (or hash) keys or values.
44  * </p>
45  * <p>
46  * Javadoc of ReferenceMap starts here:
47  * <br/>
48  * Hashtable-based {@link java.util.Map} implementation that allows
49  * mappings to be removed by the garbage collector.
50  * </p>
51  * <P>
52  * When you construct a <Code>ReferenceMap</Code>, you can
53  * specify what kind of references are used to store the
54  * map's keys and values. If non-hard references are
55  * used, then the garbage collector can remove mappings
56  * if a key or value becomes unreachable, or if the
57  * JVM's memory is running low. For information on how
58  * the different reference types behave, see
59  * {@link java.lang.ref.Reference}.<P>
60  *
61  * Different types of references can be specified for keys
62  * and values. The keys can be configured to be weak but
63  * the values hard, in which case this class will behave
64  * like a <A HREF="http://java.sun.com/j2se/1.4/docs/api/java/util/WeakHashMap.html">
65  * <Code>WeakHashMap</Code></A>. However, you
66  * can also specify hard keys and weak values, or any other
67  * combination. The default constructor uses hard keys
68  * and soft values, providing a memory-sensitive cache.<P>
69  *
70  * The algorithms used are basically the same as those
71  * in {@link java.util.HashMap}. In particular, you
72  * can specify a load factor and capacity to suit your
73  * needs. All optional {@link java.util.Map} operations are
74  * supported.<P>
75  *
76  * However, this {@link java.util.Map} implementation does <I>not</I>
77  * allow null elements. Attempting to add a null key or
78  * or a null value to the map will raise a
79  * <Code>NullPointerException</Code>.<P>
80  *
81  * As usual, this implementation is not synchronized. You
82  * can use {@link java.util.Collections#synchronizedMap} to
83  * provide synchronized access to a <Code>ReferenceMap</Code>.
84  *
85  * @deprecated use {@link org.apache.commons.collections.map.ReferenceIdentityMap} instead.
86  * @author Andy Malakov
87  * @version $Id: ReferenceMap.java,v 1.6.2.2 2005/12/21 22:27:47 tomdz Exp $
88  */

89 public class ReferenceMap extends AbstractMap JavaDoc
90 {
91
92     /**
93      * For serialization.
94      */

95     final private static long serialVersionUID = -3370601314380922368L;
96
97
98     /**
99      * Constant indicating that hard references should be used.
100      */

101     final public static int HARD = 0;
102
103
104     /**
105      * Constant indiciating that soft references should be used.
106      */

107     final public static int SOFT = 1;
108
109
110     /**
111      * Constant indicating that weak references should be used.
112      */

113     final public static int WEAK = 2;
114
115
116     // --- serialized instance variables:
117

118
119     /**
120      * The reference type for keys. Must be HARD, SOFT, WEAK.
121      * Note: I originally marked this field as final, but then this class
122      * didn't compile under JDK1.2.2.
123      * @serial
124      */

125     private int keyType;
126
127
128     /**
129      * The reference type for values. Must be HARD, SOFT, WEAK.
130      * Note: I originally marked this field as final, but then this class
131      * didn't compile under JDK1.2.2.
132      * @serial
133      */

134     private int valueType;
135
136
137     /**
138      * The threshold variable is calculated by multiplying
139      * table.length and loadFactor.
140      * Note: I originally marked this field as final, but then this class
141      * didn't compile under JDK1.2.2.
142      * @serial
143      */

144     private float loadFactor;
145
146
147     // -- Non-serialized instance variables
148

149     /**
150      * ReferenceQueue used to eliminate stale mappings.
151      * @see #purge
152      */

153     private transient ReferenceQueue JavaDoc queue = new ReferenceQueue JavaDoc();
154
155
156     /**
157      * The hash table. Its length is always a power of two.
158      */

159     private transient Entry[] table;
160
161
162     /**
163      * Number of mappings in this map.
164      */

165     private transient int size;
166
167
168     /**
169      * When size reaches threshold, the map is resized.
170      * @see #resize
171      */

172     private transient int threshold;
173
174
175     /**
176      * Number of times this map has been modified.
177      */

178     private transient volatile int modCount;
179
180
181     /**
182      * Cached key set. May be null if key set is never accessed.
183      */

184     private transient Set JavaDoc keySet;
185
186
187     /**
188      * Cached entry set. May be null if entry set is never accessed.
189      */

190     private transient Set JavaDoc entrySet;
191
192
193     /**
194      * Cached values. May be null if values() is never accessed.
195      */

196     private transient Collection JavaDoc values;
197
198     /**
199       * Note: I originally marked this field as final, but then this class
200       * didn't compile under JDK1.2.2.
201       */

202     private boolean useSystemIdentity;
203
204     /**
205      * Constructs a new <Code>ReferenceMap</Code> that will
206      * use hard references to keys and soft references to values.
207      */

208     public ReferenceMap()
209     {
210         this(HARD, SOFT);
211     }
212
213
214     /**
215      * Constructs a new <Code>ReferenceMap</Code> that will
216      * use the specified types of references.
217      *
218      * @param keyType the type of reference to use for keys;
219      * must be {@link #HARD}, {@link #SOFT}, {@link #WEAK}
220      * @param valueType the type of reference to use for values;
221      * must be {@link #HARD}, {@link #SOFT}, {@link #WEAK}
222      */

223     public ReferenceMap(int keyType, int valueType)
224     {
225         this(keyType, valueType, 16, 0.75f, false);
226     }
227
228
229     /**
230      * Constructs a new <Code>ReferenceMap</Code> with the
231      * specified reference types, load factor and initial
232      * capacity.
233      *
234      * @param keyType the type of reference to use for keys;
235      * must be {@link #HARD}, {@link #SOFT}, {@link #WEAK}
236      * @param valueType the type of reference to use for values;
237      * must be {@link #HARD}, {@link #SOFT}, {@link #WEAK}
238      * @param capacity the initial capacity for the map
239      * @param loadFactor the load factor for the map
240      * @param useSystemIdentity if true System.identityHashCode() and comparision operator (==) will be used
241      * in place of Object.hashCode and Object.equals (Please read {@link java.util.WeakHashMap}
242      * java doc for more information).
243      */

244     public ReferenceMap(int keyType, int valueType, int capacity, float loadFactor, boolean useSystemIdentity)
245     {
246         super();
247
248         verify("keyType", keyType);
249         verify("valueType", valueType);
250
251         this.useSystemIdentity = useSystemIdentity;
252
253         if (capacity <= 0)
254         {
255             throw new IllegalArgumentException JavaDoc("capacity must be positive");
256         }
257         if ((loadFactor <= 0.0f) || (loadFactor >= 1.0f))
258         {
259             throw new IllegalArgumentException JavaDoc("Load factor must be greater than 0 and less than 1.");
260         }
261
262         this.keyType = keyType;
263         this.valueType = valueType;
264
265         int v = 1;
266         while (v < capacity) v *= 2;
267
268         this.table = new Entry[v];
269         this.loadFactor = loadFactor;
270         this.threshold = (int) (v * loadFactor);
271     }
272
273
274     // used by constructor
275
private static void verify(String JavaDoc name, int type)
276     {
277         if ((type < HARD) || (type > WEAK))
278         {
279             throw new IllegalArgumentException JavaDoc(name +
280                     " must be HARD, SOFT, WEAK.");
281         }
282     }
283
284
285     /**
286      * Writes this object to the given output stream.
287      *
288      * @param out the output stream to write to
289      * @throws java.io.IOException if the stream raises it
290      */

291     private void writeObject(ObjectOutputStream JavaDoc out) throws IOException JavaDoc
292     {
293         out.defaultWriteObject();
294         out.writeInt(table.length);
295
296         // Have to use null-terminated list because size might shrink
297
// during iteration
298

299         for (Iterator JavaDoc iter = entrySet().iterator(); iter.hasNext();)
300         {
301             Map.Entry JavaDoc entry = (Map.Entry JavaDoc) iter.next();
302             out.writeObject(entry.getKey());
303             out.writeObject(entry.getValue());
304         }
305         out.writeObject(null);
306     }
307
308
309     /**
310      * Reads the contents of this object from the given input stream.
311      *
312      * @param inp the input stream to read from
313      * @throws java.io.IOException if the stream raises it
314      * @throws java.lang.ClassNotFoundException if the stream raises it
315      */

316     private void readObject(ObjectInputStream JavaDoc inp) throws IOException JavaDoc, ClassNotFoundException JavaDoc
317     {
318         inp.defaultReadObject();
319         table = new Entry[inp.readInt()];
320         threshold = (int) (table.length * loadFactor);
321         queue = new ReferenceQueue JavaDoc();
322         Object JavaDoc key = inp.readObject();
323         while (key != null)
324         {
325             Object JavaDoc value = inp.readObject();
326             put(key, value);
327             key = inp.readObject();
328         }
329     }
330
331
332     /**
333      * Constructs a reference of the given type to the given
334      * referent. The reference is registered with the queue
335      * for later purging.
336      *
337      * @param type HARD, SOFT or WEAK
338      * @param referent the object to refer to
339      * @param hash the hash code of the <I>key</I> of the mapping;
340      * this number might be different from referent.hashCode() if
341      * the referent represents a value and not a key
342      */

343     private Object JavaDoc toReference(int type, Object JavaDoc referent, int hash)
344     {
345         switch (type)
346         {
347             case HARD:
348                 return referent;
349             case SOFT:
350                 return new SoftRef(hash, referent, queue);
351             case WEAK:
352                 return new WeakRef(hash, referent, queue);
353             default:
354                 throw new Error JavaDoc();
355         }
356     }
357
358     /**
359      * Returns the entry associated with the given key.
360      *
361      * @param key the key of the entry to look up
362      * @return the entry associated with that key, or null
363      * if the key is not in this map
364      */

365     private Entry getEntry(Object JavaDoc key)
366     {
367         if (key == null) return null;
368         int hash = hashCode(key);
369         int index = indexFor(hash);
370         for (Entry entry = table[index]; entry != null; entry = entry.next)
371         {
372             if ((entry.hash == hash) && equals(key, entry.getKey()))
373             {
374                 return entry;
375             }
376         }
377         return null;
378     }
379
380
381     /**
382      * Converts the given hash code into an index into the
383      * hash table.
384      */

385     private int indexFor(int hash)
386     {
387         // mix the bits to avoid bucket collisions...
388
hash += ~(hash << 15);
389         hash ^= (hash >>> 10);
390         hash += (hash << 3);
391         hash ^= (hash >>> 6);
392         hash += ~(hash << 11);
393         hash ^= (hash >>> 16);
394         return hash & (table.length - 1);
395     }
396
397
398     /**
399      * Resizes this hash table by doubling its capacity.
400      * This is an expensive operation, as entries must
401      * be copied from the old smaller table to the new
402      * bigger table.
403      */

404     private void resize()
405     {
406         Entry[] old = table;
407         table = new Entry[old.length * 2];
408
409         for (int i = 0; i < old.length; i++)
410         {
411             Entry next = old[i];
412             while (next != null)
413             {
414                 Entry entry = next;
415                 next = next.next;
416                 int index = indexFor(entry.hash);
417                 entry.next = table[index];
418                 table[index] = entry;
419             }
420             old[i] = null;
421         }
422         threshold = (int) (table.length * loadFactor);
423     }
424
425
426     /**
427      * Purges stale mappings from this map.<P>
428      *
429      * Ordinarily, stale mappings are only removed during
430      * a write operation; typically a write operation will
431      * occur often enough that you'll never need to manually
432      * invoke this method.<P>
433      *
434      * Note that this method is not synchronized! Special
435      * care must be taken if, for instance, you want stale
436      * mappings to be removed on a periodic basis by some
437      * background thread.
438      */

439     private void purge()
440     {
441         Reference JavaDoc ref = queue.poll();
442         while (ref != null)
443         {
444             purge(ref);
445             ref = queue.poll();
446         }
447     }
448
449
450     private void purge(Reference JavaDoc ref)
451     {
452         // The hashCode of the reference is the hashCode of the
453
// mapping key, even if the reference refers to the
454
// mapping value...
455
int hash = ref.hashCode(); // note: hashCode() is referined
456
int index = indexFor(hash);
457         Entry previous = null;
458         Entry entry = table[index];
459         while (entry != null)
460         {
461             if (entry.purge(ref))
462             {
463                 if (previous == null)
464                     table[index] = entry.next;
465                 else
466                     previous.next = entry.next;
467                 this.size--;
468                 return;
469             }
470             previous = entry;
471             entry = entry.next;
472         }
473
474     }
475
476
477     /**
478      * Returns the size of this map.
479      *
480      * @return the size of this map
481      */

482     public int size()
483     {
484         purge();
485         return size;
486     }
487
488
489     /**
490      * Returns <Code>true</Code> if this map is empty.
491      *
492      * @return <Code>true</Code> if this map is empty
493      */

494     public boolean isEmpty()
495     {
496         purge();
497         return size == 0;
498     }
499
500
501     /**
502      * Returns <Code>true</Code> if this map contains the given key.
503      *
504      * @return true if the given key is in this map
505      */

506     public boolean containsKey(Object JavaDoc key)
507     {
508         purge();
509         Entry entry = getEntry(key);
510         if (entry == null) return false;
511         return entry.getValue() != null;
512     }
513
514
515     /**
516      * Returns the value associated with the given key, if any.
517      *
518      * @return the value associated with the given key, or <Code>null</Code>
519      * if the key maps to no value
520      */

521     public Object JavaDoc get(Object JavaDoc key)
522     {
523         purge();
524         Entry entry = getEntry(key);
525         if (entry == null) return null;
526         return entry.getValue();
527     }
528
529
530     /**
531      * Associates the given key with the given value.<P>
532      * Neither the key nor the value may be null.
533      *
534      * @param key the key of the mapping
535      * @param value the value of the mapping
536      * @return the last value associated with that key, or
537      * null if no value was associated with the key
538      * @throws java.lang.NullPointerException if either the key or value
539      * is null
540      */

541     public Object JavaDoc put(Object JavaDoc key, Object JavaDoc value)
542     {
543         if (key == null) throw new NullPointerException JavaDoc("null keys not allowed");
544         if (value == null) throw new NullPointerException JavaDoc("null values not allowed");
545
546         purge();
547         if (size + 1 > threshold) resize();
548
549         int hash = hashCode(key);
550         int index = indexFor(hash);
551         Entry entry = table[index];
552         while (entry != null)
553         {
554             if ((hash == entry.hash) && equals(key, entry.getKey()))
555             {
556                 Object JavaDoc result = entry.getValue();
557                 entry.setValue(value);
558                 return result;
559             }
560             entry = entry.next;
561         }
562         this.size++;
563         modCount++;
564         key = toReference(keyType, key, hash);
565         value = toReference(valueType, value, hash);
566         table[index] = new Entry(key, hash, value, table[index]);
567         return null;
568     }
569
570
571     /**
572      * Removes the key and its associated value from this map.
573      *
574      * @param key the key to remove
575      * @return the value associated with that key, or null if
576      * the key was not in the map
577      */

578     public Object JavaDoc remove(Object JavaDoc key)
579     {
580         if (key == null) return null;
581         purge();
582         int hash = hashCode(key);
583         int index = indexFor(hash);
584         Entry previous = null;
585         Entry entry = table[index];
586         while (entry != null)
587         {
588             if ((hash == entry.hash) && equals(key, entry.getKey()))
589             {
590                 if (previous == null)
591                     table[index] = entry.next;
592                 else
593                     previous.next = entry.next;
594                 this.size--;
595                 modCount++;
596                 return entry.getValue();
597             }
598             previous = entry;
599             entry = entry.next;
600         }
601         return null;
602     }
603
604
605     /**
606      * Clears this map.
607      */

608     public void clear()
609     {
610         Arrays.fill(table, null);
611         size = 0;
612         while (queue.poll() != null)
613         {
614             // drain the queue
615
}
616     }
617
618
619     /**
620      * Returns a set view of this map's entries.
621      *
622      * @return a set view of this map's entries
623      */

624     public Set JavaDoc entrySet()
625     {
626         if (entrySet != null) return entrySet;
627         entrySet = new AbstractSet JavaDoc()
628         {
629             public int size()
630             {
631                 return ReferenceMap.this.size();
632             }
633
634
635             public void clear()
636             {
637                 ReferenceMap.this.clear();
638             }
639
640
641             public boolean contains(Object JavaDoc o)
642             {
643                 if (o == null) return false;
644                 if (!(o instanceof Map.Entry JavaDoc)) return false;
645                 Map.Entry JavaDoc e = (Map.Entry JavaDoc) o;
646                 Entry e2 = getEntry(e.getKey());
647                 return (e2 != null) && e.equals(e2);
648             }
649
650
651             public boolean remove(Object JavaDoc o)
652             {
653                 boolean r = contains(o);
654                 if (r)
655                 {
656                     Map.Entry JavaDoc e = (Map.Entry JavaDoc) o;
657                     ReferenceMap.this.remove(e.getKey());
658                 }
659                 return r;
660             }
661
662
663             public Iterator JavaDoc iterator()
664             {
665                 return new EntryIterator();
666             }
667
668             public Object JavaDoc[] toArray()
669             {
670                 return toArray(new Object JavaDoc[0]);
671             }
672
673
674             public Object JavaDoc[] toArray(Object JavaDoc[] arr)
675             {
676                 ArrayList JavaDoc list = new ArrayList JavaDoc();
677                 Iterator JavaDoc iterator = iterator();
678                 while (iterator.hasNext())
679                 {
680                     Entry e = (Entry) iterator.next();
681                     list.add(new DefaultMapEntry(e.getKey(), e.getValue()));
682                 }
683                 return list.toArray(arr);
684             }
685         };
686         return entrySet;
687     }
688
689
690     /**
691      * Returns a set view of this map's keys.
692      *
693      * @return a set view of this map's keys
694      */

695     public Set JavaDoc keySet()
696     {
697         if (keySet != null) return keySet;
698         keySet = new AbstractSet JavaDoc()
699         {
700             public int size()
701             {
702                 return size;
703             }
704
705             public Iterator JavaDoc iterator()
706             {
707                 return new KeyIterator();
708             }
709
710             public boolean contains(Object JavaDoc o)
711             {
712                 return containsKey(o);
713             }
714
715
716             public boolean remove(Object JavaDoc o)
717             {
718                 Object JavaDoc r = ReferenceMap.this.remove(o);
719                 return r != null;
720             }
721
722             public void clear()
723             {
724                 ReferenceMap.this.clear();
725             }
726
727         };
728         return keySet;
729     }
730
731
732     /**
733      * Returns a collection view of this map's values.
734      *
735      * @return a collection view of this map's values.
736      */

737     public Collection JavaDoc values()
738     {
739         if (values != null) return values;
740         values = new AbstractCollection JavaDoc()
741         {
742             public int size()
743             {
744                 return size;
745             }
746
747             public void clear()
748             {
749                 ReferenceMap.this.clear();
750             }
751
752             public Iterator JavaDoc iterator()
753             {
754                 return new ValueIterator();
755             }
756         };
757         return values;
758     }
759
760
761     // If getKey() or getValue() returns null, it means
762
// the mapping is stale and should be removed.
763
private class Entry implements Map.Entry JavaDoc
764     {
765
766         Object JavaDoc key;
767         Object JavaDoc value;
768         int hash;
769         Entry next;
770
771
772         public Entry(Object JavaDoc key, int hash, Object JavaDoc value, Entry next)
773         {
774             this.key = key;
775             this.hash = hash;
776             this.value = value;
777             this.next = next;
778         }
779
780
781         public Object JavaDoc getKey()
782         {
783             return (keyType > HARD) ? ((Reference JavaDoc) key).get() : key;
784         }
785
786
787         public Object JavaDoc getValue()
788         {
789             return (valueType > HARD) ? ((Reference JavaDoc) value).get() : value;
790         }
791
792
793         public Object JavaDoc setValue(Object JavaDoc object)
794         {
795             Object JavaDoc old = getValue();
796             if (valueType > HARD) ((Reference JavaDoc) value).clear();
797             value = toReference(valueType, object, hash);
798             return old;
799         }
800
801
802         public boolean equals(Object JavaDoc o)
803         {
804             if (o == null) return false;
805             if (o == this) return true;
806             if (!(o instanceof Map.Entry JavaDoc)) return false;
807
808             Map.Entry JavaDoc entry = (Map.Entry JavaDoc) o;
809             Object JavaDoc _key = entry.getKey();
810             Object JavaDoc _value = entry.getValue();
811             if ((_key == null) || (_value == null)) return false;
812             return ReferenceMap.this.equals(_key, getKey()) &&
813                     ReferenceMap.this.equals(_value, getValue());
814         }
815
816
817         public int hashCode()
818         {
819             Object JavaDoc v = getValue();
820             return hash ^ ((v == null) ? 0 : v.hashCode());
821         }
822
823
824         public String JavaDoc toString()
825         {
826             return getKey() + "=" + getValue();
827         }
828
829
830         boolean purge(Reference JavaDoc ref)
831         {
832             boolean r = (keyType > HARD) && (key == ref);
833             r = r || ((valueType > HARD) && (value == ref));
834             if (r)
835             {
836                 if (keyType > HARD) ((Reference JavaDoc) key).clear();
837                 if (valueType > HARD) ((Reference JavaDoc) value).clear();
838             }
839             return r;
840         }
841     }
842
843
844     private class EntryIterator implements Iterator JavaDoc
845     {
846         // These fields keep track of where we are in the table.
847
int index;
848         Entry entry;
849         Entry previous;
850
851         // These Object fields provide hard references to the
852
// current and next entry; this assures that if hasNext()
853
// returns true, next() will actually return a valid element.
854
Object JavaDoc nextKey, nextValue;
855         Object JavaDoc currentKey, currentValue;
856
857         int expectedModCount;
858
859
860         public EntryIterator()
861         {
862             index = (size() != 0 ? table.length : 0);
863             // have to do this here! size() invocation above
864
// may have altered the modCount.
865
expectedModCount = modCount;
866         }
867
868
869         public boolean hasNext()
870         {
871             checkMod();
872             while (nextNull())
873             {
874                 Entry e = entry;
875                 int i = index;
876                 while ((e == null) && (i > 0))
877                 {
878                     i--;
879                     e = table[i];
880                 }
881                 entry = e;
882                 index = i;
883                 if (e == null)
884                 {
885                     currentKey = null;
886                     currentValue = null;
887                     return false;
888                 }
889                 nextKey = e.getKey();
890                 nextValue = e.getValue();
891                 if (nextNull()) entry = entry.next;
892             }
893             return true;
894         }
895
896
897         private void checkMod()
898         {
899             if (modCount != expectedModCount)
900             {
901                 throw new ConcurrentModificationException JavaDoc();
902             }
903         }
904
905
906         private boolean nextNull()
907         {
908             return (nextKey == null) || (nextValue == null);
909         }
910
911         protected Entry nextEntry()
912         {
913             checkMod();
914             if (nextNull() && !hasNext()) throw new NoSuchElementException JavaDoc();
915             previous = entry;
916             entry = entry.next;
917             currentKey = nextKey;
918             currentValue = nextValue;
919             nextKey = null;
920             nextValue = null;
921             return previous;
922         }
923
924
925         public Object JavaDoc next()
926         {
927             return nextEntry();
928         }
929
930
931         public void remove()
932         {
933             checkMod();
934             if (previous == null) throw new IllegalStateException JavaDoc();
935             ReferenceMap.this.remove(currentKey);
936             previous = null;
937             currentKey = null;
938             currentValue = null;
939             expectedModCount = modCount;
940         }
941
942     }
943
944
945     private class ValueIterator extends EntryIterator
946     {
947         public Object JavaDoc next()
948         {
949             return nextEntry().getValue();
950         }
951     }
952
953
954     private class KeyIterator extends EntryIterator
955     {
956         public Object JavaDoc next()
957         {
958             return nextEntry().getKey();
959         }
960     }
961
962
963
964     // These two classes store the hashCode of the key of
965
// of the mapping, so that after they're dequeued a quick
966
// lookup of the bucket in the table can occur.
967

968
969     private static class SoftRef extends SoftReference JavaDoc
970     {
971         private int hash;
972
973
974         public SoftRef(int hash, Object JavaDoc r, ReferenceQueue JavaDoc q)
975         {
976             super(r, q);
977             this.hash = hash;
978         }
979
980
981         public int hashCode()
982         {
983             return hash;
984         }
985     }
986
987
988     private static class WeakRef extends WeakReference JavaDoc
989     {
990         private int hash;
991
992
993         public WeakRef(int hash, Object JavaDoc r, ReferenceQueue JavaDoc q)
994         {
995             super(r, q);
996             this.hash = hash;
997         }
998
999
1000        public int hashCode()
1001        {
1002            return hash;
1003        }
1004    }
1005
1006    private int hashCode(Object JavaDoc obj)
1007    {
1008        return useSystemIdentity ? System.identityHashCode(obj) : obj.hashCode(); // null keys|values are not supported
1009
}
1010
1011    private boolean equals(Object JavaDoc obj1, Object JavaDoc obj2)
1012    {
1013        return (obj1 == obj2) ||
1014                (!useSystemIdentity && obj1.equals(obj2)); // null keys|values are not supported
1015
}
1016
1017    //***********************************************
1018
// inner class
1019
//***********************************************
1020
/** A default implementation of {@link java.util.Map.Entry}
1021      *
1022      * @since 1.0
1023      * @author <a HREF="mailto:jstrachan@apache.org">James Strachan</a>
1024      * @author <a HREF="mailto:mas@apache.org">Michael A. Smith</a>
1025      */

1026
1027    public class DefaultMapEntry implements Map.Entry JavaDoc {
1028
1029        private Object JavaDoc key;
1030        private Object JavaDoc value;
1031
1032        /**
1033         * Constructs a new <Code>DefaultMapEntry</Code> with a null key
1034         * and null value.
1035         */

1036        public DefaultMapEntry() {
1037        }
1038
1039        /**
1040         * Constructs a new <Code>DefaultMapEntry</Code> with the given
1041         * key and given value.
1042         *
1043         * @param key the key for the entry, may be null
1044         * @param value the value for the entyr, may be null
1045         */

1046        public DefaultMapEntry(Object JavaDoc key, Object JavaDoc value) {
1047            this.key = key;
1048            this.value = value;
1049        }
1050
1051        /**
1052         * Implemented per API documentation of
1053         * {@link java.util.Map.Entry#equals(java.lang.Object)}
1054         **/

1055        public boolean equals(Object JavaDoc o) {
1056            if( o == null ) return false;
1057            if( o == this ) return true;
1058
1059            if ( ! (o instanceof Map.Entry JavaDoc ) )
1060                return false;
1061            Map.Entry JavaDoc e2 = (Map.Entry JavaDoc)o;
1062            return ((getKey() == null ?
1063                     e2.getKey() == null : getKey().equals(e2.getKey())) &&
1064                    (getValue() == null ?
1065                     e2.getValue() == null : getValue().equals(e2.getValue())));
1066        }
1067
1068
1069        /**
1070         * Implemented per API documentation of
1071         * {@link java.util.Map.Entry#hashCode()}
1072         **/

1073        public int hashCode() {
1074            return ( ( getKey() == null ? 0 : getKey().hashCode() ) ^
1075                     ( getValue() == null ? 0 : getValue().hashCode() ) );
1076        }
1077
1078
1079
1080        // Map.Entry interface
1081
//-------------------------------------------------------------------------
1082

1083        /**
1084         * Returns the key.
1085         *
1086         * @return the key
1087         */

1088        public Object JavaDoc getKey() {
1089            return key;
1090        }
1091
1092
1093        /**
1094         * Returns the value.
1095         *
1096         * @return the value
1097         */

1098        public Object JavaDoc getValue() {
1099            return value;
1100        }
1101
1102        // Properties
1103
//-------------------------------------------------------------------------
1104

1105        /**
1106         * Sets the key. This method does not modify any map.
1107         *
1108         * @param key the new key
1109         */

1110        public void setKey(Object JavaDoc key) {
1111            this.key = key;
1112        }
1113
1114        /** Note that this method only sets the local reference inside this object and
1115          * does not modify the original Map.
1116          *
1117          * @return the old value of the value
1118          * @param value the new value
1119          */

1120        public Object JavaDoc setValue(Object JavaDoc value) {
1121            Object JavaDoc answer = this.value;
1122            this.value = value;
1123            return answer;
1124        }
1125
1126    }
1127}
1128
Popular Tags