| /* |
| * Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| package java.awt; |
| |
| import java.util.LinkedList; |
| import sun.awt.AWTAccessor; |
| import sun.awt.AppContext; |
| import sun.awt.SunToolkit; |
| |
| /** |
| * A mechanism for ensuring that a series of AWTEvents are executed in a |
| * precise order, even across multiple AppContexts. The nested events will be |
| * dispatched in the order in which their wrapping SequencedEvents were |
| * constructed. The only exception to this rule is if the peer of the target of |
| * the nested event was destroyed (with a call to Component.removeNotify) |
| * before the wrapping SequencedEvent was able to be dispatched. In this case, |
| * the nested event is never dispatched. |
| * |
| * @author David Mendenhall |
| */ |
| class SequencedEvent extends AWTEvent implements ActiveEvent { |
| /* |
| * serialVersionUID |
| */ |
| private static final long serialVersionUID = 547742659238625067L; |
| |
| private static final int ID = |
| java.awt.event.FocusEvent.FOCUS_LAST + 1; |
| private static final LinkedList<SequencedEvent> list = new LinkedList<>(); |
| |
| private final AWTEvent nested; |
| private AppContext appContext; |
| private boolean disposed; |
| |
| static { |
| AWTAccessor.setSequencedEventAccessor(new AWTAccessor.SequencedEventAccessor() { |
| public AWTEvent getNested(AWTEvent sequencedEvent) { |
| return ((SequencedEvent)sequencedEvent).nested; |
| } |
| public boolean isSequencedEvent(AWTEvent event) { |
| return event instanceof SequencedEvent; |
| } |
| |
| public AWTEvent create(AWTEvent event) { |
| return new SequencedEvent(event); |
| } |
| }); |
| } |
| |
| /** |
| * Constructs a new SequencedEvent which will dispatch the specified |
| * nested event. |
| * |
| * @param nested the AWTEvent which this SequencedEvent's dispatch() |
| * method will dispatch |
| */ |
| public SequencedEvent(AWTEvent nested) { |
| super(nested.getSource(), ID); |
| this.nested = nested; |
| // All AWTEvents that are wrapped in SequencedEvents are (at |
| // least currently) implicitly generated by the system |
| SunToolkit.setSystemGenerated(nested); |
| synchronized (SequencedEvent.class) { |
| list.add(this); |
| } |
| } |
| |
| /** |
| * Dispatches the nested event after all previous nested events have been |
| * dispatched or disposed. If this method is invoked before all previous nested events |
| * have been dispatched, then this method blocks until such a point is |
| * reached. |
| * While waiting disposes nested events to disposed AppContext |
| * |
| * NOTE: Locking protocol. Since dispose() can get EventQueue lock, |
| * dispatch() shall never call dispose() while holding the lock on the list, |
| * as EventQueue lock is held during dispatching. The locks should be acquired |
| * in the same order. |
| */ |
| public final void dispatch() { |
| try { |
| appContext = AppContext.getAppContext(); |
| |
| if (getFirst() != this) { |
| if (EventQueue.isDispatchThread()) { |
| EventDispatchThread edt = (EventDispatchThread) |
| Thread.currentThread(); |
| edt.pumpEvents(SentEvent.ID, new Conditional() { |
| public boolean evaluate() { |
| return !SequencedEvent.this.isFirstOrDisposed(); |
| } |
| }); |
| } else { |
| while(!isFirstOrDisposed()) { |
| synchronized (SequencedEvent.class) { |
| try { |
| SequencedEvent.class.wait(1000); |
| } catch (InterruptedException e) { |
| break; |
| } |
| } |
| } |
| } |
| } |
| |
| if (!disposed) { |
| KeyboardFocusManager.getCurrentKeyboardFocusManager(). |
| setCurrentSequencedEvent(this); |
| Toolkit.getEventQueue().dispatchEvent(nested); |
| } |
| } finally { |
| dispose(); |
| } |
| } |
| |
| /** |
| * true only if event exists and nested source appContext is disposed. |
| */ |
| private static final boolean isOwnerAppContextDisposed(SequencedEvent se) { |
| if (se != null) { |
| Object target = se.nested.getSource(); |
| if (target instanceof Component) { |
| return ((Component)target).appContext.isDisposed(); |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Sequenced events are dispatched in order, so we cannot dispatch |
| * until we are the first sequenced event in the queue (i.e. it's our |
| * turn). But while we wait for our turn to dispatch, the event |
| * could have been disposed for a number of reasons. |
| */ |
| public final boolean isFirstOrDisposed() { |
| if (disposed) { |
| return true; |
| } |
| // getFirstWithContext can dispose this |
| return this == getFirstWithContext() || disposed; |
| } |
| |
| private static final synchronized SequencedEvent getFirst() { |
| return list.getFirst(); |
| } |
| |
| /* Disposes all events from disposed AppContext |
| * return first valid event |
| */ |
| private static final SequencedEvent getFirstWithContext() { |
| SequencedEvent first = getFirst(); |
| while(isOwnerAppContextDisposed(first)) { |
| first.dispose(); |
| first = getFirst(); |
| } |
| return first; |
| } |
| |
| /** |
| * Disposes of this instance. This method is invoked once the nested event |
| * has been dispatched and handled, or when the peer of the target of the |
| * nested event has been disposed with a call to Component.removeNotify. |
| * |
| * NOTE: Locking protocol. Since SunToolkit.postEvent can get EventQueue lock, |
| * it shall never be called while holding the lock on the list, |
| * as EventQueue lock is held during dispatching and dispatch() will get |
| * lock on the list. The locks should be acquired in the same order. |
| */ |
| final void dispose() { |
| synchronized (SequencedEvent.class) { |
| if (disposed) { |
| return; |
| } |
| if (KeyboardFocusManager.getCurrentKeyboardFocusManager(). |
| getCurrentSequencedEvent() == this) { |
| KeyboardFocusManager.getCurrentKeyboardFocusManager(). |
| setCurrentSequencedEvent(null); |
| } |
| disposed = true; |
| } |
| // Wake myself up |
| if (appContext != null) { |
| SunToolkit.postEvent(appContext, new SentEvent()); |
| } |
| |
| SequencedEvent next = null; |
| |
| synchronized (SequencedEvent.class) { |
| SequencedEvent.class.notifyAll(); |
| |
| if (list.getFirst() == this) { |
| list.removeFirst(); |
| |
| if (!list.isEmpty()) { |
| next = list.getFirst(); |
| } |
| } else { |
| list.remove(this); |
| } |
| } |
| // Wake up waiting threads |
| if (next != null && next.appContext != null) { |
| SunToolkit.postEvent(next.appContext, new SentEvent()); |
| } |
| } |
| } |