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