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    *      http://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   *
43   * @see DualHashBidiMap
44   * @see DualTreeBidiMap
45   * @since 3.0
46   */
47  public abstract class AbstractDualBidiMap<K, V> implements BidiMap<K, V> {
48  
49      /**
50       * Inner class MapIterator.
51       *
52       * @param <K> the type of the keys.
53       * @param <V> the type of the values.
54       */
55      protected static class BidiMapIterator<K, V> implements MapIterator<K, V>, ResettableIterator<K> {
56  
57          /** The parent map */
58          protected final AbstractDualBidiMap<K, V> parent;
59  
60          /** The iterator being wrapped */
61          protected Iterator<Map.Entry<K, V>> iterator;
62  
63          /** The last returned entry */
64          protected Map.Entry<K, V> last;
65  
66          /** Whether remove is allowed at present */
67          protected boolean canRemove;
68  
69          /**
70           * Constructs a new instance.
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 (e.g. 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 (value == null ? entry.getValue() == null : value.equals(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          * @param iterator  the iterator to decorate
215          * @param parent  the parent map
216          */
217         protected EntrySetIterator(final Iterator<Map.Entry<K, V>> iterator, final AbstractDualBidiMap<K, V> parent) {
218             super(iterator);
219             this.parent = parent;
220         }
221 
222         @Override
223         public Map.Entry<K, V> next() {
224             last = new MapEntry<>(super.next(), parent);
225             canRemove = true;
226             return last;
227         }
228 
229         @Override
230         public void remove() {
231             if (!canRemove) {
232                 throw new IllegalStateException("Iterator remove() can only be called once after next()");
233             }
234             // store value as remove may change the entry in the decorator (e.g. TreeMap)
235             final Object value = last.getValue();
236             super.remove();
237             parent.reverseMap.remove(value);
238             last = null;
239             canRemove = false;
240         }
241     }
242 
243     /**
244      * Inner class KeySet.
245      *
246      * @param <K> the type of elements maintained by this set
247      */
248     protected static class KeySet<K> extends View<K, Object, K> implements Set<K> {
249 
250         /** Serialization version */
251         private static final long serialVersionUID = -7107935777385040694L;
252 
253         /**
254          * Constructs a new instance.
255          *
256          * @param parent  the parent BidiMap
257          */
258         @SuppressWarnings("unchecked")
259         protected KeySet(final AbstractDualBidiMap<K, ?> parent) {
260             super(parent.normalMap.keySet(), (AbstractDualBidiMap<K, Object>) parent);
261         }
262 
263         @Override
264         public boolean contains(final Object key) {
265             return parent.normalMap.containsKey(key);
266         }
267 
268         @Override
269         public Iterator<K> iterator() {
270             return parent.createKeySetIterator(super.iterator());
271         }
272 
273         @Override
274         public boolean remove(final Object key) {
275             if (parent.normalMap.containsKey(key)) {
276                 final Object value = parent.normalMap.remove(key);
277                 parent.reverseMap.remove(value);
278                 return true;
279             }
280             return false;
281         }
282     }
283 
284     /**
285      * Inner class KeySetIterator.
286      *
287      * @param <K> the key type.
288      */
289     protected static class KeySetIterator<K> extends AbstractIteratorDecorator<K> {
290 
291         /** The parent map */
292         protected final AbstractDualBidiMap<K, ?> parent;
293 
294         /** The last returned key */
295         protected K lastKey;
296 
297         /** Whether remove is allowed at present */
298         protected boolean canRemove;
299 
300         /**
301          * Constructs a new instance.
302          * @param iterator  the iterator to decorate
303          * @param parent  the parent map
304          */
305         protected KeySetIterator(final Iterator<K> iterator, final AbstractDualBidiMap<K, ?> parent) {
306             super(iterator);
307             this.parent = parent;
308         }
309 
310         @Override
311         public K next() {
312             lastKey = super.next();
313             canRemove = true;
314             return lastKey;
315         }
316 
317         @Override
318         public void remove() {
319             if (!canRemove) {
320                 throw new IllegalStateException("Iterator remove() can only be called once after next()");
321             }
322             final Object value = parent.normalMap.get(lastKey);
323             super.remove();
324             parent.reverseMap.remove(value);
325             lastKey = null;
326             canRemove = false;
327         }
328     }
329 
330     /**
331      * Inner class MapEntry.
332      *
333      * @param <K> the type of the keys.
334      * @param <V> the type of the values.
335      */
336     protected static class MapEntry<K, V> extends AbstractMapEntryDecorator<K, V> {
337 
338         /** The parent map */
339         protected final AbstractDualBidiMap<K, V> parent;
340 
341         /**
342          * Constructs a new instance.
343          * @param entry  the entry to decorate
344          * @param parent  the parent map
345          */
346         protected MapEntry(final Map.Entry<K, V> entry, final AbstractDualBidiMap<K, V> parent) {
347             super(entry);
348             this.parent = parent;
349         }
350 
351         @Override
352         public V setValue(final V value) {
353             final K key = getKey();
354             if (parent.reverseMap.containsKey(value) &&
355                 parent.reverseMap.get(value) != key) {
356                 throw new IllegalArgumentException(
357                         "Cannot use setValue() when the object being set is already in the map");
358             }
359             parent.put(key, value);
360             return super.setValue(value);
361         }
362     }
363 
364     /**
365      * Inner class Values.
366      *
367      * @param <V> the type of the values.
368      */
369     protected static class Values<V> extends View<Object, V, V> implements Set<V> {
370 
371         /** Serialization version */
372         private static final long serialVersionUID = 4023777119829639864L;
373 
374         /**
375          * Constructs a new instance.
376          *
377          * @param parent  the parent BidiMap
378          */
379         @SuppressWarnings("unchecked")
380         protected Values(final AbstractDualBidiMap<?, V> parent) {
381             super(parent.normalMap.values(), (AbstractDualBidiMap<Object, V>) parent);
382         }
383 
384         @Override
385         public boolean contains(final Object value) {
386             return parent.reverseMap.containsKey(value);
387         }
388 
389         @Override
390         public Iterator<V> iterator() {
391             return parent.createValuesIterator(super.iterator());
392         }
393 
394         @Override
395         public boolean remove(final Object value) {
396             if (parent.reverseMap.containsKey(value)) {
397                 final Object key = parent.reverseMap.remove(value);
398                 parent.normalMap.remove(key);
399                 return true;
400             }
401             return false;
402         }
403     }
404 
405     /**
406      * Inner class ValuesIterator.
407      *
408      * @param <V> the value type.
409      */
410     protected static class ValuesIterator<V> extends AbstractIteratorDecorator<V> {
411 
412         /** The parent map */
413         protected final AbstractDualBidiMap<Object, V> parent;
414 
415         /** The last returned value */
416         protected V lastValue;
417 
418         /** Whether remove is allowed at present */
419         protected boolean canRemove;
420 
421         /**
422          * Constructs a new instance.
423          * @param iterator  the iterator to decorate
424          * @param parent  the parent map
425          */
426         @SuppressWarnings("unchecked")
427         protected ValuesIterator(final Iterator<V> iterator, final AbstractDualBidiMap<?, V> parent) {
428             super(iterator);
429             this.parent = (AbstractDualBidiMap<Object, V>) parent;
430         }
431 
432         @Override
433         public V next() {
434             lastValue = super.next();
435             canRemove = true;
436             return lastValue;
437         }
438 
439         @Override
440         public void remove() {
441             if (!canRemove) {
442                 throw new IllegalStateException("Iterator remove() can only be called once after next()");
443             }
444             super.remove(); // removes from maps[0]
445             parent.reverseMap.remove(lastValue);
446             lastValue = null;
447             canRemove = false;
448         }
449     }
450 
451     /**
452      * Inner class View.
453      *
454      * @param <K> the type of the keys in the map.
455      * @param <V> the type of the values in the map.
456      * @param <E> the type of the elements in the collection.
457      */
458     protected abstract static class View<K, V, E> extends AbstractCollectionDecorator<E> {
459 
460         /** Generated serial version ID. */
461         private static final long serialVersionUID = 4621510560119690639L;
462 
463         /** The parent map */
464         protected final AbstractDualBidiMap<K, V> parent;
465 
466         /**
467          * Constructs a new instance.
468          *
469          * @param coll  the collection view being decorated
470          * @param parent  the parent BidiMap
471          */
472         protected View(final Collection<E> coll, final AbstractDualBidiMap<K, V> parent) {
473             super(coll);
474             this.parent = parent;
475         }
476 
477         @Override
478         public void clear() {
479             parent.clear();
480         }
481 
482         @Override
483         public boolean equals(final Object object) {
484             return object == this || decorated().equals(object);
485         }
486 
487         @Override
488         public int hashCode() {
489             return decorated().hashCode();
490         }
491 
492         @Override
493         public boolean removeAll(final Collection<?> coll) {
494             if (parent.isEmpty() || coll.isEmpty()) {
495                 return false;
496             }
497             boolean modified = false;
498             for (final Object current : coll) {
499                 modified |= remove(current);
500             }
501             return modified;
502         }
503 
504         /**
505          * @since 4.4
506          */
507         @Override
508         public boolean removeIf(final Predicate<? super E> filter) {
509             if (parent.isEmpty() || Objects.isNull(filter)) {
510                 return false;
511             }
512             boolean modified = false;
513             final Iterator<?> it = iterator();
514             while (it.hasNext()) {
515                 @SuppressWarnings("unchecked")
516                 final E e = (E) it.next();
517                 if (filter.test(e)) {
518                     it.remove();
519                     modified = true;
520                 }
521             }
522             return modified;
523         }
524 
525         /**
526          * {@inheritDoc}
527          * <p>
528          * This implementation iterates over the elements of this bidi map, checking each element in
529          * turn to see if it's contained in {@code coll}. If it's not contained, it's removed
530          * from this bidi map. As a consequence, it is advised to use a collection type for
531          * {@code coll} that provides a fast (e.g. O(1)) implementation of
532          * {@link Collection#contains(Object)}.
533          */
534         @Override
535         public boolean retainAll(final Collection<?> coll) {
536             if (parent.isEmpty()) {
537                 return false;
538             }
539             if (coll.isEmpty()) {
540                 parent.clear();
541                 return true;
542             }
543             boolean modified = false;
544             final Iterator<E> it = iterator();
545             while (it.hasNext()) {
546                 if (!coll.contains(it.next())) {
547                     it.remove();
548                     modified = true;
549                 }
550             }
551             return modified;
552         }
553     }
554 
555     /**
556      * Normal delegate map.
557      */
558     transient Map<K, V> normalMap;
559 
560     // Map delegation
561 
562     /**
563      * Reverse delegate map.
564      */
565     transient Map<V, K> reverseMap;
566 
567     /**
568      * Inverse view of this map.
569      */
570     transient BidiMap<V, K> inverseBidiMap;
571 
572     /**
573      * View of the keys.
574      */
575     transient Set<K> keySet;
576 
577     /**
578      * View of the values.
579      */
580     transient Set<V> values;
581 
582     /**
583      * View of the entries.
584      */
585     transient Set<Map.Entry<K, V>> entrySet;
586 
587     /**
588      * Creates an empty map, initialized by {@code createMap}.
589      * <p>
590      * This constructor remains in place for deserialization.
591      * All other usage is deprecated in favour of
592      * {@link #AbstractDualBidiMap(Map, Map)}.
593      */
594     protected AbstractDualBidiMap() {
595     }
596 
597     /**
598      * Creates an empty map using the two maps specified as storage.
599      * <p>
600      * The two maps must be a matching pair, normal and reverse.
601      * They will typically both be empty.
602      * <p>
603      * Neither map is validated, so nulls may be passed in.
604      * If you choose to do this then the subclass constructor must populate
605      * the {@code maps[]} instance variable itself.
606      *
607      * @param normalMap  the normal direction map
608      * @param reverseMap  the reverse direction map
609      * @since 3.1
610      */
611     protected AbstractDualBidiMap(final Map<K, V> normalMap, final Map<V, K> reverseMap) {
612         this.normalMap = normalMap;
613         this.reverseMap = reverseMap;
614     }
615 
616     // BidiMap changes
617 
618     /**
619      * Constructs a map that decorates the specified maps,
620      * used by the subclass {@code createBidiMap} implementation.
621      *
622      * @param normalMap  the normal direction map
623      * @param reverseMap  the reverse direction map
624      * @param inverseBidiMap  the inverse BidiMap
625      */
626     protected AbstractDualBidiMap(final Map<K, V> normalMap, final Map<V, K> reverseMap,
627                                   final BidiMap<V, K> inverseBidiMap) {
628         this.normalMap = normalMap;
629         this.reverseMap = reverseMap;
630         this.inverseBidiMap = inverseBidiMap;
631     }
632 
633     @Override
634     public void clear() {
635         normalMap.clear();
636         reverseMap.clear();
637     }
638 
639     @Override
640     public boolean containsKey(final Object key) {
641         return normalMap.containsKey(key);
642     }
643 
644     @Override
645     public boolean containsValue(final Object value) {
646         return reverseMap.containsKey(value);
647     }
648 
649     /**
650      * Creates a new instance of the subclass.
651      *
652      * @param normalMap  the normal direction map
653      * @param reverseMap  the reverse direction map
654      * @param inverseMap  this map, which is the inverse in the new map
655      * @return the bidi map
656      */
657     protected abstract BidiMap<V, K> createBidiMap(Map<V, K> normalMap, Map<K, V> reverseMap, BidiMap<K, V> inverseMap);
658 
659     /**
660      * Creates an entry set iterator.
661      * Subclasses can override this to return iterators with different properties.
662      *
663      * @param iterator  the iterator to decorate
664      * @return the entrySet iterator
665      */
666     protected Iterator<Map.Entry<K, V>> createEntrySetIterator(final Iterator<Map.Entry<K, V>> iterator) {
667         return new EntrySetIterator<>(iterator, this);
668     }
669 
670     /**
671      * Creates a key set iterator.
672      * Subclasses can override this to return iterators with different properties.
673      *
674      * @param iterator  the iterator to decorate
675      * @return the keySet iterator
676      */
677     protected Iterator<K> createKeySetIterator(final Iterator<K> iterator) {
678         return new KeySetIterator<>(iterator, this);
679     }
680 
681     /**
682      * Creates a values iterator.
683      * Subclasses can override this to return iterators with different properties.
684      *
685      * @param iterator  the iterator to decorate
686      * @return the values iterator
687      */
688     protected Iterator<V> createValuesIterator(final Iterator<V> iterator) {
689         return new ValuesIterator<>(iterator, this);
690     }
691 
692     /**
693      * Gets an entrySet view of the map.
694      * Changes made on the set are reflected in the map.
695      * The set supports remove and clear but not add.
696      * <p>
697      * The Map Entry setValue() method only allow a new value to be set.
698      * If the value being set is already in the map, an IllegalArgumentException
699      * is thrown (as setValue cannot change the size of the map).
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      * <p>
766      *
767      * @return a map iterator
768      */
769     @Override
770     public MapIterator<K, V> mapIterator() {
771         return new BidiMapIterator<>(this);
772     }
773 
774     @Override
775     public V put(final K key, final V value) {
776         if (normalMap.containsKey(key)) {
777             reverseMap.remove(normalMap.get(key));
778         }
779         if (reverseMap.containsKey(value)) {
780             normalMap.remove(reverseMap.get(value));
781         }
782         final V obj = normalMap.put(key, value);
783         reverseMap.put(value, key);
784         return obj;
785     }
786 
787     @Override
788     public void putAll(final Map<? extends K, ? extends V> map) {
789         for (final Map.Entry<? extends K, ? extends V> entry : map.entrySet()) {
790             put(entry.getKey(), entry.getValue());
791         }
792     }
793 
794     @Override
795     public V remove(final Object key) {
796         V value = null;
797         if (normalMap.containsKey(key)) {
798             value = normalMap.remove(key);
799             reverseMap.remove(value);
800         }
801         return value;
802     }
803 
804     @Override
805     public K removeValue(final Object value) {
806         K key = null;
807         if (reverseMap.containsKey(value)) {
808             key = reverseMap.remove(value);
809             normalMap.remove(key);
810         }
811         return key;
812     }
813 
814     @Override
815     public int size() {
816         return normalMap.size();
817     }
818 
819     @Override
820     public String toString() {
821         return normalMap.toString();
822     }
823 
824     /**
825      * Gets a values view of the map.
826      * Changes made on the view are reflected in the map.
827      * The set supports remove and clear but not add.
828      *
829      * @return the values view
830      */
831     @Override
832     public Set<V> values() {
833         if (values == null) {
834             values = new Values<>(this);
835         }
836         return values;
837     }
838 
839 }