    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one or more
    003 * contributor license agreements.  See the NOTICE file distributed with
    004 * this work for additional information regarding copyright ownership.
    005 * The ASF licenses this file to You under the Apache License, Version 2.0
    006 * (the "License"); you may not use this file except in compliance with
    007 * the License.  You may obtain a copy of the License at
    008 *
    009 *      http://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 * Unless required by applicable law or agreed to in writing, software
    012 * distributed under the License is distributed on an "AS IS" BASIS,
    013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 * See the License for the specific language governing permissions and
    015 * limitations under the License.
    016 */
    017package org.apache.commons.collections4.bidimap;
    018
    019import java.io.IOException;
    020import java.io.ObjectInputStream;
    021import java.io.ObjectOutputStream;
    022import java.io.Serializable;
    023import java.util.ArrayList;
    024import java.util.Comparator;
    025import java.util.Iterator;
    026import java.util.ListIterator;
    027import java.util.Map;
    028import java.util.SortedMap;
    029import java.util.TreeMap;
    030
    031import org.apache.commons.collections4.BidiMap;
    032import org.apache.commons.collections4.OrderedBidiMap;
    033import org.apache.commons.collections4.OrderedMap;
    034import org.apache.commons.collections4.OrderedMapIterator;
    035import org.apache.commons.collections4.ResettableIterator;
    036import org.apache.commons.collections4.SortedBidiMap;
    037import org.apache.commons.collections4.map.AbstractSortedMapDecorator;
    038
    039/**
    040 * Implements {@link BidiMap} with two {@link TreeMap} instances.
    041 * <p>
    042 * The setValue() method on iterators will succeed only if the new value being set is
    043 * not already in the bidi map.
    044 * </p>
    045 * <p>
    046 * When considering whether to use this class, the {@link TreeBidiMap} class should
    047 * also be considered. It implements the interface using a dedicated design, and does
    048 * not store each object twice, which can save on memory use.
    049 * </p>
    050 * <p>
    051 * NOTE: From Commons Collections 3.1, all subclasses will use {@link TreeMap}
    052 * and the flawed {@code createMap} method is ignored.
    053 * </p>
    054 *
    055 * @param <K> the type of the keys in this map
    056 * @param <V> the type of the values in this map
    057 * @since 3.0
    058 */
    059public class DualTreeBidiMap<K, V> extends AbstractDualBidiMap<K, V>
    060        implements SortedBidiMap<K, V>, Serializable {
    061
    062    /**
    063     * Inner class MapIterator.
    064     *
    065     * @param <K> the type of the keys.
    066     * @param <V> the type of the values.
    067     */
    068    protected static class BidiOrderedMapIterator<K, V> implements OrderedMapIterator<K, V>, ResettableIterator<K> {
    069
    070        /** The parent map */
    071        private final AbstractDualBidiMap<K, V> parent;
    072
    073        /** The iterator being decorated */
    074        private ListIterator<Map.Entry<K, V>> iterator;
    075
    076        /** The last returned entry */
    077        private Map.Entry<K, V> last;
    078
    079        /**
    080         * Constructs a new instance.
    081         * @param parent  the parent map
    082         */
    083        protected BidiOrderedMapIterator(final AbstractDualBidiMap<K, V> parent) {
    084            this.parent = parent;
    085            iterator = new ArrayList<>(parent.entrySet()).listIterator();
    086        }
    087
    088        @Override
    089        public K getKey() {
    090            if (last == null) {
    091                throw new IllegalStateException(
    092                        "Iterator getKey() can only be called after next() and before remove()");
    093            }
    094            return last.getKey();
    095        }
    096
    097        @Override
    098        public V getValue() {
    099            if (last == null) {
    100                throw new IllegalStateException(
    101                        "Iterator getValue() can only be called after next() and before remove()");
    102            }
    103            return last.getValue();
    104        }
    105
    106        @Override
    107        public boolean hasNext() {
    108            return iterator.hasNext();
    109        }
    110
    111        @Override
    112        public boolean hasPrevious() {
    113            return iterator.hasPrevious();
    114        }
    115
    116        @Override
    117        public K next() {
    118            last = iterator.next();
    119            return last.getKey();
    120        }
    121
    122        @Override
    123        public K previous() {
    124            last = iterator.previous();
    125            return last.getKey();
    126        }
    127
    128        @Override
    129        public void remove() {
    130            iterator.remove();
    131            parent.remove(last.getKey());
    132            last = null;
    133        }
    134
    135        @Override
    136        public void reset() {
    137            iterator = new ArrayList<>(parent.entrySet()).listIterator();
    138            last = null;
    139        }
    140
    141        @Override
    142        public V setValue(final V value) {
    143            if (last == null) {
    144                throw new IllegalStateException(
    145                        "Iterator setValue() can only be called after next() and before remove()");
    146            }
    147            if (parent.reverseMap.containsKey(value) &&
    148                parent.reverseMap.get(value) != last.getKey()) {
    149                throw new IllegalArgumentException(
    150                        "Cannot use setValue() when the object being set is already in the map");
    151            }
    152            final V oldValue = parent.put(last.getKey(), value);
    153            // Map.Entry specifies that the behavior is undefined when the backing map
    154            // has been modified (as we did with the put), so we also set the value
    155            last.setValue(value);
    156            return oldValue;
    157        }
    158
    159        @Override
    160        public String toString() {
    161            if (last != null) {
    162                return "MapIterator[" + getKey() + "=" + getValue() + "]";
    163            }
    164            return "MapIterator[]";
    165        }
    166    }
    167
    168    /**
    169     * Internal sorted map view.
    170     *
    171     * @param <K> the type of the keys.
    172     * @param <V> the type of the values.
    173     */
    174    protected static class ViewMap<K, V> extends AbstractSortedMapDecorator<K, V> {
    175        /**
    176         * Constructs a new instance.
    177         * @param bidi  the parent bidi map
    178         * @param sm  the subMap sorted map
    179         */
    180        protected ViewMap(final DualTreeBidiMap<K, V> bidi, final SortedMap<K, V> sm) {
    181            // the implementation is not great here...
    182            // use the normalMap as the filtered map, but reverseMap as the full map
    183            // this forces containsValue and clear to be overridden
    184            super(new DualTreeBidiMap<>(sm, bidi.reverseMap, bidi.inverseBidiMap));
    185        }
    186
    187        @Override
    188        public void clear() {
    189            // override as default implementation uses reverseMap
    190            for (final Iterator<K> it = keySet().iterator(); it.hasNext();) {
    191                it.next();
    192                it.remove();
    193            }
    194        }
    195
    196        @Override
    197        public boolean containsValue(final Object value) {
    198            // override as default implementation uses reverseMap
    199            return decorated().normalMap.containsValue(value);
    200        }
    201
    202        @Override
    203        protected DualTreeBidiMap<K, V> decorated() {
    204            return (DualTreeBidiMap<K, V>) super.decorated();
    205        }
    206
    207        @Override
    208        public SortedMap<K, V> headMap(final K toKey) {
    209            return new ViewMap<>(decorated(), super.headMap(toKey));
    210        }
    211
    212        @Override
    213        public K nextKey(final K key) {
    214            return decorated().nextKey(key);
    215        }
    216
    217        @Override
    218        public K previousKey(final K key) {
    219            return decorated().previousKey(key);
    220        }
    221
    222        @Override
    223        public SortedMap<K, V> subMap(final K fromKey, final K toKey) {
    224            return new ViewMap<>(decorated(), super.subMap(fromKey, toKey));
    225        }
    226
    227        @Override
    228        public SortedMap<K, V> tailMap(final K fromKey) {
    229            return new ViewMap<>(decorated(), super.tailMap(fromKey));
    230        }
    231    }
    232
    233    /** Ensure serialization compatibility */
    234    private static final long serialVersionUID = 721969328361809L;
    235
    236    /** The key comparator to use */
    237    private final Comparator<? super K> comparator;
    238
    239    /** The value comparator to use */
    240    private final Comparator<? super V> valueComparator;
    241
    242    /**
    243     * Creates an empty {@link DualTreeBidiMap}.
    244     */
    245    public DualTreeBidiMap() {
    246        super(new TreeMap<>(), new TreeMap<>());
    247        this.comparator = null;
    248        this.valueComparator = null;
    249    }
    250
    251    /**
    252     * Constructs a {@link DualTreeBidiMap} using the specified {@link Comparator}.
    253     *
    254     * @param keyComparator  the comparator
    255     * @param valueComparator  the values comparator to use
    256     */
    257    public DualTreeBidiMap(final Comparator<? super K> keyComparator, final Comparator<? super V> valueComparator) {
    258        super(new TreeMap<>(keyComparator), new TreeMap<>(valueComparator));
    259        this.comparator = keyComparator;
    260        this.valueComparator = valueComparator;
    261    }
    262
    263    /**
    264     * Constructs a {@link DualTreeBidiMap} and copies the mappings from
    265     * specified {@link Map}.
    266     *
    267     * @param map  the map whose mappings are to be placed in this map
    268     */
    269    public DualTreeBidiMap(final Map<? extends K, ? extends V> map) {
    270        super(new TreeMap<>(), new TreeMap<>());
    271        putAll(map);
    272        this.comparator = null;
    273        this.valueComparator = null;
    274    }
    275
    276    /**
    277     * Constructs a {@link DualTreeBidiMap} that decorates the specified maps.
    278     *
    279     * @param normalMap  the normal direction map
    280     * @param reverseMap  the reverse direction map
    281     * @param inverseBidiMap  the inverse BidiMap
    282     */
    283    protected DualTreeBidiMap(final Map<K, V> normalMap, final Map<V, K> reverseMap,
    284                              final BidiMap<V, K> inverseBidiMap) {
    285        super(normalMap, reverseMap, inverseBidiMap);
    286        this.comparator = ((SortedMap<K, V>) normalMap).comparator();
    287        this.valueComparator = ((SortedMap<V, K>) reverseMap).comparator();
    288    }
    289
    290    @Override
    291    public Comparator<? super K> comparator() {
    292        return ((SortedMap<K, V>) normalMap).comparator();
    293    }
    294
    295    /**
    296     * Creates a new instance of this object.
    297     *
    298     * @param normalMap  the normal direction map
    299     * @param reverseMap  the reverse direction map
    300     * @param inverseMap  the inverse BidiMap
    301     * @return new bidi map
    302     */
    303    @Override
    304    protected DualTreeBidiMap<V, K> createBidiMap(final Map<V, K> normalMap, final Map<K, V> reverseMap,
    305                                                  final BidiMap<K, V> inverseMap) {
    306        return new DualTreeBidiMap<>(normalMap, reverseMap, inverseMap);
    307    }
    308
    309    @Override
    310    public K firstKey() {
    311        return ((SortedMap<K, V>) normalMap).firstKey();
    312    }
    313
    314    @Override
    315    public SortedMap<K, V> headMap(final K toKey) {
    316        final SortedMap<K, V> sub = ((SortedMap<K, V>) normalMap).headMap(toKey);
    317        return new ViewMap<>(this, sub);
    318    }
    319
    320    @Override
    321    public SortedBidiMap<V, K> inverseBidiMap() {
    322        return (SortedBidiMap<V, K>) super.inverseBidiMap();
    323    }
    324
    325    public OrderedBidiMap<V, K> inverseOrderedBidiMap() {
    326        return inverseBidiMap();
    327    }
    328
    329    public SortedBidiMap<V, K> inverseSortedBidiMap() {
    330        return inverseBidiMap();
    331    }
    332
    333    @Override
    334    public K lastKey() {
    335        return ((SortedMap<K, V>) normalMap).lastKey();
    336    }
    337
    338    /**
    339     * Obtains an ordered map iterator.
    340     * <p>
    341     * This implementation copies the elements to an ArrayList in order to
    342     * provide the forward/backward behavior.
    343     *
    344     * @return a new ordered map iterator
    345     */
    346    @Override
    347    public OrderedMapIterator<K, V> mapIterator() {
    348        return new BidiOrderedMapIterator<>(this);
    349    }
    350
    351    @Override
    352    public K nextKey(final K key) {
    353        if (isEmpty()) {
    354            return null;
    355        }
    356        if (normalMap instanceof OrderedMap) {
    357            return ((OrderedMap<K, ?>) normalMap).nextKey(key);
    358        }
    359        final SortedMap<K, V> sm = (SortedMap<K, V>) normalMap;
    360        final Iterator<K> it = sm.tailMap(key).keySet().iterator();
    361        it.next();
    362        if (it.hasNext()) {
    363            return it.next();
    364        }
    365        return null;
    366    }
    367
    368    @Override
    369    public K previousKey(final K key) {
    370        if (isEmpty()) {
    371            return null;
    372        }
    373        if (normalMap instanceof OrderedMap) {
    374            return ((OrderedMap<K, V>) normalMap).previousKey(key);
    375        }
    376        final SortedMap<K, V> sm = (SortedMap<K, V>) normalMap;
    377        final SortedMap<K, V> hm = sm.headMap(key);
    378        if (hm.isEmpty()) {
    379            return null;
    380        }
    381        return hm.lastKey();
    382    }
    383
    384    private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
    385        in.defaultReadObject();
    386        normalMap = new TreeMap<>(comparator);
    387        reverseMap = new TreeMap<>(valueComparator);
    388        @SuppressWarnings("unchecked") // will fail at runtime if the stream is incorrect
    389        final Map<K, V> map = (Map<K, V>) in.readObject();
    390        putAll(map);
    391    }
    392
    393    @Override
    394    public SortedMap<K, V> subMap(final K fromKey, final K toKey) {
    395        final SortedMap<K, V> sub = ((SortedMap<K, V>) normalMap).subMap(fromKey, toKey);
    396        return new ViewMap<>(this, sub);
    397    }
    398
    399    @Override
    400    public SortedMap<K, V> tailMap(final K fromKey) {
    401        final SortedMap<K, V> sub = ((SortedMap<K, V>) normalMap).tailMap(fromKey);
    402        return new ViewMap<>(this, sub);
    403    }
    404
    405    @Override
    406    public Comparator<? super V> valueComparator() {
    407        return ((SortedMap<V, K>) reverseMap).comparator();
    408    }
    409
    410    // Serialization
    411    private void writeObject(final ObjectOutputStream out) throws IOException {
    412        out.defaultWriteObject();
    413        out.writeObject(normalMap);
    414    }
    415
    416}