//[skeleton1.
import java.util.*;

/**
 * The core functionality of all event queues
 *
 * This implementation is generic so that the two
 * variants, ListenerEventQueue and BubblingEventQueue,
 * can used different types of Event objects (ListenerEvent 
 * and BubblingEvent objects, respectively).
 *
 * The type parameter, E, is used to bind the type of the 
 * Event.
 *
 * This class has package visibility to prevent it
 * from being typed inappropriately in classes
 * outside of this package.
 *
 * Note: Concrete implementations of this class 
 *       need to implement the fireEvent method.
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 */
abstract class EventQueue<E> implements Runnable
{
    private   volatile boolean         keepRunning;
    protected          LinkedList<E>   events;
    private            Thread          controlThread;
    private   final    Object          lock = new Object();



    /**
     * Default Constructor
     */
    protected EventQueue()
    {
       events     = new LinkedList<E>();
    }
//]skeleton1.

//[fire.

    /**
     * Fire an event
     *
     * Note: The code in this method does not need to be
     * synchronized because it is protected and is only
     * called by firePendingEvents() which is called by
     * a synchronized block in run()
     *
     * @param event   The event to fire
     */
    protected abstract void fireEvent(E event);

    /**
     * Fire all pending events
     *
     * Note: The code in this method does not need to be
     * synchronized because it is private and is only
     * called by a synchronized block in run()
     */
    private void firePendingEvents()
    {
       E    event;
   
       while (events.size() > 0)
       {
          event = events.removeFirst();
          fireEvent(event);
       }
    }
//]fire.


//[postEvent.

    /**
     * Push an event onto the queue
     *
     * @param event   The event to post
     */
    protected void postEvent(E event)
    {
       synchronized(lock)
       {
          // Add an event to the queue
          events.addLast(event);

          // Start the processing
          lock.notifyAll();
       }
    }
//]postEvent.

//[run.

    /**
     * The code to execute in the control thread
     */
    public void run()
    {
       Event     event;

       while (keepRunning) 
       {
          synchronized(lock)
          {
             firePendingEvents();
              
             try
             {
                lock.wait();
             }
             catch (InterruptedException ie)
             {
                // stop() was called
             }
          }
       }
       controlThread = null;        
    }
//]run.

//[skeleton2.

    /**
     * Start the event queue
     */
    public void start()
    {
       if (controlThread == null)
       {
          keepRunning   = true;
          controlThread = new Thread(this);
          
          controlThread.start();
       }
    }


    /**
     * Stop the event queue (after all events have 
     * been processed)
     */
    public void stopEventQueue()
    {
       synchronized(lock)
       {
          keepRunning   = false;
          controlThread.interrupt();          
       }
    }
}
//]skeleton2.
