/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common
* Development and Distribution License("CDDL") (collectively, the
* "License"). You may not use this file except in compliance with the
* License. You can obtain a copy of the License at
* http://www.netbeans.org/cddl-gplv2.html
* or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
* specific language governing permissions and limitations under the
* License. When distributing the software, include this License Header
* Notice in each file and include the License file at
* nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the GPL Version 2 section of the License file that
* accompanied this code. If applicable, add the following below the
* License Header, with the fields enclosed by brackets [] replaced by
* your own identifying information:
* "Portions Copyrighted [year] [name of copyright owner]"
*
* Contributor(s): Alexandre Iline.
*
* The Original Software is the Jemmy library.
* The Initial Developer of the Original Software is Alexandre Iline.
* All Rights Reserved.
*
* If you wish your version of this file to be governed by only the CDDL
* or only the GPL Version 2, indicate your decision by adding
* "[Contributor] elects to include this software in this distribution
* under the [CDDL or GPL Version 2] license." If you do not indicate a
* single choice of license, a recipient has the option to distribute
* your version of this file under either the CDDL, the GPL Version 2 or
* to extend the choice of license to its licensees as provided above.
* However, if you add GPL Version 2 code and therefore, elected the GPL
* Version 2 license, then the option applies only if the new code is
* made subject to such option by the copyright holder.
*
*
*
* $Id$ $Revision$ $Date$
*
*/
package org.netbeans.jemmy;
import java.awt.AWTEvent;
import java.awt.Toolkit;
import java.awt.event.AWTEventListener;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Vector;
/**
*
* Provides methods to check last dispatched events,
* to wait for events of specific types, or to guarantee that events
* of specific types are not dispatched during some time frame.
* <BR><BR>
* All possible listeners are added during this class initialization
* in case if "jemmy.event_listening" system property is not equal to "no",
* so, by default, all events are listened.
*
* Uses timeouts:<BR>
* EventTool.WaitEventTimeout - time to wait for AWT events.<BR>
* EventTool.WaitNoEventTimeout - when checking for the absence of incoming AWT
* events.<BR>
* EventTool.EventCheckingDelta - time delta between checks for AWT events.
*
* @author Alexandre Iline (alexandre.iline@sun.com)
*/
public class EventTool implements Timeoutable, Outputable {
private static final long WAIT_EVENT_TIMEOUT = 60000;
private static final long WAIT_NO_EVENT_TIMEOUT = 180000;
private static final long EVENT_CHECKING_DELTA = 10;
private static ListenerSet listenerSet;
private static long currentEventMask = 0;
private TestOut output;
private Timeouts timeouts;
/**
* Constructor.
*/
public EventTool() {
setOutput(JemmyProperties.getProperties().getOutput());
setTimeouts(JemmyProperties.getProperties().getTimeouts());
}
/**
* Returns time of the last dispatched event under mask.
* @param eventMask Events types to be searched. <code>AWTEvent.*_EVENT_MASK</code> fields combination.
* @return time in milliseconds
* @see #addListeners(long)
*/
public static long getLastEventTime(long eventMask) {
return(listenerSet.getLastEventTime(eventMask));
}
/**
* Returns last dispatched event under mask.
* @param eventMask Events types to be searched. <code>AWTEvent.*_EVENT_MASK</code> fields combination.
* @return AWTEvent
* @see #addListeners(long)
*/
public static AWTEvent getLastEvent(long eventMask) {
return(listenerSet.getLastEvent(eventMask));
}
/**
* Returns time of the last dispatched event.
* @return time in milliseconds
* @see #addListeners(long)
*/
public static long getLastEventTime() {
return(getLastEventTime(listenerSet.getTheWholeMask()));
}
/**
* Returns last dispatched event.
* @return AWTEvent
* @see #addListeners(long)
*/
public static AWTEvent getLastEvent() {
return(getLastEvent(listenerSet.getTheWholeMask()));
}
/**
* Adds listeners to listen events under mask.
* Invokes <code>removeListeners()</code> first, so any event history is lost.
* @param eventMask Mask to listen events under. <code>AWTEvent.*_EVENT_MASK</code> fields combination.
* @see #addListeners()
* @see #removeListeners()
*/
public static void addListeners(long eventMask) {
removeListeners();
listenerSet.addListeners(eventMask);
currentEventMask = eventMask;
}
/**
* Adds listeners to listen all types of events.
* Invokes <code>removeListeners()</code> first, so any event history is lost.
* This method is invoked during static section of this class.
* @see #addListeners(long)
* @see #removeListeners()
* @see #getTheWholeEventMask()
*/
public static void addListeners() {
addListeners(listenerSet.getTheWholeMask());
}
/**
* Removes all listeners.
* @see #addListeners(long)
* @see #addListeners()
*/
public static void removeListeners() {
listenerSet.removeListeners();
}
/**
* Returns event mask last time used by <code>addListeners(long)</code> method.
* In case if <code>addListeners()</code> method was used last,
* <code>getTheWholeEventMask() </code> result is returned.
* @return a long representing the current event mask value
* @see #getTheWholeEventMask()
*/
public static long getCurrentEventMask() {
return(currentEventMask);
}
/**
* Returns a combination of all <code>AWTEvent.*_EVENT_MASK</code> fields..
* @return a combination of all <code>AWTEvent.*_EVENT_MASK</code> fields.
*/
public static long getTheWholeEventMask() {
return(listenerSet.getTheWholeMask());
}
static {
Timeouts.initDefault("EventTool.WaitEventTimeout", WAIT_EVENT_TIMEOUT);
Timeouts.initDefault("EventTool.WaitNoEventTimeout", WAIT_NO_EVENT_TIMEOUT);
Timeouts.initDefault("EventTool.EventCheckingDelta", EVENT_CHECKING_DELTA);
listenerSet = new ListenerSet();
if(System.getProperty("jemmy.event_listening") == null ||
!System.getProperty("jemmy.event_listening").equals("no")) {
listenerSet.addListeners();
}
}
/**
* Defines current timeouts.
*
* @param ts ?t? A collection of timeout assignments.
* @see org.netbeans.jemmy.Timeouts
* @see org.netbeans.jemmy.Timeoutable
* @see #getTimeouts
*/
public void setTimeouts(Timeouts ts) {
timeouts = ts;
}
/**
* Return current timeouts.
* @return the collection of current timeout assignments.
* @see org.netbeans.jemmy.Timeouts
* @see org.netbeans.jemmy.Timeoutable
* @see #setTimeouts
*/
public Timeouts getTimeouts() {
return(timeouts);
}
/**
* Defines print output streams or writers.
* @param out Identify the streams or writers used for print output.
* @see org.netbeans.jemmy.Outputable
* @see org.netbeans.jemmy.TestOut
* @see #getOutput
*/
public void setOutput(TestOut out) {
output = out;
}
/**
* Returns print output streams or writers.
* @return an object that contains references to objects for
* printing to output and err streams.
* @see org.netbeans.jemmy.Outputable
* @see org.netbeans.jemmy.TestOut
* @see #setOutput
*/
public TestOut getOutput() {
return(output);
}
/**
* Waits for the first event under mask.
* Waits during <code>EventTool.WaitEventTimeout</code> milliseconds.
* @param eventMask Mask to wait events under.
* <code>AWTEvent.*_EVENT_MASK</code> fields combination.
* @return an AWTEvent object
* @see #waitEvent()
* @throws TimeoutExpiredException
*/
public AWTEvent waitEvent(long eventMask) {
return(waitEvent(eventMask,
timeouts.getTimeout("EventTool.WaitEventTimeout"),
output.createErrorOutput()));
}
/**
* Waits for the first event.
* Waits during <code>EventTool.WaitEventTimeout</code> milliseconds.
* @return an AWTEvent object
* @see #waitEvent(long)
* @see #getTheWholeEventMask()
* @throws TimeoutExpiredException
*/
public AWTEvent waitEvent() {
return(waitEvent(listenerSet.getTheWholeMask()));
}
/**
* Check that no event under mask will be dispatched
* during time specified.
* @param eventMask Mask to wait events under.
* <code>AWTEvent.*_EVENT_MASK</code> fields combination.
* @param waitTime Quiet time (millisecons).
* @return true if no event ahs found.
* @see #checkNoEvent(long)
*/
public boolean checkNoEvent(long eventMask, long waitTime) {
return(checkNoEvent(eventMask, waitTime, output));
}
/**
* Check that no event will be dispatched during time specified.
* @param waitTime Quiet time (millisecons).
* @return true if no event ahs found.
* @see #checkNoEvent(long, long)
* @see #getTheWholeEventMask()
*/
public boolean checkNoEvent(long waitTime) {
return(checkNoEvent(listenerSet.getTheWholeMask(), waitTime));
}
/**
* During <code>EventTool.WaitNoEventTimeout</code> time waits for
* true result of checkNoEvent(long, long) method.
* @param eventMask Mask to wait events under.
* <code>AWTEvent.*_EVENT_MASK</code> fields combination.
* @param waitTime Quiet time (millisecons).
* @see #checkNoEvent(long, long)
* @see #waitNoEvent(long)
* @throws TimeoutExpiredException
*/
public void waitNoEvent(long eventMask, long waitTime) {
NoEventWaiter waiter = new NoEventWaiter(eventMask, waitTime);
waiter.setTimeouts(timeouts.cloneThis());
waiter.getTimeouts().
setTimeout("Waiter.WaitingTime",
timeouts.getTimeout("EventTool.WaitNoEventTimeout"));
waiter.getTimeouts().
setTimeout("Waiter.TimeDelta",
timeouts.getTimeout("EventTool.EventCheckingDelta"));
try {
waiter.waitAction(null);
} catch(InterruptedException e) {
output.printStackTrace(e);
}
}
/**
* During <code>EventTool.WaitNoEventTimeout</code> time waits for
* true result of <code>checkNoEvent(long)</code> method.
* @param waitTime Quiet time (millisecons).
* @see #checkNoEvent(long)
* @see #waitNoEvent(long, long)
* @throws TimeoutExpiredException
*/
public void waitNoEvent(long waitTime) {
ListenerSet ls = listenerSet;
if (ls != null) {
// surprisingly this field can be null in case of massive
// garbage collecting efforts like in NbTestCase.assertGC
waitNoEvent(ls.getTheWholeMask(), waitTime);
}
}
private AWTEvent waitEvent(long eventMask, long waitTime, TestOut waiterOutput) {
EventWaiter waiter = new EventWaiter(eventMask);
waiter.setTimeouts(timeouts.cloneThis());
waiter.setOutput(waiterOutput);
waiter.getTimeouts().
setTimeout("Waiter.WaitingTime",
waitTime);
waiter.getTimeouts().
setTimeout("Waiter.TimeDelta",
timeouts.getTimeout("EventTool.EventCheckingDelta"));
try {
return((AWTEvent)waiter.waitAction(null));
} catch(InterruptedException e) {
output.printStackTrace(e);
return(null);
}
}
private boolean checkNoEvent(long eventMask, long waitTime, TestOut waiterOutput) {
try {
AWTEvent event = waitEvent(eventMask, waitTime, TestOut.getNullOutput());
waiterOutput.printLine("AWT event was produced during waiting: ");
// used instead of event.toString() because it is not thread safe
waiterOutput.printLine(event.getClass().getName());
return(false);
} catch(TimeoutExpiredException e) {
return(true);
}
}
private static class EventType implements AWTEventListener {
long eventMask;
long eventTime;
private Reference eventRef;
public EventType(long eventMask) {
this.eventMask = eventMask;
eventRef = new WeakReference(null);
eventTime = -1;
}
public void eventDispatched(AWTEvent event) {
eventRef = new WeakReference(event);
eventTime = System.currentTimeMillis();
}
public AWTEvent getEvent() {
return (AWTEvent)eventRef.get();
}
public long getTime() {
return(eventTime);
}
public long getEventMask() {
return(eventMask);
}
}
private static class ListenerSet {
private Vector eventTypes;
private long theWholeMask;
public ListenerSet() {
eventTypes = new Vector();
try {
Class eventClass = Class.forName("java.awt.AWTEvent");
Field[] fields = eventClass.getFields();
theWholeMask = 0;
long eventMask;
for(int i = 0; i < fields.length; i++) {
if((fields[i].getModifiers() &
(Modifier.PUBLIC | Modifier.STATIC)) != 0 &&
fields[i].getType().equals(Long.TYPE) &&
fields[i].getName().endsWith("_EVENT_MASK")) {
eventMask = ((Long)fields[i].get(null)).longValue();
eventTypes.add(new EventType(eventMask));
theWholeMask = theWholeMask | eventMask;
}
}
} catch(ClassNotFoundException e) {
JemmyProperties.getCurrentOutput().printStackTrace(e);
} catch(IllegalAccessException e) {
JemmyProperties.getCurrentOutput().printStackTrace(e);
}
}
public void addListeners(long eventMask) {
Toolkit dtk = Toolkit.getDefaultToolkit();
for(int i = 0; i < eventTypes.size(); i++) {
EventType et = (EventType)eventTypes.get(i);
if((et.getEventMask() & eventMask) != 0) {
dtk.addAWTEventListener(et, et.getEventMask());
}
}
}
public void addListeners() {
addListeners(getTheWholeMask());
}
public void removeListeners() {
Toolkit dtk = Toolkit.getDefaultToolkit();
for(int i = 0; i < eventTypes.size(); i++) {
dtk.removeAWTEventListener((EventType)eventTypes.get(i));
}
}
public long getTheWholeMask() {
return(theWholeMask);
}
public long getLastEventTime(long eventMask) {
EventType et = getLastEventType(eventMask);
return((et == null) ? -1 : et.getTime());
}
public AWTEvent getLastEvent(long eventMask) {
EventType et = getLastEventType(eventMask);
return((et == null) ? null : et.getEvent());
}
private EventType getLastEventType(long eventMask) {
long maxTime = -1;
EventType maxType = null;
for(int i = 0; i < eventTypes.size(); i++) {
EventType et = (EventType)eventTypes.get(i);
if((eventMask & et.getEventMask()) != 0 &&
et.getTime() > maxTime) {
maxType = et;
maxTime = maxType.getTime();
}
}
return(maxType);
}
}
private class EventWaiter extends Waiter {
long eventMask;
long startTime;
public EventWaiter(long eventMask) {
this.eventMask = eventMask;
startTime = getLastEventTime(eventMask);
}
public Object actionProduced(Object obj) {
EventType et = listenerSet.getLastEventType(eventMask);
if(et != null &&
et.getTime() > startTime) {
return(et.getEvent());
} else {
return(null);
}
}
public String getDescription() {
return("Last event under " +
Long.toString(eventMask, 2) + " event mask");
}
}
private class NoEventWaiter extends Waiter {
long eventMask;
long waitTime;
public NoEventWaiter(long eventMask, long waitTime) {
this.eventMask = eventMask;
this.waitTime = waitTime;
}
public Object actionProduced(Object obj) {
return(checkNoEvent(eventMask, waitTime, TestOut.getNullOutput()) ?
"Reached!" :
null);
}
public String getDescription() {
return("No event under " +
Long.toString(eventMask, 2) +
" event mask during " +
Long.toString(waitTime) +
" milliseconds");
}
}
}