1// Converted, with some major refactors required. Not as memory-efficient as before, could use additional refactoring.
2// Perhaps use four different types of HashEntry classes for max efficiency:
3//   normal HashEntry for HARD,HARD
4//   HardRefEntry for HARD,(SOFT|WEAK)
5//   RefHardEntry for (SOFT|WEAK),HARD
6//   RefRefEntry for (SOFT|WEAK),(SOFT|WEAK)
7/*
8 *  Copyright 2002-2004 The Apache Software Foundation
9 *
10 *  Licensed under the Apache License, Version 2.0 (the "License");
11 *  you may not use this file except in compliance with the License.
12 *  You may obtain a copy of the License at
13 *
14 *      http://www.apache.org/licenses/LICENSE-2.0
15 *
16 *  Unless required by applicable law or agreed to in writing, software
17 *  distributed under the License is distributed on an "AS IS" BASIS,
18 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19 *  See the License for the specific language governing permissions and
20 *  limitations under the License.
21 */
22package org.jivesoftware.smack.util.collections;
23
24import java.io.IOException;
25import java.io.ObjectInputStream;
26import java.io.ObjectOutputStream;
27import java.lang.ref.Reference;
28import java.lang.ref.ReferenceQueue;
29import java.lang.ref.SoftReference;
30import java.lang.ref.WeakReference;
31import java.util.*;
32
33/**
34 * An abstract implementation of a hash-based map that allows the entries to
35 * be removed by the garbage collector.
36 * <p/>
37 * This class implements all the features necessary for a subclass reference
38 * hash-based map. Key-value entries are stored in instances of the
39 * <code>ReferenceEntry</code> class which can be overridden and replaced.
40 * The iterators can similarly be replaced, without the need to replace the KeySet,
41 * EntrySet and Values view classes.
42 * <p/>
43 * Overridable methods are provided to change the default hashing behaviour, and
44 * to change how entries are added to and removed from the map. Hopefully, all you
45 * need for unusual subclasses is here.
46 * <p/>
47 * When you construct an <code>AbstractReferenceMap</code>, you can specify what
48 * kind of references are used to store the map's keys and values.
49 * If non-hard references are used, then the garbage collector can remove
50 * mappings if a key or value becomes unreachable, or if the JVM's memory is
51 * running low. For information on how the different reference types behave,
52 * see {@link Reference}.
53 * <p/>
54 * Different types of references can be specified for keys and values.
55 * The keys can be configured to be weak but the values hard,
56 * in which case this class will behave like a
57 * <a href="http://java.sun.com/j2se/1.4/docs/api/java/util/WeakHashMap.html">
58 * <code>WeakHashMap</code></a>. However, you can also specify hard keys and
59 * weak values, or any other combination. The default constructor uses
60 * hard keys and soft values, providing a memory-sensitive cache.
61 * <p/>
62 * This {@link Map} implementation does <i>not</i> allow null elements.
63 * Attempting to add a null key or value to the map will raise a
64 * <code>NullPointerException</code>.
65 * <p/>
66 * All the available iterators can be reset back to the start by casting to
67 * <code>ResettableIterator</code> and calling <code>reset()</code>.
68 * <p/>
69 * This implementation is not synchronized.
70 * You can use {@link java.util.Collections#synchronizedMap} to
71 * provide synchronized access to a <code>ReferenceMap</code>.
72 *
73 * @author Paul Jack
74 * @author Matt Hall, John Watkinson, Stephen Colebourne
75 * @version $Revision: 1.1 $ $Date: 2005/10/11 17:05:32 $
76 * @see java.lang.ref.Reference
77 * @since Commons Collections 3.1 (extracted from ReferenceMap in 3.0)
78 */
79public abstract class AbstractReferenceMap <K,V> extends AbstractHashedMap<K, V> {
80
81    /**
82     * Constant indicating that hard references should be used
83     */
84    public static final int HARD = 0;
85
86    /**
87     * Constant indicating that soft references should be used
88     */
89    public static final int SOFT = 1;
90
91    /**
92     * Constant indicating that weak references should be used
93     */
94    public static final int WEAK = 2;
95
96    /**
97     * The reference type for keys.  Must be HARD, SOFT, WEAK.
98     *
99     * @serial
100     */
101    protected int keyType;
102
103    /**
104     * The reference type for values.  Must be HARD, SOFT, WEAK.
105     *
106     * @serial
107     */
108    protected int valueType;
109
110    /**
111     * Should the value be automatically purged when the associated key has been collected?
112     */
113    protected boolean purgeValues;
114
115    /**
116     * ReferenceQueue used to eliminate stale mappings.
117     * See purge.
118     */
119    private transient ReferenceQueue queue;
120
121    //-----------------------------------------------------------------------
122    /**
123     * Constructor used during deserialization.
124     */
125    protected AbstractReferenceMap() {
126        super();
127    }
128
129    /**
130     * Constructs a new empty map with the specified reference types,
131     * load factor and initial capacity.
132     *
133     * @param keyType     the type of reference to use for keys;
134     *                    must be {@link #SOFT} or {@link #WEAK}
135     * @param valueType   the type of reference to use for values;
136     *                    must be {@link #SOFT} or {@link #WEAK}
137     * @param capacity    the initial capacity for the map
138     * @param loadFactor  the load factor for the map
139     * @param purgeValues should the value be automatically purged when the
140     *                    key is garbage collected
141     */
142    protected AbstractReferenceMap(int keyType, int valueType, int capacity, float loadFactor, boolean purgeValues) {
143        super(capacity, loadFactor);
144        verify("keyType", keyType);
145        verify("valueType", valueType);
146        this.keyType = keyType;
147        this.valueType = valueType;
148        this.purgeValues = purgeValues;
149    }
150
151    /**
152     * Initialise this subclass during construction, cloning or deserialization.
153     */
154    protected void init() {
155        queue = new ReferenceQueue();
156    }
157
158    //-----------------------------------------------------------------------
159    /**
160     * Checks the type int is a valid value.
161     *
162     * @param name the name for error messages
163     * @param type the type value to check
164     * @throws IllegalArgumentException if the value if invalid
165     */
166    private static void verify(String name, int type) {
167        if ((type < HARD) || (type > WEAK)) {
168            throw new IllegalArgumentException(name + " must be HARD, SOFT, WEAK.");
169        }
170    }
171
172    //-----------------------------------------------------------------------
173    /**
174     * Gets the size of the map.
175     *
176     * @return the size
177     */
178    public int size() {
179        purgeBeforeRead();
180        return super.size();
181    }
182
183    /**
184     * Checks whether the map is currently empty.
185     *
186     * @return true if the map is currently size zero
187     */
188    public boolean isEmpty() {
189        purgeBeforeRead();
190        return super.isEmpty();
191    }
192
193    /**
194     * Checks whether the map contains the specified key.
195     *
196     * @param key the key to search for
197     * @return true if the map contains the key
198     */
199    public boolean containsKey(Object key) {
200        purgeBeforeRead();
201        Entry entry = getEntry(key);
202        if (entry == null) {
203            return false;
204        }
205        return (entry.getValue() != null);
206    }
207
208    /**
209     * Checks whether the map contains the specified value.
210     *
211     * @param value the value to search for
212     * @return true if the map contains the value
213     */
214    public boolean containsValue(Object value) {
215        purgeBeforeRead();
216        if (value == null) {
217            return false;
218        }
219        return super.containsValue(value);
220    }
221
222    /**
223     * Gets the value mapped to the key specified.
224     *
225     * @param key the key
226     * @return the mapped value, null if no match
227     */
228    public V get(Object key) {
229        purgeBeforeRead();
230        Entry<K, V> entry = getEntry(key);
231        if (entry == null) {
232            return null;
233        }
234        return entry.getValue();
235    }
236
237
238    /**
239     * Puts a key-value mapping into this map.
240     * Neither the key nor the value may be null.
241     *
242     * @param key   the key to add, must not be null
243     * @param value the value to add, must not be null
244     * @return the value previously mapped to this key, null if none
245     * @throws NullPointerException if either the key or value is null
246     */
247    public V put(K key, V value) {
248        if (key == null) {
249            throw new NullPointerException("null keys not allowed");
250        }
251        if (value == null) {
252            throw new NullPointerException("null values not allowed");
253        }
254
255        purgeBeforeWrite();
256        return super.put(key, value);
257    }
258
259    /**
260     * Removes the specified mapping from this map.
261     *
262     * @param key the mapping to remove
263     * @return the value mapped to the removed key, null if key not in map
264     */
265    public V remove(Object key) {
266        if (key == null) {
267            return null;
268        }
269        purgeBeforeWrite();
270        return super.remove(key);
271    }
272
273    /**
274     * Clears this map.
275     */
276    public void clear() {
277        super.clear();
278        while (queue.poll() != null) {
279        } // drain the queue
280    }
281
282    //-----------------------------------------------------------------------
283    /**
284     * Gets a MapIterator over the reference map.
285     * The iterator only returns valid key/value pairs.
286     *
287     * @return a map iterator
288     */
289    public MapIterator<K, V> mapIterator() {
290        return new ReferenceMapIterator<K, V>(this);
291    }
292
293    /**
294     * Returns a set view of this map's entries.
295     * An iterator returned entry is valid until <code>next()</code> is called again.
296     * The <code>setValue()</code> method on the <code>toArray</code> entries has no effect.
297     *
298     * @return a set view of this map's entries
299     */
300    public Set<Map.Entry<K, V>> entrySet() {
301        if (entrySet == null) {
302            entrySet = new ReferenceEntrySet<K, V>(this);
303        }
304        return entrySet;
305    }
306
307    /**
308     * Returns a set view of this map's keys.
309     *
310     * @return a set view of this map's keys
311     */
312    public Set<K> keySet() {
313        if (keySet == null) {
314            keySet = new ReferenceKeySet<K, V>(this);
315        }
316        return keySet;
317    }
318
319    /**
320     * Returns a collection view of this map's values.
321     *
322     * @return a set view of this map's values
323     */
324    public Collection<V> values() {
325        if (values == null) {
326            values = new ReferenceValues<K, V>(this);
327        }
328        return values;
329    }
330
331    //-----------------------------------------------------------------------
332    /**
333     * Purges stale mappings from this map before read operations.
334     * <p/>
335     * This implementation calls {@link #purge()} to maintain a consistent state.
336     */
337    protected void purgeBeforeRead() {
338        purge();
339    }
340
341    /**
342     * Purges stale mappings from this map before write operations.
343     * <p/>
344     * This implementation calls {@link #purge()} to maintain a consistent state.
345     */
346    protected void purgeBeforeWrite() {
347        purge();
348    }
349
350    /**
351     * Purges stale mappings from this map.
352     * <p/>
353     * Note that this method is not synchronized!  Special
354     * care must be taken if, for instance, you want stale
355     * mappings to be removed on a periodic basis by some
356     * background thread.
357     */
358    protected void purge() {
359        Reference ref = queue.poll();
360        while (ref != null) {
361            purge(ref);
362            ref = queue.poll();
363        }
364    }
365
366    /**
367     * Purges the specified reference.
368     *
369     * @param ref the reference to purge
370     */
371    protected void purge(Reference ref) {
372        // The hashCode of the reference is the hashCode of the
373        // mapping key, even if the reference refers to the
374        // mapping value...
375        int hash = ref.hashCode();
376        int index = hashIndex(hash, data.length);
377        HashEntry<K, V> previous = null;
378        HashEntry<K, V> entry = data[index];
379        while (entry != null) {
380            if (((ReferenceEntry<K, V>) entry).purge(ref)) {
381                if (previous == null) {
382                    data[index] = entry.next;
383                } else {
384                    previous.next = entry.next;
385                }
386                this.size--;
387                return;
388            }
389            previous = entry;
390            entry = entry.next;
391        }
392
393    }
394
395    //-----------------------------------------------------------------------
396    /**
397     * Gets the entry mapped to the key specified.
398     *
399     * @param key the key
400     * @return the entry, null if no match
401     */
402    protected HashEntry<K, V> getEntry(Object key) {
403        if (key == null) {
404            return null;
405        } else {
406            return super.getEntry(key);
407        }
408    }
409
410    /**
411     * Gets the hash code for a MapEntry.
412     * Subclasses can override this, for example to use the identityHashCode.
413     *
414     * @param key   the key to get a hash code for, may be null
415     * @param value the value to get a hash code for, may be null
416     * @return the hash code, as per the MapEntry specification
417     */
418    protected int hashEntry(Object key, Object value) {
419        return (key == null ? 0 : key.hashCode()) ^ (value == null ? 0 : value.hashCode());
420    }
421
422    /**
423     * Compares two keys, in internal converted form, to see if they are equal.
424     * <p/>
425     * This implementation converts the key from the entry to a real reference
426     * before comparison.
427     *
428     * @param key1 the first key to compare passed in from outside
429     * @param key2 the second key extracted from the entry via <code>entry.key</code>
430     * @return true if equal
431     */
432    protected boolean isEqualKey(Object key1, Object key2) {
433        //if ((key1 == null) && (key2 != null) || (key1 != null) || (key2 == null)) {
434        //    return false;
435        //}
436        // GenericsNote: Conversion from reference handled by getKey() which replaced all .key references
437        //key2 = (keyType > HARD ? ((Reference) key2).get() : key2);
438        return (key1 == key2 || key1.equals(key2));
439    }
440
441    /**
442     * Creates a ReferenceEntry instead of a HashEntry.
443     *
444     * @param next     the next entry in sequence
445     * @param hashCode the hash code to use
446     * @param key      the key to store
447     * @param value    the value to store
448     * @return the newly created entry
449     */
450    public HashEntry<K, V> createEntry(HashEntry<K, V> next, int hashCode, K key, V value) {
451        return new ReferenceEntry<K, V>(this, (ReferenceEntry<K, V>) next, hashCode, key, value);
452    }
453
454    /**
455     * Creates an entry set iterator.
456     *
457     * @return the entrySet iterator
458     */
459    protected Iterator<Map.Entry<K, V>> createEntrySetIterator() {
460        return new ReferenceEntrySetIterator<K, V>(this);
461    }
462
463    /**
464     * Creates an key set iterator.
465     *
466     * @return the keySet iterator
467     */
468    protected Iterator<K> createKeySetIterator() {
469        return new ReferenceKeySetIterator<K, V>(this);
470    }
471
472    /**
473     * Creates an values iterator.
474     *
475     * @return the values iterator
476     */
477    protected Iterator<V> createValuesIterator() {
478        return new ReferenceValuesIterator<K, V>(this);
479    }
480
481    //-----------------------------------------------------------------------
482    /**
483     * EntrySet implementation.
484     */
485    static class ReferenceEntrySet <K,V> extends EntrySet<K, V> {
486
487        protected ReferenceEntrySet(AbstractHashedMap<K, V> parent) {
488            super(parent);
489        }
490
491        public Object[] toArray() {
492            return toArray(new Object[0]);
493        }
494
495        public <T> T[] toArray(T[] arr) {
496            // special implementation to handle disappearing entries
497            ArrayList<Map.Entry<K, V>> list = new ArrayList<Map.Entry<K, V>>();
498            Iterator<Map.Entry<K, V>> iterator = iterator();
499            while (iterator.hasNext()) {
500                Map.Entry<K, V> e = iterator.next();
501                list.add(new DefaultMapEntry<K, V>(e.getKey(), e.getValue()));
502            }
503            return list.toArray(arr);
504        }
505    }
506
507    //-----------------------------------------------------------------------
508    /**
509     * KeySet implementation.
510     */
511    static class ReferenceKeySet <K,V> extends KeySet<K, V> {
512
513        protected ReferenceKeySet(AbstractHashedMap<K, V> parent) {
514            super(parent);
515        }
516
517        public Object[] toArray() {
518            return toArray(new Object[0]);
519        }
520
521        public <T> T[] toArray(T[] arr) {
522            // special implementation to handle disappearing keys
523            List<K> list = new ArrayList<K>(parent.size());
524            for (Iterator<K> it = iterator(); it.hasNext();) {
525                list.add(it.next());
526            }
527            return list.toArray(arr);
528        }
529    }
530
531    //-----------------------------------------------------------------------
532    /**
533     * Values implementation.
534     */
535    static class ReferenceValues <K,V> extends Values<K, V> {
536
537        protected ReferenceValues(AbstractHashedMap<K, V> parent) {
538            super(parent);
539        }
540
541        public Object[] toArray() {
542            return toArray(new Object[0]);
543        }
544
545        public <T> T[] toArray(T[] arr) {
546            // special implementation to handle disappearing values
547            List<V> list = new ArrayList<V>(parent.size());
548            for (Iterator<V> it = iterator(); it.hasNext();) {
549                list.add(it.next());
550            }
551            return list.toArray(arr);
552        }
553    }
554
555    //-----------------------------------------------------------------------
556    /**
557     * A MapEntry implementation for the map.
558     * <p/>
559     * If getKey() or getValue() returns null, it means
560     * the mapping is stale and should be removed.
561     *
562     * @since Commons Collections 3.1
563     */
564    protected static class ReferenceEntry <K,V> extends HashEntry<K, V> {
565        /**
566         * The parent map
567         */
568        protected final AbstractReferenceMap<K, V> parent;
569
570        protected Reference<K> refKey;
571        protected Reference<V> refValue;
572
573        /**
574         * Creates a new entry object for the ReferenceMap.
575         *
576         * @param parent   the parent map
577         * @param next     the next entry in the hash bucket
578         * @param hashCode the hash code of the key
579         * @param key      the key
580         * @param value    the value
581         */
582        public ReferenceEntry(AbstractReferenceMap<K, V> parent, ReferenceEntry<K, V> next, int hashCode, K key, V value) {
583            super(next, hashCode, null, null);
584            this.parent = parent;
585            if (parent.keyType != HARD) {
586                refKey = toReference(parent.keyType, key, hashCode);
587            } else {
588                this.setKey(key);
589            }
590            if (parent.valueType != HARD) {
591                refValue = toReference(parent.valueType, value, hashCode); // the key hashCode is passed in deliberately
592            } else {
593                this.setValue(value);
594            }
595        }
596
597        /**
598         * Gets the key from the entry.
599         * This method dereferences weak and soft keys and thus may return null.
600         *
601         * @return the key, which may be null if it was garbage collected
602         */
603        public K getKey() {
604            return (parent.keyType > HARD) ? refKey.get() : super.getKey();
605        }
606
607        /**
608         * Gets the value from the entry.
609         * This method dereferences weak and soft value and thus may return null.
610         *
611         * @return the value, which may be null if it was garbage collected
612         */
613        public V getValue() {
614            return (parent.valueType > HARD) ? refValue.get() : super.getValue();
615        }
616
617        /**
618         * Sets the value of the entry.
619         *
620         * @param obj the object to store
621         * @return the previous value
622         */
623        public V setValue(V obj) {
624            V old = getValue();
625            if (parent.valueType > HARD) {
626                refValue.clear();
627                refValue = toReference(parent.valueType, obj, hashCode);
628            } else {
629                super.setValue(obj);
630            }
631            return old;
632        }
633
634        /**
635         * Compares this map entry to another.
636         * <p/>
637         * This implementation uses <code>isEqualKey</code> and
638         * <code>isEqualValue</code> on the main map for comparison.
639         *
640         * @param obj the other map entry to compare to
641         * @return true if equal, false if not
642         */
643        public boolean equals(Object obj) {
644            if (obj == this) {
645                return true;
646            }
647            if (obj instanceof Map.Entry == false) {
648                return false;
649            }
650
651            Map.Entry entry = (Map.Entry) obj;
652            Object entryKey = entry.getKey();  // convert to hard reference
653            Object entryValue = entry.getValue();  // convert to hard reference
654            if ((entryKey == null) || (entryValue == null)) {
655                return false;
656            }
657            // compare using map methods, aiding identity subclass
658            // note that key is direct access and value is via method
659            return parent.isEqualKey(entryKey, getKey()) && parent.isEqualValue(entryValue, getValue());
660        }
661
662        /**
663         * Gets the hashcode of the entry using temporary hard references.
664         * <p/>
665         * This implementation uses <code>hashEntry</code> on the main map.
666         *
667         * @return the hashcode of the entry
668         */
669        public int hashCode() {
670            return parent.hashEntry(getKey(), getValue());
671        }
672
673        /**
674         * Constructs a reference of the given type to the given referent.
675         * The reference is registered with the queue for later purging.
676         *
677         * @param type     HARD, SOFT or WEAK
678         * @param referent the object to refer to
679         * @param hash     the hash code of the <i>key</i> of the mapping;
680         *                 this number might be different from referent.hashCode() if
681         *                 the referent represents a value and not a key
682         */
683        protected <T> Reference<T> toReference(int type, T referent, int hash) {
684            switch (type) {
685                case SOFT:
686                    return new SoftRef<T>(hash, referent, parent.queue);
687                case WEAK:
688                    return new WeakRef<T>(hash, referent, parent.queue);
689                default:
690                    throw new Error("Attempt to create hard reference in ReferenceMap!");
691            }
692        }
693
694        /**
695         * Purges the specified reference
696         *
697         * @param ref the reference to purge
698         * @return true or false
699         */
700        boolean purge(Reference ref) {
701            boolean r = (parent.keyType > HARD) && (refKey == ref);
702            r = r || ((parent.valueType > HARD) && (refValue == ref));
703            if (r) {
704                if (parent.keyType > HARD) {
705                    refKey.clear();
706                }
707                if (parent.valueType > HARD) {
708                    refValue.clear();
709                } else if (parent.purgeValues) {
710                    setValue(null);
711                }
712            }
713            return r;
714        }
715
716        /**
717         * Gets the next entry in the bucket.
718         *
719         * @return the next entry in the bucket
720         */
721        protected ReferenceEntry<K, V> next() {
722            return (ReferenceEntry<K, V>) next;
723        }
724    }
725
726    //-----------------------------------------------------------------------
727    /**
728     * The EntrySet iterator.
729     */
730    static class ReferenceIteratorBase <K,V> {
731        /**
732         * The parent map
733         */
734        final AbstractReferenceMap<K, V> parent;
735
736        // These fields keep track of where we are in the table.
737        int index;
738        ReferenceEntry<K, V> entry;
739        ReferenceEntry<K, V> previous;
740
741        // These Object fields provide hard references to the
742        // current and next entry; this assures that if hasNext()
743        // returns true, next() will actually return a valid element.
744        K nextKey;
745        V nextValue;
746        K currentKey;
747        V currentValue;
748
749        int expectedModCount;
750
751        public ReferenceIteratorBase(AbstractReferenceMap<K, V> parent) {
752            super();
753            this.parent = parent;
754            index = (parent.size() != 0 ? parent.data.length : 0);
755            // have to do this here!  size() invocation above
756            // may have altered the modCount.
757            expectedModCount = parent.modCount;
758        }
759
760        public boolean hasNext() {
761            checkMod();
762            while (nextNull()) {
763                ReferenceEntry<K, V> e = entry;
764                int i = index;
765                while ((e == null) && (i > 0)) {
766                    i--;
767                    e = (ReferenceEntry<K, V>) parent.data[i];
768                }
769                entry = e;
770                index = i;
771                if (e == null) {
772                    currentKey = null;
773                    currentValue = null;
774                    return false;
775                }
776                nextKey = e.getKey();
777                nextValue = e.getValue();
778                if (nextNull()) {
779                    entry = entry.next();
780                }
781            }
782            return true;
783        }
784
785        private void checkMod() {
786            if (parent.modCount != expectedModCount) {
787                throw new ConcurrentModificationException();
788            }
789        }
790
791        private boolean nextNull() {
792            return (nextKey == null) || (nextValue == null);
793        }
794
795        protected ReferenceEntry<K, V> nextEntry() {
796            checkMod();
797            if (nextNull() && !hasNext()) {
798                throw new NoSuchElementException();
799            }
800            previous = entry;
801            entry = entry.next();
802            currentKey = nextKey;
803            currentValue = nextValue;
804            nextKey = null;
805            nextValue = null;
806            return previous;
807        }
808
809        protected ReferenceEntry<K, V> currentEntry() {
810            checkMod();
811            return previous;
812        }
813
814        public ReferenceEntry<K, V> superNext() {
815            return nextEntry();
816        }
817
818        public void remove() {
819            checkMod();
820            if (previous == null) {
821                throw new IllegalStateException();
822            }
823            parent.remove(currentKey);
824            previous = null;
825            currentKey = null;
826            currentValue = null;
827            expectedModCount = parent.modCount;
828        }
829    }
830
831    /**
832     * The EntrySet iterator.
833     */
834    static class ReferenceEntrySetIterator <K,V> extends ReferenceIteratorBase<K, V> implements Iterator<Map.Entry<K, V>> {
835
836        public ReferenceEntrySetIterator(AbstractReferenceMap<K, V> abstractReferenceMap) {
837            super(abstractReferenceMap);
838        }
839
840        public ReferenceEntry<K, V> next() {
841            return superNext();
842        }
843
844    }
845
846    /**
847     * The keySet iterator.
848     */
849    static class ReferenceKeySetIterator <K,V> extends ReferenceIteratorBase<K, V> implements Iterator<K> {
850
851        ReferenceKeySetIterator(AbstractReferenceMap<K, V> parent) {
852            super(parent);
853        }
854
855        public K next() {
856            return nextEntry().getKey();
857        }
858    }
859
860    /**
861     * The values iterator.
862     */
863    static class ReferenceValuesIterator <K,V> extends ReferenceIteratorBase<K, V> implements Iterator<V> {
864
865        ReferenceValuesIterator(AbstractReferenceMap<K, V> parent) {
866            super(parent);
867        }
868
869        public V next() {
870            return nextEntry().getValue();
871        }
872    }
873
874    /**
875     * The MapIterator implementation.
876     */
877    static class ReferenceMapIterator <K,V> extends ReferenceIteratorBase<K, V> implements MapIterator<K, V> {
878
879        protected ReferenceMapIterator(AbstractReferenceMap<K, V> parent) {
880            super(parent);
881        }
882
883        public K next() {
884            return nextEntry().getKey();
885        }
886
887        public K getKey() {
888            HashEntry<K, V> current = currentEntry();
889            if (current == null) {
890                throw new IllegalStateException(AbstractHashedMap.GETKEY_INVALID);
891            }
892            return current.getKey();
893        }
894
895        public V getValue() {
896            HashEntry<K, V> current = currentEntry();
897            if (current == null) {
898                throw new IllegalStateException(AbstractHashedMap.GETVALUE_INVALID);
899            }
900            return current.getValue();
901        }
902
903        public V setValue(V value) {
904            HashEntry<K, V> current = currentEntry();
905            if (current == null) {
906                throw new IllegalStateException(AbstractHashedMap.SETVALUE_INVALID);
907            }
908            return current.setValue(value);
909        }
910    }
911
912    //-----------------------------------------------------------------------
913    // These two classes store the hashCode of the key of
914    // of the mapping, so that after they're dequeued a quick
915    // lookup of the bucket in the table can occur.
916
917    /**
918     * A soft reference holder.
919     */
920    static class SoftRef <T> extends SoftReference<T> {
921        /**
922         * the hashCode of the key (even if the reference points to a value)
923         */
924        private int hash;
925
926        public SoftRef(int hash, T r, ReferenceQueue q) {
927            super(r, q);
928            this.hash = hash;
929        }
930
931        public int hashCode() {
932            return hash;
933        }
934    }
935
936    /**
937     * A weak reference holder.
938     */
939    static class WeakRef <T> extends WeakReference<T> {
940        /**
941         * the hashCode of the key (even if the reference points to a value)
942         */
943        private int hash;
944
945        public WeakRef(int hash, T r, ReferenceQueue q) {
946            super(r, q);
947            this.hash = hash;
948        }
949
950        public int hashCode() {
951            return hash;
952        }
953    }
954
955    //-----------------------------------------------------------------------
956    /**
957     * Replaces the superclass method to store the state of this class.
958     * <p/>
959     * Serialization is not one of the JDK's nicest topics. Normal serialization will
960     * initialise the superclass before the subclass. Sometimes however, this isn't
961     * what you want, as in this case the <code>put()</code> method on read can be
962     * affected by subclass state.
963     * <p/>
964     * The solution adopted here is to serialize the state data of this class in
965     * this protected method. This method must be called by the
966     * <code>writeObject()</code> of the first serializable subclass.
967     * <p/>
968     * Subclasses may override if they have a specific field that must be present
969     * on read before this implementation will work. Generally, the read determines
970     * what must be serialized here, if anything.
971     *
972     * @param out the output stream
973     */
974    protected void doWriteObject(ObjectOutputStream out) throws IOException {
975        out.writeInt(keyType);
976        out.writeInt(valueType);
977        out.writeBoolean(purgeValues);
978        out.writeFloat(loadFactor);
979        out.writeInt(data.length);
980        for (MapIterator it = mapIterator(); it.hasNext();) {
981            out.writeObject(it.next());
982            out.writeObject(it.getValue());
983        }
984        out.writeObject(null);  // null terminate map
985        // do not call super.doWriteObject() as code there doesn't work for reference map
986    }
987
988    /**
989     * Replaces the superclassm method to read the state of this class.
990     * <p/>
991     * Serialization is not one of the JDK's nicest topics. Normal serialization will
992     * initialise the superclass before the subclass. Sometimes however, this isn't
993     * what you want, as in this case the <code>put()</code> method on read can be
994     * affected by subclass state.
995     * <p/>
996     * The solution adopted here is to deserialize the state data of this class in
997     * this protected method. This method must be called by the
998     * <code>readObject()</code> of the first serializable subclass.
999     * <p/>
1000     * Subclasses may override if the subclass has a specific field that must be present
1001     * before <code>put()</code> or <code>calculateThreshold()</code> will work correctly.
1002     *
1003     * @param in the input stream
1004     */
1005    protected void doReadObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
1006        this.keyType = in.readInt();
1007        this.valueType = in.readInt();
1008        this.purgeValues = in.readBoolean();
1009        this.loadFactor = in.readFloat();
1010        int capacity = in.readInt();
1011        init();
1012        data = new HashEntry[capacity];
1013        while (true) {
1014            K key = (K) in.readObject();
1015            if (key == null) {
1016                break;
1017            }
1018            V value = (V) in.readObject();
1019            put(key, value);
1020        }
1021        threshold = calculateThreshold(data.length, loadFactor);
1022        // do not call super.doReadObject() as code there doesn't work for reference map
1023    }
1024
1025}
1026