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.map; 018 019import java.io.Serializable; 020import java.util.AbstractSet; 021import java.util.Collection; 022import java.util.Collections; 023import java.util.Iterator; 024import java.util.Map; 025import java.util.NoSuchElementException; 026import java.util.Set; 027 028import org.apache.commons.collections4.BoundedMap; 029import org.apache.commons.collections4.KeyValue; 030import org.apache.commons.collections4.OrderedMap; 031import org.apache.commons.collections4.OrderedMapIterator; 032import org.apache.commons.collections4.ResettableIterator; 033import org.apache.commons.collections4.iterators.SingletonIterator; 034import org.apache.commons.collections4.keyvalue.TiedMapEntry; 035 036/** 037 * A <code>Map</code> implementation that holds a single item and is fixed size. 038 * <p> 039 * The single key/value pair is specified at creation. 040 * The map is fixed size so any action that would change the size is disallowed. 041 * However, the <code>put</code> or <code>setValue</code> methods can <i>change</i> 042 * the value associated with the key. 043 * </p> 044 * <p> 045 * If trying to remove or clear the map, an UnsupportedOperationException is thrown. 046 * If trying to put a new mapping into the map, an IllegalArgumentException is thrown. 047 * The put method will only succeed if the key specified is the same as the 048 * singleton key. 049 * </p> 050 * <p> 051 * The key and value can be obtained by: 052 * </p> 053 * <ul> 054 * <li>normal Map methods and views 055 * <li>the <code>MapIterator</code>, see {@link #mapIterator()} 056 * <li>the <code>KeyValue</code> interface (just cast - no object creation) 057 * </ul> 058 * 059 * @param <K> the type of the keys in this map 060 * @param <V> the type of the values in this map 061 * @since 3.1 062 */ 063public class SingletonMap<K, V> 064 implements OrderedMap<K, V>, BoundedMap<K, V>, KeyValue<K, V>, Serializable, Cloneable { 065 066 /** Serialization version */ 067 private static final long serialVersionUID = -8931271118676803261L; 068 069 /** Singleton key */ 070 private final K key; 071 /** Singleton value */ 072 private V value; 073 074 /** 075 * Constructor that creates a map of <code>null</code> to <code>null</code>. 076 */ 077 public SingletonMap() { 078 super(); 079 this.key = null; 080 } 081 082 /** 083 * Constructor specifying the key and value. 084 * 085 * @param key the key to use 086 * @param value the value to use 087 */ 088 public SingletonMap(final K key, final V value) { 089 super(); 090 this.key = key; 091 this.value = value; 092 } 093 094 /** 095 * Constructor specifying the key and value as a <code>KeyValue</code>. 096 * 097 * @param keyValue the key value pair to use 098 */ 099 public SingletonMap(final KeyValue<K, V> keyValue) { 100 super(); 101 this.key = keyValue.getKey(); 102 this.value = keyValue.getValue(); 103 } 104 105 /** 106 * Constructor specifying the key and value as a <code>MapEntry</code>. 107 * 108 * @param mapEntry the mapEntry to use 109 */ 110 public SingletonMap(final Map.Entry<? extends K, ? extends V> mapEntry) { 111 super(); 112 this.key = mapEntry.getKey(); 113 this.value = mapEntry.getValue(); 114 } 115 116 /** 117 * Constructor copying elements from another map. 118 * 119 * @param map the map to copy, must be size 1 120 * @throws NullPointerException if the map is null 121 * @throws IllegalArgumentException if the size is not 1 122 */ 123 public SingletonMap(final Map<? extends K, ? extends V> map) { 124 super(); 125 if (map.size() != 1) { 126 throw new IllegalArgumentException("The map size must be 1"); 127 } 128 final Map.Entry<? extends K, ? extends V> entry = map.entrySet().iterator().next(); 129 this.key = entry.getKey(); 130 this.value = entry.getValue(); 131 } 132 133 // KeyValue 134 //----------------------------------------------------------------------- 135 /** 136 * Gets the key. 137 * 138 * @return the key 139 */ 140 @Override 141 public K getKey() { 142 return key; 143 } 144 145 /** 146 * Gets the value. 147 * 148 * @return the value 149 */ 150 @Override 151 public V getValue() { 152 return value; 153 } 154 155 /** 156 * Sets the value. 157 * 158 * @param value the new value to set 159 * @return the old value 160 */ 161 public V setValue(final V value) { 162 final V old = this.value; 163 this.value = value; 164 return old; 165 } 166 167 // BoundedMap 168 //----------------------------------------------------------------------- 169 /** 170 * Is the map currently full, always true. 171 * 172 * @return true always 173 */ 174 @Override 175 public boolean isFull() { 176 return true; 177 } 178 179 /** 180 * Gets the maximum size of the map, always 1. 181 * 182 * @return 1 always 183 */ 184 @Override 185 public int maxSize() { 186 return 1; 187 } 188 189 // Map 190 //----------------------------------------------------------------------- 191 /** 192 * Gets the value mapped to the key specified. 193 * 194 * @param key the key 195 * @return the mapped value, null if no match 196 */ 197 @Override 198 public V get(final Object key) { 199 if (isEqualKey(key)) { 200 return value; 201 } 202 return null; 203 } 204 205 /** 206 * Gets the size of the map, always 1. 207 * 208 * @return the size of 1 209 */ 210 @Override 211 public int size() { 212 return 1; 213 } 214 215 /** 216 * Checks whether the map is currently empty, which it never is. 217 * 218 * @return false always 219 */ 220 @Override 221 public boolean isEmpty() { 222 return false; 223 } 224 225 //----------------------------------------------------------------------- 226 /** 227 * Checks whether the map contains the specified key. 228 * 229 * @param key the key to search for 230 * @return true if the map contains the key 231 */ 232 @Override 233 public boolean containsKey(final Object key) { 234 return isEqualKey(key); 235 } 236 237 /** 238 * Checks whether the map contains the specified value. 239 * 240 * @param value the value to search for 241 * @return true if the map contains the key 242 */ 243 @Override 244 public boolean containsValue(final Object value) { 245 return isEqualValue(value); 246 } 247 248 //----------------------------------------------------------------------- 249 /** 250 * Puts a key-value mapping into this map where the key must match the existing key. 251 * <p> 252 * An IllegalArgumentException is thrown if the key does not match as the map 253 * is fixed size. 254 * 255 * @param key the key to set, must be the key of the map 256 * @param value the value to set 257 * @return the value previously mapped to this key, null if none 258 * @throws IllegalArgumentException if the key does not match 259 */ 260 @Override 261 public V put(final K key, final V value) { 262 if (isEqualKey(key)) { 263 return setValue(value); 264 } 265 throw new IllegalArgumentException("Cannot put new key/value pair - Map is fixed size singleton"); 266 } 267 268 /** 269 * Puts the values from the specified map into this map. 270 * <p> 271 * The map must be of size 0 or size 1. 272 * If it is size 1, the key must match the key of this map otherwise an 273 * IllegalArgumentException is thrown. 274 * 275 * @param map the map to add, must be size 0 or 1, and the key must match 276 * @throws NullPointerException if the map is null 277 * @throws IllegalArgumentException if the key does not match 278 */ 279 @Override 280 public void putAll(final Map<? extends K, ? extends V> map) { 281 switch (map.size()) { 282 case 0: 283 return; 284 285 case 1: 286 final Map.Entry<? extends K, ? extends V> entry = map.entrySet().iterator().next(); 287 put(entry.getKey(), entry.getValue()); 288 return; 289 290 default: 291 throw new IllegalArgumentException("The map size must be 0 or 1"); 292 } 293 } 294 295 /** 296 * Unsupported operation. 297 * 298 * @param key the mapping to remove 299 * @return the value mapped to the removed key, null if key not in map 300 * @throws UnsupportedOperationException always 301 */ 302 @Override 303 public V remove(final Object key) { 304 throw new UnsupportedOperationException(); 305 } 306 307 /** 308 * Unsupported operation. 309 */ 310 @Override 311 public void clear() { 312 throw new UnsupportedOperationException(); 313 } 314 315 //----------------------------------------------------------------------- 316 /** 317 * Gets the entrySet view of the map. 318 * Changes made via <code>setValue</code> affect this map. 319 * To simply iterate through the entries, use {@link #mapIterator()}. 320 * 321 * @return the entrySet view 322 */ 323 @Override 324 public Set<Map.Entry<K, V>> entrySet() { 325 final Map.Entry<K, V> entry = new TiedMapEntry<>(this, getKey()); 326 return Collections.singleton(entry); 327 } 328 329 /** 330 * Gets the unmodifiable keySet view of the map. 331 * Changes made to the view affect this map. 332 * To simply iterate through the keys, use {@link #mapIterator()}. 333 * 334 * @return the keySet view 335 */ 336 @Override 337 public Set<K> keySet() { 338 return Collections.singleton(key); 339 } 340 341 /** 342 * Gets the unmodifiable values view of the map. 343 * Changes made to the view affect this map. 344 * To simply iterate through the values, use {@link #mapIterator()}. 345 * 346 * @return the values view 347 */ 348 @Override 349 public Collection<V> values() { 350 return new SingletonValues<>(this); 351 } 352 353 /** 354 * {@inheritDoc} 355 */ 356 @Override 357 public OrderedMapIterator<K, V> mapIterator() { 358 return new SingletonMapIterator<>(this); 359 } 360 361 /** 362 * Gets the first (and only) key in the map. 363 * 364 * @return the key 365 */ 366 @Override 367 public K firstKey() { 368 return getKey(); 369 } 370 371 /** 372 * Gets the last (and only) key in the map. 373 * 374 * @return the key 375 */ 376 @Override 377 public K lastKey() { 378 return getKey(); 379 } 380 381 /** 382 * Gets the next key after the key specified, always null. 383 * 384 * @param key the next key 385 * @return null always 386 */ 387 @Override 388 public K nextKey(final K key) { 389 return null; 390 } 391 392 /** 393 * Gets the previous key before the key specified, always null. 394 * 395 * @param key the next key 396 * @return null always 397 */ 398 @Override 399 public K previousKey(final K key) { 400 return null; 401 } 402 403 //----------------------------------------------------------------------- 404 /** 405 * Compares the specified key to the stored key. 406 * 407 * @param key the key to compare 408 * @return true if equal 409 */ 410 protected boolean isEqualKey(final Object key) { 411 return key == null ? getKey() == null : key.equals(getKey()); 412 } 413 414 /** 415 * Compares the specified value to the stored value. 416 * 417 * @param value the value to compare 418 * @return true if equal 419 */ 420 protected boolean isEqualValue(final Object value) { 421 return value == null ? getValue() == null : value.equals(getValue()); 422 } 423 424 //----------------------------------------------------------------------- 425 /** 426 * SingletonMapIterator. 427 */ 428 static class SingletonMapIterator<K, V> implements OrderedMapIterator<K, V>, ResettableIterator<K> { 429 private final SingletonMap<K, V> parent; 430 private boolean hasNext = true; 431 private boolean canGetSet = false; 432 433 SingletonMapIterator(final SingletonMap<K, V> parent) { 434 super(); 435 this.parent = parent; 436 } 437 438 @Override 439 public boolean hasNext() { 440 return hasNext; 441 } 442 443 @Override 444 public K next() { 445 if (hasNext == false) { 446 throw new NoSuchElementException(AbstractHashedMap.NO_NEXT_ENTRY); 447 } 448 hasNext = false; 449 canGetSet = true; 450 return parent.getKey(); 451 } 452 453 @Override 454 public boolean hasPrevious() { 455 return hasNext == false; 456 } 457 458 @Override 459 public K previous() { 460 if (hasNext == true) { 461 throw new NoSuchElementException(AbstractHashedMap.NO_PREVIOUS_ENTRY); 462 } 463 hasNext = true; 464 return parent.getKey(); 465 } 466 467 @Override 468 public void remove() { 469 throw new UnsupportedOperationException(); 470 } 471 472 @Override 473 public K getKey() { 474 if (canGetSet == false) { 475 throw new IllegalStateException(AbstractHashedMap.GETKEY_INVALID); 476 } 477 return parent.getKey(); 478 } 479 480 @Override 481 public V getValue() { 482 if (canGetSet == false) { 483 throw new IllegalStateException(AbstractHashedMap.GETVALUE_INVALID); 484 } 485 return parent.getValue(); 486 } 487 488 @Override 489 public V setValue(final V value) { 490 if (canGetSet == false) { 491 throw new IllegalStateException(AbstractHashedMap.SETVALUE_INVALID); 492 } 493 return parent.setValue(value); 494 } 495 496 @Override 497 public void reset() { 498 hasNext = true; 499 } 500 501 @Override 502 public String toString() { 503 if (hasNext) { 504 return "Iterator[]"; 505 } 506 return "Iterator[" + getKey() + "=" + getValue() + "]"; 507 } 508 } 509 510 /** 511 * Values implementation for the SingletonMap. 512 * This class is needed as values is a view that must update as the map updates. 513 */ 514 static class SingletonValues<V> extends AbstractSet<V> implements Serializable { 515 private static final long serialVersionUID = -3689524741863047872L; 516 private final SingletonMap<?, V> parent; 517 518 SingletonValues(final SingletonMap<?, V> parent) { 519 super(); 520 this.parent = parent; 521 } 522 523 @Override 524 public int size() { 525 return 1; 526 } 527 @Override 528 public boolean isEmpty() { 529 return false; 530 } 531 @Override 532 public boolean contains(final Object object) { 533 return parent.containsValue(object); 534 } 535 @Override 536 public void clear() { 537 throw new UnsupportedOperationException(); 538 } 539 @Override 540 public Iterator<V> iterator() { 541 return new SingletonIterator<>(parent.getValue(), false); 542 } 543 } 544 545 //----------------------------------------------------------------------- 546 /** 547 * Clones the map without cloning the key or value. 548 * 549 * @return a shallow clone 550 */ 551 @Override 552 @SuppressWarnings("unchecked") 553 public SingletonMap<K, V> clone() { 554 try { 555 return (SingletonMap<K, V>) super.clone(); 556 } catch (final CloneNotSupportedException ex) { 557 throw new InternalError(); 558 } 559 } 560 561 /** 562 * Compares this map with another. 563 * 564 * @param obj the object to compare to 565 * @return true if equal 566 */ 567 @Override 568 public boolean equals(final Object obj) { 569 if (obj == this) { 570 return true; 571 } 572 if (obj instanceof Map == false) { 573 return false; 574 } 575 final Map<?,?> other = (Map<?,?>) obj; 576 if (other.size() != 1) { 577 return false; 578 } 579 final Map.Entry<?,?> entry = other.entrySet().iterator().next(); 580 return isEqualKey(entry.getKey()) && isEqualValue(entry.getValue()); 581 } 582 583 /** 584 * Gets the standard Map hashCode. 585 * 586 * @return the hash code defined in the Map interface 587 */ 588 @Override 589 public int hashCode() { 590 return (getKey() == null ? 0 : getKey().hashCode()) ^ 591 (getValue() == null ? 0 : getValue().hashCode()); 592 } 593 594 /** 595 * Gets the map as a String. 596 * 597 * @return a string version of the map 598 */ 599 @Override 600 public String toString() { 601 return new StringBuilder(128) 602 .append('{') 603 .append(getKey() == this ? "(this Map)" : getKey()) 604 .append('=') 605 .append(getValue() == this ? "(this Map)" : getValue()) 606 .append('}') 607 .toString(); 608 } 609 610}