EventDispatcher.java revision 13978:1993af50385d
1/* 2 * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23package org.netbeans.jemmy; 24 25import java.awt.AWTEvent; 26import java.awt.Component; 27import java.awt.Toolkit; 28import java.awt.Window; 29import java.awt.event.AWTEventListener; 30import java.awt.event.InputEvent; 31import java.awt.event.KeyEvent; 32import java.awt.event.MouseEvent; 33import java.awt.event.WindowEvent; 34import java.lang.reflect.Field; 35import java.lang.reflect.InvocationTargetException; 36 37/** 38 * Provides low level functions for reproducing user actions. One dispatch model 39 * uses the managed component's event queue to dispatch events. The other 40 * dispatch model uses {@code java.awt.Robot} to generate native events. It 41 * is an option in the Robot dispatch model to wait for the managed component's 42 * event queue to empty before dispatching events. 43 * 44 * Timeouts used: <BR> 45 * EventDispatcher.WaitQueueEmptyTimeout - to wait event queue empty. <BR> 46 * EventDispatcher.RobotAutoDelay - param for java.awt.Robot.setAutoDelay 47 * method. <BR> 48 * EventDispatcher.WaitComponentUnderMouseTimeout - time to wait component under 49 * mouse. <BR> 50 * 51 * @see org.netbeans.jemmy.Timeouts 52 * 53 * @author Alexandre Iline (alexandre.iline@oracle.com) 54 * 55 */ 56public class EventDispatcher implements Outputable, Timeoutable { 57 58 private static final long WAIT_QUEUE_EMPTY_TIMEOUT = 180000; 59 private static final long ROBOT_AUTO_DELAY = 10; 60 private static final long WAIT_COMPONENT_UNDER_MOUSE_TIMEOUT = 60000; 61 62 private static Field[] keyFields; 63 private static volatile MotionListener motionListener = null; 64 65 /** 66 * Component to dispatch events to. 67 */ 68 protected Component component; 69 private TestOut output; 70 private Timeouts timeouts; 71 private final ClassReference reference; 72 private int model; 73 private ClassReference robotReference = null; 74 private boolean outsider = false; 75 private final QueueTool queueTool; 76 77 /** 78 * Constructor. 79 * 80 * @param comp Component to operate with. 81 */ 82 public EventDispatcher(Component comp) { 83 super(); 84 component = comp; 85 reference = new ClassReference(comp); 86 queueTool = new QueueTool(); 87 setOutput(JemmyProperties.getProperties().getOutput()); 88 setTimeouts(JemmyProperties.getProperties().getTimeouts()); 89 setDispatchingModel(JemmyProperties.getProperties().getDispatchingModel()); 90 } 91 92 /** 93 * Waits for the managed component's {@code java.awt.EventQueue} to 94 * empty. The timeout for this wait is 95 * EventDispatcher.WaitQueueEmptyTimeout. 96 * 97 * @param output Output to print exception into. 98 * @param timeouts A collection of timeout assignments. 99 * @throws TimeoutExpiredException 100 * @see org.netbeans.jemmy.QueueTool 101 */ 102 public static void waitQueueEmpty(TestOut output, Timeouts timeouts) { 103 QueueTool qt = new QueueTool(); 104 qt.setTimeouts(timeouts.cloneThis()); 105 qt.getTimeouts(). 106 setTimeout("QueueTool.WaitQueueEmptyTimeout", 107 JemmyProperties. 108 getCurrentTimeout("EventDispatcher.WaitQueueEmptyTimeout")); 109 qt.setOutput(output); 110 qt.waitEmpty(); 111 } 112 113 /** 114 * Waits for the managed component's {@code java.awt.EventQueue} to 115 * empty. Uses default output and timeouts. The timeout for this wait is 116 * EventDispatcher.WaitQueueEmptyTimeout. 117 * 118 * @see QueueTool 119 * @throws TimeoutExpiredException 120 */ 121 public static void waitQueueEmpty() { 122 waitQueueEmpty(JemmyProperties.getCurrentOutput(), 123 JemmyProperties.getCurrentTimeouts()); 124 } 125 126 /** 127 * Waits for the managed component's {@code java.awt.EventQueue} to 128 * stay empty. The timeout for this wait is 129 * EventDispatcher.WaitQueueEmptyTimeout. 130 * 131 * @param emptyTime The time that the event queue has to stay empty to avoid 132 * a TimeoutExpiredException. 133 * @param output Output to print exception into 134 * @param timeouts A collection of timeout assignments. 135 * @throws TimeoutExpiredException 136 * @see org.netbeans.jemmy.QueueTool 137 */ 138 public static void waitQueueEmpty(long emptyTime, TestOut output, Timeouts timeouts) { 139 QueueTool qt = new QueueTool(); 140 qt.setTimeouts(timeouts.cloneThis()); 141 qt.getTimeouts(). 142 setTimeout("QueueTool.WaitQueueEmptyTimeout", 143 JemmyProperties. 144 getCurrentTimeout("EventDispatcher.WaitQueueEmptyTimeout")); 145 qt.setOutput(output); 146 qt.waitEmpty(emptyTime); 147 } 148 149 /** 150 * Waits for the managed component's {@code java.awt.EventQueue} to 151 * stay empty. Uses default output and timeouts. The timeout for this wait 152 * is EventDispatcher.WaitQueueEmptyTimeout. 153 * 154 * @param emptyTime The time that the event queue has to stay empty to avoid 155 * a TimeoutExpiredException. 156 * @throws TimeoutExpiredException 157 * @see org.netbeans.jemmy.QueueTool 158 */ 159 public static void waitQueueEmpty(long emptyTime) { 160 waitQueueEmpty(emptyTime, 161 JemmyProperties.getCurrentOutput(), 162 JemmyProperties.getCurrentTimeouts()); 163 } 164 165 /** 166 * Get a string representation for key modifiers. Used to print trace. 167 * 168 * @param modifiers Bit mask of keyboard event modifiers. 169 * @return a string representation for the keyboard event modifiers. 170 */ 171 public static String getModifiersString(int modifiers) { 172 String result = ""; 173 if ((modifiers & InputEvent.CTRL_MASK) != 0) { 174 result = result + "CTRL_MASK | "; 175 } 176 if ((modifiers & InputEvent.META_MASK) != 0) { 177 result = result + "META_MASK | "; 178 } 179 if ((modifiers & InputEvent.ALT_MASK) != 0) { 180 result = result + "ALT_MASK | "; 181 } 182 if ((modifiers & InputEvent.ALT_GRAPH_MASK) != 0) { 183 result = result + "ALT_GRAPH_MASK | "; 184 } 185 if ((modifiers & InputEvent.SHIFT_MASK) != 0) { 186 result = result + "SHIFT_MASK | "; 187 } 188 if (result.length() > 0) { 189 return result.substring(0, result.length() - 3); 190 } 191 return result; 192 } 193 194 /** 195 * Returns a string representation for a keyboard event. Used to print 196 * trace. 197 * 198 * @param keyCode Key code ({@code KeyEvent.VK_*} value) 199 * @return the KeyEvent field name. 200 */ 201 public static String getKeyDescription(int keyCode) { 202 for (Field keyField : keyFields) { 203 try { 204 if (keyField.getName().startsWith("VK_") 205 && keyField.getInt(null) == keyCode) { 206 return keyField.getName(); 207 } 208 } catch (IllegalAccessException e) { 209 JemmyProperties.getCurrentOutput().printStackTrace(e); 210 } 211 } 212 return "VK_UNKNOWN"; 213 } 214 215 /** 216 * Returns a mouse button string representation. Used to print trace. 217 * 218 * @param button Mouse button ({@code InputEvent.BUTTON1/2/3_MASK} 219 * value). 220 * @return InputEvent field name. 221 */ 222 public static String getMouseButtonDescription(int button) { 223 String result; 224 if ((button & InputEvent.BUTTON1_MASK) != 0) { 225 result = "BUTTON1"; 226 } else if ((button & InputEvent.BUTTON2_MASK) != 0) { 227 result = "BUTTON2"; 228 } else if ((button & InputEvent.BUTTON3_MASK) != 0) { 229 result = "BUTTON3"; 230 } else { 231 result = "UNKNOWN_BUTTON"; 232 } 233 return result; 234 } 235 236 public static void performInit() { 237 Timeouts.initDefault("EventDispatcher.WaitQueueEmptyTimeout", WAIT_QUEUE_EMPTY_TIMEOUT); 238 Timeouts.initDefault("EventDispatcher.RobotAutoDelay", ROBOT_AUTO_DELAY); 239 Timeouts.initDefault("EventDispatcher.WaitComponentUnderMouseTimeout", WAIT_COMPONENT_UNDER_MOUSE_TIMEOUT); 240 try { 241 keyFields = Class.forName("java.awt.event.KeyEvent").getFields(); 242 } catch (ClassNotFoundException e) { 243 JemmyProperties.getCurrentOutput().printStackTrace(e); 244 } 245 } 246 247 static { 248 performInit(); 249 } 250 251 /** 252 * Wait (or not) for the mouse to move over a Java component before 253 * pressing. This option is relevant when using {@code java.awt.Robot} 254 * to generate mouse events. If a mouse press occurs at a position not 255 * occupied by a known Java component then a 256 * {@code NoComponentUnderMouseException} will be thrown. 257 * 258 * @param yesOrNo if {@code true} then the test system will wait for 259 * the mouse to move over a Java component before pressing. therwise, mouse 260 * presses can take place anywhere on the screen. 261 */ 262 public void checkComponentUnderMouse(boolean yesOrNo) { 263 outsider = !yesOrNo; 264 } 265 266 /** 267 * Defines print output streams or writers. 268 * 269 * @param out Identify the streams or writers used for print output. 270 * @see org.netbeans.jemmy.Outputable 271 * @see org.netbeans.jemmy.TestOut 272 * @see #getOutput 273 */ 274 @Override 275 public void setOutput(TestOut out) { 276 output = out; 277 queueTool.setOutput(out); 278 } 279 280 /** 281 * Returns print output streams or writers. 282 * 283 * @return an object that contains references to objects for printing to 284 * output and err streams. 285 * @see org.netbeans.jemmy.Outputable 286 * @see org.netbeans.jemmy.TestOut 287 * @see #setOutput 288 */ 289 @Override 290 public TestOut getOutput() { 291 return output; 292 } 293 294 /** 295 * Defines current timeouts. 296 * 297 * @param timeouts A collection of timeout assignments. 298 * @see org.netbeans.jemmy.Timeoutable 299 * @see org.netbeans.jemmy.Timeouts 300 * @see #getTimeouts 301 */ 302 @Override 303 public void setTimeouts(Timeouts timeouts) { 304 this.timeouts = timeouts; 305 queueTool.setTimeouts(timeouts); 306 queueTool.getTimeouts(). 307 setTimeout("QueueTool.WaitQueueEmptyTimeout", 308 timeouts. 309 getTimeout("EventDispatcher.WaitQueueEmptyTimeout")); 310 if (robotReference != null) { 311 try { 312 Object[] params = {(int) timeouts.getTimeout("EventDispatcher.RobotAutoDelay")}; 313 Class<?>[] paramClasses = {Integer.TYPE}; 314 robotReference.invokeMethod("setAutoDelay", params, paramClasses); 315 } catch (InvocationTargetException 316 | IllegalStateException 317 | NoSuchMethodException 318 | IllegalAccessException e) { 319 output.printStackTrace(e); 320 } 321 } 322 } 323 324 /** 325 * Return current timeouts. 326 * 327 * @return the collection of current timeout assignments. 328 * @see org.netbeans.jemmy.Timeoutable 329 * @see org.netbeans.jemmy.Timeouts 330 * @see #setTimeouts 331 */ 332 @Override 333 public Timeouts getTimeouts() { 334 return timeouts; 335 } 336 337 /** 338 * Defines dispatching model. 339 * 340 * @param m New model value. 341 * @see #getDispatchingModel() 342 * @see org.netbeans.jemmy.JemmyProperties#QUEUE_MODEL_MASK 343 * @see org.netbeans.jemmy.JemmyProperties#ROBOT_MODEL_MASK 344 * @see org.netbeans.jemmy.JemmyProperties#getCurrentDispatchingModel() 345 * @see org.netbeans.jemmy.JemmyProperties#setCurrentDispatchingModel(int) 346 * @see org.netbeans.jemmy.JemmyProperties#initDispatchingModel(boolean, 347 * boolean) 348 * @see org.netbeans.jemmy.JemmyProperties#initDispatchingModel() 349 */ 350 public void setDispatchingModel(int m) { 351 model = m; 352 if ((model & JemmyProperties.ROBOT_MODEL_MASK) != 0) { 353 createRobot(); 354 try { 355 Object[] params = {(model & JemmyProperties.QUEUE_MODEL_MASK) != 0 ? Boolean.TRUE : Boolean.FALSE}; 356 Class<?>[] paramClasses = {Boolean.TYPE}; 357 robotReference.invokeMethod("setAutoWaitForIdle", params, paramClasses); 358 } catch (InvocationTargetException 359 | IllegalStateException 360 | NoSuchMethodException 361 | IllegalAccessException e) { 362 output.printStackTrace(e); 363 } 364 } 365 } 366 367 /** 368 * Gets the dispatching model value. 369 * 370 * @return the model value. 371 * @see #setDispatchingModel(int) 372 * @see org.netbeans.jemmy.JemmyProperties#QUEUE_MODEL_MASK 373 * @see org.netbeans.jemmy.JemmyProperties#ROBOT_MODEL_MASK 374 * @see org.netbeans.jemmy.JemmyProperties#getCurrentDispatchingModel() 375 * @see org.netbeans.jemmy.JemmyProperties#setCurrentDispatchingModel(int) 376 * @see org.netbeans.jemmy.JemmyProperties#initDispatchingModel(boolean, 377 * boolean) 378 * @see org.netbeans.jemmy.JemmyProperties#initDispatchingModel() 379 */ 380 public int getDispatchingModel() { 381 return model; 382 } 383 384 /** 385 * Dispatches {@code AWTEvent} to component passed in constructor. If 386 * {@code (getDispatchingModel & JemmyProperties.QUEUE_MODEL_MASK) == 0} 387 * dispatched event directly, otherwise uses 388 * {@code javax.swing.SwingUtilities.invokeAndWait(Runnable)}<BR> 389 * 390 * @param event AWTEvent instance to be dispatched. 391 * @throws ComponentIsNotVisibleException 392 * @throws ComponentIsNotFocusedException 393 */ 394 public void dispatchEvent(final AWTEvent event) { 395 // run in dispatch thread 396 String eventToString = queueTool.invokeSmoothly( 397 new QueueTool.QueueAction<String>("event.toString()") { 398 @Override 399 public String launch() { 400 return event.toString(); 401 } 402 } 403 ); 404 output.printLine("Dispatch event " + eventToString); 405 output.printGolden("Dispatch event " + event.getClass().toString()); 406 Dispatcher<Void> disp = new Dispatcher<>(event); 407 queueTool.invokeAndWait(disp); 408 } 409 410 /** 411 * Dispatches a MouseEvent. 412 * 413 * @see #dispatchEvent(AWTEvent) 414 * @param id {@code MouseEvent.MOUSE_*} value 415 * @param mods {@code InputEvent.MOUSE1/2/3_BUTTON} | (modifiers value) 416 * @param clickCount Click count 417 * @param x Horizontal click point coordinate. 418 * @param y vertical click point coordinate. 419 * @param popup Defines if mouse event is a popup event. 420 */ 421 public void dispatchMouseEvent(int id, int mods, int clickCount, int x, int y, 422 boolean popup) { 423 MouseEvent event = new MouseEvent(component, id, System.currentTimeMillis(), 424 mods, x, y, clickCount, popup); 425 dispatchEvent(event); 426 } 427 428 /** 429 * Dispatches MouseEvent at the center of component. 430 * 431 * @see #dispatchEvent(AWTEvent) 432 * @param id {@code MouseEvent.MOUSE_*} value 433 * @param mods {@code InputEvent.MOUSE1/2/3_BUTTON} | (modiviers value) 434 * @param clickCount Click count 435 * @param popup Difines if mouse event is popup event. 436 */ 437 public void dispatchMouseEvent(int id, int mods, int clickCount, 438 boolean popup) { 439 int x = component.getWidth() / 2; 440 int y = component.getHeight() / 2; 441 dispatchMouseEvent(id, mods, clickCount, x, y, popup); 442 } 443 444 /** 445 * Dispatches WindowEvent. 446 * 447 * @see #dispatchEvent(AWTEvent) 448 * @param id {@code WindowEvent.WINDOW_*} value 449 */ 450 public void dispatchWindowEvent(int id) { 451 WindowEvent event = new WindowEvent((Window) component, id); 452 dispatchEvent(event); 453 } 454 455 /** 456 * Dispatches KeyEvent. 457 * 458 * @see #dispatchEvent(AWTEvent) 459 * @param id {@code KeyEvent.KEY_PRESSED} or 460 * {@code KeyEvent.KEY_RELEASED} value. 461 * @param mods Modifiers. 462 * @param keyCode Key code, 463 */ 464 @Deprecated 465 public void dispatchKeyEvent(int id, int mods, int keyCode) { 466 KeyEvent event = new KeyEvent(component, id, System.currentTimeMillis(), mods, keyCode); 467 dispatchEvent(event); 468 } 469 470 /** 471 * Dispatches KeyEvent. 472 * 473 * @see #dispatchEvent(AWTEvent) 474 * @param id {@code KeyEvent.KEY_TYPED} value. 475 * @param mods Modifiers. 476 * @param keyCode Key code, 477 * @param keyChar Char to be tiped 478 */ 479 public void dispatchKeyEvent(int id, int mods, int keyCode, char keyChar) { 480 KeyEvent event = new KeyEvent(component, id, System.currentTimeMillis(), 481 mods, keyCode, keyChar); 482 dispatchEvent(event); 483 } 484 485 /** 486 * Waits until all events currently on the event queue have been processed. 487 */ 488 public void waitForIdle() { 489 makeRobotOperation("waitForIdle", null, null); 490 } 491 492 /** 493 * Bind horizontal relative cursor coordinate to screen coordinate. 494 * 495 * @param x Relative coordinate 496 * @return Absolute coordinate 497 */ 498 protected int getAbsoluteX(int x) { 499 return (int) component.getLocationOnScreen().getX() + x; 500 } 501 502 /** 503 * Bind vertical relative cursor coordinate to screen coordinate. 504 * 505 * @param y Relative coordinate 506 * @return Absolute coordinate 507 */ 508 protected int getAbsoluteY(int y) { 509 return (int) component.getLocationOnScreen().getY() + y; 510 } 511 512 /** 513 * Delays robot. 514 * 515 * @param time Time to dalay robot for. 516 */ 517 public void delayRobot(long time) { 518 Object[] params = {(int) time}; 519 Class<?>[] paramClasses = {Integer.TYPE}; 520 makeRobotOperation("delay", params, paramClasses); 521 } 522 523 /** 524 * Moves mouse by robot. 525 * 526 * @param x Component relative horizontal coordinate. 527 * @param y Component relative vertical coordinate. 528 * @throws ComponentIsNotVisibleException 529 */ 530 public void robotMoveMouse(int x, int y) { 531 if (motionListener == null) { 532 initMotionListener(); 533 } 534 output.printLine("Move mouse to (" + Integer.toString(x) + "," 535 + Integer.toString(y) + ")"); 536 Object[] params = {getAbsoluteX(x), getAbsoluteY(y)}; 537 Class<?>[] paramClasses = {Integer.TYPE, Integer.TYPE}; 538 makeRobotOperation("mouseMove", params, paramClasses); 539 } 540 541 /** 542 * Press mouse button by robot. 543 * 544 * @param button Mouse button (InputEvent.MOUSE1/2/3_BUTTON value) 545 * @param modifiers Modifiers 546 * @throws ComponentIsNotVisibleException 547 */ 548 public void robotPressMouse(int button, int modifiers) { 549 if (!outsider) { 550 waitMouseOver(); 551 } 552 robotPressModifiers(modifiers); 553 output.printLine("Press " + getMouseButtonDescription(button) + " mouse button"); 554 Object[] params = {button}; 555 Class<?>[] paramClasses = {Integer.TYPE}; 556 makeRobotOperation("mousePress", params, paramClasses); 557 } 558 559 /** 560 * Press mouse button with 0 modifiers. 561 * 562 * @param button Mouse button ({@code InputEvent.MOUSE1/2/3_BUTTON} 563 * value) 564 * @see #robotPressMouse(int, int) 565 */ 566 public void robotPressMouse(int button) { 567 robotPressMouse(button, 0); 568 } 569 570 /** 571 * Releases mouse button by robot. 572 * 573 * @param button Mouse button ({@code InputEvent.MOUSE1/2/3_BUTTON} 574 * value) 575 * @param modifiers Modifiers 576 * @throws ComponentIsNotVisibleException 577 */ 578 public void robotReleaseMouse(int button, int modifiers) { 579 output.printLine("Release " + getMouseButtonDescription(button) + " mouse button"); 580 Object[] params = {button}; 581 Class<?>[] paramClasses = {Integer.TYPE}; 582 makeRobotOperation("mouseRelease", params, paramClasses); 583 robotReleaseModifiers(modifiers); 584 } 585 586 /** 587 * Releases mouse button with 0 modifiers. 588 * 589 * @param button Mouse button ({@code InputEvent.MOUSE1/2/3_BUTTON} 590 * value) 591 * @see #robotReleaseMouse(int, int) 592 */ 593 public void robotReleaseMouse(int button) { 594 robotReleaseMouse(button, 0); 595 } 596 597 /** 598 * Press a key using {@code java.awt.Robot}. 599 * 600 * @param keyCode Key ({@code KeyEvent.VK_*} value) 601 * @param modifiers Mask of KeyEvent modifiers. 602 * @throws ComponentIsNotVisibleException 603 * @throws ComponentIsNotFocusedException 604 */ 605 public void robotPressKey(int keyCode, int modifiers) { 606 robotPressModifiers(modifiers); 607 output.printLine("Press " + getKeyDescription(keyCode) + " key"); 608 Object[] params = {keyCode}; 609 Class<?>[] paramClasses = {Integer.TYPE}; 610 makeRobotOperation("keyPress", params, paramClasses); 611 } 612 613 /** 614 * Press key with no modifiers using {@code java.awt.Robot}. 615 * 616 * @param keyCode Key ({@code KeyEvent.VK_*} value) 617 * @see #robotPressKey(int, int) 618 */ 619 public void robotPressKey(int keyCode) { 620 robotPressKey(keyCode, 0); 621 } 622 623 /** 624 * Releases key by robot. 625 * 626 * @param keyCode Key ({@code KeyEvent.VK_*} value) 627 * @param modifiers Mask of KeyEvent modifiers. 628 * @throws ComponentIsNotVisibleException 629 * @throws ComponentIsNotFocusedException 630 */ 631 public void robotReleaseKey(int keyCode, int modifiers) { 632 output.printLine("Release " + getKeyDescription(keyCode) + " key"); 633 Object[] params = {keyCode}; 634 Class<?>[] paramClasses = {Integer.TYPE}; 635 makeRobotOperation("keyRelease", params, paramClasses); 636 robotReleaseModifiers(modifiers); 637 } 638 639 /** 640 * Releases key with 0 modifiers. 641 * 642 * @param keyCode Key ({@code KeyEvent.VK_*} value) 643 * @see #robotPressKey(int, int) 644 */ 645 public void robotReleaseKey(int keyCode) { 646 robotReleaseKey(keyCode, 0); 647 } 648 649 /** 650 * Invokes component method through 651 * {@code SwingUtilities.invokeAndWait(Runnable)}. 652 * 653 * @param method_name Name of a method to be invoked 654 * @param params Method params 655 * @param params_classes Method params' classes 656 * @return an Object - methods result. 657 * @see org.netbeans.jemmy.ClassReference 658 * @exception IllegalAccessException 659 * @exception NoSuchMethodException 660 * @exception IllegalStateException 661 * @exception InvocationTargetException 662 */ 663 public Object invokeMethod(String method_name, Object[] params, Class<?>[] params_classes) 664 throws InvocationTargetException, IllegalStateException, NoSuchMethodException, IllegalAccessException { 665 Invoker invk = new Invoker(method_name, params, params_classes); 666 try { 667 return queueTool.invokeAndWait(invk); 668 } catch (JemmyException e) { 669 Exception ex = invk.getException(); 670 if (ex != null) { 671 if (ex instanceof InvocationTargetException) { 672 InvocationTargetException ite = (InvocationTargetException) ex; 673 ite.addSuppressed(e); 674 throw ite; 675 } else if (ex instanceof IllegalStateException) { 676 IllegalStateException ise = (IllegalStateException) ex; 677 ise.addSuppressed(e); 678 throw ise; 679 } else if (ex instanceof NoSuchMethodException) { 680 NoSuchMethodException nsme = (NoSuchMethodException) ex; 681 nsme.addSuppressed(e); 682 throw nsme; 683 } else if (ex instanceof IllegalAccessException) { 684 IllegalAccessException iae = (IllegalAccessException) ex; 685 iae.addSuppressed(e); 686 throw iae; 687 } else { 688 e.addSuppressed(ex); 689 } 690 } 691 throw (e); 692 } 693 } 694 695 /** 696 * Gets component field value through 697 * {@code SwingUtilities.invokeAndWait(Runnable)}. 698 * 699 * @param field_name Name of a field 700 * @see #setField(String, Object) 701 * @see org.netbeans.jemmy.ClassReference 702 * @return an Object - field value 703 * @exception IllegalAccessException 704 * @exception IllegalStateException 705 * @exception InvocationTargetException 706 * @exception NoSuchFieldException 707 */ 708 public Object getField(String field_name) 709 throws InvocationTargetException, IllegalStateException, NoSuchFieldException, IllegalAccessException { 710 Getter gtr = new Getter(field_name); 711 try { 712 return queueTool.invokeAndWait(gtr); 713 } catch (JemmyException e) { 714 Exception ex = gtr.getException(); 715 if (ex != null) { 716 if (ex instanceof InvocationTargetException) { 717 InvocationTargetException ite = (InvocationTargetException) ex; 718 ite.addSuppressed(e); 719 throw ite; 720 } else if (ex instanceof IllegalStateException) { 721 IllegalStateException ise = (IllegalStateException) ex; 722 ise.addSuppressed(e); 723 throw ise; 724 } else if (ex instanceof NoSuchFieldException) { 725 NoSuchFieldException nsfe = (NoSuchFieldException) ex; 726 nsfe.addSuppressed(e); 727 throw nsfe; 728 } else if (ex instanceof IllegalAccessException) { 729 IllegalAccessException iae = (IllegalAccessException) ex; 730 iae.addSuppressed(e); 731 throw iae; 732 } else { 733 e.addSuppressed(ex); 734 } 735 } 736 throw (e); 737 } 738 } 739 740 /** 741 * Sets component field value through 742 * {@code SwingUtilities.invokeAndWait(Runnable)}. 743 * 744 * @param field_name Name of a field 745 * @param newValue New field value 746 * @see #getField(String) 747 * @see org.netbeans.jemmy.ClassReference 748 * @exception IllegalAccessException 749 * @exception IllegalStateException 750 * @exception InvocationTargetException 751 * @exception NoSuchFieldException 752 */ 753 public void setField(String field_name, Object newValue) 754 throws InvocationTargetException, IllegalStateException, NoSuchFieldException, IllegalAccessException { 755 Setter str = new Setter(field_name, newValue); 756 try { 757 queueTool.invokeAndWait(str); 758 } catch (JemmyException e) { 759 Exception ex = str.getException(); 760 if (ex != null) { 761 if (ex instanceof InvocationTargetException) { 762 InvocationTargetException ite = (InvocationTargetException) ex; 763 ite.addSuppressed(e); 764 throw ite; 765 } else if (ex instanceof IllegalStateException) { 766 IllegalStateException ise = (IllegalStateException) ex; 767 ise.addSuppressed(e); 768 throw ise; 769 } else if (ex instanceof NoSuchFieldException) { 770 NoSuchFieldException nsfe = (NoSuchFieldException) ex; 771 nsfe.addSuppressed(e); 772 throw nsfe; 773 } else if (ex instanceof IllegalAccessException) { 774 IllegalAccessException iae = (IllegalAccessException) ex; 775 iae.addSuppressed(e); 776 throw iae; 777 } else { 778 e.addSuppressed(ex); 779 } 780 } 781 throw (e); 782 } 783 } 784 785 /** 786 * Invokes component method through 787 * {@code SwingUtilities.invokeAndWait(Runnable)}. and catch all 788 * exceptions. 789 * 790 * @param method_name Name of a method to be invoked 791 * @param params Method params 792 * @param params_classes Method params' classes 793 * @param out TestOut instance to print exceptions stack trace to. 794 * @return an Object - method result 795 * @see #invokeMethod(String, Object[], Class[]) 796 * @see org.netbeans.jemmy.ClassReference 797 */ 798 public Object invokeExistingMethod(String method_name, Object[] params, Class<?>[] params_classes, 799 TestOut out) { 800 try { 801 return invokeMethod(method_name, params, params_classes); 802 } catch (InvocationTargetException 803 | IllegalStateException 804 | NoSuchMethodException 805 | IllegalAccessException e) { 806 out.printStackTrace(e); 807 } 808 return null; 809 } 810 811 /** 812 * Gets component field value through 813 * {@code SwingUtilities.invokeAndWait(Runnable)}. and catch all 814 * exceptions. 815 * 816 * @param field_name Name of a field 817 * @param out TestOut instance to print exceptions stack trace to. 818 * @return an Object - fields value 819 * @see #getField(String) 820 * @see #setExistingField(String, Object, TestOut) 821 * @see org.netbeans.jemmy.ClassReference 822 */ 823 public Object getExistingField(String field_name, 824 TestOut out) { 825 try { 826 return getField(field_name); 827 } catch (InvocationTargetException 828 | IllegalStateException 829 | NoSuchFieldException 830 | IllegalAccessException e) { 831 out.printStackTrace(e); 832 } 833 return null; 834 } 835 836 /** 837 * Sets component field value through 838 * {@code SwingUtilities.invokeAndWait(Runnable)}. and catch all 839 * exceptions. 840 * 841 * @param field_name Name of a field 842 * @param newValue New field value 843 * @param out TestOut instance to print exceptions stack trace to. 844 * @see #setField(String, Object) 845 * @see #getExistingField(String, TestOut) 846 * @see org.netbeans.jemmy.ClassReference 847 */ 848 public void setExistingField(String field_name, Object newValue, 849 TestOut out) { 850 try { 851 setField(field_name, newValue); 852 } catch (InvocationTargetException 853 | IllegalStateException 854 | NoSuchFieldException 855 | IllegalAccessException e) { 856 out.printStackTrace(e); 857 } 858 } 859 860 /** 861 * Invokes component method through 862 * {@code SwingUtilities.invokeAndWait(Runnable)}. and catch all 863 * exceptions. Exceptions are printed into TestOut object defined by 864 * setOutput(TestOut) method. 865 * 866 * @param method_name Name of a method to be invoked 867 * @param params Method params 868 * @param params_classes Method params' classes 869 * @return an Object - method result 870 * @see #invokeExistingMethod(String, Object[], Class[], TestOut) 871 * @see org.netbeans.jemmy.ClassReference 872 */ 873 public Object invokeExistingMethod(String method_name, Object[] params, Class<?>[] params_classes) { 874 return invokeExistingMethod(method_name, params, params_classes, output); 875 } 876 877 /** 878 * Gets component field value through 879 * {@code SwingUtilities.invokeAndWait(Runnable)}. and catch all 880 * exceptions. Exceptions are printed into TestOut object defined by 881 * setOutput(TestOut) method. 882 * 883 * @param field_name Name of a field 884 * @return an Object - fields value 885 * @see #getExistingField(String, TestOut) 886 * @see #setExistingField(String, Object) 887 * @see org.netbeans.jemmy.ClassReference 888 */ 889 public Object getExistingField(String field_name) { 890 return getExistingField(field_name, output); 891 } 892 893 /** 894 * Sets component field value through 895 * {@code SwingUtilities.invokeAndWait(Runnable)}. and catch all 896 * exceptions. Exceptions are printed into TestOut object defined by 897 * setOutput(TestOut) method. 898 * 899 * @param field_name Name of a field 900 * @param newValue New field value 901 * @see #setExistingField(String, Object, TestOut) 902 * @see #getExistingField(String) 903 * @see org.netbeans.jemmy.ClassReference 904 */ 905 public void setExistingField(String field_name, Object newValue) { 906 setExistingField(field_name, newValue, output); 907 } 908 909 //recursivelly releases all modifiers keys 910 private void robotReleaseModifiers(int modifiers) { 911 if ((modifiers & InputEvent.SHIFT_MASK) != 0) { 912 robotReleaseKey(KeyEvent.VK_SHIFT, modifiers - (InputEvent.SHIFT_MASK & modifiers)); 913 } else if ((modifiers & InputEvent.ALT_GRAPH_MASK) != 0) { 914 robotReleaseKey(KeyEvent.VK_ALT_GRAPH, modifiers - (InputEvent.ALT_GRAPH_MASK & modifiers)); 915 } else if ((modifiers & InputEvent.ALT_MASK) != 0) { 916 robotReleaseKey(KeyEvent.VK_ALT, modifiers - (InputEvent.ALT_MASK & modifiers)); 917 } else if ((modifiers & InputEvent.META_MASK) != 0) { 918 robotReleaseKey(KeyEvent.VK_META, modifiers - (InputEvent.META_MASK & modifiers)); 919 } else if ((modifiers & InputEvent.CTRL_MASK) != 0) { 920 robotReleaseKey(KeyEvent.VK_CONTROL, modifiers - (InputEvent.CTRL_MASK & modifiers)); 921 } 922 } 923 924 //throws ComponentIsNotVisibleException if component is not visible 925 private void checkVisibility() { 926 if (!component.isVisible()) { 927 throw (new ComponentIsNotVisibleException(component)); 928 } 929 } 930 931 //throws ComponentIsNotFocusedException if component has not focus 932 private void checkFocus() { 933 if (!component.hasFocus()) { 934 throw (new ComponentIsNotFocusedException(component)); 935 } 936 } 937 938 //creates java.awt.Robot instance 939 private void createRobot() { 940 try { 941 ClassReference robotClassReverence = new ClassReference("java.awt.Robot"); 942 robotReference = new ClassReference(robotClassReverence.newInstance(null, null)); 943 } catch (ClassNotFoundException 944 | InstantiationException 945 | InvocationTargetException 946 | IllegalStateException 947 | NoSuchMethodException 948 | IllegalAccessException e) { 949 output.printStackTrace(e); 950 } 951 } 952 953 private void waitMouseOver() { 954 try { 955 Waiter<String, Component> wt = new Waiter<>(new Waitable<String, Component>() { 956 @Override 957 public String actionProduced(Component obj) { 958 if (motionListener.getComponent() != null) { 959 return ""; 960 } else { 961 return null; 962 } 963 } 964 965 @Override 966 public String getDescription() { 967 return "Mouse over component"; 968 } 969 970 @Override 971 public String toString() { 972 return "waitMouseOver.Waiter{" + getDescription() + '}'; 973 } 974 }); 975 wt.setTimeoutsToCloneOf(timeouts, "EventDispatcher.WaitComponentUnderMouseTimeout"); 976 wt.setOutput(output.createErrorOutput()); 977 wt.waitAction(component); 978 } catch (InterruptedException e) { 979 output.printStackTrace(e); 980 } catch (TimeoutExpiredException e) { 981 throw (new NoComponentUnderMouseException()); 982 } 983 } 984 985 //produce a robot operations through reflection 986 private void makeRobotOperation(String method, Object[] params, Class<?>[] paramClasses) { 987 try { 988 robotReference.invokeMethod(method, params, paramClasses); 989 } catch (InvocationTargetException 990 | IllegalStateException 991 | NoSuchMethodException 992 | IllegalAccessException e) { 993 output.printStackTrace(e); 994 } 995 if ((model & JemmyProperties.QUEUE_MODEL_MASK) != 0) { 996 try { 997 waitQueueEmpty(output.createErrorOutput(), timeouts); 998 } catch (TimeoutExpiredException e) { 999 output.printStackTrace(e); 1000 } 1001 } 1002 } 1003 1004 //recursivelly presses all modifiers keys 1005 private void robotPressModifiers(int modifiers) { 1006 if ((modifiers & InputEvent.SHIFT_MASK) != 0) { 1007 robotPressKey(KeyEvent.VK_SHIFT, modifiers & ~InputEvent.SHIFT_MASK); 1008 } else if ((modifiers & InputEvent.ALT_GRAPH_MASK) != 0) { 1009 robotPressKey(KeyEvent.VK_ALT_GRAPH, modifiers & ~InputEvent.ALT_GRAPH_MASK); 1010 } else if ((modifiers & InputEvent.ALT_MASK) != 0) { 1011 robotPressKey(KeyEvent.VK_ALT, modifiers & ~InputEvent.ALT_MASK); 1012 } else if ((modifiers & InputEvent.META_MASK) != 0) { 1013 robotPressKey(KeyEvent.VK_META, modifiers & ~InputEvent.META_MASK); 1014 } else if ((modifiers & InputEvent.CTRL_MASK) != 0) { 1015 robotPressKey(KeyEvent.VK_CONTROL, modifiers & ~InputEvent.CTRL_MASK); 1016 } 1017 } 1018 1019 private void initMotionListener() { 1020 synchronized(EventDispatcher.class) { 1021 if (motionListener == null) { 1022 motionListener = new MotionListener(); 1023 Toolkit.getDefaultToolkit().addAWTEventListener(motionListener, AWTEvent.MOUSE_EVENT_MASK); 1024 Object[] params = new Object[2]; 1025 Class<?>[] paramClasses = {Integer.TYPE, Integer.TYPE}; 1026 params[0] = getAbsoluteX(-1); 1027 params[1] = getAbsoluteX(-1); 1028 makeRobotOperation("mouseMove", params, paramClasses); 1029 params[0] = getAbsoluteX(0); 1030 params[1] = getAbsoluteX(0); 1031 makeRobotOperation("mouseMove", params, paramClasses); 1032 } 1033 } 1034 } 1035 1036 private class Dispatcher<R> extends QueueTool.QueueAction<R> { 1037 1038 AWTEvent event; 1039 1040 public Dispatcher(AWTEvent e) { 1041 super(e.getClass().getName() + " event dispatching"); 1042 event = e; 1043 } 1044 1045 @Override 1046 public R launch() { 1047 if (event instanceof MouseEvent || event instanceof KeyEvent) { 1048 checkVisibility(); 1049 } 1050 component.dispatchEvent(event); 1051 return null; 1052 } 1053 } 1054 1055 private class Invoker extends QueueTool.QueueAction<Object> { 1056 1057 protected String methodName; 1058 protected Object[] params; 1059 protected Class<?>[] paramClasses; 1060 1061 public Invoker(String mn, Object[] p, Class<?>[] pc) { 1062 super(mn + " method invocation"); 1063 methodName = mn; 1064 params = p; 1065 paramClasses = pc; 1066 } 1067 1068 @Override 1069 public Object launch() 1070 throws InvocationTargetException, NoSuchMethodException, IllegalAccessException { 1071 checkVisibility(); 1072 if (methodName.equals("keyPress") || methodName.equals("keyRelease")) { 1073 checkFocus(); 1074 } 1075 return reference.invokeMethod(methodName, params, paramClasses); 1076 } 1077 } 1078 1079 private class Getter extends QueueTool.QueueAction<Object> { 1080 1081 String fieldName; 1082 1083 public Getter(String fn) { 1084 super(fn + " field receiving"); 1085 fieldName = fn; 1086 } 1087 1088 @Override 1089 public Object launch() 1090 throws InvocationTargetException, NoSuchFieldException, IllegalAccessException { 1091 return reference.getField(fieldName); 1092 } 1093 } 1094 1095 private class Setter extends QueueTool.QueueAction<Object> { 1096 1097 String fieldName; 1098 Object newValue; 1099 1100 public Setter(String fn, Object nv) { 1101 super(fn + " field changing"); 1102 fieldName = fn; 1103 newValue = nv; 1104 } 1105 1106 @Override 1107 public Object launch() 1108 throws InvocationTargetException, NoSuchFieldException, IllegalAccessException { 1109 reference.setField(fieldName, newValue); 1110 return null; 1111 } 1112 } 1113 1114 private static class MotionListener implements AWTEventListener { 1115 1116 private volatile Component mouseComponent; 1117 1118 @Override 1119 public void eventDispatched(AWTEvent event) { 1120 if (event instanceof MouseEvent) { 1121 MouseEvent e = (MouseEvent) event; 1122 if (e.getID() == MouseEvent.MOUSE_ENTERED) { 1123 mouseComponent = e.getComponent(); 1124 } else if (e.getID() == MouseEvent.MOUSE_EXITED) { 1125 mouseComponent = null; 1126 } 1127 } 1128 } 1129 1130 public Component getComponent() { 1131 return mouseComponent; 1132 } 1133 } 1134} 1135