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.Container;
27import java.util.Hashtable;
28
29import javax.swing.JMenu;
30import javax.swing.JMenuItem;
31import javax.swing.JPopupMenu;
32import javax.swing.event.MenuListener;
33
34import org.netbeans.jemmy.Action;
35import org.netbeans.jemmy.ComponentChooser;
36import org.netbeans.jemmy.Outputable;
37import org.netbeans.jemmy.TestOut;
38import org.netbeans.jemmy.TimeoutExpiredException;
39import org.netbeans.jemmy.Timeoutable;
40import org.netbeans.jemmy.Timeouts;
41import org.netbeans.jemmy.drivers.DescriptablePathChooser;
42import org.netbeans.jemmy.drivers.DriverManager;
43import org.netbeans.jemmy.drivers.MenuDriver;
44
45/**
46 * <BR><BR>Timeouts used: <BR>
47 * JMenuOperator.WaitBeforePopupTimeout - time to sleep before popup expanding
48 * <BR>
49 * JMenuOperator.WaitPopupTimeout - time to wait popup displayed <BR>
50 * JMenuOperator.PushMenuTimeout - time for the whole menu operation<BR>
51 * JMenuItemOperator.PushMenuTimeout - time between button pressing and
52 * releasing<BR>
53 * ComponentOperator.WaitComponentTimeout - time to wait button displayed <BR>
54 * ComponentOperator.WaitComponentEnabledTimeout - time to wait button enabled
55 * <BR>.
56 *
57 * @see org.netbeans.jemmy.Timeouts
58 *
59 * @author Alexandre Iline (alexandre.iline@oracle.com)
60 *
61 */
62public class JMenuOperator extends JMenuItemOperator
63        implements Outputable, Timeoutable {
64
65    /**
66     * Identifier for a "submenu" properties.
67     *
68     * @see #getDump
69     */
70    public static final String SUBMENU_PREFIX_DPROP = "Submenu";
71
72    private final static long WAIT_POPUP_TIMEOUT = 60000;
73    private final static long WAIT_BEFORE_POPUP_TIMEOUT = 0;
74    private final static long PUSH_MENU_TIMEOUT = 60000;
75
76    private Timeouts timeouts;
77    private TestOut output;
78    private MenuDriver driver;
79
80    /**
81     * Constructor.
82     *
83     * @param menu a component
84     */
85    public JMenuOperator(JMenu menu) {
86        super(menu);
87        driver = DriverManager.getMenuDriver(this);
88    }
89
90    /**
91     * Constructs a JMenuOperator object.
92     *
93     * @param cont a container
94     * @param chooser a component chooser specifying searching criteria.
95     * @param index an index between appropriate ones.
96     */
97    public JMenuOperator(ContainerOperator<?> cont, ComponentChooser chooser, int index) {
98        this((JMenu) cont.
99                waitSubComponent(new JMenuFinder(chooser),
100                        index));
101        copyEnvironment(cont);
102    }
103
104    /**
105     * Constructs a JMenuOperator object.
106     *
107     * @param cont a container
108     * @param chooser a component chooser specifying searching criteria.
109     */
110    public JMenuOperator(ContainerOperator<?> cont, ComponentChooser chooser) {
111        this(cont, chooser, 0);
112    }
113
114    /**
115     * Constructor. Waits component in container first. Uses cont's timeout and
116     * output for waiting and to init operator.
117     *
118     * @param cont a container
119     * @param text Button text.
120     * @param index Ordinal component index.
121     * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean)
122     * @throws TimeoutExpiredException
123     */
124    public JMenuOperator(ContainerOperator<?> cont, String text, int index) {
125        this((JMenu) waitComponent(cont,
126                new JMenuByLabelFinder(text,
127                        cont.getComparator()),
128                index));
129        copyEnvironment(cont);
130    }
131
132    /**
133     * Constructor. Waits component in container first. Uses cont's timeout and
134     * output for waiting and to init operator.
135     *
136     * @param cont a container
137     * @param text Button text.
138     * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean)
139     * @throws TimeoutExpiredException
140     */
141    public JMenuOperator(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 a container
150     * @param index Ordinal component index.
151     * @throws TimeoutExpiredException
152     */
153    public JMenuOperator(ContainerOperator<?> cont, int index) {
154        this((JMenu) waitComponent(cont,
155                new JMenuFinder(),
156                index));
157        copyEnvironment(cont);
158    }
159
160    /**
161     * Constructor. Waits component in container first. Uses cont's timeout and
162     * output for waiting and to init operator.
163     *
164     * @param cont a container
165     * @throws TimeoutExpiredException
166     */
167    public JMenuOperator(ContainerOperator<?> cont) {
168        this(cont, 0);
169    }
170
171    /**
172     * Searches JMenu in container.
173     *
174     * @param cont Container to search component in.
175     * @param chooser org.netbeans.jemmy.ComponentChooser implementation.
176     * @param index Ordinal component index.
177     * @return JMenu instance or null if component was not found.
178     */
179    public static JMenu findJMenu(Container cont, ComponentChooser chooser, int index) {
180        return (JMenu) findComponent(cont, new JMenuFinder(chooser), index);
181    }
182
183    /**
184     * Searches 0'th JMenu in container.
185     *
186     * @param cont Container to search component in.
187     * @param chooser org.netbeans.jemmy.ComponentChooser implementation.
188     * @return JMenu instance or null if component was not found.
189     */
190    public static JMenu findJMenu(Container cont, ComponentChooser chooser) {
191        return findJMenu(cont, chooser, 0);
192    }
193
194    /**
195     * Searches JMenu by text.
196     *
197     * @param cont Container to search component in.
198     * @param text Button text. If null, contents is not checked.
199     * @param ce Compare text exactly.
200     * @param ccs Compare text case sensitively.
201     * @param index Ordinal component index.
202     * @return JMenu instance or null if component was not found.
203     * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean)
204     */
205    public static JMenu findJMenu(Container cont, String text, boolean ce, boolean ccs, int index) {
206        return (findJMenu(cont,
207                new JMenuByLabelFinder(text,
208                        new DefaultStringComparator(ce, ccs)),
209                index));
210    }
211
212    /**
213     * Searches JMenu by text.
214     *
215     * @param cont Container to search component in.
216     * @param text Button text. If null, contents is not checked.
217     * @param ce Compare text exactly.
218     * @param ccs Compare text case sensitively.
219     * @return JMenu instance or null if component was not found.
220     * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean)
221     */
222    public static JMenu findJMenu(Container cont, String text, boolean ce, boolean ccs) {
223        return findJMenu(cont, text, ce, ccs, 0);
224    }
225
226    /**
227     * Waits JMenu in container.
228     *
229     * @param cont Container to search component in.
230     * @param chooser org.netbeans.jemmy.ComponentChooser implementation.
231     * @param index Ordinal component index.
232     * @return JMenu instance.
233     * @throws TimeoutExpiredException
234     */
235    public static JMenu waitJMenu(final Container cont, final ComponentChooser chooser, final int index) {
236        return (JMenu) waitComponent(cont, new JMenuFinder(chooser), index);
237    }
238
239    /**
240     * Waits 0'th JMenu in container.
241     *
242     * @param cont Container to search component in.
243     * @param chooser org.netbeans.jemmy.ComponentChooser implementation.
244     * @return JMenu instance.
245     * @throws TimeoutExpiredException
246     */
247    public static JMenu waitJMenu(Container cont, ComponentChooser chooser) {
248        return waitJMenu(cont, chooser, 0);
249    }
250
251    /**
252     * Waits JMenu by text.
253     *
254     * @param cont Container to search component in.
255     * @param text Button text. If null, contents is not checked.
256     * @param ce Compare text exactly.
257     * @param ccs Compare text case sensitively.
258     * @param index Ordinal component index.
259     * @return JMenu instance.
260     * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean)
261     * @throws TimeoutExpiredException
262     */
263    public static JMenu waitJMenu(Container cont, String text, boolean ce, boolean ccs, int index) {
264        return (waitJMenu(cont,
265                new JMenuByLabelFinder(text,
266                        new DefaultStringComparator(ce, ccs)),
267                index));
268    }
269
270    /**
271     * Waits JMenu by text.
272     *
273     * @param cont Container to search component in.
274     * @param text Button text. If null, contents is not checked.
275     * @param ce Compare text exactly.
276     * @param ccs Compare text case sensitively.
277     * @return JMenu instance.
278     * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean)
279     * @throws TimeoutExpiredException
280     */
281    public static JMenu waitJMenu(Container cont, String text, boolean ce, boolean ccs) {
282        return waitJMenu(cont, text, ce, ccs, 0);
283    }
284
285    public static void performInit() {
286        Timeouts.initDefault("JMenuOperator.WaitBeforePopupTimeout", WAIT_BEFORE_POPUP_TIMEOUT);
287        Timeouts.initDefault("JMenuOperator.WaitPopupTimeout", WAIT_POPUP_TIMEOUT);
288        Timeouts.initDefault("JMenuOperator.PushMenuTimeout", PUSH_MENU_TIMEOUT);
289    }
290
291    static {
292        performInit();
293    }
294
295    @Override
296    public void setTimeouts(Timeouts timeouts) {
297        super.setTimeouts(timeouts);
298        this.timeouts = timeouts;
299    }
300
301    @Override
302    public Timeouts getTimeouts() {
303        return timeouts;
304    }
305
306    @Override
307    public void setOutput(TestOut out) {
308        super.setOutput(out);
309        output = out;
310    }
311
312    @Override
313    public TestOut getOutput() {
314        return output;
315    }
316
317    @Override
318    public void copyEnvironment(Operator anotherOperator) {
319        super.copyEnvironment(anotherOperator);
320        driver = DriverManager.getMenuDriver(this);
321    }
322
323    /**
324     * Pushes menu.
325     *
326     * @param choosers Array of choosers to find menuItems to push.
327     * @return Last pushed JMenuItem.
328     * @throws TimeoutExpiredException
329     */
330    public JMenuItem pushMenu(final ComponentChooser[] choosers) {
331        return ((JMenuItem) produceTimeRestricted(new Action<Object, Void>() {
332            @Override
333            public Object launch(Void obj) {
334                //TDB 1.5 menu workaround
335                getQueueTool().waitEmpty();
336                Object result = driver.pushMenu(JMenuOperator.this, converChoosers(choosers));
337                getQueueTool().waitEmpty();
338                return result;
339            }
340
341            @Override
342            public String getDescription() {
343                return createDescription(choosers);
344            }
345
346            @Override
347            public String toString() {
348                return "JMenuOperator.pushMenu.Action{description = " + getDescription() + '}';
349            }
350        }, "JMenuOperator.PushMenuTimeout"));
351    }
352
353    /**
354     * Executes {@code pushMenu(choosers)} in a separate thread.
355     *
356     * @param choosers Array of choosers to find menuItems to push.
357     * @see #pushMenu(ComponentChooser[])
358     */
359    public void pushMenuNoBlock(final ComponentChooser[] choosers) {
360        produceNoBlocking(new NoBlockingAction<Object, Void>("Menu pushing") {
361            @Override
362            public Object doAction(Void param) {
363                //TDB 1.5 menu workaround
364                getQueueTool().waitEmpty();
365                Object result = driver.pushMenu(JMenuOperator.this, converChoosers(choosers));
366                getQueueTool().waitEmpty();
367                return result;
368            }
369        });
370    }
371
372    /**
373     * Pushes menu.
374     *
375     * @param names an array of menu texts.
376     * @param comparator a string comparision algorithm
377     * @return Last pushed JMenuItem.
378     * @throws TimeoutExpiredException
379     */
380    public JMenuItem pushMenu(String[] names, StringComparator comparator) {
381        return pushMenu(JMenuItemOperator.createChoosers(names, comparator));
382    }
383
384    /**
385     * Pushes menu.
386     *
387     * @param names Menu items texts.
388     * @param ce Compare text exactly.
389     * @param ccs Compare text case sensitively.
390     * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean)
391     * @throws TimeoutExpiredException
392     * @return Last pushed JMenuItem.
393     * @deprecated Use pushMenu(String[]) or pushMenu(String[],
394     * StringComparator)
395     */
396    @Deprecated
397    public JMenuItem pushMenu(String[] names, boolean ce, boolean ccs) {
398        return pushMenu(names, new DefaultStringComparator(ce, ccs));
399    }
400
401    /**
402     * Executes {@code pushMenu(names, ce, ccs)} in a separate thread.
403     *
404     * @param names an array of menu texts.
405     * @param comparator a string comparision algorithm
406     */
407    public void pushMenuNoBlock(String[] names, StringComparator comparator) {
408        pushMenuNoBlock(JMenuItemOperator.createChoosers(names, comparator));
409    }
410
411    /**
412     * Executes {@code pushMenu(names, ce, ccs)} in a separate thread.
413     *
414     * @param names Menu items texts.
415     * @param ce Compare text exactly.
416     * @param ccs Compare text case sensitively.
417     * @see #pushMenu(String[], boolean,boolean)
418     * @deprecated Use pushMenuNoBlock(String[]) or pushMenuNoBlock(String[],
419     * StringComparator)
420     */
421    @Deprecated
422    public void pushMenuNoBlock(String[] names, boolean ce, boolean ccs) {
423        pushMenuNoBlock(names, new DefaultStringComparator(ce, ccs));
424    }
425
426    /**
427     * Pushes menu. Uses StringComparator assigned to this object,
428     *
429     * @param names Menu items texts.
430     * @return Last pushed JMenuItem.
431     * @throws TimeoutExpiredException
432     */
433    public JMenuItem pushMenu(String[] names) {
434        return pushMenu(names, getComparator());
435    }
436
437    /**
438     * Executes {@code pushMenu(names)} in a separate thread.
439     *
440     * @param names Menu items texts.
441     * @see #pushMenu(String[])
442     */
443    public void pushMenuNoBlock(String[] names) {
444        pushMenuNoBlock(names, getComparator());
445    }
446
447    /**
448     * Pushes menu.
449     *
450     * @param path a menu path.
451     * @param delim a path delimiter.
452     * @param comparator a string comparision algorithm
453     * @return Last pushed JMenuItem.
454     * @throws TimeoutExpiredException
455     */
456    public JMenuItem pushMenu(String path, String delim, StringComparator comparator) {
457        output.printLine("Pushing " + path + " menu in \n    " + toStringSource());
458        output.printGolden("Pushing " + path + " menu in \n    " + toStringSource());
459        return pushMenu(parseString(path, delim), comparator);
460    }
461
462    /**
463     * Pushes menu. Uses PathParser assigned to this operator.
464     *
465     * @param path a menu path.
466     * @param comparator a string comparision algorithm
467     * @return Last pushed JMenuItem.
468     * @throws TimeoutExpiredException
469     */
470    public JMenuItem pushMenu(String path, StringComparator comparator) {
471        output.printLine("Pushing " + path + " menu in \n    " + toStringSource());
472        output.printGolden("Pushing " + path + " menu in \n    " + toStringSource());
473        return pushMenu(parseString(path), comparator);
474    }
475
476    /**
477     * Pushes menu.
478     *
479     * @param path String menupath representation ("File/New", for example).
480     * @param delim String menupath divider ("/").
481     * @param ce Compare text exactly.
482     * @param ccs Compare text case sensitively.
483     * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean)
484     * @return Last pushed JMenuItem.
485     * @throws TimeoutExpiredException
486     * @deprecated Use pushMenuNoBlock(String) or pushMenuNoBlock(String,
487     * StringComparator)
488     */
489    @Deprecated
490    public JMenuItem pushMenu(String path, String delim, boolean ce, boolean ccs) {
491        return pushMenu(path, delim, new DefaultStringComparator(ce, ccs));
492    }
493
494    /**
495     * Executes {@code pushMenu(names, delim, comparator)} in a separate
496     * thread.
497     *
498     * @param path a menu path.
499     * @param delim a path delimiter.
500     * @param comparator a string comparision algorithm
501     */
502    public void pushMenuNoBlock(String path, String delim, StringComparator comparator) {
503        output.printLine("Pushing " + path + " menu in \n    " + toStringSource());
504        output.printGolden("Pushing " + path + " menu in \n    " + toStringSource());
505        pushMenuNoBlock(parseString(path, delim), comparator);
506    }
507
508    /**
509     * Executes {@code pushMenu(names, comparator)} in a separate thread.
510     * Uses PathParser assigned to this operator.
511     *
512     * @param path a menu path.
513     * @param comparator a string comparision algorithm
514     */
515    public void pushMenuNoBlock(String path, StringComparator comparator) {
516        output.printLine("Pushing " + path + " menu in \n    " + toStringSource());
517        output.printGolden("Pushing " + path + " menu in \n    " + toStringSource());
518        pushMenuNoBlock(parseString(path), comparator);
519    }
520
521    /**
522     * Executes {@code pushMenu(path, delim, ce, ccs)} in a separate
523     * thread.
524     *
525     * @param path String menupath representation ("File/New", for example).
526     * @param delim String menupath divider ("/").
527     * @param ce Compare text exactly.
528     * @param ccs Compare text case sensitively.
529     * @see #pushMenu
530     * @deprecated Use pushMenuNoBlock(String, String) or
531     * pushMenuNoBlock(String, String, StringComparator)
532     */
533    @Deprecated
534    public void pushMenuNoBlock(String path, String delim, boolean ce, boolean ccs) {
535        pushMenuNoBlock(parseString(path, delim), new DefaultStringComparator(ce, ccs));
536    }
537
538    /**
539     * Pushes menu. Uses StringComparator assigned to this object,
540     *
541     * @param path String menupath representation ("File/New", for example).
542     * @param delim String menupath divider ("/").
543     * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean)
544     * @return Last pushed JMenuItem.
545     * @throws TimeoutExpiredException
546     */
547    public JMenuItem pushMenu(String path, String delim) {
548        output.printLine("Pushing " + path + " menu in \n    " + toStringSource());
549        output.printGolden("Pushing " + path + " menu in \n    " + toStringSource());
550        return pushMenu(parseString(path, delim));
551    }
552
553    /**
554     * Pushes menu. Uses PathParser assigned to this operator.
555     *
556     * @param path String menupath representation ("File/New", for example).
557     * @return Last pushed JMenuItem.
558     * @throws TimeoutExpiredException
559     */
560    public JMenuItem pushMenu(String path) {
561        output.printLine("Pushing " + path + " menu in \n    " + toStringSource());
562        output.printGolden("Pushing " + path + " menu in \n    " + toStringSource());
563        return pushMenu(parseString(path));
564    }
565
566    /**
567     * Executes {@code pushMenu(path, delim)} in a separate thread.
568     *
569     * @param path String menupath representation ("File/New", for example).
570     * @param delim String menupath divider ("/").
571     */
572    public void pushMenuNoBlock(String path, String delim) {
573        output.printLine("Pushing " + path + " menu in \n    " + toStringSource());
574        output.printGolden("Pushing " + path + " menu in \n    " + toStringSource());
575        pushMenuNoBlock(parseString(path, delim));
576    }
577
578    /**
579     * Executes {@code pushMenu(path)} in a separate thread.
580     *
581     * @param path String menupath representation ("File/New", for example).
582     */
583    public void pushMenuNoBlock(String path) {
584        output.printLine("Pushing " + path + " menu in \n    " + toStringSource());
585        output.printGolden("Pushing " + path + " menu in \n    " + toStringSource());
586        pushMenuNoBlock(parseString(path));
587    }
588
589    public JMenuItemOperator[] showMenuItems(ComponentChooser[] choosers) {
590        return JMenuItemOperator.getMenuItems((JMenu) pushMenu(choosers), this);
591    }
592
593    /**
594     * Shows submenu of menu specified by a {@code path} parameter.
595     *
596     * @param path an array of menu texts.
597     * @param comparator a string comparision algorithm
598     * @return an array of operators created tor items from the submenu.
599     * @throws TimeoutExpiredException
600     */
601    public JMenuItemOperator[] showMenuItems(String[] path, StringComparator comparator) {
602        return showMenuItems(JMenuItemOperator.createChoosers(path, comparator));
603    }
604
605    /**
606     * Shows submenu of menu specified by a {@code path} parameter. Uses
607     * StringComparator assigned to the operator.
608     *
609     * @param path an array of menu texts.
610     * @return an array of operators created tor items from the submenu.
611     * @throws TimeoutExpiredException
612     */
613    public JMenuItemOperator[] showMenuItems(String[] path) {
614        return showMenuItems(path, getComparator());
615    }
616
617    /**
618     * Shows submenu of menu specified by a {@code path} parameter.
619     *
620     * @param path a string identifying the menu path.
621     * @param delim a path delimiter.
622     * @param comparator a string comparision algorithm
623     * @return an array of operators created tor items from the submenu.
624     * @throws TimeoutExpiredException
625     */
626    public JMenuItemOperator[] showMenuItems(String path, String delim, StringComparator comparator) {
627        return showMenuItems(parseString(path, delim), comparator);
628    }
629
630    /**
631     * Shows submenu of menu specified by a {@code path} parameter. Uses
632     * StringComparator assigned to the operator.
633     *
634     * @param path a string identifying the menu path.
635     * @param delim a path delimiter.
636     * @return an array of operators created tor items from the submenu.
637     * @throws TimeoutExpiredException
638     */
639    public JMenuItemOperator[] showMenuItems(String path, String delim) {
640        return showMenuItems(path, delim, getComparator());
641    }
642
643    /**
644     * Shows submenu of menu specified by a {@code path} parameter. Uses
645     * PathParser assigned to this operator.
646     *
647     * @param path a string identifying the menu path.
648     * @param comparator a string comparision algorithm
649     * @return an array of operators created tor items from the submenu.
650     * @throws TimeoutExpiredException
651     */
652    public JMenuItemOperator[] showMenuItems(String path, StringComparator comparator) {
653        return showMenuItems(parseString(path), comparator);
654    }
655
656    /**
657     * Shows submenu of menu specified by a {@code path} parameter. Uses
658     * PathParser assigned to this operator. Uses StringComparator assigned to
659     * the operator.
660     *
661     * @param path a string identifying the menu path.
662     * @return an array of operators created tor items from the submenu.
663     * @throws TimeoutExpiredException
664     */
665    public JMenuItemOperator[] showMenuItems(String path) {
666        return showMenuItems(path, getComparator());
667    }
668
669    public JMenuItemOperator showMenuItem(ComponentChooser[] choosers) {
670        ComponentChooser[] parentPath = getParentPath(choosers);
671        JMenu menu;
672        if (parentPath.length > 0) {
673            menu = (JMenu) pushMenu(parentPath);
674        } else {
675            push();
676            menu = (JMenu) getSource();
677        }
678        JPopupMenuOperator popup = new JPopupMenuOperator(menu.getPopupMenu());
679        popup.copyEnvironment(this);
680        JMenuItemOperator result = new JMenuItemOperator(popup, choosers[choosers.length - 1]);
681        result.copyEnvironment(this);
682        return result;
683    }
684
685    /**
686     * Expends all menus to show menu item specified by a {@code path}
687     * parameter.
688     *
689     * @param path an array of menu texts.
690     * @param comparator a string comparision algorithm
691     * @return an operator for the last menu item in path.
692     * @throws TimeoutExpiredException
693     */
694    public JMenuItemOperator showMenuItem(String[] path, StringComparator comparator) {
695        String[] parentPath = getParentPath(path);
696        JMenu menu;
697        if (parentPath.length > 0) {
698            menu = (JMenu) pushMenu(parentPath, comparator);
699        } else {
700            push();
701            menu = (JMenu) getSource();
702        }
703        JPopupMenuOperator popup = new JPopupMenuOperator(menu.getPopupMenu());
704        popup.copyEnvironment(this);
705        JMenuItemOperator result = new JMenuItemOperator(popup, path[path.length - 1]);
706        result.copyEnvironment(this);
707        return result;
708    }
709
710    /**
711     * Expands all menus to show menu item specified by a {@code path}
712     * parameter.
713     *
714     * @param path an array of menu texts.
715     * @return an operator for the last menu item in path.
716     * @throws TimeoutExpiredException
717     */
718    public JMenuItemOperator showMenuItem(String[] path) {
719        return showMenuItem(path, getComparator());
720    }
721
722    /**
723     * Expands all menus to show menu item specified by a {@code path}
724     * parameter.
725     *
726     * @param path a string identifying the menu path.
727     * @param delim a path delimiter.
728     * @param comparator a string comparision algorithm
729     * @return an operator for the last menu item in path.
730     * @throws TimeoutExpiredException
731     */
732    public JMenuItemOperator showMenuItem(String path, String delim, StringComparator comparator) {
733        return showMenuItem(parseString(path, delim), comparator);
734    }
735
736    /**
737     * Expands all menus to show menu item specified by a {@code path}
738     * parameter. Uses StringComparator assigned to the operator.
739     *
740     * @param path a string identifying the menu path.
741     * @param delim a path delimiter.
742     * @return an operator for the last menu item in path.
743     * @throws TimeoutExpiredException
744     */
745    public JMenuItemOperator showMenuItem(String path, String delim) {
746        return showMenuItem(path, delim, getComparator());
747    }
748
749    /**
750     * Expands all menus to show menu item specified by a {@code path}
751     * parameter. Uses PathParser assigned to this operator.
752     *
753     * @param path a string identifying the menu path.
754     * @param comparator a string comparision algorithm
755     * @return an operator for the last menu item in path.
756     * @throws TimeoutExpiredException
757     */
758    public JMenuItemOperator showMenuItem(String path, StringComparator comparator) {
759        return showMenuItem(parseString(path), comparator);
760    }
761
762    /**
763     * Expands all menus to show menu item specified by a {@code path}
764     * parameter. Uses PathParser assigned to this operator. Uses
765     * StringComparator assigned to the operator.
766     *
767     * @param path a string identifying the menu path.
768     * @return an array of operators created tor items from the submenu.
769     * @throws TimeoutExpiredException
770     */
771    public JMenuItemOperator showMenuItem(String path) {
772        return showMenuItem(path, getComparator());
773    }
774
775    @Override
776    public Hashtable<String, Object> getDump() {
777        Hashtable<String, Object> result = super.getDump();
778        String[] items = new String[((JMenu) getSource()).getItemCount()];
779        for (int i = 0; i < ((JMenu) getSource()).getItemCount(); i++) {
780            if (((JMenu) getSource()).getItem(i) != null
781                    && ((JMenu) getSource()).getItem(i).getText() != null) {
782                items[i] = ((JMenu) getSource()).getItem(i).getText();
783            } else {
784                items[i] = "null";
785            }
786        }
787        addToDump(result, SUBMENU_PREFIX_DPROP, items);
788        return result;
789    }
790
791    ////////////////////////////////////////////////////////
792    //Mapping                                             //
793    /**
794     * Maps {@code JMenu.add(String)} through queue
795     */
796    public JMenuItem add(final String string) {
797        return (runMapping(new MapAction<JMenuItem>("add") {
798            @Override
799            public JMenuItem map() {
800                return ((JMenu) getSource()).add(string);
801            }
802        }));
803    }
804
805    /**
806     * Maps {@code JMenu.add(Action)} through queue
807     */
808    public JMenuItem add(final javax.swing.Action action) {
809        return (runMapping(new MapAction<JMenuItem>("add") {
810            @Override
811            public JMenuItem map() {
812                return ((JMenu) getSource()).add(action);
813            }
814        }));
815    }
816
817    /**
818     * Maps {@code JMenu.add(JMenuItem)} through queue
819     */
820    public JMenuItem add(final JMenuItem jMenuItem) {
821        return (runMapping(new MapAction<JMenuItem>("add") {
822            @Override
823            public JMenuItem map() {
824                return ((JMenu) getSource()).add(jMenuItem);
825            }
826        }));
827    }
828
829    /**
830     * Maps {@code JMenu.addMenuListener(MenuListener)} through queue
831     */
832    public void addMenuListener(final MenuListener menuListener) {
833        runMapping(new MapVoidAction("addMenuListener") {
834            @Override
835            public void map() {
836                ((JMenu) getSource()).addMenuListener(menuListener);
837            }
838        });
839    }
840
841    /**
842     * Maps {@code JMenu.addSeparator()} through queue
843     */
844    public void addSeparator() {
845        runMapping(new MapVoidAction("addSeparator") {
846            @Override
847            public void map() {
848                ((JMenu) getSource()).addSeparator();
849            }
850        });
851    }
852
853    /**
854     * Maps {@code JMenu.getDelay()} through queue
855     */
856    public int getDelay() {
857        return (runMapping(new MapIntegerAction("getDelay") {
858            @Override
859            public int map() {
860                return ((JMenu) getSource()).getDelay();
861            }
862        }));
863    }
864
865    /**
866     * Maps {@code JMenu.getItem(int)} through queue
867     */
868    public JMenuItem getItem(final int i) {
869        return (runMapping(new MapAction<JMenuItem>("getItem") {
870            @Override
871            public JMenuItem map() {
872                return ((JMenu) getSource()).getItem(i);
873            }
874        }));
875    }
876
877    /**
878     * Maps {@code JMenu.getItemCount()} through queue
879     */
880    public int getItemCount() {
881        return (runMapping(new MapIntegerAction("getItemCount") {
882            @Override
883            public int map() {
884                return ((JMenu) getSource()).getItemCount();
885            }
886        }));
887    }
888
889    /**
890     * Maps {@code JMenu.getMenuComponent(int)} through queue
891     */
892    public Component getMenuComponent(final int i) {
893        return (runMapping(new MapAction<Component>("getMenuComponent") {
894            @Override
895            public Component map() {
896                return ((JMenu) getSource()).getMenuComponent(i);
897            }
898        }));
899    }
900
901    /**
902     * Maps {@code JMenu.getMenuComponentCount()} through queue
903     */
904    public int getMenuComponentCount() {
905        return (runMapping(new MapIntegerAction("getMenuComponentCount") {
906            @Override
907            public int map() {
908                return ((JMenu) getSource()).getMenuComponentCount();
909            }
910        }));
911    }
912
913    /**
914     * Maps {@code JMenu.getMenuComponents()} through queue
915     */
916    public Component[] getMenuComponents() {
917        return ((Component[]) runMapping(new MapAction<Object>("getMenuComponents") {
918            @Override
919            public Object map() {
920                return ((JMenu) getSource()).getMenuComponents();
921            }
922        }));
923    }
924
925    /**
926     * Maps {@code JMenu.getPopupMenu()} through queue
927     */
928    public JPopupMenu getPopupMenu() {
929        return (runMapping(new MapAction<JPopupMenu>("getPopupMenu") {
930            @Override
931            public JPopupMenu map() {
932                return ((JMenu) getSource()).getPopupMenu();
933            }
934        }));
935    }
936
937    /**
938     * Maps {@code JMenu.insert(String, int)} through queue
939     */
940    public void insert(final String string, final int i) {
941        runMapping(new MapVoidAction("insert") {
942            @Override
943            public void map() {
944                ((JMenu) getSource()).insert(string, i);
945            }
946        });
947    }
948
949    /**
950     * Maps {@code JMenu.insert(Action, int)} through queue
951     */
952    public JMenuItem insert(final javax.swing.Action action, final int i) {
953        return (runMapping(new MapAction<JMenuItem>("insert") {
954            @Override
955            public JMenuItem map() {
956                return ((JMenu) getSource()).insert(action, i);
957            }
958        }));
959    }
960
961    /**
962     * Maps {@code JMenu.insert(JMenuItem, int)} through queue
963     */
964    public JMenuItem insert(final JMenuItem jMenuItem, final int i) {
965        return (runMapping(new MapAction<JMenuItem>("insert") {
966            @Override
967            public JMenuItem map() {
968                return ((JMenu) getSource()).insert(jMenuItem, i);
969            }
970        }));
971    }
972
973    /**
974     * Maps {@code JMenu.insertSeparator(int)} through queue
975     */
976    public void insertSeparator(final int i) {
977        runMapping(new MapVoidAction("insertSeparator") {
978            @Override
979            public void map() {
980                ((JMenu) getSource()).insertSeparator(i);
981            }
982        });
983    }
984
985    /**
986     * Maps {@code JMenu.isMenuComponent(Component)} through queue
987     */
988    public boolean isMenuComponent(final Component component) {
989        return (runMapping(new MapBooleanAction("isMenuComponent") {
990            @Override
991            public boolean map() {
992                return ((JMenu) getSource()).isMenuComponent(component);
993            }
994        }));
995    }
996
997    /**
998     * Maps {@code JMenu.isPopupMenuVisible()} through queue
999     */
1000    public boolean isPopupMenuVisible() {
1001        return (runMapping(new MapBooleanAction("isPopupMenuVisible") {
1002            @Override
1003            public boolean map() {
1004                return ((JMenu) getSource()).isPopupMenuVisible();
1005            }
1006        }));
1007    }
1008
1009    /**
1010     * Maps {@code JMenu.isTearOff()} through queue
1011     */
1012    public boolean isTearOff() {
1013        return (runMapping(new MapBooleanAction("isTearOff") {
1014            @Override
1015            public boolean map() {
1016                return ((JMenu) getSource()).isTearOff();
1017            }
1018        }));
1019    }
1020
1021    /**
1022     * Maps {@code JMenu.isTopLevelMenu()} through queue
1023     */
1024    public boolean isTopLevelMenu() {
1025        return (runMapping(new MapBooleanAction("isTopLevelMenu") {
1026            @Override
1027            public boolean map() {
1028                return ((JMenu) getSource()).isTopLevelMenu();
1029            }
1030        }));
1031    }
1032
1033    /**
1034     * Maps {@code JMenu.remove(JMenuItem)} through queue
1035     */
1036    public void remove(final JMenuItem jMenuItem) {
1037        runMapping(new MapVoidAction("remove") {
1038            @Override
1039            public void map() {
1040                ((JMenu) getSource()).remove(jMenuItem);
1041            }
1042        });
1043    }
1044
1045    /**
1046     * Maps {@code JMenu.removeMenuListener(MenuListener)} through queue
1047     */
1048    public void removeMenuListener(final MenuListener menuListener) {
1049        runMapping(new MapVoidAction("removeMenuListener") {
1050            @Override
1051            public void map() {
1052                ((JMenu) getSource()).removeMenuListener(menuListener);
1053            }
1054        });
1055    }
1056
1057    /**
1058     * Maps {@code JMenu.setDelay(int)} through queue
1059     */
1060    public void setDelay(final int i) {
1061        runMapping(new MapVoidAction("setDelay") {
1062            @Override
1063            public void map() {
1064                ((JMenu) getSource()).setDelay(i);
1065            }
1066        });
1067    }
1068
1069    /**
1070     * Maps {@code JMenu.setMenuLocation(int, int)} through queue
1071     */
1072    public void setMenuLocation(final int i, final int i1) {
1073        runMapping(new MapVoidAction("setMenuLocation") {
1074            @Override
1075            public void map() {
1076                ((JMenu) getSource()).setMenuLocation(i, i1);
1077            }
1078        });
1079    }
1080
1081    /**
1082     * Maps {@code JMenu.setPopupMenuVisible(boolean)} through queue
1083     */
1084    public void setPopupMenuVisible(final boolean b) {
1085        runMapping(new MapVoidAction("setPopupMenuVisible") {
1086            @Override
1087            public void map() {
1088                ((JMenu) getSource()).setPopupMenuVisible(b);
1089            }
1090        });
1091    }
1092
1093    //End of mapping                                      //
1094    ////////////////////////////////////////////////////////
1095    static String createDescription(ComponentChooser[] choosers) {
1096        StringBuilder description = new StringBuilder("(");
1097        for (int i = 0; i < choosers.length; i++) {
1098            if (i > 0) {
1099                description.append(", ");
1100            }
1101            description.append(choosers[i].getDescription());
1102        }
1103        description.append(")");
1104        return "Menu pushing: " + description.toString();
1105    }
1106
1107    static DescriptablePathChooser converChoosers(final ComponentChooser[] choosers) {
1108        return (new DescriptablePathChooser() {
1109            @Override
1110            public boolean checkPathComponent(int depth, Object component) {
1111                return choosers[depth].checkComponent((Component) component);
1112            }
1113
1114            @Override
1115            public int getDepth() {
1116                return choosers.length;
1117            }
1118
1119            @Override
1120            public String getDescription() {
1121                return createDescription(choosers);
1122            }
1123
1124            @Override
1125            public String toString() {
1126                return "JMenuOperator.converChoosers.DescriptablePathChooser{description = " + getDescription() + '}';
1127            }
1128        });
1129    }
1130
1131    /**
1132     * Allows to find component by text.
1133     */
1134    public static class JMenuByLabelFinder implements ComponentChooser {
1135
1136        String label;
1137        StringComparator comparator;
1138
1139        /**
1140         * Constructs JMenuByLabelFinder.
1141         *
1142         * @param lb a text pattern
1143         * @param comparator specifies string comparision algorithm.
1144         */
1145        public JMenuByLabelFinder(String lb, StringComparator comparator) {
1146            label = lb;
1147            this.comparator = comparator;
1148        }
1149
1150        /**
1151         * Constructs JMenuByLabelFinder.
1152         *
1153         * @param lb a text pattern
1154         */
1155        public JMenuByLabelFinder(String lb) {
1156            this(lb, Operator.getDefaultStringComparator());
1157        }
1158
1159        @Override
1160        public boolean checkComponent(Component comp) {
1161            if (comp instanceof JMenu) {
1162                if (((JMenu) comp).getText() != null) {
1163                    return (comparator.equals(((JMenu) comp).getText(),
1164                            label));
1165                }
1166            }
1167            return false;
1168        }
1169
1170        @Override
1171        public String getDescription() {
1172            return "JMenu with text \"" + label + "\"";
1173        }
1174
1175        @Override
1176        public String toString() {
1177            return "JMenuByLabelFinder{" + "label=" + label + ", comparator=" + comparator + '}';
1178        }
1179    }
1180
1181    /**
1182     * Checks component type.
1183     */
1184    public static class JMenuFinder extends Finder {
1185
1186        /**
1187         * Constructs JMenuFinder.
1188         *
1189         * @param sf other searching criteria.
1190         */
1191        public JMenuFinder(ComponentChooser sf) {
1192            super(JMenu.class, sf);
1193        }
1194
1195        /**
1196         * Constructs JMenuFinder.
1197         */
1198        public JMenuFinder() {
1199            super(JMenu.class);
1200        }
1201    }
1202}
1203