001: /*******************************************************************************
002: * Copyright (c) 2000, 2005 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.ui.tests.navigator;
011:
012: import org.eclipse.swt.widgets.Display;
013:
014: /**
015: * Runs the event loop of the given display until {@link #condition()} becomes
016: * <code>true</code> or no events have occurred for the supplied timeout.
017: * Between running the event loop, {@link Display#sleep()} is called.
018: * <p>
019: * There is a caveat: the given timeouts must be long enough that the calling
020: * thread can enter <code>Display.sleep()</code> before the timeout elapses,
021: * otherwise, the waiter may time out before <code>sleep</code> is called and
022: * the sleeping thread may never be waken up.
023: * </p>
024: *
025: * @since 3.1
026: */
027: public abstract class DisplayHelper {
028: /**
029: * Creates a new instance.
030: */
031: protected DisplayHelper() {
032: }
033:
034: /**
035: * Until {@link #condition()} becomes <code>true</code> or the timeout
036: * elapses, call {@link Display#sleep()} and run the event loop.
037: * <p>
038: * If <code>timeout < 0</code>, the event loop is never driven and
039: * only the condition is checked. If <code>timeout == 0</code>, the event
040: * loop is driven at most once, but <code>Display.sleep()</code> is never
041: * invoked.
042: * </p>
043: *
044: * @param display the display to run the event loop of
045: * @param timeout the timeout in milliseconds
046: * @return <code>true</code> if the condition became <code>true</code>,
047: * <code>false</code> if the timeout elapsed
048: */
049: public final boolean waitForCondition(Display display, long timeout) {
050: // if the condition already holds, succeed
051: if (condition())
052: return true;
053:
054: if (timeout < 0)
055: return false;
056:
057: // if driving the event loop once makes the condition hold, succeed
058: // without spawning a thread.
059: driveEventQueue(display);
060: if (condition())
061: return true;
062:
063: // if the timeout is negative or zero, fail
064: if (timeout == 0)
065: return false;
066:
067: // repeatedly sleep until condition becomes true or timeout elapses
068: DisplayWaiter waiter = new DisplayWaiter(display);
069: DisplayWaiter.Timeout timeoutState = waiter.start(timeout);
070: boolean condition;
071: try {
072: do {
073: if (display.sleep())
074: driveEventQueue(display);
075: condition = condition();
076: } while (!condition && !timeoutState.hasTimedOut());
077: } finally {
078: waiter.stop();
079: }
080: return condition;
081: }
082:
083: /**
084: * Call {@link Display#sleep()} and run the event loop until the given
085: * timeout has elapsed.
086: * <p>
087: * If <code>timeout < 0</code>, nothing happens. If
088: * <code>timeout == 0</code>, the event loop is driven exactly once, but
089: * <code>Display.sleep()</code> is never invoked.
090: * </p>
091: *
092: * @param display the display to run the event loop of
093: * @param millis the timeout in milliseconds
094: */
095: public static void sleep(Display display, long millis) {
096: new DisplayHelper() {
097: public boolean condition() {
098: return false;
099: }
100: }.waitForCondition(display, millis);
101: }
102:
103: /**
104: * Call {@link Display#sleep()} and run the event loop once if
105: * <code>sleep</code> returns before the timeout elapses. Returns
106: * <code>true</code> if any events were processed, <code>false</code> if
107: * not.
108: * <p>
109: * If <code>timeout < 0</code>, nothing happens and false is returned.
110: * If <code>timeout == 0</code>, the event loop is driven exactly once,
111: * but <code>Display.sleep()</code> is never invoked.
112: * </p>
113: *
114: * @param display the display to run the event loop of
115: * @param timeout the timeout in milliseconds
116: * @return <code>true</code> if any event was taken off the event queue,
117: * <code>false</code> if not
118: */
119: public static boolean runEventLoop(Display display, long timeout) {
120: if (timeout < 0)
121: return false;
122:
123: if (timeout == 0)
124: return driveEventQueue(display);
125:
126: // repeatedly sleep until condition becomes true or timeout elapses
127: DisplayWaiter waiter = new DisplayWaiter(display);
128: DisplayWaiter.Timeout timeoutState = waiter.start(timeout);
129: boolean events = false;
130: if (display.sleep() && !timeoutState.hasTimedOut()) {
131: driveEventQueue(display);
132: events = true;
133: }
134: waiter.stop();
135: return events;
136: }
137:
138: /**
139: * The condition which has to be met in order for
140: * {@link #waitForCondition(Display, int)} to return before the timeout
141: * elapses.
142: *
143: * @return <code>true</code> if the condition is met, <code>false</code>
144: * if the event loop should be driven some more
145: */
146: protected abstract boolean condition();
147:
148: /**
149: * Runs the event loop on the given display.
150: *
151: * @param display the display
152: * @return if <code>display.readAndDispatch</code> returned
153: * <code>true</code> at least once
154: */
155: private static boolean driveEventQueue(Display display) {
156: boolean events = false;
157: while (display.readAndDispatch()) {
158: events = true;
159: }
160: return events;
161: }
162:
163: /**
164: * Until {@link #condition()} becomes <code>true</code> or the timeout
165: * elapses, call {@link Display#sleep()} and run the event loop.
166: * <p>
167: * If <code>timeout < 0</code>, the event loop is never driven and
168: * only the condition is checked. If <code>timeout == 0</code>, the event
169: * loop is driven at most once, but <code>Display.sleep()</code> is never
170: * invoked.
171: * </p>
172: * <p>
173: * The condition gets rechecked every <code>interval</code> milliseconds, even
174: * if no events were read from the queue.
175: * </p>
176: *
177: * @param display the display to run the event loop of
178: * @param timeout the timeout in milliseconds
179: * @param interval the interval to re-check the condition in milliseconds
180: * @return <code>true</code> if the condition became <code>true</code>,
181: * <code>false</code> if the timeout elapsed
182: */
183: public final boolean waitForCondition(Display display,
184: long timeout, long interval) {
185: // if the condition already holds, succeed
186: if (condition())
187: return true;
188:
189: if (timeout < 0)
190: return false;
191:
192: // if driving the event loop once makes the condition hold, succeed
193: // without spawning a thread.
194: driveEventQueue(display);
195: if (condition())
196: return true;
197:
198: // if the timeout is negative or zero, fail
199: if (timeout == 0)
200: return false;
201:
202: // repeatedly sleep until condition becomes true or timeout elapses
203: DisplayWaiter waiter = new DisplayWaiter(display, true);
204: long currentTimeMillis = System.currentTimeMillis();
205: long finalTimeout = timeout + currentTimeMillis;
206: if (finalTimeout < currentTimeMillis)
207: finalTimeout = Long.MAX_VALUE;
208: boolean condition;
209: try {
210: do {
211: waiter.restart(interval);
212: if (display.sleep())
213: driveEventQueue(display);
214: condition = condition();
215: } while (!condition
216: && finalTimeout > System.currentTimeMillis());
217: } finally {
218: waiter.stop();
219: }
220: return condition;
221: }
222:
223: }
|