package org.zact.idk.cache;

import java.util.HashMap;
import java.util.LinkedList;

/**
 * Fixed length cache with a LRU replacement policy.
 *
 * @author <a href="mailto:colus@isoft.co.kr">Eung-ju Park</a>
 */
public class LruCache
    extends AbstractCache
{
    private HashMap m_entries;
    private LinkedList m_lruList;
    private int m_capacity;

    public LruCache( final int capacity )
    {
        super();
        m_capacity = capacity;
        m_entries = new HashMap( m_capacity );
        m_lruList = new LinkedList();
    }

    public int capacity()
    {
        return m_capacity;
    }

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

    public Object put( final Object key, final Object value )
    {
        Object oldValue;
        synchronized ( m_entries )
        {
            oldValue = remove( key );
            if ( m_entries.size() >= m_capacity )
            {
                final Object lruKey = m_lruList.getLast();
                remove( lruKey );
            }

            m_entries.put( key, value );
            m_lruList.addFirst( key );
            notifyAdded( key, value );
        }
        return oldValue;
    }

    public Object get( final Object key )
    {
        Object value;
        synchronized( m_entries )
        {
            value = m_entries.get( key );
            m_lruList.remove( key );
            m_lruList.addFirst( key );
        }
        return value;
    }

    public Object remove( final Object key )
    {
        Object value = null;
        synchronized ( m_entries )
        {
            if ( containsKey( key ) )
            {
                value = m_entries.remove( key );
                m_lruList.remove( key );
                notifyRemoved( key, value );
            }
        }
        return value;
    }

    public boolean containsKey( final Object key )
    {
        synchronized ( m_entries )
        {
            return m_entries.containsKey( key );
        }
    }

    public void clear()
    {
        synchronized ( m_entries )
        {
            final int len = m_entries.size();
            for ( int i = 0; i < len; i++ )
            {
                final Object key = m_lruList.get( i );
                remove( key );
            }
        }
    }
}

