package javolution.util;

import java.util.Collection;
import java.util.Map;
import java.util.Set;
import javolution.context.LocalContext;

/**
 * <p> This class represents a map which can be temporarily modified without 
 *     impacting other threads ({@link LocalContext scoped} changes).</p>
 *     
 * <p> Operation on instances of this class are thread-safe. 
 *     For example:
 *     public class XMLFormat {
 *         static LocalMap<Class, XMLFormat> CLASS_TO_FORMAT = new LocalMap<Class, XMLFormat>();
 *         public static void setFormat(Class forClass, XMLFormat that) {
 *             CLASS_TO_FORMAT.put(forClass, that); // No synchronization required.
 *         }
 *         public static XMLFormat getInstance(Class forClass) {
 *             return CLASS_TO_FORMAT.get(forClass); // No synchronization required.
 *         }
 *     }
 *     public void main(String[] args) {
 *         // Sets default (global settings).
 *         XMLFormat.setFormat(Foo.class, xFormat);
 *         XMLFormat.setFormat(Bar.class, yFormat);
 *     }
 *     ... // Another thread.
 *     LocalContext.enter();
 *     try { // Use of local context to avoid impacting other threads.
 *         XMLFormat.setFormat(Foo.class, zFormat);
 *         XMLFormat.getInstance(Foo.class); // Returns zFormat
 *         XMLFormat.getInstance(Bar.class); // Returns yFormat (inherited)
 *     } finally {
 *         LocalContext.exit();
 *     }
 *     getInstance(Foo.class); // Returns xFormat
 *     [/code]</p>
 * 
 * <p> <b>Note:</b> Because key-value mappings are inherited, the semantic of  
 *     {@link #remove} and {@link #clear} is slightly modified (association with
 *     <code>null</code> values instead of removing the entries).</p>
 *     
 * @author <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a>
 * @version 3.7, January 27, 2005
 */
public final class LocalMap <K,V> implements Map <K,V> {

    /**
     * Holds the fast map reference (shared map).
     */
    private final LocalContext.Reference _mapRef 
        = new LocalContext.Reference(new FastMap().shared());

    /**
     * Default constructor.
     */
    public LocalMap() {
    }

    /**
     * Sets the key comparator for this local map.
     * 
     * @param keyComparator the key comparator.
     * @return <code>this</code>
     */
    public LocalMap  <K,V> setKeyComparator(FastComparator <? super K>  keyComparator) {
        localMap().setKeyComparator(keyComparator);
        return this;
    }

    /**
     * Sets the value comparator for this local map.
     * 
     * @param valueComparator the value comparator.
     * @return <code>this</code>
     */
    public LocalMap <K,V> setValueComparator(FastComparator <? super V>  valueComparator) {
        localMap().setValueComparator(valueComparator);
        return this;
    }

    /**
     * Sets the default value for the specified key (typically done at 
     * initialization). 
     * 
     * @param key the key with which the specified value is to be associated.
     * @param defaultValue the default value to be associated with the 
     *        specified key.
     * @return the previous default value associated with specified key, or
     *         <code>null</code> if there was no mapping for key. A
     *         <code>null</code> return can also indicate that the map
     *         previously associated <code>null</code> with the specified key.
     * @throws NullPointerException if the key is <code>null</code>.
     */
    public  V putDefault( K key,  V defaultValue) {
        return ( V ) ((FastMap) _mapRef.getDefault()).put(key,
                defaultValue);
    }
    
    /**
     * Returns the default value for the specified key.
     *
     * @param key the key with which the default value is  associated.
     * @return the default value associated with specified key, or
     *         <code>null</code> if there was no mapping for the key.
     * @throws NullPointerException if the key is <code>null</code>.
     */
    public  V getDefault( K key) {
        return ( V ) ((FastMap) _mapRef.getDefault()).get(key);
    }

    /**
     * Returns the number of key-value mappings in this map.
     * 
     * @return this map's size.
     */
    public int size() {
        return ((FastMap) _mapRef.get()).size();
    }

    /**
     * Indicates if this map contains no key-value mappings.
     * 
     * @return <code>true</code> if this map contains no key-value mappings;
     *         <code>false</code> otherwise.
     */
    public boolean isEmpty() {
        return ((FastMap) _mapRef.get()).isEmpty();
    }

    /**
     * Indicates if this map contains a mapping for the specified key.
     * 
     * @param key the key whose presence in this map is to be tested.
     * @return <code>true</code> if this map contains a mapping for the
     *         specified key; <code>false</code> otherwise.
     * @throws NullPointerException if the key is <code>null</code>.
     */
    public boolean containsKey(Object key) {
        return ((FastMap) _mapRef.get()).containsKey(key);
    }

    /**
     * Indicates if this map associates one or more keys to the 
     * specified value.
     * 
     * @param value the 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 NullPointerException if the key is <code>null</code>.
     */
    public boolean containsValue(Object value) {
        return ((FastMap) _mapRef.get()).containsValue(value);
    }

    /**
     * Returns the value to which this map associates the specified key.
     * 
     * @param key the key whose associated value is to be returned.
     * @return the value to which this map maps the specified key, or
     *         <code>null</code> if there is no mapping for the key.
     * @throws NullPointerException if key is <code>null</code>.
     */
    public  V get(Object key) {
        return ( V ) ((FastMap) _mapRef.get()).get(key);
    }

    /**
     * Associates the specified value with the specified key in this map.
     * 
     * @param key the key with which the specified value is to be associated.
     * @param value the value to be associated with the specified key.
     * @return the previous value associated with specified key, or
     *         <code>null</code> if there was no mapping for key. A
     *         <code>null</code> return can also indicate that the map
     *         previously associated <code>null</code> with the specified key.
     * @throws NullPointerException if the key is <code>null</code>.
     */
    public  V put( K key,  V value) {
        return ( V ) localMap().put(key, value);
    }

    /**
     * Copies all of the mappings from the specified map to this map.
     * 
     * @param map the mappings to be stored in this map.
     * @throws NullPointerException the specified map is <code>null</code>,
     *         or the specified map contains <code>null</code> keys.
     */
    public void putAll(Map <? extends K, ? extends V> map) {
        localMap().putAll(map);
    }

    /**
     * Removes the mapping for this key from this map if present
     * (sets the local value to <code>null</code>).
     * 
     * @param key the key whose value is set to <code>null</code>
     * @return <code>put(key, null)</code>
     * @throws NullPointerException if the key is <code>null</code>.
     */
    public  V remove(Object key) {
        return put(( K )key, null);
    }

    /**
     * Removes all mappings from this map (sets the local values to
     * <code>null</code>).
     */
    public void clear() {
        FastMap localMap = localMap();
        for (FastMap.Entry e = localMap.head(), end = localMap.tail(); (e = (FastMap.Entry) e.getNext()) != end;) {
            e.setValue(null);
        }
    }

    /**
     * Returns a {@link FastCollection} view of the keys contained in this map. 
     * 
     * @return a set view of the keys contained in this map
     *         (instance of {@link FastCollection}).
     */
    public Set <K> keySet() {
        return localMap().keySet();
    }

    /**
     * Returns a {@link FastCollection} view of the values contained in this
     * map. 
     * 
     * @return a collection view of the values contained in this map 
     *         (instance of {@link FastCollection}).
     */
    public Collection <V> values() {
        return localMap().values();
    }

    /**
     * Returns a {@link FastCollection} view of the mappings contained in this
     * map. 
     * 
     * @return a collection view of the mappings contained in this map
     *         (instance of {@link FastCollection}).
     */
    public Set <Map.Entry<K,V>> entrySet() {
        return localMap().entrySet();
    }

    /**
     * Returns the local map or creates one on the stack and populates 
     * it from inherited settings.
     * 
     * @return a shared fast map belonging to the current local context.
     */
    private FastMap <K,V> localMap() {
        FastMap localMap = (FastMap) _mapRef.getLocal();
        return (localMap != null) ? localMap : newLocalMap();
    }

    private FastMap newLocalMap() {
        FastMap parentMap = (FastMap) _mapRef.get();
        FastMap localMap = FastMap.newInstance(); 
        localMap.shared();
        localMap.setKeyComparator(parentMap.getKeyComparator());
        localMap.setValueComparator(parentMap.getValueComparator());
        localMap.putAll(parentMap);
        _mapRef.set(localMap);
        return localMap;
    }
}