/*******************************************************************************
* Copyright (c)                                                                *
*           2005   Tristan Allwood,                                            *
*           2005   Daniel Burke,                                               *
*           2005   Marc Hull,                                                  *
*           2005   Ekaterina Itskova,                                          *
*           2005   Steve Zymler                                                *
*                                                                              *
* Permission is hereby granted, free of charge, to any person obtaining a      *
* copy of this software and associated documentation files (the                *
* "Software"), to deal in the Software without restriction, including          *
* without limitation the rights to use, copy, modify, merge, publish,          *
* distribute, sublicense, and/or sell copies of the Software, and to           *
* permit persons to whom the Software is furnished to do so, subject to        *
* the following conditions:                                                    *
*                                                                              *
* The above copyright notice and this permission notice shall be included      *
* in all copies or substantial portions of the Software.                       *
*                                                                              *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS      *
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF                   *
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.       *
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY         *
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,         *
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE            *
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                       *
*******************************************************************************/
/*
 * Created on 19-Oct-2004
 *
 */
package uk.ac.ic.doc.kevlar.publicUtil.collections;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import uk.ac.ic.doc.kevlar.publicUtil.strings.StringUtil;

/**
 * @author Daniel Burke 
 * A hashed implementation of IImmutableMap <K, V>. Note
 * that only the container is immutable. To get a true immutable map,
 * only immutable keys and values may be used.
 */
public final class ImmutableHashMap<K, V> implements IImmutableMap<K, V>,
        Serializable {
    /**
     * Comment for <code>serialVersionUID</code>
     */
    private static final long serialVersionUID = 6354570665788416720L;

    // The data the map will be based off.
    private Map<K, V> backend;

    // caches for the three derived sets of the backend
    private IImmutableSet<ImmutableMapEntry<K, V>> entries = null;

    private IImmutableSet<K> keys = null;

    private IImmutableSet<V> valueSet = null;

    /**
     * Provide a map to use as the immutable collection The data must have only
     * immutable keys and values however this is not enforced.
     * 
     * @param data the map of immutable keys and values.s
     */
    public ImmutableHashMap(Map<K, V> data) {
        backend = new HashMap<K, V>(data);
    }

    public boolean containsKey(K key) {
        return backend.containsKey(key);
    }

    public boolean containsValue(V value) {
        return backend.containsKey(value);
    }

    public V get(K key) {
        return backend.get(key);
    }

    public boolean isEmpty() {
        return backend.isEmpty();
    }

    public IImmutableSet<K> keySet() {
        
        // cache the key set if necessary.
        if (keys == null) {
            keys = new ImmutableHashSet<K>(backend.keySet());
        }
        return keys;
    }

    public int size() {
        return backend.size();
    }

    public IImmutableSet<V> values() {
        
        // cache the value set if necessary.
        if (valueSet == null) {
            valueSet = new ImmutableHashSet<V>(backend.values());
        }
        return valueSet;
    }

    public IImmutableSet<ImmutableMapEntry<K, V>> entrySet() {
        
        // cache the entry set if necessary.
        if (entries == null) {
            List<ImmutableMapEntry<K, V>> tempEntries = new ArrayList<ImmutableMapEntry<K, V>>();

            for (Map.Entry<K, V> entry : backend.entrySet()) {
                tempEntries.add(new ImmutableMapEntry<K, V>(entry.getKey(),
                        entry.getValue()));
            }

            entries = new ImmutableHashSet<ImmutableMapEntry<K, V>>(tempEntries);
        }

        return entries;
    }

    /**
     * @return A string that looks like this:
     * [
     * element,
     * element,
     * ...
     * ]
     * Note: This may change, do not parse this data.
     */
    public String toString() {
        StringBuilder str = new StringBuilder(200);
        String nl = StringUtil.NEWLINE;
        str.append("[").append(nl);

        for (Map.Entry<K, V> entry : backend.entrySet()) {

            str.append(entry.getKey()).append(" := ").append(entry.getValue())
                    .append(",").append(nl);
        }

        if (backend.size() > 0) {
            str.delete(str.length() - 1 - StringUtil.NEWLINE.length(), str
                    .length());
            str.append(nl);
        }

        str.append("]").append(nl);

        return str.toString();
    }
}
