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