/*
Copyright (C) 2001, 2006 United States Government
as represented by the Administrator of the
National Aeronautics and Space Administration.
All Rights Reserved.
*/
package gov.nasa.worldwind;

/**
 * An implementation class for the {@link AVList} interface. Classes implementing <code>AVList</code> can subclass or
 * aggreate this class to provide default <code>AVList</code> functionality. This class maintains a hash table of
 * attribute-value pairs.
 * <p/>
 * This class implements a notification mechanism for attribute-value changes. The mechanism provides a means for
 * objects to observe attribute changes or queries for certain keys without explicitly monitoring all keys. See {@link
 * java.beans.PropertyChangeSupport}.
 *
 * @author Tom Gaskins
 * @version $Id: AVListImpl.java 1742 2007-05-06 16:34:29Z tgaskins $
 */
public class AVListImpl implements AVList, java.beans.PropertyChangeListener
{
    // TODO: Make thread-safe
    /**
     * Available to sub-classes for further exposure of property-change functionality.
     */
    protected final java.beans.PropertyChangeSupport changeSupport = new java.beans.PropertyChangeSupport(this);

    // To avoid unnecessary overhead, this object's hash map is created only if needed.
    private java.util.Map<String, Object> avList;

    /**
     * Creates an empty attribute-value list.
     */
    public AVListImpl()
    {
    }

    private boolean hasAvList()
    {
        return this.avList != null;
    }

    private void createAvList()
    {
        if (!this.hasAvList())
        {
            this.avList = new java.util.HashMap<String, Object>();
        }
    }

    private java.util.Map<String, Object> avList(boolean createIfNone)
    {
        if (createIfNone && !this.hasAvList())
            this.createAvList();

        return this.avList;
    }

    public final Object getValue(String key)
    {
        if (key == null)
        {
            String message = WorldWind.retrieveErrMsg("nullValue.AttributeKeyIsNull");
            WorldWind.logger().log(java.util.logging.Level.FINE, message);
            throw new IllegalArgumentException(message);
        }

        if (this.hasAvList())
            return this.avList.get(key);

        return null;
    }

    public final String getStringValue(String key)
    {
        if (key == null)
        {
            String msg = WorldWind.retrieveErrMsg("nullValue.AttributeKeyIsNull");
            WorldWind.logger().log(java.util.logging.Level.FINE, msg);
            throw new IllegalStateException(msg);
        }
        try
        {
            return (String) this.getValue(key);
        }
        catch (ClassCastException e)
        {
            String msg = WorldWind.retrieveErrMsg("AVAAccessibleImpl.AttributeValueForKeyIsNotAString") + key;

            WorldWind.logger().log(java.util.logging.Level.FINE, msg);
            throw new WWRuntimeException(msg, e);
        }
    }

    public final void setValue(String key, Object value)
    {
        if (key == null)
        {
            String message = WorldWind.retrieveErrMsg("nullValue.AttributeKeyIsNull");
            WorldWind.logger().log(java.util.logging.Level.FINE, message);
            throw new IllegalArgumentException(message);
        }
        // Capture the existing value if there is one, then set the new value.
        this.avList(true).put(key, value);
    }

    public final boolean hasKey(String key)
    {
        if (key == null)
        {
            String message = WorldWind.retrieveErrMsg("nullValue.KeyIsNull");
            WorldWind.logger().log(java.util.logging.Level.FINE, message);
            throw new IllegalArgumentException(message);
        }

        return this.hasAvList() && this.avList.containsKey(key);
    }

    public final void removeKey(String key)
    {
        if (key == null)
        {
            String message = WorldWind.retrieveErrMsg("nullValue.KeyIsNull");
            WorldWind.logger().log(java.util.logging.Level.FINE, message);
            throw new IllegalArgumentException(message);
        }

        if (this.hasKey(key))
            this.avList.remove(key);
    }

    public AVList copy()
    {
        AVListImpl clone = new AVListImpl();

        clone.createAvList();
        clone.avList.putAll(this.avList);

        return clone;
    }

    public void addPropertyChangeListener(String propertyName, java.beans.PropertyChangeListener listener)
    {
        if (propertyName == null)
        {
            String msg = WorldWind.retrieveErrMsg("nullValue.PropertyNameIsNull");
            WorldWind.logger().log(java.util.logging.Level.FINE, msg);
            throw new IllegalArgumentException(msg);
        }
        if (listener == null)
        {
            String msg = WorldWind.retrieveErrMsg("nullValue.ListenerIsNull");
            WorldWind.logger().log(java.util.logging.Level.FINE, msg);
            throw new IllegalArgumentException(msg);
        }
        this.changeSupport.addPropertyChangeListener(propertyName, listener);
    }

    public void removePropertyChangeListener(String propertyName, java.beans.PropertyChangeListener listener)
    {
        if (propertyName == null)
        {
            String msg = WorldWind.retrieveErrMsg("nullValue.PropertyNameIsNull");
            WorldWind.logger().log(java.util.logging.Level.FINE, msg);
            throw new IllegalArgumentException(msg);
        }
        if (listener == null)
        {
            String msg = WorldWind.retrieveErrMsg("nullValue.ListenerIsNull");
            WorldWind.logger().log(java.util.logging.Level.FINE, msg);
            throw new IllegalArgumentException(msg);
        }
        this.changeSupport.removePropertyChangeListener(propertyName, listener);
    }

    public void addPropertyChangeListener(java.beans.PropertyChangeListener listener)
    {
        if (listener == null)
        {
            String msg = WorldWind.retrieveErrMsg("nullValue.ListenerIsNull");
            WorldWind.logger().log(java.util.logging.Level.FINE, msg);
            throw new IllegalArgumentException(msg);
        }
        this.changeSupport.addPropertyChangeListener(listener);
    }

    public void removePropertyChangeListener(java.beans.PropertyChangeListener listener)
    {
        if (listener == null)
        {
            String msg = WorldWind.retrieveErrMsg("nullValue.ListenerIsNull");
            WorldWind.logger().log(java.util.logging.Level.FINE, msg);
            throw new IllegalArgumentException(msg);
        }
        this.changeSupport.removePropertyChangeListener(listener);
    }

    public void firePropertyChange(java.beans.PropertyChangeEvent propertyChangeEvent)
    {
        if (propertyChangeEvent == null)
        {
            String msg = WorldWind.retrieveErrMsg("nullValue.PropertyChangeEventIsNull");
            WorldWind.logger().log(java.util.logging.Level.FINE, msg);
            throw new IllegalArgumentException(msg);
        }
        this.changeSupport.firePropertyChange(propertyChangeEvent);
    }

    public void firePropertyChange(String propertyName, Object oldValue, Object newValue)
    {
        if (propertyName == null)
        {
            String msg = WorldWind.retrieveErrMsg("nullValue.PropertyNameIsNull");
            WorldWind.logger().log(java.util.logging.Level.FINE, msg);
            throw new IllegalArgumentException(msg);
        }
        this.changeSupport.firePropertyChange(propertyName, oldValue, newValue);
    }

    /**
     * The property change listener for <em>this</em> instance.
     * Recieves property change notifications that this instance has registered with other proprty change notifiers.
     * @param propertyChangeEvent the event
     * @throws IllegalArgumentException if <code>propertyChangeEvent</code> is null
     */
    public void propertyChange(java.beans.PropertyChangeEvent propertyChangeEvent)
    {
        if (propertyChangeEvent == null)
        {
            String msg = WorldWind.retrieveErrMsg("nullValue.PropertyChangeEventIsNull");
            WorldWind.logger().log(java.util.logging.Level.FINE, msg);
            throw new IllegalArgumentException(msg);
        }

        // Notify all *my* listeners of the change that I caught
        this.changeSupport.firePropertyChange(propertyChangeEvent);
    }
}
