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 * Implementation of {@link BidiMap} that uses 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 bidimap. 044 * <p> 045 * When considering whether to use this class, the {@link TreeBidiMap} class should 046 * also be considered. It implements the interface using a dedicated design, and does 047 * not store each object twice, which can save on memory use. 048 * <p> 049 * NOTE: From Commons Collections 3.1, all subclasses will use {@link TreeMap} 050 * and the flawed <code>createMap</code> method is ignored. 051 * 052 * @since 3.0 053 * @version $Id: DualTreeBidiMap.BidiOrderedMapIterator.html 972421 2015-11-14 20:00:04Z tn $ 054 */ 055public class DualTreeBidiMap<K, V> extends AbstractDualBidiMap<K, V> 056 implements SortedBidiMap<K, V>, Serializable { 057 058 /** Ensure serialization compatibility */ 059 private static final long serialVersionUID = 721969328361809L; 060 061 /** The key comparator to use */ 062 private final Comparator<? super K> comparator; 063 064 /** The value comparator to use */ 065 private final Comparator<? super V> valueComparator; 066 067 /** 068 * Creates an empty <code>DualTreeBidiMap</code> 069 */ 070 public DualTreeBidiMap() { 071 super(new TreeMap<K, V>(), new TreeMap<V, K>()); 072 this.comparator = null; 073 this.valueComparator = null; 074 } 075 076 /** 077 * Constructs a <code>DualTreeBidiMap</code> and copies the mappings from 078 * specified <code>Map</code>. 079 * 080 * @param map the map whose mappings are to be placed in this map 081 */ 082 public DualTreeBidiMap(final Map<? extends K, ? extends V> map) { 083 super(new TreeMap<K, V>(), new TreeMap<V, K>()); 084 putAll(map); 085 this.comparator = null; 086 this.valueComparator = null; 087 } 088 089 /** 090 * Constructs a {@link DualTreeBidiMap} using the specified {@link Comparator}. 091 * 092 * @param keyComparator the comparator 093 * @param valueComparator the values comparator to use 094 */ 095 public DualTreeBidiMap(final Comparator<? super K> keyComparator, final Comparator<? super V> valueComparator) { 096 super(new TreeMap<K, V>(keyComparator), new TreeMap<V, K>(valueComparator)); 097 this.comparator = keyComparator; 098 this.valueComparator = valueComparator; 099 } 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 public K firstKey() { 140 return ((SortedMap<K, V>) normalMap).firstKey(); 141 } 142 143 public K lastKey() { 144 return ((SortedMap<K, V>) normalMap).lastKey(); 145 } 146 147 public K nextKey(final K key) { 148 if (isEmpty()) { 149 return null; 150 } 151 if (normalMap instanceof OrderedMap) { 152 return ((OrderedMap<K, ?>) normalMap).nextKey(key); 153 } 154 final SortedMap<K, V> sm = (SortedMap<K, V>) normalMap; 155 final Iterator<K> it = sm.tailMap(key).keySet().iterator(); 156 it.next(); 157 if (it.hasNext()) { 158 return it.next(); 159 } 160 return null; 161 } 162 163 public K previousKey(final K key) { 164 if (isEmpty()) { 165 return null; 166 } 167 if (normalMap instanceof OrderedMap) { 168 return ((OrderedMap<K, V>) normalMap).previousKey(key); 169 } 170 final SortedMap<K, V> sm = (SortedMap<K, V>) normalMap; 171 final SortedMap<K, V> hm = sm.headMap(key); 172 if (hm.isEmpty()) { 173 return null; 174 } 175 return hm.lastKey(); 176 } 177 178 //----------------------------------------------------------------------- 179 /** 180 * Obtains an ordered map iterator. 181 * <p> 182 * This implementation copies the elements to an ArrayList in order to 183 * provide the forward/backward behaviour. 184 * 185 * @return a new ordered map iterator 186 */ 187 @Override 188 public OrderedMapIterator<K, V> mapIterator() { 189 return new BidiOrderedMapIterator<K, V>(this); 190 } 191 192 public SortedBidiMap<V, K> inverseSortedBidiMap() { 193 return inverseBidiMap(); 194 } 195 196 public OrderedBidiMap<V, K> inverseOrderedBidiMap() { 197 return inverseBidiMap(); 198 } 199 200 //----------------------------------------------------------------------- 201 202 public SortedMap<K, V> headMap(final K toKey) { 203 final SortedMap<K, V> sub = ((SortedMap<K, V>) normalMap).headMap(toKey); 204 return new ViewMap<K, V>(this, sub); 205 } 206 207 public SortedMap<K, V> tailMap(final K fromKey) { 208 final SortedMap<K, V> sub = ((SortedMap<K, V>) normalMap).tailMap(fromKey); 209 return new ViewMap<K, V>(this, sub); 210 } 211 212 public SortedMap<K, V> subMap(final K fromKey, final K toKey) { 213 final SortedMap<K, V> sub = ((SortedMap<K, V>) normalMap).subMap(fromKey, toKey); 214 return new ViewMap<K, V>(this, sub); 215 } 216 217 @Override 218 public SortedBidiMap<V, K> inverseBidiMap() { 219 return (SortedBidiMap<V, K>) super.inverseBidiMap(); 220 } 221 222 //----------------------------------------------------------------------- 223 /** 224 * Internal sorted map view. 225 */ 226 protected static class ViewMap<K, V> extends AbstractSortedMapDecorator<K, V> { 227 /** 228 * Constructor. 229 * @param bidi the parent bidi map 230 * @param sm the subMap sorted map 231 */ 232 protected ViewMap(final DualTreeBidiMap<K, V> bidi, final SortedMap<K, V> sm) { 233 // the implementation is not great here... 234 // use the normalMap as the filtered map, but reverseMap as the full map 235 // this forces containsValue and clear to be overridden 236 super(new DualTreeBidiMap<K, V>(sm, bidi.reverseMap, bidi.inverseBidiMap)); 237 } 238 239 @Override 240 public boolean containsValue(final Object value) { 241 // override as default implementation uses reverseMap 242 return decorated().normalMap.containsValue(value); 243 } 244 245 @Override 246 public void clear() { 247 // override as default implementation uses reverseMap 248 for (final Iterator<K> it = keySet().iterator(); it.hasNext();) { 249 it.next(); 250 it.remove(); 251 } 252 } 253 254 @Override 255 public SortedMap<K, V> headMap(final K toKey) { 256 return new ViewMap<K, V>(decorated(), super.headMap(toKey)); 257 } 258 259 @Override 260 public SortedMap<K, V> tailMap(final K fromKey) { 261 return new ViewMap<K, V>(decorated(), super.tailMap(fromKey)); 262 } 263 264 @Override 265 public SortedMap<K, V> subMap(final K fromKey, final K toKey) { 266 return new ViewMap<K, V>(decorated(), super.subMap(fromKey, toKey)); 267 } 268 269 @Override 270 protected DualTreeBidiMap<K, V> decorated() { 271 return (DualTreeBidiMap<K, V>) super.decorated(); 272 } 273 274 @Override 275 public K previousKey(final K key) { 276 return decorated().previousKey(key); 277 } 278 279 @Override 280 public K nextKey(final K key) { 281 return decorated().nextKey(key); 282 } 283 } 284 285 //----------------------------------------------------------------------- 286 /** 287 * Inner class MapIterator. 288 */ 289 protected static class BidiOrderedMapIterator<K, V> implements OrderedMapIterator<K, V>, ResettableIterator<K> { 290 291 /** The parent map */ 292 private final AbstractDualBidiMap<K, V> parent; 293 294 /** The iterator being decorated */ 295 private ListIterator<Map.Entry<K, V>> iterator; 296 297 /** The last returned entry */ 298 private Map.Entry<K, V> last = null; 299 300 /** 301 * Constructor. 302 * @param parent the parent map 303 */ 304 protected BidiOrderedMapIterator(final AbstractDualBidiMap<K, V> parent) { 305 super(); 306 this.parent = parent; 307 iterator = new ArrayList<Map.Entry<K, V>>(parent.entrySet()).listIterator(); 308 } 309 310 public boolean hasNext() { 311 return iterator.hasNext(); 312 } 313 314 public K next() { 315 last = iterator.next(); 316 return last.getKey(); 317 } 318 319 public boolean hasPrevious() { 320 return iterator.hasPrevious(); 321 } 322 323 public K previous() { 324 last = iterator.previous(); 325 return last.getKey(); 326 } 327 328 public void remove() { 329 iterator.remove(); 330 parent.remove(last.getKey()); 331 last = null; 332 } 333 334 public K getKey() { 335 if (last == null) { 336 throw new IllegalStateException( 337 "Iterator getKey() can only be called after next() and before remove()"); 338 } 339 return last.getKey(); 340 } 341 342 public V getValue() { 343 if (last == null) { 344 throw new IllegalStateException( 345 "Iterator getValue() can only be called after next() and before remove()"); 346 } 347 return last.getValue(); 348 } 349 350 public V setValue(final V value) { 351 if (last == null) { 352 throw new IllegalStateException( 353 "Iterator setValue() can only be called after next() and before remove()"); 354 } 355 if (parent.reverseMap.containsKey(value) && 356 parent.reverseMap.get(value) != last.getKey()) { 357 throw new IllegalArgumentException( 358 "Cannot use setValue() when the object being set is already in the map"); 359 } 360 final V oldValue = parent.put(last.getKey(), value); 361 // Map.Entry specifies that the behavior is undefined when the backing map 362 // has been modified (as we did with the put), so we also set the value 363 // (especially needed for IBM JDK) 364 last.setValue(value); 365 return oldValue; 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 Map<K, V> map = (Map<K, V>) in.readObject(); 395 putAll(map); 396 } 397 398}