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