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 }
|