package java.util;

import ej.annotation.Nullable;

/**
 * An object that maps keys to values. A map cannot contain duplicate keys; each key can map to at
 * most one value.
 *
 * <p>
 * This interface takes the place of the <code>Dictionary</code> class, which was a totally abstract
 * class rather than an interface.
 *
 * <p>
 * The <code>Map</code> interface provides three <i>collection views</i>, which allow a map's contents
 * to be viewed as a set of keys, collection of values, or set of key-value mappings. The
 * <i>order</i> of a map is defined as the order in which the iterators on the map's collection
 * views return their elements. Some map implementations, like the <code>TreeMap</code> class, make
 * specific guarantees as to their order; others, like the <code>HashMap</code> class, do not.
 *
 * <p>
 * Note: great care must be exercised if mutable objects are used as map keys. The behavior of a map
 * is not specified if the value of an object is changed in a manner that affects <code>equals</code>
 * comparisons while the object is a key in the map. A special case of this prohibition is that it
 * is not permissible for a map to contain itself as a key. While it is permissible for a map to
 * contain itself as a value, extreme caution is advised: the <code>equals</code> and <code>hashCode</code>
 * methods are no longer well defined on such a map.
 *
 * <p>
 * All general-purpose map implementation classes should provide two "standard" constructors: a void
 * (no arguments) constructor which creates an empty map, and a constructor with a single argument
 * of type <code>Map</code>, which creates a new map with the same key-value mappings as its argument.
 * In effect, the latter constructor allows the user to copy any map, producing an equivalent map of
 * the desired class. There is no way to enforce this recommendation (as interfaces cannot contain
 * constructors).
 *
 * <p>
 * The "destructive" methods contained in this interface, that is, the methods that modify the map
 * on which they operate, are specified to throw <code>UnsupportedOperationException</code> if this map
 * does not support the operation. If this is the case, these methods may, but are not required to,
 * throw an <code>UnsupportedOperationException</code> if the invocation would have no effect on the
 * map. For example, invoking the {@link #putAll(Map)} method on an unmodifiable map may, but is not
 * required to, throw the exception if the map whose mappings are to be "superimposed" is empty.
 *
 * <p>
 * Some map implementations have restrictions on the keys and values they may contain. For example,
 * some implementations prohibit null keys and values, and some have restrictions on the types of
 * their keys. Attempting to insert an ineligible key or value throws an unchecked exception,
 * typically <code>NullPointerException</code> or <code>ClassCastException</code>. Attempting to query the
 * presence of an ineligible key or value may throw an exception, or it may simply return false;
 * some implementations will exhibit the former behavior and some will exhibit the latter. More
 * generally, attempting an operation on an ineligible key or value whose completion would not
 * result in the insertion of an ineligible element into the map may throw an exception or it may
 * succeed, at the option of the implementation. Such exceptions are marked as "optional" in the
 * specification for this interface.
 *
 * <p>
 * This interface is a member of the Java Collections Framework
 *
 * <p>
 * Many methods in Collections Framework interfaces are defined in terms of the
 * {@link Object#equals(Object) equals} method. For example, the specification for the
 * {@link #containsKey(Object) containsKey(Object key)} method says: "returns <code>true</code> if and
 * only if this map contains a mapping for a key <code>k</code> such that
 * <code>(key==null ? k==null : key.equals(k))</code>." This specification should <i>not</i> be
 * construed to imply that invoking <code>Map.containsKey</code> with a non-null argument <code>key</code>
 * will cause <code>key.equals(k)</code> to be invoked for any key <code>k</code>. Implementations are free
 * to implement optimizations whereby the <code>equals</code> invocation is avoided, for example, by
 * first comparing the hash codes of the two keys. (The {@link Object#hashCode()} specification
 * guarantees that two objects with unequal hash codes cannot be equal.) More generally,
 * implementations of the various Collections Framework interfaces are free to take advantage of the
 * specified behavior of underlying {@link Object} methods wherever the implementor deems it
 * appropriate.
 *
 * @param <K>
 *        the type of keys maintained by this map
 * @param <V>
 *        the type of mapped values
 *
 * @see HashMap
 * @see Hashtable
 * @see Collection
 * @see Set
 */
public interface Map<K, V> {

    /**
     * A map entry (key-value pair). The <code>Map.entrySet</code> method returns a collection-view of the
     * map, whose elements are of this class. The <i>only</i> way to obtain a reference to a map entry
     * is from the iterator of this collection-view. These <code>Map.Entry</code> objects are valid
     * <i>only</i> for the duration of the iteration; more formally, the behavior of a map entry is
     * undefined if the backing map has been modified after the entry was returned by the iterator,
     * except through the <code>setValue</code> operation on the map entry.
     *
     * @param <K>
     *        the type of the keys
     * @param <V>
     *        the type of the values
     *
     * @see Map#entrySet()
     */
    interface Entry<K, V> {

        /**
         * Compares the specified object with this entry for equality. Returns <code>true</code> if the given
         * object is also a map entry and the two entries represent the same mapping. More formally, two
         * entries <code>e1</code> and <code>e2</code> represent the same mapping if
         *
         * <pre>
         * (e1.getKey() == null ? e2.getKey() == null : e1.getKey().equals(e2.getKey()))
         * 		&amp;&amp; (e1.getValue() == null ? e2.getValue() == null : e1.getValue().equals(e2.getValue()))
         * </pre>
         *
         * This ensures that the <code>equals</code> method works properly across different implementations of
         * the <code>Map.Entry</code> interface.
         *
         * @param o
         *        object to be compared for equality with this map entry
         * @return <code>true</code> if the specified object is equal to this map entry
         */
        @Override
        boolean equals(@Nullable Object o);

        /**
         * Returns the key corresponding to this entry.
         *
         * @return the key corresponding to this entry
         * @throws IllegalStateException
         *         implementations may, but are not required to, throw this exception if the entry has been
         *         removed from the backing map.
         */
        K getKey();

        /**
         * Returns the value corresponding to this entry. If the mapping has been removed from the backing
         * map (by the iterator's <code>remove</code> operation), the results of this call are undefined.
         *
         * @return the value corresponding to this entry
         * @throws IllegalStateException
         *         implementations may, but are not required to, throw this exception if the entry has been
         *         removed from the backing map.
         */
        V getValue();

        /**
         * Returns the hash code value for this map entry. The hash code of a map entry <code>e</code> is
         * defined to be:
         *
         * <pre>
         * (e.getKey() == null ? 0 : e.getKey().hashCode()) &circ; (e.getValue() == null ? 0 : e.getValue().hashCode())
         * </pre>
         *
         * This ensures that <code>e1.equals(e2)</code> implies that <code>e1.hashCode()==e2.hashCode()</code> for
         * any two Entries <code>e1</code> and <code>e2</code>, as required by the general contract of
         * <code>Object.hashCode</code>.
         *
         * @return the hash code value for this map entry
         * @see Object#hashCode()
         * @see Object#equals(Object)
         * @see #equals(Object)
         */
        @Override
        int hashCode();

        /**
         * Replaces the value corresponding to this entry with the specified value (optional operation).
         * (Writes through to the map.) The behavior of this call is undefined if the mapping has already
         * been removed from the map (by the iterator's <code>remove</code> operation).
         *
         * @param value
         *        new value to be stored in this entry
         * @return old value corresponding to the entry
         * @throws UnsupportedOperationException
         *         if the <code>put</code> operation is not supported by the backing map
         * @throws ClassCastException
         *         if the class of the specified value prevents it from being stored in the backing map
         * @throws NullPointerException
         *         if the backing map does not permit null values, and the specified value is null
         * @throws IllegalArgumentException
         *         if some property of this value prevents it from being stored in the backing map
         * @throws IllegalStateException
         *         implementations may, but are not required to, throw this exception if the entry has been
         *         removed from the backing map.
         */
        @Nullable
        V setValue(V value);
    }

    /**
     * Removes all of the mappings from this map (optional operation). The map will be empty after this
     * call returns.
     *
     * @throws UnsupportedOperationException
     *         if the <code>clear</code> operation is not supported by this map
     */
    void clear();

    /**
     * Returns <code>true</code> if this map contains a mapping for the specified key. More formally,
     * returns <code>true</code> if and only if this map contains a mapping for a key <code>k</code> such that
     * <code>(key==null ? k==null : key.equals(k))</code>. (There can be at most one such mapping.)
     *
     * @param key
     *        key whose presence in this map is to be tested
     * @return <code>true</code> if this map contains a mapping for the specified key
     * @throws ClassCastException
     *         if the key is of an inappropriate type for this map
     *         (<a href="Collection.html#optional-restrictions">optional</a>)
     * @throws NullPointerException
     *         if the specified key is null and this map does not permit null keys
     *         (<a href="Collection.html#optional-restrictions">optional</a>)
     */
    boolean containsKey(Object key);

    /**
     * Returns <code>true</code> if this map maps one or more keys to the specified value. More formally,
     * returns <code>true</code> if and only if this map contains at least one mapping to a value <code>v</code>
     * such that <code>(value==null ? v==null : value.equals(v))</code>. This operation will probably
     * require time linear in the map size for most implementations of the <code>Map</code> interface.
     *
     * @param value
     *        value whose presence in this map is to be tested
     * @return <code>true</code> if this map maps one or more keys to the specified value
     * @throws ClassCastException
     *         if the value is of an inappropriate type for this map
     *         (<a href="Collection.html#optional-restrictions">optional</a>)
     * @throws NullPointerException
     *         if the specified value is null and this map does not permit null values
     *         (<a href="Collection.html#optional-restrictions">optional</a>)
     */
    boolean containsValue(Object value);

    /**
     * Returns a {@link Set} view of the mappings contained in this map. The set is backed by the map,
     * so changes to the map are reflected in the set, and vice-versa. If the map is modified while an
     * iteration over the set is in progress (except through the iterator's own <code>remove</code>
     * operation, or through the <code>setValue</code> operation on a map entry returned by the iterator)
     * the results of the iteration are undefined. The set supports element removal, which removes the
     * corresponding mapping from the map, via the <code>Iterator.remove</code>, <code>Set.remove</code>,
     * <code>removeAll</code>, <code>retainAll</code> and <code>clear</code> operations. It does not support the
     * <code>add</code> or <code>addAll</code> operations.
     *
     * @return a set view of the mappings contained in this map
     */
    Set<Map.Entry<K, V>> entrySet();

    /**
     * Compares the specified object with this map for equality. Returns <code>true</code> if the given
     * object is also a map and the two maps represent the same mappings. More formally, two maps
     * <code>m1</code> and <code>m2</code> represent the same mappings if
     * <code>m1.entrySet().equals(m2.entrySet())</code>. This ensures that the <code>equals</code> method works
     * properly across different implementations of the <code>Map</code> interface.
     *
     * @param o
     *        object to be compared for equality with this map
     * @return <code>true</code> if the specified object is equal to this map
     */
    @Override
    boolean equals(@Nullable Object o);

    /**
     * Returns the value to which the specified key is mapped, or {@code null} if this map contains no
     * mapping for the key.
     *
     * <p>
     * More formally, if this map contains a mapping from a key {@code k} to a value {@code v} such that
     * {@code (key==null ? k==null :
     * key.equals(k))}, then this method returns {@code v}; otherwise it returns {@code null}. (There
     * can be at most one such mapping.)
     *
     * <p>
     * If this map permits null values, then a return value of {@code null} does not <i>necessarily</i>
     * indicate that the map contains no mapping for the key; it's also possible that the map explicitly
     * maps the key to {@code null}. The {@link #containsKey containsKey} operation may be used to
     * distinguish these two cases.
     *
     * @param key
     *        the key whose associated value is to be returned
     * @return the value to which the specified key is mapped, or {@code null} if this map contains no
     *         mapping for the key
     * @throws ClassCastException
     *         if the key is of an inappropriate type for this map
     *         (<a href="Collection.html#optional-restrictions">optional</a>)
     * @throws NullPointerException
     *         if the specified key is null and this map does not permit null keys
     *         (<a href="Collection.html#optional-restrictions">optional</a>)
     */
    @Nullable
    V get(Object key);

    /**
     * Returns the hash code value for this map. The hash code of a map is defined to be the sum of the
     * hash codes of each entry in the map's <code>entrySet()</code> view. This ensures that
     * <code>m1.equals(m2)</code> implies that <code>m1.hashCode()==m2.hashCode()</code> for any two maps
     * <code>m1</code> and <code>m2</code>, as required by the general contract of {@link Object#hashCode}.
     *
     * @return the hash code value for this map
     * @see Map.Entry#hashCode()
     * @see Object#equals(Object)
     * @see #equals(Object)
     */
    @Override
    int hashCode();

    /**
     * Returns <code>true</code> if this map contains no key-value mappings.
     *
     * @return <code>true</code> if this map contains no key-value mappings
     */
    boolean isEmpty();

    /**
     * Returns a {@link Set} view of the keys contained in this map. The set is backed by the map, so
     * changes to the map are reflected in the set, and vice-versa. If the map is modified while an
     * iteration over the set is in progress (except through the iterator's own <code>remove</code>
     * operation), the results of the iteration are undefined. The set supports element removal, which
     * removes the corresponding mapping from the map, via the <code>Iterator.remove</code>,
     * <code>Set.remove</code>, <code>removeAll</code>, <code>retainAll</code>, and <code>clear</code> operations. It
     * does not support the <code>add</code> or <code>addAll</code> operations.
     *
     * @return a set view of the keys contained in this map
     */
    Set<K> keySet();

    /**
     * Associates the specified value with the specified key in this map (optional operation). If the
     * map previously contained a mapping for the key, the old value is replaced by the specified value.
     * (A map <code>m</code> is said to contain a mapping for a key <code>k</code> if and only if
     * {@link #containsKey(Object) m.containsKey(k)} would return <code>true</code>.)
     *
     * @param key
     *        key with which the specified value is to be associated
     * @param value
     *        value to be associated with the specified key
     * @return the previous value associated with <code>key</code>, or <code>null</code> if there was no mapping
     *         for <code>key</code>. (A <code>null</code> return can also indicate that the map previously
     *         associated <code>null</code> with <code>key</code>, if the implementation supports <code>null</code>
     *         values.)
     * @throws UnsupportedOperationException
     *         if the <code>put</code> operation is not supported by this map
     * @throws ClassCastException
     *         if the class of the specified key or value prevents it from being stored in this map
     * @throws NullPointerException
     *         if the specified key or value is null and this map does not permit null keys or values
     * @throws IllegalArgumentException
     *         if some property of the specified key or value prevents it from being stored in this map
     */
    @Nullable
    V put(K key, V value);

    /**
     * Copies all of the mappings from the specified map to this map (optional operation). The effect of
     * this call is equivalent to that of calling {@link #put(Object,Object) put(k, v)} on this map once
     * for each mapping from key <code>k</code> to value <code>v</code> in the specified map. The behavior of
     * this operation is undefined if the specified map is modified while the operation is in progress.
     *
     * @param m
     *        mappings to be stored in this map
     * @throws UnsupportedOperationException
     *         if the <code>putAll</code> operation is not supported by this map
     * @throws ClassCastException
     *         if the class of a key or value in the specified map prevents it from being stored in this
     *         map
     * @throws NullPointerException
     *         if the specified map is null, or if this map does not permit null keys or values, and the
     *         specified map contains null keys or values
     * @throws IllegalArgumentException
     *         if some property of a key or value in the specified map prevents it from being stored in
     *         this map
     */
    void putAll(Map<? extends K, ? extends V> m);

    /**
     * Removes the mapping for a key from this map if it is present (optional operation). More formally,
     * if this map contains a mapping from key <code>k</code> to value <code>v</code> such that
     * <code>(key==null ?  k==null : key.equals(k))</code>, that mapping is removed. (The map can
     * contain at most one such mapping.)
     *
     * <p>
     * Returns the value to which this map previously associated the key, or <code>null</code> if the map
     * contained no mapping for the key.
     *
     * <p>
     * If this map permits null values, then a return value of <code>null</code> does not <i>necessarily</i>
     * indicate that the map contained no mapping for the key; it's also possible that the map
     * explicitly mapped the key to <code>null</code>.
     *
     * <p>
     * The map will not contain a mapping for the specified key once the call returns.
     *
     * @param key
     *        key whose mapping is to be removed from the map
     * @return the previous value associated with <code>key</code>, or <code>null</code> if there was no mapping
     *         for <code>key</code>.
     * @throws UnsupportedOperationException
     *         if the <code>remove</code> operation is not supported by this map
     * @throws ClassCastException
     *         if the key is of an inappropriate type for this map
     *         (<a href="Collection.html#optional-restrictions">optional</a>)
     * @throws NullPointerException
     *         if the specified key is null and this map does not permit null keys
     *         (<a href="Collection.html#optional-restrictions">optional</a>)
     */
    @Nullable
    V remove(Object key);

    /**
     * Returns the number of key-value mappings in this map. If the map contains more than
     * <code>Integer.MAX_VALUE</code> elements, returns <code>Integer.MAX_VALUE</code>.
     *
     * @return the number of key-value mappings in this map
     */
    int size();

    /**
     * Returns a {@link Collection} view of the values contained in this map. The collection is backed
     * by the map, so changes to the map are reflected in the collection, and vice-versa. If the map is
     * modified while an iteration over the collection is in progress (except through the iterator's own
     * <code>remove</code> operation), the results of the iteration are undefined. The collection supports
     * element removal, which removes the corresponding mapping from the map, via the
     * <code>Iterator.remove</code>, <code>Collection.remove</code>, <code>removeAll</code>, <code>retainAll</code> and
     * <code>clear</code> operations. It does not support the <code>add</code> or <code>addAll</code> operations.
     *
     * @return a collection view of the values contained in this map
     */
    Collection<V> values();
}
