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 Waiter<String, Void> stateWaiter = new Waiter<>(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 stateWaiter.setTimeoutsToCloneOf(getTimeouts(), "ComponentOperator.WaitStateTimeout"); 714 stateWaiter.setOutput(getOutput().createErrorOutput()); 715 try { 716 stateWaiter.waitAction(null); 717 } catch (InterruptedException e) { 718 throw (new JemmyException("Waiting of \"" + state.getDescription() 719 + "\" state has been interrupted!")); 720 } 721 } 722 723 /** 724 * Waits a state specified by a ComponentChooser instance on EDT queue. 725 * 726 * @param state a ComponentChooser defining the state criteria. 727 * @throws TimeoutExpiredException if the state has not achieved in a value 728 * defined by {@code "ComponentOperator.WaitStateTimeout"} 729 */ 730 public void waitStateOnQueue(final ComponentChooser state) { 731 waitState((comp) -> { 732 return (boolean) (queueTool.invokeSmoothly( 733 new QueueTool.QueueAction<Object>("checkComponent") { 734 @Override 735 public final Object launch() throws Exception { 736 return state.checkComponent(comp); 737 } 738 })); 739 }); 740 } 741 742 //////////////////////////////////////////////////////// 743 //Mapping // 744 //////////////////////////////////////////////////////// 745 /** 746 * Performs an operation with time control. 747 * 748 * @param action an action to execute. 749 * @param param an action parameters. 750 * @param actionTimeOrigin is a timeout name to use for waiting for the 751 * action to be finished. 752 * @return an action result. 753 */ 754 protected <R, P> R produceTimeRestricted(Action<R, P> action, final P param, 755 String actionTimeOrigin) { 756 ActionProducer<R, P> producer = new ActionProducer<>(action); 757 producer.setOutput(getOutput().createErrorOutput()); 758 producer.setTimeouts(getTimeouts().cloneThis()); 759 producer.getTimeouts().setTimeout("ActionProducer.MaxActionTime", 760 getTimeouts().getTimeout(actionTimeOrigin)); 761 try { 762 R result = producer.produceAction(param, actionTimeOrigin); 763 Throwable exception = producer.getException(); 764 if (exception != null) { 765 if (exception instanceof JemmyException) { 766 throw ((JemmyException) exception); 767 } else { 768 throw (new JemmyException("Exception during " + action.getDescription(), 769 exception)); 770 } 771 } 772 return result; 773 } catch (InterruptedException e) { 774 throw (new JemmyException("Interrupted!", e)); 775 } 776 } 777 778 /** 779 * Performs an operation with time control. 780 * 781 * @param action an action to execute. 782 * @param actionTimeOrigin is a timeout name to use for waiting for the 783 * action to be finished. 784 * @return an action result. 785 */ 786 protected <R, P> R produceTimeRestricted(Action<R, P> action, String actionTimeOrigin) { 787 return produceTimeRestricted(action, null, actionTimeOrigin); 788 } 789 790 /** 791 * Performs an operation without time control. 792 * 793 * @param action an action to execute. 794 * @param param an action parameters. 795 */ 796 protected <R, P> void produceNoBlocking(NoBlockingAction<R, P> action, P param) { 797 try { 798 ActionProducer<R, P> noBlockingProducer = new ActionProducer<>(action, false); 799 noBlockingProducer.setOutput(output.createErrorOutput()); 800 noBlockingProducer.setTimeouts(timeouts); 801 noBlockingProducer.produceAction(param, null); 802 } catch (InterruptedException e) { 803 throw (new JemmyException("Exception during \"" 804 + action.getDescription() 805 + "\" execution", 806 e)); 807 } 808 if (action.exception != null) { 809 throw (new JemmyException("Exception during nonblocking \"" 810 + action.getDescription() + "\"", 811 action.exception)); 812 } 813 } 814 815 /** 816 * Performs an operation without time control. 817 * 818 * @param action an action to execute. 819 */ 820 protected void produceNoBlocking(NoBlockingAction<?, ?> action) { 821 produceNoBlocking(action, null); 822 } 823 824 /** 825 * Equivalent to {@code getQueue().lock();}. 826 */ 827 protected void lockQueue() { 828 queueTool.lock(); 829 } 830 831 /** 832 * Equivalent to {@code getQueue().unlock();}. 833 */ 834 protected void unlockQueue() { 835 queueTool.unlock(); 836 } 837 838 /** 839 * Unlocks Queue and then throw exception. 840 * 841 * @param e an exception to be thrown. 842 */ 843 protected void unlockAndThrow(Exception e) { 844 unlockQueue(); 845 throw (new JemmyException("Exception during queue locking", e)); 846 } 847 848 /** 849 * To map nonprimitive type component's method. 850 * 851 * @param action a mapping action. 852 * @return an action result. 853 * @see Operator.MapAction 854 */ 855 protected <R> R runMapping(MapAction<R> action) { 856 return runMappingPrimitive(action); 857 } 858 859 /** 860 * To map char component's method. 861 * 862 * @param action a mapping action. 863 * @return an action result. 864 * @see #runMapping(Operator.MapAction) 865 * @see Operator.MapCharacterAction 866 */ 867 protected char runMapping(MapCharacterAction action) { 868 return (Character) runMappingPrimitive(action); 869 } 870 871 /** 872 * To map byte component's method. 873 * 874 * @param action a mapping action. 875 * @return an action result. 876 * @see #runMapping(Operator.MapAction) 877 * @see Operator.MapByteAction 878 */ 879 protected byte runMapping(MapByteAction action) { 880 return (Byte) runMappingPrimitive(action); 881 } 882 883 /** 884 * To map int component's method. 885 * 886 * @param action a mapping action. 887 * @return an action result. 888 * @see #runMapping(Operator.MapAction) 889 * @see Operator.MapIntegerAction 890 */ 891 protected int runMapping(MapIntegerAction action) { 892 return (Integer) runMappingPrimitive(action); 893 } 894 895 /** 896 * To map long component's method. 897 * 898 * @param action a mapping action. 899 * @return an action result. 900 * @see #runMapping(Operator.MapAction) 901 * @see Operator.MapLongAction 902 */ 903 protected long runMapping(MapLongAction action) { 904 return (Long) runMappingPrimitive(action); 905 } 906 907 /** 908 * To map float component's method. 909 * 910 * @param action a mapping action. 911 * @return an action result. 912 * @see #runMapping(Operator.MapAction) 913 * @see Operator.MapFloatAction 914 */ 915 protected float runMapping(MapFloatAction action) { 916 return (Float) runMappingPrimitive(action); 917 } 918 919 /** 920 * To map double component's method. 921 * 922 * @param action a mapping action. 923 * @return an action result. 924 * @see #runMapping(Operator.MapAction) 925 * @see Operator.MapDoubleAction 926 */ 927 protected double runMapping(MapDoubleAction action) { 928 return (Double) runMappingPrimitive(action); 929 } 930 931 /** 932 * To map boolean component's method. 933 * 934 * @param action a mapping action. 935 * @return an action result. 936 * @see #runMapping(Operator.MapAction) 937 * @see Operator.MapBooleanAction 938 */ 939 protected boolean runMapping(MapBooleanAction action) { 940 return (Boolean) runMappingPrimitive(action); 941 } 942 943 /** 944 * To map void component's method. 945 * 946 * @param action a mapping action. 947 * @see #runMapping(Operator.MapAction) 948 * @see Operator.MapVoidAction 949 */ 950 protected void runMapping(MapVoidAction action) { 951 runMappingPrimitive(action); 952 } 953 954 /** 955 * Adds array of objects to dump hashtable. Is used for multiple properties 956 * such as list items and tree nodes. 957 * 958 * @param table a table to add properties to. 959 * @param title property names prefix. Property names are constructed by 960 * adding a number to the prefix: 961 * {@code title + "_" + Iteger.toString("ordinal index")} 962 * @param items an array of property values. 963 * @return an array of property names (with added numbers). 964 */ 965 protected String[] addToDump(Hashtable<String, Object> table, String title, Object[] items) { 966 String[] names = createNames(title + "_", items.length); 967 for (int i = 0; i < items.length; i++) { 968 table.put(names[i], items[i].toString()); 969 } 970 return names; 971 } 972 973 /** 974 * Adds two dimentional array of objects to dump hashtable. Is used for 975 * multiple properties such as table cells. 976 * 977 * @param table a table to add properties to. 978 * @param title property names prefix. Property names are constructed by 979 * adding two numbers to the prefix: 980 * {@code title + "_" + Iteger.toString("row index") + "_" + Iteger.toString("column index")} 981 * @param items an array of property values. 982 * @return an array of property names (with added numbers). 983 */ 984 protected String[] addToDump(Hashtable<String, Object> table, String title, Object[][] items) { 985 String[] names = createNames(title + "_", items.length); 986 for (int i = 0; i < items.length; i++) { 987 addToDump(table, names[i], items[i]); 988 } 989 return names; 990 } 991 //////////////////////////////////////////////////////// 992 //Private // 993 //////////////////////////////////////////////////////// 994 995 private <R> R runMappingPrimitive(QueueTool.QueueAction<R> action) { 996 return queueTool.invokeSmoothly(action); 997 } 998 999 private String[] createNames(String title, int count) { 1000 String[] result = new String[count]; 1001 int indexLength = Integer.toString(count).length(); 1002 StringBuilder zeroStringB = new StringBuilder(indexLength); 1003 for (int i = 0; i < indexLength; i++) { 1004 zeroStringB.append('0'); 1005 } 1006 String zeroString = zeroStringB.toString(); 1007 for (int i = 0; i < count; i++) { 1008 String indexString = Integer.toString(i); 1009 result[i] = title 1010 + zeroString.substring(0, indexLength - indexString.length()) 1011 + indexString; 1012 } 1013 return result; 1014 } 1015 1016 private static ComponentOperator createOperator(Component comp, Class<?> compClass) { 1017 StringTokenizer token = new StringTokenizer(compClass.getName(), "."); 1018 String className = ""; 1019 while (token.hasMoreTokens()) { 1020 className = token.nextToken(); 1021 } 1022 Object[] params = {comp}; 1023 Class<?>[] param_classes = {compClass}; 1024 String operatorPackage; 1025 for (String operatorPkg : operatorPkgs) { 1026 operatorPackage = operatorPkg; 1027 try { 1028 return ((ComponentOperator) new ClassReference(operatorPackage + "." 1029 + className + "Operator"). 1030 newInstance(params, param_classes)); 1031 } catch (ClassNotFoundException ignored) { 1032 } catch (InvocationTargetException ignored) { 1033 } catch (NoSuchMethodException ignored) { 1034 } catch (IllegalAccessException ignored) { 1035 } catch (InstantiationException ignored) { 1036 } 1037 } 1038 return null; 1039 } 1040 1041 private void initEnvironment() { 1042 queueTool = new QueueTool(); 1043 setTimeouts(JemmyProperties.getProperties().getTimeouts()); 1044 setOutput(JemmyProperties.getProperties().getOutput()); 1045 setCharBindingMap(JemmyProperties.getProperties().getCharBindingMap()); 1046 setVisualizer(getDefaultComponentVisualizer()); 1047 setComparator(getDefaultStringComparator()); 1048 setVerification(getDefaultVerification()); 1049 setProperties(JemmyProperties.getProperties()); 1050 setPathParser(getDefaultPathParser()); 1051 } 1052 1053 /** 1054 * Returns toString() result from component of this operator. It calls 1055 * {@link #getSource}.toString() in dispatch thread. 1056 * 1057 * @return toString() result from component of this operator. 1058 */ 1059 public String toStringSource() { 1060 return runMapping(new MapAction<String>("getSource().toString()") { 1061 @Override 1062 public String map() { 1063 return getSource().toString(); 1064 } 1065 }); 1066 } 1067 1068 /** 1069 * Interface used to make component visible & ready to to make operations 1070 * with. 1071 */ 1072 public interface ComponentVisualizer { 1073 1074 /** 1075 * Prepares component for a user input. 1076 * 1077 * @param compOper Operator asking for necessary actions. 1078 */ 1079 public void makeVisible(ComponentOperator compOper); 1080 } 1081 1082 /** 1083 * Interface to compare string resources like labels, button text, ... with 1084 * match. <BR> 1085 */ 1086 public interface StringComparator { 1087 1088 /** 1089 * Imlementation must return true if strings are equal. 1090 * 1091 * @param caption a text to compare with pattern. 1092 * @param match a pattern 1093 * @return true if text and pattern matches. 1094 */ 1095 public boolean equals(String caption, String match); 1096 } 1097 1098 /** 1099 * Default StringComparator implementation. 1100 */ 1101 public static class DefaultStringComparator implements StringComparator { 1102 1103 boolean ce; 1104 boolean ccs; 1105 1106 /** 1107 * Constructs a DefaultStringComparator object. 1108 * 1109 * @param ce Compare exactly. If false, text can be a substring of 1110 * caption. 1111 * @param ccs Compare case sensitively. 1112 */ 1113 public DefaultStringComparator(boolean ce, boolean ccs) { 1114 this.ce = ce; 1115 this.ccs = ccs; 1116 } 1117 1118 /** 1119 * Compares a caption with a match using switched passed into 1120 * constructor. 1121 * 1122 * @param caption String to be compared with match. Method returns 1123 * false, if parameter is null. 1124 * @param match Sample to compare with. Method returns true, if 1125 * parameter is null. 1126 * @return true if text and pattern matches. 1127 */ 1128 @Override 1129 public boolean equals(String caption, String match) { 1130 if (match == null) { 1131 return true; 1132 } 1133 if (caption == null) { 1134 return false; 1135 } 1136 String c, t; 1137 if (!ccs) { 1138 c = caption.toUpperCase(); 1139 t = match.toUpperCase(); 1140 } else { 1141 c = caption; 1142 t = match; 1143 } 1144 if (ce) { 1145 return c.equals(t); 1146 } else { 1147 return c.contains(t); 1148 } 1149 } 1150 } 1151 1152 /** 1153 * Used for parsing of path-like strings. 1154 */ 1155 public interface PathParser { 1156 1157 /** 1158 * Parses a string to a String array. 1159 * 1160 * @param path a String to parse. 1161 * @return a parsed array. 1162 */ 1163 public String[] parse(String path); 1164 } 1165 1166 /** 1167 * Used for parsing of path-like strings where path components are separated 1168 * by a string-separator: "drive|directory|subdirectory|file". 1169 */ 1170 public static class DefaultPathParser implements PathParser { 1171 1172 String separator; 1173 1174 /** 1175 * Constructs a DefaultPathParser object. 1176 * 1177 * @param separator a string used as separator. 1178 */ 1179 public DefaultPathParser(String separator) { 1180 this.separator = separator; 1181 } 1182 1183 @Override 1184 public String[] parse(String path) { 1185 if (path.length() > 0) { 1186 Vector<String> parsed = new Vector<>(); 1187 int position = 0; 1188 int sepIndex = 0; 1189 while ((sepIndex = path.indexOf(separator, position)) != -1) { 1190 parsed.add(path.substring(position, sepIndex)); 1191 position = sepIndex + separator.length(); 1192 } 1193 parsed.add(path.substring(position)); 1194 String[] result = new String[parsed.size()]; 1195 for (int i = 0; i < parsed.size(); i++) { 1196 result[i] = parsed.get(i); 1197 } 1198 return result; 1199 } else { 1200 return new String[0]; 1201 } 1202 } 1203 } 1204 1205 /** 1206 * Allows to bind a component by a component type. 1207 */ 1208 public static class Finder implements ComponentChooser { 1209 1210 Class<?> clz; 1211 ComponentChooser subchooser; 1212 1213 /** 1214 * Constructs Finder. 1215 * 1216 * @param clz a component class. 1217 * @param subchooser other searching criteria. 1218 */ 1219 public Finder(Class<?> clz, ComponentChooser subchooser) { 1220 this.clz = clz; 1221 this.subchooser = subchooser; 1222 } 1223 1224 /** 1225 * Constructs Finder. 1226 * 1227 * @param clz a component class. 1228 */ 1229 public Finder(Class<?> clz) { 1230 this(clz, ComponentSearcher.getTrueChooser("Any " + clz.getName())); 1231 } 1232 1233 @Override 1234 public boolean checkComponent(Component comp) { 1235 if (clz.isInstance(comp)) { 1236 return subchooser.checkComponent(comp); 1237 } 1238 return false; 1239 } 1240 1241 @Override 1242 public String getDescription() { 1243 return subchooser.getDescription(); 1244 } 1245 1246 @Override 1247 public String toString() { 1248 return "Finder{" + "clz=" + clz + ", subchooser=" + subchooser + '}'; 1249 } 1250 } 1251 1252 /** 1253 * Can be used to make nonblocking operation implementation. Typical 1254 * scenario is: <BR> 1255 * produceNoBlocking(new NoBlockingAction("Button pushing") {<BR> 1256 * public Object doAction(Object param) {<BR> 1257 * push();<BR> 1258 * return null;<BR> 1259 * }<BR> 1260 * });<BR> 1261 */ 1262 protected abstract class NoBlockingAction<R, P> implements Action<R, P> { 1263 1264 String description; 1265 Exception exception; 1266 1267 /** 1268 * Constructs a NoBlockingAction object. 1269 * 1270 * @param description an action description. 1271 */ 1272 public NoBlockingAction(String description) { 1273 this.description = description; 1274 exception = null; 1275 } 1276 1277 @Override 1278 public final R launch(P param) { 1279 R result = null; 1280 try { 1281 result = doAction(param); 1282 } catch (Exception e) { 1283 exception = e; 1284 } 1285 return result; 1286 } 1287 1288 /** 1289 * Performs a mapping action. 1290 * 1291 * @param param an action parameter. 1292 * @return an action result. 1293 */ 1294 public abstract R doAction(P param); 1295 1296 @Override 1297 public String getDescription() { 1298 return description; 1299 } 1300 1301 @Override 1302 public String toString() { 1303 return "NoBlockingAction{" + "description=" + description + ", exception=" + exception + '}'; 1304 } 1305 1306 /** 1307 * Specifies the exception. 1308 * 1309 * @param e an exception. 1310 * @see #getException 1311 */ 1312 protected void setException(Exception e) { 1313 exception = e; 1314 } 1315 1316 /** 1317 * Returns an exception occurred during the action execution. 1318 * 1319 * @return an exception. 1320 * @see #setException 1321 */ 1322 public Exception getException() { 1323 return exception; 1324 } 1325 } 1326 1327 /** 1328 * Can be used to simplify non-primitive type component's methods mapping. 1329 * Like this: <BR> 1330 * public Color getBackground() { <BR> 1331 * return((Color)runMapping(new MapAction("getBackground") { <BR> 1332 * public Object map() { <BR> 1333 * return ((Component)getSource()).getBackground(); <BR> 1334 * } <BR> 1335 * })); <BR> 1336 * } <BR> 1337 * 1338 * @see #runMapping(Operator.MapAction) 1339 */ 1340 protected abstract class MapAction<R> extends QueueTool.QueueAction<R> { 1341 1342 /** 1343 * Constructs a MapAction object. 1344 * 1345 * @param description an action description. 1346 */ 1347 public MapAction(String description) { 1348 super(description); 1349 } 1350 1351 @Override 1352 public final R launch() throws Exception { 1353 return map(); 1354 } 1355 1356 /** 1357 * Executes a map action. 1358 * 1359 * @return an action result. 1360 * @throws Exception 1361 */ 1362 public abstract R map() throws Exception; 1363 } 1364 1365 /** 1366 * Can be used to simplify char component's methods mapping. 1367 * 1368 * @see #runMapping(Operator.MapCharacterAction) 1369 */ 1370 protected abstract class MapCharacterAction extends QueueTool.QueueAction<Object> { 1371 1372 /** 1373 * Constructs a MapCharacterAction object. 1374 * 1375 * @param description an action description. 1376 */ 1377 public MapCharacterAction(String description) { 1378 super(description); 1379 } 1380 1381 @Override 1382 public final Object launch() throws Exception { 1383 return map(); 1384 } 1385 1386 /** 1387 * Executes a map action. 1388 * 1389 * @return an action result. 1390 * @throws Exception 1391 */ 1392 public abstract char map() throws Exception; 1393 } 1394 1395 /** 1396 * Can be used to simplify byte component's methods mapping. 1397 * 1398 * @see #runMapping(Operator.MapByteAction) 1399 */ 1400 protected abstract class MapByteAction extends QueueTool.QueueAction<Object> { 1401 1402 /** 1403 * Constructs a MapByteAction object. 1404 * 1405 * @param description an action description. 1406 */ 1407 public MapByteAction(String description) { 1408 super(description); 1409 } 1410 1411 @Override 1412 public final Object launch() throws Exception { 1413 return map(); 1414 } 1415 1416 /** 1417 * Executes a map action. 1418 * 1419 * @return an action result. 1420 * @throws Exception 1421 */ 1422 public abstract byte map() throws Exception; 1423 } 1424 1425 /** 1426 * Can be used to simplify int component's methods mapping. 1427 * 1428 * @see #runMapping(Operator.MapIntegerAction) 1429 */ 1430 protected abstract class MapIntegerAction extends QueueTool.QueueAction<Object> { 1431 1432 /** 1433 * Constructs a MapIntegerAction object. 1434 * 1435 * @param description an action description. 1436 */ 1437 public MapIntegerAction(String description) { 1438 super(description); 1439 } 1440 1441 @Override 1442 public final Object launch() throws Exception { 1443 return map(); 1444 } 1445 1446 /** 1447 * Executes a map action. 1448 * 1449 * @return an action result. 1450 * @throws Exception 1451 */ 1452 public abstract int map() throws Exception; 1453 } 1454 1455 /** 1456 * Can be used to simplify long component's methods mapping. 1457 * 1458 * @see #runMapping(Operator.MapLongAction) 1459 */ 1460 protected abstract class MapLongAction extends QueueTool.QueueAction<Object> { 1461 1462 /** 1463 * Constructs a MapLongAction object. 1464 * 1465 * @param description an action description. 1466 */ 1467 public MapLongAction(String description) { 1468 super(description); 1469 } 1470 1471 @Override 1472 public final Object launch() throws Exception { 1473 return map(); 1474 } 1475 1476 /** 1477 * Executes a map action. 1478 * 1479 * @return an action result. 1480 * @throws Exception 1481 */ 1482 public abstract long map() throws Exception; 1483 } 1484 1485 /** 1486 * Can be used to simplify float component's methods mapping. 1487 * 1488 * @see #runMapping(Operator.MapFloatAction) 1489 */ 1490 protected abstract class MapFloatAction extends QueueTool.QueueAction<Object> { 1491 1492 /** 1493 * Constructs a MapFloatAction object. 1494 * 1495 * @param description an action description. 1496 */ 1497 public MapFloatAction(String description) { 1498 super(description); 1499 } 1500 1501 @Override 1502 public final Object launch() throws Exception { 1503 return map(); 1504 } 1505 1506 /** 1507 * Executes a map action. 1508 * 1509 * @return an action result. 1510 * @throws Exception 1511 */ 1512 public abstract float map() throws Exception; 1513 } 1514 1515 /** 1516 * Can be used to simplify double component's methods mapping. 1517 * 1518 * @see #runMapping(Operator.MapDoubleAction) 1519 */ 1520 protected abstract class MapDoubleAction extends QueueTool.QueueAction<Object> { 1521 1522 /** 1523 * Constructs a MapDoubleAction object. 1524 * 1525 * @param description an action description. 1526 */ 1527 public MapDoubleAction(String description) { 1528 super(description); 1529 } 1530 1531 @Override 1532 public final Object launch() throws Exception { 1533 return map(); 1534 } 1535 1536 /** 1537 * Executes a map action. 1538 * 1539 * @return an action result. 1540 * @throws Exception 1541 */ 1542 public abstract double map() throws Exception; 1543 } 1544 1545 /** 1546 * Can be used to simplify boolean component's methods mapping. 1547 * 1548 * @see #runMapping(Operator.MapBooleanAction) 1549 */ 1550 protected abstract class MapBooleanAction extends QueueTool.QueueAction<Object> { 1551 1552 /** 1553 * Constructs a MapBooleanAction object. 1554 * 1555 * @param description an action description. 1556 */ 1557 public MapBooleanAction(String description) { 1558 super(description); 1559 } 1560 1561 @Override 1562 public final Object launch() throws Exception { 1563 return map() ? Boolean.TRUE : Boolean.FALSE; 1564 } 1565 1566 /** 1567 * Executes a map action. 1568 * 1569 * @return an action result. 1570 * @throws Exception 1571 */ 1572 public abstract boolean map() throws Exception; 1573 } 1574 1575 /** 1576 * Can be used to simplify void component's methods mapping. 1577 * 1578 * @see #runMapping(Operator.MapVoidAction) 1579 */ 1580 protected abstract class MapVoidAction extends QueueTool.QueueAction<Object> { 1581 1582 /** 1583 * Constructs a MapVoidAction object. 1584 * 1585 * @param description an action description. 1586 */ 1587 public MapVoidAction(String description) { 1588 super(description); 1589 } 1590 1591 @Override 1592 public final Object launch() throws Exception { 1593 map(); 1594 return null; 1595 } 1596 1597 /** 1598 * Executes a map action. 1599 * 1600 * @throws Exception 1601 */ 1602 public abstract void map() throws Exception; 1603 } 1604 1605 private static class NullOperator extends Operator { 1606 1607 public NullOperator() { 1608 super(); 1609 } 1610 1611 @Override 1612 public Component getSource() { 1613 return null; 1614 } 1615 } 1616 }