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