1 /* 2 * Copyright (c) 2000, 2014, 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.AWTAccessor; 30 import sun.awt.AppContext; 31 import sun.awt.SunToolkit; 32 33 /** 34 * A mechanism for ensuring that a series of AWTEvents are executed in a 35 * precise order, even across multiple AppContexts. The nested events will be 36 * dispatched in the order in which their wrapping SequencedEvents were 37 * constructed. The only exception to this rule is if the peer of the target of 38 * the nested event was destroyed (with a call to Component.removeNotify) 39 * before the wrapping SequencedEvent was able to be dispatched. In this case, 40 * the nested event is never dispatched. 41 * 42 * @author David Mendenhall 43 */ 44 class SequencedEvent extends AWTEvent implements ActiveEvent { 45 /* 46 * serialVersionUID 47 */ 48 private static final long serialVersionUID = 547742659238625067L; 49 50 private static final int ID = 51 java.awt.event.FocusEvent.FOCUS_LAST + 1; 52 private static final LinkedList<SequencedEvent> list = new LinkedList<>(); 53 54 private final AWTEvent nested; 55 private AppContext appContext; 56 private boolean disposed; 57 58 static { 59 AWTAccessor.setSequencedEventAccessor(new AWTAccessor.SequencedEventAccessor() { 60 public AWTEvent getNested(AWTEvent sequencedEvent) { 61 return ((SequencedEvent)sequencedEvent).nested; 62 } 63 public boolean isSequencedEvent(AWTEvent event) { 64 return event instanceof SequencedEvent; 65 } 66 67 public AWTEvent create(AWTEvent event) { 68 return new SequencedEvent(event); 69 } 70 }); 71 } 72 73 /** 74 * Constructs a new SequencedEvent which will dispatch the specified 75 * nested event. 76 * 77 * @param nested the AWTEvent which this SequencedEvent's dispatch() 78 * method will dispatch 79 */ 80 public SequencedEvent(AWTEvent nested) { 81 super(nested.getSource(), ID); 82 this.nested = nested; 83 // All AWTEvents that are wrapped in SequencedEvents are (at 84 // least currently) implicitly generated by the system 85 SunToolkit.setSystemGenerated(nested); 86 synchronized (SequencedEvent.class) { 87 list.add(this); 88 } 89 } 90 91 /** 92 * Dispatches the nested event after all previous nested events have been 93 * dispatched or disposed. If this method is invoked before all previous nested events 94 * have been dispatched, then this method blocks until such a point is 95 * reached. 96 * While waiting disposes nested events to disposed AppContext 97 * 98 * NOTE: Locking protocol. Since dispose() can get EventQueue lock, 99 * dispatch() shall never call dispose() while holding the lock on the list, 100 * as EventQueue lock is held during dispatching. The locks should be acquired 101 * in the same order. 102 */ 103 public final void dispatch() { 104 try { 105 appContext = AppContext.getAppContext(); 106 107 if (getFirst() != this) { 108 if (EventQueue.isDispatchThread()) { 109 if (Thread.currentThread() instanceof EventDispatchThread) { 110 EventDispatchThread edt = (EventDispatchThread) 111 Thread.currentThread(); 112 edt.pumpEvents(SentEvent.ID, new Conditional() { 113 public boolean evaluate() { 114 return !SequencedEvent.this.isFirstOrDisposed(); 115 } 116 }); 117 } 118 } else { 119 while(!isFirstOrDisposed()) { 120 synchronized (SequencedEvent.class) { 121 try { 122 SequencedEvent.class.wait(1000); 123 } catch (InterruptedException e) { 124 break; 125 } 126 } 127 } 128 } 129 } 130 131 if (!disposed) { 132 KeyboardFocusManager.getCurrentKeyboardFocusManager(). 133 setCurrentSequencedEvent(this); 134 Toolkit.getEventQueue().dispatchEvent(nested); 135 } 136 } finally { 137 dispose(); 138 } 139 } 140 141 /** 142 * true only if event exists and nested source appContext is disposed. 143 */ 144 private static final boolean isOwnerAppContextDisposed(SequencedEvent se) { 145 if (se != null) { 146 Object target = se.nested.getSource(); 147 if (target instanceof Component) { 148 return ((Component)target).appContext.isDisposed(); 149 } 150 } 151 return false; 152 } 153 154 /** 155 * Sequenced events are dispatched in order, so we cannot dispatch 156 * until we are the first sequenced event in the queue (i.e. it's our 157 * turn). But while we wait for our turn to dispatch, the event 158 * could have been disposed for a number of reasons. 159 */ 160 public final boolean isFirstOrDisposed() { 161 if (disposed) { 162 return true; 163 } 164 // getFirstWithContext can dispose this 165 return this == getFirstWithContext() || disposed; 166 } 167 168 private static final synchronized SequencedEvent getFirst() { 169 return list.getFirst(); 170 } 171 172 /* Disposes all events from disposed AppContext 173 * return first valid event 174 */ 175 private static final SequencedEvent getFirstWithContext() { 176 SequencedEvent first = getFirst(); 177 while(isOwnerAppContextDisposed(first)) { 178 first.dispose(); 179 first = getFirst(); 180 } 181 return first; 182 } 183 184 /** 185 * Disposes of this instance. This method is invoked once the nested event 186 * has been dispatched and handled, or when the peer of the target of the 187 * nested event has been disposed with a call to Component.removeNotify. 188 * 189 * NOTE: Locking protocol. Since SunToolkit.postEvent can get EventQueue lock, 190 * it shall never be called while holding the lock on the list, 191 * as EventQueue lock is held during dispatching and dispatch() will get 192 * lock on the list. The locks should be acquired in the same order. 193 */ 194 final void dispose() { 195 synchronized (SequencedEvent.class) { 196 if (disposed) { 197 return; 198 } 199 if (KeyboardFocusManager.getCurrentKeyboardFocusManager(). 200 getCurrentSequencedEvent() == this) { 201 KeyboardFocusManager.getCurrentKeyboardFocusManager(). 202 setCurrentSequencedEvent(null); 203 } 204 disposed = true; 205 } 206 // Wake myself up 207 if (appContext != null) { 208 SunToolkit.postEvent(appContext, new SentEvent()); 209 } 210 211 SequencedEvent next = null; 212 213 synchronized (SequencedEvent.class) { 214 SequencedEvent.class.notifyAll(); 215 216 if (list.getFirst() == this) { 217 list.removeFirst(); 218 219 if (!list.isEmpty()) { 220 next = list.getFirst(); 221 } 222 } else { 223 list.remove(this); 224 } 225 } 226 // Wake up waiting threads 227 if (next != null && next.appContext != null) { 228 SunToolkit.postEvent(next.appContext, new SentEvent()); 229 } 230 } 231 }