
package uk.co.wingpath.event;

import java.util.*;
import javax.swing.event.*;
import java.awt.EventQueue;

/**
* This class provides an implementation of a ListDataEvent source.
* <p>The "fire" methods of this class always call the
* methods of registered listeners from the AWT event dispatch
* thread, using the {@link EventQueue#invokeLater
* EventQueue.invokeLater} method if necessary.
* <p>Instances of this class are thread-safe.
* @see ListDataEvent
*/
public class ListEventSource
{
    /**
    * Constructs a {@code ListEventSource}.
    */
    public ListEventSource ()
    {
    }

    private List<ListDataListener> listeners = null;

    /**
    * Adds a listener.
    * @param l the listener to be added.
    */
    public synchronized void addListener (ListDataListener l)
    {
        if (listeners == null)
            listeners = new LinkedList<ListDataListener> ();
        listeners.add (l);
    }

    /**
    * Removes a listener.
    * @param l the listener to be removed.
    */
    public synchronized void removeListener (ListDataListener l)
    {
        if (listeners != null)
            listeners.remove (l);
    }

    /**
    * Fires an inserted event with the specified source and indexes.
    * <p>A {@link ListDataEvent} is constructed and passed to each registered
    * listener.
    * @param source the source to be used for the {@code ListDataEvent}.
    * @param index0 one end of the interval.
    * @param index1 the other end of the interval.
    */
    public void fireInserted (Object source, int index0, int index1)
    {
        fireEvent (source, ListDataEvent.INTERVAL_ADDED, index0, index1);
    }

    /**
    * Fires a removed event with the specified source and indexes.
    * <p>A {@link ListDataEvent} is constructed and passed to each registered
    * listener.
    * @param source the source to be used for the {@code ListDataEvent}.
    * @param index0 one end of the interval.
    * @param index1 the other end of the interval.
    */
    public void fireRemoved (Object source, int index0, int index1)
    {
        fireEvent (source, ListDataEvent.INTERVAL_REMOVED, index0, index1);
    }

    /**
    * Fires a changed event with the specified source and indexes.
    * <p>A {@link ListDataEvent} is constructed and passed to each registered
    * listener.
    * @param source the source to be used for the {@code ListDataEvent}.
    * @param index0 one end of the interval.
    * @param index1 the other end of the interval.
    */
    public void fireChanged (Object source, int index0, int index1)
    {
        fireEvent (source, ListDataEvent.CONTENTS_CHANGED, index0, index1);
    }

    /**
    * Fires an event with the specified source, type and indexes.
    * <p>A {@link ListDataEvent} is constructed and passed to each registered
    * listener.
    * @param source the source to be used for the {@code ListDataEvent}.
    * @param type an int specifying CONTENTS_CHANGED, INTERVAL_ADDED,
    * or INTERVAL_REMOVED.
    * @param index0 one end of the interval.
    * @param index1 the other end of the interval.
    */
    public synchronized void fireEvent (Object source, int type,
        int index0, int index1)
    {
        if (listeners == null || listeners.size () == 0)
            return;
        final ListDataEvent event =
            new ListDataEvent (source, type, index0, index1);
        fireEvent (event);
    }

    /**
    * Fires the specified event.
    * <p>The appropriate method of each registered listener is called,
    * passing the supplied event as a parameter.
    * <p>The method is called from the AWT event dispatch
    * thread, using the {@link EventQueue#invokeLater
    * EventQueue.invokeLater} method if necessary.
    * @param event the event to passed to each listener.
    */
    public synchronized void fireEvent (final ListDataEvent event)
    {
        if (listeners == null || listeners.size () == 0)
            return;
        if (EventQueue.isDispatchThread ())
        {
            for (final ListDataListener listener : listeners)
            {
                switch (event.getType ())
                {
                case ListDataEvent.INTERVAL_ADDED:
                    listener.intervalAdded (event);
                    break;
                case ListDataEvent.INTERVAL_REMOVED:
                    listener.intervalRemoved (event);
                    break;
                case ListDataEvent.CONTENTS_CHANGED:
                    listener.contentsChanged (event);
                    break;
                }
            }
        }
        else
        {
            EventQueue.invokeLater (new Runnable ()
                {
                    public void run ()
                    {
                        fireEvent (event);
                    }
                });
        }
    }
}

