1 /* 2 * Copyright (c) 1997, 2018, 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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 package org.netbeans.jemmy.operators; 26 27 import java.awt.Component; 28 import java.awt.event.InputEvent; 29 import java.lang.reflect.InvocationTargetException; 30 import java.util.Hashtable; 31 import java.util.StringTokenizer; 32 import java.util.Vector; 33 34 import org.netbeans.jemmy.Action; 35 import org.netbeans.jemmy.ActionProducer; 36 import org.netbeans.jemmy.CharBindingMap; 37 import org.netbeans.jemmy.ClassReference; 38 import org.netbeans.jemmy.ComponentChooser; 39 import org.netbeans.jemmy.ComponentSearcher; 40 import org.netbeans.jemmy.JemmyException; 41 import org.netbeans.jemmy.JemmyProperties; 42 import org.netbeans.jemmy.Outputable; 43 import org.netbeans.jemmy.QueueTool; 44 import org.netbeans.jemmy.TestOut; 45 import org.netbeans.jemmy.TimeoutExpiredException; 46 import org.netbeans.jemmy.Timeoutable; 47 import org.netbeans.jemmy.Timeouts; 48 import org.netbeans.jemmy.Waitable; 49 import org.netbeans.jemmy.Waiter; 50 import org.netbeans.jemmy.util.DefaultVisualizer; 51 import org.netbeans.jemmy.util.MouseVisualizer; 52 import org.netbeans.jemmy.util.Platform; 53 54 /** 55 * Keeps all environment and low-level methods. 56 * 57 * @author Alexandre Iline (alexandre.iline@oracle.com) 58 */ 59 public abstract class Operator 60 implements Timeoutable, Outputable { 61 62 /** 63 * Identifier for a "class" property. 64 * 65 * @see #getDump 66 */ 67 public static final String CLASS_DPROP = "Class"; 68 69 /** 70 * Identifier for a "toString" property. 71 * 72 * @see #getDump 73 */ 74 public static final String TO_STRING_DPROP = "toString"; 75 76 private static Vector<String> operatorPkgs; 77 78 private Timeouts timeouts; 79 private TestOut output; 80 private CharBindingMap map; 81 private ComponentVisualizer visualizer; 82 private StringComparator comparator; 83 private PathParser parser; 84 private QueueTool queueTool; 85 private boolean verification = false; 86 private JemmyProperties properties; 87 88 /** 89 * Inits environment. 90 */ 91 public Operator() { 92 super(); 93 initEnvironment(); 94 } 95 96 /** 97 * Specifies an object to be used by default to prepare component. Each new 98 * operator created after the method using will have defined visualizer. 99 * Default implementation is org.netbeans.jemmy.util.DefaultVisualizer 100 * class. 101 * 102 * @param visualizer ComponentVisualizer implementation 103 * @return previous value 104 * @see #setVisualizer(Operator.ComponentVisualizer) 105 * @see #getDefaultComponentVisualizer() 106 * @see org.netbeans.jemmy.util.DefaultVisualizer 107 */ 108 public static ComponentVisualizer setDefaultComponentVisualizer(ComponentVisualizer visualizer) { 109 return ((ComponentVisualizer) JemmyProperties. 110 setCurrentProperty("ComponentOperator.ComponentVisualizer", visualizer)); 111 } 112 113 /** 114 * Returns an object to be used by default to prepare component. 115 * 116 * @return Object is used by default to prepare component 117 * @see #getVisualizer() 118 * @see #setDefaultComponentVisualizer(Operator.ComponentVisualizer) 119 */ 120 public static ComponentVisualizer getDefaultComponentVisualizer() { 121 return ((ComponentVisualizer) JemmyProperties. 122 getCurrentProperty("ComponentOperator.ComponentVisualizer")); 123 } 124 125 /** 126 * Defines string comparator to be assigned in constructor. 127 * 128 * @param comparator the comparator to be used by default. 129 * @return previous value. 130 * @see #getDefaultStringComparator() 131 * @see Operator.StringComparator 132 */ 133 public static StringComparator setDefaultStringComparator(StringComparator comparator) { 134 return ((StringComparator) JemmyProperties. 135 setCurrentProperty("ComponentOperator.StringComparator", comparator)); 136 } 137 138 /** 139 * Returns string comparator used to init operators. 140 * 141 * @return the comparator used by default. 142 * @see #setDefaultStringComparator(Operator.StringComparator) 143 * @see Operator.StringComparator 144 */ 145 public static StringComparator getDefaultStringComparator() { 146 return ((StringComparator) JemmyProperties. 147 getCurrentProperty("ComponentOperator.StringComparator")); 148 } 149 150 /** 151 * Specifies an object used for parsing of path-like strings. 152 * 153 * @param parser the parser. 154 * @return a previous value. 155 * @see Operator.PathParser 156 * @see #getDefaultPathParser 157 */ 158 public static PathParser setDefaultPathParser(PathParser parser) { 159 return ((PathParser) JemmyProperties. 160 setCurrentProperty("ComponentOperator.PathParser", parser)); 161 } 162 163 /** 164 * Returns an object used for parsing of path-like strings. 165 * 166 * @return a parser used by default. 167 * @see Operator.PathParser 168 * @see #setDefaultPathParser 169 */ 170 public static PathParser getDefaultPathParser() { 171 return ((PathParser) JemmyProperties. 172 getCurrentProperty("ComponentOperator.PathParser")); 173 } 174 175 /** 176 * Defines whether newly created operators should perform operation 177 * verifications by default. 178 * 179 * @param verification a verification mode to be used by default. 180 * @return a previous value. 181 * @see #getDefaultVerification() 182 * @see #setVerification(boolean) 183 */ 184 public static boolean setDefaultVerification(boolean verification) { 185 Boolean oldValue = (Boolean) (JemmyProperties. 186 setCurrentProperty("Operator.Verification", 187 verification ? Boolean.TRUE : Boolean.FALSE)); 188 return (oldValue != null) ? oldValue : false; 189 } 190 191 /** 192 * Says whether newly created operators perform operations verifications by 193 * default. 194 * 195 * @return a verification mode used by default. 196 * @see #setDefaultVerification(boolean) 197 * @see #getVerification() 198 */ 199 public static boolean getDefaultVerification() { 200 return ((Boolean) (JemmyProperties. 201 getCurrentProperty("Operator.Verification"))); 202 } 203 204 /** 205 * Compares caption (button text, window title, ...) with a sample text. 206 * 207 * @param caption String to be compared with match. Method returns false, if 208 * parameter is null. 209 * @param match Sample to compare with. Method returns true, if parameter is 210 * null. 211 * @param ce Compare exactly. If true, text can be a substring of caption. 212 * @param ccs Compare case sensitively. If true, both text and caption are 213 * converted to upper case before comparison. 214 * @return true is the captions matched the match. 215 * @see #isCaptionEqual 216 * @deprecated use another methods with the same name. 217 */ 218 @Deprecated 219 public static boolean isCaptionEqual(String caption, String match, boolean ce, boolean ccs) { 220 return new DefaultStringComparator(ce, ccs).equals(caption, match); 221 } 222 223 /** 224 * Compares caption (button text, window title, ...) with a sample text. 225 * 226 * @param caption String to be compared with match 227 * @param match Sample to compare with 228 * @param comparator StringComparator instance. 229 * @return true is the captions matched the match. 230 * @see #isCaptionEqual 231 */ 232 public static boolean isCaptionEqual(String caption, String match, StringComparator comparator) { 233 return comparator.equals(caption, match); 234 } 235 236 /** 237 * Returns default mouse button mask. 238 * 239 * @return {@code InputEvent.BUTTON*_MASK} field value 240 */ 241 public static int getDefaultMouseButton() { 242 return InputEvent.BUTTON1_MASK; 243 } 244 245 /** 246 * Returns mask of mouse button which used to popup expanding. 247 * (InputEvent.BUTTON3_MASK) 248 * 249 * @return {@code InputEvent.BUTTON*_MASK} field value 250 */ 251 public static int getPopupMouseButton() { 252 return InputEvent.BUTTON3_MASK; 253 } 254 255 /** 256 * Creates operator for component. Tries to find class with "operator 257 * package"."class name"Operator name, where "operator package" is a package 258 * from operator packages list, and "class name" is the name of class or one 259 * of its superclasses. 260 * 261 * @param comp Component to create operator for. 262 * @return a new operator with default environment. 263 * @see #addOperatorPackage(String) 264 */ 265 public static ComponentOperator createOperator(Component comp) { 266 //hack! 267 try { 268 Class<?> cclass = Class.forName("java.awt.Component"); 269 Class<?> compClass = comp.getClass(); 270 ComponentOperator result; 271 do { 272 if ((result = createOperator(comp, compClass)) != null) { 273 return result; 274 } 275 } while (cclass.isAssignableFrom(compClass = compClass.getSuperclass())); 276 } catch (ClassNotFoundException ignored) { 277 } 278 return null; 279 } 280 281 /** 282 * Adds package to the list of packages containing operators. <BR> 283 * "org.netbeans.jemmy.operators" is in the list by default. 284 * 285 * @param pkgName Package name. 286 * @see #createOperator(Component) 287 */ 288 public static void addOperatorPackage(String pkgName) { 289 operatorPkgs.add(pkgName); 290 } 291 292 /** 293 * Returns an operator containing default environment. 294 * 295 * @return an empty operator (not having any component source) having 296 * default environment. 297 */ 298 public static Operator getEnvironmentOperator() { 299 return new NullOperator(); 300 } 301 302 static { 303 //init visualizer depending on OS: 304 //Linux - new MouseVisualizer(MouseVisualizer.TOP, 0.5, 10, false) 305 //solaris - new MouseVisualizer() 306 //others - new DefaultVisualizer() 307 if (Platform.isLinux()) { 308 setDefaultComponentVisualizer(new MouseVisualizer(MouseVisualizer.TOP, 0.5, 10, false)); 309 } else if (Platform.isSolaris()) { 310 setDefaultComponentVisualizer(new MouseVisualizer()); 311 } else { 312 setDefaultComponentVisualizer(new DefaultVisualizer()); 313 } 314 operatorPkgs = new Vector<>(); 315 setDefaultStringComparator(new DefaultStringComparator(false, false)); 316 setDefaultPathParser(new DefaultPathParser("|")); 317 addOperatorPackage("org.netbeans.jemmy.operators"); 318 setDefaultVerification(true); 319 } 320 321 /** 322 * Returns object operator is used for. 323 * 324 * @return an instance of java.awt.Component subclass which this operator 325 * was created for. 326 */ 327 public abstract Component getSource(); 328 329 //////////////////////////////////////////////////////// 330 //Environment // 331 //////////////////////////////////////////////////////// 332 /** 333 * Returns QueueTool is used to work with queue. 334 * 335 * @return a QueueTool. 336 */ 337 public QueueTool getQueueTool() { 338 return queueTool; 339 } 340 341 /** 342 * Copies all environment (output, timeouts, visualizer) from another 343 * operator. 344 * 345 * @param anotherOperator an operator to copy the environment to. 346 */ 347 public void copyEnvironment(Operator anotherOperator) { 348 setTimeouts(anotherOperator.getTimeouts()); 349 setOutput(anotherOperator.getOutput()); 350 setVisualizer(anotherOperator.getVisualizer()); 351 setComparator(anotherOperator.getComparator()); 352 setVerification(anotherOperator.getVerification()); 353 setCharBindingMap(anotherOperator.getCharBindingMap()); 354 setProperties(anotherOperator.getProperties()); 355 } 356 357 @Override 358 public void setTimeouts(Timeouts timeouts) { 359 this.timeouts = timeouts; 360 queueTool.setTimeouts(timeouts); 361 } 362 363 @Override 364 public Timeouts getTimeouts() { 365 return timeouts; 366 } 367 368 /** 369 * Returns component visualizer. Visualizer is used from from 370 * makeComponentVisible() method. 371 * 372 * @return a visualizer assigned to this operator. 373 * @see #getDefaultComponentVisualizer() 374 * @see #setVisualizer(Operator.ComponentVisualizer) 375 */ 376 public ComponentVisualizer getVisualizer() { 377 return visualizer; 378 } 379 380 /** 381 * Changes component visualizer. Visualizer is used from from 382 * makeComponentVisible() method. 383 * 384 * @param vo a visualizer to assign to this operator. 385 * @see #setDefaultComponentVisualizer(Operator.ComponentVisualizer) 386 * @see #getVisualizer() 387 */ 388 public void setVisualizer(ComponentVisualizer vo) { 389 visualizer = vo; 390 } 391 392 /** 393 * Returns a JemmyProperty object assigned to this operator. 394 * 395 * @return a JemmyProperty object got from the top of property stack or from 396 * another operator by copyuing environment. 397 * @see #setProperties 398 */ 399 public JemmyProperties getProperties() { 400 return properties; 401 } 402 403 /** 404 * Assigns a JemmyProperty object to this operator. 405 * 406 * @param properties a properties to assign to this operator. 407 * @return previously assigned properties. 408 * @see #getProperties 409 */ 410 public JemmyProperties setProperties(JemmyProperties properties) { 411 JemmyProperties oldProperties = getProperties(); 412 this.properties = properties; 413 return oldProperties; 414 } 415 416 /** 417 * Defines CharBindingMap. 418 * 419 * @param map a CharBindingMap to use for keyboard operations. 420 * @see org.netbeans.jemmy.CharBindingMap 421 * @see 422 * org.netbeans.jemmy.JemmyProperties#setCurrentCharBindingMap(CharBindingMap) 423 * @see #getCharBindingMap 424 */ 425 public void setCharBindingMap(CharBindingMap map) { 426 this.map = map; 427 } 428 429 /** 430 * Returns CharBindingMap used for keyboard operations. 431 * 432 * @return a map assigned to this object. 433 * @see #setCharBindingMap 434 */ 435 public CharBindingMap getCharBindingMap() { 436 return map; 437 } 438 439 @Override 440 public void setOutput(TestOut out) { 441 output = out; 442 queueTool.setOutput(output.createErrorOutput()); 443 } 444 445 @Override 446 public TestOut getOutput() { 447 return output; 448 } 449 450 /** 451 * Returns object which is used for string comparison. 452 * 453 * @return a comparator assigned to this operator. 454 * @see org.netbeans.jemmy.operators.Operator.StringComparator 455 * @see org.netbeans.jemmy.operators.Operator.DefaultStringComparator 456 * @see #setComparator 457 */ 458 public StringComparator getComparator() { 459 return comparator; 460 } 461 462 /** 463 * Defines object which is used for string comparison. 464 * 465 * @param comparator a comparator to use for string comparision. 466 * @see org.netbeans.jemmy.operators.Operator.StringComparator 467 * @see org.netbeans.jemmy.operators.Operator.DefaultStringComparator 468 * @see #getComparator 469 */ 470 public void setComparator(StringComparator comparator) { 471 this.comparator = comparator; 472 } 473 474 /** 475 * Returns object which is used for parsing of path-like strings. 476 * 477 * @return a comparator assigned to this operator. 478 * @see #setPathParser 479 */ 480 public PathParser getPathParser() { 481 return parser; 482 } 483 484 /** 485 * Specifies object which is used for parsing of path-like strings. 486 * 487 * @param parser a parser to use for path parsing. 488 * @see #getPathParser 489 */ 490 public void setPathParser(PathParser parser) { 491 this.parser = parser; 492 } 493 494 /** 495 * Defines whether operator should perform operation verifications. 496 * 497 * @param verification new value. 498 * @return old value 499 * @see #setDefaultVerification(boolean) 500 * @see #getDefaultVerification() 501 * @see #getVerification() 502 */ 503 public boolean setVerification(boolean verification) { 504 boolean oldValue = this.verification; 505 this.verification = verification; 506 return oldValue; 507 } 508 509 /** 510 * Says whether operator performs operation verifications. 511 * 512 * @return old value 513 * @see #setDefaultVerification(boolean) 514 * @see #getDefaultVerification() 515 * @see #setVerification(boolean) 516 */ 517 public boolean getVerification() { 518 return verification; 519 } 520 521 //////////////////////////////////////////////////////// 522 //Util // 523 //////////////////////////////////////////////////////// 524 /** 525 * Creates new array which has all elements from first array, except last 526 * element. 527 * 528 * @param path an original array 529 * @return new array 530 */ 531 public String[] getParentPath(String path[]) { 532 if (path.length > 1) { 533 String[] ppath = new String[path.length - 1]; 534 System.arraycopy(path, 0, ppath, 0, ppath.length); 535 return ppath; 536 } else { 537 return new String[0]; 538 } 539 } 540 541 public ComponentChooser[] getParentPath(ComponentChooser path[]) { 542 if (path.length > 1) { 543 ComponentChooser[] ppath = new ComponentChooser[path.length - 1]; 544 System.arraycopy(path, 0, ppath, 0, ppath.length); 545 return ppath; 546 } else { 547 return new ComponentChooser[0]; 548 } 549 } 550 551 /** 552 * Parses a string to a string array using a PathParser assigned to this 553 * operator. 554 * 555 * @param path an original string 556 * @return created String array. 557 */ 558 public String[] parseString(String path) { 559 return getPathParser().parse(path); 560 } 561 562 /** 563 * Parses strings like "1|2|3" into arrays {"1", "2", "3"}. 564 * 565 * @param path an original string 566 * @param delim a delimiter string 567 * @return created String array. 568 */ 569 public String[] parseString(String path, String delim) { 570 return new DefaultPathParser(delim).parse(path); 571 } 572 573 /** 574 * Returns key code to be pressed for character typing. 575 * 576 * @param c Character to be typed. 577 * @return a value of one of the {@code KeyEvent.VK_*} fields. 578 * @see org.netbeans.jemmy.CharBindingMap 579 */ 580 public int getCharKey(char c) { 581 return map.getCharKey(c); 582 } 583 584 /** 585 * Returns modifiers mask for character typing. 586 * 587 * @param c Character to be typed. 588 * @return a combination of {@code InputEvent.*_MASK} fields. 589 * @see org.netbeans.jemmy.CharBindingMap 590 */ 591 public int getCharModifiers(char c) { 592 return map.getCharModifiers(c); 593 } 594 595 /** 596 * Returns key codes to by pressed for characters typing. 597 * 598 * @param c Characters to be typed. 599 * @return an array of {@code KeyEvent.VK_*} values. 600 * @see org.netbeans.jemmy.CharBindingMap 601 */ 602 public int[] getCharsKeys(char[] c) { 603 int[] result = new int[c.length]; 604 for (int i = 0; i < c.length; i++) { 605 result[i] = getCharKey(c[i]); 606 } 607 return result; 608 } 609 610 /** 611 * Returns modifiers masks for characters typing. 612 * 613 * @param c Characters to be typed. 614 * @return an array of a combination of {@code InputEvent.*_MASK} 615 * fields. 616 * @see org.netbeans.jemmy.CharBindingMap 617 */ 618 public int[] getCharsModifiers(char[] c) { 619 int[] result = new int[c.length]; 620 for (int i = 0; i < c.length; i++) { 621 result[i] = getCharModifiers(c[i]); 622 } 623 return result; 624 } 625 626 /** 627 * Returns key codes to by pressed for the string typing. 628 * 629 * @param s String to be typed. 630 * @return an array of {@code KeyEvent.VK_*} values. 631 * @see org.netbeans.jemmy.CharBindingMap 632 */ 633 public int[] getCharsKeys(String s) { 634 return getCharsKeys(s.toCharArray()); 635 } 636 637 /** 638 * Returns modifiers masks for the string typing. 639 * 640 * @param s String to be typed. 641 * @return an array of a combination of {@code InputEvent.*_MASK} 642 * fields. 643 * @see org.netbeans.jemmy.CharBindingMap 644 */ 645 public int[] getCharsModifiers(String s) { 646 return getCharsModifiers(s.toCharArray()); 647 } 648 649 /** 650 * Compares string using getComparator StringComparator. 651 * 652 * @param caption a caption 653 * @param match a pattern 654 * @return true if {@code caption} and {@code match} match 655 * @see #isCaptionEqual 656 */ 657 public boolean isCaptionEqual(String caption, String match) { 658 return comparator.equals(caption, match); 659 } 660 661 /** 662 * Prints component information into operator output. 663 */ 664 public void printDump() { 665 Hashtable<String, Object> result = getDump(); 666 Object[] keys = result.keySet().toArray(); 667 for (int i = 0; i < result.size(); i++) { 668 output.printLine(keys[i] 669 + " = " 670 + result.get(keys[i])); 671 } 672 } 673 674 /** 675 * Returns information about component. All records marked by simbolic 676 * constants defined in public static final {@code *_DPROP} fields for 677 * each operator type. 678 * 679 * @return a Hashtable containing name-value pairs. 680 */ 681 public Hashtable<String, Object> getDump() { 682 Hashtable<String, Object> result = new Hashtable<>(); 683 result.put(CLASS_DPROP, getSource().getClass().getName()); 684 result.put(TO_STRING_DPROP, getSource().toString()); 685 return result; 686 } 687 688 /** 689 * Waits a state specified by a ComponentChooser instance. 690 * 691 * @param state a ComponentChooser defining the state criteria. 692 * @throws TimeoutExpiredException if the state has not achieved in a value 693 * defined by {@code "ComponentOperator.WaitStateTimeout"} 694 */ 695 public void waitState(final ComponentChooser state) { 696 waitState(new Waitable<String, Void>() { 697 @Override 698 public String actionProduced(Void obj) { 699 return state.checkComponent(getSource()) ? "" : null; 700 } 701 702 @Override 703 public String getDescription() { 704 return "Wait \"" + state.getDescription() 705 + "\" state to be reached"; 706 } 707 708 @Override 709 public String toString() { 710 return "Operator.waitState.Waitable{description = " + getDescription() + '}'; 711 } 712 }); 713 } 714 715 public <R> R waitState(Waitable<R, Void> waitable) { 716 Waiter<R, Void> stateWaiter = new Waiter<>(waitable); 717 stateWaiter.setTimeoutsToCloneOf(getTimeouts(), 718 "ComponentOperator.WaitStateTimeout"); 719 stateWaiter.setOutput(getOutput().createErrorOutput()); 720 try { 721 return stateWaiter.waitAction(null); 722 } catch (InterruptedException e) { 723 Thread.currentThread().interrupt(); 724 throw (new JemmyException( 725 "Waiting of \"" + waitable.getDescription() 726 + "\" state has been interrupted!")); 727 } 728 } 729 730 /** 731 * Waits a state specified by a ComponentChooser instance on EDT queue. 732 * 733 * @param state a ComponentChooser defining the state criteria. 734 * @throws TimeoutExpiredException if the state has not achieved in a value 735 * defined by {@code "ComponentOperator.WaitStateTimeout"} 736 */ 737 public void waitStateOnQueue(final ComponentChooser state) { 738 waitState((comp) -> { 739 return (boolean) (queueTool.invokeSmoothly( 740 new QueueTool.QueueAction<Object>("checkComponent") { 741 @Override 742 public final Object launch() throws Exception { 743 return state.checkComponent(comp); 744 } 745 })); 746 }); 747 } 748 749 //////////////////////////////////////////////////////// 750 //Mapping // 751 //////////////////////////////////////////////////////// 752 /** 753 * Performs an operation with time control. 754 * 755 * @param action an action to execute. 756 * @param param an action parameters. 757 * @param actionTimeOrigin is a timeout name to use for waiting for the 758 * action to be finished. 759 * @return an action result. 760 */ 761 protected <R, P> R produceTimeRestricted(Action<R, P> action, final P param, 762 String actionTimeOrigin) { 763 ActionProducer<R, P> producer = new ActionProducer<>(action); 764 producer.setOutput(getOutput().createErrorOutput()); 765 producer.setTimeouts(getTimeouts().cloneThis()); 766 producer.getTimeouts().setTimeout("ActionProducer.MaxActionTime", 767 getTimeouts().getTimeout(actionTimeOrigin)); 768 try { 769 R result = producer.produceAction(param, actionTimeOrigin); 770 Throwable exception = producer.getException(); 771 if (exception != null) { 772 if (exception instanceof JemmyException) { 773 throw ((JemmyException) exception); 774 } else { 775 throw (new JemmyException("Exception during " + action.getDescription(), 776 exception)); 777 } 778 } 779 return result; 780 } catch (InterruptedException e) { 781 throw (new JemmyException("Interrupted!", e)); 782 } 783 } 784 785 /** 786 * Performs an operation with time control. 787 * 788 * @param action an action to execute. 789 * @param actionTimeOrigin is a timeout name to use for waiting for the 790 * action to be finished. 791 * @return an action result. 792 */ 793 protected <R, P> R produceTimeRestricted(Action<R, P> action, String actionTimeOrigin) { 794 return produceTimeRestricted(action, null, actionTimeOrigin); 795 } 796 797 /** 798 * Performs an operation without time control. 799 * 800 * @param action an action to execute. 801 * @param param an action parameters. 802 */ 803 protected <R, P> void produceNoBlocking(NoBlockingAction<R, P> action, P param) { 804 try { 805 ActionProducer<R, P> noBlockingProducer = new ActionProducer<>(action, false); 806 noBlockingProducer.setOutput(output.createErrorOutput()); 807 noBlockingProducer.setTimeouts(timeouts); 808 noBlockingProducer.produceAction(param, null); 809 } catch (InterruptedException e) { 810 throw (new JemmyException("Exception during \"" 811 + action.getDescription() 812 + "\" execution", 813 e)); 814 } 815 if (action.exception != null) { 816 throw (new JemmyException("Exception during nonblocking \"" 817 + action.getDescription() + "\"", 818 action.exception)); 819 } 820 } 821 822 /** 823 * Performs an operation without time control. 824 * 825 * @param action an action to execute. 826 */ 827 protected void produceNoBlocking(NoBlockingAction<?, ?> action) { 828 produceNoBlocking(action, null); 829 } 830 831 /** 832 * Equivalent to {@code getQueue().lock();}. 833 */ 834 protected void lockQueue() { 835 queueTool.lock(); 836 } 837 838 /** 839 * Equivalent to {@code getQueue().unlock();}. 840 */ 841 protected void unlockQueue() { 842 queueTool.unlock(); 843 } 844 845 /** 846 * Unlocks Queue and then throw exception. 847 * 848 * @param e an exception to be thrown. 849 */ 850 protected void unlockAndThrow(Exception e) { 851 unlockQueue(); 852 throw (new JemmyException("Exception during queue locking", e)); 853 } 854 855 /** 856 * To map nonprimitive type component's method. 857 * 858 * @param action a mapping action. 859 * @return an action result. 860 * @see Operator.MapAction 861 */ 862 protected <R> R runMapping(MapAction<R> action) { 863 return runMappingPrimitive(action); 864 } 865 866 /** 867 * To map char component's method. 868 * 869 * @param action a mapping action. 870 * @return an action result. 871 * @see #runMapping(Operator.MapAction) 872 * @see Operator.MapCharacterAction 873 */ 874 protected char runMapping(MapCharacterAction action) { 875 return (Character) runMappingPrimitive(action); 876 } 877 878 /** 879 * To map byte component's method. 880 * 881 * @param action a mapping action. 882 * @return an action result. 883 * @see #runMapping(Operator.MapAction) 884 * @see Operator.MapByteAction 885 */ 886 protected byte runMapping(MapByteAction action) { 887 return (Byte) runMappingPrimitive(action); 888 } 889 890 /** 891 * To map int component's method. 892 * 893 * @param action a mapping action. 894 * @return an action result. 895 * @see #runMapping(Operator.MapAction) 896 * @see Operator.MapIntegerAction 897 */ 898 protected int runMapping(MapIntegerAction action) { 899 return (Integer) runMappingPrimitive(action); 900 } 901 902 /** 903 * To map long component's method. 904 * 905 * @param action a mapping action. 906 * @return an action result. 907 * @see #runMapping(Operator.MapAction) 908 * @see Operator.MapLongAction 909 */ 910 protected long runMapping(MapLongAction action) { 911 return (Long) runMappingPrimitive(action); 912 } 913 914 /** 915 * To map float component's method. 916 * 917 * @param action a mapping action. 918 * @return an action result. 919 * @see #runMapping(Operator.MapAction) 920 * @see Operator.MapFloatAction 921 */ 922 protected float runMapping(MapFloatAction action) { 923 return (Float) runMappingPrimitive(action); 924 } 925 926 /** 927 * To map double component's method. 928 * 929 * @param action a mapping action. 930 * @return an action result. 931 * @see #runMapping(Operator.MapAction) 932 * @see Operator.MapDoubleAction 933 */ 934 protected double runMapping(MapDoubleAction action) { 935 return (Double) runMappingPrimitive(action); 936 } 937 938 /** 939 * To map boolean component's method. 940 * 941 * @param action a mapping action. 942 * @return an action result. 943 * @see #runMapping(Operator.MapAction) 944 * @see Operator.MapBooleanAction 945 */ 946 protected boolean runMapping(MapBooleanAction action) { 947 return (Boolean) runMappingPrimitive(action); 948 } 949 950 /** 951 * To map void component's method. 952 * 953 * @param action a mapping action. 954 * @see #runMapping(Operator.MapAction) 955 * @see Operator.MapVoidAction 956 */ 957 protected void runMapping(MapVoidAction action) { 958 runMappingPrimitive(action); 959 } 960 961 /** 962 * Adds array of objects to dump hashtable. Is used for multiple properties 963 * such as list items and tree nodes. 964 * 965 * @param table a table to add properties to. 966 * @param title property names prefix. Property names are constructed by 967 * adding a number to the prefix: 968 * {@code title + "_" + Iteger.toString("ordinal index")} 969 * @param items an array of property values. 970 * @return an array of property names (with added numbers). 971 */ 972 protected String[] addToDump(Hashtable<String, Object> table, String title, Object[] items) { 973 String[] names = createNames(title + "_", items.length); 974 for (int i = 0; i < items.length; i++) { 975 table.put(names[i], items[i].toString()); 976 } 977 return names; 978 } 979 980 /** 981 * Adds two dimentional array of objects to dump hashtable. Is used for 982 * multiple properties such as table cells. 983 * 984 * @param table a table to add properties to. 985 * @param title property names prefix. Property names are constructed by 986 * adding two numbers to the prefix: 987 * {@code title + "_" + Iteger.toString("row index") + "_" + Iteger.toString("column index")} 988 * @param items an array of property values. 989 * @return an array of property names (with added numbers). 990 */ 991 protected String[] addToDump(Hashtable<String, Object> table, String title, Object[][] items) { 992 String[] names = createNames(title + "_", items.length); 993 for (int i = 0; i < items.length; i++) { 994 addToDump(table, names[i], items[i]); 995 } 996 return names; 997 } 998 //////////////////////////////////////////////////////// 999 //Private // 1000 //////////////////////////////////////////////////////// 1001 1002 private <R> R runMappingPrimitive(QueueTool.QueueAction<R> action) { 1003 return queueTool.invokeSmoothly(action); 1004 } 1005 1006 private String[] createNames(String title, int count) { 1007 String[] result = new String[count]; 1008 int indexLength = Integer.toString(count).length(); 1009 StringBuilder zeroStringB = new StringBuilder(indexLength); 1010 for (int i = 0; i < indexLength; i++) { 1011 zeroStringB.append('0'); 1012 } 1013 String zeroString = zeroStringB.toString(); 1014 for (int i = 0; i < count; i++) { 1015 String indexString = Integer.toString(i); 1016 result[i] = title 1017 + zeroString.substring(0, indexLength - indexString.length()) 1018 + indexString; 1019 } 1020 return result; 1021 } 1022 1023 private static ComponentOperator createOperator(Component comp, Class<?> compClass) { 1024 StringTokenizer token = new StringTokenizer(compClass.getName(), "."); 1025 String className = ""; 1026 while (token.hasMoreTokens()) { 1027 className = token.nextToken(); 1028 } 1029 Object[] params = {comp}; 1030 Class<?>[] param_classes = {compClass}; 1031 String operatorPackage; 1032 for (String operatorPkg : operatorPkgs) { 1033 operatorPackage = operatorPkg; 1034 try { 1035 return ((ComponentOperator) new ClassReference(operatorPackage + "." 1036 + className + "Operator"). 1037 newInstance(params, param_classes)); 1038 } catch (ClassNotFoundException ignored) { 1039 } catch (InvocationTargetException ignored) { 1040 } catch (NoSuchMethodException ignored) { 1041 } catch (IllegalAccessException ignored) { 1042 } catch (InstantiationException ignored) { 1043 } 1044 } 1045 return null; 1046 } 1047 1048 private void initEnvironment() { 1049 queueTool = new QueueTool(); 1050 setTimeouts(JemmyProperties.getProperties().getTimeouts()); 1051 setOutput(JemmyProperties.getProperties().getOutput()); 1052 setCharBindingMap(JemmyProperties.getProperties().getCharBindingMap()); 1053 setVisualizer(getDefaultComponentVisualizer()); 1054 setComparator(getDefaultStringComparator()); 1055 setVerification(getDefaultVerification()); 1056 setProperties(JemmyProperties.getProperties()); 1057 setPathParser(getDefaultPathParser()); 1058 } 1059 1060 /** 1061 * Returns toString() result from component of this operator. It calls 1062 * {@link #getSource}.toString() in dispatch thread. 1063 * 1064 * @return toString() result from component of this operator. 1065 */ 1066 public String toStringSource() { 1067 return runMapping(new MapAction<String>("getSource().toString()") { 1068 @Override 1069 public String map() { 1070 return getSource().toString(); 1071 } 1072 }); 1073 } 1074 1075 /** 1076 * Interface used to make component visible & ready to to make operations 1077 * with. 1078 */ 1079 public interface ComponentVisualizer { 1080 1081 /** 1082 * Prepares component for a user input. 1083 * 1084 * @param compOper Operator asking for necessary actions. 1085 */ 1086 public void makeVisible(ComponentOperator compOper); 1087 } 1088 1089 /** 1090 * Interface to compare string resources like labels, button text, ... with 1091 * match. <BR> 1092 */ 1093 public interface StringComparator { 1094 1095 /** 1096 * Imlementation must return true if strings are equal. 1097 * 1098 * @param caption a text to compare with pattern. 1099 * @param match a pattern 1100 * @return true if text and pattern matches. 1101 */ 1102 public boolean equals(String caption, String match); 1103 } 1104 1105 /** 1106 * Default StringComparator implementation. 1107 */ 1108 public static class DefaultStringComparator implements StringComparator { 1109 1110 boolean ce; 1111 boolean ccs; 1112 1113 /** 1114 * Constructs a DefaultStringComparator object. 1115 * 1116 * @param ce Compare exactly. If false, text can be a substring of 1117 * caption. 1118 * @param ccs Compare case sensitively. 1119 */ 1120 public DefaultStringComparator(boolean ce, boolean ccs) { 1121 this.ce = ce; 1122 this.ccs = ccs; 1123 } 1124 1125 /** 1126 * Compares a caption with a match using switched passed into 1127 * constructor. 1128 * 1129 * @param caption String to be compared with match. Method returns 1130 * false, if parameter is null. 1131 * @param match Sample to compare with. Method returns true, if 1132 * parameter is null. 1133 * @return true if text and pattern matches. 1134 */ 1135 @Override 1136 public boolean equals(String caption, String match) { 1137 if (match == null) { 1138 return true; 1139 } 1140 if (caption == null) { 1141 return false; 1142 } 1143 String c, t; 1144 if (!ccs) { 1145 c = caption.toUpperCase(); 1146 t = match.toUpperCase(); 1147 } else { 1148 c = caption; 1149 t = match; 1150 } 1151 if (ce) { 1152 return c.equals(t); 1153 } else { 1154 return c.contains(t); 1155 } 1156 } 1157 } 1158 1159 /** 1160 * Used for parsing of path-like strings. 1161 */ 1162 public interface PathParser { 1163 1164 /** 1165 * Parses a string to a String array. 1166 * 1167 * @param path a String to parse. 1168 * @return a parsed array. 1169 */ 1170 public String[] parse(String path); 1171 } 1172 1173 /** 1174 * Used for parsing of path-like strings where path components are separated 1175 * by a string-separator: "drive|directory|subdirectory|file". 1176 */ 1177 public static class DefaultPathParser implements PathParser { 1178 1179 String separator; 1180 1181 /** 1182 * Constructs a DefaultPathParser object. 1183 * 1184 * @param separator a string used as separator. 1185 */ 1186 public DefaultPathParser(String separator) { 1187 this.separator = separator; 1188 } 1189 1190 @Override 1191 public String[] parse(String path) { 1192 if (path.length() > 0) { 1193 Vector<String> parsed = new Vector<>(); 1194 int position = 0; 1195 int sepIndex = 0; 1196 while ((sepIndex = path.indexOf(separator, position)) != -1) { 1197 parsed.add(path.substring(position, sepIndex)); 1198 position = sepIndex + separator.length(); 1199 } 1200 parsed.add(path.substring(position)); 1201 String[] result = new String[parsed.size()]; 1202 for (int i = 0; i < parsed.size(); i++) { 1203 result[i] = parsed.get(i); 1204 } 1205 return result; 1206 } else { 1207 return new String[0]; 1208 } 1209 } 1210 } 1211 1212 /** 1213 * Allows to bind a component by a component type. 1214 */ 1215 public static class Finder implements ComponentChooser { 1216 1217 Class<?> clz; 1218 ComponentChooser subchooser; 1219 1220 /** 1221 * Constructs Finder. 1222 * 1223 * @param clz a component class. 1224 * @param subchooser other searching criteria. 1225 */ 1226 public Finder(Class<?> clz, ComponentChooser subchooser) { 1227 this.clz = clz; 1228 this.subchooser = subchooser; 1229 } 1230 1231 /** 1232 * Constructs Finder. 1233 * 1234 * @param clz a component class. 1235 */ 1236 public Finder(Class<?> clz) { 1237 this(clz, ComponentSearcher.getTrueChooser("Any " + clz.getName())); 1238 } 1239 1240 @Override 1241 public boolean checkComponent(Component comp) { 1242 if (clz.isInstance(comp)) { 1243 return subchooser.checkComponent(comp); 1244 } 1245 return false; 1246 } 1247 1248 @Override 1249 public String getDescription() { 1250 return subchooser.getDescription(); 1251 } 1252 1253 @Override 1254 public String toString() { 1255 return "Finder{" + "clz=" + clz + ", subchooser=" + subchooser + '}'; 1256 } 1257 } 1258 1259 /** 1260 * Can be used to make nonblocking operation implementation. Typical 1261 * scenario is: <BR> 1262 * produceNoBlocking(new NoBlockingAction("Button pushing") {<BR> 1263 * public Object doAction(Object param) {<BR> 1264 * push();<BR> 1265 * return null;<BR> 1266 * }<BR> 1267 * });<BR> 1268 */ 1269 protected abstract class NoBlockingAction<R, P> implements Action<R, P> { 1270 1271 String description; 1272 Exception exception; 1273 1274 /** 1275 * Constructs a NoBlockingAction object. 1276 * 1277 * @param description an action description. 1278 */ 1279 public NoBlockingAction(String description) { 1280 this.description = description; 1281 exception = null; 1282 } 1283 1284 @Override 1285 public final R launch(P param) { 1286 R result = null; 1287 try { 1288 result = doAction(param); 1289 } catch (Exception e) { 1290 exception = e; 1291 } 1292 return result; 1293 } 1294 1295 /** 1296 * Performs a mapping action. 1297 * 1298 * @param param an action parameter. 1299 * @return an action result. 1300 */ 1301 public abstract R doAction(P param); 1302 1303 @Override 1304 public String getDescription() { 1305 return description; 1306 } 1307 1308 @Override 1309 public String toString() { 1310 return "NoBlockingAction{" + "description=" + description + ", exception=" + exception + '}'; 1311 } 1312 1313 /** 1314 * Specifies the exception. 1315 * 1316 * @param e an exception. 1317 * @see #getException 1318 */ 1319 protected void setException(Exception e) { 1320 exception = e; 1321 } 1322 1323 /** 1324 * Returns an exception occurred during the action execution. 1325 * 1326 * @return an exception. 1327 * @see #setException 1328 */ 1329 public Exception getException() { 1330 return exception; 1331 } 1332 } 1333 1334 /** 1335 * Can be used to simplify non-primitive type component's methods mapping. 1336 * Like this: <BR> 1337 * public Color getBackground() { <BR> 1338 * return((Color)runMapping(new MapAction("getBackground") { <BR> 1339 * public Object map() { <BR> 1340 * return ((Component)getSource()).getBackground(); <BR> 1341 * } <BR> 1342 * })); <BR> 1343 * } <BR> 1344 * 1345 * @see #runMapping(Operator.MapAction) 1346 */ 1347 protected abstract class MapAction<R> extends QueueTool.QueueAction<R> { 1348 1349 /** 1350 * Constructs a MapAction object. 1351 * 1352 * @param description an action description. 1353 */ 1354 public MapAction(String description) { 1355 super(description); 1356 } 1357 1358 @Override 1359 public final R launch() throws Exception { 1360 return map(); 1361 } 1362 1363 /** 1364 * Executes a map action. 1365 * 1366 * @return an action result. 1367 * @throws Exception 1368 */ 1369 public abstract R map() throws Exception; 1370 } 1371 1372 /** 1373 * Can be used to simplify char component's methods mapping. 1374 * 1375 * @see #runMapping(Operator.MapCharacterAction) 1376 */ 1377 protected abstract class MapCharacterAction extends QueueTool.QueueAction<Object> { 1378 1379 /** 1380 * Constructs a MapCharacterAction object. 1381 * 1382 * @param description an action description. 1383 */ 1384 public MapCharacterAction(String description) { 1385 super(description); 1386 } 1387 1388 @Override 1389 public final Object launch() throws Exception { 1390 return map(); 1391 } 1392 1393 /** 1394 * Executes a map action. 1395 * 1396 * @return an action result. 1397 * @throws Exception 1398 */ 1399 public abstract char map() throws Exception; 1400 } 1401 1402 /** 1403 * Can be used to simplify byte component's methods mapping. 1404 * 1405 * @see #runMapping(Operator.MapByteAction) 1406 */ 1407 protected abstract class MapByteAction extends QueueTool.QueueAction<Object> { 1408 1409 /** 1410 * Constructs a MapByteAction object. 1411 * 1412 * @param description an action description. 1413 */ 1414 public MapByteAction(String description) { 1415 super(description); 1416 } 1417 1418 @Override 1419 public final Object launch() throws Exception { 1420 return map(); 1421 } 1422 1423 /** 1424 * Executes a map action. 1425 * 1426 * @return an action result. 1427 * @throws Exception 1428 */ 1429 public abstract byte map() throws Exception; 1430 } 1431 1432 /** 1433 * Can be used to simplify int component's methods mapping. 1434 * 1435 * @see #runMapping(Operator.MapIntegerAction) 1436 */ 1437 protected abstract class MapIntegerAction extends QueueTool.QueueAction<Object> { 1438 1439 /** 1440 * Constructs a MapIntegerAction object. 1441 * 1442 * @param description an action description. 1443 */ 1444 public MapIntegerAction(String description) { 1445 super(description); 1446 } 1447 1448 @Override 1449 public final Object launch() throws Exception { 1450 return map(); 1451 } 1452 1453 /** 1454 * Executes a map action. 1455 * 1456 * @return an action result. 1457 * @throws Exception 1458 */ 1459 public abstract int map() throws Exception; 1460 } 1461 1462 /** 1463 * Can be used to simplify long component's methods mapping. 1464 * 1465 * @see #runMapping(Operator.MapLongAction) 1466 */ 1467 protected abstract class MapLongAction extends QueueTool.QueueAction<Object> { 1468 1469 /** 1470 * Constructs a MapLongAction object. 1471 * 1472 * @param description an action description. 1473 */ 1474 public MapLongAction(String description) { 1475 super(description); 1476 } 1477 1478 @Override 1479 public final Object launch() throws Exception { 1480 return map(); 1481 } 1482 1483 /** 1484 * Executes a map action. 1485 * 1486 * @return an action result. 1487 * @throws Exception 1488 */ 1489 public abstract long map() throws Exception; 1490 } 1491 1492 /** 1493 * Can be used to simplify float component's methods mapping. 1494 * 1495 * @see #runMapping(Operator.MapFloatAction) 1496 */ 1497 protected abstract class MapFloatAction extends QueueTool.QueueAction<Object> { 1498 1499 /** 1500 * Constructs a MapFloatAction object. 1501 * 1502 * @param description an action description. 1503 */ 1504 public MapFloatAction(String description) { 1505 super(description); 1506 } 1507 1508 @Override 1509 public final Object launch() throws Exception { 1510 return map(); 1511 } 1512 1513 /** 1514 * Executes a map action. 1515 * 1516 * @return an action result. 1517 * @throws Exception 1518 */ 1519 public abstract float map() throws Exception; 1520 } 1521 1522 /** 1523 * Can be used to simplify double component's methods mapping. 1524 * 1525 * @see #runMapping(Operator.MapDoubleAction) 1526 */ 1527 protected abstract class MapDoubleAction extends QueueTool.QueueAction<Object> { 1528 1529 /** 1530 * Constructs a MapDoubleAction object. 1531 * 1532 * @param description an action description. 1533 */ 1534 public MapDoubleAction(String description) { 1535 super(description); 1536 } 1537 1538 @Override 1539 public final Object launch() throws Exception { 1540 return map(); 1541 } 1542 1543 /** 1544 * Executes a map action. 1545 * 1546 * @return an action result. 1547 * @throws Exception 1548 */ 1549 public abstract double map() throws Exception; 1550 } 1551 1552 /** 1553 * Can be used to simplify boolean component's methods mapping. 1554 * 1555 * @see #runMapping(Operator.MapBooleanAction) 1556 */ 1557 protected abstract class MapBooleanAction extends QueueTool.QueueAction<Object> { 1558 1559 /** 1560 * Constructs a MapBooleanAction object. 1561 * 1562 * @param description an action description. 1563 */ 1564 public MapBooleanAction(String description) { 1565 super(description); 1566 } 1567 1568 @Override 1569 public final Object launch() throws Exception { 1570 return map() ? Boolean.TRUE : Boolean.FALSE; 1571 } 1572 1573 /** 1574 * Executes a map action. 1575 * 1576 * @return an action result. 1577 * @throws Exception 1578 */ 1579 public abstract boolean map() throws Exception; 1580 } 1581 1582 /** 1583 * Can be used to simplify void component's methods mapping. 1584 * 1585 * @see #runMapping(Operator.MapVoidAction) 1586 */ 1587 protected abstract class MapVoidAction extends QueueTool.QueueAction<Object> { 1588 1589 /** 1590 * Constructs a MapVoidAction object. 1591 * 1592 * @param description an action description. 1593 */ 1594 public MapVoidAction(String description) { 1595 super(description); 1596 } 1597 1598 @Override 1599 public final Object launch() throws Exception { 1600 map(); 1601 return null; 1602 } 1603 1604 /** 1605 * Executes a map action. 1606 * 1607 * @throws Exception 1608 */ 1609 public abstract void map() throws Exception; 1610 } 1611 1612 private static class NullOperator extends Operator { 1613 1614 public NullOperator() { 1615 super(); 1616 } 1617 1618 @Override 1619 public Component getSource() { 1620 return null; 1621 } 1622 } 1623 }