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