/*
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You under the Apache License, Version 2.0
 *  (the "License"); you may not use this file except in compliance with
 *  the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package org.apache.commons.collections.map;

import java.io.Serializable;
import java.util.AbstractSet;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;

import org.apache.commons.collections.BoundedMap;
import org.apache.commons.collections.KeyValue;
import org.apache.commons.collections.MapIterator;
import org.apache.commons.collections.OrderedMap;
import org.apache.commons.collections.OrderedMapIterator;
import org.apache.commons.collections.ResettableIterator;
import org.apache.commons.collections.iterators.SingletonIterator;
import org.apache.commons.collections.keyvalue.TiedMapEntry;

A Map implementation that holds a single item and is fixed size.

The single key/value pair is specified at creation. The map is fixed size so any action that would change the size is disallowed. However, the put or setValue methods can change the value associated with the key.

If trying to remove or clear the map, an UnsupportedOperationException is thrown. If trying to put a new mapping into the map, an IllegalArgumentException is thrown. The put method will only suceed if the key specified is the same as the singleton key.

The key and value can be obtained by:

  • normal Map methods and views
  • the MapIterator, see mapIterator()
  • the KeyValue interface (just cast - no object creation)
Author:Stephen Colebourne
Since:Commons Collections 3.1
Version:$Revision: 646777 $ $Date: 2008-04-10 14:33:15 +0200 (Thu, 10 Apr 2008) $
/** * A <code>Map</code> implementation that holds a single item and is fixed size. * <p> * The single key/value pair is specified at creation. * The map is fixed size so any action that would change the size is disallowed. * However, the <code>put</code> or <code>setValue</code> methods can <i>change</i> * the value associated with the key. * <p> * If trying to remove or clear the map, an UnsupportedOperationException is thrown. * If trying to put a new mapping into the map, an IllegalArgumentException is thrown. * The put method will only suceed if the key specified is the same as the * singleton key. * <p> * The key and value can be obtained by: * <ul> * <li>normal Map methods and views * <li>the <code>MapIterator</code>, see {@link #mapIterator()} * <li>the <code>KeyValue</code> interface (just cast - no object creation) * </ul> * * @since Commons Collections 3.1 * @version $Revision: 646777 $ $Date: 2008-04-10 14:33:15 +0200 (Thu, 10 Apr 2008) $ * * @author Stephen Colebourne */
public class SingletonMap implements OrderedMap, BoundedMap, KeyValue, Serializable, Cloneable {
Serialization version
/** Serialization version */
private static final long serialVersionUID = -8931271118676803261L;
Singleton key
/** Singleton key */
private final Object key;
Singleton value
/** Singleton value */
private Object value;
Constructor that creates a map of null to null.
/** * Constructor that creates a map of <code>null</code> to <code>null</code>. */
public SingletonMap() { super(); this.key = null; }
Constructor specifying the key and value.
Params:
  • key – the key to use
  • value – the value to use
/** * Constructor specifying the key and value. * * @param key the key to use * @param value the value to use */
public SingletonMap(Object key, Object value) { super(); this.key = key; this.value = value; }
Constructor specifying the key and value as a KeyValue.
Params:
  • keyValue – the key value pair to use
/** * Constructor specifying the key and value as a <code>KeyValue</code>. * * @param keyValue the key value pair to use */
public SingletonMap(KeyValue keyValue) { super(); this.key = keyValue.getKey(); this.value = keyValue.getValue(); }
Constructor specifying the key and value as a MapEntry.
Params:
  • mapEntry – the mapEntry to use
/** * Constructor specifying the key and value as a <code>MapEntry</code>. * * @param mapEntry the mapEntry to use */
public SingletonMap(Map.Entry mapEntry) { super(); this.key = mapEntry.getKey(); this.value = mapEntry.getValue(); }
Constructor copying elements from another map.
Params:
  • map – the map to copy, must be size 1
Throws:
/** * Constructor copying elements from another map. * * @param map the map to copy, must be size 1 * @throws NullPointerException if the map is null * @throws IllegalArgumentException if the size is not 1 */
public SingletonMap(Map map) { super(); if (map.size() != 1) { throw new IllegalArgumentException("The map size must be 1"); } Map.Entry entry = (Map.Entry) map.entrySet().iterator().next(); this.key = entry.getKey(); this.value = entry.getValue(); } // KeyValue //-----------------------------------------------------------------------
Gets the key.
Returns:the key
/** * Gets the key. * * @return the key */
public Object getKey() { return key; }
Gets the value.
Returns:the value
/** * Gets the value. * * @return the value */
public Object getValue() { return value; }
Sets the value.
Params:
  • value – the new value to set
Returns:the old value
/** * Sets the value. * * @param value the new value to set * @return the old value */
public Object setValue(Object value) { Object old = this.value; this.value = value; return old; } // BoundedMap //-----------------------------------------------------------------------
Is the map currently full, always true.
Returns:true always
/** * Is the map currently full, always true. * * @return true always */
public boolean isFull() { return true; }
Gets the maximum size of the map, always 1.
Returns:1 always
/** * Gets the maximum size of the map, always 1. * * @return 1 always */
public int maxSize() { return 1; } // Map //-----------------------------------------------------------------------
Gets the value mapped to the key specified.
Params:
  • key – the key
Returns:the mapped value, null if no match
/** * Gets the value mapped to the key specified. * * @param key the key * @return the mapped value, null if no match */
public Object get(Object key) { if (isEqualKey(key)) { return value; } return null; }
Gets the size of the map, always 1.
Returns:the size of 1
/** * Gets the size of the map, always 1. * * @return the size of 1 */
public int size() { return 1; }
Checks whether the map is currently empty, which it never is.
Returns:false always
/** * Checks whether the map is currently empty, which it never is. * * @return false always */
public boolean isEmpty() { return false; } //-----------------------------------------------------------------------
Checks whether the map contains the specified key.
Params:
  • key – the key to search for
Returns:true if the map contains the key
/** * Checks whether the map contains the specified key. * * @param key the key to search for * @return true if the map contains the key */
public boolean containsKey(Object key) { return (isEqualKey(key)); }
Checks whether the map contains the specified value.
Params:
  • value – the value to search for
Returns:true if the map contains the key
/** * Checks whether the map contains the specified value. * * @param value the value to search for * @return true if the map contains the key */
public boolean containsValue(Object value) { return (isEqualValue(value)); } //-----------------------------------------------------------------------
Puts a key-value mapping into this map where the key must match the existing key.

An IllegalArgumentException is thrown if the key does not match as the map is fixed size.

Params:
  • key – the key to set, must be the key of the map
  • value – the value to set
Throws:
Returns:the value previously mapped to this key, null if none
/** * Puts a key-value mapping into this map where the key must match the existing key. * <p> * An IllegalArgumentException is thrown if the key does not match as the map * is fixed size. * * @param key the key to set, must be the key of the map * @param value the value to set * @return the value previously mapped to this key, null if none * @throws IllegalArgumentException if the key does not match */
public Object put(Object key, Object value) { if (isEqualKey(key)) { return setValue(value); } throw new IllegalArgumentException("Cannot put new key/value pair - Map is fixed size singleton"); }
Puts the values from the specified map into this map.

The map must be of size 0 or size 1. If it is size 1, the key must match the key of this map otherwise an IllegalArgumentException is thrown.

Params:
  • map – the map to add, must be size 0 or 1, and the key must match
Throws:
/** * Puts the values from the specified map into this map. * <p> * The map must be of size 0 or size 1. * If it is size 1, the key must match the key of this map otherwise an * IllegalArgumentException is thrown. * * @param map the map to add, must be size 0 or 1, and the key must match * @throws NullPointerException if the map is null * @throws IllegalArgumentException if the key does not match */
public void putAll(Map map) { switch (map.size()) { case 0: return; case 1: Map.Entry entry = (Map.Entry) map.entrySet().iterator().next(); put(entry.getKey(), entry.getValue()); return; default: throw new IllegalArgumentException("The map size must be 0 or 1"); } }
Unsupported operation.
Params:
  • key – the mapping to remove
Throws:
Returns:the value mapped to the removed key, null if key not in map
/** * Unsupported operation. * * @param key the mapping to remove * @return the value mapped to the removed key, null if key not in map * @throws UnsupportedOperationException always */
public Object remove(Object key) { throw new UnsupportedOperationException(); }
Unsupported operation.
/** * Unsupported operation. */
public void clear() { throw new UnsupportedOperationException(); } //-----------------------------------------------------------------------
Gets the entrySet view of the map. Changes made via setValue affect this map. To simply iterate through the entries, use mapIterator().
Returns:the entrySet view
/** * Gets the entrySet view of the map. * Changes made via <code>setValue</code> affect this map. * To simply iterate through the entries, use {@link #mapIterator()}. * * @return the entrySet view */
public Set entrySet() { Map.Entry entry = new TiedMapEntry(this, getKey()); return Collections.singleton(entry); }
Gets the unmodifiable keySet view of the map. Changes made to the view affect this map. To simply iterate through the keys, use mapIterator().
Returns:the keySet view
/** * Gets the unmodifiable keySet view of the map. * Changes made to the view affect this map. * To simply iterate through the keys, use {@link #mapIterator()}. * * @return the keySet view */
public Set keySet() { return Collections.singleton(key); }
Gets the unmodifiable values view of the map. Changes made to the view affect this map. To simply iterate through the values, use mapIterator().
Returns:the values view
/** * Gets the unmodifiable values view of the map. * Changes made to the view affect this map. * To simply iterate through the values, use {@link #mapIterator()}. * * @return the values view */
public Collection values() { return new SingletonValues(this); }
Gets an iterator over the map. Changes made to the iterator using setValue affect this map. The remove method is unsupported.

A MapIterator returns the keys in the map. It also provides convenient methods to get the key and value, and set the value. It avoids the need to create an entrySet/keySet/values object. It also avoids creating the Map Entry object.

Returns:the map iterator
/** * Gets an iterator over the map. * Changes made to the iterator using <code>setValue</code> affect this map. * The <code>remove</code> method is unsupported. * <p> * A MapIterator returns the keys in the map. It also provides convenient * methods to get the key and value, and set the value. * It avoids the need to create an entrySet/keySet/values object. * It also avoids creating the Map Entry object. * * @return the map iterator */
public MapIterator mapIterator() { return new SingletonMapIterator(this); } // OrderedMap //-----------------------------------------------------------------------
Obtains an OrderedMapIterator over the map.

A ordered map iterator is an efficient way of iterating over maps in both directions.

Returns:an ordered map iterator
/** * Obtains an <code>OrderedMapIterator</code> over the map. * <p> * A ordered map iterator is an efficient way of iterating over maps * in both directions. * * @return an ordered map iterator */
public OrderedMapIterator orderedMapIterator() { return new SingletonMapIterator(this); }
Gets the first (and only) key in the map.
Returns:the key
/** * Gets the first (and only) key in the map. * * @return the key */
public Object firstKey() { return getKey(); }
Gets the last (and only) key in the map.
Returns:the key
/** * Gets the last (and only) key in the map. * * @return the key */
public Object lastKey() { return getKey(); }
Gets the next key after the key specified, always null.
Params:
  • key – the next key
Returns:null always
/** * Gets the next key after the key specified, always null. * * @param key the next key * @return null always */
public Object nextKey(Object key) { return null; }
Gets the previous key before the key specified, always null.
Params:
  • key – the next key
Returns:null always
/** * Gets the previous key before the key specified, always null. * * @param key the next key * @return null always */
public Object previousKey(Object key) { return null; } //-----------------------------------------------------------------------
Compares the specified key to the stored key.
Params:
  • key – the key to compare
Returns:true if equal
/** * Compares the specified key to the stored key. * * @param key the key to compare * @return true if equal */
protected boolean isEqualKey(Object key) { return (key == null ? getKey() == null : key.equals(getKey())); }
Compares the specified value to the stored value.
Params:
  • value – the value to compare
Returns:true if equal
/** * Compares the specified value to the stored value. * * @param value the value to compare * @return true if equal */
protected boolean isEqualValue(Object value) { return (value == null ? getValue() == null : value.equals(getValue())); } //-----------------------------------------------------------------------
SingletonMapIterator.
/** * SingletonMapIterator. */
static class SingletonMapIterator implements OrderedMapIterator, ResettableIterator { private final SingletonMap parent; private boolean hasNext = true; private boolean canGetSet = false; SingletonMapIterator(SingletonMap parent) { super(); this.parent = parent; } public boolean hasNext() { return hasNext; } public Object next() { if (hasNext == false) { throw new NoSuchElementException(AbstractHashedMap.NO_NEXT_ENTRY); } hasNext = false; canGetSet = true; return parent.getKey(); } public boolean hasPrevious() { return (hasNext == false); } public Object previous() { if (hasNext == true) { throw new NoSuchElementException(AbstractHashedMap.NO_PREVIOUS_ENTRY); } hasNext = true; return parent.getKey(); } public void remove() { throw new UnsupportedOperationException(); } public Object getKey() { if (canGetSet == false) { throw new IllegalStateException(AbstractHashedMap.GETKEY_INVALID); } return parent.getKey(); } public Object getValue() { if (canGetSet == false) { throw new IllegalStateException(AbstractHashedMap.GETVALUE_INVALID); } return parent.getValue(); } public Object setValue(Object value) { if (canGetSet == false) { throw new IllegalStateException(AbstractHashedMap.SETVALUE_INVALID); } return parent.setValue(value); } public void reset() { hasNext = true; } public String toString() { if (hasNext) { return "Iterator[]"; } else { return "Iterator[" + getKey() + "=" + getValue() + "]"; } } }
Values implementation for the SingletonMap. This class is needed as values is a view that must update as the map updates.
/** * Values implementation for the SingletonMap. * This class is needed as values is a view that must update as the map updates. */
static class SingletonValues extends AbstractSet implements Serializable { private static final long serialVersionUID = -3689524741863047872L; private final SingletonMap parent; SingletonValues(SingletonMap parent) { super(); this.parent = parent; } public int size() { return 1; } public boolean isEmpty() { return false; } public boolean contains(Object object) { return parent.containsValue(object); } public void clear() { throw new UnsupportedOperationException(); } public Iterator iterator() { return new SingletonIterator(parent.getValue(), false); } } //-----------------------------------------------------------------------
Clones the map without cloning the key or value.
Returns:a shallow clone
/** * Clones the map without cloning the key or value. * * @return a shallow clone */
public Object clone() { try { SingletonMap cloned = (SingletonMap) super.clone(); return cloned; } catch (CloneNotSupportedException ex) { throw new InternalError(); } }
Compares this map with another.
Params:
  • obj – the object to compare to
Returns:true if equal
/** * Compares this map with another. * * @param obj the object to compare to * @return true if equal */
public boolean equals(Object obj) { if (obj == this) { return true; } if (obj instanceof Map == false) { return false; } Map other = (Map) obj; if (other.size() != 1) { return false; } Map.Entry entry = (Map.Entry) other.entrySet().iterator().next(); return isEqualKey(entry.getKey()) && isEqualValue(entry.getValue()); }
Gets the standard Map hashCode.
Returns:the hash code defined in the Map interface
/** * Gets the standard Map hashCode. * * @return the hash code defined in the Map interface */
public int hashCode() { return (getKey() == null ? 0 : getKey().hashCode()) ^ (getValue() == null ? 0 : getValue().hashCode()); }
Gets the map as a String.
Returns:a string version of the map
/** * Gets the map as a String. * * @return a string version of the map */
public String toString() { return new StringBuffer(128) .append('{') .append((getKey() == this ? "(this Map)" : getKey())) .append('=') .append((getValue() == this ? "(this Map)" : getValue())) .append('}') .toString(); } }