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 *      https://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         *
082         * @param parent  The parent map
083         */
084        protected BidiOrderedMapIterator(final AbstractDualBidiMap<K, V> parent) {
085            this.parent = parent;
086            iterator = new ArrayList<>(parent.entrySet()).listIterator();
087        }
088
089        @Override
090        public K getKey() {
091            if (last == null) {
092                throw new IllegalStateException(
093                        "Iterator getKey() can only be called after next() and before remove()");
094            }
095            return last.getKey();
096        }
097
098        @Override
099        public V getValue() {
100            if (last == null) {
101                throw new IllegalStateException(
102                        "Iterator getValue() can only be called after next() and before remove()");
103            }
104            return last.getValue();
105        }
106
107        @Override
108        public boolean hasNext() {
109            return iterator.hasNext();
110        }
111
112        @Override
113        public boolean hasPrevious() {
114            return iterator.hasPrevious();
115        }
116
117        @Override
118        public K next() {
119            last = iterator.next();
120            return last.getKey();
121        }
122
123        @Override
124        public K previous() {
125            last = iterator.previous();
126            return last.getKey();
127        }
128
129        @Override
130        public void remove() {
131            iterator.remove();
132            parent.remove(last.getKey());
133            last = null;
134        }
135
136        @Override
137        public void reset() {
138            iterator = new ArrayList<>(parent.entrySet()).listIterator();
139            last = null;
140        }
141
142        @Override
143        public V setValue(final V value) {
144            if (last == null) {
145                throw new IllegalStateException(
146                        "Iterator setValue() can only be called after next() and before remove()");
147            }
148            if (parent.reverseMap.containsKey(value) &&
149                parent.reverseMap.get(value) != last.getKey()) {
150                throw new IllegalArgumentException(
151                        "Cannot use setValue() when the object being set is already in the map");
152            }
153            final V oldValue = parent.put(last.getKey(), value);
154            // Map.Entry specifies that the behavior is undefined when the backing map
155            // has been modified (as we did with the put), so we also set the value
156            last.setValue(value);
157            return oldValue;
158        }
159
160        @Override
161        public String toString() {
162            if (last != null) {
163                return "MapIterator[" + getKey() + "=" + getValue() + "]";
164            }
165            return "MapIterator[]";
166        }
167    }
168
169    /**
170     * Internal sorted map view.
171     *
172     * @param <K> The type of the keys.
173     * @param <V> The type of the values.
174     */
175    protected static class ViewMap<K, V> extends AbstractSortedMapDecorator<K, V> {
176
177        /**
178         * Constructs a new instance.
179         *
180         * @param bidi  The parent bidi map
181         * @param sm  The subMap sorted map
182         */
183        protected ViewMap(final DualTreeBidiMap<K, V> bidi, final SortedMap<K, V> sm) {
184            // the implementation is not great here...
185            // use the normalMap as the filtered map, but reverseMap as the full map
186            // this forces containsValue and clear to be overridden
187            super(new DualTreeBidiMap<>(sm, bidi.reverseMap, bidi.inverseBidiMap));
188        }
189
190        @Override
191        public void clear() {
192            // override as default implementation uses reverseMap
193            for (final Iterator<K> it = keySet().iterator(); it.hasNext();) {
194                it.next();
195                it.remove();
196            }
197        }
198
199        @Override
200        public boolean containsValue(final Object value) {
201            // override as default implementation uses reverseMap
202            return decorated().normalMap.containsValue(value);
203        }
204
205        @Override
206        protected DualTreeBidiMap<K, V> decorated() {
207            return (DualTreeBidiMap<K, V>) super.decorated();
208        }
209
210        @Override
211        public SortedMap<K, V> headMap(final K toKey) {
212            return new ViewMap<>(decorated(), super.headMap(toKey));
213        }
214
215        @Override
216        public K nextKey(final K key) {
217            return decorated().nextKey(key);
218        }
219
220        @Override
221        public K previousKey(final K key) {
222            return decorated().previousKey(key);
223        }
224
225        @Override
226        public SortedMap<K, V> subMap(final K fromKey, final K toKey) {
227            return new ViewMap<>(decorated(), super.subMap(fromKey, toKey));
228        }
229
230        @Override
231        public SortedMap<K, V> tailMap(final K fromKey) {
232            return new ViewMap<>(decorated(), super.tailMap(fromKey));
233        }
234    }
235
236    /** Ensure serialization compatibility */
237    private static final long serialVersionUID = 721969328361809L;
238
239    /** The key comparator to use */
240    private final Comparator<? super K> comparator;
241
242    /** The value comparator to use */
243    private final Comparator<? super V> valueComparator;
244
245    /**
246     * Creates an empty {@link DualTreeBidiMap}.
247     */
248    public DualTreeBidiMap() {
249        super(new TreeMap<>(), new TreeMap<>());
250        this.comparator = null;
251        this.valueComparator = null;
252    }
253
254    /**
255     * Constructs a {@link DualTreeBidiMap} using the specified {@link Comparator}.
256     *
257     * @param keyComparator  The comparator
258     * @param valueComparator  The values comparator to use
259     */
260    public DualTreeBidiMap(final Comparator<? super K> keyComparator, final Comparator<? super V> valueComparator) {
261        super(new TreeMap<>(keyComparator), new TreeMap<>(valueComparator));
262        this.comparator = keyComparator;
263        this.valueComparator = valueComparator;
264    }
265
266    /**
267     * Constructs a {@link DualTreeBidiMap} and copies the mappings from
268     * specified {@link Map}.
269     *
270     * @param map  The map whose mappings are to be placed in this map
271     */
272    public DualTreeBidiMap(final Map<? extends K, ? extends V> map) {
273        super(new TreeMap<>(), new TreeMap<>());
274        putAll(map);
275        this.comparator = null;
276        this.valueComparator = null;
277    }
278
279    /**
280     * Constructs a {@link DualTreeBidiMap} that decorates the specified maps.
281     *
282     * @param normalMap  The normal direction map
283     * @param reverseMap  The reverse direction map
284     * @param inverseBidiMap  The inverse BidiMap
285     */
286    protected DualTreeBidiMap(final Map<K, V> normalMap, final Map<V, K> reverseMap,
287                              final BidiMap<V, K> inverseBidiMap) {
288        super(normalMap, reverseMap, inverseBidiMap);
289        this.comparator = ((SortedMap<K, V>) normalMap).comparator();
290        this.valueComparator = ((SortedMap<V, K>) reverseMap).comparator();
291    }
292
293    @Override
294    public Comparator<? super K> comparator() {
295        return ((SortedMap<K, V>) normalMap).comparator();
296    }
297
298    /**
299     * Creates a new instance of this object.
300     *
301     * @param normalMap  The normal direction map
302     * @param reverseMap  The reverse direction map
303     * @param inverseMap  The inverse BidiMap
304     * @return new bidi map
305     */
306    @Override
307    protected DualTreeBidiMap<V, K> createBidiMap(final Map<V, K> normalMap, final Map<K, V> reverseMap,
308                                                  final BidiMap<K, V> inverseMap) {
309        return new DualTreeBidiMap<>(normalMap, reverseMap, inverseMap);
310    }
311
312    @Override
313    public K firstKey() {
314        return ((SortedMap<K, V>) normalMap).firstKey();
315    }
316
317    @Override
318    public SortedMap<K, V> headMap(final K toKey) {
319        final SortedMap<K, V> sub = ((SortedMap<K, V>) normalMap).headMap(toKey);
320        return new ViewMap<>(this, sub);
321    }
322
323    @Override
324    public SortedBidiMap<V, K> inverseBidiMap() {
325        return (SortedBidiMap<V, K>) super.inverseBidiMap();
326    }
327
328    /**
329     * Defaults to {@link #inverseBidiMap()}.
330     *
331     * @return Defaults to {@link #inverseBidiMap()}.
332     */
333    public OrderedBidiMap<V, K> inverseOrderedBidiMap() {
334        return inverseBidiMap();
335    }
336
337    /**
338     * Defaults to {@link #inverseBidiMap()}.
339     *
340     * @return Defaults to {@link #inverseBidiMap()}.
341     */
342    public SortedBidiMap<V, K> inverseSortedBidiMap() {
343        return inverseBidiMap();
344    }
345
346    @Override
347    public K lastKey() {
348        return ((SortedMap<K, V>) normalMap).lastKey();
349    }
350
351    /**
352     * Obtains an ordered map iterator.
353     * <p>
354     * This implementation copies the elements to an ArrayList in order to
355     * provide the forward/backward behavior.
356     * </p>
357     *
358     * @return A new ordered map iterator
359     */
360    @Override
361    public OrderedMapIterator<K, V> mapIterator() {
362        return new BidiOrderedMapIterator<>(this);
363    }
364
365    @Override
366    public K nextKey(final K key) {
367        if (isEmpty()) {
368            return null;
369        }
370        if (normalMap instanceof OrderedMap) {
371            return ((OrderedMap<K, ?>) normalMap).nextKey(key);
372        }
373        final SortedMap<K, V> sm = (SortedMap<K, V>) normalMap;
374        final Iterator<K> it = sm.tailMap(key).keySet().iterator();
375        it.next();
376        if (it.hasNext()) {
377            return it.next();
378        }
379        return null;
380    }
381
382    @Override
383    public K previousKey(final K key) {
384        if (isEmpty()) {
385            return null;
386        }
387        if (normalMap instanceof OrderedMap) {
388            return ((OrderedMap<K, V>) normalMap).previousKey(key);
389        }
390        final SortedMap<K, V> sm = (SortedMap<K, V>) normalMap;
391        final SortedMap<K, V> hm = sm.headMap(key);
392        if (hm.isEmpty()) {
393            return null;
394        }
395        return hm.lastKey();
396    }
397
398    /**
399     * Deserializes an instance from an ObjectInputStream.
400     *
401     * @param in The source ObjectInputStream.
402     * @throws IOException            Any of the usual Input/Output related exceptions.
403     * @throws ClassNotFoundException A class of a serialized object cannot be found.
404     */
405    private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
406        in.defaultReadObject();
407        normalMap = new TreeMap<>(comparator);
408        reverseMap = new TreeMap<>(valueComparator);
409        @SuppressWarnings("unchecked") // will fail at runtime if the stream is incorrect
410        final Map<K, V> map = (Map<K, V>) in.readObject();
411        putAll(map);
412    }
413
414    @Override
415    public SortedMap<K, V> subMap(final K fromKey, final K toKey) {
416        final SortedMap<K, V> sub = ((SortedMap<K, V>) normalMap).subMap(fromKey, toKey);
417        return new ViewMap<>(this, sub);
418    }
419
420    @Override
421    public SortedMap<K, V> tailMap(final K fromKey) {
422        final SortedMap<K, V> sub = ((SortedMap<K, V>) normalMap).tailMap(fromKey);
423        return new ViewMap<>(this, sub);
424    }
425
426    @Override
427    public Comparator<? super V> valueComparator() {
428        return ((SortedMap<V, K>) reverseMap).comparator();
429    }
430
431    /**
432     * Serializes this object to an ObjectOutputStream.
433     *
434     * @param out The target ObjectOutputStream.
435     * @throws IOException thrown when an I/O errors occur writing to the target stream.
436     */
437    private void writeObject(final ObjectOutputStream out) throws IOException {
438        out.defaultWriteObject();
439        out.writeObject(normalMap);
440    }
441
442}