Save This Page
Home » openjdk-7 » java » awt » [javadoc | source]
    1   /*
    2    * Copyright 1996-2007 Sun Microsystems, Inc.  All Rights Reserved.
    3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    4    *
    5    * This code is free software; you can redistribute it and/or modify it
    6    * under the terms of the GNU General Public License version 2 only, as
    7    * published by the Free Software Foundation.  Sun designates this
    8    * particular file as subject to the "Classpath" exception as provided
    9    * by Sun in the LICENSE file that accompanied this code.
   10    *
   11    * This code is distributed in the hope that it will be useful, but WITHOUT
   12    * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   13    * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   14    * version 2 for more details (a copy is included in the LICENSE file that
   15    * accompanied this code).
   16    *
   17    * You should have received a copy of the GNU General Public License version
   18    * 2 along with this work; if not, write to the Free Software Foundation,
   19    * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   20    *
   21    * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
   22    * CA 95054 USA or visit www.sun.com if you need additional information or
   23    * have any questions.
   24    */
   25   
   26   package java.awt;
   27   
   28   import java.awt.event.InputEvent;
   29   import java.awt.event.MouseEvent;
   30   import java.awt.event.ActionEvent;
   31   import java.awt.event.WindowEvent;
   32   import java.lang.reflect.Method;
   33   import java.security.AccessController;
   34   import sun.security.action.GetPropertyAction;
   35   import sun.awt.AWTAutoShutdown;
   36   import sun.awt.SunToolkit;
   37   
   38   import java.util.Vector;
   39   import java.util.logging;
   40   
   41   import sun.awt.dnd.SunDragSourceContextPeer;
   42   
   43   /**
   44    * EventDispatchThread is a package-private AWT class which takes
   45    * events off the EventQueue and dispatches them to the appropriate
   46    * AWT components.
   47    *
   48    * The Thread starts a "permanent" event pump with a call to
   49    * pumpEvents(Conditional) in its run() method. Event handlers can choose to
   50    * block this event pump at any time, but should start a new pump (<b>not</b>
   51    * a new EventDispatchThread) by again calling pumpEvents(Conditional). This
   52    * secondary event pump will exit automatically as soon as the Condtional
   53    * evaluate()s to false and an additional Event is pumped and dispatched.
   54    *
   55    * @author Tom Ball
   56    * @author Amy Fowler
   57    * @author Fred Ecks
   58    * @author David Mendenhall
   59    *
   60    * @since 1.1
   61    */
   62   class EventDispatchThread extends Thread {
   63       private static final Logger eventLog = Logger.getLogger("java.awt.event.EventDispatchThread");
   64   
   65       private EventQueue theQueue;
   66       private boolean doDispatch = true;
   67       private static final int ANY_EVENT = -1;
   68   
   69       private Vector<EventFilter> eventFilters = new Vector<EventFilter>();
   70       // used in handleException
   71       private int modalFiltersCount = 0;
   72   
   73       EventDispatchThread(ThreadGroup group, String name, EventQueue queue) {
   74           super(group, name);
   75           theQueue = queue;
   76       }
   77   
   78       void stopDispatchingImpl(boolean wait) {
   79           // Note: We stop dispatching via a flag rather than using
   80           // Thread.interrupt() because we can't guarantee that the wait()
   81           // we interrupt will be EventQueue.getNextEvent()'s.  -fredx 8-11-98
   82   
   83           StopDispatchEvent stopEvent = new StopDispatchEvent();
   84   
   85           // wait for the dispatcher to complete
   86           if (Thread.currentThread() != this) {
   87   
   88               // fix 4122683, 4128923
   89               // Post an empty event to ensure getNextEvent is unblocked
   90               //
   91               // We have to use postEventPrivate instead of postEvent because
   92               // EventQueue.pop calls EventDispatchThread.stopDispatching.
   93               // Calling SunToolkit.flushPendingEvents in this case could
   94               // lead to deadlock.
   95               theQueue.postEventPrivate(stopEvent);
   96   
   97               if (wait) {
   98                   try {
   99                       join();
  100                   } catch(InterruptedException e) {
  101                   }
  102               }
  103           } else {
  104               stopEvent.dispatch();
  105           }
  106           synchronized (theQueue) {
  107               if (theQueue.getDispatchThread() == this) {
  108                   theQueue.detachDispatchThread();
  109               }
  110           }
  111       }
  112   
  113       public void stopDispatching() {
  114           stopDispatchingImpl(true);
  115       }
  116   
  117       public void stopDispatchingLater() {
  118           stopDispatchingImpl(false);
  119       }
  120   
  121       class StopDispatchEvent extends AWTEvent implements ActiveEvent {
  122           /*
  123            * serialVersionUID
  124            */
  125           static final long serialVersionUID = -3692158172100730735L;
  126   
  127           public StopDispatchEvent() {
  128               super(EventDispatchThread.this,0);
  129           }
  130   
  131           public void dispatch() {
  132               doDispatch = false;
  133           }
  134       }
  135   
  136       public void run() {
  137           try {
  138               pumpEvents(new Conditional() {
  139                   public boolean evaluate() {
  140                       return true;
  141                   }
  142               });
  143           } finally {
  144               /*
  145                * This synchronized block is to secure that the event dispatch
  146                * thread won't die in the middle of posting a new event to the
  147                * associated event queue. It is important because we notify
  148                * that the event dispatch thread is busy after posting a new event
  149                * to its queue, so the EventQueue.dispatchThread reference must
  150                * be valid at that point.
  151                */
  152               synchronized (theQueue) {
  153                   if (theQueue.getDispatchThread() == this) {
  154                       theQueue.detachDispatchThread();
  155                   }
  156                   /*
  157                    * Event dispatch thread dies in case of an uncaught exception.
  158                    * A new event dispatch thread for this queue will be started
  159                    * only if a new event is posted to it. In case if no more
  160                    * events are posted after this thread died all events that
  161                    * currently are in the queue will never be dispatched.
  162                    */
  163                   /*
  164                    * Fix for 4648733. Check both the associated java event
  165                    * queue and the PostEventQueue.
  166                    */
  167                   if (theQueue.peekEvent() != null ||
  168                       !SunToolkit.isPostEventQueueEmpty()) {
  169                       theQueue.initDispatchThread();
  170                   }
  171                   AWTAutoShutdown.getInstance().notifyThreadFree(this);
  172               }
  173           }
  174       }
  175   
  176       void pumpEvents(Conditional cond) {
  177           pumpEvents(ANY_EVENT, cond);
  178       }
  179   
  180       void pumpEventsForHierarchy(Conditional cond, Component modalComponent) {
  181           pumpEventsForHierarchy(ANY_EVENT, cond, modalComponent);
  182       }
  183   
  184       void pumpEvents(int id, Conditional cond) {
  185           pumpEventsForHierarchy(id, cond, null);
  186       }
  187   
  188       void pumpEventsForHierarchy(int id, Conditional cond, Component modalComponent)
  189       {
  190           pumpEventsForFilter(id, cond, new HierarchyEventFilter(modalComponent));
  191       }
  192   
  193       void pumpEventsForFilter(Conditional cond, EventFilter filter) {
  194           pumpEventsForFilter(ANY_EVENT, cond, filter);
  195       }
  196   
  197       void pumpEventsForFilter(int id, Conditional cond, EventFilter filter) {
  198           addEventFilter(filter);
  199           while (doDispatch && cond.evaluate()) {
  200               if (isInterrupted() || !pumpOneEventForFilters(id)) {
  201                   doDispatch = false;
  202               }
  203           }
  204           removeEventFilter(filter);
  205       }
  206   
  207       void addEventFilter(EventFilter filter) {
  208           synchronized (eventFilters) {
  209               if (!eventFilters.contains(filter)) {
  210                   if (filter instanceof ModalEventFilter) {
  211                       ModalEventFilter newFilter = (ModalEventFilter)filter;
  212                       int k = 0;
  213                       for (k = 0; k < eventFilters.size(); k++) {
  214                           EventFilter f = eventFilters.get(k);
  215                           if (f instanceof ModalEventFilter) {
  216                               ModalEventFilter cf = (ModalEventFilter)f;
  217                               if (cf.compareTo(newFilter) > 0) {
  218                                   break;
  219                               }
  220                           }
  221                       }
  222                       eventFilters.add(k, filter);
  223                       modalFiltersCount++;
  224                   } else {
  225                       eventFilters.add(filter);
  226                   }
  227               }
  228           }
  229       }
  230   
  231       void removeEventFilter(EventFilter filter) {
  232           synchronized (eventFilters) {
  233               if (eventFilters.contains(filter)) {
  234                   if (filter instanceof ModalEventFilter) {
  235                       modalFiltersCount--;
  236                   }
  237                   eventFilters.remove(filter);
  238               }
  239           }
  240       }
  241   
  242       boolean pumpOneEventForFilters(int id) {
  243           try {
  244               AWTEvent event;
  245               boolean eventOK;
  246               do {
  247                   event = (id == ANY_EVENT)
  248                       ? theQueue.getNextEvent()
  249                       : theQueue.getNextEvent(id);
  250   
  251                   eventOK = true;
  252                   synchronized (eventFilters) {
  253                       for (int i = eventFilters.size() - 1; i >= 0; i--) {
  254                           EventFilter f = eventFilters.get(i);
  255                           EventFilter.FilterAction accept = f.acceptEvent(event);
  256                           if (accept == EventFilter.FilterAction.REJECT) {
  257                               eventOK = false;
  258                               break;
  259                           } else if (accept == EventFilter.FilterAction.ACCEPT_IMMEDIATELY) {
  260                               break;
  261                           }
  262                       }
  263                   }
  264                   eventOK = eventOK && SunDragSourceContextPeer.checkEvent(event);
  265                   if (!eventOK) {
  266                       event.consume();
  267                   }
  268               }
  269               while (eventOK == false);
  270   
  271               if (eventLog.isLoggable(Level.FINEST)) {
  272                   eventLog.log(Level.FINEST, "Dispatching: " + event);
  273               }
  274   
  275               theQueue.dispatchEvent(event);
  276               return true;
  277           }
  278           catch (ThreadDeath death) {
  279               return false;
  280   
  281           }
  282           catch (InterruptedException interruptedException) {
  283               return false; // AppContext.dispose() interrupts all
  284                             // Threads in the AppContext
  285   
  286           }
  287           // Can get and throw only unchecked exceptions
  288           catch (RuntimeException e) {
  289               processException(e, modalFiltersCount > 0);
  290           } catch (Error e) {
  291               processException(e, modalFiltersCount > 0);
  292           }
  293           return true;
  294       }
  295   
  296       private void processException(Throwable e, boolean isModal) {
  297           if (eventLog.isLoggable(Level.FINE)) {
  298               eventLog.log(Level.FINE, "Processing exception: " + e +
  299                                        ", isModal = " + isModal);
  300           }
  301           if (!handleException(e)) {
  302               // See bug ID 4499199.
  303               // If we are in a modal dialog, we cannot throw
  304               // an exception for the ThreadGroup to handle (as added
  305               // in RFE 4063022).  If we did, the message pump of
  306               // the modal dialog would be interrupted.
  307               // We instead choose to handle the exception ourselves.
  308               // It may be useful to add either a runtime flag or API
  309               // later if someone would like to instead dispose the
  310               // dialog and allow the thread group to handle it.
  311               if (isModal) {
  312                   System.err.println(
  313                       "Exception occurred during event dispatching:");
  314                   e.printStackTrace();
  315               } else if (e instanceof RuntimeException) {
  316                   throw (RuntimeException)e;
  317               } else if (e instanceof Error) {
  318                   throw (Error)e;
  319               }
  320           }
  321       }
  322   
  323       private static final String handlerPropName = "sun.awt.exception.handler";
  324       private static String handlerClassName = null;
  325       private static String NO_HANDLER = new String();
  326   
  327       /**
  328        * Handles an exception thrown in the event-dispatch thread.
  329        *
  330        * <p> If the system property "sun.awt.exception.handler" is defined, then
  331        * when this method is invoked it will attempt to do the following:
  332        *
  333        * <ol>
  334        * <li> Load the class named by the value of that property, using the
  335        *      current thread's context class loader,
  336        * <li> Instantiate that class using its zero-argument constructor,
  337        * <li> Find the resulting handler object's <tt>public void handle</tt>
  338        *      method, which should take a single argument of type
  339        *      <tt>Throwable</tt>, and
  340        * <li> Invoke the handler's <tt>handle</tt> method, passing it the
  341        *      <tt>thrown</tt> argument that was passed to this method.
  342        * </ol>
  343        *
  344        * If any of the first three steps fail then this method will return
  345        * <tt>false</tt> and all following invocations of this method will return
  346        * <tt>false</tt> immediately.  An exception thrown by the handler object's
  347        * <tt>handle</tt> will be caught, and will cause this method to return
  348        * <tt>false</tt>.  If the handler's <tt>handle</tt> method is successfully
  349        * invoked, then this method will return <tt>true</tt>.  This method will
  350        * never throw any sort of exception.
  351        *
  352        * <p> <i>Note:</i> This method is a temporary hack to work around the
  353        * absence of a real API that provides the ability to replace the
  354        * event-dispatch thread.  The magic "sun.awt.exception.handler" property
  355        * <i>will be removed</i> in a future release.
  356        *
  357        * @param  thrown  The Throwable that was thrown in the event-dispatch
  358        *                 thread
  359        *
  360        * @return  <tt>false</tt> if any of the above steps failed, otherwise
  361        *          <tt>true</tt>
  362        */
  363       private boolean handleException(Throwable thrown) {
  364   
  365           try {
  366   
  367               if (handlerClassName == NO_HANDLER) {
  368                   return false;   /* Already tried, and failed */
  369               }
  370   
  371               /* Look up the class name */
  372               if (handlerClassName == null) {
  373                   handlerClassName = ((String) AccessController.doPrivileged(
  374                       new GetPropertyAction(handlerPropName)));
  375                   if (handlerClassName == null) {
  376                       handlerClassName = NO_HANDLER; /* Do not try this again */
  377                       return false;
  378                   }
  379               }
  380   
  381               /* Load the class, instantiate it, and find its handle method */
  382               Method m;
  383               Object h;
  384               try {
  385                   ClassLoader cl = Thread.currentThread().getContextClassLoader();
  386                   Class c = Class.forName(handlerClassName, true, cl);
  387                   m = c.getMethod("handle", new Class[] { Throwable.class });
  388                   h = c.newInstance();
  389               } catch (Throwable x) {
  390                   handlerClassName = NO_HANDLER; /* Do not try this again */
  391                   return false;
  392               }
  393   
  394               /* Finally, invoke the handler */
  395               m.invoke(h, new Object[] { thrown });
  396   
  397           } catch (Throwable x) {
  398               return false;
  399           }
  400   
  401           return true;
  402       }
  403   
  404       boolean isDispatching(EventQueue eq) {
  405           return theQueue.equals(eq);
  406       }
  407   
  408       EventQueue getEventQueue() { return theQueue; }
  409   
  410       private static class HierarchyEventFilter implements EventFilter {
  411           private Component modalComponent;
  412           public HierarchyEventFilter(Component modalComponent) {
  413               this.modalComponent = modalComponent;
  414           }
  415           public FilterAction acceptEvent(AWTEvent event) {
  416               if (modalComponent != null) {
  417                   int eventID = event.getID();
  418                   boolean mouseEvent = (eventID >= MouseEvent.MOUSE_FIRST) &&
  419                                        (eventID <= MouseEvent.MOUSE_LAST);
  420                   boolean actionEvent = (eventID >= ActionEvent.ACTION_FIRST) &&
  421                                         (eventID <= ActionEvent.ACTION_LAST);
  422                   boolean windowClosingEvent = (eventID == WindowEvent.WINDOW_CLOSING);
  423                   /*
  424                    * filter out MouseEvent and ActionEvent that's outside
  425                    * the modalComponent hierarchy.
  426                    * KeyEvent is handled by using enqueueKeyEvent
  427                    * in Dialog.show
  428                    */
  429                   if (Component.isInstanceOf(modalComponent, "javax.swing.JInternalFrame")) {
  430                       /*
  431                        * Modal internal frames are handled separately. If event is
  432                        * for some component from another heavyweight than modalComp,
  433                        * it is accepted. If heavyweight is the same - we still accept
  434                        * event and perform further filtering in LightweightDispatcher
  435                        */
  436                       return windowClosingEvent ? FilterAction.REJECT : FilterAction.ACCEPT;
  437                   }
  438                   if (mouseEvent || actionEvent || windowClosingEvent) {
  439                       Object o = event.getSource();
  440                       if (o instanceof sun.awt.ModalExclude) {
  441                           // Exclude this object from modality and
  442                           // continue to pump it's events.
  443                           return FilterAction.ACCEPT;
  444                       } else if (o instanceof Component) {
  445                           Component c = (Component) o;
  446                           // 5.0u3 modal exclusion
  447                           boolean modalExcluded = false;
  448                           if (modalComponent instanceof Container) {
  449                               while (c != modalComponent && c != null) {
  450                                   if ((c instanceof Window) &&
  451                                       (sun.awt.SunToolkit.isModalExcluded((Window)c))) {
  452                                       // Exclude this window and all its children from
  453                                       //  modality and continue to pump it's events.
  454                                       modalExcluded = true;
  455                                       break;
  456                                   }
  457                                   c = c.getParent();
  458                               }
  459                           }
  460                           if (!modalExcluded && (c != modalComponent)) {
  461                               return FilterAction.REJECT;
  462                           }
  463                       }
  464                   }
  465               }
  466               return FilterAction.ACCEPT;
  467           }
  468       }
  469   }

Save This Page
Home » openjdk-7 » java » awt » [javadoc | source]