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.Window;
27import java.awt.event.WindowListener;
28import java.lang.reflect.InvocationTargetException;
29import java.util.ResourceBundle;
30
31import org.netbeans.jemmy.ClassReference;
32import org.netbeans.jemmy.ComponentChooser;
33import org.netbeans.jemmy.ComponentSearcher;
34import org.netbeans.jemmy.JemmyException;
35import org.netbeans.jemmy.JemmyProperties;
36import org.netbeans.jemmy.Outputable;
37import org.netbeans.jemmy.TestOut;
38import org.netbeans.jemmy.TimeoutExpiredException;
39import org.netbeans.jemmy.Timeouts;
40import org.netbeans.jemmy.WindowWaiter;
41import org.netbeans.jemmy.drivers.DriverManager;
42import org.netbeans.jemmy.drivers.WindowDriver;
43
44/**
45 * <BR><BR>Timeouts used: <BR>
46 * WindowWaiter.WaitWindowTimeout - time to wait window displayed <BR>
47 * WindowWaiter.AfterWindowTimeout - time to sleep after window has been
48 * dispayed <BR>.
49 *
50 * @see org.netbeans.jemmy.Timeouts
51 *
52 * @author Alexandre Iline (alexandre.iline@oracle.com)
53 *
54 */
55public class WindowOperator extends ContainerOperator<Window>
56        implements Outputable {
57
58    TestOut output;
59    WindowDriver driver;
60
61    /**
62     * Constructor.
63     *
64     * @param w a component
65     */
66    public WindowOperator(Window w) {
67        super(w);
68        driver = DriverManager.getWindowDriver(getClass());
69    }
70
71    /**
72     * Constructs a DialogOperator object.
73     *
74     * @param owner window - owner
75     * @param chooser a component chooser specifying searching criteria.
76     * @param index an index between appropriate ones.
77     */
78    public WindowOperator(WindowOperator owner, ComponentChooser chooser, int index) {
79        this(owner.
80                waitSubWindow(chooser,
81                        index));
82        copyEnvironment(owner);
83    }
84
85    /**
86     * Constructs a DialogOperator object.
87     *
88     * @param owner window - owner
89     * @param chooser a component chooser specifying searching criteria.
90     */
91    public WindowOperator(WindowOperator owner, ComponentChooser chooser) {
92        this(owner, chooser, 0);
93    }
94
95    /**
96     * Constructor. Waits for the index'th displayed owner's child. Uses owner's
97     * timeout and output for waiting and to init operator.
98     *
99     * @param owner Operator pointing on a window owner.
100     * @param index an index between appropriate ones.
101     * @throws TimeoutExpiredException
102     */
103    public WindowOperator(WindowOperator owner, int index) {
104        this(waitWindow(owner,
105                ComponentSearcher.getTrueChooser("Any Window"),
106                index));
107        copyEnvironment(owner);
108    }
109
110    /**
111     * Constructor. Waits for the first displayed owner's child. Uses owner's
112     * timeout and output for waiting and to init operator.
113     *
114     * @param owner Operator pointing on a window owner.
115     * @throws TimeoutExpiredException
116     */
117    public WindowOperator(WindowOperator owner) {
118        this(owner, 0);
119    }
120
121    /**
122     * Constructor. Waits for the index'th displayed window. Constructor can be
123     * used in complicated cases when output or timeouts should differ from
124     * default.
125     *
126     * @param index an index between appropriate ones.
127     * @param env an operator to copy environment from.
128     * @throws TimeoutExpiredException
129     */
130    public WindowOperator(int index, Operator env) {
131        this(waitWindow(ComponentSearcher.getTrueChooser("Any Window"),
132                index, env.getTimeouts(), env.getOutput()));
133        copyEnvironment(env);
134    }
135
136    /**
137     * Constructor. Waits for the index'th displayed window. Uses current
138     * timeouts and output values.
139     *
140     * @see JemmyProperties#getCurrentTimeouts()
141     * @see JemmyProperties#getCurrentOutput()
142     * @param index an index between appropriate ones.
143     * @throws TimeoutExpiredException
144     */
145    public WindowOperator(int index) {
146        this(index,
147                getEnvironmentOperator());
148    }
149
150    /**
151     * Constructor. Waits for the first displayed window. Uses current timeouts
152     * and output values.
153     *
154     * @see JemmyProperties#getCurrentTimeouts()
155     * @see JemmyProperties#getCurrentOutput()
156     * @throws TimeoutExpiredException
157     */
158    public WindowOperator() {
159        this(0);
160    }
161
162    /**
163     * Searches an index'th window.
164     *
165     * @param chooser a component chooser specifying searching criteria.
166     * @param index an index between appropriate ones.
167     * @return a window
168     */
169    public static Window findWindow(ComponentChooser chooser, int index) {
170        return WindowWaiter.getWindow(chooser, index);
171    }
172
173    /**
174     * Searches a window.
175     *
176     * @param chooser a component chooser specifying searching criteria.
177     * @return a window
178     */
179    public static Window findWindow(ComponentChooser chooser) {
180        return findWindow(chooser, 0);
181    }
182
183    /**
184     * Searches an index'th window.
185     *
186     * @param owner Window - owner.
187     * @param chooser a component chooser specifying searching criteria.
188     * @param index an index between appropriate ones.
189     * @return a window
190     */
191    public static Window findWindow(Window owner, ComponentChooser chooser, int index) {
192        return WindowWaiter.getWindow(owner, chooser, index);
193    }
194
195    /**
196     * Searches a window.
197     *
198     * @param owner Window - owner.
199     * @param chooser a component chooser specifying searching criteria.
200     * @return a window
201     */
202    public static Window findWindow(Window owner, ComponentChooser chooser) {
203        return findWindow(owner, chooser, 0);
204    }
205
206    /**
207     * Waits an index'th window.
208     *
209     * @param chooser a component chooser specifying searching criteria.
210     * @param index an index between appropriate ones.
211     * @throws TimeoutExpiredException
212     * @return a window
213     */
214    public static Window waitWindow(ComponentChooser chooser, int index) {
215        return (waitWindow(chooser, index,
216                JemmyProperties.getCurrentTimeouts(),
217                JemmyProperties.getCurrentOutput()));
218    }
219
220    /**
221     * Waits a window.
222     *
223     * @param chooser a component chooser specifying searching criteria.
224     * @throws TimeoutExpiredException
225     * @return a window
226     */
227    public static Window waitWindow(ComponentChooser chooser) {
228        return waitWindow(chooser, 0);
229    }
230
231    /**
232     * Waits an index'th window.
233     *
234     * @param owner Window - owner.
235     * @param chooser a component chooser specifying searching criteria.
236     * @param index an index between appropriate ones.
237     * @throws TimeoutExpiredException
238     * @return a window
239     */
240    public static Window waitWindow(Window owner, ComponentChooser chooser, int index) {
241        return (waitWindow(owner, chooser, index,
242                JemmyProperties.getCurrentTimeouts(),
243                JemmyProperties.getCurrentOutput()));
244    }
245
246    /**
247     * Waits a window.
248     *
249     * @param owner Window - owner.
250     * @param chooser a component chooser specifying searching criteria.
251     * @throws TimeoutExpiredException
252     * @return a window
253     */
254    public static Window waitWindow(Window owner, ComponentChooser chooser) {
255        return waitWindow(owner, chooser, 0);
256    }
257
258    @Override
259    public void setOutput(TestOut out) {
260        super.setOutput(out);
261        output = out;
262    }
263
264    @Override
265    public TestOut getOutput() {
266        return output;
267    }
268
269    @Override
270    public void copyEnvironment(Operator anotherOperator) {
271        super.copyEnvironment(anotherOperator);
272        driver
273                = (WindowDriver) DriverManager.
274                getDriver(DriverManager.WINDOW_DRIVER_ID,
275                        getClass(),
276                        anotherOperator.getProperties());
277    }
278
279    /**
280     * Activates the window. Uses WindowDriver registered for the operator type.
281     */
282    public void activate() {
283        output.printLine("Activate window\n    " + getSource().toString());
284        output.printGolden("Activate window");
285        driver.activate(this);
286    }
287
288    /**
289     * Requests the window to close. Uses WindowDriver registered for the
290     * operator type.
291     */
292    public void requestClose() {
293        output.printLine("Requesting close of window\n    " + getSource().toString());
294        output.printGolden("Requesting close of window");
295        driver.requestClose(this);
296    }
297
298    /**
299     * Closes a window by requesting it to close and then hiding it. Not
300     * implemented for internal frames at the moment. Uses WindowDriver
301     * registered for the operator type.
302     *
303     * @see #requestClose()
304     */
305    public void requestCloseAndThenHide() {
306        output.printLine("Closing window\n    " + getSource().toString());
307        output.printGolden("Closing window");
308        driver.requestCloseAndThenHide(this);
309        if (getVerification()) {
310            waitClosed();
311        }
312    }
313
314    /**
315     * Closes a window by requesting it to close and then, if it's a top-level
316     * frame, hiding it. Uses WindowDriver registered for the operator type.
317     *
318     * @deprecated Use requestClose(). It is the target window's responsibility
319     * to hide itself if needed. Or, if you really have to, use
320     * requestCloseAndThenHide().
321     * @see #requestClose()
322     * @see #requestCloseAndThenHide()
323     */
324    @Deprecated
325    public void close() {
326        output.printLine("Closing window\n    " + getSource().toString());
327        output.printGolden("Closing window");
328        driver.close(this);
329        if (getVerification()) {
330            waitClosed();
331        }
332    }
333
334    /**
335     * Moves the window to another location. Uses WindowDriver registered for
336     * the operator type.
337     *
338     * @param x coordinate in screen coordinate system
339     * @param y coordinate in screen coordinate system
340     */
341    public void move(int x, int y) {
342        output.printLine("Moving frame\n    " + getSource().toString());
343        output.printGolden("Moving frame");
344        driver.move(this, x, y);
345    }
346
347    /**
348     * Resizes the window. Uses WindowDriver registered for the operator type.
349     *
350     * @param width new width
351     * @param height new height
352     */
353    public void resize(int width, int height) {
354        output.printLine("Resizing frame\n    " + getSource().toString());
355        output.printGolden("Resizing frame");
356        driver.resize(this, width, height);
357    }
358
359    /**
360     * Searches an index'th window between windows owned by this window.
361     *
362     * @param chooser a component chooser specifying searching criteria.
363     * @param index an index between appropriate ones.
364     * @return a subwindow
365     */
366    public Window findSubWindow(ComponentChooser chooser, int index) {
367        getOutput().printLine("Looking for \"" + chooser.getDescription()
368                + "\" subwindow");
369        return findWindow((Window) getSource(), chooser, index);
370    }
371
372    /**
373     * Searches a window between windows owned by this window.
374     *
375     * @param chooser a component chooser specifying searching criteria.
376     * @return a subwindow
377     */
378    public Window findSubWindow(ComponentChooser chooser) {
379        return findSubWindow(chooser, 0);
380    }
381
382    /**
383     * Waits an index'th window between windows owned by this window.
384     *
385     * @param chooser a component chooser specifying searching criteria.
386     * @param index an index between appropriate ones.
387     * @return a subwindow
388     */
389    public Window waitSubWindow(ComponentChooser chooser, int index) {
390        getOutput().printLine("Waiting for \"" + chooser.getDescription()
391                + "\" subwindow");
392        WindowWaiter ww = new WindowWaiter();
393        ww.setOutput(getOutput());
394        ww.setTimeouts(getTimeouts());
395        try {
396            return ww.waitWindow((Window) getSource(), chooser, index);
397        } catch (InterruptedException e) {
398            throw (new JemmyException("Waiting for \"" + chooser.getDescription()
399                    + "\" window has been interrupted", e));
400        }
401    }
402
403    /**
404     * Waits a window between windows owned by this window.
405     *
406     * @param chooser a component chooser specifying searching criteria.
407     * @return a subwindow
408     */
409    public Window waitSubWindow(ComponentChooser chooser) {
410        return waitSubWindow(chooser, 0);
411    }
412
413    /**
414     * Waits the window to be closed.
415     */
416    public void waitClosed() {
417        getOutput().printLine("Wait window to be closed \n    : "
418                + getSource().toString());
419        getOutput().printGolden("Wait window to be closed");
420        waitState(new ComponentChooser() {
421            @Override
422            public boolean checkComponent(Component comp) {
423                return !comp.isVisible();
424            }
425
426            @Override
427            public String getDescription() {
428                return "Closed window";
429            }
430
431            @Override
432            public String toString() {
433                return "WindowOperator.waitClosed.Action{description = " + getDescription() + '}';
434            }
435        });
436    }
437
438    ////////////////////////////////////////////////////////
439    //Mapping                                             //
440    /**
441     * Maps {@code Window.addWindowListener(WindowListener)} through queue
442     */
443    public void addWindowListener(final WindowListener windowListener) {
444        runMapping(new MapVoidAction("addWindowListener") {
445            @Override
446            public void map() {
447                ((Window) getSource()).addWindowListener(windowListener);
448            }
449        });
450    }
451
452    /**
453     * Maps {@code Window.applyResourceBundle(String)} through queue
454     */
455    @Deprecated
456    public void applyResourceBundle(final String string) {
457        runMapping(new MapVoidAction("applyResourceBundle") {
458            @Override
459            public void map() {
460                ((Window) getSource()).applyResourceBundle(string);
461            }
462        });
463    }
464
465    /**
466     * Maps {@code Window.applyResourceBundle(ResourceBundle)} through queue
467     */
468    @Deprecated
469    public void applyResourceBundle(final ResourceBundle resourceBundle) {
470        runMapping(new MapVoidAction("applyResourceBundle") {
471            @Override
472            public void map() {
473                ((Window) getSource()).applyResourceBundle(resourceBundle);
474            }
475        });
476    }
477
478    /**
479     * Maps {@code Window.dispose()} through queue
480     */
481    public void dispose() {
482        runMapping(new MapVoidAction("dispose") {
483            @Override
484            public void map() {
485                ((Window) getSource()).dispose();
486            }
487        });
488    }
489
490    /**
491     * Maps {@code Window.getFocusOwner()} through queue
492     */
493    public Component getFocusOwner() {
494        return (runMapping(new MapAction<Component>("getFocusOwner") {
495            @Override
496            public Component map() {
497                return ((Window) getSource()).getFocusOwner();
498            }
499        }));
500    }
501
502    /**
503     * Maps {@code Window.getOwnedWindows()} through queue
504     */
505    public Window[] getOwnedWindows() {
506        return ((Window[]) runMapping(new MapAction<Object>("getOwnedWindows") {
507            @Override
508            public Object map() {
509                return ((Window) getSource()).getOwnedWindows();
510            }
511        }));
512    }
513
514    /**
515     * Maps {@code Window.getOwner()} through queue
516     */
517    public Window getOwner() {
518        return (runMapping(new MapAction<Window>("getOwner") {
519            @Override
520            public Window map() {
521                return ((Window) getSource()).getOwner();
522            }
523        }));
524    }
525
526    /**
527     * Maps {@code Window.getWarningString()} through queue
528     */
529    public String getWarningString() {
530        return (runMapping(new MapAction<String>("getWarningString") {
531            @Override
532            public String map() {
533                return ((Window) getSource()).getWarningString();
534            }
535        }));
536    }
537
538    /**
539     * Maps {@code Window.pack()} through queue
540     */
541    public void pack() {
542        runMapping(new MapVoidAction("pack") {
543            @Override
544            public void map() {
545                ((Window) getSource()).pack();
546            }
547        });
548    }
549
550    /**
551     * Maps {@code Window.removeWindowListener(WindowListener)} through queue
552     */
553    public void removeWindowListener(final WindowListener windowListener) {
554        runMapping(new MapVoidAction("removeWindowListener") {
555            @Override
556            public void map() {
557                ((Window) getSource()).removeWindowListener(windowListener);
558            }
559        });
560    }
561
562    /**
563     * Maps {@code Window.toBack()} through queue
564     */
565    public void toBack() {
566        runMapping(new MapVoidAction("toBack") {
567            @Override
568            public void map() {
569                ((Window) getSource()).toBack();
570            }
571        });
572    }
573
574    /**
575     * Maps {@code Window.toFront()} through queue
576     */
577    public void toFront() {
578        runMapping(new MapVoidAction("toFront") {
579            @Override
580            public void map() {
581                ((Window) getSource()).toFront();
582            }
583        });
584    }
585
586    //End of mapping                                      //
587    ////////////////////////////////////////////////////////
588    ////////////////////////////////////////////////////////
589    //Mapping 1.4                                         //
590    /**
591     * Maps {@code Window.isFocused()} through queue.
592     *
593     * @return result of the mapped method
594     */
595    public boolean isFocused() {
596        if (System.getProperty("java.specification.version").compareTo("1.3") > 0) {
597            return (runMapping(new MapBooleanAction("isFocused") {
598                @Override
599                public boolean map() {
600                    try {
601                        return (((Boolean) new ClassReference(getSource()).
602                                invokeMethod("isFocused", null, null)).booleanValue());
603                    } catch (InvocationTargetException e) {
604                        return false;
605                    } catch (NoSuchMethodException e) {
606                        return false;
607                    } catch (IllegalAccessException e) {
608                        return false;
609                    }
610                }
611            }));
612        } else {
613            return getFocusOwner() != null;
614        }
615    }
616
617    /**
618     * Maps {@code Window.isActive()} through queue.
619     *
620     * @return result of the mapped method
621     */
622    public boolean isActive() {
623        if (System.getProperty("java.specification.version").compareTo("1.3") > 0) {
624            return (runMapping(new MapBooleanAction("isActive") {
625                @Override
626                public boolean map() {
627                    try {
628                        return (((Boolean) new ClassReference(getSource()).
629                                invokeMethod("isActive", null, null)).booleanValue());
630                    } catch (InvocationTargetException e) {
631                        return false;
632                    } catch (NoSuchMethodException e) {
633                        return false;
634                    } catch (IllegalAccessException e) {
635                        return false;
636                    }
637                }
638            }));
639        } else {
640            return isShowing();
641        }
642    }
643
644    //End of mapping 1.4                                  //
645    ////////////////////////////////////////////////////////
646    /**
647     * A method to be used from subclasses. Uses timeouts and output passed as
648     * parameters during the waiting.
649     *
650     * @param chooser org.netbeans.jemmy.ComponentChooser implementation.
651     * @param index Ordinal component index.
652     * @param timeouts timeouts to be used during the waiting.
653     * @param output an output to be used during the waiting.
654     * @return Component instance or null if component was not found.
655     */
656    protected static Window waitWindow(ComponentChooser chooser, int index,
657            Timeouts timeouts, TestOut output) {
658        try {
659            WindowWaiter waiter = new WindowWaiter();
660            waiter.setTimeouts(timeouts);
661            waiter.setOutput(output);
662            return waiter.waitWindow(chooser, index);
663        } catch (InterruptedException e) {
664            output.printStackTrace(e);
665            return null;
666        }
667    }
668
669    /**
670     * A method to be used from subclasses. Uses {@code owner}'s timeouts
671     * and output during the waiting.
672     *
673     * @param owner a window - dialog owner.
674     * @param chooser org.netbeans.jemmy.ComponentChooser implementation.
675     * @param index Ordinal component index.
676     * @return Component instance or null if component was not found.
677     */
678    protected static Window waitWindow(WindowOperator owner, ComponentChooser chooser, int index) {
679        return (waitWindow((Window) owner.getSource(),
680                chooser, index,
681                owner.getTimeouts(), owner.getOutput()));
682    }
683
684    /**
685     * A method to be used from subclasses. Uses timeouts and output passed as
686     * parameters during the waiting.
687     *
688     * @param owner a window - dialog owner.
689     * @param chooser org.netbeans.jemmy.ComponentChooser implementation.
690     * @param index Ordinal component index.
691     * @param timeouts timeouts to be used during the waiting.
692     * @param output an output to be used during the waiting.
693     * @return Component instance or null if component was not found.
694     */
695    protected static Window waitWindow(Window owner, ComponentChooser chooser, int index,
696            Timeouts timeouts, TestOut output) {
697        try {
698            WindowWaiter waiter = new WindowWaiter();
699            waiter.setTimeouts(timeouts);
700            waiter.setOutput(output);
701            return waiter.waitWindow(owner, chooser, index);
702        } catch (InterruptedException e) {
703            JemmyProperties.getCurrentOutput().printStackTrace(e);
704            return null;
705        }
706    }
707
708}
709