KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > commons > collections > map > AbstractReferenceMap


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

16 package org.apache.commons.collections.map;
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.ArrayList JavaDoc;
26 import java.util.Collection JavaDoc;
27 import java.util.ConcurrentModificationException JavaDoc;
28 import java.util.Iterator JavaDoc;
29 import java.util.List JavaDoc;
30 import java.util.Map JavaDoc;
31 import java.util.NoSuchElementException JavaDoc;
32 import java.util.Set JavaDoc;
33
34 import org.apache.commons.collections.MapIterator;
35 import org.apache.commons.collections.keyvalue.DefaultMapEntry;
36
37 /**
38  * An abstract implementation of a hash-based map that allows the entries to
39  * be removed by the garbage collector.
40  * <p>
41  * This class implements all the features necessary for a subclass reference
42  * hash-based map. Key-value entries are stored in instances of the
43  * <code>ReferenceEntry</code> class which can be overridden and replaced.
44  * The iterators can similarly be replaced, without the need to replace the KeySet,
45  * EntrySet and Values view classes.
46  * <p>
47  * Overridable methods are provided to change the default hashing behaviour, and
48  * to change how entries are added to and removed from the map. Hopefully, all you
49  * need for unusual subclasses is here.
50  * <p>
51  * When you construct an <code>AbstractReferenceMap</code>, you can specify what
52  * kind of references are used to store the map's keys and values.
53  * If non-hard references are used, then the garbage collector can remove
54  * mappings if a key or value becomes unreachable, or if the JVM's memory is
55  * running low. For information on how the different reference types behave,
56  * see {@link Reference}.
57  * <p>
58  * Different types of references can be specified for keys and values.
59  * The keys can be configured to be weak but the values hard,
60  * in which case this class will behave like a
61  * <a HREF="http://java.sun.com/j2se/1.4/docs/api/java/util/WeakHashMap.html">
62  * <code>WeakHashMap</code></a>. However, you can also specify hard keys and
63  * weak values, or any other combination. The default constructor uses
64  * hard keys and soft values, providing a memory-sensitive cache.
65  * <p>
66  * This {@link Map} implementation does <i>not</i> allow null elements.
67  * Attempting to add a null key or value to the map will raise a
68  * <code>NullPointerException</code>.
69  * <p>
70  * All the available iterators can be reset back to the start by casting to
71  * <code>ResettableIterator</code> and calling <code>reset()</code>.
72  * <p>
73  * This implementation is not synchronized.
74  * You can use {@link java.util.Collections#synchronizedMap} to
75  * provide synchronized access to a <code>ReferenceMap</code>.
76  *
77  * @see java.lang.ref.Reference
78  * @since Commons Collections 3.1 (extracted from ReferenceMap in 3.0)
79  * @version $Revision: 1.3 $ $Date: 2004/06/07 22:14:42 $
80  *
81  * @author Paul Jack
82  * @author Stephen Colebourne
83  */

84 public abstract class AbstractReferenceMap extends AbstractHashedMap {
85
86     /** Constant indicating that hard references should be used */
87     public static final int HARD = 0;
88
89     /** Constant indicating that soft references should be used */
90     public static final int SOFT = 1;
91
92     /** Constant indicating that weak references should be used */
93     public static final int WEAK = 2;
94
95     /**
96      * The reference type for keys. Must be HARD, SOFT, WEAK.
97      * @serial
98      */

99     protected int keyType;
100
101     /**
102      * The reference type for values. Must be HARD, SOFT, WEAK.
103      * @serial
104      */

105     protected int valueType;
106
107     /**
108      * Should the value be automatically purged when the associated key has been collected?
109      */

110     protected boolean purgeValues;
111
112     /**
113      * ReferenceQueue used to eliminate stale mappings.
114      * See purge.
115      */

116     private transient ReferenceQueue JavaDoc queue;
117
118     //-----------------------------------------------------------------------
119
/**
120      * Constructor used during deserialization.
121      */

122     protected AbstractReferenceMap() {
123         super();
124     }
125
126     /**
127      * Constructs a new empty map with the specified reference types,
128      * load factor and initial capacity.
129      *
130      * @param keyType the type of reference to use for keys;
131      * must be {@link #HARD}, {@link #SOFT}, {@link #WEAK}
132      * @param valueType the type of reference to use for values;
133      * must be {@link #HARD}, {@link #SOFT}, {@link #WEAK}
134      * @param capacity the initial capacity for the map
135      * @param loadFactor the load factor for the map
136      * @param purgeValues should the value be automatically purged when the
137      * key is garbage collected
138      */

139     protected AbstractReferenceMap(
140             int keyType, int valueType, int capacity,
141             float loadFactor, boolean purgeValues) {
142         super(capacity, loadFactor);
143         verify("keyType", keyType);
144         verify("valueType", valueType);
145         this.keyType = keyType;
146         this.valueType = valueType;
147         this.purgeValues = purgeValues;
148     }
149
150     /**
151      * Initialise this subclass during construction, cloning or deserialization.
152      */

153     protected void init() {
154         queue = new ReferenceQueue JavaDoc();
155     }
156
157     //-----------------------------------------------------------------------
158
/**
159      * Checks the type int is a valid value.
160      *
161      * @param name the name for error messages
162      * @param type the type value to check
163      * @throws IllegalArgumentException if the value if invalid
164      */

165     private static void verify(String JavaDoc name, int type) {
166         if ((type < HARD) || (type > WEAK)) {
167             throw new IllegalArgumentException JavaDoc(name + " must be HARD, SOFT, WEAK.");
168         }
169     }
170
171     //-----------------------------------------------------------------------
172
/**
173      * Gets the size of the map.
174      *
175      * @return the size
176      */

177     public int size() {
178         purgeBeforeRead();
179         return super.size();
180     }
181
182     /**
183      * Checks whether the map is currently empty.
184      *
185      * @return true if the map is currently size zero
186      */

187     public boolean isEmpty() {
188         purgeBeforeRead();
189         return super.isEmpty();
190     }
191
192     /**
193      * Checks whether the map contains the specified key.
194      *
195      * @param key the key to search for
196      * @return true if the map contains the key
197      */

198     public boolean containsKey(Object JavaDoc key) {
199         purgeBeforeRead();
200         Entry entry = getEntry(key);
201         if (entry == null) {
202             return false;
203         }
204         return (entry.getValue() != null);
205     }
206
207     /**
208      * Checks whether the map contains the specified value.
209      *
210      * @param value the value to search for
211      * @return true if the map contains the value
212      */

213     public boolean containsValue(Object JavaDoc value) {
214         purgeBeforeRead();
215         if (value == null) {
216             return false;
217         }
218         return super.containsValue(value);
219     }
220
221     /**
222      * Gets the value mapped to the key specified.
223      *
224      * @param key the key
225      * @return the mapped value, null if no match
226      */

227     public Object JavaDoc get(Object JavaDoc key) {
228         purgeBeforeRead();
229         Entry entry = getEntry(key);
230         if (entry == null) {
231             return null;
232         }
233         return entry.getValue();
234     }
235
236
237     /**
238      * Puts a key-value mapping into this map.
239      * Neither the key nor the value may be null.
240      *
241      * @param key the key to add, must not be null
242      * @param value the value to add, must not be null
243      * @return the value previously mapped to this key, null if none
244      * @throws NullPointerException if either the key or value is null
245      */

246     public Object JavaDoc put(Object JavaDoc key, Object JavaDoc value) {
247         if (key == null) {
248             throw new NullPointerException JavaDoc("null keys not allowed");
249         }
250         if (value == null) {
251             throw new NullPointerException JavaDoc("null values not allowed");
252         }
253
254         purgeBeforeWrite();
255         return super.put(key, value);
256     }
257     
258     /**
259      * Removes the specified mapping from this map.
260      *
261      * @param key the mapping to remove
262      * @return the value mapped to the removed key, null if key not in map
263      */

264     public Object JavaDoc remove(Object JavaDoc key) {
265         if (key == null) {
266             return null;
267         }
268         purgeBeforeWrite();
269         return super.remove(key);
270     }
271
272     /**
273      * Clears this map.
274      */

275     public void clear() {
276         super.clear();
277         while (queue.poll() != null) {} // drain the queue
278
}
279
280     //-----------------------------------------------------------------------
281
/**
282      * Gets a MapIterator over the reference map.
283      * The iterator only returns valid key/value pairs.
284      *
285      * @return a map iterator
286      */

287     public MapIterator mapIterator() {
288         return new ReferenceMapIterator(this);
289     }
290
291     /**
292      * Returns a set view of this map's entries.
293      * An iterator returned entry is valid until <code>next()</code> is called again.
294      * The <code>setValue()</code> method on the <code>toArray</code> entries has no effect.
295      *
296      * @return a set view of this map's entries
297      */

298     public Set JavaDoc entrySet() {
299         if (entrySet == null) {
300             entrySet = new ReferenceEntrySet(this);
301         }
302         return entrySet;
303     }
304
305     /**
306      * Returns a set view of this map's keys.
307      *
308      * @return a set view of this map's keys
309      */

310     public Set JavaDoc keySet() {
311         if (keySet == null) {
312             keySet = new ReferenceKeySet(this);
313         }
314         return keySet;
315     }
316
317     /**
318      * Returns a collection view of this map's values.
319      *
320      * @return a set view of this map's values
321      */

322     public Collection JavaDoc values() {
323         if (values == null) {
324             values = new ReferenceValues(this);
325         }
326         return values;
327     }
328
329     //-----------------------------------------------------------------------
330
/**
331      * Purges stale mappings from this map before read operations.
332      * <p>
333      * This implementation calls {@link #purge()} to maintain a consistent state.
334      */

335     protected void purgeBeforeRead() {
336         purge();
337     }
338
339     /**
340      * Purges stale mappings from this map before write operations.
341      * <p>
342      * This implementation calls {@link #purge()} to maintain a consistent state.
343      */

344     protected void purgeBeforeWrite() {
345         purge();
346     }
347
348     /**
349      * Purges stale mappings from this map.
350      * <p>
351      * Note that this method is not synchronized! Special
352      * care must be taken if, for instance, you want stale
353      * mappings to be removed on a periodic basis by some
354      * background thread.
355      */

356     protected void purge() {
357         Reference JavaDoc ref = queue.poll();
358         while (ref != null) {
359             purge(ref);
360             ref = queue.poll();
361         }
362     }
363
364     /**
365      * Purges the specified reference.
366      *
367      * @param ref the reference to purge
368      */

369     protected void purge(Reference JavaDoc ref) {
370         // The hashCode of the reference is the hashCode of the
371
// mapping key, even if the reference refers to the
372
// mapping value...
373
int hash = ref.hashCode();
374         int index = hashIndex(hash, data.length);
375         HashEntry previous = null;
376         HashEntry entry = data[index];
377         while (entry != null) {
378             if (((ReferenceEntry) entry).purge(ref)) {
379                 if (previous == null) {
380                     data[index] = entry.next;
381                 } else {
382                     previous.next = entry.next;
383                 }
384                 this.size--;
385                 return;
386             }
387             previous = entry;
388             entry = entry.next;
389         }
390
391     }
392
393     //-----------------------------------------------------------------------
394
/**
395      * Gets the entry mapped to the key specified.
396      *
397      * @param key the key
398      * @return the entry, null if no match
399      */

400     protected HashEntry getEntry(Object JavaDoc key) {
401         if (key == null) {
402             return null;
403         } else {
404             return super.getEntry(key);
405         }
406     }
407
408     /**
409      * Gets the hash code for a MapEntry.
410      * Subclasses can override this, for example to use the identityHashCode.
411      *
412      * @param key the key to get a hash code for, may be null
413      * @param value the value to get a hash code for, may be null
414      * @return the hash code, as per the MapEntry specification
415      */

416     protected int hashEntry(Object JavaDoc key, Object JavaDoc value) {
417         return (key == null ? 0 : key.hashCode()) ^
418                (value == null ? 0 : value.hashCode());
419     }
420     
421     /**
422      * Compares two keys, in internal converted form, to see if they are equal.
423      * <p>
424      * This implementation converts the key from the entry to a real reference
425      * before comparison.
426      *
427      * @param key1 the first key to compare passed in from outside
428      * @param key2 the second key extracted from the entry via <code>entry.key</code>
429      * @return true if equal
430      */

431     protected boolean isEqualKey(Object JavaDoc key1, Object JavaDoc key2) {
432         key2 = (keyType > HARD ? ((Reference JavaDoc) key2).get() : key2);
433         return (key1 == key2 || key1.equals(key2));
434     }
435     
436     /**
437      * Creates a ReferenceEntry instead of a HashEntry.
438      *
439      * @param next the next entry in sequence
440      * @param hashCode the hash code to use
441      * @param key the key to store
442      * @param value the value to store
443      * @return the newly created entry
444      */

445     protected HashEntry createEntry(HashEntry next, int hashCode, Object JavaDoc key, Object JavaDoc value) {
446         return new ReferenceEntry(this, next, hashCode, key, value);
447     }
448
449     /**
450      * Creates an entry set iterator.
451      *
452      * @return the entrySet iterator
453      */

454     protected Iterator JavaDoc createEntrySetIterator() {
455         return new ReferenceEntrySetIterator(this);
456     }
457
458     /**
459      * Creates an key set iterator.
460      *
461      * @return the keySet iterator
462      */

463     protected Iterator JavaDoc createKeySetIterator() {
464         return new ReferenceKeySetIterator(this);
465     }
466
467     /**
468      * Creates an values iterator.
469      *
470      * @return the values iterator
471      */

472     protected Iterator JavaDoc createValuesIterator() {
473         return new ReferenceValuesIterator(this);
474     }
475
476     //-----------------------------------------------------------------------
477
/**
478      * EntrySet implementation.
479      */

480     static class ReferenceEntrySet extends EntrySet {
481         
482         protected ReferenceEntrySet(AbstractHashedMap parent) {
483             super(parent);
484         }
485
486         public Object JavaDoc[] toArray() {
487             return toArray(new Object JavaDoc[0]);
488         }
489
490         public Object JavaDoc[] toArray(Object JavaDoc[] arr) {
491             // special implementation to handle disappearing entries
492
ArrayList JavaDoc list = new ArrayList JavaDoc();
493             Iterator JavaDoc iterator = iterator();
494             while (iterator.hasNext()) {
495                 Entry e = (Entry) iterator.next();
496                 list.add(new DefaultMapEntry(e.getKey(), e.getValue()));
497             }
498             return list.toArray(arr);
499         }
500     }
501
502     //-----------------------------------------------------------------------
503
/**
504      * KeySet implementation.
505      */

506     static class ReferenceKeySet extends KeySet {
507         
508         protected ReferenceKeySet(AbstractHashedMap parent) {
509             super(parent);
510         }
511
512         public Object JavaDoc[] toArray() {
513             return toArray(new Object JavaDoc[0]);
514         }
515
516         public Object JavaDoc[] toArray(Object JavaDoc[] arr) {
517             // special implementation to handle disappearing keys
518
List JavaDoc list = new ArrayList JavaDoc(parent.size());
519             for (Iterator JavaDoc it = iterator(); it.hasNext(); ) {
520                 list.add(it.next());
521             }
522             return list.toArray(arr);
523         }
524     }
525
526     //-----------------------------------------------------------------------
527
/**
528      * Values implementation.
529      */

530     static class ReferenceValues extends Values {
531         
532         protected ReferenceValues(AbstractHashedMap parent) {
533             super(parent);
534         }
535
536         public Object JavaDoc[] toArray() {
537             return toArray(new Object JavaDoc[0]);
538         }
539
540         public Object JavaDoc[] toArray(Object JavaDoc[] arr) {
541             // special implementation to handle disappearing values
542
List JavaDoc list = new ArrayList JavaDoc(parent.size());
543             for (Iterator JavaDoc it = iterator(); it.hasNext(); ) {
544                 list.add(it.next());
545             }
546             return list.toArray(arr);
547         }
548     }
549
550     //-----------------------------------------------------------------------
551
/**
552      * A MapEntry implementation for the map.
553      * <p>
554      * If getKey() or getValue() returns null, it means
555      * the mapping is stale and should be removed.
556      *
557      * @since Commons Collections 3.1
558      */

559     protected static class ReferenceEntry extends HashEntry {
560         /** The parent map */
561         protected final AbstractReferenceMap parent;
562
563         /**
564          * Creates a new entry object for the ReferenceMap.
565          *
566          * @param parent the parent map
567          * @param next the next entry in the hash bucket
568          * @param hashCode the hash code of the key
569          * @param key the key
570          * @param value the value
571          */

572         public ReferenceEntry(AbstractReferenceMap parent, HashEntry next, int hashCode, Object JavaDoc key, Object JavaDoc value) {
573             super(next, hashCode, null, null);
574             this.parent = parent;
575             this.key = toReference(parent.keyType, key, hashCode);
576             this.value = toReference(parent.valueType, value, hashCode); // the key hashCode is passed in deliberately
577
}
578
579         /**
580          * Gets the key from the entry.
581          * This method dereferences weak and soft keys and thus may return null.
582          *
583          * @return the key, which may be null if it was garbage collected
584          */

585         public Object JavaDoc getKey() {
586             return (parent.keyType > HARD) ? ((Reference JavaDoc) key).get() : key;
587         }
588
589         /**
590          * Gets the value from the entry.
591          * This method dereferences weak and soft value and thus may return null.
592          *
593          * @return the value, which may be null if it was garbage collected
594          */

595         public Object JavaDoc getValue() {
596             return (parent.valueType > HARD) ? ((Reference JavaDoc) value).get() : value;
597         }
598
599         /**
600          * Sets the value of the entry.
601          *
602          * @param obj the object to store
603          * @return the previous value
604          */

605         public Object JavaDoc setValue(Object JavaDoc obj) {
606             Object JavaDoc old = getValue();
607             if (parent.valueType > HARD) {
608                 ((Reference JavaDoc)value).clear();
609             }
610             value = toReference(parent.valueType, obj, hashCode);
611             return old;
612         }
613
614         /**
615          * Compares this map entry to another.
616          * <p>
617          * This implementation uses <code>isEqualKey</code> and
618          * <code>isEqualValue</code> on the main map for comparison.
619          *
620          * @param obj the other map entry to compare to
621          * @return true if equal, false if not
622          */

623         public boolean equals(Object JavaDoc obj) {
624             if (obj == this) {
625                 return true;
626             }
627             if (obj instanceof Map.Entry JavaDoc == false) {
628                 return false;
629             }
630             
631             Map.Entry JavaDoc entry = (Map.Entry JavaDoc)obj;
632             Object JavaDoc entryKey = entry.getKey(); // convert to hard reference
633
Object JavaDoc entryValue = entry.getValue(); // convert to hard reference
634
if ((entryKey == null) || (entryValue == null)) {
635                 return false;
636             }
637             // compare using map methods, aiding identity subclass
638
// note that key is direct access and value is via method
639
return parent.isEqualKey(entryKey, key) &&
640                    parent.isEqualValue(entryValue, getValue());
641         }
642
643         /**
644          * Gets the hashcode of the entry using temporary hard references.
645          * <p>
646          * This implementation uses <code>hashEntry</code> on the main map.
647          *
648          * @return the hashcode of the entry
649          */

650         public int hashCode() {
651             return parent.hashEntry(getKey(), getValue());
652         }
653
654         /**
655          * Constructs a reference of the given type to the given referent.
656          * The reference is registered with the queue for later purging.
657          *
658          * @param type HARD, SOFT or WEAK
659          * @param referent the object to refer to
660          * @param hash the hash code of the <i>key</i> of the mapping;
661          * this number might be different from referent.hashCode() if
662          * the referent represents a value and not a key
663          */

664         protected Object JavaDoc toReference(int type, Object JavaDoc referent, int hash) {
665             switch (type) {
666                 case HARD: return referent;
667                 case SOFT: return new SoftRef(hash, referent, parent.queue);
668                 case WEAK: return new WeakRef(hash, referent, parent.queue);
669                 default: throw new Error JavaDoc();
670             }
671         }
672
673         /**
674          * Purges the specified reference
675          * @param ref the reference to purge
676          * @return true or false
677          */

678         boolean purge(Reference JavaDoc ref) {
679             boolean r = (parent.keyType > HARD) && (key == ref);
680             r = r || ((parent.valueType > HARD) && (value == ref));
681             if (r) {
682                 if (parent.keyType > HARD) {
683                     ((Reference JavaDoc)key).clear();
684                 }
685                 if (parent.valueType > HARD) {
686                     ((Reference JavaDoc)value).clear();
687                 } else if (parent.purgeValues) {
688                     value = null;
689                 }
690             }
691             return r;
692         }
693
694         /**
695          * Gets the next entry in the bucket.
696          *
697          * @return the next entry in the bucket
698          */

699         protected ReferenceEntry next() {
700             return (ReferenceEntry) next;
701         }
702     }
703
704     //-----------------------------------------------------------------------
705
/**
706      * The EntrySet iterator.
707      */

708     static class ReferenceEntrySetIterator implements Iterator JavaDoc {
709         /** The parent map */
710         final AbstractReferenceMap parent;
711         
712         // These fields keep track of where we are in the table.
713
int index;
714         ReferenceEntry entry;
715         ReferenceEntry previous;
716
717         // These Object fields provide hard references to the
718
// current and next entry; this assures that if hasNext()
719
// returns true, next() will actually return a valid element.
720
Object JavaDoc nextKey, nextValue;
721         Object JavaDoc currentKey, currentValue;
722
723         int expectedModCount;
724
725         public ReferenceEntrySetIterator(AbstractReferenceMap parent) {
726             super();
727             this.parent = parent;
728             index = (parent.size() != 0 ? parent.data.length : 0);
729             // have to do this here! size() invocation above
730
// may have altered the modCount.
731
expectedModCount = parent.modCount;
732         }
733
734         public boolean hasNext() {
735             checkMod();
736             while (nextNull()) {
737                 ReferenceEntry e = entry;
738                 int i = index;
739                 while ((e == null) && (i > 0)) {
740                     i--;
741                     e = (ReferenceEntry) parent.data[i];
742                 }
743                 entry = e;
744                 index = i;
745                 if (e == null) {
746                     currentKey = null;
747                     currentValue = null;
748                     return false;
749                 }
750                 nextKey = e.getKey();
751                 nextValue = e.getValue();
752                 if (nextNull()) {
753                     entry = entry.next();
754                 }
755             }
756             return true;
757         }
758
759         private void checkMod() {
760             if (parent.modCount != expectedModCount) {
761                 throw new ConcurrentModificationException JavaDoc();
762             }
763         }
764
765         private boolean nextNull() {
766             return (nextKey == null) || (nextValue == null);
767         }
768
769         protected ReferenceEntry nextEntry() {
770             checkMod();
771             if (nextNull() && !hasNext()) {
772                 throw new NoSuchElementException JavaDoc();
773             }
774             previous = entry;
775             entry = entry.next();
776             currentKey = nextKey;
777             currentValue = nextValue;
778             nextKey = null;
779             nextValue = null;
780             return previous;
781         }
782
783         protected ReferenceEntry currentEntry() {
784             checkMod();
785             return previous;
786         }
787         
788         public Object JavaDoc next() {
789             return nextEntry();
790         }
791
792         public void remove() {
793             checkMod();
794             if (previous == null) {
795                 throw new IllegalStateException JavaDoc();
796             }
797             parent.remove(currentKey);
798             previous = null;
799             currentKey = null;
800             currentValue = null;
801             expectedModCount = parent.modCount;
802         }
803     }
804
805     /**
806      * The keySet iterator.
807      */

808     static class ReferenceKeySetIterator extends ReferenceEntrySetIterator {
809         
810         ReferenceKeySetIterator(AbstractReferenceMap parent) {
811             super(parent);
812         }
813         
814         public Object JavaDoc next() {
815             return nextEntry().getKey();
816         }
817     }
818
819     /**
820      * The values iterator.
821      */

822     static class ReferenceValuesIterator extends ReferenceEntrySetIterator {
823         
824         ReferenceValuesIterator(AbstractReferenceMap parent) {
825             super(parent);
826         }
827         
828         public Object JavaDoc next() {
829             return nextEntry().getValue();
830         }
831     }
832
833     /**
834      * The MapIterator implementation.
835      */

836     static class ReferenceMapIterator extends ReferenceEntrySetIterator implements MapIterator {
837         
838         protected ReferenceMapIterator(AbstractReferenceMap parent) {
839             super(parent);
840         }
841
842         public Object JavaDoc next() {
843             return nextEntry().getKey();
844         }
845
846         public Object JavaDoc getKey() {
847             HashEntry current = currentEntry();
848             if (current == null) {
849                 throw new IllegalStateException JavaDoc(AbstractHashedMap.GETKEY_INVALID);
850             }
851             return current.getKey();
852         }
853
854         public Object JavaDoc getValue() {
855             HashEntry current = currentEntry();
856             if (current == null) {
857                 throw new IllegalStateException JavaDoc(AbstractHashedMap.GETVALUE_INVALID);
858             }
859             return current.getValue();
860         }
861
862         public Object JavaDoc setValue(Object JavaDoc value) {
863             HashEntry current = currentEntry();
864             if (current == null) {
865                 throw new IllegalStateException JavaDoc(AbstractHashedMap.SETVALUE_INVALID);
866             }
867             return current.setValue(value);
868         }
869     }
870     
871     //-----------------------------------------------------------------------
872
// These two classes store the hashCode of the key of
873
// of the mapping, so that after they're dequeued a quick
874
// lookup of the bucket in the table can occur.
875

876     /**
877      * A soft reference holder.
878      */

879     static class SoftRef extends SoftReference JavaDoc {
880         /** the hashCode of the key (even if the reference points to a value) */
881         private int hash;
882
883         public SoftRef(int hash, Object JavaDoc r, ReferenceQueue JavaDoc q) {
884             super(r, q);
885             this.hash = hash;
886         }
887
888         public int hashCode() {
889             return hash;
890         }
891     }
892
893     /**
894      * A weak reference holder.
895      */

896     static class WeakRef extends WeakReference JavaDoc {
897         /** the hashCode of the key (even if the reference points to a value) */
898         private int hash;
899
900         public WeakRef(int hash, Object JavaDoc r, ReferenceQueue JavaDoc q) {
901             super(r, q);
902             this.hash = hash;
903         }
904
905         public int hashCode() {
906             return hash;
907         }
908     }
909
910     //-----------------------------------------------------------------------
911
/**
912      * Replaces the superclass method to store the state of this class.
913      * <p>
914      * Serialization is not one of the JDK's nicest topics. Normal serialization will
915      * initialise the superclass before the subclass. Sometimes however, this isn't
916      * what you want, as in this case the <code>put()</code> method on read can be
917      * affected by subclass state.
918      * <p>
919      * The solution adopted here is to serialize the state data of this class in
920      * this protected method. This method must be called by the
921      * <code>writeObject()</code> of the first serializable subclass.
922      * <p>
923      * Subclasses may override if they have a specific field that must be present
924      * on read before this implementation will work. Generally, the read determines
925      * what must be serialized here, if anything.
926      *
927      * @param out the output stream
928      */

929     protected void doWriteObject(ObjectOutputStream JavaDoc out) throws IOException JavaDoc {
930         out.writeInt(keyType);
931         out.writeInt(valueType);
932         out.writeBoolean(purgeValues);
933         out.writeFloat(loadFactor);
934         out.writeInt(data.length);
935         for (MapIterator it = mapIterator(); it.hasNext();) {
936             out.writeObject(it.next());
937             out.writeObject(it.getValue());
938         }
939         out.writeObject(null); // null terminate map
940
// do not call super.doWriteObject() as code there doesn't work for reference map
941
}
942
943     /**
944      * Replaces the superclassm method to read the state of this class.
945      * <p>
946      * Serialization is not one of the JDK's nicest topics. Normal serialization will
947      * initialise the superclass before the subclass. Sometimes however, this isn't
948      * what you want, as in this case the <code>put()</code> method on read can be
949      * affected by subclass state.
950      * <p>
951      * The solution adopted here is to deserialize the state data of this class in
952      * this protected method. This method must be called by the
953      * <code>readObject()</code> of the first serializable subclass.
954      * <p>
955      * Subclasses may override if the subclass has a specific field that must be present
956      * before <code>put()</code> or <code>calculateThreshold()</code> will work correctly.
957      *
958      * @param in the input stream
959      */

960     protected void doReadObject(ObjectInputStream JavaDoc in) throws IOException JavaDoc, ClassNotFoundException JavaDoc {
961         this.keyType = in.readInt();
962         this.valueType = in.readInt();
963         this.purgeValues = in.readBoolean();
964         this.loadFactor = in.readFloat();
965         int capacity = in.readInt();
966         init();
967         data = new HashEntry[capacity];
968         while (true) {
969             Object JavaDoc key = in.readObject();
970             if (key == null) {
971                 break;
972             }
973             Object JavaDoc value = in.readObject();
974             put(key, value);
975         }
976         threshold = calculateThreshold(data.length, loadFactor);
977         // do not call super.doReadObject() as code there doesn't work for reference map
978
}
979
980 }
981
Popular Tags