package org.freehep.util.parameterdatabase;

import java.util.LinkedList;

public class ParameterKey {

    /**
     * The object to which this parameter belongs. */
    private Object object;

    /**
     * The name of this parameter. */
    private String name;

    /** 
     * This monitors the state of this ParameterKey.  If this key has
     * been cleared, then this ParameterKey is invalid and most
     * user-visible calls will return an IllegalStateException. */
    private boolean isValid;

    /**
     * This contains the maximum number of recycled keys. */
    static private int inventoryLimit = 10000;

    /**
     * This linked list contains the set of recycled keys. */
    static private LinkedList inventory = new LinkedList();

    /**
     * This protected constructor creates a new ParameterKey with the
     * given parameters.  Users should not create ParameterKeys via
     * this constructor, but should instead use the static factory
     * method createParameterKey(). */
    protected ParameterKey(Object object, String name) {
        this.object = object;
        this.name = name;
        isValid = true;
    }

    /**
     * This static factory method returns a ParameterKey using the
     * given arguments.  This method will try to use a recycled
     * ParameterKey if one is available. */
    static public ParameterKey createParameterKey(Object object, 
                                                  String name) {

        // Check the validity of the input parameters.  Neither one
        // can be null.
        if (object==null || name==null) {
            throw new IllegalArgumentException();
        }

        ParameterKey newKey = null;

        // First check to see if a key is already available.  This
        // must be synchronized to guarantee that the same key isn't
        // given out twice by accident.
        synchronized (inventory) {
            if (!inventory.isEmpty()) {
                newKey = (ParameterKey) inventory.removeFirst();
                newKey.object = object;
                newKey.name = name;
                newKey.isValid = true;
            }
        }

        // If we couldn't find an existing key, then make a new one.
        if (newKey==null) {
            newKey = new ParameterKey(object, name);
        }

        return newKey;
    }

    /**
     * Return the object associated with this ParameterKey.  Note that
     * no operations should be performed on the returned object which
     * will affect the results of the equals() method. */
    public Object getObject() {
        if (!isValid) throw new IllegalStateException();
        return object;
    }

    /**
     * Return the name of the parameter. */
    public String getName() {
        if (!isValid) throw new IllegalStateException();
        return name;
    }

    /**
     * This method will recycle this ParameterKey.  Any further user
     * method calls on this object will result in an
     * IllegalStateException being thrown.  This object will be
     * available for reuse only after the JVM determines that there
     * are no more outstanding references to this ParameterKey and the
     * JVM runs the finalize() method. */
    protected void recycle() {
        isValid = false;
        object = null;
        name = null;
    }

    /**
     * Two ParameterKeys are equal if and only if the corresponding
     * objects and names are equal. */
    public boolean equals(Object otherKey) {

        if (!isValid) throw new IllegalStateException();

        if (!(otherKey instanceof ParameterKey)) {
            return false;
        } else {
            ParameterKey other = (ParameterKey) otherKey;
            if (!other.isValid) throw new IllegalStateException();
            return (object.equals(other.object) && 
                    name.equals(other.name));
        }
    }

    /**
     * The hashcode returned is simply the exclusive-or of the
     * hashcodes of the object and the name. */
    public int hashCode() {
        if (!isValid) throw new IllegalStateException();
        return (object.hashCode() ^ name.hashCode());
    }

    /**
     * We override the finalize method to allow this ParameterKey to
     * be recycled.  It is added to the list of recycled keys if the
     * inventory is below the given limit. */
    public void finalize() 
        throws Throwable {

        // Recycle this key if we haven't exceeded the limit.  A
        // normal return will allow this key to be garbage collected
        // normally. 
        synchronized (inventory) {
            if (inventory.size()<inventoryLimit) {
                recycle();
                inventory.add(this);
                throw new Throwable();
            }
        }
    }

}
