001: /*
002: * @(#)SequencedEvent.java 1.7 03/01/23
003: *
004: * Copyright 1990-2006 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: */
026:
027: package java.awt;
028:
029: import java.awt.AWTEvent;
030: import java.awt.event.WindowEvent;
031: import java.awt.ActiveEvent;
032: import java.util.LinkedList;
033: import sun.awt.AppContext;
034: import sun.awt.SunToolkit;
035:
036: /**
037: * A mechanism for ensuring that a series of AWTEvents are executed in a
038: * precise order, even across multiple AppContexts. The nested events will be
039: * dispatched in the order in which their wrapping SequencedEvents were
040: * constructed. The only exception to this rule is if the peer of the target of
041: * the nested event was destroyed (with a call to Component.removeNotify)
042: * before the wrapping SequencedEvent was able to be dispatched. In this case,
043: * the nested event is never dispatched.
044: *
045: * @version 1.7, 01/23/03
046: * @author David Mendenhall
047: */
048: class SequencedEvent extends AWTEvent implements ActiveEvent {
049: private static final int ID = java.awt.event.FocusEvent.FOCUS_LAST + 1;
050: private static final LinkedList list = new LinkedList();
051:
052: private final AWTEvent nested;
053: private AppContext appContext;
054: private boolean disposed;
055:
056: /**
057: * Constructs a new SequencedEvent which will dispatch the specified
058: * nested event.
059: *
060: * @param nested the AWTEvent which this SequencedEvent's dispatch()
061: * method will dispatch
062: */
063: SequencedEvent(AWTEvent nested) {
064: super (nested.getSource(), ID);
065: this .nested = nested;
066: synchronized (SequencedEvent.class) {
067: list.add(this );
068: }
069: }
070:
071: /**
072: * Dispatches the nested event after all previous nested events have been
073: * dispatched or disposed. If this method is invoked before all previous nested events
074: * have been dispatched, then this method blocks until such a point is
075: * reached.
076: * While waiting disposes nested events to disposed AppContext
077: *
078: * NOTE: Locking protocol. Since dispose() can get EventQueue lock,
079: * dispatch() shall never call dispose() while holding the lock on the list,
080: * as EventQueue lock is held during dispatching. The locks should be acquired
081: * in the same order.
082: */
083: public final void dispatch() {
084: try {
085: appContext = AppContext.getAppContext();
086: if (getFirst() != this ) {
087:
088: if (EventQueue.isDispatchThread()) {
089: EventDispatchThread edt = (EventDispatchThread) Thread
090: .currentThread();
091: edt.pumpEvents(SentEvent.ID, new Conditional() {
092: public boolean evaluate() {
093: return !SequencedEvent.this
094: .isFirstOrDisposed();
095: }
096: });
097: } else {
098: while (!isFirstOrDisposed()) {
099: synchronized (SequencedEvent.class) {
100: try {
101: SequencedEvent.class.wait(1000);
102: } catch (InterruptedException e) {
103: break;
104: }
105: }
106: }
107: }
108: }
109:
110: if (!disposed) {
111: KeyboardFocusManager.getCurrentKeyboardFocusManager()
112: .setCurrentSequencedEvent(this );
113: Toolkit.getEventQueue().dispatchEvent(nested);
114: }
115: } finally {
116: dispose();
117: }
118: }
119:
120: /**
121: * true only if event exists and nested source appContext is disposed.
122: */
123: private final static boolean isOwnerAppContextDisposed(
124: SequencedEvent se) {
125: /*
126: if (se != null) {
127: Object target = se.nested.getSource();
128: if (target instanceof Component) {
129: return ((Component)target).appContext.isDisposed();
130: }
131: }
132: */
133: return false;
134: }
135:
136: /**
137: * Sequenced events are dispatched in order, so we cannot dispatch
138: * until we are the first sequenced event in the queue (i.e. it's our
139: * turn). But while we wait for our turn to dispatch, the event
140: * could have been disposed for a number of reasons.
141: */
142: public final boolean isFirstOrDisposed() {
143: if (disposed) {
144: return true;
145: }
146: // getFirstWithContext can dispose this
147: return this == getFirstWithContext() || disposed;
148: }
149:
150: private final synchronized static SequencedEvent getFirst() {
151: AWTEvent ev = (AWTEvent) list.getFirst();
152: return (SequencedEvent) list.getFirst();
153: }
154:
155: /* Disposes all events from disposed AppContext
156: * return first valid event
157: */
158: private final static SequencedEvent getFirstWithContext() {
159: SequencedEvent first = getFirst();
160: while (isOwnerAppContextDisposed(first)) {
161: first.dispose();
162: first = getFirst();
163: }
164: return first;
165: }
166:
167: /**
168: * Disposes of this instance. This method is invoked once the nested event
169: * has been dispatched and handled, or when the peer of the target of the
170: * nested event has been disposed with a call to Component.removeNotify.
171: *
172: * NOTE: Locking protocol. Since SunToolkit.postEvent can get EventQueue lock,
173: * it shall never be called while holding the lock on the list,
174: * as EventQueue lock is held during dispatching and dispatch() will get
175: * lock on the list. The locks should be acquired in the same order.
176: */
177: final void dispose() {
178: synchronized (SequencedEvent.class) {
179: if (disposed) {
180: return;
181: }
182: if (KeyboardFocusManager.getCurrentKeyboardFocusManager()
183: .getCurrentSequencedEvent() == this ) {
184: KeyboardFocusManager.getCurrentKeyboardFocusManager()
185: .setCurrentSequencedEvent(null);
186: }
187: disposed = true;
188: }
189: // Wake myself up
190: if (appContext != null) {
191: SunToolkit.postEvent(appContext, new SentEvent());
192: }
193:
194: SequencedEvent next = null;
195:
196: synchronized (SequencedEvent.class) {
197: SequencedEvent.class.notifyAll();
198:
199: if (list.getFirst() == this ) {
200: list.removeFirst();
201:
202: if (!list.isEmpty()) {
203: next = (SequencedEvent) list.getFirst();
204: }
205: } else {
206: list.remove(this );
207: }
208: }
209: // Wake up waiting threads
210: if (next != null && next.appContext != null) {
211: SunToolkit.postEvent(next.appContext, new SentEvent());
212: }
213: }
214: }
|