View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      https://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.collections4.bidimap;
18  
19  import java.util.Collection;
20  import java.util.Iterator;
21  import java.util.Map;
22  import java.util.Objects;
23  import java.util.Set;
24  import java.util.function.Predicate;
25  
26  import org.apache.commons.collections4.BidiMap;
27  import org.apache.commons.collections4.MapIterator;
28  import org.apache.commons.collections4.ResettableIterator;
29  import org.apache.commons.collections4.collection.AbstractCollectionDecorator;
30  import org.apache.commons.collections4.iterators.AbstractIteratorDecorator;
31  import org.apache.commons.collections4.keyvalue.AbstractMapEntryDecorator;
32  
33  /**
34   * Abstract {@link BidiMap} implemented using two maps.
35   * <p>
36   * An implementation can be written simply by implementing the
37   * {@link #createBidiMap(Map, Map, BidiMap)} method.
38   * </p>
39   *
40   * @param <K> The type of the keys in the map
41   * @param <V> The type of the values in the map
42   * @see DualHashBidiMap
43   * @see DualTreeBidiMap
44   * @since 3.0
45   */
46  public abstract class AbstractDualBidiMap<K, V> implements BidiMap<K, V> {
47  
48      /**
49       * Inner class MapIterator.
50       *
51       * @param <K> The type of the keys.
52       * @param <V> The type of the values.
53       */
54      protected static class BidiMapIterator<K, V> implements MapIterator<K, V>, ResettableIterator<K> {
55  
56          /** The parent map */
57          protected final AbstractDualBidiMap<K, V> parent;
58  
59          /** The iterator being wrapped */
60          protected Iterator<Map.Entry<K, V>> iterator;
61  
62          /** The last returned entry */
63          protected Map.Entry<K, V> last;
64  
65          /** Whether remove is allowed at present */
66          protected boolean canRemove;
67  
68          /**
69           * Constructs a new instance.
70           *
71           * @param parent  The parent map
72           */
73          protected BidiMapIterator(final AbstractDualBidiMap<K, V> parent) {
74              this.parent = parent;
75              this.iterator = parent.normalMap.entrySet().iterator();
76          }
77  
78          @Override
79          public K getKey() {
80              if (last == null) {
81                  throw new IllegalStateException(
82                          "Iterator getKey() can only be called after next() and before remove()");
83              }
84              return last.getKey();
85          }
86  
87          @Override
88          public V getValue() {
89              if (last == null) {
90                  throw new IllegalStateException(
91                          "Iterator getValue() can only be called after next() and before remove()");
92              }
93              return last.getValue();
94          }
95  
96          @Override
97          public boolean hasNext() {
98              return iterator.hasNext();
99          }
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 }