//[skeleton1.
import java.util.*;

/**
 * An event queue with (different categories of) listeners
 *
 * Note: This implementation uses the Singleton pattern to ensure
 * that there is at most one ListenerEventQueue
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 */
public class ListenerEventQueue extends 
                                EventQueue<ListenerEvent>
{
//]skeleton1.
//[singleton1.

    // The instance used in the Singleton pattern
    private static ListenerEventQueue instance;
//]singleton1.
//[constructor.
    
    private   Hashtable<String, 
                        Vector<EventListener>>  categories;


    /**
     * Default Constructor
     */
    private ListenerEventQueue()
    {
       super();

       categories = new Hashtable<String,
                                  Vector<EventListener>>();
    }
//]constructor.

//[addListener.

    /**
     * Add an event listener
     *
     * Note: This method is not synchronized
     * because Vector and Hashtable are thread safe
     *
     * @param listener    The event listener to add
     * @param category    The category/name of the events to listen to
     */
    public void addListener(EventListener listener, 
                            String category)
    {
       Vector<EventListener> listeners;
       
       
       // Get the Hashtable of listeners for this category
       listeners = categories.get(category);

       // If no such category exists, create one
       if (listeners == null)
       {
          listeners = new Vector<EventListener>();
          categories.put(category, listeners);          
       }
       
       // Add this listener
       listeners.add(listener);
    }
//]addListener.


//[fireEvent.

    /**
     * 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 void fireEvent(ListenerEvent event)
    {
	Enumeration<EventListener>  e;
	EventListener               listener;
        Vector<EventListener>       listeners;        
        String                      category;

        // The name of the event corresponds to the category
        category  = event.getName();
        
        // Get the listeners for this category
        listeners = categories.get(category);
        
        // Inform all of the listeners (if there are any)
        if (listeners != null)
        {           
           e = listeners.elements();
           while (e.hasMoreElements()) 
           {
              listener = e.nextElement();
              listener.handleEvent(event);
           }
        }
    }
//]fireEvent.

//[singleton2.

    /**
     * Get the (Singleton) instance 
     *
     * @return  The ListenerEventQueue
     */
    public static synchronized ListenerEventQueue getEventQueue()
    {
       if (instance == null) instance=new ListenerEventQueue();
       
       return instance;       
    }
//]singleton2.
    

//[removeListener.

    /**
     * Remove an event listener
     *
     * Note: This method is not synchronized
     * because Vector and Hashtable are thread safe
     *
     * @param listener    The event listener to remove
     * @param category    The category/name of the events
     */
    protected void removeListener(EventListener listener, 
                                  String        category)
    {
       Vector<EventListener>     listeners;
       
       
       // Get the Hashtable of listeners for this category
       listeners = categories.get(category);

       // If the category exists, remove the listener
       if (listeners != null) listeners.remove(listener);
    }
//]removeListener.
//[skeleton2.

}
//]skeleton2.
