/*
 * The Apache Software License, Version 1.1
 *
 *
 * Copyright (c) 2001 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. 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.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Apache Cocoon" and "Apache Software Foundation" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache",
 *    nor may "Apache" appear in their name, without prior written
 *    permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 APACHE SOFTWARE FOUNDATION OR
 * ITS 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.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */
package org.apache.commons.simplestore.cache.impl;

import org.apache.commons.simplestore.cache.Cache;

import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.util.Map;

/**
 *@author     Juozas Baliuka <a href="mailto:baliuka@mwm.lt">
 *      baliuka@mwm.lt</a>
 *@author     Gerhard Froehlich <a href="mailto:g-froehlich@gmx.de">
 *      g-froehlich@gmx.de</a>
 *@version    $Id: SoftRefMemoryCache.java,v 1.4 2002/03/10 12:32:30 baliuka Exp $
 */
public class SoftRefMemoryCache implements Cache, org.apache.commons.simplestore.tools.Constants  {

    

    private int m_current = 0;
    private int m_maxStrongRefCount;
    private Map m_map;
    private Object[] m_strongRefs;
    private ReferenceQueue m_queue = new ReferenceQueue();

    /**
     * Creates new SoftRefMemoryCache
     *
     *@param  map
     *@param  maxStrongRefCount
     */
    protected SoftRefMemoryCache(Map map, int maxStrongRefCount) {
        this.m_map = map;

        if (maxStrongRefCount < 0) {
            throw new java.lang.IllegalArgumentException();
        }
        this.m_maxStrongRefCount = maxStrongRefCount;

        if (maxStrongRefCount > 0) {
            m_strongRefs = new Object[maxStrongRefCount];
        }
    }

    public static Cache getInstance(Map map, int maxStrongRef) {
        return new SynchronizedCache(new SoftRefMemoryCache(map, maxStrongRef));
    }

    /**
     * Get the object associated to the given unique key.
     *
     *@param  key  the Key Object
     *@return      requested object or null
     */
    public Object get(Object key) {
        removeSoftRef();
        Object object = null;
        SoftRef ref = (SoftRef) m_map.get(key);

        if (ref != null) {
            object = ref.get();
        }

        addStrongRef(object);
        return object;
    }

    /**
     * Cache the object associated to the given unique key.
     *
     *@param  key     the key object
     *@param  object
     */
    public void put(Object key, Object object) {
        removeSoftRef();
        internalStoreObject(key, object);
    }

    private SoftRef makeValue(Object key, Object value, ReferenceQueue queue) {
        return new SoftRef(key, value, queue);
    }

    // remove unused keys
    private void removeSoftRef() {
        SoftRef ref = (SoftRef) m_queue.poll();

        while (ref != null) {
            m_map.remove(ref.key);

            if (DEBUG) {
                System.out.println("Key " + ref.key + " removed from Reference queue, map size is " + m_map.size());
            }
            ref = (SoftRef) m_queue.poll();
        }
    }

    private void addStrongRef(Object object) {
        if (m_strongRefs != null) {
            m_strongRefs[(m_current++) % m_maxStrongRefCount] = object;
        }
    }

    private void internalStoreObject(Object key, Object object) {
        SoftRef ref = makeValue(key, object, m_queue);
        addStrongRef(ref.get());
        m_map.put(key, ref);
    }

    static class SoftRef extends SoftReference {
        Object key;

        private SoftRef(Object key, Object object, ReferenceQueue queue) {
            super(object, queue);
            this.key = key;
        }
    }
}


