/*
 * $Header: /home/cvspublic/jakarta-slide/proposals/tamino/src/util/org/apache/slide/util/cache/LRUSoftCache.java,v 1.3 2004/07/30 06:52:17 ozeigermann Exp $
 * $Revision: 1.3 $
 * $Date: 2004/07/30 06:52:17 $
 *
 * ====================================================================
 *
 * Copyright 1999-2004 The Apache Software Foundation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.slide.util.cache;
import java.util.Date;
import java.util.Iterator;
import java.util.Map;

/**
 ** Extends SoftCache by an LRU mechanism in order to save a given number of cache
 ** entries of being displaced by the garbage collector.
 **
 ** @author    peter.zaretzke@softwareag.com
 ** @version   $Revision: 1.3 $
 **/
public class LRUSoftCache extends SoftCache {
    
    private LRUQueue lruQueue;
    
    /*******************************************************************************
     * Creates a  LRUSoftCache which protects queueLength object from being displaced
     * by the garbage collector.
     * @param queueLength the number of objects which should be protected from displacement.
     *        Specify 0 if no elements should be kept.
     *******************************************************************************/
    public LRUSoftCache ( int queueLength ) {
        super( Math.max (queueLength, 20) );
        lruQueue = new LRUQueue ( queueLength );
    }
    
    /*******************************************************************************
     * Creates a  LRUSoftCache which protects queueLength and initial capacity object
     * from being displaced by the garbage collector.
     * @param queueLength the number of objects which should be protected from displacement.
     *        Specify 0 if no elements should be kept.
     * @param initialCapacity the initial capacity of this cache.
     *******************************************************************************/
    public LRUSoftCache ( int queueLength, int initialCapacity ) {
        super( initialCapacity );
        lruQueue = new LRUQueue ( queueLength );
    }
    
    /*******************************************************************************
     * @see SoftCache#clear
     *******************************************************************************/
    public void clear() {
        super.clear();
        lruQueue.clear();
    }
    
    /*******************************************************************************
     * Get the value from the specified key
     * @param key key
     * @return value of the given key
     * @see SoftCache#get
     *******************************************************************************/
    public Object get( Object key ) {
        Object result = super.get( key );
        
        if( result != null ) {
            lruQueue.enqueue( result );
        }
        return result;
    }
    
    /*******************************************************************************
     * @see SoftCache#put
     * @param key identifies the object.
     * @param value is the object which is "soft referenced".
     * @return put object
     *******************************************************************************/
    public Object put ( Object key, Object value ) {
        Object result = super.put ( key, value );
        
        lruQueue.enqueue( value );
        
        return result;
    }
    
    /*******************************************************************************
     * @see SoftCache#remove
     * @param key key
     * @return object to remove
     *******************************************************************************/
    public Object remove( Object key ) {
        Object result = super.remove( key );
        
        if( result != null ) {
            lruQueue.remove( result );
        }
        
        return result;
    }
    
    /*******************************************************************************
     * Put all.
     * @param t  map
     * @see SoftCache#putAll
     *******************************************************************************/
    public void putAll( Map t ) {
        Iterator i = t.entrySet().iterator();
        while (i.hasNext()) {
            Map.Entry e = (Map.Entry) i.next();
            put(e.getKey(), e.getValue());
        }
    }
    
    /*******************************************************************************
     * Redefines the number of elements which are kept.
     * @param queueLength queueLength
     *******************************************************************************/
    public void resize ( int queueLength ) {
        lruQueue.resize ( queueLength );
    }
    
    
    /*******************************************************************************
     * TEST
     * @param args arguments
     *******************************************************************************/
    public static void main ( String[] args ) {
        
        Date timer;
        long start, stop;
        LRUSoftCache sa = new LRUSoftCache( 20 );
        SoftCacheTestObject s;
        int loops = Integer.parseInt(args[0]); // at least 10000 elements required
        
        if ( loops < 10000 )
            System.out.println ("Warning at least 10000 elements required to invoke GC");
        
        timer = new Date();
        start = timer.getTime();
        
        for (int i = 0; i < loops; i++) {
            sa.put ( String.valueOf( i ), new SoftCacheTestObject( "Object " +  String.valueOf( i ) ) );
        }
        
        Object xxxx;
        int count = 0;
        for (int i = 0; i < loops; i++)  {
            xxxx = sa.get(String.valueOf( i ));
            if ( xxxx != null )
                count++;
        }
        
        System.out.println ("Using the values().iterator() now");
        for ( Iterator it = sa.values().iterator(); it.hasNext(); )
            it.next();
        
        System.out.println ("SoftIndex elements = " + count );
        
        timer = new Date();
        stop = timer.getTime();
        System.out.println ("SoftIndex: " + (stop-start) + " (ms), size = " + sa.size());
        
        try { Thread.sleep( 3000 ); } catch ( Exception e ) {}
        System.out.println ("\nCall the garbage collector...");
        System.gc();
        System.runFinalization();
        
        count = 0;
        for (int i = 0; i < loops; i++)  {
            xxxx = sa.get(String.valueOf( i ));
            if ( xxxx != null )
                count++;
        }
        System.out.println ("SoftIndex elements after delete = " + count );
        
        try { Thread.sleep( 3000 ); } catch ( Exception e ) {}
        
        String[] values = new String[loops*2];
        
        for (int i = 0; i < loops*2; i++) {
            values[i] = "Object " +  String.valueOf( i );
        }
        
        start = System.currentTimeMillis();
        LRUSoftCache sc = new LRUSoftCache( loops );
        
        for (int i = 0; i < loops*2; i++) {
            sc.put ( String.valueOf( i ), values[i] );
        }
        
        for (int i = loops*2; i >= 0; i--) {
            sc.get (  String.valueOf( i ) );
        }
        
        stop = System.currentTimeMillis();
        System.out.println ("SoftIndex: " + (stop-start) + " (ms)" );
    }
    
}

/**
 ** SoftCacheTestObject.
 **
 ** @author    peter.zaretzke@softwareag.com
 ** @version   $Revision: 1.3 $
 **
 **/
class SoftCacheTestObject {
    
    private String name;
    
    /**
     ** Constructor.
     ** @param      name  name
     **
     **/
    public SoftCacheTestObject ( String name ) {
        this.name = name;
    }
    
    /**
     ** Finalize.
     **/
    public  void finalize() throws Throwable {
        // System.out.println ("finalize " + name );
    }
    
    /**
     ** Returns the string representation of the name.
     ** @return string representation of the name
     **/
    public String toString() {
        return name;
    }
}


