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