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.awt.Dimension;
28import java.awt.List;
29import java.awt.event.ActionListener;
30import java.awt.event.ItemListener;
31import java.util.Hashtable;
32
33import org.netbeans.jemmy.ComponentChooser;
34import org.netbeans.jemmy.Outputable;
35import org.netbeans.jemmy.TestOut;
36import org.netbeans.jemmy.TimeoutExpiredException;
37import org.netbeans.jemmy.drivers.DriverManager;
38import org.netbeans.jemmy.drivers.MultiSelListDriver;
39
40/**
41 * <BR><BR>Timeouts used: <BR>
42 * ComponentOperator.WaitComponentTimeout - time to wait component displayed
43 * <BR>
44 * ComponentOperator.WaitComponentEnabledTimeout - time to wait component
45 * enabled <BR>.
46 *
47 * @see org.netbeans.jemmy.Timeouts
48 *
49 * @author Alexandre Iline (alexandre.iline@oracle.com)
50 *
51 */
52public class ListOperator extends ComponentOperator
53        implements Outputable {
54
55    /**
56     * Identifier for a "item" properties.
57     *
58     * @see #getDump
59     */
60    public static final String ITEM_PREFIX_DPROP = "Item";
61
62    /**
63     * Identifier for a "selected item" property.
64     *
65     * @see #getDump
66     */
67    public static final String SELECTED_ITEM_PREFIX_DPROP = "SelectedItem";
68
69    private TestOut output;
70    private MultiSelListDriver driver;
71
72    /**
73     * Constructor.
74     *
75     * @param b a component
76     */
77    public ListOperator(List b) {
78        super(b);
79        driver = DriverManager.getMultiSelListDriver(getClass());
80    }
81
82    /**
83     * Constructs a ListOperator object.
84     *
85     * @param cont a container
86     * @param chooser a component chooser specifying searching criteria.
87     * @param index an index between appropriate ones.
88     */
89    public ListOperator(ContainerOperator<?> cont, ComponentChooser chooser, int index) {
90        this((List) cont.
91                waitSubComponent(new ListFinder(chooser),
92                        index));
93        copyEnvironment(cont);
94    }
95
96    /**
97     * Constructs a ListOperator object.
98     *
99     * @param cont a container
100     * @param chooser a component chooser specifying searching criteria.
101     */
102    public ListOperator(ContainerOperator<?> cont, ComponentChooser chooser) {
103        this(cont, chooser, 0);
104    }
105
106    /**
107     * Constructor. Waits item text first. Uses cont's timeout and output for
108     * waiting and to init operator.
109     *
110     * @param cont a container
111     * @param text Text of item which is currently selected.
112     * @param itemIndex Item index.
113     * @param index Ordinal component index.
114     * @throws TimeoutExpiredException
115     */
116    public ListOperator(ContainerOperator<?> cont, String text, int itemIndex, int index) {
117        this((List) waitComponent(cont,
118                new ListByItemFinder(text, itemIndex,
119                        cont.getComparator()),
120                index));
121        copyEnvironment(cont);
122    }
123
124    /**
125     * Constructor. Waits component by selected item text first. Uses cont's
126     * timeout and output for waiting and to init operator.
127     *
128     * @param cont a container
129     * @param text Text of item which is currently selected.
130     * @param index Ordinal component index.
131     * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean)
132     * @throws TimeoutExpiredException
133     */
134    public ListOperator(ContainerOperator<?> cont, String text, int index) {
135        this(cont, text, -1, index);
136    }
137
138    /**
139     * Constructor. Waits component in container first. Uses cont's timeout and
140     * output for waiting and to init operator.
141     *
142     * @param cont a container
143     * @param text Text of item which is currently selected.
144     * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean)
145     * @throws TimeoutExpiredException
146     */
147    public ListOperator(ContainerOperator<?> cont, String text) {
148        this(cont, text, 0);
149    }
150
151    /**
152     * Constructor. Waits component in container first. Uses cont's timeout and
153     * output for waiting and to init operator.
154     *
155     * @param cont a container
156     * @param index Ordinal component index.
157     * @throws TimeoutExpiredException
158     */
159    public ListOperator(ContainerOperator<?> cont, int index) {
160        this((List) waitComponent(cont,
161                new ListFinder(),
162                index));
163        copyEnvironment(cont);
164    }
165
166    /**
167     * Constructor. Waits component in container first. Uses cont's timeout and
168     * output for waiting and to init operator.
169     *
170     * @param cont a container
171     * @throws TimeoutExpiredException
172     */
173    public ListOperator(ContainerOperator<?> cont) {
174        this(cont, 0);
175    }
176
177    /**
178     * Searches List in container.
179     *
180     * @param cont Container to search component in.
181     * @param chooser org.netbeans.jemmy.ComponentChooser implementation.
182     * @param index Ordinal component index.
183     * @return List instance or null if component was not found.
184     */
185    public static List findList(Container cont, ComponentChooser chooser, int index) {
186        return (List) findComponent(cont, new ListFinder(chooser), index);
187    }
188
189    /**
190     * Searches 0'th List in container.
191     *
192     * @param cont Container to search component in.
193     * @param chooser org.netbeans.jemmy.ComponentChooser implementation.
194     * @return List instance or null if component was not found.
195     */
196    public static List findList(Container cont, ComponentChooser chooser) {
197        return findList(cont, chooser, 0);
198    }
199
200    @Override
201    public void setOutput(TestOut output) {
202        super.setOutput(output.createErrorOutput());
203        this.output = output;
204    }
205
206    @Override
207    public TestOut getOutput() {
208        return output;
209    }
210
211    @Override
212    public void copyEnvironment(Operator anotherOperator) {
213        super.copyEnvironment(anotherOperator);
214        driver
215                = (MultiSelListDriver) DriverManager.
216                getDriver(DriverManager.MULTISELLIST_DRIVER_ID,
217                        getClass(),
218                        anotherOperator.getProperties());
219    }
220
221    private int findItemIndex(String item, StringComparator comparator, int index) {
222        int count = 0;
223        for (int i = 0; i < getItemCount(); i++) {
224            if (comparator.equals(getItem(i), item)) {
225                if (count == index) {
226                    return i;
227                } else {
228                    count++;
229                }
230            }
231        }
232        return -1;
233    }
234
235    /**
236     * Searches an item index.
237     *
238     * @param item item text.
239     * @param index an ordinal index between appropriate ones.
240     * @return an index.
241     */
242    public int findItemIndex(String item, int index) {
243        return findItemIndex(item, getComparator(), index);
244    }
245
246    /**
247     * Searches an item index.
248     *
249     * @param item item text.
250     * @return an index.
251     */
252    public int findItemIndex(String item) {
253        return findItemIndex(item, 0);
254    }
255
256    private void selectItem(String item, StringComparator comparator, int index) {
257        selectItem(findItemIndex(item, comparator, index));
258    }
259
260    /**
261     * Selects an item.
262     *
263     * @param item item text.
264     * @param index an ordinal index between appropriate ones.
265     */
266    public void selectItem(String item, int index) {
267        selectItem(item, getComparator(), index);
268    }
269
270    /**
271     * Selects an item.
272     *
273     * @param item item text.
274     */
275    public void selectItem(String item) {
276        selectItem(item, 0);
277    }
278
279    /**
280     * Selects an item.
281     *
282     * @param index an item index.
283     */
284    public void selectItem(int index) {
285        output.printLine("Select " + Integer.toString(index) + "`th item in list\n    : "
286                + toStringSource());
287        output.printGolden("Select " + Integer.toString(index) + "`th item in list");
288        driver.selectItem(this, index);
289        if (getVerification()) {
290            waitItemSelection(index, true);
291        }
292    }
293
294    /**
295     * Selects some items.
296     *
297     * @param from start selection index.
298     * @param to end selection index.
299     */
300    public void selectItems(int from, int to) {
301        output.printLine("Select items from " + Integer.toString(from)
302                + "`th to " + Integer.toString(from) + "'th in list\n    : "
303                + toStringSource());
304        output.printGolden("Select items from " + Integer.toString(from)
305                + "`th to " + Integer.toString(from) + "'th");
306        driver.selectItems(this, new int[]{from, to});
307        if (getVerification()) {
308            waitItemsSelection(from, to, true);
309        }
310    }
311
312    /**
313     * Waits for items to be selected.
314     *
315     * @param from Start selection inex
316     * @param to End selection inex
317     * @param selected Selected (true) or unselected (false).
318     */
319    public void waitItemsSelection(final int from, final int to, final boolean selected) {
320        getOutput().printLine("Wait items to be "
321                + (selected ? "" : "un") + "selected in component \n    : "
322                + toStringSource());
323        getOutput().printGolden("Wait items to be "
324                + (selected ? "" : "un") + "selected");
325        waitState(new ComponentChooser() {
326            @Override
327            public boolean checkComponent(Component comp) {
328                int[] indices = getSelectedIndexes();
329                for (int indice : indices) {
330                    if (indice < from
331                            || indice > to) {
332                        return false;
333                    }
334                }
335                return true;
336            }
337
338            @Override
339            public String getDescription() {
340                return ("Items has been "
341                        + (selected ? "" : "un") + "selected");
342            }
343
344            @Override
345            public String toString() {
346                return "ListOperator.waitItemsSelection.ComponentChooser{description = " + getDescription() + '}';
347            }
348        });
349    }
350
351    /**
352     * Waits for item to be selected.
353     *
354     * @param itemIndex an item index to be selected.
355     * @param selected Selected (true) or unselected (false).
356     */
357    public void waitItemSelection(final int itemIndex, final boolean selected) {
358        waitItemsSelection(itemIndex, itemIndex, selected);
359    }
360
361    @Override
362    public Hashtable<String, Object> getDump() {
363        Hashtable<String, Object> result = super.getDump();
364        addToDump(result, ITEM_PREFIX_DPROP, ((List) getSource()).getItems());
365        addToDump(result, SELECTED_ITEM_PREFIX_DPROP, ((List) getSource()).getSelectedItems());
366        return result;
367    }
368
369    ////////////////////////////////////////////////////////
370    //Mapping                                             //
371    /**
372     * Maps {@code List.addActionListener(ActionListener)} through queue
373     */
374    public void addActionListener(final ActionListener actionListener) {
375        runMapping(new MapVoidAction("addActionListener") {
376            @Override
377            public void map() {
378                ((List) getSource()).addActionListener(actionListener);
379            }
380        });
381    }
382
383    /**
384     * Maps {@code List.addItemListener(ItemListener)} through queue
385     */
386    public void addItemListener(final ItemListener itemListener) {
387        runMapping(new MapVoidAction("addItemListener") {
388            @Override
389            public void map() {
390                ((List) getSource()).addItemListener(itemListener);
391            }
392        });
393    }
394
395    /**
396     * Maps {@code List.deselect(int)} through queue
397     */
398    public void deselect(final int i) {
399        runMapping(new MapVoidAction("deselect") {
400            @Override
401            public void map() {
402                ((List) getSource()).deselect(i);
403            }
404        });
405    }
406
407    /**
408     * Maps {@code List.getItem(int)} through queue
409     */
410    public String getItem(final int i) {
411        return (runMapping(new MapAction<String>("getItem") {
412            @Override
413            public String map() {
414                return ((List) getSource()).getItem(i);
415            }
416        }));
417    }
418
419    /**
420     * Maps {@code List.getItemCount()} through queue
421     */
422    public int getItemCount() {
423        return (runMapping(new MapIntegerAction("getItemCount") {
424            @Override
425            public int map() {
426                return ((List) getSource()).getItemCount();
427            }
428        }));
429    }
430
431    /**
432     * Maps {@code List.getItems()} through queue
433     */
434    public String[] getItems() {
435        return ((String[]) runMapping(new MapAction<Object>("getItems") {
436            @Override
437            public Object map() {
438                return ((List) getSource()).getItems();
439            }
440        }));
441    }
442
443    /**
444     * Maps {@code List.getMinimumSize(int)} through queue
445     */
446    public Dimension getMinimumSize(final int i) {
447        return (runMapping(new MapAction<Dimension>("getMinimumSize") {
448            @Override
449            public Dimension map() {
450                return ((List) getSource()).getMinimumSize(i);
451            }
452        }));
453    }
454
455    /**
456     * Maps {@code List.getPreferredSize(int)} through queue
457     */
458    public Dimension getPreferredSize(final int i) {
459        return (runMapping(new MapAction<Dimension>("getPreferredSize") {
460            @Override
461            public Dimension map() {
462                return ((List) getSource()).getPreferredSize(i);
463            }
464        }));
465    }
466
467    /**
468     * Maps {@code List.getRows()} through queue
469     */
470    public int getRows() {
471        return (runMapping(new MapIntegerAction("getRows") {
472            @Override
473            public int map() {
474                return ((List) getSource()).getRows();
475            }
476        }));
477    }
478
479    /**
480     * Maps {@code List.getSelectedIndex()} through queue
481     */
482    public int getSelectedIndex() {
483        return (runMapping(new MapIntegerAction("getSelectedIndex") {
484            @Override
485            public int map() {
486                return ((List) getSource()).getSelectedIndex();
487            }
488        }));
489    }
490
491    /**
492     * Maps {@code List.getSelectedIndexes()} through queue
493     */
494    public int[] getSelectedIndexes() {
495        return ((int[]) runMapping(new MapAction<Object>("getSelectedIndexes") {
496            @Override
497            public Object map() {
498                return ((List) getSource()).getSelectedIndexes();
499            }
500        }));
501    }
502
503    /**
504     * Maps {@code List.getSelectedItem()} through queue
505     */
506    public String getSelectedItem() {
507        return (runMapping(new MapAction<String>("getSelectedItem") {
508            @Override
509            public String map() {
510                return ((List) getSource()).getSelectedItem();
511            }
512        }));
513    }
514
515    /**
516     * Maps {@code List.getSelectedItems()} through queue
517     */
518    public String[] getSelectedItems() {
519        return ((String[]) runMapping(new MapAction<Object>("getSelectedItems") {
520            @Override
521            public Object map() {
522                return ((List) getSource()).getSelectedItems();
523            }
524        }));
525    }
526
527    /**
528     * Maps {@code List.getSelectedObjects()} through queue
529     */
530    public Object[] getSelectedObjects() {
531        return ((Object[]) runMapping(new MapAction<Object>("getSelectedObjects") {
532            @Override
533            public Object map() {
534                return ((List) getSource()).getSelectedObjects();
535            }
536        }));
537    }
538
539    /**
540     * Maps {@code List.getVisibleIndex()} through queue
541     */
542    public int getVisibleIndex() {
543        return (runMapping(new MapIntegerAction("getVisibleIndex") {
544            @Override
545            public int map() {
546                return ((List) getSource()).getVisibleIndex();
547            }
548        }));
549    }
550
551    /**
552     * Maps {@code List.isIndexSelected(int)} through queue
553     */
554    public boolean isIndexSelected(final int i) {
555        return (runMapping(new MapBooleanAction("isIndexSelected") {
556            @Override
557            public boolean map() {
558                return ((List) getSource()).isIndexSelected(i);
559            }
560        }));
561    }
562
563    /**
564     * Maps {@code List.isMultipleMode()} through queue
565     */
566    public boolean isMultipleMode() {
567        return (runMapping(new MapBooleanAction("isMultipleMode") {
568            @Override
569            public boolean map() {
570                return ((List) getSource()).isMultipleMode();
571            }
572        }));
573    }
574
575    /**
576     * Maps {@code List.makeVisible(int)} through queue
577     */
578    public void makeVisible(final int i) {
579        runMapping(new MapVoidAction("makeVisible") {
580            @Override
581            public void map() {
582                ((List) getSource()).makeVisible(i);
583            }
584        });
585    }
586
587    /**
588     * Maps {@code List.remove(int)} through queue
589     */
590    public void remove(final int i) {
591        runMapping(new MapVoidAction("remove") {
592            @Override
593            public void map() {
594                ((List) getSource()).remove(i);
595            }
596        });
597    }
598
599    /**
600     * Maps {@code List.remove(String)} through queue
601     */
602    public void remove(final String string) {
603        runMapping(new MapVoidAction("remove") {
604            @Override
605            public void map() {
606                ((List) getSource()).remove(string);
607            }
608        });
609    }
610
611    /**
612     * Maps {@code List.removeActionListener(ActionListener)} through queue
613     */
614    public void removeActionListener(final ActionListener actionListener) {
615        runMapping(new MapVoidAction("removeActionListener") {
616            @Override
617            public void map() {
618                ((List) getSource()).removeActionListener(actionListener);
619            }
620        });
621    }
622
623    /**
624     * Maps {@code List.removeAll()} through queue
625     */
626    public void removeAll() {
627        runMapping(new MapVoidAction("removeAll") {
628            @Override
629            public void map() {
630                ((List) getSource()).removeAll();
631            }
632        });
633    }
634
635    /**
636     * Maps {@code List.removeItemListener(ItemListener)} through queue
637     */
638    public void removeItemListener(final ItemListener itemListener) {
639        runMapping(new MapVoidAction("removeItemListener") {
640            @Override
641            public void map() {
642                ((List) getSource()).removeItemListener(itemListener);
643            }
644        });
645    }
646
647    /**
648     * Maps {@code List.replaceItem(String, int)} through queue
649     */
650    public void replaceItem(final String string, final int i) {
651        runMapping(new MapVoidAction("replaceItem") {
652            @Override
653            public void map() {
654                ((List) getSource()).replaceItem(string, i);
655            }
656        });
657    }
658
659    /**
660     * Maps {@code List.select(int)} through queue
661     */
662    public void select(final int i) {
663        runMapping(new MapVoidAction("select") {
664            @Override
665            public void map() {
666                ((List) getSource()).select(i);
667            }
668        });
669    }
670
671    /**
672     * Maps {@code List.setMultipleMode(boolean)} through queue
673     */
674    public void setMultipleMode(final boolean b) {
675        runMapping(new MapVoidAction("setMultipleMode") {
676            @Override
677            public void map() {
678                ((List) getSource()).setMultipleMode(b);
679            }
680        });
681    }
682
683    //End of mapping                                      //
684    ////////////////////////////////////////////////////////
685    /**
686     * Allows to find component by item text.
687     */
688    public static class ListByItemFinder implements ComponentChooser {
689
690        String label;
691        int itemIndex;
692        StringComparator comparator;
693
694        /**
695         * Constructs ListByItemFinder.
696         *
697         * @param lb a text pattern
698         * @param ii item index to check. If equal to -1, selected item is
699         * checked.
700         * @param comparator specifies string comparision algorithm.
701         */
702        public ListByItemFinder(String lb, int ii, StringComparator comparator) {
703            label = lb;
704            itemIndex = ii;
705            this.comparator = comparator;
706        }
707
708        /**
709         * Constructs ListByItemFinder.
710         *
711         * @param lb a text pattern
712         * @param ii item index to check. If equal to -1, selected item is
713         * checked.
714         */
715        public ListByItemFinder(String lb, int ii) {
716            this(lb, ii, Operator.getDefaultStringComparator());
717        }
718
719        @Override
720        public boolean checkComponent(Component comp) {
721            if (comp instanceof List) {
722                if (label == null) {
723                    return true;
724                }
725                if (((List) comp).getItemCount() > itemIndex) {
726                    int ii = itemIndex;
727                    if (ii == -1) {
728                        ii = ((List) comp).getSelectedIndex();
729                        if (ii == -1) {
730                            return false;
731                        }
732                    }
733                    return (comparator.equals(((List) comp).getItem(ii),
734                            label));
735                }
736            }
737            return false;
738        }
739
740        @Override
741        public String getDescription() {
742            return ("List with text \"" + label + "\" in "
743                    + itemIndex + "'th item");
744        }
745
746        @Override
747        public String toString() {
748            return "ListByItemFinder{" + "label=" + label + ", itemIndex=" + itemIndex + ", comparator=" + comparator + '}';
749        }
750    }
751
752    /**
753     * Checks component type.
754     */
755    public static class ListFinder extends Finder {
756
757        /**
758         * Constructs ListFinder.
759         *
760         * @param sf other searching criteria.
761         */
762        public ListFinder(ComponentChooser sf) {
763            super(List.class, sf);
764        }
765
766        /**
767         * Constructs ListFinder.
768         */
769        public ListFinder() {
770            super(List.class);
771        }
772    }
773}
774