//--------------------------------------------------------------------------
//	Copyright (c) 1998-2004, Drew Davidson and Luke Blanshard
//  All rights reserved.
//
//	Redistribution and use in source and binary forms, with or without
//  modification, are permitted provided that the following conditions are
//  met:
//
//	Redistributions of source code must retain the above copyright notice,
//  this list of conditions and the following disclaimer.
//	Redistributions in binary form must reproduce the above copyright
//  notice, this list of conditions and the following disclaimer in the
//  documentation and/or other materials provided with the distribution.
//	Neither the name of the Drew Davidson nor the names of its contributors
//  may be used to endorse or promote products derived from this software
//  without specific prior written permission.
//
//	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
//  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
//  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
//  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
//  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
//  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
//  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
//  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
//  AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
//  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
//  THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
//  DAMAGE.
//--------------------------------------------------------------------------
package ognl;

import java.util.*;

/**
 * A Map that uses ints as the keys.
 * <p>Use just like any java.util.Map, except that the keys must be ints.
 * This is much faster than creating a new Integer for each access.</p>
 * <p>For non-Map access (faster) use the put(int, Object) method.</p>
 * <p>This class implements Map for convenience, but this is not the most
 * efficient usage.</p>
 * @see java.util.HashMap
 * @see java.util.Map
*/
public class IntHashMap extends Object implements Map
{
    private Entry       table[];
    private int         count;
    private int         threshold;
    private float       loadFactor;

    /*===================================================================
        Private static classes
      ===================================================================*/
    private static class IntHashMapIterator implements Iterator
    {
        boolean         keys;
        int             index;
        Entry           table[];
        Entry           entry;

        IntHashMapIterator(Entry table[], boolean keys)
    	{
    	    super();
        	this.table = table;
        	this.keys = keys;
        	this.index = table.length;
    	}

        /*===================================================================
            Iterator interface
          ===================================================================*/
        public boolean hasNext()
        {
            if (entry != null) {
                return true;
            }
            while (index-- > 0) {
                if ((entry = table[index]) != null) {
                    return true;
                }
            }
            return false;
        }

        public Object next()
        {
            if (entry == null) {
                while ((index-- > 0) && ((entry = table[index]) == null)) {
                    /* do nothing */
                }
            }
            if (entry != null) {
                Entry       e = entry;

                entry = e.next;
                return keys ? new Integer(e.key) : e.value;
            }
            throw new NoSuchElementException("IntHashMapIterator");
        }

        public void remove()
        {
            throw new UnsupportedOperationException("remove");
        }
    }

    /*===================================================================
        Public static classes
      ===================================================================*/
    public static class Entry extends Object
    {
        int         hash;
        int         key;
        Object      value;
        Entry       next;

        public Entry()
        {
            super();
        }
    }

    /*===================================================================
        Constructors
      ===================================================================*/
    public IntHashMap(int initialCapacity, float loadFactor)
    {
        super();
        if (initialCapacity <= 0 || loadFactor <= 0.0) {
            throw new IllegalArgumentException();
        }
        this.loadFactor = loadFactor;
        table = new Entry[initialCapacity];
        threshold = (int)(initialCapacity * loadFactor);
    }

    public IntHashMap(int initialCapacity)
    {
        this(initialCapacity, 0.75f);
    }

    public IntHashMap()
    {
        this(101, 0.75f);
    }

    /*===================================================================
        Protected methods
      ===================================================================*/
    protected void rehash()
    {
        int         oldCapacity = table.length;
        Entry       oldTable[] = table;
        int         newCapacity = oldCapacity * 2 + 1;
        Entry       newTable[] = new Entry[newCapacity];

        threshold = (int)(newCapacity * loadFactor);
        table = newTable;
        for (int i = oldCapacity ; i-- > 0 ;) {
            for (Entry old = oldTable[i] ; old != null;) {
                Entry       e = old;
                int         index = ( e.hash & 0x7FFFFFFF ) % newCapacity;

                old = old.next;
                e.next = newTable[index];
                newTable[index] = e;
            }
        }
    }

    /*===================================================================
        Public methods
      ===================================================================*/
    public final boolean containsKey(int key)
	{
        int         index = (key & 0x7FFFFFFF) % table.length;

        for (Entry e = table[index] ; e != null ; e = e.next) {
            if ((e.hash == key) && (e.key == key)) {
                return true;
            }
        }
        return false;
	}

    public final Object get(int key)
    {
        int         index = (key & 0x7FFFFFFF) % table.length;

        for (Entry e = table[index] ; e != null ; e = e.next) {
            if ((e.hash == key) && (e.key == key)) {
                return e.value;
            }
        }
        return null;
    }

    public final Object put(int key, Object value)
    {
        int         index = ( key & 0x7FFFFFFF ) % table.length;

        if (value == null) {
            throw new IllegalArgumentException();
        }
        for (Entry e = table[index] ; e != null ; e = e.next) {
            if ((e.hash == key) && (e.key == key)) {
                Object      old = e.value;

                e.value = value;
                return old;
            }
        }

        if (count >= threshold) {
            // Rehash the table if the threshold is exceeded.
            rehash();
            return put(key, value);
        }

        Entry       e = new Entry();

        e.hash = key;
        e.key = key;
        e.value = value;
        e.next = table[index];
        table[index] = e;
        ++count;
        return null;
    }

    public final Object remove(int key)
    {
        int         index = (key & 0x7FFFFFFF) % table.length;

        for (Entry e = table[index], prev = null ; e != null ; prev = e, e = e.next) {
            if ((e.hash == key) && (e.key == key)) {
                if ( prev != null ) {
                    prev.next = e.next;
                } else {
                    table[index] = e.next;
                }
                --count;
                return e.value;
            }
        }
        return null;
    }

    /*===================================================================
        Map interface
      ===================================================================*/
    public int size()
    {
    	return count;
    }

    public boolean isEmpty()
    {
        return count == 0;
    }

    public Object get(Object key)
	{
        if (!(key instanceof Number)) {
            throw new IllegalArgumentException("key is not an Number subclass");
        }
        return get(((Number)key).intValue());
	}

    public Object put(Object key, Object value)
    {
        if (!(key instanceof Number)) {
            throw new IllegalArgumentException( "key cannot be null" );
        }
        return put(((Number)key).intValue(), value );
    }

    public void putAll(Map otherMap)
    {
        for (Iterator it = otherMap.keySet().iterator(); it.hasNext();) {
            Object      k = it.next();

            put(k, otherMap.get(k));
        }
    }

    public Object remove(Object key)
	{
        if (!(key instanceof Number)) {
            throw new IllegalArgumentException("key cannot be null");
        }
    	return remove(((Number)key).intValue());
	}

    public void clear()
	{
        Entry     tab[] = table;

        for (int index = tab.length; --index >= 0;) {
            tab[index] = null;
        }
        count = 0;
	}

    public boolean containsKey(Object key)
	{
        if (!(key instanceof Number)) {
            throw new InternalError( "key is not an Number subclass" );
        }
        return containsKey(((Number)key).intValue());
	}

    public boolean containsValue(Object value)
    {
        Entry       tab[] = table;

        if (value == null) {
            throw new IllegalArgumentException();
        }
        for (int i = tab.length ; i-- > 0;) {
            for (Entry e = tab[i] ; e != null ; e = e.next ) {
                if (e.value.equals(value)) {
                    return true;
                }
            }
        }
        return false;
    }

    public Set keySet()
    {
        Set     result = new HashSet();

        for (Iterator it = new IntHashMapIterator(table, true); it.hasNext();) {
            result.add(it.next());
        }
        return result;
    }

    public Collection values()
    {
        List        result = new ArrayList();

        for (Iterator it = new IntHashMapIterator(table, false); it.hasNext();) {
            result.add(it.next());
        }
        return result;
    }

    public Set entrySet()
    {
        throw new UnsupportedOperationException("entrySet");
    }
}
