src/share/classes/java/awt/SequencedEvent.java
author Jaroslav Tulach <jtulach@netbeans.org>
Wed, 23 May 2012 11:51:09 +0200
branchjavafx
changeset 5229 4ed8674de305
parent 3294 a06412e13bf7
permissions -rw-r--r--
Allowing JavaFX to dispatchEvent in its own, dedicated FX dispatch thread
     1 /*
     2  * Copyright (c) 2000, 2010, Oracle and/or its affiliates. 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.  Oracle designates this
     8  * particular file as subject to the "Classpath" exception as provided
     9  * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    22  * or visit www.oracle.com if you need additional information or have any
    23  * questions.
    24  */
    25 
    26 package java.awt;
    27 
    28 import java.util.LinkedList;
    29 import sun.awt.AppContext;
    30 import sun.awt.SunToolkit;
    31 
    32 /**
    33  * A mechanism for ensuring that a series of AWTEvents are executed in a
    34  * precise order, even across multiple AppContexts. The nested events will be
    35  * dispatched in the order in which their wrapping SequencedEvents were
    36  * constructed. The only exception to this rule is if the peer of the target of
    37  * the nested event was destroyed (with a call to Component.removeNotify)
    38  * before the wrapping SequencedEvent was able to be dispatched. In this case,
    39  * the nested event is never dispatched.
    40  *
    41  * @author David Mendenhall
    42  */
    43 class SequencedEvent extends AWTEvent implements ActiveEvent {
    44     /*
    45      * serialVersionUID
    46      */
    47     private static final long serialVersionUID = 547742659238625067L;
    48 
    49     private static final int ID =
    50         java.awt.event.FocusEvent.FOCUS_LAST + 1;
    51     private static final LinkedList list = new LinkedList();
    52 
    53     private final AWTEvent nested;
    54     private AppContext appContext;
    55     private boolean disposed;
    56 
    57     /**
    58      * Constructs a new SequencedEvent which will dispatch the specified
    59      * nested event.
    60      *
    61      * @param nested the AWTEvent which this SequencedEvent's dispatch()
    62      *        method will dispatch
    63      */
    64     public SequencedEvent(AWTEvent nested) {
    65         super(nested.getSource(), ID);
    66         this.nested = nested;
    67         // All AWTEvents that are wrapped in SequencedEvents are (at
    68         // least currently) implicitly generated by the system
    69         SunToolkit.setSystemGenerated(nested);
    70         synchronized (SequencedEvent.class) {
    71             list.add(this);
    72         }
    73     }
    74 
    75     /**
    76      * Dispatches the nested event after all previous nested events have been
    77      * dispatched or disposed. If this method is invoked before all previous nested events
    78      * have been dispatched, then this method blocks until such a point is
    79      * reached.
    80      * While waiting disposes nested events to disposed AppContext
    81      *
    82      * NOTE: Locking protocol.  Since dispose() can get EventQueue lock,
    83      * dispatch() shall never call dispose() while holding the lock on the list,
    84      * as EventQueue lock is held during dispatching.  The locks should be acquired
    85      * in the same order.
    86      */
    87     public final void dispatch() {
    88         try {
    89             appContext = AppContext.getAppContext();
    90 
    91             if (getFirst() != this) {
    92                 if (EventQueue.isDispatchThread()) {
    93                     EventDispatchThread edt = EventDispatchThread.findCurrent();
    94                     edt.pumpEvents(SentEvent.ID, new Conditional() {
    95                         public boolean evaluate() {
    96                             return !SequencedEvent.this.isFirstOrDisposed();
    97                         }
    98                     });
    99                 } else {
   100                     while(!isFirstOrDisposed()) {
   101                         synchronized (SequencedEvent.class) {
   102                             try {
   103                                 SequencedEvent.class.wait(1000);
   104                             } catch (InterruptedException e) {
   105                                 break;
   106                             }
   107                         }
   108                     }
   109                 }
   110             }
   111 
   112             if (!disposed) {
   113                 KeyboardFocusManager.getCurrentKeyboardFocusManager().
   114                     setCurrentSequencedEvent(this);
   115                 Toolkit.getEventQueue().dispatchEvent(nested);
   116             }
   117         } finally {
   118             dispose();
   119         }
   120     }
   121 
   122     /**
   123      * true only if event exists and nested source appContext is disposed.
   124      */
   125     private final static boolean isOwnerAppContextDisposed(SequencedEvent se) {
   126         if (se != null) {
   127             Object target = se.nested.getSource();
   128             if (target instanceof Component) {
   129                 return ((Component)target).appContext.isDisposed();
   130             }
   131         }
   132         return false;
   133     }
   134 
   135     /**
   136      * Sequenced events are dispatched in order, so we cannot dispatch
   137      * until we are the first sequenced event in the queue (i.e. it's our
   138      * turn).  But while we wait for our turn to dispatch, the event
   139      * could have been disposed for a number of reasons.
   140      */
   141     public final boolean isFirstOrDisposed() {
   142         if (disposed) {
   143             return true;
   144         }
   145         // getFirstWithContext can dispose this
   146         return this == getFirstWithContext() || disposed;
   147     }
   148 
   149     private final synchronized static SequencedEvent getFirst() {
   150         return (SequencedEvent)list.getFirst();
   151     }
   152 
   153     /* Disposes all events from disposed AppContext
   154      * return first valid event
   155      */
   156     private final static SequencedEvent getFirstWithContext() {
   157         SequencedEvent first = getFirst();
   158         while(isOwnerAppContextDisposed(first)) {
   159             first.dispose();
   160             first = getFirst();
   161         }
   162         return first;
   163     }
   164 
   165     /**
   166      * Disposes of this instance. This method is invoked once the nested event
   167      * has been dispatched and handled, or when the peer of the target of the
   168      * nested event has been disposed with a call to Component.removeNotify.
   169      *
   170      * NOTE: Locking protocol.  Since SunToolkit.postEvent can get EventQueue lock,
   171      * it shall never be called while holding the lock on the list,
   172      * as EventQueue lock is held during dispatching and dispatch() will get
   173      * lock on the list. The locks should be acquired in the same order.
   174      */
   175     final void dispose() {
   176       synchronized (SequencedEvent.class) {
   177             if (disposed) {
   178                 return;
   179             }
   180             if (KeyboardFocusManager.getCurrentKeyboardFocusManager().
   181                     getCurrentSequencedEvent() == this) {
   182                 KeyboardFocusManager.getCurrentKeyboardFocusManager().
   183                     setCurrentSequencedEvent(null);
   184             }
   185             disposed = true;
   186         }
   187         // Wake myself up
   188         if (appContext != null) {
   189             SunToolkit.postEvent(appContext, new SentEvent());
   190         }
   191 
   192         SequencedEvent next = null;
   193 
   194         synchronized (SequencedEvent.class) {
   195           SequencedEvent.class.notifyAll();
   196 
   197           if (list.getFirst() == this) {
   198               list.removeFirst();
   199 
   200               if (!list.isEmpty()) {
   201                     next = (SequencedEvent)list.getFirst();
   202               }
   203           } else {
   204               list.remove(this);
   205           }
   206       }
   207         // Wake up waiting threads
   208         if (next != null && next.appContext != null) {
   209             SunToolkit.postEvent(next.appContext, new SentEvent());
   210         }
   211     }
   212 }