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