/*
* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
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;
}
});
}
/**
* 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 final static 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 final synchronized static
SequencedEvent getFirst() {
return (
SequencedEvent)
list.
getFirst();
}
/* Disposes all events from disposed AppContext
* return first valid event
*/
private final static
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 = (
SequencedEvent)
list.
getFirst();
}
} else {
list.
remove(this);
}
}
// Wake up waiting threads
if (
next != null &&
next.
appContext != null) {
SunToolkit.
postEvent(
next.
appContext, new
SentEvent());
}
}
}