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   * @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           * @param parent  the parent map
71           */
72          protected BidiMapIterator(final AbstractDualBidiMap<K, V> parent) {
73              this.parent = parent;
74              this.iterator = parent.normalMap.entrySet().iterator();
75          }
76  
77          @Override
78          public K getKey() {
79              if (last == null) {
80                  throw new IllegalStateException(
81                          "Iterator getKey() can only be called after next() and before remove()");
82              }
83              return last.getKey();
84          }
85  
86          @Override
87          public V getValue() {
88              if (last == null) {
89                  throw new IllegalStateException(
90                          "Iterator getValue() can only be called after next() and before remove()");
91              }
92              return last.getValue();
93          }
94  
95          @Override
96          public boolean hasNext() {
97              return iterator.hasNext();
98          }
99  
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 }