001    /*
002     * Created on Mar 27, 2008
003     *
004     * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
005     * the License. You may obtain a copy of the License at
006     *
007     * http://www.apache.org/licenses/LICENSE-2.0
008     *
009     * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
010     * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
011     * specific language governing permissions and limitations under the License.
012     *
013     * Copyright @2008-2010 the original author or authors.
014     */
015    package org.fest.swing.listener;
016    
017    import static javax.swing.SwingUtilities.invokeLater;
018    import static javax.swing.SwingUtilities.isEventDispatchThread;
019    
020    import java.awt.AWTEvent;
021    import java.awt.event.AWTEventListener;
022    import java.util.ArrayList;
023    import java.util.List;
024    
025    import net.jcip.annotations.GuardedBy;
026    import net.jcip.annotations.ThreadSafe;
027    
028    /**
029     * Understands a <code>{@link AWTEventListener}</code> that ensures all events are handled on the event dispatch
030     * thread.
031     * <p>
032     * NOTE from Abbot: Applet runners may run several simultaneous event dispatch threads when displaying multiple applets
033     * simultaneously. If this listener is installed in the parent context of those dispatch threads, it will be invoked on
034     * each of those threads, possibly simultaneously.
035     * </p>
036     *
037     * @author Yvonne Wang
038     * @author Alex Ruiz
039     */
040    @ThreadSafe
041    public abstract class EventDispatchThreadedEventListener implements AWTEventListener {
042    
043      @GuardedBy("lock") private final List<AWTEvent> deferredEvents = new ArrayList<AWTEvent>();
044      private final Object lock = new Object();
045    
046      private final Runnable processDeferredEventsTask = new Runnable() {
047        public void run() {
048          processDeferredEvents();
049        }
050      };
051    
052      /**
053       * If this method is called in the event dispatch thread, it processes the given event and the queued ones. Otherwise
054       * it will add the given event to the queue and process all the events in the queue in the event dispatch thread.
055       * @param event the event to process.
056       */
057      public void eventDispatched(AWTEvent event) {
058        if (!isEventDispatchThread()) {
059          // Often the application under test will invoke Window.show, which spawns hierarchy events. We want to ensure we
060          // respond to those events on the dispatch thread to avoid deadlock.
061          synchronized (lock) {
062            deferredEvents.add(event);
063          }
064          // Ensure that in the absence of any subsequent event thread events deferred events still get processed.
065          // If regular events are received before this action is run, the deferred events will be processed prior to those
066          // events and the action will do nothing.
067          invokeLater(processDeferredEventsTask);
068          return;
069        }
070        // Ensure any deferred events are processed prior to subsequently posted events.
071        processDeferredEvents();
072        processEvent(event);
073      }
074    
075      /** Processes any events that were generated off the event queue but not immediately handled. */
076      protected void processDeferredEvents() {
077        // Make a copy of the deferred events and empty the queue
078        List<AWTEvent> queue = new ArrayList<AWTEvent>();
079        synchronized (lock) {
080          // In the rare case where there are multiple simultaneous dispatch threads, it's possible for deferred events to
081          // get posted while another event is being processed. At most this will mean a few events get processed out of
082          // order, but they will likely be from different event dispatch contexts, so it shouldn't matter.
083          queue.addAll(deferredEvents);
084          deferredEvents.clear();
085        }
086        while (queue.size() > 0) {
087          AWTEvent event = queue.get(0);
088          queue.remove(0);
089          processEvent(event);
090        }
091      }
092    
093      /**
094       * This method is not protected by any synchronization locks (nor should it be); in the presence of multiple
095       * simultaneous event dispatch threads, the listener must be thread-safe.
096       * @param event the event to process.
097       */
098      protected abstract void processEvent(AWTEvent event);
099    }