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.collections.bidimap;
18  
19  import java.io.IOException;
20  import java.io.ObjectInputStream;
21  import java.io.ObjectOutputStream;
22  import java.io.Serializable;
23  import java.util.ArrayList;
24  import java.util.Comparator;
25  import java.util.Iterator;
26  import java.util.ListIterator;
27  import java.util.Map;
28  import java.util.SortedMap;
29  import java.util.TreeMap;
30  
31  import org.apache.commons.collections.BidiMap;
32  import org.apache.commons.collections.OrderedBidiMap;
33  import org.apache.commons.collections.OrderedMap;
34  import org.apache.commons.collections.OrderedMapIterator;
35  import org.apache.commons.collections.ResettableIterator;
36  import org.apache.commons.collections.SortedBidiMap;
37  import org.apache.commons.collections.map.AbstractSortedMapDecorator;
38  
39  /**
40   * Implementation of {@link BidiMap} that uses two {@link TreeMap} instances.
41   * <p>
42   * The setValue() method on iterators will succeed only if the new value being set is
43   * not already in the bidimap.
44   * <p>
45   * When considering whether to use this class, the {@link TreeBidiMap} class should
46   * also be considered. It implements the interface using a dedicated design, and does
47   * not store each object twice, which can save on memory use.
48   * <p>
49   * NOTE: From Commons Collections 3.1, all subclasses will use {@link TreeMap}
50   * and the flawed <code>createMap</code> method is ignored.
51   *
52   * @since 3.0
53   * @version $Id: DualTreeBidiMap.java 1435824 2013-01-20 11:41:17Z tn $
54   */
55  public class DualTreeBidiMap<K, V> extends AbstractDualBidiMap<K, V>
56          implements SortedBidiMap<K, V>, Serializable {
57  
58      /** Ensure serialization compatibility */
59      private static final long serialVersionUID = 721969328361809L;
60  
61      /** The key comparator to use */
62      protected final Comparator<? super K> comparator;
63  
64      /** The value comparator to use */
65      protected final Comparator<? super V> valueComparator;
66      
67      /**
68       * Creates an empty <code>DualTreeBidiMap</code>
69       */
70      public DualTreeBidiMap() {
71          super(new TreeMap<K, V>(), new TreeMap<V, K>());
72          this.comparator = null;
73          this.valueComparator = null;
74      }
75  
76      /**
77       * Constructs a <code>DualTreeBidiMap</code> and copies the mappings from
78       * specified <code>Map</code>.
79       *
80       * @param map  the map whose mappings are to be placed in this map
81       */
82      public DualTreeBidiMap(final Map<K, V> map) {
83          super(new TreeMap<K, V>(), new TreeMap<V, K>());
84          putAll(map);
85          this.comparator = null;
86          this.valueComparator = null;
87      }
88  
89      /**
90       * Constructs a {@link DualTreeBidiMap} using the specified {@link Comparator}.
91       *
92       * @param keyComparator  the comparator
93       * @param valueComparator  the values comparator to use
94       */
95      public DualTreeBidiMap(final Comparator<? super K> keyComparator, final Comparator<? super V> valueComparator) {
96          super(new TreeMap<K, V>(keyComparator), new TreeMap<V, K>(valueComparator));
97          this.comparator = keyComparator;
98          this.valueComparator = valueComparator;
99      }
100 
101     /**
102      * Constructs a {@link DualTreeBidiMap} that decorates the specified maps.
103      *
104      * @param normalMap  the normal direction map
105      * @param reverseMap  the reverse direction map
106      * @param inverseBidiMap  the inverse BidiMap
107      */
108     protected DualTreeBidiMap(final Map<K, V> normalMap, final Map<V, K> reverseMap,
109                               final BidiMap<V, K> inverseBidiMap) {
110         super(normalMap, reverseMap, inverseBidiMap);
111         this.comparator = ((SortedMap<K, V>) normalMap).comparator();
112         this.valueComparator = ((SortedMap<V, K>) reverseMap).comparator();
113     }
114 
115     /**
116      * Creates a new instance of this object.
117      *
118      * @param normalMap  the normal direction map
119      * @param reverseMap  the reverse direction map
120      * @param inverseMap  the inverse BidiMap
121      * @return new bidi map
122      */
123     @Override
124     protected DualTreeBidiMap<V, K> createBidiMap(final Map<V, K> normalMap, final Map<K, V> reverseMap,
125                                                   final BidiMap<K, V> inverseMap) {
126         return new DualTreeBidiMap<V, K>(normalMap, reverseMap, inverseMap);
127     }
128 
129     //-----------------------------------------------------------------------
130     
131     public Comparator<? super K> comparator() {
132         return ((SortedMap<K, V>) normalMap).comparator();
133     }
134 
135     public Comparator<? super V> valueComparator() {
136         return ((SortedMap<V, K>) reverseMap).comparator();
137         
138     }
139 
140     public K firstKey() {
141         return ((SortedMap<K, V>) normalMap).firstKey();
142     }
143 
144     public K lastKey() {
145         return ((SortedMap<K, V>) normalMap).lastKey();
146     }
147 
148     public K nextKey(final K key) {
149         if (isEmpty()) {
150             return null;
151         }
152         if (normalMap instanceof OrderedMap) {
153             return ((OrderedMap<K, ?>) normalMap).nextKey(key);
154         }
155         final SortedMap<K, V> sm = (SortedMap<K, V>) normalMap;
156         final Iterator<K> it = sm.tailMap(key).keySet().iterator();
157         it.next();
158         if (it.hasNext()) {
159             return it.next();
160         }
161         return null;
162     }
163 
164     public K previousKey(final K key) {
165         if (isEmpty()) {
166             return null;
167         }
168         if (normalMap instanceof OrderedMap) {
169             return ((OrderedMap<K, V>) normalMap).previousKey(key);
170         }
171         final SortedMap<K, V> sm = (SortedMap<K, V>) normalMap;
172         final SortedMap<K, V> hm = sm.headMap(key);
173         if (hm.isEmpty()) {
174             return null;
175         }
176         return hm.lastKey();
177     }
178 
179     //-----------------------------------------------------------------------
180     /**
181      * Obtains an ordered map iterator.
182      * <p>
183      * This implementation copies the elements to an ArrayList in order to
184      * provide the forward/backward behaviour.
185      *
186      * @return a new ordered map iterator
187      */
188     @Override
189     public OrderedMapIterator<K, V> mapIterator() {
190         return new BidiOrderedMapIterator<K, V>(this);
191     }
192 
193     public SortedBidiMap<V, K> inverseSortedBidiMap() {
194         return inverseBidiMap();
195     }
196 
197     public OrderedBidiMap<V, K> inverseOrderedBidiMap() {
198         return inverseBidiMap();
199     }
200 
201     //-----------------------------------------------------------------------
202     
203     public SortedMap<K, V> headMap(final K toKey) {
204         final SortedMap<K, V> sub = ((SortedMap<K, V>) normalMap).headMap(toKey);
205         return new ViewMap<K, V>(this, sub);
206     }
207 
208     public SortedMap<K, V> tailMap(final K fromKey) {
209         final SortedMap<K, V> sub = ((SortedMap<K, V>) normalMap).tailMap(fromKey);
210         return new ViewMap<K, V>(this, sub);
211     }
212 
213     public SortedMap<K, V> subMap(final K fromKey, final K toKey) {
214         final SortedMap<K, V> sub = ((SortedMap<K, V>) normalMap).subMap(fromKey, toKey);
215         return new ViewMap<K, V>(this, sub);
216     }
217 
218     @Override
219     public SortedBidiMap<V, K> inverseBidiMap() {
220         return (SortedBidiMap<V, K>) super.inverseBidiMap();
221     }
222 
223     //-----------------------------------------------------------------------
224     /**
225      * Internal sorted map view.
226      */
227     protected static class ViewMap<K, V> extends AbstractSortedMapDecorator<K, V> {
228         /** The parent bidi map. */
229         final DualTreeBidiMap<K, V> bidi;
230 
231         /**
232          * Constructor.
233          * @param bidi  the parent bidi map
234          * @param sm  the subMap sorted map
235          */
236         protected ViewMap(final DualTreeBidiMap<K, V> bidi, final SortedMap<K, V> sm) {
237             // the implementation is not great here...
238             // use the normalMap as the filtered map, but reverseMap as the full map
239             // this forces containsValue and clear to be overridden
240             super(new DualTreeBidiMap<K, V>(sm, bidi.reverseMap, bidi.inverseBidiMap));
241             this.bidi = decorated();
242         }
243 
244         @Override
245         public boolean containsValue(final Object value) {
246             // override as default implementation uses reverseMap
247             return decorated().normalMap.containsValue(value);
248         }
249 
250         @Override
251         public void clear() {
252             // override as default implementation uses reverseMap
253             for (final Iterator<K> it = keySet().iterator(); it.hasNext();) {
254                 it.next();
255                 it.remove();
256             }
257         }
258 
259         @Override
260         public SortedMap<K, V> headMap(final K toKey) {
261             return new ViewMap<K, V>(decorated(), super.headMap(toKey));
262         }
263 
264         @Override
265         public SortedMap<K, V> tailMap(final K fromKey) {
266             return new ViewMap<K, V>(decorated(), super.tailMap(fromKey));
267         }
268 
269         @Override
270         public SortedMap<K, V> subMap(final K fromKey, final K toKey) {
271             return new ViewMap<K, V>(decorated(), super.subMap(fromKey, toKey));
272         }
273 
274         @Override
275         protected DualTreeBidiMap<K, V> decorated() {
276             return (DualTreeBidiMap<K, V>) super.decorated();
277         }
278 
279         @Override
280         public K previousKey(final K key) {
281             return decorated().previousKey(key);
282         }
283 
284         @Override
285         public K nextKey(final K key) {
286             return decorated().nextKey(key);
287         }
288     }
289 
290     //-----------------------------------------------------------------------
291     /**
292      * Inner class MapIterator.
293      */
294     protected static class BidiOrderedMapIterator<K, V> implements OrderedMapIterator<K, V>, ResettableIterator<K> {
295 
296         /** The parent map */
297         protected final AbstractDualBidiMap<K, V> parent;
298 
299         /** The iterator being decorated */
300         protected ListIterator<Map.Entry<K, V>> iterator;
301 
302         /** The last returned entry */
303         private Map.Entry<K, V> last = null;
304 
305         /**
306          * Constructor.
307          * @param parent  the parent map
308          */
309         protected BidiOrderedMapIterator(final AbstractDualBidiMap<K, V> parent) {
310             super();
311             this.parent = parent;
312             iterator = new ArrayList<Map.Entry<K, V>>(parent.entrySet()).listIterator();
313         }
314 
315         public boolean hasNext() {
316             return iterator.hasNext();
317         }
318 
319         public K next() {
320             last = iterator.next();
321             return last.getKey();
322         }
323 
324         public boolean hasPrevious() {
325             return iterator.hasPrevious();
326         }
327 
328         public K previous() {
329             last = iterator.previous();
330             return last.getKey();
331         }
332 
333         public void remove() {
334             iterator.remove();
335             parent.remove(last.getKey());
336             last = null;
337         }
338 
339         public K getKey() {
340             if (last == null) {
341                 throw new IllegalStateException(
342                         "Iterator getKey() can only be called after next() and before remove()");
343             }
344             return last.getKey();
345         }
346 
347         public V getValue() {
348             if (last == null) {
349                 throw new IllegalStateException(
350                         "Iterator getValue() can only be called after next() and before remove()");
351             }
352             return last.getValue();
353         }
354 
355         public V setValue(final V value) {
356             if (last == null) {
357                 throw new IllegalStateException(
358                         "Iterator setValue() can only be called after next() and before remove()");
359             }
360             if (parent.reverseMap.containsKey(value) &&
361                 parent.reverseMap.get(value) != last.getKey()) {
362                 throw new IllegalArgumentException(
363                         "Cannot use setValue() when the object being set is already in the map");
364             }
365             return parent.put(last.getKey(), value);
366         }
367 
368         public void reset() {
369             iterator = new ArrayList<Map.Entry<K, V>>(parent.entrySet()).listIterator();
370             last = null;
371         }
372 
373         @Override
374         public String toString() {
375             if (last != null) {
376                 return "MapIterator[" + getKey() + "=" + getValue() + "]";
377             }
378             return "MapIterator[]";
379         }
380     }
381 
382     // Serialization
383     //-----------------------------------------------------------------------
384     private void writeObject(final ObjectOutputStream out) throws IOException {
385         out.defaultWriteObject();
386         out.writeObject(normalMap);
387     }
388 
389     private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
390         in.defaultReadObject();
391         normalMap = new TreeMap<K, V>(comparator);
392         reverseMap = new TreeMap<V, K>(valueComparator);
393         @SuppressWarnings("unchecked") // will fail at runtime if the stream is incorrect
394         final
395         Map<K, V> map = (Map<K, V>) in.readObject();
396         putAll(map);
397     }
398 
399 }