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