JMU
Event Listening and Event Bubbling
An Introduction with Examples in Java


Prof. David Bernstein
James Madison University

Computer Science Department
bernstdh@jmu.edu


Event Queue
Using an Event Queue
images/event-queue.gif
Variants

Event Listening

images/event-listening.gif

Event Bubbling

images/event-bubbling.gif
Implementation: Event

One Simple Implementation

javaexamples/eventbased/Event.java
        /**
 * An encapsulation of an Event
 *
 * This implementation is generic so that the two
 * variants, event listening and event bubbling, can
 * have their own type of Event.
 *
 * The type parameter, S, is used to bind the type of the 
 * source.  It can be either EventGenerator  (for event 
 * listening) or EventHandler (for event bubbling).
 *
 * This class has package visibility to prevent it
 * from being typed inappropriately in classes
 * outside of this package.
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 */
class Event<S>
{
    private S          source;
    private String     name, value;
    

    /**
     * Explicit Value Constructor
     *
     * @param source   The source of this Event
     * @param name     The name of this Event
     * @param value    The value of this Event
     */
    public Event(S source, String name, String value)
    {
       this.source = source;
       this.name   = name;
       this.value  = value;       
    }


    /**
     * Get the source of this Event
     *
     * @return    The source
     */
    protected S getSource()
    {
       return source;       
    }


    /**
     * Get the name of this Event
     *
     * @return    The name
     */
    public String getName()
    {
       return name;       
    }


    /**
     * Get the value of this Event
     *
     * @return    The value
     */
    public String getValue()
    {
       return value;       
    }
}
        
Implementation: Event Queue
javaexamples/eventbased/EventQueue.java (Fragment: skeleton)
        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>();
    }

    /**
     * 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();          
       }
    }
}
        
javaexamples/eventbased/EventQueue.java (Fragment: 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);
       }
    }
        
javaexamples/eventbased/EventQueue.java (Fragment: 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;        
    }
        
javaexamples/eventbased/EventQueue.java (Fragment: 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();
       }
    }
        
Event Listening

Overview

images/event-listening_detailed.gif
Event Listenening (cont.)

ListenerEvent

javaexamples/eventbased/ListenerEvent.java
        /**
 * A simple encapsulation of an Event in an event 
 * listener system
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 */
public class ListenerEvent extends Event<EventGenerator>
{
    /**
     * Explicit Value Constructor
     *
     * @param source   The source of this Event
     * @param name     The name of this Event
     * @param value    The value of this Event
     */
    public ListenerEvent(EventGenerator source, 
                         String name, 
                         String value)
    {
       super(source, name, value);
    }
}
        
Event Listenening (cont.)

EventListener

javaexamples/eventbased/EventListener.java
        /**
 * Requirements of an EventListener in an
 * event listener system
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 */
public interface EventListener extends EventReceiver
{
    /**
     * Handle an event
     *
     * @param event   The Event to handle
     * @return        true if the Event was handled
     */
    public abstract boolean handleEvent(ListenerEvent event);

}
        
Event Listenening (cont.)

ListenerEventQueue

javaexamples/eventbased/ListenerEventQueue.java (Fragment: skeleton)
        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>
{

}
        
javaexamples/eventbased/ListenerEventQueue.java (Fragment: constructor)
            
    private   Hashtable<String, 
                        Vector<EventListener>>  categories;


    /**
     * Default Constructor
     */
    private ListenerEventQueue()
    {
       super();

       categories = new Hashtable<String,
                                  Vector<EventListener>>();
    }
        
javaexamples/eventbased/ListenerEventQueue.java (Fragment: 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);
    }
        
javaexamples/eventbased/ListenerEventQueue.java (Fragment: 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);
    }
        
javaexamples/eventbased/ListenerEventQueue.java (Fragment: 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);
           }
        }
    }
        
javaexamples/eventbased/ListenerEventQueue.java (Fragment: singleton)
        
    // The instance used in the Singleton pattern
    private static ListenerEventQueue instance;

    /**
     * Get the (Singleton) instance 
     *
     * @return  The ListenerEventQueue
     */
    public static synchronized ListenerEventQueue getEventQueue()
    {
       if (instance == null) instance=new ListenerEventQueue();
       
       return instance;       
    }
        
Event Bubbling

Overview

images/event-bubbling_detailed.gif
Event Bubbling (cont.)

BubblingEvent

javaexamples/eventbased/BubblingEvent.java
        /**
 * A simple encapsulation of an Event in an event bubbling 
 * system
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 */
public class BubblingEvent extends Event<EventHandler>
{
    /**
     * Explicit Value Constructor
     *
     * @param source   The source (and receiver) of this Event
     * @param name     The name of this Event
     * @param value    The value of this Event
     */
    public BubblingEvent(EventHandler source, 
                         String name, 
                         String value)
    {
       super(source, name, value);
    }

}
        
Event Bubbling (cont.)

EventHandler

javaexamples/eventbased/EventHandler.java
        /**
 * Requirements of an EventHandler in an event bubbling 
 * system
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 */
public interface EventHandler extends EventReceiver
{
    /**
     * Get the parent of this EventHandler
     *
     * @return  The parent (or null)
     */
    public abstract EventHandler getParent();
    

    /**
     * Handle an event
     *
     * @param event                The event to handle
     * @param bubbleHandledEvents  true if handled events should be bubbled
     * @return                     true if the event was handled
     */
    public abstract boolean handleEvent(BubblingEvent event, 
                                  boolean bubbleHandledEvents);
}
        
Event Bubbling (cont.)

An Abstract Event Handler that "Bubbles"

javaexamples/eventbased/AbstractEventHandler.java (Fragment: skeleton)
        
/**
 * An abstract EventHandler in an event bubbling system
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 */
public abstract class AbstractEventHandler implements 
                                           EventGenerator, 
                                           EventHandler
{

    /**
     * Handle an event
     *
     * @param event                The event to handle
     * @param bubbleHandledEvents  true if handled events should be bubbled
     * @return                     true if the event was handled
     */
    public boolean handleEvent(BubblingEvent   event, 
                               boolean bubbleHandledEvents)
    {
       boolean                    handled;
       EventHandler               parent;       
       
       
       // Try and handle the Event locally
       handled = handleEvent(event);

       // Bubble the Event as necessary
       if (bubbleHandledEvents || !handled) 
       {
          parent  = getParent();
          if (parent != null)
          {
             handled = parent.handleEvent(event, 
                                          bubbleHandledEvents);
          }          
       }
       return handled;       
    }


    /**
     * Handle an event
     *
     * @param event   The event to handle
     * @return        true if the event was handled
     */
    public abstract boolean handleEvent(BubblingEvent event);
    
}
        
Event Bubbling (cont.)

BubblingEventQueue

javaexamples/eventbased/BubblingEventQueue.java (Fragment: skeleton)
        import java.util.*;

/**
 * An event queue with event bubbling
 *
 * 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 BubblingEventQueue extends EventQueue<BubblingEvent>
{

}
        
javaexamples/eventbased/BubblingEventQueue.java (Fragment: constructor)
        
    private   Hashtable<String, Boolean>   handlers;

    // "Constants"
    private static final Boolean F = new Boolean(false);
    private static final Boolean T = new Boolean(true);
    

    /**
     * Default Constructor
     */
    private BubblingEventQueue()
    {
       super();
       handlers = new Hashtable<String, Boolean>();        
    }
        
javaexamples/eventbased/BubblingEventQueue.java (Fragment: setBubbleIfHandled)
        
    /**
     * Set whether a particular Event should be bubbled
     * if it was already handled
     *
     * @param name             The name of the Event
     * @param bubbleIfHandled  true to make this name bubble if handled
     */
    public void setBubbleIfHandled(String  name, 
                                   boolean bubbleIfHandled)
    {
       if   (bubbleIfHandled) handlers.put(name, T);
       else                   handlers.put(name, F);       
    }
        
javaexamples/eventbased/BubblingEventQueue.java (Fragment: 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(BubblingEvent event)
    {
       boolean                 bubbleHandledEvents;       
       Boolean                 b;       
       EventHandler            source;

       source = event.getSource();
       b      = handlers.get(event.getName());
       
       bubbleHandledEvents = false;
       if (b != null) bubbleHandledEvents = b.booleanValue();
       
       source.handleEvent(event, bubbleHandledEvents);
    }
        
javaexamples/eventbased/BubblingEventQueue.java (Fragment: singleton)
        
    // The instance used in the Singleton pattern
    private static BubblingEventQueue instance;

    /**
     * Get the (Singleton) instance 
     *
     * @return  The BubblingEventQueue
     */
    public static synchronized BubblingEventQueue getEventQueue()
    {
       if (instance == null) instance = new BubblingEventQueue();
       
       return instance;       
    }
        
Example: Event Listening
images/event-listening_example.gif
Example: Event Listening (cont.)
javaexamples/eventbased/SituationBoard.java
        /**
 * Makes operators aware of messages
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 */
public class SituationBoard implements EventListener
{
    private int      messageCounter;

    /**
     * Default Constructor
     */
    public SituationBoard()
    {
        messageCounter = 0;
    }


    /**
     * Display a message on this SituationBoard
     *
     * For simplicity, this implementation just displays
     * the message on the console
     *
     * @param name   The name of the message
     * @param value  The message
     */
    private void displayMessage(String name, String value)
    {
       System.out.println(messageCounter+":\t"+
                          name + "\t" + value + "\n");
    }
    

    /**
     * Handle an event
     *
     * @param event   The event to handle
     * @return        true if the event was handled
     */
    public boolean handleEvent(ListenerEvent event)
    {
       String name, value;
       
       name  = event.getName();
       value = event.getValue();
       messageCounter++;

       displayMessage(name, value);

       return true;
    }


}
        
javaexamples/eventbased/MessageLogger.java
        import java.io.*;

/**
 * Logs messages to a file
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 */
public class MessageLogger implements EventListener
{
    FileWriter    out;
    String        fileName;

    /**
     * Default Constructor
     */
    public MessageLogger()
    {
       fileName = "log.txt";
    }


    /**
     * Handle an event
     *
     * @param event   The event to handle
     * @return        true if the event was handled
     */
    public boolean handleEvent(ListenerEvent event)
    {
       String name, value;
       
       name  = event.getName();
       value = event.getValue();
        
       logMessage(name, value);

       return true;
    }


    /**
     * Log a message
     *
     * This method re-opens the file (for append) each time
     * it is called to ensure that no messages are lost
     *
     * @param name   The name of the message
     * @param value  The message
     */
    private void logMessage(String name, String value)
    {
       try
       {
          out = new FileWriter(fileName, true);        
          out.write(name + "\t" + value + "\n");
          out.close();
       } 
       catch (IOException ioe)
       {
          System.out.println("Problem logging: " + name + 
                             "\t" + value);
       }
    }
    
}
        
javaexamples/eventbased/MessageEntrySystem.java
        import java.util.*;


/**
 * A simple MessageEntrySystem that can be used to 
 * demonstrate event listening
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 */
public class MessageEntrySystem implements EventGenerator
{
    private ListenerEventQueue  eventq;    
    private Scanner             keyboard;    
    

    /**
     * Default Constructor
     */
    public MessageEntrySystem()
    {
       keyboard = new Scanner(System.in);       
       eventq   = ListenerEventQueue.getEventQueue();
    }
    


    /**
     * Start this MessageEntrySystem
     */
    public void start()
    {
       ListenerEvent    event;       
       String           line, msg, name;
       StringTokenizer  st;
       
       
       while (keyboard.hasNext())
       {
          line  = keyboard.nextLine();
          st    = new StringTokenizer(line, "\t");
          name  = st.nextToken();
          msg   = st.nextToken();
          
          event = new ListenerEvent(this, name, msg);

          eventq.postEvent(event);          
       }
    }
    

    
}

        
javaexamples/eventbased/ListeningDriver.java
        /**
 * An example that uses event listening
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 */
public class ListeningDriver
{

    /**
     * The entry point of the application
     *
     * @param args   The command-line arguments
     */
    public static void main(String[] args)
    {
       ListenerEventQueue     eventq;
       SituationBoard         board;
       MessageEntrySystem     entry;       
       MessageLogger          logger;


       logger = new MessageLogger();
       board  = new SituationBoard();

       eventq = ListenerEventQueue.getEventQueue();
       eventq.start();       

       eventq.addListener(logger, "Message");
       eventq.addListener(logger, "Alert");
       eventq.addListener(board,  "Alert");

       entry  = new MessageEntrySystem();       
       entry.start();       
    }

}
        
Example: Event Bubbling
images/event-bubbling_example.gif
Example: Event Bubbling (cont.)
javaexamples/eventbased/AbstractDocumentElement.java
        /**
 * An abstract element in a document
 *
 * This is used in an example of event bubbling
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 */
public abstract class AbstractDocumentElement 
                      extends AbstractEventHandler
{
    

    /**
     * Handle an event
     *
     * @param event   The event to handle
     * @return        true if the event was handled
     */
    public boolean handleEvent(BubblingEvent event)
    {
        boolean      handled;
        String       name, value;

        name  = event.getName();
        value = event.getValue();

        handled = false;

        if      (name.equals("FaceChange"))  
        {
           handled = handleFaceChange(value);
        }
        else if (name.equals("SizeChange"))
        {
           handled = handleSizeChange(value);
        }
        else if (name.equals("StyleChange"))
        {
           handled = handleStyleChange(value);
        }

        return handled;
    }


    /**
     * Handle a font face change event
     *
     * @param face    The new font face (e.g., TimesRoman)
     */
    protected boolean handleFaceChange(String face)
    {
       return false;       
    }
    



    /**
     * Handle a font size change event
     *
     * @param pointSize    The new font size (e.g., 12)
     */
    protected boolean handleSizeChange(String pointSize)
    {
       return false;       
    }



    /**
     * Handle a font style change event
     *
     * @param style    The new font style (e.g., Italic)
     */
    protected boolean handleStyleChange(String style)
    {
       return false;       
    }
}
        
javaexamples/eventbased/Word.java
        import java.io.*;

/**
 * An encapsulation of a word in a document
 *
 * This is used in an example of event bubbling
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 */
public class Word extends AbstractDocumentElement
{
    private Paragraph            parent;    
    private String               style, text;


    /**
     * Explicit Value Constructor
     *
     * @param text   The text of the word
     */
    public Word(String text)
    {
       super();       
       this.text = text;
       style     = null;       
       parent    = null;        
    }


    
    /**
     * Get the parent of this EventHandler
     *
     * @return  The parent
     */
    public EventHandler getParent()
    {
       return parent;       
    }
    


    /**
     * Handle a font style change event
     *
     * @param style    The new font style (e.g., Italic)
     */
    protected boolean handleStyleChange(String style)
    {
        this.style = style;
        return true;        
    }


    /**
     * Set the parent of this Word
     *
     * Note: In this example, a Word must be in a Paragraph.
     * This could be relaxed.
     */
    public void setParent(Paragraph p)
    {
       parent = p;       
    }
    


    /**
     * Print this Word
     *
     * @param out   The PrintStream to use
     */
    public void print(PrintStream  out)
    {
        out.println("[Style: "+style+"]\t"+ text + " ");
    }

}
        
javaexamples/eventbased/Paragraph.java
        import java.io.*;

/**
 * An encapsulation of a paragraph in a document
 *
 * This is used in an example of event bubbling
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 */
public class Paragraph extends AbstractDocumentElement
{
    private Section      parent;    
    private String       pointSize, style;


    /**
     * Default Constructor
     */
    public Paragraph()
    {
       super();
       pointSize = null;
       style     = null;       
       parent    = null;       
    }


    /**
     * Get the parent of this EventHandler
     *
     * @return  The parent
     */
    public EventHandler getParent()
    {
       return parent;       
    }

    /**
     * Handle a font size change event
     *
     * @param pointSize    The new font size (e.g., 12)
     */
    protected boolean handleSizeChange(String pointSize)
    {
        this.pointSize = pointSize;
        return true;        
    }


    /**
     * Handle a font style change event
     *
     * @param style    The new font style (e.g., Italic)
     */
    protected boolean handleStyleChange(String style)
    {
        this.style = style;
        return true;        
    }


    /**
     * Print this Paragraph
     *
     * @param out   The PrintStream to use
     */
    public void print(PrintStream  out)
    {
        out.println("Size: "+pointSize+"\tStyle: "+style);
    }



    /**
     * Set the parent of this Paragraph
     *
     * Note: In this example, a Paragraph must be in a Section.
     * This could be relaxed.
     */
    public void setParent(Section s)
    {
       parent = s;       
    }
}
        
javaexamples/eventbased/Section.java
        import java.io.*;

/**
 * An encapsulation of a section of in a document
 *
 * This is used in an example of event bubbling
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 */
public class Section extends AbstractDocumentElement
{
    private String        face, pointSize, style;


    /**
     * Default Constructor
     */
    public Section()
    {
       super();       
       face      = null;       
       pointSize = null;
       style     = null;       
    }


    /**
     * Get the parent of this EventHandler
     *
     * @return  The parent
     */
    public EventHandler getParent()
    {
       return null;       
    }


    /**
     * Handle a font face change event
     *
     * Note: These events are handled by this Section
     *
     * @param face    The new font face (e.g., TimesRoman)
     */
    protected boolean handleFaceChange(String face)
    {
        this.face = face;
        return true;        
    }


    /**
     * Handle a font size change event
     *
     * @param pointSize    The new font size (e.g., 12)
     */
    protected boolean handleSizeChange(String pointSize)
    {
        this.pointSize = pointSize;
        return true;        
    }


    /**
     * Handle a font style change event
     *
     * @param style    The new font style (e.g., Italic)
     */
    protected boolean handleStyleChange(String style)
    {
        this.style = style;
        return true;        
    }

    /**
     * Print this Section
     *
     * @param out   The PrintStream to use
     */
    public void print(PrintStream  out)
    {
        out.println("Face: "+face+"\t"+"Size: "+pointSize+"\tStyle: "+style);
    }

}
        
javaexamples/eventbased/BubblingDriver.java
        /**
 * An example of event bubbling
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 */
public class BubblingDriver
{
    private static Paragraph[]            paragraphs;
    private static Section                section;
    private static Word[]                 words;


    /**
     * The entry point of the application
     *
     * @param args   The command-line arguments
     */
    public static void main(String[] args)
    {
        BubblingEventQueue             eventq;
        BubblingEvent                  event;

        words = new Word[8];
        words[0] = new Word("This");
        words[1] = new Word("is");
        words[2] = new Word("paragraph");
        words[3] = new Word("one");
        words[4] = new Word("This");
        words[5] = new Word("is");
        words[6] = new Word("paragraph");
        words[7] = new Word("two");

        paragraphs = new Paragraph[2];
        paragraphs[0] = new Paragraph();
        for (int i=0; i<4; i++) words[i].setParent(paragraphs[0]);

        paragraphs[1] = new Paragraph();
        for (int i=4; i<8; i++) words[i].setParent(paragraphs[1]);

        section = new Section();
        paragraphs[0].setParent(section);
        paragraphs[1].setParent(section);

        eventq = BubblingEventQueue.getEventQueue();
        eventq.start();

        System.out.println("Initial Document");
        print();

        // Change the face for the Section (can't bubble)
        event = new BubblingEvent(section, "FaceChange", "Futura");
        eventq.postEvent(event);
        try { Thread.sleep(1000); } catch (InterruptedException ie) {}
        System.out.println("\n\nAfter Section face change");
        print();
        
        // Change the style for a Word (shouldn't bubble)
        event = new BubblingEvent(words[4], "StyleChange", "Italic");
        eventq.postEvent(event);
        try { Thread.sleep(1000); } catch (InterruptedException ie) {}
        System.out.println("\n\nAfter Word style change");
        print();

        // Change the size for a Paragraph (shouldn't bubble)
        event = new BubblingEvent(paragraphs[0], "SizeChange", "12");
        eventq.postEvent(event);
        try { Thread.sleep(1000); } catch (InterruptedException ie) {}
        System.out.println("\n\nAfter Paragraph size change");
        print();

        // Change the face for a Word (should bubble to Section
        event = new BubblingEvent(words[0], "FaceChange", "Garamond");
        eventq.postEvent(event);
        try { Thread.sleep(1000); } catch (InterruptedException ie) {}
        System.out.println("\n\nAfter Word face change");
        print();
    }


    /**
     * Prnt the current state
     */
    private static void print()
    {
        section.print(System.out);
        paragraphs[0].print(System.out);
        for (int i=0; i<4; i++) words[i].print(System.out);           

        paragraphs[1].print(System.out);
        for (int i=4; i<8; i++) words[i].print(System.out);           
    }
    
}