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