//  Copyright 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.tapestry.util.pool;

/**
 *  A wrapper around a list of objects for a given key in a {@link Pool}.
 *  The current implementation of this is FIFO.  This class is closely
 *  tied to {@link Pool}, which controls synchronization for it.
 *
 *  <p>This class, and {@link Pool}, were heavily revised in 1.0.5
 *  to support generational cleaning.  The PoolList acts like a first-in
 *  first-out queue and each pooled object is tagged with a "generation
 *  count", provided by the {@link Pool}. The generation count is
 *  incremented periodically.  This allows us to track, roughly,
 *  how often a pooled object has been accessed; unused objects will
 *  be buried with relatively low generation counts.
 *
 *  @author Howard Lewis Ship
 *  @version $Id: PoolList.java 243791 2004-02-19 17:38:13Z hlship $
 *
 **/

class PoolList
{
    /** @since 3.0 **/

    private Pool _pool;

    /**
     *  Linked list of pooled objects.
     *
     * @since 1.0.5
     **/

    private Entry _first;

    /**
     *  Linked list of "spare" Entries, ready to be re-used.
     *
     * @since 1.0.5
     **/

    private Entry _spare;

    /**
     *  Overall count of items pooled.
     *
     **/

    private int _count;

    /**
     * A simple linked-list entry for items stored in the PoolList.
     *
     * @since 1.0.5
     * 
     **/

    private static class Entry
    {
        int generation;
        Object pooled;
        Entry next;
    }

    /**
     *  @since 3.0
     * 
     **/
    
    PoolList(Pool pool)
    {
        _pool = pool;
    }

    /**
     *  Returns the number of pooled objects currently stored.
     *
     *  @since 1.0.5
     **/

    public int getPooledCount()
    {
        return _count;
    }

    /**
     *  Returns an object previously stored into the list, or null if the list
     *  is empty.  The returned object is removed from the list.
     *
     **/

    public Object retrieve()
    {
        if (_count == 0)
            return null;

        _count--;

        Entry e = _first;
        Object result = e.pooled;

        // Okay, store e into the list of spare entries.

        _first = e.next;

        e.next = _spare;
        _spare = e;
        e.generation = 0;
        e.pooled = null;

        return result;
    }

    /**
     *  Adds the object to this PoolList.  An arbitrary number of objects can be
     *  stored.  The objects can later be retrieved using {@link #get()}.
     *  The list requires that generation never decrease.  On each subsequent
     *  invocation, it should be the same as, or greater, than the previous value.
     *
     *  @return The number of objects stored in the list (after adding the new object).
     **/

    public int store(int generation, Object object)
    {
        Entry e;

        if (_spare == null)
        {
            e = new Entry();
        }
        else
        {
            e = _spare;
            _spare = _spare.next;
        }

        e.generation = generation;
        e.pooled = object;
        e.next = _first;
        _first = e;

        return ++_count;
    }

    /**
     *  Invoked to cleanup the list, freeing unneeded objects.
     *
     * @param generation pooled objects stored in this generation or
     * earlier are released.
     *
     * @since 1.0.5
     **/

    public int cleanup(int generation)
    {
        _spare = null;

        _count = 0;

        Entry prev = null;

        // Walk through the list.  They'll be sorted by generation.

        Entry e = _first;
        while (true)
        {
            if (e == null)
                break;

            // If found a too-old entry then we want to
            // delete it.

            if (e.generation <= generation)
            {
                Object pooled = e.pooled;

                // Notify the object that it is being dropped
                // through the cracks!

                _pool.getAdaptor(pooled).discardFromPool(pooled);

                // Set the next pointer of the previous node to null.
                // If the very first node inspected was too old,
                // set the first pointer to null.

                if (prev == null)
                    _first = null;
                else
                    prev.next = null;
            }
            else
                _count++;

            prev = e;
            e = e.next;
        }

        return _count;
    }

    public String toString()
    {
        return "PoolList[" + _count + "]";
    }

    /** 
     *  Much like {@link #cleanup(int)}, but discards all
     *  pooled objects.
     * 
     *  @since 3.0 
     * 
     **/

    void discardAll()
    {
        Entry e = _first;

        while (e != null)
        {
            Object pooled = e.pooled;

            _pool.getAdaptor(pooled).discardFromPool(pooled);

            e = e.next;
        }

        _first = null;
        _spare = null;
        _count = 0;

    }
}
