001: /*
002: * Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package java.awt;
027:
028: import java.util.LinkedList;
029: import sun.awt.AppContext;
030: import sun.awt.SunToolkit;
031:
032: /**
033: * A mechanism for ensuring that a series of AWTEvents are executed in a
034: * precise order, even across multiple AppContexts. The nested events will be
035: * dispatched in the order in which their wrapping SequencedEvents were
036: * constructed. The only exception to this rule is if the peer of the target of
037: * the nested event was destroyed (with a call to Component.removeNotify)
038: * before the wrapping SequencedEvent was able to be dispatched. In this case,
039: * the nested event is never dispatched.
040: *
041: * @version 1.20, 06/05/07
042: * @author David Mendenhall
043: */
044: class SequencedEvent extends AWTEvent implements ActiveEvent {
045: /*
046: * serialVersionUID
047: */
048: private static final long serialVersionUID = 547742659238625067L;
049:
050: private static final int ID = java.awt.event.FocusEvent.FOCUS_LAST + 1;
051: private static final LinkedList list = new LinkedList();
052:
053: private final AWTEvent nested;
054: private AppContext appContext;
055: private boolean disposed;
056:
057: /**
058: * Constructs a new SequencedEvent which will dispatch the specified
059: * nested event.
060: *
061: * @param nested the AWTEvent which this SequencedEvent's dispatch()
062: * method will dispatch
063: */
064: public SequencedEvent(AWTEvent nested) {
065: super (nested.getSource(), ID);
066: this .nested = nested;
067: synchronized (SequencedEvent.class) {
068: list.add(this );
069: }
070: }
071:
072: /**
073: * Dispatches the nested event after all previous nested events have been
074: * dispatched or disposed. If this method is invoked before all previous nested events
075: * have been dispatched, then this method blocks until such a point is
076: * reached.
077: * While waiting disposes nested events to disposed AppContext
078: *
079: * NOTE: Locking protocol. Since dispose() can get EventQueue lock,
080: * dispatch() shall never call dispose() while holding the lock on the list,
081: * as EventQueue lock is held during dispatching. The locks should be acquired
082: * in the same order.
083: */
084: public final void dispatch() {
085: try {
086: appContext = AppContext.getAppContext();
087:
088: if (getFirst() != this ) {
089: if (EventQueue.isDispatchThread()) {
090: EventDispatchThread edt = (EventDispatchThread) Thread
091: .currentThread();
092: edt.pumpEvents(SentEvent.ID, new Conditional() {
093: public boolean evaluate() {
094: return !SequencedEvent.this
095: .isFirstOrDisposed();
096: }
097: });
098: } else {
099: while (!isFirstOrDisposed()) {
100: synchronized (SequencedEvent.class) {
101: try {
102: SequencedEvent.class.wait(1000);
103: } catch (InterruptedException e) {
104: break;
105: }
106: }
107: }
108: }
109: }
110:
111: if (!disposed) {
112: KeyboardFocusManager.getCurrentKeyboardFocusManager()
113: .setCurrentSequencedEvent(this );
114: Toolkit.getEventQueue().dispatchEvent(nested);
115: }
116: } finally {
117: dispose();
118: }
119: }
120:
121: /**
122: * true only if event exists and nested source appContext is disposed.
123: */
124: private final static boolean isOwnerAppContextDisposed(
125: 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: }
|