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 *      https://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.bidimap;
018
019import java.util.Collection;
020import java.util.Iterator;
021import java.util.Map;
022import java.util.Objects;
023import java.util.Set;
024import java.util.function.Predicate;
025
026import org.apache.commons.collections4.BidiMap;
027import org.apache.commons.collections4.MapIterator;
028import org.apache.commons.collections4.ResettableIterator;
029import org.apache.commons.collections4.collection.AbstractCollectionDecorator;
030import org.apache.commons.collections4.iterators.AbstractIteratorDecorator;
031import org.apache.commons.collections4.keyvalue.AbstractMapEntryDecorator;
032
033/**
034 * Abstract {@link BidiMap} implemented using two maps.
035 * <p>
036 * An implementation can be written simply by implementing the
037 * {@link #createBidiMap(Map, Map, BidiMap)} method.
038 * </p>
039 *
040 * @param <K> The type of the keys in the map
041 * @param <V> The type of the values in the map
042 * @see DualHashBidiMap
043 * @see DualTreeBidiMap
044 * @since 3.0
045 */
046public abstract class AbstractDualBidiMap<K, V> implements BidiMap<K, V> {
047
048    /**
049     * Inner class MapIterator.
050     *
051     * @param <K> The type of the keys.
052     * @param <V> The type of the values.
053     */
054    protected static class BidiMapIterator<K, V> implements MapIterator<K, V>, ResettableIterator<K> {
055
056        /** The parent map */
057        protected final AbstractDualBidiMap<K, V> parent;
058
059        /** The iterator being wrapped */
060        protected Iterator<Map.Entry<K, V>> iterator;
061
062        /** The last returned entry */
063        protected Map.Entry<K, V> last;
064
065        /** Whether remove is allowed at present */
066        protected boolean canRemove;
067
068        /**
069         * Constructs a new instance.
070         *
071         * @param parent  The parent map
072         */
073        protected BidiMapIterator(final AbstractDualBidiMap<K, V> parent) {
074            this.parent = parent;
075            this.iterator = parent.normalMap.entrySet().iterator();
076        }
077
078        @Override
079        public K getKey() {
080            if (last == null) {
081                throw new IllegalStateException(
082                        "Iterator getKey() can only be called after next() and before remove()");
083            }
084            return last.getKey();
085        }
086
087        @Override
088        public V getValue() {
089            if (last == null) {
090                throw new IllegalStateException(
091                        "Iterator getValue() can only be called after next() and before remove()");
092            }
093            return last.getValue();
094        }
095
096        @Override
097        public boolean hasNext() {
098            return iterator.hasNext();
099        }
100
101        @Override
102        public K next() {
103            last = iterator.next();
104            canRemove = true;
105            return last.getKey();
106        }
107
108        @Override
109        public void remove() {
110            if (!canRemove) {
111                throw new IllegalStateException("Iterator remove() can only be called once after next()");
112            }
113            // store value as remove may change the entry in the decorator (for example TreeMap)
114            final V value = last.getValue();
115            iterator.remove();
116            parent.reverseMap.remove(value);
117            last = null;
118            canRemove = false;
119        }
120
121        @Override
122        public void reset() {
123            iterator = parent.normalMap.entrySet().iterator();
124            last = null;
125            canRemove = false;
126        }
127
128        @Override
129        public V setValue(final V value) {
130            if (last == null) {
131                throw new IllegalStateException(
132                        "Iterator setValue() can only be called after next() and before remove()");
133            }
134            if (parent.reverseMap.containsKey(value) &&
135                parent.reverseMap.get(value) != last.getKey()) {
136                throw new IllegalArgumentException(
137                        "Cannot use setValue() when the object being set is already in the map");
138            }
139            return parent.put(last.getKey(), value);
140        }
141
142        @Override
143        public String toString() {
144            if (last != null) {
145                return "MapIterator[" + getKey() + "=" + getValue() + "]";
146            }
147            return "MapIterator[]";
148        }
149    }
150
151    /**
152     * Inner class EntrySet.
153     *
154     * @param <K> The type of the keys.
155     * @param <V> The type of the values.
156     */
157    protected static class EntrySet<K, V> extends View<K, V, Map.Entry<K, V>> implements Set<Map.Entry<K, V>> {
158
159        /** Serialization version */
160        private static final long serialVersionUID = 4040410962603292348L;
161
162        /**
163         * Constructs a new instance.
164         *
165         * @param parent  The parent BidiMap
166         */
167        protected EntrySet(final AbstractDualBidiMap<K, V> parent) {
168            super(parent.normalMap.entrySet(), parent);
169        }
170
171        @Override
172        public Iterator<Map.Entry<K, V>> iterator() {
173            return parent.createEntrySetIterator(super.iterator());
174        }
175
176        @Override
177        public boolean remove(final Object obj) {
178            if (!(obj instanceof Map.Entry)) {
179                return false;
180            }
181            final Map.Entry<?, ?> entry = (Map.Entry<?, ?>) obj;
182            final Object key = entry.getKey();
183            if (parent.containsKey(key)) {
184                final V value = parent.normalMap.get(key);
185                if (Objects.equals(value, entry.getValue())) {
186                    parent.normalMap.remove(key);
187                    parent.reverseMap.remove(value);
188                    return true;
189                }
190            }
191            return false;
192        }
193    }
194
195    /**
196     * Inner class EntrySetIterator.
197     *
198     * @param <K> The type of the keys.
199     * @param <V> The type of the values.
200     */
201    protected static class EntrySetIterator<K, V> extends AbstractIteratorDecorator<Map.Entry<K, V>> {
202
203        /** The parent map */
204        protected final AbstractDualBidiMap<K, V> parent;
205
206        /** The last returned entry */
207        protected Map.Entry<K, V> last;
208
209        /** Whether remove is allowed at present */
210        protected boolean canRemove;
211
212        /**
213         * Constructs a new instance.
214         *
215         * @param iterator  The iterator to decorate
216         * @param parent  The parent map
217         */
218        protected EntrySetIterator(final Iterator<Map.Entry<K, V>> iterator, final AbstractDualBidiMap<K, V> parent) {
219            super(iterator);
220            this.parent = parent;
221        }
222
223        @Override
224        public Map.Entry<K, V> next() {
225            last = new MapEntry<>(super.next(), parent);
226            canRemove = true;
227            return last;
228        }
229
230        @Override
231        public void remove() {
232            if (!canRemove) {
233                throw new IllegalStateException("Iterator remove() can only be called once after next()");
234            }
235            // store value as remove may change the entry in the decorator (for example TreeMap)
236            final Object value = last.getValue();
237            super.remove();
238            parent.reverseMap.remove(value);
239            last = null;
240            canRemove = false;
241        }
242    }
243
244    /**
245     * Inner class KeySet.
246     *
247     * @param <K> The type of elements maintained by this set
248     */
249    protected static class KeySet<K> extends View<K, Object, K> implements Set<K> {
250
251        /** Serialization version */
252        private static final long serialVersionUID = -7107935777385040694L;
253
254        /**
255         * Constructs a new instance.
256         *
257         * @param parent  The parent BidiMap
258         */
259        @SuppressWarnings("unchecked")
260        protected KeySet(final AbstractDualBidiMap<K, ?> parent) {
261            super(parent.normalMap.keySet(), (AbstractDualBidiMap<K, Object>) parent);
262        }
263
264        @Override
265        public boolean contains(final Object key) {
266            return parent.normalMap.containsKey(key);
267        }
268
269        @Override
270        public Iterator<K> iterator() {
271            return parent.createKeySetIterator(super.iterator());
272        }
273
274        @Override
275        public boolean remove(final Object key) {
276            if (parent.normalMap.containsKey(key)) {
277                final Object value = parent.normalMap.remove(key);
278                parent.reverseMap.remove(value);
279                return true;
280            }
281            return false;
282        }
283    }
284
285    /**
286     * Inner class KeySetIterator.
287     *
288     * @param <K> The key type.
289     */
290    protected static class KeySetIterator<K> extends AbstractIteratorDecorator<K> {
291
292        /** The parent map */
293        protected final AbstractDualBidiMap<K, ?> parent;
294
295        /** The last returned key */
296        protected K lastKey;
297
298        /** Whether remove is allowed at present */
299        protected boolean canRemove;
300
301        /**
302         * Constructs a new instance.
303         *
304         * @param iterator  The iterator to decorate
305         * @param parent  The parent map
306         */
307        protected KeySetIterator(final Iterator<K> iterator, final AbstractDualBidiMap<K, ?> parent) {
308            super(iterator);
309            this.parent = parent;
310        }
311
312        @Override
313        public K next() {
314            lastKey = super.next();
315            canRemove = true;
316            return lastKey;
317        }
318
319        @Override
320        public void remove() {
321            if (!canRemove) {
322                throw new IllegalStateException("Iterator remove() can only be called once after next()");
323            }
324            final Object value = parent.normalMap.get(lastKey);
325            super.remove();
326            parent.reverseMap.remove(value);
327            lastKey = null;
328            canRemove = false;
329        }
330    }
331
332    /**
333     * Inner class MapEntry.
334     *
335     * @param <K> The type of the keys.
336     * @param <V> The type of the values.
337     */
338    protected static class MapEntry<K, V> extends AbstractMapEntryDecorator<K, V> {
339
340        /** The parent map */
341        protected final AbstractDualBidiMap<K, V> parent;
342
343        /**
344         * Constructs a new instance.
345         *
346         * @param entry  The entry to decorate
347         * @param parent  The parent map
348         */
349        protected MapEntry(final Map.Entry<K, V> entry, final AbstractDualBidiMap<K, V> parent) {
350            super(entry);
351            this.parent = parent;
352        }
353
354        @Override
355        public V setValue(final V value) {
356            final K key = getKey();
357            if (parent.reverseMap.containsKey(value) &&
358                parent.reverseMap.get(value) != key) {
359                throw new IllegalArgumentException(
360                        "Cannot use setValue() when the object being set is already in the map");
361            }
362            final V oldValue = parent.put(key, value);
363            super.setValue(value);
364            return oldValue;
365        }
366    }
367
368    /**
369     * Inner class Values.
370     *
371     * @param <V> The type of the values.
372     */
373    protected static class Values<V> extends View<Object, V, V> implements Set<V> {
374
375        /** Serialization version */
376        private static final long serialVersionUID = 4023777119829639864L;
377
378        /**
379         * Constructs a new instance.
380         *
381         * @param parent  The parent BidiMap
382         */
383        @SuppressWarnings("unchecked")
384        protected Values(final AbstractDualBidiMap<?, V> parent) {
385            super(parent.normalMap.values(), (AbstractDualBidiMap<Object, V>) parent);
386        }
387
388        @Override
389        public boolean contains(final Object value) {
390            return parent.reverseMap.containsKey(value);
391        }
392
393        @Override
394        public Iterator<V> iterator() {
395            return parent.createValuesIterator(super.iterator());
396        }
397
398        @Override
399        public boolean remove(final Object value) {
400            if (parent.reverseMap.containsKey(value)) {
401                final Object key = parent.reverseMap.remove(value);
402                parent.normalMap.remove(key);
403                return true;
404            }
405            return false;
406        }
407    }
408
409    /**
410     * Inner class ValuesIterator.
411     *
412     * @param <V> The value type.
413     */
414    protected static class ValuesIterator<V> extends AbstractIteratorDecorator<V> {
415
416        /** The parent map */
417        protected final AbstractDualBidiMap<Object, V> parent;
418
419        /** The last returned value */
420        protected V lastValue;
421
422        /** Whether remove is allowed at present */
423        protected boolean canRemove;
424
425        /**
426         * Constructs a new instance.
427         *
428         * @param iterator  The iterator to decorate
429         * @param parent  The parent map
430         */
431        @SuppressWarnings("unchecked")
432        protected ValuesIterator(final Iterator<V> iterator, final AbstractDualBidiMap<?, V> parent) {
433            super(iterator);
434            this.parent = (AbstractDualBidiMap<Object, V>) parent;
435        }
436
437        @Override
438        public V next() {
439            lastValue = super.next();
440            canRemove = true;
441            return lastValue;
442        }
443
444        @Override
445        public void remove() {
446            if (!canRemove) {
447                throw new IllegalStateException("Iterator remove() can only be called once after next()");
448            }
449            super.remove(); // removes from maps[0]
450            parent.reverseMap.remove(lastValue);
451            lastValue = null;
452            canRemove = false;
453        }
454    }
455
456    /**
457     * Inner class View.
458     *
459     * @param <K> The type of the keys in the map.
460     * @param <V> The type of the values in the map.
461     * @param <E> The type of the elements in the collection.
462     */
463    protected abstract static class View<K, V, E> extends AbstractCollectionDecorator<E> {
464
465        /** Generated serial version ID. */
466        private static final long serialVersionUID = 4621510560119690639L;
467
468        /** The parent map */
469        protected final AbstractDualBidiMap<K, V> parent;
470
471        /**
472         * Constructs a new instance.
473         *
474         * @param coll  The collection view being decorated
475         * @param parent  The parent BidiMap
476         */
477        protected View(final Collection<E> coll, final AbstractDualBidiMap<K, V> parent) {
478            super(coll);
479            this.parent = parent;
480        }
481
482        @Override
483        public void clear() {
484            parent.clear();
485        }
486
487        @Override
488        public boolean equals(final Object object) {
489            return object == this || decorated().equals(object);
490        }
491
492        @Override
493        public int hashCode() {
494            return decorated().hashCode();
495        }
496
497        @Override
498        public boolean removeAll(final Collection<?> coll) {
499            if (parent.isEmpty() || coll.isEmpty()) {
500                return false;
501            }
502            boolean modified = false;
503            for (final Object current : coll) {
504                modified |= remove(current);
505            }
506            return modified;
507        }
508
509        /**
510         * @since 4.4
511         */
512        @Override
513        public boolean removeIf(final Predicate<? super E> filter) {
514            if (parent.isEmpty() || Objects.isNull(filter)) {
515                return false;
516            }
517            boolean modified = false;
518            final Iterator<?> it = iterator();
519            while (it.hasNext()) {
520                @SuppressWarnings("unchecked")
521                final E e = (E) it.next();
522                if (filter.test(e)) {
523                    it.remove();
524                    modified = true;
525                }
526            }
527            return modified;
528        }
529
530        /**
531         * {@inheritDoc}
532         * <p>
533         * This implementation iterates over the elements of this bidi map, checking each element in
534         * turn to see if it's contained in {@code coll}. If it's not contained, it's removed
535         * from this bidi map. As a consequence, it is advised to use a collection type for
536         * {@code coll} that provides a fast (for example O(1)) implementation of
537         * {@link Collection#contains(Object)}.
538         */
539        @Override
540        public boolean retainAll(final Collection<?> coll) {
541            if (parent.isEmpty()) {
542                return false;
543            }
544            if (coll.isEmpty()) {
545                parent.clear();
546                return true;
547            }
548            boolean modified = false;
549            final Iterator<E> it = iterator();
550            while (it.hasNext()) {
551                if (!coll.contains(it.next())) {
552                    it.remove();
553                    modified = true;
554                }
555            }
556            return modified;
557        }
558    }
559
560    /**
561     * Normal delegate map.
562     */
563    transient Map<K, V> normalMap;
564
565    // Map delegation
566
567    /**
568     * Reverse delegate map.
569     */
570    transient Map<V, K> reverseMap;
571
572    /**
573     * Inverse view of this map.
574     */
575    transient BidiMap<V, K> inverseBidiMap;
576
577    /**
578     * View of the keys.
579     */
580    transient Set<K> keySet;
581
582    /**
583     * View of the values.
584     */
585    transient Set<V> values;
586
587    /**
588     * View of the entries.
589     */
590    transient Set<Map.Entry<K, V>> entrySet;
591
592    /**
593     * Creates an empty map, initialized by {@code createMap}.
594     * <p>
595     * This constructor remains in place for deserialization.
596     * All other usage is deprecated in favor of
597     * {@link #AbstractDualBidiMap(Map, Map)}.
598     */
599    protected AbstractDualBidiMap() {
600    }
601
602    /**
603     * Creates an empty map using the two maps specified as storage.
604     * <p>
605     * The two maps must be a matching pair, normal and reverse.
606     * They will typically both be empty.
607     * <p>
608     * Neither map is validated, so nulls may be passed in.
609     * If you choose to do this then the subclass constructor must populate
610     * the {@code maps[]} instance variable itself.
611     *
612     * @param normalMap  The normal direction map
613     * @param reverseMap  The reverse direction map
614     * @since 3.1
615     */
616    protected AbstractDualBidiMap(final Map<K, V> normalMap, final Map<V, K> reverseMap) {
617        this.normalMap = normalMap;
618        this.reverseMap = reverseMap;
619    }
620
621    // BidiMap changes
622
623    /**
624     * Constructs a map that decorates the specified maps,
625     * used by the subclass {@code createBidiMap} implementation.
626     *
627     * @param normalMap  The normal direction map
628     * @param reverseMap  The reverse direction map
629     * @param inverseBidiMap  The inverse BidiMap
630     */
631    protected AbstractDualBidiMap(final Map<K, V> normalMap, final Map<V, K> reverseMap,
632                                  final BidiMap<V, K> inverseBidiMap) {
633        this.normalMap = normalMap;
634        this.reverseMap = reverseMap;
635        this.inverseBidiMap = inverseBidiMap;
636    }
637
638    @Override
639    public void clear() {
640        normalMap.clear();
641        reverseMap.clear();
642    }
643
644    @Override
645    public boolean containsKey(final Object key) {
646        return normalMap.containsKey(key);
647    }
648
649    @Override
650    public boolean containsValue(final Object value) {
651        return reverseMap.containsKey(value);
652    }
653
654    /**
655     * Creates a new instance of the subclass.
656     *
657     * @param normalMap  The normal direction map
658     * @param reverseMap  The reverse direction map
659     * @param inverseMap  this map, which is the inverse in the new map
660     * @return The bidi map
661     */
662    protected abstract BidiMap<V, K> createBidiMap(Map<V, K> normalMap, Map<K, V> reverseMap, BidiMap<K, V> inverseMap);
663
664    /**
665     * Creates an entry set iterator.
666     * Subclasses can override this to return iterators with different properties.
667     *
668     * @param iterator  The iterator to decorate
669     * @return The entrySet iterator
670     */
671    protected Iterator<Map.Entry<K, V>> createEntrySetIterator(final Iterator<Map.Entry<K, V>> iterator) {
672        return new EntrySetIterator<>(iterator, this);
673    }
674
675    /**
676     * Creates a key set iterator.
677     * Subclasses can override this to return iterators with different properties.
678     *
679     * @param iterator  The iterator to decorate
680     * @return The keySet iterator
681     */
682    protected Iterator<K> createKeySetIterator(final Iterator<K> iterator) {
683        return new KeySetIterator<>(iterator, this);
684    }
685
686    /**
687     * Creates a values iterator.
688     * Subclasses can override this to return iterators with different properties.
689     *
690     * @param iterator  The iterator to decorate
691     * @return The values iterator
692     */
693    protected Iterator<V> createValuesIterator(final Iterator<V> iterator) {
694        return new ValuesIterator<>(iterator, this);
695    }
696
697    /**
698     * Gets an entrySet view of the map.
699     * Changes made on the set are reflected in the map.
700     * The set supports remove and clear but not add.
701     * <p>
702     * The Map Entry setValue() method only allow a new value to be set.
703     * If the value being set is already in the map, an IllegalArgumentException
704     * is thrown (as setValue cannot change the size of the map).
705     * </p>
706     *
707     * @return The entrySet view
708     */
709    @Override
710    public Set<Map.Entry<K, V>> entrySet() {
711        if (entrySet == null) {
712            entrySet = new EntrySet<>(this);
713        }
714        return entrySet;
715    }
716
717    @Override
718    public boolean equals(final Object obj) {
719        return normalMap.equals(obj);
720    }
721
722    @Override
723    public V get(final Object key) {
724        return normalMap.get(key);
725    }
726
727    @Override
728    public K getKey(final Object value) {
729        return reverseMap.get(value);
730    }
731
732    @Override
733    public int hashCode() {
734        return normalMap.hashCode();
735    }
736
737    @Override
738    public BidiMap<V, K> inverseBidiMap() {
739        if (inverseBidiMap == null) {
740            inverseBidiMap = createBidiMap(reverseMap, normalMap, this);
741        }
742        return inverseBidiMap;
743    }
744
745    @Override
746    public boolean isEmpty() {
747        return normalMap.isEmpty();
748    }
749
750    // Map views
751
752    /**
753     * Gets a keySet view of the map.
754     * Changes made on the view are reflected in the map.
755     * The set supports remove and clear but not add.
756     *
757     * @return The keySet view
758     */
759    @Override
760    public Set<K> keySet() {
761        if (keySet == null) {
762            keySet = new KeySet<>(this);
763        }
764        return keySet;
765    }
766
767    // BidiMap
768
769    /**
770     * Obtains a {@code MapIterator} over the map.
771     * The iterator implements {@link BidiMapIterator}.
772     * This implementation relies on the entrySet iterator.
773     *
774     * @return A map iterator
775     */
776    @Override
777    public MapIterator<K, V> mapIterator() {
778        return new BidiMapIterator<>(this);
779    }
780
781    @Override
782    public V put(final K key, final V value) {
783        if (normalMap.containsKey(key)) {
784            reverseMap.remove(normalMap.get(key));
785        }
786        if (reverseMap.containsKey(value)) {
787            normalMap.remove(reverseMap.get(value));
788        }
789        final V obj = normalMap.put(key, value);
790        reverseMap.put(value, key);
791        return obj;
792    }
793
794    @Override
795    public void putAll(final Map<? extends K, ? extends V> map) {
796        for (final Map.Entry<? extends K, ? extends V> entry : map.entrySet()) {
797            put(entry.getKey(), entry.getValue());
798        }
799    }
800
801    @Override
802    public V remove(final Object key) {
803        V value = null;
804        if (normalMap.containsKey(key)) {
805            value = normalMap.remove(key);
806            reverseMap.remove(value);
807        }
808        return value;
809    }
810
811    @Override
812    public K removeValue(final Object value) {
813        K key = null;
814        if (reverseMap.containsKey(value)) {
815            key = reverseMap.remove(value);
816            normalMap.remove(key);
817        }
818        return key;
819    }
820
821    @Override
822    public int size() {
823        return normalMap.size();
824    }
825
826    @Override
827    public String toString() {
828        return normalMap.toString();
829    }
830
831    /**
832     * Gets a values view of the map.
833     * Changes made on the view are reflected in the map.
834     * The set supports remove and clear but not add.
835     *
836     * @return The values view
837     */
838    @Override
839    public Set<V> values() {
840        if (values == null) {
841            values = new Values<>(this);
842        }
843        return values;
844    }
845
846}