1 /* 2 * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package java.awt; 27 28 import java.util.LinkedList; 29 import sun.awt.AppContext; 30 import sun.awt.SunToolkit; 31 32 /** 33 * A mechanism for ensuring that a series of AWTEvents are executed in a 34 * precise order, even across multiple AppContexts. The nested events will be 35 * dispatched in the order in which their wrapping SequencedEvents were 36 * constructed. The only exception to this rule is if the peer of the target of 37 * the nested event was destroyed (with a call to Component.removeNotify) 38 * before the wrapping SequencedEvent was able to be dispatched. In this case, 39 * the nested event is never dispatched. 40 * 41 * @author David Mendenhall 42 */ 43 class SequencedEvent extends AWTEvent implements ActiveEvent { 44 /* 45 * serialVersionUID 46 */ 47 private static final long serialVersionUID = 547742659238625067L; 48 49 private static final int ID = 50 java.awt.event.FocusEvent.FOCUS_LAST + 1; 51 private static final LinkedList list = new LinkedList(); 52 53 private final AWTEvent nested; 54 private AppContext appContext; 55 private boolean disposed; 56 57 /** 58 * Constructs a new SequencedEvent which will dispatch the specified 59 * nested event. 60 * 61 * @param nested the AWTEvent which this SequencedEvent's dispatch() 62 * method will dispatch 63 */ 64 public SequencedEvent(AWTEvent nested) { 65 super(nested.getSource(), ID); 66 this.nested = nested; 67 // All AWTEvents that are wrapped in SequencedEvents are (at 68 // least currently) implicitly generated by the system 69 SunToolkit.setSystemGenerated(nested); 70 synchronized (SequencedEvent.class) { 71 list.add(this); 72 } 73 } 74 75 /** 76 * Dispatches the nested event after all previous nested events have been 77 * dispatched or disposed. If this method is invoked before all previous nested events 78 * have been dispatched, then this method blocks until such a point is 79 * reached. 80 * While waiting disposes nested events to disposed AppContext 81 * 82 * NOTE: Locking protocol. Since dispose() can get EventQueue lock, 83 * dispatch() shall never call dispose() while holding the lock on the list, 84 * as EventQueue lock is held during dispatching. The locks should be acquired 85 * in the same order. 86 */ 87 public final void dispatch() { 88 try { 89 appContext = AppContext.getAppContext(); 90 91 if (getFirst() != this) { 92 if (EventQueue.isDispatchThread()) { 93 EventDispatchThread edt = (EventDispatchThread) 94 Thread.currentThread(); 95 edt.pumpEvents(SentEvent.ID, new Conditional() { 96 public boolean evaluate() { 97 return !SequencedEvent.this.isFirstOrDisposed(); 98 } 99 }); 100 } else { 101 while(!isFirstOrDisposed()) { 102 synchronized (SequencedEvent.class) { 103 try { 104 SequencedEvent.class.wait(1000); 105 } catch (InterruptedException e) { 106 break; 107 } 108 } 109 } 110 } 111 } 112 113 if (!disposed) { 114 KeyboardFocusManager.getCurrentKeyboardFocusManager(). 115 setCurrentSequencedEvent(this); 116 Toolkit.getEventQueue().dispatchEvent(nested); 117 } 118 } finally { 119 dispose(); 120 } 121 } 122 123 /** 124 * true only if event exists and nested source appContext is disposed. 125 */ 126 private final static boolean isOwnerAppContextDisposed(SequencedEvent se) { 127 if (se != null) { 128 Object target = se.nested.getSource(); 129 if (target instanceof Component) { 130 return ((Component)target).appContext.isDisposed(); 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 return (SequencedEvent)list.getFirst(); 152 } 153 154 /* Disposes all events from disposed AppContext 155 * return first valid event 156 */ 157 private final static SequencedEvent getFirstWithContext() { 158 SequencedEvent first = getFirst(); 159 while(isOwnerAppContextDisposed(first)) { 160 first.dispose(); 161 first = getFirst(); 162 } 163 return first; 164 } 165 166 /** 167 * Disposes of this instance. This method is invoked once the nested event 168 * has been dispatched and handled, or when the peer of the target of the 169 * nested event has been disposed with a call to Component.removeNotify. 170 * 171 * NOTE: Locking protocol. Since SunToolkit.postEvent can get EventQueue lock, 172 * it shall never be called while holding the lock on the list, 173 * as EventQueue lock is held during dispatching and dispatch() will get 174 * lock on the list. The locks should be acquired in the same order. 175 */ 176 final void dispose() { 177 synchronized (SequencedEvent.class) { 178 if (disposed) { 179 return; 180 } 181 if (KeyboardFocusManager.getCurrentKeyboardFocusManager(). 182 getCurrentSequencedEvent() == this) { 183 KeyboardFocusManager.getCurrentKeyboardFocusManager(). 184 setCurrentSequencedEvent(null); 185 } 186 disposed = true; 187 } 188 // Wake myself up 189 if (appContext != null) { 190 SunToolkit.postEvent(appContext, new SentEvent()); 191 } 192 193 SequencedEvent next = null; 194 195 synchronized (SequencedEvent.class) { 196 SequencedEvent.class.notifyAll(); 197 198 if (list.getFirst() == this) { 199 list.removeFirst(); 200 201 if (!list.isEmpty()) { 202 next = (SequencedEvent)list.getFirst(); 203 } 204 } else { 205 list.remove(this); 206 } 207 } 208 // Wake up waiting threads 209 if (next != null && next.appContext != null) { 210 SunToolkit.postEvent(next.appContext, new SentEvent()); 211 } 212 } 213 }