001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.collections4.map;
018
019import java.io.IOException;
020import java.io.ObjectInputStream;
021import java.io.ObjectOutputStream;
022import java.lang.ref.Reference;
023import java.lang.ref.ReferenceQueue;
024import java.lang.ref.SoftReference;
025import java.lang.ref.WeakReference;
026import java.util.ArrayList;
027import java.util.Collection;
028import java.util.ConcurrentModificationException;
029import java.util.Iterator;
030import java.util.List;
031import java.util.Map;
032import java.util.NoSuchElementException;
033import java.util.Objects;
034import java.util.Set;
035
036import org.apache.commons.collections4.MapIterator;
037import org.apache.commons.collections4.keyvalue.DefaultMapEntry;
038
039/**
040 * An abstract implementation of a hash-based map that allows the entries to
041 * be removed by the garbage collector.
042 * <p>
043 * This class implements all the features necessary for a subclass reference
044 * hash-based map. Key-value entries are stored in instances of the
045 * {@code ReferenceEntry} class which can be overridden and replaced.
046 * The iterators can similarly be replaced, without the need to replace the KeySet,
047 * EntrySet and Values view classes.
048 * </p>
049 * <p>
050 * Overridable methods are provided to change the default hashing behavior, and
051 * to change how entries are added to and removed from the map. Hopefully, all you
052 * need for unusual subclasses is here.
053 * </p>
054 * <p>
055 * When you construct an {@code AbstractReferenceMap}, you can specify what
056 * kind of references are used to store the map's keys and values.
057 * If non-hard references are used, then the garbage collector can remove
058 * mappings if a key or value becomes unreachable, or if the JVM's memory is
059 * running low. For information on how the different reference types behave,
060 * see {@link Reference}.
061 * </p>
062 * <p>
063 * Different types of references can be specified for keys and values.
064 * The keys can be configured to be weak but the values hard,
065 * in which case this class will behave like a
066 * <a href="https://docs.oracle.com/javase/8/docs/api/java/util/WeakHashMap.html">
067 * {@code WeakHashMap}</a>. However, you can also specify hard keys and
068 * weak values, or any other combination. The default constructor uses
069 * hard keys and soft values, providing a memory-sensitive cache.
070 * </p>
071 * <p>
072 * This {@link Map} implementation does <em>not</em> allow null elements.
073 * Attempting to add a null key or value to the map will raise a
074 * {@code NullPointerException}.
075 * </p>
076 * <p>
077 * All the available iterators can be reset back to the start by casting to
078 * {@code ResettableIterator} and calling {@code reset()}.
079 * </p>
080 * <p>
081 * This implementation is not synchronized.
082 * You can use {@link java.util.Collections#synchronizedMap} to
083 * provide synchronized access to a {@code ReferenceMap}.
084 * </p>
085 *
086 * @param <K> the type of the keys in this map
087 * @param <V> the type of the values in this map
088 * @see java.lang.ref.Reference
089 * @since 3.1 (extracted from ReferenceMap in 3.0)
090 */
091public abstract class AbstractReferenceMap<K, V> extends AbstractHashedMap<K, V> {
092
093    /**
094     * Base iterator class.
095     */
096    static class ReferenceBaseIterator<K, V> {
097        /** The parent map */
098        final AbstractReferenceMap<K, V> parent;
099
100        // These fields keep track of where we are in the table.
101        int index;
102        ReferenceEntry<K, V> next;
103        ReferenceEntry<K, V> current;
104
105        // These Object fields provide hard references to the
106        // current and next entry; this assures that if hasNext()
107        // returns true, next() will actually return a valid element.
108        K currentKey;
109        K nextKey;
110        V currentValue;
111        V nextValue;
112
113        int expectedModCount;
114
115        ReferenceBaseIterator(final AbstractReferenceMap<K, V> parent) {
116            this.parent = parent;
117            index = !parent.isEmpty() ? parent.data.length : 0;
118            // have to do this here!  size() invocation above
119            // may have altered the modCount.
120            expectedModCount = parent.modCount;
121        }
122
123        private void checkMod() {
124            if (parent.modCount != expectedModCount) {
125                throw new ConcurrentModificationException();
126            }
127        }
128
129        protected ReferenceEntry<K, V> currentEntry() {
130            checkMod();
131            return current;
132        }
133
134        public boolean hasNext() {
135            checkMod();
136            while (nextNull()) {
137                ReferenceEntry<K, V> e = next;
138                int i = index;
139                while (e == null && i > 0) {
140                    i--;
141                    e = (ReferenceEntry<K, V>) parent.data[i];
142                }
143                next = e;
144                index = i;
145                if (e == null) {
146                    return false;
147                }
148                nextKey = e.getKey();
149                nextValue = e.getValue();
150                if (nextNull()) {
151                    next = next.next();
152                }
153            }
154            return true;
155        }
156
157        protected ReferenceEntry<K, V> nextEntry() {
158            checkMod();
159            if (nextNull() && !hasNext()) {
160                throw new NoSuchElementException();
161            }
162            current = next;
163            next = next.next();
164            currentKey = nextKey;
165            currentValue = nextValue;
166            nextKey = null;
167            nextValue = null;
168            return current;
169        }
170
171        private boolean nextNull() {
172            return nextKey == null || nextValue == null;
173        }
174
175        public void remove() {
176            checkMod();
177            if (current == null) {
178                throw new IllegalStateException();
179            }
180            parent.remove(currentKey);
181            current = null;
182            currentKey = null;
183            currentValue = null;
184            expectedModCount = parent.modCount;
185        }
186    }
187
188    /**
189     * A MapEntry implementation for the map.
190     * <p>
191     * If getKey() or getValue() returns null, it means
192     * the mapping is stale and should be removed.
193     * </p>
194     *
195     * @param <K> the type of the keys
196     * @param <V> the type of the values
197     * @since 3.1
198     */
199    protected static class ReferenceEntry<K, V> extends HashEntry<K, V> {
200        /** The parent map */
201        private final AbstractReferenceMap<K, V> parent;
202
203        /**
204         * Creates a new entry object for the ReferenceMap.
205         *
206         * @param parent  the parent map
207         * @param next  the next entry in the hash bucket
208         * @param hashCode  the hash code of the key
209         * @param key  the key
210         * @param value  the value
211         */
212        public ReferenceEntry(final AbstractReferenceMap<K, V> parent, final HashEntry<K, V> next,
213                              final int hashCode, final K key, final V value) {
214            super(next, hashCode, null, null);
215            this.parent = parent;
216            this.key = toReference(parent.keyType, key, hashCode);
217            this.value = toReference(parent.valueType, value, hashCode); // the key hashCode is passed in deliberately
218        }
219
220        /**
221         * Compares this map entry to another.
222         * <p>
223         * This implementation uses {@code isEqualKey} and
224         * {@code isEqualValue} on the main map for comparison.
225         * </p>
226         *
227         * @param obj  the other map entry to compare to
228         * @return true if equal, false if not
229         */
230        @Override
231        public boolean equals(final Object obj) {
232            if (obj == this) {
233                return true;
234            }
235            if (!(obj instanceof Map.Entry)) {
236                return false;
237            }
238
239            final Map.Entry<?, ?> entry = (Map.Entry<?, ?>) obj;
240            final Object entryKey = entry.getKey();  // convert to hard reference
241            final Object entryValue = entry.getValue();  // convert to hard reference
242            if (entryKey == null || entryValue == null) {
243                return false;
244            }
245            // compare using map methods, aiding identity subclass
246            // note that key is direct access and value is via method
247            return parent.isEqualKey(entryKey, key) &&
248                   parent.isEqualValue(entryValue, getValue());
249        }
250
251        /**
252         * Gets the key from the entry.
253         * This method dereferences weak and soft keys and thus may return null.
254         *
255         * @return the key, which may be null if it was garbage collected
256         */
257        @Override
258        @SuppressWarnings("unchecked")
259        public K getKey() {
260            return (K) (parent.keyType == ReferenceStrength.HARD ? key : ((Reference<K>) key).get());
261        }
262
263        /**
264         * Gets the value from the entry.
265         * This method dereferences weak and soft value and thus may return null.
266         *
267         * @return the value, which may be null if it was garbage collected
268         */
269        @Override
270        @SuppressWarnings("unchecked")
271        public V getValue() {
272            return (V) (parent.valueType == ReferenceStrength.HARD ? value : ((Reference<V>) value).get());
273        }
274
275        /**
276         * Gets the hash code of the entry using temporary hard references.
277         * <p>
278         * This implementation uses {@code hashEntry} on the main map.
279         *
280         * @return the hash code of the entry
281         */
282        @Override
283        public int hashCode() {
284            return parent.hashEntry(getKey(), getValue());
285        }
286
287        /**
288         * Gets the next entry in the bucket.
289         *
290         * @return the next entry in the bucket
291         */
292        protected ReferenceEntry<K, V> next() {
293            return (ReferenceEntry<K, V>) next;
294        }
295
296        /**
297         * This method can be overridden to provide custom logic to purge value
298         */
299        protected void nullValue() {
300            value = null;
301        }
302
303        /**
304         * This is the callback for custom "after purge" logic
305         */
306        protected void onPurge() {
307            // empty
308        }
309
310        /**
311         * Purges the specified reference
312         * @param ref  the reference to purge
313         * @return true or false
314         */
315        protected boolean purge(final Reference<?> ref) {
316            boolean r = parent.keyType != ReferenceStrength.HARD && key == ref;
317            r = r || parent.valueType != ReferenceStrength.HARD && value == ref;
318            if (r) {
319                if (parent.keyType != ReferenceStrength.HARD) {
320                    ((Reference<?>) key).clear();
321                }
322                if (parent.valueType != ReferenceStrength.HARD) {
323                    ((Reference<?>) value).clear();
324                } else if (parent.purgeValues) {
325                    nullValue();
326                }
327            }
328            return r;
329        }
330
331        /**
332         * Sets the value of the entry.
333         *
334         * @param value  the object to store
335         * @return the previous value
336         */
337        @Override
338        @SuppressWarnings("unchecked")
339        public V setValue(final V value) {
340            final V old = getValue();
341            if (parent.valueType != ReferenceStrength.HARD) {
342                ((Reference<V>) this.value).clear();
343            }
344            this.value = toReference(parent.valueType, value, hashCode);
345            return old;
346        }
347
348        /**
349         * Constructs a reference of the given type to the given referent.
350         * The reference is registered with the queue for later purging.
351         *
352         * @param <T> the type of the referenced object
353         * @param type  HARD, SOFT or WEAK
354         * @param referent  the object to refer to
355         * @param hash  the hash code of the <em>key</em> of the mapping;
356         *    this number might be different from referent.hashCode() if
357         *    the referent represents a value and not a key
358         * @return the reference to the object
359         */
360        protected <T> Object toReference(final ReferenceStrength type, final T referent, final int hash) {
361            switch (type) {
362            case HARD:
363                return referent;
364            case SOFT:
365                return new SoftRef<>(hash, referent, parent.queue);
366            case WEAK:
367                return new WeakRef<>(hash, referent, parent.queue);
368            default:
369                break;
370            }
371            throw new Error();
372        }
373    }
374
375    /**
376     * EntrySet implementation.
377     */
378    static class ReferenceEntrySet<K, V> extends EntrySet<K, V> {
379
380        protected ReferenceEntrySet(final AbstractHashedMap<K, V> parent) {
381            super(parent);
382        }
383
384        @Override
385        public Object[] toArray() {
386            return toArray(new Object[size()]);
387        }
388
389        @Override
390        public <T> T[] toArray(final T[] arr) {
391            // special implementation to handle disappearing entries
392            final ArrayList<Map.Entry<K, V>> list = new ArrayList<>(size());
393            for (final Map.Entry<K, V> entry : this) {
394                list.add(new DefaultMapEntry<>(entry));
395            }
396            return list.toArray(arr);
397        }
398    }
399
400    /**
401     * The EntrySet iterator.
402     */
403    static class ReferenceEntrySetIterator<K, V>
404            extends ReferenceBaseIterator<K, V> implements Iterator<Map.Entry<K, V>> {
405
406        ReferenceEntrySetIterator(final AbstractReferenceMap<K, V> parent) {
407            super(parent);
408        }
409
410        @Override
411        public Map.Entry<K, V> next() {
412            return nextEntry();
413        }
414
415    }
416
417    /**
418     * KeySet implementation.
419     */
420    static class ReferenceKeySet<K> extends KeySet<K> {
421
422        protected ReferenceKeySet(final AbstractHashedMap<K, ?> parent) {
423            super(parent);
424        }
425
426        @Override
427        public Object[] toArray() {
428            return toArray(new Object[size()]);
429        }
430
431        @Override
432        public <T> T[] toArray(final T[] arr) {
433            // special implementation to handle disappearing keys
434            final List<K> list = new ArrayList<>(size());
435            forEach(list::add);
436            return list.toArray(arr);
437        }
438    }
439
440    /**
441     * The keySet iterator.
442     */
443    static class ReferenceKeySetIterator<K> extends ReferenceBaseIterator<K, Object> implements Iterator<K> {
444
445        @SuppressWarnings("unchecked")
446        ReferenceKeySetIterator(final AbstractReferenceMap<K, ?> parent) {
447            super((AbstractReferenceMap<K, Object>) parent);
448        }
449
450        @Override
451        public K next() {
452            return nextEntry().getKey();
453        }
454    }
455
456    /**
457     * The MapIterator implementation.
458     */
459    static class ReferenceMapIterator<K, V> extends ReferenceBaseIterator<K, V> implements MapIterator<K, V> {
460
461        protected ReferenceMapIterator(final AbstractReferenceMap<K, V> parent) {
462            super(parent);
463        }
464
465        @Override
466        public K getKey() {
467            final HashEntry<K, V> current = currentEntry();
468            if (current == null) {
469                throw new IllegalStateException(GETKEY_INVALID);
470            }
471            return current.getKey();
472        }
473
474        @Override
475        public V getValue() {
476            final HashEntry<K, V> current = currentEntry();
477            if (current == null) {
478                throw new IllegalStateException(GETVALUE_INVALID);
479            }
480            return current.getValue();
481        }
482
483        @Override
484        public K next() {
485            return nextEntry().getKey();
486        }
487
488        @Override
489        public V setValue(final V value) {
490            final HashEntry<K, V> current = currentEntry();
491            if (current == null) {
492                throw new IllegalStateException(SETVALUE_INVALID);
493            }
494            return current.setValue(value);
495        }
496    }
497
498    /**
499     * Enumerates reference types.
500     */
501    public enum ReferenceStrength {
502
503        /**
504         * Hard reference type.
505         */
506        HARD(0),
507
508        /**
509         * Soft reference type.
510         */
511        SOFT(1),
512
513        /**
514         * Weak reference type.
515         */
516        WEAK(2);
517
518        /**
519         * Resolve enum from int.
520         * @param value  the int value
521         * @return ReferenceType
522         * @throws IllegalArgumentException if the specified value is invalid.
523         */
524        public static ReferenceStrength resolve(final int value) {
525            switch (value) {
526            case 0:
527                return HARD;
528            case 1:
529                return SOFT;
530            case 2:
531                return WEAK;
532            default:
533                throw new IllegalArgumentException();
534            }
535        }
536
537        /** Value */
538        public final int value;
539
540        ReferenceStrength(final int value) {
541            this.value = value;
542        }
543
544    }
545
546    /**
547     * Values implementation.
548     */
549    static class ReferenceValues<V> extends Values<V> {
550
551        protected ReferenceValues(final AbstractHashedMap<?, V> parent) {
552            super(parent);
553        }
554
555        @Override
556        public Object[] toArray() {
557            return toArray(new Object[size()]);
558        }
559
560        @Override
561        public <T> T[] toArray(final T[] arr) {
562            // special implementation to handle disappearing values
563            final List<V> list = new ArrayList<>(size());
564            forEach(list::add);
565            return list.toArray(arr);
566        }
567    }
568
569    /**
570     * The values iterator.
571     */
572    static class ReferenceValuesIterator<V> extends ReferenceBaseIterator<Object, V> implements Iterator<V> {
573
574        @SuppressWarnings("unchecked")
575        ReferenceValuesIterator(final AbstractReferenceMap<?, V> parent) {
576            super((AbstractReferenceMap<Object, V>) parent);
577        }
578
579        @Override
580        public V next() {
581            return nextEntry().getValue();
582        }
583    }
584
585    /**
586     * A soft reference holder.
587     */
588    static class SoftRef<T> extends SoftReference<T> {
589        /** The hashCode of the key (even if the reference points to a value) */
590        private final int hash;
591
592        SoftRef(final int hash, final T r, final ReferenceQueue<? super T> q) {
593            super(r, q);
594            this.hash = hash;
595        }
596
597        @Override
598        public boolean equals(final Object obj) {
599            if (this == obj) {
600                return true;
601            }
602            if (obj == null) {
603                return false;
604            }
605            if (getClass() != obj.getClass()) {
606                return false;
607            }
608            final SoftRef<?> other = (SoftRef<?>) obj;
609            return hash == other.hash;
610        }
611
612        @Override
613        public int hashCode() {
614            return hash;
615        }
616    }
617
618    /**
619     * A weak reference holder.
620     */
621    static class WeakRef<T> extends WeakReference<T> {
622        /** The hashCode of the key (even if the reference points to a value) */
623        private final int hash;
624
625        WeakRef(final int hash, final T r, final ReferenceQueue<? super T> q) {
626            super(r, q);
627            this.hash = hash;
628        }
629
630        @Override
631        public boolean equals(final Object obj) {
632            if (this == obj) {
633                return true;
634            }
635            if (obj == null) {
636                return false;
637            }
638            if (getClass() != obj.getClass()) {
639                return false;
640            }
641            final WeakRef<?> other = (WeakRef<?>) obj;
642            return hash == other.hash;
643        }
644
645        @Override
646        public int hashCode() {
647            return hash;
648        }
649    }
650
651    /**
652     * The reference type for keys.
653     */
654    private ReferenceStrength keyType;
655
656    /**
657     * The reference type for values.
658     */
659    private ReferenceStrength valueType;
660
661    /**
662     * Should the value be automatically purged when the associated key has been collected?
663     */
664    private boolean purgeValues;
665
666    /**
667     * ReferenceQueue used to eliminate stale mappings.
668     * See purge.
669     */
670    private transient ReferenceQueue<Object> queue;
671
672    /**
673     * Constructor used during deserialization.
674     */
675    protected AbstractReferenceMap() {
676    }
677
678    /**
679     * Constructs a new empty map with the specified reference types,
680     * load factor and initial capacity.
681     *
682     * @param keyType  the type of reference to use for keys;
683     *   must be {@link ReferenceStrength#HARD HARD},
684     *   {@link ReferenceStrength#SOFT SOFT},
685     *   {@link ReferenceStrength#WEAK WEAK}
686     * @param valueType  the type of reference to use for values;
687     *   must be {@link ReferenceStrength#HARD},
688     *   {@link ReferenceStrength#SOFT SOFT},
689     *   {@link ReferenceStrength#WEAK WEAK}
690     * @param capacity  the initial capacity for the map
691     * @param loadFactor  the load factor for the map
692     * @param purgeValues  should the value be automatically purged when the
693     *   key is garbage collected
694     */
695    protected AbstractReferenceMap(
696            final ReferenceStrength keyType, final ReferenceStrength valueType, final int capacity,
697            final float loadFactor, final boolean purgeValues) {
698        super(capacity, loadFactor);
699        this.keyType = keyType;
700        this.valueType = valueType;
701        this.purgeValues = purgeValues;
702    }
703
704    /**
705     * Clears this map.
706     */
707    @Override
708    public void clear() {
709        super.clear();
710        // Drain the queue
711        while (queue.poll() != null) { // NOPMD
712        }
713    }
714
715    /**
716     * Checks whether the map contains the specified key.
717     *
718     * @param key  the key to search for
719     * @return true if the map contains the key
720     */
721    @Override
722    public boolean containsKey(final Object key) {
723        purgeBeforeRead();
724        final Entry<K, V> entry = getEntry(key);
725        if (entry == null) {
726            return false;
727        }
728        return entry.getValue() != null;
729    }
730
731    /**
732     * Checks whether the map contains the specified value.
733     *
734     * @param value  the value to search for
735     * @return true if the map contains the value
736     */
737    @Override
738    public boolean containsValue(final Object value) {
739        purgeBeforeRead();
740        if (value == null) {
741            return false;
742        }
743        return super.containsValue(value);
744    }
745
746    /**
747     * Creates a ReferenceEntry instead of a HashEntry.
748     *
749     * @param next  the next entry in sequence
750     * @param hashCode  the hash code to use
751     * @param key  the key to store
752     * @param value  the value to store
753     * @return the newly created entry
754     */
755    @Override
756    protected ReferenceEntry<K, V> createEntry(final HashEntry<K, V> next, final int hashCode,
757                                               final K key, final V value) {
758        return new ReferenceEntry<>(this, next, hashCode, key, value);
759    }
760
761    /**
762     * Creates an entry set iterator.
763     *
764     * @return the entrySet iterator
765     */
766    @Override
767    protected Iterator<Map.Entry<K, V>> createEntrySetIterator() {
768        return new ReferenceEntrySetIterator<>(this);
769    }
770
771    /**
772     * Creates a key set iterator.
773     *
774     * @return the keySet iterator
775     */
776    @Override
777    protected Iterator<K> createKeySetIterator() {
778        return new ReferenceKeySetIterator<>(this);
779    }
780
781    /**
782     * Creates a values iterator.
783     *
784     * @return the values iterator
785     */
786    @Override
787    protected Iterator<V> createValuesIterator() {
788        return new ReferenceValuesIterator<>(this);
789    }
790
791    /**
792     * Replaces the superclass method to read the state of this class.
793     * <p>
794     * Serialization is not one of the JDK's nicest topics. Normal serialization will
795     * initialize the superclass before the subclass. Sometimes however, this isn't
796     * what you want, as in this case the {@code put()} method on read can be
797     * affected by subclass state.
798     * </p>
799     * <p>
800     * The solution adopted here is to deserialize the state data of this class in
801     * this protected method. This method must be called by the
802     * {@code readObject()} of the first serializable subclass.
803     * </p>
804     * <p>
805     * Subclasses may override if the subclass has a specific field that must be present
806     * before {@code put()} or {@code calculateThreshold()} will work correctly.
807     * </p>
808     *
809     * @param in  the input stream
810     * @throws IOException if an error occurs while reading from the stream
811     * @throws ClassNotFoundException if an object read from the stream cannot be loaded
812     */
813    @Override
814    @SuppressWarnings("unchecked")
815    protected void doReadObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
816        keyType = ReferenceStrength.resolve(in.readInt());
817        valueType = ReferenceStrength.resolve(in.readInt());
818        purgeValues = in.readBoolean();
819        loadFactor = in.readFloat();
820        final int capacity = in.readInt();
821        init();
822        data = new HashEntry[capacity];
823
824        // COLLECTIONS-599: Calculate threshold before populating, otherwise it will be 0
825        // when it hits AbstractHashedMap.checkCapacity() and so will unnecessarily
826        // double up the size of the "data" array during population.
827        //
828        // NB: AbstractHashedMap.doReadObject() DOES calculate the threshold before populating.
829        //
830        threshold = calculateThreshold(data.length, loadFactor);
831
832        while (true) {
833            final K key = (K) in.readObject();
834            if (key == null) {
835                break;
836            }
837            final V value = (V) in.readObject();
838            put(key, value);
839        }
840        // do not call super.doReadObject() as code there doesn't work for reference map
841    }
842
843    /**
844     * Replaces the superclass method to store the state of this class.
845     * <p>
846     * Serialization is not one of the JDK's nicest topics. Normal serialization will
847     * initialize the superclass before the subclass. Sometimes however, this isn't
848     * what you want, as in this case the {@code put()} method on read can be
849     * affected by subclass state.
850     * </p>
851     * <p>
852     * The solution adopted here is to serialize the state data of this class in
853     * this protected method. This method must be called by the
854     * {@code writeObject()} of the first serializable subclass.
855     * </p>
856     * <p>
857     * Subclasses may override if they have a specific field that must be present
858     * on read before this implementation will work. Generally, the read determines
859     * what must be serialized here, if anything.
860     * </p>
861     *
862     * @param out  the output stream
863     * @throws IOException if an error occurs while writing to the stream
864     */
865    @Override
866    protected void doWriteObject(final ObjectOutputStream out) throws IOException {
867        out.writeInt(keyType.value);
868        out.writeInt(valueType.value);
869        out.writeBoolean(purgeValues);
870        out.writeFloat(loadFactor);
871        out.writeInt(data.length);
872        for (final MapIterator<K, V> it = mapIterator(); it.hasNext();) {
873            out.writeObject(it.next());
874            out.writeObject(it.getValue());
875        }
876        out.writeObject(null);  // null terminate map
877        // do not call super.doWriteObject() as code there doesn't work for reference map
878    }
879
880    /**
881     * Returns a set view of this map's entries.
882     * An iterator returned entry is valid until {@code next()} is called again.
883     * The {@code setValue()} method on the {@code toArray} entries has no effect.
884     *
885     * @return a set view of this map's entries
886     */
887    @Override
888    public Set<Map.Entry<K, V>> entrySet() {
889        if (entrySet == null) {
890            entrySet = new ReferenceEntrySet<>(this);
891        }
892        return entrySet;
893    }
894
895    /**
896     * Gets the value mapped to the key specified.
897     *
898     * @param key  the key
899     * @return the mapped value, null if no match
900     */
901    @Override
902    public V get(final Object key) {
903        purgeBeforeRead();
904        final Entry<K, V> entry = getEntry(key);
905        if (entry == null) {
906            return null;
907        }
908        return entry.getValue();
909    }
910
911    /**
912     * Gets the entry mapped to the key specified.
913     *
914     * @param key  the key
915     * @return the entry, null if no match
916     */
917    @Override
918    protected HashEntry<K, V> getEntry(final Object key) {
919        if (key == null) {
920            return null;
921        }
922        return super.getEntry(key);
923    }
924
925    /**
926     * Gets the hash code for a MapEntry.
927     * Subclasses can override this, for example to use the identityHashCode.
928     *
929     * @param key  the key to get a hash code for, may be null
930     * @param value  the value to get a hash code for, may be null
931     * @return the hash code, as per the MapEntry specification
932     */
933    protected int hashEntry(final Object key, final Object value) {
934        return (key == null ? 0 : key.hashCode()) ^
935               (value == null ? 0 : value.hashCode());
936    }
937
938    /**
939     * Initialize this subclass during construction, cloning or deserialization.
940     */
941    @Override
942    protected void init() {
943        queue = new ReferenceQueue<>();
944    }
945
946    /**
947     * Checks whether the map is currently empty.
948     *
949     * @return true if the map is currently size zero
950     */
951    @Override
952    public boolean isEmpty() {
953        purgeBeforeRead();
954        return super.isEmpty();
955    }
956
957    /**
958     * Compares two keys, in internal converted form, to see if they are equal.
959     * <p>
960     * This implementation converts the key from the entry to a real reference
961     * before comparison.
962     * </p>
963     *
964     * @param key1  the first key to compare passed in from outside
965     * @param key2  the second key extracted from the entry via {@code entry.key}
966     * @return true if equal
967     */
968    @Override
969    @SuppressWarnings("unchecked")
970    protected boolean isEqualKey(final Object key1, Object key2) {
971        key2 = keyType == ReferenceStrength.HARD ? key2 : ((Reference<K>) key2).get();
972        return Objects.equals(key1, key2);
973    }
974
975    /**
976     * Provided protected read-only access to the key type.
977     *
978     * @param type the type to check against.
979     * @return true if keyType has the specified type
980     */
981    protected boolean isKeyType(final ReferenceStrength type) {
982        return keyType == type;
983    }
984
985    /**
986     * Provided protected read-only access to the value type.
987     *
988     * @param type the type to check against.
989     * @return true if valueType has the specified type
990     */
991    protected boolean isValueType(final ReferenceStrength type) {
992        return valueType == type;
993    }
994
995    /**
996     * Returns a set view of this map's keys.
997     *
998     * @return a set view of this map's keys
999     */
1000    @Override
1001    public Set<K> keySet() {
1002        if (keySet == null) {
1003            keySet = new ReferenceKeySet<>(this);
1004        }
1005        return keySet;
1006    }
1007
1008    /**
1009     * Gets a MapIterator over the reference map.
1010     * The iterator only returns valid key/value pairs.
1011     *
1012     * @return a map iterator
1013     */
1014    @Override
1015    public MapIterator<K, V> mapIterator() {
1016        return new ReferenceMapIterator<>(this);
1017    }
1018
1019    /**
1020     * Purges stale mappings from this map.
1021     * <p>
1022     * Note that this method is not synchronized!  Special
1023     * care must be taken if, for instance, you want stale
1024     * mappings to be removed on a periodic basis by some
1025     * background thread.
1026     * </p>
1027     */
1028    protected void purge() {
1029        Reference<?> ref = queue.poll();
1030        while (ref != null) {
1031            purge(ref);
1032            ref = queue.poll();
1033        }
1034    }
1035
1036    /**
1037     * Purges the specified reference.
1038     *
1039     * @param ref  the reference to purge
1040     */
1041    protected void purge(final Reference<?> ref) {
1042        // The hashCode of the reference is the hashCode of the
1043        // mapping key, even if the reference refers to the
1044        // mapping value...
1045        final int hash = ref.hashCode();
1046        final int index = hashIndex(hash, data.length);
1047        HashEntry<K, V> previous = null;
1048        HashEntry<K, V> entry = data[index];
1049        while (entry != null) {
1050            final ReferenceEntry<K, V> refEntry = (ReferenceEntry<K, V>) entry;
1051            if (refEntry.purge(ref)) {
1052                if (previous == null) {
1053                    data[index] = entry.next;
1054                } else {
1055                    previous.next = entry.next;
1056                }
1057                size--;
1058                refEntry.onPurge();
1059                return;
1060            }
1061            previous = entry;
1062            entry = entry.next;
1063        }
1064
1065    }
1066
1067    // These two classes store the hashCode of the key of
1068    // the mapping, so that after they're dequeued a quick
1069    // lookup of the bucket in the table can occur.
1070
1071    /**
1072     * Purges stale mappings from this map before read operations.
1073     * <p>
1074     * This implementation calls {@link #purge()} to maintain a consistent state.
1075     */
1076    protected void purgeBeforeRead() {
1077        purge();
1078    }
1079
1080    /**
1081     * Purges stale mappings from this map before write operations.
1082     * <p>
1083     * This implementation calls {@link #purge()} to maintain a consistent state.
1084     * </p>
1085     */
1086    protected void purgeBeforeWrite() {
1087        purge();
1088    }
1089
1090    /**
1091     * Puts a key-value mapping into this map.
1092     * Neither the key nor the value may be null.
1093     *
1094     * @param key  the key to add, must not be null
1095     * @param value  the value to add, must not be null
1096     * @return the value previously mapped to this key, null if none
1097     * @throws NullPointerException if either the key or value is null
1098     */
1099    @Override
1100    public V put(final K key, final V value) {
1101        Objects.requireNonNull(key, "key");
1102        Objects.requireNonNull(value, "value");
1103        purgeBeforeWrite();
1104        return super.put(key, value);
1105    }
1106
1107    /**
1108     * Removes the specified mapping from this map.
1109     *
1110     * @param key  the mapping to remove
1111     * @return the value mapped to the removed key, null if key not in map
1112     */
1113    @Override
1114    public V remove(final Object key) {
1115        if (key == null) {
1116            return null;
1117        }
1118        purgeBeforeWrite();
1119        return super.remove(key);
1120    }
1121
1122    /**
1123     * Gets the size of the map.
1124     *
1125     * @return the size
1126     */
1127    @Override
1128    public int size() {
1129        purgeBeforeRead();
1130        return super.size();
1131    }
1132
1133    /**
1134     * Returns a collection view of this map's values.
1135     *
1136     * @return a set view of this map's values
1137     */
1138    @Override
1139    public Collection<V> values() {
1140        if (values == null) {
1141            values = new ReferenceValues<>(this);
1142        }
1143        return values;
1144    }
1145}