001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s): Alexandre Iline.
025: *
026: * The Original Software is the Jemmy library.
027: * The Initial Developer of the Original Software is Alexandre Iline.
028: * All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: *
041: *
042: *
043: * $Id$ $Revision$ $Date$
044: *
045: */
046:
047: package org.netbeans.jemmy;
048:
049: import java.awt.AWTEvent;
050: import java.awt.Toolkit;
051:
052: import java.awt.event.AWTEventListener;
053:
054: import java.lang.reflect.Field;
055: import java.lang.reflect.Modifier;
056:
057: import java.util.Vector;
058:
059: /**
060: *
061: * Provides methods to check last dispatched events,
062: * to wait for events of specific types, or to guarantee that events
063: * of specific types are not dispatched during some time frame.
064: * <BR><BR>
065: * All possible listeners are added during this class initialization
066: * in case if "jemmy.event_listening" system property is not equal to "no",
067: * so, by default, all events are listened.
068: *
069: * Uses timeouts:<BR>
070: * EventTool.WaitEventTimeout - time to wait for AWT events.<BR>
071: * EventTool.WaitNoEventTimeout - when checking for the absence of incoming AWT
072: * events.<BR>
073: * EventTool.EventCheckingDelta - time delta between checks for AWT events.
074: *
075: * @author Alexandre Iline (alexandre.iline@sun.com)
076: */
077:
078: public class EventTool implements Timeoutable, Outputable {
079:
080: private static final long WAIT_EVENT_TIMEOUT = 60000;
081: private static final long WAIT_NO_EVENT_TIMEOUT = 180000;
082: private static final long EVENT_CHECKING_DELTA = 10;
083:
084: private static ListenerSet listenerSet;
085: private static long currentEventMask = 0;
086:
087: private TestOut output;
088: private Timeouts timeouts;
089:
090: /**
091: * Constructor.
092: */
093: public EventTool() {
094: setOutput(JemmyProperties.getProperties().getOutput());
095: setTimeouts(JemmyProperties.getProperties().getTimeouts());
096: }
097:
098: /**
099: * Returns time of the last dispatched event under mask.
100: * @param eventMask Events types to be searched. <code>AWTEvent.*_EVENT_MASK</code> fields combination.
101: * @return time in milliseconds
102: * @see #addListeners(long)
103: */
104: public static long getLastEventTime(long eventMask) {
105: return (listenerSet.getLastEventTime(eventMask));
106: }
107:
108: /**
109: * Returns last dispatched event under mask.
110: * @param eventMask Events types to be searched. <code>AWTEvent.*_EVENT_MASK</code> fields combination.
111: * @return AWTEvent
112: * @see #addListeners(long)
113: */
114: public static AWTEvent getLastEvent(long eventMask) {
115: return (listenerSet.getLastEvent(eventMask));
116: }
117:
118: /**
119: * Returns time of the last dispatched event.
120: * @return time in milliseconds
121: * @see #addListeners(long)
122: */
123: public static long getLastEventTime() {
124: return (getLastEventTime(listenerSet.getTheWholeMask()));
125: }
126:
127: /**
128: * Returns last dispatched event.
129: * @return AWTEvent
130: * @see #addListeners(long)
131: */
132: public static AWTEvent getLastEvent() {
133: return (getLastEvent(listenerSet.getTheWholeMask()));
134: }
135:
136: /**
137: * Adds listeners to listen events under mask.
138: * Invokes <code>removeListeners()</code> first, so any event history is lost.
139: * @param eventMask Mask to listen events under. <code>AWTEvent.*_EVENT_MASK</code> fields combination.
140: * @see #addListeners()
141: * @see #removeListeners()
142: */
143: public static void addListeners(long eventMask) {
144: removeListeners();
145: listenerSet.addListeners(eventMask);
146: currentEventMask = eventMask;
147: }
148:
149: /**
150: * Adds listeners to listen all types of events.
151: * Invokes <code>removeListeners()</code> first, so any event history is lost.
152: * This method is invoked during static section of this class.
153: * @see #addListeners(long)
154: * @see #removeListeners()
155: * @see #getTheWholeEventMask()
156: */
157: public static void addListeners() {
158: addListeners(listenerSet.getTheWholeMask());
159: }
160:
161: /**
162: * Removes all listeners.
163: * @see #addListeners(long)
164: * @see #addListeners()
165: */
166: public static void removeListeners() {
167: listenerSet.removeListeners();
168: }
169:
170: /**
171: * Returns event mask last time used by <code>addListeners(long)</code> method.
172: * In case if <code>addListeners()</code> method was used last,
173: * <code>getTheWholeEventMask() </code> result is returned.
174: * @return a long representing the current event mask value
175: * @see #getTheWholeEventMask()
176: */
177: public static long getCurrentEventMask() {
178: return (currentEventMask);
179: }
180:
181: /**
182: * Returns a combination of all <code>AWTEvent.*_EVENT_MASK</code> fields..
183: * @return a combination of all <code>AWTEvent.*_EVENT_MASK</code> fields.
184: */
185: public static long getTheWholeEventMask() {
186: return (listenerSet.getTheWholeMask());
187: }
188:
189: static {
190: Timeouts.initDefault("EventTool.WaitEventTimeout",
191: WAIT_EVENT_TIMEOUT);
192: Timeouts.initDefault("EventTool.WaitNoEventTimeout",
193: WAIT_NO_EVENT_TIMEOUT);
194: Timeouts.initDefault("EventTool.EventCheckingDelta",
195: EVENT_CHECKING_DELTA);
196: listenerSet = new ListenerSet();
197: if (System.getProperty("jemmy.event_listening") == null
198: || !System.getProperty("jemmy.event_listening").equals(
199: "no")) {
200: listenerSet.addListeners();
201: }
202: }
203:
204: /**
205: * Defines current timeouts.
206: *
207: * @param ts ?t? A collection of timeout assignments.
208: * @see org.netbeans.jemmy.Timeouts
209: * @see org.netbeans.jemmy.Timeoutable
210: * @see #getTimeouts
211: */
212: public void setTimeouts(Timeouts ts) {
213: timeouts = ts;
214: }
215:
216: /**
217: * Return current timeouts.
218: * @return the collection of current timeout assignments.
219: * @see org.netbeans.jemmy.Timeouts
220: * @see org.netbeans.jemmy.Timeoutable
221: * @see #setTimeouts
222: */
223: public Timeouts getTimeouts() {
224: return (timeouts);
225: }
226:
227: /**
228: * Defines print output streams or writers.
229: * @param out Identify the streams or writers used for print output.
230: * @see org.netbeans.jemmy.Outputable
231: * @see org.netbeans.jemmy.TestOut
232: * @see #getOutput
233: */
234: public void setOutput(TestOut out) {
235: output = out;
236: }
237:
238: /**
239: * Returns print output streams or writers.
240: * @return an object that contains references to objects for
241: * printing to output and err streams.
242: * @see org.netbeans.jemmy.Outputable
243: * @see org.netbeans.jemmy.TestOut
244: * @see #setOutput
245: */
246: public TestOut getOutput() {
247: return (output);
248: }
249:
250: /**
251: * Waits for the first event under mask.
252: * Waits during <code>EventTool.WaitEventTimeout</code> milliseconds.
253: * @param eventMask Mask to wait events under.
254: * <code>AWTEvent.*_EVENT_MASK</code> fields combination.
255: * @return an AWTEvent object
256: * @see #waitEvent()
257: * @throws TimeoutExpiredException
258: */
259: public AWTEvent waitEvent(long eventMask) {
260: return (waitEvent(eventMask, timeouts
261: .getTimeout("EventTool.WaitEventTimeout"), output
262: .createErrorOutput()));
263: }
264:
265: /**
266: * Waits for the first event.
267: * Waits during <code>EventTool.WaitEventTimeout</code> milliseconds.
268: * @return an AWTEvent object
269: * @see #waitEvent(long)
270: * @see #getTheWholeEventMask()
271: * @throws TimeoutExpiredException
272: */
273: public AWTEvent waitEvent() {
274: return (waitEvent(listenerSet.getTheWholeMask()));
275: }
276:
277: /**
278: * Check that no event under mask will be dispatched
279: * during time specified.
280: * @param eventMask Mask to wait events under.
281: * <code>AWTEvent.*_EVENT_MASK</code> fields combination.
282: * @param waitTime Quiet time (millisecons).
283: * @return true if no event ahs found.
284: * @see #checkNoEvent(long)
285: */
286: public boolean checkNoEvent(long eventMask, long waitTime) {
287: return (checkNoEvent(eventMask, waitTime, output));
288: }
289:
290: /**
291: * Check that no event will be dispatched during time specified.
292: * @param waitTime Quiet time (millisecons).
293: * @return true if no event ahs found.
294: * @see #checkNoEvent(long, long)
295: * @see #getTheWholeEventMask()
296: */
297: public boolean checkNoEvent(long waitTime) {
298: return (checkNoEvent(listenerSet.getTheWholeMask(), waitTime));
299: }
300:
301: /**
302: * During <code>EventTool.WaitNoEventTimeout</code> time waits for
303: * true result of checkNoEvent(long, long) method.
304: * @param eventMask Mask to wait events under.
305: * <code>AWTEvent.*_EVENT_MASK</code> fields combination.
306: * @param waitTime Quiet time (millisecons).
307: * @see #checkNoEvent(long, long)
308: * @see #waitNoEvent(long)
309: * @throws TimeoutExpiredException
310: */
311: public void waitNoEvent(long eventMask, long waitTime) {
312: NoEventWaiter waiter = new NoEventWaiter(eventMask, waitTime);
313: waiter.setTimeouts(timeouts.cloneThis());
314: waiter.getTimeouts().setTimeout("Waiter.WaitingTime",
315: timeouts.getTimeout("EventTool.WaitNoEventTimeout"));
316: waiter.getTimeouts().setTimeout("Waiter.TimeDelta",
317: timeouts.getTimeout("EventTool.EventCheckingDelta"));
318: try {
319: waiter.waitAction(null);
320: } catch (InterruptedException e) {
321: output.printStackTrace(e);
322: }
323: }
324:
325: /**
326: * During <code>EventTool.WaitNoEventTimeout</code> time waits for
327: * true result of <code>checkNoEvent(long)</code> method.
328: * @param waitTime Quiet time (millisecons).
329: * @see #checkNoEvent(long)
330: * @see #waitNoEvent(long, long)
331: * @throws TimeoutExpiredException
332: */
333: public void waitNoEvent(long waitTime) {
334: waitNoEvent(listenerSet.getTheWholeMask(), waitTime);
335: }
336:
337: private AWTEvent waitEvent(long eventMask, long waitTime,
338: TestOut waiterOutput) {
339: EventWaiter waiter = new EventWaiter(eventMask);
340: waiter.setTimeouts(timeouts.cloneThis());
341: waiter.setOutput(waiterOutput);
342: waiter.getTimeouts().setTimeout("Waiter.WaitingTime", waitTime);
343: waiter.getTimeouts().setTimeout("Waiter.TimeDelta",
344: timeouts.getTimeout("EventTool.EventCheckingDelta"));
345: try {
346: return ((AWTEvent) waiter.waitAction(null));
347: } catch (InterruptedException e) {
348: output.printStackTrace(e);
349: return (null);
350: }
351: }
352:
353: private boolean checkNoEvent(long eventMask, long waitTime,
354: TestOut waiterOutput) {
355: try {
356: AWTEvent event = waitEvent(eventMask, waitTime, TestOut
357: .getNullOutput());
358: waiterOutput
359: .printLine("AWT event was produced during waiting: ");
360: // used instead of event.toString() because it is not thread safe
361: waiterOutput.printLine(event.getClass().getName());
362: return (false);
363: } catch (TimeoutExpiredException e) {
364: return (true);
365: }
366: }
367:
368: private static class EventType implements AWTEventListener {
369: long eventMask;
370: long eventTime;
371: private AWTEvent event;
372:
373: public EventType(long eventMask) {
374: this .eventMask = eventMask;
375: event = null;
376: eventTime = -1;
377: }
378:
379: public void eventDispatched(AWTEvent event) {
380: this .event = event;
381: eventTime = System.currentTimeMillis();
382: }
383:
384: public AWTEvent getEvent() {
385: return (event);
386: }
387:
388: public long getTime() {
389: return (eventTime);
390: }
391:
392: public long getEventMask() {
393: return (eventMask);
394: }
395: }
396:
397: private static class ListenerSet {
398: private Vector eventTypes;
399: private long theWholeMask;
400:
401: public ListenerSet() {
402: eventTypes = new Vector();
403: try {
404: Class eventClass = Class.forName("java.awt.AWTEvent");
405: Field[] fields = eventClass.getFields();
406: theWholeMask = 0;
407: long eventMask;
408: for (int i = 0; i < fields.length; i++) {
409: if ((fields[i].getModifiers() & (Modifier.PUBLIC | Modifier.STATIC)) != 0
410: && fields[i].getType().equals(Long.TYPE)
411: && fields[i].getName().endsWith(
412: "_EVENT_MASK")) {
413: eventMask = ((Long) fields[i].get(null))
414: .longValue();
415: eventTypes.add(new EventType(eventMask));
416: theWholeMask = theWholeMask | eventMask;
417: }
418: }
419: } catch (ClassNotFoundException e) {
420: JemmyProperties.getCurrentOutput().printStackTrace(e);
421: } catch (IllegalAccessException e) {
422: JemmyProperties.getCurrentOutput().printStackTrace(e);
423: }
424: }
425:
426: public void addListeners(long eventMask) {
427: Toolkit dtk = Toolkit.getDefaultToolkit();
428: for (int i = 0; i < eventTypes.size(); i++) {
429: EventType et = (EventType) eventTypes.get(i);
430: if ((et.getEventMask() & eventMask) != 0) {
431: dtk.addAWTEventListener(et, et.getEventMask());
432: }
433: }
434: }
435:
436: public void addListeners() {
437: addListeners(getTheWholeMask());
438: }
439:
440: public void removeListeners() {
441: Toolkit dtk = Toolkit.getDefaultToolkit();
442: for (int i = 0; i < eventTypes.size(); i++) {
443: dtk.removeAWTEventListener((EventType) eventTypes
444: .get(i));
445: }
446: }
447:
448: public long getTheWholeMask() {
449: return (theWholeMask);
450: }
451:
452: public long getLastEventTime(long eventMask) {
453: EventType et = getLastEventType(eventMask);
454: return ((et == null) ? -1 : et.getTime());
455: }
456:
457: public AWTEvent getLastEvent(long eventMask) {
458: EventType et = getLastEventType(eventMask);
459: return ((et == null) ? null : et.getEvent());
460: }
461:
462: private EventType getLastEventType(long eventMask) {
463: long maxTime = -1;
464: EventType maxType = null;
465: for (int i = 0; i < eventTypes.size(); i++) {
466: EventType et = (EventType) eventTypes.get(i);
467: if ((eventMask & et.getEventMask()) != 0
468: && et.getTime() > maxTime) {
469: maxType = et;
470: maxTime = maxType.getTime();
471: }
472: }
473: return (maxType);
474: }
475: }
476:
477: private class EventWaiter extends Waiter {
478: long eventMask;
479: long startTime;
480:
481: public EventWaiter(long eventMask) {
482: this .eventMask = eventMask;
483: startTime = getLastEventTime(eventMask);
484: }
485:
486: public Object actionProduced(Object obj) {
487: EventType et = listenerSet.getLastEventType(eventMask);
488: if (et != null && et.getTime() > startTime) {
489: return (et.getEvent());
490: } else {
491: return (null);
492: }
493: }
494:
495: public String getDescription() {
496: return ("Last event under " + Long.toString(eventMask, 2) + " event mask");
497: }
498: }
499:
500: private class NoEventWaiter extends Waiter {
501: long eventMask;
502: long waitTime;
503:
504: public NoEventWaiter(long eventMask, long waitTime) {
505: this .eventMask = eventMask;
506: this .waitTime = waitTime;
507: }
508:
509: public Object actionProduced(Object obj) {
510: return (checkNoEvent(eventMask, waitTime, TestOut
511: .getNullOutput()) ? "Reached!" : null);
512: }
513:
514: public String getDescription() {
515: return ("No event under " + Long.toString(eventMask, 2)
516: + " event mask during " + Long.toString(waitTime) + " milliseconds");
517: }
518: }
519: }
|