DualTreeBidiMap.java

  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. import java.io.IOException;
  19. import java.io.ObjectInputStream;
  20. import java.io.ObjectOutputStream;
  21. import java.io.Serializable;
  22. import java.util.ArrayList;
  23. import java.util.Comparator;
  24. import java.util.Iterator;
  25. import java.util.ListIterator;
  26. import java.util.Map;
  27. import java.util.SortedMap;
  28. import java.util.TreeMap;

  29. import org.apache.commons.collections4.BidiMap;
  30. import org.apache.commons.collections4.OrderedBidiMap;
  31. import org.apache.commons.collections4.OrderedMap;
  32. import org.apache.commons.collections4.OrderedMapIterator;
  33. import org.apache.commons.collections4.ResettableIterator;
  34. import org.apache.commons.collections4.SortedBidiMap;
  35. import org.apache.commons.collections4.map.AbstractSortedMapDecorator;

  36. /**
  37.  * Implements {@link BidiMap} with two {@link TreeMap} instances.
  38.  * <p>
  39.  * The setValue() method on iterators will succeed only if the new value being set is
  40.  * not already in the bidi map.
  41.  * </p>
  42.  * <p>
  43.  * When considering whether to use this class, the {@link TreeBidiMap} class should
  44.  * also be considered. It implements the interface using a dedicated design, and does
  45.  * not store each object twice, which can save on memory use.
  46.  * </p>
  47.  * <p>
  48.  * NOTE: From Commons Collections 3.1, all subclasses will use {@link TreeMap}
  49.  * and the flawed {@code createMap} method is ignored.
  50.  * </p>
  51.  *
  52.  * @param <K> the type of the keys in this map
  53.  * @param <V> the type of the values in this map
  54.  * @since 3.0
  55.  */
  56. public class DualTreeBidiMap<K, V> extends AbstractDualBidiMap<K, V>
  57.         implements SortedBidiMap<K, V>, Serializable {

  58.     /**
  59.      * Inner class MapIterator.
  60.      *
  61.      * @param <K> the type of the keys.
  62.      * @param <V> the type of the values.
  63.      */
  64.     protected static class BidiOrderedMapIterator<K, V> implements OrderedMapIterator<K, V>, ResettableIterator<K> {

  65.         /** The parent map */
  66.         private final AbstractDualBidiMap<K, V> parent;

  67.         /** The iterator being decorated */
  68.         private ListIterator<Map.Entry<K, V>> iterator;

  69.         /** The last returned entry */
  70.         private Map.Entry<K, V> last;

  71.         /**
  72.          * Constructs a new instance.
  73.          * @param parent  the parent map
  74.          */
  75.         protected BidiOrderedMapIterator(final AbstractDualBidiMap<K, V> parent) {
  76.             this.parent = parent;
  77.             iterator = new ArrayList<>(parent.entrySet()).listIterator();
  78.         }

  79.         @Override
  80.         public K getKey() {
  81.             if (last == null) {
  82.                 throw new IllegalStateException(
  83.                         "Iterator getKey() can only be called after next() and before remove()");
  84.             }
  85.             return last.getKey();
  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.         @Override
  96.         public boolean hasNext() {
  97.             return iterator.hasNext();
  98.         }

  99.         @Override
  100.         public boolean hasPrevious() {
  101.             return iterator.hasPrevious();
  102.         }

  103.         @Override
  104.         public K next() {
  105.             last = iterator.next();
  106.             return last.getKey();
  107.         }

  108.         @Override
  109.         public K previous() {
  110.             last = iterator.previous();
  111.             return last.getKey();
  112.         }

  113.         @Override
  114.         public void remove() {
  115.             iterator.remove();
  116.             parent.remove(last.getKey());
  117.             last = null;
  118.         }

  119.         @Override
  120.         public void reset() {
  121.             iterator = new ArrayList<>(parent.entrySet()).listIterator();
  122.             last = null;
  123.         }

  124.         @Override
  125.         public V setValue(final V value) {
  126.             if (last == null) {
  127.                 throw new IllegalStateException(
  128.                         "Iterator setValue() can only be called after next() and before remove()");
  129.             }
  130.             if (parent.reverseMap.containsKey(value) &&
  131.                 parent.reverseMap.get(value) != last.getKey()) {
  132.                 throw new IllegalArgumentException(
  133.                         "Cannot use setValue() when the object being set is already in the map");
  134.             }
  135.             final V oldValue = parent.put(last.getKey(), value);
  136.             // Map.Entry specifies that the behavior is undefined when the backing map
  137.             // has been modified (as we did with the put), so we also set the value
  138.             last.setValue(value);
  139.             return oldValue;
  140.         }

  141.         @Override
  142.         public String toString() {
  143.             if (last != null) {
  144.                 return "MapIterator[" + getKey() + "=" + getValue() + "]";
  145.             }
  146.             return "MapIterator[]";
  147.         }
  148.     }

  149.     /**
  150.      * Internal sorted map view.
  151.      *
  152.      * @param <K> the type of the keys.
  153.      * @param <V> the type of the values.
  154.      */
  155.     protected static class ViewMap<K, V> extends AbstractSortedMapDecorator<K, V> {
  156.         /**
  157.          * Constructs a new instance.
  158.          * @param bidi  the parent bidi map
  159.          * @param sm  the subMap sorted map
  160.          */
  161.         protected ViewMap(final DualTreeBidiMap<K, V> bidi, final SortedMap<K, V> sm) {
  162.             // the implementation is not great here...
  163.             // use the normalMap as the filtered map, but reverseMap as the full map
  164.             // this forces containsValue and clear to be overridden
  165.             super(new DualTreeBidiMap<>(sm, bidi.reverseMap, bidi.inverseBidiMap));
  166.         }

  167.         @Override
  168.         public void clear() {
  169.             // override as default implementation uses reverseMap
  170.             for (final Iterator<K> it = keySet().iterator(); it.hasNext();) {
  171.                 it.next();
  172.                 it.remove();
  173.             }
  174.         }

  175.         @Override
  176.         public boolean containsValue(final Object value) {
  177.             // override as default implementation uses reverseMap
  178.             return decorated().normalMap.containsValue(value);
  179.         }

  180.         @Override
  181.         protected DualTreeBidiMap<K, V> decorated() {
  182.             return (DualTreeBidiMap<K, V>) super.decorated();
  183.         }

  184.         @Override
  185.         public SortedMap<K, V> headMap(final K toKey) {
  186.             return new ViewMap<>(decorated(), super.headMap(toKey));
  187.         }

  188.         @Override
  189.         public K nextKey(final K key) {
  190.             return decorated().nextKey(key);
  191.         }

  192.         @Override
  193.         public K previousKey(final K key) {
  194.             return decorated().previousKey(key);
  195.         }

  196.         @Override
  197.         public SortedMap<K, V> subMap(final K fromKey, final K toKey) {
  198.             return new ViewMap<>(decorated(), super.subMap(fromKey, toKey));
  199.         }

  200.         @Override
  201.         public SortedMap<K, V> tailMap(final K fromKey) {
  202.             return new ViewMap<>(decorated(), super.tailMap(fromKey));
  203.         }
  204.     }

  205.     /** Ensure serialization compatibility */
  206.     private static final long serialVersionUID = 721969328361809L;

  207.     /** The key comparator to use */
  208.     private final Comparator<? super K> comparator;

  209.     /** The value comparator to use */
  210.     private final Comparator<? super V> valueComparator;

  211.     /**
  212.      * Creates an empty {@link DualTreeBidiMap}.
  213.      */
  214.     public DualTreeBidiMap() {
  215.         super(new TreeMap<>(), new TreeMap<>());
  216.         this.comparator = null;
  217.         this.valueComparator = null;
  218.     }

  219.     /**
  220.      * Constructs a {@link DualTreeBidiMap} using the specified {@link Comparator}.
  221.      *
  222.      * @param keyComparator  the comparator
  223.      * @param valueComparator  the values comparator to use
  224.      */
  225.     public DualTreeBidiMap(final Comparator<? super K> keyComparator, final Comparator<? super V> valueComparator) {
  226.         super(new TreeMap<>(keyComparator), new TreeMap<>(valueComparator));
  227.         this.comparator = keyComparator;
  228.         this.valueComparator = valueComparator;
  229.     }

  230.     /**
  231.      * Constructs a {@link DualTreeBidiMap} and copies the mappings from
  232.      * specified {@link Map}.
  233.      *
  234.      * @param map  the map whose mappings are to be placed in this map
  235.      */
  236.     public DualTreeBidiMap(final Map<? extends K, ? extends V> map) {
  237.         super(new TreeMap<>(), new TreeMap<>());
  238.         putAll(map);
  239.         this.comparator = null;
  240.         this.valueComparator = null;
  241.     }

  242.     /**
  243.      * Constructs a {@link DualTreeBidiMap} that decorates the specified maps.
  244.      *
  245.      * @param normalMap  the normal direction map
  246.      * @param reverseMap  the reverse direction map
  247.      * @param inverseBidiMap  the inverse BidiMap
  248.      */
  249.     protected DualTreeBidiMap(final Map<K, V> normalMap, final Map<V, K> reverseMap,
  250.                               final BidiMap<V, K> inverseBidiMap) {
  251.         super(normalMap, reverseMap, inverseBidiMap);
  252.         this.comparator = ((SortedMap<K, V>) normalMap).comparator();
  253.         this.valueComparator = ((SortedMap<V, K>) reverseMap).comparator();
  254.     }

  255.     @Override
  256.     public Comparator<? super K> comparator() {
  257.         return ((SortedMap<K, V>) normalMap).comparator();
  258.     }

  259.     /**
  260.      * Creates a new instance of this object.
  261.      *
  262.      * @param normalMap  the normal direction map
  263.      * @param reverseMap  the reverse direction map
  264.      * @param inverseMap  the inverse BidiMap
  265.      * @return new bidi map
  266.      */
  267.     @Override
  268.     protected DualTreeBidiMap<V, K> createBidiMap(final Map<V, K> normalMap, final Map<K, V> reverseMap,
  269.                                                   final BidiMap<K, V> inverseMap) {
  270.         return new DualTreeBidiMap<>(normalMap, reverseMap, inverseMap);
  271.     }

  272.     @Override
  273.     public K firstKey() {
  274.         return ((SortedMap<K, V>) normalMap).firstKey();
  275.     }

  276.     @Override
  277.     public SortedMap<K, V> headMap(final K toKey) {
  278.         final SortedMap<K, V> sub = ((SortedMap<K, V>) normalMap).headMap(toKey);
  279.         return new ViewMap<>(this, sub);
  280.     }

  281.     @Override
  282.     public SortedBidiMap<V, K> inverseBidiMap() {
  283.         return (SortedBidiMap<V, K>) super.inverseBidiMap();
  284.     }

  285.     /**
  286.      * Defaults to {@link #inverseBidiMap()}.
  287.      *
  288.      * @return Defaults to {@link #inverseBidiMap()}.
  289.      */
  290.     public OrderedBidiMap<V, K> inverseOrderedBidiMap() {
  291.         return inverseBidiMap();
  292.     }

  293.     /**
  294.      * Defaults to {@link #inverseBidiMap()}.
  295.      *
  296.      * @return Defaults to {@link #inverseBidiMap()}.
  297.      */
  298.     public SortedBidiMap<V, K> inverseSortedBidiMap() {
  299.         return inverseBidiMap();
  300.     }

  301.     @Override
  302.     public K lastKey() {
  303.         return ((SortedMap<K, V>) normalMap).lastKey();
  304.     }

  305.     /**
  306.      * Obtains an ordered map iterator.
  307.      * <p>
  308.      * This implementation copies the elements to an ArrayList in order to
  309.      * provide the forward/backward behavior.
  310.      * </p>
  311.      *
  312.      * @return a new ordered map iterator
  313.      */
  314.     @Override
  315.     public OrderedMapIterator<K, V> mapIterator() {
  316.         return new BidiOrderedMapIterator<>(this);
  317.     }

  318.     @Override
  319.     public K nextKey(final K key) {
  320.         if (isEmpty()) {
  321.             return null;
  322.         }
  323.         if (normalMap instanceof OrderedMap) {
  324.             return ((OrderedMap<K, ?>) normalMap).nextKey(key);
  325.         }
  326.         final SortedMap<K, V> sm = (SortedMap<K, V>) normalMap;
  327.         final Iterator<K> it = sm.tailMap(key).keySet().iterator();
  328.         it.next();
  329.         if (it.hasNext()) {
  330.             return it.next();
  331.         }
  332.         return null;
  333.     }

  334.     @Override
  335.     public K previousKey(final K key) {
  336.         if (isEmpty()) {
  337.             return null;
  338.         }
  339.         if (normalMap instanceof OrderedMap) {
  340.             return ((OrderedMap<K, V>) normalMap).previousKey(key);
  341.         }
  342.         final SortedMap<K, V> sm = (SortedMap<K, V>) normalMap;
  343.         final SortedMap<K, V> hm = sm.headMap(key);
  344.         if (hm.isEmpty()) {
  345.             return null;
  346.         }
  347.         return hm.lastKey();
  348.     }

  349.     /**
  350.      * Deserializes an instance from an ObjectInputStream.
  351.      *
  352.      * @param in The source ObjectInputStream.
  353.      * @throws IOException            Any of the usual Input/Output related exceptions.
  354.      * @throws ClassNotFoundException A class of a serialized object cannot be found.
  355.      */
  356.     private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
  357.         in.defaultReadObject();
  358.         normalMap = new TreeMap<>(comparator);
  359.         reverseMap = new TreeMap<>(valueComparator);
  360.         @SuppressWarnings("unchecked") // will fail at runtime if the stream is incorrect
  361.         final Map<K, V> map = (Map<K, V>) in.readObject();
  362.         putAll(map);
  363.     }

  364.     @Override
  365.     public SortedMap<K, V> subMap(final K fromKey, final K toKey) {
  366.         final SortedMap<K, V> sub = ((SortedMap<K, V>) normalMap).subMap(fromKey, toKey);
  367.         return new ViewMap<>(this, sub);
  368.     }

  369.     @Override
  370.     public SortedMap<K, V> tailMap(final K fromKey) {
  371.         final SortedMap<K, V> sub = ((SortedMap<K, V>) normalMap).tailMap(fromKey);
  372.         return new ViewMap<>(this, sub);
  373.     }

  374.     @Override
  375.     public Comparator<? super V> valueComparator() {
  376.         return ((SortedMap<V, K>) reverseMap).comparator();
  377.     }

  378.     /**
  379.      * Serializes this object to an ObjectOutputStream.
  380.      *
  381.      * @param out the target ObjectOutputStream.
  382.      * @throws IOException thrown when an I/O errors occur writing to the target stream.
  383.      */
  384.     private void writeObject(final ObjectOutputStream out) throws IOException {
  385.         out.defaultWriteObject();
  386.         out.writeObject(normalMap);
  387.     }

  388. }