0001: /*
0002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003: *
0004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * The contents of this file are subject to the terms of either the GNU
0007: * General Public License Version 2 only ("GPL") or the Common
0008: * Development and Distribution License("CDDL") (collectively, the
0009: * "License"). You may not use this file except in compliance with the
0010: * License. You can obtain a copy of the License at
0011: * http://www.netbeans.org/cddl-gplv2.html
0012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
0013: * specific language governing permissions and limitations under the
0014: * License. When distributing the software, include this License Header
0015: * Notice in each file and include the License file at
0016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
0017: * particular file as subject to the "Classpath" exception as provided
0018: * by Sun in the GPL Version 2 section of the License file that
0019: * accompanied this code. If applicable, add the following below the
0020: * License Header, with the fields enclosed by brackets [] replaced by
0021: * your own identifying information:
0022: * "Portions Copyrighted [year] [name of copyright owner]"
0023: *
0024: * Contributor(s): Alexandre Iline.
0025: *
0026: * The Original Software is the Jemmy library.
0027: * The Initial Developer of the Original Software is Alexandre Iline.
0028: * All Rights Reserved.
0029: *
0030: * If you wish your version of this file to be governed by only the CDDL
0031: * or only the GPL Version 2, indicate your decision by adding
0032: * "[Contributor] elects to include this software in this distribution
0033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
0034: * single choice of license, a recipient has the option to distribute
0035: * your version of this file under either the CDDL, the GPL Version 2 or
0036: * to extend the choice of license to its licensees as provided above.
0037: * However, if you add GPL Version 2 code and therefore, elected the GPL
0038: * Version 2 license, then the option applies only if the new code is
0039: * made subject to such option by the copyright holder.
0040: *
0041: *
0042: *
0043: * $Id$ $Revision$ $Date$
0044: *
0045: */
0046:
0047: package org.netbeans.jemmy;
0048:
0049: import java.awt.AWTEvent;
0050: import java.awt.Component;
0051: import java.awt.EventQueue;
0052: import java.awt.Toolkit;
0053: import java.awt.Window;
0054:
0055: import java.awt.event.AWTEventListener;
0056: import java.awt.event.InputEvent;
0057: import java.awt.event.KeyEvent;
0058: import java.awt.event.MouseEvent;
0059: import java.awt.event.WindowEvent;
0060:
0061: import java.lang.reflect.InvocationTargetException;
0062: import java.lang.reflect.Field;
0063:
0064: import javax.swing.SwingUtilities;
0065:
0066: /**
0067: * Provides low level functions for reproducing user actions.
0068: * One dispatch model uses the managed component's event queue to dispatch
0069: * events. The other dispatch model uses <code>java.awt.Robot</code> to
0070: * generate native events. It is an option in the Robot dispatch model
0071: * to wait for the managed component's event queue to empty before dispatching
0072: * events.
0073: *
0074: * Timeouts used: <BR>
0075: * EventDispatcher.WaitQueueEmptyTimeout - to wait event queue empty. <BR>
0076: * EventDispatcher.RobotAutoDelay - param for java.awt.Robot.setAutoDelay method. <BR>
0077: * EventDispatcher.WaitComponentUnderMouseTimeout - time to wait component under mouse. <BR>
0078: *
0079: * @see org.netbeans.jemmy.Timeouts
0080: *
0081: * @author Alexandre Iline (alexandre.iline@sun.com)
0082: *
0083: */
0084: public class EventDispatcher implements Outputable, Timeoutable {
0085:
0086: private final static long WAIT_QUEUE_EMPTY_TIMEOUT = 180000;
0087: private final static long ROBOT_AUTO_DELAY = 10;
0088: private final static long WAIT_COMPONENT_UNDER_MOUSE_TIMEOUT = 60000;
0089:
0090: private static Field[] keyFields;
0091: private static MotionListener motionListener = null;
0092:
0093: /**
0094: * Component to dispatch events to.
0095: */
0096: protected Component component;
0097: private TestOut output;
0098: private Timeouts timeouts;
0099: private ClassReference reference;
0100: private int model;
0101: private ClassReference robotReference = null;
0102: private boolean outsider = false;
0103: private QueueTool queueTool;
0104:
0105: /**
0106: * Constructor.
0107: * @param comp Component to operate with.
0108: */
0109: public EventDispatcher(Component comp) {
0110: super ();
0111: component = comp;
0112: reference = new ClassReference(comp);
0113: queueTool = new QueueTool();
0114: setOutput(JemmyProperties.getProperties().getOutput());
0115: setTimeouts(JemmyProperties.getProperties().getTimeouts());
0116: setDispatchingModel(JemmyProperties.getProperties()
0117: .getDispatchingModel());
0118: }
0119:
0120: /**
0121: * Waits for the managed component's <code>java.awt.EventQueue</code> to empty.
0122: * The timeout for this wait is EventDispatcher.WaitQueueEmptyTimeout.
0123: * @param output Output to print exception into.
0124: * @param timeouts A collection of timeout assignments.
0125: * @throws TimeoutExpiredException
0126: * @see org.netbeans.jemmy.QueueTool
0127: */
0128: public static void waitQueueEmpty(TestOut output, Timeouts timeouts) {
0129: QueueTool qt = new QueueTool();
0130: qt.setTimeouts(timeouts.cloneThis());
0131: qt
0132: .getTimeouts()
0133: .setTimeout(
0134: "QueueTool.WaitQueueEmptyTimeout",
0135: JemmyProperties
0136: .getCurrentTimeout("EventDispatcher.WaitQueueEmptyTimeout"));
0137: qt.setOutput(output);
0138: qt.waitEmpty();
0139: }
0140:
0141: /**
0142: * Waits for the managed component's <code>java.awt.EventQueue</code> to empty.
0143: * Uses default output and timeouts. The timeout for this wait is
0144: * EventDispatcher.WaitQueueEmptyTimeout.
0145: * @see QueueTool
0146: * @throws TimeoutExpiredException
0147: */
0148: public static void waitQueueEmpty() {
0149: waitQueueEmpty(JemmyProperties.getCurrentOutput(),
0150: JemmyProperties.getCurrentTimeouts());
0151: }
0152:
0153: /**
0154: * Waits for the managed component's <code>java.awt.EventQueue</code> to stay empty.
0155: * The timeout for this wait is EventDispatcher.WaitQueueEmptyTimeout.
0156: * @param emptyTime The time that the event queue has to stay empty to avoid
0157: * a TimeoutExpiredException.
0158: * @param output Output to print exception into
0159: * @param timeouts A collection of timeout assignments.
0160: * @throws TimeoutExpiredException
0161: * @see org.netbeans.jemmy.QueueTool
0162: */
0163: public static void waitQueueEmpty(long emptyTime, TestOut output,
0164: Timeouts timeouts) {
0165: QueueTool qt = new QueueTool();
0166: qt.setTimeouts(timeouts.cloneThis());
0167: qt
0168: .getTimeouts()
0169: .setTimeout(
0170: "QueueTool.WaitQueueEmptyTimeout",
0171: JemmyProperties
0172: .getCurrentTimeout("EventDispatcher.WaitQueueEmptyTimeout"));
0173: qt.setOutput(output);
0174: qt.waitEmpty(emptyTime);
0175: }
0176:
0177: /**
0178: * Waits for the managed component's <code>java.awt.EventQueue</code> to stay empty.
0179: * Uses default output and timeouts. The timeout for this wait is
0180: * EventDispatcher.WaitQueueEmptyTimeout.
0181: * @param emptyTime The time that the event queue has to stay empty to avoid
0182: * a TimeoutExpiredException.
0183: * @throws TimeoutExpiredException
0184: * @see org.netbeans.jemmy.QueueTool
0185: */
0186: public static void waitQueueEmpty(long emptyTime) {
0187: waitQueueEmpty(emptyTime, JemmyProperties.getCurrentOutput(),
0188: JemmyProperties.getCurrentTimeouts());
0189: }
0190:
0191: /**
0192: * Get a string representation for key modifiers.
0193: * Used to print trace.
0194: * @param modifiers Bit mask of keyboard event modifiers.
0195: * @return a string representation for the keyboard event modifiers.
0196: */
0197: public static String getModifiersString(int modifiers) {
0198: String result = "";
0199: if ((modifiers & InputEvent.CTRL_MASK) != 0) {
0200: result = result + "CTRL_MASK | ";
0201: }
0202: if ((modifiers & InputEvent.META_MASK) != 0) {
0203: result = result + "META_MASK | ";
0204: }
0205: if ((modifiers & InputEvent.ALT_MASK) != 0) {
0206: result = result + "ALT_MASK | ";
0207: }
0208: if ((modifiers & InputEvent.ALT_GRAPH_MASK) != 0) {
0209: result = result + "ALT_GRAPH_MASK | ";
0210: }
0211: if ((modifiers & InputEvent.SHIFT_MASK) != 0) {
0212: result = result + "SHIFT_MASK | ";
0213: }
0214: if (result.length() > 0) {
0215: return (result.substring(0, result.length() - 3));
0216: }
0217: return (result);
0218: }
0219:
0220: /**
0221: * Returns a string representation for a keyboard event.
0222: * Used to print trace.
0223: * @param keyCode Key code (<code>KeyEvent.VK_*</code> value)
0224: * @return the KeyEvent field name.
0225: */
0226: public static String getKeyDescription(int keyCode) {
0227: for (int i = 0; i < keyFields.length; i++) {
0228: try {
0229: if (keyFields[i].getName().startsWith("VK_")
0230: && keyFields[i].getInt(null) == keyCode) {
0231: return (keyFields[i].getName());
0232: }
0233: } catch (IllegalAccessException e) {
0234: JemmyProperties.getCurrentOutput().printStackTrace(e);
0235: }
0236: }
0237: return ("VK_UNKNOWN");
0238: }
0239:
0240: /**
0241: * Returns a mouse button string representation.
0242: * Used to print trace.
0243: * @param button Mouse button (<code>InputEvent.BUTTON1/2/3_MASK</code> value).
0244: * @return InputEvent field name.
0245: */
0246: public static String getMouseButtonDescription(int button) {
0247: String result;
0248: if ((button & InputEvent.BUTTON1_MASK) != 0) {
0249: result = "BUTTON1";
0250: } else if ((button & InputEvent.BUTTON2_MASK) != 0) {
0251: result = "BUTTON2";
0252: } else if ((button & InputEvent.BUTTON3_MASK) != 0) {
0253: result = "BUTTON3";
0254: } else {
0255: result = "UNKNOWN_BUTTON";
0256: }
0257: return (result);
0258: }
0259:
0260: static {
0261: Timeouts.initDefault("EventDispatcher.WaitQueueEmptyTimeout",
0262: WAIT_QUEUE_EMPTY_TIMEOUT);
0263: Timeouts.initDefault("EventDispatcher.RobotAutoDelay",
0264: ROBOT_AUTO_DELAY);
0265: Timeouts.initDefault(
0266: "EventDispatcher.WaitComponentUnderMouseTimeout",
0267: WAIT_COMPONENT_UNDER_MOUSE_TIMEOUT);
0268: try {
0269: keyFields = Class.forName("java.awt.event.KeyEvent")
0270: .getFields();
0271: } catch (ClassNotFoundException e) {
0272: JemmyProperties.getCurrentOutput().printStackTrace(e);
0273: }
0274: }
0275:
0276: /**
0277: * Wait (or not) for the mouse to move over a Java component before pressing.
0278: * This option is relevant when using <code>java.awt.Robot</code> to generate
0279: * mouse events. If a mouse press occurs at a position not occupied by a
0280: * known Java component then a <code>NoComponentUnderMouseException</code>
0281: * will be thrown.
0282: * @param yesOrNo if <code>true</code> then the test system will wait for
0283: * the mouse to move over a Java component before pressing.
0284: * therwise, mouse presses can take place anywhere on the screen.
0285: */
0286: public void checkComponentUnderMouse(boolean yesOrNo) {
0287: outsider = !yesOrNo;
0288: }
0289:
0290: /**
0291: * Defines print output streams or writers.
0292: * @param out Identify the streams or writers used for print output.
0293: * @see org.netbeans.jemmy.Outputable
0294: * @see org.netbeans.jemmy.TestOut
0295: * @see #getOutput
0296: */
0297: public void setOutput(TestOut out) {
0298: output = out;
0299: queueTool.setOutput(out);
0300: }
0301:
0302: /**
0303: * Returns print output streams or writers.
0304: * @return an object that contains references to objects for
0305: * printing to output and err streams.
0306: * @see org.netbeans.jemmy.Outputable
0307: * @see org.netbeans.jemmy.TestOut
0308: * @see #setOutput
0309: */
0310: public TestOut getOutput() {
0311: return (output);
0312: }
0313:
0314: /**
0315: * Defines current timeouts.
0316: * @param timeouts A collection of timeout assignments.
0317: * @see org.netbeans.jemmy.Timeoutable
0318: * @see org.netbeans.jemmy.Timeouts
0319: * @see #getTimeouts
0320: */
0321: public void setTimeouts(Timeouts timeouts) {
0322: this .timeouts = timeouts;
0323: queueTool.setTimeouts(timeouts);
0324: queueTool
0325: .getTimeouts()
0326: .setTimeout(
0327: "QueueTool.WaitQueueEmptyTimeout",
0328: timeouts
0329: .getTimeout("EventDispatcher.WaitQueueEmptyTimeout"));
0330: if (robotReference != null) {
0331: try {
0332: Object[] params = { new Integer((int) timeouts
0333: .getTimeout("EventDispatcher.RobotAutoDelay")) };
0334: Class[] paramClasses = { Integer.TYPE };
0335: robotReference.invokeMethod("setAutoDelay", params,
0336: paramClasses);
0337: } catch (InvocationTargetException e) {
0338: output.printStackTrace(e);
0339: } catch (IllegalStateException e) {
0340: output.printStackTrace(e);
0341: } catch (NoSuchMethodException e) {
0342: output.printStackTrace(e);
0343: } catch (IllegalAccessException e) {
0344: output.printStackTrace(e);
0345: }
0346: }
0347: }
0348:
0349: /**
0350: * Return current timeouts.
0351: * @return the collection of current timeout assignments.
0352: * @see org.netbeans.jemmy.Timeoutable
0353: * @see org.netbeans.jemmy.Timeouts
0354: * @see #setTimeouts
0355: */
0356: public Timeouts getTimeouts() {
0357: return (timeouts);
0358: }
0359:
0360: /**
0361: * Defines dispatching model.
0362: * @param m New model value.
0363: * @see #getDispatchingModel()
0364: * @see org.netbeans.jemmy.JemmyProperties#QUEUE_MODEL_MASK
0365: * @see org.netbeans.jemmy.JemmyProperties#ROBOT_MODEL_MASK
0366: * @see org.netbeans.jemmy.JemmyProperties#getCurrentDispatchingModel()
0367: * @see org.netbeans.jemmy.JemmyProperties#setCurrentDispatchingModel(int)
0368: * @see org.netbeans.jemmy.JemmyProperties#initDispatchingModel(boolean, boolean)
0369: * @see org.netbeans.jemmy.JemmyProperties#initDispatchingModel()
0370: */
0371: public void setDispatchingModel(int m) {
0372: model = m;
0373: if ((model & JemmyProperties.ROBOT_MODEL_MASK) != 0) {
0374: createRobot();
0375: try {
0376: Object[] params = { (model & JemmyProperties.QUEUE_MODEL_MASK) != 0 ? Boolean.TRUE
0377: : Boolean.FALSE };
0378: Class[] paramClasses = { Boolean.TYPE };
0379: robotReference.invokeMethod("setAutoWaitForIdle",
0380: params, paramClasses);
0381: } catch (InvocationTargetException e) {
0382: output.printStackTrace(e);
0383: } catch (IllegalStateException e) {
0384: output.printStackTrace(e);
0385: } catch (NoSuchMethodException e) {
0386: output.printStackTrace(e);
0387: } catch (IllegalAccessException e) {
0388: output.printStackTrace(e);
0389: }
0390: }
0391: }
0392:
0393: /**
0394: * Gets the dispatching model value.
0395: * @return the model value.
0396: * @see #setDispatchingModel(int)
0397: * @see org.netbeans.jemmy.JemmyProperties#QUEUE_MODEL_MASK
0398: * @see org.netbeans.jemmy.JemmyProperties#ROBOT_MODEL_MASK
0399: * @see org.netbeans.jemmy.JemmyProperties#getCurrentDispatchingModel()
0400: * @see org.netbeans.jemmy.JemmyProperties#setCurrentDispatchingModel(int)
0401: * @see org.netbeans.jemmy.JemmyProperties#initDispatchingModel(boolean, boolean)
0402: * @see org.netbeans.jemmy.JemmyProperties#initDispatchingModel()
0403: */
0404: public int getDispatchingModel() {
0405: return (model);
0406: }
0407:
0408: /**
0409: * Dispatches <code>AWTEvent</code> to component passed in constructor.
0410: * If <code>(getDispatchingModel & JemmyProperties.QUEUE_MODEL_MASK) == 0</code>
0411: * dispatched event directly, otherwise uses
0412: * <code>javax.swing.SwingUtilities.invokeAndWait(Runnable)</code><BR>
0413: * @param event AWTEvent instance to be dispatched.
0414: * @throws ComponentIsNotVisibleException
0415: * @throws ComponentIsNotFocusedException
0416: */
0417: public void dispatchEvent(final AWTEvent event) {
0418: // run in dispatch thread
0419: String eventToString = (String) queueTool
0420: .invokeSmoothly(new QueueTool.QueueAction(
0421: "event.toString()") {
0422: public Object launch() {
0423: return event.toString();
0424: }
0425: });
0426: output.printLine("Dispatch event " + eventToString);
0427: output.printGolden("Dispatch event "
0428: + event.getClass().toString());
0429: Dispatcher disp = new Dispatcher(event);
0430: queueTool.invokeAndWait(disp);
0431: }
0432:
0433: /**
0434: * Dispatches a MouseEvent.
0435: * @see #dispatchEvent(AWTEvent)
0436: * @param id <code>MouseEvent.MOUSE_*</code> value
0437: * @param mods <code>InputEvent.MOUSE1/2/3_BUTTON</code> | (modiviers value)
0438: * @param clickCount Click count
0439: * @param x Horizontal click point coordinate.
0440: * @param y vertical click point coordinate.
0441: * @param popup Difines if mouse event is popup event.
0442: */
0443: public void dispatchMouseEvent(int id, int mods, int clickCount,
0444: int x, int y, boolean popup) {
0445: MouseEvent event = new MouseEvent(component, id, System
0446: .currentTimeMillis(), mods, x, y, clickCount, popup);
0447: dispatchEvent(event);
0448: }
0449:
0450: /**
0451: * Dispatches MouseEvent at the center of component.
0452: * @see #dispatchEvent(AWTEvent)
0453: * @param id <code>MouseEvent.MOUSE_*</code> value
0454: * @param mods <code>InputEvent.MOUSE1/2/3_BUTTON</code> | (modiviers value)
0455: * @param clickCount Click count
0456: * @param popup Difines if mouse event is popup event.
0457: */
0458: public void dispatchMouseEvent(int id, int mods, int clickCount,
0459: boolean popup) {
0460: int x = component.getWidth() / 2;
0461: int y = component.getHeight() / 2;
0462: dispatchMouseEvent(id, mods, clickCount, x, y, popup);
0463: }
0464:
0465: /**
0466: * Dispatches WindowEvent.
0467: * @see #dispatchEvent(AWTEvent)
0468: * @param id <code>WindowEvent.WINDOW_*</code> value
0469: */
0470: public void dispatchWindowEvent(int id) {
0471: WindowEvent event = new WindowEvent((Window) component, id);
0472: dispatchEvent(event);
0473: }
0474:
0475: /**
0476: * Dispatches KeyEvent.
0477: * @see #dispatchEvent(AWTEvent)
0478: * @param id <code>KeyEvent.KEY_PRESSED</code> or <code>KeyEvent.KEY_RELEASED</code> value.
0479: * @param mods Modifiers.
0480: * @param keyCode Key code,
0481: */
0482: public void dispatchKeyEvent(int id, int mods, int keyCode) {
0483: KeyEvent event = new KeyEvent(component, id, System
0484: .currentTimeMillis(), mods, keyCode);
0485: dispatchEvent(event);
0486: }
0487:
0488: /**
0489: * Dispatches KeyEvent.
0490: * @see #dispatchEvent(AWTEvent)
0491: * @param id <code>KeyEvent.KEY_TYPED</code> value.
0492: * @param mods Modifiers.
0493: * @param keyCode Key code,
0494: * @param keyChar Char to be tiped
0495: */
0496: public void dispatchKeyEvent(int id, int mods, int keyCode,
0497: char keyChar) {
0498: KeyEvent event = new KeyEvent(component, id, System
0499: .currentTimeMillis(), mods, keyCode, keyChar);
0500: dispatchEvent(event);
0501: }
0502:
0503: /**
0504: * Waits until all events currently on the event queue have been processed.
0505: */
0506: public void waitForIdle() {
0507: makeRobotOperation("waitForIdle", null, null);
0508: }
0509:
0510: /**
0511: * Bind horizontal relative cursor coordinate to screen coordinate.
0512: * @param x Relative coordinate
0513: * @return Absolute coordinate
0514: */
0515: protected int getAbsoluteX(int x) {
0516: return ((int) component.getLocationOnScreen().getX() + x);
0517: }
0518:
0519: /**
0520: * Bind vertical relative cursor coordinate to screen coordinate.
0521: * @param y Relative coordinate
0522: * @return Absolute coordinate
0523: */
0524: protected int getAbsoluteY(int y) {
0525: return ((int) component.getLocationOnScreen().getY() + y);
0526: }
0527:
0528: /**
0529: * Delays robot.
0530: * @param time Time to dalay robot for.
0531: */
0532: public void delayRobot(long time) {
0533: Object[] params = { new Integer((int) time) };
0534: Class[] paramClasses = { Integer.TYPE };
0535: makeRobotOperation("delay", params, paramClasses);
0536: }
0537:
0538: /**
0539: * Moves mouse by robot.
0540: * @param x Component relative horizontal coordinate.
0541: * @param y Component relative vertical coordinate.
0542: * @throws ComponentIsNotVisibleException
0543: */
0544: public void robotMoveMouse(int x, int y) {
0545: if (motionListener == null) {
0546: initMotionListener();
0547: }
0548: output.printLine("Move mouse to (" + Integer.toString(x) + ","
0549: + Integer.toString(y) + ")");
0550: Object[] params = { new Integer(getAbsoluteX(x)),
0551: new Integer(getAbsoluteY(y)) };
0552: Class[] paramClasses = { Integer.TYPE, Integer.TYPE };
0553: makeRobotOperation("mouseMove", params, paramClasses);
0554: }
0555:
0556: /**
0557: * Press mouse button by robot.
0558: * @param button Mouse button (InputEvent.MOUSE1/2/3_BUTTON value)
0559: * @param modifiers Modifiers
0560: * @throws ComponentIsNotVisibleException
0561: */
0562: public void robotPressMouse(int button, int modifiers) {
0563: if (!outsider) {
0564: waitMouseOver();
0565: }
0566: robotPressModifiers(modifiers);
0567: output.printLine("Press " + getMouseButtonDescription(button)
0568: + " mouse button");
0569: Object[] params = { new Integer(button) };
0570: Class[] paramClasses = { Integer.TYPE };
0571: makeRobotOperation("mousePress", params, paramClasses);
0572: }
0573:
0574: /**
0575: * Press mouse button with 0 modifiers.
0576: * @param button Mouse button (<code>InputEvent.MOUSE1/2/3_BUTTON</code> value)
0577: * @see #robotPressMouse(int, int)
0578: */
0579: public void robotPressMouse(int button) {
0580: robotPressMouse(button, 0);
0581: }
0582:
0583: /**
0584: * Releases mouse button by robot.
0585: * @param button Mouse button (<code>InputEvent.MOUSE1/2/3_BUTTON</code> value)
0586: * @param modifiers Modifiers
0587: * @throws ComponentIsNotVisibleException
0588: */
0589: public void robotReleaseMouse(int button, int modifiers) {
0590: output.printLine("Release " + getMouseButtonDescription(button)
0591: + " mouse button");
0592: Object[] params = { new Integer(button) };
0593: Class[] paramClasses = { Integer.TYPE };
0594: makeRobotOperation("mouseRelease", params, paramClasses);
0595: robotReleaseModifiers(modifiers);
0596: }
0597:
0598: /**
0599: * Releases mouse button with 0 modifiers.
0600: * @param button Mouse button (<code>InputEvent.MOUSE1/2/3_BUTTON</code> value)
0601: * @see #robotReleaseMouse(int, int)
0602: */
0603: public void robotReleaseMouse(int button) {
0604: robotReleaseMouse(button, 0);
0605: }
0606:
0607: /**
0608: * Press a key using <code>java.awt.Robot</code>.
0609: * @param keyCode Key (<code>KeyEvent.VK_*</code> value)
0610: * @param modifiers Mask of KeyEvent modifiers.
0611: * @throws ComponentIsNotVisibleException
0612: * @throws ComponentIsNotFocusedException
0613: */
0614: public void robotPressKey(int keyCode, int modifiers) {
0615: robotPressModifiers(modifiers);
0616: output
0617: .printLine("Press " + getKeyDescription(keyCode)
0618: + " key");
0619: Object[] params = { new Integer(keyCode) };
0620: Class[] paramClasses = { Integer.TYPE };
0621: makeRobotOperation("keyPress", params, paramClasses);
0622: }
0623:
0624: /**
0625: * Press key with no modifiers using <code>java.awt.Robot</code>.
0626: * @param keyCode Key (<code>KeyEvent.VK_*</code> value)
0627: * @see #robotPressKey(int, int)
0628: */
0629: public void robotPressKey(int keyCode) {
0630: robotPressKey(keyCode, 0);
0631: }
0632:
0633: /**
0634: * Releases key by robot.
0635: * @param keyCode Key (<code>KeyEvent.VK_*</code> value)
0636: * @param modifiers Mask of KeyEvent modifiers.
0637: * @throws ComponentIsNotVisibleException
0638: * @throws ComponentIsNotFocusedException
0639: */
0640: public void robotReleaseKey(int keyCode, int modifiers) {
0641: output.printLine("Release " + getKeyDescription(keyCode)
0642: + " key");
0643: Object[] params = { new Integer(keyCode) };
0644: Class[] paramClasses = { Integer.TYPE };
0645: makeRobotOperation("keyRelease", params, paramClasses);
0646: robotReleaseModifiers(modifiers);
0647: }
0648:
0649: /**
0650: * Releases key with 0 modifiers.
0651: * @param keyCode Key (<code>KeyEvent.VK_*</code> value)
0652: * @see #robotPressKey(int, int)
0653: */
0654: public void robotReleaseKey(int keyCode) {
0655: robotReleaseKey(keyCode, 0);
0656: }
0657:
0658: /**
0659: * Invokes component method through <code>SwingUtilities.invokeAndWait(Runnable)</code>.
0660: *
0661: * @param method_name Name of a method to be invoked
0662: * @param params Method params
0663: * @param params_classes Method params' classes
0664: * @return an Object - methods result.
0665: * @see org.netbeans.jemmy.ClassReference
0666: * @exception IllegalAccessException
0667: * @exception NoSuchMethodException
0668: * @exception IllegalStateException
0669: * @exception InvocationTargetException
0670: */
0671: public Object invokeMethod(String method_name, Object[] params,
0672: Class[] params_classes) throws InvocationTargetException,
0673: IllegalStateException, NoSuchMethodException,
0674: IllegalAccessException {
0675: Invoker invk = new Invoker(method_name, params, params_classes);
0676: try {
0677: return (queueTool.invokeAndWait(invk));
0678: } catch (JemmyException e) {
0679: if (invk.getException() != null) {
0680: if (invk.getException() instanceof InvocationTargetException) {
0681: throw ((InvocationTargetException) invk
0682: .getException());
0683: } else if (invk.getException() instanceof IllegalStateException) {
0684: throw ((IllegalStateException) invk.getException());
0685: } else if (invk.getException() instanceof NoSuchMethodException) {
0686: throw ((NoSuchMethodException) invk.getException());
0687: } else if (invk.getException() instanceof IllegalAccessException) {
0688: throw ((IllegalAccessException) invk.getException());
0689: }
0690: }
0691: throw (e);
0692: }
0693: }
0694:
0695: /**
0696: * Gets component field value through <code>SwingUtilities.invokeAndWait(Runnable)</code>.
0697: *
0698: * @param field_name Name of a field
0699: * @see #setField(String, Object)
0700: * @see org.netbeans.jemmy.ClassReference
0701: * @return an Object - field value
0702: * @exception IllegalAccessException
0703: * @exception IllegalStateException
0704: * @exception InvocationTargetException
0705: * @exception NoSuchFieldException
0706: */
0707: public Object getField(String field_name)
0708: throws InvocationTargetException, IllegalStateException,
0709: NoSuchFieldException, IllegalAccessException {
0710: Getter gtr = new Getter(field_name);
0711: try {
0712: return (queueTool.invokeAndWait(gtr));
0713: } catch (JemmyException e) {
0714: if (gtr.getException() != null) {
0715: if (gtr.getException() instanceof InvocationTargetException) {
0716: throw ((InvocationTargetException) gtr
0717: .getException());
0718: } else if (gtr.getException() instanceof IllegalStateException) {
0719: throw ((IllegalStateException) gtr.getException());
0720: } else if (gtr.getException() instanceof NoSuchFieldException) {
0721: throw ((NoSuchFieldException) gtr.getException());
0722: } else if (gtr.getException() instanceof IllegalAccessException) {
0723: throw ((IllegalAccessException) gtr.getException());
0724: }
0725: }
0726: throw (e);
0727: }
0728: }
0729:
0730: /**
0731: * Sets component field value through <code>SwingUtilities.invokeAndWait(Runnable)</code>.
0732: *
0733: * @param field_name Name of a field
0734: * @param newValue New field value
0735: * @see #getField(String)
0736: * @see org.netbeans.jemmy.ClassReference
0737: * @exception IllegalAccessException
0738: * @exception IllegalStateException
0739: * @exception InvocationTargetException
0740: * @exception NoSuchFieldException
0741: */
0742: public void setField(String field_name, Object newValue)
0743: throws InvocationTargetException, IllegalStateException,
0744: NoSuchFieldException, IllegalAccessException {
0745: Setter str = new Setter(field_name, newValue);
0746: try {
0747: queueTool.invokeAndWait(str);
0748: } catch (JemmyException e) {
0749: if (str.getException() != null) {
0750: if (str.getException() instanceof InvocationTargetException) {
0751: throw ((InvocationTargetException) str
0752: .getException());
0753: } else if (str.getException() instanceof IllegalStateException) {
0754: throw ((IllegalStateException) str.getException());
0755: } else if (str.getException() instanceof NoSuchFieldException) {
0756: throw ((NoSuchFieldException) str.getException());
0757: } else if (str.getException() instanceof IllegalAccessException) {
0758: throw ((IllegalAccessException) str.getException());
0759: }
0760: }
0761: throw (e);
0762: }
0763: }
0764:
0765: /**
0766: * Invokes component method through <code>SwingUtilities.invokeAndWait(Runnable)</code>.
0767: * and catch all exceptions.
0768: * @param method_name Name of a method to be invoked
0769: * @param params Method params
0770: * @param params_classes Method params' classes
0771: * @param out TestOut instance to print exceptions stack trace to.
0772: * @return an Object - method result
0773: * @see #invokeMethod(String, Object[], Class[])
0774: * @see org.netbeans.jemmy.ClassReference
0775: */
0776: public Object invokeExistingMethod(String method_name,
0777: Object[] params, Class[] params_classes, TestOut out) {
0778: try {
0779: return (invokeMethod(method_name, params, params_classes));
0780: } catch (InvocationTargetException e) {
0781: out.printStackTrace(e);
0782: } catch (IllegalStateException e) {
0783: out.printStackTrace(e);
0784: } catch (NoSuchMethodException e) {
0785: out.printStackTrace(e);
0786: } catch (IllegalAccessException e) {
0787: out.printStackTrace(e);
0788: }
0789: return (null);
0790: }
0791:
0792: /**
0793: * Gets component field value through <code>SwingUtilities.invokeAndWait(Runnable)</code>.
0794: * and catch all exceptions.
0795: * @param field_name Name of a field
0796: * @param out TestOut instance to print exceptions stack trace to.
0797: * @return an Object - fields value
0798: * @see #getField(String)
0799: * @see #setExistingField(String, Object, TestOut)
0800: * @see org.netbeans.jemmy.ClassReference
0801: */
0802: public Object getExistingField(String field_name, TestOut out) {
0803: try {
0804: return (getField(field_name));
0805: } catch (InvocationTargetException e) {
0806: out.printStackTrace(e);
0807: } catch (IllegalStateException e) {
0808: out.printStackTrace(e);
0809: } catch (NoSuchFieldException e) {
0810: out.printStackTrace(e);
0811: } catch (IllegalAccessException e) {
0812: out.printStackTrace(e);
0813: }
0814: return (null);
0815: }
0816:
0817: /**
0818: * Sets component field value through <code>SwingUtilities.invokeAndWait(Runnable)</code>.
0819: * and catch all exceptions.
0820: * @param field_name Name of a field
0821: * @param newValue New field value
0822: * @param out TestOut instance to print exceptions stack trace to.
0823: * @see #setField(String, Object)
0824: * @see #getExistingField(String, TestOut)
0825: * @see org.netbeans.jemmy.ClassReference
0826: */
0827: public void setExistingField(String field_name, Object newValue,
0828: TestOut out) {
0829: try {
0830: setField(field_name, newValue);
0831: } catch (InvocationTargetException e) {
0832: out.printStackTrace(e);
0833: } catch (IllegalStateException e) {
0834: out.printStackTrace(e);
0835: } catch (NoSuchFieldException e) {
0836: out.printStackTrace(e);
0837: } catch (IllegalAccessException e) {
0838: out.printStackTrace(e);
0839: }
0840: }
0841:
0842: /**
0843: * Invokes component method through <code>SwingUtilities.invokeAndWait(Runnable)</code>.
0844: * and catch all exceptions.
0845: * Exceptions are printed into TestOut object defined
0846: * by setOutput(TestOut) method.
0847: * @param method_name Name of a method to be invoked
0848: * @param params Method params
0849: * @param params_classes Method params' classes
0850: * @return an Object - method result
0851: * @see #invokeExistingMethod(String, Object[], Class[], TestOut)
0852: * @see org.netbeans.jemmy.ClassReference
0853: */
0854: public Object invokeExistingMethod(String method_name,
0855: Object[] params, Class[] params_classes) {
0856: return (invokeExistingMethod(method_name, params,
0857: params_classes, output));
0858: }
0859:
0860: /**
0861: * Gets component field value through <code>SwingUtilities.invokeAndWait(Runnable)</code>.
0862: * and catch all exceptions.
0863: * Exceptions are printed into TestOut object defined
0864: * by setOutput(TestOut) method.
0865: * @param field_name Name of a field
0866: * @return an Object - fields value
0867: * @see #getExistingField(String, TestOut)
0868: * @see #setExistingField(String, Object)
0869: * @see org.netbeans.jemmy.ClassReference
0870: */
0871: public Object getExistingField(String field_name) {
0872: return (getExistingField(field_name, output));
0873: }
0874:
0875: /**
0876: * Sets component field value through <code>SwingUtilities.invokeAndWait(Runnable)</code>.
0877: * and catch all exceptions.
0878: * Exceptions are printed into TestOut object defined
0879: * by setOutput(TestOut) method.
0880: * @param field_name Name of a field
0881: * @param newValue New field value
0882: * @see #setExistingField(String, Object, TestOut)
0883: * @see #getExistingField(String)
0884: * @see org.netbeans.jemmy.ClassReference
0885: */
0886: public void setExistingField(String field_name, Object newValue) {
0887: setExistingField(field_name, output);
0888: }
0889:
0890: //recursivelly releases all modifiers keys
0891: private void robotReleaseModifiers(int modifiers) {
0892: if ((modifiers & InputEvent.SHIFT_MASK) != 0) {
0893: robotReleaseKey(KeyEvent.VK_SHIFT, modifiers
0894: - (InputEvent.SHIFT_MASK & modifiers));
0895: } else if ((modifiers & InputEvent.ALT_GRAPH_MASK) != 0) {
0896: robotReleaseKey(KeyEvent.VK_ALT_GRAPH, modifiers
0897: - (InputEvent.ALT_GRAPH_MASK & modifiers));
0898: } else if ((modifiers & InputEvent.ALT_MASK) != 0) {
0899: robotReleaseKey(KeyEvent.VK_ALT, modifiers
0900: - (InputEvent.ALT_MASK & modifiers));
0901: } else if ((modifiers & InputEvent.META_MASK) != 0) {
0902: robotReleaseKey(KeyEvent.VK_META, modifiers
0903: - (InputEvent.META_MASK & modifiers));
0904: } else if ((modifiers & InputEvent.CTRL_MASK) != 0) {
0905: robotReleaseKey(KeyEvent.VK_CONTROL, modifiers
0906: - (InputEvent.CTRL_MASK & modifiers));
0907: }
0908: }
0909:
0910: //throws ComponentIsNotVisibleException if component is not visible
0911: private void checkVisibility() {
0912: if (!component.isVisible()) {
0913: throw (new ComponentIsNotVisibleException(component));
0914: }
0915: }
0916:
0917: //throws ComponentIsNotFocusedException if component has not focus
0918: private void checkFocus() {
0919: if (!component.hasFocus()) {
0920: throw (new ComponentIsNotFocusedException(component));
0921: }
0922: }
0923:
0924: //creates java.awt.Robot instance
0925: private void createRobot() {
0926: try {
0927: ClassReference robotClassReverence = new ClassReference(
0928: "java.awt.Robot");
0929: robotReference = new ClassReference(robotClassReverence
0930: .newInstance(null, null));
0931: } catch (ClassNotFoundException e) {
0932: output.printStackTrace(e);
0933: } catch (InstantiationException e) {
0934: output.printStackTrace(e);
0935: } catch (InvocationTargetException e) {
0936: output.printStackTrace(e);
0937: } catch (IllegalStateException e) {
0938: output.printStackTrace(e);
0939: } catch (NoSuchMethodException e) {
0940: output.printStackTrace(e);
0941: } catch (IllegalAccessException e) {
0942: output.printStackTrace(e);
0943: }
0944: }
0945:
0946: private void waitMouseOver() {
0947: try {
0948: Waiter wt = new Waiter(new Waitable() {
0949: public Object actionProduced(Object obj) {
0950: if (motionListener.getComponent() != null) {
0951: return ("");
0952: } else {
0953: return (null);
0954: }
0955: }
0956:
0957: public String getDescription() {
0958: return ("Mouse over component");
0959: }
0960: });
0961: wt.setTimeouts(timeouts.cloneThis());
0962: wt
0963: .getTimeouts()
0964: .setTimeout(
0965: "Waiter.WaitingTime",
0966: timeouts
0967: .getTimeout("EventDispatcher.WaitComponentUnderMouseTimeout"));
0968: wt.setOutput(output.createErrorOutput());
0969: wt.waitAction(component);
0970: } catch (InterruptedException e) {
0971: output.printStackTrace(e);
0972: } catch (TimeoutExpiredException e) {
0973: throw (new NoComponentUnderMouseException());
0974: }
0975: }
0976:
0977: //produce a robot operations through reflection
0978: private void makeRobotOperation(String method, Object[] params,
0979: Class[] paramClasses) {
0980: try {
0981: robotReference.invokeMethod(method, params, paramClasses);
0982: } catch (InvocationTargetException e) {
0983: output.printStackTrace(e);
0984: } catch (IllegalStateException e) {
0985: output.printStackTrace(e);
0986: } catch (NoSuchMethodException e) {
0987: output.printStackTrace(e);
0988: } catch (IllegalAccessException e) {
0989: output.printStackTrace(e);
0990: }
0991: if ((model & JemmyProperties.QUEUE_MODEL_MASK) != 0) {
0992: try {
0993: waitQueueEmpty(output.createErrorOutput(), timeouts);
0994: } catch (TimeoutExpiredException e) {
0995: output.printStackTrace(e);
0996: }
0997: }
0998: }
0999:
1000: //recursivelly presses all modifiers keys
1001: private void robotPressModifiers(int modifiers) {
1002: if ((modifiers & InputEvent.SHIFT_MASK) != 0) {
1003: robotPressKey(KeyEvent.VK_SHIFT, modifiers
1004: & ~InputEvent.SHIFT_MASK);
1005: } else if ((modifiers & InputEvent.ALT_GRAPH_MASK) != 0) {
1006: robotPressKey(KeyEvent.VK_ALT_GRAPH, modifiers
1007: & ~InputEvent.ALT_GRAPH_MASK);
1008: } else if ((modifiers & InputEvent.ALT_MASK) != 0) {
1009: robotPressKey(KeyEvent.VK_ALT, modifiers
1010: & ~InputEvent.ALT_MASK);
1011: } else if ((modifiers & InputEvent.META_MASK) != 0) {
1012: robotPressKey(KeyEvent.VK_META, modifiers
1013: & ~InputEvent.META_MASK);
1014: } else if ((modifiers & InputEvent.CTRL_MASK) != 0) {
1015: robotPressKey(KeyEvent.VK_CONTROL, modifiers
1016: & ~InputEvent.CTRL_MASK);
1017: }
1018: }
1019:
1020: private void initMotionListener() {
1021: if (motionListener == null) {
1022: motionListener = new MotionListener();
1023: Toolkit.getDefaultToolkit().addAWTEventListener(
1024: motionListener, AWTEvent.MOUSE_EVENT_MASK);
1025: Object[] params = new Object[2];
1026: Class[] paramClasses = { Integer.TYPE, Integer.TYPE };
1027: params[0] = new Integer(getAbsoluteX(-1));
1028: params[1] = new Integer(getAbsoluteX(-1));
1029: makeRobotOperation("mouseMove", params, paramClasses);
1030: params[0] = new Integer(getAbsoluteX(0));
1031: params[1] = new Integer(getAbsoluteX(0));
1032: makeRobotOperation("mouseMove", params, paramClasses);
1033: }
1034: }
1035:
1036: private class Dispatcher extends QueueTool.QueueAction {
1037: AWTEvent event;
1038:
1039: public Dispatcher(AWTEvent e) {
1040: super (e.getClass().getName() + " event dispatching");
1041: event = e;
1042: }
1043:
1044: public Object launch() {
1045: if (event instanceof MouseEvent
1046: || event instanceof KeyEvent) {
1047: checkVisibility();
1048: }
1049: component.dispatchEvent(event);
1050: return (null);
1051: }
1052: }
1053:
1054: private class Invoker extends QueueTool.QueueAction {
1055: protected String methodName;
1056: protected Object[] params;
1057: protected Class[] paramClasses;
1058:
1059: public Invoker(String mn, Object[] p, Class[] pc) {
1060: super (mn + " method invocation");
1061: methodName = mn;
1062: params = p;
1063: paramClasses = pc;
1064: }
1065:
1066: public Object launch() throws InvocationTargetException,
1067: NoSuchMethodException, IllegalAccessException {
1068: checkVisibility();
1069: if (methodName.equals("keyPress")
1070: || methodName.equals("keyRelease")) {
1071: checkFocus();
1072: }
1073: return (reference.invokeMethod(methodName, params,
1074: paramClasses));
1075: }
1076: }
1077:
1078: private class Getter extends QueueTool.QueueAction {
1079: String fieldName;
1080:
1081: public Getter(String fn) {
1082: super (fn + " field receiving");
1083: fieldName = fn;
1084: }
1085:
1086: public Object launch() throws InvocationTargetException,
1087: NoSuchFieldException, IllegalAccessException {
1088: return (reference.getField(fieldName));
1089: }
1090: }
1091:
1092: private class Setter extends QueueTool.QueueAction {
1093: String fieldName;
1094: Object newValue;
1095:
1096: public Setter(String fn, Object nv) {
1097: super (fn + " field changing");
1098: fieldName = fn;
1099: newValue = nv;
1100: }
1101:
1102: public Object launch() throws InvocationTargetException,
1103: NoSuchFieldException, IllegalAccessException {
1104: reference.setField(fieldName, newValue);
1105: return (null);
1106: }
1107: }
1108:
1109: private static class MotionListener implements AWTEventListener {
1110: private Component mouseComponent;
1111:
1112: public void eventDispatched(AWTEvent event) {
1113: if (event instanceof MouseEvent) {
1114: MouseEvent e = (MouseEvent) event;
1115: if (e.getID() == MouseEvent.MOUSE_ENTERED) {
1116: mouseComponent = e.getComponent();
1117: } else if (e.getID() == MouseEvent.MOUSE_EXITED) {
1118: mouseComponent = null;
1119: }
1120: }
1121: }
1122:
1123: public Component getComponent() {
1124: return (mouseComponent);
1125: }
1126: }
1127: }
|