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.Button;
26import java.awt.Component;
27import java.awt.Container;
28import java.awt.event.ActionListener;
29import java.util.Hashtable;
30
31import org.netbeans.jemmy.ComponentChooser;
32import org.netbeans.jemmy.Outputable;
33import org.netbeans.jemmy.TestOut;
34import org.netbeans.jemmy.TimeoutExpiredException;
35import org.netbeans.jemmy.Timeoutable;
36import org.netbeans.jemmy.Timeouts;
37import org.netbeans.jemmy.drivers.ButtonDriver;
38import org.netbeans.jemmy.drivers.DriverManager;
39
40/**
41 *
42 * <BR><BR>Timeouts used: <BR>
43 * ButtonOperator.PushButtonTimeout - time between button pressing and
44 * releasing<BR>
45 * ComponentOperator.WaitComponentTimeout - time to wait button displayed <BR>
46 * ComponentOperator.WaitComponentEnabledTimeout - time to wait button enabled
47 * <BR>.
48 *
49 * @see org.netbeans.jemmy.Timeouts
50 *
51 * @author Alexandre Iline (alexandre.iline@oracle.com)
52 *
53 */
54public class ButtonOperator extends ComponentOperator
55        implements Timeoutable, Outputable {
56
57    /**
58     * Identifier for a label property.
59     *
60     * @see #getDump
61     */
62    public static final String TEXT_DPROP = "Label";
63
64    private final static long PUSH_BUTTON_TIMEOUT = 0;
65
66    private Timeouts timeouts;
67    private TestOut output;
68
69    ButtonDriver driver;
70
71    /**
72     * Constructor.
73     *
74     * @param b The {@code java.awt.Button} managed by this instance.
75     */
76    public ButtonOperator(Button b) {
77        super(b);
78        driver = DriverManager.getButtonDriver(getClass());
79    }
80
81    /**
82     * Constructs a ButtonOperator object.
83     *
84     * @param cont container
85     * @param chooser a component chooser specifying searching criteria.
86     * @param index an index between appropriate ones.
87     */
88    public ButtonOperator(ContainerOperator<?> cont, ComponentChooser chooser, int index) {
89        this((Button) cont.
90                waitSubComponent(new ButtonFinder(chooser),
91                        index));
92        copyEnvironment(cont);
93    }
94
95    /**
96     * Constructs a ButtonOperator object.
97     *
98     * @param cont container
99     * @param chooser a component chooser specifying searching criteria.
100     */
101    public ButtonOperator(ContainerOperator<?> cont, ComponentChooser chooser) {
102        this(cont, chooser, 0);
103    }
104
105    /**
106     * Constructor. Waits for a component in a container to show. The component
107     * is identified as the {@code index+1}'th {@code java.awt.Button}
108     * that shows, lies below the container in the display containment
109     * hierarchy, and that has the desired text. Uses cont's timeout and output
110     * for waiting and to init this operator.
111     *
112     * @param cont The operator for a container containing the sought for
113     * button.
114     * @param text Button text.
115     * @param index Ordinal component index. The first component has
116     * {@code index} 0.
117     * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean)
118     * @throws TimeoutExpiredException
119     */
120    public ButtonOperator(ContainerOperator<?> cont, String text, int index) {
121        this((Button) waitComponent(cont,
122                new ButtonByLabelFinder(text,
123                        cont.getComparator()),
124                index));
125        copyEnvironment(cont);
126    }
127
128    /**
129     * Constructor. Waits for a component in a container to show. The component
130     * is identified as the first {@code java.awt.Button} that shows, lies
131     * below the container in the display containment hierarchy, and that has
132     * the desired text. Uses cont's timeout and output for waiting and to init
133     * this operator.
134     *
135     * @param cont The operator for a container containing the sought for
136     * button.
137     * @param text Button text.
138     * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean)
139     * @throws TimeoutExpiredException
140     */
141    public ButtonOperator(ContainerOperator<?> cont, String text) {
142        this(cont, text, 0);
143    }
144
145    /**
146     * Constructor. Waits component in container first. Uses cont's timeout and
147     * output for waiting and to init operator.
148     *
149     * @param cont The operator for a container containing the sought for
150     * button.
151     * @param index Ordinal component index.
152     * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean)
153     * @throws TimeoutExpiredException
154     */
155    public ButtonOperator(ContainerOperator<?> cont, int index) {
156        this((Button) waitComponent(cont,
157                new ButtonFinder(),
158                index));
159        copyEnvironment(cont);
160    }
161
162    /**
163     * Constructor. Waits component in container first. Uses cont's timeout and
164     * output for waiting and to init operator.
165     *
166     * @param cont The operator for a container containing the sought for
167     * button.
168     * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean)
169     * @throws TimeoutExpiredException
170     */
171    public ButtonOperator(ContainerOperator<?> cont) {
172        this(cont, 0);
173    }
174
175    /**
176     * Searches Button in a container.
177     *
178     * @param cont Container in which to search for the component. The container
179     * lies above the component in the display containment hierarchy. The
180     * containment need not be direct.
181     * @param chooser org.netbeans.jemmy.ComponentChooser implementation,
182     * defining and applying search criteria.
183     * @param index Ordinal component index. The first {@code index} is 0.
184     * @return Button instance or null if component was not found.
185     */
186    public static Button findButton(Container cont, ComponentChooser chooser, int index) {
187        return (Button) findComponent(cont, new ButtonFinder(chooser), index);
188    }
189
190    /**
191     * Searches for the first Button in a container.
192     *
193     * @param cont Container in which to search for the component. The container
194     * lies above the component in the display containment hierarchy. The
195     * containment need not be direct.
196     * @param chooser org.netbeans.jemmy.ComponentChooser implementation,
197     * defining and applying search criteria.
198     * @return Button instance or null if component was not found.
199     */
200    public static Button findButton(Container cont, ComponentChooser chooser) {
201        return findButton(cont, chooser, 0);
202    }
203
204    /**
205     * Searches Button by text.
206     *
207     * @param cont Container to search component in.
208     * @param text Button text. If null, contents is not checked.
209     * @param ce Compare text exactly.
210     * @param ccs Compare text case sensitively.
211     * @param index Ordinal component index.
212     * @return Button instance or null if component was not found.
213     * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean)
214     */
215    public static Button findButton(Container cont, String text, boolean ce, boolean ccs, int index) {
216        return findButton(cont, new ButtonByLabelFinder(text, new DefaultStringComparator(ce, ccs)), index);
217    }
218
219    /**
220     * Searches Button by text.
221     *
222     * @param cont Container to search component in.
223     * @param text Button text. If null, contents is not checked.
224     * @param ce Compare text exactly.
225     * @param ccs Compare text case sensitively.
226     * @return Button instance or null if component was not found.
227     * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean)
228     */
229    public static Button findButton(Container cont, String text, boolean ce, boolean ccs) {
230        return findButton(cont, text, ce, ccs, 0);
231    }
232
233    /**
234     * Waits Button in container.
235     *
236     * @param cont Container to search component in.
237     * @param chooser org.netbeans.jemmy.ComponentChooser implementation.
238     * @param index Ordinal component index.
239     * @return Button instance.
240     * @throws TimeoutExpiredException
241     */
242    public static Button waitButton(Container cont, ComponentChooser chooser, int index) {
243        return (Button) waitComponent(cont, new ButtonFinder(chooser), index);
244    }
245
246    /**
247     * Waits 0'th Button in container.
248     *
249     * @param cont Container to search component in.
250     * @param chooser org.netbeans.jemmy.ComponentChooser implementation.
251     * @return Button instance.
252     * @throws TimeoutExpiredException
253     */
254    public static Button waitButton(Container cont, ComponentChooser chooser) {
255        return waitButton(cont, chooser, 0);
256    }
257
258    /**
259     * Waits Button by text.
260     *
261     * @param cont Container to search component in.
262     * @param text Button text. If null, contents is not checked.
263     * @param ce Compare text exactly.
264     * @param ccs Compare text case sensitively.
265     * @param index Ordinal component index.
266     * @return Button instance.
267     * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean)
268     * @throws TimeoutExpiredException
269     */
270    public static Button waitButton(Container cont, String text, boolean ce, boolean ccs, int index) {
271        return waitButton(cont, new ButtonByLabelFinder(text, new DefaultStringComparator(ce, ccs)), index);
272    }
273
274    /**
275     * Waits Button by text.
276     *
277     * @param cont Container to search component in.
278     * @param text Button text. If null, contents is not checked.
279     * @param ce Compare text exactly.
280     * @param ccs Compare text case sensitively.
281     * @return Button instance.
282     * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean)
283     * @throws TimeoutExpiredException
284     */
285    public static Button waitButton(Container cont, String text, boolean ce, boolean ccs) {
286        return waitButton(cont, text, ce, ccs, 0);
287    }
288
289    static {
290        Timeouts.initDefault("ButtonOperator.PushButtonTimeout", PUSH_BUTTON_TIMEOUT);
291    }
292
293    @Override
294    public void setTimeouts(Timeouts timeouts) {
295        super.setTimeouts(timeouts);
296        this.timeouts = timeouts;
297    }
298
299    @Override
300    public Timeouts getTimeouts() {
301        return timeouts;
302    }
303
304    @Override
305    public void setOutput(TestOut out) {
306        output = out;
307        super.setOutput(output.createErrorOutput());
308    }
309
310    @Override
311    public TestOut getOutput() {
312        return output;
313    }
314
315    @Override
316    public void copyEnvironment(Operator anotherOperator) {
317        super.copyEnvironment(anotherOperator);
318        driver
319                = (ButtonDriver) DriverManager.
320                getDriver(DriverManager.BUTTON_DRIVER_ID,
321                        getClass(),
322                        anotherOperator.getProperties());
323    }
324
325    /**
326     * Pushes the button by mouse click.
327     *
328     * @throws TimeoutExpiredException
329     */
330    public void push() {
331        output.printLine("Push button\n    :" + toStringSource());
332        output.printGolden("Push button");
333        driver.push(this);
334    }
335
336    /**
337     * Runs {@code push()} method in a separate thread.
338     */
339    public void pushNoBlock() {
340        produceNoBlocking(new NoBlockingAction<Void, Void>("Button pushing") {
341            @Override
342            public Void doAction(Void param) {
343                push();
344                return null;
345            }
346        });
347    }
348
349    /**
350     * Press the button by mouse.
351     *
352     * @throws TimeoutExpiredException
353     */
354    public void press() {
355        output.printLine("Press button\n    :" + toStringSource());
356        output.printGolden("Press button");
357        driver.press(this);
358    }
359
360    /**
361     * Releases the button by mouse.
362     *
363     * @throws TimeoutExpiredException
364     */
365    public void release() {
366        output.printLine("Release button\n    :" + toStringSource());
367        output.printGolden("Release button");
368        driver.press(this);
369    }
370
371    /**
372     * Returns information about component.
373     */
374    @Override
375    public Hashtable<String, Object> getDump() {
376        Hashtable<String, Object> result = super.getDump();
377        if (((Button) getSource()).getLabel() != null) {
378            result.put(TEXT_DPROP, ((Button) getSource()).getLabel());
379        }
380        return result;
381    }
382
383    ////////////////////////////////////////////////////////
384    //Mapping                                             //
385    /**
386     * Maps {@code Button.addActionListener(ActionListener)} through queue
387     */
388    public void addActionListener(final ActionListener actionListener) {
389        runMapping(new MapVoidAction("addActionListener") {
390            @Override
391            public void map() {
392                ((Button) getSource()).addActionListener(actionListener);
393            }
394        });
395    }
396
397    /**
398     * Maps {@code Button.getActionCommand()} through queue
399     */
400    public String getActionCommand() {
401        return (runMapping(new MapAction<String>("getActionCommand") {
402            @Override
403            public String map() {
404                return ((Button) getSource()).getActionCommand();
405            }
406        }));
407    }
408
409    /**
410     * Maps {@code Button.getLabel()} through queue
411     */
412    public String getLabel() {
413        return (runMapping(new MapAction<String>("getLabel") {
414            @Override
415            public String map() {
416                return ((Button) getSource()).getLabel();
417            }
418        }));
419    }
420
421    /**
422     * Maps {@code Button.removeActionListener(ActionListener)} through queue
423     */
424    public void removeActionListener(final ActionListener actionListener) {
425        runMapping(new MapVoidAction("removeActionListener") {
426            @Override
427            public void map() {
428                ((Button) getSource()).removeActionListener(actionListener);
429            }
430        });
431    }
432
433    /**
434     * Maps {@code Button.setActionCommand(String)} through queue
435     */
436    public void setActionCommand(final String string) {
437        runMapping(new MapVoidAction("setActionCommand") {
438            @Override
439            public void map() {
440                ((Button) getSource()).setActionCommand(string);
441            }
442        });
443    }
444
445    /**
446     * Maps {@code Button.setLabel(String)} through queue
447     */
448    public void setLabel(final String string) {
449        runMapping(new MapVoidAction("setLabel") {
450            @Override
451            public void map() {
452                ((Button) getSource()).setLabel(string);
453            }
454        });
455    }
456
457    //End of mapping                                      //
458    ////////////////////////////////////////////////////////
459    /**
460     * Allows to find component by label.
461     */
462    public static class ButtonByLabelFinder implements ComponentChooser {
463
464        String label;
465        StringComparator comparator;
466
467        /**
468         * Constructs ButtonByLabelFinder.
469         *
470         * @param lb a text pattern
471         * @param comparator specifies string comparision algorithm.
472         */
473        public ButtonByLabelFinder(String lb, StringComparator comparator) {
474            label = lb;
475            this.comparator = comparator;
476        }
477
478        /**
479         * Constructs ButtonByLabelFinder.
480         *
481         * @param lb a text pattern
482         */
483        public ButtonByLabelFinder(String lb) {
484            this(lb, Operator.getDefaultStringComparator());
485        }
486
487        @Override
488        public boolean checkComponent(Component comp) {
489            if (comp instanceof Button) {
490                if (((Button) comp).getLabel() != null) {
491                    return (comparator.equals(((Button) comp).getLabel(),
492                            label));
493                }
494            }
495            return false;
496        }
497
498        @Override
499        public String getDescription() {
500            return "Button with label \"" + label + "\"";
501        }
502
503        @Override
504        public String toString() {
505            return "ButtonByLabelFinder{" + "label=" + label + ", comparator=" + comparator + '}';
506        }
507    }
508
509    /**
510     * Checks component type.
511     */
512    public static class ButtonFinder extends Finder {
513
514        /**
515         * Constructs AbstractButtonFinder.
516         *
517         * @param sf other searching criteria.
518         */
519        public ButtonFinder(ComponentChooser sf) {
520            super(Button.class, sf);
521        }
522
523        /**
524         * Constructs AbstractButtonFinder.
525         */
526        public ButtonFinder() {
527            super(Button.class);
528        }
529    }
530}
531