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