1. /*
  2. * @(#)SequencedEvent.java 1.10 03/12/19
  3. *
  4. * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package java.awt;
  8. import java.awt.AWTEvent;
  9. import java.awt.ActiveEvent;
  10. import java.util.LinkedList;
  11. import sun.awt.AppContext;
  12. import sun.awt.SunToolkit;
  13. /**
  14. * A mechanism for ensuring that a series of AWTEvents are executed in a
  15. * precise order, even across multiple AppContexts. The nested events will be
  16. * dispatched in the order in which their wrapping SequencedEvents were
  17. * constructed. The only exception to this rule is if the peer of the target of
  18. * the nested event was destroyed (with a call to Component.removeNotify)
  19. * before the wrapping SequencedEvent was able to be dispatched. In this case,
  20. * the nested event is never dispatched.
  21. *
  22. * @version 1.10, 12/19/03
  23. * @author David Mendenhall
  24. */
  25. class SequencedEvent extends AWTEvent implements ActiveEvent {
  26. private static final int ID =
  27. java.awt.event.FocusEvent.FOCUS_LAST + 1;
  28. private static final LinkedList list = new LinkedList();
  29. private final AWTEvent nested;
  30. private AppContext appContext;
  31. private boolean disposed;
  32. /**
  33. * Constructs a new SequencedEvent which will dispatch the specified
  34. * nested event.
  35. *
  36. * @param nested the AWTEvent which this SequencedEvent's dispatch()
  37. * method will dispatch
  38. */
  39. public SequencedEvent(AWTEvent nested) {
  40. super(nested.getSource(), ID);
  41. this.nested = nested;
  42. synchronized (SequencedEvent.class) {
  43. list.add(this);
  44. }
  45. }
  46. /**
  47. * Dispatches the nested event after all previous nested events have been
  48. * dispatched or disposed. If this method is invoked before all previous nested events
  49. * have been dispatched, then this method blocks until such a point is
  50. * reached.
  51. * While waiting disposes nested events to disposed AppContext
  52. *
  53. * NOTE: Locking protocol. Since dispose() can get EventQueue lock,
  54. * dispatch() shall never call dispose() while holding the lock on the list,
  55. * as EventQueue lock is held during dispatching. The locks should be acquired
  56. * in the same order.
  57. */
  58. public final void dispatch() {
  59. try {
  60. appContext = AppContext.getAppContext();
  61. if (getFirst() != this) {
  62. if (EventQueue.isDispatchThread()) {
  63. EventDispatchThread edt = (EventDispatchThread)
  64. Thread.currentThread();
  65. edt.pumpEvents(SentEvent.ID, new Conditional() {
  66. public boolean evaluate() {
  67. return !SequencedEvent.this.isFirstOrDisposed();
  68. }
  69. });
  70. } else {
  71. while(!isFirstOrDisposed()) {
  72. synchronized (SequencedEvent.class) {
  73. try {
  74. SequencedEvent.class.wait(1000);
  75. } catch (InterruptedException e) {
  76. break;
  77. }
  78. }
  79. }
  80. }
  81. }
  82. if (!disposed) {
  83. KeyboardFocusManager.getCurrentKeyboardFocusManager().
  84. setCurrentSequencedEvent(this);
  85. Toolkit.getEventQueue().dispatchEvent(nested);
  86. }
  87. } finally {
  88. dispose();
  89. }
  90. }
  91. /**
  92. * true only if event exists and nested source appContext is disposed.
  93. */
  94. private final static boolean isOwnerAppContextDisposed(SequencedEvent se) {
  95. if (se != null) {
  96. Object target = se.nested.getSource();
  97. if (target instanceof Component) {
  98. return ((Component)target).appContext.isDisposed();
  99. }
  100. }
  101. return false;
  102. }
  103. /**
  104. * Sequenced events are dispatched in order, so we cannot dispatch
  105. * until we are the first sequenced event in the queue (i.e. it's our
  106. * turn). But while we wait for our turn to dispatch, the event
  107. * could have been disposed for a number of reasons.
  108. */
  109. public final boolean isFirstOrDisposed() {
  110. if (disposed) {
  111. return true;
  112. }
  113. // getFirstWithContext can dispose this
  114. return this == getFirstWithContext() || disposed;
  115. }
  116. private final synchronized static SequencedEvent getFirst() {
  117. return (SequencedEvent)list.getFirst();
  118. }
  119. /* Disposes all events from disposed AppContext
  120. * return first valid event
  121. */
  122. private final static SequencedEvent getFirstWithContext() {
  123. SequencedEvent first = getFirst();
  124. while(isOwnerAppContextDisposed(first)) {
  125. first.dispose();
  126. first = getFirst();
  127. }
  128. return first;
  129. }
  130. /**
  131. * Disposes of this instance. This method is invoked once the nested event
  132. * has been dispatched and handled, or when the peer of the target of the
  133. * nested event has been disposed with a call to Component.removeNotify.
  134. *
  135. * NOTE: Locking protocol. Since SunToolkit.postEvent can get EventQueue lock,
  136. * it shall never be called while holding the lock on the list,
  137. * as EventQueue lock is held during dispatching and dispatch() will get
  138. * lock on the list. The locks should be acquired in the same order.
  139. */
  140. final void dispose() {
  141. synchronized (SequencedEvent.class) {
  142. if (disposed) {
  143. return;
  144. }
  145. if (KeyboardFocusManager.getCurrentKeyboardFocusManager().
  146. getCurrentSequencedEvent() == this) {
  147. KeyboardFocusManager.getCurrentKeyboardFocusManager().
  148. setCurrentSequencedEvent(null);
  149. }
  150. disposed = true;
  151. }
  152. // Wake myself up
  153. if (appContext != null) {
  154. SunToolkit.postEvent(appContext, new SentEvent());
  155. }
  156. SequencedEvent next = null;
  157. synchronized (SequencedEvent.class) {
  158. SequencedEvent.class.notifyAll();
  159. if (list.getFirst() == this) {
  160. list.removeFirst();
  161. if (!list.isEmpty()) {
  162. next = (SequencedEvent)list.getFirst();
  163. }
  164. } else {
  165. list.remove(this);
  166. }
  167. }
  168. // Wake up waiting threads
  169. if (next != null && next.appContext != null) {
  170. SunToolkit.postEvent(next.appContext, new SentEvent());
  171. }
  172. }
  173. }