SingletonMap.java

  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.collections4.map;

  18. import java.io.Serializable;
  19. import java.util.AbstractSet;
  20. import java.util.Collection;
  21. import java.util.Collections;
  22. import java.util.Iterator;
  23. import java.util.Map;
  24. import java.util.NoSuchElementException;
  25. import java.util.Objects;
  26. import java.util.Set;

  27. import org.apache.commons.collections4.BoundedMap;
  28. import org.apache.commons.collections4.KeyValue;
  29. import org.apache.commons.collections4.OrderedMap;
  30. import org.apache.commons.collections4.OrderedMapIterator;
  31. import org.apache.commons.collections4.ResettableIterator;
  32. import org.apache.commons.collections4.iterators.SingletonIterator;
  33. import org.apache.commons.collections4.keyvalue.TiedMapEntry;

  34. /**
  35.  * A {@code Map} implementation that holds a single item and is fixed size.
  36.  * <p>
  37.  * The single key/value pair is specified at creation.
  38.  * The map is fixed size so any action that would change the size is disallowed.
  39.  * However, the {@code put} or {@code setValue} methods can <em>change</em>
  40.  * the value associated with the key.
  41.  * </p>
  42.  * <p>
  43.  * If trying to remove or clear the map, an UnsupportedOperationException is thrown.
  44.  * If trying to put a new mapping into the map, an  IllegalArgumentException is thrown.
  45.  * The put method will only succeed if the key specified is the same as the
  46.  * singleton key.
  47.  * </p>
  48.  * <p>
  49.  * The key and value can be obtained by:
  50.  * </p>
  51.  * <ul>
  52.  * <li>normal Map methods and views
  53.  * <li>the {@code MapIterator}, see {@link #mapIterator()}
  54.  * <li>the {@code KeyValue} interface (just cast - no object creation)
  55.  * </ul>
  56.  *
  57.  * @param <K> the type of the keys in this map
  58.  * @param <V> the type of the values in this map
  59.  * @since 3.1
  60.  */
  61. public class SingletonMap<K, V>
  62.         implements OrderedMap<K, V>, BoundedMap<K, V>, KeyValue<K, V>, Serializable, Cloneable {

  63.     /**
  64.      * SingletonMapIterator.
  65.      */
  66.     static class SingletonMapIterator<K, V> implements OrderedMapIterator<K, V>, ResettableIterator<K> {
  67.         private final SingletonMap<K, V> parent;
  68.         private boolean hasNext = true;
  69.         private boolean canGetSet;

  70.         SingletonMapIterator(final SingletonMap<K, V> parent) {
  71.             this.parent = parent;
  72.         }

  73.         @Override
  74.         public K getKey() {
  75.             if (!canGetSet) {
  76.                 throw new IllegalStateException(AbstractHashedMap.GETKEY_INVALID);
  77.             }
  78.             return parent.getKey();
  79.         }

  80.         @Override
  81.         public V getValue() {
  82.             if (!canGetSet) {
  83.                 throw new IllegalStateException(AbstractHashedMap.GETVALUE_INVALID);
  84.             }
  85.             return parent.getValue();
  86.         }

  87.         @Override
  88.         public boolean hasNext() {
  89.             return hasNext;
  90.         }

  91.         @Override
  92.         public boolean hasPrevious() {
  93.             return !hasNext;
  94.         }

  95.         @Override
  96.         public K next() {
  97.             if (!hasNext) {
  98.                 throw new NoSuchElementException(AbstractHashedMap.NO_NEXT_ENTRY);
  99.             }
  100.             hasNext = false;
  101.             canGetSet = true;
  102.             return parent.getKey();
  103.         }

  104.         @Override
  105.         public K previous() {
  106.             if (hasNext) {
  107.                 throw new NoSuchElementException(AbstractHashedMap.NO_PREVIOUS_ENTRY);
  108.             }
  109.             hasNext = true;
  110.             return parent.getKey();
  111.         }

  112.         @Override
  113.         public void remove() {
  114.             throw new UnsupportedOperationException();
  115.         }

  116.         @Override
  117.         public void reset() {
  118.             hasNext = true;
  119.         }

  120.         @Override
  121.         public V setValue(final V value) {
  122.             if (!canGetSet) {
  123.                 throw new IllegalStateException(AbstractHashedMap.SETVALUE_INVALID);
  124.             }
  125.             return parent.setValue(value);
  126.         }

  127.         @Override
  128.         public String toString() {
  129.             if (hasNext) {
  130.                 return "Iterator[]";
  131.             }
  132.             return "Iterator[" + getKey() + "=" + getValue() + "]";
  133.         }
  134.     }

  135.     /**
  136.      * Values implementation for the SingletonMap.
  137.      * This class is needed as values is a view that must update as the map updates.
  138.      *
  139.      * @param <V> the type of the values in this set.
  140.      */
  141.     static class SingletonValues<V> extends AbstractSet<V> implements Serializable {
  142.         private static final long serialVersionUID = -3689524741863047872L;
  143.         private final SingletonMap<?, V> parent;

  144.         SingletonValues(final SingletonMap<?, V> parent) {
  145.             this.parent = parent;
  146.         }

  147.         @Override
  148.         public void clear() {
  149.             throw new UnsupportedOperationException();
  150.         }
  151.         @Override
  152.         public boolean contains(final Object object) {
  153.             return parent.containsValue(object);
  154.         }
  155.         @Override
  156.         public boolean isEmpty() {
  157.             return false;
  158.         }
  159.         @Override
  160.         public Iterator<V> iterator() {
  161.             return new SingletonIterator<>(parent.getValue(), false);
  162.         }
  163.         @Override
  164.         public int size() {
  165.             return 1;
  166.         }
  167.     }
  168.     /** Serialization version */
  169.     private static final long serialVersionUID = -8931271118676803261L;

  170.     /** Singleton key */
  171.     private final K key;

  172.     /** Singleton value */
  173.     private V value;

  174.     /**
  175.      * Constructor that creates a map of {@code null} to {@code null}.
  176.      */
  177.     public SingletonMap() {
  178.         this.key = null;
  179.     }

  180.     /**
  181.      * Constructor specifying the key and value.
  182.      *
  183.      * @param key  the key to use
  184.      * @param value  the value to use
  185.      */
  186.     public SingletonMap(final K key, final V value) {
  187.         this.key = key;
  188.         this.value = value;
  189.     }

  190.     /**
  191.      * Constructor specifying the key and value as a {@code KeyValue}.
  192.      *
  193.      * @param keyValue  the key value pair to use
  194.      */
  195.     public SingletonMap(final KeyValue<K, V> keyValue) {
  196.         this.key = keyValue.getKey();
  197.         this.value = keyValue.getValue();
  198.     }

  199.     /**
  200.      * Constructor specifying the key and value as a {@code MapEntry}.
  201.      *
  202.      * @param mapEntry  the mapEntry to use
  203.      */
  204.     public SingletonMap(final Map.Entry<? extends K, ? extends V> mapEntry) {
  205.         this.key = mapEntry.getKey();
  206.         this.value = mapEntry.getValue();
  207.     }

  208.     /**
  209.      * Constructor copying elements from another map.
  210.      *
  211.      * @param map  the map to copy, must be size 1
  212.      * @throws NullPointerException if the map is null
  213.      * @throws IllegalArgumentException if the size is not 1
  214.      */
  215.     public SingletonMap(final Map<? extends K, ? extends V> map) {
  216.         if (map.size() != 1) {
  217.             throw new IllegalArgumentException("The map size must be 1");
  218.         }
  219.         final Map.Entry<? extends K, ? extends V> entry = map.entrySet().iterator().next();
  220.         this.key = entry.getKey();
  221.         this.value = entry.getValue();
  222.     }

  223.     /**
  224.      * Unsupported operation.
  225.      */
  226.     @Override
  227.     public void clear() {
  228.         throw new UnsupportedOperationException();
  229.     }

  230.     /**
  231.      * Clones the map without cloning the key or value.
  232.      *
  233.      * @return a shallow clone
  234.      */
  235.     @Override
  236.     @SuppressWarnings("unchecked")
  237.     public SingletonMap<K, V> clone() {
  238.         try {
  239.             return (SingletonMap<K, V>) super.clone();
  240.         } catch (final CloneNotSupportedException ex) {
  241.             throw new UnsupportedOperationException(ex);
  242.         }
  243.     }

  244.     /**
  245.      * Checks whether the map contains the specified key.
  246.      *
  247.      * @param key  the key to search for
  248.      * @return true if the map contains the key
  249.      */
  250.     @Override
  251.     public boolean containsKey(final Object key) {
  252.         return isEqualKey(key);
  253.     }

  254.     /**
  255.      * Checks whether the map contains the specified value.
  256.      *
  257.      * @param value  the value to search for
  258.      * @return true if the map contains the key
  259.      */
  260.     @Override
  261.     public boolean containsValue(final Object value) {
  262.         return isEqualValue(value);
  263.     }

  264.     /**
  265.      * Gets the entrySet view of the map.
  266.      * Changes made via {@code setValue} affect this map.
  267.      * To simply iterate through the entries, use {@link #mapIterator()}.
  268.      *
  269.      * @return the entrySet view
  270.      */
  271.     @Override
  272.     public Set<Map.Entry<K, V>> entrySet() {
  273.         final Map.Entry<K, V> entry = new TiedMapEntry<>(this, getKey());
  274.         return Collections.singleton(entry);
  275.     }

  276.     /**
  277.      * Compares this map with another.
  278.      *
  279.      * @param obj  the object to compare to
  280.      * @return true if equal
  281.      */
  282.     @Override
  283.     public boolean equals(final Object obj) {
  284.         if (obj == this) {
  285.             return true;
  286.         }
  287.         if (!(obj instanceof Map)) {
  288.             return false;
  289.         }
  290.         final Map<?, ?> other = (Map<?, ?>) obj;
  291.         if (other.size() != 1) {
  292.             return false;
  293.         }
  294.         final Map.Entry<?, ?> entry = other.entrySet().iterator().next();
  295.         return isEqualKey(entry.getKey()) && isEqualValue(entry.getValue());
  296.     }

  297.     /**
  298.      * Gets the first (and only) key in the map.
  299.      *
  300.      * @return the key
  301.      */
  302.     @Override
  303.     public K firstKey() {
  304.         return getKey();
  305.     }

  306.     // Map
  307.     /**
  308.      * Gets the value mapped to the key specified.
  309.      *
  310.      * @param key  the key
  311.      * @return the mapped value, null if no match
  312.      */
  313.     @Override
  314.     public V get(final Object key) {
  315.         if (isEqualKey(key)) {
  316.             return value;
  317.         }
  318.         return null;
  319.     }

  320.     // KeyValue
  321.     /**
  322.      * Gets the key.
  323.      *
  324.      * @return the key
  325.      */
  326.     @Override
  327.     public K getKey() {
  328.         return key;
  329.     }

  330.     /**
  331.      * Gets the value.
  332.      *
  333.      * @return the value
  334.      */
  335.     @Override
  336.     public V getValue() {
  337.         return value;
  338.     }

  339.     /**
  340.      * Gets the standard Map hashCode.
  341.      *
  342.      * @return the hash code defined in the Map interface
  343.      */
  344.     @Override
  345.     public int hashCode() {
  346.         return (getKey() == null ? 0 : getKey().hashCode()) ^
  347.                (getValue() == null ? 0 : getValue().hashCode());
  348.     }

  349.     /**
  350.      * Checks whether the map is currently empty, which it never is.
  351.      *
  352.      * @return false always
  353.      */
  354.     @Override
  355.     public boolean isEmpty() {
  356.         return false;
  357.     }

  358.     /**
  359.      * Compares the specified key to the stored key.
  360.      *
  361.      * @param key  the key to compare
  362.      * @return true if equal
  363.      */
  364.     protected boolean isEqualKey(final Object key) {
  365.         return Objects.equals(key, getKey());
  366.     }

  367.     /**
  368.      * Compares the specified value to the stored value.
  369.      *
  370.      * @param value  the value to compare
  371.      * @return true if equal
  372.      */
  373.     protected boolean isEqualValue(final Object value) {
  374.         return Objects.equals(value, getValue());
  375.     }

  376.     // BoundedMap
  377.     /**
  378.      * Is the map currently full, always true.
  379.      *
  380.      * @return true always
  381.      */
  382.     @Override
  383.     public boolean isFull() {
  384.         return true;
  385.     }

  386.     /**
  387.      * Gets the unmodifiable keySet view of the map.
  388.      * Changes made to the view affect this map.
  389.      * To simply iterate through the keys, use {@link #mapIterator()}.
  390.      *
  391.      * @return the keySet view
  392.      */
  393.     @Override
  394.     public Set<K> keySet() {
  395.         return Collections.singleton(key);
  396.     }

  397.     /**
  398.      * Gets the last (and only) key in the map.
  399.      *
  400.      * @return the key
  401.      */
  402.     @Override
  403.     public K lastKey() {
  404.         return getKey();
  405.     }

  406.     /**
  407.      * {@inheritDoc}
  408.      */
  409.     @Override
  410.     public OrderedMapIterator<K, V> mapIterator() {
  411.         return new SingletonMapIterator<>(this);
  412.     }

  413.     /**
  414.      * Gets the maximum size of the map, always 1.
  415.      *
  416.      * @return 1 always
  417.      */
  418.     @Override
  419.     public int maxSize() {
  420.         return 1;
  421.     }

  422.     /**
  423.      * Gets the next key after the key specified, always null.
  424.      *
  425.      * @param key  the next key
  426.      * @return null always
  427.      */
  428.     @Override
  429.     public K nextKey(final K key) {
  430.         return null;
  431.     }

  432.     /**
  433.      * Gets the previous key before the key specified, always null.
  434.      *
  435.      * @param key  the next key
  436.      * @return null always
  437.      */
  438.     @Override
  439.     public K previousKey(final K key) {
  440.         return null;
  441.     }

  442.     /**
  443.      * Puts a key-value mapping into this map where the key must match the existing key.
  444.      * <p>
  445.      * An IllegalArgumentException is thrown if the key does not match as the map
  446.      * is fixed size.
  447.      * </p>
  448.      *
  449.      * @param key  the key to set, must be the key of the map
  450.      * @param value  the value to set
  451.      * @return the value previously mapped to this key, null if none
  452.      * @throws IllegalArgumentException if the key does not match
  453.      */
  454.     @Override
  455.     public V put(final K key, final V value) {
  456.         if (isEqualKey(key)) {
  457.             return setValue(value);
  458.         }
  459.         throw new IllegalArgumentException("Cannot put new key/value pair - Map is fixed size singleton");
  460.     }

  461.     /**
  462.      * Puts the values from the specified map into this map.
  463.      * <p>
  464.      * The map must be of size 0 or size 1.
  465.      * If it is size 1, the key must match the key of this map otherwise an
  466.      * IllegalArgumentException is thrown.
  467.      * </p>
  468.      *
  469.      * @param map  the map to add, must be size 0 or 1, and the key must match
  470.      * @throws NullPointerException if the map is null
  471.      * @throws IllegalArgumentException if the key does not match
  472.      */
  473.     @Override
  474.     public void putAll(final Map<? extends K, ? extends V> map) {
  475.         switch (map.size()) {
  476.         case 0:
  477.             return;

  478.         case 1:
  479.             final Map.Entry<? extends K, ? extends V> entry = map.entrySet().iterator().next();
  480.             put(entry.getKey(), entry.getValue());
  481.             return;

  482.         default:
  483.             throw new IllegalArgumentException("The map size must be 0 or 1");
  484.         }
  485.     }

  486.     /**
  487.      * Unsupported operation.
  488.      *
  489.      * @param key  the mapping to remove
  490.      * @return the value mapped to the removed key, null if key not in map
  491.      * @throws UnsupportedOperationException always
  492.      */
  493.     @Override
  494.     public V remove(final Object key) {
  495.         throw new UnsupportedOperationException();
  496.     }

  497.     /**
  498.      * Sets the value.
  499.      *
  500.      * @param value  the new value to set
  501.      * @return the old value
  502.      */
  503.     public V setValue(final V value) {
  504.         final V old = this.value;
  505.         this.value = value;
  506.         return old;
  507.     }

  508.     /**
  509.      * Gets the size of the map, always 1.
  510.      *
  511.      * @return the size of 1
  512.      */
  513.     @Override
  514.     public int size() {
  515.         return 1;
  516.     }

  517.     /**
  518.      * Gets the map as a String.
  519.      *
  520.      * @return a string version of the map
  521.      */
  522.     @Override
  523.     public String toString() {
  524.         return new StringBuilder(128)
  525.             .append('{')
  526.             .append(getKey() == this ? "(this Map)" : getKey())
  527.             .append('=')
  528.             .append(getValue() == this ? "(this Map)" : getValue())
  529.             .append('}')
  530.             .toString();
  531.     }

  532.     /**
  533.      * Gets the unmodifiable values view of the map.
  534.      * Changes made to the view affect this map.
  535.      * To simply iterate through the values, use {@link #mapIterator()}.
  536.      *
  537.      * @return the values view
  538.      */
  539.     @Override
  540.     public Collection<V> values() {
  541.         return new SingletonValues<>(this);
  542.     }

  543. }