From 0ab1a9c7a824276411cdb969a23aca2b9c3d48e9 Mon Sep 17 00:00:00 2001 From: Alexander Kouznetsov Date: Wed, 30 Mar 2016 19:05:58 -0700 Subject: [PATCH] 8153141: Develop initial set of tests for SwingSet Reviewed-by: prr --- jdk/.hgignore | 2 + jdk/make/netbeans/client_sanity/README | 15 + jdk/make/netbeans/client_sanity/build.xml | 94 + jdk/make/netbeans/client_sanity/manifest.mf | 3 + .../client_sanity/nbproject/build-impl.xml | 1429 +++++++ .../nbproject/genfiles.properties | 8 + .../nbproject/project.properties | 79 + .../client_sanity/nbproject/project.xml | 17 + jdk/test/TEST.groups | 7 +- jdk/test/sanity/client/README | 45 + .../src/ButtonDemoScreenshotTest.java | 94 + .../client/SwingSet/src/ButtonDemoTest.java | 177 + .../client/SwingSet/src/ComboBoxDemoTest.java | 82 + .../client/SwingSet/src/ListDemoTest.java | 120 + .../SwingSet/src/OptionPaneDemoTest.java | 339 ++ .../SwingSet/src/ProgressBarDemoTest.java | 160 + .../SwingSet/src/ScrollPaneDemoTest.java | 109 + .../client/SwingSet/src/SpinnerDemoTest.java | 94 + .../SwingSet/src/SplitPaneDemoTest.java | 174 + .../SwingSet/src/TabbedPaneDemoTest.java | 78 + .../SwingSet/src/TextFieldDemoTest.java | 135 + .../SwingSet/src/ToggleButtonDemoTest.java | 194 + .../client/SwingSet/src/TreeDemoTest.java | 108 + .../client/SwingSet/src/WindowDemoTest.java | 76 + jdk/test/sanity/client/TEST.ROOT.template | 24 + jdk/test/sanity/client/TEST.properties | 14 + .../Jemmy2Ext/src/org/jemmy2ext/JemmyExt.java | 614 +++ jdk/test/sanity/client/lib/SwingSet3/README | 4 + .../src/com/sun/swingset3/DemoProperties.java | 45 + .../sun/swingset3/demos/DemoUtilities.java | 115 + .../com/sun/swingset3/demos/JGridPanel.java | 231 ++ .../com/sun/swingset3/demos/JHyperlink.java | 237 ++ .../sun/swingset3/demos/ResourceManager.java | 77 + .../swingset3/demos/button/ButtonDemo.java | 224 ++ .../demos/button/resources/ButtonDemo.html | 36 + .../button/resources/images/ButtonDemo.gif | Bin 0 -> 1170 bytes .../demos/button/resources/images/blogs.png | Bin 0 -> 9198 bytes .../resources/images/document-print.png | Bin 0 -> 1503 bytes .../button/resources/images/earth_day.gif | Bin 0 -> 1648 bytes .../button/resources/images/earth_night.gif | Bin 0 -> 1648 bytes .../button/resources/images/edit-find.png | Bin 0 -> 1227 bytes .../button/resources/images/redbutton.png | Bin 0 -> 7087 bytes .../resources/images/redbutton_dark.png | Bin 0 -> 7038 bytes .../resources/images/redbutton_glow.png | Bin 0 -> 7562 bytes .../demos/combobox/ComboBoxDemo.java | 445 +++ .../resources/ComboBoxDemo.properties | 42 + .../resources/images/ComboBoxDemo.gif | Bin 0 -> 672 bytes .../combobox/resources/images/brenteyes.jpg | Bin 0 -> 2191 bytes .../combobox/resources/images/brenthair.jpg | Bin 0 -> 3584 bytes .../combobox/resources/images/brentmouth.jpg | Bin 0 -> 1701 bytes .../combobox/resources/images/georgeseyes.jpg | Bin 0 -> 2426 bytes .../combobox/resources/images/georgeshair.jpg | Bin 0 -> 2024 bytes .../resources/images/georgesmouth.jpg | Bin 0 -> 2043 bytes .../combobox/resources/images/hanseyes.jpg | Bin 0 -> 2500 bytes .../combobox/resources/images/hanshair.jpg | Bin 0 -> 2266 bytes .../combobox/resources/images/hansmouth.jpg | Bin 0 -> 2196 bytes .../combobox/resources/images/howardeyes.jpg | Bin 0 -> 2226 bytes .../combobox/resources/images/howardhair.jpg | Bin 0 -> 1917 bytes .../combobox/resources/images/howardmouth.jpg | Bin 0 -> 3703 bytes .../combobox/resources/images/jameseyes.jpg | Bin 0 -> 2285 bytes .../combobox/resources/images/jameshair.jpg | Bin 0 -> 1482 bytes .../combobox/resources/images/jamesmouth.jpg | Bin 0 -> 3251 bytes .../combobox/resources/images/jeffeyes.jpg | Bin 0 -> 2594 bytes .../combobox/resources/images/jeffhair.jpg | Bin 0 -> 2196 bytes .../combobox/resources/images/jeffmouth.jpg | Bin 0 -> 2385 bytes .../combobox/resources/images/joneyes.jpg | Bin 0 -> 2726 bytes .../combobox/resources/images/jonhair.jpg | Bin 0 -> 2293 bytes .../combobox/resources/images/jonmouth.jpg | Bin 0 -> 2174 bytes .../combobox/resources/images/laraeyes.jpg | Bin 0 -> 2275 bytes .../combobox/resources/images/larahair.jpg | Bin 0 -> 2324 bytes .../combobox/resources/images/laramouth.jpg | Bin 0 -> 2187 bytes .../combobox/resources/images/larryeyes.jpg | Bin 0 -> 2412 bytes .../combobox/resources/images/larryhair.jpg | Bin 0 -> 2054 bytes .../combobox/resources/images/larrymouth.jpg | Bin 0 -> 2927 bytes .../combobox/resources/images/lisaeyes.jpg | Bin 0 -> 2386 bytes .../combobox/resources/images/lisahair.jpg | Bin 0 -> 2237 bytes .../combobox/resources/images/lisamouth.jpg | Bin 0 -> 2245 bytes .../combobox/resources/images/michaeleyes.jpg | Bin 0 -> 2654 bytes .../combobox/resources/images/michaelhair.jpg | Bin 0 -> 2130 bytes .../resources/images/michaelmouth.jpg | Bin 0 -> 2399 bytes .../combobox/resources/images/philipeyes.jpg | Bin 0 -> 2354 bytes .../combobox/resources/images/philiphair.jpg | Bin 0 -> 2897 bytes .../combobox/resources/images/philipmouth.jpg | Bin 0 -> 1718 bytes .../combobox/resources/images/scotteyes.jpg | Bin 0 -> 2825 bytes .../combobox/resources/images/scotthair.jpg | Bin 0 -> 2755 bytes .../combobox/resources/images/scottmouth.jpg | Bin 0 -> 3106 bytes .../sun/swingset3/demos/list/ListDemo.java | 404 ++ .../sun/swingset3/demos/list/Permuter.java | 100 + .../demos/list/resources/ListDemo.properties | 18 + .../demos/list/resources/images/ListDemo.gif | Bin 0 -> 265 bytes .../demos/list/resources/images/blue.gif | Bin 0 -> 283 bytes .../demos/list/resources/images/cyan.gif | Bin 0 -> 118 bytes .../demos/list/resources/images/gray.gif | Bin 0 -> 86 bytes .../demos/list/resources/images/green.gif | Bin 0 -> 287 bytes .../demos/list/resources/images/magenta.gif | Bin 0 -> 96 bytes .../demos/list/resources/images/red.gif | Bin 0 -> 118 bytes .../demos/list/resources/images/yellow.gif | Bin 0 -> 118 bytes .../demos/optionpane/OptionPaneDemo.java | 270 ++ .../resources/OptionPaneDemo.properties | 42 + .../resources/images/OptionPaneDemo.gif | Bin 0 -> 881 bytes .../optionpane/resources/images/bottle.gif | Bin 0 -> 11638 bytes .../demos/progressbar/ProgressBarDemo.java | 199 + .../resources/ProgressBarDemo.properties | 35 + .../resources/images/ProgressBarDemo.gif | Bin 0 -> 165 bytes .../demos/scrollpane/ScrollPaneDemo.java | 133 + .../resources/ScrollPaneDemo.properties | 11 + .../scrollpane/resources/images/COPYRIGHT | 2 + .../resources/images/ScrollPaneDemo.gif | Bin 0 -> 1458 bytes .../scrollpane/resources/images/colheader.jpg | Bin 0 -> 12729 bytes .../scrollpane/resources/images/crayons.jpg | Bin 0 -> 72754 bytes .../scrollpane/resources/images/lowerleft.jpg | Bin 0 -> 5049 bytes .../scrollpane/resources/images/rowheader.jpg | Bin 0 -> 11835 bytes .../scrollpane/resources/images/upperleft.jpg | Bin 0 -> 5220 bytes .../resources/images/upperright.jpg | Bin 0 -> 5014 bytes .../swingset3/demos/spinner/JMandelbrot.java | 328 ++ .../demos/spinner/JPaletteShower.java | 62 + .../demos/spinner/JSpinnerPanel.java | 66 + .../demos/spinner/MandelbrotControl.java | 165 + .../sun/swingset3/demos/spinner/Palette.java | 95 + .../demos/spinner/PaletteChooser.java | 166 + .../swingset3/demos/spinner/SpinnerDemo.java | 101 + .../spinner/resources/SpinnerDemo.properties | 15 + .../spinner/resources/images/SpinnerDemo.gif | Bin 0 -> 1055 bytes .../demos/splitpane/SplitPaneDemo.java | 292 ++ .../resources/SplitPaneDemo.properties | 14 + .../resources/images/SplitPaneDemo.gif | Bin 0 -> 210 bytes .../demos/splitpane/resources/images/day.jpg | Bin 0 -> 267699 bytes .../splitpane/resources/images/night.jpg | Bin 0 -> 381348 bytes .../demos/tabbedpane/TabbedPaneDemo.java | 257 ++ .../resources/TabbedPaneDemo.properties | 22 + .../resources/images/TabbedPaneDemo.gif | Bin 0 -> 226 bytes .../tabbedpane/resources/images/blake.gif | Bin 0 -> 3110 bytes .../tabbedpane/resources/images/brooke.gif | Bin 0 -> 4463 bytes .../tabbedpane/resources/images/camille.jpg | Bin 0 -> 51435 bytes .../tabbedpane/resources/images/david.gif | Bin 0 -> 4901 bytes .../tabbedpane/resources/images/ewan.gif | Bin 0 -> 7676 bytes .../tabbedpane/resources/images/ewan.jpg | Bin 0 -> 10687 bytes .../tabbedpane/resources/images/matthew.gif | Bin 0 -> 4326 bytes .../tabbedpane/resources/images/miranda.jpg | Bin 0 -> 114192 bytes .../tabbedpane/resources/images/stephen.gif | Bin 0 -> 4294 bytes .../demos/textfield/JHistoryTextField.java | 265 ++ .../demos/textfield/TextFieldDemo.java | 175 + .../resources/TextFieldDemo.properties | 7 + .../resources/images/TextFieldDemo.gif | Bin 0 -> 569 bytes .../demos/togglebutton/DirectionPanel.java | 140 + .../togglebutton/LayoutControlPanel.java | 281 ++ .../demos/togglebutton/ToggleButtonDemo.java | 563 +++ .../resources/ToggleButtonDemo.properties | 69 + .../resources/images/ToggleButtonDemo.gif | Bin 0 -> 1157 bytes .../togglebutton/resources/images/b1.gif | Bin 0 -> 2553 bytes .../togglebutton/resources/images/b1d.gif | Bin 0 -> 2640 bytes .../togglebutton/resources/images/b1p.gif | Bin 0 -> 2704 bytes .../togglebutton/resources/images/b1r.gif | Bin 0 -> 2625 bytes .../togglebutton/resources/images/b2.gif | Bin 0 -> 2523 bytes .../togglebutton/resources/images/b2d.gif | Bin 0 -> 2758 bytes .../togglebutton/resources/images/b2p.gif | Bin 0 -> 2777 bytes .../togglebutton/resources/images/b2r.gif | Bin 0 -> 2682 bytes .../togglebutton/resources/images/b3.gif | Bin 0 -> 2721 bytes .../togglebutton/resources/images/b3d.gif | Bin 0 -> 2835 bytes .../togglebutton/resources/images/b3p.gif | Bin 0 -> 2807 bytes .../togglebutton/resources/images/b3r.gif | Bin 0 -> 2822 bytes .../togglebutton/resources/images/bl.gif | Bin 0 -> 624 bytes .../togglebutton/resources/images/bldn.gif | Bin 0 -> 1125 bytes .../togglebutton/resources/images/bm.gif | Bin 0 -> 582 bytes .../togglebutton/resources/images/bmdn.gif | Bin 0 -> 1115 bytes .../togglebutton/resources/images/br.gif | Bin 0 -> 621 bytes .../togglebutton/resources/images/brdn.gif | Bin 0 -> 1159 bytes .../demos/togglebutton/resources/images/c.gif | Bin 0 -> 204 bytes .../togglebutton/resources/images/cb.gif | Bin 0 -> 1007 bytes .../togglebutton/resources/images/cbr.gif | Bin 0 -> 1270 bytes .../togglebutton/resources/images/cbrs.gif | Bin 0 -> 2353 bytes .../togglebutton/resources/images/cbs.gif | Bin 0 -> 994 bytes .../togglebutton/resources/images/cdn.gif | Bin 0 -> 636 bytes .../togglebutton/resources/images/ml.gif | Bin 0 -> 581 bytes .../togglebutton/resources/images/mldn.gif | Bin 0 -> 1110 bytes .../togglebutton/resources/images/mr.gif | Bin 0 -> 374 bytes .../togglebutton/resources/images/mrdn.gif | Bin 0 -> 708 bytes .../togglebutton/resources/images/rb.gif | Bin 0 -> 336 bytes .../togglebutton/resources/images/rbp.gif | Bin 0 -> 345 bytes .../togglebutton/resources/images/rbr.gif | Bin 0 -> 703 bytes .../togglebutton/resources/images/rbrs.gif | Bin 0 -> 492 bytes .../togglebutton/resources/images/rbs.gif | Bin 0 -> 492 bytes .../togglebutton/resources/images/tl.gif | Bin 0 -> 614 bytes .../togglebutton/resources/images/tldn.gif | Bin 0 -> 1165 bytes .../togglebutton/resources/images/tm.gif | Bin 0 -> 577 bytes .../togglebutton/resources/images/tmdn.gif | Bin 0 -> 1119 bytes .../togglebutton/resources/images/tr.gif | Bin 0 -> 614 bytes .../togglebutton/resources/images/trdn.gif | Bin 0 -> 708 bytes .../sun/swingset3/demos/tree/TreeDemo.java | 144 + .../demos/tree/resources/TreeDemo.properties | 8 + .../demos/tree/resources/images/TreeDemo.gif | Bin 0 -> 257 bytes .../swingset3/demos/tree/resources/tree.txt | 628 ++++ .../swingset3/demos/window/WindowDemo.java | 159 + .../demos/window/resources/WindowDemo.html | 36 + .../window/resources/images/WindowDemo.gif | Bin 0 -> 284 bytes jdk/test/sanity/client/lib/jemmy/README | 3 + .../jemmy/src/org/netbeans/jemmy/Action.java | 50 + .../org/netbeans/jemmy/ActionProducer.java | 335 ++ .../jemmy/src/org/netbeans/jemmy/Bundle.java | 147 + .../src/org/netbeans/jemmy/BundleManager.java | 292 ++ .../org/netbeans/jemmy/CharBindingMap.java | 55 + .../org/netbeans/jemmy/ClassReference.java | 227 ++ .../org/netbeans/jemmy/ComponentChooser.java | 54 + .../jemmy/ComponentIsNotFocusedException.java | 47 + .../jemmy/ComponentIsNotVisibleException.java | 47 + .../org/netbeans/jemmy/ComponentSearcher.java | 255 ++ .../netbeans/jemmy/DefaultCharBindingMap.java | 201 + .../src/org/netbeans/jemmy/DialogWaiter.java | 672 ++++ .../org/netbeans/jemmy/EventDispatcher.java | 1134 ++++++ .../src/org/netbeans/jemmy/EventTool.java | 538 +++ .../src/org/netbeans/jemmy/FrameWaiter.java | 465 +++ .../org/netbeans/jemmy/JemmyException.java | 161 + .../netbeans/jemmy/JemmyInputException.java | 64 + .../org/netbeans/jemmy/JemmyProperties.java | 985 +++++ .../jemmy/NoComponentUnderMouseException.java | 43 + .../src/org/netbeans/jemmy/ObjectBrowser.java | 159 + .../src/org/netbeans/jemmy/Outputable.java | 52 + .../src/org/netbeans/jemmy/QueueTool.java | 818 ++++ .../src/org/netbeans/jemmy/Scenario.java | 48 + .../jemmy/src/org/netbeans/jemmy/Test.java | 441 +++ .../jemmy/TestCompletedException.java | 75 + .../jemmy/src/org/netbeans/jemmy/TestOut.java | 351 ++ .../jemmy/src/org/netbeans/jemmy/Timeout.java | 109 + .../jemmy/TimeoutExpiredException.java | 43 + .../src/org/netbeans/jemmy/Timeoutable.java | 50 + .../src/org/netbeans/jemmy/Timeouts.java | 424 +++ .../src/org/netbeans/jemmy/Waitable.java | 49 + .../jemmy/src/org/netbeans/jemmy/Waiter.java | 352 ++ .../src/org/netbeans/jemmy/WindowWaiter.java | 483 +++ .../accessibility/AccessibilityChooser.java | 53 + .../AccessibleDescriptionChooser.java | 58 + .../accessibility/AccessibleNameChooser.java | 58 + .../src/org/netbeans/jemmy/debug.timeouts | 19 + .../jemmy/drivers/APIDriverInstaller.java | 171 + .../jemmy/drivers/ArrayDriverInstaller.java | 58 + .../netbeans/jemmy/drivers/ButtonDriver.java | 52 + .../jemmy/drivers/DefaultDriverInstaller.java | 166 + .../drivers/DescriptablePathChooser.java | 37 + .../org/netbeans/jemmy/drivers/Driver.java | 41 + .../jemmy/drivers/DriverInstaller.java | 32 + .../netbeans/jemmy/drivers/DriverManager.java | 810 ++++ .../netbeans/jemmy/drivers/EditorDriver.java | 41 + .../netbeans/jemmy/drivers/FocusDriver.java | 38 + .../netbeans/jemmy/drivers/FrameDriver.java | 59 + .../jemmy/drivers/InputDriverInstaller.java | 158 + .../jemmy/drivers/InternalFrameDriver.java | 41 + .../org/netbeans/jemmy/drivers/KeyDriver.java | 80 + .../netbeans/jemmy/drivers/LightDriver.java | 41 + .../jemmy/drivers/LightSupportiveDriver.java | 62 + .../netbeans/jemmy/drivers/ListDriver.java | 39 + .../netbeans/jemmy/drivers/MenuDriver.java | 43 + .../netbeans/jemmy/drivers/MouseDriver.java | 123 + .../jemmy/drivers/MultiSelListDriver.java | 39 + .../jemmy/drivers/OrderedListDriver.java | 40 + .../netbeans/jemmy/drivers/PathChooser.java | 47 + .../netbeans/jemmy/drivers/ScrollDriver.java | 62 + .../jemmy/drivers/SupportiveDriver.java | 62 + .../netbeans/jemmy/drivers/TableDriver.java | 50 + .../netbeans/jemmy/drivers/TextDriver.java | 82 + .../netbeans/jemmy/drivers/TreeDriver.java | 67 + .../drivers/UnsupportedOperatorException.java | 83 + .../netbeans/jemmy/drivers/WindowDriver.java | 79 + .../drivers/buttons/ButtonMouseDriver.java | 80 + .../jemmy/drivers/buttons/package-info.java | 30 + .../jemmy/drivers/focus/APIFocusDriver.java | 48 + .../jemmy/drivers/focus/MouseFocusDriver.java | 68 + .../jemmy/drivers/focus/package-info.java | 28 + .../jemmy/drivers/input/EventDriver.java | 104 + .../jemmy/drivers/input/KeyEventDriver.java | 123 + .../jemmy/drivers/input/KeyRobotDriver.java | 95 + .../jemmy/drivers/input/MouseEventDriver.java | 186 + .../jemmy/drivers/input/MouseRobotDriver.java | 142 + .../jemmy/drivers/input/RobotDriver.java | 340 ++ .../jemmy/drivers/input/package-info.java | 30 + .../jemmy/drivers/lists/ChoiceDriver.java | 88 + .../drivers/lists/JComboMouseDriver.java | 79 + .../jemmy/drivers/lists/JListMouseDriver.java | 101 + .../jemmy/drivers/lists/JTabAPIDriver.java | 60 + .../jemmy/drivers/lists/JTabMouseDriver.java | 75 + .../drivers/lists/JTableHeaderDriver.java | 105 + .../jemmy/drivers/lists/ListAPIDriver.java | 65 + .../drivers/lists/ListKeyboardDriver.java | 72 + .../jemmy/drivers/lists/package-info.java | 30 + .../jemmy/drivers/menus/APIJMenuDriver.java | 122 + .../jemmy/drivers/menus/AppleMenuDriver.java | 140 + .../drivers/menus/DefaultJMenuDriver.java | 265 ++ .../jemmy/drivers/menus/QueueJMenuDriver.java | 325 ++ .../jemmy/drivers/menus/package-info.java | 28 + .../netbeans/jemmy/drivers/package-info.java | 38 + .../drivers/scrolling/AWTScrollDriver.java | 165 + .../scrolling/AbstractScrollDriver.java | 277 ++ .../scrolling/JScrollBarAPIDriver.java | 158 + .../drivers/scrolling/JScrollBarDriver.java | 252 ++ .../drivers/scrolling/JSliderAPIDriver.java | 168 + .../drivers/scrolling/JSliderDriver.java | 259 ++ .../drivers/scrolling/JSpinnerDriver.java | 129 + .../drivers/scrolling/JSplitPaneDriver.java | 158 + .../KeyboardJSliderScrollDriver.java | 54 + .../drivers/scrolling/ScrollAdjuster.java | 68 + .../drivers/scrolling/ScrollPaneDriver.java | 151 + .../drivers/scrolling/ScrollbarDriver.java | 137 + .../jemmy/drivers/scrolling/package-info.java | 30 + .../drivers/tables/JTableMouseDriver.java | 105 + .../jemmy/drivers/tables/package-info.java | 30 + .../jemmy/drivers/text/AWTTextAPIDriver.java | 61 + .../drivers/text/AWTTextKeyboardDriver.java | 87 + .../netbeans/jemmy/drivers/text/DownKey.java | 46 + .../netbeans/jemmy/drivers/text/EndKey.java | 47 + .../jemmy/drivers/text/GoAndBackKey.java | 32 + .../netbeans/jemmy/drivers/text/HomeKey.java | 40 + .../jemmy/drivers/text/NavigationKey.java | 44 + .../jemmy/drivers/text/OffsetKey.java | 32 + .../drivers/text/SwingTextAPIDriver.java | 61 + .../drivers/text/SwingTextKeyboardDriver.java | 116 + .../jemmy/drivers/text/TextAPIDriver.java | 154 + .../drivers/text/TextKeyboardDriver.java | 223 ++ .../netbeans/jemmy/drivers/text/UpKey.java | 46 + .../jemmy/drivers/text/package-info.java | 30 + .../jemmy/drivers/trees/JTreeAPIDriver.java | 97 + .../jemmy/drivers/trees/JTreeMouseDriver.java | 184 + .../jemmy/drivers/trees/package-info.java | 30 + .../drivers/windows/DefaultFrameDriver.java | 76 + .../windows/DefaultInternalFrameDriver.java | 150 + .../drivers/windows/DefaultWindowDriver.java | 93 + .../jemmy/drivers/windows/package-info.java | 28 + .../netbeans/jemmy/explorer/GUIBrowser.java | 1619 ++++++++ .../jemmy/explorer/TrialListenerManager.java | 204 + .../netbeans/jemmy/explorer/package-info.java | 30 + .../jemmy/image/ColorImageComparator.java | 179 + .../jemmy/image/FileImageComparator.java | 86 + .../netbeans/jemmy/image/ImageComparator.java | 42 + .../org/netbeans/jemmy/image/ImageFinder.java | 45 + .../org/netbeans/jemmy/image/ImageLoader.java | 43 + .../org/netbeans/jemmy/image/ImageSaver.java | 43 + .../org/netbeans/jemmy/image/ImageTool.java | 199 + .../netbeans/jemmy/image/PNGImageLoader.java | 45 + .../netbeans/jemmy/image/PNGImageSaver.java | 48 + .../jemmy/image/RoughImageComparator.java | 73 + .../jemmy/image/RoughImageFinder.java | 103 + .../jemmy/image/StrictImageComparator.java | 68 + .../jemmy/image/StrictImageFinder.java | 98 + .../netbeans/jemmy/image/package-info.java | 31 + .../operators/AbstractButtonOperator.java | 1213 ++++++ .../jemmy/operators/ButtonOperator.java | 530 +++ .../jemmy/operators/CheckboxOperator.java | 540 +++ .../jemmy/operators/ChoiceOperator.java | 667 ++++ .../jemmy/operators/ComponentOperator.java | 2535 +++++++++++++ .../jemmy/operators/ContainerOperator.java | 636 ++++ .../jemmy/operators/DialogOperator.java | 511 +++ .../jemmy/operators/FrameOperator.java | 558 +++ .../jemmy/operators/JButtonOperator.java | 339 ++ .../operators/JCheckBoxMenuItemOperator.java | 236 ++ .../jemmy/operators/JCheckBoxOperator.java | 273 ++ .../operators/JColorChooserOperator.java | 548 +++ .../jemmy/operators/JComboBoxOperator.java | 1452 ++++++++ .../jemmy/operators/JComponentOperator.java | 1286 +++++++ .../jemmy/operators/JDialogOperator.java | 748 ++++ .../jemmy/operators/JEditorPaneOperator.java | 481 +++ .../jemmy/operators/JFileChooserOperator.java | 1739 +++++++++ .../jemmy/operators/JFrameOperator.java | 443 +++ .../operators/JInternalFrameOperator.java | 1552 ++++++++ .../jemmy/operators/JLabelOperator.java | 629 ++++ .../jemmy/operators/JListOperator.java | 1735 +++++++++ .../jemmy/operators/JMenuBarOperator.java | 992 +++++ .../jemmy/operators/JMenuItemOperator.java | 657 ++++ .../jemmy/operators/JMenuOperator.java | 1202 ++++++ .../operators/JPasswordFieldOperator.java | 347 ++ .../jemmy/operators/JPopupMenuOperator.java | 1384 +++++++ .../jemmy/operators/JProgressBarOperator.java | 679 ++++ .../JRadioButtonMenuItemOperator.java | 212 ++ .../jemmy/operators/JRadioButtonOperator.java | 273 ++ .../jemmy/operators/JScrollBarOperator.java | 1025 +++++ .../jemmy/operators/JScrollPaneOperator.java | 961 +++++ .../jemmy/operators/JSliderOperator.java | 917 +++++ .../jemmy/operators/JSpinnerOperator.java | 1296 +++++++ .../jemmy/operators/JSplitPaneOperator.java | 914 +++++ .../jemmy/operators/JTabbedPaneOperator.java | 1168 ++++++ .../jemmy/operators/JTableHeaderOperator.java | 571 +++ .../jemmy/operators/JTableOperator.java | 2760 ++++++++++++++ .../jemmy/operators/JTextAreaOperator.java | 658 ++++ .../operators/JTextComponentOperator.java | 1599 ++++++++ .../jemmy/operators/JTextFieldOperator.java | 451 +++ .../jemmy/operators/JTextPaneOperator.java | 450 +++ .../operators/JToggleButtonOperator.java | 280 ++ .../jemmy/operators/JTreeOperator.java | 3306 +++++++++++++++++ .../jemmy/operators/LabelOperator.java | 383 ++ .../jemmy/operators/ListOperator.java | 773 ++++ .../netbeans/jemmy/operators/Operator.java | 1595 ++++++++ .../jemmy/operators/ScrollPaneOperator.java | 882 +++++ .../jemmy/operators/ScrollbarOperator.java | 683 ++++ .../jemmy/operators/TextAreaOperator.java | 474 +++ .../operators/TextComponentOperator.java | 773 ++++ .../jemmy/operators/TextFieldOperator.java | 481 +++ .../jemmy/operators/WindowOperator.java | 708 ++++ .../jemmy/operators/package-info.java | 45 + .../src/org/netbeans/jemmy/package-info.java | 32 + .../jemmy/util/AbstractTextStyleChooser.java | 70 + .../jemmy/util/DefaultVisualizer.java | 256 ++ .../netbeans/jemmy/util/DumpController.java | 37 + .../src/org/netbeans/jemmy/util/Dumper.java | 303 ++ .../netbeans/jemmy/util/EmptyVisualizer.java | 45 + .../netbeans/jemmy/util/MouseVisualizer.java | 174 + .../jemmy/util/NameComponentChooser.java | 81 + .../org/netbeans/jemmy/util/PNGDecoder.java | 222 ++ .../org/netbeans/jemmy/util/PNGEncoder.java | 289 ++ .../org/netbeans/jemmy/util/PropChooser.java | 230 ++ .../netbeans/jemmy/util/RegExComparator.java | 173 + .../jemmy/util/StringPropChooser.java | 331 ++ .../netbeans/jemmy/util/TextStyleChooser.java | 291 ++ .../org/netbeans/jemmy/util/WindowJob.java | 57 + .../netbeans/jemmy/util/WindowManager.java | 205 + .../org/netbeans/jemmy/util/package-info.java | 32 + .../jemmy/src/org/netbeans/jemmy/version_info | 6 + 413 files changed, 83163 insertions(+), 1 deletion(-) create mode 100644 jdk/make/netbeans/client_sanity/README create mode 100644 jdk/make/netbeans/client_sanity/build.xml create mode 100644 jdk/make/netbeans/client_sanity/manifest.mf create mode 100644 jdk/make/netbeans/client_sanity/nbproject/build-impl.xml create mode 100644 jdk/make/netbeans/client_sanity/nbproject/genfiles.properties create mode 100644 jdk/make/netbeans/client_sanity/nbproject/project.properties create mode 100644 jdk/make/netbeans/client_sanity/nbproject/project.xml create mode 100644 jdk/test/sanity/client/README create mode 100644 jdk/test/sanity/client/SwingSet/src/ButtonDemoScreenshotTest.java create mode 100644 jdk/test/sanity/client/SwingSet/src/ButtonDemoTest.java create mode 100644 jdk/test/sanity/client/SwingSet/src/ComboBoxDemoTest.java create mode 100644 jdk/test/sanity/client/SwingSet/src/ListDemoTest.java create mode 100644 jdk/test/sanity/client/SwingSet/src/OptionPaneDemoTest.java create mode 100644 jdk/test/sanity/client/SwingSet/src/ProgressBarDemoTest.java create mode 100644 jdk/test/sanity/client/SwingSet/src/ScrollPaneDemoTest.java create mode 100644 jdk/test/sanity/client/SwingSet/src/SpinnerDemoTest.java create mode 100644 jdk/test/sanity/client/SwingSet/src/SplitPaneDemoTest.java create mode 100644 jdk/test/sanity/client/SwingSet/src/TabbedPaneDemoTest.java create mode 100644 jdk/test/sanity/client/SwingSet/src/TextFieldDemoTest.java create mode 100644 jdk/test/sanity/client/SwingSet/src/ToggleButtonDemoTest.java create mode 100644 jdk/test/sanity/client/SwingSet/src/TreeDemoTest.java create mode 100644 jdk/test/sanity/client/SwingSet/src/WindowDemoTest.java create mode 100644 jdk/test/sanity/client/TEST.ROOT.template create mode 100644 jdk/test/sanity/client/TEST.properties create mode 100644 jdk/test/sanity/client/lib/Jemmy2Ext/src/org/jemmy2ext/JemmyExt.java create mode 100644 jdk/test/sanity/client/lib/SwingSet3/README create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/DemoProperties.java create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/DemoUtilities.java create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/JGridPanel.java create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/JHyperlink.java create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/ResourceManager.java create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/button/ButtonDemo.java create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/button/resources/ButtonDemo.html create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/button/resources/images/ButtonDemo.gif create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/button/resources/images/blogs.png create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/button/resources/images/document-print.png create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/button/resources/images/earth_day.gif create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/button/resources/images/earth_night.gif create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/button/resources/images/edit-find.png create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/button/resources/images/redbutton.png create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/button/resources/images/redbutton_dark.png create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/button/resources/images/redbutton_glow.png create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/ComboBoxDemo.java create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/ComboBoxDemo.properties create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/ComboBoxDemo.gif create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/brenteyes.jpg create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/brenthair.jpg create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/brentmouth.jpg create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/georgeseyes.jpg create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/georgeshair.jpg create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/georgesmouth.jpg create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/hanseyes.jpg create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/hanshair.jpg create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/hansmouth.jpg create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/howardeyes.jpg create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/howardhair.jpg create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/howardmouth.jpg create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/jameseyes.jpg create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/jameshair.jpg create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/jamesmouth.jpg create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/jeffeyes.jpg create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/jeffhair.jpg create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/jeffmouth.jpg create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/joneyes.jpg create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/jonhair.jpg create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/jonmouth.jpg create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/laraeyes.jpg create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/larahair.jpg create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/laramouth.jpg create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/larryeyes.jpg create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/larryhair.jpg create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/larrymouth.jpg create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/lisaeyes.jpg create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/lisahair.jpg create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/lisamouth.jpg create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/michaeleyes.jpg create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/michaelhair.jpg create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/michaelmouth.jpg create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/philipeyes.jpg create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/philiphair.jpg create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/philipmouth.jpg create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/scotteyes.jpg create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/scotthair.jpg create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/scottmouth.jpg create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/list/ListDemo.java create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/list/Permuter.java create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/list/resources/ListDemo.properties create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/list/resources/images/ListDemo.gif create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/list/resources/images/blue.gif create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/list/resources/images/cyan.gif create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/list/resources/images/gray.gif create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/list/resources/images/green.gif create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/list/resources/images/magenta.gif create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/list/resources/images/red.gif create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/list/resources/images/yellow.gif create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/optionpane/OptionPaneDemo.java create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/optionpane/resources/OptionPaneDemo.properties create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/optionpane/resources/images/OptionPaneDemo.gif create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/optionpane/resources/images/bottle.gif create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/progressbar/ProgressBarDemo.java create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/progressbar/resources/ProgressBarDemo.properties create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/progressbar/resources/images/ProgressBarDemo.gif create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/scrollpane/ScrollPaneDemo.java create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/scrollpane/resources/ScrollPaneDemo.properties create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/scrollpane/resources/images/COPYRIGHT create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/scrollpane/resources/images/ScrollPaneDemo.gif create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/scrollpane/resources/images/colheader.jpg create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/scrollpane/resources/images/crayons.jpg create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/scrollpane/resources/images/lowerleft.jpg create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/scrollpane/resources/images/rowheader.jpg create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/scrollpane/resources/images/upperleft.jpg create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/scrollpane/resources/images/upperright.jpg create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/spinner/JMandelbrot.java create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/spinner/JPaletteShower.java create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/spinner/JSpinnerPanel.java create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/spinner/MandelbrotControl.java create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/spinner/Palette.java create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/spinner/PaletteChooser.java create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/spinner/SpinnerDemo.java create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/spinner/resources/SpinnerDemo.properties create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/spinner/resources/images/SpinnerDemo.gif create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/splitpane/SplitPaneDemo.java create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/splitpane/resources/SplitPaneDemo.properties create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/splitpane/resources/images/SplitPaneDemo.gif create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/splitpane/resources/images/day.jpg create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/splitpane/resources/images/night.jpg create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/tabbedpane/TabbedPaneDemo.java create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/tabbedpane/resources/TabbedPaneDemo.properties create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/tabbedpane/resources/images/TabbedPaneDemo.gif create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/tabbedpane/resources/images/blake.gif create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/tabbedpane/resources/images/brooke.gif create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/tabbedpane/resources/images/camille.jpg create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/tabbedpane/resources/images/david.gif create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/tabbedpane/resources/images/ewan.gif create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/tabbedpane/resources/images/ewan.jpg create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/tabbedpane/resources/images/matthew.gif create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/tabbedpane/resources/images/miranda.jpg create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/tabbedpane/resources/images/stephen.gif create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/textfield/JHistoryTextField.java create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/textfield/TextFieldDemo.java create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/textfield/resources/TextFieldDemo.properties create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/textfield/resources/images/TextFieldDemo.gif create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/DirectionPanel.java create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/LayoutControlPanel.java create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/ToggleButtonDemo.java create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/ToggleButtonDemo.properties create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/ToggleButtonDemo.gif create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/b1.gif create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/b1d.gif create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/b1p.gif create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/b1r.gif create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/b2.gif create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/b2d.gif create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/b2p.gif create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/b2r.gif create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/b3.gif create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/b3d.gif create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/b3p.gif create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/b3r.gif create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/bl.gif create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/bldn.gif create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/bm.gif create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/bmdn.gif create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/br.gif create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/brdn.gif create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/c.gif create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/cb.gif create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/cbr.gif create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/cbrs.gif create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/cbs.gif create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/cdn.gif create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/ml.gif create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/mldn.gif create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/mr.gif create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/mrdn.gif create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/rb.gif create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/rbp.gif create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/rbr.gif create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/rbrs.gif create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/rbs.gif create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/tl.gif create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/tldn.gif create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/tm.gif create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/tmdn.gif create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/tr.gif create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/trdn.gif create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/tree/TreeDemo.java create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/tree/resources/TreeDemo.properties create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/tree/resources/images/TreeDemo.gif create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/tree/resources/tree.txt create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/window/WindowDemo.java create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/window/resources/WindowDemo.html create mode 100644 jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/window/resources/images/WindowDemo.gif create mode 100644 jdk/test/sanity/client/lib/jemmy/README create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/Action.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/ActionProducer.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/Bundle.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/BundleManager.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/CharBindingMap.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/ClassReference.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/ComponentChooser.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/ComponentIsNotFocusedException.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/ComponentIsNotVisibleException.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/ComponentSearcher.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/DefaultCharBindingMap.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/DialogWaiter.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/EventDispatcher.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/EventTool.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/FrameWaiter.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/JemmyException.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/JemmyInputException.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/JemmyProperties.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/NoComponentUnderMouseException.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/ObjectBrowser.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/Outputable.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/QueueTool.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/Scenario.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/Test.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/TestCompletedException.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/TestOut.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/Timeout.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/TimeoutExpiredException.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/Timeoutable.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/Timeouts.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/Waitable.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/Waiter.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/WindowWaiter.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/accessibility/AccessibilityChooser.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/accessibility/AccessibleDescriptionChooser.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/accessibility/AccessibleNameChooser.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/debug.timeouts create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/APIDriverInstaller.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/ArrayDriverInstaller.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/ButtonDriver.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/DefaultDriverInstaller.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/DescriptablePathChooser.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/Driver.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/DriverInstaller.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/DriverManager.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/EditorDriver.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/FocusDriver.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/FrameDriver.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/InputDriverInstaller.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/InternalFrameDriver.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/KeyDriver.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/LightDriver.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/LightSupportiveDriver.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/ListDriver.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/MenuDriver.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/MouseDriver.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/MultiSelListDriver.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/OrderedListDriver.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/PathChooser.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/ScrollDriver.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/SupportiveDriver.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/TableDriver.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/TextDriver.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/TreeDriver.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/UnsupportedOperatorException.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/WindowDriver.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/buttons/ButtonMouseDriver.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/buttons/package-info.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/focus/APIFocusDriver.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/focus/MouseFocusDriver.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/focus/package-info.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/input/EventDriver.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/input/KeyEventDriver.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/input/KeyRobotDriver.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/input/MouseEventDriver.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/input/MouseRobotDriver.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/input/RobotDriver.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/input/package-info.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/lists/ChoiceDriver.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/lists/JComboMouseDriver.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/lists/JListMouseDriver.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/lists/JTabAPIDriver.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/lists/JTabMouseDriver.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/lists/JTableHeaderDriver.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/lists/ListAPIDriver.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/lists/ListKeyboardDriver.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/lists/package-info.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/menus/APIJMenuDriver.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/menus/AppleMenuDriver.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/menus/DefaultJMenuDriver.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/menus/QueueJMenuDriver.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/menus/package-info.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/package-info.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/scrolling/AWTScrollDriver.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/scrolling/AbstractScrollDriver.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/scrolling/JScrollBarAPIDriver.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/scrolling/JScrollBarDriver.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/scrolling/JSliderAPIDriver.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/scrolling/JSliderDriver.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/scrolling/JSpinnerDriver.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/scrolling/JSplitPaneDriver.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/scrolling/KeyboardJSliderScrollDriver.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/scrolling/ScrollAdjuster.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/scrolling/ScrollPaneDriver.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/scrolling/ScrollbarDriver.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/scrolling/package-info.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/tables/JTableMouseDriver.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/tables/package-info.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/text/AWTTextAPIDriver.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/text/AWTTextKeyboardDriver.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/text/DownKey.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/text/EndKey.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/text/GoAndBackKey.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/text/HomeKey.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/text/NavigationKey.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/text/OffsetKey.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/text/SwingTextAPIDriver.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/text/SwingTextKeyboardDriver.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/text/TextAPIDriver.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/text/TextKeyboardDriver.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/text/UpKey.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/text/package-info.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/trees/JTreeAPIDriver.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/trees/JTreeMouseDriver.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/trees/package-info.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/windows/DefaultFrameDriver.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/windows/DefaultInternalFrameDriver.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/windows/DefaultWindowDriver.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/windows/package-info.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/explorer/GUIBrowser.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/explorer/TrialListenerManager.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/explorer/package-info.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/image/ColorImageComparator.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/image/FileImageComparator.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/image/ImageComparator.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/image/ImageFinder.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/image/ImageLoader.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/image/ImageSaver.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/image/ImageTool.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/image/PNGImageLoader.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/image/PNGImageSaver.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/image/RoughImageComparator.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/image/RoughImageFinder.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/image/StrictImageComparator.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/image/StrictImageFinder.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/image/package-info.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/AbstractButtonOperator.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/ButtonOperator.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/CheckboxOperator.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/ChoiceOperator.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/ComponentOperator.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/ContainerOperator.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/DialogOperator.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/FrameOperator.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JButtonOperator.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JCheckBoxMenuItemOperator.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JCheckBoxOperator.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JColorChooserOperator.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JComboBoxOperator.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JComponentOperator.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JDialogOperator.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JEditorPaneOperator.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JFileChooserOperator.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JFrameOperator.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JInternalFrameOperator.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JLabelOperator.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JListOperator.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JMenuBarOperator.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JMenuItemOperator.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JMenuOperator.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JPasswordFieldOperator.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JPopupMenuOperator.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JProgressBarOperator.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JRadioButtonMenuItemOperator.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JRadioButtonOperator.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JScrollBarOperator.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JScrollPaneOperator.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JSliderOperator.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JSpinnerOperator.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JSplitPaneOperator.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JTabbedPaneOperator.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JTableHeaderOperator.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JTableOperator.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JTextAreaOperator.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JTextComponentOperator.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JTextFieldOperator.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JTextPaneOperator.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JToggleButtonOperator.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JTreeOperator.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/LabelOperator.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/ListOperator.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/Operator.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/ScrollPaneOperator.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/ScrollbarOperator.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/TextAreaOperator.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/TextComponentOperator.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/TextFieldOperator.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/WindowOperator.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/package-info.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/package-info.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/util/AbstractTextStyleChooser.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/util/DefaultVisualizer.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/util/DumpController.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/util/Dumper.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/util/EmptyVisualizer.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/util/MouseVisualizer.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/util/NameComponentChooser.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/util/PNGDecoder.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/util/PNGEncoder.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/util/PropChooser.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/util/RegExComparator.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/util/StringPropChooser.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/util/TextStyleChooser.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/util/WindowJob.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/util/WindowManager.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/util/package-info.java create mode 100644 jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/version_info diff --git a/jdk/.hgignore b/jdk/.hgignore index 1ed4344d03f..9710bc5d891 100644 --- a/jdk/.hgignore +++ b/jdk/.hgignore @@ -7,3 +7,5 @@ ^make/netbeans/.*/dist/ ^.hgtip .DS_Store +.*/JTreport/.* +.*/JTwork/.* diff --git a/jdk/make/netbeans/client_sanity/README b/jdk/make/netbeans/client_sanity/README new file mode 100644 index 00000000000..76d57db49c3 --- /dev/null +++ b/jdk/make/netbeans/client_sanity/README @@ -0,0 +1,15 @@ +This NetBeans project corresponds to client sanity test suite in jdk/test/sanity/client. + +It simplifies working on the suite in NetBeans. + +It also includes the following custom tasks: + +prepare-bundle creates dist/sanity.zip containing standalone bundle of the suite + +run-jemmy-browser runs Jemmy browser for the ButtonDemo (hardcoded in build.xml file) + The tool allows to explore the UI hierarchy. + +There is no task to run tests using JTReg. Please refer to corresponding README file +in the client sanity test suite folder on how to run the tests. + +Contact alexander.kouznetsov@oracle.com in case of issues. \ No newline at end of file diff --git a/jdk/make/netbeans/client_sanity/build.xml b/jdk/make/netbeans/client_sanity/build.xml new file mode 100644 index 00000000000..734fe10b036 --- /dev/null +++ b/jdk/make/netbeans/client_sanity/build.xml @@ -0,0 +1,94 @@ + + + + + + + + + + + Builds, tests, and runs the project SanityTests. + + + + + + + + + + + + + + + + + + + + + diff --git a/jdk/make/netbeans/client_sanity/manifest.mf b/jdk/make/netbeans/client_sanity/manifest.mf new file mode 100644 index 00000000000..1574df4a2de --- /dev/null +++ b/jdk/make/netbeans/client_sanity/manifest.mf @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +X-COMMENT: Main-Class will be added automatically by build + diff --git a/jdk/make/netbeans/client_sanity/nbproject/build-impl.xml b/jdk/make/netbeans/client_sanity/nbproject/build-impl.xml new file mode 100644 index 00000000000..00073338957 --- /dev/null +++ b/jdk/make/netbeans/client_sanity/nbproject/build-impl.xml @@ -0,0 +1,1429 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set src.src3.dir + Must set src.src4.dir + Must set src.src2.dir + Must set src.src.dir + Must set build.dir + Must set dist.dir + Must set build.classes.dir + Must set dist.javadoc.dir + Must set build.test.classes.dir + Must set build.test.results.dir + Must set build.classes.excludes + Must set dist.jar + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + No tests executed. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set JVM to use for profiling in profiler.info.jvm + Must set profiler agent JVM arguments in profiler.info.jvmargs.agent + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + To run this application from the command line without Ant, try: + + java -jar "${dist.jar.resolved}" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set run.class + + + + Must select one file in the IDE or set run.class + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set debug.class + + + + + Must select one file in the IDE or set debug.class + + + + + Must set fix.includes + + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + Must select one file in the IDE or set profile.class + This target only works when run from inside the NetBeans IDE. + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + + + + + This target only works when run from inside the NetBeans IDE. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select one file in the IDE or set run.class + + + + + + Must select some files in the IDE or set test.includes + + + + + Must select one file in the IDE or set run.class + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must select some files in the IDE or set javac.includes + + + + + + + + + + + + + + + + + + Some tests failed; see details above. + + + + + + + + + Must select some files in the IDE or set test.includes + + + + Some tests failed; see details above. + + + + Must select some files in the IDE or set test.class + Must select some method in the IDE or set test.method + + + + Some tests failed; see details above. + + + + + Must select one file in the IDE or set test.class + + + + Must select one file in the IDE or set test.class + Must select some method in the IDE or set test.method + + + + + + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + Must select one file in the IDE or set applet.url + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/jdk/make/netbeans/client_sanity/nbproject/genfiles.properties b/jdk/make/netbeans/client_sanity/nbproject/genfiles.properties new file mode 100644 index 00000000000..fc956fd5f2e --- /dev/null +++ b/jdk/make/netbeans/client_sanity/nbproject/genfiles.properties @@ -0,0 +1,8 @@ +build.xml.data.CRC32=0f9d9977 +build.xml.script.CRC32=f902e8b8 +build.xml.stylesheet.CRC32=8064a381@1.75.2.48 +# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. +# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. +nbproject/build-impl.xml.data.CRC32=55414227 +nbproject/build-impl.xml.script.CRC32=c12f9d04 +nbproject/build-impl.xml.stylesheet.CRC32=05530350@1.79.1.48 diff --git a/jdk/make/netbeans/client_sanity/nbproject/project.properties b/jdk/make/netbeans/client_sanity/nbproject/project.properties new file mode 100644 index 00000000000..a43ee024fd8 --- /dev/null +++ b/jdk/make/netbeans/client_sanity/nbproject/project.properties @@ -0,0 +1,79 @@ +annotation.processing.enabled=true +annotation.processing.enabled.in.editor=false +annotation.processing.processors.list= +annotation.processing.run.all.processors=true +annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output +application.title=SanityTests +application.vendor=akouznet +build.classes.dir=${build.dir}/classes +build.classes.excludes=**/*.java,**/*.form +# This directory is removed when the project is cleaned: +build.dir=build +build.generated.dir=${build.dir}/generated +build.generated.sources.dir=${build.dir}/generated-sources +# Only compile against the classpath explicitly listed here: +build.sysclasspath=ignore +build.test.classes.dir=${build.dir}/test/classes +build.test.results.dir=${build.dir}/test/results +# Uncomment to specify the preferred debugger connection transport: +#debug.transport=dt_socket +debug.classpath=\ + ${run.classpath} +debug.test.classpath=\ + ${run.test.classpath} +# Files in build.classes.dir which should be excluded from distribution jar +dist.archive.excludes= +# This directory is removed when the project is cleaned: +dist.dir=dist +dist.jar=${dist.dir}/SanityTests.jar +dist.javadoc.dir=${dist.dir}/javadoc +endorsed.classpath= +excludes=*.cfg +includes=** +jar.compress=false +javac.classpath=\ + ${libs.testng.classpath} +# Space-separated list of extra javac options +javac.compilerargs=-Xmaxwarns 9999 -Xlint:all -Xlint:-serial +javac.deprecation=false +javac.external.vm=false +javac.processorpath=\ + ${javac.classpath} +javac.source=1.8 +javac.target=1.8 +javac.test.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir} +javac.test.processorpath=\ + ${javac.test.classpath} +javadoc.additionalparam= +javadoc.author=false +javadoc.encoding=${source.encoding} +javadoc.noindex=false +javadoc.nonavbar=false +javadoc.notree=false +javadoc.private=false +javadoc.splitindex=true +javadoc.use=true +javadoc.version=false +javadoc.windowtitle= +main.class= +manifest.file=manifest.mf +meta.inf.dir=${src.dir}/META-INF +mkdist.disabled=false +platform.active=default_platform +run.classpath=\ + ${javac.classpath}:\ + ${build.classes.dir} +# Space-separated list of JVM arguments used when running the project. +# You may also define separate properties like run-sys-prop.name=value instead of -Dname=value. +# To set system properties for unit tests define test-sys-prop.name=value: +run.jvmargs=-ea +run.test.classpath=\ + ${javac.test.classpath}:\ + ${build.test.classes.dir} +source.encoding=UTF-8 +src.src.dir=..\\..\\..\\test\\sanity\\client\\SwingSet\\src +src.src2.dir=..\\..\\..\\test\\sanity\\client\\lib\\SwingSet3\\src +src.src3.dir=..\\..\\..\\test\\sanity\\client\\lib\\jemmy\\src +src.src4.dir=..\\..\\..\\test\\sanity\\client\\lib\\Jemmy2Ext\\src diff --git a/jdk/make/netbeans/client_sanity/nbproject/project.xml b/jdk/make/netbeans/client_sanity/nbproject/project.xml new file mode 100644 index 00000000000..fccac4ecf31 --- /dev/null +++ b/jdk/make/netbeans/client_sanity/nbproject/project.xml @@ -0,0 +1,17 @@ + + + org.netbeans.modules.java.j2seproject + + + SanityTests + + + + + + + + + + + diff --git a/jdk/test/TEST.groups b/jdk/test/TEST.groups index 898ace52108..53deee5c732 100644 --- a/jdk/test/TEST.groups +++ b/jdk/test/TEST.groups @@ -60,7 +60,8 @@ tier3 = \ :jdk_rmi \ :jdk_beans \ :jdk_imageio \ - :jdk_sound + :jdk_sound \ + :jdk_client_sanity ############################################################################### # @@ -351,6 +352,10 @@ jdk_desktop = \ :jdk_sound \ :jdk_imageio +# SwingSet3 tests. +jdk_client_sanity = \ + sanity/client/SwingSet + ############################################################################### # # Serviceability sanity groups diff --git a/jdk/test/sanity/client/README b/jdk/test/sanity/client/README new file mode 100644 index 00000000000..09f8e16326a --- /dev/null +++ b/jdk/test/sanity/client/README @@ -0,0 +1,45 @@ +This suite contains automated client sanity tests which can be run using JTReg. + +Contact alexander.kouznetsov@oracle.com in case of issues. + +------------------------------------------------------------------------------- +How to run: + +1) Download/Install the JDK to be tested in the system. + (For example C:/java/jdk1.9.0 in windows or + /export/jdk/jdk1.9.0 in linux/mac/solaris) +2) Download/Install JTReg harness, minimum required version is 4.1 b13. +3) Open terminal(cmd in windows, *not* cygwin) and go to the this directory. +4) To run + - see the notes below on how to prepare for the test run + - set JT_HOME to , for example + set JT_HOME=C:\Java\client\jtreg (Windows) + - run the command + 'sh -ea -k:\!screenshots -jdk: SwingSet' + + For example: 'sh C:/jtreg/bin/jtreg -ea -k:\!screenshots -jdk:C:/java/jdk1.9.0 SwingSet' (Windows) + 'sh /export/jtreg/bin/jtreg -ea -k:\!screenshots -jdk:/export/jdk/jdk1.9.0 SwingSet' (Linux/Solaris) + 'sh /export/jtreg/bin/jtreg -ea -k:\!screenshots -jdk:/export/jdk/jdk1.9.0/Contents/Home SwingSet' (Mac) + +Try to minimize all the other windows for no interference and test stability. +Do not touch keyboard or mouse, open any window, nor lock the screen while the tests are running. +The tests will be executed, and the results will be displayed in the terminal. + +A report will be generated under + a) JTReg: "JTReport/index.html". +The failure logs could be found under: + a) JTReg: "JTWork//.jtr" + +The following additional options might be useful: +-retain:all to keep work files for passed tests +-k:\!screenshots removal of this option will run tests that require full environment with Robot and screenshots +-g to run JavaTest GUI + +------------------------------------------------------------------------------- + +The tests in the suite are based on SwingSet3 demo application. They use Jemmy to +operate on controls of the demo and verify that it is behaving as expected. Both +Jemmy and SwingSet3 sources are available as copies in lib folder. + +Original Jemmy repository is https://jemmy.java.net +Original SwingSet3 repository is https://java.net/projects/swingset3 \ No newline at end of file diff --git a/jdk/test/sanity/client/SwingSet/src/ButtonDemoScreenshotTest.java b/jdk/test/sanity/client/SwingSet/src/ButtonDemoScreenshotTest.java new file mode 100644 index 00000000000..b6959ac150a --- /dev/null +++ b/jdk/test/sanity/client/SwingSet/src/ButtonDemoScreenshotTest.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.awt.Point; +import java.awt.Robot; +import java.awt.event.InputEvent; +import java.awt.image.BufferedImage; +import org.netbeans.jemmy.ClassReference; +import org.netbeans.jemmy.image.StrictImageComparator; +import org.netbeans.jemmy.operators.JButtonOperator; +import org.netbeans.jemmy.operators.JFrameOperator; +import static org.jemmy2ext.JemmyExt.*; +import org.testng.annotations.Test; +import static com.sun.swingset3.demos.button.ButtonDemo.*; + +/* + * @test + * @key headful screenshots + * @summary Verifies buttons on SwingSet3 ButtonDemo page by clicking each + * button, taking its screenshots and checking that pressed button + * image is different from initial button image. + * + * @library /sanity/client/lib/jemmy/src + * @library /sanity/client/lib/Jemmy2Ext/src + * @library /sanity/client/lib/SwingSet3/src + * @build org.jemmy2ext.JemmyExt + * @build com.sun.swingset3.demos.button.ButtonDemo + * @run testng ButtonDemoScreenshotTest + */ +public class ButtonDemoScreenshotTest { + + private static final int BUTTON_COUNT = 6; // TODO: Decide about "open browser" buttons (value was 8 originally) + + @Test + public void test() throws Exception { + captureDebugInfoOnFail(() -> { + Robot rob = new Robot(); + + new ClassReference(com.sun.swingset3.demos.button.ButtonDemo.class.getCanonicalName()).startApplication(); + + JFrameOperator mainFrame = new JFrameOperator(DEMO_TITLE); + waitImageIsStill(rob, mainFrame); + + // Check all the buttons + for (int i = 0; i < BUTTON_COUNT; i++) { + checkButton(mainFrame, i, rob); + } + }); + } + + public void checkButton(JFrameOperator jfo, int i, Robot rob) { + JButtonOperator button = new JButtonOperator(jfo, i); + + Point loc = button.getLocationOnScreen(); + rob.mouseMove(loc.x, loc.y); + + BufferedImage initialButtonImage = capture(rob, button); + assertNotBlack(initialButtonImage); + save(initialButtonImage, "button" + i + "_0initial.png"); + rob.mousePress(InputEvent.BUTTON1_MASK); + try { + waitPressed(button); + BufferedImage pressedButtonImage = capture(rob, button); + assertNotBlack(pressedButtonImage); + save(pressedButtonImage, "button" + i + "_1pressed.png"); + + StrictImageComparator sComparator = new StrictImageComparator(); + assertNotEquals("Button " + i + " Test", sComparator, initialButtonImage, pressedButtonImage); + } finally { + rob.mouseRelease(InputEvent.BUTTON1_MASK); + } + } + +} diff --git a/jdk/test/sanity/client/SwingSet/src/ButtonDemoTest.java b/jdk/test/sanity/client/SwingSet/src/ButtonDemoTest.java new file mode 100644 index 00000000000..3d0d8f1afea --- /dev/null +++ b/jdk/test/sanity/client/SwingSet/src/ButtonDemoTest.java @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import com.sun.swingset3.demos.JHyperlink; +import com.sun.swingset3.demos.button.ButtonDemo; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; +import javax.swing.ButtonModel; +import javax.swing.JButton; +import javax.swing.event.ChangeEvent; +import static org.testng.AssertJUnit.*; +import org.testng.annotations.Test; +import org.jemmy2ext.JemmyExt.ByToolTipChooser; +import static org.jemmy2ext.JemmyExt.EXACT_STRING_COMPARATOR; +import org.netbeans.jemmy.ClassReference; +import org.netbeans.jemmy.operators.JButtonOperator; +import org.netbeans.jemmy.operators.JFrameOperator; +import static com.sun.swingset3.demos.button.ButtonDemo.*; +import org.jemmy2ext.JemmyExt; +import org.jemmy2ext.JemmyExt.MultiThreadedTryCatch; +import static org.jemmy2ext.JemmyExt.captureDebugInfoOnFail; + +/* + * @test + * @key headful + * @summary Verifies buttons on SwingSet3 ButtonDemo page by clicking each button + * and checking model change events. It also verifies tooltips and text + * on buttons before and after click. + * + * @library /sanity/client/lib/jemmy/src + * @library /sanity/client/lib/Jemmy2Ext/src + * @library /sanity/client/lib/SwingSet3/src + * @build org.jemmy2ext.JemmyExt + * @build com.sun.swingset3.demos.button.ButtonDemo + * @run testng ButtonDemoTest + */ +public class ButtonDemoTest { + + private static final String[] BUTTON_TEXT_AFTER = { + DO_IT_AGAIN,}; + + private static final String[] BUTTON_TEXT_BEFORE = { + DO_IT, + "", + FIND, + GO, + CONNECT, + "", + GET_MORE_INFO, + null + }; + + private static final String[] BUTTON_TOOLTIP = { + SIMPLE_BUTTON, + IMAGE_BUTTON, + BUTTON_WITH_TEXT_AND_IMAGE, + BUTTON_WITH_BACKGROUND_COLOR, + BUTTON_WITH_NO_BORDER, + BUTTON_WITH_ROLLOVER_IMAGE, + JAVA_SE_URL, + JAVA_BLOGS_URL + }; + + private static final String[] GOLDEN = { + "isArmed = false, isEnabled = true, isPressed = false, isSelected = false", + "isArmed = true, isEnabled = true, isPressed = false, isSelected = false", + "isArmed = true, isEnabled = true, isPressed = true, isSelected = false", + "isArmed = true, isEnabled = true, isPressed = false, isSelected = false", + "isArmed = false, isEnabled = true, isPressed = false, isSelected = false" + }; + + @Test + public void test() throws Exception { + + captureDebugInfoOnFail(() -> { + + new ClassReference(ButtonDemo.class.getCanonicalName()).startApplication(); + + JFrameOperator mainFrame = new JFrameOperator(DEMO_TITLE); + mainFrame.setComparator(EXACT_STRING_COMPARATOR); + + // Check all the buttons + for (int i = 0; i < BUTTON_TOOLTIP.length; i++) { + String tooltip = BUTTON_TOOLTIP[i]; + + JButtonOperator button = new JButtonOperator(mainFrame, new ByToolTipChooser(tooltip)); + + assertEquals(BUTTON_TEXT_BEFORE[i], button.getText()); + + // Two buttons are hyperlinks, we don't want to click them + if (!button.getSource().getClass().equals(JHyperlink.class)) { + checkButton(button); + } + + if (BUTTON_TEXT_AFTER.length > i) { + assertEquals(BUTTON_TEXT_AFTER[i], button.getText()); + } else { + assertEquals(BUTTON_TEXT_BEFORE[i], button.getText()); + } + } + + }); + } + + private void checkButton(JButtonOperator button) throws Exception { + MultiThreadedTryCatch tryCatch = new JemmyExt.MultiThreadedTryCatch(); + try { + BlockingQueue modelStateChanges = new ArrayBlockingQueue<>(GOLDEN.length); + button.getQueueTool().invokeAndWait(() -> { + try { + JButton jButton = (JButton) button.getSource(); + ButtonModel model = jButton.getModel(); + String line = toString(model); + System.out.println("Inital: " + line); + modelStateChanges.add(line); + model.addChangeListener((ChangeEvent e) -> { + try { + String line2 = toString(model); + System.out.println("ChangeEvent: " + line2); + + // We are only interested in the first GOLDEN.length events + if (modelStateChanges.remainingCapacity() > 0) { + modelStateChanges.add(line2); + } + } catch (RuntimeException | Error t) { + tryCatch.register(t); + } + }); + } catch (Error error) { + // All exceptions are already handled by Jemmy but Errors are not + tryCatch.register(error); + throw error; + } + }); + + assertEquals("Initial state check", GOLDEN[0], modelStateChanges.take()); + + button.clickMouse(); + + for (int state = 1; state < GOLDEN.length; state++) { + assertEquals(GOLDEN[state], modelStateChanges.take()); + } + } catch (RuntimeException | Error | InterruptedException t) { + tryCatch.registerRoot(t); + } finally { + tryCatch.throwRegistered(); + } + } + + private static String toString(ButtonModel model) { + return "isArmed = " + model.isArmed() + + ", isEnabled = " + model.isEnabled() + + ", isPressed = " + model.isPressed() + + ", isSelected = " + model.isSelected(); + } + +} diff --git a/jdk/test/sanity/client/SwingSet/src/ComboBoxDemoTest.java b/jdk/test/sanity/client/SwingSet/src/ComboBoxDemoTest.java new file mode 100644 index 00000000000..303bbd681f1 --- /dev/null +++ b/jdk/test/sanity/client/SwingSet/src/ComboBoxDemoTest.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import com.sun.swingset3.demos.combobox.ComboBoxDemo; +import static org.testng.AssertJUnit.*; +import org.testng.annotations.Test; +import org.netbeans.jemmy.ClassReference; +import org.netbeans.jemmy.operators.JComboBoxOperator; +import org.netbeans.jemmy.operators.JFrameOperator; +import static com.sun.swingset3.demos.combobox.ComboBoxDemo.*; +import static org.jemmy2ext.JemmyExt.captureDebugInfoOnFail; + +/* + * @test + * @key headful + * @summary Verifies ComboBoxes on SwingSet2 ComboBoxDemo page by selecting + * each value of each ComboBox. + * + * @library /sanity/client/lib/jemmy/src + * @library /sanity/client/lib/Jemmy2Ext/src + * @library /sanity/client/lib/SwingSet3/src + * @build org.jemmy2ext.JemmyExt + * @build com.sun.swingset3.demos.combobox.ComboBoxDemo + * @run testng ComboBoxDemoTest + */ +public class ComboBoxDemoTest { + + private static enum ComboBoxInfo { + PRESETS("Presets:"), + HAIR("Hair:"), + EYES_N_NOSE("Eyes & Nose:"), + MOUTH("Mouth:"); + + private final String comboBoxName; + + private ComboBoxInfo(String comboBoxName) { + this.comboBoxName = comboBoxName; + } + + } + + @Test + public void test() throws Exception { + captureDebugInfoOnFail(() -> { + new ClassReference(ComboBoxDemo.class.getCanonicalName()).startApplication(); + + JFrameOperator frame = new JFrameOperator(DEMO_TITLE); + for (ComboBoxInfo comboBoxInfo : ComboBoxInfo.values()) { + comboBoxChecker(frame, comboBoxInfo); + } + }); + } + + private void comboBoxChecker(JFrameOperator jfo, ComboBoxInfo comboBoxInfo) { + JComboBoxOperator jcbo = new JComboBoxOperator(jfo, comboBoxInfo.ordinal()); + for (int i = 0; i < jcbo.getItemCount(); i++) { + jcbo.selectItem(i); + assertEquals(comboBoxInfo.comboBoxName + " ComboBox SelectedIndex is correct", i, jcbo.getSelectedIndex()); + } + } + +} diff --git a/jdk/test/sanity/client/SwingSet/src/ListDemoTest.java b/jdk/test/sanity/client/SwingSet/src/ListDemoTest.java new file mode 100644 index 00000000000..c24399e526b --- /dev/null +++ b/jdk/test/sanity/client/SwingSet/src/ListDemoTest.java @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import com.sun.swingset3.demos.list.ListDemo; +import static com.sun.swingset3.demos.list.ListDemo.DEMO_TITLE; +import static org.testng.AssertJUnit.*; +import org.testng.annotations.Test; +import static org.jemmy2ext.JemmyExt.getLabeledContainerOperator; +import org.netbeans.jemmy.ClassReference; +import org.netbeans.jemmy.operators.JCheckBoxOperator; +import org.netbeans.jemmy.operators.JFrameOperator; +import org.netbeans.jemmy.operators.JListOperator; +import static org.jemmy2ext.JemmyExt.captureDebugInfoOnFail; + +/* + * @test + * @key headful + * @summary Verifies SwingSet3 ListDemo page by checking and unchecking all + * the checkboxes on the page and verifying the number of items in the + * list. + * + * @library /sanity/client/lib/jemmy/src + * @library /sanity/client/lib/Jemmy2Ext/src + * @library /sanity/client/lib/SwingSet3/src + * @build org.jemmy2ext.JemmyExt + * @build com.sun.swingset3.demos.list.ListDemo + * @run testng ListDemoTest + */ +public class ListDemoTest { + + private static final int CHECKBOX_COUNT = 50; + + @Test + public void test() throws Exception { + captureDebugInfoOnFail(() -> { + new ClassReference(ListDemo.class.getCanonicalName()).startApplication(); + + JFrameOperator frame = new JFrameOperator(DEMO_TITLE); + JListOperator listOp = new JListOperator(frame); + + // Check *NO* Prefix and Suffixes Marked + for (int i = 0; i < CHECKBOX_COUNT; i++) { + JCheckBoxOperator checkBox = getJCheckBoxOperator(frame, i); + checkBox.changeSelection(false); + } + System.out.println("######## Number of Items = " + listOp.getModel().getSize()); + assertEquals("Select None number of items is correct", 0, listOp.getModel().getSize()); + + // Check *ALL* Prefix and Suffixes Marked + for (int i = 0; i < CHECKBOX_COUNT; i++) { + JCheckBoxOperator checkBox = getJCheckBoxOperator(frame, i); + checkBox.changeSelection(true); + } + System.out.println("######## Number of Items = " + listOp.getModel().getSize()); + assertEquals("Select All number of items is correct", CHECKBOX_COUNT / 2 * CHECKBOX_COUNT / 2, listOp.getModel().getSize()); + + // Check *ALL* Prefix and *NO* Suffixes Marked + for (int i = 0; i < CHECKBOX_COUNT; i++) { + JCheckBoxOperator checkBox = getJCheckBoxOperator(frame, i); + if (i < CHECKBOX_COUNT / 2) { + checkBox.changeSelection(true); + } else { + checkBox.changeSelection(false); + } + } + System.out.println("######## Number of Items = " + listOp.getModel().getSize()); + assertEquals("Select All Prefixes and NO Suffixes number of items is correct", 0, listOp.getModel().getSize()); + + // Check *NO* Prefix and *ALL* Suffixes Marked + for (int i = 0; i < CHECKBOX_COUNT; i++) { + JCheckBoxOperator checkBox = getJCheckBoxOperator(frame, i); + if (i < CHECKBOX_COUNT / 2) { + checkBox.changeSelection(false); + } else { + checkBox.changeSelection(true); + } + } + System.out.println("######## Number of Items = " + listOp.getModel().getSize()); + assertEquals("Select NO Prefixes and All Suffixes number of items is correct", 0, listOp.getModel().getSize()); + }); + } + + private JCheckBoxOperator getJCheckBoxOperator(JFrameOperator frame, int index) { + + // We map first half of indexes to the Prefixes panel and the second half + // to the Suffixes panel + String labelText; + int subindex; + if (index < CHECKBOX_COUNT / 2) { + labelText = "Prefixes"; + subindex = index; + } else { + labelText = "Suffixes"; + subindex = index - CHECKBOX_COUNT / 2; + } + + return new JCheckBoxOperator(getLabeledContainerOperator(frame, labelText), subindex); + } + +} diff --git a/jdk/test/sanity/client/SwingSet/src/OptionPaneDemoTest.java b/jdk/test/sanity/client/SwingSet/src/OptionPaneDemoTest.java new file mode 100644 index 00000000000..2cecfe72637 --- /dev/null +++ b/jdk/test/sanity/client/SwingSet/src/OptionPaneDemoTest.java @@ -0,0 +1,339 @@ +/* + * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import com.sun.swingset3.demos.optionpane.OptionPaneDemo; +import static com.sun.swingset3.demos.optionpane.OptionPaneDemo.*; +import javax.swing.UIManager; +import static org.jemmy2ext.JemmyExt.*; +import static org.testng.AssertJUnit.*; +import org.testng.annotations.Test; +import org.netbeans.jemmy.ClassReference; +import org.netbeans.jemmy.operators.JButtonOperator; +import org.netbeans.jemmy.operators.JComboBoxOperator; +import org.netbeans.jemmy.operators.JDialogOperator; +import org.netbeans.jemmy.operators.JFrameOperator; +import org.netbeans.jemmy.operators.JLabelOperator; +import org.netbeans.jemmy.operators.JTextFieldOperator; + + +/* + * @test + * @key headful + * @summary Verifies SwingSet3 OptionPaneDemo page by opening all the dialogs + * and choosing different options in them. + * + * @library /sanity/client/lib/jemmy/src + * @library /sanity/client/lib/Jemmy2Ext/src + * @library /sanity/client/lib/SwingSet3/src + * @build org.jemmy2ext.JemmyExt + * @build com.sun.swingset3.demos.optionpane.OptionPaneDemo + * @run testng OptionPaneDemoTest + */ +public class OptionPaneDemoTest { + + public static final String SOME_TEXT_TO_TYPE = "I am some text"; + public static final String MESSAGE = UIManager.getString("OptionPane.messageDialogTitle"); + public static final String OK = "OK"; + public static final String CANCEL = "Cancel"; + public static final String INPUT = UIManager.getString("OptionPane.inputDialogTitle"); + public static final String TEXT_TO_TYPE = "Hooray! I'm a textField"; + public static final String NO = "No"; + public static final String YES = "Yes"; + public static final String SELECT_AN__OPTION = UIManager.getString("OptionPane.titleText"); + + @Test + public void test() throws Exception { + captureDebugInfoOnFail(() -> { + new ClassReference(OptionPaneDemo.class.getCanonicalName()).startApplication(); + + JFrameOperator frame = new JFrameOperator(DEMO_TITLE); + + showInputDialog(frame); + showWarningDialog(frame); + showMessageDialog(frame); + showComponentDialog(frame); + showConfirmationDialog(frame); + }); + } + + public void showInputDialog(JFrameOperator jfo) throws Exception { + // Cancel with text case + { + new JButtonOperator(jfo, INPUT_BUTTON).pushNoBlock(); + + JDialogOperator jdo = new JDialogOperator(INPUT); + JTextFieldOperator jto = new JTextFieldOperator(jdo); + jto.setText(SOME_TEXT_TO_TYPE); + + assertTrue("Show Input Dialog cancel w/ Text", jdo.isShowing()); + + new JButtonOperator(jdo, CANCEL).push(); + + assertFalse("Show Input Dialog cancel w/ Text", jdo.isShowing()); + } + + // Cancel with *NO* text case + { + new JButtonOperator(jfo, INPUT_BUTTON).pushNoBlock(); + + JDialogOperator jdo = new JDialogOperator(INPUT); + + assertTrue("Show Input Dialog cancel w/o Text", jdo.isShowing()); + + new JButtonOperator(jdo, CANCEL).push(); + + assertFalse("Show Input Dialog cancel w/o Text", jdo.isShowing()); + } + + // Text field has *NO* input + { + new JButtonOperator(jfo, INPUT_BUTTON).pushNoBlock(); + + JDialogOperator jdo = new JDialogOperator(INPUT); + + assertTrue("Show Input Dialog w/o Input", jdo.isShowing()); + + new JButtonOperator(jdo, OK).push(); + + assertFalse("Show Input Dialog w/o Input", jdo.isShowing()); + } + + // Text field has input + { + final String enteredText = "Rambo"; + + new JButtonOperator(jfo, INPUT_BUTTON).pushNoBlock(); + + JDialogOperator jdo = new JDialogOperator(INPUT); + JTextFieldOperator jto = new JTextFieldOperator(jdo); + jto.setText(enteredText); + new JButtonOperator(jdo, OK).pushNoBlock(); + + JDialogOperator jdo1 = new JDialogOperator(MESSAGE); + + assertTrue("Show Input Dialog w/ Input", jdo1.isShowing()); + + final String labelText = enteredText + INPUT_RESPONSE; + JLabelOperator jLabelOperator = new JLabelOperator(jdo1, labelText); + assertEquals("Text from the field made it into the dialog", labelText, jLabelOperator.getText()); + + new JButtonOperator(jdo1, OK).push(); + + assertFalse("Show Input Dialog w/ Input", jdo1.isShowing()); + } + } + + public void showWarningDialog(JFrameOperator jfo) throws Exception { + new JButtonOperator(jfo, WARNING_BUTTON).pushNoBlock(); + + JDialogOperator jdo = new JDialogOperator(WARNING_TITLE); + + assertTrue("Show Warning Dialog", jdo.isShowing()); + + new JButtonOperator(jdo, OK).push(); + + assertFalse("Show Warning Dialog", jdo.isShowing()); + } + + public void showMessageDialog(JFrameOperator jfo) throws Exception { + new JButtonOperator(jfo, MESSAGE_BUTTON).pushNoBlock(); + + JDialogOperator jdo = new JDialogOperator(MESSAGE); + + assertTrue("Show Message Dialog", jdo.isShowing()); + + new JButtonOperator(jdo, OK).push(); + + assertFalse("Show Message Dialog", jdo.isShowing()); + } + + public void showComponentDialog(JFrameOperator jfo) throws Exception { + // Case: Cancel + { + new JButtonOperator(jfo, COMPONENT_BUTTON).pushNoBlock(); + + JDialogOperator jdo = new JDialogOperator(COMPONENT_TITLE); + + assertTrue("Show Component Dialog Cancel Option", jdo.isShowing()); + + new JButtonOperator(jdo, COMPONENT_OP5).push(); + + assertFalse("Show Component Dialog Cancel Option", jdo.isShowing()); + } + + // Case: Yes option selected + { + new JButtonOperator(jfo, COMPONENT_BUTTON).pushNoBlock(); + + JDialogOperator jdo = new JDialogOperator(COMPONENT_TITLE); + new JButtonOperator(jdo, COMPONENT_OP1).pushNoBlock(); + + JDialogOperator jdo1 = new JDialogOperator(MESSAGE); + + assertTrue("Component Dialog Example Yes Option", jdo1.isShowing()); + + final String labelText = COMPONENT_R1; + JLabelOperator jLabelOperator = new JLabelOperator(jdo1, labelText); + assertEquals("Dialog contains appropriate text", labelText, jLabelOperator.getText()); + + new JButtonOperator(jdo1, OK).push(); + + assertFalse("Component Dialog Example Yes Option", jdo1.isShowing()); + } + + // Case: No option selected + { + new JButtonOperator(jfo, COMPONENT_BUTTON).pushNoBlock(); + + JDialogOperator jdo = new JDialogOperator(COMPONENT_TITLE); + new JButtonOperator(jdo, COMPONENT_OP2).pushNoBlock(); + + JDialogOperator jdo1 = new JDialogOperator(MESSAGE); + + assertTrue("Component Dialog Example No Option", jdo1.isShowing()); + + final String labelText = COMPONENT_R2; + JLabelOperator jLabelOperator = new JLabelOperator(jdo1, labelText); + assertEquals("Dialog contains appropriate text", labelText, jLabelOperator.getText()); + + new JButtonOperator(jdo1, OK).push(); + + assertFalse("Component Dialog Example No Option", jdo1.isShowing()); + } + + // Case: Maybe option selected + { + new JButtonOperator(jfo, COMPONENT_BUTTON).pushNoBlock(); + + JDialogOperator jdo = new JDialogOperator(COMPONENT_TITLE); + new JButtonOperator(jdo, COMPONENT_OP3).pushNoBlock(); + + JDialogOperator jdo1 = new JDialogOperator(MESSAGE); + + assertTrue("Component Dialog Maybe Yes Option", jdo1.isShowing()); + + final String labelText = COMPONENT_R3; + JLabelOperator jLabelOperator = new JLabelOperator(jdo1, labelText); + assertEquals("Dialog contains appropriate text", labelText, jLabelOperator.getText()); + + new JButtonOperator(jdo1, OK).push(); + + assertFalse("Component Dialog Maybe Yes Option", jdo1.isShowing()); + } + + // Case: Probably option selected + { + new JButtonOperator(jfo, COMPONENT_BUTTON).pushNoBlock(); + + JDialogOperator jdo = new JDialogOperator(COMPONENT_TITLE); + new JButtonOperator(jdo, COMPONENT_OP4).pushNoBlock(); + + JDialogOperator jdo1 = new JDialogOperator(MESSAGE); + + assertTrue("Component Dialog Example Probably Option", jdo1.isShowing()); + + final String labelText = COMPONENT_R4; + JLabelOperator jLabelOperator = new JLabelOperator(jdo1, labelText); + assertEquals("Dialog contains appropriate text", labelText, jLabelOperator.getText()); + + new JButtonOperator(jdo1, OK).push(); + + assertFalse("Component Dialog Example Probably Option", jdo1.isShowing()); + } + + // Case TextField and ComboBox functional + { + new JButtonOperator(jfo, COMPONENT_BUTTON).push(); + + JDialogOperator jdo = new JDialogOperator(COMPONENT_TITLE); + + JTextFieldOperator jto = new JTextFieldOperator(jdo); + jto.clearText(); + jto.typeText(TEXT_TO_TYPE); + + JComboBoxOperator jcbo = new JComboBoxOperator(jdo); + jcbo.selectItem(2); + + assertEquals("Show Component Dialog TextField", TEXT_TO_TYPE, jto.getText()); + assertEquals("Show Component Dialog ComboBox", 2, jcbo.getSelectedIndex()); + + new JButtonOperator(jdo, "cancel").push(); + } + } + + public void showConfirmationDialog(JFrameOperator jfo) throws Exception { + // Case: Yes option selected + { + new JButtonOperator(jfo, CONFIRM_BUTTON).pushNoBlock(); + + JDialogOperator jdo = new JDialogOperator(SELECT_AN__OPTION); + new JButtonOperator(jdo, YES).pushNoBlock(); + + JDialogOperator jdo1 = new JDialogOperator(MESSAGE); + + assertTrue("Show Confirmation Dialog Yes Option", jdo1.isShowing()); + + final String labelText = CONFIRM_YES; + JLabelOperator jLabelOperator = new JLabelOperator(jdo1, labelText); + assertEquals("Dialog contains appropriate text", labelText, jLabelOperator.getText()); + + new JButtonOperator(jdo1, OK).push(); + + assertFalse("Show Confirmation Dialog Yes Option", jdo1.isShowing()); + } + + // Case: No option selected + { + new JButtonOperator(jfo, CONFIRM_BUTTON).pushNoBlock(); + + JDialogOperator jdo = new JDialogOperator(SELECT_AN__OPTION); + new JButtonOperator(jdo, NO).pushNoBlock(); + + JDialogOperator jdo1 = new JDialogOperator(MESSAGE); + + assertTrue("Show Confirmation Dialog No Option", jdo1.isShowing()); + + final String labelText = CONFIRM_NO; + JLabelOperator jLabelOperator = new JLabelOperator(jdo1, labelText); + assertEquals("Dialog contains appropriate text", labelText, jLabelOperator.getText()); + + new JButtonOperator(jdo1, OK).push(); + + assertFalse("Show Confirmation Dialog No Option", jdo1.isShowing()); + } + + // Case: Cancel option selected + { + new JButtonOperator(jfo, CONFIRM_BUTTON).pushNoBlock(); + + JDialogOperator jdo = new JDialogOperator(SELECT_AN__OPTION); + + assertTrue("Show Confirmation Dialog Cancel Option", jdo.isShowing()); + + new JButtonOperator(jdo, CANCEL).push(); + + assertFalse("Show Confirmation Dialog Cancel Option", jdo.isShowing()); + } + } + +} diff --git a/jdk/test/sanity/client/SwingSet/src/ProgressBarDemoTest.java b/jdk/test/sanity/client/SwingSet/src/ProgressBarDemoTest.java new file mode 100644 index 00000000000..411d05cd1b7 --- /dev/null +++ b/jdk/test/sanity/client/SwingSet/src/ProgressBarDemoTest.java @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import com.sun.swingset3.demos.progressbar.ProgressBarDemo; +import static com.sun.swingset3.demos.progressbar.ProgressBarDemo.*; +import java.awt.Component; +import static org.testng.AssertJUnit.*; +import org.testng.annotations.Test; +import org.netbeans.jemmy.ClassReference; +import org.netbeans.jemmy.ComponentChooser; +import org.netbeans.jemmy.operators.JButtonOperator; +import org.netbeans.jemmy.operators.JFrameOperator; +import org.netbeans.jemmy.operators.JProgressBarOperator; +import static org.jemmy2ext.JemmyExt.captureDebugInfoOnFail; + +/* + * @test + * @key headful + * @summary Verifies SwingSet3 ProgressBarDemo page by pressing start and stop + * buttons and checking the progress bar and the buttons state. + * + * @library /sanity/client/lib/jemmy/src + * @library /sanity/client/lib/Jemmy2Ext/src + * @library /sanity/client/lib/SwingSet3/src + * @build org.jemmy2ext.JemmyExt + * @build com.sun.swingset3.demos.progressbar.ProgressBarDemo + * @run testng ProgressBarDemoTest + */ +public class ProgressBarDemoTest { + + @Test + public void test() throws Exception { + captureDebugInfoOnFail(() -> { + new ClassReference(ProgressBarDemo.class.getCanonicalName()).startApplication(); + + JFrameOperator frame = new JFrameOperator(DEMO_TITLE); + + JButtonOperator startButton = new JButtonOperator(frame, START_BUTTON); + JButtonOperator stopButton = new JButtonOperator(frame, STOP_BUTTON); + JProgressBarOperator jpbo = new JProgressBarOperator(frame); + + // Check that progress completes and corect enable/disable of start/stop buttons + checkCompleteProgress(frame, startButton, stopButton, jpbo); + + // Check progess bar progression and start/stop button disabled/enabled states + checkStartStop(frame, startButton, stopButton, jpbo); + }); + } + + // Check that progress completes and corect enable/disable of start/stop buttons + public void checkStartStop(JFrameOperator frame, JButtonOperator startButton, JButtonOperator stopButton, JProgressBarOperator progressBar) throws Exception { + int initialProgress = progressBar.getValue(); + System.out.println("initialProgress = " + initialProgress); + int maximum = progressBar.getMaximum(); + + startButton.pushNoBlock(); + + progressBar.waitState(new ComponentChooser() { + + @Override + public boolean checkComponent(Component comp) { + int value = progressBar.getValue(); + System.out.println("checkComponent1 value = " + value); + return value < maximum; + } + + @Override + public String getDescription() { + return "Progress < maximum (" + maximum + ")"; + } + }); + + stopButton.waitComponentEnabled(); + + progressBar.waitState(new ComponentChooser() { + + @Override + public boolean checkComponent(Component comp) { + int value = progressBar.getValue(); + System.out.println("checkComponent2 value = " + value); + return value > 0; + } + + @Override + public String getDescription() { + return "Progress > 0"; + } + }); + + int progress = progressBar.getValue(); + System.out.println("progress = " + progress); + + //Check that progress par has progressed and Start Button Disabled + assertTrue("Progress Bar Progressing (progress > 0, actual value: " + progress + ")", progress > 0); + assertFalse("Start Button Disabled", startButton.isEnabled()); + assertTrue("Stop Button Enabled", stopButton.isEnabled()); + + //Wait a liitle bit longer then Press stop get progress + progressBar.waitState(new ComponentChooser() { + + @Override + public boolean checkComponent(Component comp) { + return progressBar.getValue() > progress; + } + + @Override + public String getDescription() { + return "Progress > " + progress; + } + }); + + stopButton.pushNoBlock(); + + startButton.waitComponentEnabled(); + + int interimProgress = progressBar.getValue(); + + // Check that progress par has Stopped and Start Button Disabled + assertTrue("Progress Bar Stopped " + + "(interimProgress, actual value: " + interimProgress + " " + + "> progress, actual value: " + progress + ")", + interimProgress > progress); + assertTrue("Start Button Enabled", startButton.isEnabled()); + assertFalse("Stop Button Disabled", stopButton.isEnabled()); + } + + // Check progess bar progression and start/stop button disabled/enabled states + public void checkCompleteProgress(JFrameOperator frame, JButtonOperator startButton, JButtonOperator stopButton, JProgressBarOperator progressBar) throws Exception { + startButton.pushNoBlock(); + + progressBar.waitValue(progressBar.getMaximum()); + + startButton.waitComponentEnabled(); + + assertEquals("Complete Progress", progressBar.getMaximum(), progressBar.getValue()); + assertTrue("Start Button Enabled", startButton.isEnabled()); + assertFalse("Stop Button Disabled", stopButton.isEnabled()); + } + +} diff --git a/jdk/test/sanity/client/SwingSet/src/ScrollPaneDemoTest.java b/jdk/test/sanity/client/SwingSet/src/ScrollPaneDemoTest.java new file mode 100644 index 00000000000..bd88904f875 --- /dev/null +++ b/jdk/test/sanity/client/SwingSet/src/ScrollPaneDemoTest.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import com.sun.swingset3.demos.scrollpane.ScrollPaneDemo; +import static com.sun.swingset3.demos.scrollpane.ScrollPaneDemo.DEMO_TITLE; +import static org.testng.AssertJUnit.*; +import org.testng.annotations.Test; +import org.netbeans.jemmy.ClassReference; +import org.netbeans.jemmy.operators.JFrameOperator; +import org.netbeans.jemmy.operators.JScrollPaneOperator; +import static org.jemmy2ext.JemmyExt.captureDebugInfoOnFail; + +/* + * @test + * @key headful + * @summary Verifies SwingSet3 ScrollPaneDemo by scrolling to bottom, to top, + * to left and to right and checking scroll bar values. + * + * @library /sanity/client/lib/jemmy/src + * @library /sanity/client/lib/Jemmy2Ext/src + * @library /sanity/client/lib/SwingSet3/src + * @build org.jemmy2ext.JemmyExt + * @build com.sun.swingset3.demos.scrollpane.ScrollPaneDemo + * @run testng ScrollPaneDemoTest + */ +public class ScrollPaneDemoTest { + + @Test + public void test() throws Exception { + captureDebugInfoOnFail(() -> { + new ClassReference(ScrollPaneDemo.class.getName()).startApplication(); + + JFrameOperator frame = new JFrameOperator(DEMO_TITLE); + JScrollPaneOperator jspo = new JScrollPaneOperator(frame); + + // Set initial scrollbar positions + int initialVerticalValue = jspo.getVerticalScrollBar().getValue(); + int initialHorizontalValue = jspo.getHorizontalScrollBar().getValue(); + + System.out.println("Initial Vertical Value = " + jspo.getVerticalScrollBar().getValue()); + System.out.println("Initial HoriZontal Value = " + jspo.getHorizontalScrollBar().getValue()); + + // Check scroll to Bottom + { + jspo.scrollToBottom(); + int currentValue = jspo.getVerticalScrollBar().getValue(); + System.out.println("Final Value = " + currentValue); + assertTrue("Scroll to Bottom of Pane " + + "(initialVerticalValue, actual value: " + initialVerticalValue + " " + + "< currentValue, actual value = " + currentValue + ")", + initialVerticalValue < currentValue); + } + + // Check scroll to Top + { + jspo.scrollToTop(); + int currentValue = jspo.getVerticalScrollBar().getValue(); + System.out.println("Top Scroll Final Value = " + currentValue); + assertTrue("Scroll to Top of Pane " + + "(initialVerticalValue, actual value: " + initialVerticalValue + " " + + "> currentValue, actual value = " + currentValue + ")", + initialVerticalValue > currentValue); + } + + // Check scroll to Left + { + jspo.scrollToLeft(); + int currentValue = jspo.getHorizontalScrollBar().getValue(); + System.out.println("Scroll to Left Final Value = " + currentValue); + assertTrue("Scroll to Left of Pane " + + "(initialHorizontalValue, actual value: " + initialHorizontalValue + " " + + "> currentValue, actual value = " + currentValue + ")", + initialHorizontalValue > currentValue); + } + + // Check scroll to Right + { + jspo.scrollToRight(); + int currentValue = jspo.getHorizontalScrollBar().getValue(); + System.out.println("Scroll to Right Final Value = " + currentValue); + assertTrue("Scroll to Right of Pane " + + "(initialHorizontalValue, actual value: " + initialHorizontalValue + " " + + "< currentValue, actual value = " + currentValue + ")", + initialHorizontalValue < currentValue); + } + }); + } + +} diff --git a/jdk/test/sanity/client/SwingSet/src/SpinnerDemoTest.java b/jdk/test/sanity/client/SwingSet/src/SpinnerDemoTest.java new file mode 100644 index 00000000000..89585adb6ad --- /dev/null +++ b/jdk/test/sanity/client/SwingSet/src/SpinnerDemoTest.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import com.sun.swingset3.demos.spinner.SpinnerDemo; +import static com.sun.swingset3.demos.spinner.SpinnerDemo.DEMO_TITLE; +import java.text.DecimalFormat; +import static org.testng.AssertJUnit.*; +import org.testng.annotations.Test; +import org.netbeans.jemmy.ClassReference; +import org.netbeans.jemmy.operators.JFrameOperator; +import org.netbeans.jemmy.operators.JSpinnerOperator; +import org.netbeans.jemmy.operators.JTextFieldOperator; +import static org.jemmy2ext.JemmyExt.captureDebugInfoOnFail; + +/* + * @test + * @key headful + * @summary Verifies SwingSet3 SpinnerDemo by adjusting each spinner value via + * the spinner button and checking text field value. + * + * @library /sanity/client/lib/jemmy/src + * @library /sanity/client/lib/Jemmy2Ext/src + * @library /sanity/client/lib/SwingSet3/src + * @build org.jemmy2ext.JemmyExt + * @build com.sun.swingset3.demos.spinner.SpinnerDemo + * @run testng SpinnerDemoTest + */ +public class SpinnerDemoTest { + + private static final int SPINNERS_COUNT = 9; + private static final DecimalFormat decimalFormat = new DecimalFormat(); + + @Test + public void test() throws Exception { + captureDebugInfoOnFail(() -> { + new ClassReference(SpinnerDemo.class.getCanonicalName()).startApplication(); + + JFrameOperator frame = new JFrameOperator(DEMO_TITLE); + + // Check changing different spinners + for (int i = 0; i < SPINNERS_COUNT; i++) { + changeValues(frame, i); + } + }); + } + + private void changeValues(JFrameOperator jfo, int spinnerIndex) throws Exception { + JSpinnerOperator spinner = new JSpinnerOperator(jfo, spinnerIndex); + JTextFieldOperator jtfo = new JTextFieldOperator(spinner); + float originalFieldValue = decimalFormat.parse(jtfo.getText()).floatValue(); + float finalFieldValue; + + // increment by one the value via spinner + spinner.getIncreaseOperator().push(); + finalFieldValue = decimalFormat.parse(jtfo.getText()).floatValue(); + + // check that the value was increased + assertTrue("Increment Spinner " + spinner + + " (originalFieldValue, actual value: " + originalFieldValue + " " + + "< finalFieldValue, actual value = " + finalFieldValue + ")", + originalFieldValue < finalFieldValue); + + // decrease value via spinner + spinner.getDecreaseOperator().push(); + finalFieldValue = decimalFormat.parse(jtfo.getText()).floatValue(); + + // check that the value was decrimented + assertTrue("Decrement Spinner " + spinner + + " (originalFieldValue, actual value: " + originalFieldValue + " " + + ">= finalFieldValue, actual value = " + finalFieldValue + ")", + originalFieldValue >= finalFieldValue); + } + +} diff --git a/jdk/test/sanity/client/SwingSet/src/SplitPaneDemoTest.java b/jdk/test/sanity/client/SwingSet/src/SplitPaneDemoTest.java new file mode 100644 index 00000000000..2a242cc3f16 --- /dev/null +++ b/jdk/test/sanity/client/SwingSet/src/SplitPaneDemoTest.java @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import com.sun.swingset3.demos.splitpane.SplitPaneDemo; +import static com.sun.swingset3.demos.splitpane.SplitPaneDemo.*; +import java.awt.event.KeyEvent; +import javax.swing.JSplitPane; +import static org.testng.AssertJUnit.*; +import org.testng.annotations.Test; +import org.netbeans.jemmy.ClassReference; +import org.netbeans.jemmy.operators.JButtonOperator; +import org.netbeans.jemmy.operators.JCheckBoxOperator; +import org.netbeans.jemmy.operators.JFrameOperator; +import org.netbeans.jemmy.operators.JRadioButtonOperator; +import org.netbeans.jemmy.operators.JSplitPaneOperator; +import org.netbeans.jemmy.operators.JTextFieldOperator; +import static org.jemmy2ext.JemmyExt.*; + +/* + * @test + * @key headful + * @summary Verifies SwingSet3 SplitPaneDemo by performing OneClick expansion, + * changing size of the divier, moving the divider to different positions + * and changing the divider orientation. + * + * @library /sanity/client/lib/jemmy/src + * @library /sanity/client/lib/Jemmy2Ext/src + * @library /sanity/client/lib/SwingSet3/src + * @build org.jemmy2ext.JemmyExt + * @build com.sun.swingset3.demos.splitpane.SplitPaneDemo + * @run testng SplitPaneDemoTest + */ +public class SplitPaneDemoTest { + + @Test + public void test() throws Exception { + captureDebugInfoOnFail(() -> { + new ClassReference(SplitPaneDemo.class.getCanonicalName()).startApplication(); + + JFrameOperator frame = new JFrameOperator(DEMO_TITLE); + + JSplitPaneOperator splitPane = new JSplitPaneOperator(frame); + + // Toggle OneTouch Expandable + checkOneTouch(frame, splitPane, true); + checkOneTouch(frame, splitPane, false); + + // Check changing divider size to minimum and maximum values + changeDividerSize(frame, splitPane, 50); + changeDividerSize(frame, splitPane, 6); + + // Check moving the divider + checkDividerMoves(frame, splitPane, false); + checkDividerMoves(frame, splitPane, true); + + // Check different minumum Day/Night sizes + changeMinimumSizes(frame, splitPane, 100); + changeMinimumSizes(frame, splitPane, 0); + }); + } + + // Check for different day and night minimum size + public void changeMinimumSizes(JFrameOperator frame, JSplitPaneOperator splitPane, int amount) throws Exception { + for (String label : new String[]{FIRST_COMPONENT_MIN_SIZE, SECOND_COMPONENT_MIN_SIZE}) { + JTextFieldOperator size = new JTextFieldOperator(getLabeledContainerOperator(frame, label)); + size.enterText(Integer.toString(amount)); + size.pressKey(KeyEvent.VK_ENTER); + } + checkDividerMoves(frame, splitPane, false); + checkDividerMoves(frame, splitPane, true); + } + + // Check moving of divider + public void checkDividerMoves(JFrameOperator frame, JSplitPaneOperator splitPane, boolean isVertical) throws Exception { + if (isVertical) { + new JRadioButtonOperator(frame, VERTICAL_SPLIT).doClick(); + } else { + new JRadioButtonOperator(frame, HORIZONTAL_SPLIT).doClick(); + } + + splitPane.moveDivider(0.0); + assertEquals("Move Minimum, dividerLocation is at minimumDividerLocation", + splitPane.getMinimumDividerLocation(), splitPane.getDividerLocation()); + + // use getMaximumDividerLocation() to move divider to here because using proportion 1.0 does not work + splitPane.moveDivider(1.0); + + assertEquals("Move Maximum, dividerLocation is at maximumDividerLocation", + splitPane.getMaximumDividerLocation(), splitPane.getDividerLocation()); + + splitPane.moveDivider(0.5); + assertEquals("Move Middle, dividerLocation is at the artithmetic average of minimum and maximum DividerLocations", + (splitPane.getMaximumDividerLocation() + splitPane.getMinimumDividerLocation()) / 2, splitPane.getDividerLocation()); + } + + // Check changing the size of the divider + public void changeDividerSize(JFrameOperator frame, JSplitPaneOperator splitPane, int amount) throws Exception { + JTextFieldOperator size = new JTextFieldOperator(getLabeledContainerOperator(frame, DIVIDER_SIZE)); + size.clearText(); + size.typeText(Integer.toString(amount)); + size.pressKey(KeyEvent.VK_ENTER); + + assertEquals("Change Divider Size", amount, splitPane.getDividerSize()); + } + + public void checkOneTouch(JFrameOperator frame, JSplitPaneOperator splitPane, boolean oneTouch) throws Exception { + JCheckBoxOperator checkBox = new JCheckBoxOperator(frame, ONE_TOUCH_EXPANDABLE); + JButtonOperator buttonLeft = new JButtonOperator(splitPane.getDivider(), 0); + JButtonOperator buttonRight = new JButtonOperator(splitPane.getDivider(), 1); + int initDividerLocation = splitPane.getDividerLocation(); + + if (oneTouch) { + if (!checkBox.isSelected()) { + // uncheck + checkBox.doClick(); + } + + int left = getUIValue(splitPane, (JSplitPane sp) -> sp.getInsets().left); + System.out.println("left = " + left); + int right = getUIValue(splitPane, (JSplitPane sp) -> sp.getInsets().right); + System.out.println("right = " + right); + + // expand full left + buttonLeft.push(); + assertEquals("Expandable Left", left, splitPane.getDividerLocation()); + + // expand back from full left + buttonRight.push(); + assertEquals("Expandable Back to Original from Left", + initDividerLocation, splitPane.getDividerLocation()); + + // expand all the way right + buttonRight.push(); + assertEquals("Expandable Right", + splitPane.getWidth() - splitPane.getDividerSize() - right, + splitPane.getDividerLocation()); + + // Click to move back from right expansion + buttonLeft.push(); + assertEquals("Expandable Back to Original from Right", + initDividerLocation, splitPane.getDividerLocation()); + } + + // Test for case where one touch expandable is disabled + if (!oneTouch) { + if (checkBox.isSelected()) { + // uncheck + checkBox.doClick(); + } + assertFalse("One Touch Expandable Off", splitPane.isOneTouchExpandable()); + } + } + +} diff --git a/jdk/test/sanity/client/SwingSet/src/TabbedPaneDemoTest.java b/jdk/test/sanity/client/SwingSet/src/TabbedPaneDemoTest.java new file mode 100644 index 00000000000..d3d8d28245f --- /dev/null +++ b/jdk/test/sanity/client/SwingSet/src/TabbedPaneDemoTest.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import com.sun.swingset3.demos.tabbedpane.TabbedPaneDemo; +import static com.sun.swingset3.demos.tabbedpane.TabbedPaneDemo.*; +import static org.jemmy2ext.JemmyExt.getLabeledContainerOperator; +import static org.testng.AssertJUnit.*; +import org.testng.annotations.Test; +import org.netbeans.jemmy.ClassReference; +import org.netbeans.jemmy.operators.ContainerOperator; +import org.netbeans.jemmy.operators.JFrameOperator; +import org.netbeans.jemmy.operators.JRadioButtonOperator; +import org.netbeans.jemmy.operators.JTabbedPaneOperator; +import static org.jemmy2ext.JemmyExt.captureDebugInfoOnFail; + +/* + * @test + * @key headful + * @summary Verifies SwingSet3 TabbedPaneDemo by iterating through tab placement + * positions, opening each tab and verifying the the tab gets selected. + * + * @library /sanity/client/lib/jemmy/src + * @library /sanity/client/lib/Jemmy2Ext/src + * @library /sanity/client/lib/SwingSet3/src + * @build org.jemmy2ext.JemmyExt + * @build com.sun.swingset3.demos.tabbedpane.TabbedPaneDemo + * @run testng TabbedPaneDemoTest + */ +public class TabbedPaneDemoTest { + + @Test + public void test() throws Exception { + captureDebugInfoOnFail(() -> { + new ClassReference(TabbedPaneDemo.class.getCanonicalName()).startApplication(); + + JFrameOperator mainFrame = new JFrameOperator(DEMO_TITLE); + + for (String tp : new String[]{TOP, LEFT, BOTTOM, RIGHT}) { + testTabs(mainFrame, tp); + } + }); + } + + public void testTabs(JFrameOperator mainFrame, String tabPlacement) throws Exception { + ContainerOperator rbCont = getLabeledContainerOperator(mainFrame, TAB_PLACEMENT); + new JRadioButtonOperator(rbCont, tabPlacement).doClick(); + + final String[] tabTitles = new String[]{CAMILLE, MIRANDA, EWAN, BOUNCE}; + for (int i = 0; i < tabTitles.length; i++) { + String pageTitle = tabTitles[i]; + JTabbedPaneOperator tabOperator = new JTabbedPaneOperator(mainFrame); + tabOperator.selectPage(pageTitle); + + assertEquals("Selected tab is selected", i, tabOperator.getSelectedIndex()); + } + } + +} diff --git a/jdk/test/sanity/client/SwingSet/src/TextFieldDemoTest.java b/jdk/test/sanity/client/SwingSet/src/TextFieldDemoTest.java new file mode 100644 index 00000000000..4d38ce38a38 --- /dev/null +++ b/jdk/test/sanity/client/SwingSet/src/TextFieldDemoTest.java @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import com.sun.swingset3.demos.textfield.JHistoryTextField; +import com.sun.swingset3.demos.textfield.TextFieldDemo; +import static com.sun.swingset3.demos.textfield.TextFieldDemo.*; +import java.awt.Color; +import java.awt.event.KeyEvent; +import java.util.Calendar; +import java.util.Date; +import java.util.Locale; +import javax.swing.JFormattedTextField; +import static org.jemmy2ext.JemmyExt.*; +import static org.testng.AssertJUnit.*; +import org.testng.annotations.Test; +import org.netbeans.jemmy.ClassReference; +import org.netbeans.jemmy.QueueTool; +import org.netbeans.jemmy.operators.ContainerOperator; +import org.netbeans.jemmy.operators.JButtonOperator; +import org.netbeans.jemmy.operators.JFrameOperator; +import org.netbeans.jemmy.operators.JLabelOperator; +import org.netbeans.jemmy.operators.JPasswordFieldOperator; +import org.netbeans.jemmy.operators.JTextFieldOperator; + +/* + * @test + * @key headful + * @summary Verifies SwingSet3 TextFieldDemo by entering text in each field and + * checking that app reacts accordingly. + * + * @library /sanity/client/lib/jemmy/src + * @library /sanity/client/lib/Jemmy2Ext/src + * @library /sanity/client/lib/SwingSet3/src + * @build org.jemmy2ext.JemmyExt + * @build com.sun.swingset3.demos.textfield.TextFieldDemo + * @run testng TextFieldDemoTest + */ +public class TextFieldDemoTest { + + @Test + public void test() throws Exception { + captureDebugInfoOnFail(() -> { + new ClassReference(TextFieldDemo.class.getCanonicalName()).startApplication(); + + JFrameOperator frame = new JFrameOperator(DEMO_TITLE); + + historyTextField(frame); + dateTextField(frame); + passwordField(frame); + }); + } + + private void historyTextField(JFrameOperator jfo) throws Exception { + JTextFieldOperator jtfo = new JTextFieldOperator(jfo, new ByClassChooser(JHistoryTextField.class)); + jtfo.typeText("cat"); + + jtfo.pressKey(KeyEvent.VK_DOWN); + jtfo.pressKey(KeyEvent.VK_DOWN); + jtfo.pressKey(KeyEvent.VK_ENTER); + + final String expectedValue = "category"; + jtfo.waitText(expectedValue); + assertEquals("Select History Item", expectedValue, jtfo.getText()); + } + + public void dateTextField(JFrameOperator jfo) throws Exception { + JTextFieldOperator jtfo = new JTextFieldOperator(jfo, + new ByClassChooser(JFormattedTextField.class)); + ContainerOperator containerOperator = new ContainerOperator<>(jtfo.getParent()); + JButtonOperator jbo = new JButtonOperator(containerOperator, GO); + JLabelOperator dowLabel = new JLabelOperator(containerOperator); + Calendar calendar = Calendar.getInstance(Locale.ENGLISH); + + // Check default date Day of the Week + jbo.push(); + assertEquals("Default DOW", + calendar.getDisplayName(Calendar.DAY_OF_WEEK, Calendar.LONG, Locale.ENGLISH), + dowLabel.getText()); + + // Check Custom Day of the Week + calendar.set(2012, 9, 11); // Represents "Oct 11, 2012" + Date date = calendar.getTime(); + String dateString = jtfo.getQueueTool().invokeAndWait( + new QueueTool.QueueAction("Formatting the value using JFormattedTextField formatter") { + + @Override + public String launch() throws Exception { + return ((JFormattedTextField) jtfo.getSource()).getFormatter().valueToString(date); + } + }); + System.out.println("dateString = " + dateString); + jtfo.enterText(dateString); + + jbo.push(); + assertEquals("Custom DOW", "Thursday", dowLabel.getText()); + } + + public void passwordField(JFrameOperator jfo) throws Exception { + JPasswordFieldOperator password1 = new JPasswordFieldOperator(jfo, 0); + JPasswordFieldOperator password2 = new JPasswordFieldOperator(jfo, 1); + + password1.typeText("password"); + password2.typeText("password"); + + // Check Matching Passwords + assertEquals("Matching Passwords", Color.green, password1.getBackground()); + assertEquals("Matching Passwords", Color.green, password2.getBackground()); + + // Check non-matching passwords + password2.typeText("passwereertegrs"); + assertEquals("Non-Matching Passwords", Color.white, password1.getBackground()); + assertEquals("Non-Matching Passwords", Color.white, password2.getBackground()); + } + +} diff --git a/jdk/test/sanity/client/SwingSet/src/ToggleButtonDemoTest.java b/jdk/test/sanity/client/SwingSet/src/ToggleButtonDemoTest.java new file mode 100644 index 00000000000..c240540739e --- /dev/null +++ b/jdk/test/sanity/client/SwingSet/src/ToggleButtonDemoTest.java @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import com.sun.swingset3.DemoProperties; +import com.sun.swingset3.demos.togglebutton.DirectionPanel; +import com.sun.swingset3.demos.togglebutton.LayoutControlPanel; +import com.sun.swingset3.demos.togglebutton.ToggleButtonDemo; +import static com.sun.swingset3.demos.togglebutton.ToggleButtonDemo.*; +import java.util.function.BiFunction; +import org.jemmy2ext.JemmyExt.ByClassChooser; +import static org.jemmy2ext.JemmyExt.EXACT_STRING_COMPARATOR; +import static org.jemmy2ext.JemmyExt.getBorderTitledJPanelOperator; +import static org.jemmy2ext.JemmyExt.getLabeledContainerOperator; +import static org.testng.AssertJUnit.*; +import org.testng.annotations.Test; +import org.netbeans.jemmy.ClassReference; +import org.netbeans.jemmy.ComponentChooser; +import org.netbeans.jemmy.operators.ContainerOperator; +import org.netbeans.jemmy.operators.JCheckBoxOperator; +import org.netbeans.jemmy.operators.JFrameOperator; +import org.netbeans.jemmy.operators.JRadioButtonOperator; +import org.netbeans.jemmy.operators.JTabbedPaneOperator; +import static org.jemmy2ext.JemmyExt.captureDebugInfoOnFail; + +/* + * @test + * @key headful + * @summary Verifies SwingSet3 ToggleButtonDemo by toggling each radio button, + * each checkbox and each location of the direction dial toggle. + * It verifies initial selected values for all the elements and checks + * that those change upon clicking. When toggling radio buttons it + * verifies that other radio buttons in the same group are not longer + * selected. + * + * @library /sanity/client/lib/jemmy/src + * @library /sanity/client/lib/Jemmy2Ext/src + * @library /sanity/client/lib/SwingSet3/src + * @build org.jemmy2ext.JemmyExt + * @build com.sun.swingset3.demos.togglebutton.ToggleButtonDemo + * @run testng ToggleButtonDemoTest + */ +public class ToggleButtonDemoTest { + + @Test + public void test() throws Exception { + captureDebugInfoOnFail(() -> { + new ClassReference(ToggleButtonDemo.class.getCanonicalName()).startApplication(); + + JFrameOperator mainFrame = new JFrameOperator(ToggleButtonDemo.class.getAnnotation(DemoProperties.class).value()); + JTabbedPaneOperator tabPane = new JTabbedPaneOperator(mainFrame); + + // Radio Button Toggles + testRadioButtons(getBorderTitledJPanelOperator(mainFrame, TEXT_RADIO_BUTTONS), 3, null); + testRadioButtons(getBorderTitledJPanelOperator(mainFrame, IMAGE_RADIO_BUTTONS), 3, null); + testRadioButtons(getLabeledContainerOperator(mainFrame, PAD_AMOUNT), 3, (t, i) -> DEFAULT.equals(t)); + + // switch to the Check Boxes Tab + tabPane.selectPage(CHECK_BOXES); + + // Check Box Toggles + ContainerOperator textCheckBoxesJPanel = getBorderTitledJPanelOperator(mainFrame, TEXT_CHECKBOXES); + testCheckBox(textCheckBoxesJPanel, CHECK1, false); + testCheckBox(textCheckBoxesJPanel, CHECK2, false); + testCheckBox(textCheckBoxesJPanel, CHECK3, false); + + ContainerOperator imageCheckBoxesJPanel = getBorderTitledJPanelOperator(mainFrame, IMAGE_CHECKBOXES); + testCheckBox(imageCheckBoxesJPanel, CHECK1, false); + testCheckBox(imageCheckBoxesJPanel, CHECK2, false); + testCheckBox(imageCheckBoxesJPanel, CHECK3, false); + + ContainerOperator displayOptionsContainer = getLabeledContainerOperator(mainFrame, DISPLAY_OPTIONS); + testCheckBox(displayOptionsContainer, PAINT_BORDER, false); + testCheckBox(displayOptionsContainer, PAINT_FOCUS, true); + testCheckBox(displayOptionsContainer, ENABLED, true); + testCheckBox(displayOptionsContainer, CONTENT_FILLED, true); + + // Direction Button Toggles + testToggleButtons(mainFrame); + }); + } + + /** + * The interface is invoked for each radio button providing its name and + * index and it should return whether the radio button has to be selected. + */ + private static interface SelectedRadioButton extends BiFunction { + } + + /** + * Tests a group of radio buttons + * + * @param parent container containing the buttons + * @param radioButtonCount number of radio buttons + * @param selectedRadioButton initial selected radio button + */ + private void testRadioButtons(ContainerOperator parent, int radioButtonCount, SelectedRadioButton selectedRadioButton) { + JRadioButtonOperator[] jrbo = new JRadioButtonOperator[radioButtonCount]; + for (int i = 0; i < radioButtonCount; i++) { + jrbo[i] = new JRadioButtonOperator(parent, i); + if (selectedRadioButton != null && selectedRadioButton.apply(jrbo[i].getText(), i)) { + assertTrue("Radio Button " + i + " is initially selected", jrbo[i].isSelected()); + } else { + assertFalse("Radio Button " + i + " is initially not selected", jrbo[i].isSelected()); + } + } + + for (int i = 0; i < radioButtonCount; i++) { + jrbo[i].doClick(); + assertTrue("Radio Button " + i + " is selected", jrbo[i].isSelected()); + + for (int j = 0; j < radioButtonCount; j++) { + if (i != j) { + assertFalse("Radio Button " + j + " is not selected", jrbo[j].isSelected()); + } + } + } + } + + /** + * Will change the state of the CheckBox then change back to initial state. + * + * @param parent + * @param text + * @param expectedValue + * @throws Exception + */ + private void testCheckBox(ContainerOperator parent, String text, boolean expectedValue) { + + parent.setComparator(EXACT_STRING_COMPARATOR); + JCheckBoxOperator jcbo = new JCheckBoxOperator(parent, text); + assertEquals("Initial selection state of the checkbox '" + text + "'", expectedValue, jcbo.isSelected()); + + // click check box (toggle the state) + jcbo.doClick(); + assertEquals("Selection state of the checkbox '" + text + "' after click", !expectedValue, jcbo.isSelected()); + if (jcbo.isSelected()) { + // toggle back to not-selected state + jcbo.doClick(); + assertFalse("Check Box '" + text + "' is not selected", jcbo.isSelected()); + } else { + // toggle back to selected state + jcbo.doClick(); + + assertTrue("Check Box '" + text + "' is selected", jcbo.isSelected()); + } + } + + + /* + * testDirectionRadioButtons(JFrameOperator) will toggle each position of + * the direction radio button panels + */ + private void testToggleButtons(JFrameOperator jfo) { + ComponentChooser directionPanelChooser = new ByClassChooser(DirectionPanel.class); + + String text_Position = LayoutControlPanel.TEXT_POSITION; + ContainerOperator textPositionContainer = getLabeledContainerOperator(jfo, text_Position); + ContainerOperator directionPanelOperator = new ContainerOperator<>(textPositionContainer, directionPanelChooser); + testRadioButtons(directionPanelOperator, 9, (t, i) -> i == 5); + + // Unfortunately, both directionPanels are in the same parent container + // so we have to use indexes here. + // There is no guarantee that if the UI changes, the code would be still + // valid in terms of which directionPanel is checked first. However, it + // does guarantee that two different directionPanels are checked. + String content_Alignment = LayoutControlPanel.CONTENT_ALIGNMENT; + ContainerOperator contentAlignmentContainer = getLabeledContainerOperator(jfo, content_Alignment); + ContainerOperator directionPanelOperator2 = new ContainerOperator<>(contentAlignmentContainer, directionPanelChooser, + contentAlignmentContainer.getSource() == textPositionContainer.getSource() ? 1 : 0); + testRadioButtons(directionPanelOperator2, 9, (t, i) -> i == 4); + + } + +} diff --git a/jdk/test/sanity/client/SwingSet/src/TreeDemoTest.java b/jdk/test/sanity/client/SwingSet/src/TreeDemoTest.java new file mode 100644 index 00000000000..b558d6fb96c --- /dev/null +++ b/jdk/test/sanity/client/SwingSet/src/TreeDemoTest.java @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import com.sun.swingset3.demos.tree.TreeDemo; +import static com.sun.swingset3.demos.tree.TreeDemo.DEMO_TITLE; +import javax.swing.tree.TreePath; +import static org.testng.AssertJUnit.*; +import org.testng.annotations.Test; +import org.netbeans.jemmy.ClassReference; +import org.netbeans.jemmy.operators.JFrameOperator; +import org.netbeans.jemmy.operators.JTreeOperator; +import static org.jemmy2ext.JemmyExt.captureDebugInfoOnFail; + +/* + * @test + * @key headful + * @summary Verifies SwingSet3 TreeDemo by expanding all collapsed nodes in the + * tree and then collapsing all the expanded nodes in the tree. It + * verifies the number of nodes expanded, number of nodes collapsed and + * number of rows in the tree in the begininng, after expanding and + * after collapsing the nodes. It also checks that the tree grows + * vertically (as ScrollPane allows it). + * + * @library /sanity/client/lib/jemmy/src + * @library /sanity/client/lib/Jemmy2Ext/src + * @library /sanity/client/lib/SwingSet3/src + * @build org.jemmy2ext.JemmyExt + * @build com.sun.swingset3.demos.tree.TreeDemo + * @run testng TreeDemoTest + */ +public class TreeDemoTest { + + @Test + public void test() throws Exception { + captureDebugInfoOnFail(() -> { + new ClassReference(TreeDemo.class.getCanonicalName()).startApplication(); + + JFrameOperator frame = new JFrameOperator(DEMO_TITLE); + + JTreeOperator tree = new JTreeOperator(frame); + + assertEquals("Initial number of rows in the tree", 4, tree.getRowCount()); + + int initialTreeHeight = tree.getHeight(); + + // expand all nodes + int expandsCount = 0; + for (int i = 0; i < tree.getRowCount(); i++) { + TreePath tp = tree.getPathForRow(i); + if (tree.getChildCount(tp) > 0 && !tree.isExpanded(tp)) { + tree.expandRow(i); + expandsCount++; + } + } + + assertEquals("Number of rows expanded", 75, expandsCount); + assertEquals("Number of rows in the tree after expanding all of them", + 616, tree.getRowCount()); + + int expandedTreeHeight = tree.getHeight(); + assertTrue("Expanded tree height has increased, current " + + expandedTreeHeight + " > initial " + initialTreeHeight, + expandedTreeHeight > initialTreeHeight); + + // collapse all nodes + int collapsesCount = 0; + for (int i = tree.getRowCount() - 1; i >= 0; i--) { + TreePath tp = tree.getPathForRow(i); + if (tree.getChildCount(tp) > 0 && tree.isExpanded(tp)) { + tree.collapseRow(i); + collapsesCount++; + } + } + + assertEquals("Number of rows collapsed", 76, collapsesCount); + assertEquals("Number of rows in the tree after collapsing all of them", + 1, tree.getRowCount()); + + int collapsedTreeHeight = tree.getHeight(); + assertTrue("Collpased tree height is not longer than initial, " + + "current " + collapsedTreeHeight + " <= initial " + + initialTreeHeight, + collapsedTreeHeight <= initialTreeHeight); + + }); + } + +} diff --git a/jdk/test/sanity/client/SwingSet/src/WindowDemoTest.java b/jdk/test/sanity/client/SwingSet/src/WindowDemoTest.java new file mode 100644 index 00000000000..ef0fb850360 --- /dev/null +++ b/jdk/test/sanity/client/SwingSet/src/WindowDemoTest.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import com.sun.swingset3.demos.window.WindowDemo; +import static com.sun.swingset3.demos.window.WindowDemo.*; +import static org.jemmy2ext.JemmyExt.*; +import static org.testng.AssertJUnit.*; +import org.testng.annotations.Test; +import org.netbeans.jemmy.ClassReference; +import org.netbeans.jemmy.operators.JButtonOperator; +import org.netbeans.jemmy.operators.JFrameOperator; +import org.netbeans.jemmy.operators.JLabelOperator; +import org.netbeans.jemmy.operators.WindowOperator; + +/* + * @test + * @key headful + * @summary Verifies SwingSet3 WindowDemo by checking that separate JWindow is + * shown, it contains predefined label and no new windows are opened + * when the "Show JWindow..." button is clicked. + * + * @library /sanity/client/lib/jemmy/src + * @library /sanity/client/lib/Jemmy2Ext/src + * @library /sanity/client/lib/SwingSet3/src + * @build org.jemmy2ext.JemmyExt + * @build com.sun.swingset3.demos.window.WindowDemo + * @run testng WindowDemoTest + */ +public class WindowDemoTest { + + @Test + public void test() throws Exception { + captureDebugInfoOnFail(() -> { + new ClassReference(WindowDemo.class.getCanonicalName()).startApplication(); + + JFrameOperator frame = new JFrameOperator(); + + assertEquals("Only one JWindow is shown", 1, getJWindowCount()); + + WindowOperator window = new WindowOperator(getJWindow()); + + assertTrue("JFrame is showing", frame.isShowing()); + assertFalse("JFrame is not iconified", isIconified(frame)); + assertTrue("JWindow is showing", window.isShowing()); + + final String labelText = I_HAVE_NO_SYSTEM_BORDER; + JLabelOperator jLabelOperator = new JLabelOperator(window, labelText); + assertEquals("JWindow contains the label with corresponding text", labelText, jLabelOperator.getText()); + + new JButtonOperator(frame, SHOW_J_WINDOW).push(); + + assertEquals("Only one JWindow is shown", 1, getJWindowCount()); + }); + } + +} diff --git a/jdk/test/sanity/client/TEST.ROOT.template b/jdk/test/sanity/client/TEST.ROOT.template new file mode 100644 index 00000000000..6881809d9f8 --- /dev/null +++ b/jdk/test/sanity/client/TEST.ROOT.template @@ -0,0 +1,24 @@ +# This file identifies the root of the test-suite hierarchy. +# It also contains test-suite configuration information. + +# The list of keywords supported in the entire test suite. The +# "intermittent" keyword marks tests known to fail intermittently. +# The "randomness" keyword marks tests using randomness with test +# cases differing from run to run. (A test using a fixed random seed +# would not count as "randomness" by this definition.) Extra care +# should be taken to handle test failures of intermittent or +# randomness tests. +# +# A "headful" test requires a graphical environment to meaningfully +# run. Tests that are not headful are "headless." + +keys=screenshots + +# Tests that must run in othervm mode +othervm.dirs=sanity/client/SwingSet + +# Tests that cannot run concurrently +exclusiveAccess.dirs=sanity/client/SwingSet + +# Tests using jtreg 4.1 b13 features +requiredVersion=4.1 b13 diff --git a/jdk/test/sanity/client/TEST.properties b/jdk/test/sanity/client/TEST.properties new file mode 100644 index 00000000000..343e80e76f2 --- /dev/null +++ b/jdk/test/sanity/client/TEST.properties @@ -0,0 +1,14 @@ +# This file contains test-suite configuration information related to this portion +# of the test suite. +# +# A "screenshots" test requires a 100% headful graphical environment +# as they rely on Robot to take screenshots and move mouse. + +# The list of keywords supported in this part of the test suite +keys=screenshots + +# Tests that cannot run concurrently +exclusiveAccess.dirs=SwingSet + +# Tests require jtreg 4.1 b13 features +#requiredVersion=4.1 b13 diff --git a/jdk/test/sanity/client/lib/Jemmy2Ext/src/org/jemmy2ext/JemmyExt.java b/jdk/test/sanity/client/lib/Jemmy2Ext/src/org/jemmy2ext/JemmyExt.java new file mode 100644 index 00000000000..fe0431b684e --- /dev/null +++ b/jdk/test/sanity/client/lib/Jemmy2Ext/src/org/jemmy2ext/JemmyExt.java @@ -0,0 +1,614 @@ +/* + * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.jemmy2ext; + +import java.awt.Component; +import java.awt.EventQueue; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Rectangle; +import java.awt.Robot; +import java.awt.Window; +import java.awt.image.BufferedImage; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.function.Function; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.stream.IntStream; +import javax.imageio.ImageIO; +import javax.swing.JButton; +import javax.swing.JComponent; +import javax.swing.JPanel; +import javax.swing.JWindow; +import javax.swing.border.Border; +import javax.swing.border.CompoundBorder; +import javax.swing.border.TitledBorder; +import org.netbeans.jemmy.ComponentChooser; +import org.netbeans.jemmy.DefaultCharBindingMap; +import org.netbeans.jemmy.QueueTool; +import org.netbeans.jemmy.TimeoutExpiredException; +import org.netbeans.jemmy.Waitable; +import org.netbeans.jemmy.Waiter; +import org.netbeans.jemmy.drivers.scrolling.JSpinnerDriver; +import org.netbeans.jemmy.image.StrictImageComparator; +import org.netbeans.jemmy.operators.ComponentOperator; +import org.netbeans.jemmy.operators.ContainerOperator; +import org.netbeans.jemmy.operators.FrameOperator; +import org.netbeans.jemmy.operators.JButtonOperator; +import org.netbeans.jemmy.operators.JFrameOperator; +import org.netbeans.jemmy.operators.JLabelOperator; +import org.netbeans.jemmy.operators.Operator; +import org.netbeans.jemmy.util.Dumper; +import org.netbeans.jemmy.util.PNGEncoder; +import static org.testng.AssertJUnit.*; + +/** + * This class solves two tasks: 1. It adds functionality that is missing in + * Jemmy 2. It references all the Jemmy API that is needed by tests so that they + * can just @build JemmyExt class and do not worry about Jemmy + * + * @author akouznet + */ +public class JemmyExt { + + /** + * Statically referencing all the classes that are needed by tests so that + * they're compiled by jtreg + */ + static final Class[] DEPENDENCIES = { + JSpinnerDriver.class, + DefaultCharBindingMap.class + }; + + public static void assertNotBlack(BufferedImage image) { + int w = image.getWidth(); + int h = image.getHeight(); + try { + assertFalse("All pixels are not black", IntStream.range(0, w).parallel().allMatch(x + -> IntStream.range(0, h).allMatch(y -> (image.getRGB(x, y) & 0xffffff) == 0) + )); + } catch (Throwable t) { + save(image, "allPixelsAreBlack.png"); + throw t; + } + } + + public static void waitArmed(JButtonOperator button) { + button.waitState(new ComponentChooser() { + + @Override + public boolean checkComponent(Component comp) { + return isArmed(button); + } + + @Override + public String getDescription() { + return "Button is armed"; + } + }); + } + + public static boolean isArmed(JButtonOperator button) { + return button.getQueueTool().invokeSmoothly(new QueueTool.QueueAction("getModel().isArmed()") { + + @Override + public Boolean launch() throws Exception { + return ((JButton) button.getSource()).getModel().isArmed(); + } + }); + } + + public static void waitPressed(JButtonOperator button) { + button.waitState(new ComponentChooser() { + + @Override + public boolean checkComponent(Component comp) { + return isPressed(button); + } + + @Override + public String getDescription() { + return "Button is pressed"; + } + }); + } + + public static boolean isPressed(JButtonOperator button) { + return button.getQueueTool().invokeSmoothly(new QueueTool.QueueAction("getModel().isPressed()") { + + @Override + public Boolean launch() throws Exception { + return ((JButton) button.getSource()).getModel().isPressed(); + } + }); + } + + public static void assertEquals(String string, StrictImageComparator comparator, BufferedImage expected, BufferedImage actual) { + try { + assertTrue(string, comparator.compare(expected, actual)); + } catch (Error err) { + save(expected, "expected.png"); + save(actual, "actual.png"); + throw err; + } + } + + public static void assertNotEquals(String string, StrictImageComparator comparator, BufferedImage notExpected, BufferedImage actual) { + try { + assertFalse(string, comparator.compare(notExpected, actual)); + } catch (Error err) { + save(notExpected, "notExpected.png"); + save(actual, "actual.png"); + throw err; + } + } + + public static void save(BufferedImage image, String filename) { + String filepath = filename; + try { + filepath = new File(filename).getCanonicalPath(); + System.out.println("Saving screenshot to " + filepath); + BufferedOutputStream file = new BufferedOutputStream(new FileOutputStream(filepath)); + new PNGEncoder(file, PNGEncoder.COLOR_MODE).encode(image); + } catch (IOException ioe) { + throw new RuntimeException("Failed to save image to " + filepath, ioe); + } + } + + public static void waitImageIsStill(Robot rob, ComponentOperator operator) { + operator.waitState(new ComponentChooser() { + + private BufferedImage previousImage = null; + private int index = 0; + private final StrictImageComparator sComparator = new StrictImageComparator(); + + @Override + public boolean checkComponent(Component comp) { + BufferedImage currentImage = capture(rob, operator); + save(currentImage, "waitImageIsStill" + index + ".png"); + index++; + boolean compareResult = previousImage == null ? false : sComparator.compare(currentImage, previousImage); + previousImage = currentImage; + return compareResult; + } + + @Override + public String getDescription() { + return "Image of " + operator + " is still"; + } + }); + } + + private static class ThrowableHolder { + + volatile Throwable t; + } + + public static void waitFor(String description, RunnableWithException r) throws Exception { + Waiter waiter = new Waiter<>(new Waitable() { + + @Override + public Boolean actionProduced(ThrowableHolder obj) { + try { + r.run(); + return true; + } catch (Throwable t) { + obj.t = t; + return null; + } + } + + @Override + public String getDescription() { + return description; + } + }); + ThrowableHolder th = new ThrowableHolder(); + try { + waiter.waitAction(th); + } catch (TimeoutExpiredException tee) { + Throwable t = th.t; + if (t != null) { + t.addSuppressed(tee); + if (t instanceof Exception) { + throw (Exception) t; + } else if (t instanceof Error) { + throw (Error) t; + } else if (t instanceof RuntimeException) { + throw (RuntimeException) t; + } else { + throw new IllegalStateException("Unexpected exception type", t); + } + } + } + } + + public static BufferedImage capture(Robot rob, ComponentOperator operator) { + Rectangle boundary = new Rectangle(operator.getLocationOnScreen(), + operator.getSize()); + return rob.createScreenCapture(boundary); + } + + /** + * Wraps the test code so that in case of any failure as much information as + * possible is captured + * + * @param r test code Runnable + * @throws Exception whatever exception the test may throw + */ + public static void captureDebugInfoOnFail(RunnableWithException r) throws Exception { + // TODO: Remove this once https://bugs.openjdk.java.net/browse/JDK-8151671 is fixed + try { + r.run(); + System.out.println("TEST PASSED"); + } catch (Throwable t) { + captureAll(); + throw t; + } + } + + /** + * This is a helper class which allows to catch throwables thrown in other + * threads and throw them in the main test thread + */ + public static class MultiThreadedTryCatch { + + private final List throwables + = Collections.synchronizedList(new ArrayList<>()); + + /** + * Throws registered throwables. If the list of the registered + * throwables is not empty, it re-throws the first throwable in the list + * adding all others into its suppressed list. Can be used in any + * thread. + * + * @throws Exception + */ + public void throwRegistered() throws Exception { + Throwable root = null; + synchronized (throwables) { + if (!throwables.isEmpty()) { + root = throwables.remove(0); + while (!throwables.isEmpty()) { + root.addSuppressed(throwables.remove(0)); + } + } + } + if (root != null) { + if (root instanceof Error) { + throw (Error) root; + } else if (root instanceof Exception) { + throw (Exception) root; + } else { + throw new AssertionError("Unexpected exception type: " + root.getClass() + " (" + root + ")"); + } + } + } + + /** + * Registers a throwable and adds it to the list of throwables. Can be + * used in any thread. + * + * @param t + */ + public void register(Throwable t) { + t.printStackTrace(); + throwables.add(t); + } + + /** + * Registers a throwable and adds it as the first item of the list of + * catched throwables. + * + * @param t + */ + public void registerRoot(Throwable t) { + t.printStackTrace(); + throwables.add(0, t); + } + } + + /** + * Trying to capture as much information as possible. Currently it includes + * full dump and a screenshot of the whole screen. + */ + public static void captureAll() { + PNGEncoder.captureScreen("failure.png", PNGEncoder.COLOR_MODE); + try { + Dumper.dumpAll("dumpAll.xml"); + } catch (FileNotFoundException ex) { + Logger.getLogger(JemmyExt.class.getName()).log(Level.SEVERE, null, ex); + } + captureWindows(); + } + + /** + * Captures each showing window image using Window.paint() method. + */ + private static void captureWindows() { + try { + EventQueue.invokeAndWait(() -> { + Window[] windows = Window.getWindows(); + int index = 0; + for (Window w : windows) { + if (!w.isShowing()) { + continue; + } + BufferedImage img = new BufferedImage(w.getWidth(), w.getHeight(), BufferedImage.TYPE_INT_ARGB); + Graphics g = img.getGraphics(); + w.paint(g); + g.dispose(); + + try { + ImageIO.write(img, "png", new File("window" + index++ + ".png")); + } catch (IOException e) { + e.printStackTrace(); + } + } + }); + } catch (InterruptedException | InvocationTargetException ex) { + Logger.getLogger(JemmyExt.class.getName()).log(Level.SEVERE, null, ex); + } + } + + public static interface RunnableWithException { + + public void run() throws Exception; + } + + public static void waitIsFocused(JFrameOperator jfo) { + jfo.waitState(new ComponentChooser() { + + @Override + public boolean checkComponent(Component comp) { + return jfo.isFocused(); + } + + @Override + public String getDescription() { + return "JFrame is focused"; + } + }); + } + + public static int getJWindowCount() { + return new QueueTool().invokeAndWait(new QueueTool.QueueAction(null) { + + @Override + public Integer launch() throws Exception { + Window[] windows = Window.getWindows(); + int windowCount = 0; + for (Window w : windows) { + if (w.getClass().equals(JWindow.class)) { + windowCount++; + } + } + return windowCount; + } + }); + } + + public static JWindow getJWindow() { + return getJWindow(0); + } + + public static JWindow getJWindow(int index) { + return new QueueTool().invokeAndWait(new QueueTool.QueueAction(null) { + + @Override + public JWindow launch() throws Exception { + Window[] windows = Window.getWindows(); + int windowIndex = 0; + for (Window w : windows) { + if (w.getClass().equals(JWindow.class)) { + if (windowIndex == index) { + return (JWindow) w; + } + windowIndex++; + } + } + return null; + } + }); + } + + public static boolean isIconified(FrameOperator frameOperator) { + return frameOperator.getQueueTool().invokeAndWait(new QueueTool.QueueAction("Frame is iconified") { + + @Override + public Boolean launch() throws Exception { + return (((Frame) frameOperator.getSource()).getState() & Frame.ICONIFIED) != 0; + } + }); + } + + public static final Operator.DefaultStringComparator EXACT_STRING_COMPARATOR + = new Operator.DefaultStringComparator(true, true); + + /** + * Finds a label with the exact labelText and returns the operator for its + * parent container. + * + * @param container + * @param labelText + * @return + */ + public static ContainerOperator getLabeledContainerOperator(ContainerOperator container, String labelText) { + + container.setComparator(EXACT_STRING_COMPARATOR); + + JLabelOperator jLabelOperator = new JLabelOperator(container, labelText); + + assert labelText.equals(jLabelOperator.getText()); + + return new ContainerOperator<>(jLabelOperator.getParent()); + } + + /** + * Finds a JPanel with exact title text. + * + * @param container + * @param titleText + * @return + */ + public static ContainerOperator getBorderTitledJPanelOperator(ContainerOperator container, String titleText) { + return new ContainerOperator<>(container, new JPanelByBorderTitleFinder(titleText, EXACT_STRING_COMPARATOR)); + } + + public static final QueueTool QUEUE_TOOL = new QueueTool(); + + /** + * Allows to find JPanel by the title text in its border. + */ + public static class JPanelByBorderTitleFinder implements ComponentChooser { + + String titleText; + Operator.StringComparator comparator; + + /** + * @param titleText title text pattern + * @param comparator specifies string comparison algorithm. + */ + public JPanelByBorderTitleFinder(String titleText, Operator.StringComparator comparator) { + this.titleText = titleText; + this.comparator = comparator; + } + + /** + * @param titleText title text pattern + */ + public JPanelByBorderTitleFinder(String titleText) { + this(titleText, Operator.getDefaultStringComparator()); + } + + @Override + public boolean checkComponent(Component comp) { + assert EventQueue.isDispatchThread(); + if (comp instanceof JPanel) { + return checkBorder(((JPanel) comp).getBorder()); + } + return false; + } + + public boolean checkBorder(Border border) { + if (border instanceof TitledBorder) { + String title = ((TitledBorder) border).getTitle(); + return comparator.equals(title, titleText); + } else if (border instanceof CompoundBorder) { + CompoundBorder compoundBorder = (CompoundBorder) border; + return checkBorder(compoundBorder.getInsideBorder()) || checkBorder(compoundBorder.getOutsideBorder()); + } else { + return false; + } + } + + @Override + public String getDescription() { + return ("JPanel with border title text \"" + titleText + "\" with comparator " + comparator); + } + } + + public static class ByClassSimpleNameChooser implements ComponentChooser { + + private final String className; + + public ByClassSimpleNameChooser(String className) { + this.className = className; + } + + @Override + public boolean checkComponent(Component comp) { + return comp.getClass().getSimpleName().equals(className); + } + + @Override + public String getDescription() { + return "Component with the simple class name of " + className; + } + + } + + public static class ByClassChooser implements ComponentChooser { + + private final Class clazz; + + public ByClassChooser(Class clazz) { + this.clazz = clazz; + } + + @Override + public boolean checkComponent(Component comp) { + return comp.getClass().equals(clazz); + } + + @Override + public String getDescription() { + return "Component with the class of " + clazz; + } + + } + + public static class ByToolTipChooser implements ComponentChooser { + + private final String tooltip; + + public ByToolTipChooser(String tooltip) { + if (tooltip == null) { + throw new NullPointerException("Tooltip cannot be null"); + } + this.tooltip = tooltip; + } + + @Override + public boolean checkComponent(Component comp) { + return (comp instanceof JComponent) + ? tooltip.equals(((JComponent) comp).getToolTipText()) + : false; + } + + @Override + public String getDescription() { + return "JComponent with the tooltip '" + tooltip + "'"; + } + + } + + @SuppressWarnings(value = "unchecked") + public static R getUIValue(O operator, Function getter) { + return operator.getQueueTool().invokeSmoothly(new QueueTool.QueueAction("getting UI value through the queue using " + getter) { + + @Override + public R launch() throws Exception { + return getter.apply((S) operator.getSource()); + } + }); + } +} diff --git a/jdk/test/sanity/client/lib/SwingSet3/README b/jdk/test/sanity/client/lib/SwingSet3/README new file mode 100644 index 00000000000..1952a70cd26 --- /dev/null +++ b/jdk/test/sanity/client/lib/SwingSet3/README @@ -0,0 +1,4 @@ +This content of this src folder was originally taken from SwingSet3 demo project: https://java.net/projects/swingset3/. +Then it was modified to increase testability and remove extra content and extra dependencies. + +Do NOT modify files in it. \ No newline at end of file diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/DemoProperties.java b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/DemoProperties.java new file mode 100644 index 00000000000..19781096f74 --- /dev/null +++ b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/DemoProperties.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.sun.swingset3; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Annotation type for specifying meta-data about Demo + * + * @author aim + */ +@Retention(RetentionPolicy.RUNTIME) +public @interface DemoProperties { + + String value(); // Name + + String category(); + + String description(); + + String iconFile() default ""; + + String[] sourceFiles() default ""; +} diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/DemoUtilities.java b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/DemoUtilities.java new file mode 100644 index 00000000000..8049ec9ba3b --- /dev/null +++ b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/DemoUtilities.java @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.sun.swingset3.demos; + +import java.awt.*; +import java.net.URI; +import java.io.IOException; +import javax.swing.*; + +/** + * @author Pavel Porvatov + */ +public class DemoUtilities { + + private DemoUtilities() { + // Hide constructor + } + + public static void setToplevelLocation(Window toplevel, Component component, + int relativePosition) { + + Rectangle compBounds = component.getBounds(); + + // Convert component location to screen coordinates + Point p = new Point(); + SwingUtilities.convertPointToScreen(p, component); + + int x; + int y; + + // Set frame location to be centered on panel + switch (relativePosition) { + case SwingConstants.NORTH: { + x = (p.x + (compBounds.width / 2)) - (toplevel.getWidth() / 2); + y = p.y - toplevel.getHeight(); + break; + } + case SwingConstants.EAST: { + x = p.x + compBounds.width; + y = (p.y + (compBounds.height / 2)) - (toplevel.getHeight() / 2); + break; + } + case SwingConstants.SOUTH: { + x = (p.x + (compBounds.width / 2)) - (toplevel.getWidth() / 2); + y = p.y + compBounds.height; + break; + } + case SwingConstants.WEST: { + x = p.x - toplevel.getWidth(); + y = (p.y + (compBounds.height / 2)) - (toplevel.getHeight() / 2); + break; + } + case SwingConstants.NORTH_EAST: { + x = p.x + compBounds.width; + y = p.y - toplevel.getHeight(); + break; + } + case SwingConstants.NORTH_WEST: { + x = p.x - toplevel.getWidth(); + y = p.y - toplevel.getHeight(); + break; + } + case SwingConstants.SOUTH_EAST: { + x = p.x + compBounds.width; + y = p.y + compBounds.height; + break; + } + case SwingConstants.SOUTH_WEST: { + x = p.x - toplevel.getWidth(); + y = p.y + compBounds.height; + break; + } + default: + case SwingConstants.CENTER: { + x = (p.x + (compBounds.width / 2)) - (toplevel.getWidth() / 2); + y = (p.y + (compBounds.height / 2)) - (toplevel.getHeight() / 2); + } + } + toplevel.setLocation(x, y); + } + + public static boolean browse(URI uri) throws IOException { + // Try using the Desktop api first + try { + Desktop desktop = Desktop.getDesktop(); + desktop.browse(uri); + + return true; + } catch (SecurityException e) { + e.printStackTrace(); + } + + return false; + } +} diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/JGridPanel.java b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/JGridPanel.java new file mode 100644 index 00000000000..a0f5bf577b7 --- /dev/null +++ b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/JGridPanel.java @@ -0,0 +1,231 @@ +/* + * Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.sun.swingset3.demos; + +import java.awt.*; +import javax.swing.*; + +/** + * JGridPanel + * + * @author Pavel Porvatov + */ +public class JGridPanel extends JPanel { + + public static final int DEFAULT_GAP = 5; + + public enum Layout { + FIRST, + CENTER, + LAST, + FILL + } + + private enum ComponentType { + NO_RESIZABLE, + HORIZONTAL_FILL, + FULL + } + + private final int cols; + + private int bigCol = -1; + + private int bigRow = -1; + + private int vGap = -1; + + private int hGap = -1; + + public JGridPanel(int cols) { + this(cols, -1, -1); + } + + public JGridPanel(int cols, int bigCol) { + this(cols, bigCol, -1); + } + + public JGridPanel(int cols, int bigCol, int bigRow) { + super(new GridBagLayout()); + + assert cols > 0; + assert bigCol >= -1 && bigCol < cols; + assert bigRow >= -1; + + this.cols = cols; + this.bigCol = bigCol; + this.bigRow = bigRow; + } + + public void setVGap(int vGap) { + this.vGap = vGap; + } + + public void setHGap(int hGap) { + this.hGap = hGap; + } + + public JGridPanel cell() { + return cell(null, getHLayout(null), getVLayout(null), null); + } + + public JGridPanel cell(Component component) { + return cell(component, getHLayout(component), getVLayout(component), null); + } + + public JGridPanel cell(Component component, Layout hLayout) { + return cell(component, hLayout, getVLayout(component), null); + } + + public JGridPanel cell(Component component, Layout hLayout, Layout vLayout) { + return cell(component, hLayout, vLayout, null); + } + + public JGridPanel cell(Component component, Insets insets) { + assert insets != null; + + return cell(component, getHLayout(component), getVLayout(component), insets); + } + + private JGridPanel cell(Component component, Layout hLayout, Layout vLayout, Insets insets) { + int componentCount = getComponentCount(); + int x = componentCount % cols; + int y = componentCount / cols; + + int weightx = x == bigCol || (bigCol < 0 && hLayout == Layout.FILL) ? 1 : 0; + int weighty = y == bigRow || (bigRow < 0 && vLayout == Layout.FILL) ? 1 : 0; + + if (insets == null) { + int topGap = y == 0 ? 0 : vGap; + int leftGap = x == 0 ? 0 : hGap; + + insets = new Insets(topGap < 0 ? DEFAULT_GAP : topGap, + leftGap < 0 ? DEFAULT_GAP : leftGap, 0, 0); + } + + add(component == null ? createSeparator() : component, + new GridBagConstraints(x, y, + 1, 1, + weightx, weighty, + getAnchor(hLayout, vLayout), getFill(hLayout, vLayout), + insets, 0, 0)); + + return this; + } + + public void setComponent(Component component, int col, int row) { + assert col >= 0 && col < cols; + assert row >= 0; + + GridBagLayout layout = (GridBagLayout) getLayout(); + + for (int i = 0; i < getComponentCount(); i++) { + Component oldComponent = getComponent(i); + + GridBagConstraints constraints = layout.getConstraints(oldComponent); + + if (constraints.gridx == col && constraints.gridy == row) { + remove(i); + + add(component == null ? createSeparator() : component, constraints); + + validate(); + repaint(); + + return; + } + } + + // Cell not found + assert false; + } + + private static JComponent createSeparator() { + return new JLabel(); + } + + private static int getFill(Layout hLayout, Layout vLayout) { + if (hLayout == Layout.FILL) { + return vLayout == Layout.FILL ? GridBagConstraints.BOTH : GridBagConstraints.HORIZONTAL; + } + + return vLayout == Layout.FILL ? GridBagConstraints.VERTICAL : GridBagConstraints.NONE; + } + + private static ComponentType getComponentType(Component component) { + if (component == null + || component instanceof JLabel + || component instanceof JRadioButton + || component instanceof JCheckBox + || component instanceof JButton) { + return ComponentType.NO_RESIZABLE; + } + + if (component instanceof JComboBox + || component instanceof JTextField) { + return ComponentType.HORIZONTAL_FILL; + } + + return ComponentType.FULL; + } + + private static Layout getHLayout(Component component) { + if (getComponentType(component) == ComponentType.NO_RESIZABLE) { + return Layout.FIRST; + } else { + return Layout.FILL; + } + } + + private static Layout getVLayout(Component component) { + if (getComponentType(component) == ComponentType.FULL) { + return Layout.FILL; + } else { + return Layout.CENTER; + } + } + + private static final int[][] ANCHORS = new int[][]{ + {GridBagConstraints.NORTHWEST, GridBagConstraints.NORTH, GridBagConstraints.NORTHEAST}, + {GridBagConstraints.WEST, GridBagConstraints.CENTER, GridBagConstraints.EAST}, + {GridBagConstraints.SOUTHWEST, GridBagConstraints.SOUTH, GridBagConstraints.SOUTHEAST} + }; + + private static int getAnchorIndex(Layout layout) { + if (layout == Layout.CENTER) { + return 1; + } else if (layout == Layout.LAST) { + return 2; + } else { + return 0; + } + } + + private static int getAnchor(Layout hLayout, Layout vLayout) { + return ANCHORS[getAnchorIndex(vLayout)][getAnchorIndex(hLayout)]; + } + + public void setBorderEqual(int border) { + setBorder(BorderFactory.createEmptyBorder(border, border, border, border)); + } +} diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/JHyperlink.java b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/JHyperlink.java new file mode 100644 index 00000000000..1deb250c5fe --- /dev/null +++ b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/JHyperlink.java @@ -0,0 +1,237 @@ +/* + * Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.sun.swingset3.demos; + +import java.awt.Color; +import java.awt.Cursor; +import java.awt.Graphics; +import java.awt.Insets; +import java.awt.Rectangle; +import java.awt.event.ActionEvent; +import java.net.URI; +import java.net.URISyntaxException; +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.ButtonModel; +import javax.swing.Icon; +import javax.swing.JButton; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; + +/** + * + * @author aim + */ +//Create HTML hyperlink +//Create HTML image hyperlink +public class JHyperlink extends JButton { + + private static final BrowseAction defaultBrowseAction = new BrowseAction(); + + private URI targetURI; + private boolean visited; + + private final transient Rectangle viewRect = new Rectangle(); + private final transient Rectangle iconRect = new Rectangle(); + private final transient Rectangle textRect = new Rectangle(); + + //remind(aim): lookup colors instead of hardcoding them + private Color normalForeground; + private Color activeForeground; + private Color visitedForeground; + private boolean drawUnderline = true; + + static { + UIManager.put("Hyperlink.foreground", Color.blue); + UIManager.put("Hyperlink.activeForeground", Color.red); + UIManager.put("Hyperlink.visitedForeground", new Color(85, 145, 90)); + } + + /** + * Creates a new instance of JHyperlink + */ + public JHyperlink() { + super(); + normalForeground = UIManager.getColor("Hyperlink.foreground"); + activeForeground = UIManager.getColor("Hyperlink.activeForeground"); + visitedForeground = UIManager.getColor("Hyperlink.visitedForeground"); + setBorderPainted(false); + setContentAreaFilled(false); + setForeground(normalForeground); + setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); + setMargin(new Insets(0, 0, 0, 0)); + setAction(defaultBrowseAction); + } + + /** + * Creates a new instance of JHyperlink + */ + public JHyperlink(String text) { + this(); + setText(text); // override the inheritence of the action's name + } + + public JHyperlink(String text, String targetURI) throws URISyntaxException { + this(text, new URI(targetURI)); + } + + public JHyperlink(String text, URI target) { + this(text); + setTarget(target); + } + + public JHyperlink(String text, Action action) { + this(text); + setAction(action); // replaces default browse action + setText(text); // override the inheritence of the action's name + } + + public JHyperlink(String text, Icon icon) { + this(text); + setIcon(icon); + } + + public JHyperlink(Icon icon, String targetURI) throws URISyntaxException { + this(null, icon, targetURI); + } + + public JHyperlink(String text, Icon icon, String targetURI) throws URISyntaxException { + this(text, new URI(targetURI)); + setIcon(icon); + } + + public JHyperlink(String text, Icon icon, URI target) { + this(text); + setIcon(icon); + setTarget(target); + } + + public void setTarget(URI target) { + this.targetURI = target; + setToolTipText(target.toASCIIString()); + } + + public URI getTarget() { + return targetURI; + } + + public void setVisited(boolean visited) { + this.visited = visited; + } + + public boolean isVisited() { + return visited; + } + + @Override + public void setForeground(Color foreground) { + normalForeground = foreground; + super.setForeground(foreground); + } + + public void setVisitedForeground(Color visited) { + visitedForeground = visited; + } + + public void setDrawUnderline(boolean drawUnderline) { + this.drawUnderline = drawUnderline; + } + + public boolean getDrawUnderline() { + return drawUnderline; + } + + @Override + protected void paintComponent(Graphics g) { + // Set the foreground on the fly to ensure the text is painted + // with the proper color in super.paintComponent + ButtonModel model = getModel(); + if (model.isArmed()) { + super.setForeground(activeForeground); + } else if (visited) { + super.setForeground(visitedForeground); + } else { + super.setForeground(normalForeground); + } + super.paintComponent(g); + + if (drawUnderline) { + Insets insets = getInsets(); + viewRect.x = insets.left; + viewRect.y = insets.top; + viewRect.width = getWidth() - insets.left - insets.right; + viewRect.height = getHeight() - insets.top - insets.bottom; + int baseline = getBaseline(viewRect.width, viewRect.height); + + iconRect.x = iconRect.y = iconRect.width = iconRect.height = 0; + textRect.x = textRect.y = textRect.width = textRect.height = 0; + SwingUtilities.layoutCompoundLabel(g.getFontMetrics(), getText(), + getIcon(), getVerticalAlignment(), getHorizontalAlignment(), + getVerticalTextPosition(), getHorizontalTextPosition(), + viewRect, iconRect, textRect, getIconTextGap()); + + // getBaseline not returning correct results, so workaround for now + if (UIManager.getLookAndFeel().getName().equals("Nimbus")) { + baseline += 7; + } else { + baseline += 3; + } + + g.setColor(getForeground()); + g.drawLine(textRect.x, + baseline, + textRect.x + textRect.width, + baseline); + } + + } + + // This action is stateless and hence can be shared across hyperlinks + private static class BrowseAction extends AbstractAction { + + public BrowseAction() { + super(); + } + + @Override + public void actionPerformed(ActionEvent e) { + JHyperlink hyperlink = (JHyperlink) e.getSource(); + + URI targetURI = hyperlink.getTarget(); + if (targetURI != null) { + try { + DemoUtilities.browse(targetURI); + hyperlink.setVisited(true); + } catch (Exception ex) { + ex.printStackTrace(); + System.err.println(ex); + } + + } + } + + } +// +// + +} diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/ResourceManager.java b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/ResourceManager.java new file mode 100644 index 00000000000..af44f84a6ad --- /dev/null +++ b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/ResourceManager.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.sun.swingset3.demos; + +import java.net.URL; +import java.util.MissingResourceException; +import java.util.ResourceBundle; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.swing.*; + +/** + * @author Pavel Porvatov + */ +public class ResourceManager { + + private static final Logger logger = Logger.getLogger(ResourceManager.class.getName()); + + private final Class demoClass; + + // Resource bundle for internationalized and accessible text + private ResourceBundle bundle = null; + + public ResourceManager(Class demoClass) { + this.demoClass = demoClass; + + String bundleName = demoClass.getPackage().getName() + ".resources." + demoClass.getSimpleName(); + + try { + bundle = ResourceBundle.getBundle(bundleName); + } catch (MissingResourceException e) { + logger.log(Level.SEVERE, "Couldn't load bundle: " + bundleName); + } + } + + public String getString(String key) { + return bundle != null ? bundle.getString(key) : key; + } + + public char getMnemonic(String key) { + return (getString(key)).charAt(0); + } + + public ImageIcon createImageIcon(String filename, String description) { + String path = "resources/images/" + filename; + + URL imageURL = demoClass.getResource(path); + + if (imageURL == null) { + logger.log(Level.SEVERE, "unable to access image file: " + path); + + return null; + } else { + return new ImageIcon(imageURL, description); + } + } +} diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/button/ButtonDemo.java b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/button/ButtonDemo.java new file mode 100644 index 00000000000..728e0543b68 --- /dev/null +++ b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/button/ButtonDemo.java @@ -0,0 +1,224 @@ +/* + * Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.sun.swingset3.demos.button; + +import java.awt.Color; +import java.awt.FlowLayout; +import java.awt.GridLayout; +import java.awt.event.ActionEvent; +import java.net.URISyntaxException; +import javax.swing.BorderFactory; +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.SwingUtilities; + +import com.sun.swingset3.DemoProperties; +import com.sun.swingset3.demos.JHyperlink; + +/** + * + * @author aim + */ +@DemoProperties( + value = "JButton Demo", + category = "Controls", + description = "Demonstrates the many uses of JButton, Swing's push button component.", + sourceFiles = { + "com/sun/swingset3/demos/button/ButtonDemo.java", + "com/sun/swingset3/demos/JHyperlink.java", + "com/sun/swingset3/demos/button/resources/ButtonDemo.html", + "com/sun/swingset3/demos/button/resources/images/blogs.png", + "com/sun/swingset3/demos/button/resources/images/ButtonDemo.gif", + "com/sun/swingset3/demos/button/resources/images/document-print.png", + "com/sun/swingset3/demos/button/resources/images/earth_day.gif", + "com/sun/swingset3/demos/button/resources/images/earth_night.gif", + "com/sun/swingset3/demos/button/resources/images/edit-find.png", + "com/sun/swingset3/demos/button/resources/images/redbutton.png", + "com/sun/swingset3/demos/button/resources/images/redbutton_dark.png", + "com/sun/swingset3/demos/button/resources/images/redbutton_glow.png" + } +) +public final class ButtonDemo extends JPanel { + + public static final String DEMO_TITLE = ButtonDemo.class.getAnnotation(DemoProperties.class).value(); + public static final String DO_IT_AGAIN = "Do it again"; + public static final String DO_IT = "Do it"; + public static final String BUTTON_WITH_TEXT_AND_IMAGE = "button with text and image"; + public static final String BUTTON_WITH_BACKGROUND_COLOR = "button with background color"; + public static final String GO = "Go"; + public static final String FIND = "Find"; + public static final String IMAGE_BUTTON = "image button"; + public static final String SIMPLE_BUTTON = "simple button"; + public static final String GET_MORE_INFO = "Get More Info"; + public static final String JAVA_BLOGS_URL = "https://blogs.oracle.com/java/"; + public static final String JAVA_SE_URL = "http://www.oracle.com/technetwork/java/javase/overview/index.html"; + public static final String BUTTON_WITH_ROLLOVER_IMAGE = "button with rollover image"; + public static final String BUTTON_WITH_NO_BORDER = "button with no border"; + public static final String CONNECT = "Connect"; + + public ButtonDemo() { + setToolTipText("Demonstrates JButton, Swing's push button component."); + initComponents(); + setOpaque(false); + } + + protected void initComponents() { + setLayout(new GridLayout(0, 1)); + + add(createSimpleButtonPanel()); + add(createCreativeButtonPanel()); + } + + protected JPanel createSimpleButtonPanel() { + JPanel panel = new JPanel(); + panel.setLayout(new FlowLayout(FlowLayout.CENTER, 20, 8)); + panel.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEmptyBorder(), + "Simple Buttons")); + + //Create simple button + final JButton simpleButton = new JButton(DO_IT); + simpleButton.setToolTipText(SIMPLE_BUTTON); + // + //Add action listener using anonymous inner class + // This style is useful when the action code is tied to a + // single button instance and it's useful for simplicity + // sake to keep the action code located near the button. + // More global application actions should be implemented + // using Action classes instead. + simpleButton.addActionListener((ActionEvent event) -> { + simpleButton.setText(DO_IT_AGAIN); + // Need to force toplevel to relayout to accommodate new button size + SwingUtilities.getWindowAncestor(simpleButton).validate(); + }); + // + simpleButton.putClientProperty("snippetKey", "Create simple button"); + panel.add(simpleButton); + + //Create image button + // Image is from the Java Look and Feel Graphics Repository + JButton button = new JButton(new ImageIcon(getClass(). + getResource("resources/images/document-print.png"))); + button.setToolTipText(IMAGE_BUTTON); + // + button.putClientProperty("snippetKey", "Create image button"); + panel.add(button); + + //Create button with text and image + // Image is from the Java Look and Feel Graphics Repository + button = new JButton(FIND, + new ImageIcon(getClass(). + getResource("resources/images/edit-find.png"))); + button.setToolTipText(BUTTON_WITH_TEXT_AND_IMAGE); + button.setHorizontalTextPosition(JButton.LEADING); + button.setIconTextGap(6); + // + button.putClientProperty("snippetKey", "Create button with text and image"); + panel.add(button); + + //Create button with background color + button = new JButton(GO); + button.setBackground(Color.green); + button.setContentAreaFilled(true); + button.setOpaque(false); + button.setToolTipText(BUTTON_WITH_BACKGROUND_COLOR); + // + button.putClientProperty("snippetKey", "Create button with background color"); + panel.add(button); + + return panel; + } + + protected JPanel createCreativeButtonPanel() { + JPanel panel = new JPanel(); + panel.setLayout(new FlowLayout(FlowLayout.CENTER, 16, 8)); + panel.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEmptyBorder(), + "More Interesting Buttons")); + + //Create button with no border + JButton button = new JButton(); + button.setText(CONNECT); + button.setIcon(new ImageIcon(getClass().getResource("resources/images/earth_day.gif"))); + button.setPressedIcon(new ImageIcon(getClass().getResource("resources/images/earth_night.gif"))); + button.setBorderPainted(false); + button.setContentAreaFilled(false); + button.setVerticalTextPosition(JButton.BOTTOM); + button.setHorizontalTextPosition(JButton.CENTER); + button.setIconTextGap(0); + button.setToolTipText(BUTTON_WITH_NO_BORDER); + // + button.putClientProperty("snippetKey", "Create button with no border"); + panel.add(button); + + //Create image button with rollover image + button = new JButton(); + button.setBorderPainted(false); + button.setContentAreaFilled(false); + button.setIcon(new ImageIcon(getClass().getResource("resources/images/redbutton.png"))); + button.setRolloverEnabled(true); + button.setRolloverIcon(new ImageIcon(getClass().getResource("resources/images/redbutton_glow.png"))); + button.setPressedIcon(new ImageIcon(getClass().getResource("resources/images/redbutton_dark.png"))); + button.setToolTipText(BUTTON_WITH_ROLLOVER_IMAGE); + // + button.putClientProperty("snippetKey", "Create image button with rollover image"); + panel.add(button); + + //Create HTML hyperlink + JHyperlink hyperlink; + try { + hyperlink = new JHyperlink(GET_MORE_INFO, JAVA_SE_URL); + } catch (URISyntaxException use) { + use.printStackTrace(); + hyperlink = new JHyperlink(GET_MORE_INFO); + } + // + hyperlink.putClientProperty("snippetKey", "Create HTML hyperlink"); + panel.add(hyperlink); + + //Create HTML image hyperlink + try { + hyperlink = new JHyperlink( + new ImageIcon(getClass().getResource("resources/images/blogs.png")), JAVA_BLOGS_URL); + } catch (URISyntaxException use) { + use.printStackTrace(); + } + // + button.putClientProperty("snippetKey", "Create HTML image hyperlink"); + panel.add(hyperlink); + + return panel; + } + + public static void main(String args[]) { + final ButtonDemo buttonDemo = new ButtonDemo(); + + javax.swing.SwingUtilities.invokeLater(() -> { + JFrame frame = new JFrame(DEMO_TITLE); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.add(buttonDemo); + frame.pack(); + frame.setVisible(true); + }); + } +} diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/button/resources/ButtonDemo.html b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/button/resources/ButtonDemo.html new file mode 100644 index 00000000000..866856b9e6a --- /dev/null +++ b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/button/resources/ButtonDemo.html @@ -0,0 +1,36 @@ + + + + + + + +

Today's modern GUIs often use buttons which don't look like the traditional "push" button. Swing's button component, javax.swing.JButton, + can be used to create both ordinary and more creative button visuals. In the end, they all perform an action when clicked. +

+

+ To highlight the source code used to create a particular button, popup the context menu over that button. +

+ + diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/button/resources/images/ButtonDemo.gif b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/button/resources/images/ButtonDemo.gif new file mode 100644 index 0000000000000000000000000000000000000000..9f400fe4ac06f08413b3d2eb863736314a8c94a8 GIT binary patch literal 1170 zcmeH`?@tqV0LHIy#g3p>bel_H14TC%G;nt{U3jqq8$V1Ibu)`*F2Ntr!jjK?!MRij{XsQx98Q9 zJjwIo^WZU9 z3ZSCuvXWm*NGVYhL^YO_G2EhpyrOEd0v5x3PKD`=6611gQu;g0J&E!mHXCMAEGNqV zb~^@qRO+&$?{_K@SdPz9@%e}lib?;l>9nK?S&+)8yr|49tp%ure>omvMVi^9R?{&q z%O*2SLhN+7?apD+>9+R_UUKyPwzM8tUJpivV1x^?X_^sOKF4rUr*p{eba(X(+It4N z9Q~wo&_^e{R6Ia$_^I`|XF@Q_&qujnB)Jex&@qwWbM_vWKNR=T8^K7NO>WLblXNUi zu>!+ogshUv$h?@7HJAgsq~^V$4VvZYSc+o#=e)GaXINg8fG%WZUQ)QM40KpWSa?a6 zwY&}?fV!*!jKK3ySGO1g6}Mm*QIF&G1c4QDSV(kT2Vjf2N)QB&;~0h&M*3g>4S{L} zH6RFz)E4RspFm6ydC64KHZ?x;rD^xuXJ@A-e1qow$G&fy_5?=u9`?vH)At_Vu4>r4 z>giu8uX*dE`)7w3_g{C9MBMe~$_IjDM=z|0|M=NnhSiUnwL3OnB~I?!J8LHI6qW7T zVHh^geC0ulu~BkmZQ}!aE!k`?=EX>R X&Gh*eVwqh1+8Uc&y=5suk%oT(Zw2S1 literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/button/resources/images/blogs.png b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/button/resources/images/blogs.png new file mode 100644 index 0000000000000000000000000000000000000000..cde6b947c2c1c303ea3b163ff9607f0d25f2e443 GIT binary patch literal 9198 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 literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/button/resources/images/document-print.png b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/button/resources/images/document-print.png new file mode 100644 index 0000000000000000000000000000000000000000..99abfb51b08507908967624da51d5b716ccdaeca GIT binary patch literal 1503 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 literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/button/resources/images/earth_night.gif b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/button/resources/images/earth_night.gif new file mode 100644 index 0000000000000000000000000000000000000000..649a3892f706c785b989bfcaa94e14a2c7b922f1 GIT binary patch literal 1648 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 literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/button/resources/images/redbutton.png b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/button/resources/images/redbutton.png new file mode 100644 index 0000000000000000000000000000000000000000..b07c818766d50f8098690baad86d520b771dd958 GIT binary patch literal 7087 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 ZZvYZ`ARbYm=ZgRU002ovPDHLkV1jG?s8;|0 literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/button/resources/images/redbutton_dark.png b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/button/resources/images/redbutton_dark.png new file mode 100644 index 0000000000000000000000000000000000000000..64539c9e4615acbc875918aedc8faf21050a773d GIT binary patch literal 7038 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 literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/button/resources/images/redbutton_glow.png b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/button/resources/images/redbutton_glow.png new file mode 100644 index 0000000000000000000000000000000000000000..b2e99220d1c8aeaa2c10d280e6e3d86ede8e23e0 GIT binary patch literal 7562 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 hairCB; + private JComboBox eyesCB; + private JComboBox mouthCB; + + private JComboBox presetCB; + + private final Map parts = new HashMap<>(); + + /** + * main method allows us to run as a standalone demo. + * + * @param args + */ + public static void main(String[] args) { + JFrame frame = new JFrame(DEMO_TITLE); + + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.getContentPane().add(new ComboBoxDemo()); + frame.setPreferredSize(new Dimension(800, 600)); + frame.pack(); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + } + + /** + * ComboBoxDemo Constructor + */ + public ComboBoxDemo() { + createComboBoxDemo(); + } + + private void createComboBoxDemo() { + setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); + + JPanel innerPanel = new JPanel(); + innerPanel.setLayout(new BoxLayout(innerPanel, BoxLayout.X_AXIS)); + + add(Box.createRigidArea(VGAP20)); + add(innerPanel); + add(Box.createRigidArea(VGAP20)); + + innerPanel.add(Box.createRigidArea(HGAP20)); + + // Create a panel to hold buttons + JPanel comboBoxPanel = new JPanel() { + @Override + public Dimension getMaximumSize() { + return new Dimension(getPreferredSize().width, super.getMaximumSize().height); + } + }; + comboBoxPanel.setLayout(new BoxLayout(comboBoxPanel, BoxLayout.Y_AXIS)); + + comboBoxPanel.add(Box.createRigidArea(VGAP15)); + + JLabel l = (JLabel) comboBoxPanel.add(new JLabel(resourceManager.getString("ComboBoxDemo.presets"))); + l.setAlignmentX(JLabel.LEFT_ALIGNMENT); + comboBoxPanel.add(presetCB = createPresetComboBox()); + presetCB.setAlignmentX(JComboBox.LEFT_ALIGNMENT); + l.setLabelFor(presetCB); + comboBoxPanel.add(Box.createRigidArea(VGAP30)); + + l = (JLabel) comboBoxPanel.add(new JLabel(resourceManager.getString("ComboBoxDemo.hair_description"))); + l.setAlignmentX(JLabel.LEFT_ALIGNMENT); + comboBoxPanel.add(hairCB = createHairComboBox()); + hairCB.setAlignmentX(JComboBox.LEFT_ALIGNMENT); + l.setLabelFor(hairCB); + comboBoxPanel.add(Box.createRigidArea(VGAP15)); + + l = (JLabel) comboBoxPanel.add(new JLabel(resourceManager.getString("ComboBoxDemo.eyes_description"))); + l.setAlignmentX(JLabel.LEFT_ALIGNMENT); + comboBoxPanel.add(eyesCB = createEyesComboBox()); + eyesCB.setAlignmentX(JComboBox.LEFT_ALIGNMENT); + l.setLabelFor(eyesCB); + comboBoxPanel.add(Box.createRigidArea(VGAP15)); + + l = (JLabel) comboBoxPanel.add(new JLabel(resourceManager.getString("ComboBoxDemo.mouth_description"))); + l.setAlignmentX(JLabel.LEFT_ALIGNMENT); + comboBoxPanel.add(mouthCB = createMouthComboBox()); + + mouthCB.setAlignmentX(JComboBox.LEFT_ALIGNMENT); + l.setLabelFor(mouthCB); + comboBoxPanel.add(Box.createRigidArea(VGAP15)); + + // Fill up the remaining space + comboBoxPanel.add(new JPanel(new BorderLayout())); + + // Create and place the Face. + face = new Face(); + JPanel facePanel = new JPanel(); + facePanel.setLayout(new BorderLayout()); + facePanel.setBorder(new BevelBorder(BevelBorder.LOWERED)); + + faceLabel = new JLabel(face); + facePanel.add(faceLabel, BorderLayout.CENTER); + // Indicate that the face panel is controlled by the hair, eyes and + // mouth combo boxes. + Object[] controlledByObjects = new Object[3]; + controlledByObjects[0] = hairCB; + controlledByObjects[1] = eyesCB; + controlledByObjects[2] = mouthCB; + AccessibleRelation controlledByRelation + = new AccessibleRelation(AccessibleRelation.CONTROLLED_BY_PROPERTY, + controlledByObjects); + facePanel.getAccessibleContext().getAccessibleRelationSet().add(controlledByRelation); + + // Indicate that the hair, eyes and mouth combo boxes are controllers + // for the face panel. + AccessibleRelation controllerForRelation + = new AccessibleRelation(AccessibleRelation.CONTROLLER_FOR_PROPERTY, + facePanel); + hairCB.getAccessibleContext().getAccessibleRelationSet().add(controllerForRelation); + eyesCB.getAccessibleContext().getAccessibleRelationSet().add(controllerForRelation); + mouthCB.getAccessibleContext().getAccessibleRelationSet().add(controllerForRelation); + + // add buttons and image panels to inner panel + innerPanel.add(comboBoxPanel); + innerPanel.add(Box.createRigidArea(HGAP30)); + innerPanel.add(facePanel); + innerPanel.add(Box.createRigidArea(HGAP20)); + + // load up the face parts + addFace("brent", resourceManager.getString("ComboBoxDemo.brent")); + addFace("georges", resourceManager.getString("ComboBoxDemo.georges")); + addFace("hans", resourceManager.getString("ComboBoxDemo.hans")); + addFace("howard", resourceManager.getString("ComboBoxDemo.howard")); + addFace("james", resourceManager.getString("ComboBoxDemo.james")); + addFace("jeff", resourceManager.getString("ComboBoxDemo.jeff")); + addFace("jon", resourceManager.getString("ComboBoxDemo.jon")); + addFace("lara", resourceManager.getString("ComboBoxDemo.lara")); + addFace("larry", resourceManager.getString("ComboBoxDemo.larry")); + addFace("lisa", resourceManager.getString("ComboBoxDemo.lisa")); + addFace("michael", resourceManager.getString("ComboBoxDemo.michael")); + addFace("philip", resourceManager.getString("ComboBoxDemo.philip")); + addFace("scott", resourceManager.getString("ComboBoxDemo.scott")); + + // set the default face + presetCB.setSelectedIndex(0); + } + + private void addFace(String name, String i18n_name) { + ImageIcon i; + String i18n_hair = resourceManager.getString("ComboBoxDemo.hair"); + String i18n_eyes = resourceManager.getString("ComboBoxDemo.eyes"); + String i18n_mouth = resourceManager.getString("ComboBoxDemo.mouth"); + + parts.put(i18n_name, name); // i18n name lookup + parts.put(name, i18n_name); // reverse name lookup + + i = resourceManager.createImageIcon(name + "hair.jpg", i18n_name + i18n_hair); + parts.put(name + "hair", i); + + i = resourceManager.createImageIcon(name + "eyes.jpg", i18n_name + i18n_eyes); + parts.put(name + "eyes", i); + + i = resourceManager.createImageIcon(name + "mouth.jpg", i18n_name + i18n_mouth); + parts.put(name + "mouth", i); + } + + private JComboBox createHairComboBox() { + JComboBox cb = new JComboBox<>(); + fillComboBox(cb); + cb.addActionListener(this); + return cb; + } + + private JComboBox createEyesComboBox() { + JComboBox cb = new JComboBox<>(); + fillComboBox(cb); + cb.addActionListener(this); + return cb; + } + + private JComboBox createMouthComboBox() { + JComboBox cb = new JComboBox<>(); + fillComboBox(cb); + cb.addActionListener(this); + return cb; + } + + private JComboBox createPresetComboBox() { + JComboBox cb = new JComboBox<>(); + cb.addItem(resourceManager.getString("ComboBoxDemo.preset1")); + cb.addItem(resourceManager.getString("ComboBoxDemo.preset2")); + cb.addItem(resourceManager.getString("ComboBoxDemo.preset3")); + cb.addItem(resourceManager.getString("ComboBoxDemo.preset4")); + cb.addItem(resourceManager.getString("ComboBoxDemo.preset5")); + cb.addItem(resourceManager.getString("ComboBoxDemo.preset6")); + cb.addItem(resourceManager.getString("ComboBoxDemo.preset7")); + cb.addItem(resourceManager.getString("ComboBoxDemo.preset8")); + cb.addItem(resourceManager.getString("ComboBoxDemo.preset9")); + cb.addItem(resourceManager.getString("ComboBoxDemo.preset10")); + cb.addActionListener(this); + return cb; + } + + private void fillComboBox(JComboBox cb) { + cb.addItem(resourceManager.getString("ComboBoxDemo.brent")); + cb.addItem(resourceManager.getString("ComboBoxDemo.georges")); + cb.addItem(resourceManager.getString("ComboBoxDemo.hans")); + cb.addItem(resourceManager.getString("ComboBoxDemo.howard")); + cb.addItem(resourceManager.getString("ComboBoxDemo.james")); + cb.addItem(resourceManager.getString("ComboBoxDemo.jeff")); + cb.addItem(resourceManager.getString("ComboBoxDemo.jon")); + cb.addItem(resourceManager.getString("ComboBoxDemo.lara")); + cb.addItem(resourceManager.getString("ComboBoxDemo.larry")); + cb.addItem(resourceManager.getString("ComboBoxDemo.lisa")); + cb.addItem(resourceManager.getString("ComboBoxDemo.michael")); + cb.addItem(resourceManager.getString("ComboBoxDemo.philip")); + cb.addItem(resourceManager.getString("ComboBoxDemo.scott")); + } + + @Override + public void actionPerformed(ActionEvent e) { + if (e.getSource() == hairCB) { + String name = (String) parts.get(hairCB.getSelectedItem()); + face.setHair((ImageIcon) parts.get(name + "hair")); + faceLabel.repaint(); + } else if (e.getSource() == eyesCB) { + String name = (String) parts.get(eyesCB.getSelectedItem()); + face.setEyes((ImageIcon) parts.get(name + "eyes")); + faceLabel.repaint(); + } else if (e.getSource() == mouthCB) { + String name = (String) parts.get(mouthCB.getSelectedItem()); + face.setMouth((ImageIcon) parts.get(name + "mouth")); + faceLabel.repaint(); + } else if (e.getSource() == presetCB) { + String hair = null; + String eyes = null; + String mouth = null; + switch (presetCB.getSelectedIndex()) { + case 0: + hair = (String) parts.get("philip"); + eyes = (String) parts.get("howard"); + mouth = (String) parts.get("jeff"); + break; + case 1: + hair = (String) parts.get("jeff"); + eyes = (String) parts.get("larry"); + mouth = (String) parts.get("philip"); + break; + case 2: + hair = (String) parts.get("howard"); + eyes = (String) parts.get("scott"); + mouth = (String) parts.get("hans"); + break; + case 3: + hair = (String) parts.get("philip"); + eyes = (String) parts.get("jeff"); + mouth = (String) parts.get("hans"); + break; + case 4: + hair = (String) parts.get("brent"); + eyes = (String) parts.get("jon"); + mouth = (String) parts.get("scott"); + break; + case 5: + hair = (String) parts.get("lara"); + eyes = (String) parts.get("larry"); + mouth = (String) parts.get("lisa"); + break; + case 6: + hair = (String) parts.get("james"); + eyes = (String) parts.get("philip"); + mouth = (String) parts.get("michael"); + break; + case 7: + hair = (String) parts.get("philip"); + eyes = (String) parts.get("lisa"); + mouth = (String) parts.get("brent"); + break; + case 8: + hair = (String) parts.get("james"); + eyes = (String) parts.get("philip"); + mouth = (String) parts.get("jon"); + break; + case 9: + hair = (String) parts.get("lara"); + eyes = (String) parts.get("jon"); + mouth = (String) parts.get("scott"); + break; + } + if (hair != null) { + hairCB.setSelectedItem(hair); + eyesCB.setSelectedItem(eyes); + mouthCB.setSelectedItem(mouth); + faceLabel.repaint(); + } + } + } + + private static class Face implements Icon { + + private ImageIcon hair; + private ImageIcon eyes; + private ImageIcon mouth; + + void setHair(ImageIcon i) { + hair = i; + } + + void setEyes(ImageIcon i) { + eyes = i; + } + + void setMouth(ImageIcon i) { + mouth = i; + } + + @Override + public void paintIcon(Component c, Graphics g, int x, int y) { + int height = y; + x = c.getWidth() / 2 - getIconWidth() / 2; + + if (hair != null) { + hair.paintIcon(c, g, x, height); + height += hair.getIconHeight(); + } + + if (eyes != null) { + eyes.paintIcon(c, g, x, height); + height += eyes.getIconHeight(); + } + + if (mouth != null) { + mouth.paintIcon(c, g, x, height); + } + } + + @Override + public int getIconWidth() { + return 344; + } + + @Override + public int getIconHeight() { + return 455; + } + } +} diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/ComboBoxDemo.properties b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/ComboBoxDemo.properties new file mode 100644 index 00000000000..51ee144e0c8 --- /dev/null +++ b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/ComboBoxDemo.properties @@ -0,0 +1,42 @@ +### ComboBox Demo ### + +ComboBoxDemo.accessible_description=This demo shows an example of using the JComboBox component. +ComboBoxDemo.tooltip=JComboBox demo +ComboBoxDemo.name=ComboBox Demo + +ComboBoxDemo.hair=hair +ComboBoxDemo.eyes=eyes +ComboBoxDemo.mouth=mouth +ComboBoxDemo.presets=Presets: + +ComboBoxDemo.preset1=Philip, Howard, Jeff +ComboBoxDemo.preset2=Jeff, Larry, Philip +ComboBoxDemo.preset3=Howard, Scott, Hans +ComboBoxDemo.preset4=Philip, Jeff, Hans +ComboBoxDemo.preset5=Brent, Jon, Scott +ComboBoxDemo.preset6=Lara, Larry, Lisa +ComboBoxDemo.preset7=James, Philip, Michael +ComboBoxDemo.preset8=Philip, Lisa, Brent +ComboBoxDemo.preset9=James, Philip, Jon +ComboBoxDemo.preset10=Lara, Jon, Scott + + +ComboBoxDemo.hair_description=Hair: +ComboBoxDemo.eyes_description=Eyes & Nose: +ComboBoxDemo.mouth_description=Mouth: + +ComboBoxDemo.amy=Amy +ComboBoxDemo.brent=Brent +ComboBoxDemo.georges=Georges +ComboBoxDemo.hans=Hans +ComboBoxDemo.howard=Howard +ComboBoxDemo.james=James +ComboBoxDemo.jeff=Jeff +ComboBoxDemo.jon=Jon +ComboBoxDemo.lara=Lara +ComboBoxDemo.larry=Larry +ComboBoxDemo.lisa=Lisa +ComboBoxDemo.michael=Michael +ComboBoxDemo.philip=Philip +ComboBoxDemo.scott=Scott + diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/ComboBoxDemo.gif b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/ComboBoxDemo.gif new file mode 100644 index 0000000000000000000000000000000000000000..f8e971f19cec3a378e5711c594a8ff18d6ce6958 GIT binary patch literal 672 zcma)(+e=e%0LM35Nw=A4B;A}rIb%Y3&dMk##SBA?poj`BT^9=zg`l=JojP^rOikUz zwn*$L6Ca$46w!mFWJ%2sa?@hY=}NcX+1Z)ya<0w)pwH9i;rsOYo-fGHK3OW_iFh6X zF9yePOqBEkjEgDD!C6w8;vh)MFdRiOBuQf!&axayahN~o!zqg9Aow^%vr&=;7_MwK zuZHPhi1K1VC1O!)^=@xSj&`e1ixM%{B1R(203zZGgl9dg%XmbATI7gXg&37uLk()0 zn#a|sp%&54dI?r;N_ckKycmZKI0YF22CpJ%;Ou7GVG|L*kvaQf_?;LH-?1|bF7 zWpnzxMC37Qmg~$<5rax+QliF*xfR<2{>=#-e~6hSd=ad=eL*5jMQE-@XHcPfn+rHR zm!0xA8MUCkLDSgOtVK{= zOKaP+_Kr@y!D#AwZjxq-Uv_sN5od@b=k~+BZ+Z_vn#^65*1@6jvdRY~dsB9&y#J7r zmZuR9jmxa}1`ii+6>h$LD=F?_)~E5Aad=|||M#7{H@Bz%v|Bd}%4E4W5|iVLwv=8? zayN`E+2QNgs;lo;RSm$ea)fi%vQ&|F;2^)QF7fPz9mCE3o_>C6_E_&n&17fBWkq4~ zqXeg<<#b8A+LilNc&H$Mrx*dhBzrEkiQaX{8io7L6uzx*Y^{57B4Jbi7ksp7`gKvO nAZ>VgI$eCRXIsu_9GY;YPf|XSH`4DIDxdBvo*bzV@lO2%8&`h$ literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/brenteyes.jpg b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/brenteyes.jpg new file mode 100644 index 0000000000000000000000000000000000000000..6f7d0f4fee2ad654ee5e44a97eabd0552b26bd0a GIT binary patch literal 2191 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 literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/georgeseyes.jpg b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/georgeseyes.jpg new file mode 100644 index 0000000000000000000000000000000000000000..224cef61da14d4eb6e310fe2c3911e42659ccc56 GIT binary patch literal 2426 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 QS4)fL+`uThroD9PKctd-VgLXD literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/georgesmouth.jpg b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/georgesmouth.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b917e515d4812e680d996cf8113acae5968ed1e5 GIT binary patch literal 2043 zcma)6X;hQf7QJ64sYZnel~S1!86y&cM8YHrVTc4FWs*^A0@e_sDM3JKnTnv|6i^01 zna3Cdihwd!DCD57nAKUJ;>bI~iUANn002;HvU#xSiwrPY*BEGZ z2qc58s;b&wYZEm!iF-^9O!rt3Oic+^lzpE;`6qzXwY3d#h6F7wf+N9@;7D;KksLo8 z|CyaaS#1R>N+1DpheKEZQ-RrO}gWzyD z0{*uHQh}Mlb@yTro`<)bAgW$2z#y2CUlWNC%8&nIwY zOUo~@_1BZ6MwK)A;nR@;?x>F zRC}$Xx~PONFM%fP5XasTDFCPf2vwC4#R}83o8ZM&XWk0(4wV@b8ywjg?-I{l;Ydf7 zVW%;%{pT1bklRk3W(*+ktiI)HExzMig&&pt;UadR(#x}}=E;hBnS7mm z+s)c58bSKiRUGHG^yl6djcVT-?vU@|=FFXD}kR zm>VVIj=n_9IbR)Gmoq)mB&awc!^obr@OH?{i1m#7$|gq+;CzaQALR89sN=kmFMlxi zZ@(MMKmBzrt~Ne(GNa9EVrbk2Gbo9+`uE92XOrQ2TXv-Xbmi@ICo2Kk-Ut2p7JwNV z%9168KU(mdm=!0TBD!^E0VL49bnFJ#S{OWW{5!+NY3iuaj3{!M{Ki+*+2-%>so_C~ z9il(b;Hw?V@=2&y&sw(`JGW&lPEk(VS*q1sW9^sQAh0Og7ge1e7%|LspwA{)I^`Nj zlUzy;+Gqsol(8_i8~k#(?MC!drtqlbA7yU|68{0-%#tl%{I6>tjW7C1kK6>PYyj-P z467>ym7P0Q{yJ(Ro7Qr|+ysF20BrO%TvWvHI&Epu<^hxUy@MRcUNgrRc+#GcV>83) zm^%d%lY+bumPvpMh1FI}V+g!5j$$2dK2;;#{FSmo8PfB$4ynNJkxoYvmUOZbFW=R( z+m%p`(o+syZ`EP%qlG4Q-$Q67`miHX_{r25KIy?i;?bs(c8zMC5*o3v8Jl;*F*Yo3 z-01ENks{~9Nd*KPAb>^?fO6|Mxnu0Crg~$WrUrdZglqOuw7n|Y9)8f4#!2eB<@wU1 zo0WMD)rDjAN+ROq{TG;FzUam8-P!M%RAaM%*THp`dQ!zejIs5W-B;8aDA8khcIz`U z{h1D*9KB-_Ljm?m+FgVkAGV!wRx&18+BHh|z2}kkTt=RY*cklSIazh?pidC6K7ggi(MiF|iQz4&tL~|xG>-ZatkR5jG zFVGqri*oZK4!9O~%t~+QZYdQT3k?6@MNS^-ClwYoRu_tXtHXup`UcSx$mE~CqrOD; z2PB$awGOiT>VA^@{H9ZZBsM?qUO3{8Fyc=o&)#RAOS}D(ZFz~8)B;;a)p2gkN+X$! zovl+5CI+JMSMTtf=bck!1Aojd3r#g?4YoH8Xm?sHxO8fY4uDd@A>D*=n|Pa4i4zR$ z_TG^{Aol0W8ZnUp{(75}m*L{d zr?ndQw60e zicqI)k58ps(yw*LQ93%D^J%v=655hCBo+k~*2KZ~Y(z%fhl-7Gd|7$U}>U zAG?U>Z*A1ovox+k_;|;PNbH($wBM90sq`_a4O8Z=bf|SGJ2mw4CUJG(uIT|u@V%P* z^#>tUvsi#YO8_JyfNGwti61Zz!YZBtVxOBmQe)R`;N?E$0qh?FII$JD0TCD&8 literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/hanseyes.jpg b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/hanseyes.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e7001add02bf2729b55f0453c39b6fae3198328f GIT binary patch literal 2500 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 literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/hanshair.jpg b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/hanshair.jpg new file mode 100644 index 0000000000000000000000000000000000000000..bf46bb63ed0be0a8e09556865e935994ff6c1f90 GIT binary patch literal 2266 zcma)7c~sK*7XSW$00~RnatSkVA;AF=t(bg@Aug$jDQTIxV4`L&xnxb{T53`%+PJi6 znT3eCXI7);iq*Jeikgk5XehI$oN}gmjt`sj&UxqkHJ|T2-#_lT%je#6zu&toU!DaT zZX8z*fM5XdRR=8p1vFXwn2<1I*XZEzFz>Kne#-JZz_AlUqf!6_5C8zA%IqCj^UVzK zJ5?HJc^YJbb=uk+bT`oPc)C5+oNCXcQK>Yhlj91U{s|zx4I4;A63xhn#-@>IY$vv@ zEqlfBpSe3ZEq4MO8r+0D;Sd49a1a~^E%$&Z0N{`cDxUuZ3=SbyAy<_Dt5wwjgKGd7 z3_`334F0z{goACgVrjy;h~T&atyKhiT4jTyHtzp@fhyS)z``L_W;hPmgV%6}l>o!v zqM%i`It@I=DlVl)RPB$!^N{;mj?&hTQrqvW|KqnE@#I!^lJn)5ILnbWLvhpY7`yM2 zEwY!Ii~4q&F9Y^m-|@i*=LU>ssE=G8btw~*`m&aREtpe;HuQZhwEo@Ys`P1KxV`mO zXsniNeYTalH*=3iVSrG<-OhFoVgP_;fKCQ*V02de&4)o$2XK{uetrvaa(+BJw<%7n zJcgg_KDGVMbkZ*<^_wz9?(5^@twt_^BZVGaXWypnABrwHKi{#!BY&RFjj{bHE<%}B4csuTr4BnA9XcK)?}`8XvB946%xDc!pFuik@1xn) zqXqUF2~RP;*Ov-f=XQQ6@NT9~Br`R3WTG@@F0`DSKSx4GwoMS3)eY%%dOf!MDs$6} zLYd-7q1jn1UT>T0zr45QRKlnNziFUH5*8z7YsiXt1OeXSCHb(Bp4*GB>8_3+e<=xd zxuGXOBTJB};2ZNwzoE@ji-|Q%yPrOdS4Ix4YidoHkYPjH>YgD)=#hZd$Xa2KG*VZy z*B&X;%`u!yHYf@f(W`^}QiAxT&eHw?DmQLD!^Ru{+?Rb+&n5 zJi6@O8=;q!EJ&~z93yJ#(4|r*9Cv(#H z{1As%Z{`=0c%vy1T@w>0>z)1dN~;VaLsFe|&UhxVwM!KfZ;ZWL@ zNl%yN+Cl$Tn_V*y=@_R@q-XYE5Jb&UuXHPHLZWGUVmcgYHddE;kx-}YD+f3Vbs~$c&Vqr3!!D>>|o_6##oLx2n4ImKA=;vsIdg$bFR6fuj;DfHDjVK z#zth2j&8y*C?<&B&`I=TC-xLAiHPnDHbeS{wf!J{bbwo3HltJcvohLJw%*q3!NHna`(lT8oW!cQxWACmSF4-JCz1xa|Su8J$zQlhRJ~*@vq& zJ>@vf&5vjqYvHLq(H+h`WY$R)1(Bot4UjGZ3mnne_o*C;4d&$e=Hoi^Bsc02m$0$C zRRRkS^tfB@2x1TJN^QA}OD5?Q3ChvC)UA*n4eL&mB!oY9##c+G1KfrKuVZ^-Ui6;X zy%sXcWemPFU;}`vL6$^A)dt|mGPo5HXP42t)Z664QYN-B&+i_4?*8V@5u%%=8(ZXl zwBT;od5=0)xmOKAoWmU@+@R$Qyh6Daxt5$3SC*oPF2lHOb0K6{TAAp`XR^Lkg;@nJ z*P+U1%Dr4d`KZ~+84x)=bMB%Dp{*TG@yX zZHYd9Ldkr8-;ODuT|5+dngHMqs$Ndfh5@{Hsr}LZuD3?0w^F;@J0>jUW6He5l_i@H zADs#)s7=^SG$nXReQR-bKhR^X9JduD#}Onx0c+%NY>M8hIX+ zo`-^-)Jasn$PjMAv(|Jh&Xy0c^kIz@iJ4q8MRPzRBMWfr#Nt$%P^o zV*HGM*Ik|XGM_pkHt65uM=H65u|&U;CYXIPYFCQ^Hu1bFN#&j_E;dh|plw1kc0sx& z+TzNvxfy?faq`&4s(v^r!vN9~1|17>xE9iEty8cogu%_eZ(eCw)X_yo5Ulc=tui^I qLJx*wI-Kk6*&FWn1}oYnuv`WQJgABa71uEy%1RKSdNU`NC;tO?(36V* literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/hansmouth.jpg b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/hansmouth.jpg new file mode 100644 index 0000000000000000000000000000000000000000..09eb9e014d4c4271eb1fe29007dbc5e72eb254c3 GIT binary patch literal 2196 zcma)6S5%V;6aBs<#E=a_5MscDA|fF{0})UWBtR&N^bkUqDjkvH3W5OxiZlTM#Ra7# zlu!c!M2fl~O{}4di=Zyeidc7(AKkO(f89F|bIzSRb7mgqiYLW$fXrcwLlyuC0RaDv z0K_6d*2F*3H$df3xX-Zw#{eJyQ{q(sO^fvlJq3UO3;+Q5hU^Wn`KJu`7buEg~BA@;1B%E{mdW)Dg!_uAQT2d zp)lw_H4qJfs_Mv^IQYP}MCER$$VnuZnW{PZ?m_=E0Q&zKAX*2aY9hN`JPn{WXb2jL z28@BV#^cxBj{{Vl9g_70)e?2s%ZF|Xw!$5|oB=r#R<5;I=Tl|pHxJ9(_P^MsYR(rx;_<{ zK`tNV+j}jh#!~%awxl-;>nq{E3dttL=+=t3< zXeeHuD|bI4OHXM1#8n+TJ|^#M7~CwXTj*ko3f(c5-93Z6gXF9vMu2Ne;Dc#iB664e)aaWT&pUrsB;Z0h-k%BFy~m6! zpVxJ}R^erZT5BcxhKG3$BE5(4X?hecmokGUkOt?F5$mMcluyE|@Q|)-ltQ@Hlj3*2 z@r61et(-!TfUN@{2m-}4B)uA@nS%9UDa~h&m*0{=sh`BLbS!+=34WZv63eE}I2C90 zoXx!Uu%&w0HO%GOCIffkqsJP<${JC8^2e7sUy3XJ22PoCaV}X>W5K_9=2&Lj*LL@Rj>q!%Lj*jywJGX$&Oxu#z}lgtBfXok6@%Y{ zoL79lhBwUG7PZLtJ>IKU&|s#DMW~_?tl%9imIBf@6~xvB0uZcoM<2l0SBJ<-x|%7V zs9Dx6s3&pGwiNk37_;8}(KleSWpneV?ERAzQQ$5dFE&6jsm@dXuAHbJiwCAa#{JaOj7J=U@#Xys6e!)uJ{DrBpt2!_8cOSl6KK|6o z0C&B!!6B{vMaFx7HHT)DW1Zo!6t(3D5>XUYY#xNgN%d1KkCNECE`_;0^1KeOkhdmy%tA8N7$$-HCyhnB+?L55O<`@p;XXj(=J;(6_z(!vU0SsUe2Av`w(Jzz}4t9 zNx4Y)$Oyd!>6z7(`68k4x7^7U*E?a){o6_Uhnv`{s9F@jLdXIehXM&K@^Qccaoz+$ zWz`W-gbKmZOd(;_HNz5axA3+*p}4S6f4Db3_l^jk)UP)=^LocSJGUw08yC_@jk)}M zJsy0L*o~+gx*dKZ7F}j)fc-#HGi@HKFY&1GG91dSar?d5anLYroW!}YUz3=dPjwTn z(pSJZYhRv+B4cheUn>MPRSFD;K}2 zl@|*kGcA+^tVnC3s?_-#h0#?If~lm)zO6wBn|PrnVdr^>Kdw*WTlHUQmt3AxyA>$QP_<{_(@r30kJ z*5wfO80{58?=z40PJ7)%`hhVWf7sY&OSn6J?70+rlCNv2TBFV!<9TN+RepwA?q#-| zxM?3R4P!82CJD8P>{vnrv`<^VOtK$2ayb)uEg8pO+@!E7ahNk!g}+^j~n}b>s7=qXG9Nn zEYl4fYVLmGcNK*#f5!-dzSKlE{~o-)hau0Fl!8+cD%$dhA&@1PoMHk)!r(|_@znnS D{ZOGK literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/howardeyes.jpg b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/howardeyes.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b5f1ba50bb1e8638bcec0345c459babfa38a34b7 GIT binary patch literal 2226 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 literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/jameseyes.jpg b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/jameseyes.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a53f9224760f593983ba0ee58a0a8b9e9e2d62d3 GIT binary patch literal 2285 zcma)6X*|@68vf5rgUU9830WFT)*8lA5oH<6SPIQdXgHP;#@GrO2e%;=u0l~n_BDek z#H7ehi*1ah5F-x55fQn}(fQr```wS{dA~jH`#kUR;pL6;rU6l`g|!6$0t0~eegM2Z z0B+(PjL1&5Eya*fb#t!|DWDpZ$BXlfWcr81jNS& z20{N8fM8%GMBBsxe!wFnRRJxwdNaRuz_b?X=t&z?6o>ut*l+MZ^ZjeUklH3-IBy&f zgzQ_wATZz@usuWzKIR#0D1JAC-#!xNm7cCt?yq?egZ>$efA&65O7Ip2mhmp7Dy>i4 znP>oKJ-8fE>>io9K4VcFD0$-4Xl*kvO2_+wpPh_e)E&8k#A`E8Ar zghzhf$(|P?NT|QVVbeaj9b9aj;-~brdiM)gXB$rtXEn2%RxBn9Lhbe4RTIji?~1Km zNP9Rql$nwUOZlPG%a zj7-a&gIzKhPCQ8IOZ4oa?zk0N%3G-rrH6KwHC1SzCq=!E{iHIMxkDM!hCUZg#93#a z4{L9LG^w97R;BWQCF^yD%9!(v(d)@4VucaUd4Q7a6&~O|dOdp~?$UA>WYzL8QLkZm zc1@SP8_vYJu9ya@bnpNL{N)eTecEfWxX7IhA%E-Oo`)m>I! zo6k=z?VS^;zM94z>ej1DJLswTOn5lP+^nyx{)PnO)aY{j`OMG``h%%=y#zd|`fdSp zxmrBNn3Ksx_F?>bcZDM=xRe2AZ261G;M}9Fw<%R;D0_u8M?=B8BH9q9f;%T>$o{aDrQU&2FTFE7K)G&c<49Zg0QSanTYRFbSt27P!8Xaj zL{Pm()&BG}O6l0?`#G~J3VXS)!+$^=DP2xOr?Nx&OSPyd*WX1GnoGha)+P1va>5lE z`0v@!;uFZw?}U;I$#TVS#)x`9$oS;zV#B*ST%CyrmU-w;xRVV?l02rJF-F8k%~Reu7!xxx_gh0bu`mOdr+(J-Y2#o zC6K;387be_P0S=%TY{I?s%<6d>2{m}LlsolPS}qagN)`7t|HmY;X|w^Zi(vZ&o-~d zFHg z=Qo1N!(_Y0&SMuIZ+O<;4H|t?*(vYdWpwC|4MY9Yfzb;=d(@ICovr&$Hl>0io#>SJ zbh_qiaAZjW^(P~|@I3ouAz6{z6mC=MpwViB!qKn?nfOkS=&gmy9(hUO*|~@K0CuNyGalPZ3!Q zLiQ({!^fwS+cfaiIB$!-<8mcYqGxZBLvFXGK*w3b{j91F=k}! znA?9i1VY}{H`w!~B%a_RoN(bJ_@RrsaTm$e6=nuCtk?bLlpMmm1`D?kmuOa7bCJPW zPeZb!s_LCtrjKs*vun3X_T<*3s|Dh4r{#J~q+d$ElO}CQJiPg0+%-33Pk2-aaZC2; zf?xLdrmdyLmY%9-(@l)iOOFV(*<;k^qSRol(f}nOy$IgvlSiM*=O?*9YjH;6NeDw? zjH@e?2k=F4hBH%MJk|29NgB%4ST@C#O#7^2v^}0X`8zE*OKxz#%_oqHi+^*j88cK= zjQ64esgX#)L>h!fWt(oJQ8xVn4;K0YE^^k5j6XKi8qZhO%|tz-^ME5>(m$Wb?&b$0 zUin1&=D4)4*-|SwV+4U7H*Eb3s5GFEi7(M)807>Z?!=B6^MF>?i3=IMtJ%c%85{;* z@^*=Q#vyfxoF@ydY{U)*-$!&dX^;Hr0byT8NkE#HJE2teW@wL&>wN5Y`a#_c7^NXu z(J=G?EA^3jjT~+8l=;xN7@L%0?wtakW+Oj0#!I!$Ik}jBSG#WC-~snmDfGpO(Qc_F z!xrzMMP0a09Z)uDAe|GH6yZX*CL=xv;=Gg^SPm7Mb=k3e@0l9YuZ$b7akc8ehW77n zF$+eNJ$#CG3PMsR()^MRvE^p!VuT5Ti6qA-*F}eECvHfde*(FC_W4tO8G&SH*;V8b z+uV;uO=DFTRKKO^Qi(hu@Y}#G^4B@{z@w%tyM#|AGdNCPM17VBxIb9^QbF{c5`*Kj z7_26|P|Z>OSZhy9eJ#IRzv+))em^00cPsiC=j-jfhB2454mCQ%*DYuKHBoTVl2)=7?Z9DJ#Oo>i8XqL%JKYGTI_a4_fa2FhZTMBQXbA)>RHzT=ON^Cg|)I-JD7F zvG@SYgnI|{qV+w?U6p#pY8r=67Tt3k@r`TKoxjBHDM%qbb~^KU+H7#mGLqfMG0IpG zQTb#x5>?R@_vcU}r%&oE+D0a%h1PpM;WveBfNNpRw96^@n5Xt}QRv+HrRwIrudZ&- z3d~jgh=L73f#`L+4qr}1t9r1`@q4FLq6D!VAq*>JoE8rK7o0_eGAb|jyQ^lJSkm7} z=*f4B3++Go1B~#u@TZC=U!teVp}AKW6}whWDb45PzOh&AP)XqW9)q>epBIleKEQq{ zYf!k=gQz_ks{SB{LCDXYnR9&8Dd6lq;1(}cYCwo9xg5ZU)SJ73%vyB literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/jameshair.jpg b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/jameshair.jpg new file mode 100644 index 0000000000000000000000000000000000000000..52d2ca53ba960aeeef831ba2fe66aeaf92e9e5cd GIT binary patch literal 1482 zcma)3dsNbQ6#xDJono4(;G@hWQxj7pH7lFifaQxuD)}s5_#$%X6e-;St!Ah#G#^vS zj-?`@=0vo#)J!b3JoyT78kMe9&N=Hidot>5J7@p(xqsYqKKJuE_nyyHE7U4r=tuS? z0|*8{yrzKq12FQ5PmQH-^kv2*()ZJ2;yLOCfFfqZF*pDM2mpY#Mz#nHKFL6QkA{KN z)8IHTH8HWmSP?8O30}^2&R#q5&d&Iqq&*)&`W!%JR#x_#?C~2m;E8y9Jds3ncPD-{ zex98~QuhFq4#XZt&7s-{`OV= zeS5V^Y-W8puEGn+!jOweo4*;RY|U#TDkX*U?%4*>DO|X>W&>gkw@1_THWy35ATyDz z-JTF}WP~|2!>gUzEhrY|Hube+HyvHPS?|EFyrYWK>F~ZsOd(b-;VUP%_4Gwz2+4&L zO>e)tRvt3G+_1RXm@&(|l^*TXdb(Ta@c8O?D^c0)52oCgB+_#0G7haXr|@}i1dqe3 zd0!#y(FP{GGXTKw00y0%DUm7YZ0&O;Y3YTh_Q}3O zV&7?&Y^LK>C)GIo%)2b3Y$0E;?o3_%iI^O!B#}4Ky#K|$Sf_2huii3vTsaM0gzS-r zt_Bvd^u(=&haIPeBE*hS3H=ON*6V|;x9OMLx@E$JjJvg-15TZfWL+=Oe1(aNCKp_B zzj1&a+8e-%w@)M64Gtx!G_h=vLUZ}ZXiQq}g?2lh+wFSEfWsHI)T`R2*0;0hT|3Z- z0)fe1QCjqzg`=+ciUe7Jd_Jbd-E%nEFMmY0rOudVt|+jWI<626B=MEAiE|t2A~B>1V`$^$hhq!RSlKS!^wOATL)EtP zYEV5j{pLXcjU1$uGz%()69N2KE@-ybybcmPeYNEU!J%s{cKC7_>VU0*PWs8HwV{*5 z=cB|FU0h&Bt%QN1q%_sw&Qd3Gl2p(WSc=Wy(wc>mxw-0C;ZIO6YbDDx1nK*I*i5)D zHnUoD?fLtS`Lcd9ruk}W>(&dUW%u*yRv-pVQ0nm^ch z?UxaUp;3n-_VC~z_=E!0qih{*JzLx$A~u&G-P4#MB%DthJ0uNKHizDuosiw|@Ei9K z*#y(?hs#?Bbd$06a#L*V&#t3ej0(ps&Qh3`k|lkX;PBhTDvCEPV~2D#;ak(pk^Hn8 z11z81=jGc5BZUn5pfOH$6J)od(?u8cYvc@9MVGC-LbCAsgMS47SnMj9ll%i)ypno) z{cMxVkJIA96}#MaB!#ef_xHn>dJFpQ5MY(bmx2n~jdcp191oIvt}>fXvfs5$;Ha$q zn9GH-u~13b4a72k6sOCd3+ksCiTF_z$^ji}v>*~PPV+MfR{dGZ$g4n|JH?P0>eSxs z_!?>xw8Mhz=?FY% HR!{v6&=X&; literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/jamesmouth.jpg b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/jamesmouth.jpg new file mode 100644 index 0000000000000000000000000000000000000000..7b0e35c187261105aaa41d34e5ae9e0d02fb3d03 GIT binary patch literal 3251 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 ap2sBt4?WLFu%n*ZKtd#&KL4@9k^cdDE8Zpm literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/jeffeyes.jpg b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/jeffeyes.jpg new file mode 100644 index 0000000000000000000000000000000000000000..0608e070772850a49ae30f7aa988fc4893badc76 GIT binary patch literal 2594 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 literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/jeffhair.jpg b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/jeffhair.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b67834a5c0154510ae4c93968e3f42782e7136ef GIT binary patch literal 2196 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 literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/joneyes.jpg b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/joneyes.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f7440841e6f33378b0531432e427954531cd8f71 GIT binary patch literal 2726 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 literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/jonmouth.jpg b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/jonmouth.jpg new file mode 100644 index 0000000000000000000000000000000000000000..9f39f0b1e4d91ffa04faf4e77466a608c1ddd573 GIT binary patch literal 2174 zcma)-X*d*W8^@nnVshvhi5X>QOq89&kbRUr!-OGQGsZGfGNF>OyyH|9Gqx-_*^=ET zWeL-9GWN(=qMAA?ggU~ZUd_>aU0v7v@%)}||L6a|@8|wEOELzI=VQ#&3Ews1_(+~5vz*TRZ!5i)5YrA;q8o#?Y;;9T^x@W zb^{~=q(EF@5ETGLLSRUUum_w500!A%=bb-*KwXAc z5hY0`pd`?{uzyYyogNE5QZ+c7eDC3kS_52k1ZB^$`3c5?4CaQ0EulQCQoFk9-E?z( z-~wk0(`U3xug|4^MJlqO?Zv~zw|RQW@JctZ(hh)^1E+=H8zTeBaE{l*;DiH~$ij9HM3_6G=l|B2O-CMlFVh zoBCTJpy;kTpQhpS7wChz(Rpc=OeI`vD*$uz<$%GE?16&45QCB_kASA9gDdUW%Z?_Y zg8BHP+8$|w`HBkUD1Gkb9W!nn&HhHPb8m_-$vHn+gAxS zR=9-%npjO)bFQ=#g2i7KZ_@wPd>LZ#$Fm8yT$y#vybW!G_tiSfm4E4N=n28a#Qu;0 z8-MFj)x+_NuSp7iJ)dDDFGW!g2h{VPiAJm=^1df}R8B=byhv_Tz%?%zAvAg1&mZIT za>fMJLLhCu|3KiI_PO~Np=_4fsX5hzaLsFoPa;JOCJ-%Kn3RfNCyWH(FYx6SM?$(m zOb`z$XNtp$r~q4}jbsm;p9$yxJ$#} z#mP3uB9n@jQR2@liVFDl&Q&K@s;@qdr4f(95=uf&)_uEv3AJ=@Fb=Ue=}p)QVUmnv z>*I~I@=;8fqpJ`ExUp||Z5+u6Q#)<1$zq@~H~{Q1Bq+;VcVz+q1rVtx7&r$_W4enE zZxCI&MABbxY@Ee!X<;kcof1_;2W)dkTMIsz>}&U?JitWm4ooS1z#8_sRPcCwl{p-E@ubB{?j;8KpDfooGY44@3WDj;l(V7ElB*#hlh2ii#C9b% zK0W0dn;LYn{o%gCIBWSh#5I%0n0{>kmFUA$HD7nx7Yadf=PfsD1uu<&nij94n?`=c zwvxQ+nlz2NGHUoO(`O%+7X{Jul3KH8b- zN#QK!oTuhQm|2Xl%bw3nuluQHm#&#~Tq>)5J^p8~*{D`R9Cp>|co;M^!@zpHG6{}r zd8kQKBasQqiHwhQ!Kf}xoWK0Z=~1VI&(9_>*$934i|5!O$)Jw_*_Ke)ot|V=D>hqT zE}1=d70aP43BidNqe)TA#zxt{2K+64uSrYt%3O^(dx6xFSV0|O+YVL~`_;-zZ*=%v zW}cyE7oo=%7v1f=Dn6|A7~D5C#|6}w9bA%E5AOZVKhBbz z6j4L&Pj=B_;grXR%1U?BS>oy31&idz^zZau(YV#H)mj5}$zh8rU`39cY;3o4(@b+M=3#RsdxUgvaMi#Ju)pIrTA0- zv&*?|T$v+kL<`74IAye&XXp|ge!D8j`<{A=6G1hFb0}reJy+7Gw^C#nd zGH1CN*W&sL`}S$6UJ*nI+W4DX`0GGsSk#+Fa`Ijew!|xq;QBq5R76w3n=jXAuUF+p z4t9&~{9XdM6oBmx1{UR3BAyD=F1=eq@E~9cG-(Y0n3ZfMFtP$r@)kfKwHTnm*KNIF zYNA_2UaXQ2#6rvx0mR+k1wgfL2ZCB1t`gj%!9pSrK)ja>8nEEXNzsy(Xokd5+??GO UFyy8KH~~O7R$?Lmt|*-T2LO?w4gdfE literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/laraeyes.jpg b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/laraeyes.jpg new file mode 100644 index 0000000000000000000000000000000000000000..24c8778e5f98acac53c382264a2be5b9cf70b06d GIT binary patch literal 2275 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 literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/larahair.jpg b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/larahair.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8c23d01953d89b19ef7e17e3b34e4fcd04f0131e GIT binary patch literal 2324 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 literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/larryeyes.jpg b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/larryeyes.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a2b771ec70977e915b7451bca0dee7de062e94aa GIT binary patch literal 2412 zcma)8dpHyN8~<)&zj7#>G+~iZnw7b<4kcl(MHyDcTE<)_j!elVx}0;0Ixe|H9mkz% zrU`8wmrm|;DYw~hELKOvp%bSh`>EgaoaguF`Mm$U&-;Gf&-1?jyzeU?l}mxDi-WTR zfItBV*f1bp2S}TM2s%U8IovOZL1Op?#LC|S1d-(*77HLi0RRBspe=wcUuhtqWg`NS zkAZ6dwQZZub{$hR+SJ;3kFm8S-q;v#X>a=l_WuSD4ILdrj3IvKPCOBBh$q?;2?XMo z!hct8Z!d2Fh)p0G;s%500Vo0jLqO!MfCT^)27zp}`43WS5*&M zCG0=ewAYrD5#^DhH8lm6QNqn;{ouYC1Lm^n-vqLVW1$fi5tR`)bnlgbQB*AlKR%!pqbzh(=Qzyn3RabG!2Wl78dH3#S3yIOY zpZI2?$Tejq{OuoLN1ifSt=4w#H*@dUl3aIudxh8Zu&C11yf+v?6pA^tMeD0sUbs2z zdditLbdqi+6X>SF1K#CCd94tlP5yl4SZL(C`s$-*#)DPu-h$D2Chh#raxKOooTv~( z-B;*(yVMg?Fg0iz?d(M5N>Z1?EC`ev?;L|e(&uWYh@VrA*Pm<5I)!Y*9$_kD1rL@m z)^ju|i&oRI7lu(=k_Z{uckL#oGwwyAlm`#2de^7^#w>OGF(RpI)+4NC$DjV_ zrn2_8j~?5KYHXW2s=63cKXC_6Tt4+vMy|`!K)f8-q&UUcIVgRNU<2@DQ*+PlDO%b378PGJUVUOpVw>Y>pu*b>pftFv&^np382HY_ zi%#Y*_aED1CUmcr&3QTZ{Z)x_T|P;-WKA_;4{F5wv!c4w>9~|{)ftEH2H$G%6sl20 zl4;6%V15W$*oCeUom0yCp|iT1f*UO;9oC@6&pD zu{owbyiVH!x6BmvCG={p_)z2aDp^;Q-RFso+_#_u^*VX=9?N^9`PpN$-F+mzhEsm7 zIW6q2V?1-0dbFTNxoG}@{@5!y;I>>JPQwVb6(JW zAh=%leqMP-6{WmO^Vx7fwZv^RxuKFk;@Mm=Z4Gxw@w+if4l&L=mnX0@*^^l%-gc&l zYtkgHzIgTfXb!)Tc1`W`QTuK>B7sMgQKJ#hP(Hw!eZ}V&?4nynstbA>RM-YAHu~%HQ)q0 zZFK2xcdn=R;x|{^JFBZ_g%<1Kq_Da;bk+WL5x?iJ5jBU))QHipVz`8n`^fJZ;koDc zSBwmL8X)LvMKc68m(<-V>Iy;9piRh z7S3R(i%&< zomfe>H2=*)GURx%9KRM zQ`NHRgKl%^dC&Pu64z@}8npQ2*MYZQIlB3%9Z;HHpB7KLP4)x~PK^D`NJ2ia$^eW3 zqmY?XzMX(le76g=0I;as4pr=D%#BW<7~2nL{~f&yAkD`=+pes<2UykD$Jcp4k!^M& zChj889Vl=3v|VJ81_r{cpe+hJ8c;JyFkBqJg}VaGd#T7Te-ylb%OPG&4uEHr0I0QG F{9ks{I#mDw literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/larryhair.jpg b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/larryhair.jpg new file mode 100644 index 0000000000000000000000000000000000000000..5366c15d599e99eaed8d5c269a7bd689e902e7fb GIT binary patch literal 2054 zcma)4dpr|*8~<&?@#fY-F0qMR%55>Lb_iu#Nh?t*%`hwtYh-1JRPQOdD|085iqXxb z$Thi43Xz#h#}cDas9a)aozMGzKJVxK>wKTTp6~O0p3id=4~Rd2^^W-ccmRO{;3F|0 zUIQEKedyl4I{O2?sJ=&hy?nyNp8?z8{|g8dCM7NNi~L_!G6xj4UP3`3Fc?%yS_<~J z9s-BLHgDfx?=0oTxb#R@I@v;5Pqpz+OZ4p+75M*G01&u%5Xi$MMc^ezkny^-+AI4nMXVx{n2wN$mk{Hfwgs6RUwdD`)k&l~q zP0dIAah4VJ(eT03rIgyzEr_8dM==mycs@s|+23|~AkV-oBJO=W=0SFvjEyMF*CY5V zdTAqeagFG}+MJO6xPrY+<3C+X7PkZRK^j!n>Dj@r908Tp8TqEEoU;*$Syp+)iw~E` zzmvz`mw%i;KO*XXt5sVwHzW#fMQ}TFlIzy?6zZqAB><{+D*d*=&`K5C`83mQBg6X#;@mry5y&Rvybk9=|PQXsfD*4tlmVc{jdNK<&NTqeD-E&j>Z zU1e&QuADBiM#zANs9JeX*L`P9pl8%r1~FUl>3Ebb^V$=IZ2_(nicJNV20xmmRWp6% z)e8QYcgb%Y8iuPySyiflSL17_1>Gn8>u*%u_-HL)gC7@`{9aSt;#*!*ddEJ_T{dmh zOh|(5^TR_g9ia6VC?S7b3`!+j*Do8*sn65T?Q+L@X&y|^sBct0)fZ4_8ZvZz^ajD> z5nC&O>)EE~IH{BL1sx1aX-<|}&WTNZH*uJd;P~oJIBb)gg2^FYxEIB^NIu|TptlJ; zp6Y(Qt1-&>@o=bxgqi9;1Zq5AHF5L$l3AO{ol;c<-)z{ zKS|cChxH{?zsxcHGw6PAghLJ#bH=Dtk05_87Po$^$Tdbc^q7(*FQird#XF)hCDN~| zZh$yXK9Ka@rVnFPeXvQsw26Lnw)YWJRW--o`KZP~*DH2`QCx{>mPlQ+(bAc5L)p^I zy9$p*e=>RF(({ezkUuF6+eVdz^)H@ZsL7=^k9G@L+prB79*~uJljh#a_}s@f+#=`^ zMH(DQOc@NFvPv5o8E%;li9E_L9@N{rzmHcmIWCwcvGjw`j*`2moH(+zr7gW$u1h6D zwa=axtkN<&;3WG@m$|zoiRx`~I!oF&rer48*x2RdF{bRB)3H5Iqyjpsc(Jc&Higy# zh03_q-5dh;%{wPmVKC`lLOdO3*+t92f;%P!_`(Z{^1keShxfW> z;C9}3==0c}r070*(QUxHh2)AjKWV*oeOg+2Y2s~d%62=^8=v8O>iL%LR|D7D39kdU8}_k0 zu2%Vf7lR|Yd4GIDbE0;Tk#30Ggb_q{=DQ$YRd}_*3pWF~qlJXh_$_|7H5Z$|Zk;UQ z*{|BVlCb9je?Ro>a;|^s7yt68moQu5U18=(K0FlHOd6OSf}f2~>`9S7A6HWFkY8=d zyGnnu?*4HR!=++cDLGfPtCvp;yfff2uB!Pp+=}|DJ2M}i&l+;$n12skwod;>{i*i3 HP|W)a$j+M4 literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/larrymouth.jpg b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/larrymouth.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d0642d274bac14266386ca35ffd31095484f580c GIT binary patch literal 2927 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 literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/lisahair.jpg b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/lisahair.jpg new file mode 100644 index 0000000000000000000000000000000000000000..3eb7d4f30840f36cb6657f582b85af54454bd11a GIT binary patch literal 2237 zcma)4c{tST7k|GqLu7eojD0tRMi}cw$V3IIp;kBo?rnW-CYT;0D=KP z5gs7;1>~G4iD41ig!s^?2%m^h$|=DbK;fz3v8Mn85C8yiVb}(c{W}a$*g_8^mVvKr>i-yo1?PaYuo?^nM*#=W3gFsgArTys zrV06JkW_i$UTW!bT%#;CWh-HD`wc|rr3p1Cudp}h8fJ3_BCEV)9QIB$?H2&@Z-Z*| z9~B$P>z5`M{5D>!XFmJUzoEsfdE_3~6EYsYL1o%6G#nV~_P{8SDHVH%C6nbL8H* z;@rWg(LDER^;@CkNnQWFRk62fzqBYWL)(+|gp&#FGZ^ zA{XlIy(}^=cGvAg`no0Qd8y6-EZf?cWDh(gfN(p_02_#D3mZJ2WXXY%gM%ywwQQcH z`f_~4bD4qhiXFr%?PNEr|Dah#)0}Z%zv*n}9+OCFn2&fhuZQ%YlhhSRZ!`Dt4Pxpa z_CVnBY2c7Z1zUQn@0b0!$=rT}v5Cb39`@qaCD%h{!X1Gd1P);5B*zA5CV|fUZ2i^8 zlG759p%M}U2S`yWaQoZ10a@3)V#w5)%0>JJte*bJS2^}QA)0kL9OGlj;oN1h3RlB* z?0AP0JR;8{2soHLB zPP%<|UHcrLl3hu+LNrh>QCW>XtvALEdwzhWp9 zF_`88)Qtc#vIqh=&+w8C+>E&8P_)h}&QNfB8dfx>+Z)!kH*u;hk?z+f<-f6u&1h37 z-NVv6yW6Bi0CbX*q8^5rsQ3niDX-Dgj0|yVnyFVj2@# z0e}+^z~j(=ygL=3kzyD3=2XnEjZ~5U@U70gS!A6sREQcQyUa+e!G`f9k)|A&i z$5Nat_bWc_F@x?-3U+IcE!;Tm!MnZM*SM4t9IBjgz5l7p61&Zx5&tFf%nnataWOW) zq^OabM-6;*M}6E_FQ>XDCEKb}NxVuj6P2?!i%gR!a)hg(GCSmQcQo5vDmxP42p}Gm z*&^|7i?vb0a6M?T{9x`Vu!-9jCL`+I^!_$ z6Ph+NR`zuV72|HI%(CQW74@*Aa<{i;15WReBK|zu-dU5A#hu6RRKN1RP3y#9eewtM zD?`mcQ|-#z@lUWrQtaX6wZ$8;->c2eEKaEh{5&{3{Zi|_XLy|Mp8by08uF8#WeoN8 zt@?lkPbX@@9HwJn#59B$eO<4}alifR)X|oKj-T#pmxvkz)*t?y5BAtHT-qYqtZINe zmCF@<{CkvnMjHdh88Wzh!s_^eDbk7`sT$Q`trYC>(CH8jfdnIPRSW^TEU#D|mi5uD z4oK|Q2Ju3$~0gJAjqyU#FEnTZjItM5j};E8trLm2_htr3RZ`b zp$01cSb$Hy9;qxJ(?IO%9B9M;a9{o*`&>q!MeU*wCppa++(Y@h+D-^J^!+>Q0ZlpFHN&Qdm1g?@@4}2b{o$%Fm1KT8#WIe_N~MVKt}QcJ{bR t@2dmh&1IvEy8YHG63n;!rYU`(EeXIbR^8(RT)N_Gezg^VD$WRI{|l#Rf>!_l literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/lisamouth.jpg b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/lisamouth.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ac5650299109a635794cffd617f2035e1a086ad6 GIT binary patch literal 2245 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 literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/michaelhair.jpg b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/michaelhair.jpg new file mode 100644 index 0000000000000000000000000000000000000000..271d20c2c56ca8f636eb035e172ee14156723101 GIT binary patch literal 2130 zcma)*doPwfl;$$pT(dDmnbXQO)c2@R_LWPMBUIQ( zZn=HkD!v$!n9J5pt5hmqNh;cJ^*!f%et&)+?{nVgeV*s}e7)bV_j$jilhS8kGl}Rz z1P}}Wx@-XH51@pjA3MU(afu2JV|Xxv>2cCIfW)5&iHrjfKmY*N$ZQ|L#$Pr-?~*By zR0PffH5CYisY&+kw^6!s4+zuy_LA+8X~e z_}}6Lg0u@D*^P)6-&0|1lHiCKy7-0RzhN{?aXpU`{eYBJ%8U>PPt(* zGV54ny_&nK9p^ewat(qx%G#9yb4V$`2+wFG4`FZloG#CgZ&>-OdsKaVu6yv~fMwewCWmrj zKBau8!Tv0$G5DCn*1}H}15;&IQ5vP<*IsLKv-6`#>aCBhDsXav0hu++mH8{25^b*S zCX5=1&FJiIos6~>-?5x9n&KAu7WB5Z_@qg}#m|q?8lhXh8V=XL3n`lM(GcgS@doFH zcC~Ic3^y4ZeE9>l9Gb8~^saqCGo9fJ z%--oa?+Mf#?a*wZpUnLI*Ci+c1f&n?;B{DtjC#7rAfkol^tGPRPP;1Jw0L4g@flHfu(#Erf+*NtOm@PGZDU z+`I&{I)>Y;p;PtDAmeJd8~e5-hcq@bGql!KHGue%ab4c+k83Fa!e=|newhwV2LL(W zKd+$JyZ#u+Wrt}uicgLCd=W{3(GSO*1+*aWd*tW6Z*V(V;w^c(1+4yyv}lye2Wx(S z)zf}QDbR2yH+Bm`4*u49CBEK2rZmz6JF$Vom}tD1R;B7~Q60~@bc>hH%fshTdxFk! zQi13B4QgrX>zI5e#ReExIgu64q~W&y*}Ug%<|sSLQ}w78xy83OuZekr<^3Tn!jdjj zyyu@psbI0_S3G@gPcuG?^Nbvj`UPG$BkysyWY!(`I$2AXr0eC53!c3h?{6OHLgXsw zr#S$i00W9QCAa10LT_ob*=s_VB;Rd4Zk${=F3LBKrcJ+}{@zZS38(q@jYb71+Ma8( zd>p;%c4u{jKy6Jr+_}-f`vbi@fbtC;afouAn!AzdZGaQ}R)wFij0oc& zW&TTxG^B=LL1jfHW0gEsiVaHqXw;kT!COD!d^rOXq;uhNMz?o~IST8B!uKi59X2OE zbD^BqO{H9@8_JebZ1CfarA8^%Sy~zhVM-&Jg7;R375W+seN01{W$Bpck~}J|++x1P zt?l8tzmkm;Vk@~O;5?>iA||BXpM3X5Nzu_Q3|#Y2L`jTSNS3+m$pPfCLwb|l;yL}o z0Bh}HbJwhZDoJQvebJ~Coc@NX-}e4kTi=}aDh+TBL2>=VMLJ*G~@Ir*r4#1sUxsdiHk;L*C~!sEmMQ!Mawr+wKg29z?s z@4QXQ8nhbz8ZbV82q0#iQUHI)1^_G#yFixWZD2T%f^cX9fG^(+dDQKdEMGAO;RB^T dmA?TH+Ch9M8UeWn$`?&QFvTemsGOEg{SQE;kj(%9 literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/michaelmouth.jpg b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/michaelmouth.jpg new file mode 100644 index 0000000000000000000000000000000000000000..02119ed0202e0dfc3252fa3b2121a4863f4d0791 GIT binary patch literal 2399 zcma)*X;ji_8;AdZ_#!SNE@_)6fw^QTq7~IqQE+e61{(JbRB*{uY+BS@vRqo+C{3qa zvXoH60JSnrQ!Q&eO;F-4_f+f0#Z$!l}Ye0aaj^?Z2Fxu4&4pZosssAtr2K;MV# zO$I<<0HCcOK)nVSxX}*B(9OM*qvPp8^k~{K^%8*aV8tdL13-WV005fnvA2NU-!XvJ zw_X9MWk3O7Vq}EefW&RxigUqk$GQ;gu~>ToiTEATzX4>5L|SdLvNtoc_prCJ_aJ#V zJ9~T=|D8@Esrvu~3}Ar{KtLz}i~vCpAoU=S3IJdT2(&)tKY$?sNCT<~`p!SK?>A^Y zs1Ja@1JVGmOMgFt5MT?4gMpidVPG^0cLZ8yluHb9M|^kxuPq2+;RbdvP|pH7>n0ch zK>#kmUoxyz&~duivlLfxYWt!uTW?63n0~%j>(HE7UgmS%LBP=N$Fz6(s{wPD4kq_Y zxes`NYaD>#c^*HHMIIDhlQV?jRq;pti}{;ucm%#t#~6NuUBWwsP8ocJ7Ug!tkb_;X zBy&%e?`nYSJixzy7`l1OZmD0CE#=-e6OqD6Fpj!iKYat0%(h3cy+2#?MAub# z0(WvD4M@~Ul0WvBbPhP%G@k;WCsV+cx*EQS+rDLjRLdoI6W(*d9z4P9O~X~Oy)xCI zc<;3cQ^{{`J!$|2p+iv~ zbt*ILa>EwKU>V_Bg$AJoY)16)p||pUDY~(FK^*bU)qN3KK6Xkf zwM>hWEGAtTEIrCpuy)WT401t#<5F(vhT4X5&FZY8MfvN5DQ&&3cS6 z-oS~Mw&`+6ln(=*+o;M#-eK#gi666UchR2eY&f1_tCS>NMfAx6tyu-j4il?(`==SF zRrteeV)9rrrRl4lLyqTHmM@HapLI(OU_xaR&quHu9o4{HZYW3i{23;{qMSl)FMOq! z@}_dyFCDX*J?#HjE@ZZ^nzlraM)*2(zs_zx=a}*=y@dpJqT&+Z5;8B8VLapiRwrMW z=nxtKX;>`1+pcvs{l0jtD!VWvG)0w(!uRuEm`g=dm06E)ZU;7|UA8kp5<^`N@iWu(A1h(8P?f8LO9QZu<6N;e8|9;r&cWH=0NO5=IM4+f5ed|*LU zONyTC`HGfCT9hm_=0I%Cn%DFVni|jyN#PDG(MREzE+)MG3~Jd)D{|d%BC)*JMe{6H zv!<$=;fF2c^tV`v9`UCissV70AJX8@;^p#DvMlK;%+J|3IC~Ggrg6u(7Qh3K2Wm>g zAn~VYCLP_ixhK1{g#lW;C+A&+Jvl~9mGEO^dQIiaKbek9iqWPaxG!o{me;-UxI8=% zgIg?7eu8hyBPXWHvro$`k7F(8G8TSz^ctt_7{N5>-fjT+==nZ;_R!b@*@Mya^REM0 z1-P%r`Y)w1rOw@@FqO?L^Z9by)xu=)FW?9ef^Zx$pU^7nr`F;N(7VB?nAz=W#q?;&v) z{;1~NdG&7clkAN=aE90R=m*prW#BE8-cL}UyqYt6P0xMh4ehlEDUorHq)dhU?+H z<;@_Q^1)g)uvWU2T_)fJJvmMOvVjuT8PsHDxxW5ByPwC#h4~bxk3nBzKPFOh~SI9pkRjxvE zjvVvF4b%>ObhC|>7BHv_uytI(K4(G z+PB^J$*nqHVLRsA1)slNiFrsj2(r*dUrQd$ za$kc}>WUEOLKcj@q+B=!ALuqVIa*&7rQv|kj)2ZK&Kj*16vMCMEWK08ZPt3?dJ2`O zEkb2S#1-uJK&8jBBLC8R=dD>8y{}dip;JJojT^IGs%U+g>!oEfLd%ccKYZInV!;@nVKI@4&gsk*?&Nic!k<_?p{`jc;S=*!!V_k3-oQ7oyF?s+1 literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/philipeyes.jpg b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/philipeyes.jpg new file mode 100644 index 0000000000000000000000000000000000000000..1d308070cd23da1521580f0403953872ffe37bdb GIT binary patch literal 2354 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 GpZG5zSp%;C literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/philiphair.jpg b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/philiphair.jpg new file mode 100644 index 0000000000000000000000000000000000000000..18988dfc5cc97931431bd45a22b1cf90129a0199 GIT binary patch literal 2897 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 literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/philipmouth.jpg b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/philipmouth.jpg new file mode 100644 index 0000000000000000000000000000000000000000..dcf4b96bc74de9b2a124fac647d881061d167d21 GIT binary patch literal 1718 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 X(irwt!6d%Esd*_G!L=S^*8Kkfa&;H> literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/scotthair.jpg b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/combobox/resources/images/scotthair.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ef237cb248b9d7bafc5c723b48b1580307cd29b0 GIT binary patch literal 2755 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 literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/list/ListDemo.java b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/list/ListDemo.java new file mode 100644 index 00000000000..b8dce72ba9e --- /dev/null +++ b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/list/ListDemo.java @@ -0,0 +1,404 @@ +/* + * Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.sun.swingset3.demos.list; + +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.FocusAdapter; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; +import java.util.Vector; +import javax.swing.*; + +import com.sun.swingset3.DemoProperties; +import com.sun.swingset3.demos.ResourceManager; + +/** + * List Demo. This demo shows that it is not always necessary to have an array + * of objects as big as the size of the list stored. + * + * Indeed, in this example, there is no array kept for the list data, rather it + * is generated on the fly as only those elements are needed. + * + * @version 1.17 11/17/05 + * @author Jeff Dinkins + */ +@DemoProperties( + value = "JList Demo", + category = "Data", + description = "Demonstrates JList, a component which supports display/editing of a data list", + sourceFiles = { + "com/sun/swingset3/demos/list/ListDemo.java", + "com/sun/swingset3/demos/list/Permuter.java", + "com/sun/swingset3/demos/ResourceManager.java", + "com/sun/swingset3/demos/list/resources/ListDemo.properties", + "com/sun/swingset3/demos/list/resources/images/blue.gif", + "com/sun/swingset3/demos/list/resources/images/cyan.gif", + "com/sun/swingset3/demos/list/resources/images/gray.gif", + "com/sun/swingset3/demos/list/resources/images/green.gif", + "com/sun/swingset3/demos/list/resources/images/ListDemo.gif", + "com/sun/swingset3/demos/list/resources/images/magenta.gif", + "com/sun/swingset3/demos/list/resources/images/red.gif", + "com/sun/swingset3/demos/list/resources/images/yellow.gif" + } +) +public final class ListDemo extends JPanel { + + private static final Dimension HGAP10 = new Dimension(10, 1); + private static final Dimension VGAP10 = new Dimension(1, 10); + private static final Dimension HGAP15 = new Dimension(15, 1); + private static final Dimension HGAP30 = new Dimension(30, 1); + + private final ResourceManager resourceManager = new ResourceManager(this.getClass()); + public static final String DEMO_TITLE = ListDemo.class.getAnnotation(DemoProperties.class).value(); + + private final JList list; + + private JPanel prefixList; + private JPanel suffixList; + + private Action prefixAction; + private Action suffixAction; + + private final GeneratedListModel listModel; + + /** + * main method allows us to run as a standalone demo. + * + * @param args + */ + public static void main(String[] args) { + JFrame frame = new JFrame(DEMO_TITLE); + + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.getContentPane().add(new ListDemo()); + frame.setPreferredSize(new Dimension(800, 600)); + frame.pack(); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + } + + /** + * ListDemo Constructor + */ + public ListDemo() { + setLayout(new BorderLayout()); + + loadImages(); + + JLabel description = new JLabel(resourceManager.getString("ListDemo.description")); + add(description, BorderLayout.NORTH); + + JPanel centerPanel = new JPanel(); + centerPanel.setLayout(new BoxLayout(centerPanel, BoxLayout.X_AXIS)); + centerPanel.add(Box.createRigidArea(HGAP10)); + add(centerPanel, BorderLayout.CENTER); + + JPanel listPanel = new JPanel(); + listPanel.setLayout(new BoxLayout(listPanel, BoxLayout.Y_AXIS)); + listPanel.add(Box.createRigidArea(VGAP10)); + + centerPanel.add(listPanel); + centerPanel.add(Box.createRigidArea(HGAP30)); + + // Create the list + list = new JList<>(); + list.setCellRenderer(new CompanyLogoListCellRenderer()); + listModel = new GeneratedListModel(); + list.setModel(listModel); + + // Set the preferred row count. This affects the preferredSize + // of the JList when it's in a scrollpane. + list.setVisibleRowCount(22); + + // Add list to a scrollpane + JScrollPane scrollPane = new JScrollPane(list); + listPanel.add(scrollPane); + listPanel.add(Box.createRigidArea(VGAP10)); + + // Add the control panel (holds the prefix/suffix list and prefix/suffix checkboxes) + centerPanel.add(createControlPanel()); + + // create prefixes and suffixes + addPrefix("Tera", true); + addPrefix("Micro", false); + addPrefix("Southern", false); + addPrefix("Net", true); + addPrefix("YoYo", true); + addPrefix("Northern", false); + addPrefix("Tele", false); + addPrefix("Eastern", false); + addPrefix("Neo", false); + addPrefix("Digi", false); + addPrefix("National", false); + addPrefix("Compu", true); + addPrefix("Meta", true); + addPrefix("Info", false); + addPrefix("Western", false); + addPrefix("Data", false); + addPrefix("Atlantic", false); + addPrefix("Advanced", false); + addPrefix("Euro", false); + addPrefix("Pacific", false); + addPrefix("Mobile", false); + addPrefix("In", false); + addPrefix("Computa", false); + addPrefix("Digital", false); + addPrefix("Analog", false); + + addSuffix("Tech", true); + addSuffix("Soft", true); + addSuffix("Telecom", true); + addSuffix("Solutions", false); + addSuffix("Works", true); + addSuffix("Dyne", false); + addSuffix("Services", false); + addSuffix("Vers", false); + addSuffix("Devices", false); + addSuffix("Software", false); + addSuffix("Serv", false); + addSuffix("Systems", true); + addSuffix("Dynamics", true); + addSuffix("Net", false); + addSuffix("Sys", false); + addSuffix("Computing", false); + addSuffix("Scape", false); + addSuffix("Com", false); + addSuffix("Ware", false); + addSuffix("Widgets", false); + addSuffix("Media", false); + addSuffix("Computer", false); + addSuffix("Hardware", false); + addSuffix("Gizmos", false); + addSuffix("Concepts", false); + } + + private JPanel createControlPanel() { + JPanel controlPanel = new JPanel() { + private final Insets insets = new Insets(0, 4, 10, 10); + + @Override + public Insets getInsets() { + return insets; + } + }; + controlPanel.setLayout(new BoxLayout(controlPanel, BoxLayout.X_AXIS)); + + JPanel prefixPanel = new JPanel(); + prefixPanel.setLayout(new BoxLayout(prefixPanel, BoxLayout.Y_AXIS)); + prefixPanel.add(new JLabel(resourceManager.getString("ListDemo.prefixes"))); + + JPanel suffixPanel = new JPanel(); + suffixPanel.setLayout(new BoxLayout(suffixPanel, BoxLayout.Y_AXIS)); + suffixPanel.add(new JLabel(resourceManager.getString("ListDemo.suffixes"))); + + prefixList = new JPanel() { + private final Insets insets = new Insets(0, 4, 0, 0); + + @Override + public Insets getInsets() { + return insets; + } + }; + prefixList.setLayout(new BoxLayout(prefixList, BoxLayout.Y_AXIS)); + JScrollPane scrollPane = new JScrollPane(prefixList); + scrollPane.getVerticalScrollBar().setUnitIncrement(10); + prefixPanel.add(scrollPane); + prefixPanel.add(Box.createRigidArea(HGAP10)); + + suffixList = new JPanel() { + private final Insets insets = new Insets(0, 4, 0, 0); + + @Override + public Insets getInsets() { + return insets; + } + }; + suffixList.setLayout(new BoxLayout(suffixList, BoxLayout.Y_AXIS)); + scrollPane = new JScrollPane(suffixList); + scrollPane.getVerticalScrollBar().setUnitIncrement(10); + suffixPanel.add(scrollPane); + suffixPanel.add(Box.createRigidArea(HGAP10)); + + controlPanel.add(prefixPanel); + controlPanel.add(Box.createRigidArea(HGAP15)); + controlPanel.add(suffixPanel); + return controlPanel; + } + + private final FocusListener listFocusListener = new FocusAdapter() { + @Override + public void focusGained(FocusEvent e) { + JComponent c = (JComponent) e.getComponent(); + c.scrollRectToVisible(new Rectangle(0, 0, c.getWidth(), c.getHeight())); + } + }; + + private void addPrefix(String prefix, boolean selected) { + if (prefixAction == null) { + prefixAction = new UpdatePrefixListAction(listModel); + } + final JCheckBox cb = (JCheckBox) prefixList.add(new JCheckBox(prefix)); + cb.setSelected(selected); + cb.addActionListener(prefixAction); + if (selected) { + listModel.addPrefix(prefix); + } + cb.addFocusListener(listFocusListener); + } + + private void addSuffix(String suffix, boolean selected) { + if (suffixAction == null) { + suffixAction = new UpdateSuffixListAction(listModel); + } + final JCheckBox cb = (JCheckBox) suffixList.add(new JCheckBox(suffix)); + cb.setSelected(selected); + cb.addActionListener(suffixAction); + if (selected) { + listModel.addSuffix(suffix); + } + cb.addFocusListener(listFocusListener); + } + + private static class UpdatePrefixListAction extends AbstractAction { + + private final GeneratedListModel listModel; + + protected UpdatePrefixListAction(GeneratedListModel listModel) { + this.listModel = listModel; + } + + @Override + public void actionPerformed(ActionEvent e) { + JCheckBox cb = (JCheckBox) e.getSource(); + if (cb.isSelected()) { + listModel.addPrefix(cb.getText()); + } else { + listModel.removePrefix(cb.getText()); + } + } + } + + private static class UpdateSuffixListAction extends AbstractAction { + + private final GeneratedListModel listModel; + + protected UpdateSuffixListAction(GeneratedListModel listModel) { + this.listModel = listModel; + } + + @Override + public void actionPerformed(ActionEvent e) { + JCheckBox cb = (JCheckBox) e.getSource(); + if (cb.isSelected()) { + listModel.addSuffix(cb.getText()); + } else { + listModel.removeSuffix(cb.getText()); + } + } + } + + private static class GeneratedListModel extends AbstractListModel { + + private Permuter permuter; + + private final Vector prefix = new Vector<>(); + private final Vector suffix = new Vector<>(); + + private void update() { + permuter = new Permuter(getSize()); + fireContentsChanged(this, 0, getSize()); + } + + public void addPrefix(String s) { + if (!prefix.contains(s)) { + prefix.addElement(s); + update(); + } + } + + public void removePrefix(String s) { + prefix.removeElement(s); + update(); + } + + public void addSuffix(String s) { + if (!suffix.contains(s)) { + suffix.addElement(s); + update(); + } + } + + public void removeSuffix(String s) { + suffix.removeElement(s); + update(); + } + + @Override + public int getSize() { + return prefix.size() * suffix.size(); + } + + @Override + public String getElementAt(int index) { + if (permuter == null) { + update(); + } + // morph the index to another int -- this has the benefit of + // causing the list to look random. + int j = permuter.map(index); + int ps = prefix.size(); + int ss = suffix.size(); + return prefix.elementAt(j % ps) + suffix.elementAt((j / ps) % ss); + } + } + + private final ImageIcon[] images = new ImageIcon[7]; + + void loadImages() { + images[0] = resourceManager.createImageIcon("red.gif", resourceManager.getString("ListDemo.red")); + images[1] = resourceManager.createImageIcon("blue.gif", resourceManager.getString("ListDemo.blue")); + images[2] = resourceManager.createImageIcon("yellow.gif", resourceManager.getString("ListDemo.yellow")); + images[3] = resourceManager.createImageIcon("green.gif", resourceManager.getString("ListDemo.green")); + images[4] = resourceManager.createImageIcon("gray.gif", resourceManager.getString("ListDemo.gray")); + images[5] = resourceManager.createImageIcon("cyan.gif", resourceManager.getString("ListDemo.cyan")); + images[6] = resourceManager.createImageIcon("magenta.gif", resourceManager.getString("ListDemo.magenta")); + } + + private class CompanyLogoListCellRenderer extends DefaultListCellRenderer { + + @Override + public Component getListCellRendererComponent( + JList list, + Object value, + int index, + boolean isSelected, + boolean cellHasFocus) { + Component retValue = super.getListCellRendererComponent( + list, value, index, isSelected, cellHasFocus + ); + setIcon(images[index % 7]); + return retValue; + } + } +} diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/list/Permuter.java b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/list/Permuter.java new file mode 100644 index 00000000000..7b3dc726723 --- /dev/null +++ b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/list/Permuter.java @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.sun.swingset3.demos.list; + +/** + * An object that implements a cheesy pseudorandom permutation of the integers + * from zero to some user-specified value. (The permutation is a linear + * function.) + * + * @version 1.9 11/17/05 + * @author Josh Bloch + */ +class Permuter { + + /** + * The size of the permutation. + */ + private int modulus; + + /** + * Nonnegative integer less than n that is relatively prime to m. + */ + private int multiplier; + + /** + * Pseudorandom nonnegative integer less than n. + */ + private static final int ADDEND = 22; + + public Permuter(int n) { + if (n < 0) { + throw new IllegalArgumentException(); + } + modulus = n; + if (n == 1) { + return; + } + + // Initialize the multiplier and offset + multiplier = (int) Math.sqrt(n); + while (gcd(multiplier, n) != 1) { + if (++multiplier == n) { + multiplier = 1; + } + } + } + + /** + * Returns the integer to which this permuter maps the specified integer. + * The specified integer must be between 0 and n-1, and the returned integer + * will be as well. + */ + public int map(int i) { + return (multiplier * i + ADDEND) % modulus; + } + + /** + * Calculate GCD of a and b, which are assumed to be non-negative. + */ + private static int gcd(int a, int b) { + while (b != 0) { + int tmp = a % b; + a = b; + b = tmp; + } + return a; + } + + /** + * Simple test. Takes modulus on command line and prints out permutation. + */ + public static void main(String[] args) { + int modulus = Integer.parseInt(args[0]); + Permuter p = new Permuter(modulus); + for (int i = 0; i < modulus; i++) { + System.out.print(p.map(i) + " "); + } + System.out.println(); + } +} diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/list/resources/ListDemo.properties b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/list/resources/ListDemo.properties new file mode 100644 index 00000000000..197cb7f69e9 --- /dev/null +++ b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/list/resources/ListDemo.properties @@ -0,0 +1,18 @@ +### List Demo ### + +ListDemo.accessible_description=JList Demo +ListDemo.name=List Demo +ListDemo.tooltip=JList demo +ListDemo.prefixes=Prefixes +ListDemo.suffixes=Suffixes +ListDemo.count_label=Number of generated list entries: +ListDemo.all=All +ListDemo.none=None +ListDemo.red=Red Company Logo Image +ListDemo.yellow=Red Company Logo Image +ListDemo.blue=Blue Company Logo Image +ListDemo.gray=Gray Company Logo Image +ListDemo.green=Green Company Logo Image +ListDemo.magenta=Magenta Company Logo Image +ListDemo.cyan=Cyan Company Logo Image +ListDemo.description=

This demo shows how to present lists of data in two different ways. On the left is a JList component whose list items consist of the permutations of the checked prefixes and suffixes. The prefix and suffix checkbox columns on the right are created by using a JPanel with a Y Axis BoxLayout inside a JScrollPane.

diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/list/resources/images/ListDemo.gif b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/list/resources/images/ListDemo.gif new file mode 100644 index 0000000000000000000000000000000000000000..d50e74f75ee451857223ee52e3ab0d098730b3e7 GIT binary patch literal 265 zcmZ?wbhEHbRA5kG*v!NL1pk46;eT?|{+;Ka@4Nis%ufQI=yDoj1M8_ zY~*wtT>UmVOZ1;mIn*x2)pcscMUxd#b9dj%NwAXOiqP%PW}Up|#g<#Q{w7^7*t+PC zoX66f*6V9l-&C$C6>1)?- K++-4Dum%8G)O3;n literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/list/resources/images/blue.gif b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/list/resources/images/blue.gif new file mode 100644 index 0000000000000000000000000000000000000000..0cbfc10f3c5c9e527eeb0c44f5361a063f2073df GIT binary patch literal 283 zcmZ?wbhEHbL&k`rDPRAKu+HF zzr4J>Eb}U9hSicB>%`f%u`;u;FfcGP@-WGBGcoZnNHZ}=h%>M;FfcJNFtRc*GB7Z( zGcd4%`9L-ef#OdVkZlY)AOhqk2Q~`_289U|OxT+YZB&7o) wKxQzoC<`z+3J41E9Qiq;BT%NbG2leS1hMV=VwP?Sx0ts4TBT%_ryzqh0MTh4Gynhq literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/list/resources/images/gray.gif b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/list/resources/images/gray.gif new file mode 100644 index 0000000000000000000000000000000000000000..7e168315415a9807689de7a652b57c6fe842a7b1 GIT binary patch literal 86 zcmZ?wbh9u|nK?jI|DmWP!n3XvU pN=>;YIBP~8=P^8!IA4K}Pjz0%=3c&)g>ojL$CozrPhw!O1^{3g8KD3G literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/list/resources/images/green.gif b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/list/resources/images/green.gif new file mode 100644 index 0000000000000000000000000000000000000000..5613d8dbe1c74c083f7cda57c324670021574f49 GIT binary patch literal 287 zcmZ?wbhEHbf!rQ$jijRvn!eNPYvhpXjVoBRt5%UB_?J? zR%Qk!AdiuOfkBH6EXu&Z#K6GF%)khws0kE*vVd%1&;b!3KRK}3I4~qkIHAGe^spgO zhQoL!=hIIG4(yTvDM~j}SX$1gJ$g~c*x|!E%aBEA>WL(&lLjJ^CyxehTYNKsgTax( F8UTUBJOKaz literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/list/resources/images/magenta.gif b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/list/resources/images/magenta.gif new file mode 100644 index 0000000000000000000000000000000000000000..e3741ab69eec521515beefc10f461726bd086de4 GIT binary patch literal 96 zcmZ?wbhEHbd9#o=J|Mfq_BsCkrExV$cB*Ahiz6 tsvHKTre22Z2Q3w!TlWbzCdfE{Z&h}Q(P1=6P_x)zCVt?QTpkmHH2@%E8V3LX literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/list/resources/images/red.gif b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/list/resources/images/red.gif new file mode 100644 index 0000000000000000000000000000000000000000..c197ffce1bc5dfc014f1df247de15aec523e9ede GIT binary patch literal 118 zcmZ?wbhEHbaFf&yDxTX}hTCOLiv96<3W3nNHM2Sk9( uU|>-eU`U#vC?s;Ea^?v`mcxw!CnP3_ZQmEObW^y+wB^?-C96CI8LR;f10I_I literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/optionpane/OptionPaneDemo.java b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/optionpane/OptionPaneDemo.java new file mode 100644 index 00000000000..0e8c1e46a13 --- /dev/null +++ b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/optionpane/OptionPaneDemo.java @@ -0,0 +1,270 @@ +/* + * Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.sun.swingset3.demos.optionpane; + +import java.awt.*; +import java.awt.event.ActionEvent; +import java.net.URL; +import javax.swing.*; + +import com.sun.swingset3.demos.ResourceManager; +import com.sun.swingset3.DemoProperties; + +/** + * JOptionPaneDemo + * + * @author Jeff Dinkins + * @version 1.11 11/17/05 + */ +@DemoProperties( + value = "JOptionPane Demo", + category = "Choosers", + description = "Demonstrates JOptionPane, a component which displays standard message dialogs (question, warning, error, etc).", + sourceFiles = { + "com/sun/swingset3/demos/optionpane/OptionPaneDemo.java", + "com/sun/swingset3/demos/ResourceManager.java", + "com/sun/swingset3/demos/optionpane/resources/OptionPaneDemo.properties", + "com/sun/swingset3/demos/optionpane/resources/images/bottle.gif", + "com/sun/swingset3/demos/optionpane/resources/images/OptionPaneDemo.gif" + } +) +public class OptionPaneDemo extends JPanel { + + private static final Dimension VGAP15 = new Dimension(1, 15); + private static final Dimension VGAP30 = new Dimension(1, 30); + + private static final ResourceManager resourceManager = new ResourceManager(OptionPaneDemo.class); + public static final String WARNING_TITLE = resourceManager.getString("OptionPaneDemo.warningtitle"); + public static final String WARNING_TEXT = resourceManager.getString("OptionPaneDemo.warningtext"); + public static final String WARNING_BUTTON = resourceManager.getString("OptionPaneDemo.warningbutton"); + public static final String CONFIRM_NO = resourceManager.getString("OptionPaneDemo.confirmno"); + public static final String CONFIRM_YES = resourceManager.getString("OptionPaneDemo.confirmyes"); + public static final String CONFIRM_QUESTION = resourceManager.getString("OptionPaneDemo.confirmquestion"); + public static final String CONFIRM_BUTTON = resourceManager.getString("OptionPaneDemo.confirmbutton"); + public static final String MESSAGE_TEXT = resourceManager.getString("OptionPaneDemo.messagetext"); + public static final String MESSAGE_BUTTON = resourceManager.getString("OptionPaneDemo.messagebutton"); + public static final String INPUT_QUESTION = resourceManager.getString("OptionPaneDemo.inputquestion"); + public static final String INPUT_RESPONSE = ": " + resourceManager.getString("OptionPaneDemo.inputresponse"); + public static final String INPUT_BUTTON = resourceManager.getString("OptionPaneDemo.inputbutton"); + public static final String COMPONENT_R4 = resourceManager.getString("OptionPaneDemo.component_r4"); + public static final String COMPONENT_R3 = resourceManager.getString("OptionPaneDemo.component_r3"); + public static final String COMPONENT_R2 = resourceManager.getString("OptionPaneDemo.component_r2"); + public static final String COMPONENT_R1 = resourceManager.getString("OptionPaneDemo.component_r1"); + public static final String COMPONENT_TITLE = resourceManager.getString("OptionPaneDemo.componenttitle"); + public static final String COMPONENT_OP5 = resourceManager.getString("OptionPaneDemo.component_op5"); + public static final String COMPONENT_OP4 = resourceManager.getString("OptionPaneDemo.component_op4"); + public static final String COMPONENT_OP3 = resourceManager.getString("OptionPaneDemo.component_op3"); + public static final String COMPONENT_OP2 = resourceManager.getString("OptionPaneDemo.component_op2"); + public static final String COMPONENT_OP1 = resourceManager.getString("OptionPaneDemo.component_op1"); + public static final String COMPONENT_MESSAGE_2 = resourceManager.getString("OptionPaneDemo.componentmessage2"); + public static final String COMPONENT_CB3 = resourceManager.getString("OptionPaneDemo.component_cb3"); + public static final String COMPONENT_CB2 = resourceManager.getString("OptionPaneDemo.component_cb2"); + public static final String COMPONENT_CB1 = resourceManager.getString("OptionPaneDemo.component_cb1"); + public static final String COMPONENT_BUTTON = resourceManager.getString("OptionPaneDemo.componentbutton"); + public static final String COMPONENT_TEXT_FIELD = resourceManager.getString("OptionPaneDemo.componenttextfield"); + public static final String COMPONENT_MESSAGE = resourceManager.getString("OptionPaneDemo.componentmessage"); + public static final String DEMO_TITLE = OptionPaneDemo.class.getAnnotation(DemoProperties.class).value(); + + /** + * main method allows us to run as a standalone demo. + * + * @param args + */ + public static void main(String[] args) { + JFrame frame = new JFrame(DEMO_TITLE); + + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.getContentPane().add(new OptionPaneDemo()); + frame.setPreferredSize(new Dimension(800, 600)); + frame.pack(); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + } + + /** + * OptionPaneDemo Constructor + */ + public OptionPaneDemo() { + setLayout(new BoxLayout(this, BoxLayout.X_AXIS)); + + JPanel bp = new JPanel() { + @Override + public Dimension getMaximumSize() { + return new Dimension(getPreferredSize().width, super.getMaximumSize().height); + } + }; + bp.setLayout(new BoxLayout(bp, BoxLayout.Y_AXIS)); + + bp.add(Box.createRigidArea(VGAP30)); + bp.add(Box.createRigidArea(VGAP30)); + + bp.add(createInputDialogButton()); + bp.add(Box.createRigidArea(VGAP15)); + bp.add(createWarningDialogButton()); + bp.add(Box.createRigidArea(VGAP15)); + bp.add(createMessageDialogButton()); + bp.add(Box.createRigidArea(VGAP15)); + bp.add(createComponentDialogButton()); + bp.add(Box.createRigidArea(VGAP15)); + bp.add(createConfirmDialogButton()); + bp.add(Box.createVerticalGlue()); + + add(Box.createHorizontalGlue()); + add(bp); + add(Box.createHorizontalGlue()); + } + + private JButton createWarningDialogButton() { + Action a = new AbstractAction(WARNING_BUTTON) { + @Override + public void actionPerformed(ActionEvent e) { + JOptionPane.showMessageDialog(OptionPaneDemo.this, + WARNING_TEXT, + WARNING_TITLE, + JOptionPane.WARNING_MESSAGE + ); + } + }; + return createButton(a); + } + + private JButton createMessageDialogButton() { + Action a = new AbstractAction(MESSAGE_BUTTON) { + final URL img = getClass().getResource("resources/images/bottle.gif"); + final String imagesrc = ""; + final String message = MESSAGE_TEXT; + + @Override + public void actionPerformed(ActionEvent e) { + JOptionPane.showMessageDialog( + OptionPaneDemo.this, + "" + imagesrc + "
" + message + "

" + ); + } + }; + return createButton(a); + } + + private JButton createConfirmDialogButton() { + Action a = new AbstractAction(CONFIRM_BUTTON) { + @Override + public void actionPerformed(ActionEvent e) { + int result = JOptionPane.showConfirmDialog(OptionPaneDemo.this, CONFIRM_QUESTION); + if (result == JOptionPane.YES_OPTION) { + JOptionPane.showMessageDialog(OptionPaneDemo.this, CONFIRM_YES); + } else if (result == JOptionPane.NO_OPTION) { + JOptionPane.showMessageDialog(OptionPaneDemo.this, CONFIRM_NO); + } + } + }; + return createButton(a); + } + + private JButton createInputDialogButton() { + Action a = new AbstractAction(INPUT_BUTTON) { + @Override + public void actionPerformed(ActionEvent e) { + String result = JOptionPane.showInputDialog(OptionPaneDemo.this, INPUT_QUESTION); + if ((result != null) && (result.length() > 0)) { + JOptionPane.showMessageDialog(OptionPaneDemo.this, + result + INPUT_RESPONSE); + } + } + }; + return createButton(a); + } + + private JButton createComponentDialogButton() { + Action a = new AbstractAction(COMPONENT_BUTTON) { + @Override + public void actionPerformed(ActionEvent e) { + // In a ComponentDialog, you can show as many message components and + // as many options as you want: + + // Messages + Object[] message = new Object[4]; + message[0] = COMPONENT_MESSAGE; + message[1] = new JTextField(COMPONENT_TEXT_FIELD); + + JComboBox cb = new JComboBox<>(); + cb.addItem(COMPONENT_CB1); + cb.addItem(COMPONENT_CB2); + cb.addItem(COMPONENT_CB3); + message[2] = cb; + + message[3] = COMPONENT_MESSAGE_2; + + // Options + String[] options = { + COMPONENT_OP1, COMPONENT_OP2, COMPONENT_OP3, COMPONENT_OP4, COMPONENT_OP5}; + int result = JOptionPane.showOptionDialog( + OptionPaneDemo.this, // the parent that the dialog blocks + message, // the dialog message array + COMPONENT_TITLE, // the title of the dialog window + JOptionPane.DEFAULT_OPTION, // option type + JOptionPane.INFORMATION_MESSAGE, // message type + null, // optional icon, use null to use the default icon + options, // options string array, will be made into buttons + options[3] // option that should be made into a default button + ); + switch (result) { + case 0: // yes + JOptionPane.showMessageDialog(OptionPaneDemo.this, COMPONENT_R1); + break; + case 1: // no + JOptionPane.showMessageDialog(OptionPaneDemo.this, COMPONENT_R2); + break; + case 2: // maybe + JOptionPane.showMessageDialog(OptionPaneDemo.this, COMPONENT_R3); + break; + case 3: // probably + JOptionPane.showMessageDialog(OptionPaneDemo.this, COMPONENT_R4); + break; + default: + break; + } + + } + }; + return createButton(a); + } + + private JButton createButton(Action a) { + JButton b = new JButton() { + @Override + public Dimension getMaximumSize() { + int width = Short.MAX_VALUE; + int height = super.getMaximumSize().height; + return new Dimension(width, height); + } + }; + // setting the following client property informs the button to show + // the action text as it's name. The default is to not show the + // action text. + b.putClientProperty("displayActionText", Boolean.TRUE); + b.setAction(a); + // b.setAlignmentX(JButton.CENTER_ALIGNMENT); + return b; + } + +} diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/optionpane/resources/OptionPaneDemo.properties b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/optionpane/resources/OptionPaneDemo.properties new file mode 100644 index 00000000000..e4cf7077992 --- /dev/null +++ b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/optionpane/resources/OptionPaneDemo.properties @@ -0,0 +1,42 @@ +### OptionPane Demo ### + +OptionPaneDemo.accessible_description=The OptionPane Demo shows examples of using JOptionPane to generate different common option dialog boxes +OptionPaneDemo.tooltip=JOptionPane Demo +OptionPaneDemo.name=Option Pane Demo + +OptionPaneDemo.warningbutton=Show Warning Dialog +OptionPaneDemo.componentbutton=Show Component Dialog +OptionPaneDemo.inputbutton=Show Input Dialog +OptionPaneDemo.confirmbutton=Show Confirmation Dialog +OptionPaneDemo.messagebutton=Show Message Dialog + +OptionPaneDemo.warningtitle=Warning Dialog Example +OptionPaneDemo.warningtext=

This is a test of the Emergency Broadcast System. This is
only a test
. The webmaster of your local intranet, in voluntary
cooperation with the Federal and State authorities, have
developed this system to keep you informed in the event of an
emergency. If this had been an actual emergency, the signal you
just heard would have been followed by official information, news
or instructions. This concludes this test of the Emergency
Broadcast System
.


Developer Note: This dialog demo used HTML for text formatting.

+ +OptionPaneDemo.messagetext=Message in a Bottle (yeah) + +OptionPaneDemo.confirmquestion=Is the sun shining outside today? +OptionPaneDemo.confirmyes=Well what are you doing playing on the computer?
Get outside! Take a trip to the beach! Get a little sun! +OptionPaneDemo.confirmno=Well good thing you're inside protected from the elements! + +OptionPaneDemo.inputquestion=What is your favorite movie? +OptionPaneDemo.inputresponse=That was a pretty good movie! + +OptionPaneDemo.componenttitle=Component Dialog Example +OptionPaneDemo.componentmessage=JOptionPane can contain as many components
as you want, such as a text field: +OptionPaneDemo.componenttextfield=or a combobox: +OptionPaneDemo.component_cb1=item 1 +OptionPaneDemo.component_cb2=item 2 +OptionPaneDemo.component_cb3=item 3 +OptionPaneDemo.componentmessage2=JOptionPane can also show as many options
as you want: +OptionPaneDemo.component_op1=Yes +OptionPaneDemo.component_op2=No +OptionPaneDemo.component_op3=Maybe +OptionPaneDemo.component_op4=Probably +OptionPaneDemo.component_op5=Cancel + +OptionPaneDemo.component_r1=Upbeat and positive! I like that! Good choice. +OptionPaneDemo.component_r2=Definitely not, I wouldn't do it either. +OptionPaneDemo.component_r3= Mmmm.. yes, the situation is unclear at this
time. Check back when you know for sure.
+OptionPaneDemo.component_r4=You know you want to. I think you should
have gone for broke and pressed "Yes".
+ diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/optionpane/resources/images/OptionPaneDemo.gif b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/optionpane/resources/images/OptionPaneDemo.gif new file mode 100644 index 0000000000000000000000000000000000000000..114e1ab7bd39e954fb9ea0ecc97c3e8fea023d70 GIT binary patch literal 881 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 S{rLGmF%nOY=d?h8&Hn?lJvX5M literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/progressbar/ProgressBarDemo.java b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/progressbar/ProgressBarDemo.java new file mode 100644 index 00000000000..e49b47926ea --- /dev/null +++ b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/progressbar/ProgressBarDemo.java @@ -0,0 +1,199 @@ +/* + * Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.sun.swingset3.demos.progressbar; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.event.ActionEvent; +import javax.swing.*; +import javax.swing.border.BevelBorder; +import javax.swing.border.SoftBevelBorder; + +import com.sun.swingset3.DemoProperties; +import com.sun.swingset3.demos.ResourceManager; + +/** + * JProgressBar Demo + * + * @version 1.12 11/17/05 + * @author Jeff Dinkins # @author Peter Korn (accessibility support) + */ +@DemoProperties( + value = "ProgressBar Demo", + category = "Controls", + description = "Demonstrates the JProgressBar, a control which displays progress to the user", + sourceFiles = { + "com/sun/swingset3/demos/progressbar/ProgressBarDemo.java", + "com/sun/swingset3/demos/ResourceManager.java", + "com/sun/swingset3/demos/progressbar/resources/ProgressBarDemo.properties", + "com/sun/swingset3/demos/progressbar/resources/images/ProgressBarDemo.gif" + } +) +public class ProgressBarDemo extends JPanel { + + private static final ResourceManager resourceManager = new ResourceManager(ProgressBarDemo.class); + public static final String STOP_BUTTON = resourceManager.getString("ProgressBarDemo.stop_button"); + public static final String START_BUTTON = resourceManager.getString("ProgressBarDemo.start_button"); + public static final String DEMO_TITLE = ProgressBarDemo.class.getAnnotation(DemoProperties.class).value(); + + /** + * main method allows us to run as a standalone demo. + * + * @param args + */ + public static void main(String[] args) { + JFrame frame = new JFrame(DEMO_TITLE); + + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.getContentPane().add(new ProgressBarDemo()); + frame.setPreferredSize(new Dimension(800, 600)); + frame.pack(); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + } + + /** + * ProgressBarDemo Constructor + */ + public ProgressBarDemo() { + createProgressPanel(); + } + + private final javax.swing.Timer timer = new javax.swing.Timer(18, createTextLoadAction()); + private Action loadAction; + private Action stopAction; + private JProgressBar progressBar; + private JTextArea progressTextArea; + + private void createProgressPanel() { + setLayout(new BorderLayout()); + + JPanel textWrapper = new JPanel(new BorderLayout()); + textWrapper.setBorder(new SoftBevelBorder(BevelBorder.LOWERED)); + textWrapper.setAlignmentX(LEFT_ALIGNMENT); + progressTextArea = new MyTextArea(); + + progressTextArea.getAccessibleContext().setAccessibleName( + resourceManager.getString("ProgressBarDemo.accessible_text_area_name")); + progressTextArea.getAccessibleContext().setAccessibleDescription( + resourceManager.getString("ProgressBarDemo.accessible_text_area_description")); + textWrapper.add(new JScrollPane(progressTextArea), BorderLayout.CENTER); + + add(textWrapper, BorderLayout.CENTER); + + JPanel progressPanel = new JPanel(); + add(progressPanel, BorderLayout.SOUTH); + + progressBar = new JProgressBar(JProgressBar.HORIZONTAL, 0, text.length()) { + @Override + public Dimension getPreferredSize() { + return new Dimension(300, super.getPreferredSize().height); + } + }; + progressBar.getAccessibleContext().setAccessibleName( + resourceManager.getString("ProgressBarDemo.accessible_text_loading_progress")); + + progressPanel.add(progressBar); + progressPanel.add(createLoadButton()); + progressPanel.add(createStopButton()); + } + + private JButton createLoadButton() { + loadAction = new AbstractAction(START_BUTTON) { + @Override + public void actionPerformed(ActionEvent e) { + loadAction.setEnabled(false); + stopAction.setEnabled(true); + if (progressBar.getValue() == progressBar.getMaximum()) { + progressBar.setValue(0); + textLocation = 0; + progressTextArea.setText(""); + } + timer.start(); + } + }; + return createButton(loadAction); + } + + private JButton createStopButton() { + stopAction = new AbstractAction(STOP_BUTTON) { + @Override + public void actionPerformed(ActionEvent e) { + timer.stop(); + loadAction.setEnabled(true); + stopAction.setEnabled(false); + } + }; + return createButton(stopAction); + } + + private static JButton createButton(Action a) { + JButton b = new JButton(); + // setting the following client property informs the button to show + // the action text as it's name. The default is to not show the + // action text. + b.putClientProperty("displayActionText", Boolean.TRUE); + b.setAction(a); + return b; + } + + private int textLocation = 0; + + private final String text = resourceManager.getString("ProgressBarDemo.text"); + + private Action createTextLoadAction() { + return new AbstractAction("text load action") { + @Override + public void actionPerformed(ActionEvent e) { + if (progressBar.getValue() < progressBar.getMaximum()) { + progressBar.setValue(progressBar.getValue() + 1); + progressTextArea.append(text.substring(textLocation, textLocation + 1)); + textLocation++; + } else { + timer.stop(); + loadAction.setEnabled(true); + stopAction.setEnabled(false); + } + } + }; + } + + private static class MyTextArea extends JTextArea { + + private MyTextArea() { + super(null, 0, 0); + setEditable(false); + setText(""); + } + + @Override + public float getAlignmentX() { + return LEFT_ALIGNMENT; + } + + @Override + public float getAlignmentY() { + return TOP_ALIGNMENT; + } + } +} diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/progressbar/resources/ProgressBarDemo.properties b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/progressbar/resources/ProgressBarDemo.properties new file mode 100644 index 00000000000..7660e0b141e --- /dev/null +++ b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/progressbar/resources/ProgressBarDemo.properties @@ -0,0 +1,35 @@ +### ProgressBar Demo ### + +ProgressBarDemo.accessible_description=This demo shows an example of using the JProgressBar component. +ProgressBarDemo.tooltip=JProgressBar demo +ProgressBarDemo.name=ProgressBar Demo +ProgressBarDemo.start_button=Start Loading Text +ProgressBarDemo.stop_button=Stop Loading Text +ProgressBarDemo.accessible_text_loading_progress=Text loading progress +ProgressBarDemo.accessible_text_area_name=Text progressively being loaded in + +ProgressBarDemo.accessible_text_area_description=This JTextArea is being filled with text from a buffer progressively a character at a time while the progress bar a the bottom of the window shows the loading progress + +ProgressBarDemo.text=\ +The saying goes: if an infinite number of monkeys typed on an infinite number of typewriters, eventually \n\ +all the great works of mankind would emerge. Now, with today's high speed computers, we can finally test \n\ +this theory... \n\n\ +\tLzskd jfy 92y;ho4 th;qlh sd 6yty;q2 hnlj 8sdf. Djfy 92y;ho4, th;qxhz d7yty; \n\ +\tQ0hnlj 23&^ (# ljask djf y92y; fy92y; Sd6y ty;q2h nl jk la gfa harvin garvel\n\ +\tlasdfsd a83sl la8z ks8l 92y;ho4 th;qlh sd 6yty;q2 hnlj 8sdf. Djfy 92y;ho4,\n\ +\tth;qxhz d7yty; Q0hnlj 23&^ nknod mrs88 jsd79lfm#%$JLaoz6df lso7dj f2 jfls\n\ +\t67d9ol1@2fou99s 1lkj2 @l.k1 2; a89o7aljf 1l3i7ou8 d8l3 lqwerty0092 #1!\n\ +\tja9o do8lkjj139rojsd9**!l6*hd # ljasd78 l2awkjad78 3ol7asljf 3 ldif & l.js\n\ +\tLl ls ewan la8uj 23lll7u 8l 3h hhxx8 8d lsd fixx 891lkjno99sl d8l@@@!!8#8\n\ +\tdfoil jarooda mklaoorj nowai the smisthliylka jkdlfjiw ladajadra lthhheeejfjl\n\ +\tdkddooolda bub mirznod of the koojgaf!! But 2 be or not to be... that is the\n\ +\tquestion. Then when shall we three meet again In thunder, lightning, or in\n\ +\train? When the hurlyburly's done, When the battle's lost and won. That will\n\ +\tbe ere the set of sun. Where the place? Upon the heath. There to meet with\n\ +\tMacbeth. But hath forth not to want..... a banana, or to be.... a banana.\n\ +\tBanana, I knew him banana. Banana banana. Banana banana banana banana.\n\ +\n\ +\n\ +\n\ +\n\ +Well... hmm.... it seemed like a good idea... diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/progressbar/resources/images/ProgressBarDemo.gif b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/progressbar/resources/images/ProgressBarDemo.gif new file mode 100644 index 0000000000000000000000000000000000000000..1aa706d257cb5acf16d4bd721cbceec60774f712 GIT binary patch literal 165 zcmZ?wbhEHbRA5kG*v!NL1pk46;eSf=o~7GQ?>zskp=-+fcW*!v*nr|s7DfgJCI%gl zB*+W~mY{$Wo~!p-ygs}8e}YC|O6I(bRqJ%p_Y~-yH%aBGSogm2;eWg4Tn7!Q{u7T9 vPsVVmZ(s7|Mvk58#g|)7?cv&VOXYgz;+f|+OshJZxBLEw9X01V85pbqMnpk& literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/scrollpane/ScrollPaneDemo.java b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/scrollpane/ScrollPaneDemo.java new file mode 100644 index 00000000000..5a492ea53fd --- /dev/null +++ b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/scrollpane/ScrollPaneDemo.java @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.sun.swingset3.demos.scrollpane; + +import java.awt.*; +import javax.swing.*; + +import com.sun.swingset3.DemoProperties; +import com.sun.swingset3.demos.ResourceManager; + +/** + * Scroll Pane Demo + * + * @version 1.9 11/17/05 + * @author Jeff Dinkins + */ +@DemoProperties( + value = "JScrollPane Demo", + category = "Containers", + description = "Demonstrates JScrollPane, a container for scrolling contents within a view port", + sourceFiles = { + "com/sun/swingset3/demos/scrollpane/ScrollPaneDemo.java", + "com/sun/swingset3/demos/ResourceManager.java", + "com/sun/swingset3/demos/scrollpane/resources/ScrollPaneDemo.properties", + "com/sun/swingset3/demos/scrollpane/resources/images/colheader.jpg", + "com/sun/swingset3/demos/scrollpane/resources/images/COPYRIGHT", + "com/sun/swingset3/demos/scrollpane/resources/images/crayons.jpg", + "com/sun/swingset3/demos/scrollpane/resources/images/lowerleft.jpg", + "com/sun/swingset3/demos/scrollpane/resources/images/rowheader.jpg", + "com/sun/swingset3/demos/scrollpane/resources/images/ScrollPaneDemo.gif", + "com/sun/swingset3/demos/scrollpane/resources/images/upperleft.jpg", + "com/sun/swingset3/demos/scrollpane/resources/images/upperright.jpg"} +) +public class ScrollPaneDemo extends JPanel { + + private final ResourceManager resourceManager = new ResourceManager(this.getClass()); + public static final String DEMO_TITLE = ScrollPaneDemo.class.getAnnotation(DemoProperties.class).value(); + + /** + * main method allows us to run as a standalone demo. + * + * @param args + */ + public static void main(String[] args) { + JFrame frame = new JFrame(DEMO_TITLE); + + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.getContentPane().add(new ScrollPaneDemo()); + frame.setPreferredSize(new Dimension(800, 600)); + frame.pack(); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + } + + /** + * ScrollPaneDemo Constructor + */ + public ScrollPaneDemo() { + setLayout(new BorderLayout()); + + ImageIcon crayons = resourceManager.createImageIcon("crayons.jpg", + resourceManager.getString("ScrollPaneDemo.crayons")); + add(new ImageScroller(crayons), BorderLayout.CENTER); + } + + /** + * ScrollPane class that demonstrates how to set the various column and row + * headers and corners. + */ + private class ImageScroller extends JScrollPane { + + public ImageScroller(Icon icon) { + super(); + + // Panel to hold the icon image + JPanel p = new JPanel(new BorderLayout()); + p.add(new JLabel(icon), BorderLayout.CENTER); + getViewport().add(p); + + // Create and add a column header to the scrollpane + JLabel colHeader = new JLabel( + resourceManager.createImageIcon("colheader.jpg", resourceManager.getString("ScrollPaneDemo.colheader"))); + setColumnHeaderView(colHeader); + + // Create and add a row header to the scrollpane + JLabel rowHeaderLabel = new JLabel( + resourceManager.createImageIcon("rowheader.jpg", resourceManager.getString("ScrollPaneDemo.rowheader"))); + setRowHeaderView(rowHeaderLabel); + + // Create and add the upper left corner + JLabel cornerUL = new JLabel( + resourceManager.createImageIcon("upperleft.jpg", resourceManager.getString("ScrollPaneDemo.upperleft"))); + setCorner(UPPER_LEFT_CORNER, cornerUL); + + // Create and add the upper right corner + JLabel cornerUR = new JLabel( + resourceManager.createImageIcon("upperright.jpg", resourceManager.getString("ScrollPaneDemo.upperright"))); + setCorner(UPPER_RIGHT_CORNER, cornerUR); + + // Create and add the lower left corner + JLabel cornerLL = new JLabel( + resourceManager.createImageIcon("lowerleft.jpg", resourceManager.getString("ScrollPaneDemo.lowerleft"))); + setCorner(LOWER_LEFT_CORNER, cornerLL); + + JScrollBar vsb = getVerticalScrollBar(); + JScrollBar hsb = getHorizontalScrollBar(); + + vsb.setValue(icon.getIconHeight()); + hsb.setValue(icon.getIconWidth() / 10); + } + } + +} diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/scrollpane/resources/ScrollPaneDemo.properties b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/scrollpane/resources/ScrollPaneDemo.properties new file mode 100644 index 00000000000..4d33fb50162 --- /dev/null +++ b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/scrollpane/resources/ScrollPaneDemo.properties @@ -0,0 +1,11 @@ +### ScrollPane Demo ### + +ScrollPaneDemo.accessible_description=JScrollPane Demo +ScrollPaneDemo.name=Scroll Pane Demo +ScrollPaneDemo.tooltip=JScrollPane demo +ScrollPaneDemo.crayons=Lots of Crayons +ScrollPaneDemo.colheader=Column Header +ScrollPaneDemo.rowheader=Row Header +ScrollPaneDemo.upperleft=Upper Left Corner +ScrollPaneDemo.upperright=Upper Right Column Header Corner +ScrollPaneDemo.lowerleft=Lower Left Row Header Corner diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/scrollpane/resources/images/COPYRIGHT b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/scrollpane/resources/images/COPYRIGHT new file mode 100644 index 00000000000..a7bdd30d377 --- /dev/null +++ b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/scrollpane/resources/images/COPYRIGHT @@ -0,0 +1,2 @@ + + All images in this directory are copyright 1995 by Jeff Dinkins. diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/scrollpane/resources/images/ScrollPaneDemo.gif b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/scrollpane/resources/images/ScrollPaneDemo.gif new file mode 100644 index 0000000000000000000000000000000000000000..95fcc12da4767d20369d3efa1cae5cabed756397 GIT binary patch literal 1458 zcmbu9`%l(p9L38;Ems?GQbo;M6-Sk+9d1zZ2lgN!9XjdQ)Ov}mySP15G@XQ{WJ9P! zXAun5nWb^#QoPYkh;@_LIJ{I)Y?QHgMD$gdv(A{j!MVMf7@vXv!an)_c+Sc9mviz9 z@-o>HALheC*eC>skr+l}#OMhaDPjzRaTF#1Ol5G4!Z8-77@UGQt>6$4I86{DK}rO} z5)2?1iC{H?0|aN07*3KL$tWbNkfJ*(p-Aclkm3xAw{f^WmQ)MNf9(floc7MB&qU( z3Qbi38ZBvpqKT#k6%861v^0g$d7z874lP}Rx(0NE)m_aX8G|MaXd04j$hM&a6DLjC zHWl79IMW(?)RHVqRcunWDbfant+2MP+ZJcLp6w4hitebqqZy9HJ2vmqgv&rz5nR=A zHPf{?*QVS+(2rw&O7gR$U*!D~@SC*Xbo@3IV8ws{1CS5MdcY6^ej(tOJ)n5J?1`Kw z>Yi?ThUJ;AXPKTudR{QdkU5|9d!DkI%MmV;~;IWBT_6d;kuA`hTZdq_bqA`s#b zf(Xe7spx+|J&g>*uxnV_SQW-Pf#J`w#;9q_PJG{0jVI1sH_+O6zJ{2^%^qmYt4q%L zDs4wsBdD8^F!OHtPV-x9^SK)*)}HQ&yBIf%>wZ|-m>m(rOvP7crw&>l-`IL|$-UtL z=9`GYUG-~b=EcTDPE&ur?yC$R*Kq8eUt2k2&yV@^+{Tg*u11Io`ykoG&X2i`wtkhGWqY=>@HyM%BM@xphtTimVOGnPt0}nrDyKx4hgF z?vpQI_`=G_$%%*Sj5&w;uO;U+oqL{qF8yN9fd%!8Di3W9>k1pHJ)J+}p1C&Tqgyvu zMm2cJX}N2j`INX!{Z3BVV7otWUhkqOap`(ySk2_yKIt;O^Fmi-bX3dGfH-NSBR2g? z+TT~_Cv+@~&FEhm(L1+_*as3fZ+x%0Hah<8!rktsrYLd?dHqU8&p<+9>fV;2>Itt6 z@Bh6bE3hhW2Jtvlo8H;|sC#!=I1$tg>UGk)zOO=KWIL*D4{0iuOroa*?UX=h^EzE zrnu;>Q>WaHJ#+ec;@~ literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/scrollpane/resources/images/colheader.jpg b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/scrollpane/resources/images/colheader.jpg new file mode 100644 index 0000000000000000000000000000000000000000..774deed99a904f3a0bcc98a5a78f4f052e32e75f GIT binary patch literal 12729 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%0A;s*Cn$gtQVr*U{q7gBosm#3k@y@utZ_+ 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∨NUXcE4u;y^2O`69F;0B2qk%Vi5% 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 MXrWsF0M38^*%?Q9PXGV_ literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/scrollpane/resources/images/lowerleft.jpg b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/scrollpane/resources/images/lowerleft.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b9e60474b99fea21a51652c0a433807bb95ba75f GIT binary patch literal 5049 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 literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/scrollpane/resources/images/rowheader.jpg b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/scrollpane/resources/images/rowheader.jpg new file mode 100644 index 0000000000000000000000000000000000000000..c791147c9f2bec482e23067168f39fe369e60160 GIT binary patch literal 11835 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 MIec7w6$6g^KVqB05&!@I literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/scrollpane/resources/images/upperleft.jpg b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/scrollpane/resources/images/upperleft.jpg new file mode 100644 index 0000000000000000000000000000000000000000..69e2a00c6f9627466f3119f58619da6daf5b4fdd GIT binary patch literal 5220 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

$VPspo^rYJN#za|OvO;e zPbFDpgG#;1ZIzFzTB`1<0@XFDb*gQu@6^Vrxv2@%^40dMwX1zp=cs$BC#e^xA5nj# zfzvS62-e8f*sgJ2XfD*O(!8oUq%~g4M=MRMRO_79 zi}5Ps-Nw%yUp)TQ_-EQI?J3$q?PBfI+P`y@IqsZf&K6EHr(Z`?$5$s)r$XnN&L>?X z-EiF;-Tk_c^eB3cdhvS2dQE!$`r7)_^cU&Z>fbjY8#o#y8f-RbF?eTaWEg3<%J7I` zpOKo8pV0!NTBC=?bYoZJbmL0nTP6e(N0TIzZ6-HNQByn9MALH9>t?8#y_wK#n_24w z`~;^7q6w7~?wHfe-OaPiYt1_?R4f84ax9Kmyt34{jJ7PWY_a@0(Qab$#OjF;tdy*# zTIE#w+j!e7wK-}tU~6V8u-$HZ*N$a3&2E)llie442Ya!7o&7Th zeTO*?TOIB=vK@mQ*EwEv!aI35Epjyp{Kq8oNlBCTPU@R%G+8jYdUCgm zu1l;-g-gd2&XhS*ewxzZs^c2#TIt&5rsu|Y+vV2lZsI=IeV_X)kBJ^)kHa4CxX#?g z+%uk-r;lg8=QS@SuW+w2ugBiH-U;5d-u*syJ_~)$_~L#2eG7c=_-Xm^{A&FAr#ehs zJhj=M<{#=`=HC@y8Xyj6oQ6)DI<08h!ygQO5dCl@5Cr-K76m>EG73r$Ivz|24hk+0 zei~vGvM8h_loc8ix;OM~m`B)#um|DB;hEu05sDErBWfbvMS4aSMs`fMoW6Mal_-rU zVbsxRVsu3GuIQl|UNbh$=$UCZGjHbYSw^$w&AK#OV|McFlQD`hu`vhd;O9in**oWR zY+!6<>`FXlfJxCjabz40#bh4Fm}E(t{m&k|h|OA=oQxxzBx zz+B(CJLbMi3QXFaBu$P?-k(BBnUiu%q$ElbHK&eGotN60W|o$h){*X@VJx zfn?0eI4WVokEyGf#+fTKJF})_m1n)rj>tYZk2y~?@9KQh`D^C)F7RHkb0KaaZ(-9S zokhzRbuM;aT(JaM61Svjsov7ur9I1hmhD+iS)R1~*PMwtg*k6lM6PJe)yiF#+r83f zWo@29o;dH$s!6MMtj4VtuD-U$W=-juulfA^%WEgDEm`|{9dF&`AFY1e@}qQp{Q7Gf z>^E%RNZ2Uac)P&0U{4{la6w^r(X^t5Voq^>@taLCn=X~umh9L}*_^q#V@tr6qow+# z8%sZzC6=|9dzA0rs=0N|)_2?Z+uF9fZLiy*wPWp$k3R{2x?AB>(NJkvSyDx)%Bp%= z9Z}t~({X3bE{$DlcYWC{+TF1yWKVOALru+I&Al6H(b~+~XZvREyYaK<&qwRb>nirE z>|gr}_$Bj~-w(_=aJ$~WzUiRj!MZ~RhqfMO9nNn+8s<0jA4xpYaWv{^>#?cFni?lH z9zJeyeD?{R6XhqBPi{CxIhA`#dV0a>p)+Y`UYt!h+uan?^x)j|bGMpaMkYW;a}~3J$%jnTElgR>ql=m-Dqr`+T%;#i3baCi(QAmE$EMNw?5 z_j`?tBA86$5{MYA&0+;mu+28K~x^rn4LB%asB$@oB;!_ZD?U- zJujol&eN!<>Y%-97+-R(t&3#1f=p3U&n@=yu09lTGu&9?b@!l5=L#}cZW`9Qe<5`p zeWxeRTfJq^(TjKc-kUi2MG5DxDXlqn>E5#srp{BN=Pt-E+uL~g{_h`$H8F2|UVq`#j literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/spinner/JMandelbrot.java b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/spinner/JMandelbrot.java new file mode 100644 index 00000000000..c777ec80da9 --- /dev/null +++ b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/spinner/JMandelbrot.java @@ -0,0 +1,328 @@ +/* + * Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.sun.swingset3.demos.spinner; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.*; +import java.awt.geom.Point2D; +import java.awt.image.BufferedImage; +import java.awt.image.DataBufferInt; +import java.util.List; + +import com.sun.swingset3.demos.ResourceManager; + +/** + * @author Mikhail Lapshin + */ +public class JMandelbrot extends JComponent { + + private static final double EPSILON = 1E-16; + private static final int MIN_WIDTH = 50; + private static final int MIN_HEIGHT = 50; + private static final double ZOOM_RATE = 3; + private static final int NUM_OF_THREADS = 4; + + private Point2D center; + public static final String CENTER_PROPERTY_NAME = "center"; + + private int maxIteration = 300; + public static final String MAX_ITERATION_PROPERTY_NAME = "maxIteration"; + + private Palette palette; + public static final String PALETTE_PROPERTY_NAME = "palette"; + + private BufferedImage buffer; + private final MandelbrotCalculator[] calculators + = new MandelbrotCalculator[NUM_OF_THREADS]; + + private double xLowLimit = -2; + private double xHighLimit = 2; + private double yLowLimit = -2; + private double yHighLimit = 2; + private double xScale = 100; + private double yScale = 100; + private int oldComponentWidth = (int) (xScale * (xHighLimit - xLowLimit)); + private int oldComponentHeight = (int) (yScale * (yHighLimit - yLowLimit)); + + public JMandelbrot(int width, int height, Palette palette, + ResourceManager resourceManager) { + setPreferredSize(new Dimension(width, height)); + setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT)); + calcConstants(width, height); + setPalette(palette); + setToolTipText(resourceManager.getString("SpinnerDemo.toolTip")); + installListeners(); + } + + private void calcConstants() { + calcConstants(getWidth(), getHeight()); + } + + private void calcConstants(int width, int height) { + if ((width >= MIN_WIDTH) && (height >= MIN_HEIGHT)) { + double oldIntervalWidth = xHighLimit - xLowLimit; + double oldIntervalHeight = yHighLimit - yLowLimit; + double newIntervalWidth + = width * oldIntervalWidth / oldComponentWidth; + double newIntervalHeight + = height * oldIntervalHeight / oldComponentHeight; + double xDiff = newIntervalWidth - oldIntervalWidth; + double yDiff = newIntervalHeight - oldIntervalHeight; + xLowLimit -= xDiff / 2; + xHighLimit += xDiff / 2; + yLowLimit -= yDiff / 2; + yHighLimit += yDiff / 2; + buffer = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); + oldComponentWidth = width; + oldComponentHeight = height; + setCenter(calcCenter()); + } + } + + private void installListeners() { + addMouseListener(new MouseAdapter() { + @Override + public void mousePressed(MouseEvent e) { + int xCoord = e.getX(); + int yCoord = e.getY(); + double intervalWidth = xHighLimit - xLowLimit; + double intervalHeight = yHighLimit - yLowLimit; + double x = intervalWidth * xCoord / getWidth() + xLowLimit; + double y = intervalHeight * yCoord / getHeight() + yLowLimit; + + double newIntervalWidth; + double newIntervalHeight; + if (e.getButton() == MouseEvent.BUTTON1) { + boolean limitReached = false; + newIntervalWidth = intervalWidth / ZOOM_RATE; + if ((newIntervalWidth / getWidth()) < EPSILON) { + newIntervalWidth = intervalWidth; + limitReached = true; + } + newIntervalHeight = intervalHeight / ZOOM_RATE; + if ((newIntervalHeight / getHeight()) < EPSILON) { + newIntervalHeight = intervalHeight; + limitReached = true; + } + if (!limitReached) { + xLowLimit = x - (x - xLowLimit) / ZOOM_RATE; + yLowLimit = y - (y - yLowLimit) / ZOOM_RATE; + } + } else { + newIntervalWidth = intervalWidth * ZOOM_RATE; + newIntervalHeight = intervalHeight * ZOOM_RATE; + xLowLimit = x - (x - xLowLimit) * ZOOM_RATE; + yLowLimit = y - (y - yLowLimit) * ZOOM_RATE; + } + + xHighLimit = xLowLimit + newIntervalWidth; + yHighLimit = yLowLimit + newIntervalHeight; + + setCenter(calcCenter()); + + xScale = getWidth() / newIntervalWidth; + yScale = getHeight() / newIntervalHeight; + + calculatePicture(); + } + }); + + addComponentListener(new ComponentListener() { + @Override + public void componentResized(ComponentEvent e) { + calcConstants(); + calculatePicture(); + repaint(); + } + + @Override + public void componentMoved(ComponentEvent e) { + } + + @Override + public void componentShown(ComponentEvent e) { + } + + @Override + public void componentHidden(ComponentEvent e) { + } + }); + } + + //Use SwingWorker to asynchronously calculate parts of the picture + public void calculatePicture() { + int yStep = getHeight() / NUM_OF_THREADS; + int yStart = 0; + for (int i = 0; i < calculators.length; i++) { + if ((calculators[i] != null) && !calculators[i].isDone()) { + calculators[i].cancel(true); + } + int yEnd = i == calculators.length - 1 ? getHeight() : yStart + yStep; + calculators[i] = new MandelbrotCalculator(yStart, yEnd); + calculators[i].execute(); + yStart = yEnd; + } + } + // + + private Point2D calcCenter() { + return new Point2D.Double(xLowLimit + (xHighLimit - xLowLimit) / 2, + yLowLimit + (yHighLimit - yLowLimit) / 2); + } + + @Override + protected void paintComponent(Graphics g) { + super.paintComponent(g); + g.drawImage(buffer, 0, 0, null); + } + + //Use SwingWorker to asynchronously calculate parts of the picture + private class MandelbrotCalculator extends SwingWorker { + + private final int yStart; + private final int yEnd; + + public MandelbrotCalculator(int yStart, int yEnd) { + this.yStart = yStart; + this.yEnd = yEnd; + } + + @Override + protected Object doInBackground() throws Exception { + int[] data = ((DataBufferInt) buffer.getRaster().getDataBuffer()).getData(); + + double xArr[] = new double[buffer.getWidth()]; + + for (int i = 0; i < xArr.length; i++) { + xArr[i] = i / xScale + xLowLimit; + } + + double yArr[] = new double[yEnd - yStart]; + + for (int i = 0; i < yArr.length; i++) { + yArr[i] = (yStart + i) / yScale + yLowLimit; + } + + int i = yStart * buffer.getWidth(); + + for (double y : yArr) { + for (double x : xArr) { + int value = calcValue(x, y); + + data[i] = value == maxIteration ? 0 : palette.getRgbColor(value); + + i++; + } + if (Thread.currentThread().isInterrupted()) { + return null; + } + publish(); + } + return null; + } + + private int calcValue(double x, double y) { + double x0 = x; + double y0 = y; + + for (int i = 0; i < maxIteration; i++) { + double x2 = x * x; + double y2 = y * y; + + if (x2 + y2 > 4) { + return i; + } + + y = 2 * x * y + y0; + x = x2 - y2 + x0; + } + + return maxIteration; + } + + @Override + protected void process(List chunks) { + repaint(); + } + } + // + + // Getters and Setters + public int getMaxIteration() { + return maxIteration; + } + + public void setMaxIteration(int maxIteration) { + int oldValue = this.maxIteration; + this.maxIteration = maxIteration; + firePropertyChange(MAX_ITERATION_PROPERTY_NAME, oldValue, maxIteration); + palette.setSize(maxIteration); + } + + public double getXHighLimit() { + return xHighLimit; + } + + public double getXLowLimit() { + return xLowLimit; + } + + public double getYLowLimit() { + return yLowLimit; + } + + public double getYHighLimit() { + return yHighLimit; + } + + public Point2D getCenter() { + return center; + } + + public void setCenter(Point2D coords) { + Point2D oldValue = this.center; + this.center = coords; + + double width = xHighLimit - xLowLimit; + double height = yHighLimit - yLowLimit; + + xLowLimit = coords.getX() - width / 2; + xHighLimit = xLowLimit + width; + yLowLimit = coords.getY() - height / 2; + yHighLimit = yLowLimit + height; + + firePropertyChange(CENTER_PROPERTY_NAME, oldValue, coords); + } + + public Palette getPalette() { + return palette; + } + + public void setPalette(Palette palette) { + Palette oldValue = this.palette; + palette.setSize(maxIteration); + this.palette = palette; + firePropertyChange(PALETTE_PROPERTY_NAME, oldValue, palette); + } +} diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/spinner/JPaletteShower.java b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/spinner/JPaletteShower.java new file mode 100644 index 00000000000..fc305da03b0 --- /dev/null +++ b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/spinner/JPaletteShower.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.sun.swingset3.demos.spinner; + +import javax.swing.*; +import java.awt.*; + +/** + * @author Mikhail Lapshin + */ +public class JPaletteShower extends JComponent { + + private Palette palette; + + public JPaletteShower(Palette palette, int width, int height) { + setPreferredSize(new Dimension(width, height)); + setMinimumSize(new Dimension(width, height)); + this.palette = palette; + } + + @Override + protected void paintComponent(Graphics g) { + super.paintComponent(g); + int w = getSize().width; + int h = getSize().height; + int maxIndex = palette.getSize() - 1; + double rate = (double) maxIndex / w; + for (int x = 0; x < w; x++) { + g.setColor(palette.getColor((int) (x * rate))); + g.fillRect(x, 0, 1, h); + } + } + + public Palette getPalette() { + return palette; + } + + public void setPalette(Palette palette) { + this.palette = palette; + repaint(); + } +} diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/spinner/JSpinnerPanel.java b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/spinner/JSpinnerPanel.java new file mode 100644 index 00000000000..b3cfb0d3d95 --- /dev/null +++ b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/spinner/JSpinnerPanel.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.sun.swingset3.demos.spinner; + +import javax.swing.*; +import java.awt.*; + +/** + * Arranges labels and spinners into two vertical columns. Labels at the left, + * spinners at the right. + * + * @author Mikhail Lapshin + */ +//Helpful component for layout of labeled spinners +public class JSpinnerPanel extends JPanel { + + private final JPanel labelPanel; + private final JPanel spinnerPanel; + + public JSpinnerPanel() { + setLayout(new BoxLayout(this, BoxLayout.X_AXIS)); + + labelPanel = new JPanel(); + labelPanel.setLayout(new GridLayout(0, 1)); + + spinnerPanel = new JPanel(); + spinnerPanel.setLayout(new GridLayout(0, 1)); + + add(labelPanel); + add(Box.createHorizontalStrut(5)); + add(spinnerPanel); + } + + public void addSpinner(String labelText, JSpinner spinner) { + JLabel label = new JLabel(labelText); + label.setHorizontalAlignment(SwingConstants.TRAILING); + labelPanel.add(label); + + JPanel flowPanel = new JPanel(); + flowPanel.setLayout(new FlowLayout(FlowLayout.LEADING, 5, 1)); + flowPanel.add(spinner); + spinnerPanel.add(flowPanel); + } +} +// + diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/spinner/MandelbrotControl.java b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/spinner/MandelbrotControl.java new file mode 100644 index 00000000000..9773febbc1c --- /dev/null +++ b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/spinner/MandelbrotControl.java @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.sun.swingset3.demos.spinner; + +import javax.swing.*; +import java.awt.*; +import java.awt.geom.Point2D; +import javax.swing.event.ChangeListener; +import javax.swing.event.ChangeEvent; +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeEvent; + +import com.sun.swingset3.demos.ResourceManager; + +/** + * @author Mikhail Lapshin + */ +public class MandelbrotControl extends JPanel { + + private final JMandelbrot mandelbrot; + private JSpinner iterSpinner; + private CoordSpinner xSpinner; + private CoordSpinner ySpinner; + private static final double COORD_SPINNER_STEP = 0.1d; // part of width or height + private final ResourceManager resourceManager; + + public MandelbrotControl(JMandelbrot mandelbrot, + ResourceManager resourceManager) { + this.mandelbrot = mandelbrot; + this.resourceManager = resourceManager; + createUI(); + installListeners(); + } + + private void createUI() { + setLayout(new FlowLayout(FlowLayout.LEADING, 5, 0)); + setBorder(BorderFactory.createTitledBorder( + resourceManager.getString("SpinnerDemo.fractalControls"))); + JSpinnerPanel spinnerPanel = new JSpinnerPanel(); + + //Create spinner + iterSpinner = new JSpinner(new SpinnerNumberModel( + mandelbrot.getMaxIteration(), 10, 100000, 50)); + // + //Add change listener using anonymus inner class + iterSpinner.addChangeListener(new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + mandelbrot.setMaxIteration((Integer) iterSpinner.getValue()); + mandelbrot.calculatePicture(); + } + }); + // + spinnerPanel.addSpinner( + resourceManager.getString("SpinnerDemo.iterations"), iterSpinner); + + //Create spinner + final double xValue = mandelbrot.getCenter().getX(); + double width = mandelbrot.getXHighLimit() - mandelbrot.getXLowLimit(); + xSpinner = new CoordSpinner(xValue, width * COORD_SPINNER_STEP); + // + //Add change listener using anonymus inner class + xSpinner.addChangeListener(new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + Double newX = (Double) xSpinner.getValue(); + mandelbrot.setCenter(new Point2D.Double( + newX, mandelbrot.getCenter().getY())); + mandelbrot.calculatePicture(); + } + }); + // + spinnerPanel.addSpinner( + resourceManager.getString("SpinnerDemo.x"), xSpinner); + + //Create spinner + final double yValue = mandelbrot.getCenter().getY(); + double height = mandelbrot.getYHighLimit() - mandelbrot.getYLowLimit(); + ySpinner = new CoordSpinner(yValue, height * COORD_SPINNER_STEP); + // + //Add change listener using anonymus inner class + ySpinner.addChangeListener(new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + Double newY = (Double) ySpinner.getValue(); + mandelbrot.setCenter(new Point2D.Double( + mandelbrot.getCenter().getX(), newY)); + mandelbrot.calculatePicture(); + } + }); + // + spinnerPanel.addSpinner( + resourceManager.getString("SpinnerDemo.y"), ySpinner); + + add(spinnerPanel); + } + + private void installListeners() { + mandelbrot.addPropertyChangeListener( + JMandelbrot.CENTER_PROPERTY_NAME, + new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent evt) { + double width = mandelbrot.getXHighLimit() + - mandelbrot.getXLowLimit(); + double newX = mandelbrot.getCenter().getX(); + xSpinner.updateModel(newX, width * COORD_SPINNER_STEP); + double height = mandelbrot.getYHighLimit() + - mandelbrot.getYLowLimit(); + double newY = mandelbrot.getCenter().getY(); + ySpinner.updateModel(newY, height * COORD_SPINNER_STEP); + } + } + ); + } + + //Customized spinner class + // It uses special format for NumberEditor and has constant preferred width + private static class CoordSpinner extends JSpinner { + + @Override + protected JComponent createEditor(SpinnerModel model) { + return new NumberEditor(this, "#.####################"); + } + + public CoordSpinner(double value, double stepSize) { + super(new SpinnerNumberModel(value, -100, 100, stepSize)); + } + + //A useful shortcut method + public void updateModel(double value, double stepSize) { + SpinnerNumberModel model = (SpinnerNumberModel) getModel(); + model.setValue(value); + model.setStepSize(stepSize); + } + + @Override + public Dimension getPreferredSize() { + Dimension prefSize = super.getPreferredSize(); + prefSize.setSize(180, prefSize.getHeight()); + return prefSize; + } + } + // +} diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/spinner/Palette.java b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/spinner/Palette.java new file mode 100644 index 00000000000..6e7ac6950fa --- /dev/null +++ b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/spinner/Palette.java @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.sun.swingset3.demos.spinner; + +/** + * @author Mikhail Lapshin + */ +import java.awt.*; + +public class Palette { + + private final int minColor; + private final int colorRange; + private Color[] colors; + private int[] rgbColors; + private final int rSteps; + private final int gSteps; + private final int bSteps; + private int totalRange; + private int rRange; + private int gRange; + private int bRange; + private final double rStart; + private final double gStart; + private final double bStart; + + public Palette(int totalRange, int minColor, int maxColor, double rStart, + double gStart, double bStart, int rSteps, int gSteps, int bSteps) { + this.minColor = minColor; + this.colorRange = maxColor - minColor; + this.rStart = rStart; + this.gStart = gStart; + this.bStart = bStart; + this.rSteps = rSteps; + this.gSteps = gSteps; + this.bSteps = bSteps; + setSize(totalRange); + } + + public void setSize(int newSize) { + totalRange = newSize; + rRange = totalRange / rSteps; + gRange = totalRange / gSteps; + bRange = totalRange / bSteps; + fillColorTable(); + } + + private void fillColorTable() { + colors = new Color[totalRange]; + rgbColors = new int[totalRange]; + for (int i = 0; i < totalRange; i++) { + double cosR = Math.cos(i * 2 * Math.PI / rRange + rStart); + double cosG = Math.cos(i * 2 * Math.PI / gRange + gStart); + double cosB = Math.cos(i * 2 * Math.PI / bRange + bStart); + Color color = new Color( + (int) ((cosR * colorRange) + colorRange) / 2 + minColor, + (int) ((cosG * colorRange) + colorRange) / 2 + minColor, + (int) ((cosB * colorRange) + colorRange) / 2 + minColor); + colors[i] = color; + rgbColors[i] = color.getRGB(); + } + } + + public Color getColor(int index) { + return colors[index]; + } + + public int getRgbColor(int index) { + return rgbColors[index]; + } + + public int getSize() { + return totalRange; + } +} diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/spinner/PaletteChooser.java b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/spinner/PaletteChooser.java new file mode 100644 index 00000000000..0a839dbd672 --- /dev/null +++ b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/spinner/PaletteChooser.java @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.sun.swingset3.demos.spinner; + +import javax.swing.*; +import java.awt.*; +import javax.swing.event.ChangeListener; +import javax.swing.event.ChangeEvent; + +import com.sun.swingset3.demos.ResourceManager; + +/** + * @author Mikhail Lapshin + */ +public class PaletteChooser extends JPanel { + + private static final int MIN_COLOR = 50; + private static final int MAX_COLOR = 255; + private static final int R_STEPS = 5; + private static final int G_STEPS = 5; + private static final int B_STEPS = 5; + private static final int R_ANGLE = 270; + private static final int G_ANGLE = 90; + private static final int B_ANGLE = 0; + + public static final String PALETTE_PROPERTY_NAME = "palette"; + + private final ResourceManager resourceManager; + private Palette palette; + private final JPaletteShower shower; + private final ChangeListener changeListener; + + private JSpinner rsSpinner; + private JSpinner gsSpinner; + private JSpinner bsSpinner; + private JSpinner raSpinner; + private JSpinner gaSpinner; + private JSpinner baSpinner; + + public PaletteChooser(ResourceManager resourceManager) { + this.resourceManager = resourceManager; + palette = new Palette(MAX_COLOR - MIN_COLOR, MIN_COLOR, MAX_COLOR, + Math.toRadians(R_ANGLE), Math.toRadians(G_ANGLE), + Math.toRadians(B_ANGLE), R_STEPS, G_STEPS, B_STEPS); + shower = new JPaletteShower(palette, 250, 25); + + //Use single change listener for several spinners + changeListener = new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + setPalette(createPalette()); + shower.setPalette(palette); + repaint(); + } + }; + // + + setBorder(BorderFactory.createTitledBorder( + resourceManager.getString("SpinnerDemo.colorPalette"))); + setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); + add(shower); + add(createControlPanel()); + } + + private double toRadians(JSpinner spinner) { + return Math.toRadians(getIntValue(spinner)); + } + + private Palette createPalette() { + return new Palette(getWidth(), MIN_COLOR, MAX_COLOR, + toRadians(raSpinner), toRadians(gaSpinner), + toRadians(baSpinner), getIntValue(rsSpinner), + getIntValue(gsSpinner), getIntValue(bsSpinner)); + } + + private static int getIntValue(JSpinner spinner) { + return (Integer) spinner.getValue(); + } + + private JPanel createControlPanel() { + JPanel controlPanel = new JPanel(); + controlPanel.setLayout(new GridLayout(1, 2)); + controlPanel.add(createStepPanel()); + controlPanel.add(createStartAnglePanel()); + return controlPanel; + } + + private JPanel createStartAnglePanel() { + JSpinnerPanel startAnglePanel = new JSpinnerPanel(); + startAnglePanel.setBorder(BorderFactory.createTitledBorder( + resourceManager.getString("SpinnerDemo.startAngles"))); + + raSpinner = createAngleSpinner(R_ANGLE, "SpinnerDemo.r", startAnglePanel); + gaSpinner = createAngleSpinner(G_ANGLE, "SpinnerDemo.g", startAnglePanel); + baSpinner = createAngleSpinner(B_ANGLE, "SpinnerDemo.b", startAnglePanel); + + return startAnglePanel; + } + + private JPanel createStepPanel() { + JSpinnerPanel stepPanel = new JSpinnerPanel(); + stepPanel.setBorder(BorderFactory.createTitledBorder( + resourceManager.getString("SpinnerDemo.steps"))); + + rsSpinner = createStepSpinner(R_STEPS, "SpinnerDemo.r", stepPanel); + gsSpinner = createStepSpinner(G_STEPS, "SpinnerDemo.g", stepPanel); + bsSpinner = createStepSpinner(B_STEPS, "SpinnerDemo.b", stepPanel); + + return stepPanel; + } + + private JSpinner createAngleSpinner(int startAngle, String resourceName, + JSpinnerPanel parent) { + SpinnerModel model = new SpinnerNumberModel(startAngle, 0, 360, 10); + return createSpinner(model, resourceName, parent); + } + + private JSpinner createStepSpinner(int startSteps, String resourceName, + JSpinnerPanel parent) { + SpinnerModel model = new SpinnerNumberModel(startSteps, 1, 1000, 1); + return createSpinner(model, resourceName, parent); + } + + private JSpinner createSpinner(SpinnerModel model, String resourceName, + JSpinnerPanel parent) { + + //Create spinner + JSpinner spinner = new JSpinner(model); + // + //Use single change listener for several spinners + spinner.addChangeListener(changeListener); + // + parent.addSpinner(resourceManager.getString(resourceName), spinner); + return spinner; + } + + public Palette getPalette() { + return palette; + } + + private void setPalette(Palette palette) { + Palette oldValue = this.palette; + this.palette = palette; + firePropertyChange(PALETTE_PROPERTY_NAME, oldValue, palette); + } +} diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/spinner/SpinnerDemo.java b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/spinner/SpinnerDemo.java new file mode 100644 index 00000000000..0694a843341 --- /dev/null +++ b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/spinner/SpinnerDemo.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.sun.swingset3.demos.spinner; + +import javax.swing.*; +import java.awt.*; +import java.beans.PropertyChangeEvent; + +import com.sun.swingset3.DemoProperties; +import com.sun.swingset3.demos.ResourceManager; + +/** + * Demonstrates JSpinner and SwingWorker + * + * @author Mikhail Lapshin + */ +@DemoProperties( + value = "Spinner Demo", + category = "Controls", + description = "Demonstrates JSpinner and SwingWorker", + sourceFiles = { + "com/sun/swingset3/demos/spinner/SpinnerDemo.java", + "com/sun/swingset3/demos/spinner/JMandelbrot.java", + "com/sun/swingset3/demos/spinner/JPaletteShower.java", + "com/sun/swingset3/demos/spinner/JSpinnerPanel.java", + "com/sun/swingset3/demos/spinner/MandelbrotControl.java", + "com/sun/swingset3/demos/spinner/Palette.java", + "com/sun/swingset3/demos/spinner/PaletteChooser.java", + "com/sun/swingset3/demos/ResourceManager.java", + "com/sun/swingset3/demos/spinner/resources/SpinnerDemo.properties", + "com/sun/swingset3/demos/spinner/resources/images/SpinnerDemo.gif" + } +) +public class SpinnerDemo extends JPanel { + + private final ResourceManager resourceManager = new ResourceManager(getClass()); + public static final String DEMO_TITLE = SpinnerDemo.class.getAnnotation(DemoProperties.class).value(); + + public static void main(String[] args) { + JFrame frame = new JFrame(DEMO_TITLE); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.getContentPane().add(new SpinnerDemo()); + frame.setPreferredSize(new Dimension(800, 600)); + frame.pack(); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + } + + public SpinnerDemo() { + setLayout(new BorderLayout()); + + // Create main components + PaletteChooser chooser + = new PaletteChooser(resourceManager); + final JMandelbrot mandelbrot + = new JMandelbrot(400, 400, chooser.getPalette(), resourceManager); + MandelbrotControl mandelbrotControl + = new MandelbrotControl(mandelbrot, resourceManager); + + // Connect palette chooser and mandelbrot component + chooser.addPropertyChangeListener(PaletteChooser.PALETTE_PROPERTY_NAME, + (PropertyChangeEvent evt) -> { + mandelbrot.setPalette((Palette) evt.getNewValue()); + mandelbrot.calculatePicture(); + }); + + // Layout components + add(mandelbrot); + + JPanel controlPanel = new JPanel(); + controlPanel.setLayout(new BorderLayout()); + controlPanel.add(chooser, BorderLayout.NORTH); + + JPanel mandelbrotControlPanel = new JPanel(); + mandelbrotControlPanel.setLayout(new BorderLayout()); + mandelbrotControlPanel.add(mandelbrotControl, BorderLayout.NORTH); + controlPanel.add(mandelbrotControlPanel, BorderLayout.CENTER); + + add(controlPanel, BorderLayout.EAST); + } +} diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/spinner/resources/SpinnerDemo.properties b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/spinner/resources/SpinnerDemo.properties new file mode 100644 index 00000000000..c5372efe5fb --- /dev/null +++ b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/spinner/resources/SpinnerDemo.properties @@ -0,0 +1,15 @@ +### Spinner Demo ### + +SpinnerDemo.toolTip=Use left and right mouse buttons to zoom in and zoom out +SpinnerDemo.colorPalette=Color palette +SpinnerDemo.steps=Steps 1..1000 +SpinnerDemo.startAngles=Start angle 0..360 +SpinnerDemo.iterations=Iterations +SpinnerDemo.fractalControls=Fractal controls +SpinnerDemo.zoomRate=Zoom rate +SpinnerDemo.threads=Threads +SpinnerDemo.x=X +SpinnerDemo.y=Y +SpinnerDemo.r=R +SpinnerDemo.g=G +SpinnerDemo.b=B diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/spinner/resources/images/SpinnerDemo.gif b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/spinner/resources/images/SpinnerDemo.gif new file mode 100644 index 0000000000000000000000000000000000000000..87d560def4383e5c59429ce76a4f4cb3ea739ef2 GIT binary patch literal 1055 zcmeIx?@JSL90%}|ET>pV8sx811Pl9tb~iJ6l2K|JBt&FkHM39&N_tRkZF6o9+}#SJ zZR!n?TsXXQVsg+XXvftNsx}(7!FVz$BQsh$?-yh ze5cVHPEdccW&SNdh_^2r*0k$ZsmHz$|hUSD|L5 z-lWF*=RK4LGj*fJB|q(4pnn(&nkP<{O&T}l!kyH=_NtRD+Pzk~Y zNB{r=fh&S!AOzM0LEt!!BuM}Of*>#q|8@Nz|A0VoA~gkqQlW#JTQ`3qO~OOzl7c$h z(@C8qyR3QEHZt9tRZ>4xH){Q0*nQ|*dyn;5ZC_dAb7<09mzKBxyfsp;lNmB@U;i|O z);!FYUage9*0i*p%PiJsovIjL8ysxAVOblqS8KP+N+s?&>O zKRfbu+Gd~VYCG%?Y2W`Wtbfu}s>vl>IASfRzS}yTQCOuO>`c!-u;u8TJ(*2LukMlC q-kqO*veP%y*EoD4SF7Ecv$yQR<@v{>Z=!^A{FZy}-9%Ia9sdJ!)`M^W literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/splitpane/SplitPaneDemo.java b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/splitpane/SplitPaneDemo.java new file mode 100644 index 00000000000..0bc0cd78f0f --- /dev/null +++ b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/splitpane/SplitPaneDemo.java @@ -0,0 +1,292 @@ +/* + * Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.sun.swingset3.demos.splitpane; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import javax.swing.*; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +import com.sun.swingset3.DemoProperties; +import com.sun.swingset3.demos.ResourceManager; + +/** + * Split Pane demo + * + * @version 1.12 11/17/05 + * @author Scott Violet + * @author Jeff Dinkins + */ +@DemoProperties( + value = "JSplitPane Demo", + category = "Containers", + description = "Demonstrates JSplitPane, a container which lays out two components in an adjustable split view (horizontal or vertical)", + sourceFiles = { + "com/sun/swingset3/demos/splitpane/SplitPaneDemo.java", + "com/sun/swingset3/demos/ResourceManager.java", + "com/sun/swingset3/demos/splitpane/resources/SplitPaneDemo.properties", + "com/sun/swingset3/demos/splitpane/resources/images/day.jpg", + "com/sun/swingset3/demos/splitpane/resources/images/night.jpg", + "com/sun/swingset3/demos/splitpane/resources/images/SplitPaneDemo.gif" + } +) +public class SplitPaneDemo extends JPanel { + + private static final ResourceManager resourceManager = new ResourceManager(SplitPaneDemo.class); + public static final String VERTICAL_SPLIT = resourceManager.getString("SplitPaneDemo.vert_split"); + public static final String HORIZONTAL_SPLIT = resourceManager.getString("SplitPaneDemo.horz_split"); + public static final String ONE_TOUCH_EXPANDABLE = resourceManager.getString("SplitPaneDemo.one_touch_expandable"); + public static final String SECOND_COMPONENT_MIN_SIZE = resourceManager.getString("SplitPaneDemo.second_component_min_size"); + public static final String FIRST_COMPONENT_MIN_SIZE = resourceManager.getString("SplitPaneDemo.first_component_min_size"); + public static final String DIVIDER_SIZE = resourceManager.getString("SplitPaneDemo.divider_size"); + public static final String DEMO_TITLE = SplitPaneDemo.class.getAnnotation(DemoProperties.class).value(); + + private static final Insets insets = new Insets(4, 8, 4, 8); + + private final JSplitPane splitPane; + private final JLabel day; + private final JLabel night; + + private JPanel controlPanel; + private GridBagLayout gridbag; + private GridBagConstraints c; + + /** + * main method allows us to run as a standalone demo. + * + * @param args + */ + public static void main(String[] args) { + JFrame frame = new JFrame(DEMO_TITLE); + + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.getContentPane().add(new SplitPaneDemo()); + frame.setPreferredSize(new Dimension(800, 600)); + frame.pack(); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + } + + /** + * SplitPaneDemo Constructor + */ + public SplitPaneDemo() { + setLayout(new BorderLayout()); + + //Create horizontal SplitPane with day and night + day = new JLabel(resourceManager.createImageIcon("day.jpg", + resourceManager.getString("SplitPaneDemo.day"))); + night = new JLabel(resourceManager.createImageIcon("night.jpg", + resourceManager.getString("SplitPaneDemo.night"))); + + splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, day, night); + // + + //Turn on continuous layout + splitPane.setContinuousLayout(true); + // + + //Turn on one-touch expansion + splitPane.setOneTouchExpandable(true); + // + + //Set divider location + splitPane.setDividerLocation(200); + // + + //Set minimum size for each child + day.setMinimumSize(new Dimension(20, 20)); + night.setMinimumSize(new Dimension(20, 20)); + // + + add(splitPane, BorderLayout.CENTER); + setBackground(Color.black); + + add(createSplitPaneControls(), BorderLayout.SOUTH); + } + + /** + * Creates controls to alter the JSplitPane. + * + * @return + */ + protected final JPanel createSplitPaneControls() { + + gridbag = new GridBagLayout(); + c = new GridBagConstraints(); + controlPanel = new JPanel(gridbag); + + //Create radio box to edit splitpane orientation + Box box = Box.createHorizontalBox(); + ButtonGroup group = new ButtonGroup(); + + OrientationListener orientationListener = new OrientationListener(); + + JRadioButton button = new JRadioButton(VERTICAL_SPLIT); + button.setActionCommand("vertical"); + button.addActionListener(orientationListener); + group.add(button); + box.add(button); + + button = new JRadioButton(HORIZONTAL_SPLIT); + button.setActionCommand("horizontal"); + button.setSelected(true); + button.addActionListener(orientationListener); + group.add(button); + box.add(button); + // + + addToGridbag(box, 0, 0, 1, 1, + GridBagConstraints.NONE, GridBagConstraints.WEST); + + //Create checkbox to edit continuous layout + JCheckBox checkBox = new JCheckBox(resourceManager.getString("SplitPaneDemo.cont_layout")); + checkBox.setSelected(true); + + checkBox.addChangeListener((ChangeEvent e) -> { + splitPane.setContinuousLayout( + ((JCheckBox) e.getSource()).isSelected()); + }); + // + + c.gridy++; + addToGridbag(checkBox, 0, 1, 1, 1, + GridBagConstraints.NONE, GridBagConstraints.WEST); + + //Create checkbox to edit one-touch-expandable + checkBox = new JCheckBox(ONE_TOUCH_EXPANDABLE); + checkBox.setSelected(true); + + checkBox.addChangeListener((ChangeEvent e) -> { + splitPane.setOneTouchExpandable( + ((JCheckBox) e.getSource()).isSelected()); + }); + // + + addToGridbag(checkBox, 0, 2, 1, 1, + GridBagConstraints.NONE, GridBagConstraints.WEST); + + JSeparator separator = new JSeparator(JSeparator.VERTICAL); + addToGridbag(separator, 1, 0, 1, 3, + GridBagConstraints.VERTICAL, GridBagConstraints.CENTER); + + //Create spinner to edit divider size + final JSpinner spinner = new JSpinner( + new SpinnerNumberModel(splitPane.getDividerSize(), 5, 50, 2)); + + spinner.addChangeListener((ChangeEvent event) -> { + SpinnerNumberModel model = (SpinnerNumberModel) spinner.getModel(); + splitPane.setDividerSize(model.getNumber().intValue()); + }); + // + + JLabel label = new JLabel(DIVIDER_SIZE); + label.setLabelFor(spinner); + addToGridbag(label, 2, 0, 1, 1, + GridBagConstraints.NONE, GridBagConstraints.EAST); + addToGridbag(spinner, 3, 0, 1, 1, + GridBagConstraints.NONE, GridBagConstraints.WEST); + + //Create spinners to edit day & night's minimum sizes + JSpinner minSizeSpinner = new JSpinner( + new SpinnerNumberModel(day.getMinimumSize().width, 0, 300, 10)); + + minSizeSpinner.addChangeListener(new MinimumSizeListener(day)); + // + + label = new JLabel(FIRST_COMPONENT_MIN_SIZE); + label.setLabelFor(minSizeSpinner); + addToGridbag(label, 2, 1, 1, 1, + GridBagConstraints.NONE, GridBagConstraints.EAST); + addToGridbag(minSizeSpinner, 3, 1, 1, 1, + GridBagConstraints.NONE, GridBagConstraints.WEST); + + //Create spinners to edit day & night's minimum sizes + minSizeSpinner = new JSpinner( + new SpinnerNumberModel(night.getMinimumSize().width, 0, 300, 10)); + + minSizeSpinner.addChangeListener(new MinimumSizeListener(night)); + // + + label = new JLabel(SECOND_COMPONENT_MIN_SIZE); + label.setLabelFor(minSizeSpinner); + addToGridbag(label, 2, 2, 1, 1, + GridBagConstraints.NONE, GridBagConstraints.EAST); + addToGridbag(minSizeSpinner, 3, 2, 1, 1, + GridBagConstraints.NONE, GridBagConstraints.WEST); + + return controlPanel; + } + + protected void addToGridbag(JComponent child, int gx, int gy, + int gwidth, int gheight, int fill, int anchor) { + c.insets = insets; + c.gridx = gx; + c.gridy = gy; + c.gridwidth = gwidth; + c.gridheight = gheight; + c.fill = fill; + c.anchor = anchor; + gridbag.addLayoutComponent(child, c); + controlPanel.add(child); + + } + + //Create radio box to edit splitpane orientation + private class OrientationListener implements ActionListener { + + @Override + public void actionPerformed(ActionEvent event) { + splitPane.setOrientation(event.getActionCommand().equals("vertical") + ? JSplitPane.VERTICAL_SPLIT : JSplitPane.HORIZONTAL_SPLIT); + } + + } + // + + //Create spinners to edit day & night's minimum sizes + public class MinimumSizeListener implements ChangeListener { + + private final JComponent component; + + public MinimumSizeListener(JComponent c) { + this.component = c; + } + + @Override + public void stateChanged(ChangeEvent event) { + JSpinner spinner = (JSpinner) event.getSource(); + SpinnerNumberModel model = (SpinnerNumberModel) spinner.getModel(); + int min = model.getNumber().intValue(); + component.setMinimumSize(new Dimension(min, min)); + } + } + // +} diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/splitpane/resources/SplitPaneDemo.properties b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/splitpane/resources/SplitPaneDemo.properties new file mode 100644 index 00000000000..ef61ebdbdfd --- /dev/null +++ b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/splitpane/resources/SplitPaneDemo.properties @@ -0,0 +1,14 @@ +### SplitPane Demo ### + +SplitPaneDemo.accessible_description=JSplitPane Demo +SplitPaneDemo.name=Split Pane Demo +SplitPaneDemo.tooltip=JSplitPane demo +SplitPaneDemo.day=San Francisco by day +SplitPaneDemo.night=San Francisco by night +SplitPaneDemo.vert_split=Vertical Split +SplitPaneDemo.horz_split=Horizontal Split +SplitPaneDemo.cont_layout=Continuous Layout +SplitPaneDemo.one_touch_expandable=One-Touch expandable +SplitPaneDemo.divider_size=Divider Size +SplitPaneDemo.first_component_min_size=Day's Minimum Size +SplitPaneDemo.second_component_min_size=Night's Minimum Size diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/splitpane/resources/images/SplitPaneDemo.gif b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/splitpane/resources/images/SplitPaneDemo.gif new file mode 100644 index 0000000000000000000000000000000000000000..945aa5b5e779617a53d8798ce345049074da0e35 GIT binary patch literal 210 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 z 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%0Db7Ycxwc#GVWOPl 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

hop*HcX5npBL$ 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

azTvLne) zL~34nA`Y`tDkU%E2Bc{rc#+0wENBX@w=K9&slZF6X6wp8ejsO<)pY}fY zr2^1;{k?gr$0S#;1&Yn6KE4Ws(pOJ9wK4T8SbszBOmkHNoo`43OIEbU`nHhP#T5l)W4%Oq)b2ruq#ZLR!UUAjw| zHK@>q15lb4{YOQjjFGER9054#ZH=^7cMAmf(OX7AJ|vpTf(1Z25!<_f?WK5;)1){} zy8Eu1ZjAmV=p@R?hC9LXIZS@kWS*s|Xty-A6?yzpdfh; zla+YXrINNKuE*B>m3;I%@%09#3e~2TSlYgU%t&X(%oAL55W3NUu=)k5FqA?}Or?`n zMJZA6ws78~Jtvygqwz9s!5Z_8@Y9flHc9iZF=_`-NVDORSHyq zQ&C_8;S80j0OY7sQUx`1^`sNus@YlX{w3LML6=st05w`_(@(|l>aHv0(q+YMZ8}w$ zuS-gh(~04xiWEg)d`mka@oM;IgPurfJ4mWmHFx%2n=H+@AqpTQt{@@BYN=9O;6jQK@B=FZMR|-86-uh3p z>ps;Io8*=N7}Hp$O>#93CaP*H#Cr6uMBZf)?Xs=IEOcQR)kPGjAr1x_pn<`?4^p=N zdwSs}iyyV~dy5TKCg8%>!43wpx{n~6lM`6qz-p+Q8doH_Ty=DXsf-88vskU%FTLH` z?RN@iytaZx5WjP(k>RlK1h;5bUy8I82M&V89Rs zO-BlW(2td<-8)YmxG~GR@_7oHtZcV+M>T3j#z2us*VWZ z#AB-`k0Fr@RaF}JX%bl>it;d6qjytDZlSmOG?LzFCS#>&jO36BtGbOVg(p*$1mF_H z0)i_!uNwAPX10zctF>KGKqiz$bFGC)0Z=gmC$th4`!{FxjQB0}k?hU&x%SrJ+qC%h z+Z#`7ZF&sz?qA}#tes6YeNN@B&SUWStbRK!9Rw9n1)8o(YS*3s#+FE#4&%A*$ac}O zZO~4WOQqUZc=Rf@9*U^USSVt((xre>Mxc68W3}3}x1)HyXc8E;ewbMuivVK)YYIbD z2BQ{Q>e4`JB=4V`4Zd2Q5Q_IF6ETJ6+A4&Dt%j z=?r0C^^hcjCq0bmUeeVZE97_{kq|8*5(<}+Cb4LqIF@U;4f{_Xw9+MOdWH1=07LA| zh9yd{)8}9C{$8CHjU-Z?-|F)7>ntrv5%RoiP^3C|36dv6q-l`2RzeFCU^y1&>+Z5E z$TY^q7*iuP`BSGO@S{4}Lvb2vMLi8^{Dyj&E>$OaB&ehn49O5hd8An5k_fyhIf@Y) zJFG;l><`zTP3iQ2BBHrbUNjZ;;%mpN8>wCOIn)IOG6evjs2XdJJ}1wnsbr2)7}KO| zp?rQlK~aUldq`v@*b{pZ?L{cGEsrvM`e4?C;19E`lT}pJB|6mAR8*}cI?~dt^UE-$ zrALxPrKVX~#XU4~%fZ6a(&eziX!bAC}uTk04mrOKBS5Ynw$aU)r^wL6=aaM zfuvNEq;UXOfXT)Ndce$D*Eh0&w*c8k&|cr;?)pHf3y!F2K&k7iX&|k^1TbJqokNg8 z1Xz!+9`*=10-aO}8u0%BSFcOWzuJA9wJ;lxX>Fdv-nbpDyV9aP*S9e{k1Iuk+=*8~ zHZHgL)-DQ_qR6#oGO6k1GNTz_VvI`g3-0!ft8t#r?)z?rYpZtE8#;o~wL#Q`0ug=!z>Z`nxwnC1bX=9`+UP&6QYv-w| zu9jo#EP+bc{cr6Zy5FqIvcm#ds$)qdiSovf6+q#VYx3&%KTJ?OHxj(;#J07dr6VJCd*Xi`Q)$dVG{qWovO5>U!ug8Cr~nG|5i! zWFe@C0va<6gUB7ej^)f!TS~!W)J$LvGSx^>>_(+kG+Gq1GVVp&hHMm+g(XkDz?@x zn+pam9l6(gi*nfA)UwpP zw2))|{{ZZxNgT*xgk|Tc$%vxWrF_ymn_ZZmNRkD0g%RDEz{y~`zSE|QU$9fANjjTK zgz*_j=+k?UM+`;xTdKG(A2+4ulW5><+6p!PIohF3GIhEx@C8pu^t8*3&N;91e6r|T zj(mUF3(}`DsuRuCIBpQXs8Lk(fGeqTR(@Er(I~rn-uyvL(3RUC&1KB}m2IqG;w< zJiMr8j*=?dMIE&SR=4pJi(nBo{JcC2J~m-#27q;HOEkpqNj7pUiZ%_og!AILgj_Fi zIitnME`r8J>{@z_RK6nXiBH}PdP_-`Y4dlU{0iREsvRcM|Q zIMGS*vR3QlXTkR^+{I;hU4;QuC8qtV9q84%q*xk!LKz25Itn2_(?jiLc`{EgCm3zz$e__jCx?$ zJ=KiCTmooKJ2i{&in~`I`aC}OwO)W-54Ft))ktqN@iO$W;FjZ4tqV7Rv=!ubw0v}} zx-Ejh%EQ+Js=j4y?H%ebFQR5$`MhFNi%n`v5g;DBS*1pL#tecH;~6a~RAz za}hZSmy__8X--J26Oo_Pi6usZyHGHKD5y%%rKnp5q+XlcXea13F89sB4S``43%MW+ z5@(<$7$(#JYSID=H0+w^mAw$`_PFc4zPR*BZ}y2D$+Qtqu~a+YWEB)TE6g+TZL{M= zTUM^h88S!$3C1P!NlH>%oJFjy7t%PqT>GxTkysr!t`mEQ(!cD)`)=l<&22I= zywMgujwrkR@sqgsRcByvl3}6OAgCz~S%=tQw0Liot0c6Z7a*=ZSaJ6yE4SxymGj45 z*>z^)C>MZvbf-ngjX?6Sthbbh25#f_C8l+FUMxIPFt0G7r~H(RF^+E#8}dMYTUyr^ zqi>W?!|xYm4wZ_D;`m$UKfuJdHQncYGh|eiE$fY38z)Hdr+W*>3f{|^BmS_44EfzuX zfmFuGNQT+MHwWIc*MHa~R87N{>*fhf`CeUs@{x!zL;0~s7w5_Gp3d?2y?`1n@Lae| zkvyOe@}|18?o5KM9g7&!;Q^+v;I{G7GcIP1Gp75`zQzSma55Dh9(_R|^7%_aa49uD zX|@vpL4X-xUtX#!SKDA}qnv0DD!iocEU~jNeZ#lsx~hh}`SZ6t@qTJgFLwx$sjry7 z{Xz_YMS3&jR~`PL^iT!+2v&6McU?gh4J)pevLNs|ysa_o(cioj-GU;`n0!fY!mngW zhCv;njbq`}w3nU6d}wd;-VuH!wL|Cvre2N%3*{s(j)=3S&3ok`*JS8EZ~D$U8pZbP zdjGzr)Ai#o*G=Zb`)JEE=VVI8gF1Q}YhQh5l=@cCHAt~t&Dnyp2SHA8wv~RJh_7V3{pMKi)K1N-xT)hU+yk}n zH&f{MQ990N2q}c;llFg9RoysK(hJ5-b6O!5?LV=vt3vyJ9EFFid;zs(b|^;@a_xQK z!ulBg=$|P9yn*EFK3CshP5F*e>P1@EG#aLwfmbkw#~~`X_U__h!>*`oEf6iJalxKK zsEDJ}(#Dkz?Pay0PYeBb9nZEK&W3+=$gp^(X#qU&W3Qa3tn}>dAcO3gp;wi|gk0WO zJEx1o5_uKMjs5nu?u@x1xD`?x+P*|?sU_ZIbkB`j+i|D*OwD8X83OEcKswth}}#pg9VWFzK=3Bh?TocLzuJo(KE~I1Bxp0Z@OP zp+9^Q%#&r;*u}_8Cx|nT9{WFUtc&!qYyK+tPmBkZ(Za!j6@YK4W|?Cqx7{lsWT!mk zBHF}9ePHq9D=o5oHIo7LK$jFQb9=uQl?Q5EOf^yaHPW)ppT%KaOURk+f{~^RMhm^B zuC?9HVpL2Vv2C^ar=GXZ!%1y39~+t6Yqkr62EsfJ$UUA}LDC5-%`PPLa&gnmXcq+X zVtkN@dcR4Vnu!D2O(z*7-J()7aeN=I#0IOC``b?cz%X)_ z?F~5#iJSou6Ac{p`K9Dk(?km@jf_jz9d%JdI9wop@|LZO^Q1ui_+(Slu3f!huk@vC z3?<3tjdYv)rRW4~#MZr+Ha+Weq5b0Ld_IQdszoafsl61tk2yGCiq6luOlXH>fEJdX z2DjCRMRz*8z}z^{7b-k(Q`CS&-HC~nY@siCoFo&|!d~8$hX`PAva~EEXaHu%iN5XT zmF`|=w>y_Do_=5LHak4uVpWKbr)w@XBtCL9a37PuWJ{QyNvqRa#7sKwf*8!=uCpX* zAv`*%klBZ;kdMcBk2f)=Ld!xxWWG~6H*rHsy2IcezC~~9Al_}MWDE9a=Bc$Q@=a_6 zQXYgc5K;OSG1d&j5n8S-IHL{lI*0z(RsUwMuKxTH649mL18gg0ubiz1>Rb#3rp3Tv zj;GwTti9w8dp%|8o>QMWFd@W>#~mb`V1(?LCn2^@^jWJ^K!Q%by?p)Xdj)8CZEgzx zx_)!fBVRO$Zr8qcv1J4FIUk2vJQ@PN`E*)`U1nu#ULJ zL%{Z{w-QxKD}pN`WvM#UbO3nU6rjkz42a;651jPvG{V&0e5ugUU!uZ7mTQ~wUHOQs zb>3E6R1BUAugQ}28F>B~N4l_Hc>laP#~Vxa%g)qo?tj$@is8C)iaI<09HVDKA_tS2 zK5y;kDHgvzzIW!`a8jRKW_W*^Sk`!U)i}DxS;w`gi{S@9rnYw}#J?;uB<>B?cN?Tc zi4FApIJ11h4QN+`dOjj$g27bGc~IF#1>)EiSR0XuzMjsWt75O|H910#w?t(L-TwPP zXq&sfp>bjx>Dl{EvDrIQeo?@ZdrZN$6Qn#1LKB(LO2nyzz z*{l^WzQo@F82O27;fgJU6EGs?GHvdwHzP?|UD$cB6>4DA%wQ6&r=O=LMk+w%Itt!d z%SUXZV&ap%iYpHv<}-f0Cy3a5c^+EeD2tYF;K01{X3b^>&siG6%I? zfv957)hg0CzN2K-ER77){!(ju`2ZP3bI(F~w5=7hD1TY@k#VC03YCLptFNJ@%B5u5 z6hSdX2fK0*!IT5-2Zot4ItQMewU5c#jh$TGJq?^KA(|;A;zC*|xlCwz@N#r}KMgGV z=o5$4yq%zprC*Z;EEGO4x6xQjaBM-Sue8vrN1VISBIKv&S-si`By(}J{I^F4mt66S zc1=-c9I9XI52`v$?J2HhAy{fl=jqsfc1&G5f0M`}HO$lVHfpX`2p)S-^Y$9MKpL>q zG-UoLyclHiV0+PtgzEJh8pbJ!f@L=85lG+tgHpj$Ru9g)GEOJYpg%pH$`j0g<^yV^uvSdNhu<69a3qc0Za3bx->` zsdW98@(G|)&K^~R>)J>hBl^4T1;^Cge95$JO^b;u5#}BT+u`JvmX5o*ZjESxmS$GHV6I^!zu?a`M%oN zRl%R9d4|+9Gi%N5+Nwd!Q|Qe}m=Z75J3vmmxHve=E?Nn0adxA4$v&4p8IpOqx)1g| z>pHwfEQ1+?!Dw0bsK{h(ctnf#I3hCgde|CeJtWt#C8UZCb%LWmogzh>0rQ}*b@J^TzHPQC_{PU|PQKsf0kf=F1XiNm} z5`Cl+rs1MckJ+Nqfbzi;^A~Tlu(Bh$^Z~zO~(T zvhkr(drLJt3TzB;kg|I4&13g(Gn^5#mVaNtnM0Uqpal+fftvg7;I4r0N$n)pep?%J z8Fm%T`lHl<>ImK;Svxu0#YYXJm$ue}7eLRF(_RfOn9NfKc=IvQGf?&OM`h?W`o_fC zzcoi_O!S>Qlp6W#Zj^$iZSnG#u&KYpYzNHUJ;GxrhF5}`L7jKZSjafXKX?xIlQsP7QkUTpO zW$AGoYdp%rZuZrQ!@bx`003R{*_Y_bi}n8bsAWj8;Zc7EQRb6qV9u^3M(RXhI#lqE zsoK6xHpM6&+76pzOMpuv*mMyomRvd&3ilv2IVxFzu?z!gA#r3Ur&q|JyLQ z4c9-}Nj;Dt>i=Ejp2SSsBBUGZ$<5k~uBe)B+dIR&>R%1OWgJcK8p^g9lOOEfqxr!s zOo+C(YvUXy&*>4@6Gq2a=ctUy<1-6fY^F{Ia1qz4(fI9du1%%N6H;I}UbB`;R%?<7 zaNJeHnp<^%S7^1wINx&dZxB7hkVc!DXRmD92wPeO4zhwsG#893!g39L$-K!h{-zp< z<0KdGh)7@86Lc1}<>wFJUuxgp>n;klR>Mf3#|vwLN5>$99&^6y`vM`$`-Q1Z4aII! z4sUf{;kCWEvnU6uAw6DBHgUitY&H7ZA&o7=Ql*#&s8Gx4#t>W5xoCp{p$EN?IdK%} zN;}tlG#D;jtpV;?7Vw_QGVXl0(ccg0daMtDkmzBn3 z!y)!k|H@0JD$GxE{ff7-B9LAq^sn9fC5TRz*&-QfE>vsMU#b+jf(%^$PGgXZgI}d_ zrFwgqNmFENgTKT%lS&H)WVX{dZ%o~eWwC)>zX1A8Ybrff!aht!Y!$8TF&pw zytS10RPGB`NuK#B?xU$oZ(#)RsRnb^233Ps^{1Qc>GN4Mo)j}kf2S8b(f(?Ez8>*{ zbps4FrEsRuQmYw^(~zpgb*y*4F>{dE1gLw;l_Q0gG1&( zE}C-%FTlwk)W_hW3wu5KTkpp!Ss1S zGGI^vvmG@ze96plF97Fnfyt0;axgsA99m74oZ)SAZs~AZkp5C=r;=La2QOTyNX`W<1G$T~a$A=m&zwyqBDXBTaR}L}!&j^ce_!B@ zTS6N_1tstPdP$uE3<4 z4gDj$XZ^VUTMDt&F(D8ic_aDZDdm0vxi7s~aYpm}i|~&gL1L}xQT>!U zJijz}POKHKX#^4A@6R_|W}eiv$kaT{e@00qu*0IsYd)HM-|N}TG`7mCC|XO>c%gH3 z|71(JVA`7zLtv$=7v}Wt2Zl?ZR1Ce;2k#W~#>gs8{5l}1xD0O!N-m{i5_4D%w;h_t zb|9r1_aVo$a;u_U7Oq~$l0=lQ^x$g|E_uK0&9p+E zv9|E>e+)|Byfi)I=86orj=v7dx7YN2Eb5urw#$jCxfc`hxBSecVrCBMR(*&~Qi!QJ zW{t5soN6T3ZKEDDy~uRQ02=lUKzdEf<(}1#083ud@c|h*5TbAmZ_?Hw{^{P;Fw};p zdhTNhxzr1nx1|BXc%!(Ohh<5PMI#%3Ec9jgb-!1m>=iKP<@)rDKGcWtximj}#XIhQ z&02d%7AGEN@;Gb<1|sY7VKaY^mLI@#p|dYpu)E-v z$b>YUTmz)B4^Kh7Y6+nu!6$R&_@ zdx;WtH!tW=C;Nws=d#%eHlvmd-Y~;e*M2)FidRrGbYro@D=W7@(;t+@$0aQ)gxfvBKT__|nS>$;vv`G(rnoo2YC>33vMOp?FWllNl-V z5E*bt1>7#g6@>4_Zp0S|lqh?I@^@uEA6xMft6579>IKGs zfH)mPx4`s?2bV+h!n_CWuQ)@C?~^vQG;^YPBF4lG5i{BM)!#Dt_%MvyST=KC ziH%DI{{hY;ih!(aINRFOCnh(?y;U6 zJ1cy4r;|h}!W@4%{+*^Xu&z~Zv%81i$Gt0z&=`v@V&{NgNQs%@d}F<*=%ieId1gK6 z4o!~ZtK~HRH=J1oL)UftugDyX)z!9VYqEF1X5T6%WO#M58?2snpvcM)F6 z`(NUV`;vh5dpCd8b0Z=@F}Bs(oco%pMDQkONc0eoq;Mj&<>bScDY1dS^K(PL{n^_s zb%cni0a97_(vJG)_2B>9w>BO3j7g%F0wmKE$=QjtKflr@r8rYAN1QTG*X1doYI)`D zDZCwOacd~b$JeFpvwk$lZ6DCQ#eTOVcdM~-g`OC#0?;{;ZjJI3m$hjuy3LyiftBTj z&78b{i66mTYsg&{>8Swxa8Y%-Q6+Z14u|YjB4Ba~(p>(ShQuBpG_0Jv_Iu-Qp@jL? zt8=$#o1f?nZEug^_b$rP8_p2Uw_oXPXwJ-h9GyRK75$-J;AEV-gJ!_V+|@90%@#zR ztfmA^u{PrF<3qZO=yu}^U}ie6KCizyh3Fl~G$s^!)XmlX3N;eE*)6I^GD&m3LnqeI zY9{@0_z33x5peA@mqp|9P*YLtDwtdiyMlzLUNY3#bg)mj_GSN~70N6EKJS>mI2EID8lxcz7mQ6NhG;%<<&4MoJnK_~017A=*SE}sx zyA9TN!0Xc>anM{&`gdpT!-y zNbD?Ld-HbSE&}?eb${;Mi6DId9!heei}KQx#PUv+&di>G7@0RcI7)h;y3!AjTPT)S zU8{3_q<#G(ia@F^jGUoNhEc}q|DAuJ=TX`YjXTmCC7vl+c7_#Jr%=@+A*T%`AeJ&1 zE#JiAFS^c#NniSn{9j|vW@wMe`|0e-D;E%}Gx?_wme5AqlYz`GG}jxo3Pj7nQM;|O zZGqc+5i{5_)$3L1Z7{C1U%(>WM2UZ|ws4u9gP(;}FWX{u5@Otc7 z2!nY)Ky&MId_Z0PaR6Caj_uIq7ST*#>Ynn{a(^n z74BAIx;X9l!W`;Q{hM#nx>EHV{mPY1{kQqfuAdZz+{zq#eKG-xc0})Qc1Ou(*2QT0 zN1Y#ja)+OP_QUz4Kc|lxSDgQxJF0w@_+dP8>ryxHW8u%6CIgwLu`RPb|7zIICI`5f zmwV!&xYlyd*sWIWr@bwai#2Z}dHbby5Bd*wsu$^$uI&%>(nZfjQ>^58w&;gVerA)uXk@!I5^)QHVd02AZC|Yl$Xa3 zSS%AT&&ARI-;w=@8CzQY53n}=TV(c;1hL0VoA}Tq@6VmGr1mEoZ+au8cUUOW9~Frg zDw5>H{@!ZbPTW~~rV1OB=a;bby z&+d|Ihk9d2XxPBr0!tukbYAzTG;)xzR&VL~^xK`X==@P-R~wUptRb%2%P!w-50nne zj%GyueD_NF2S~iTeD+_`zw1x-SiNt`e}G?Y{{hZC+Sv;J4^W%_PCki8$IEYJZu1&W46lt#z$RO6&GtJ)ePH*oKbI21MTxKNX3E*e@qAm zWXsp3v&Sw;=7*m${t>py9puBQt2#k6zdgIZb&&$@)(!o5el~BmxqZprS-GOZxvSjK z5@v#HcQ{Kjh8hfm`?c)-0`qw9$BEyAV^@6vkA6lP(xl(^C*Z|pui9pVy8S5Rzz;33C|K@u;6@B=i z6@d=4K$4$6eHZ?~%k1$O>%$7tmcvt1>qu5q0A9nTYSmex_|m)AU(kuz!}SLz0wqrr z+<{8ExVE6Ias5PDP0-Mor3r?;&P{>MWsXTTZfL|PNIm!Wp;xkfnnlD6CQe0El z4uZc{p`KoVqB9N`xBC-cR*@K)gDRG%^3 z{2d>sy^i5Lt1E?YsYO&(2Aq(4(lZ*=)3bDD&MVYbpi0t1%0)>n0q3Tk!wVh}6%i)B zf7cd%YIJnt_2LwDD-}2;IZs^TPEDtwse<>7H%}Xl-<3IqA_tJAn;tVz(;gPuC}nZO zYqJ-ap`O@odULkK$cXL||BFh#DQ}&D_IZM9wHG3E5Nf!*?ykU{E_(nw_`I~>t-1}1 zM;5mUtA#ervB2 zRI9!nb`{s!0{PTgvG7Gnrv$fvchl&qkYQSJ!9*$@biQ6ieK|QHSC;XG0bp93%f#~v zAQ8ewd8hiug++(aE-p5!WVyeF9iz@3SaiYY2l&K{CE8v`hYO0wcG1Kc&2x@<67i6o zFo~#45D}{{ZO2LF-}mo@y0>ULQFN8|#<60rbSrFoVzM0dr8`w*e-wkOyJdyqi&90x zZLR*E54jnJ*pP~9S{>;KQ(Z3l?dMa-6LHlDd6n7|6C@n|{I==*W0Y)v`O>#wC(XO# zd@7_O&BnWMarxRZp{$;Z;fh{sQhSK5B(_8=9q6J(H{#L>j*)1Vyff-&Zre}9_+l~O zbl&Te2mv=|_LmDc>Dq$_k=wUeWmo6*8}sYL%45e5oA~c1YcB^;o!%TfycO}tc$IJi zuWpdE`^+$E31h$Zw(m&TYf_&f7(Y-@%`aN5^L%u6m5+StW~P>mB%drpQRwz6TOIhR zlb}MKoNb+d>_}esrIKNVcT`839N)KXL)ooHF@$Z+*1)+NAEeMp*Pq|O-86ff zVBP#P7}5G-dnWh%gwAlUfsgH*IV2jC*!8NTQ-b5G&I2?lWux+{+tc}2R&pp1oUYmP z;`pDo5b14rY$oElz}!#qlXg#EuN=SH^yGay(uSl(YzZ2hkEcPSmRJ!qRbSM3Xf6V{58Ys{+s_7cOgkH?w!kq>qnD7?;2Jn^s16 z5@|fVv^egjemdTAK=){6dBzMHXwe`B14T}A;zSApvS?RsyihBzdb?!j_1^w6Tq9yI zBnlvx``Rd~Ma)m|th_qg8^I`h zqE$S+iyt7G>&)MMRWfyRELc8+6n!NlTovf$k=r=2ykOy2usDrohi?MwoEbt_J-5Pw zNmFk?1DLBqJ_K#3u1qqXVy+iKeJv&I;Ez_Xc>V~voT3j7PxxD?9qfD=@g!Rz=-Zoy=htCI(H7wA$M4a56j8? z^;)t-?iQ<#-kp?v`JVc^ulYJVrL{peuBpzxGmcg@Mwxmd(ClD35mb`S<3&C95F%k$7xLT!B$C2i#Z!t5F&KNLEF_x zMk#v3)ID+5x!=H$W&RAv5mw~$AiR=r{bINMld}M`_O1IM;R*s#mg#*^Q{54x$6P`V z`gzQpSH?|?C1US*yqb~wY2zVhSVYVc@^Dy$iK2&5z_Jg^e|*mAX6~PBq6VhEzCd*~ z3zwn6!eqOdxiPv+wZsB=GNFNr2lwMwZ0kL=)QL4})KvaW!MXhR&M*%_mg(%SQ~>#m z4flxHXU2x$6=9f}-oEPXjd>5Ow4nL$KlbyihV4AsO|gh1+vU$G`t*R=vDim|iZ8>J zQ&hmYl2JxsR{EQ!zWKrx(gs#7G7F=WDp@y{4|jQ5{r2&}W=}78KI5qvGDOqsw&JDI z50sd5LLFy=*6L^1bxTY>KRWf|DWd-@lbo*&lDlHUdMIzRjA{+ACcULV8S*`5!l9!~tv8pNJ2D8S6Wy?OvTVry8~Mn|w?t zmAp*CJ;8{r9{Oz^&h`)5ob!_o=zq?4NUn*{n14VW2dCye89Gx{w(!qJVd410QM0b( zH&3g!y?YfLy!D1^KII7>uF0=%wwLs3GTkRzWVXJY=D*KzTP|`O)zou+do<@ysaZ>9 zl@F~jO`vb(iO?mRGjGFN=zjGhpM+p0Nqkd`M{=u18hg+F$v_&dyCSI z{sl*`p}<9ehu{O53oaMEb;SMTF| zSkbqU7jNITjX$DnLA_%vFlXpDqW@YK(_a}w%&&&6gJ}jv2e@bZF31j-WimBot)5u5 zyy!MR2~&CZMFo8$P}4%tL+lQ<)we%A7L?58e)eZ2NpTIek^D*w>r9yh@k zRr$Q{w@F~VjVI?+|Mg zt_3wPQEAL0+e-9wmDrH_pM)iHwbI9;S^mRrCAriSf4xmmEKs?Gc7E+iFR|jMm#KQu zlA``mrN3H6W_>vM^^ZM+X$_6iy#_CZVuDijK$s5rM2E?X-wrs=_6zT@ps=VBav-w! zfV)%;CaE{cFbcBW86bJIDFTfE+7TPL11reY3R*4!HkWWpj|saLcCF&g({Ci|`Ss9y z01FV-3%vdROv=LK^{p_*B;>vK@9u>V$W_h>lP1iS>}^B2cumOOHcC8O+EkLtdHGXGd34EomitT3;1OB3|C?&9~SX|wA*M<($qCFhCW z@I`w8n{EsV;br%k(QeO`m}^OL*C2&VJIL@@=Yp%^6YEiH1J5t&BcySrc8kMVfcM8a zq}YS|)RL0bWKass5_D6w_h*E9vFgMJ+x$K0wLAJSYeQ@eHNC~NzoCI_#3 zguzvM30WFV_p)tzcRP2j2&XMoAqfP2Z>2A09XUD6#XRAEx0ORX8Onq+@^4+@(zX&A zF^ZV=4yN?^*0o|Q0L2^#&v8^6x23Driu*pqmQ+JypJ{YqkrM+nO+Q1_8gh!2+5~0W z_<{Qzs+l);79oL{l!d0cH6EcfANQ6Bi?*x9+KV}T{H5AEogRZ{xh^`(o6^vIg$6&3 z`3d(K=SQ}{e)Lq&sn_0ng>oGZAi+IgfgF3qidc2FPMG(F_FigM^#adzGN&jyJ89TiCYeYIVP!6JR=ZpELtqAZ=A@l`>yw6~MfMr2*HdtA zErZLlg^TwC<4?=2;PYq9y~}-UtXjdEnp9uBhv}aGR(`i6l5SNQiKr?U7U*ufw%dz( z`!Y~W(|w4BC*a@XZ{|_Qot%E2JuODc=$gmq2pJKOH#l|*x8px*R@1AAp0eJ>SeO}v z`LdW`Lde>SE;{5B$J(#ecI$9HiIf&MCDNa+QHo2wOno%T_oG zJL&J+AAw}iy>V^0WG6vf%(jB9W<7uszu0h1Aa%3d)LGhqcf-SU#gNAHXO0~_wOY`?*#!Oh&p3K}gA%eS&UWFBr8B$D(S-R5J{yWafp<`< zt+i^>QF-S1EB;95I&lV`wiX&JJ9#t{V&sx|V(#Xwlf9~Bx+YRoB(Vwxe&0^DjND-4T*?oNV9@GYp1E*+d#-un zLp?v?TidisxtUx)X=m)NOMZmfWAL(DSg3P_P&|%&uL?k~Z4)0G9nN&^G!{1MJ71U7 z^MfW==z?lgB1HUoGQ41uoq^Z16^;G#A7HiL#oU>V*DkW;^CKOo%_~nqMvRAd45s_# z5FMvYYxD*=IN$!gSxqttQ;g1Pk)em)=F2%NIJ90MkZtKL%k=@yeUwE7k-DoFHx~av zcpHr7Hs@_xN%m`FMOCJnKw9p_#LHzTJ&3fY&i7J;xLI5{J;{9R!aA^BMy4SH{3a5+ zLEFB&yZ#8YfM)RIITBJ*485}Ii5p>{zp*b1mD=J7bD`d8E^j$>gkZSEcL=qCo{o!8 zZ&hI3B$a-cS(x4C;oMU5tVtIx)Ap+&_CJPre_x)5xw&>hD{(p!43}ZmBk+s>0{_AFB^kHMGofZJ?xl zsw3IbILpl&Y3%<*Rt4ZMc`p`z2G_TVGwRjQWB!@$Z!4lnmUh&F1z&yYwA`rcTFXa8NErfY+YE{%&14h&|sk!Ke007wKH+$*ih8@~ZQQ z!%wEBaco?tFRK-7#m_=aGAPiJvR?e|&OAf|T zKFinXICFm*D|p;|1d=^fyleCxSHQw*sCSRbNvvcUp7fk;79Xih=f?p=HJuQaDkZ*r z@`h8|1BMI<)vWj5nU)+Xs?{{Rbuw!7LWVj~%~yu7wPCCNMI-s5{d|hQh4V`!k0Q8& zy?bq#KC#TE(o?{TZDO;`_0Y($Q@IVw(;IJ73&eu%GgRuJ0;WI)(@Kr#u9nh03e`eV zv)7TYb?#{XtN1f-tI{D`1jgj4@J^V=G?))FrEvbSYZ&_2y&+~`7j`Y7pJ|5u2ZT|lj4 zi`1FN^M({PaSKYI&HeeHa~acPxuEdo&`b{GZ?*xBWGHtZ4_FbzEj(Vpem6YjW$pAG0N&w+K5n|B0>U$oh9_ z`(TGr#hLbhHR5b*c>6%(SSCyL%TnA_K22YlRe}Z2ov&fdksW?hLExP*N1lB4C+0?f zA9-)y_$-U8H1Kp|EE;|v_h=H&Fy(`(T^Jvdz&fDW(VNYOb>9|vx7EKR?`Yd!y4W%PiNvg-veXr;-eIa}{IgIVKjYIN~slFP41lJ?G~ z_I@jynLo8Lm9ycRAymZBg;7E~yx+ieFY%FOOzv8<)c{ox;A?&IH{HOvz)C*@k2zk; zTIFB-{9(AwF6++8P*+GbmCx{B_6HjU|7phIIjzi=VFWysWCEL5rGAf{?V&eJjG75s z-YiPTxGlQ8ip_u^zS@E{p5e^lV-4BN@zrvFuQA zZ7%f#>EY9n6N{2Uf_y5qlwx9&s{njX0^QIJifE09zrPpcaOZByiYyfe z2-9>gSI&~Pp^Mq9SvMN*aD_pkCMX9-=Hd|At_@UNq)t-xBu;t=LGGANdN@0-*yaQM z5Tz%+hfv(~>-ssGe6{f=PM8AAx_fJ?a}}i(Muks9JXX}5Ae4kBqTW4KPe!yhEgF61 zmq+apKH~F>7{-$2k1ZF>vrL+fr(34A0a(>FO*W5Q zJC-)*dz`kuG4NIpoyT?27NK;%wcsNrf^oBCBbLU~s&rf*6yKxsTN^DXb5rA87Rd>g3`J6Gexvq2mdIhNRCuMFuN91&+2TfDdC)TM+0?| zdy-{Dq8rSjwublx#OWOTnHFuN{wdNUiy8u<1ik*`j;*}6{_ z7CdfGp7&hEl~#0^`M>7@kutw>U-jx2N!L1)>POalx)bHvKRtj%eD;nOU5o6Mo3V+L zJ<*s+%4cLd3Pgf`t95vVoYm{{9EYg9mAuh>Wm5nCx3&KO(tZwh6yYk5*ILLUaQyiJ z(w9$RVXOP@0s0euxfcj6eDeFKHyH5PuS~3q0LirxxoO-u3DbcYzMR-Mri5JfG;r)= zLl{Pj!oV)*xplBs$7xG>A?r*#vc!oTNjA$~Q}UbiD78Xci9&imUO=1`yL5B8JVI`{ zMv1n%YcXK+ZPM_v)gqSHLO8$KI%L5~@)QN*_s9qZLmu7Ca8)lT522bSWUNb)EHrm^ z$Q49?E~ji#4c~gMREV{HAJ5_>WU_JF*ZyrvMf==+lVUu~hF7K0zCo|zUe^5wrMpFB z-WUeAtbP5p!8EuSWsVTTE|Y0);z(BaCZE%`$4Yh`7BRQviuMG;5W(Zspu-@IPW zM%96?gs?LSg6u$#rb}StA|dT!v(B?+Buub+om2{u3=LNRLME z8SdCrk`~M##r8Yo7G^tKmBYEfv$B>pju$I8j-4enZ1j1E=GODQ! z+KUh&5K}lH_VAg!0ApM(KToB=a5l!=Sj^ZBdrCTT_Q4yhDv};8@&Tln|K~3s>#+9h zjiOFqlWTp3O>AXXoES?7ZbE#boH>mS(!+Kg97Dn|P)V#IvI!=4U!?;J(mW33;uLST3B1aXT3}RjWDSVYCXx8Rh3OE60zZ?I3gtx)@G~mT= z4pz-h0C+YQU!RO8-&W2^i*e172O?s`ml>qsyNbD_K+mbt&8=$c1Y}yE=|YplU7ie( z*9!L&Qb>5fE0w)EOqjXlJz=JxohL?=MTUs?kk5&nW)C-r)iWeW?}3ZI_*I>t$^f+5 z8c`huVVnBE?sG}u>MPTJa?@ILM0MNAWe>0Gnjd63~cHhTK-u53p<$%%ZX&$=%8r%BPa*bCV&^)RGqAx56Y zPK~85I`9O6L`Pb4jTWZxF#)2uI$4SbO0lu1^8pj@qmoSX-e!bS#aIaqIR+v4l!nkB zzY2&Mw|Y(IwLpD6b9Gu2dxe%}RUQ5JKtME5tR5Nz^RdibxQxt`$r=;4R$BDT-oq)kTL`&X%8yV)W7sueLJ^%S zK58$@X4`+bq2}H{N&6~){BZBL;#F2|YTHIY;7pQ+j{`AP_SqXNGcGfEC^q0$9J9{O zW%w*-f@Rz@FSClpq{olm7hZjkJoQg{X-HB6S=&ous~WZ2NrFWO5$9g%_69Am?anrY zaGAx+|1&up4_Qmli1LkiDH!zX+57?d?`%L*d+|fJYxTFLo~ON`GfQ5$7qt} zrx4q!t1MCjRRprI&1=W{ByjTRtk*|9WhMfOe5PtFMmrAiV-De_nmlGYvZ1Q7SzKlu z(KbW<#@6IZG!7L@%_(V8F|GZIp!!JWxt0MR)JVX-nrdnQs$0&XXb;E$Ix3QDxgocL zSy`cs5P;ML40RB{YZ6bX&qb#F3VbO+;*hFC!xojHUezYlCs^ zUuzOV_W?@(0ISQU^*)UzL!joB$J$4)&yQF_@yZgw+!EXoZ^*Fx{{WA@kSR*zssK0> z(3L#Bdi!pcblID(hb%T!n5N4<>PpN;U$G+Grlx({4I|*_jxPrW*O6#j4zpIXRiUW+ zh{O2NjYq{QBnP^@1_zM!{Qm%-S|PqIW`z0-dX5|qA5eVydfGh!+&k+v)Hn^Rurt|= zuYTlfFx@pv2k`j6(k4?Kn|-%mLxHHOt%8u$QfJW8y3X(sEx!z;x+GaykyMvfp#U1! z$Z*N~I@48aRX7^B9FI|q@H{KRhpG7Qjy=hh*nPFxxb1_Qs@VMxoxyFM-_NU1Bs&i| zx$@Q63=IZ$j#{+IA+F8irlk@^46#Qt>32fQcG`6e11o9sG&B|92^=fsT4$z&psXwh zYn&PydeeptJ!#NgnA-b?0Sy%S+!kvG4n~d~jxt=HwNC@x4pBvc~aKyIUO2|5rb(X$$%$G`5!)%6lI`gq4|n*z_6JcNFWv*a0Sim z2lN;Aq_7>wfa^xMHR<;kw(7D{%|{${FO!v_rNvQF$4x6%nj8fEjs_EpVSErbm z-4;^P1yZ0A%ARY;VL>Tn@I^}#TrCbo0j&)%X^QngvJ#;MbNT$~^Zx*6sMOeL7bOKQ zOixJyS4B;c`*o+5zM2)460ND}r>LfidZ=R5Qa6${W&jpYZY*vCLS7eEBvor#A4<}+ z9=Io`0uzeU{a@<*x?1f1zs2XXb8 zYDQ?3Lp@X!q2(It5icU6xZEa=1Tb2bx^fkDd>V~DCLO^zt)<WZpt)YS?k6$x0; ztVE}Um51${GSn786 zVu}h?$zkg;v^7j?=`s@Ir1DkMRIHIe)b(xROobnojnrsjsUk{0Qo4;tb!_rU;M^<9 zJ4mi7IMjXZ8SCV)Mk%#`8+4Ba{~f!1t-I*4EYTZXj}05>_D6vCRgRQK^k6 zNCO%4=|pydYmseja*SJpIT0zvNF~mdHNdN6(wus3#$sZx)Y4SFF^!CpLE=EqLkv$P zO+`Gj%^WjSqbO8?wD}3)htu1a7YztnAYyoNrmD~a0H_|V<<(%e5|%A4locdmx1tKPn<@BWRA{^V%* z_`l;H)9e4$()+Y`6nm!~w`u6)6Qt>T6XRE~;r27-0~`b5SZ`M>N6b5Y>Ls9fU1#Ps|=l06%D8WF-r8YTE*<;Sz=*)8?}_-fv0OJ zzzae_S`%D?0HaiO^2V{#QZ-nO>CH6IwCLhfBeO`9>>);i30}?PI)=NOoBMsDM%40z z)Ec1E_E$M0g>jCn9mI+jJ96|Zq>6*bh+GOO;#Ai@eLmHte$>k^@LkB?e+?}SN@X!} zwlb~>a@d5Lwvj6-YGeUmO35juF;2-ZKZYt1#?fvNH`Q5wlQRtrHG&oUNm4xM8EOF^ zm3mOd<8QfFvD^rpDxFFSGM8TEuaFfgNHlLO9+UH$K~&RHL}}?I)YR1eVOcFWC{RHp z@36Wje#x(kUTjEZ(0 zEm7tgl?SKou8q8lBU?uy1mVFeoKUSmAaNM{`o&n%R@288$bwTUaSNnijz27@ zry7zf0q9f0bzL??3%W8{E&bQ1+j1IH7LLThQ%{su#tP9*io;aU%ahF-bd$#+hd@`2 z;!v|+BPqTB*Trm3TnqDack5~=nYvE~cC>gp?9+m`o1x6KpkifdSWHE7>bmvxY^ zW}=i7DoWsk#Liu{tYCSdp3!e3lA4z380lrDRVm!2rmPJ`Xv!(k7r^H88%HOP%i-!O zF*#gKbv0cq$xBV=rKqEoXNExqbTh!Nj-ZWkvXS*t2_EEoh#>j|xQtmluD|1woo0+LAGm?J^$F&t!oSDke)H_d# zdU{Bt$qjKB3<%B&4N2+tf=#PxmF-dEiJlsS8jVM8l0GVu7maFZ>C%yVI!7#M{{W{U z6$Mv_H58~c{Hcs^>K@`DEt8if&tmAVZ<&@QCmZqEU zqyZue5mq6QmcVZ7X|}p!Ez+xyjEY%!M%2`pBB0ZW&3yWH3-`7Pu?jAr#0@5_0Rg*| z(*}fmC(Lw*tdg%MldP<%Em2#Ntf{D|Wr`JgimJ(8d07$#NXeRc2>}o-&42>K*`#7P zqKY&iMv4IR0AtV4dj8&!>ekIrdx_u&o^k%K%cWecC0!*%`RduVB`Z;5DB)@|Sq4WP zOFL65sr-= zns}aT*@yvjqOO|iRZt5hK~-hPi9$&f%{ujNEgq#Mp`xf*t0<_dC@EHzEhR%zK`m-3 ze-o>gI00E+ClWipkPK;Zsp#%mQH1iQrDhCk5I#sRgS~g?I*u1T}NoiW2~pmqkC3ssegKnB$qpnq5o@w=K=AZE~SWjRBQ` zpyNO*=a4Wd>%-3-Byf^H=_ZiWqSsYEL5@vnT5-)Y)R5BDi1E?VP(WU-B=RjM??W=9 zLc!|_)K>km)C)ujk6oKh{nCOmZW>o^+ETb9ju#87(D9}#PMrtE(QdIufW2!>gTpPB z3V4xHD?>nf=T8LH3tdMRJ`9Y82Msi9J2pBhDbl8Fp!@wyiDP@6Bq(hC^Zf=?$jxxH6^_Q=A+L&HnGVdk~EAt^<2|Qsca92%BLMfp~+QaMR2D3TV!$d z7}~G8qM@!+NmOGpcs}+DkF%;*iCQ{{e033#=x_-Kb3+x+f?T&_hgvf(SA}$usBr~a zsz(rdtT!^-iB>Rpd!6}wFLiM0Ws*Y2Qdo92Xj;L9Fa=aol0C~eH}X*`iH{O0IBMP+M5PqaR6$bGRYNO6(`kWT78=#;3u0)bdPD@0#V&$Il4B%hwIGZUT4aJS zM^AVckVbr0XrY=Q>KL+#P&EZ82yBC=RtA%hdTpYqr>08vn;#up)nnzQq?M|w=^q_a zB+xjnj8aP+b&dA%JWUMY;dPXfx|K+JSmbjFN%RG9NT6WDJ-M!&kZPnbBL;-kh^0u@ z;5W-E{cG0@_F#oas?Pc;u0M6N{{V~~W)-T9 zV4m7DuyBJa%+;YZp%m$wd}UP?URM>kVK8O!wa<^69Xp|tlCc(|I-rY1I>D+rAyUkm zk&TJCAp2bu(A-0Bb7bSda2PNj5_wlpukFbBbXjzl7PHN5eH|u@vXm#_e>^Fm_4Fs# zryaRbpW4`*hT+9an#1C^MiX#sI;^C0)biuAV!ED)?j5?bRpV-@CXehY;Feg?maYO) zMRYnF+Dq@Iy`DQMyeTJ!M4TZcG6g5YdIksL1OjTOAcK+8n?|~`wvzfGEOxU-(Cg9) zIqmTf7E-2)Ll6jc!KlIMzFD!*VPM76)IW-*sVnE6XlM(^kdB^uCv~ifo=mhfD;$IT zootepCO}ycg_K-9+uTTks=7d{d?beWY7n+QBg63Ek=DFxRI(Pa$Y{iW~IbBEo_vS(>mp4#k)CRh zrFFw<7}*T2qKb8_l1kcVh18%~&yeY{ zB>Q&O;bx8ymrEs63>Y1FuA!s=)#KZat9JoOXLhxCKx5cdyDD&}Q7k<>jedP7_Ksr< zwRc_*BVC2TZps=om-wC|amARg!em;qj-eo0x=p>9+S&R^sT!eK4K79}S)y43I^HHp zdvA4rVRDgNNerZoB?_YrED57J$b>Zwbw}`$C=YN~WhJGZ?UPFiNf=)6+L=ipkTj`{ z)U7}?ej`vc$5D>qT#Yn4cPUAOiZdoYij#8WX|m0j+VI6%tt&NU1dmf&Lkuq>O$$?0 zdKrvSi9(-uTLy}Mu7Y{1MlBkY@Z~heX%q&uAckDf6Q-l9%ebRL_K#%n zRCD>JdXgHH!ic(KU>QNxAY#CQ>9;>$e$z9%gDoRvpr^%J{{Whl{{RP94+_S#TFM(W z_-F?MpI$iO=hBaIRAlJ0F+p3kn`Py(5z>XkE;l8DugA*E4oa4;NUPjtqpFZhv%xI# zJBXx184-xUNof>wTQOVz0I9c6xLAkeH?a#|uRfU?5Clzf?R#uKy%x-$T_Uu??u`&z&wo(MMhKoSO008%u>BOt1!8iJZwY3&+tdQ}Wpd%Tmp zklbAE6EX10uwq;tQW+G6cB<6W6*cZ9fFq>dT83;bboi>)p%s*~$qcDMl*i?1Op&N1 zpsvPLRpYB-WN^zYGRTalgU7Yjv&VG`T*a@#VNh9F4u7J+GM`Yu5z;HGc`W5?d4h=) z_psPVHsGD{oy zL~QP?o(Qb$C3|lbjr7Z{t5WW%L<~(RbW|w(KoMGu^saMvxZ4}#wN=}pA~IHiRhSm0 zO={swd4fTp1dvg5xjM=umKbJ{A(crex^1nB9bt;Gz2aZ?-`AgOq_u`!GBED{08r?* zJBcF=7=FX@9Ddp!muGA44b_6qZ%k5P=B3(H`1!XTJ{q<Mg(98X8LHrHFV zt;X?fZ*3G%1X&UmU{yd-;*@u03Rw3Q?bb~I80j5N6y{2RDp*qpm{dseeY?FRAzD~h zQ#rdvN9~Z}_TYs!Gm+y_5itVQz!{-D4-e;$w>=V~N(_NzA#~)NV~H8^KAEpi^;rBm zW3lsZ+nif|` zvrKLzw3ayIlC_NYI=T%yn2jnC>RCdUCX{-bkQb~ln`m$2nptBDrcfyxM%fCWlI75f z8ifLtOdRyZ7B;RaXxP@$QGM8zY3d`%Q%RxL7~Cwzj-pv;QgcwM8c>KeHOL7eRZTQ3 zl7SP+r-&+jPXNFMr7Mb_6cr+=Qr1;yYceLP>0rupPdy}+twv1%^Efo)4j#5@{C+hi zp^m31Frub;^Ek>{GRYym z<#Ey?ww+<9Btq1Y$dS}T3)M$h!YNLuz@8;Q&KK~(SX+<}vshd@;y~;ef@oJG%Y~J6>HsxD6{4LBr9sFQB$w{Kt}mo% z$%(uWBOwA3gc?++1?y5ZHljrVG#F1}%<_}L$$d)Q&v10Yni|VZ1uANxxRZtd;NCecBBozXpjJ3^c!>?LvTTA#- z$z)!~W@6!xDa8W1sSHgGQOFz$ob|^gjj5oermD(fa(LsHm8!AQ`BE%>RXseSI@W@n z;*w7!UPV+o#*JKp7<;w6;&~lyuA`A-6ns^|BNZTlOd8`qDs%Q%74K9kNvOQq?%GqerPGl2~hGBvn&_USva0GA{<>+p8=OEK!-< zNL0911a_JXf}?@p3DmxQF(tjkXwvuzr5s1CNMWf#CWJ8uP~yU>-d%S~Lzt?DeB5wU zNll1aS!!!bLeSFw`qI!NB|&)1e|M%eO)P@o5CI;1&GU_Cfv|NI5RyY-{Gg{Z@ z*Om*(V{6e1v{7l)*dcSJYhNu`rbm|!pYnTKBaNzRY;@H5)y=2b$5jp*uAL2HQ&Qz} z^b~_9M-$T5VT_8CJVXYKwMNYo@{-?hwVoA{R%luiT_wRfYRIfgk{MhAKs*iz3_4YD zw%%LL_V7k0Nk)+24-ghd2p9saoEWVz4GBY(l47f5t%`ye47HP0EetRvgp$>?!U0aY zil~sz+Ib*f^X$yr!m%{W`eOr8C&_7A{>=1PuuAhJ5us^Ww17O=ngBj^$4~gZiJ!{k zGMI=UkNCbCD)}q$-*Haah{iIWuBRYZ9%e}-OnOmC_a#`E5Ed#_6YUiH#g)7k_e|&M zr79KZbaxOJx|HtK+x$lruS;Or@8!9k%tcF>^%hEvV@M?SlU!4{kB7`tr))03+|?1* zX`9(TI&4eDEmBSHf*e(Az1Q?e=spSDFk~@bGz-~ zql9jW9P+aw3J(ypV6&>DcQXYQ&<3hx0to3u8#Vlh^BuBQf-tPlkbz|FjI+~1DQ*tO zLC7R^BWCrs;osZG0kF0zrfYR>8KI=2pmvUd{{V`Z{{YGas~pjd zLf+}QZ+06#-F3TeY-6y9RhpF);I*LCGfWZNT=lK9XTIGxI5w@h1K-HSRcn*#7PMA0 ztpe9oJ8DSiZ|`r3A0j?3cBb6eeUGvzavRexg4#QyYp&R<+r7!M_f8LWndtVjVD}Et zpLJ}F;T}RtDJj0%mZqtarYEL#S>j|3L%ZzvR`bC3vBneVHK87O zJv}w1@NUGj+D#?IdSW7IUVtFbihM=V%b*P;5(46&sQ|afvNs)7RedBlY^?P(wUsmh zD6v@@x_X?QDm?XgifL%6Cy8<}QzOgZVvLbS#DQ;SQf`(oh>KlD5P(fxK?-mjh9H$D zg0#sak4SCyjl$`rjvF~zL0SbyV8umQK~Tb>d$N8JQb$UC)v-7J-jY04{OkPY)2Q1S z8oU;6OqSB09k?iSktIvThTczIB`swRV68bsrLAgd%{*n|N76|yu-!I`u{GAwyvK1I z$QeS-sy#Iw(9jht=28;!4XNo?%*Ew8|lb&8Y^p zR9u^5DwMRjDQNsuIVy>%=wDTVvw2kFm8$V2M*>AkkU`FRl~03~zACDm zwN6f+SH;&?V=6{GA=I{JFLhl%Ku z&(1|!NY9yVP8mrEuO#714u?y*gcE2@7#Gt;E!d670x@ z*0i8W6{r*fNbx(yK~>{UJCkfy?W|tasjScKy!JCafQND6v6x-kL7$&*!9=f07Mg^vEH-YuyA<&Lj`rqmjK;9VyqfvpVBiG8crwU;D^vhbjei4;O5+TGtD3+}@=6z_gl{AzT`6)3u2(fe3)_EF|o(idHe$uLH)P>AiTUYI|LO^e6 zTK>buF}?ggBx+cwbu0klNCu!SbN0~Ft;cV0;p2+lM`vIGP}HeCpioej$O9P|uS%T$ z&fJ(?m4wV^HugS?AC$~w=rEg|YO89h_haJ<#gnGTRl`?Tfvcn2Q^_1PD;S2NDJF|r zux(z^T5dL18@0qXm$EygN?tlBNUN160zlFZ<*8*L8WF>zJKeJG*J+MxyBMSWT`MN1 zGD-avqZo?EN-zOkNfob1U$c?pj%aCSof0;VaN%yA3bBQOWw=ul9ZC&}8mQR^tf3l>Sd#Y3b>gj zo}Q@JG_pu4mS*v&(8@s9WDj)kNi?&v{RjZ5Q01yBUtIwd;ij269-Uob@c;=^HF#hO zUftGPpW98Y63ZwZow=S>AeX&_{S>)J=1DbxP|MI=t^c^rs*GE@Z` zi9ZcP5JI0^k?4A7NBdTI(lW7EM`&tf@|j_cqyxwjo?r`xjDiZc7vt&eQ?H4knZ5&% zDMOA1p#8i@O>6L4MG@36!8NGQ8Ux0-$UQ$(U}w)%$kZn%lCErQ`3!W9v!qAO=>BhTIPb%#;#G4M|s_9 z)mjkM&T4fEcUK;isHwr@o@g<&pTOzbj}1Kk0DE0eLp?oRQOG*5T+UVnnn4^Z4WJES z7!9Fr&3%s!87Gw86C|K%QoyLwU+mKvH9EMHikuF&B%)X?ZAG*|#8{V6`ixqi8&uRS zJ4OQM6(^-;^~zFK;`UBCw?^aMxlOZy&27reer`;KcG=q$%bBd33s0~za^_~KslZgy zRsGqI?J=>_))pQK12Y!v;Z#ZFP_QJDhwTca;8$KgB0&wr(w*LnJGm&b5bA({DoGXj zj8JhD$EfKUB`Zp)RV5TG0@FijYWzyBu$X_!QY%Xtm&K@hmcU%xA8!W=-U>=ZYHLdT z@#KHi!>0)jM6$k_Vk^6JyGjZ;es`3IX___fPW%7 zok6;?_&nSh{kKy#4J(?H&1qi+@q$H$_byKWpNDgLV}dhZ`gt zvlN-S9m=%z^s`aP97Y`eGM=p@rdVZ;SjLtBus2jiK#LgCt3mPy)An^%0pD8yTaN*c zv(xhO$5SaEW##ENRz|L~Be$zD_>643X9*2C%`RUk*$OH=jWu3nX&@N-dU^+%sUAp% zqJkY-ti-8?;cX?15vt1@$ihY^PL-i5Lh3q+YApfvJUUp-9O7ACO)eBrQzY;?01g%M z9-TVg$=So*6*ohSE&UGngA<9-G&E>k~;OQixkn#G?FQe)rN{IV2bH#cC`gm z=8c*jy4<_w8p^H5Nw;R)QEkkI;e&AE;N8`jO#bakzqUvwmovHPuoN{J8n+|VV*6@& z6b(?b5`CxJw#j0G#jn!b-lRr0FssKEDo6jB>E`8-T%N){{*lHM@9RH5@TztxD-2jQ;=-H3Nr5F-aQKOCF{i1$5Bx z2BZ8QeR`vEZe7V+hBa9nl}&at?I>_-iH15_s(DaH1tFn}6%{MVl#$Xk^9$6>!EVgJ zd(?J2eS~)!Aw`Yq&RouD>bitZLr;3%yQm26}H4FKuQpLnr~;^ZyDyrLo~g-Z&WREqH9 zoadtRHpc9XrXqao8$))_wej)CJk^Ph$W&rzV@QGto@bB65}45Xi24J~h5d;jxv+`E zVtBr!npXjhPY>JD32veitS>Da$w~d4AmX5sqA@DGXobvSrELJQ zw&vZZhKgKvVbYFzh?Yuf+L2Y4po*w#6?N2<5E?09r$9k8@8xl)R7!aEhTCwsnpa6+ zqYBV~LYiQJSk|ZJDdS zCdS>8Z@fOk+})e<=1z{@T@#GYW%`vjFIZ={M$)6Gq^HNz(QVuwdjUTE%G6{x1vD?= zq^jG?gkcP^JxUn@utq*w+70IWwp;9W@>p&WgK%D114~BK7fK@NH8l_7h6aZn9Z$OM zv3&;N71EjQ+e%P;I)x2O>sDG% z`->B`u$jEpW{)4Zc68WyI`Xe|Nt)R7l~QDCWQQIoYUv;=MJ&}190YhTp@LMjc3%@h z;;cmuC|H3~q-5uXYtai^ZW2W$*|i!qR}8CK`Hmb3p-?_x5IO+&B_?(_(+icVrmUZ3 zRMg=yG5-Jouf}9+FnKEK?A1Pe%~6HLl`>BPPfbrcr;3H+rfC?nCT1XY52aupFb{x9u2ZXA(rRAP{QV*@jhHX*yv-CJ~h6>q^88yb|kyn zi*4m(Y6(RpMjLT&8tu(Z9x|%FTxM2C@KhUXYEL<7VUD4)RTQzt^z?p1F%QT6Zaa<4 zx2Dx>jHncaI+ifK>sJd?BLGEfqNa@e0+SVkMI3goBQ~0mUM*AI+o)hr74xArP6QjL z+!R!=h@;8VR%fyJoRt)r?iWHfR^QjH{!8zih- z*om#>k!G3+Rw*7bJ5@;yN-Jt3Ng{%pfm6WLbj&wVERZG2yphJk49%xSNh?9zqa^m^ z8jvb!({}02$CcbUs=QmgI3>tc!?*Eilg#94u={Z`6;)VlEe&RJXU=0-S)TyNhraETw?n&ojCM^nf#tRnGfRd^U z0Ca$HP!FgaI)2+YjhJd{n{e(to;e1^%jPlfiu+cnuiVK?GUN8=o~4osN?PiI1kxQ% z;tYzVnMgr%b-75Cx3NYGb`p(R2*d)v6{viqNKZ0*%wJ$BoouB=Q2eSXBH%Is=͟xmno2}+@5?kPAZzn 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

X@zcyzJRl0E7FD;#mH(5=A#!p{Gm&|2S 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�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 literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/tabbedpane/TabbedPaneDemo.java b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/tabbedpane/TabbedPaneDemo.java new file mode 100644 index 00000000000..9809d6babbe --- /dev/null +++ b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/tabbedpane/TabbedPaneDemo.java @@ -0,0 +1,257 @@ +/* + * Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.sun.swingset3.demos.tabbedpane; + +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.Random; +import javax.swing.*; +import javax.swing.event.ChangeEvent; + +import com.sun.swingset3.DemoProperties; +import com.sun.swingset3.demos.ResourceManager; + +/** + * JTabbedPane Demo + * + * @version 1.11 11/17/05 + * @author Jeff Dinkins + */ +@DemoProperties( + value = "JTabbedPane Demo", + category = "Containers", + description = "Demonstrates JTabbedPane, a container which allows tabbed navigation of components", + sourceFiles = { + "com/sun/swingset3/demos/tabbedpane/TabbedPaneDemo.java", + "com/sun/swingset3/demos/ResourceManager.java", + "com/sun/swingset3/demos/tabbedpane/resources/TabbedPaneDemo.properties", + "com/sun/swingset3/demos/tabbedpane/resources/images/blake.gif", + "com/sun/swingset3/demos/tabbedpane/resources/images/brooke.gif", + "com/sun/swingset3/demos/tabbedpane/resources/images/camille.jpg", + "com/sun/swingset3/demos/tabbedpane/resources/images/david.gif", + "com/sun/swingset3/demos/tabbedpane/resources/images/ewan.gif", + "com/sun/swingset3/demos/tabbedpane/resources/images/ewan.jpg", + "com/sun/swingset3/demos/tabbedpane/resources/images/miranda.jpg", + "com/sun/swingset3/demos/tabbedpane/resources/images/matthew.gif", + "com/sun/swingset3/demos/tabbedpane/resources/images/stephen.gif", + "com/sun/swingset3/demos/tabbedpane/resources/images/TabbedPaneDemo.gif" + } +) +public class TabbedPaneDemo extends JPanel implements ActionListener { + + private static final ResourceManager resourceManager = new ResourceManager(TabbedPaneDemo.class); + public static final String BOUNCE = resourceManager.getString("TabbedPaneDemo.bounce"); + public static final String EWAN = resourceManager.getString("TabbedPaneDemo.ewan"); + public static final String MIRANDA = resourceManager.getString("TabbedPaneDemo.miranda"); + public static final String CAMILLE = resourceManager.getString("TabbedPaneDemo.camille"); + public static final String TAB_PLACEMENT = resourceManager.getString("TabbedPaneDemo.label"); + public static final String RIGHT = resourceManager.getString("TabbedPaneDemo.right"); + public static final String BOTTOM = resourceManager.getString("TabbedPaneDemo.bottom"); + public static final String LEFT = resourceManager.getString("TabbedPaneDemo.left"); + public static final String TOP = resourceManager.getString("TabbedPaneDemo.top"); + public static final String DEMO_TITLE = TabbedPaneDemo.class.getAnnotation(DemoProperties.class).value(); + + private final HeadSpin spin; + + private final JTabbedPane tabbedpane; + + private final ButtonGroup group; + + private final JRadioButton top; + private final JRadioButton bottom; + private final JRadioButton left; + private final JRadioButton right; + + /** + * main method allows us to run as a standalone demo. + * + * @param args + */ + public static void main(String[] args) { + JFrame frame = new JFrame(DEMO_TITLE); + + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.getContentPane().add(new TabbedPaneDemo()); + frame.setPreferredSize(new Dimension(800, 600)); + frame.pack(); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + } + + /** + * TabbedPaneDemo Constructor + */ + public TabbedPaneDemo() { + setLayout(new BorderLayout()); + + // create tab position controls + JPanel tabControls = new JPanel(); + tabControls.add(new JLabel(TAB_PLACEMENT)); + top = (JRadioButton) tabControls.add(new JRadioButton(TOP)); + left = (JRadioButton) tabControls.add(new JRadioButton(LEFT)); + bottom = (JRadioButton) tabControls.add(new JRadioButton(BOTTOM)); + right = (JRadioButton) tabControls.add(new JRadioButton(RIGHT)); + add(tabControls, BorderLayout.NORTH); + + group = new ButtonGroup(); + group.add(top); + group.add(bottom); + group.add(left); + group.add(right); + + top.setSelected(true); + + top.addActionListener(this); + bottom.addActionListener(this); + left.addActionListener(this); + right.addActionListener(this); + + // create tab + tabbedpane = new JTabbedPane(); + add(tabbedpane, BorderLayout.CENTER); + + String name = CAMILLE; + JLabel pix = new JLabel(resourceManager.createImageIcon("camille.jpg", name)); + tabbedpane.add(name, pix); + + name = MIRANDA; + pix = new JLabel(resourceManager.createImageIcon("miranda.jpg", name)); + pix.setToolTipText(resourceManager.getString("TabbedPaneDemo.miranda.tooltip")); + tabbedpane.add(name, pix); + + name = EWAN; + pix = new JLabel(resourceManager.createImageIcon("ewan.jpg", name)); + tabbedpane.add(name, pix); + + name = BOUNCE; + spin = new HeadSpin(); + tabbedpane.add(name, spin); + + tabbedpane.getModel().addChangeListener((ChangeEvent e) -> { + SingleSelectionModel model = (SingleSelectionModel) e.getSource(); + if (model.getSelectedIndex() == tabbedpane.getTabCount() - 1) { + spin.go(); + } + }); + } + + @Override + public void actionPerformed(ActionEvent e) { + if (e.getSource() == top) { + tabbedpane.setTabPlacement(JTabbedPane.TOP); + } else if (e.getSource() == left) { + tabbedpane.setTabPlacement(JTabbedPane.LEFT); + } else if (e.getSource() == bottom) { + tabbedpane.setTabPlacement(JTabbedPane.BOTTOM); + } else if (e.getSource() == right) { + tabbedpane.setTabPlacement(JTabbedPane.RIGHT); + } + } + + private class HeadSpin extends JComponent implements ActionListener { + + private javax.swing.Timer animator; + + private final ImageIcon[] icon = new ImageIcon[6]; + + private final static int numImages = 6; + + private final double[] x = new double[numImages]; + private final double[] y = new double[numImages]; + + private final int[] xh = new int[numImages]; + private final int[] yh = new int[numImages]; + + private final double[] scale = new double[numImages]; + + private final Random rand = new Random(); + + public HeadSpin() { + setBackground(Color.black); + icon[0] = resourceManager.createImageIcon("ewan.gif", resourceManager.getString("TabbedPaneDemo.ewan")); + icon[1] = resourceManager.createImageIcon("stephen.gif", resourceManager.getString("TabbedPaneDemo.stephen")); + icon[2] = resourceManager.createImageIcon("david.gif", resourceManager.getString("TabbedPaneDemo.david")); + icon[3] = resourceManager.createImageIcon("matthew.gif", resourceManager.getString("TabbedPaneDemo.matthew")); + icon[4] = resourceManager.createImageIcon("blake.gif", resourceManager.getString("TabbedPaneDemo.blake")); + icon[5] = resourceManager.createImageIcon("brooke.gif", resourceManager.getString("TabbedPaneDemo.brooke")); + + /* + for(int i = 0; i < 6; i++) { + x[i] = (double) rand.nextInt(500); + y[i] = (double) rand.nextInt(500); + } + */ + } + + public void go() { + animator = new javax.swing.Timer(22 + 22 + 22, this); + animator.start(); + } + + @Override + public void paint(Graphics g) { + g.setColor(getBackground()); + g.fillRect(0, 0, getWidth(), getHeight()); + + for (int i = 0; i < numImages; i++) { + if (x[i] > 3 * i) { + nudge(i); + squish(g, icon[i], xh[i], yh[i], scale[i]); + } else { + x[i] += .05; + y[i] += .05; + } + } + } + + public void nudge(int i) { + x[i] += (double) rand.nextInt(1000) / 8756; + y[i] += (double) rand.nextInt(1000) / 5432; + int tmpScale = (int) (Math.abs(Math.sin(x[i])) * 10); + scale[i] = (double) tmpScale / 10; + int nudgeX = (int) (((double) getWidth() / 2) * .8); + int nudgeY = (int) (((double) getHeight() / 2) * .60); + xh[i] = (int) (Math.sin(x[i]) * nudgeX) + nudgeX; + yh[i] = (int) (Math.sin(y[i]) * nudgeY) + nudgeY; + } + + public void squish(Graphics g, ImageIcon icon, int x, int y, double scale) { + if (isVisible()) { + g.drawImage(icon.getImage(), x, y, + (int) (icon.getIconWidth() * scale), + (int) (icon.getIconHeight() * scale), + this); + } + } + + @Override + public void actionPerformed(ActionEvent e) { + if (isVisible()) { + repaint(); + } else { + animator.stop(); + } + } + } +} diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/tabbedpane/resources/TabbedPaneDemo.properties b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/tabbedpane/resources/TabbedPaneDemo.properties new file mode 100644 index 00000000000..f32d96330e0 --- /dev/null +++ b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/tabbedpane/resources/TabbedPaneDemo.properties @@ -0,0 +1,22 @@ +### TabbedPane Demo ### + +TabbedPaneDemo.accessible_description=This demo shows an example of using the JTabbedPane component. +TabbedPaneDemo.tooltip=JTabbedPane demo +TabbedPaneDemo.name=TabbedPane Demo + +TabbedPaneDemo.bounce=
Bouncing Babies!
+TabbedPaneDemo.stephen=Stephen +TabbedPaneDemo.david=David +TabbedPaneDemo.matthew=Matthew +TabbedPaneDemo.ewan=Ewan +TabbedPaneDemo.blake=Blake +TabbedPaneDemo.brooke=Brooke +TabbedPaneDemo.camille=Camille +TabbedPaneDemo.miranda=Miranda +TabbedPaneDemo.miranda.tooltip=Check out the Java code on her onesie! + +TabbedPaneDemo.label=Tab Placement: +TabbedPaneDemo.top=Top +TabbedPaneDemo.bottom=Bottom +TabbedPaneDemo.left=Left +TabbedPaneDemo.right=Right \ No newline at end of file diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/tabbedpane/resources/images/TabbedPaneDemo.gif b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/tabbedpane/resources/images/TabbedPaneDemo.gif new file mode 100644 index 0000000000000000000000000000000000000000..9be6463cd04e5caa14dae239c8ef9f0e88cf4603 GIT binary patch literal 226 zcmZ?wbhEHbRA5kG*v!NL1pk46;lD@zhEum+r&YAQe)a6tt5+ZiY(ViR3nK#q6N3&& z5@ZGgOMk!#&((V^UZ4Ffm!RS2nmO;NDND|6zM#Z2C3C~H7rwXFkw38DgW1WCea-U= zDiyeT-0vMYkYcqk@a~pfd)PJwX_>!Z{eI-Jn(L9g-M6!+*Z7qm?D|^sPcQQ2|FXh{ z<{JOzmZtWq*1E3h&c2eKiTxE*CQX>GKXVr2uyK=! HAcHjk>N;Lo literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/tabbedpane/resources/images/blake.gif b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/tabbedpane/resources/images/blake.gif new file mode 100644 index 0000000000000000000000000000000000000000..679639edf776667a92024e2be14f534a2d62f3d3 GIT binary patch literal 3110 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 A+W-In literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/tabbedpane/resources/images/brooke.gif b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/tabbedpane/resources/images/brooke.gif new file mode 100644 index 0000000000000000000000000000000000000000..815ce772553939fe5636812baee0040e2b400e01 GIT binary patch literal 4463 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 B9Kiqp literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/tabbedpane/resources/images/camille.jpg b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/tabbedpane/resources/images/camille.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d00eabd779b15ec6801f5d9e2f95cfe7de4e09b9 GIT binary patch literal 51435 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 XkG<{NxS<9zaNIIL+{%s35dZ)?GQhYf literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/tabbedpane/resources/images/ewan.gif b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/tabbedpane/resources/images/ewan.gif new file mode 100644 index 0000000000000000000000000000000000000000..82c8e58f5243351fa75cf5eeece4a7c3e5d036d2 GIT binary patch literal 7676 zcmV&+$jU?`4aXP#)Pu53rXaNN#xZL@K%=XdY_r=4&p0~(JuqH;+>I-eY- zbP9r6p}PZh`?`94S*{rNEw8fLvASt)Yhnj&dQE4$?;cWo{^8gEA9!XkaR73Let?RC zS0j6dB8fSYibIS}laY{)dX-z3K%9_#bCQReo}ZvKdTD;7j)fp{l&M6kGqZcHv$4Lm zRJDP)DY|`|nZAd-z+q&>CVji5OVgXO%|h5C&`}=+yNHC>A+D#RV}-EV$hF-Vv*3@y zsJ+wHb@azCvfEYf6ynJ{CyF1Ud9;vifyGSWIBMJgmK%7gA-`?+)KS{lsh7ibg#Rig zdKaRANqmV2anZz)Y&uWFhnSuzx0rZY~_Ic~-u9tRpamV<$-1{;e~hKoa7zAoc}z_sYYFS z`MI`}H2T#yoPFWl2VZGVpa+$J0s{C~Y8M6wU}oZ}GZ1LswfEpvAGSxD73lq^;egn^ zI9-PvQj}nb4dw_Oh~&K&p?erYhJ=5UW!PJI(`9I+Up*puontCS8C-|c_*hqkyrDKD zm0NbXEsF3FnbA`o-Rq@12NWnHX-^W04WYSRI9I z8UoG$h^9B$e|;rdqoQ+e`Q?mCnklAhlv(N9rIaF>;)iSAAP`?OGMeb0CLx-rqdEOI zX_6lvsbZY@ZR)A2XW5x+S!WTN1X+zv*XRIzY1yi($SRs@m(0S5=Kp%oI*TBOKR!xq zb+RrCPN=!ygs8EtLW(O?!y=okvZY1}W|1`3*yp;_3OcE)sLH6`96jigDy!t0rK$mu zA`5V`xXx*Avy%3Sp{MM!C+ot~nf0!=Z~o_N3#Sq5Xs{ZmH-G^ElWQ)b1CPwH$><_X z=c|-5neD5^>e?=@__Fd;u?racU$KZb0J6vegDkYk<^H=Wq$)oubJ9yL{3@}e*2u4_ zq+VRiQ{U#SrHfR5{PU{j3OzKyB@66z*#-ADY_lvU&E(Uk0=+1yY1k}a);afun%*Fj z>~qooiY++VgAac2!5)LHvbtT;Tko;pB7N+sR!4KQ)<^RSb^p#FH()Mbg*QMz;b)7T zKm!S&&9BFaXU;6g&BFP3tC6$5@3-m=b4DM05j*tlxcmKg&}TOwy77ezemVgJoZh$B zL=)Y3!Hkou;p`qqee9yi){MeL=`A31?}@i=0P;o?F0|;(6X3JhsISjH1M&BbcI!r; zYd!w_FKWBeYExY6@W&VLSq^Jj;~dy9S3Khdu5)5z9s`(XJfB(6g3BX-04Dgp(wR(b zAERFM4#%i)rR+`U3Lfi-);r2I<$JThnfO3Ps?CwiYv>!_$B<_{0x;l*9CRP*Dwu%w zF<^rf6dd%_2f`7iZ)CPZnbNpczN49pXA-O21u~{J8vim1iy^F_{p8m}3I4Es$2(s8 zd?)}UA`gU0Or8_Z7D6dPF@Pl;A;9tmHQX6+1G)fRv1mvt@nsEu?>nCb4X{QZ9`THg zY~&ByC;;Z2u6~9STIx1Aw2_f+j^mPA6suY`$ewmImyw)hX#eWTy(ZG8&;M0z&RqL|iTLiHi>0b8{|U?D0@18z)$LAu zTTul#z`Si8Z(1kWQ_2bzxyF^}CEX{|dzv;TWyzaX-#(aS`SCK*RuV)pG9rR5pRNNQD^deCfLHLJ~Q>htRO)!5GVs~g>5AfMTxY(^agi#X?BV^pELUO*~! zF#xL;^u%VgbWU@d<5NGo(D$x)mje(0F}JnV*lx9sBYf@jTDY650L(b}1rEGdXnTsFJV`o1^4f&Xr3ev`b*=e2go`+aBzV>`%=CNH=j?$daK)12k(Xci0m zTIsag-SR8v{BpX`8GqT{8?P~{k1l4Wvt0phM|GktoAe#uI>^0sEJdrn;ZY}d>j5bV zH(AU`32GdYKL6el)n6*yxdNP@2ibxPnY)g$D?5`sQWl6>EDq zXpsnB7gb9cX8%=TIEIGeXB7m3Kevdw^<@|~fuLw|O9zLS_=J=vel3V}3+Rbg2yI$c zYTiRZU@v6frm z^?89PNgd^Gg;-s)U}S1!hR#-F_K1hNbxV+VcbFG>`^Js_xOiD zdB-Sc>s>fc}g)hk+Bz9khqa2 zxR-RejV?HnxwwtGIB=9$lqflc0Y`kk)mFy`hULbJ^oN*=DRZ${kBet!E_aPs`Il}e zd0VNA-*=lW7y#)-n~&F5}8k11RZMT?T?hDz6c zm0sR@(Pp?ZeLdh}+IHECn*^?B79d_&2Lx+!`1 z+5dvw$(P^hV82Cc2U%u~W}amvk%sv~@t~NTMQW=SjjyR&4sd3pNSprll^=PDJqeEE zX`jNEa9GKQw}xRwc0o+mUxx{k2M~RW`JT@ipIX>*3OSicCxh1Jp_!R`{kVb?I-4Z- zW232Z0EkX$C3bvd7~rOnZDSW|JZLGDwZZUq2yI-JtlQR zg@2use@U92lEF=<35{weX!baKCa9BCs(y3|n3r0k9%^m6XsN+xpTKo|#Hn6my8m9H zc5E@lWNEsn=(c*W_?mDDr=?0^vS+E|cx{Uon&@|(|5$?qIF`KvXGUZQ$;5=dd-24#bUs7$G- z)^wg2_@u!Vt2O3r>NTT0HGwIKsribMH_EHrD4b)7d7nt4MewxmiI-W*kC>={;cAq|2Y?flSLK969rZjKvp$H`U#7=9=OeKY zDs(uxv$cw`y;Y^WikEX}oB1h{Ci`@9TClv-S*F-NUKBW!%apAZ2$I1*IytI1sks?z zV`BQP87jKFser#5w74pyJ9$}ImrKqwFjZ!v736$P(Fcq)SLoD#x*L@ln}dUfqrfYa zlUKUl2(XstbPfolo#jh>)q^c4jF6p1EXW8TY%ul7+%&A{NB@5Qn125Ine@i5)Fz!t zG%RwPr1)Y8YaGc`{J&brg=d9uzz3Gur%n!(bJCO{T0=|d2Yh5X^ACpb$f0VKmltj8SFxFUtzOK+5>`piqd@}TZm7`!3P1n{fLH|3d4H^# zTa1w@SELMuZX4-i3z-0M+h;>uxrqtMm#oeIdyf@`W)o(s-)ORLi>w4-%$=+?j%;)S z(q~CmQM;L)n+c|s#Y?CN%K2wY5_m?bgEQTfsMhSrY`m?OXUYa^S-KpuB_~er1WXAq zL1K)~;P8&>mVXSuf`E#N^1RU;ZDAY)(x5zcpQpzregC3c*T}QXt$O%xm3OooVPC%K`13m^x;de0<#IskX?k zQM^;g2~!o_Lu_nxzO|8 zjNxju7m9T0WX`vHt=Q_N9x1%ixopH7ZQgsA+!@=hMn6_1AO;{03G`r|{4o#r!{US4 zXk8A%J;Oj-lxEKpOOB$Ruub24m~H z;sKVx-!K3W7UP_X*xDw*Y;?Tm7qX>nm)e=tG_0~g+OG)4XJ5Th5>!#=ylYGTMhF!N z1;EBc`LCSG;A_^vxZ8#lzQsEFlg0g|hWu7;+}AsGfgQcJezxW-^)n`n!r*6n82*u` zZHg=FW9R4S-3yYJnWrU-$^~lUbSL4(C;zF|c<4h&Mh1`)P>!d;3zDIF+#)@yb(-T` zndAEjvihoWWE#IVsCjlQ+vhff&I>XxPylc_emkm{z$NJ`3&oUp%o-ZL!b^F4ZG%Df zu4{F+9=^+b&B|{^X{wsWvtR%}P3xJ7ZS><^U+cwyoY5?No4ZKb-5gX)OQZ0vsiKKl z#6;i+YE|GagrWP|;RneS`>dWGprb6#7#zGzr{Vm&V=9@wMas1G39$8!$r-rj2uj52 zxuS5s?)-*=6H4%-UBQli?3cNT4X?v{Xw>mXr4`@nrTAoGB$KCJ8YO|k9{=$J?Q?s_ zky3ng1qkq^%%`DP!<}wUncR^Q-v8H>ghs$_yUZho)FgNUkn{b{VGfKFZGS zl^SaFo@#=Sbkn-N+}S#y9Nz887D*bvj3;dMyou(=h|t3Ti1Lf?%SyOS{Qg*1-$e=6vL`EvZ_VGJzwTo;5U zal{J`@Z;>@x+a^;ocEJo%0ODBp2^IM%iiLdr^Va1P0#i`#74|h^Tf9CqGOp;?86*} zj0L~L*{&(ISbiLp9*B!zAtj>L$eG?XtFMSxiVqz`J<`jAQs2AJ?On|XDDiiLO4aK7PS zsrG)iULcaG%@Ti}gp5dN2O9ElIhx=hP$G~ab7E*X$hwlm$|~TaTNC`aC_BqYgQ4?t zgDbVskrAp>WmQ_na{b#YgVvbCuXg8JDFsYoxcKEm zlO7xgKh=qIL)}3m3IB98AdoHwq6q~e$(uwknIj)X zA8^L4rTB!d5~v6WB;eBksa|@WL>8VH(dr?fK-!CktP-R_yJ9?(W0+<1)&MUua0T4` z*t|47xxUA-TmO)#0@lNbZFdI{4_I^<4=o!mQ^p($3|P-D&=?p^KBbsC;D7;mDbTn9 zEZn7n>#&n5w^m5`EkMf}>P@ybR?H)_2|g2wm(N1Oi#Z8Fdd|Z2l31XC3b(+>0zNLs zAW$46%_#lwsRIQOVK7KO2&j?E=1eQ*IUs}N4^7#CV5|hH+N5n9Z^jGb$yx@nDI?H` zqo5rEBJ}f5KmjcvKQjzlX{vnaWNa2j30jh&^So(A6(Z1k5znE9gfFj7<@;0$9ruGR zJMHWf2mg>&O<{DQA|GXDEjx3mAg&$v@KM)Y{iHBdETbtknGq8mWyK`%7&ldQmMIC= zVQrc6ruT$0YC(9er56iI0MOQnH2OOf*;v)I4PXQ9@affi((UGz7w&v(%peWuvtD|O zxo}(Yq+)3{aKHPvV>QdO1viYn{Vw5W$#@ao9U=>g5^-Q!pmZgH2SIuD6@gonYGD|%QV6p_ zgrgFX1_l^&fdQCyx?YNRG*gX?HAa>Ut@j2^-<-CQl`V`VFl!8w11D*$&kR6~ak%52 z82@T4RYRCqmzd-`^UPn^yg0U-JbKR;!W4;U?Oh$DQ^C5}1B~1gytMysv#y98b2Fv$l9H zDHK)ouKVq32&4J7Df+#w*BbuowH~PpHxR!w_~D5c-}t7q#fq^0;~gfgk9|1NngMYq zKe7N$QJQF<5*P+kA8#$$kird4$#1>S}n8S zwT@Rlu}xz&u}h1i$}$${nJ|GUq?ygY0GXgTjwvyW+YD7=sFpNpAv`Q0?rfr>RsRs| zY;8eAT#|M~Bs#4Uq4FCHmQfAws1O!BG}`BYNX7dN;U^ivQ_{fK#UvgtgEbr@8Pm5N zZ%lD&%o2e!(1#)IztvM9(YSPc1t*%VC6zXN=JG)tdXEd zMK``>nU&UJGIbKmOJRzq7cz9F;wwhdZWWmg!mfi$x@68C)26lK~9CbQe83BwDMvRll|MP>O-%zhEZ^ zd!|I8!_#X>YZ|khR83VjVADb%H3q_*Lz;+9>`_07!F6KiuOyliIK|19XvTDZnMG?c zgh@BL{)1=Y+8Hy~g^*~9W|L<{ZE95;Dp9$PgU3uIJqb}n;T6@WxtE=3w%A5Dp>3~O z2^;4HG$}mXBGg@P{Dtv^DGc8T0028*4$V^l literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/tabbedpane/resources/images/ewan.jpg b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/tabbedpane/resources/images/ewan.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d19210eea287880f85a7cf1856f9927c6f905057 GIT binary patch literal 10687 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 Uwd)|KL|nv5yu?i01`z-NJEdQ)g#Z8m literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/tabbedpane/resources/images/miranda.jpg b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/tabbedpane/resources/images/miranda.jpg new file mode 100644 index 0000000000000000000000000000000000000000..83df2778530ba8378131c9b5ca2c0a6cc69690fa GIT binary patch literal 114192 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 eku!(`k@Au;;D0W$|4>B literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/tabbedpane/resources/images/stephen.gif b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/tabbedpane/resources/images/stephen.gif new file mode 100644 index 0000000000000000000000000000000000000000..470c3bcb2e06f359c0f1d4757f7856770f45230a GIT binary patch literal 4294 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 literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/textfield/JHistoryTextField.java b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/textfield/JHistoryTextField.java new file mode 100644 index 00000000000..a6474bdefa2 --- /dev/null +++ b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/textfield/JHistoryTextField.java @@ -0,0 +1,265 @@ +/* + * Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.sun.swingset3.demos.textfield; + +import java.awt.*; +import java.awt.event.*; +import java.util.*; +import java.util.List; +import javax.swing.*; +import javax.swing.border.LineBorder; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; + +/** + * JHistoryTextField + * + * @author Pavel Porvatov + */ +public class JHistoryTextField extends JTextField { + + private static final int MAX_VISIBLE_ROWS = 8; + + private final List history = new ArrayList(); + + private final JPopupMenu popup = new JPopupMenu() { + @Override + public Dimension getPreferredSize() { + Dimension dimension = super.getPreferredSize(); + + dimension.width = JHistoryTextField.this.getWidth(); + + return dimension; + } + }; + + private final JList list = new JList<>(new DefaultListModel<>()); + + private String userText; + + private boolean notificationDenied; + + public JHistoryTextField() { + JScrollPane scrollPane = new JScrollPane(list, + ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, + ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); + scrollPane.setHorizontalScrollBar(null); + scrollPane.setBorder(null); + + list.setFocusable(false); + + popup.add(scrollPane); + popup.setFocusable(false); + popup.setBorder(new LineBorder(Color.BLACK, 1)); + + getDocument().addDocumentListener(new DocumentListener() { + @Override + public void insertUpdate(DocumentEvent e) { + onTextChanged(); + } + + @Override + public void removeUpdate(DocumentEvent e) { + onTextChanged(); + } + + @Override + public void changedUpdate(DocumentEvent e) { + onTextChanged(); + } + }); + + list.addMouseMotionListener(new MouseAdapter() { + @Override + public void mouseMoved(MouseEvent e) { + int index = list.locationToIndex(e.getPoint()); + + if (index >= 0 && list.getSelectedIndex() != index) { + list.setSelectedIndex(index); + } + } + }); + + list.addMouseListener(new MouseAdapter() { + @Override + public void mouseReleased(MouseEvent e) { + if (SwingUtilities.isLeftMouseButton(e)) { + setTextWithoutNotification(list.getSelectedValue()); + + popup.setVisible(false); + } + } + }); + + addFocusListener(new FocusAdapter() { + @Override + public void focusLost(FocusEvent e) { + popup.setVisible(false); + } + }); + + addKeyListener(new KeyAdapter() { + @Override + public void keyPressed(KeyEvent e) { + if (popup.isShowing()) { + switch (e.getKeyCode()) { + case KeyEvent.VK_UP: { + changeListSelectedIndex(-1); + + break; + } + + case KeyEvent.VK_PAGE_UP: { + changeListSelectedIndex(-list.getVisibleRowCount()); + + break; + } + + case KeyEvent.VK_DOWN: { + changeListSelectedIndex(1); + + break; + } + + case KeyEvent.VK_PAGE_DOWN: { + changeListSelectedIndex(list.getVisibleRowCount()); + + break; + } + + case KeyEvent.VK_ESCAPE: { + popup.setVisible(false); + + setTextWithoutNotification(userText); + + break; + } + + case KeyEvent.VK_ENTER: + case KeyEvent.VK_LEFT: + case KeyEvent.VK_RIGHT: { + popup.setVisible(false); + + break; + } + } + } else if (e.getKeyCode() == KeyEvent.VK_DOWN + || e.getKeyCode() == KeyEvent.VK_UP + || e.getKeyCode() == KeyEvent.VK_PAGE_UP + || e.getKeyCode() == KeyEvent.VK_PAGE_DOWN) { + userText = getText(); + + showFilteredHistory(); + } + } + }); + } + + private void changeListSelectedIndex(int delta) { + int size = list.getModel().getSize(); + int index = list.getSelectedIndex(); + + int newIndex; + + if (index < 0) { + newIndex = delta > 0 ? 0 : size - 1; + } else { + newIndex = index + delta; + } + + if (newIndex >= size || newIndex < 0) { + newIndex = newIndex < 0 ? 0 : size - 1; + + if (index == newIndex) { + newIndex = -1; + } + } + + if (newIndex < 0) { + list.getSelectionModel().clearSelection(); + list.ensureIndexIsVisible(0); + + setTextWithoutNotification(userText); + } else { + list.setSelectedIndex(newIndex); + list.ensureIndexIsVisible(newIndex); + + setTextWithoutNotification(list.getSelectedValue()); + } + } + + private void setTextWithoutNotification(String text) { + notificationDenied = true; + + try { + setText(text); + } finally { + notificationDenied = false; + } + } + + private void onTextChanged() { + if (!notificationDenied) { + userText = getText(); + + showFilteredHistory(); + } + } + + private void showFilteredHistory() { + list.getSelectionModel().clearSelection(); + + DefaultListModel model = (DefaultListModel) list.getModel(); + + model.clear(); + + for (String s : history) { + if (s.contains(userText)) { + model.addElement(s); + } + } + + int size = model.size(); + + if (size == 0) { + popup.setVisible(false); + } else { + list.setVisibleRowCount(size < MAX_VISIBLE_ROWS ? size : MAX_VISIBLE_ROWS); + + popup.pack(); + + if (!popup.isShowing()) { + popup.show(JHistoryTextField.this, 0, getHeight()); + } + } + } + + public List getHistory() { + return Collections.unmodifiableList(history); + } + + public void setHistory(List history) { + this.history.clear(); + this.history.addAll(history); + } +} diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/textfield/TextFieldDemo.java b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/textfield/TextFieldDemo.java new file mode 100644 index 00000000000..be7c9a7e7d0 --- /dev/null +++ b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/textfield/TextFieldDemo.java @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.sun.swingset3.demos.textfield; + +import java.awt.*; +import java.awt.event.ActionEvent; +import java.util.*; +import javax.swing.*; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; + +import com.sun.swingset3.demos.JGridPanel; +import com.sun.swingset3.demos.ResourceManager; +import com.sun.swingset3.DemoProperties; + +/** + * JTextField Demo + * + * @author Pavel Porvatov + */ +@DemoProperties( + value = "TextField Demo", + category = "Text", + description = "Demonstrates the JTextField, a control which allows to input text", + sourceFiles = { + "com/sun/swingset3/demos/textfield/TextFieldDemo.java", + "com/sun/swingset3/demos/textfield/JHistoryTextField.java", + "com/sun/swingset3/demos/JGridPanel.java", + "com/sun/swingset3/demos/ResourceManager.java", + "com/sun/swingset3/demos/textfield/resources/TextFieldDemo.properties", + "com/sun/swingset3/demos/textfield/resources/images/TextFieldDemo.gif" + } +) +public class TextFieldDemo extends JPanel { + + private static final ResourceManager resourceManager = new ResourceManager(TextFieldDemo.class); + public static final String DEMO_TITLE = TextFieldDemo.class.getAnnotation(DemoProperties.class).value(); + + private final JLabel lbHistoryTextField = new JLabel(resourceManager.getString("TextFieldDemo.historytextfield.text")); + + private final JHistoryTextField tfHistory = new JHistoryTextField(); + + private final JLabel lbDow = new JLabel(resourceManager.getString("TextFieldDemo.dow.text")); + + private final JFormattedTextField tfDow = new JFormattedTextField(); + + private final JButton btnGo = new JButton(GO); + public static final String GO = resourceManager.getString("TextFieldDemo.go.text"); + + private final JLabel lbDowResult = new JLabel(); + + private final JLabel lbPassword = new JLabel(resourceManager.getString("TextFieldDemo.password.text")); + + private final JPasswordField tfPassword1 = new JPasswordField(20); + + private final JPasswordField tfPassword2 = new JPasswordField(20); + + private final DocumentListener passwordListener = new DocumentListener() { + + @Override + public void insertUpdate(DocumentEvent e) { + highlightPasswords(); + } + + @Override + public void removeUpdate(DocumentEvent e) { + highlightPasswords(); + } + + @Override + public void changedUpdate(DocumentEvent e) { + highlightPasswords(); + } + + private void highlightPasswords() { + Color color; + + if (tfPassword1.getPassword().length > 0 + && Arrays.equals(tfPassword1.getPassword(), tfPassword2.getPassword())) { + color = Color.GREEN; + } else { + color = UIManager.getColor("TextField.background"); + } + + tfPassword1.setBackground(color); + tfPassword2.setBackground(color); + } + }; + + /** + * main method allows us to run as a standalone demo. + * + * @param args + */ + public static void main(String[] args) { + JFrame frame = new JFrame(DEMO_TITLE); + + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.getContentPane().add(new TextFieldDemo()); + frame.setPreferredSize(new Dimension(800, 600)); + frame.pack(); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + } + + public TextFieldDemo() { + setLayout(new BorderLayout()); + + initUI(); + + tfDow.setValue(new Date()); + + btnGo.addActionListener((ActionEvent e) -> { + Calendar calendar = Calendar.getInstance(Locale.ENGLISH); + + calendar.setTime((Date) tfDow.getValue()); + + lbDowResult.setText(calendar.getDisplayName(Calendar.DAY_OF_WEEK, Calendar.LONG, Locale.ENGLISH)); + }); + + tfPassword1.getDocument().addDocumentListener(passwordListener); + + tfPassword2.getDocument().addDocumentListener(passwordListener); + } + + private void initUI() { + tfHistory.setHistory(Arrays.asList(resourceManager.getString("TextFieldDemo.history.words").split("\\,"))); + + JGridPanel pnDow = new JGridPanel(3, 2); + + pnDow.cell(tfDow). + cell(btnGo). + cell(lbDowResult); + + JGridPanel pnPassword = new JGridPanel(3, 2); + + pnPassword.cell(tfPassword1). + cell(tfPassword2). + cell(); + + JGridPanel pnContent = new JGridPanel(1, 0, 6); + + pnContent.setBorderEqual(10); + + pnContent.cell(lbHistoryTextField). + cell(tfHistory). + cell(lbDow, new Insets(20, 0, 0, 0)). + cell(pnDow). + cell(lbPassword, new Insets(20, 0, 0, 0)). + cell(pnPassword). + cell(); + + add(pnContent); + } +} diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/textfield/resources/TextFieldDemo.properties b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/textfield/resources/TextFieldDemo.properties new file mode 100644 index 00000000000..5bb7b0aba7c --- /dev/null +++ b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/textfield/resources/TextFieldDemo.properties @@ -0,0 +1,7 @@ +### TextField Demo ### + +TextFieldDemo.historytextfield.text = This is an example of a text field with a history. To try it in action, type "cat". +TextFieldDemo.history.words = armageddon,cars,cartoon,casanova,casino,castle,castlevania,cat,category,caterpillar,click,dog,magpie,hare,rabbit +TextFieldDemo.dow.text = Enter a date and find out what day of the week it is. +TextFieldDemo.go.text = Go +TextFieldDemo.password.text = The text fields below become highlighted when they get identical values. diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/textfield/resources/images/TextFieldDemo.gif b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/textfield/resources/images/TextFieldDemo.gif new file mode 100644 index 0000000000000000000000000000000000000000..a4053d7e84a71d6672ad232580c0ffb4893220f9 GIT binary patch literal 569 zcma)(O>fd*0D#dM^b@<2yv&n4wxWUv1 zW2`GG4I0M81fP)Af#b}JcKkuQ8fSscaZ7yG^vYN zD7mSGFBECAT_>IN&M0>e8UshQa;7adn;U#Ct4Ea{m`smq!4$eX!rcHew<;N2(3 zm$A3`#T5B9^Zr+%lwPV7e^eG5{-1LTmA$W~Gus^3w+~#)9}7I0oV1^_AGcVa$@UkY X{o`l14i25qPCx$;+%dR$qv_~BA=!$N literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/DirectionPanel.java b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/DirectionPanel.java new file mode 100644 index 00000000000..81d96b1019a --- /dev/null +++ b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/DirectionPanel.java @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.sun.swingset3.demos.togglebutton; + +import java.awt.event.ActionListener; +import javax.swing.Box; +import javax.swing.BoxLayout; +import javax.swing.ButtonGroup; +import javax.swing.Icon; +import javax.swing.ImageIcon; +import javax.swing.JPanel; +import javax.swing.JRadioButton; +import javax.swing.border.Border; + +/** + * @version 1.8 11/17/05 + * @author Jeff Dinkins + * @author Chester Rose + * @author Brian Beck + */ +public class DirectionPanel extends JPanel { + + private final ButtonGroup group; + + public DirectionPanel(boolean enable, String selection, ActionListener l) { + setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); + setAlignmentY(TOP_ALIGNMENT); + setAlignmentX(LEFT_ALIGNMENT); + + Box firstThree = Box.createHorizontalBox(); + Box secondThree = Box.createHorizontalBox(); + Box thirdThree = Box.createHorizontalBox(); + + if (!enable) { + selection = "None"; + } + + group = new ButtonGroup(); + DirectionButton b; + b = (DirectionButton) firstThree.add(new DirectionButton(tl_dot, tldn_dot, "NW", "Sets the orientation to the North-West", l, group, selection.equals("NW"))); + b.setEnabled(enable); + b = (DirectionButton) firstThree.add(new DirectionButton(tm_dot, tmdn_dot, "N", "Sets the orientation to the North", l, group, selection.equals("N"))); + b.setEnabled(enable); + b = (DirectionButton) firstThree.add(new DirectionButton(tr_dot, trdn_dot, "NE", "Sets the orientation to the North-East", l, group, selection.equals("NE"))); + b.setEnabled(enable); + b = (DirectionButton) secondThree.add(new DirectionButton(ml_dot, mldn_dot, "W", "Sets the orientation to the West", l, group, selection.equals("W"))); + b.setEnabled(enable); + b = (DirectionButton) secondThree.add(new DirectionButton(c_dot, cdn_dot, "C", "Sets the orientation to the Center", l, group, selection.equals("C"))); + b.setEnabled(enable); + b = (DirectionButton) secondThree.add(new DirectionButton(mr_dot, mrdn_dot, "E", "Sets the orientation to the East", l, group, selection.equals("E"))); + b.setEnabled(enable); + b = (DirectionButton) thirdThree.add(new DirectionButton(bl_dot, bldn_dot, "SW", "Sets the orientation to the South-West", l, group, selection.equals("SW"))); + b.setEnabled(enable); + b = (DirectionButton) thirdThree.add(new DirectionButton(bm_dot, bmdn_dot, "S", "Sets the orientation to the South", l, group, selection.equals("S"))); + b.setEnabled(enable); + b = (DirectionButton) thirdThree.add(new DirectionButton(br_dot, brdn_dot, "SE", "Sets the orientation to the South-East", l, group, selection.equals("SE"))); + b.setEnabled(enable); + + add(firstThree); + add(secondThree); + add(thirdThree); + } + + // Chester's way cool layout buttons + private final ImageIcon bl_dot = loadImageIcon("bl.gif", "bottom left layout button"); + private final ImageIcon bldn_dot = loadImageIcon("bldn.gif", "selected bottom left layout button"); + private final ImageIcon bm_dot = loadImageIcon("bm.gif", "bottom middle layout button"); + private final ImageIcon bmdn_dot = loadImageIcon("bmdn.gif", "selected bottom middle layout button"); + private final ImageIcon br_dot = loadImageIcon("br.gif", "bottom right layout button"); + private final ImageIcon brdn_dot = loadImageIcon("brdn.gif", "selected bottom right layout button"); + private final ImageIcon c_dot = loadImageIcon("c.gif", "center layout button"); + private final ImageIcon cdn_dot = loadImageIcon("cdn.gif", "selected center layout button"); + private final ImageIcon ml_dot = loadImageIcon("ml.gif", "middle left layout button"); + private final ImageIcon mldn_dot = loadImageIcon("mldn.gif", "selected middle left layout button"); + private final ImageIcon mr_dot = loadImageIcon("mr.gif", "middle right layout button"); + private final ImageIcon mrdn_dot = loadImageIcon("mrdn.gif", "selected middle right layout button"); + private final ImageIcon tl_dot = loadImageIcon("tl.gif", "top left layout button"); + private final ImageIcon tldn_dot = loadImageIcon("tldn.gif", "selected top left layout button"); + private final ImageIcon tm_dot = loadImageIcon("tm.gif", "top middle layout button"); + private final ImageIcon tmdn_dot = loadImageIcon("tmdn.gif", "selected top middle layout button"); + private final ImageIcon tr_dot = loadImageIcon("tr.gif", "top right layout button"); + private final ImageIcon trdn_dot = loadImageIcon("trdn.gif", "selected top right layout button"); + + private ImageIcon loadImageIcon(String filename, String description) { + String path = "resources/images/" + filename; + return new ImageIcon(getClass().getResource(path), description); + } + + private static class DirectionButton extends JRadioButton { + + /** + * A layout direction button + */ + public DirectionButton(Icon icon, Icon downIcon, String direction, + String description, ActionListener l, + ButtonGroup group, boolean selected) { + super(); + this.addActionListener(l); + setFocusPainted(false); + setHorizontalTextPosition(CENTER); + group.add(this); + setIcon(icon); + setSelectedIcon(downIcon); + setActionCommand(direction); + getAccessibleContext().setAccessibleName(direction); + getAccessibleContext().setAccessibleDescription(description); + setSelected(selected); + } + + @Override + @Deprecated + public boolean isFocusTraversable() { + return false; + } + + @Override + public void setBorder(Border b) { + } + } +} diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/LayoutControlPanel.java b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/LayoutControlPanel.java new file mode 100644 index 00000000000..c8c79bac29e --- /dev/null +++ b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/LayoutControlPanel.java @@ -0,0 +1,281 @@ +/* + * Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.sun.swingset3.demos.togglebutton; + +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import javax.swing.*; + +import com.sun.swingset3.demos.ResourceManager; + +/* + * The LayoutControlPanel contains controls for setting an + * AbstractButton's horizontal and vertical text position and + * horizontal and vertical alignment. + */ +public final class LayoutControlPanel extends JPanel implements SwingConstants { + + private static final Dimension VGAP20 = new Dimension(1, 20); + private static final ResourceManager resourceManager = ToggleButtonDemo.resourceManager; + public static final String CONTENT_ALIGNMENT = resourceManager.getString("LayoutControlPanel.contentalignment_label"); + public static final String TEXT_POSITION = resourceManager.getString("LayoutControlPanel.textposition_label"); + private final boolean absolutePositions; + private ToggleButtonDemo demo = null; + + // private ComponentOrientChanger componentOrientChanger = null; + LayoutControlPanel(ToggleButtonDemo demo) { + this.demo = demo; + + // this.componentOrientationChanger = componentOrientationChanger; + setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); + setAlignmentX(LEFT_ALIGNMENT); + setAlignmentY(TOP_ALIGNMENT); + + JLabel l; + + // If SwingSet has a ComponentOrientationChanger, then include control + // for choosing between absolute and relative positioning. This will + // only happen when we're running on JDK 1.2 or above. + // + // if(componentOrientationChanger != null ) { + // l = new JLabel("Positioning:"); + // add(l); + // + // ButtonGroup group = new ButtonGroup(); + // PositioningListener positioningListener = new PositioningListener(); + // JRadioButton absolutePos = new JRadioButton("Absolute"); + // absolutePos.setMnemonic('a'); + // absolutePos.setToolTipText("Text/Content positioning is independant of line direction"); + // group.add(absolutePos); + // absolutePos.addItemListener(positioningListener); + // add(absolutePos); + // + // JRadioButton relativePos = new JRadioButton("Relative"); + // relativePos.setMnemonic('r'); + // relativePos.setToolTipText("Text/Content positioning depends on line direction."); + // group.add(relativePos); + // relativePos.addItemListener(positioningListener); + // add(relativePos); + // + // add(Box.createRigidArea(demo.VGAP20)); + // + // absolutePositions = false; + // relativePos.setSelected(true); + // + // componentOrientationChanger.addActionListener( new OrientationChangeListener() ); + //} else { + absolutePositions = true; + //} + + DirectionPanel textPosition = new DirectionPanel(true, "E", new TextPositionListener()); + DirectionPanel labelAlignment = new DirectionPanel(true, "C", new LabelAlignmentListener()); + + // Make sure the controls' text position and label alignment match + // the initial value of the associated direction panel. + for (JComponent control : demo.getCurrentControls()) { + setPosition(control, RIGHT, CENTER); + setAlignment(control, CENTER, CENTER); + } + + l = new JLabel(TEXT_POSITION); + add(l); + add(textPosition); + + add(Box.createRigidArea(VGAP20)); + + l = new JLabel(CONTENT_ALIGNMENT); + add(l); + add(labelAlignment); + + add(Box.createGlue()); + } + + // Text Position Listener + private class TextPositionListener implements ActionListener { + + @Override + public void actionPerformed(ActionEvent e) { + JRadioButton rb = (JRadioButton) e.getSource(); + if (!rb.isSelected()) { + return; + } + String cmd = rb.getActionCommand(); + int hPos, vPos; + switch (cmd) { + case "NW": + hPos = LEFT; + vPos = TOP; + break; + case "N": + hPos = CENTER; + vPos = TOP; + break; + case "NE": + hPos = RIGHT; + vPos = TOP; + break; + case "W": + hPos = LEFT; + vPos = CENTER; + break; + case "C": + hPos = CENTER; + vPos = CENTER; + break; + case "E": + hPos = RIGHT; + vPos = CENTER; + break; + case "SW": + hPos = LEFT; + vPos = BOTTOM; + break; + case "S": + hPos = CENTER; + vPos = BOTTOM; + break; + /*if(cmd.equals("SE"))*/ + default: + hPos = RIGHT; + vPos = BOTTOM; + break; + } + for (JComponent control : demo.getCurrentControls()) { + setPosition(control, hPos, vPos); + } + demo.invalidate(); + demo.validate(); + demo.repaint(); + } + } + + // Label Alignment Listener + private class LabelAlignmentListener implements ActionListener { + + @Override + public void actionPerformed(ActionEvent e) { + JRadioButton rb = (JRadioButton) e.getSource(); + if (!rb.isSelected()) { + return; + } + String cmd = rb.getActionCommand(); + int hPos, vPos; + switch (cmd) { + case "NW": + hPos = LEFT; + vPos = TOP; + break; + case "N": + hPos = CENTER; + vPos = TOP; + break; + case "NE": + hPos = RIGHT; + vPos = TOP; + break; + case "W": + hPos = LEFT; + vPos = CENTER; + break; + case "C": + hPos = CENTER; + vPos = CENTER; + break; + case "E": + hPos = RIGHT; + vPos = CENTER; + break; + case "SW": + hPos = LEFT; + vPos = BOTTOM; + break; + case "S": + hPos = CENTER; + vPos = BOTTOM; + break; + /*if(cmd.equals("SE"))*/ + default: + hPos = RIGHT; + vPos = BOTTOM; + break; + } + for (JComponent control : demo.getCurrentControls()) { + setAlignment(control, hPos, vPos); + control.invalidate(); + } + demo.invalidate(); + demo.validate(); + demo.repaint(); + } + } + + // Position + void setPosition(Component c, int hPos, int vPos) { + boolean ltr = c.getComponentOrientation().isLeftToRight(); + if (absolutePositions) { + if (hPos == LEADING) { + hPos = ltr ? LEFT : RIGHT; + } else if (hPos == TRAILING) { + hPos = ltr ? RIGHT : LEFT; + } + } else if (hPos == LEFT) { + hPos = ltr ? LEADING : TRAILING; + } else if (hPos == RIGHT) { + hPos = ltr ? TRAILING : LEADING; + } + if (c instanceof AbstractButton) { + AbstractButton x = (AbstractButton) c; + x.setHorizontalTextPosition(hPos); + x.setVerticalTextPosition(vPos); + } else if (c instanceof JLabel) { + JLabel x = (JLabel) c; + x.setHorizontalTextPosition(hPos); + x.setVerticalTextPosition(vPos); + } + } + + void setAlignment(Component c, int hPos, int vPos) { + boolean ltr = c.getComponentOrientation().isLeftToRight(); + if (absolutePositions) { + if (hPos == LEADING) { + hPos = ltr ? LEFT : RIGHT; + } else if (hPos == TRAILING) { + hPos = ltr ? RIGHT : LEFT; + } + } else if (hPos == LEFT) { + hPos = ltr ? LEADING : TRAILING; + } else if (hPos == RIGHT) { + hPos = ltr ? TRAILING : LEADING; + } + if (c instanceof AbstractButton) { + AbstractButton x = (AbstractButton) c; + x.setHorizontalAlignment(hPos); + x.setVerticalAlignment(vPos); + } else if (c instanceof JLabel) { + JLabel x = (JLabel) c; + x.setHorizontalAlignment(hPos); + x.setVerticalAlignment(vPos); + } + } +} diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/ToggleButtonDemo.java b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/ToggleButtonDemo.java new file mode 100644 index 00000000000..5adf5f7a62b --- /dev/null +++ b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/ToggleButtonDemo.java @@ -0,0 +1,563 @@ +/* + * Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.sun.swingset3.demos.togglebutton; + +import java.awt.*; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.util.*; +import java.util.List; +import javax.swing.*; +import javax.swing.border.*; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +import com.sun.swingset3.DemoProperties; +import com.sun.swingset3.demos.ResourceManager; + +/** + * JButton, JRadioButton, JToggleButton, JCheckBox Demos + * + * @version 1.15 11/17/05 + * @author Jeff Dinkins + */ +@DemoProperties( + value = "ToggleButtons Demo", + category = "Controls", + description = "Demonstrates JCheckBox & JRadioButton", + sourceFiles = { + "com/sun/swingset3/demos/togglebutton/ToggleButtonDemo.java", + "com/sun/swingset3/demos/togglebutton/DirectionPanel.java", + "com/sun/swingset3/demos/togglebutton/LayoutControlPanel.java", + "com/sun/swingset3/demos/ResourceManager.java", + "com/sun/swingset3/demos/togglebutton/resources/ToggleButtonDemo.properties", + "com/sun/swingset3/demos/togglebutton/resources/images/b1.gif", + "com/sun/swingset3/demos/togglebutton/resources/images/b1d.gif", + "com/sun/swingset3/demos/togglebutton/resources/images/b1p.gif", + "com/sun/swingset3/demos/togglebutton/resources/images/b1r.gif", + "com/sun/swingset3/demos/togglebutton/resources/images/b2.gif", + "com/sun/swingset3/demos/togglebutton/resources/images/b2d.gif", + "com/sun/swingset3/demos/togglebutton/resources/images/b2p.gif", + "com/sun/swingset3/demos/togglebutton/resources/images/b2r.gif", + "com/sun/swingset3/demos/togglebutton/resources/images/b3.gif", + "com/sun/swingset3/demos/togglebutton/resources/images/b3d.gif", + "com/sun/swingset3/demos/togglebutton/resources/images/b3p.gif", + "com/sun/swingset3/demos/togglebutton/resources/images/b3r.gif", + "com/sun/swingset3/demos/togglebutton/resources/images/bl.gif", + "com/sun/swingset3/demos/togglebutton/resources/images/bldn.gif", + "com/sun/swingset3/demos/togglebutton/resources/images/bm.gif", + "com/sun/swingset3/demos/togglebutton/resources/images/bmdn.gif", + "com/sun/swingset3/demos/togglebutton/resources/images/br.gif", + "com/sun/swingset3/demos/togglebutton/resources/images/brdn.gif", + "com/sun/swingset3/demos/togglebutton/resources/images/c.gif", + "com/sun/swingset3/demos/togglebutton/resources/images/cb.gif", + "com/sun/swingset3/demos/togglebutton/resources/images/cbr.gif", + "com/sun/swingset3/demos/togglebutton/resources/images/cbrs.gif", + "com/sun/swingset3/demos/togglebutton/resources/images/cbs.gif", + "com/sun/swingset3/demos/togglebutton/resources/images/cdn.gif", + "com/sun/swingset3/demos/togglebutton/resources/images/ml.gif", + "com/sun/swingset3/demos/togglebutton/resources/images/mldn.gif", + "com/sun/swingset3/demos/togglebutton/resources/images/mr.gif", + "com/sun/swingset3/demos/togglebutton/resources/images/mrdn.gif", + "com/sun/swingset3/demos/togglebutton/resources/images/rb.gif", + "com/sun/swingset3/demos/togglebutton/resources/images/rbp.gif", + "com/sun/swingset3/demos/togglebutton/resources/images/rbr.gif", + "com/sun/swingset3/demos/togglebutton/resources/images/rbrs.gif", + "com/sun/swingset3/demos/togglebutton/resources/images/rbs.gif", + "com/sun/swingset3/demos/togglebutton/resources/images/tl.gif", + "com/sun/swingset3/demos/togglebutton/resources/images/tldn.gif", + "com/sun/swingset3/demos/togglebutton/resources/images/tm.gif", + "com/sun/swingset3/demos/togglebutton/resources/images/tmdn.gif", + "com/sun/swingset3/demos/togglebutton/resources/images/ToggleButtonDemo.gif", + "com/sun/swingset3/demos/togglebutton/resources/images/tr.gif", + "com/sun/swingset3/demos/togglebutton/resources/images/trdn.gif" + } +) +public class ToggleButtonDemo extends JPanel implements ChangeListener { + + private static final Dimension HGAP10 = new Dimension(10, 1); + private static final Dimension HGAP20 = new Dimension(20, 1); + private static final Dimension VGAP20 = new Dimension(1, 20); + private static final Dimension VGAP30 = new Dimension(1, 30); + + static final ResourceManager resourceManager = new ResourceManager(ToggleButtonDemo.class); + public static final String RADIO3 = resourceManager.getString("ToggleButtonDemo.radio3"); + public static final String RADIO2 = resourceManager.getString("ToggleButtonDemo.radio2"); + public static final String RADIO1 = resourceManager.getString("ToggleButtonDemo.radio1"); + public static final String IMAGE_RADIO_BUTTONS = resourceManager.getString("ToggleButtonDemo.imageradiobuttons"); + public static final String TEXT_RADIO_BUTTONS = resourceManager.getString("ToggleButtonDemo.textradiobuttons"); + public static final String CHECK3 = resourceManager.getString("ToggleButtonDemo.check3"); + public static final String CHECK2 = resourceManager.getString("ToggleButtonDemo.check2"); + public static final String CHECK1 = resourceManager.getString("ToggleButtonDemo.check1"); + public static final String CHECK_BOXES = resourceManager.getString("ToggleButtonDemo.checkboxes"); + public static final String IMAGE_CHECKBOXES = resourceManager.getString("ToggleButtonDemo.imagecheckboxes"); + public static final String TEXT_CHECKBOXES = resourceManager.getString("ToggleButtonDemo.textcheckboxes"); + public static final String CONTENT_FILLED = resourceManager.getString("ToggleButtonDemo.contentfilled"); + public static final String ENABLED = resourceManager.getString("ToggleButtonDemo.enabled"); + public static final String PAINT_FOCUS = resourceManager.getString("ToggleButtonDemo.paintfocus"); + public static final String PAINT_BORDER = resourceManager.getString("ToggleButtonDemo.paintborder"); + public static final String DISPLAY_OPTIONS = resourceManager.getString("ToggleButtonDemo.controlpanel_label"); + public static final String DEFAULT = resourceManager.getString("ToggleButtonDemo.default"); + public static final String PAD_AMOUNT = resourceManager.getString("ToggleButtonDemo.padamount_label"); + + private final JTabbedPane tab; + + private final JPanel checkboxPanel = new JPanel(); + private final JPanel radioButtonPanel = new JPanel(); + + private final List buttons = new ArrayList<>(); + private final List checkboxes = new ArrayList<>(); + private final List radiobuttons = new ArrayList<>(); + private final List togglebuttons = new ArrayList<>(); + + private List currentControls = buttons; + + private final EmptyBorder border5 = new EmptyBorder(5, 5, 5, 5); + + private ItemListener buttonDisplayListener = null; + private ItemListener buttonPadListener = null; + + private final Insets insets0 = new Insets(0, 0, 0, 0); + private final Insets insets10 = new Insets(10, 10, 10, 10); + + private final Border loweredBorder = new CompoundBorder( + new SoftBevelBorder(SoftBevelBorder.LOWERED), new EmptyBorder(5, 5, 5, 5)); + + /** + * main method allows us to run as a standalone demo. + * + * @param args + */ + public static void main(String[] args) { + JFrame frame = new JFrame(ToggleButtonDemo.class.getAnnotation(DemoProperties.class).value()); + + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.getContentPane().add(new ToggleButtonDemo()); + frame.setPreferredSize(new Dimension(800, 600)); + frame.pack(); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + } + + /** + * ButtonDemo Constructor + */ + public ToggleButtonDemo() { + setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); + + tab = new JTabbedPane(); + tab.getModel().addChangeListener(this); + + add(tab); + + //addButtons(); + addRadioButtons(); + addCheckBoxes(); + //addToggleButtons(); + currentControls = checkboxes; + } + + private void addRadioButtons() { + ButtonGroup group = new ButtonGroup(); + + tab.addTab(resourceManager.getString("ToggleButtonDemo.radiobuttons"), radioButtonPanel); + radioButtonPanel.setLayout(new BoxLayout(radioButtonPanel, BoxLayout.X_AXIS)); + radioButtonPanel.setBorder(border5); + + JPanel p1 = createVerticalPanel(true); + p1.setAlignmentY(TOP_ALIGNMENT); + radioButtonPanel.add(p1); + + // Text Radio Buttons + JPanel p2 = createHorizontalPanel(false); + p1.add(p2); + p2.setBorder(new CompoundBorder( + new TitledBorder( + null, TEXT_RADIO_BUTTONS, + TitledBorder.LEFT, TitledBorder.TOP), border5) + ); + + JRadioButton radio = (JRadioButton) p2.add(new JRadioButton(RADIO1)); + group.add(radio); + radiobuttons.add(radio); + p2.add(Box.createRigidArea(HGAP10)); + + radio = (JRadioButton) p2.add(new JRadioButton(RADIO2)); + group.add(radio); + radiobuttons.add(radio); + p2.add(Box.createRigidArea(HGAP10)); + + radio = (JRadioButton) p2.add(new JRadioButton(RADIO3)); + group.add(radio); + radiobuttons.add(radio); + + // Image Radio Buttons + group = new ButtonGroup(); + p1.add(Box.createRigidArea(VGAP30)); + JPanel p3 = createHorizontalPanel(false); + p1.add(p3); + p3.setLayout(new BoxLayout(p3, BoxLayout.X_AXIS)); + p3.setBorder(new TitledBorder(null, IMAGE_RADIO_BUTTONS, + TitledBorder.LEFT, TitledBorder.TOP)); + + // image radio button 1 + String description = resourceManager.getString("ToggleButtonDemo.customradio"); + String text = RADIO1; + radio = new JRadioButton(text, resourceManager.createImageIcon("rb.gif", description)); + radio.setPressedIcon(resourceManager.createImageIcon("rbp.gif", description)); + radio.setRolloverIcon(resourceManager.createImageIcon("rbr.gif", description)); + radio.setRolloverSelectedIcon(resourceManager.createImageIcon("rbrs.gif", description)); + radio.setSelectedIcon(resourceManager.createImageIcon("rbs.gif", description)); + radio.setMargin(new Insets(0, 0, 0, 0)); + group.add(radio); + p3.add(radio); + radiobuttons.add(radio); + p3.add(Box.createRigidArea(HGAP20)); + + // image radio button 2 + text = RADIO2; + radio = new JRadioButton(text, resourceManager.createImageIcon("rb.gif", description)); + radio.setPressedIcon(resourceManager.createImageIcon("rbp.gif", description)); + radio.setRolloverIcon(resourceManager.createImageIcon("rbr.gif", description)); + radio.setRolloverSelectedIcon(resourceManager.createImageIcon("rbrs.gif", description)); + radio.setSelectedIcon(resourceManager.createImageIcon("rbs.gif", description)); + radio.setMargin(new Insets(0, 0, 0, 0)); + group.add(radio); + p3.add(radio); + radiobuttons.add(radio); + p3.add(Box.createRigidArea(HGAP20)); + + // image radio button 3 + text = RADIO3; + radio = new JRadioButton(text, resourceManager.createImageIcon("rb.gif", description)); + radio.setPressedIcon(resourceManager.createImageIcon("rbp.gif", description)); + radio.setRolloverIcon(resourceManager.createImageIcon("rbr.gif", description)); + radio.setRolloverSelectedIcon(resourceManager.createImageIcon("rbrs.gif", description)); + radio.setSelectedIcon(resourceManager.createImageIcon("rbs.gif", description)); + radio.setMargin(new Insets(0, 0, 0, 0)); + group.add(radio); + radiobuttons.add(radio); + p3.add(radio); + + // verticaly glue fills out the rest of the box + p1.add(Box.createVerticalGlue()); + + radioButtonPanel.add(Box.createHorizontalGlue()); + currentControls = radiobuttons; + radioButtonPanel.add(createControls()); + } + + private void addCheckBoxes() { + tab.addTab(CHECK_BOXES, checkboxPanel); + checkboxPanel.setLayout(new BoxLayout(checkboxPanel, BoxLayout.X_AXIS)); + checkboxPanel.setBorder(border5); + + JPanel p1 = createVerticalPanel(true); + p1.setAlignmentY(TOP_ALIGNMENT); + checkboxPanel.add(p1); + + // Text Radio Buttons + JPanel p2 = createHorizontalPanel(false); + p1.add(p2); + p2.setBorder(new CompoundBorder( + new TitledBorder( + null, TEXT_CHECKBOXES, + TitledBorder.LEFT, TitledBorder.TOP), border5) + ); + + JCheckBox checkBox1 = new JCheckBox(CHECK1); + checkboxes.add(checkBox1); + p2.add(checkBox1); + p2.add(Box.createRigidArea(HGAP10)); + + JCheckBox checkBox2 = new JCheckBox(CHECK2); + checkboxes.add(checkBox2); + p2.add(checkBox2); + p2.add(Box.createRigidArea(HGAP10)); + + JCheckBox checkBox3 = new JCheckBox(CHECK3); + checkboxes.add(checkBox3); + p2.add(checkBox3); + + // Image Radio Buttons + p1.add(Box.createRigidArea(VGAP30)); + JPanel p3 = createHorizontalPanel(false); + p1.add(p3); + p3.setLayout(new BoxLayout(p3, BoxLayout.X_AXIS)); + p3.setBorder(new TitledBorder(null, IMAGE_CHECKBOXES, + TitledBorder.LEFT, TitledBorder.TOP)); + + // image checkbox 1 + String description = resourceManager.getString("ToggleButtonDemo.customcheck"); + String text = CHECK1; + JCheckBox check = new JCheckBox(text, + resourceManager.createImageIcon("cb.gif", description)); + check.setRolloverIcon(resourceManager.createImageIcon("cbr.gif", description)); + check.setRolloverSelectedIcon(resourceManager.createImageIcon("cbrs.gif", description)); + check.setSelectedIcon(resourceManager.createImageIcon("cbs.gif", description)); + check.setMargin(new Insets(0, 0, 0, 0)); + p3.add(check); + checkboxes.add(check); + p3.add(Box.createRigidArea(HGAP20)); + + // image checkbox 2 + text = CHECK2; + check = new JCheckBox(text, resourceManager.createImageIcon("cb.gif", description)); + check.setRolloverIcon(resourceManager.createImageIcon("cbr.gif", description)); + check.setRolloverSelectedIcon(resourceManager.createImageIcon("cbrs.gif", description)); + check.setSelectedIcon(resourceManager.createImageIcon("cbs.gif", description)); + check.setMargin(new Insets(0, 0, 0, 0)); + p3.add(check); + checkboxes.add(check); + p3.add(Box.createRigidArea(HGAP20)); + + // image checkbox 3 + text = CHECK3; + check = new JCheckBox(text, resourceManager.createImageIcon("cb.gif", description)); + check.setRolloverIcon(resourceManager.createImageIcon("cbr.gif", description)); + check.setRolloverSelectedIcon(resourceManager.createImageIcon("cbrs.gif", description)); + check.setSelectedIcon(resourceManager.createImageIcon("cbs.gif", description)); + check.setMargin(new Insets(0, 0, 0, 0)); + p3.add(check); + checkboxes.add(check); + + // verticaly glue fills out the rest of the box + p1.add(Box.createVerticalGlue()); + + checkboxPanel.add(Box.createHorizontalGlue()); + currentControls = checkboxes; + checkboxPanel.add(createControls()); + } + + private JPanel createControls() { + JPanel controls = new JPanel() { + @Override + public Dimension getMaximumSize() { + return new Dimension(300, super.getMaximumSize().height); + } + }; + controls.setLayout(new BoxLayout(controls, BoxLayout.Y_AXIS)); + controls.setAlignmentY(TOP_ALIGNMENT); + controls.setAlignmentX(LEFT_ALIGNMENT); + + JPanel buttonControls = createHorizontalPanel(true); + buttonControls.setAlignmentY(TOP_ALIGNMENT); + buttonControls.setAlignmentX(LEFT_ALIGNMENT); + + JPanel leftColumn = createVerticalPanel(false); + leftColumn.setAlignmentX(LEFT_ALIGNMENT); + leftColumn.setAlignmentY(TOP_ALIGNMENT); + + JPanel rightColumn = new LayoutControlPanel(this); + + buttonControls.add(leftColumn); + buttonControls.add(Box.createRigidArea(HGAP20)); + buttonControls.add(rightColumn); + buttonControls.add(Box.createRigidArea(HGAP20)); + + controls.add(buttonControls); + + createListeners(); + + // Display Options + JLabel l = new JLabel(DISPLAY_OPTIONS); + leftColumn.add(l); + + JCheckBox bordered = new JCheckBox(PAINT_BORDER); + bordered.setActionCommand("PaintBorder"); + bordered.setToolTipText(resourceManager.getString("ToggleButtonDemo.paintborder_tooltip")); + bordered.setMnemonic(resourceManager.getMnemonic("ToggleButtonDemo.paintborder_mnemonic")); + if (currentControls == buttons) { + bordered.setSelected(true); + } + bordered.addItemListener(buttonDisplayListener); + leftColumn.add(bordered); + + JCheckBox focused = new JCheckBox(PAINT_FOCUS); + focused.setActionCommand("PaintFocus"); + focused.setToolTipText(resourceManager.getString("ToggleButtonDemo.paintfocus_tooltip")); + focused.setMnemonic(resourceManager.getMnemonic("ToggleButtonDemo.paintfocus_mnemonic")); + focused.setSelected(true); + focused.addItemListener(buttonDisplayListener); + leftColumn.add(focused); + + JCheckBox enabled = new JCheckBox(ENABLED); + enabled.setActionCommand("Enabled"); + enabled.setToolTipText(resourceManager.getString("ToggleButtonDemo.enabled_tooltip")); + enabled.setSelected(true); + enabled.addItemListener(buttonDisplayListener); + enabled.setMnemonic(resourceManager.getMnemonic("ToggleButtonDemo.enabled_mnemonic")); + leftColumn.add(enabled); + + JCheckBox filled = new JCheckBox(CONTENT_FILLED); + filled.setActionCommand("ContentFilled"); + filled.setToolTipText(resourceManager.getString("ToggleButtonDemo.contentfilled_tooltip")); + filled.setSelected(true); + filled.addItemListener(buttonDisplayListener); + filled.setMnemonic(resourceManager.getMnemonic("ToggleButtonDemo.contentfilled_mnemonic")); + leftColumn.add(filled); + + leftColumn.add(Box.createRigidArea(VGAP20)); + + l = new JLabel(PAD_AMOUNT); + leftColumn.add(l); + ButtonGroup group = new ButtonGroup(); + JRadioButton defaultPad = new JRadioButton(DEFAULT); + defaultPad.setToolTipText(resourceManager.getString("ToggleButtonDemo.default_tooltip")); + defaultPad.setMnemonic(resourceManager.getMnemonic("ToggleButtonDemo.default_mnemonic")); + defaultPad.addItemListener(buttonPadListener); + group.add(defaultPad); + defaultPad.setSelected(true); + leftColumn.add(defaultPad); + + JRadioButton zeroPad = new JRadioButton(resourceManager.getString("ToggleButtonDemo.zero")); + zeroPad.setActionCommand("ZeroPad"); + zeroPad.setToolTipText(resourceManager.getString("ToggleButtonDemo.zero_tooltip")); + zeroPad.addItemListener(buttonPadListener); + zeroPad.setMnemonic(resourceManager.getMnemonic("ToggleButtonDemo.zero_mnemonic")); + group.add(zeroPad); + leftColumn.add(zeroPad); + + JRadioButton tenPad = new JRadioButton(resourceManager.getString("ToggleButtonDemo.ten")); + tenPad.setActionCommand("TenPad"); + tenPad.setMnemonic(resourceManager.getMnemonic("ToggleButtonDemo.ten_mnemonic")); + tenPad.setToolTipText(resourceManager.getString("ToggleButtonDemo.ten_tooltip")); + tenPad.addItemListener(buttonPadListener); + group.add(tenPad); + leftColumn.add(tenPad); + + leftColumn.add(Box.createRigidArea(VGAP20)); + return controls; + } + + private void createListeners() { + buttonDisplayListener = (ItemEvent e) -> { + JCheckBox cb = (JCheckBox) e.getSource(); + String command = cb.getActionCommand(); + switch (command) { + case "Enabled": + for (JComponent control : currentControls) { + control.setEnabled(cb.isSelected()); + control.invalidate(); + } + break; + case "PaintBorder": + if (currentControls.get(0) instanceof AbstractButton) { + for (JComponent control : currentControls) { + AbstractButton b = (AbstractButton) control; + b.setBorderPainted(cb.isSelected()); + b.invalidate(); + } + } + break; + case "PaintFocus": + if (currentControls.get(0) instanceof AbstractButton) { + for (JComponent control : currentControls) { + AbstractButton b = (AbstractButton) control; + b.setFocusPainted(cb.isSelected()); + b.invalidate(); + } + } + break; + case "ContentFilled": + if (currentControls.get(0) instanceof AbstractButton) { + for (JComponent control : currentControls) { + AbstractButton b = (AbstractButton) control; + b.setContentAreaFilled(cb.isSelected()); + b.invalidate(); + } + } + break; + } + invalidate(); + validate(); + repaint(); + }; + + buttonPadListener = (ItemEvent e) -> { + // *** pad = 0 + int pad = -1; + JRadioButton rb = (JRadioButton) e.getSource(); + String command = rb.getActionCommand(); + if ("ZeroPad".equals(command) && rb.isSelected()) { + pad = 0; + } else if ("TenPad".equals(command) && rb.isSelected()) { + pad = 10; + } + + for (JComponent control : currentControls) { + AbstractButton b = (AbstractButton) control; + if (pad == -1) { + b.setMargin(null); + } else if (pad == 0) { + b.setMargin(insets0); + } else { + b.setMargin(insets10); + } + } + invalidate(); + validate(); + repaint(); + }; + } + + @Override + public void stateChanged(ChangeEvent e) { + SingleSelectionModel model = (SingleSelectionModel) e.getSource(); + if (model.getSelectedIndex() == 0) { + currentControls = buttons; + } else if (model.getSelectedIndex() == 1) { + currentControls = radiobuttons; + } else if (model.getSelectedIndex() == 2) { + currentControls = checkboxes; + } else { + currentControls = togglebuttons; + } + } + + public List getCurrentControls() { + return currentControls; + } + + private JPanel createHorizontalPanel(boolean threeD) { + JPanel p = new JPanel(); + p.setLayout(new BoxLayout(p, BoxLayout.X_AXIS)); + p.setAlignmentY(TOP_ALIGNMENT); + p.setAlignmentX(LEFT_ALIGNMENT); + if (threeD) { + p.setBorder(loweredBorder); + } + return p; + } + + private JPanel createVerticalPanel(boolean threeD) { + JPanel p = new JPanel(); + p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS)); + p.setAlignmentY(TOP_ALIGNMENT); + p.setAlignmentX(LEFT_ALIGNMENT); + if (threeD) { + p.setBorder(loweredBorder); + } + return p; + } +} diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/ToggleButtonDemo.properties b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/ToggleButtonDemo.properties new file mode 100644 index 00000000000..241368aacaa --- /dev/null +++ b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/ToggleButtonDemo.properties @@ -0,0 +1,69 @@ +### Button Demo ### + +ToggleButtonDemo.accessible_description=The ButtonDemo shows examples of using JButton, JRadioButton, JToggleButton, and JCheckBox +ToggleButtonDemo.tooltip=JButton, JRadioButton, JToggleButton, JCheckbox demos +ToggleButtonDemo.name=Button Demo + +ToggleButtonDemo.buttons=Buttons +ToggleButtonDemo.checkboxes=Check Boxes +ToggleButtonDemo.radiobuttons=Radio Buttons +ToggleButtonDemo.togglebuttons=Toggle Buttons + +ToggleButtonDemo.textbuttons=Text Buttons +ToggleButtonDemo.imagebuttons=Image Buttons +ToggleButtonDemo.textradiobuttons=Text Radio Buttons +ToggleButtonDemo.imageradiobuttons=Image Radio Buttons +ToggleButtonDemo.texttogglebuttons=Text Toggle Buttons +ToggleButtonDemo.imagetogglebuttons=Image Toggle Buttons +ToggleButtonDemo.textcheckboxes=Text CheckBoxes +ToggleButtonDemo.imagecheckboxes=Image CheckBoxes + +ToggleButtonDemo.button1=One +ToggleButtonDemo.button2=Two +ToggleButtonDemo.button3=Three! + +ToggleButtonDemo.radio1=Radio One +ToggleButtonDemo.radio2=Radio Two +ToggleButtonDemo.radio3=Radio Three +ToggleButtonDemo.radioX=Three(HTML!) + +ToggleButtonDemo.check1=One +ToggleButtonDemo.check2=Two +ToggleButtonDemo.check3=Three +ToggleButtonDemo.checkX=Three(HTML!) + +ToggleButtonDemo.customradio=A custom "chrome" radio button. +ToggleButtonDemo.customcheck=A custom lightbulb checkbox. + +ToggleButtonDemo.phone=Phone +ToggleButtonDemo.write=Write +ToggleButtonDemo.peace=Peace + +ToggleButtonDemo.controlpanel_label=Display Options: +ToggleButtonDemo.paintborder=Paint Border +ToggleButtonDemo.paintborder_tooltip=Click here to turn border painting on or off. +ToggleButtonDemo.paintborder_mnemonic=b +ToggleButtonDemo.paintfocus=Paint Focus +ToggleButtonDemo.paintfocus_tooltip=Click here to turn focus painting on or off. +ToggleButtonDemo.paintfocus_mnemonic=f +ToggleButtonDemo.enabled=Enabled +ToggleButtonDemo.enabled_tooltip=Click here to enable or disable the buttons. +ToggleButtonDemo.enabled_mnemonic=e +ToggleButtonDemo.contentfilled=Content Filled +ToggleButtonDemo.contentfilled_tooltip=Click here to control the filling of the content area. +ToggleButtonDemo.contentfilled_mnemonic=i + +ToggleButtonDemo.padamount_label=Pad Amount: +ToggleButtonDemo.default=Default +ToggleButtonDemo.default_tooltip=Uses the default padding between the border and label. +ToggleButtonDemo.default_mnemonic=d +ToggleButtonDemo.zero=0 +ToggleButtonDemo.zero_mnemonic=0 +ToggleButtonDemo.zero_tooltip=Uses no padding between the border and label. +ToggleButtonDemo.ten=10 +ToggleButtonDemo.ten_mnemonic=1 +ToggleButtonDemo.ten_tooltip=Uses a 10 pixel pad between the border and label. + +LayoutControlPanel.textposition_label=Text Position: +LayoutControlPanel.contentalignment_label=Content Alignment: + diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/ToggleButtonDemo.gif b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/ToggleButtonDemo.gif new file mode 100644 index 0000000000000000000000000000000000000000..35099bb894fdbcf559cca16bb84d5e2e577fabd1 GIT binary patch literal 1157 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 P7x~0Bcm#DS1OWg$YOidd literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/b1d.gif b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/b1d.gif new file mode 100644 index 0000000000000000000000000000000000000000..383404e76468b2631211516324aa14f526cf5022 GIT binary patch literal 2640 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 literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/b1p.gif b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/b1p.gif new file mode 100644 index 0000000000000000000000000000000000000000..1f3fd66328ba678811608133a189ae76837cb7db GIT binary patch literal 2704 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 j6y~34 literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/b2.gif b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/b2.gif new file mode 100644 index 0000000000000000000000000000000000000000..af6d626058a674f22162d4f4f28f23c203b81eca GIT binary patch literal 2523 zcmV<12_*JMNk%w1VN3u`0M-8hOs?5*lc5DdVB6c<|K0!vj?0dYk}xc<0Vg?@(d$o6 zxDXGKF*G;?cdjXjvj=FD4Gd(tpei{roM&dmZf43n3VVR-D8sHgXUyp~kIBhkv1JVwC^? z|NsC0A^!_OL{CCRI!RJ7E-(V#?EoS91OWg5KL9KM0000?08Ib@2LHgHa7Zi~kI1BQ z$!t2G(5Q4uty+_qYz(vAX0PC;HfAWB&*hl<8*cy9v<{*v1zC z?(gmK0PO%3-kEA{S!KF~=+wpl;HX!@fx&_ZE68{^@0~phHNepd;|Z39X~2rPGw5K% zhzB4;HW<-hgFzW68E(`NF%6q4YCtKwknvbSj}JKL+{u%J$o~co3MgC%U||IiEhkpN zkjoH>U^YAEa-3;?@OFsA?naC@jO0lBq;=g_0eeoosq z-vWKHkKhrmEEA@{&k4g(;iFp&Nb(_xIn0s4znU zqd8C-0xG1&Tx;ze;GlyaeDL6e@45G!1SrsO*>(Cg5dT0pVw6*eF9nt+00_&;HerM; zMo8g{?O}-DZP)=7z#2<@R>%tkw&Q?tIiR?p0`Rri;)@WnNMVM1?Y99?N?{?Q1=s-4 zky|OO_F9rgx=3GiTq^nC2OM~%-;*A0a)e-okbq5jSgN+=0bP12A8fL*xWEEO()oc7 zdU04lR}SPOC2$sGQiYIfvt6BN`t2$1(3w3 z4v1?UYCV{15&@tEIJ5&NTzJkm@Ps`? zfF}>p643C&eA0QY#CzVGTLHYD;H?o{$fQ9)S8~wk1nrIdU6kns=TO#%Y3Kk5X6ib5X{LI}^bwy??sL_q7j3pf}fkz;;fE5$YD1j3pz z0M{{FIhg>Pz{+M20)4Gza5Mo*GtfZ?IWS$HxqfDfLUx1Zu0{?@?nQvZ!#T=k;;maJeHyo^X z${j=?f!vuwz=8uPRjyMEx8p8}3h!Jg*Y6{%P&V;LAAP*7dlId}`lHvnz|#vr@WBaS zZD3pgFyuePZ=`JAv+sZtQ@~*3fx{s#UMxeO1*XNP7Dx+y@}l4DN`pWB34wna@SnxL z7dNIk$OQy6;0hQ}I<}$j1`)88${@G^3Cf^r0;pj2-jIt7ilqS>dJdPCWdJ;l4r%c! z9sJO@rxa$Y0|c;@>0HRIs>pB%;8B4FP*MiC_-wCU;H3& zgA)`*1{OO`0N^-7rhI~q6&RGo@JON18NdTYdshvP$2N3bNCFX3K;Gn{jPY*(1urVQSPscOCKwkKO#!cahtda%5 z4C*me%VL(En!QCF%(NdG(Dqu^vMq9<7d_L(kRArIECZ+sTr8-;0Pu`$hjeh%YSq@e z$-NbLn+pL1qyV}jQ2#AXtE&WDkc38q(t!zY06p-c@mjSFuM$nCf`Al+0}OC0Yg>!A z7zCgz@O^-MyL(?fHJG{Nl~;eEYtzeSHn^Qo!$B?p+!vgW=Fe4Cmx|*1^n!ec5vg>9b<>7{5JvMkwK>8OkDMUX=U%o32HfaoNIQQqFgKX1T^XKSCBVzRDZSa-3?bx?uoxhL2>>4O zhBw>-B$%h2SImifW0amTAb|=j;P8OI*=iFfc)^Px?VM(o3#u@M%46WIkgIs)ODMU^ lXC87W!`uk>ra8^ycylM@T<4V3?A3bCgxgtM+O`M+06SJcV>kc+ literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/b2d.gif b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/b2d.gif new file mode 100644 index 0000000000000000000000000000000000000000..b745943ed6c333dcf41d1a90157d7f65b2d37f60 GIT binary patch literal 2758 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 M{oil@6oLQ%J72=46aWAK literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/b2p.gif b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/b2p.gif new file mode 100644 index 0000000000000000000000000000000000000000..0f4abf379926388d752f6f23e24a846c74e1ae15 GIT binary patch literal 2777 zcmV;~3MTbONk%w1VN3u`0M-8hqf>9}?CdWruCOmq$w+8QMYxodlx!wXZD?y^2PEfm zkJBJe|K0#02N9)kgrtvIs;Q|@NKJ2V#V8{ttQta*1unazE4c$S5Dt>-kf|6Lr4I~t zWMsh8x*-k;QPzN&?WMJHJy=dgZh1~)=E@8%Cu+(7F>_-wPe+m}D3Lu7V}g8xBo#Y^ zgwMT)mpv{y&d$!>-rlvfwIUCK|NsAgZBc%E%+p($ZJ38zb1Qm5o;?3 zUUPI(T03faeG?5VkqwlRk_~=>2!&yYiA!2{dXSf;n2(l~m@j#oah{(>p(~D)mwgc< zBQ*}aYr21=r>&f@u|HjEdy|wC5e_aHARrml)gRaj9|`~?f04KmX>!N25fhh~&pgx9 z*4EhD+Y0mwHvqwagDZ!hTFSzlxoGDO-n@DBZrHoQ@CJckdt8N~BZHBiR1pReG#Kv# z!-Wk)G9*Cq53511##E&BqSAyF1gT}{*vkO`00?g0bVz_F$^V`|2mHx1&RCRW=P)&h z0Ab_Ic)hxXm`Zg-h?_sNE+DyJYao;pr>smW>4Z~kE*w~mz@n{(w{TUkP*GwkfdO0z zY~8yc1gjAUQbzfg6cfyLPt$rU0k^T^#wrek7=X7c->wu`ZR+=ga7#=M5I;_(I# z8&YFP&ARan$&?3JwoG9Fv9L}c7(jq7QfPPaR=i;0n6+vQ$XCzUu=@0A4+m-wpfKA+ z?N)-BtQ;=s@HH1MMi6hzIynXn=hK@vo;osx_td+ZFmYg?mnMXJM<|HRL5SkXp{K!s z=?RFQdCj#K9R(&V(BOO~gs{m9xFw+*Z<4&ggFz06K>r?d1s*uyhz1h49&@izaKVEP zns9-H{1{b)76?SpOf=Vg;6Q0_9fur&BNm9@k0v6gUNAv5B+)A zBZwrL2xM{_%z&VP2+oi|k`KCg!2!i2K_go%Oz=UD;f;qHkwR{1q;W(BiJ%HdLg-=z z6^xLbEG$J*Lyk6$*Cm}n9+=#6&-s|8qsZ||=8|acAfpfvK#9qlGu^NwTv#$nX=`u2 zKtcyIWSM7x3G_K%k`*AZ1rinUMhO7f=r{snbDl?{dXJ_C7YigXz(5Te2q3_)1|l~@ z1uo7C=mDA{(dk7KxR8wl1ON+SokODP0Sg@{%m2UwI6SMRu<-J^B$5@Zu<5ptM4-(= zBar$kn9`I2x7?f}5dTb1BaNT}YF>uusNc_pF1QUgNKn87B(S?d z2_>x7+>g!{kU|L+y!UhhJIp?LjeX!w0Ny1Wr)Sb3td^?K3`n3p2Pr>bfd%MyU_lBP zsO_u(B_v?I#0g-py$}{NNkhIDg58yBd?L^QTvUa=^`$HYOVdCN9w34W!fylv5&#Ys zmOuDV08j~7!T)}+n?D?YBo2{G_{P;W42Zy3$Pyh1Bw)1rHA@2`c)+t<$bk7ZYhlu3 zob{j!R{X=pzLVhI>G9sVT% z0Wdh?5O#Bp5a@*@2RK0Z_GX?LSpUfb!b6w@u$Z!jjYt3%P@D$RB>*6DP=C|;VE+(c zL?kK^5P6}%0aU<3^$08iZ%LN2X83>>tl$9;7?ygxm>&2=PmC|1%mU2FNHx|@4+rSV zT?Q~o)e*}B8hC*Z@My9Q_|li5+~JTcU>^7ZQ3pCe!59yaNT@Zy0Y;3$5Vo}ky_p5VW^zoqFdc9J2P|Ob0tNuW2CSfw)1-wSER)D= zmdgWsI6(>0m4k(qt&bmr)M`3VOa`=*1MiGyET_}I2J9dJM$Bi8Qb`+0YV$-j=l}^c zi837+0HGf<&J2jzyZ0?@oc|msK|Eta#01CypF)rU8#!tjCaIuY8wi}bI*mLeNztBBK!@C=;!E*S8u7Kmr|5A31?| zzsgqjh73Rf4n%;02k3R2#k=ZcDB6Go)F1#DHSC4*5T8{_?5s&60SZ>&gZ0(002{Qd zZUKqD^hv-5Ezm(*CZiW;#;mYJSV@f-w4^gJR)QATD>9AN$K4jvvW6^xJ0;M97Dzw@ z4FGIb6A<1QAUC;eO#f_i&C0S<8nRv1)h)y3d(7W)-(Nf(%wRf+A{Eh*$>U4UV(B9%{e=E=(CBHUI+u z<#2%25CSBv=w)WLpUNhsB_MG*dQ?SnhOk1~!J;;`YDxwIUgtEZl|XG;+~TBIMq~{x z%)Tx5KzK($k}}&g%2H0QPFGN6YB}u!377$#aqD0%XAr_b1g&?IJOMs4*_r)4Gl0{~ zhywuX&=g4YqW=w;T+4djKR@8AVWkJSgAju*WA*^9GgmsUJ>KW6 z1cfpb=XYrtId@toVs=N2!PiS0_PxIc@Y6}8A`>9=(uP0=g*W^s5FbqggaGc04}1S= zs&q+IT*k`w_Xs!7c_Ws)?VzUwzXi|l7a*Vq0m%Hn>pq5~M>DlMu*w8Z4{o@V7vePl zK@4c1`y=w64z0)NnPH$@T>u6E{OG}~{r&Ri$0GXD=l+>>&;3Sv{}@8>y?M7U{`S-Q f{OBjb(bM>Q_eb;S@xQ|S{y^M9ff+}_D!V7fPn;;En7;EKmdcpj2dO+$W@6#82>9>NDgy=a$g0OF$E}H zDj=pz1{g3{ltHzEj0_zO{+yykD5M3-4pf+!l&Mk#49qrtDk18{i&@!#eA2b#NqhSU zv{Z`#L5{yUO8o7+G_A{-QZGg&z-s|Jx)a_Fy9;itUb852EbtJbv*!>IaCqpE13}ZZ zGuuX$vAE|}CldxNnCwu;(YSQYG9MntvQ#pyQ4nvH&87{1pH~Q!^P@;7knkGQlPkD73*d^U-);cPDhX zV*@(^iDLmCdI;o(hapy=ftt-#1bpc%Bi2Mq)rg^nT=K}{h6ZG)p`2dkc;1+)&7#rpUksutQ%!?FRW;Qy+t(CXO03exiV5(5XJY>-Ue6YoIZy59|Qwf zuq+Sg27p4t>^=*ttFy*HF_Ho-naThJZ5t3-!3yWy2?2BH0R)W>ka7(ubl`BLId%y# zmMcsvGtC1W(W$lpjdfjxKPQ@Jh&NDBCvXWbJA%>oWft_2r-GBxwWDu3y0_-695F?3@4nJ4z)jJL!OIpAJCp4-oy2&S>wgnJyka7SV zaFF`C)2|RaZacJKyAZfTfl>6r0KasuZ#`gC1dKMn_Bo7YSh|4- z!3T#Rn?4}GDr6~O0$XRQ5IwImE>r;u&bG95xr+ojeATL6^*{|UAOkYc+;u!KL;wl} z1yo5Q0ziPmSkdWu!Z98WJ8%P~k^i8IPip`K+;#!^;jdvN=%MvEID#_%KsMPB!Br&E zk~K!pOLejU1;~aj7>=M1Dj0#gvZNymz>07W++hb0Kov16g^d0ig5CO{n0bMKi3RY{ zcOvis9z?)+HQ)dQb_sz6usPT6cl~Y(Rb-=)ee6K)6-u zl9%;MSzI;AB^EF%qRHC;MR^7Q5#&^-&t!xM(u1TGTp(l{5?%vPkk)WkQHPLiKmbkv z0te7A04Z%)@+O+Oi!Q(g8YKdQ_CSECrA7e2>kz9(Fai^xEuoQxY+F-?*HP{xjA;SD zO>_D`T`htnrIi_q7+_d;EdT-|xMM`u2~iTb%DK<|V??Lhz6}_~qUoq7VDH(a97yd_ z0>B_dmmAyIK6bjA?f-xYbjPMgc)$fN;OY=W!U)aDi@4&2EX1D1$5)9rl*g=YKyWKh z0sH{8^3@fC7JvX09VrF~fUbD?>oOksH@^W+9s(fJ-oC>&!@yb)l|2xtMAonQtEz+}%-F@bV>eBm}bYkKX=Yn-(< zX94(N(U6&d2mer`n#CaCwXiL3hTAt$aIERjU8VpHKn;Z~j5sE$Nn>doi(d39Ez>60 z0JjW1X8?=Jqe-Tw8z zntTDv9=qTRYyuJ7zz0cJx}DvQgs*Ei$uQ_D8g2lK4U8ac+EaAiMZS2(E1~ay<-F)e oPvZpJ%kz%^E4@kYZH?yK^Plhg<5ll>L&Dw)j?ew}5D^3bJ2RuXm;e9( literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/b3.gif b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/b3.gif new file mode 100644 index 0000000000000000000000000000000000000000..67876264d8f7f4d350815a4e08f6eb1ec6f1bb0e GIT binary patch literal 2721 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 literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/b3d.gif b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/b3d.gif new file mode 100644 index 0000000000000000000000000000000000000000..4d397a394f4632da6d5ed6e0aa259f5fae2d75af GIT binary patch literal 2835 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 literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/b3p.gif b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/b3p.gif new file mode 100644 index 0000000000000000000000000000000000000000..0db029f56c308115edf1c081a7ec3eceab7d9c22 GIT binary patch literal 2807 zcmV}V)S%}8mmFjD{C0GODVacXYXag5U- zPcA91wgWRRCoM}yNvWu*843}EeuG|K!MdX>5Dt>67eVZhsi=`Z7Z;_lQEk+?APxw1 z?Wnlgfti#}YKeGXUsqm&1}h>Vk#tL7P(yB2PN8yfx(x_RrEi2aDP|=Rc6DY=$^bDP z8%IPBSG|asKs1TY&d%Q6-nF&0=*tU)gw7!tjQ{`t)|92wUVMCd%zJA&lY2pHV}!=F zz5oCJA^!_OL{CCRI!RJ7E-(V#?EoS91OWg5KL9KM0000?08Ib@2LHgHa7Zi~kI1BQ z$!t2G(5Q4uty+(>*<<@Sbh(f0b*$8O50MB00b;um>4P^+CY#Z0kfXux_Z@&BZ4q*G zby8Y*dVLU$j*pFedk%wMVuwtLYI_ZkkA9wim4=vDT@j0+4U`BdttcJ{Ba5Aps}69M zrA4QypCA!0791QF#>W;N9SI2;9s{+KqcgjSY_*OKC^*B!$i~VE%FE0-IUalvlDVXI z52$;N4InSX_Q>JOHNKrFylf4N0+T+$0#fTJz%WfD=y7cH1 zqg$jHK(}sW1uO~xf>Sj~7X;W)+*9_#1B??85SO+=p*V%nB214~(b%=?3M?uJqD}4c z0R#>l=@!WwM2q04NgE%Jq5Sycrd3oZP@#fr777?hXLtFXP7po8c_kryGzAR*{Z}4< z#Su7Oa-=cV9>jzybpUtYH}vU1X+Ce#z_yo_Oa0=>Oq>=bdN6av@Z}fCA7l$Ur64 zC7}g&kq84$2Ph2CAAtrM=V5@wsX&2`=aDu511CD@pnUWx@s5Qf7()^>{E4=Kj^p__ zC6&dM7GjnvguuXqDnd9=i$=Vd-G$&VGXi1$?bxH1=c!Pd2s-BYqnAcL$Uq1q$U$Zi z23gocnhEs4CXfOemtK&g?O*^6DF|pDk1i1T<&k0@@Fx-Wb<%(`$pjUpf07b7WeXQP z3c`4#fnb0J228NPfRh$yL#3Az$wCMSY=MM9xHaNIsA`f$Dy^(eK*J6b7_fq(8?->f z1r+dLzycyHFsr0JhVUS!5Nb*xUk!*uj&D-#2>+t32JBFr1~kwqrwESPfWZgXYHP1~ zJK~CI2q8qUsSuq)075wCtQlT`$d0E059N{IY?d2LTiyg3Xsa!)w08OJmjILS>vgqc zAPzGbj7nw0#qmJHfbbHx0SpF!fC0@H004l-rIvTAu6l+rzynwukXgCqoa4g6J+Dgv z%_NjJ!o(ERiva=#Ah0vGA9C!1m%0KR@DWRP;y}_Fj1bDTFhs!~{Nn+C$F|MmaUvLS0dfZL;Qy+k(3Z|t z1HK9XEC3NG-w%wohXJ%C2N}4CoQM-3e(|VPb9*2SK7cV;J@8e!TcCh627nZ}pgkt~ zAOd6{1Je<(gfL+X`>y1^AC1OU7qFr3bha}9^xy-qq5%vrn8Ce~;B)Q~TmS+v#3Byi zI+J*cOa=fyhB>V?G?+kfnk6gkY5xpsIb7i0bOuKDcu;&p9N-AC!-uy>WkhxQ5XDB* zE($C_Vg@Kd-#oW~3)FFY1JWP>N)VRy6ktzdETasJS3fA)1jxz;+4_(sOTd@`%w=$7B%%?2P&O0Rg*^pu0isxz8kaqg1Ph=*>|%Js2g2Zy z`Qlvn03ZPXs6{615g!NzK*ZC*qXEaPO9fJ}Ol~>RS}Onn4QRkZM~OfLSwuhyE?9zm z`((PvA)pjjQeD6M{0Kn?axg6}k71nNC21bm=Y0SFQc92r5Ii4??p23V7H-sG1>+T%$F*#t0XM2T~7 z;?yFb&}w$kDgOolFefuQ1T7E&4C)-|Jd5Np)v2m}BE5h%2wlU#-wD4N^y0ip}WlIlqV8OY1lfxydMf`?~qk2Y(elpWa7aFyqmr9 zE@I1@Q2)9ZZ2Rb9V*q>RqI3jaWCUnmimlDj6oxC<-SKAm)}7Gx0}Nn{Vgq7S-co?3 zq|omKPjiz3lB7z#kp{ZU>~a^J`Un6uKVz12_QarHA1fCxD?RLIyYm#s_*y^?;;P J2LeF=06R>@(c}OC literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/b3r.gif b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/b3r.gif new file mode 100644 index 0000000000000000000000000000000000000000..7ddd2f9dc74c1cc2a05299c8bbcfa154549f1052 GIT binary patch literal 2822 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 literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/bm.gif b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/bm.gif new file mode 100644 index 0000000000000000000000000000000000000000..80f7352332b335f3b71830c8b9b6df1204f8747a GIT binary patch literal 582 zcmZ?wbhEHblw#0e_{P9+;J|@*@7_Il@ZjFPdpB?1ymIBrg$ox>pFVx+)TxsvPo6k& z;@GicM~@yoa^%S2!-w|*fw=gtUAuPd*s*Qfwyj&YZr;3k5W5$f>)2C0HHf`$EsZ*v*nLK&&q)C(d z`}_O)`g(eL+S}Xf>+9?4>Z+@&tE#FhD=UkOi;Iei3JVMK^73+Xb8~WXva+($)6>(^ z(o$1X)ya+?l_0L2Ld`{D+MrskH`w)T$BuI`@RzWxalCrzF*b=ve9GiS}7Gk4zn zc5xLg>8biY4i26+ll7bxrKIGn?K=fE^(6RPJU49I=(e;)SjpPOM8?qGQAbw8dH;S3 zeGy3&Qx7L&RX#=@Au&;911r~y8!f%<%#F0AIGLHaTDaMH`MuOG%DKyPu(Gg7BwR^4 k(HS9EwPDf|B~L9?Go40;LdGdayCsaX?wt4-sK8(i0EB_OegFUf literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/bmdn.gif b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/bmdn.gif new file mode 100644 index 0000000000000000000000000000000000000000..3d42d0b9e15be0a8b3ec73f6349c3eee8ee48aaf GIT binary patch literal 1115 zcmZ?wbhEHblw#0e`2L^a|NsA=KY#x8>C^l7@87+9_xA1EXV0EJefsp#qel-OJh*r7 z-p!jguU)%#<;s-{7cLw)aNzXm)2B|II(hQsi4!M|9XodP=+PrbjvPLG_|Tz4d-v|$ zwQJYTojZ5z*s*Qfwyj&YZrQSB^XAQ)Hf`FtapQ&!8`iI1zjp1~)vH&pTD5BB%9Sft ztXRH$`LbopmM&eoWXY1nix)3iv}obNg$ouem_L91ym|BH%$YN5)~uN`XU>>0WBT;z z)224Mj*gD@_V%{6w#LTB`uh61y1Lrh z+M1f0>gwvMs;bJ$%CfSu($dnBl9J-$;-aFW!otG5yu94p+?<@8?Ck8UtgOt;%#4hT z^z`(!w6xUJ)a2ykq@<*TgoOC`__(;Z*x1;Zn3(A3=%}cu$jHcuh=}m;@X*lEkdTm| zprF9Oz<_`Ne}8{(Z*NaePghr07Z(?2XJ=bmTN4u#O-)UCd3gy52~kl|etv#-c6K&4 zHil6!2tq*dCkrD313!Zf$St5e!NBpKL7qd#W5a@j%^bp7F()=GJlrmz>@~+@5&Tif9MkzfK9*a**QuAmzF(G0xr-W(txq`$=%#XN~lkaJS z=c#q|wwo>55V>#8nTgkVO9K|}+ruj=%XfL3#lv-Tx&_+xCe2a&f9&*xcH0eW9^QM# q)!(kwmf`8Vw?|0g7~?}1vABwfM}^LNx-|1U^E`Cskzr+Fum%8>8BtRJ literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/br.gif b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/br.gif new file mode 100644 index 0000000000000000000000000000000000000000..b02e4484fd0bfd935271d0004f5a23e2275ab246 GIT binary patch literal 621 zcmZ?wbhEHbRAOS6f?KQ&Uq}Sy@t2l9!j4larH`m6e{Jo|2Lh7Z(>B8ygc76CE8L9v&VV z8X6oN91sxT@9*#7;oj;wdKXMSh+X$ zNIM#+>)6?fuM0}rQbIxnCsa)IhlR`uysYDqoAFWWspTgX+Qv{tQ?z1)``aB-CQj5WLX-S ykFzoFlKM13aS`L;HZe1mA1y@}j!ZL1J~hX4^RlzEEs9^=Ik|bczgHszgEatP`^~xl literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/brdn.gif b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/brdn.gif new file mode 100644 index 0000000000000000000000000000000000000000..58cf892514b683f4dbc6126d92f4c013939b73aa GIT binary patch literal 1159 zcmZ?wbhEHbRAC>i7n=)m}q)C$|PMkPl!i4_*{@&i+o}QlW z?(WXc&W?_b_V)I+wzig*mgeT>#>U2mhKBn3`ntNh+S=NhnwskB>Z+=$%F4>Jva*tr zlH%gx!otG5yu6&8ob2rEtgNie%*>38jP&&Mw6wI8l$7M;r@bJ*k(2$Uj;Nak(prF9OK!1OK4-XGFH#b*TR~Hu-J3BjD zTU!$o6HQG`d3kv$DJcmF34VTlc6N3)Ha3P)Fo;7y@h1x-1A`QU4#f{Mk<4UJ7a zaxA|%CQej!W@KP1Ik51E%LHb=9K9R{h6YAK*8^*I1~|NMVs;hZv)~dNUpJ>8uU&#d z<6%+r;x{W!W_>uqF5!Le%0}UK-kVMKO+Jy%$NOYu^X~J71gdq*Y%?v(xaeKbCBZMI z@xep6X~O~0=&l8chuyp%H)@7VDeziyimjC`@{2)JiU*&l&TfW@8!jbqt3({>`RVl3 zqi2HEp#=&G7Zo@aqR(-Nzf0}p?o~OVa4>-3wUfB_DIWQAt|!vkwRaQ+;%Kw-a}N>EX2wrv@^;_j1SD zoXxYDX~AwU8L6Zytw}3hZ!UAkQaS;l;2Rr#8aXQ$gcmC>FFA@lGCVUgEe8R6K7D>k z6CNHE7ZoQfDk_gH0RsdWmX}O59U&nnC?Y8)CX%L=s!cux1q1{G0t2SJxVgs1s!_@Y z%R^DfNE167Aqy!A3T%aID%DU_+XH!2=0hu!FjIX!Y}j*tNY1sKpm8s`z%*(LrZ zcSx1ubFat-O%@iga8W|W9T6>p@hW%(LkklkI+z&2Q4+mz6hn#}Niw9eC_XYU5P+bi zl?MnIkc>iQ!UhNxC{R#w!9pSbeHJX3&`=5Fr3@QfpoownMk6&O);z5vLj{Q#4SW&I zMdJn<8QoN*kui1-9TJaH%%gM+!wYnU47HoHNJTsl2lQ-3h5-~IY!?u%kU{v74oPf7 zy3oWa@Y`-P)DA2_azce_B3wKmQRf1rU<8VZ#NfaLhY=DajF4qQtwf^~H?&R>V%BL6 zc?awrxr=~+0|!9h^+>Fw@#DyoD_@>`ne*k(3;@zV9ToB@7=*DzV5LEV?+F|@$S@MX zlngc>c&JbzLWBhobS~)7K>`Cyu%ATHz`$nvpkM_R6gMmQWB~-k#?WB@!xwd7G{T4sIW#nb4!Z5;2MrN;mBR~cpc9G;GMLqa z8V}`>m?|2evBEi^ptHh^a?HU)LwC6Lqbe+@FbF7Z9HNMhOh#r)XbL*P!V8AP#ZC|H z@W4Y1T9y)8K`i)nfdYXklE#TCq~OshUHb6}1r`8iBULGMu%(O0_<+I-jb+mYj*;oG zmUSW>w8;f1;9w-0r0A7rE|z()fC4+=D8nNTNJJS)8?@Jf0yS*F8Csv|wrMU6#8=-8 zD#V~`3L+rJ$E>Ps&_W0m)KFUoDd4sip%hJELI*RTFhgsm<;E;U2uMK7MI+p{EU>Ed d#=!^Va)g(r;QCPv2?o6g5I_JPwU}-Y06X&Yisk?S literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/cbr.gif b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/cbr.gif new file mode 100644 index 0000000000000000000000000000000000000000..278dab92f3867dbdfb07cd66bf6f086731a9e62e GIT binary patch literal 1270 zcmVfrE373l!P%M07@|cV?qFgS^$)UTCIBky^IJU7yzYv0M4Z;y{&Vl zj4`#00M5po-p+I9-kkU6Yxn0;*2X!#wOX~ULe|bw=iW-StpIa#YqhndV`Ey@)};Ud z00000A^8LW000C4EC2ui05JeL000L6K!R{cEEkYnDS^0dO?FMW#vo~YGueY7xs+AXCICe!bfdYF&FsbiSbaxSL}V`lmSc6APMuv$00f{#bTE2%O+7F(m_@C-1g%Ph8d?67lgN;xn@4enJr;0o&a1 z6C_|LfT5s-2r*#1c*AE1!e&SSM9{Xa!Gwzs9wfjp0RsdK86FUz70TWnHJv;SX%#|S z0|zi4C{VCbLBS zVW?aLI6HQ{fPv$fkfaSH;5?83#DxS27Ff6dqQZr!3nZ{fBx6*iIGVz;fRtX$aU= z1Wa&f0osC!)FnQ&{*YS52jU+(lwe@lHYy~~g*4E>J9r7x7d{04pdv>O6)H|7FJS_o zEf}>4pqpK!frkz#P#6aRLi_6O-oKZqP=W*l0b-FwgB_a;HNz1$Ot3-+9SBF?gOKHy zgLV~=pg;g2)Ib9bHLx)M3oJ0e;Nd1UFvy@=Ae@k*3n!eAp9>(Apd1A{1YiRT!TeR* z0B>x<0)uv`cpQB$6q%oKDkz5mJ0W!S7iZHL(L)6v*!3V?AQmIAG5fV7S zR00}w0IdZiNWkvFL@cEkyxd7E0iojN!|w!Zu`q$R5D(g9$9_aGfdiqj;DB|dZk#X0 z4y{bV0Ti&H0e29uiz&?xVL-tH86aSRYwC`GE3&I@009Idq<}!wwjw|&hA6M9mIn>g zFhQv!WI%1!`FMcA10HZt!U!0M0KwQm6Cptc6qMkB0%0Qnfet$S9Jf9bj4%QP9*}@Q z25t|E&))|o7%5kUX|J9n}DX#fBK literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/cbrs.gif b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/cbrs.gif new file mode 100644 index 0000000000000000000000000000000000000000..0cd04d731b48cf16b9bbbe9a9e9a7d92c763fdea GIT binary patch literal 2353 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 literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/mldn.gif b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/mldn.gif new file mode 100644 index 0000000000000000000000000000000000000000..0902554010bd9f74e8de43d94076b1e12fab019a GIT binary patch literal 1110 zcmZ?wbhEHbRA-Q2`2L^a|NsAg{`~p;`SYhwpFV#4_~FBc_wV1oef##!n>R0Byme#VkhYueH zitOFHclYkyyLRo`xpU{1En7Bi+O&TC`gQBptzNx))v8r1SFT*KV#V_1%a<)%wsh&z zB}ln$WM^k*Wo2b%W@cn$q^GB+rKP2&rY0vRCnY5%BqYSf#>T|N zL`O$QMMXtMMn*(LgolTRhK7cOgaii%2L%NM1_t{3`}_L(`uO;`y1Kf!xHvmI+uGWi zn3!m4YRb#YOGrqFii+~{^Ru(Fv$3%;jDo=(0*XIb7#SEu7<53+1my_^j{gjr95NmS z3VhsbTv{namiTB@hD8-V-VNM3VD%mkWG};tV=|L;Q)hx?W7iol^20#u=|Mo zENV-2W!9Ixu;S3OUg6ook|qrw7`>$}ORvpfxb@+1hm_Bq3oC_}cL^-xi5FP7D9vxK zmG``uqq_pmN?3$UpW+bQ{KiQ&CE|ca;36-H?L2drZ2ZJ>_HLtI+=>I*7mf%?n6MpV zNMboDAg|A{onwOI0v?sfLmeN3o_6r=(Gp5n(4f>IX1pS>JkjXz+fH^tjWY>OYNfAq zo!F9n0~a#3O8ez3*L(bg?WUj{N5G1UOL_{fgx2i%%g}I`ODS&N?46&IcQNvcl(-lR zD(~T$B9rIoF+<_QaS?-UJ4G@dhPkk_F$I1IPW z%<0poPn|k-?AWmbV6bP;o}D{)Zr{Fr%a$#hH*emwY17)ZYgesWwQ}Xk6)RROTC`~X z{Q2|d&6_=Y_Vnq~r%jtSdGh3m6DRif_xJVnb#!#p*VmVqmzR~5<>lq&(=LKHZ}Vg$+qjsb^EC6N;POt^3^cV($t?Z(^pbWX5vCWQHJGyQbLTYePy|sH~7eL rdTn)6WaHSin}KJ)i=_Cf!_Ep^TTVEsu(6+Yl0SY{k(VjKk--`Oxo4Z+ literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/mrdn.gif b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/mrdn.gif new file mode 100644 index 0000000000000000000000000000000000000000..3b700779b1ea4d0ce46770e5cd26360c234ad47b GIT binary patch literal 708 zcmZ?wbhEHbRA-Q2c=n&+|NsBLfB*jc`SYhwpWeTJ|MuC>lAojSE=&z_w-cW&Rlean_Dn>TOX zv}x0Z4I9?4U%z(k+BIv|tX{o()v8r1SFT*KV#V_1%a<)%wq(hY#fuj&TC`~4!i5VK zESNuk{=9kf=FFKhd-m*Evu4elIdjI08PlgvpEhmUlqpjtPo6w+;=~CPCiM6B_xASo z^z?LhcXxDjw6(Q0Ha6DR*Von6)z;S5)YMd0S65Y4RaRD(mzS57m6eo~6c-m478d5^ z<>ln$WM^k*Wo2b%W@cn$q^GB+rKKe&CnqH(B_t%o#>U3P#6(9&M@2ZvU2I1B`gUM*&}0;bbPX|iQ;+|$BjA18(CXD9vD1w=@L$9Ir0UPxjh(sd|Vvn z=(73gYiROvv-z+k^RiaeaB|15WaiLMW%M#(3+3kMS~;D&V*Lj0e0CRB0Rf%hPWFww x)49A^xcNEFxaLSRxvB1LL>5uZ^gA5`Mm>3+hA5 zb9C8!^))nkx!Jtg5_wq}nOImuxVgAiF>~lAGpU(ygmbq!GYcd!Hk!Dk<+g7U5MXDj z;$U-UU_Zv?#lmeH?BdRuSaO1y=MV=MH;+xCKqun@=8!nHY7-Vq&xOpa4a|K;@5WjZq z+7))0OAKNM4jeeoAby%b^7!%N`x(Uc?%lhyrg8iB?b|A=w@#Y0vAcIY5HN_WTDW-C zq{+*HK;Lw6U;pCT`i0V(^XJc>-P|&pL2x<{Fz`>!&6_GHJGrZC5`$2Ge}7*|c^?CR zZ&g)$dwX+zU44ChWob#JyH{CRSwVhYE)du`7?~aK*>R`!F!sGq70eYcUKy zp!k!8k%2*uK?meCP@Fih|8J0MYSOiL^K^5u)@*L}_BPUx6y)JxW9O4Mv2im}oZqaf zsj93fCoLf%E-u|G=hAPiCMqCf;qGK=q`SMt#Z=SC!Nb-}OV8ZR+j8aFCe8kSPsb}R zj`rQnj@~Xh!YqtT+*)2f9@h5COXS?#bhVv*y<~Yg1eElgymh6em#JEpiE=rqoLnp` z9k#P5l?A|L-Hdi<2#(K$nj*Scq)&Swh B)P4W} literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/rbrs.gif b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/rbrs.gif new file mode 100644 index 0000000000000000000000000000000000000000..f48e799f44835e2de1e05fc549aa8d64b090641b GIT binary patch literal 492 zcmVcwRG5;7L{}31e00000 z00000A^8LW000L7EC2ui022Tc06+%+z=Kdo7zmDy!D2XcJT9Rf&UUkbbi0=7RACqt z!$N^U_JR=!rm*qIY;qpZZiOnz0)hd7*l0v#GA1f6J3B5cDjWuC1x_(AFc}#U5i>~z zLjw;L9Ue6_G&MUaC?+K(5CjYd4Gt6;C@Cr|IyN>qb0`#xU`c9UmbhA{Y@4zu5#1F&8;DH8nUqDRwdudaD8v79XB805>}< zE4GxNu)u%;0S_#USiz$q2wNWJaABZ8fsq*)Orc;Q1qTQoq$VU20<1v*86{4rSOG8v i31kTt2`Nxy0E8KmF1(O%0D&?IXa>Is@hNji5CA*9ETuaD literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/rbs.gif b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/rbs.gif new file mode 100644 index 0000000000000000000000000000000000000000..5c11fce032956ddba6fe33e36162f8971bb406b7 GIT binary patch literal 492 zcmZ?wbhEHb6lM@+xcZ;r|NsA*nwn8jQQ6tqEiElQJv~d8F5SC#@3CXY4jede?b@~H z|Nn2j_kZoyy;F|;uipAUs(6Z_QaE#R?j_Fws(&xx)FTmfj>E8;!)tF)a*L%` zwpaFAL$7KH%W46`Vh+u01_>{(sFt-`&pCM|GjM1gJ9cjE+O0q#OG_^f4n+nep!k!8 zk%57eK?fuT@{-27a)7i?pBKlU7S#G*qHCEy0<5QI_v$nQcG2LH#&O8PlF*yYZO`|Z0if<`Q zto*VT1uG0zA7U3UFfrNDxS)Z7_h8BrhQviKom_1R9XmF@ncl!)ZITqQkm)d|_(>6$ f*P#oZG4Sd{tPyBbJ3EUr^_;*9FXdui0S0RT7y_a@jixw@K zKY#w5Idf*snl)p_jOo*-Pn$Mv(xgcfCr+F&VM2d@e{XMZS65eOXJSx`tr{QOY$;`_W@BR!rd=~7?5$0$?PaZ$Il1OI%JAHhah7I0HOolr))Nh_d1j(d zZi(8wc=h`2JR`l=JX%KcofMxaIxeuN`Lg2SWA-+GLmieA3mR{z3n(!(Ftai@ng;4l un0YF|X_2NFZ^O(_3({Sdy?XZS z+0&;_A3b{X;K74CckW!fcJ0!oOBXL*yl~;d>C>lAo;-Qrz=31OjvYOE^vIDThYlS& zc<|u%?c29(*|KTVruFOBuU)%#_3G8DR;^mOa^;E@E0!%=wq(hY#fuj&TC`~4!i5VK zESNuk{+u~;X3d&4bLPw$GiFSmK7HD>X;Y?5nKWtA#EBCpOqkH$-{0HY+tbt2-QC^Q z)z#VA+0oI_*4Eb8*jQg*UsqRGTU%RGQ&U}CT~$?8Sy@?8QBhV_R$5wGQc_Y}TwGLC zR9IM;mzS5Do12r9lbxNNm6es5nVFH1k)EEOmX?;BoSc-Dl$e;9kdP1;7Z)2F8xs=~ z9UUDN6%`p784(c?9v&VR78V*B8WIu`6ciK~7#I)`;P3D6=jZ3?>FMU?=IZL|;^N}$ z?Cj*^WNT|{Vq&7HsVOfnFC`@ZFbW1`2q^w!VPs$sWzYe+4wNSt zIQ}zebI5pXSa7hJLs%>3#D;~3+Xa-p=6GyebhKN-IP1=djf;=>D>z5BY;;X&RnDBS zhC#BaYo@G(kj0M*mDAi@tSS*X3lt8wH|d{wF`q2U6Hfd03+fk*iSSPdN+ zIWICCm3TSz;}IT#mjQ83)*g&*W{2@Mf!6}W19o)<;TmqdNnCit?Ss1JV@g-8f literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/tm.gif b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/tm.gif new file mode 100644 index 0000000000000000000000000000000000000000..c69edc99e5c0aa15faf8ecc6096e61c8c8627461 GIT binary patch literal 577 zcmZ?wbhEHblw#0ec=n&+z<~n~9z3{z|NfmjcP?DGaPHi>)2C0LI(6#Ai4(_;9XopT z=#e8w_U+rZckkYvJ9qBbv18k|ZCkc%*}Qr4rcIkRY}l}V{rYw5)~#K;cGap?D_5>u zv0}yY<;#~XTefJ?qWSaZ&zUo4_Uzd+X3Us4abkaee_vl;Pft&KdwW}3TXS=Bb#--B zRaIG8SxHGrVPRojUS4i)Zca{4c6N4FR#sYCT54))TwGjiY;06iRAgjiczAeNSXfX{ zP(VO{kB^V1r>Co{tBZ>Z!%zl_KUo+-?$-ekpg3V*pVz?9)ZEhA*51+C)!ozE*FRz6 zq{&k{d|=WVjoQITL{WnO*UR+OESUszC4(@0B{jfs(^S$Xl&<$A50Tn}Y5 uINH_h=Q=FtC^l7@87uZ1wY9Z1H8s`M)m2qhm6erc zWo0ELCB?FH@{X{o8H$;rt{Nl6I_ z32||8v9Yl+F)`86(NR%Rk&%%R5fS0x;bCE6p`oE6AtAxR!9hVmfq{Yk{{FtczCJ!a zUS3|Fo}R9*t}ZSvwzjq=CMKGin)34U5)u-kqN4o#{Os)PY;0@{qhOGQfZ|UUMg|66 z1|5*QKzV|J<3EEmhm6OD1qYisgtcN$Y*=`>T|n7uPR9a=Rsjh$GY$p^2X;9xI}aD2 zjz%H3BWosFELhSg8UEzV@x7rURIq!FAa(|Aagr(Zyvr7v<(@)2fsu3K|fPA=i(G?Q2GIB_BI zaF25Egqn&842;cZOlC(7xn9P`+;v}dM4_=GDB6;2-FrUx3E6O|q>~%}q4~w`S%CQNbeDt`pOz;^Gf90pgoQ3UH^#uMqdV=eo^^|~$jD#~00+EC_y7O^ literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/tr.gif b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/tr.gif new file mode 100644 index 0000000000000000000000000000000000000000..42e601738f43e7d2f2881f6a168245c126188782 GIT binary patch literal 614 zcmZ?wbhEHbRAWlNSUS-g1hf&~la&!0bM&YW4ZX3dx}WBT;z zQ>RYt@9*#E=xA?mZ)j%uGy7Oh`zGkB^Uwi;IYe2n!1f z2ng`^_xJPjb8~ZZb#=9~vtt;_K=CIFBgp+aAOaL84D5#+7@C?}THD$?I=i}idi(k( zOq?`%%G7DoXUv>6d(PZ>^A}7KSlDW!CbXnUQo~qJWC@?7q@up6;7WooUgZghnor4k1-Ln->$jCa|&Y35q;1A;HO+M?lMjWx+xRmmoY#Wk$7Nrh!l@W#*c1>lMGU_tV|5n0L>G}oB#j- literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/trdn.gif b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/togglebutton/resources/images/trdn.gif new file mode 100644 index 0000000000000000000000000000000000000000..8f3c186f2ad5f9eb080d36282649aa6dd152ef32 GIT binary patch literal 708 zcmZ?wbhEHbRAC@Y{Z(qH7_3YWRr%#_gdi3bQg9rET-@kqP z_O)x*E?v5G>eQ(dCr%tadi2171BVVB0+Rdp@87p?-`>4@ckSA>bLY-&+qP}px^>Hz zEt@xQ-n41c`t|GAty{Nt?bC>lAn>KCAlqr)YO`14y;)Dqk`uqEPdV0FMyE{5M z+S}XP+S*!MTbrAko0^&$8XD^B>+9<3YHMq2YHF&htE;N2Dl03?%F0ShON)z(^YZd? zb91w^v$L|YGBY#N)6>(^(vp*tlai7W6B82>65`_GVq#*Vqobpuq9P+BBO)Tg!oosB zLqkGBf`fyDf`S4A1O5H|{rvoVeSLj=e7wEAy}Z2K+}vDUU0qyU?Ck7pZEa0VOf)q$ z<>lq2q@*MyB>4IH+1c6I*w`3|0E$0Z7#SD@7<52tL2<&s{=Y$>skx=Kt-YhOtGlPS zuYbbCNt35coi=^O%vrPN%$+xX!NM7S+Mi*tw;GatcdQ=cy_hc;*(D z$5f2UsElc;;@r)^0HSx&uKy3%BtVN8j z-I9wF4`@u>$k`@r7`>pau_^g7r()c)o<=Fv;}" + line + "<-"); + char linetype = line.charAt(0); + switch (linetype) { + case 'C': + catagory = new DefaultMutableTreeNode(line.substring(2)); + top.add(catagory); + break; + case 'A': + if (catagory != null) { + catagory.add(artist = new DefaultMutableTreeNode(line.substring(2))); + } + break; + case 'R': + if (artist != null) { + artist.add(record = new DefaultMutableTreeNode(line.substring(2))); + } + break; + case 'S': + if (record != null) { + record.add(new DefaultMutableTreeNode(line.substring(2))); + } + break; + default: + break; + } + line = reader.readLine(); + } + } catch (IOException ignored) { + } + + JTree tree = new JTree(top) { + @Override + public Insets getInsets() { + return new Insets(5, 5, 5, 5); + } + }; + + tree.setEditable(true); + + return tree; + } +} diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/tree/resources/TreeDemo.properties b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/tree/resources/TreeDemo.properties new file mode 100644 index 00000000000..77c6f82fc1e --- /dev/null +++ b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/tree/resources/TreeDemo.properties @@ -0,0 +1,8 @@ +### Tree Demo ### + +TreeDemo.accessible_description=This demo shows shows a sample usage of a JTree component. +TreeDemo.tooltip=JTree demo +TreeDemo.name=Tree Demo +TreeDemo.music=Music + + diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/tree/resources/images/TreeDemo.gif b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/tree/resources/images/TreeDemo.gif new file mode 100644 index 0000000000000000000000000000000000000000..0f72dca4edfa346fd873fb77cac74f173b657a6f GIT binary patch literal 257 zcmZ?wbhEHbRA5kG*v!NL1pk46;eT@D!L4VXR&~wXasFv|dCreate window + JWindow window = new JWindow(); + // + + //Add a border to the window + window.getRootPane().setBorder(new LineBorder(Color.BLACK, 1)); + // + + //Add window's content + JLabel label = new JLabel(I_HAVE_NO_SYSTEM_BORDER); + label.setHorizontalAlignment(JLabel.CENTER); + label.setPreferredSize(new Dimension(250, 200)); + window.add(label); + // + + //Initialize window's size + // which will shrink-to-fit its contents + window.pack(); + // + + return window; + } + + public void start() { + DemoUtilities.setToplevelLocation(window, windowSpaceholder, SwingConstants.CENTER); + showWindow(); + } + + public void stop() { + //Hide window + window.setVisible(false); + // + } + + public void showWindow() { + //Show window + // if window already visible, then bring to the front + if (window.isShowing()) { + window.toFront(); + } else { + window.setVisible(true); + } + // + } + + private class ShowActionListener implements ActionListener { + + @Override + public void actionPerformed(ActionEvent actionEvent) { + showWindow(); + } + } + + public static void main(String args[]) { + EventQueue.invokeLater(() -> { + JFrame frame = new JFrame(); + WindowDemo demo = new WindowDemo(); + frame.add(demo); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.pack(); + frame.setVisible(true); + demo.start(); + }); + } +} diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/window/resources/WindowDemo.html b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/window/resources/WindowDemo.html new file mode 100644 index 00000000000..e075e5d4505 --- /dev/null +++ b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/window/resources/WindowDemo.html @@ -0,0 +1,36 @@ + + + + + + + + + diff --git a/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/window/resources/images/WindowDemo.gif b/jdk/test/sanity/client/lib/SwingSet3/src/com/sun/swingset3/demos/window/resources/images/WindowDemo.gif new file mode 100644 index 0000000000000000000000000000000000000000..4eef882938b0713739fbb1d08079df0c0d58d323 GIT binary patch literal 284 zcmZ?wbhEHblwgoxIKlt||ABzv|L1SNUw!yl+cnc9ZT8OdPmkVsbM48es;-$8T{G{$ z{#w;F?c|NsAc_wHRvS&?S>h>{Rz2-( z)0vXo`zL}P*yJg79?`K7Vd~^rxZ$FQ(WHY%gr#c3e1qAZ>^y74ac}uMZw>p?5A%w^g^cw{>)WE w)1xAzWBnsS!vaHB2KhKR@VIVv-mGf7!({u;9b7&-N_vNMxqbHWDl%9D0MapUUH||9 literal 0 HcmV?d00001 diff --git a/jdk/test/sanity/client/lib/jemmy/README b/jdk/test/sanity/client/lib/jemmy/README new file mode 100644 index 00000000000..b3318ecfcb6 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/README @@ -0,0 +1,3 @@ +This src folder contains a copy of Jemmy 2 library sources from https://jemmy.java.net/. + +Do NOT modify files in it. \ No newline at end of file diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/Action.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/Action.java new file mode 100644 index 00000000000..1d5c359b533 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/Action.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy; + +/** + * + * Defines an action to be executed by {@code ActionProducer} instance. + * + * @see org.netbeans.jemmy.ActionProducer + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + */ +public interface Action { + + /** + * Executes this action. + * + * @param obj action argument. This argument might be the method parameter + * in an invocation of {@code ActionProducer.produceAction(Object)}. + * @return action result. + */ + public R launch(P obj); + + /** + * Returns the description value. + * + * @return Action description. + */ + public String getDescription(); +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/ActionProducer.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/ActionProducer.java new file mode 100644 index 00000000000..4af9d0d88c4 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/ActionProducer.java @@ -0,0 +1,335 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy; + +import java.util.Optional; + +/** + * + * Runs actions with or without waiting. + * + *

Timeouts used:
+ * ActionProducer.MaxActionTime - time action should be finished in.
+ * + * @see Action + * @see Timeouts + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + */ +public class ActionProducer extends Thread + implements Action, Waitable, P>, Timeoutable { + + private final static long ACTION_TIMEOUT = 10000; + + private Action action; + private boolean needWait = true; + private P parameter; + private boolean finished; + private R result = null; + private Timeouts timeouts; + private Waiter, P> waiter; + private TestOut output; + private Throwable exception; + + /** + * Creates a producer for an action. + * + * @param a Action implementation. + */ + public ActionProducer(Action a) { + super(); + waiter = new Waiter<>(this); + action = a; + setTimeouts(JemmyProperties.getProperties().getTimeouts()); + setOutput(JemmyProperties.getProperties().getOutput()); + finished = false; + exception = null; + } + + /** + * Creates a producer for an action. + * + * @param a Action implementation. + * @param nw Defines if {@code produceAction} method should wait for + * the end of action. + */ + public ActionProducer(Action a, boolean nw) { + super(); + waiter = new Waiter<>(this); + action = a; + needWait = nw; + setTimeouts(JemmyProperties.getProperties().getTimeouts()); + setOutput(JemmyProperties.getProperties().getOutput()); + finished = false; + exception = null; + } + + /** + * Creates a producer. {@code produceAction} must be overridden. + */ + protected ActionProducer() { + super(); + waiter = new Waiter<>(this); + setTimeouts(JemmyProperties.getProperties().getTimeouts()); + setOutput(JemmyProperties.getProperties().getOutput()); + finished = false; + exception = null; + } + + /** + * Creates a producer. {@code produceAction} must be overridden. + * + * @param nw Defines if {@code produceAction} method should wait for + * the end of action. + */ + protected ActionProducer(boolean nw) { + super(); + waiter = new Waiter<>(this); + needWait = nw; + setTimeouts(JemmyProperties.getProperties().getTimeouts()); + setOutput(JemmyProperties.getProperties().getOutput()); + finished = false; + exception = null; + } + + static { + Timeouts.initDefault("ActionProducer.MaxActionTime", ACTION_TIMEOUT); + } + + /** + * Set all the time outs used by sleeps or waits used by the launched + * action. + * + * @param ts An object containing timeout information. + * @see org.netbeans.jemmy.Timeouts + * @see org.netbeans.jemmy.Timeoutable + * @see #getTimeouts + */ + @Override + public void setTimeouts(Timeouts ts) { + timeouts = ts; + } + + /** + * Get all the time outs used by sleeps or waits used by the launched + * action. + * + * @return an object containing information about timeouts. + * @see org.netbeans.jemmy.Timeouts + * @see org.netbeans.jemmy.Timeoutable + * @see #setTimeouts + */ + @Override + public Timeouts getTimeouts() { + return timeouts; + } + + /** + * Identity of the streams or writers used for print output. + * + * @param out An object containing print output assignments for output and + * error streams. + * @see org.netbeans.jemmy.TestOut + * @see org.netbeans.jemmy.Outputable + */ + public void setOutput(TestOut out) { + output = out; + waiter.setOutput(output); + } + + /** + * Returns the exception value. + * + * @return a Throwable object representing the exception value + */ + public Throwable getException() { + return exception; + } + + /** + * Defines action priority in terms of thread priority. Increase (decrease) + * parameter value to Thread.MIN_PRIORITY(MAX_PRIORITY) in case if it is + * less(more) then it. + * + * @param newPriority New thread priority. + */ + public void setActionPriority(int newPriority) { + int priority; + if (newPriority < Thread.MIN_PRIORITY) { + priority = MIN_PRIORITY; + } else if (newPriority > Thread.MAX_PRIORITY) { + priority = MAX_PRIORITY; + } else { + priority = newPriority; + } + try { + setPriority(priority); + } catch (IllegalArgumentException | SecurityException e) { + e.printStackTrace(); + } + } + + /** + * Get the result of a launched action. + * + * @return a launched action's result. without waiting in case if + * {@code getFinished()} + * @see #getFinished() + */ + public R getResult() { + return result; + } + + /** + * Check if a launched action has finished. + * + * @return {@code true} if the launched action has completed, either + * normally or with an exception; {@code false} otherwise. + */ + public boolean getFinished() { + synchronized (this) { + return finished; + } + } + + /** + * Does nothing; the method should be overridden by inheritors. + * + * @param obj An object used to modify execution. This might be a + * {@code java.lang.String[]} that lists a test's command line + * arguments. + * @return An object - result of the action. + * @see org.netbeans.jemmy.Action + */ + @Override + public R launch(P obj) { + return null; + } + + /** + * @return this {@code ActionProducer}'s description. + * @see Action + */ + @Override + public String getDescription() { + if (action != null) { + return action.getDescription(); + } else { + return "Unknown action"; + } + } + + /** + * Starts execution. Uses ActionProducer.MaxActionTime timeout. + * + * @param obj Parameter to be passed into action's + * {@code launch(Object)} method. This parameter might be a + * {@code java.lang.String[]} that lists a test's command line + * arguments. + * @param actionTimeOrigin is used for timeout reporting, if non-null. + * @return {@code launch(Object)} result. + * @throws TimeoutExpiredException + * @exception InterruptedException + */ + public R produceAction(P obj, String actionTimeOrigin) throws InterruptedException { + parameter = obj; + synchronized (this) { + finished = false; + } + start(); + if (needWait) { + waiter.setTimeoutsToCloneOf(timeouts, "ActionProducer.MaxActionTime", actionTimeOrigin); + try { + waiter.waitAction(null); + } catch (TimeoutExpiredException e) { + output.printError("Timeout for \"" + getDescription() + + "\" action has been expired. Thread has been interrupted."); + interrupt(); + throw (e); + } + } + return result; + } + + /** + * Launch an action in a separate thread of execution. When the action + * finishes, record that fact. If the action finishes normally, store it's + * result. Use {@code getFinished()} and {@code getResult} to + * answer questions about test completion and return value, respectively. + * + * @see #getFinished() + * @see #getResult() + * @see java.lang.Runnable + */ + @Override + public final void run() { + result = null; + try { + result = launchAction(parameter); + } catch (Throwable e) { + exception = e; + } + synchronized (this) { + finished = true; + } + } + + /** + * Inquire for a reference to the object returned by a launched action. + * + * @param obj Not used. + * @return the result returned when a launched action finishes normally. + * @see org.netbeans.jemmy.Waitable + */ + @Override + public final Optional actionProduced(P obj) { + synchronized (this) { + if (finished) { + return Optional.ofNullable(result); + } else { + return null; + } + } + } + + /** + * Launch some action. Pass the action parameters and get it's return value, + * too. + * + * @param obj Parameter used to configure the execution of whatever this + * {@code ActionProducer} puts into execution. + * @return the return value of the action. + */ + private R launchAction(P obj) { + if (action != null) { + return action.launch(obj); + } else { + return launch(obj); + } + } + + @Override + public String toString() { + return "ActionProducer{" + "action=" + action + ", needWait=" + needWait + ", parameter=" + parameter + ", finished=" + finished + ", result=" + result + ", exception=" + exception + '}'; + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/Bundle.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/Bundle.java new file mode 100644 index 00000000000..19a15024c92 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/Bundle.java @@ -0,0 +1,147 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy; + +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintStream; +import java.io.PrintWriter; +import java.util.Enumeration; +import java.util.Properties; +import java.util.jar.JarFile; +import java.util.zip.ZipException; +import java.util.zip.ZipFile; + +/** + * + * Load string resources from file. Resources should be stored in + * {@code name=value} format. + * + * @see org.netbeans.jemmy.BundleManager + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + */ +public class Bundle extends Object { + + private Properties resources; + + /** + * Bunble constructor. + */ + public Bundle() { + resources = new Properties(); + } + + /** + * Loads resources from an input stream. + * + * @param stream Stream to load resources from. + * @exception IOException + */ + public void load(InputStream stream) + throws IOException { + resources.load(stream); + } + + /** + * Loads resources from a simple file. + * + * @param fileName Name of the file to load resources from. + * @exception IOException + * @exception FileNotFoundException + */ + public void loadFromFile(String fileName) + throws IOException, FileNotFoundException { + try (FileInputStream fileInputStream = new FileInputStream(fileName)) { + load(fileInputStream); + } + } + + /** + * Loads resources from a file in a jar archive. + * + * @param fileName Name of the jar archive. + * @param entryName ?enryName? Name of the file to load resources from. + * @exception IOException + * @exception FileNotFoundException + */ + public void loadFromJar(String fileName, String entryName) + throws IOException, FileNotFoundException { + try (JarFile jFile = new JarFile(fileName); + InputStream inputStream = jFile.getInputStream(jFile.getEntry(entryName))) { + load(inputStream); + } + } + + /** + * Loads resources from a file in a zip archive. + * + * @param fileName Name of the zip archive. + * @param entryName ?enryName? Name of the file to load resources from. + * @exception ZipException + * @exception IOException + * @exception FileNotFoundException + */ + public void loadFromZip(String fileName, String entryName) + throws IOException, FileNotFoundException, ZipException { + try (ZipFile zFile = new ZipFile(fileName); + InputStream inputStream = zFile.getInputStream(zFile.getEntry(entryName))) { + load(inputStream); + } + } + + /** + * Prints bundle contents. + * + * @param writer Writer to print data in. + */ + public void print(PrintWriter writer) { + Enumeration keys = resources.keys(); + while (keys.hasMoreElements()) { + String key = (String) keys.nextElement(); + writer.println(key + "=" + getResource(key)); + } + } + + /** + * Prints bundle contents. + * + * @param stream Stream to print data in. + */ + public void print(PrintStream stream) { + print(new PrintWriter(stream)); + } + + /** + * Gets resource by key. + * + * @param key Resource key + * @return Resource value or null if resource was not found. + */ + public String getResource(String key) { + return resources.getProperty(key); + } + +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/BundleManager.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/BundleManager.java new file mode 100644 index 00000000000..308f5b36ddb --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/BundleManager.java @@ -0,0 +1,292 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintStream; +import java.io.PrintWriter; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.zip.ZipException; + +/** + * + * Provides functionality to work with a bunch of resource files.
+ * + * @see org.netbeans.jemmy.Bundle + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + */ +public class BundleManager { + + private Hashtable bundles; + + /** + * Bundle manager constructor. + */ + public BundleManager() { + bundles = new Hashtable<>(); + try { + load(); + } catch (IOException ignored) { + } + } + + /** + * Adds a Bundle to the managed collection of resource files. + * + * @param bundle Bundle object + * @param ID Symbolic bundle id + * @return First parameter or null if bundle with ID already exists. + * @see org.netbeans.jemmy.Bundle + */ + public Bundle addBundle(Bundle bundle, String ID) { + if (getBundle(ID) != null) { + return null; + } else { + bundles.put(ID, bundle); + return bundle; + } + } + + /** + * Removes a Bundle from the managed collection of resource files. + * + * @param ID Symbolic bundle id + * @return Removed bundle or null if no bundle ID is. + */ + public Bundle removeBundle(String ID) { + Bundle value = getBundle(ID); + bundles.remove(ID); + return value; + } + + /** + * Returns a Bundle given it's symbolic ID. + * + * @param ID Symbolic bundle ID + * @return the Bundle. A null reference is returned if no bundle with the + * symbolic ID was found. + */ + public Bundle getBundle(String ID) { + return bundles.get(ID); + } + + /** + * Create a new Bundle, load resources from a simple text file, and add the + * bundle. Load resources from a text file to a new Bundle object. The new + * Bundle is added to the collection of objects managed by this + * {@code BundleManager}. + * + * @param fileName Name of a file to load resources from. + * @param ID Symbolic bundle ID used to identify the new bundle used to + * manage the resources from the file. + * @return a newly created bundle. + * @exception IOException + * @exception FileNotFoundException + */ + public Bundle loadBundleFromFile(String fileName, String ID) + throws IOException, FileNotFoundException { + if (getBundle(ID) != null) { + return null; + } + Bundle bundle = new Bundle(); + bundle.loadFromFile(fileName); + return addBundle(bundle, ID); + } + + public Bundle loadBundleFromStream(InputStream stream, String ID) + throws IOException, FileNotFoundException { + if (getBundle(ID) != null) { + return null; + } + Bundle bundle = new Bundle(); + bundle.load(stream); + return addBundle(bundle, ID); + } + + public Bundle loadBundleFromResource(ClassLoader cl, String resource, String ID) + throws IOException, FileNotFoundException { + return loadBundleFromStream(cl.getResourceAsStream(resource), ID); + } + + /** + * Loads resources from simple text file pointed by jemmy.resources system + * property. The resources are loaded into the Bundle with ID "". Does not + * do anything if jemmy.resources has not been set or is empty. + * + * @return a newly created bundle. + * @exception IOException + * @exception FileNotFoundException + */ + public Bundle load() + throws IOException, FileNotFoundException { + if (System.getProperty("jemmy.resources") != null + && !System.getProperty("jemmy.resources").equals("")) { + return loadBundleFromFile(System.getProperty("jemmy.resources"), ""); + } + return null; + } + + /** + * Loads resources from file in jar archive into new Bundle object and adds + * it. + * + * @param fileName Name of jar file. + * @param entryName ?enryName? Name of file to load resources from. + * @param ID Symbolic bundle id + * @return a newly created bundle. + * @exception IOException + * @exception FileNotFoundException + */ + public Bundle loadBundleFromJar(String fileName, String entryName, String ID) + throws IOException, FileNotFoundException { + if (getBundle(ID) != null) { + return null; + } + Bundle bundle = new Bundle(); + bundle.loadFromJar(fileName, entryName); + return addBundle(bundle, ID); + } + + /** + * Loads resources from file in zip archive into new Bundle object and adds + * it. + * + * @param fileName Name of jar file. + * @param entryName ?enryName? Name of file to load resources from. + * @param ID Symbolic bundle id + * @return a newly created bundle. + * @exception ZipException + * @exception IOException + * @exception FileNotFoundException + */ + public Bundle loadBundleFromZip(String fileName, String entryName, String ID) + throws IOException, FileNotFoundException, ZipException { + if (getBundle(ID) != null) { + return null; + } + Bundle bundle = new Bundle(); + bundle.loadFromZip(fileName, entryName); + return addBundle(bundle, ID); + } + + /** + * Prints bundles contents. + * + * @param writer Writer to print data in. + */ + public void print(PrintWriter writer) { + Enumeration keys = bundles.keys(); + Bundle bundle; + String key; + while (keys.hasMoreElements()) { + key = keys.nextElement(); + writer.println("\"" + key + "\" bundle contents"); + bundle = getBundle(key); + bundle.print(writer); + } + } + + /** + * Prints bundles contents. + * + * @param stream Stream to print data in. + */ + public void print(PrintStream stream) { + print(new PrintWriter(stream)); + } + + /** + * Returns resource from ID bundle. + * + * @param bundleID Bundle ID. + * @param key Resource key. + * @return the resource value. If the bundle ID does not exist if the + * resource with the given key cannot be found, a null reference is + * returned. + */ + public String getResource(String bundleID, String key) { + Bundle bundle = getBundle(bundleID); + if (bundle != null) { + return bundle.getResource(key); + } + return null; + } + + /** + * Searches for a resource in all the managed Bundles. + * + * @param key Resource key. + * @return first resource value found that is indexed by the given key. If + * no resource is found, return a null reference. + */ + public String getResource(String key) { + Enumeration data = bundles.elements(); + String value; + while (data.hasMoreElements()) { + value = data.nextElement().getResource(key); + if (value != null) { + return value; + } + } + return null; + } + + /** + * Counts the number of resource occurences in all the managed Bundles. + * + * @param key Resource key + * @return the number of resource occurences with the given key among all + * the Bundles managed by this BundleManager. + */ + public int calculateResources(String key) { + Enumeration data = bundles.elements(); + int count = 0; + while (data.hasMoreElements()) { + if (data.nextElement().getResource(key) != null) { + count++; + } + } + return count; + } + + /** + * Creates a shallow copy of this BundleManager. Does not copy bundles, only + * their references. + * + * @return a copy of this BundleManager. + */ + public BundleManager cloneThis() { + BundleManager result = new BundleManager(); + Enumeration keys = bundles.keys(); + Enumeration elements = bundles.elements(); + while (keys.hasMoreElements()) { + result.bundles.put(keys.nextElement(), + elements.nextElement()); + } + return result; + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/CharBindingMap.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/CharBindingMap.java new file mode 100644 index 00000000000..6b6c51ef93b --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/CharBindingMap.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy; + +/** + * + * Defines char-to-key binding. The generation of a symbol will, in general, + * require modifier keys to be pressed prior to pressing a primary key. Classes + * that implement {@code CharBindingMap} communicate what modifiers and + * primary key are required to generate a given symbol. + * + * @see org.netbeans.jemmy.DefaultCharBindingMap + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + */ +public interface CharBindingMap { + + /** + * Returns the code of the primary key used to type a symbol. + * + * @param c Symbol code. + * @return a key code. + * @see java.awt.event.InputEvent + */ + public int getCharKey(char c); + + /** + * Returns the modifiers that should be pressed to type a symbol. + * + * @param c Symbol code. + * @return a combination of InputEvent MASK fields. + * @see java.awt.event.InputEvent + */ + public int getCharModifiers(char c); +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/ClassReference.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/ClassReference.java new file mode 100644 index 00000000000..5347deb692c --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/ClassReference.java @@ -0,0 +1,227 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +/** + * + * Allows access to classes by reflection. + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + */ +public class ClassReference { + + private Class cl; + private Object instance; + + /** + * Constructor. + * + * @param o Object to work with. + */ + public ClassReference(Object o) { + super(); + instance = o; + cl = o.getClass(); + } + + /** + * Contructor. The object created by this constructor can be used to access + * static methods and fields only. + * + * @param className name of class + * @exception ClassNotFoundException + */ + public ClassReference(String className) + throws ClassNotFoundException { + super(); + cl = Class.forName(className); + instance = null; + } + + /** + * Executes class's {@code main(java.lang.String[])} method with a + * zero-length {@code java.lang.String} array as a parameter. + * + * @exception NoSuchMethodException + * @exception InvocationTargetException + */ + public void startApplication() + throws InvocationTargetException, NoSuchMethodException { + String[] params = new String[0]; + startApplication(params); + } + + /** + * Executes class's {@code main(java.lang.String[])} method. + * + * @param params The {@code java.lang.String} array to pass to + * {@code main(java.lang.String[])}. + * @exception NoSuchMethodException + * @exception InvocationTargetException + */ + public void startApplication(String[] params) + throws InvocationTargetException, NoSuchMethodException { + String[] real_params; + if (params == null) { + real_params = new String[0]; + } else { + real_params = params; + } + String[][] methodParams = {real_params}; + Class[] classes = {real_params.getClass()}; + try { + invokeMethod("main", methodParams, classes); + } catch (IllegalAccessException | IllegalStateException e) { + e.printStackTrace(); + } + } + + /** + * Locates method by name and parameter types and executes it. + * + * @param method_name Name of method. + * @param params Method parameters. + * @param params_classes Method parameters types. + * @return the return value from an invocation of the Method.
+ * If {@code method_name} method is void, {@code null} is + * returned.
+ * If {@code method_name} method returns a primitive type, then return + * wrapper class instance. + * @throws InvocationTargetException when the invoked method throws an + * exception. + * @throws NoSuchMethodException when the method cannot be found. + * @throws IllegalAccessException when access to the class or method is + * lacking. + * @throws SecurityException if access to the package or method is denied. + * @exception IllegalAccessException + * @exception NoSuchMethodException + * @exception InvocationTargetException + */ + public Object invokeMethod(String method_name, Object[] params, Class[] params_classes) + throws InvocationTargetException, NoSuchMethodException, IllegalAccessException { + if (params == null) { + params = new Object[0]; + } + if (params_classes == null) { + params_classes = new Class[0]; + } + Method method = cl.getMethod(method_name, + params_classes); + return method.invoke(instance, params); + } + + /** + * Locates constructor by parameter types and creates an instance. + * + * @param params An array of Method parameters. + * @param params_classes An array of Method parameter types. + * @return a new class instance. + * @throws InvocationTargetException when the invoked constructor throws an + * exception. + * @throws NoSuchMethodException when the constructor cannot be found. + * @throws IllegalAccessException when access to the class or constructor is + * lacking. + * @throws InstantiationException when the constructor is for an abstract + * class. + * @throws SecurityException if access to the package or constructor is + * denied. + * @exception IllegalAccessException + * @exception NoSuchMethodException + * @exception InstantiationException + * @exception InvocationTargetException + */ + public Object newInstance(Object[] params, Class[] params_classes) + throws InvocationTargetException, NoSuchMethodException, + IllegalAccessException, InstantiationException { + if (params == null) { + params = new Object[0]; + } + if (params_classes == null) { + params_classes = new Class[0]; + } + Constructor constructor = cl.getConstructor(params_classes); + return constructor.newInstance(params); + } + + /** + * Returns the field value. + * + * @param field_name The name of the field. + * @return the field value + * @see #setField + * @throws NoSuchFieldException when the field cannot be found. + * @throws IllegalAccessException when access to the class or constructor is + * lacking. + * @throws SecurityException if access to the package or field is denied. + * @exception IllegalAccessException + * @exception NoSuchFieldException + */ + public Object getField(String field_name) + throws NoSuchFieldException, IllegalAccessException { + return cl.getField(field_name).get(instance); + } + + /** + * Change a field's value. + * + * @param field_name The name of the field. + * @param newValue The fields new value. + * @see #getField + * @throws NoSuchFieldException when the field cannot be found. + * @throws IllegalAccessException when access to the class or constructor is + * lacking. + * @throws SecurityException if access to the package or field is denied. + * @exception IllegalAccessException + * @exception NoSuchFieldException + */ + public void setField(String field_name, Object newValue) + throws NoSuchFieldException, IllegalAccessException { + cl.getField(field_name).set(instance, newValue); + } + + /** + * Returns all superclasses. + * + * @return an array of superclasses, starting with the reference class and + * ending with {@code java.lang.Object}. + */ + public Class[] getClasses() { + Class cls = cl; + int count = 0; + do { + count++; + cls = cls.getSuperclass(); + } while (cls != null); + Class[] result = new Class[count]; + cls = cl; + for (int i = 0; i < count; i++) { + result[i] = cls; + cls = cls.getSuperclass(); + } + return result; + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/ComponentChooser.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/ComponentChooser.java new file mode 100644 index 00000000000..945900d6825 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/ComponentChooser.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy; + +import java.awt.Component; + +/** + * + * This interface should be implemented to define the criteria used to search + * for a component. + * + * @see org.netbeans.jemmy.ComponentSearcher + * @see org.netbeans.jemmy.WindowWaiter + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + */ +public interface ComponentChooser { + + /** + * Check if the component argument meets the search criteria. + * + * @param comp Component to check. + * @return {@code true} when the component conforms to the search + * criteria; {@code false} otherwise. + */ + public boolean checkComponent(Component comp); + + /** + * Returns searched component description. + * + * @return a String representing the description value + */ + public String getDescription(); +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/ComponentIsNotFocusedException.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/ComponentIsNotFocusedException.java new file mode 100644 index 00000000000..de6dc9a37dd --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/ComponentIsNotFocusedException.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy; + +import java.awt.Component; + +/** + * + * Exception can be thrown as a result of attempt to produce a key operation for + * a component which does not have focus. + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + */ +public class ComponentIsNotFocusedException extends JemmyInputException { + + private static final long serialVersionUID = 42L; + + /** + * Constructs a ComponentIsNotFocusedException object. + * + * @param comp a Component. + */ + public ComponentIsNotFocusedException(Component comp) { + super("Component do not have focus", comp); + } + +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/ComponentIsNotVisibleException.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/ComponentIsNotVisibleException.java new file mode 100644 index 00000000000..e4212c3cdd1 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/ComponentIsNotVisibleException.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy; + +import java.awt.Component; + +/** + * + * Exception can be thrown as a result of attempt to produce a mouse operation + * for a component which is not visible. + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + */ +public class ComponentIsNotVisibleException extends JemmyInputException { + + private static final long serialVersionUID = 42L; + + /** + * Constructs a ComponentIsNotVisibleException object. + * + * @param comp a Component. + */ + public ComponentIsNotVisibleException(Component comp) { + super("Component is not visible", comp); + } + +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/ComponentSearcher.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/ComponentSearcher.java new file mode 100644 index 00000000000..8c7b1ad2af9 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/ComponentSearcher.java @@ -0,0 +1,255 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy; + +import java.awt.Component; +import java.awt.Container; +import java.util.Vector; + +/** + * + * Contains methods to search for components below a a given + * {@code java.awt.Container} in the display containment hierarchy. Uses a + * {@code ComponentChooser} interface implementation to find a component. + * + * @see ComponentChooser + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + */ +public class ComponentSearcher implements Outputable { + + private int ordinalIndex; + private Container container; + private TestOut out; + private QueueTool queueTool; + private String containerToString; + + /** + * Contructor. The search is constrained so that only components that lie + * below the given container in the containment hierarchy are considered. + * + * @param c Container to find components in. + */ + public ComponentSearcher(Container c) { + super(); + container = c; + setOutput(JemmyProperties.getProperties().getOutput()); + queueTool = new QueueTool(); + } + + /** + * Creates {@code ComponentChooser} implementation whose + * {@code checkComponent(Component)} method returns {@code true} + * for any component. + * + * @param description Component description. + * @return ComponentChooser instance. + */ + public static ComponentChooser getTrueChooser(String description) { + class TrueChooser implements ComponentChooser { + + private String description; + + public TrueChooser(String desc) { + description = desc; + } + + @Override + public boolean checkComponent(Component comp) { + return true; + } + + @Override + public String getDescription() { + return description; + } + + @Override + public String toString() { + return "TrueChooser{" + "description=" + description + '}'; + } + } + return new TrueChooser(description); + } + + /** + * Defines print output streams or writers. + * + * @param output ?out? Identify the streams or writers used for print + * output. + * @see org.netbeans.jemmy.TestOut + * @see org.netbeans.jemmy.Outputable + * @see #getOutput + */ + @Override + public void setOutput(TestOut output) { + out = output; + } + + /** + * Returns print output streams or writers. + * + * @return an object that contains references to objects for printing to + * output and err streams. + * @see org.netbeans.jemmy.TestOut + * @see org.netbeans.jemmy.Outputable + * @see #setOutput + */ + @Override + public TestOut getOutput() { + return out; + } + + /** + * Returns container.toString(). It is called in dispatch thread. + * + * @return container.toString() + */ + private String containerToString() { + if (containerToString == null) { + containerToString = container == null ? "null" + : queueTool.invokeSmoothly( + new QueueTool.QueueAction("container.toString()") { + @Override + public String launch() { + return container.toString(); + } + } + ); + } + return containerToString; + } + + /** + * Searches for a component. The search for the component proceeds + * recursively in the component hierarchy rooted in this + * {@code ComponentChooser}'s container. + * + * @param chooser ComponentChooser instance, defining and applying the + * search criteria. + * @param index Ordinal component index. Indices start at 0. + * @return the {@code index}'th component from among those components + * for which the chooser's {@code checkComponent(Component)} method + * returns {@code true}. A {@code null} reference is returned if + * there are fewer than {@code index-1} components meeting the search + * criteria exist in the component hierarchy rooted in this + * {@code ComponentChooser}'s container. + */ + public Component findComponent(ComponentChooser chooser, int index) { + ordinalIndex = 0; + final Component result = findComponentInContainer(container, chooser, index, null); + if (result != null) { + // get result.toString() - run in dispatch thread + String resultToString = queueTool.invokeSmoothly( + new QueueTool.QueueAction("result.toString()") { + @Override + public String launch() { + return result.toString(); + } + } + ); + out.printTrace("Component " + chooser.getDescription() + + "\n was found in container " + containerToString() + + "\n " + resultToString); + out.printGolden("Component \"" + chooser.getDescription() + "\" was found"); + } else { + out.printTrace("Component " + chooser.getDescription() + + "\n was not found in container " + containerToString()); + out.printGolden("Component \"" + chooser.getDescription() + "\" was not found"); + } + return result; + } + + /** + * Searches for a component. The search for the component proceeds + * recursively in the component hierarchy rooted in this + * {@code ComponentChooser}'s container. + * + * @param chooser ComponentChooser instance, defining and applying the + * search criteria. + * @return the first component for which the chooser's + * {@code checkComponent(Component)} method returns {@code true}. + * A {@code null} reference is returned if no component meeting the + * search criteria exist in the component hierarchy rooted in this + * {@code ComponentChooser}'s container. + */ + public Component findComponent(ComponentChooser chooser) { + return findComponent(chooser, 0); + } + + /** + * Searches for all components. The search for the components proceeds + * recursively in the component hierarchy rooted in this + * {@code ComponentChooser}'s container. + * + * @param chooser ComponentChooser instance, defining and applying the + * search criteria. + * @return the components for which the chooser's + * {@code checkComponent(Component)} method returns {@code true}. + * An empty array is returned if no component meeting the search criteria + * exists in the component hierarchy rooted in this + * {@code ComponentChooser}'s container. + */ + public Component[] findComponents(ComponentChooser chooser) { + Vector allSeen = new Vector<>(); + findComponentInContainer(container, chooser, 0, allSeen); + Component[] result = new Component[allSeen.size()]; + for (int i = 0, n = allSeen.size(); i < n; i++) { + result[i] = allSeen.get(i); + } + return result; + } + + private Component findComponentInContainer(final Container cont, final ComponentChooser chooser, final int index, + final Vector allSeen) { + return queueTool.invokeSmoothly(new QueueTool.QueueAction("findComponentInContainer with " + chooser.getDescription()) { + + @Override + public Component launch() throws Exception { + Component[] components = cont.getComponents(); + Component target; + for (Component component : components) { + if (component != null) { + if (chooser.checkComponent(component)) { + if (allSeen != null) { + allSeen.add(component); + } else if (ordinalIndex == index) { + return component; + } else { + ordinalIndex++; + } + } + if (component instanceof Container) { + if ((target = findComponentInContainer((Container) component, + chooser, index, allSeen)) != null) { + return target; + } + } + } + } + return null; + } + }); + } + +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/DefaultCharBindingMap.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/DefaultCharBindingMap.java new file mode 100644 index 00000000000..36c9ca49880 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/DefaultCharBindingMap.java @@ -0,0 +1,201 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy; + +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.Enumeration; +import java.util.Hashtable; + +/** + * + * Default implementation of CharBindingMap interface. Provides a mapping for + * the following symbols:
+ * + * @see org.netbeans.jemmy.CharBindingMap + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + */ +public class DefaultCharBindingMap implements CharBindingMap { + + private Hashtable chars; + + /** + * Constructor. + */ + public DefaultCharBindingMap() { + initMap(); + } + + /** + * Returns the code of the primary key used to type a symbol. + * + * @param c Symbol code. + * @return a key code. + * @see CharBindingMap#getCharKey(char) + * @see java.awt.event.InputEvent + */ + @Override + public int getCharKey(char c) { + return getKeyAndModifiers(c)[0]; + } + + /** + * Returns the modifiers that should be pressed to type a symbol. + * + * @param c Symbol code. + * @return a combination of InputEvent MASK fields. + * @see CharBindingMap#getCharModifiers(char) + * @see java.awt.event.InputEvent + */ + @Override + public int getCharModifiers(char c) { + return getKeyAndModifiers(c)[1]; + } + + /** + * Returns key + modifiers pair. + * + * @param c Symbol code. + * @return an array of two elements: key code and modifiers mask - a + * combination of InputEvent MASK fields. + */ + public int[] getKeyAndModifiers(char c) { + CharKey key = chars.get(c); + if (key != null) { + return new int[]{key.key, key.modifiers}; + } else { + return new int[]{KeyEvent.VK_UNDEFINED, 0}; + } + } + + /** + * Returns an array of all supported chars. + * + * @return an array of chars representing the supported chars values. + */ + public char[] getSupportedChars() { + char[] charArray = new char[chars.size()]; + Enumeration keys = chars.keys(); + int index = 0; + while (keys.hasMoreElements()) { + charArray[index] = keys.nextElement(); + } + return charArray; + } + + /** + * Removes a char from supported. + * + * @param c Symbol code. + */ + public void removeChar(char c) { + chars.remove(c); + } + + /** + * Adds a char to supported. + * + * @param c Symbol code. + * @param key key code. + * @param modifiers a combination of InputEvent MASK fields. + */ + public void addChar(char c, int key, int modifiers) { + chars.put(c, new CharKey(key, modifiers)); + } + + private void initMap() { + chars = new Hashtable<>(); + //first add latters and digits represented by KeyEvent.VK_. fields + Field[] fields = KeyEvent.class.getFields(); + for (Field field : fields) { + String name = field.getName(); + if ((field.getModifiers() & Modifier.PUBLIC) != 0 + && (field.getModifiers() & Modifier.STATIC) != 0 + && field.getType() == Integer.TYPE + && name.startsWith("VK_") + && name.length() == 4) { + String latter = name.substring(3, 4); + try { + int key = field.getInt(null); + addChar(latter.toLowerCase().charAt(0), key, 0); + if (!latter.toUpperCase().equals(latter.toLowerCase())) { + addChar(latter.toUpperCase().charAt(0), key, InputEvent.SHIFT_MASK); + } + } catch (IllegalAccessException e) { + throw new AssertionError("Never could happen!", e); + } + } + } + //add special simbols + addChar('\t', KeyEvent.VK_TAB, 0); + addChar(' ', KeyEvent.VK_SPACE, 0); + addChar('!', KeyEvent.VK_1, InputEvent.SHIFT_MASK); + addChar('"', KeyEvent.VK_QUOTE, InputEvent.SHIFT_MASK); + addChar('#', KeyEvent.VK_3, InputEvent.SHIFT_MASK); + addChar('$', KeyEvent.VK_4, InputEvent.SHIFT_MASK); + addChar('%', KeyEvent.VK_5, InputEvent.SHIFT_MASK); + addChar('&', KeyEvent.VK_7, InputEvent.SHIFT_MASK); + addChar('\'', KeyEvent.VK_QUOTE, 0); + addChar('(', KeyEvent.VK_9, InputEvent.SHIFT_MASK); + addChar(')', KeyEvent.VK_0, InputEvent.SHIFT_MASK); + addChar('*', KeyEvent.VK_8, InputEvent.SHIFT_MASK); + addChar('+', KeyEvent.VK_EQUALS, InputEvent.SHIFT_MASK); + addChar(',', KeyEvent.VK_COMMA, 0); + addChar('-', KeyEvent.VK_MINUS, 0); + addChar('.', KeyEvent.VK_PERIOD, 0); + addChar('/', KeyEvent.VK_SLASH, 0); + addChar(':', KeyEvent.VK_SEMICOLON, InputEvent.SHIFT_MASK); + addChar(';', KeyEvent.VK_SEMICOLON, 0); + addChar('<', KeyEvent.VK_COMMA, InputEvent.SHIFT_MASK); + addChar('=', KeyEvent.VK_EQUALS, 0); + addChar('>', KeyEvent.VK_PERIOD, InputEvent.SHIFT_MASK); + addChar('?', KeyEvent.VK_SLASH, InputEvent.SHIFT_MASK); + addChar('@', KeyEvent.VK_2, InputEvent.SHIFT_MASK); + addChar('[', KeyEvent.VK_OPEN_BRACKET, 0); + addChar('\\', KeyEvent.VK_BACK_SLASH, 0); + addChar(']', KeyEvent.VK_CLOSE_BRACKET, 0); + addChar('^', KeyEvent.VK_6, InputEvent.SHIFT_MASK); + addChar('_', KeyEvent.VK_MINUS, InputEvent.SHIFT_MASK); + addChar('`', KeyEvent.VK_BACK_QUOTE, 0); + addChar('{', KeyEvent.VK_OPEN_BRACKET, InputEvent.SHIFT_MASK); + addChar('|', KeyEvent.VK_BACK_SLASH, InputEvent.SHIFT_MASK); + addChar('}', KeyEvent.VK_CLOSE_BRACKET, InputEvent.SHIFT_MASK); + addChar('~', KeyEvent.VK_BACK_QUOTE, InputEvent.SHIFT_MASK); + addChar('\n', KeyEvent.VK_ENTER, 0); + } + + private static class CharKey { + + public int key; + public int modifiers; + + public CharKey(int key, int modifiers) { + this.key = key; + this.modifiers = modifiers; + } + } + +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/DialogWaiter.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/DialogWaiter.java new file mode 100644 index 00000000000..2817d60decb --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/DialogWaiter.java @@ -0,0 +1,672 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy; + +import java.awt.Component; +import java.awt.Dialog; +import java.awt.Window; + +/** + * A DialogWaiter is a utility class used to look or wait for Dialogs. It + * contains methods to search for a Dialog among the currently showing Dialogs + * as well as methods that wait for a Dialog to show within an allotted time + * period. + * + * Searches and waits can either involve search criteria applied by a + * ComponentChooser instance or a search criteria based on the Dialog title. + * Searches and waits can both be restricted to dialogs owned by a given window. + * + *

Timeouts used:
+ * DialogWaiter.WaitDialogTimeout - time to wait dialog displayed
+ * DialogWaiter.AfterDialogTimeout - time to sleep after dialog has been + * displayed
+ * + * @see org.netbeans.jemmy.Timeouts + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + */ +public class DialogWaiter extends WindowWaiter implements Timeoutable, Outputable { + + private final static long WAIT_TIME = 60000; + private final static long AFTER_WAIT_TIME = 0; + + private Timeouts timeouts; + private TestOut output; + + /** + * Constructor. + */ + public DialogWaiter() { + super(); + setTimeouts(JemmyProperties.getProperties().getTimeouts()); + } + + /** + * Searches for a dialog. Search among the currently showing dialogs for one + * that meets the search criteria applied by the + * {@code ComponentChooser} parameter. + * + * @param cc A component chooser used to define and apply the search + * criteria. + * @return a reference to the first dialog that is showing and that meets + * the search criteria. If no such dialog can be found, a {@code null} + * reference is returned. + */ + public static Dialog getDialog(ComponentChooser cc) { + return (Dialog) WindowWaiter.getWindow(new DialogSubChooser(cc)); + } + + /** + * Searches for a dialog. The search proceeds among the currently showing + * dialogs for the {@code index+1}'th dialog that meets the criteria + * defined and applied by the {@code ComonentChooser} parameter. + * + * @param cc A component chooser used to define and apply the search + * criteria. + * @param index The ordinal index of the dialog in the set of currently + * displayed dialogs. The first index is 0. + * @return a reference to the {@code index+1}'th dialog that is showing + * and that meets the search criteria. If there are fewer than + * {@code index+1} dialogs, a {@code null} reference is returned. + */ + public static Dialog getDialog(ComponentChooser cc, int index) { + return (Dialog) WindowWaiter.getWindow(new DialogSubChooser(cc), index); + } + + /** + * Searches for a dialog by title. The search proceeds among the currently + * showing dialogs for the first with a suitable title. + * + * @param title Dialog title or subtitle. + * @param ce If {@code true} and the search is case sensitive, then a + * match occurs when the {@code title} argument is a substring of a + * dialog title. If {@code false} and the search is case sensitive, + * then the {@code title} argument and the dialog title must be the + * same. If {@code true} and the search is case insensitive, then a + * match occurs when the {@code title} argument is a substring of the + * dialog title after changing both to upper case. If {@code false} and + * the search is case insensitive, then a match occurs when the + * {@code title} argument is a substring of the dialog title after + * changing both to upper case. + * @param cc If {@code true} the search is case sensitive; otherwise, + * the search is case insensitive. + * @return a reference to the first dialog that is showing and that has a + * suitable title. If no such dialog can be found, a {@code null} + * reference is returned. + */ + public static Dialog getDialog(String title, boolean ce, boolean cc) { + return (Dialog) WindowWaiter.getWindow(new DialogByTitleChooser(title, ce, cc)); + } + + /** + * Searches for a dialog by title. The search is for the + * {@code index+1}'th dialog among the currently showing dialogs that + * possess a suitable title. + * + * @param title Dialog title or subtitle. + * @param ce If {@code true} and the search is case sensitive, then a + * match occurs when the {@code title} argument is a substring of a + * dialog title. If {@code false} and the search is case sensitive, + * then the {@code title} argument and the dialog title must be the + * same. If {@code true} and the search is case insensitive, then a + * match occurs when the {@code title} argument is a substring of the + * dialog title after changing both to upper case. If {@code false} and + * the search is case insensitive, then a match occurs when the + * {@code title} argument is a substring of the dialog title after + * changing both to upper case. + * @param cc If {@code true} the search is case sensitive; otherwise, + * the search is case insensitive. + * @param index Ordinal index between appropriate dialogs + * @return a reference to the {@code index+1}'th dialog that is showing + * and that has a suitable title. If there are fewer than + * {@code index+1} dialogs, a {@code null} reference is returned. + */ + public static Dialog getDialog(String title, boolean ce, boolean cc, int index) { + return getDialog(new DialogByTitleChooser(title, ce, cc), index); + } + + /** + * Searches for a dialog. Search among the currently showing dialogs for the + * first that is both owned by the {@code java.awt.Window} + * {@code owner} and that meets the search criteria applied by the + * {@code ComponentChooser} parameter. + * + * @param owner The owner window of the dialogs to be searched. + * @param cc A component chooser used to define and apply the search + * criteria. + * @return a reference to the first dialog that is showing, has a proper + * owner window, and that meets the search criteria. If no such dialog can + * be found, a {@code null} reference is returned. + */ + public static Dialog getDialog(Window owner, ComponentChooser cc) { + return (Dialog) WindowWaiter.getWindow(owner, new DialogSubChooser(cc)); + } + + /** + * Searches for a dialog. The search proceeds among the currently showing + * dialogs for the {@code index+1}'th dialog that is both owned by the + * {@code java.awt.Window} {@code owner} and that meets the + * criteria defined and applied by the {@code ComponentChooser} + * parameter. + * + * @param owner The owner window of all the dialogs to be searched. + * @param cc A component chooser used to define and apply the search + * criteria. + * @param index Ordinal index between appropriate dialogs + * @return a reference to the {@code index+1}'th dialog that is + * showing, has the proper window ownership, and that meets the search + * criteria. If there are fewer than {@code index+1} dialogs, a + * {@code null} reference is returned. + */ + public static Dialog getDialog(Window owner, ComponentChooser cc, int index) { + return (Dialog) WindowWaiter.getWindow(owner, new DialogSubChooser(cc), index); + } + + /** + * Searches for a dialog by title. The search proceeds among the currently + * showing dialogs that are owned by the {@code java.awt.Window} + * {@code owner} for the first with a suitable title. + * + * @param owner A window - owner of a dialods to be checked. + * @param title Dialog title or subtitle. + * @param ce If {@code true} and the search is case sensitive, then a + * match occurs when the {@code title} argument is a substring of a + * dialog title. If {@code false} and the search is case sensitive, + * then the {@code title} argument and the dialog title must be the + * same. If {@code true} and the search is case insensitive, then a + * match occurs when the {@code title} argument is a substring of the + * dialog title after changing both to upper case. If {@code false} and + * the search is case insensitive, then a match occurs when the + * {@code title} argument is a substring of the dialog title after + * changing both to upper case. + * @param cc If {@code true} the search is case sensitive; otherwise, + * the search is case insensitive. + * @return a reference to the first dialog that is showing, has the proper + * window ownership, and a suitable title. If no such dialog can be found, a + * {@code null} reference is returned. + */ + public static Dialog getDialog(Window owner, String title, boolean ce, boolean cc) { + return (Dialog) WindowWaiter.getWindow(owner, new DialogByTitleChooser(title, ce, cc)); + } + + /** + * Searches for a dialog by title. The search is for the + * {@code index+1}'th dialog among the currently showing dialogs that + * are owned by the {@code java.awt.Window} {@code owner} and that + * have a suitable title. + * + * @param owner ?title? Dialog title or subtitle. + * @param title ?ce? If {@code true} and the search is case sensitive, + * then a match occurs when the {@code title} argument is a substring + * of a dialog title. If {@code false} and the search is case + * sensitive, then the {@code title} argument and the dialog title must + * be the same. If {@code true} and the search is case insensitive, + * then a match occurs when the {@code title} argument is a substring + * of the dialog title after changing both to upper case. If + * {@code false} and the search is case insensitive, then a match + * occurs when the {@code title} argument is a substring of the dialog + * title after changing both to upper case. + * @param ce ?cc? If {@code true} the search is case sensitive; + * otherwise, the search is case insensitive. + * @param cc ?index? The ordinal index of the dialog in the set of currently + * displayed dialogs with the proper window ownership and a suitable title. + * The first index is 0. + * @param index Ordinal index between appropriate dialogs + * @return a reference to the {@code index+1}'th dialog that is + * showing, has the proper window ownership, and a suitable title. If there + * are fewer than {@code index+1} dialogs, a {@code null} + * reference is returned. + */ + public static Dialog getDialog(Window owner, String title, boolean ce, boolean cc, int index) { + return getDialog(owner, new DialogByTitleChooser(title, ce, cc), index); + } + + static { + Timeouts.initDefault("DialogWaiter.WaitDialogTimeout", WAIT_TIME); + Timeouts.initDefault("DialogWaiter.AfterDialogTimeout", AFTER_WAIT_TIME); + } + + /** + * Defines current timeouts. + * + * @param timeouts ?t? A collection of timeout assignments. + * @see org.netbeans.jemmy.Timeouts + * @see org.netbeans.jemmy.Timeoutable + * @see #getTimeouts + */ + @Override + public void setTimeouts(Timeouts timeouts) { + this.timeouts = timeouts; + Timeouts times = timeouts.cloneThis(); + times.setTimeout("WindowWaiter.WaitWindowTimeout", + timeouts.getTimeout("DialogWaiter.WaitDialogTimeout")); + times.setTimeout("WindowWaiter.AfterWindowTimeout", + timeouts.getTimeout("DialogWaiter.AfterDialogTimeout")); + super.setTimeouts(times); + } + + /** + * Return current timeouts. + * + * @return the collection of current timeout assignments. + * @see org.netbeans.jemmy.Timeouts + * @see org.netbeans.jemmy.Timeoutable + * @see #setTimeouts + */ + @Override + public Timeouts getTimeouts() { + return timeouts; + } + + /** + * Defines print output streams or writers. + * + * @param output ?out? Identify the streams or writers used for print + * output. + * @see org.netbeans.jemmy.TestOut + * @see org.netbeans.jemmy.Outputable + * @see #getOutput + */ + @Override + public void setOutput(TestOut output) { + this.output = output; + super.setOutput(output); + } + + /** + * Returns print output streams or writers. + * + * @return an object that contains references to objects for printing to + * output and err streams. + * @see org.netbeans.jemmy.TestOut + * @see org.netbeans.jemmy.Outputable + * @see #setOutput + */ + @Override + public TestOut getOutput() { + return output; + } + + /** + * Waits for a dialog to show. Wait for the {@code index+1}'th dialog + * that meets the criteria defined and applied by the + * {@code ComonentChooser} parameter to show up. + * + * @param ch A component chooser used to define and apply the search + * criteria. + * @param index The ordinal index of the dialog in the set of currently + * displayed dialogs. The first index is 0. + * @return a reference to the {@code index+1}'th dialog that shows and + * that meets the search criteria. If fewer than {@code index+1} + * dialogs show up in the allotted time period then a {@code null} + * reference is returned. + * @throws TimeoutExpiredException + * @see org.netbeans.jemmy.WindowWaiter#actionProduced(Object) + * @exception InterruptedException + */ + public Dialog waitDialog(ComponentChooser ch, int index) + throws InterruptedException { + setTimeouts(timeouts); + return (Dialog) waitWindow(new DialogSubChooser(ch), index); + } + + /** + * Waits for a dialog to show. Wait for a dialog that meets the search + * criteria applied by the {@code ComponentChooser} parameter to show + * up. + * + * @param ch A component chooser used to define and apply the search + * criteria. + * @return a reference to the first dialog that shows and that meets the + * search criteria. If no such dialog can be found within the time period + * allotted, a {@code null} reference is returned. + * @throws TimeoutExpiredException + * @see org.netbeans.jemmy.WindowWaiter#actionProduced(Object) + * @exception InterruptedException + */ + public Dialog waitDialog(ComponentChooser ch) + throws InterruptedException { + return waitDialog(ch, 0); + } + + /** + * Waits for a dialog to show. Wait for the {@code index+1}'th dialog + * to show with a suitable title. + * + * @param title Dialog title or subtitle. + * @param compareExactly If {@code true} and the search is case + * sensitive, then a match occurs when the {@code title} argument is a + * substring of a dialog title. If {@code false} and the search is case + * sensitive, then the {@code title} argument and the dialog title must + * be the same. If {@code true} and the search is case insensitive, + * then a match occurs when the {@code title} argument is a substring + * of the dialog title after changing both to upper case. If + * {@code false} and the search is case insensitive, then a match + * occurs when the {@code title} argument is a substring of the dialog + * title after changing both to upper case. + * @param compareCaseSensitive If {@code true} the search is case + * sensitive; otherwise, the search is case insensitive. + * @param index The ordinal index of the dialog in the set of currently + * displayed dialogs with the proper window ownership and a suitable title. + * The first index is 0. + * @return a reference to the {@code index+1}'th dialog to show and + * that has a suitable title. If no such dialog can be found within the time + * period allotted, a {@code null} reference is returned. + * @throws TimeoutExpiredException + * @see org.netbeans.jemmy.WindowWaiter#actionProduced(Object) + * @exception InterruptedException + */ + public Dialog waitDialog(String title, boolean compareExactly, boolean compareCaseSensitive, int index) + throws InterruptedException { + return waitDialog(new DialogByTitleChooser(title, compareExactly, compareCaseSensitive), index); + } + + /** + * Waits for a dialog to show. Wait for the first dialog to show with a + * suitable title. + * + * @param title Dialog title or subtitle. + * @param compareExactly If {@code true} and the search is case + * sensitive, then a match occurs when the {@code title} argument is a + * substring of a dialog title. If {@code false} and the search is case + * sensitive, then the {@code title} argument and the dialog title must + * be the same. If {@code true} and the search is case insensitive, + * then a match occurs when the {@code title} argument is a substring + * of the dialog title after changing both to upper case. If + * {@code false} and the search is case insensitive, then a match + * occurs when the {@code title} argument is a substring of the dialog + * title after changing both to upper case. + * @param compareCaseSensitive If {@code true} the search is case + * sensitive; otherwise, the search is case insensitive. + * @return a reference to the first dialog to show and that has a suitable + * title. If no such dialog can be found within the time period allotted, a + * {@code null} reference is returned. + * @throws TimeoutExpiredException + * @see org.netbeans.jemmy.WindowWaiter#actionProduced(Object) + * @exception InterruptedException + */ + public Dialog waitDialog(String title, boolean compareExactly, boolean compareCaseSensitive) + throws InterruptedException { + return waitDialog(title, compareExactly, compareCaseSensitive, 0); + } + + /** + * Waits for a dialog to show. Wait for the {@code index+1}'th dialog + * to show that is both owned by the {@code java.awt.Window} + * {@code owner} and that meets the criteria defined and applied by the + * {@code ComponentChooser} parameter. + * + * @param owner The owner window of all the dialogs to be searched. + * @param ch A component chooser used to define and apply the search + * criteria. + * @param index The ordinal index of the dialog in the set of currently + * displayed dialogs with the proper window ownership and a suitable title. + * The first index is 0. + * @return a reference to the {@code index+1}'th dialog to show that + * has the proper window ownership, and that meets the search criteria. If + * there are fewer than {@code index+1} dialogs, a {@code null} + * reference is returned. + * @throws TimeoutExpiredException + * @see org.netbeans.jemmy.WindowWaiter#actionProduced(Object) + * @exception InterruptedException + */ + public Dialog waitDialog(Window owner, ComponentChooser ch, int index) + throws InterruptedException { + setTimeouts(timeouts); + return (Dialog) waitWindow(owner, new DialogSubChooser(ch), index); + } + + /** + * Waits for a dialog to show. Wait for the first dialog to show that is + * both owned by the {@code java.awt.Window} {@code owner} and + * that meets the criteria defined and applied by the + * {@code ComponentChooser} parameter. + * + * @param owner The owner window of all the dialogs to be searched. + * @param ch A component chooser used to define and apply the search + * criteria. + * @return a reference to the first dialog to show that has the proper + * window ownership, and that meets the search criteria. If there is no such + * dialog, a {@code null} reference is returned. + * @throws TimeoutExpiredException + * @see org.netbeans.jemmy.WindowWaiter#actionProduced(Object) + * @exception InterruptedException + */ + public Dialog waitDialog(Window owner, ComponentChooser ch) + throws InterruptedException { + return waitDialog(owner, ch, 0); + } + + /** + * Waits for a dialog to show. Wait for the {@code index+1}'th dialog + * to show with the proper owner and a suitable title. + * + * @param owner The owner window of all the dialogs to be searched. + * @param title Dialog title or subtitle. + * @param compareExactly If {@code true} and the search is case + * sensitive, then a match occurs when the {@code title} argument is a + * substring of a dialog title. If {@code false} and the search is case + * sensitive, then the {@code title} argument and the dialog title must + * be the same. If {@code true} and the search is case insensitive, + * then a match occurs when the {@code title} argument is a substring + * of the dialog title after changing both to upper case. If + * {@code false} and the search is case insensitive, then a match + * occurs when the {@code title} argument is a substring of the dialog + * title after changing both to upper case. + * @param compareCaseSensitive If {@code true} the search is case + * sensitive; otherwise, the search is case insensitive. + * @param index Ordinal index between appropriate dialogs + * @return a reference to the {@code index+1}'th dialog to show that + * has both the proper owner window and a suitable title. If no such dialog + * can be found within the time period allotted, a {@code null} + * reference is returned. + * @throws TimeoutExpiredException + * @see org.netbeans.jemmy.WindowWaiter#actionProduced(Object) + * @exception InterruptedException + */ + public Dialog waitDialog(Window owner, String title, boolean compareExactly, boolean compareCaseSensitive, int index) + throws InterruptedException { + return waitDialog(owner, new DialogByTitleChooser(title, compareExactly, compareCaseSensitive), index); + } + + /** + * Waits for a dialog to show. Wait for the first dialog to show with the + * proper owner and a suitable title. + * + * @see org.netbeans.jemmy.WindowWaiter#actionProduced(Object) + * @param owner The owner window of all the dialogs to be searched. + * @param title Dialog title or subtitle. + * @param compareExactly If {@code true} and the search is case + * sensitive, then a match occurs when the {@code title} argument is a + * substring of a dialog title. If {@code false} and the search is case + * sensitive, then the {@code title} argument and the dialog title must + * be the same. If {@code true} and the search is case insensitive, + * then a match occurs when the {@code title} argument is a substring + * of the dialog title after changing both to upper case. If + * {@code false} and the search is case insensitive, then a match + * occurs when the {@code title} argument is a substring of the dialog + * title after changing both to upper case. + * @param compareCaseSensitive If {@code true} the search is case + * sensitive; otherwise, the search is case insensitive. + * @return a reference to the first dialog to show and that has both the + * proper owner and a suitable title. If no such dialog can be found within + * the time period allotted, a {@code null} reference is returned. + * @throws TimeoutExpiredException + * @exception InterruptedException + */ + public Dialog waitDialog(Window owner, String title, boolean compareExactly, boolean compareCaseSensitive) + throws InterruptedException { + return waitDialog(owner, title, compareExactly, compareCaseSensitive, 0); + } + + /** + * @see org.netbeans.jemmy.Waiter#getWaitingStartedMessage() + */ + @Override + protected String getWaitingStartedMessage() { + return "Start to wait dialog \"" + getComponentChooser().getDescription() + "\" opened"; + } + + /** + * Overrides WindowWaiter.getTimeoutExpiredMessage. Returns the timeout + * expired message value. + * + * @see org.netbeans.jemmy.Waiter#getTimeoutExpiredMessage(long) + * @param spendedTime Time spent for waiting + * @return A message string. + */ + @Override + protected String getTimeoutExpiredMessage(long spendedTime) { + return ("Dialog \"" + getComponentChooser().getDescription() + "\" has not been opened in " + + spendedTime + " milliseconds"); + } + + /** + * Overrides WindowWaiter.getActionProducedMessage. Returns the action + * produced message value. + * + * @see org.netbeans.jemmy.Waiter#getActionProducedMessage(long, Object) + * @param spendedTime Time spent for waiting + * @param result A result of the action + * @return A message string. + */ + @Override + protected String getActionProducedMessage(long spendedTime, final Object result) { + String resultToString; + if (result instanceof Component) { + // run toString in dispatch thread + resultToString = new QueueTool().invokeSmoothly( + new QueueTool.QueueAction("result.toString()") { + @Override + public String launch() { + return result.toString(); + } + } + ); + } else { + resultToString = result.toString(); + } + return ("Dialog \"" + getComponentChooser().getDescription() + "\" has been opened in " + + spendedTime + " milliseconds" + + "\n " + resultToString); + } + + /** + * @see org.netbeans.jemmy.Waiter#getGoldenWaitingStartedMessage() + */ + @Override + protected String getGoldenWaitingStartedMessage() { + return "Start to wait dialog \"" + getComponentChooser().getDescription() + "\" opened"; + } + + /** + * @see org.netbeans.jemmy.Waiter#getGoldenTimeoutExpiredMessage() + */ + @Override + protected String getGoldenTimeoutExpiredMessage() { + return "Dialog \"" + getComponentChooser().getDescription() + "\" has not been opened"; + } + + /** + * @see org.netbeans.jemmy.Waiter#getGoldenActionProducedMessage() + */ + @Override + protected String getGoldenActionProducedMessage() { + return "Dialog \"" + getComponentChooser().getDescription() + "\" has been opened"; + } + + private static class DialogSubChooser implements ComponentChooser { + + private ComponentChooser chooser; + + public DialogSubChooser(ComponentChooser c) { + super(); + chooser = c; + } + + @Override + public boolean checkComponent(Component comp) { + if (comp instanceof Dialog) { + return ((FIND_INVISIBLE_WINDOWS || (comp.isShowing() && comp.isVisible())) + && chooser.checkComponent(comp)); + } else { + return false; + } + } + + @Override + public String getDescription() { + return chooser.getDescription(); + } + + @Override + public String toString() { + return "DialogSubChooser{" + "chooser=" + chooser + '}'; + } + } + + private static class DialogByTitleChooser implements ComponentChooser { + + String title; + boolean compareExactly; + boolean compareCaseSensitive; + + public DialogByTitleChooser(String t, boolean ce, boolean cc) { + super(); + title = t; + compareExactly = ce; + compareCaseSensitive = cc; + } + + @Override + public boolean checkComponent(Component comp) { + if (comp instanceof Dialog) { + if ((FIND_INVISIBLE_WINDOWS || (comp.isShowing() && comp.isVisible())) + && ((Dialog) comp).getTitle() != null) { + String titleToComp = ((Dialog) comp).getTitle(); + String contextToComp = title; + if (compareCaseSensitive) { + titleToComp = titleToComp.toUpperCase(); + contextToComp = contextToComp.toUpperCase(); + } + if (compareExactly) { + return titleToComp.equals(contextToComp); + } else { + return titleToComp.contains(contextToComp); + } + } + } + return false; + } + + @Override + public String getDescription() { + return title; + } + + @Override + public String toString() { + return "DialogByTitleChooser{" + "title=" + title + ", compareExactly=" + compareExactly + ", compareCaseSensitive=" + compareCaseSensitive + '}'; + } + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/EventDispatcher.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/EventDispatcher.java new file mode 100644 index 00000000000..65fef2552db --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/EventDispatcher.java @@ -0,0 +1,1134 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy; + +import java.awt.AWTEvent; +import java.awt.Component; +import java.awt.Toolkit; +import java.awt.Window; +import java.awt.event.AWTEventListener; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.awt.event.MouseEvent; +import java.awt.event.WindowEvent; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; + +/** + * Provides low level functions for reproducing user actions. One dispatch model + * uses the managed component's event queue to dispatch events. The other + * dispatch model uses {@code java.awt.Robot} to generate native events. It + * is an option in the Robot dispatch model to wait for the managed component's + * event queue to empty before dispatching events. + * + * Timeouts used:
+ * EventDispatcher.WaitQueueEmptyTimeout - to wait event queue empty.
+ * EventDispatcher.RobotAutoDelay - param for java.awt.Robot.setAutoDelay + * method.
+ * EventDispatcher.WaitComponentUnderMouseTimeout - time to wait component under + * mouse.
+ * + * @see org.netbeans.jemmy.Timeouts + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + * + */ +public class EventDispatcher implements Outputable, Timeoutable { + + private static final long WAIT_QUEUE_EMPTY_TIMEOUT = 180000; + private static final long ROBOT_AUTO_DELAY = 10; + private static final long WAIT_COMPONENT_UNDER_MOUSE_TIMEOUT = 60000; + + private static Field[] keyFields; + private static volatile MotionListener motionListener = null; + + /** + * Component to dispatch events to. + */ + protected Component component; + private TestOut output; + private Timeouts timeouts; + private final ClassReference reference; + private int model; + private ClassReference robotReference = null; + private boolean outsider = false; + private final QueueTool queueTool; + + /** + * Constructor. + * + * @param comp Component to operate with. + */ + public EventDispatcher(Component comp) { + super(); + component = comp; + reference = new ClassReference(comp); + queueTool = new QueueTool(); + setOutput(JemmyProperties.getProperties().getOutput()); + setTimeouts(JemmyProperties.getProperties().getTimeouts()); + setDispatchingModel(JemmyProperties.getProperties().getDispatchingModel()); + } + + /** + * Waits for the managed component's {@code java.awt.EventQueue} to + * empty. The timeout for this wait is + * EventDispatcher.WaitQueueEmptyTimeout. + * + * @param output Output to print exception into. + * @param timeouts A collection of timeout assignments. + * @throws TimeoutExpiredException + * @see org.netbeans.jemmy.QueueTool + */ + public static void waitQueueEmpty(TestOut output, Timeouts timeouts) { + QueueTool qt = new QueueTool(); + qt.setTimeouts(timeouts.cloneThis()); + qt.getTimeouts(). + setTimeout("QueueTool.WaitQueueEmptyTimeout", + JemmyProperties. + getCurrentTimeout("EventDispatcher.WaitQueueEmptyTimeout")); + qt.setOutput(output); + qt.waitEmpty(); + } + + /** + * Waits for the managed component's {@code java.awt.EventQueue} to + * empty. Uses default output and timeouts. The timeout for this wait is + * EventDispatcher.WaitQueueEmptyTimeout. + * + * @see QueueTool + * @throws TimeoutExpiredException + */ + public static void waitQueueEmpty() { + waitQueueEmpty(JemmyProperties.getCurrentOutput(), + JemmyProperties.getCurrentTimeouts()); + } + + /** + * Waits for the managed component's {@code java.awt.EventQueue} to + * stay empty. The timeout for this wait is + * EventDispatcher.WaitQueueEmptyTimeout. + * + * @param emptyTime The time that the event queue has to stay empty to avoid + * a TimeoutExpiredException. + * @param output Output to print exception into + * @param timeouts A collection of timeout assignments. + * @throws TimeoutExpiredException + * @see org.netbeans.jemmy.QueueTool + */ + public static void waitQueueEmpty(long emptyTime, TestOut output, Timeouts timeouts) { + QueueTool qt = new QueueTool(); + qt.setTimeouts(timeouts.cloneThis()); + qt.getTimeouts(). + setTimeout("QueueTool.WaitQueueEmptyTimeout", + JemmyProperties. + getCurrentTimeout("EventDispatcher.WaitQueueEmptyTimeout")); + qt.setOutput(output); + qt.waitEmpty(emptyTime); + } + + /** + * Waits for the managed component's {@code java.awt.EventQueue} to + * stay empty. Uses default output and timeouts. The timeout for this wait + * is EventDispatcher.WaitQueueEmptyTimeout. + * + * @param emptyTime The time that the event queue has to stay empty to avoid + * a TimeoutExpiredException. + * @throws TimeoutExpiredException + * @see org.netbeans.jemmy.QueueTool + */ + public static void waitQueueEmpty(long emptyTime) { + waitQueueEmpty(emptyTime, + JemmyProperties.getCurrentOutput(), + JemmyProperties.getCurrentTimeouts()); + } + + /** + * Get a string representation for key modifiers. Used to print trace. + * + * @param modifiers Bit mask of keyboard event modifiers. + * @return a string representation for the keyboard event modifiers. + */ + public static String getModifiersString(int modifiers) { + String result = ""; + if ((modifiers & InputEvent.CTRL_MASK) != 0) { + result = result + "CTRL_MASK | "; + } + if ((modifiers & InputEvent.META_MASK) != 0) { + result = result + "META_MASK | "; + } + if ((modifiers & InputEvent.ALT_MASK) != 0) { + result = result + "ALT_MASK | "; + } + if ((modifiers & InputEvent.ALT_GRAPH_MASK) != 0) { + result = result + "ALT_GRAPH_MASK | "; + } + if ((modifiers & InputEvent.SHIFT_MASK) != 0) { + result = result + "SHIFT_MASK | "; + } + if (result.length() > 0) { + return result.substring(0, result.length() - 3); + } + return result; + } + + /** + * Returns a string representation for a keyboard event. Used to print + * trace. + * + * @param keyCode Key code ({@code KeyEvent.VK_*} value) + * @return the KeyEvent field name. + */ + public static String getKeyDescription(int keyCode) { + for (Field keyField : keyFields) { + try { + if (keyField.getName().startsWith("VK_") + && keyField.getInt(null) == keyCode) { + return keyField.getName(); + } + } catch (IllegalAccessException e) { + JemmyProperties.getCurrentOutput().printStackTrace(e); + } + } + return "VK_UNKNOWN"; + } + + /** + * Returns a mouse button string representation. Used to print trace. + * + * @param button Mouse button ({@code InputEvent.BUTTON1/2/3_MASK} + * value). + * @return InputEvent field name. + */ + public static String getMouseButtonDescription(int button) { + String result; + if ((button & InputEvent.BUTTON1_MASK) != 0) { + result = "BUTTON1"; + } else if ((button & InputEvent.BUTTON2_MASK) != 0) { + result = "BUTTON2"; + } else if ((button & InputEvent.BUTTON3_MASK) != 0) { + result = "BUTTON3"; + } else { + result = "UNKNOWN_BUTTON"; + } + return result; + } + + public static void performInit() { + Timeouts.initDefault("EventDispatcher.WaitQueueEmptyTimeout", WAIT_QUEUE_EMPTY_TIMEOUT); + Timeouts.initDefault("EventDispatcher.RobotAutoDelay", ROBOT_AUTO_DELAY); + Timeouts.initDefault("EventDispatcher.WaitComponentUnderMouseTimeout", WAIT_COMPONENT_UNDER_MOUSE_TIMEOUT); + try { + keyFields = Class.forName("java.awt.event.KeyEvent").getFields(); + } catch (ClassNotFoundException e) { + JemmyProperties.getCurrentOutput().printStackTrace(e); + } + } + + static { + performInit(); + } + + /** + * Wait (or not) for the mouse to move over a Java component before + * pressing. This option is relevant when using {@code java.awt.Robot} + * to generate mouse events. If a mouse press occurs at a position not + * occupied by a known Java component then a + * {@code NoComponentUnderMouseException} will be thrown. + * + * @param yesOrNo if {@code true} then the test system will wait for + * the mouse to move over a Java component before pressing. therwise, mouse + * presses can take place anywhere on the screen. + */ + public void checkComponentUnderMouse(boolean yesOrNo) { + outsider = !yesOrNo; + } + + /** + * Defines print output streams or writers. + * + * @param out Identify the streams or writers used for print output. + * @see org.netbeans.jemmy.Outputable + * @see org.netbeans.jemmy.TestOut + * @see #getOutput + */ + @Override + public void setOutput(TestOut out) { + output = out; + queueTool.setOutput(out); + } + + /** + * Returns print output streams or writers. + * + * @return an object that contains references to objects for printing to + * output and err streams. + * @see org.netbeans.jemmy.Outputable + * @see org.netbeans.jemmy.TestOut + * @see #setOutput + */ + @Override + public TestOut getOutput() { + return output; + } + + /** + * Defines current timeouts. + * + * @param timeouts A collection of timeout assignments. + * @see org.netbeans.jemmy.Timeoutable + * @see org.netbeans.jemmy.Timeouts + * @see #getTimeouts + */ + @Override + public void setTimeouts(Timeouts timeouts) { + this.timeouts = timeouts; + queueTool.setTimeouts(timeouts); + queueTool.getTimeouts(). + setTimeout("QueueTool.WaitQueueEmptyTimeout", + timeouts. + getTimeout("EventDispatcher.WaitQueueEmptyTimeout")); + if (robotReference != null) { + try { + Object[] params = {(int) timeouts.getTimeout("EventDispatcher.RobotAutoDelay")}; + Class[] paramClasses = {Integer.TYPE}; + robotReference.invokeMethod("setAutoDelay", params, paramClasses); + } catch (InvocationTargetException + | IllegalStateException + | NoSuchMethodException + | IllegalAccessException e) { + output.printStackTrace(e); + } + } + } + + /** + * Return current timeouts. + * + * @return the collection of current timeout assignments. + * @see org.netbeans.jemmy.Timeoutable + * @see org.netbeans.jemmy.Timeouts + * @see #setTimeouts + */ + @Override + public Timeouts getTimeouts() { + return timeouts; + } + + /** + * Defines dispatching model. + * + * @param m New model value. + * @see #getDispatchingModel() + * @see org.netbeans.jemmy.JemmyProperties#QUEUE_MODEL_MASK + * @see org.netbeans.jemmy.JemmyProperties#ROBOT_MODEL_MASK + * @see org.netbeans.jemmy.JemmyProperties#getCurrentDispatchingModel() + * @see org.netbeans.jemmy.JemmyProperties#setCurrentDispatchingModel(int) + * @see org.netbeans.jemmy.JemmyProperties#initDispatchingModel(boolean, + * boolean) + * @see org.netbeans.jemmy.JemmyProperties#initDispatchingModel() + */ + public void setDispatchingModel(int m) { + model = m; + if ((model & JemmyProperties.ROBOT_MODEL_MASK) != 0) { + createRobot(); + try { + Object[] params = {(model & JemmyProperties.QUEUE_MODEL_MASK) != 0 ? Boolean.TRUE : Boolean.FALSE}; + Class[] paramClasses = {Boolean.TYPE}; + robotReference.invokeMethod("setAutoWaitForIdle", params, paramClasses); + } catch (InvocationTargetException + | IllegalStateException + | NoSuchMethodException + | IllegalAccessException e) { + output.printStackTrace(e); + } + } + } + + /** + * Gets the dispatching model value. + * + * @return the model value. + * @see #setDispatchingModel(int) + * @see org.netbeans.jemmy.JemmyProperties#QUEUE_MODEL_MASK + * @see org.netbeans.jemmy.JemmyProperties#ROBOT_MODEL_MASK + * @see org.netbeans.jemmy.JemmyProperties#getCurrentDispatchingModel() + * @see org.netbeans.jemmy.JemmyProperties#setCurrentDispatchingModel(int) + * @see org.netbeans.jemmy.JemmyProperties#initDispatchingModel(boolean, + * boolean) + * @see org.netbeans.jemmy.JemmyProperties#initDispatchingModel() + */ + public int getDispatchingModel() { + return model; + } + + /** + * Dispatches {@code AWTEvent} to component passed in constructor. If + * {@code (getDispatchingModel & JemmyProperties.QUEUE_MODEL_MASK) == 0} + * dispatched event directly, otherwise uses + * {@code javax.swing.SwingUtilities.invokeAndWait(Runnable)}
+ * + * @param event AWTEvent instance to be dispatched. + * @throws ComponentIsNotVisibleException + * @throws ComponentIsNotFocusedException + */ + public void dispatchEvent(final AWTEvent event) { + // run in dispatch thread + String eventToString = queueTool.invokeSmoothly( + new QueueTool.QueueAction("event.toString()") { + @Override + public String launch() { + return event.toString(); + } + } + ); + output.printLine("Dispatch event " + eventToString); + output.printGolden("Dispatch event " + event.getClass().toString()); + Dispatcher disp = new Dispatcher<>(event); + queueTool.invokeAndWait(disp); + } + + /** + * Dispatches a MouseEvent. + * + * @see #dispatchEvent(AWTEvent) + * @param id {@code MouseEvent.MOUSE_*} value + * @param mods {@code InputEvent.MOUSE1/2/3_BUTTON} | (modifiers value) + * @param clickCount Click count + * @param x Horizontal click point coordinate. + * @param y vertical click point coordinate. + * @param popup Defines if mouse event is a popup event. + */ + public void dispatchMouseEvent(int id, int mods, int clickCount, int x, int y, + boolean popup) { + MouseEvent event = new MouseEvent(component, id, System.currentTimeMillis(), + mods, x, y, clickCount, popup); + dispatchEvent(event); + } + + /** + * Dispatches MouseEvent at the center of component. + * + * @see #dispatchEvent(AWTEvent) + * @param id {@code MouseEvent.MOUSE_*} value + * @param mods {@code InputEvent.MOUSE1/2/3_BUTTON} | (modiviers value) + * @param clickCount Click count + * @param popup Difines if mouse event is popup event. + */ + public void dispatchMouseEvent(int id, int mods, int clickCount, + boolean popup) { + int x = component.getWidth() / 2; + int y = component.getHeight() / 2; + dispatchMouseEvent(id, mods, clickCount, x, y, popup); + } + + /** + * Dispatches WindowEvent. + * + * @see #dispatchEvent(AWTEvent) + * @param id {@code WindowEvent.WINDOW_*} value + */ + public void dispatchWindowEvent(int id) { + WindowEvent event = new WindowEvent((Window) component, id); + dispatchEvent(event); + } + + /** + * Dispatches KeyEvent. + * + * @see #dispatchEvent(AWTEvent) + * @param id {@code KeyEvent.KEY_PRESSED} or + * {@code KeyEvent.KEY_RELEASED} value. + * @param mods Modifiers. + * @param keyCode Key code, + */ + @Deprecated + public void dispatchKeyEvent(int id, int mods, int keyCode) { + KeyEvent event = new KeyEvent(component, id, System.currentTimeMillis(), mods, keyCode); + dispatchEvent(event); + } + + /** + * Dispatches KeyEvent. + * + * @see #dispatchEvent(AWTEvent) + * @param id {@code KeyEvent.KEY_TYPED} value. + * @param mods Modifiers. + * @param keyCode Key code, + * @param keyChar Char to be tiped + */ + public void dispatchKeyEvent(int id, int mods, int keyCode, char keyChar) { + KeyEvent event = new KeyEvent(component, id, System.currentTimeMillis(), + mods, keyCode, keyChar); + dispatchEvent(event); + } + + /** + * Waits until all events currently on the event queue have been processed. + */ + public void waitForIdle() { + makeRobotOperation("waitForIdle", null, null); + } + + /** + * Bind horizontal relative cursor coordinate to screen coordinate. + * + * @param x Relative coordinate + * @return Absolute coordinate + */ + protected int getAbsoluteX(int x) { + return (int) component.getLocationOnScreen().getX() + x; + } + + /** + * Bind vertical relative cursor coordinate to screen coordinate. + * + * @param y Relative coordinate + * @return Absolute coordinate + */ + protected int getAbsoluteY(int y) { + return (int) component.getLocationOnScreen().getY() + y; + } + + /** + * Delays robot. + * + * @param time Time to dalay robot for. + */ + public void delayRobot(long time) { + Object[] params = {(int) time}; + Class[] paramClasses = {Integer.TYPE}; + makeRobotOperation("delay", params, paramClasses); + } + + /** + * Moves mouse by robot. + * + * @param x Component relative horizontal coordinate. + * @param y Component relative vertical coordinate. + * @throws ComponentIsNotVisibleException + */ + public void robotMoveMouse(int x, int y) { + if (motionListener == null) { + initMotionListener(); + } + output.printLine("Move mouse to (" + Integer.toString(x) + "," + + Integer.toString(y) + ")"); + Object[] params = {getAbsoluteX(x), getAbsoluteY(y)}; + Class[] paramClasses = {Integer.TYPE, Integer.TYPE}; + makeRobotOperation("mouseMove", params, paramClasses); + } + + /** + * Press mouse button by robot. + * + * @param button Mouse button (InputEvent.MOUSE1/2/3_BUTTON value) + * @param modifiers Modifiers + * @throws ComponentIsNotVisibleException + */ + public void robotPressMouse(int button, int modifiers) { + if (!outsider) { + waitMouseOver(); + } + robotPressModifiers(modifiers); + output.printLine("Press " + getMouseButtonDescription(button) + " mouse button"); + Object[] params = {button}; + Class[] paramClasses = {Integer.TYPE}; + makeRobotOperation("mousePress", params, paramClasses); + } + + /** + * Press mouse button with 0 modifiers. + * + * @param button Mouse button ({@code InputEvent.MOUSE1/2/3_BUTTON} + * value) + * @see #robotPressMouse(int, int) + */ + public void robotPressMouse(int button) { + robotPressMouse(button, 0); + } + + /** + * Releases mouse button by robot. + * + * @param button Mouse button ({@code InputEvent.MOUSE1/2/3_BUTTON} + * value) + * @param modifiers Modifiers + * @throws ComponentIsNotVisibleException + */ + public void robotReleaseMouse(int button, int modifiers) { + output.printLine("Release " + getMouseButtonDescription(button) + " mouse button"); + Object[] params = {button}; + Class[] paramClasses = {Integer.TYPE}; + makeRobotOperation("mouseRelease", params, paramClasses); + robotReleaseModifiers(modifiers); + } + + /** + * Releases mouse button with 0 modifiers. + * + * @param button Mouse button ({@code InputEvent.MOUSE1/2/3_BUTTON} + * value) + * @see #robotReleaseMouse(int, int) + */ + public void robotReleaseMouse(int button) { + robotReleaseMouse(button, 0); + } + + /** + * Press a key using {@code java.awt.Robot}. + * + * @param keyCode Key ({@code KeyEvent.VK_*} value) + * @param modifiers Mask of KeyEvent modifiers. + * @throws ComponentIsNotVisibleException + * @throws ComponentIsNotFocusedException + */ + public void robotPressKey(int keyCode, int modifiers) { + robotPressModifiers(modifiers); + output.printLine("Press " + getKeyDescription(keyCode) + " key"); + Object[] params = {keyCode}; + Class[] paramClasses = {Integer.TYPE}; + makeRobotOperation("keyPress", params, paramClasses); + } + + /** + * Press key with no modifiers using {@code java.awt.Robot}. + * + * @param keyCode Key ({@code KeyEvent.VK_*} value) + * @see #robotPressKey(int, int) + */ + public void robotPressKey(int keyCode) { + robotPressKey(keyCode, 0); + } + + /** + * Releases key by robot. + * + * @param keyCode Key ({@code KeyEvent.VK_*} value) + * @param modifiers Mask of KeyEvent modifiers. + * @throws ComponentIsNotVisibleException + * @throws ComponentIsNotFocusedException + */ + public void robotReleaseKey(int keyCode, int modifiers) { + output.printLine("Release " + getKeyDescription(keyCode) + " key"); + Object[] params = {keyCode}; + Class[] paramClasses = {Integer.TYPE}; + makeRobotOperation("keyRelease", params, paramClasses); + robotReleaseModifiers(modifiers); + } + + /** + * Releases key with 0 modifiers. + * + * @param keyCode Key ({@code KeyEvent.VK_*} value) + * @see #robotPressKey(int, int) + */ + public void robotReleaseKey(int keyCode) { + robotReleaseKey(keyCode, 0); + } + + /** + * Invokes component method through + * {@code SwingUtilities.invokeAndWait(Runnable)}. + * + * @param method_name Name of a method to be invoked + * @param params Method params + * @param params_classes Method params' classes + * @return an Object - methods result. + * @see org.netbeans.jemmy.ClassReference + * @exception IllegalAccessException + * @exception NoSuchMethodException + * @exception IllegalStateException + * @exception InvocationTargetException + */ + public Object invokeMethod(String method_name, Object[] params, Class[] params_classes) + throws InvocationTargetException, IllegalStateException, NoSuchMethodException, IllegalAccessException { + Invoker invk = new Invoker(method_name, params, params_classes); + try { + return queueTool.invokeAndWait(invk); + } catch (JemmyException e) { + Exception ex = invk.getException(); + if (ex != null) { + if (ex instanceof InvocationTargetException) { + InvocationTargetException ite = (InvocationTargetException) ex; + ite.addSuppressed(e); + throw ite; + } else if (ex instanceof IllegalStateException) { + IllegalStateException ise = (IllegalStateException) ex; + ise.addSuppressed(e); + throw ise; + } else if (ex instanceof NoSuchMethodException) { + NoSuchMethodException nsme = (NoSuchMethodException) ex; + nsme.addSuppressed(e); + throw nsme; + } else if (ex instanceof IllegalAccessException) { + IllegalAccessException iae = (IllegalAccessException) ex; + iae.addSuppressed(e); + throw iae; + } else { + e.addSuppressed(ex); + } + } + throw (e); + } + } + + /** + * Gets component field value through + * {@code SwingUtilities.invokeAndWait(Runnable)}. + * + * @param field_name Name of a field + * @see #setField(String, Object) + * @see org.netbeans.jemmy.ClassReference + * @return an Object - field value + * @exception IllegalAccessException + * @exception IllegalStateException + * @exception InvocationTargetException + * @exception NoSuchFieldException + */ + public Object getField(String field_name) + throws InvocationTargetException, IllegalStateException, NoSuchFieldException, IllegalAccessException { + Getter gtr = new Getter(field_name); + try { + return queueTool.invokeAndWait(gtr); + } catch (JemmyException e) { + Exception ex = gtr.getException(); + if (ex != null) { + if (ex instanceof InvocationTargetException) { + InvocationTargetException ite = (InvocationTargetException) ex; + ite.addSuppressed(e); + throw ite; + } else if (ex instanceof IllegalStateException) { + IllegalStateException ise = (IllegalStateException) ex; + ise.addSuppressed(e); + throw ise; + } else if (ex instanceof NoSuchFieldException) { + NoSuchFieldException nsfe = (NoSuchFieldException) ex; + nsfe.addSuppressed(e); + throw nsfe; + } else if (ex instanceof IllegalAccessException) { + IllegalAccessException iae = (IllegalAccessException) ex; + iae.addSuppressed(e); + throw iae; + } else { + e.addSuppressed(ex); + } + } + throw (e); + } + } + + /** + * Sets component field value through + * {@code SwingUtilities.invokeAndWait(Runnable)}. + * + * @param field_name Name of a field + * @param newValue New field value + * @see #getField(String) + * @see org.netbeans.jemmy.ClassReference + * @exception IllegalAccessException + * @exception IllegalStateException + * @exception InvocationTargetException + * @exception NoSuchFieldException + */ + public void setField(String field_name, Object newValue) + throws InvocationTargetException, IllegalStateException, NoSuchFieldException, IllegalAccessException { + Setter str = new Setter(field_name, newValue); + try { + queueTool.invokeAndWait(str); + } catch (JemmyException e) { + Exception ex = str.getException(); + if (ex != null) { + if (ex instanceof InvocationTargetException) { + InvocationTargetException ite = (InvocationTargetException) ex; + ite.addSuppressed(e); + throw ite; + } else if (ex instanceof IllegalStateException) { + IllegalStateException ise = (IllegalStateException) ex; + ise.addSuppressed(e); + throw ise; + } else if (ex instanceof NoSuchFieldException) { + NoSuchFieldException nsfe = (NoSuchFieldException) ex; + nsfe.addSuppressed(e); + throw nsfe; + } else if (ex instanceof IllegalAccessException) { + IllegalAccessException iae = (IllegalAccessException) ex; + iae.addSuppressed(e); + throw iae; + } else { + e.addSuppressed(ex); + } + } + throw (e); + } + } + + /** + * Invokes component method through + * {@code SwingUtilities.invokeAndWait(Runnable)}. and catch all + * exceptions. + * + * @param method_name Name of a method to be invoked + * @param params Method params + * @param params_classes Method params' classes + * @param out TestOut instance to print exceptions stack trace to. + * @return an Object - method result + * @see #invokeMethod(String, Object[], Class[]) + * @see org.netbeans.jemmy.ClassReference + */ + public Object invokeExistingMethod(String method_name, Object[] params, Class[] params_classes, + TestOut out) { + try { + return invokeMethod(method_name, params, params_classes); + } catch (InvocationTargetException + | IllegalStateException + | NoSuchMethodException + | IllegalAccessException e) { + out.printStackTrace(e); + } + return null; + } + + /** + * Gets component field value through + * {@code SwingUtilities.invokeAndWait(Runnable)}. and catch all + * exceptions. + * + * @param field_name Name of a field + * @param out TestOut instance to print exceptions stack trace to. + * @return an Object - fields value + * @see #getField(String) + * @see #setExistingField(String, Object, TestOut) + * @see org.netbeans.jemmy.ClassReference + */ + public Object getExistingField(String field_name, + TestOut out) { + try { + return getField(field_name); + } catch (InvocationTargetException + | IllegalStateException + | NoSuchFieldException + | IllegalAccessException e) { + out.printStackTrace(e); + } + return null; + } + + /** + * Sets component field value through + * {@code SwingUtilities.invokeAndWait(Runnable)}. and catch all + * exceptions. + * + * @param field_name Name of a field + * @param newValue New field value + * @param out TestOut instance to print exceptions stack trace to. + * @see #setField(String, Object) + * @see #getExistingField(String, TestOut) + * @see org.netbeans.jemmy.ClassReference + */ + public void setExistingField(String field_name, Object newValue, + TestOut out) { + try { + setField(field_name, newValue); + } catch (InvocationTargetException + | IllegalStateException + | NoSuchFieldException + | IllegalAccessException e) { + out.printStackTrace(e); + } + } + + /** + * Invokes component method through + * {@code SwingUtilities.invokeAndWait(Runnable)}. and catch all + * exceptions. Exceptions are printed into TestOut object defined by + * setOutput(TestOut) method. + * + * @param method_name Name of a method to be invoked + * @param params Method params + * @param params_classes Method params' classes + * @return an Object - method result + * @see #invokeExistingMethod(String, Object[], Class[], TestOut) + * @see org.netbeans.jemmy.ClassReference + */ + public Object invokeExistingMethod(String method_name, Object[] params, Class[] params_classes) { + return invokeExistingMethod(method_name, params, params_classes, output); + } + + /** + * Gets component field value through + * {@code SwingUtilities.invokeAndWait(Runnable)}. and catch all + * exceptions. Exceptions are printed into TestOut object defined by + * setOutput(TestOut) method. + * + * @param field_name Name of a field + * @return an Object - fields value + * @see #getExistingField(String, TestOut) + * @see #setExistingField(String, Object) + * @see org.netbeans.jemmy.ClassReference + */ + public Object getExistingField(String field_name) { + return getExistingField(field_name, output); + } + + /** + * Sets component field value through + * {@code SwingUtilities.invokeAndWait(Runnable)}. and catch all + * exceptions. Exceptions are printed into TestOut object defined by + * setOutput(TestOut) method. + * + * @param field_name Name of a field + * @param newValue New field value + * @see #setExistingField(String, Object, TestOut) + * @see #getExistingField(String) + * @see org.netbeans.jemmy.ClassReference + */ + public void setExistingField(String field_name, Object newValue) { + setExistingField(field_name, newValue, output); + } + + //recursivelly releases all modifiers keys + private void robotReleaseModifiers(int modifiers) { + if ((modifiers & InputEvent.SHIFT_MASK) != 0) { + robotReleaseKey(KeyEvent.VK_SHIFT, modifiers - (InputEvent.SHIFT_MASK & modifiers)); + } else if ((modifiers & InputEvent.ALT_GRAPH_MASK) != 0) { + robotReleaseKey(KeyEvent.VK_ALT_GRAPH, modifiers - (InputEvent.ALT_GRAPH_MASK & modifiers)); + } else if ((modifiers & InputEvent.ALT_MASK) != 0) { + robotReleaseKey(KeyEvent.VK_ALT, modifiers - (InputEvent.ALT_MASK & modifiers)); + } else if ((modifiers & InputEvent.META_MASK) != 0) { + robotReleaseKey(KeyEvent.VK_META, modifiers - (InputEvent.META_MASK & modifiers)); + } else if ((modifiers & InputEvent.CTRL_MASK) != 0) { + robotReleaseKey(KeyEvent.VK_CONTROL, modifiers - (InputEvent.CTRL_MASK & modifiers)); + } + } + + //throws ComponentIsNotVisibleException if component is not visible + private void checkVisibility() { + if (!component.isVisible()) { + throw (new ComponentIsNotVisibleException(component)); + } + } + + //throws ComponentIsNotFocusedException if component has not focus + private void checkFocus() { + if (!component.hasFocus()) { + throw (new ComponentIsNotFocusedException(component)); + } + } + + //creates java.awt.Robot instance + private void createRobot() { + try { + ClassReference robotClassReverence = new ClassReference("java.awt.Robot"); + robotReference = new ClassReference(robotClassReverence.newInstance(null, null)); + } catch (ClassNotFoundException + | InstantiationException + | InvocationTargetException + | IllegalStateException + | NoSuchMethodException + | IllegalAccessException e) { + output.printStackTrace(e); + } + } + + private void waitMouseOver() { + try { + Waiter wt = new Waiter<>(new Waitable() { + @Override + public String actionProduced(Component obj) { + if (motionListener.getComponent() != null) { + return ""; + } else { + return null; + } + } + + @Override + public String getDescription() { + return "Mouse over component"; + } + + @Override + public String toString() { + return "waitMouseOver.Waiter{" + getDescription() + '}'; + } + }); + wt.setTimeoutsToCloneOf(timeouts, "EventDispatcher.WaitComponentUnderMouseTimeout"); + wt.setOutput(output.createErrorOutput()); + wt.waitAction(component); + } catch (InterruptedException e) { + output.printStackTrace(e); + } catch (TimeoutExpiredException e) { + throw (new NoComponentUnderMouseException()); + } + } + + //produce a robot operations through reflection + private void makeRobotOperation(String method, Object[] params, Class[] paramClasses) { + try { + robotReference.invokeMethod(method, params, paramClasses); + } catch (InvocationTargetException + | IllegalStateException + | NoSuchMethodException + | IllegalAccessException e) { + output.printStackTrace(e); + } + if ((model & JemmyProperties.QUEUE_MODEL_MASK) != 0) { + try { + waitQueueEmpty(output.createErrorOutput(), timeouts); + } catch (TimeoutExpiredException e) { + output.printStackTrace(e); + } + } + } + + //recursivelly presses all modifiers keys + private void robotPressModifiers(int modifiers) { + if ((modifiers & InputEvent.SHIFT_MASK) != 0) { + robotPressKey(KeyEvent.VK_SHIFT, modifiers & ~InputEvent.SHIFT_MASK); + } else if ((modifiers & InputEvent.ALT_GRAPH_MASK) != 0) { + robotPressKey(KeyEvent.VK_ALT_GRAPH, modifiers & ~InputEvent.ALT_GRAPH_MASK); + } else if ((modifiers & InputEvent.ALT_MASK) != 0) { + robotPressKey(KeyEvent.VK_ALT, modifiers & ~InputEvent.ALT_MASK); + } else if ((modifiers & InputEvent.META_MASK) != 0) { + robotPressKey(KeyEvent.VK_META, modifiers & ~InputEvent.META_MASK); + } else if ((modifiers & InputEvent.CTRL_MASK) != 0) { + robotPressKey(KeyEvent.VK_CONTROL, modifiers & ~InputEvent.CTRL_MASK); + } + } + + private void initMotionListener() { + synchronized(EventDispatcher.class) { + if (motionListener == null) { + motionListener = new MotionListener(); + Toolkit.getDefaultToolkit().addAWTEventListener(motionListener, AWTEvent.MOUSE_EVENT_MASK); + Object[] params = new Object[2]; + Class[] paramClasses = {Integer.TYPE, Integer.TYPE}; + params[0] = getAbsoluteX(-1); + params[1] = getAbsoluteX(-1); + makeRobotOperation("mouseMove", params, paramClasses); + params[0] = getAbsoluteX(0); + params[1] = getAbsoluteX(0); + makeRobotOperation("mouseMove", params, paramClasses); + } + } + } + + private class Dispatcher extends QueueTool.QueueAction { + + AWTEvent event; + + public Dispatcher(AWTEvent e) { + super(e.getClass().getName() + " event dispatching"); + event = e; + } + + @Override + public R launch() { + if (event instanceof MouseEvent || event instanceof KeyEvent) { + checkVisibility(); + } + component.dispatchEvent(event); + return null; + } + } + + private class Invoker extends QueueTool.QueueAction { + + protected String methodName; + protected Object[] params; + protected Class[] paramClasses; + + public Invoker(String mn, Object[] p, Class[] pc) { + super(mn + " method invocation"); + methodName = mn; + params = p; + paramClasses = pc; + } + + @Override + public Object launch() + throws InvocationTargetException, NoSuchMethodException, IllegalAccessException { + checkVisibility(); + if (methodName.equals("keyPress") || methodName.equals("keyRelease")) { + checkFocus(); + } + return reference.invokeMethod(methodName, params, paramClasses); + } + } + + private class Getter extends QueueTool.QueueAction { + + String fieldName; + + public Getter(String fn) { + super(fn + " field receiving"); + fieldName = fn; + } + + @Override + public Object launch() + throws InvocationTargetException, NoSuchFieldException, IllegalAccessException { + return reference.getField(fieldName); + } + } + + private class Setter extends QueueTool.QueueAction { + + String fieldName; + Object newValue; + + public Setter(String fn, Object nv) { + super(fn + " field changing"); + fieldName = fn; + newValue = nv; + } + + @Override + public Object launch() + throws InvocationTargetException, NoSuchFieldException, IllegalAccessException { + reference.setField(fieldName, newValue); + return null; + } + } + + private static class MotionListener implements AWTEventListener { + + private volatile Component mouseComponent; + + @Override + public void eventDispatched(AWTEvent event) { + if (event instanceof MouseEvent) { + MouseEvent e = (MouseEvent) event; + if (e.getID() == MouseEvent.MOUSE_ENTERED) { + mouseComponent = e.getComponent(); + } else if (e.getID() == MouseEvent.MOUSE_EXITED) { + mouseComponent = null; + } + } + } + + public Component getComponent() { + return mouseComponent; + } + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/EventTool.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/EventTool.java new file mode 100644 index 00000000000..5cd2e4d2b1a --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/EventTool.java @@ -0,0 +1,538 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy; + +import java.awt.AWTEvent; +import java.awt.Toolkit; +import java.awt.event.AWTEventListener; +import java.lang.ref.Reference; +import java.lang.ref.WeakReference; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.Vector; + +/** + * + * Provides methods to check last dispatched events, to wait for events of + * specific types, or to guarantee that events of specific types are not + * dispatched during some time frame. + *

+ * All possible listeners are added during this class initialization in case if + * "jemmy.event_listening" system property is not equal to "no", so, by default, + * all events are listened. + * + * Uses timeouts:
+ * EventTool.WaitEventTimeout - time to wait for AWT events.
+ * EventTool.WaitNoEventTimeout - when checking for the absence of incoming AWT + * events.
+ * EventTool.EventCheckingDelta - time delta between checks for AWT events. + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + */ +public class EventTool implements Timeoutable, Outputable { + + private static final long WAIT_EVENT_TIMEOUT = 60000; + private static final long WAIT_NO_EVENT_TIMEOUT = 180000; + private static final long EVENT_CHECKING_DELTA = 10; + + private static ListenerSet listenerSet; + private static long currentEventMask = 0; + + private TestOut output; + private Timeouts timeouts; + + /** + * Constructor. + */ + public EventTool() { + setOutput(JemmyProperties.getProperties().getOutput()); + setTimeouts(JemmyProperties.getProperties().getTimeouts()); + } + + /** + * Returns time of the last dispatched event under mask. + * + * @param eventMask Events types to be searched. + * {@code AWTEvent.*_EVENT_MASK} fields combination. + * @return time in milliseconds + * @see #addListeners(long) + */ + public static long getLastEventTime(long eventMask) { + return listenerSet.getLastEventTime(eventMask); + } + + /** + * Returns last dispatched event under mask. + * + * @param eventMask Events types to be searched. + * {@code AWTEvent.*_EVENT_MASK} fields combination. + * @return AWTEvent + * @see #addListeners(long) + */ + public static AWTEvent getLastEvent(long eventMask) { + return listenerSet.getLastEvent(eventMask); + } + + /** + * Returns time of the last dispatched event. + * + * @return time in milliseconds + * @see #addListeners(long) + */ + public static long getLastEventTime() { + return getLastEventTime(listenerSet.getTheWholeMask()); + } + + /** + * Returns last dispatched event. + * + * @return AWTEvent + * @see #addListeners(long) + */ + public static AWTEvent getLastEvent() { + return getLastEvent(listenerSet.getTheWholeMask()); + } + + /** + * Adds listeners to listen events under mask. Invokes + * {@code removeListeners()} first, so any event history is lost. + * + * @param eventMask Mask to listen events under. + * {@code AWTEvent.*_EVENT_MASK} fields combination. + * @see #addListeners() + * @see #removeListeners() + */ + public static void addListeners(long eventMask) { + removeListeners(); + listenerSet.addListeners(eventMask); + currentEventMask = eventMask; + } + + /** + * Adds listeners to listen all types of events. Invokes + * {@code removeListeners()} first, so any event history is lost. This + * method is invoked during static section of this class. + * + * @see #addListeners(long) + * @see #removeListeners() + * @see #getTheWholeEventMask() + */ + public static void addListeners() { + addListeners(listenerSet.getTheWholeMask()); + } + + /** + * Removes all listeners. + * + * @see #addListeners(long) + * @see #addListeners() + */ + public static void removeListeners() { + listenerSet.removeListeners(); + } + + /** + * Returns event mask last time used by {@code addListeners(long)} + * method. In case if {@code addListeners()} method was used last, + * {@code getTheWholeEventMask() } result is returned. + * + * @return a long representing the current event mask value + * @see #getTheWholeEventMask() + */ + public static long getCurrentEventMask() { + return currentEventMask; + } + + /** + * Returns a combination of all {@code AWTEvent.*_EVENT_MASK} fields.. + * + * @return a combination of all {@code AWTEvent.*_EVENT_MASK} fields. + */ + public static long getTheWholeEventMask() { + return listenerSet.getTheWholeMask(); + } + + static { + Timeouts.initDefault("EventTool.WaitEventTimeout", WAIT_EVENT_TIMEOUT); + Timeouts.initDefault("EventTool.WaitNoEventTimeout", WAIT_NO_EVENT_TIMEOUT); + Timeouts.initDefault("EventTool.EventCheckingDelta", EVENT_CHECKING_DELTA); + listenerSet = new ListenerSet(); + if (System.getProperty("jemmy.event_listening") == null + || !System.getProperty("jemmy.event_listening").equals("no")) { + listenerSet.addListeners(); + } + } + + /** + * Defines current timeouts. + * + * @param ts ?t? A collection of timeout assignments. + * @see org.netbeans.jemmy.Timeouts + * @see org.netbeans.jemmy.Timeoutable + * @see #getTimeouts + */ + @Override + public void setTimeouts(Timeouts ts) { + timeouts = ts; + } + + /** + * Return current timeouts. + * + * @return the collection of current timeout assignments. + * @see org.netbeans.jemmy.Timeouts + * @see org.netbeans.jemmy.Timeoutable + * @see #setTimeouts + */ + @Override + public Timeouts getTimeouts() { + return timeouts; + } + + /** + * Defines print output streams or writers. + * + * @param out Identify the streams or writers used for print output. + * @see org.netbeans.jemmy.Outputable + * @see org.netbeans.jemmy.TestOut + * @see #getOutput + */ + @Override + public void setOutput(TestOut out) { + output = out; + } + + /** + * Returns print output streams or writers. + * + * @return an object that contains references to objects for printing to + * output and err streams. + * @see org.netbeans.jemmy.Outputable + * @see org.netbeans.jemmy.TestOut + * @see #setOutput + */ + @Override + public TestOut getOutput() { + return output; + } + + /** + * Waits for the first event under mask. Waits during + * {@code EventTool.WaitEventTimeout} milliseconds. + * + * @param eventMask Mask to wait events under. + * {@code AWTEvent.*_EVENT_MASK} fields combination. + * @return an AWTEvent object + * @see #waitEvent() + * @throws TimeoutExpiredException + */ + public AWTEvent waitEvent(long eventMask) { + return (waitEvent(eventMask, + timeouts.getTimeout("EventTool.WaitEventTimeout"), + output.createErrorOutput())); + } + + /** + * Waits for the first event. Waits during + * {@code EventTool.WaitEventTimeout} milliseconds. + * + * @return an AWTEvent object + * @see #waitEvent(long) + * @see #getTheWholeEventMask() + * @throws TimeoutExpiredException + */ + public AWTEvent waitEvent() { + return waitEvent(listenerSet.getTheWholeMask()); + } + + /** + * Check that no event under mask will be dispatched during time specified. + * + * @param eventMask Mask to wait events under. + * {@code AWTEvent.*_EVENT_MASK} fields combination. + * @param waitTime Quiet time (millisecons). + * @return true if no event ahs found. + * @see #checkNoEvent(long) + */ + public boolean checkNoEvent(long eventMask, long waitTime) { + return checkNoEvent(eventMask, waitTime, output); + } + + /** + * Check that no event will be dispatched during time specified. + * + * @param waitTime Quiet time (millisecons). + * @return true if no event ahs found. + * @see #checkNoEvent(long, long) + * @see #getTheWholeEventMask() + */ + public boolean checkNoEvent(long waitTime) { + return checkNoEvent(listenerSet.getTheWholeMask(), waitTime); + } + + /** + * During {@code EventTool.WaitNoEventTimeout} time waits for true + * result of checkNoEvent(long, long) method. + * + * @param eventMask Mask to wait events under. + * {@code AWTEvent.*_EVENT_MASK} fields combination. + * @param waitTime Quiet time (millisecons). + * @see #checkNoEvent(long, long) + * @see #waitNoEvent(long) + * @throws TimeoutExpiredException + */ + public void waitNoEvent(long eventMask, long waitTime) { + NoEventWaiter waiter = new NoEventWaiter(eventMask, waitTime); + waiter.setTimeouts(timeouts.cloneThis()); + waiter.getTimeouts(). + setTimeout("Waiter.WaitingTime", + timeouts.getTimeout("EventTool.WaitNoEventTimeout")); + waiter.getTimeouts(). + setTimeout("Waiter.TimeDelta", + timeouts.getTimeout("EventTool.EventCheckingDelta")); + try { + waiter.waitAction(null); + } catch (InterruptedException e) { + output.printStackTrace(e); + } + } + + /** + * During {@code EventTool.WaitNoEventTimeout} time waits for true + * result of {@code checkNoEvent(long)} method. + * + * @param waitTime Quiet time (millisecons). + * @see #checkNoEvent(long) + * @see #waitNoEvent(long, long) + * @throws TimeoutExpiredException + */ + public void waitNoEvent(long waitTime) { + ListenerSet ls = listenerSet; + if (ls != null) { + // surprisingly this field can be null in case of massive + // garbage collecting efforts like in NbTestCase.assertGC + waitNoEvent(ls.getTheWholeMask(), waitTime); + } + } + + private AWTEvent waitEvent(long eventMask, long waitTime, TestOut waiterOutput) { + EventWaiter waiter = new EventWaiter(eventMask); + waiter.setTimeouts(timeouts.cloneThis()); + waiter.setOutput(waiterOutput); + waiter.getTimeouts(). + setTimeout("Waiter.WaitingTime", + waitTime); + waiter.getTimeouts(). + setTimeout("Waiter.TimeDelta", + timeouts.getTimeout("EventTool.EventCheckingDelta")); + try { + return waiter.waitAction(null); + } catch (InterruptedException e) { + output.printStackTrace(e); + return null; + } + } + + private boolean checkNoEvent(long eventMask, long waitTime, TestOut waiterOutput) { + try { + AWTEvent event = waitEvent(eventMask, waitTime, TestOut.getNullOutput()); + waiterOutput.printLine("AWT event was produced during waiting: "); + // used instead of event.toString() because it is not thread safe + waiterOutput.printLine(event.getClass().getName()); + return false; + } catch (TimeoutExpiredException e) { + return true; + } + } + + private static class EventType implements AWTEventListener { + + long eventMask; + long eventTime; + private Reference eventRef; + + public EventType(long eventMask) { + this.eventMask = eventMask; + eventRef = new WeakReference<>(null); + eventTime = -1; + } + + @Override + public void eventDispatched(AWTEvent event) { + eventRef = new WeakReference<>(event); + eventTime = System.currentTimeMillis(); + } + + public AWTEvent getEvent() { + return eventRef.get(); + } + + public long getTime() { + return eventTime; + } + + public long getEventMask() { + return eventMask; + } + } + + private static class ListenerSet { + + private Vector eventTypes; + private long theWholeMask; + + public ListenerSet() { + eventTypes = new Vector<>(); + try { + Class eventClass = Class.forName("java.awt.AWTEvent"); + Field[] fields = eventClass.getFields(); + theWholeMask = 0; + long eventMask; + for (Field field : fields) { + if ((field.getModifiers() + & (Modifier.PUBLIC | Modifier.STATIC)) != 0 + && field.getType().equals(Long.TYPE) + && field.getName().endsWith("_EVENT_MASK")) { + eventMask = (Long) field.get(null); + eventTypes.add(new EventType(eventMask)); + theWholeMask = theWholeMask | eventMask; + } + } + } catch (ClassNotFoundException | IllegalAccessException e) { + JemmyProperties.getCurrentOutput().printStackTrace(e); + } + } + + public void addListeners(long eventMask) { + Toolkit dtk = Toolkit.getDefaultToolkit(); + for (EventType et : eventTypes) { + if ((et.getEventMask() & eventMask) != 0) { + dtk.addAWTEventListener(et, et.getEventMask()); + } + } + } + + public void addListeners() { + addListeners(getTheWholeMask()); + } + + public void removeListeners() { + Toolkit dtk = Toolkit.getDefaultToolkit(); + for (EventType eventType : eventTypes) { + dtk.removeAWTEventListener(eventType); + } + } + + public long getTheWholeMask() { + return theWholeMask; + } + + public long getLastEventTime(long eventMask) { + EventType et = getLastEventType(eventMask); + return (et == null) ? -1 : et.getTime(); + } + + public AWTEvent getLastEvent(long eventMask) { + EventType et = getLastEventType(eventMask); + return (et == null) ? null : et.getEvent(); + } + + private EventType getLastEventType(long eventMask) { + long maxTime = -1; + EventType maxType = null; + for (EventType et : eventTypes) { + if ((eventMask & et.getEventMask()) != 0 + && et.getTime() > maxTime) { + maxType = et; + maxTime = maxType.getTime(); + } + } + return maxType; + } + } + + private static class EventWaiter extends Waiter { + + long eventMask; + long startTime; + + public EventWaiter(long eventMask) { + this.eventMask = eventMask; + startTime = getLastEventTime(eventMask); + } + + @Override + public AWTEvent actionProduced(Void obj) { + EventType et = listenerSet.getLastEventType(eventMask); + if (et != null + && et.getTime() > startTime) { + return et.getEvent(); + } else { + return null; + } + } + + @Override + public String getDescription() { + return ("Last event under " + + Long.toString(eventMask, 2) + " event mask"); + } + + @Override + public String toString() { + return "EventWaiter{" + "eventMask=" + Long.toString(eventMask, 2) + ", startTime=" + startTime + '}'; + } + } + + private class NoEventWaiter extends Waiter { + + long eventMask; + long waitTime; + + public NoEventWaiter(long eventMask, long waitTime) { + this.eventMask = eventMask; + this.waitTime = waitTime; + } + + @Override + public String actionProduced(Void obj) { + return (checkNoEvent(eventMask, waitTime, TestOut.getNullOutput()) + ? "Reached!" + : null); + } + + @Override + public String getDescription() { + return ("No event under " + + Long.toString(eventMask, 2) + + " event mask during " + + Long.toString(waitTime) + + " milliseconds"); + } + + @Override + public String toString() { + return "NoEventWaiter{" + "eventMask=" + Long.toString(eventMask, 2) + ", waitTime=" + waitTime + '}'; + } + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/FrameWaiter.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/FrameWaiter.java new file mode 100644 index 00000000000..3ca7c5408cd --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/FrameWaiter.java @@ -0,0 +1,465 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy; + +import java.awt.Component; +import java.awt.Frame; + +/** + * + * Contains methods to search and wait Frame. A FrameWaiter is a utility class + * used to look or wait for Frames. It contains methods to search for a Frame + * among the currently showing Frames as well as methods that wait for a Frame + * to show within an allotted time period. + * + *

Timeouts used:
+ * FrameWaiter.WaitFrameTimeout - time to wait frame displayed.
+ * FrameWaiter.AfterFrameTimeout - time to sleep after frame has been displayed. + *
+ * + * @see org.netbeans.jemmy.Timeouts + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + */ +public class FrameWaiter extends WindowWaiter implements Timeoutable, Outputable { + + private final static long WAIT_TIME = 60000; + private final static long AFTER_WAIT_TIME = 0; + + private Timeouts timeouts; + private TestOut output; + + /** + * Constructor. + */ + public FrameWaiter() { + super(); + setTimeouts(JemmyProperties.getProperties().getTimeouts()); + setOutput(JemmyProperties.getProperties().getOutput()); + } + + /** + * Searches for a Frame. Search among the currently showing Frames for one + * that meets the search criteria applied by the + * {@code ComponentChooser} parameter. + * + * @param cc A component chooser used to define and apply the search + * criteria. + * @return a reference to the first Frame that is showing and that meets the + * search criteria. If no such Frame can be found, a {@code null} + * reference is returned. + */ + public static Frame getFrame(ComponentChooser cc) { + return (Frame) WindowWaiter.getWindow(new FrameSubChooser(cc)); + } + + /** + * Searches for a Frame. The search proceeds among the currently showing + * Frames for the {@code index+1}'th Frame that meets the criteria + * defined and applied by the {@code ComonentChooser} parameter. + * + * @param cc A component chooser used to define and apply the search + * criteria. + * @param index The ordinal index of the Frame in the set of currently + * displayed Frames. The first index is 0. + * @return a reference to the {@code index+1}'th Frame that is showing + * and that meets the search criteria. If there are fewer than + * {@code index+1} Frames, a {@code null} reference is returned. + */ + public static Frame getFrame(ComponentChooser cc, int index) { + return (Frame) WindowWaiter.getWindow(new FrameSubChooser(cc), index); + } + + /** + * Searches for a Frame by title. The search proceeds among the currently + * showing Frames for the first with a suitable title. + * + * @param title Frame title or subtitle. + * @param ce If {@code true} and the search is case sensitive, then a + * match occurs when the {@code title} argument is a substring of a + * Frame title. If {@code false} and the search is case sensitive, then + * the {@code title} argument and the Frame title must be the same. If + * {@code true} and the search is case insensitive, then a match occurs + * when the {@code title} argument is a substring of the Frame title + * after changing both to upper case. If {@code false} and the search + * is case insensitive, then a match occurs when the {@code title} + * argument is a substring of the Frame title after changing both to upper + * case. + * @param cc If {@code true} the search is case sensitive; otherwise, + * the search is case insensitive. + * @return a reference to the first Frame that is showing and that has a + * suitable title. If no such Frame can be found, a {@code null} + * reference is returned. + */ + public static Frame getFrame(String title, boolean ce, boolean cc) { + return (Frame) WindowWaiter.getWindow(new FrameByTitleChooser(title, ce, cc)); + } + + /** + * Searches for a Frame by title. The search is for the + * {@code index+1}'th Frame among the currently showing Frames that + * possess a suitable title. + * + * @param title Frame title or subtitle. + * @param ce If {@code true} and the search is case sensitive, then a + * match occurs when the {@code title} argument is a substring of a + * Frame title. If {@code false} and the search is case sensitive, then + * the {@code title} argument and the Frame title must be the same. If + * {@code true} and the search is case insensitive, then a match occurs + * when the {@code title} argument is a substring of the Frame title + * after changing both to upper case. If {@code false} and the search + * is case insensitive, then a match occurs when the {@code title} + * argument is a substring of the Frame title after changing both to upper + * case. + * @param cc If {@code true} the search is case sensitive; otherwise, + * the search is case insensitive. + * @param index The ordinal index of the Frame in the set of currently + * displayed Frames. The first index is 0. + * @return a reference to the {@code index+1}'th Frame that is showing + * and that has a suitable title. If there are fewer than + * {@code index+1} Frames, a {@code null} reference is returned. + */ + public static Frame getFrame(String title, boolean ce, boolean cc, int index) { + return (Frame) WindowWaiter.getWindow(new FrameByTitleChooser(title, ce, cc), index); + } + + static { + Timeouts.initDefault("FrameWaiter.WaitFrameTimeout", WAIT_TIME); + Timeouts.initDefault("FrameWaiter.AfterFrameTimeout", AFTER_WAIT_TIME); + } + + /** + * Defines current timeouts. + * + * @param timeouts A collection of timeout assignments. + * @see org.netbeans.jemmy.Timeoutable + * @see org.netbeans.jemmy.Timeouts + * @see #getTimeouts + */ + @Override + public void setTimeouts(Timeouts timeouts) { + this.timeouts = timeouts; + Timeouts times = timeouts.cloneThis(); + times.setTimeout("WindowWaiter.WaitWindowTimeout", + timeouts.getTimeout("FrameWaiter.WaitFrameTimeout")); + times.setTimeout("WindowWaiter.AfterWindowTimeout", + timeouts.getTimeout("FrameWaiter.AfterFrameTimeout")); + super.setTimeouts(times); + } + + /** + * Return current timeouts. + * + * @return the collection of current timeout assignments. + * @see org.netbeans.jemmy.Timeoutable + * @see org.netbeans.jemmy.Timeouts + * @see #setTimeouts + */ + @Override + public Timeouts getTimeouts() { + return timeouts; + } + + /** + * Defines print output streams or writers. + * + * @param output Identify the streams or writers used for print output. + * @see org.netbeans.jemmy.Outputable + * @see org.netbeans.jemmy.TestOut + * @see #getOutput + */ + @Override + public void setOutput(TestOut output) { + this.output = output; + super.setOutput(output); + } + + /** + * Returns print output streams or writers. + * + * @return an object that contains references to objects for printing to + * output and err streams. + * @see org.netbeans.jemmy.Outputable + * @see org.netbeans.jemmy.TestOut + * @see #setOutput + */ + @Override + public TestOut getOutput() { + return output; + } + + /** + * Waits for a Frame to show. Wait for the {@code index+1}'th Frame + * that meets the criteria defined and applied by the + * {@code ComonentChooser} parameter to show up. + * + * @param ch A component chooser used to define and apply the search + * criteria. + * @param index The ordinal index of the Frame in the set of currently + * displayed Frames. The first index is 0. + * @return a reference to the {@code index+1}'th Frame that shows and + * that meets the search criteria. If fewer than {@code index+1} Frames + * show up in the allotted time period then a {@code null} reference is + * returned. + * @throws TimeoutExpiredException + * @see org.netbeans.jemmy.WindowWaiter#actionProduced(Object) + * @exception InterruptedException + */ + public Frame waitFrame(ComponentChooser ch, int index) + throws InterruptedException { + setTimeouts(timeouts); + return (Frame) waitWindow(new FrameSubChooser(ch), index); + } + + /** + * Waits for a Frame to show. Wait for a Frame that meets the search + * criteria applied by the {@code ComponentChooser} parameter to show + * up. + * + * @param ch A component chooser used to define and apply the search + * criteria. + * @return a reference to the first Frame that shows and that meets the + * search criteria. If no such Frame can be found within the time period + * allotted, a {@code null} reference is returned. + * @throws TimeoutExpiredException + * @see org.netbeans.jemmy.WindowWaiter#actionProduced(Object) + * @exception InterruptedException + */ + public Frame waitFrame(ComponentChooser ch) + throws InterruptedException { + return waitFrame(ch, 0); + } + + /** + * Waits for a Frame to show. Wait for the {@code index+1}'th Frame to + * show with a suitable title. + * + * @param title Frame title or subtitle. + * @param compareExactly If {@code true} and the search is case + * sensitive, then a match occurs when the {@code title} argument is a + * substring of a Frame title. If {@code false} and the search is case + * sensitive, then the {@code title} argument and the Frame title must + * be the same. If {@code true} and the search is case insensitive, + * then a match occurs when the {@code title} argument is a substring + * of the Frame title after changing both to upper case. If + * {@code false} and the search is case insensitive, then a match + * occurs when the {@code title} argument is a substring of the Frame + * title after changing both to upper case. + * @param compareCaseSensitive If {@code true} the search is case + * sensitive; otherwise, the search is case insensitive. + * @param index The ordinal index of the Frame in the set of currently + * displayed Frames with the proper window ownership and a suitable title. + * The first index is 0. + * @return a reference to the {@code index+1}'th Frame to show and that + * has a suitable title. If no such Frame can be found within the time + * period allotted, a {@code null} reference is returned. + * @throws TimeoutExpiredException + * @see org.netbeans.jemmy.WindowWaiter#actionProduced(Object) + * @exception InterruptedException + */ + public Frame waitFrame(String title, boolean compareExactly, boolean compareCaseSensitive, int index) + throws InterruptedException { + return waitFrame(new FrameByTitleChooser(title, compareExactly, compareCaseSensitive), index); + } + + /** + * Waits for a Frame to show. Wait for the first Frame to show with a + * suitable title. + * + * @param title Frame title or subtitle. + * @param compareExactly If {@code true} and the search is case + * sensitive, then a match occurs when the {@code title} argument is a + * substring of a Frame title. If {@code false} and the search is case + * sensitive, then the {@code title} argument and the Frame title must + * be the same. If {@code true} and the search is case insensitive, + * then a match occurs when the {@code title} argument is a substring + * of the Frame title after changing both to upper case. If + * {@code false} and the search is case insensitive, then a match + * occurs when the {@code title} argument is a substring of the Frame + * title after changing both to upper case. + * @param compareCaseSensitive If {@code true} the search is case + * sensitive; otherwise, the search is case insensitive. + * @return a reference to the first Frame to show and that has a suitable + * title. If no such Frame can be found within the time period allotted, a + * {@code null} reference is returned. + * @throws TimeoutExpiredException + * @see org.netbeans.jemmy.WindowWaiter#actionProduced(Object) + * @exception InterruptedException + */ + public Frame waitFrame(String title, boolean compareExactly, boolean compareCaseSensitive) + throws InterruptedException { + return waitFrame(title, compareExactly, compareCaseSensitive, 0); + } + + /** + * @see Waiter#getWaitingStartedMessage() + */ + @Override + protected String getWaitingStartedMessage() { + return "Start to wait frame \"" + getComponentChooser().getDescription() + "\" opened"; + } + + /** + * Overrides WindowWaiter.getTimeoutExpiredMessage. Returns the timeout + * expired message value. + * + * @param timeSpent Time spent for waiting + * @return a message tring + * @see Waiter#getTimeoutExpiredMessage(long) + */ + @Override + protected String getTimeoutExpiredMessage(long timeSpent) { + return ("Frame \"" + getComponentChooser().getDescription() + "\" has not been opened in " + + timeSpent + " milliseconds"); + } + + /** + * Overrides WindowWaiter.getActionProducedMessage. Returns the action + * produced message value. + * + * @param timeSpent Time spent for waiting. + * @param result A message string. + * @return a message tring + * @see Waiter#getActionProducedMessage(long, Object) + */ + @Override + protected String getActionProducedMessage(long timeSpent, final Object result) { + String resultToString = null; + if (result instanceof Component) { + // run toString in dispatch thread + resultToString = new QueueTool().invokeSmoothly( + new QueueTool.QueueAction("result.toString()") { + @Override + public String launch() { + return result.toString(); + } + } + ); + } else { + resultToString = result.toString(); + } + return ("Frame \"" + getComponentChooser().getDescription() + "\" has been opened in " + + timeSpent + " milliseconds" + + "\n " + resultToString); + } + + /** + * @see Waiter#getGoldenWaitingStartedMessage() + */ + @Override + protected String getGoldenWaitingStartedMessage() { + return "Start to wait frame \"" + getComponentChooser().getDescription() + "\" opened"; + } + + /** + * @see Waiter#getGoldenTimeoutExpiredMessage() + */ + @Override + protected String getGoldenTimeoutExpiredMessage() { + return "Frame \"" + getComponentChooser().getDescription() + "\" has not been opened"; + } + + /** + * @see Waiter#getGoldenActionProducedMessage() + */ + @Override + protected String getGoldenActionProducedMessage() { + return "Frame \"" + getComponentChooser().getDescription() + "\" has been opened"; + } + + private static class FrameSubChooser implements ComponentChooser { + + private ComponentChooser chooser; + + public FrameSubChooser(ComponentChooser c) { + super(); + chooser = c; + } + + @Override + public boolean checkComponent(Component comp) { + if (comp instanceof Frame) { + return ((FIND_INVISIBLE_WINDOWS || (comp.isShowing() && comp.isVisible())) + && chooser.checkComponent(comp)); + } else { + return false; + } + } + + @Override + public String getDescription() { + return chooser.getDescription(); + } + + @Override + public String toString() { + return "FrameSubChooser{" + "chooser=" + chooser + '}'; + } + } + + private static class FrameByTitleChooser implements ComponentChooser { + + String title; + boolean compareExactly; + boolean compareCaseSensitive; + + public FrameByTitleChooser(String t, boolean ce, boolean cc) { + super(); + title = t; + compareExactly = ce; + compareCaseSensitive = cc; + } + + @Override + public boolean checkComponent(Component comp) { + if (comp instanceof Frame) { + if ((FIND_INVISIBLE_WINDOWS || (comp.isShowing() && comp.isVisible())) + && ((Frame) comp).getTitle() != null) { + String titleToComp = ((Frame) comp).getTitle(); + String contextToComp = title; + if (compareCaseSensitive) { + titleToComp = titleToComp.toUpperCase(); + contextToComp = contextToComp.toUpperCase(); + } + if (compareExactly) { + return titleToComp.equals(contextToComp); + } else { + return titleToComp.contains(contextToComp); + } + } + } + return false; + } + + @Override + public String getDescription() { + return title; + } + + @Override + public String toString() { + return "FrameByTitleChooser{" + "title=" + title + ", compareExactly=" + compareExactly + ", compareCaseSensitive=" + compareCaseSensitive + '}'; + } + } + +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/JemmyException.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/JemmyException.java new file mode 100644 index 00000000000..a704af0cfea --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/JemmyException.java @@ -0,0 +1,161 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy; + +import java.io.PrintStream; +import java.io.PrintWriter; + +/** + * + * Parent of all Jemmy exceptions. Exception can be throught from inside jemmy + * methods, if some exception occurs from code invoked from jemmy. + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + */ +public class JemmyException extends RuntimeException { + + private static final long serialVersionUID = 42L; + private Throwable innerException = null; + private Object object = null; + + /** + * Constructor. + * + * @param description An exception description. + */ + public JemmyException(String description) { + super(description); + } + + /** + * Constructor. + * + * @param description An exception description. + * @param innerException Exception from code invoked from jemmy. + */ + public JemmyException(String description, Throwable innerException) { + this(description); + this.innerException = innerException; + } + + /** + * Constructor. + * + * @param description An exception description. + * @param object Object regarding which exception is thrown. + */ + public JemmyException(String description, Object object) { + this(description); + this.object = object; + } + + /** + * Constructor. + * + * @param description An exception description. + * @param innerException Exception from code invoked from jemmy. + * @param object Object regarding which exception is thrown. + */ + public JemmyException(String description, Throwable innerException, Object object) { + this(description, innerException); + this.object = object; + } + + /** + * Returns "object" constructor parameter. + * + * @return the Object value associated with the exception. + */ + public Object getObject() { + return object; + } + + /** + * Returns inner exception. + * + * @return An inner exception. + * @deprecated Use getInnerThrowable() + */ + @Deprecated + public Exception getInnerException() { + if (innerException instanceof Exception) { + return (Exception) innerException; + } else { + return null; + } + } + + /** + * Returns inner throwable. + * + * @return An inner throwable. + */ + public Throwable getInnerThrowable() { + return innerException; + } + + /** + * Prints stack trace into System.out. + */ + @Override + public void printStackTrace() { + printStackTrace(System.out); + } + + /** + * Prints stack trace. + * + * @param ps PrintStream to print stack trace into. + */ + @Override + public void printStackTrace(PrintStream ps) { + super.printStackTrace(ps); + if (innerException != null) { + ps.println("Inner exception:"); + innerException.printStackTrace(ps); + } + if (object != null) { + ps.println("Object:"); + ps.println(object.toString()); + } + } + + /** + * Prints stack trace. + * + * @param pw PrintWriter to print stack trace into. + * + */ + @Override + public void printStackTrace(PrintWriter pw) { + super.printStackTrace(pw); + if (innerException != null) { + pw.println("Inner exception:"); + innerException.printStackTrace(pw); + } + if (object != null) { + pw.println("Object:"); + pw.println(object.toString()); + } + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/JemmyInputException.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/JemmyInputException.java new file mode 100644 index 00000000000..f38fed9a9a0 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/JemmyInputException.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy; + +import java.awt.Component; + +/** + * + * Exception can be thrown as a result of incorrect input operations. + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + */ +public class JemmyInputException extends JemmyException { + + private static final long serialVersionUID = 42L; + + /** + * Constructor. + * + * @param comp Component regarding which exception is thrown. + */ + public JemmyInputException(Component comp) { + super("Input exception", comp); + } + + /** + * Constructor. + * + * @param message A descriptive message. + * @param comp Component regarding which exception is thrown. + */ + public JemmyInputException(String message, Component comp) { + super(message, comp); + } + + /** + * Returns component. + * + * @return the Component associated with the exception. + */ + public Component getComponent() { + return (Component) getObject(); + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/JemmyProperties.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/JemmyProperties.java new file mode 100644 index 00000000000..02633b2f305 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/JemmyProperties.java @@ -0,0 +1,985 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy; + +import java.io.BufferedReader; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.lang.reflect.InvocationTargetException; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Properties; +import java.util.Stack; +import java.util.StringTokenizer; + +import org.netbeans.jemmy.drivers.APIDriverInstaller; +import org.netbeans.jemmy.drivers.DefaultDriverInstaller; +import org.netbeans.jemmy.drivers.DriverInstaller; +import org.netbeans.jemmy.drivers.InputDriverInstaller; +import org.netbeans.jemmy.explorer.GUIBrowser; + +/** + * + * Keeps default Jemmy properties. + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + * + */ +public class JemmyProperties { + + /** + * The event queue model mask. + * + * @see #getCurrentDispatchingModel() + * @see #setCurrentDispatchingModel(int) + */ + public static final int QUEUE_MODEL_MASK = 1; + + /** + * The robot using model mask. + * + * @see #getCurrentDispatchingModel() + * @see #setCurrentDispatchingModel(int) + */ + public static final int ROBOT_MODEL_MASK = 2; + + /** + * Event shorcutting model mask. Should not be used together with robot + * mask. + * + * @see #getCurrentDispatchingModel() + * @see #setCurrentDispatchingModel(int) + */ + public static final int SHORTCUT_MODEL_MASK = 4; + + /** + * The robot using model mask. + * + * @see #getCurrentDispatchingModel() + * @see #setCurrentDispatchingModel(int) + */ + public static final int SMOOTH_ROBOT_MODEL_MASK = 8; + + private static final int DEFAULT_DRAG_AND_DROP_STEP_LENGTH = 100; + private static final Stack propStack = new Stack<>(); + + Hashtable properties; + + /** + * + */ + protected JemmyProperties() { + super(); + properties = new Hashtable<>(); + setProperty("timeouts", new Timeouts()); + setProperty("output", new TestOut()); + setProperty("resources", new BundleManager()); + setProperty("binding.map", new DefaultCharBindingMap()); + setProperty("dispatching.model", getDefaultDispatchingModel()); + setProperty("drag_and_drop.step_length", DEFAULT_DRAG_AND_DROP_STEP_LENGTH); + } + + /** + * Returns major version (like 1.0). + * + * @return a String representing the major version value. + */ + public static String getMajorVersion() { + return (extractValue(getProperties().getClass(). + getClassLoader().getResourceAsStream("org/netbeans/jemmy/version_info"), + "Jemmy-MajorVersion")); + } + + /** + * Returns minor version (like 1). + * + * @return a String representing the minor version value. + */ + public static String getMinorVersion() { + return (extractValue(getProperties().getClass(). + getClassLoader().getResourceAsStream("org/netbeans/jemmy/version_info"), + "Jemmy-MinorVersion")); + } + + /** + * Returns build (like 20011231 (yyyymmdd)). + * + * @return a String representing the build value. + */ + public static String getBuild() { + return (extractValue(getProperties().getClass(). + getClassLoader().getResourceAsStream("org/netbeans/jemmy/version_info"), + "Jemmy-Build")); + } + + /** + * Returns full version string (like 1.0.1-20011231). + * + * @return a String representing the full version value. + */ + public static String getFullVersion() { + return (getMajorVersion() + "." + + getMinorVersion() + "-" + + getBuild()); + } + + /** + * Returns version string (like 1.0.1). + * + * @return a String representing the short version value. + */ + public static String getVersion() { + return (getMajorVersion() + "." + + getMinorVersion()); + } + + /** + * Creates a copy of the current JemmyProperties object and pushes it into + * the properties stack. + * + * @return New current properties. + */ + public static JemmyProperties push() { + return push(getProperties().cloneThis()); + } + + /** + * Pops last pushed properties from the properties stack. If stack has just + * one element, does nothing. + * + * @return Poped properties. + */ + public static JemmyProperties pop() { + JemmyProperties result = propStack.pop(); + if (propStack.isEmpty()) { + propStack.push(result); + } + return result; + } + + /** + * Just like getProperties().getProperty(propertyName). + * + * @param propertyName a property key + * @return a property value + * @see #setCurrentProperty + * @see #setCurrentTimeout + */ + public static Object getCurrentProperty(String propertyName) { + return getProperties().getProperty(propertyName); + } + + /** + * Just like getProperties().setProperty(propertyName, propertyValue). + * + * @param propertyName a property key + * @param propertyValue a property value + * @return previous property value + * @see #getCurrentProperty + * @see #getCurrentTimeout + */ + public static Object setCurrentProperty(String propertyName, Object propertyValue) { + return getProperties().setProperty(propertyName, propertyValue); + } + + /** + * Removes a property from current properties list. + * + * @param propertyName a property key. + * @return previous property value + */ + public static Object removeCurrentProperty(String propertyName) { + return getProperties().removeProperty(propertyName); + } + + /** + * Returns the current key values. + * + * @return an array of Strings representing the current key values + */ + public static String[] getCurrentKeys() { + return getProperties().getKeys(); + } + + /** + * Just like getProperties().getTimeouts(). + * + * @return a Timeouts object representing the current timeouts. + * @see #setCurrentTimeouts + */ + public static Timeouts getCurrentTimeouts() { + return getProperties().getTimeouts(); + } + + /** + * Just like getProperties().setTimeouts(to). + * + * @param to New timeouts + * @return old timeouts. + * @see #getCurrentTimeouts + */ + public static Timeouts setCurrentTimeouts(Timeouts to) { + return getProperties().setTimeouts(to); + } + + /** + * Just like getProperties().getTimeouts().setTimeout(name, newValue). + * + * @param name a timeout name + * @param newValue a timeout value + * @return previous timeout value + * @see #getCurrentTimeout + */ + public static long setCurrentTimeout(String name, long newValue) { + return getProperties().getTimeouts().setTimeout(name, newValue); + } + + /** + * Just like getProperties().getTimeouts().getTimeout(name). + * + * @param name a timeout name + * @return a timeout value + * @see #setCurrentTimeout + */ + public static long getCurrentTimeout(String name) { + return getProperties().getTimeouts().getTimeout(name); + } + + /** + * Just like getProperties().getTimeouts().initTimeout(name, newValue). + * + * @param name a timeout name + * @param newValue a timeout value + * @return a timeout value + * @see #setCurrentTimeout + */ + public static long initCurrentTimeout(String name, long newValue) { + return getProperties().getTimeouts().initTimeout(name, newValue); + } + + /** + * Just like getProperties().getOutput(). + * + * @return a TestOut object representing the current output. + * @see #setCurrentOutput + */ + public static TestOut getCurrentOutput() { + return getProperties().getOutput(); + } + + /** + * Just like getProperties().setOutput(out). + * + * @param out new output + * @return a TestOut object representing the current output. + * @see #getCurrentOutput + */ + public static TestOut setCurrentOutput(TestOut out) { + return getProperties().setOutput(out); + } + + /** + * Just like getProperties().getBundleManager(). + * + * @return a BundleManager object representing the current bundle manager. + * @see #setCurrentBundleManager + */ + public static BundleManager getCurrentBundleManager() { + return getProperties().getBundleManager(); + } + + /** + * Just like getProperties().setBundleManager(resources). + * + * @param resources new BundleManager + * @return a BundleManager object representing the current bundle manager. + * @see #getCurrentBundleManager + */ + public static BundleManager setCurrentBundleManager(BundleManager resources) { + return getProperties().setBundleManager(resources); + } + + /** + * Just like getProperties().getBundleManager().getResource(key). + * + * @param key a resource key. + * @return a resource value + */ + public static String getCurrentResource(String key) { + return getProperties().getBundleManager().getResource(key); + } + + /** + * Just like getProperties().getBundleManager().getResource(bundleID, key). + * + * @param key a resource key. + * @param bundleID a bundle ID + * @return a resource value + */ + public static String getCurrentResource(String bundleID, String key) { + return getProperties().getBundleManager().getResource(bundleID, key); + } + + /** + * Just like getProperties().getCharBindingMap(). + * + * @return a CharBindingMap object representing the current char binding + * map. + * @see #setCurrentCharBindingMap + */ + public static CharBindingMap getCurrentCharBindingMap() { + return getProperties().getCharBindingMap(); + } + + /** + * Just like getProperties().setCharBindingMap(map). + * + * @param map new CharBindingMap. + * @return old CharBindingMap object. + * @see #getCurrentCharBindingMap + */ + public static CharBindingMap setCurrentCharBindingMap(CharBindingMap map) { + return getProperties().setCharBindingMap(map); + } + + /** + * Returns the current dispatching model. + * + * @return Event dispatching model. + * @see #getDispatchingModel() + * @see #setCurrentDispatchingModel(int) + * @see #QUEUE_MODEL_MASK + * @see #ROBOT_MODEL_MASK + */ + public static int getCurrentDispatchingModel() { + return getProperties().getDispatchingModel(); + } + + /** + * Defines event dispatching model. If (model & ROBOT_MODEL_MASK) != 0 + * java.awt.Robot class is used to reproduce user actions, otherwise actions + * are reproduced by event posting. If (model & QUEUE_MODEL_MASK) != 0 + * actions are reproduced through event queue. + * + * @param model New dispatching model value. + * @return Previous dispatching model value. + * @see #setDispatchingModel(int) + * @see #getCurrentDispatchingModel() + * @see #QUEUE_MODEL_MASK + * @see #ROBOT_MODEL_MASK + * @see #initDispatchingModel(boolean, boolean) + * @see #initDispatchingModel() + */ + public static int setCurrentDispatchingModel(int model) { + return getProperties().setDispatchingModel(model); + } + + /** + * Returns default event dispatching model. + * + * @return QUEUE_MODEL_MASK + * @see #setCurrentDispatchingModel(int) + * @see #QUEUE_MODEL_MASK + * @see #ROBOT_MODEL_MASK + */ + public static int getDefaultDispatchingModel() { + return SHORTCUT_MODEL_MASK | QUEUE_MODEL_MASK; + } + + /** + * Returns the current drag and drop step length value. + * + * @return Pixel count to move mouse during one drag'n'drop step. + * @see #getDragAndDropStepLength() + * @see #setCurrentDragAndDropStepLength(int) + */ + public static int getCurrentDragAndDropStepLength() { + return getProperties().getDragAndDropStepLength(); + } + + /** + * Specifies the current drag and drop step length value. + * + * @param model Pixel count to move mouse during one drag'n'drop step. + * @return Previous value. + * @see #setDragAndDropStepLength(int) + * @see #getCurrentDragAndDropStepLength() + */ + public static int setCurrentDragAndDropStepLength(int model) { + return getProperties().setDragAndDropStepLength(model); + } + + /** + * Peeks upper JemmyProperties instance from stack. + * + * @return a JemmyProperties object representing the properties value. + */ + public static JemmyProperties getProperties() { + if (propStack.empty()) { + propStack.add(new JemmyProperties()); + } + return propStack.peek(); + } + + /** + * Prints full version into standard output. + * + * @param argv Application args. + */ + public static void main(String[] argv) { + if (argv.length == 0) { + System.out.println("Jemmy version : " + getVersion()); + } else if (argv.length == 1 + && argv[0].equals("-f")) { + System.out.println("Jemmy full version : " + getFullVersion()); + } else if (argv.length > 0 + && argv[0].equals("-e")) { + String[] newArgv = new String[argv.length - 1]; + System.arraycopy(argv, 1, newArgv, 0, argv.length - 1); + GUIBrowser.main(newArgv); + } else { + System.out.println("Parameters: "); + System.out.println(" - report Jemmy version."); + System.out.println("\"-f\" - report full jemmy version."); + } + } + + /** + * Pushes properties stack. + * + * @param props a JemmyProperties instance to put into the stack head. + * @return a JemmyProperties object. + */ + protected static JemmyProperties push(JemmyProperties props) { + return propStack.push(props); + } + + static { + setCurrentDispatchingModel(getDefaultDispatchingModel()); + } + + /** + * Method to initialize timeouts and resources. + * + * @param prop_file File to get filenames from.
+ * Can contain definition of variables TIMEOUTS_FILE - full path to timeouts + * file,
+ * RESOURCE_FILE - full path to resource file. + * @see org.netbeans.jemmy.JemmyProperties#initProperties() + */ + public void initProperties(String prop_file) { + try { + getOutput().printLine("Loading properties from " + prop_file + " file"); + Properties props = new Properties(); + try (FileInputStream fileStream = new FileInputStream(prop_file)) { + props.load(fileStream); + } + if (props.getProperty("TIMEOUTS_FILE") != null + && !props.getProperty("TIMEOUTS_FILE").equals("")) { + getOutput().printLine("Loading timeouts from " + props.getProperty("TIMEOUTS_FILE") + + " file"); + getTimeouts().loadDefaults(props.getProperty("TIMEOUTS_FILE")); + } + if (props.getProperty("RESOURCE_FILE") != null + && !props.getProperty("RESOURCE_FILE").equals("")) { + getOutput().printLine("Loading resources from " + props.getProperty("RESOURCE_FILE") + + " file"); + getBundleManager().loadBundleFromFile(props.getProperty("RESOURCE_FILE"), ""); + } + } catch (IOException e) { + getOutput().printStackTrace(e); + } + } + + /** + * Method to initialize timeouts and resources.
+ * Uses jemmy.properties system property to find file. + * + * @see org.netbeans.jemmy.JemmyProperties#initProperties(String) + */ + public void initProperties() { + if (System.getProperty("jemmy.properties") != null + && !System.getProperty("jemmy.properties").equals("")) { + initProperties(System.getProperty("jemmy.properties")); + } else { + try { + getTimeouts().load(); + getBundleManager().load(); + } catch (IOException e) { + getOutput().printStackTrace(e); + } + } + } + + /** + * Initializes dispatching model. + * + * @param queue Notifies that event queue dispatching should be used. + * @param robot Notifies that robot dispatching should be used. + * @param shortcut Notifies that event shorcutting should be used. + */ + public void initDispatchingModel(boolean queue, boolean robot, boolean shortcut) { + initDispatchingModel(queue, robot, shortcut, false); + } + + /** + * Initializes dispatching model. + * + * @param queue Notifies that event queue dispatching should be used. + * @param robot Notifies that robot dispatching should be used. + * @param shortcut Notifies that event shorcutting should be used. + */ + public void initDispatchingModel(boolean queue, boolean robot, boolean shortcut, boolean smooth) { + int model = getDefaultDispatchingModel(); + getOutput().print("Reproduce user actions "); + if (queue) { + model = QUEUE_MODEL_MASK; + getOutput().printLine("through event queue."); + } else { + model = model - (model & QUEUE_MODEL_MASK); + getOutput().printLine("directly."); + } + getOutput().print("Use "); + if (robot) { + model = model | ROBOT_MODEL_MASK; + getOutput().print("java.awt.Robot class"); + } else { + model = model - (model & ROBOT_MODEL_MASK); + getOutput().print("event dispatching"); + } + if (smooth) { + model = model | SMOOTH_ROBOT_MODEL_MASK; + } else { + model = model - (model & SMOOTH_ROBOT_MODEL_MASK); + } + getOutput().printLine(" to reproduce user actions"); + if (shortcut) { + model = model | SHORTCUT_MODEL_MASK; + getOutput().print("Shortcut"); + } else { + model = model - (model & SHORTCUT_MODEL_MASK); + getOutput().print("Dispatch"); + } + getOutput().printLine(" test events"); + setDispatchingModel(model); + } + + /** + * Initializes dispatching model. + * + * @param queue Notifies that event queue dispatching should be used. + * @param robot Notifies that robot dispatching should be used. + */ + public void initDispatchingModel(boolean queue, boolean robot) { + this.initDispatchingModel(queue, robot, false); + } + + /** + * Initializes dispatching model. Uses "jemmy.queue_dispatching" and + * "jemmy.robot_dispatching" system properties to determine what model + * should be used. Possible values for the both properties:
+ * "off" - switch mode off.
+ * "on" - switch mode on.
+ * "" - use default value. + * + * @see #getDefaultDispatchingModel() + */ + public void initDispatchingModel() { + boolean qmask = ((getDefaultDispatchingModel() & QUEUE_MODEL_MASK) != 0); + boolean rmask = ((getDefaultDispatchingModel() & ROBOT_MODEL_MASK) != 0); + boolean srmask = ((getDefaultDispatchingModel() & SMOOTH_ROBOT_MODEL_MASK) != 0); + boolean smask = ((getDefaultDispatchingModel() & SHORTCUT_MODEL_MASK) != 0); + if (System.getProperty("jemmy.queue_dispatching") != null + && !System.getProperty("jemmy.queue_dispatching").equals("")) { + qmask = System.getProperty("jemmy.queue_dispatching").equals("on"); + } + if (System.getProperty("jemmy.robot_dispatching") != null + && !System.getProperty("jemmy.robot_dispatching").equals("")) { + rmask = System.getProperty("jemmy.robot_dispatching").equals("on"); + } + if (System.getProperty("jemmy.smooth_robot_dispatching") != null + && !System.getProperty("jemmy.smooth_robot_dispatching").equals("")) { + srmask = System.getProperty("jemmy.smooth_robot_dispatching").equals("on"); + } + if (System.getProperty("jemmy.shortcut_events") != null + && !System.getProperty("jemmy.shortcut_events").equals("")) { + smask = System.getProperty("jemmy.shortcut_events").equals("on"); + } + initDispatchingModel(qmask, rmask, smask, srmask); + } + + /** + * Inits properties and dispatching model from system environment variables. + * + * @see #initProperties() + * @see #initDispatchingModel() + */ + public void init() { + initProperties(); + initDispatchingModel(); + } + + /** + * Returns timeouts. + * + * @return the Timeouts value. + * @see #setTimeouts + */ + public Timeouts getTimeouts() { + return (Timeouts) getProperty("timeouts"); + } + + /** + * Changes timeouts. + * + * @param to new timeouts. + * @return old timeouts. + * @see #getTimeouts + */ + public Timeouts setTimeouts(Timeouts to) { + return (Timeouts) setProperty("timeouts", to); + } + + /** + * Changes a timeouts value. + * + * @param name Timeout name + * @param newValue New timeout value + * @return previous timeout value + * @see #getTimeout + */ + public long setTimeout(String name, long newValue) { + return getTimeouts().setTimeout(name, newValue); + } + + /** + * Returns a timeouts value. + * + * @param name Timeout name + * @return a timeout value + * @see #setTimeout + */ + public long getTimeout(String name) { + return getTimeouts().getTimeout(name); + } + + /** + * Inits a timeouts value. + * + * @param name Timeout name + * @param newValue New timeout value + * @return a timeout value + */ + public long initTimeout(String name, long newValue) { + return getTimeouts().initTimeout(name, newValue); + } + + /** + * Returns output. + * + * @return a TestOut object representing the output value + * @see #setOutput + */ + public TestOut getOutput() { + return (TestOut) getProperty("output"); + } + + /** + * Changes output. + * + * @param out new output. + * @return old output. + * @see #getOutput + */ + public TestOut setOutput(TestOut out) { + return (TestOut) setProperty("output", out); + } + + /** + * Returns bundle manager. + * + * @return a BundleManager object representing the bundle manager value. + * @see #setBundleManager + */ + public BundleManager getBundleManager() { + return (BundleManager) getProperty("resources"); + } + + /** + * Changes bundle manager. + * + * @param resources new bundle manager. + * @return old bundle manager + * @see #getBundleManager + */ + public BundleManager setBundleManager(BundleManager resources) { + return (BundleManager) setProperty("resources", resources); + } + + /** + * Returns resource value. + * + * @param key Resource key. + * @return resource value + */ + public String getResource(String key) { + return getBundleManager().getResource(key); + } + + /** + * Returns resource value from the specified bundle. + * + * @param bundleID Id of a bundle to get resource from. + * @param key Resource key. + * @return resource value + */ + public String getResource(String bundleID, String key) { + return getBundleManager().getResource(bundleID, key); + } + + /** + * Returns char binding map. + * + * @return the char binding map. + * @see #setCharBindingMap + */ + public CharBindingMap getCharBindingMap() { + return (CharBindingMap) getProperty("binding.map"); + } + + /** + * Changes char binding map. + * + * @param map new char binding map. + * @return old char binding map. + * @see #getCharBindingMap + */ + public CharBindingMap setCharBindingMap(CharBindingMap map) { + return (CharBindingMap) setProperty("binding.map", map); + } + + /** + * Returns the dispatching model. + * + * @return Event dispatching model. + * @see #getCurrentDispatchingModel() + * @see #setDispatchingModel(int) + * @see #QUEUE_MODEL_MASK + * @see #ROBOT_MODEL_MASK + */ + public int getDispatchingModel() { + return (Integer) getProperty("dispatching.model"); + } + + private static DriverInstaller getDriverInstaller(int model) { + String name = System.getProperty("jemmy.drivers.installer"); + DriverInstaller installer = null; + try { + if (name != null && !(name.length() == 0)) { + installer = (DriverInstaller) new ClassReference(name).newInstance(null, null); + } + } catch (ClassNotFoundException + | IllegalAccessException + | NoSuchMethodException + | InstantiationException + | InvocationTargetException e) { + getCurrentOutput().printLine("Cannot init driver installer:"); + getCurrentOutput().printStackTrace(e); + } + if (installer == null) { + if (System.getProperty("os.name").startsWith("Mac OS X")) { + installer = new APIDriverInstaller((model & SHORTCUT_MODEL_MASK) != 0); + } else { + installer = new DefaultDriverInstaller((model & SHORTCUT_MODEL_MASK) != 0); + } + } + getCurrentOutput().printLine("Using " + installer.getClass().getName() + " driver installer"); + return installer; + } + + /** + * Specifies the dispatching model value. + * + * @param model New dispatching model value. + * @return Previous dispatching model value. + * @see #setCurrentDispatchingModel(int) + * @see #getDispatchingModel() + * @see #QUEUE_MODEL_MASK + * @see #ROBOT_MODEL_MASK + */ + public int setDispatchingModel(int model) { + new InputDriverInstaller((model & ROBOT_MODEL_MASK) == 0, (model & SMOOTH_ROBOT_MODEL_MASK) != 0).install(); + getDriverInstaller(model).install(); + return (Integer) setProperty("dispatching.model", model); + } + + /** + * Returns the drag and drop step length value. + * + * @return Pixel count to move mouse during one drag'n'drop step. + * @see #getCurrentDragAndDropStepLength() + * @see #setDragAndDropStepLength(int) + */ + public int getDragAndDropStepLength() { + return (Integer) getProperty("drag_and_drop.step_length"); + } + + /** + * Specifies the drag and drop step length value. + * + * @param length Pixel count to move mouse during one drag'n'drop step. + * @return Previous value. + * @see #setCurrentDragAndDropStepLength(int) + * @see #getDragAndDropStepLength() + */ + public int setDragAndDropStepLength(int length) { + return (Integer) setProperty("drag_and_drop.step_length", length); + } + + /** + * Checks if "name" propery currently has a value. + * + * @param name Property name. Should by unique. + * @return true if property was defined. + * @see #setProperty(String, Object) + * @see #getProperty(String) + */ + public boolean contains(String name) { + return properties.containsKey(name); + } + + /** + * Saves object as a static link to be used by other objects. + * + * @param name Property name. Should by unique. + * @param newValue Property value. + * @return Previous value of "name" property. + * @see #setCurrentProperty(String, Object) + * @see #getProperty(String) + * @see #contains(String) + */ + public Object setProperty(String name, Object newValue) { + Object oldValue = null; + if (contains(name)) { + oldValue = properties.get(name); + properties.remove(name); + } + properties.put(name, newValue); + return oldValue; + } + + /** + * Returns the property value. + * + * @param name Property name. Should by unique. + * @return Property value stored by setProperty(String, Object) method. + * @see #getCurrentProperty(String) + * @see #setProperty(String, Object) + * @see #contains(String) + */ + public Object getProperty(String name) { + if (contains(name)) { + return properties.get(name); + } else { + return null; + } + } + + /** + * Removes the property. + * + * @param name A name of the property to be removed. + * @return previous property value + */ + public Object removeProperty(String name) { + if (contains(name)) { + return properties.remove(name); + } else { + return null; + } + } + + /** + * Returns the key values. + * + * @return an array of Strings representing the key values. + */ + public String[] getKeys() { + Enumeration keys = properties.keys(); + String[] result = new String[properties.size()]; + int i = 0; + while (keys.hasMoreElements()) { + result[i] = keys.nextElement(); + i++; + } + return result; + } + + /** + * Copy all properties from this instance into another. + * + * @param properties a JemmyProperties instance to copy properties into. + */ + public void copyTo(JemmyProperties properties) { + String[] keys = getKeys(); + for (String key : keys) { + properties.setProperty(key, getProperty(key)); + } + //some should be cloned + properties.setTimeouts(getTimeouts().cloneThis()); + properties.setBundleManager(getBundleManager().cloneThis()); + } + + /** + * Creates an exact copy on this instance. + * + * @return new JemmyProperties object. + */ + protected JemmyProperties cloneThis() { + JemmyProperties result = new JemmyProperties(); + copyTo(result); + return result; + } + + private static String extractValue(InputStream stream, String varName) { + try { + BufferedReader reader = new BufferedReader(new InputStreamReader(stream)); + StringTokenizer token; + String nextLine; + while ((nextLine = reader.readLine()) != null) { + token = new StringTokenizer(nextLine, ":"); + String nextToken = token.nextToken(); + if (nextToken.trim().equals(varName)) { + return token.nextToken().trim(); + } + } + return ""; + } catch (IOException e) { + getCurrentOutput().printStackTrace(e); + return ""; + } + } + +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/NoComponentUnderMouseException.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/NoComponentUnderMouseException.java new file mode 100644 index 00000000000..4bc7b1c0005 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/NoComponentUnderMouseException.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy; + +/** + * + * Exception can be throwht as a result of attempt to produce a mouse pressing + * when mouse is not over the java component. + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + */ +public class NoComponentUnderMouseException extends RuntimeException { + + private static final long serialVersionUID = 42L; + + /** + * Constructor. + */ + public NoComponentUnderMouseException() { + super("No component under the mouse!"); + } + +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/ObjectBrowser.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/ObjectBrowser.java new file mode 100644 index 00000000000..bf17fe8e361 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/ObjectBrowser.java @@ -0,0 +1,159 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; + +/** + * + * Class to display information about object: fields, methods, ancestors and so + * on. + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + */ +public class ObjectBrowser implements Outputable { + + private Object object; + + private TestOut output; + + /** + * Constructor. + */ + public ObjectBrowser() { + } + + /** + * Defines print output streams or writers. + * + * @param out Identify the streams or writers used for print output. + * @see org.netbeans.jemmy.Outputable + * @see org.netbeans.jemmy.TestOut + * @see #getOutput + */ + @Override + public void setOutput(TestOut out) { + output = out; + } + + /** + * Returns print output streams or writers. + * + * @return an object that contains references to objects for printing to + * output and err streams. + * @see org.netbeans.jemmy.Outputable + * @see org.netbeans.jemmy.TestOut + * @see #setOutput + */ + @Override + public TestOut getOutput() { + return output; + } + + /** + * Specifies the object value. + * + * @param obj Object to work with. + * @see #getObject + */ + public void setObject(Object obj) { + object = obj; + } + + /** + * Returns the object value. + * + * @return Current object. + * @see #setObject + */ + public Object getObject() { + return object; + } + + /** + * Prints {@code toString()} information. + */ + public void printToString() { + output.printLine(object.toString()); + } + + /** + * Prints object fields names and values. + */ + public void printFields() { + Class cl = object.getClass(); + output.printLine("Class: " + cl.getName()); + output.printLine("Fields: "); + Field[] fields = cl.getFields(); + for (Field field : fields) { + output.printLine(Modifier.toString(field.getModifiers()) + " " + + field.getType().getName() + " " + + field.getName()); + Object value = "Inaccessible"; + try { + value = field.get(object); + } catch (IllegalAccessException ignored) { + } + output.printLine(" Value: " + value.toString()); + } + } + + /** + * Prints object methods names and parameters. + */ + public void printMethods() { + Class cl = object.getClass(); + output.printLine("Class: " + cl.getName()); + output.printLine("Methods: "); + Method[] methods = cl.getMethods(); + for (Method method : methods) { + output.printLine(Modifier.toString(method.getModifiers()) + " " + + method.getReturnType().getName() + " " + + method.getName()); + Class[] params = method.getParameterTypes(); + for (Class param : params) { + output.printLine(" " + param.getName()); + } + } + } + + /** + * Prints allsuperclasses names. + */ + public void printClasses() { + Class cl = object.getClass(); + do { + output.printLine(cl.getName()); + } while ((cl = cl.getSuperclass()) != null); + } + + /** + * Prints everything. + */ + public void printFull() { + printFields(); + printMethods(); + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/Outputable.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/Outputable.java new file mode 100644 index 00000000000..832b311921f --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/Outputable.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy; + +/** + * Communicate the identity of the output streams or writers used by the + * application. Communicate the identity of the input stream, too. Any object + * with methods that generates print output should implement this interface. + * + * @see org.netbeans.jemmy.TestOut + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + */ +public interface Outputable { + + /** + * Defines print output streams or writers. + * + * @param out Identify the streams or writers used for print output. + * @see #getOutput + */ + public void setOutput(TestOut out); + + /** + * Returns print output streams or writers. + * + * @return an object that contains references to objects for printing to + * output and err streams. + * @see #setOutput + */ + public TestOut getOutput(); +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/QueueTool.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/QueueTool.java new file mode 100644 index 00000000000..dbb9d58f494 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/QueueTool.java @@ -0,0 +1,818 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy; + +import java.awt.AWTEvent; +import java.awt.EventQueue; +import java.awt.Toolkit; +import java.awt.event.InvocationEvent; +import java.lang.reflect.InvocationTargetException; + +/** + * + * Provides functionality to work with java.awt.EventQueue empty. + * + *

Timeouts used:
+ * QueueTool.WaitQueueEmptyTimeout - timeout to wait queue emptied
+ * QueueTool.QueueCheckingDelta - time delta to check result
+ * QueueTool.LockTimeout - time to wait queue locked after lock action has been + * put there
+ * QueueTool.InvocationTimeout - time for action was put into queue to be + * started
+ * QueueTool.MaximumLockingTime - maximum time to lock queue.
+ * + * @see Timeouts + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + * + */ +public class QueueTool implements Outputable, Timeoutable { + + private final static long WAIT_QUEUE_EMPTY_TIMEOUT = 180000; + private final static long QUEUE_CHECKING_DELTA = 10; + private final static long LOCK_TIMEOUT = 180000; + private final static long MAXIMUM_LOCKING_TIME = 180000; + private final static long INVOCATION_TIMEOUT = 180000; + + private static JemmyQueue jemmyQueue = null; + + private TestOut output; + private Timeouts timeouts; + private Locker locker; + private Waiter lockWaiter; + + /** + * Constructor. + */ + public QueueTool() { + locker = new Locker(); + lockWaiter = new Waiter(new Waitable() { + @Override + public String actionProduced(Void obj) { + return locker.isLocked() ? "" : null; + } + + @Override + public String getDescription() { + return "Event queue to be locked"; + } + + @Override + public String toString() { + return "QueueTool.Waiter{" + getDescription() + '}'; + } + }); + setOutput(JemmyProperties.getProperties().getOutput()); + setTimeouts(JemmyProperties.getProperties().getTimeouts()); + } + + /** + * Returns system EventQueue. + * + * @return system EventQueue. + */ + public static EventQueue getQueue() { + return Toolkit.getDefaultToolkit().getSystemEventQueue(); + } + + /** + * Map to {@code EventQueue.isDispatchThread()}. + * + * @return true if this thread is the AWT dispatching thread. + */ + public static boolean isDispatchThread() { + return EventQueue.isDispatchThread(); + } + + /** + * Checks if system event queue is empty. + * + * @return true if EventQueue is empty. + */ + public static boolean checkEmpty() { + return getQueue().peekEvent() == null; + } + + /** + * Shortcuts event if + * {@code ((JemmyProperties.getCurrentDispatchingModel() & JemmyProperties.SHORTCUT_MODEL_MASK) != 0)} + * and if executed in the dispatch thread. Otherwise posts event. + * + * @param event Event to dispatch. + */ + public static void processEvent(AWTEvent event) { + if ((JemmyProperties.getCurrentDispatchingModel() + & JemmyProperties.SHORTCUT_MODEL_MASK) != 0) { + installQueue(); + } + if ((JemmyProperties.getCurrentDispatchingModel() + & JemmyProperties.SHORTCUT_MODEL_MASK) != 0 + && isDispatchThread()) { + shortcutEvent(event); + } else { + postEvent(event); + } + } + + /** + * Simply posts events into the system event queue. + * + * @param event Event to dispatch. + */ + public static void postEvent(AWTEvent event) { + getQueue().postEvent(event); + } + + /** + * Dispatches event ahead of all events staying in the event queue. + * + * @param event an event to be shortcut. + */ + public static void shortcutEvent(AWTEvent event) { + installQueue(); + jemmyQueue.shortcutEvent(event); + } + + /** + * Installs own Jemmy EventQueue implementation. The method is executed in + * dispatchmode only. + * + * @see #uninstallQueue + */ + public static void installQueue() { + if (jemmyQueue == null) { + jemmyQueue = new JemmyQueue(); + } + jemmyQueue.install(); + } + + /** + * Uninstalls own Jemmy EventQueue implementation. + * + * @see #installQueue + */ + public static void uninstallQueue() { + if (jemmyQueue != null) { + jemmyQueue.uninstall(); + } + } + + static { + Timeouts.initDefault("QueueTool.WaitQueueEmptyTimeout", WAIT_QUEUE_EMPTY_TIMEOUT); + Timeouts.initDefault("QueueTool.QueueCheckingDelta", QUEUE_CHECKING_DELTA); + Timeouts.initDefault("QueueTool.LockTimeout", LOCK_TIMEOUT); + Timeouts.initDefault("QueueTool.InvocationTimeout", INVOCATION_TIMEOUT); + Timeouts.initDefault("QueueTool.MaximumLockingTime", MAXIMUM_LOCKING_TIME); + } + + /** + * Defines current timeouts. + * + * @param ts ?t? A collection of timeout assignments. + * @see org.netbeans.jemmy.Timeouts + * @see org.netbeans.jemmy.Timeoutable + * @see #getTimeouts + */ + @Override + public void setTimeouts(Timeouts ts) { + timeouts = ts; + lockWaiter.setTimeouts(getTimeouts().cloneThis()); + } + + /** + * Return current timeouts. + * + * @return the collection of current timeout assignments. + * @see org.netbeans.jemmy.Timeouts + * @see org.netbeans.jemmy.Timeoutable + * @see #setTimeouts + */ + @Override + public Timeouts getTimeouts() { + return timeouts; + } + + /** + * Defines print output streams or writers. + * + * @param out Identify the streams or writers used for print output. + * @see org.netbeans.jemmy.Outputable + * @see org.netbeans.jemmy.TestOut + * @see #getOutput + */ + @Override + public void setOutput(TestOut out) { + output = out; + lockWaiter.setOutput(output.createErrorOutput()); + } + + /** + * Returns print output streams or writers. + * + * @return an object that contains references to objects for printing to + * output and err streams. + * @see org.netbeans.jemmy.Outputable + * @see org.netbeans.jemmy.TestOut + * @see #setOutput + */ + @Override + public TestOut getOutput() { + return output; + } + + /** + * Waits for system event queue empty. Uses + * "QueueTool.WaitQueueEmptyTimeout" milliseconds to wait. + * + * @throws TimeoutExpiredException + */ + public void waitEmpty() { + Waiter waiter = new Waiter<>(new Waitable() { + @Override + public String actionProduced(Void obj) { + if (checkEmpty()) { + return "Empty"; + } + return null; + } + + @Override + public String getDescription() { + return "Wait event queue empty"; + } + + @Override + public String toString() { + return "waitEmpty.Waiter{" + getDescription() + '}'; + } + }); + waiter.setTimeoutsToCloneOf(timeouts, "QueueTool.WaitQueueEmptyTimeout"); + waiter.setOutput(output); + try { + waiter.waitAction(null); + } catch (TimeoutExpiredException e) { + final AWTEvent event = getQueue().peekEvent(); + // if event != null run toString in dispatch thread + String eventToString = (event == null) ? "null" : invokeSmoothly( + new QueueTool.QueueAction("event.toString()") { + @Override + public String launch() { + return event.toString(); + } + } + ); + getOutput().printErrLine("Event at the top of stack: " + eventToString); + throw (e); + } catch (InterruptedException e) { + output.printStackTrace(e); + } + } + + /** + * Waits for system event queue be empty for {@code emptyTime} + * milliseconds. Uses "QueueTool.WaitQueueEmptyTimeout" milliseconds to + * wait. + * + * @param emptyTime time for the queue to stay empty. + * @throws TimeoutExpiredException + */ + public void waitEmpty(long emptyTime) { + + StayingEmptyWaiter waiter = new StayingEmptyWaiter(emptyTime); + waiter.setTimeoutsToCloneOf(timeouts, "QueueTool.WaitQueueEmptyTimeout"); + waiter.setOutput(output); + try { + waiter.waitAction(null); + } catch (TimeoutExpiredException e) { + final AWTEvent event = getQueue().peekEvent(); + String eventToString = (event == null) ? "null" : invokeSmoothly( + new QueueTool.QueueAction("event.toString()") { + @Override + public String launch() { + return event.toString(); + } + } + ); + getOutput().printErrLine("Event at the top of stack: " + eventToString); + throw (e); + } catch (InterruptedException e) { + output.printStackTrace(e); + } + } + + /** + * Invokes action through EventQueue. Does not wait for it execution. + * + * @param action an action to be invoked. + */ + public void invoke(QueueAction action) { + output.printTrace("Invoking \"" + action.getDescription() + "\" action through event queue"); + EventQueue.invokeLater(action); + } + + /** + * Invokes runnable through EventQueue. Does not wait for it execution. + * + * @param runnable a runnable to be invoked. + * @return QueueAction instance which can be use for execution monitoring. + * @see QueueTool.QueueAction + */ + public QueueAction invoke(Runnable runnable) { + QueueAction result = new RunnableRunnable(runnable); + invoke(result); + return result; + } + + /** + * Invokes action through EventQueue. Does not wait for it execution. + * + * @param action an action to be invoked. + * @param param {@code action.launch(Object)} method parameter. + * @return QueueAction instance which can be use for execution monitoring. + * @see QueueTool.QueueAction + */ + public QueueAction invoke(Action action, P param) { + QueueAction result = new ActionRunnable<>(action, param); + invoke(result); + return result; + } + + /** + * Being executed outside of AWT dispatching thread, invokes an action + * through the event queue. Otherwise executes {@code action.launch()} + * method directly. + * + * @param action anaction to be executed. + * @return Action result. + */ + public R invokeSmoothly(QueueAction action) { + if (!EventQueue.isDispatchThread()) { + return invokeAndWait(action); + } else { + try { + return action.launch(); + } catch (Exception e) { + throw (new JemmyException("Exception in " + action.getDescription(), e)); + } + } + } + + /** + * Being executed outside of AWT dispatching thread, invokes a runnable + * through the event queue. Otherwise executes {@code runnable.run()} + * method directly. + * + * @param runnable a runnable to be executed. + */ + public void invokeSmoothly(Runnable runnable) { + if (!EventQueue.isDispatchThread()) { + invokeAndWait(runnable); + } else { + runnable.run(); + } + } + + /** + * Being executed outside of AWT dispatching thread, invokes an action + * through the event queue. Otherwise executes + * {@code action.launch(Object)} method directly. + * + * @param action anaction to be executed. + * @param param an action parameter + * @return Action result. + */ + public R invokeSmoothly(Action action, P param) { + if (!EventQueue.isDispatchThread()) { + return invokeAndWait(action, param); + } else { + return action.launch(param); + } + } + + /** + * Invokes action through EventQueue. Waits for it execution. + * + * @param action an action to be invoked. + * @return a result of action + * @throws TimeoutExpiredException if action was not executed in + * "QueueTool.InvocationTimeout" milliseconds. + */ + public R invokeAndWait(QueueAction action) { + + class JemmyInvocationLock { + } + Object lock = new JemmyInvocationLock(); + InvocationEvent event + = new JemmyInvocationEvent(Toolkit.getDefaultToolkit(), + action, + lock, + true); + try { + synchronized (lock) { + getQueue().postEvent(event); + while (!action.getFinished()) { + lock.wait(); + } + } + } catch (InterruptedException e) { + throw (new JemmyException("InterruptedException during " + + action.getDescription() + + " execution", e)); + } + if (action.getException() != null) { + throw (new JemmyException("Exception in " + action.getDescription(), + action.getException())); + } + if (event.getException() != null) { + throw (new JemmyException("Exception in " + action.getDescription(), + event.getException())); + } + return action.getResult(); + } + + public static final class JemmyInvocationEvent extends InvocationEvent { + + private static final long serialVersionUID = 42L; + + public JemmyInvocationEvent(Object source, Runnable runnable, + Object notifier, boolean catchThrowables) { + super(source, runnable, notifier, catchThrowables); + } + } + + /** + * Invokes runnable through EventQueue. Waits for it execution. + * + * @param runnable a runnable to be invoked. + * @throws TimeoutExpiredException if runnable was not executed in + * "QueueTool.InvocationTimeout" milliseconds. + */ + public void invokeAndWait(Runnable runnable) { + invokeAndWait(new RunnableRunnable(runnable)); + } + + /** + * Invokes action through EventQueue. Waits for it execution. May throw + * TimeoutExpiredException if action was not executed in + * "QueueTool.InvocationTimeout" milliseconds. + * + * @param action an action to be invoked. + * @param param action.launch(Object method parameter. + * @return a result of action + * @throws TimeoutExpiredException if action was not executed in + * "QueueTool.InvocationTimeout" milliseconds. + */ + public R invokeAndWait(Action action, P param) { + return invokeAndWait(new ActionRunnable<>(action, param)); + } + + /** + * Locks EventQueue. Locking will be automatically aborted after + * "QueueTool.MaximumLockingTime" milliseconds. + * + * @see #unlock() + * @throws TimeoutExpiredException + */ + public void lock() { + output.printTrace("Locking queue."); + invoke(locker); + try { + lockWaiter. + getTimeouts(). + setTimeout("Waiter.WaitingTime", + timeouts. + getTimeout("QueueTool.LockTimeout")); + lockWaiter. + getTimeouts(). + setTimeout("Waiter.TimeDelta", + timeouts. + getTimeout("QueueTool.QueueCheckingDelta")); + lockWaiter.waitAction(null); + } catch (InterruptedException e) { + output.printStackTrace(e); + } + } + + /** + * Unlocks EventQueue. + * + * @see #lock() + */ + public void unlock() { + output.printTrace("Unlocking queue."); + locker.setLocked(false); + } + + /** + * Locks event queue for "time" milliseconds. Returns immediately after + * locking. + * + * @param time a time to lock the queue for. + */ + public void lock(long time) { + output.printTrace("Locking queue for " + Long.toString(time) + " milliseconds"); + lock(); + invoke(new UnlockPostponer(time)); + } + + /** + * Sais if last locking was expired. + * + * @return true if last locking had beed expired. + */ + public boolean wasLockingExpired() { + return locker.expired; + } + + /** + * Action to be executed through event queue. Even if it was executed without + * waiting by {@code invoke(QueueAction)} execution process can be + * monitored by {@code getResult()}, {@code getException()}, + * {@code getFinished()} methods. + */ + public static abstract class QueueAction implements Runnable { + + private volatile boolean finished; + private Exception exception; + private R result; + private String description; + + /** + * Constructor. + * + * @param description a description. + */ + public QueueAction(String description) { + this.description = description; + finished = false; + exception = null; + result = null; + } + + /** + * Method to implement action functionality. + * + * @return an Object - action result + * @throws Exception + */ + public abstract R launch() + throws Exception; + + /** + */ + @Override + public final void run() { + finished = false; + exception = null; + result = null; + try { + result = launch(); + } catch (Exception e) { + exception = e; + } finally { + finished = true; + } + } + + /** + * Action description. + * + * @return the description. + */ + public String getDescription() { + return description; + } + + /** + * Returns action result if action has already been finished, null + * otherwise. + * + * @return an action result. + */ + public R getResult() { + return result; + } + + /** + * Returns exception occured during action execution (if any). + * + * @return the Exception happened inside {@code launch()} method. + */ + public Exception getException() { + return exception; + } + + /** + * Informs whether action has been finished or not. + * + * @return true if this action have been finished + */ + public boolean getFinished() { + return finished; + } + + @Override + public String toString() { + return "QueueAction{description=" + description + ", result=" + result + ", finished=" + finished + ", exception=" + exception + '}'; + } + } + + private static class JemmyQueue extends EventQueue { + + private boolean installed = false; + + public void shortcutEvent(AWTEvent event) { + super.dispatchEvent(event); + } + + @Override + protected void dispatchEvent(AWTEvent event) { + //it's necessary to catch exception here. + //because test might already fail by timeout + //but generated events are still in stack + try { + super.dispatchEvent(event); + } catch (Exception e) { + //the exceptions should be printed into + //Jemmy output - not System.out + JemmyProperties.getCurrentOutput().printStackTrace(e); + } + } + + public synchronized void install() { + if (!installed) { + getQueue().push(this); + installed = true; + } + } + + public synchronized void uninstall() { + if (installed) { + pop(); + installed = false; + } + } + } + + private class EventWaiter implements Runnable { + + boolean empty = true; + long emptyTime; + + public EventWaiter(long emptyTime) { + this.emptyTime = emptyTime; + } + + @Override + public void run() { + long startTime = System.currentTimeMillis(); + while ((empty = checkEmpty()) + && (System.currentTimeMillis() - startTime) < emptyTime) { + timeouts.sleep("QueueTool.QueueCheckingDelta"); + } + } + } + + private class StayingEmptyWaiter extends Waiter { + + long emptyTime; + + public StayingEmptyWaiter(long emptyTime) { + this.emptyTime = emptyTime; + } + + @Override + public String actionProduced(Void obj) { + try { + EventWaiter eventWaiter = new EventWaiter(emptyTime); + EventQueue.invokeAndWait(eventWaiter); + if (eventWaiter.empty + && timeFromStart() <= super.getTimeouts().getTimeout("Waiter.WaitingTime")) { + return "Reached"; + } + } catch (InterruptedException | InvocationTargetException e) { + output.printStackTrace(e); + } + return null; + } + + @Override + public String getDescription() { + return "Wait event queue staying empty for " + emptyTime; + } + + @Override + public String toString() { + return "StayingEmptyWaiter{" + "emptyTime=" + emptyTime + '}'; + } + } + + private class ActionRunnable extends QueueAction { + + Action action; + P param; + + public ActionRunnable(Action action, P param) { + super(action.getDescription()); + this.action = action; + this.param = param; + } + + @Override + public R launch() throws Exception { + return action.launch(param); + } + } + + private class RunnableRunnable extends QueueAction { + + Runnable action; + + public RunnableRunnable(Runnable action) { + super("Runnable"); + this.action = action; + } + + @Override + public Void launch() throws Exception { + action.run(); + return null; + } + } + + private class Locker extends QueueAction { + + volatile boolean locked = false; + long wholeTime, deltaTime; + boolean expired; + + public Locker() { + super("Event queue locking"); + } + + @Override + public Void launch() { + wholeTime = timeouts.getTimeout("QueueTool.MaximumLockingTime"); + deltaTime = timeouts.getTimeout("QueueTool.QueueCheckingDelta"); + setLocked(true); + expired = false; + long startTime = System.currentTimeMillis(); + while (isLocked()) { + try { + Thread.sleep(deltaTime); + } catch (InterruptedException e) { + getOutput().printStackTrace(e); + } + if (System.currentTimeMillis() - startTime > wholeTime) { + getOutput().printLine("Locking has been expired!"); + expired = true; + break; + } + } + return null; + } + + public void setLocked(boolean locked) { + this.locked = locked; + } + + public boolean isLocked() { + return locked; + } + } + + private class UnlockPostponer implements Runnable { + + long time; + + public UnlockPostponer(long time) { + this.time = time; + } + + @Override + public void run() { + new Timeout("", time).sleep(); + unlock(); + } + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/Scenario.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/Scenario.java new file mode 100644 index 00000000000..216bd1c30c0 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/Scenario.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy; + +/** + * + * A test scenario. This interface provides a mechanism for putting something + * into execution. The execution is conditioned in a very general way by passing + * a {@code java.lang.Object} to it's {@code runIt} method. + * + * @see Test + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + */ +public interface Scenario { + + /** + * Defines a way to execute this test scenario. + * + * @param param An object passed to configure the test scenario execution. + * For example, this parameter might be a java.lang.String[] object that lists the + * command line arguments to the Java application corresponding + * to a test. + * @return an int that tells something about the execution. For, example, a + * status code. + */ + public int runIt(Object param); +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/Test.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/Test.java new file mode 100644 index 00000000000..1cfa549ec0c --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/Test.java @@ -0,0 +1,441 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy; + +import java.io.PrintStream; +import java.io.PrintWriter; +import java.lang.reflect.InvocationTargetException; + +/** + * + * Jemmy itself provides a way to create tests. Test should implement + * org.netbeans.jemmy.Scenario interface. + * + * Test can be executed from command line:
+ * {@code java [application options] [jemmy options] org.netbeans.jemmy.Test [full name of test class] [test args]}
+ * Test elso can be executed by one of the run(...) methods or by
+ * {@code new Test([test class name]).startTest([test args]);}
+ * + *

Timeouts used:
+ * Test.WholeTestTimeout - time for the whole test
+ * + * @author Alexandre Iline (alexandre.iline@oracle.com) + */ +public class Test extends ActionProducer + implements Timeoutable, Outputable, Scenario { + + private final static long WHOLE_TEST_TIMEOUT = 3600000; + + /** + * Status returned by test if wrong parameter was passed. + */ + public static int WRONG_PARAMETERS_STATUS = 101; + + /** + * Status returned by test if exception appeared inside scenario. + */ + public static int SCENARIO_EXCEPTION_STATUS = 102; + + /** + * Positive test status. + */ + public static int TEST_PASSED_STATUS = 0; + + /** + * Test timeouts. + */ + protected Timeouts timeouts; + + /** + * Test output. + */ + protected TestOut output; + + private Scenario scenario; + + /** + * Constructor for tests requiring only a class instance. Creates a subclass + * of {@code ActionProducer} and {@code java.lang.Thread} that + * runs in a separate thread of execution and waits for execution to finish. + * The current output stream assignments and timeouts are used. + * + * @param testClassName Full test class name + */ + public Test(String testClassName) { + super(true); + setOutput(JemmyProperties.getCurrentOutput()); + setTimeouts(JemmyProperties.getCurrentTimeouts()); + scenario = testForName(testClassName); + } + + /** + * Constructor for scenarios that require an instance and might require an + * argument. Creates a subclass of {@code ActionProducer} and + * {@code java.lang.Thread} that runs in a separate thread of execution + * and waits for execution to finish. The current output stream assignments + * and timeouts are used. + * + * @param scenario a test scenario + * @see org.netbeans.jemmy.Scenario + */ + public Test(Scenario scenario) { + super(true); + setOutput(JemmyProperties.getCurrentOutput()); + setTimeouts(JemmyProperties.getCurrentTimeouts()); + this.scenario = scenario; + } + + /** + * No argument constructor. Used by subclasses of this {@code Test} + * class. Creates a subclass of {@code ActionProducer} and + * {@code java.lang.Thread} that runs in a separate thread of execution + * and waits for execution to finish. The current output stream assignments + * and timeouts are used. + */ + protected Test() { + super(true); + setOutput(JemmyProperties.getCurrentOutput()); + setTimeouts(JemmyProperties.getCurrentTimeouts()); + } + + /** + * Throws TestCompletedException exception. The exception thrown contains a + * pass/fail status and a short status {@code java.lang.String}. Can by + * invoked from test to abort test execution. + * + * @param status If 0 - test passed, otherwise failed. + * @throws TestCompletedException all of the time. + */ + public static void closeDown(int status) { + if (status == 0) { + throw (new TestCompletedException(status, "Test passed")); + } else { + throw (new TestCompletedException(status, "Test failed with status " + + Integer.toString(status))); + } + } + + /** + * Executes a test. + * + * @param argv First element should be a test class name, all others - test + * args. + * @return test status. + */ + public static int run(String[] argv) { + String[] args = argv; + JemmyProperties.getProperties().init(); + if (argv.length < 1) { + JemmyProperties.getCurrentOutput(). + printErrLine("First element of String array should be test classname"); + return WRONG_PARAMETERS_STATUS; + } + JemmyProperties.getCurrentOutput().printLine("Executed test " + argv[0]); + Test test = new Test(argv[0]); + if (argv.length >= 1) { + args = shiftArray(args); + } + if (argv.length >= 2) { + JemmyProperties.getCurrentOutput().printLine("Work directory: " + argv[1]); + System.setProperty("user.dir", argv[1]); + args = shiftArray(args); + } + int status; + status = test.startTest(args); + JemmyProperties.getCurrentOutput().flush(); + return status; + } + + /** + * Executes a test. + * + * @param argv First element should be a test class name, all others - test + * args. + * @param output Stream to put test output and errput into. + * @return test status. + */ + public static int run(String[] argv, PrintStream output) { + JemmyProperties.setCurrentOutput(new TestOut(System.in, output, output)); + return run(argv); + } + + /** + * Executes a test. + * + * @param argv First element should be a test class name, all others - test + * args. + * @param output Stream to put test output into. + * @param errput Stream to put test errput into. + * @return test status. + */ + public static int run(String[] argv, PrintStream output, PrintStream errput) { + JemmyProperties.setCurrentOutput(new TestOut(System.in, output, errput)); + return run(argv); + } + + /** + * Executes a test. + * + * @param argv First element should be a test class name, all others - test + * args. + * @param output Writer to put test output and errput into. + * @return test status. + */ + public static int run(String[] argv, PrintWriter output) { + JemmyProperties.setCurrentOutput(new TestOut(System.in, output, output)); + return run(argv); + } + + /** + * Executes a test. + * + * @param argv First element should be a test class name, all others - test + * args. + * @param output Writer to put test output into. + * @param errput Writer to put test errput into. + * @return test status. + */ + public static int run(String[] argv, PrintWriter output, PrintWriter errput) { + JemmyProperties.setCurrentOutput(new TestOut(System.in, output, errput)); + return run(argv); + } + + /** + * Invoke this {@code Test}. The call might be directly from the + * command line. + * + * @param argv First element should be a test class name, all others - test + * args. + */ + public static void main(String[] argv) { + System.exit(run(argv, System.out)); + } + + static { + Timeouts.initDefault("Test.WholeTestTimeout", WHOLE_TEST_TIMEOUT); + } + + /** + * Creates an instance of a class named by the parameter. + * + * @param testName Full test class name + * @return an instance of the test {@code Scenario} to launch. + * @see org.netbeans.jemmy.Scenario + */ + public Scenario testForName(String testName) { + try { + return ((Scenario) (Class.forName(testName). + getConstructor(new Class[0]). + newInstance())); + } catch (ClassNotFoundException e) { + output.printErrLine("Class " + testName + " does not exist!"); + output.printStackTrace(e); + } catch (NoSuchMethodException e) { + output.printErrLine("Class " + testName + " has not constructor!"); + output.printStackTrace(e); + } catch (InvocationTargetException e) { + output.printErrLine("Exception inside " + testName + " constructor:"); + output.printStackTrace(e.getTargetException()); + } catch (IllegalAccessException e) { + output.printErrLine("Cannot access to " + testName + " constructor!"); + output.printStackTrace(e); + } catch (InstantiationException e) { + output.printErrLine("Cannot instantiate " + testName + " class!"); + output.printStackTrace(e); + } + return null; + } + + /** + * Set the timeouts used by this {@code Test}. + * + * @param timeouts A collection of timeout assignments. + * @see org.netbeans.jemmy.Timeoutable + * @see org.netbeans.jemmy.Timeouts + * @see #getTimeouts + */ + @Override + public void setTimeouts(Timeouts timeouts) { + this.timeouts = timeouts; + Timeouts times = timeouts.cloneThis(); + times.setTimeout("ActionProducer.MaxActionTime", + timeouts.getTimeout("Test.WholeTestTimeout")); + super.setTimeouts(times); + } + + /** + * Get the timeouts used by this {@code Test}. + * + * @see org.netbeans.jemmy.Timeoutable + * @see org.netbeans.jemmy.Timeouts + * @see #setTimeouts + */ + @Override + public Timeouts getTimeouts() { + return timeouts; + } + + /** + * Set the streams or writers used for print output. + * + * @param out An object used to identify both output and error print + * streams. + * @see org.netbeans.jemmy.Outputable + * @see org.netbeans.jemmy.TestOut + * @see #getOutput + */ + @Override + public void setOutput(TestOut out) { + output = out; + super.setOutput(out); + } + + /** + * Get the streams or writers used for print output. + * + * @return an object containing references to both output and error print + * streams. + * @see org.netbeans.jemmy.Outputable + * @see org.netbeans.jemmy.TestOut + * @see #setOutput + */ + @Override + public TestOut getOutput() { + return output; + } + + /** + * Executes test. + * + * @param param Object to be passed into this test's launch(Object) method. + * @return test status. + */ + public int startTest(Object param) { + if (scenario != null) { + output.printLine("Test " + scenario.getClass().getName() + + " has been started"); + } else { + output.printLine("Test " + getClass().getName() + + " has been started"); + } + try { + return ((Integer) produceAction(param, "Test.WholeTestTimeout")).intValue(); + } catch (InterruptedException e) { + output.printErrLine("Test was interrupted."); + output.printStackTrace(e); + } catch (TimeoutExpiredException e) { + output.printErrLine("Test was not finished in " + + Long.toString(timeouts.getTimeout("Test.WholeTestTimeout")) + + " milliseconds"); + output.printStackTrace(e); + } catch (Exception e) { + output.printStackTrace(e); + } + return 1; + } + + /** + * Launch an action. Pass arguments to and execute a test + * {@code Scenario}. + * + * @param obj An argument object that controls test execution. This might be + * a {@code java.lang.String[]} containing command line arguments. + * @see org.netbeans.jemmy.Action + * @return an Integer containing test status. + */ + @Override + public final Object launch(Object obj) { + setTimeouts(timeouts); + try { + if (scenario != null) { + closeDown(scenario.runIt(obj)); + } else { + closeDown(runIt(obj)); + } + } catch (TestCompletedException e) { + output.printStackTrace(e); + return e.getStatus(); + } catch (Throwable e) { + output.printStackTrace(e); + return SCENARIO_EXCEPTION_STATUS; + } + return TEST_PASSED_STATUS; + } + + /** + * Supposed to be overridden to print a synopsys into test output. + */ + public void printSynopsis() { + output.printLine("Here should be a test synopsis."); + } + + /** + * @see org.netbeans.jemmy.Action + */ + @Override + public final String getDescription() { + return "Test " + scenario.getClass().getName() + " finished"; + } + + @Override + public String toString() { + return "Test{" + "scenario=" + scenario + '}'; + } + + /** + * Defines a way to execute this {@code Test}. + * + * @param param An object passed to configure the test scenario execution. + * For example, this parameter might be a java.lang.String[] object that lists the + * command line arguments to the Java application corresponding + * to a test. + * @return an int that tells something about the execution. For, example, a + * status code. + * @see org.netbeans.jemmy.Scenario + */ + @Override + public int runIt(Object param) { + return 0; + } + + /** + * Sleeps. + * + * @param time The sleep time in milliseconds. + */ + protected void doSleep(long time) { + try { + Thread.sleep(time); + } catch (InterruptedException e) { + } + } + + private static String[] shiftArray(String[] orig) { + String[] result = new String[orig.length - 1]; + for (int i = 0; i < result.length; i++) { + result[i] = orig[i + 1]; + } + return result; + } + +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/TestCompletedException.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/TestCompletedException.java new file mode 100644 index 00000000000..7cff80dd669 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/TestCompletedException.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy; + +/** + * + * Exception is throught as a result of test. either test failed or passed. + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + */ +public class TestCompletedException extends JemmyException { + + private static final long serialVersionUID = 42L; + + private int status; + + /** + * Constructor. + * + * @param st Exit status. + * @param ex Exception provoked test failure. + */ + public TestCompletedException(int st, Exception ex) { + super("Test " + + ((st == 0) + ? "passed" + : "failed with status " + Integer.toString(st)), + ex); + status = st; + } + + /** + * Constructor. + * + * @param st Exit status. + * @param description Failure reason + */ + public TestCompletedException(int st, String description) { + super("Test " + + ((st == 0) + ? "passed" + : "failed with status " + Integer.toString(st) + + "\n" + description)); + status = st; + } + + /** + * Returns status. + * + * @return test status + */ + public int getStatus() { + return status; + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/TestOut.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/TestOut.java new file mode 100644 index 00000000000..c3552c045e9 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/TestOut.java @@ -0,0 +1,351 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.PrintStream; +import java.io.PrintWriter; + +/** + * + * Test output. + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + */ +public class TestOut { + + private InputStream input; + private PrintWriter output; + private PrintWriter errput; + private PrintWriter golden_output; + private BufferedReader buffInput; + private boolean autoFlushMode = true; + + /** + * Constructor. + * + * @param in Input stream + * @param out Output stream + * @param err Errput stream + */ + public TestOut(InputStream in, PrintStream out, PrintStream err) { + this(in, out, err, null); + } + + /** + * Constructor. + * + * @param in Input stream + * @param out Output stream + * @param err Errput stream + * @param golden Golgen output stream + */ + public TestOut(InputStream in, PrintStream out, PrintStream err, PrintStream golden) { + super(); + PrintWriter tout = null; + if (out != null) { + tout = new PrintWriter(out); + } + PrintWriter terr = null; + if (err != null) { + terr = new PrintWriter(err); + } + PrintWriter tgolden = null; + if (golden != null) { + tgolden = new PrintWriter(golden); + } + initStreams(in, tout, terr, tgolden); + } + + /** + * Constructor. + * + * @param in Input stream + * @param out Output stream + * @param err Errput stream + */ + public TestOut(InputStream in, PrintWriter out, PrintWriter err) { + this(in, out, err, null); + } + + /** + * Constructor. + * + * @param in Input stream + * @param out Output stream + * @param err Errput stream + * @param golden Golgen output stream + */ + public TestOut(InputStream in, PrintWriter out, PrintWriter err, PrintWriter golden) { + super(); + initStreams(in, out, err, golden); + autoFlushMode = true; + } + + /** + * Creates unstance using System.in, System.out and System.err streams. + */ + public TestOut() { + this(System.in, + new PrintWriter(System.out), + new PrintWriter(System.err), + null); + } + + /** + * Creates output which does not print any message anywhere. + * + * @return a TestOut object which does not print any message anywhere. + */ + public static TestOut getNullOutput() { + return new TestOut((InputStream) null, (PrintWriter) null, (PrintWriter) null); + } + + /** + * Specifies either flush is invoked after each output. + * + * @param autoFlushMode If true flush is invoking after each output. + * @return Old value of the auto flush mode. + * @see #getAutoFlushMode + */ + public boolean setAutoFlushMode(boolean autoFlushMode) { + boolean oldValue = getAutoFlushMode(); + this.autoFlushMode = autoFlushMode; + return oldValue; + } + + /** + * Says if flush is invoked after each output. + * + * @return Value of the auto flush mode. + * @see #setAutoFlushMode + */ + public boolean getAutoFlushMode() { + return autoFlushMode; + } + + /** + * Read one byte from input. + * + * @return an int from input stream. + * @exception IOException + */ + public int read() throws IOException { + if (input != null) { + return input.read(); + } else { + return -1; + } + } + + /** + * Read a line from input. + * + * @return a line from input stream. + * @exception IOException + */ + public String readLine() throws IOException { + if (buffInput != null) { + return buffInput.readLine(); + } else { + return null; + } + } + + /** + * Prints a line into output. + * + * @param line a string to print into output stream. + */ + public void print(String line) { + if (output != null) { + output.print(line); + } + } + + /** + * Prints a line and then terminate the line by writing the line separator + * string. + * + * @param line a string to print into output stream. + */ + public void printLine(String line) { + if (output != null) { + output.println(line); + if (autoFlushMode) { + output.flush(); + } + } + } + + /** + * Prints a line into golden output. + * + * @param line a string to print into golden output stream. + */ + public void printGolden(String line) { + if (golden_output != null) { + golden_output.println(line); + if (autoFlushMode) { + golden_output.flush(); + } + } + } + + /** + * Prints a line into error output. + * + * @param line a string to print into error output stream. + */ + public void printErrLine(String line) { + if (errput != null) { + errput.println(line); + if (autoFlushMode) { + errput.flush(); + } + } + } + + /** + * Prints a line into either output or errput. + * + * @param toOut If true prints a line into output. + * @param line a string to print. + */ + public void printLine(boolean toOut, String line) { + if (toOut) { + printLine(line); + } else { + printErrLine(line); + } + } + + /** + * Prints a trace line. + * + * @param text a trace text. + */ + public void printTrace(String text) { + printLine("Trace:"); + printLine(text); + } + + /** + * Prints a error line. + * + * @param text a error text. + */ + public void printError(String text) { + printErrLine("Error:"); + printErrLine(text); + } + + /** + * Prints an exception stack trace into error stream. + * + * @param e exception + */ + public void printStackTrace(Throwable e) { + if (errput != null) { + e.printStackTrace(errput); + if (autoFlushMode) { + errput.flush(); + } + } + } + + /** + * Returns input stream. + * + * @return an input stream + */ + public InputStream getInput() { + return input; + } + + /** + * Returns output writer. + * + * @return an output stream + */ + public PrintWriter getOutput() { + return output; + } + + /** + * Returns errput writer. + * + * @return a error stream + */ + public PrintWriter getErrput() { + return errput; + } + + /** + * Returns golden output writer. + * + * @return a golden output stream + */ + public PrintWriter getGolden() { + return golden_output; + } + + /** + * Creates an output which prints only error messages. + * + * @return a TestOut instance which has only error stream. + */ + public TestOut createErrorOutput() { + return new TestOut(null, null, getErrput()); + } + + /** + * Flushes all output threads. + */ + public void flush() { + if (output != null) { + output.flush(); + } + if (errput != null) { + errput.flush(); + } + if (golden_output != null) { + golden_output.flush(); + } + } + + private void initStreams(InputStream in, PrintWriter out, PrintWriter err, PrintWriter golden) { + input = in; + output = out; + errput = err; + golden_output = golden; + if (input != null) { + buffInput = new BufferedReader(new InputStreamReader(in)); + } else { + buffInput = null; + } + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/Timeout.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/Timeout.java new file mode 100644 index 00000000000..ca47fde5916 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/Timeout.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy; + +/** + * Represents one timeout. + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + */ +public class Timeout extends Object { + + private String name; + private long value; + private long startTime; + + /** + * Constructor. + * + * @param name Timeout name. + * @param value Timeout value in milliseconds. + */ + public Timeout(String name, long value) { + this.name = name; + this.value = value; + } + + /** + * Returns timeout name. + * + * @return timeout name. + */ + public String getName() { + return name; + } + + /** + * Returns timeout value. + * + * @return timeout value. + */ + public long getValue() { + return value; + } + + /** + * Sleeps for timeout value. + */ + public void sleep() { + if (getValue() > 0) { + try { + Thread.sleep(getValue()); + } catch (InterruptedException e) { + throw (new JemmyException("Sleep " + + getName() + + " was interrupted!", + e)); + } + } + } + + /** + * Starts timeout measuring. + */ + public void start() { + startTime = System.currentTimeMillis(); + } + + /** + * Checks if timeout has been expired after start() invocation. + * + * @return true if timeout has been expired. + */ + public boolean expired() { + return System.currentTimeMillis() - startTime > getValue(); + } + + /** + * Throws a TimeoutExpiredException exception if timeout has been expired. + * + * @throws TimeoutExpiredException if timeout has been expired after start() + * invocation. + */ + public void check() { + if (expired()) { + throw (new TimeoutExpiredException(getName() + + " timeout expired!")); + } + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/TimeoutExpiredException.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/TimeoutExpiredException.java new file mode 100644 index 00000000000..90c6fca4f84 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/TimeoutExpiredException.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy; + +/** + * + * Exception is supposed to be used to notice that some waiting was expired. + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + */ +public class TimeoutExpiredException extends JemmyException { + + private static final long serialVersionUID = 42L; + + /** + * Constructor. + * + * @param description Waiting description. + */ + public TimeoutExpiredException(String description) { + super(description); + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/Timeoutable.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/Timeoutable.java new file mode 100644 index 00000000000..a1c428cb443 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/Timeoutable.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy; + +/** + * + * Any class which contains methods requiring waiting or sleeping should + * implement this interface. Waiting and sleeping operations have time limits + * that can be set or returned using the methods of this interface. + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + */ +public interface Timeoutable { + + /** + * Defines current timeouts. + * + * @param t A collection of timeout assignments. + * @see #getTimeouts + */ + public void setTimeouts(Timeouts t); + + /** + * Return current timeouts. + * + * @return the collection of current timeout assignments. + * @see #setTimeouts + */ + public Timeouts getTimeouts(); +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/Timeouts.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/Timeouts.java new file mode 100644 index 00000000000..652f31ea3a9 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/Timeouts.java @@ -0,0 +1,424 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy; + +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintStream; +import java.io.PrintWriter; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Properties; + +/** + * + * Class to store and process a set of timeout values. + * + * @see #setDefault(String, long) + * @see #getDefault(String) + * @see #setTimeout(String, long) + * @see #getTimeout(String) + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + */ +public class Timeouts { + + private static final long DELTA_TIME = 100; + + private static Timeouts defaults; + + private Hashtable timeouts; + private static double timeoutsScale = -1; + + /** + * Creates empty Timeouts object. + */ + public Timeouts() { + super(); + timeouts = new Hashtable<>(); + setTimeout("Timeouts.DeltaTimeout", DELTA_TIME); + try { + load(); + } catch (IOException ignored) { + } + } + + /** + * Stores default timeout value. + * + * @param name Timeout name. + * @param newValue Timeout value. + * @see #getDefault(String) + * @see #initDefault(String, long) + * @see #containsDefault(String) + */ + public static void setDefault(String name, long newValue) { + defaults.setTimeout(name, newValue); + } + + /** + * Sets default timeout value if it was not set before. + * + * @param name Timeout name. + * @param newValue Timeout value. + * @see #setDefault(String, long) + * @see #getDefault(String) + * @see #containsDefault(String) + */ + public static void initDefault(String name, long newValue) { + defaults.initTimeout(name, newValue); + } + + /** + * Gets default timeout value. + * + * @param name Timeout name. + * @return Timeout value or -1 if timeout is not defined. + * @see #setDefault(String, long) + * @see #initDefault(String, long) + * @see #containsDefault(String) + */ + public static long getDefault(String name) { + return defaults.getTimeout(name); + } + + /** + * Check that default timeout value was defined. + * + * @param name Timeout name. + * @return True if timeout has been defined, false otherwise. + * @see #setDefault(String, long) + * @see #getDefault(String) + * @see #initDefault(String, long) + */ + public static boolean containsDefault(String name) { + return defaults.contains(name); + } + + static { + defaults = new Timeouts(); + } + + /** + * Loads default timeouts values. + * + * @param stream Stream to load timeouts from. + * @see org.netbeans.jemmy.Timeouts#loadDefaults(String) + * @see org.netbeans.jemmy.Timeouts#loadDefaults() + * @exception IOException + */ + public void loadDefaults(InputStream stream) + throws IOException { + defaults.load(stream); + } + + /** + * Loads default timeouts values from file. + * + * @param fileName File to load timeouts from. + * @see org.netbeans.jemmy.Timeouts#loadDefaults(InputStream) + * @see org.netbeans.jemmy.Timeouts#loadDefaults(String) + * @exception IOException + * @exception FileNotFoundException + */ + public void loadDefaults(String fileName) + throws FileNotFoundException, IOException { + defaults.load(fileName); + } + + /** + * Loads default timeouts values. Uses jemmy.timeouts system property to get + * timeouts file. + * + * @see org.netbeans.jemmy.Timeouts#loadDefaults(InputStream) + * @see org.netbeans.jemmy.Timeouts#loadDefaults(String) + * @exception IOException + * @exception FileNotFoundException + */ + public void loadDefaults() + throws FileNotFoundException, IOException { + defaults.load(); + } + + /** + * Creates Timeout new object by name and getTimeout(name) value. + * + * @param name Timeout name. + * @return a Timeout instance. + */ + public Timeout create(String name) { + return new Timeout(name, getTimeout(name)); + } + + /** + * Create timeout for "Timeouts.DeltaTimeout" name. + * + * @return a Timeout instance. + */ + public Timeout createDelta() { + return create("Timeouts.DeltaTimeout"); + } + + /** + * Checks if timeout has already been defined in this timeout instance. + * + * @param name Timeout name. + * @return True if timeout has been defined, false otherwise. + * @see #containsDefault(String) + */ + public boolean contains(String name) { + return timeouts.containsKey(name); + } + + /** + * Sets new timeout value. + * + * @param name Timeout name. + * @param newValue Timeout value. + * @return old timeout value + * @see #getTimeout + */ + public long setTimeout(String name, long newValue) { + long oldValue = -1; + if (contains(name)) { + oldValue = getTimeout(name); + timeouts.remove(name); + } + timeouts.put(name, newValue); + return oldValue; + } + + /** + * Gets timeout value. It timeout was not defined in this instance, returns + * default timeout value. + * + * @param name Timeout name. + * @return Timeout value. + * @see #getDefault(String) + * @see #setTimeout + */ + public long getTimeout(String name) { + long timeout; + if (contains(name) && timeouts.get(name) != null) { + timeout = timeouts.get(name); + timeout = (long) ((double) timeout * getTimeoutsScale()); + } else if (this != defaults) { + timeout = getDefault(name); + } else { + timeout = -1; + } + return timeout; + } + + /** + * Gets "Timeouts.DeltaTimeout" timeout value. + * + * @return Timeout value. + * @see #getDefault(String) + */ + public long getDeltaTimeout() { + return getTimeout("Timeouts.DeltaTimeout"); + } + + /** + * Sets timeout value if it was not set before. + * + * @param name Timeout name. + * @param newValue Timeout value. + * @return old timeout value + */ + public long initTimeout(String name, long newValue) { + long result = getTimeout(name); + if (!contains(name)) { + setTimeout(name, newValue); + } + return result; + } + + /** + * Creates a copy of the current timeouts set. + * + * @return A copy. + */ + public Timeouts cloneThis() { + Timeouts t = new Timeouts(); + Enumeration e = timeouts.keys(); + String name = ""; + while (e.hasMoreElements()) { + name = e.nextElement(); + t.setTimeout(name, + getTimeout(name)); + } + return t; + } + + /** + * Sleeps for the "name" timeout value. Can throw InterruptedException if + * current thread was interrupted. + * + * @param name Timeout name. + * @exception InterruptedException + */ + public void eSleep(String name) throws InterruptedException { + if (contains(name) + || defaults.contains(name)) { + Thread.sleep(getTimeout(name)); + } + } + + /** + * Sleeps for the "name" timeout value. Does not throw InterruptedException + * anyway. + * + * @param name Timeout name. + */ + public void sleep(String name) { + create(name).sleep(); + } + + /** + * Prints all defined timeouts. + * + * @param pw PrintWriter to print into. + */ + public void print(PrintWriter pw) { + Enumeration e = timeouts.keys(); + String name = ""; + while (e.hasMoreElements()) { + name = e.nextElement(); + pw.println(name + " = " + Long.toString(getTimeout(name))); + } + pw.println("Default values:"); + e = defaults.timeouts.keys(); + name = ""; + while (e.hasMoreElements()) { + name = e.nextElement(); + if (!contains(name)) { + pw.println(name + " = " + Long.toString(getDefault(name))); + } + } + } + + /** + * Prins all defined timeouts. + * + * @param ps PrintStream to print into. + */ + public void print(PrintStream ps) { + print(new PrintWriter(ps)); + } + + /** + * Loads timeouts values. + * + * @param stream Stream to load timeouts from. + * @see org.netbeans.jemmy.Timeouts#load(String) + * @see org.netbeans.jemmy.Timeouts#load() + * @exception IOException + */ + public void load(InputStream stream) + throws IOException { + Properties props = new Properties(); + props.load(stream); + Enumeration propNames = props.propertyNames(); + long propValue; + String propName = null; + while (propNames.hasMoreElements()) { + propName = (String) propNames.nextElement(); + propValue = Long.parseLong(props.getProperty(propName)); + setTimeout(propName, propValue); + } + } + + /** + * Loads timeouts values from file. + * + * @param fileName File to load timeouts from. + * @see org.netbeans.jemmy.Timeouts#load(InputStream) + * @see org.netbeans.jemmy.Timeouts#load(String) + * @exception IOException + * @exception FileNotFoundException + */ + public void load(String fileName) + throws FileNotFoundException, IOException { + try (FileInputStream fileInputStream = new FileInputStream(fileName)) { + load(fileInputStream); + } + } + + /** + * Loads timeouts values. Uses jemmy.timeouts system property to get + * timeouts file. + * + * @see org.netbeans.jemmy.Timeouts#load(InputStream) + * @see org.netbeans.jemmy.Timeouts#load(String) + * @exception IOException + * @exception FileNotFoundException + */ + public void load() + throws FileNotFoundException, IOException { + if (System.getProperty("jemmy.timeouts") != null + && !System.getProperty("jemmy.timeouts").equals("")) { + load(System.getProperty("jemmy.timeouts")); + } + } + + /** + * Loads debug timeouts values. + * + * @exception IOException + */ + public void loadDebugTimeouts() throws IOException { + load(getClass().getClassLoader().getResourceAsStream("org/netbeans/jemmy/debug.timeouts")); + } + + /** + * Get timeouts scale. Uses jemmy.timeouts.scale system property to get the + * value. + * + * @return timeouts scale or 1 if the property is not set. + */ + public static double getTimeoutsScale() { + if (timeoutsScale == -1) { + String s = System.getProperty("jemmy.timeouts.scale", "1"); + try { + timeoutsScale = Double.parseDouble(s); + } catch (NumberFormatException e) { + timeoutsScale = 1; + } + } + if (timeoutsScale < 0) { + timeoutsScale = 1; + } + return timeoutsScale; + } + + /** + * This method is designed to be used by unit test for testing purpose. + */ + static void resetTimeoutScale() { + timeoutsScale = -1; + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/Waitable.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/Waitable.java new file mode 100644 index 00000000000..e9dbabead5f --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/Waitable.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy; + +/** + * + * Defines criteria for waiting. + * + * @see org.netbeans.jemmy.Waiter + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + */ +public interface Waitable { + + /** + * Checks if wait criteria have been met. + * + * @param obj optional waiting parameter. + * @return null is criteria have not been met. + */ + public R actionProduced(P obj); + + /** + * Returns description. + * + * @return a description of the wait criteria. + */ + public String getDescription(); +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/Waiter.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/Waiter.java new file mode 100644 index 00000000000..9aaa68c0e42 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/Waiter.java @@ -0,0 +1,352 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy; + +import java.awt.Component; + +/** + * + * Waits for something defined by Waitable interface to be happened. + * + *

Timeouts used:
+ * Waiter.TimeDelta - time delta to check actionProduced result.
+ * Waiter.WaitingTime - maximal waiting time
+ * Waiter.AfterWaitingTime - time to sleep after waiting has been finished.
+ * + * @see Timeouts + * @see Waitable + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + */ +public class Waiter implements Waitable, Timeoutable, Outputable { + + private final static long TIME_DELTA = 10; + private final static long WAIT_TIME = 60000; + private final static long AFTER_WAIT_TIME = 0; + + private final Waitable waitable; + private long startTime = 0; + private long endTime = -1; + private R result; + private Timeouts timeouts; + private String waitingTimeOrigin; + private TestOut out; + + /** + * Replace the fine-grained timeouts with a global flag which can be set, + * for instance, by a separate thread when a global timeout runs out. + */ + public static volatile boolean USE_GLOBAL_TIMEOUT = false; + public static volatile boolean globalTimeoutExpired = false; + + /** + * Constructor. + * + * @param w Waitable object defining waiting criteria. + */ + public Waiter(Waitable w) { + super(); + if (w == null) { + throw new NullPointerException("Waitable cannot be null"); + } + setTimeouts(JemmyProperties.getProperties().getTimeouts()); + setOutput(JemmyProperties.getProperties().getOutput()); + waitable = w; + } + + /** + * Can be used from subclass. actionProduced() method must be overriden in + * a class that uses this super constructor. actionProduced() method in + * Waiter will throw UnsupportedOperationException whenever invoked. + */ + protected Waiter() { + super(); + setTimeouts(JemmyProperties.getProperties().getTimeouts()); + setOutput(JemmyProperties.getProperties().getOutput()); + waitable = null; + } + + static { + Timeouts.initDefault("Waiter.TimeDelta", TIME_DELTA); + Timeouts.initDefault("Waiter.WaitingTime", WAIT_TIME); + Timeouts.initDefault("Waiter.AfterWaitingTime", AFTER_WAIT_TIME); + } + + /** + * Defines current timeouts. + * + * @param timeouts A collection of timeout assignments. + * @see org.netbeans.jemmy.Timeoutable + * @see org.netbeans.jemmy.Timeouts + * @see #getTimeouts + */ + @Override + public void setTimeouts(Timeouts timeouts) { + this.timeouts = timeouts; + } + + /** + * Like {@link #setTimeouts(Timeouts)}, but clones the timeouts first, then + * sets "Waiter.WaitingTime" to the timeout whose name is passed in. This + * name is remembered for display in timeout error messages so people know + * what to adjust. + * + * @param timeouts to be cloned and in which to look up "useAsWaitingTime". + * @param useAsWaitingTime the name of the timeout to apply to + * "Waiter.WaitingTime". + * @param waitingTimeOrigin overrides {@code useAsWaitingTime} in timeout + * reporting if non-null. + * @return the cloned timeouts. + */ + public Timeouts setTimeoutsToCloneOf(Timeouts timeouts, + String useAsWaitingTime, String waitingTimeOrigin) { + Timeouts t = timeouts.cloneThis(); + t.setTimeout("Waiter.WaitingTime", t.getTimeout(useAsWaitingTime)); + setTimeouts(t); + setWaitingTimeOrigin((null != waitingTimeOrigin) ? waitingTimeOrigin : useAsWaitingTime); + return t; + } + + /** + * @see #setTimeoutsToCloneOf(Timeouts, String, String) + */ + public Timeouts setTimeoutsToCloneOf(Timeouts timeouts, + String useAsWaitingTime) { + return setTimeoutsToCloneOf(timeouts, useAsWaitingTime, null); + } + + /** + * Sets the origin of the current "Waiter.WaitingTime" to be shown in + * timeout error messages + * + * @param origin is the name of the origin. + */ + public void setWaitingTimeOrigin(String origin) { + waitingTimeOrigin = origin; + } + + /** + * Return current timeouts. + * + * @return the collection of current timeout assignments. + * @see org.netbeans.jemmy.Timeoutable + * @see org.netbeans.jemmy.Timeouts + * @see #setTimeouts + */ + @Override + public Timeouts getTimeouts() { + return timeouts; + } + + /** + * Defines print output streams or writers. + * + * @param out Identify the streams or writers used for print output. + * @see org.netbeans.jemmy.Outputable + * @see org.netbeans.jemmy.TestOut + * @see #getOutput + */ + @Override + public void setOutput(TestOut out) { + this.out = out; + } + + /** + * Returns print output streams or writers. + * + * @return an object that contains references to objects for printing to + * output and err streams. + * @see org.netbeans.jemmy.Outputable + * @see org.netbeans.jemmy.TestOut + * @see #setOutput + */ + @Override + public TestOut getOutput() { + return out; + } + + /** + * Waits for not null result of actionProduced method of Waitable + * implementation passed into constructor. + * + * @param waitableObject Object to be passed into actionProduced method. + * @return non null result of action. + * @throws TimeoutExpiredException + * @exception InterruptedException + */ + public R waitAction(P waitableObject) + throws InterruptedException { + startTime = System.currentTimeMillis(); + out.printTrace(getWaitingStartedMessage()); + out.printGolden(getGoldenWaitingStartedMessage()); + long timeDelta = timeouts.getTimeout("Waiter.TimeDelta"); + while ((result = actionProduced(waitableObject)) == null) { + Thread.sleep(timeDelta); + if (timeoutExpired()) { + out.printError(getTimeoutExpiredMessage(timeFromStart())); + out.printGolden(getGoldenTimeoutExpiredMessage()); + throw (new TimeoutExpiredException(getActualDescription())); + } + } + endTime = System.currentTimeMillis(); + out.printTrace(getActionProducedMessage(endTime - startTime, result)); + out.printGolden(getGoldenActionProducedMessage()); + Thread.sleep(timeouts.getTimeout("Waiter.AfterWaitingTime")); + return result; + } + + /** + * This method delegates call to the waitable passed in constructor. If a + * subclass was created using protected no-parameters constructor, it should + * implement its own actionProduced method() as this one will throw an + * UnsupportedOperationException. + * @see Waitable + * @param obj + */ + @Override + public R actionProduced(P obj) { + if (waitable != null) { + return waitable.actionProduced(obj); + } else { + throw new UnsupportedOperationException("actionProduced() return " + + "value is not defined. It used to return Boolean.TRUE " + + "in previous versions of Jemmy."); + } + } + + /** + * @see Waitable + */ + @Override + public String getDescription() { + return "Unknown waiting"; + } + + @Override + public String toString() { + return "Waiter{" + "description = " + getDescription() + ", waitable=" + waitable + ", startTime=" + startTime + ", endTime=" + endTime + ", result=" + result + ", waitingTimeOrigin=" + waitingTimeOrigin + '}'; + } + + /** + * Returns message to be printed before waiting start. + * + * @return a message. + */ + protected String getWaitingStartedMessage() { + return "Start to wait action \"" + getActualDescription() + "\""; + } + + /** + * Returns message to be printed when waiting timeout has been expired. + * + * @param timeSpent time from waiting start (milliseconds) + * @return a message. + */ + protected String getTimeoutExpiredMessage(long timeSpent) { + return ("\"" + getActualDescription() + "\" action has not been produced in " + + timeSpent + " milliseconds"); + } + + /** + * Returns message to be printed when waiting has been successfully + * finished. + * + * @param timeSpent time from waiting start (milliseconds) + * @param result result of Waitable.actionproduced method. + * @return a message. + */ + protected String getActionProducedMessage(long timeSpent, final Object result) { + String resultToString; + if (result instanceof Component) { + // run toString in dispatch thread + resultToString = new QueueTool().invokeSmoothly( + new QueueTool.QueueAction("result.toString()") { + @Override + public String launch() { + return result.toString(); + } + } + ); + } else { + resultToString = result.toString(); + } + return ("\"" + getActualDescription() + "\" action has been produced in " + + timeSpent + " milliseconds with result " + + "\n : " + resultToString); + } + + /** + * Returns message to be printed int golden output before waiting start. + * + * @return a message. + */ + protected String getGoldenWaitingStartedMessage() { + return "Start to wait action \"" + getActualDescription() + "\""; + } + + /** + * Returns message to be printed int golden output when waiting timeout has + * been expired. + * + * @return a message. + */ + protected String getGoldenTimeoutExpiredMessage() { + return "\"" + getActualDescription() + "\" action has not been produced"; + } + + /** + * Returns message to be printed int golden output when waiting has been + * successfully finished. + * + * @return a message. + */ + protected String getGoldenActionProducedMessage() { + return "\"" + getActualDescription() + "\" action has been produced"; + } + + /** + * Returns time from waiting start. + * + * @return Time spent for waiting already. + */ + protected long timeFromStart() { + return System.currentTimeMillis() - startTime; + } + + private String getActualDescription() { + final String suffix = (null == waitingTimeOrigin) ? "" : " (" + waitingTimeOrigin + ")"; + if (waitable != null) { + return waitable.getDescription() + suffix; + } else { + return getDescription() + suffix; + } + } + + private boolean timeoutExpired() { + if (USE_GLOBAL_TIMEOUT) { + return globalTimeoutExpired; + } + return timeFromStart() > timeouts.getTimeout("Waiter.WaitingTime"); + } + +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/WindowWaiter.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/WindowWaiter.java new file mode 100644 index 00000000000..7d480000fd6 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/WindowWaiter.java @@ -0,0 +1,483 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy; + +import java.awt.Component; +import java.awt.Frame; +import java.awt.Window; + +/** + * A WindowWaiter is a utility class used to look or wait for Windows. It + * contains methods to search for a Window among the currently showing Windows + * as well as methods that wait for a Window to show within an allotted time + * period. + * + * Searches and waits always involve search criteria applied by a + * ComponentChooser instance. Searches and waits can both be restricted to + * windows owned by a given window. + * + *
Timeouts used:
+ * WindowWaiter.WaitWindowTimeout - time to wait window displayed
+ * WindowWaiter.AfterWindowTimeout - time to sleep after window has been + * dispayed
+ * + * @see org.netbeans.jemmy.Timeouts + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + */ +public class WindowWaiter extends Waiter implements Timeoutable { + + public static boolean FIND_INVISIBLE_WINDOWS = false; + + private final static long WAIT_TIME = 60000; + private final static long AFTER_WAIT_TIME = 0; + + private ComponentChooser chooser; + private Window owner = null; + private int index = 0; + private Timeouts timeouts; + + /** + * Constructor. + */ + public WindowWaiter() { + super(); + setTimeouts(JemmyProperties.getProperties().getTimeouts()); + } + + /** + * Searches for a window. The search proceeds among the currently showing + * windows for the {@code index+1}'th window that is both owned by the + * {@code java.awt.Window} {@code owner} and that meets the + * criteria defined and applied by the {@code ComponentChooser} + * parameter. + * + * @param owner The owner window of all the windows to be searched. + * @param cc A component chooser used to define and apply the search + * criteria. + * @param index The ordinal index of the window in the set of currently + * displayed windows with the proper window ownership and a suitable title. + * The first index is 0. + * @return a reference to the {@code index+1}'th window that is + * showing, has the proper window ownership, and that meets the search + * criteria. If there are fewer than {@code index+1} windows, a + * {@code null} reference is returned. + */ + public static Window getWindow(Window owner, ComponentChooser cc, int index) { + return getAWindow(owner, new IndexChooser(cc, index)); + } + + /** + * Searches for a window. Search among the currently showing windows for the + * first that is both owned by the {@code java.awt.Window} + * {@code owner} and that meets the search criteria applied by the + * {@code ComponentChooser} parameter. + * + * @param owner The owner window of the windows to be searched. + * @param cc A component chooser used to define and apply the search + * criteria. + * @return a reference to the first window that is showing, has a proper + * owner window, and that meets the search criteria. If no such window can + * be found, a {@code null} reference is returned. + */ + public static Window getWindow(Window owner, ComponentChooser cc) { + return getWindow(owner, cc, 0); + } + + /** + * Searches for a window. The search proceeds among the currently showing + * windows for the {@code index+1}'th window that meets the criteria + * defined and applied by the {@code ComonentChooser} parameter. + * + * @param cc A component chooser used to define and apply the search + * criteria. + * @param index The ordinal index of the window in the set of currently + * displayed windows. The first index is 0. + * @return a reference to the {@code index+1}'th window that is showing + * and that meets the search criteria. If there are fewer than + * {@code index+1} windows, a {@code null} reference is returned. + */ + public static Window getWindow(ComponentChooser cc, int index) { + return getAWindow(new IndexChooser(cc, index)); + } + + /** + * Searches for a window. Search among the currently showing windows for one + * that meets the search criteria applied by the + * {@code ComponentChooser} parameter. + * + * @param cc A component chooser used to define and apply the search + * criteria. + * @return a reference to the first window that is showing and that meets + * the search criteria. If no such window can be found, a {@code null} + * reference is returned. + */ + public static Window getWindow(ComponentChooser cc) { + return getWindow(cc, 0); + } + + static { + Timeouts.initDefault("WindowWaiter.WaitWindowTimeout", WAIT_TIME); + Timeouts.initDefault("WindowWaiter.AfterWindowTimeout", AFTER_WAIT_TIME); + } + + /** + * Defines current timeouts. + * + * @param timeouts A collection of timeout assignments. + * @see org.netbeans.jemmy.Timeoutable + * @see org.netbeans.jemmy.Timeouts + * @see #getTimeouts + */ + @Override + public void setTimeouts(Timeouts timeouts) { + this.timeouts = timeouts; + Timeouts times = timeouts.cloneThis(); + times.setTimeout("Waiter.WaitingTime", + timeouts.getTimeout("WindowWaiter.WaitWindowTimeout")); + times.setTimeout("Waiter.AfterWaitingTime", + timeouts.getTimeout("WindowWaiter.AfterWindowTimeout")); + setWaitingTimeOrigin("WindowWaiter.WaitWindowTimeout"); + super.setTimeouts(times); + } + + /** + * Return current timeouts. + * + * @return the collection of current timeout assignments. + * @see org.netbeans.jemmy.Timeoutable + * @see org.netbeans.jemmy.Timeouts + * @see #setTimeouts + */ + @Override + public Timeouts getTimeouts() { + return timeouts; + } + + /** + * Action producer--get a window. Get a window. The search uses constraints + * on window ownership, ordinal index, and search criteria defined by an + * instance of {@code org.netbeans.jemmy.ComponentChooser}. + * + * @param obj Not used. + * @return the window waited upon. If a window cannot be found then a + * {@code null} reference is returned. + * @see org.netbeans.jemmy.Action + */ + @Override + public Window actionProduced(Void obj) { + return WindowWaiter.getWindow(owner, chooser, index); + } + + /** + * Waits for a window to show. Wait for the {@code index+1}'th window + * that meets the criteria defined and applied by the + * {@code ComonentChooser} parameter to show up. + * + * @param ch A component chooser used to define and apply the search + * criteria. + * @param index The ordinal index of the window in the set of currently + * displayed windows. The first index is 0. + * @return a reference to the {@code index+1}'th window that shows and + * that meets the search criteria. If fewer than {@code index+1} + * windows show up in the allotted time period then a {@code null} + * reference is returned. + * @throws TimeoutExpiredException + * @see #actionProduced(Object) + * @exception InterruptedException + */ + public Window waitWindow(ComponentChooser ch, int index) + throws InterruptedException { + chooser = ch; + owner = null; + this.index = index; + return waitWindow(); + } + + /** + * Waits for a window to show. Wait for a window that meets the search + * criteria applied by the {@code ComponentChooser} parameter to show + * up. + * + * @param ch A component chooser used to define and apply the search + * criteria. + * @return a reference to the first window that shows and that meets the + * search criteria. If no such window can be found within the time period + * allotted, a {@code null} reference is returned. + * @throws TimeoutExpiredException + * @see #actionProduced(Object) + * @exception InterruptedException + */ + public Window waitWindow(ComponentChooser ch) + throws InterruptedException { + return waitWindow(ch, 0); + } + + /** + * Waits for a window to show. Wait for the {@code index+1}'th window + * to show that is both owned by the {@code java.awt.Window} + * {@code o} and that meets the criteria defined and applied by the + * {@code ComponentChooser} parameter. + * + * @param o The owner window of all the windows to be searched. + * @param ch A component chooser used to define and apply the search + * criteria. + * @param index The ordinal index of the window in the set of currently + * displayed windows with the proper window ownership and a suitable title. + * The first index is 0. + * @return a reference to the {@code index+1}'th window to show that + * has the proper window ownership, and that meets the search criteria. If + * there are fewer than {@code index+1} windows, a {@code null} + * reference is returned. + * @throws TimeoutExpiredException + * @see #actionProduced(Object) + * @exception InterruptedException + */ + public Window waitWindow(Window o, ComponentChooser ch, int index) + throws InterruptedException { + owner = o; + chooser = ch; + this.index = index; + return waitAction(null); + } + + /** + * Waits for a window to show. Wait for the first window to show that is + * both owned by the {@code java.awt.Window} {@code o} and that + * meets the criteria defined and applied by the + * {@code ComponentChooser} parameter. + * + * @param o The owner window of all the windows to be searched. + * @param ch A component chooser used to define and apply the search + * criteria. + * @return a reference to the first window to show that has the proper + * window ownership, and that meets the search criteria. If there is no such + * window, a {@code null} reference is returned. + * @throws TimeoutExpiredException + * @see #actionProduced(Object) + * @exception InterruptedException + */ + public Window waitWindow(Window o, ComponentChooser ch) + throws InterruptedException { + return waitWindow(o, ch, 0); + } + + @Override + public String getDescription() { + return chooser.getDescription(); + } + + @Override + public String toString() { + return "WindowWaiter{" + "chooser=" + chooser + ", owner=" + owner + ", index=" + index + '}'; + } + + /** + * Method can be used by a subclass to define chooser. + * + * @param ch a chooser specifying searching criteria. + * @see #getComponentChooser + */ + protected void setComponentChooser(ComponentChooser ch) { + chooser = ch; + } + + /** + * Method can be used by a subclass to define chooser. + * + * @return a chooser specifying searching criteria. + * @see #setComponentChooser + */ + protected ComponentChooser getComponentChooser() { + return chooser; + } + + /** + * Method can be used by a subclass to define window owner. + * + * @param owner Window-owner of the set of windows. + * @see #getOwner + */ + protected void setOwner(Window owner) { + this.owner = owner; + } + + /** + * Method can be used by a subclass to define window owner. + * + * @return Window-owner of the set of windows. + * @see #setOwner + */ + protected Window getOwner() { + return owner; + } + + /** + * @see org.netbeans.jemmy.Waiter#getWaitingStartedMessage() + */ + @Override + protected String getWaitingStartedMessage() { + return "Start to wait window \"" + chooser.getDescription() + "\" opened"; + } + + /** + * Overrides Waiter.getTimeoutExpiredMessage. + * + * @see org.netbeans.jemmy.Waiter#getTimeoutExpiredMessage(long) + * @param timeSpent time from waiting start (milliseconds) + * @return a message. + */ + @Override + protected String getTimeoutExpiredMessage(long timeSpent) { + return ("Window \"" + chooser.getDescription() + "\" has not been opened in " + + timeSpent + " milliseconds"); + } + + /** + * Overrides Waiter.getActionProducedMessage. + * + * @see org.netbeans.jemmy.Waiter#getActionProducedMessage(long, Object) + * @param timeSpent time from waiting start (milliseconds) + * @param result result of Waitable.actionproduced method. + * @return a message. + */ + @Override + protected String getActionProducedMessage(long timeSpent, final Object result) { + String resultToString; + if (result instanceof Component) { + // run toString in dispatch thread + resultToString = new QueueTool().invokeSmoothly( + new QueueTool.QueueAction("result.toString()") { + @Override + public String launch() { + return result.toString(); + } + } + ); + } else { + resultToString = result.toString(); + } + return ("Window \"" + chooser.getDescription() + "\" has been opened in " + + timeSpent + " milliseconds" + + "\n " + resultToString); + } + + /** + * @return a message. + * @see org.netbeans.jemmy.Waiter#getGoldenWaitingStartedMessage() + */ + @Override + protected String getGoldenWaitingStartedMessage() { + return "Start to wait window \"" + chooser.getDescription() + "\" opened"; + } + + /** + * @return a message. + * @see org.netbeans.jemmy.Waiter#getGoldenTimeoutExpiredMessage() + */ + @Override + protected String getGoldenTimeoutExpiredMessage() { + return "Window \"" + chooser.getDescription() + "\" has not been opened"; + } + + /** + * @return a message. + * @see org.netbeans.jemmy.Waiter#getGoldenActionProducedMessage() + */ + @Override + protected String getGoldenActionProducedMessage() { + return "Window \"" + chooser.getDescription() + "\" has been opened"; + } + + private static Window getAWindow(Window owner, ComponentChooser cc) { + if (owner == null) { + return WindowWaiter.getAWindow(cc); + } else { + Window result = null; + Window[] windows = owner.getOwnedWindows(); + for (Window window : windows) { + if (cc.checkComponent(window)) { + return window; + } + if ((result = WindowWaiter.getWindow(window, cc)) != null) { + return result; + } + } + return null; + } + } + + private static Window getAWindow(ComponentChooser cc) { + Window result = null; + Frame[] frames = Frame.getFrames(); + for (Frame frame : frames) { + if (cc.checkComponent(frame)) { + return frame; + } + if ((result = WindowWaiter.getWindow(frame, cc)) != null) { + return result; + } + } + return null; + } + + private Window waitWindow() + throws InterruptedException { + return waitAction(null); + } + + private static class IndexChooser implements ComponentChooser { + + private int curIndex = 0; + private int index; + private ComponentChooser chooser; + + public IndexChooser(ComponentChooser ch, int i) { + index = i; + chooser = ch; + curIndex = 0; + } + + @Override + public boolean checkComponent(Component comp) { + if ((FIND_INVISIBLE_WINDOWS || (comp.isShowing() && comp.isVisible())) + && chooser.checkComponent(comp)) { + if (curIndex == index) { + return true; + } + curIndex++; + } + return false; + } + + @Override + public String getDescription() { + return chooser.getDescription(); + } + + @Override + public String toString() { + return "IndexChooser{" + "curIndex=" + curIndex + ", index=" + index + ", chooser=" + chooser + '}'; + } + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/accessibility/AccessibilityChooser.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/accessibility/AccessibilityChooser.java new file mode 100644 index 00000000000..2e9aa1e6330 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/accessibility/AccessibilityChooser.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.accessibility; + +import java.awt.Component; + +import javax.accessibility.AccessibleContext; +import javax.swing.JComponent; +import javax.swing.JDialog; +import javax.swing.JFrame; +import javax.swing.JWindow; + +import org.netbeans.jemmy.ComponentChooser; + +public abstract class AccessibilityChooser implements ComponentChooser { + + @Override + public final boolean checkComponent(Component comp) { + if (comp instanceof JComponent) { + return checkContext(comp.getAccessibleContext()); + } else if (comp instanceof JDialog) { + return checkContext(comp.getAccessibleContext()); + } else if (comp instanceof JFrame) { + return checkContext(comp.getAccessibleContext()); + } else if (comp instanceof JWindow) { + return checkContext(comp.getAccessibleContext()); + } else { + return false; + } + } + + public abstract boolean checkContext(AccessibleContext context); +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/accessibility/AccessibleDescriptionChooser.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/accessibility/AccessibleDescriptionChooser.java new file mode 100644 index 00000000000..984637c339b --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/accessibility/AccessibleDescriptionChooser.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.accessibility; + +import javax.accessibility.AccessibleContext; + +import org.netbeans.jemmy.operators.Operator; +import org.netbeans.jemmy.operators.Operator.StringComparator; + +public class AccessibleDescriptionChooser extends AccessibilityChooser { + + private final String description; + private final StringComparator comparator; + + public AccessibleDescriptionChooser(String description, StringComparator comparator) { + this.description = description; + this.comparator = comparator; + } + + public AccessibleDescriptionChooser(String description) { + this(description, Operator.getDefaultStringComparator()); + } + + @Override + public final boolean checkContext(AccessibleContext context) { + return comparator.equals(context.getAccessibleDescription(), description); + } + + @Override + public String getDescription() { + return "JComponent with \"" + description + "\" accessible description"; + } + + @Override + public String toString() { + return "AccessibleDescriptionChooser{" + "description=" + description + ", comparator=" + comparator + '}'; + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/accessibility/AccessibleNameChooser.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/accessibility/AccessibleNameChooser.java new file mode 100644 index 00000000000..8a173e5d1d7 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/accessibility/AccessibleNameChooser.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.accessibility; + +import javax.accessibility.AccessibleContext; + +import org.netbeans.jemmy.operators.Operator; +import org.netbeans.jemmy.operators.Operator.StringComparator; + +public class AccessibleNameChooser extends AccessibilityChooser { + + String name; + StringComparator comparator; + + public AccessibleNameChooser(String name, StringComparator comparator) { + this.name = name; + this.comparator = comparator; + } + + public AccessibleNameChooser(String name) { + this(name, Operator.getDefaultStringComparator()); + } + + @Override + public final boolean checkContext(AccessibleContext context) { + return comparator.equals(context.getAccessibleName(), name); + } + + @Override + public String getDescription() { + return "JComponent with \"" + name + "\" accessible name"; + } + + @Override + public String toString() { + return "AccessibleNameChooser{" + "name=" + name + ", comparator=" + comparator + '}'; + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/debug.timeouts b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/debug.timeouts new file mode 100644 index 00000000000..215b7d04f16 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/debug.timeouts @@ -0,0 +1,19 @@ +AbstractButtonOperator.PushButtonTimeout=100 +ComponentOperator.AfterDragTimeout=100 +ComponentOperator.BeforeDragTimeout=100 +ComponentOperator.MouseClickTimeout=100 +ComponentOperator.PushKeyTimeout=100 +DialogWaiter.AfterDialogTimeout=3000 +EventDispatcher.RobotAutoDelay=100 +FrameWaiter.AfterFrameTimeout=3000 +JComboBoxOperator.BeforeSelectingTimeout=100 +JComponentOperator.ShowToolTipTimeout=1000 +JMenuItemOperator.PushMenuTimeout=100 +JMenuOperator.WaitBeforePopupTimeout=100 +JScrollBarOperator.OneScrollClickTimeout=10 +JSplitPaneOperator.ScrollClickTimeout=10 +JTextComponentOperator.BetweenKeysTimeout=100 +JTextComponentOperator.PushKeyTimeout=100 +JTextComponentOperator.TypeTextTimeout=60000 +JTreeOperator.WaitAfterNodeExpandedTimeout=100 +WindowWaiter.AfterWindowTimeout=3000 diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/APIDriverInstaller.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/APIDriverInstaller.java new file mode 100644 index 00000000000..b85dfe70425 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/APIDriverInstaller.java @@ -0,0 +1,171 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers; + +import org.netbeans.jemmy.ClassReference; +import org.netbeans.jemmy.JemmyException; +import org.netbeans.jemmy.JemmyProperties; +import org.netbeans.jemmy.drivers.buttons.ButtonMouseDriver; +import org.netbeans.jemmy.drivers.focus.APIFocusDriver; +import org.netbeans.jemmy.drivers.focus.MouseFocusDriver; +import org.netbeans.jemmy.drivers.lists.ChoiceDriver; +import org.netbeans.jemmy.drivers.lists.JComboMouseDriver; +import org.netbeans.jemmy.drivers.lists.JListMouseDriver; +import org.netbeans.jemmy.drivers.lists.JTabAPIDriver; +import org.netbeans.jemmy.drivers.lists.JTableHeaderDriver; +import org.netbeans.jemmy.drivers.lists.ListKeyboardDriver; +import org.netbeans.jemmy.drivers.menus.AppleMenuDriver; +import org.netbeans.jemmy.drivers.menus.DefaultJMenuDriver; +import org.netbeans.jemmy.drivers.menus.QueueJMenuDriver; +import org.netbeans.jemmy.drivers.scrolling.JScrollBarAPIDriver; +import org.netbeans.jemmy.drivers.scrolling.JSliderAPIDriver; +import org.netbeans.jemmy.drivers.scrolling.JSplitPaneDriver; +import org.netbeans.jemmy.drivers.scrolling.ScrollPaneDriver; +import org.netbeans.jemmy.drivers.scrolling.ScrollbarDriver; +import org.netbeans.jemmy.drivers.tables.JTableMouseDriver; +import org.netbeans.jemmy.drivers.text.AWTTextKeyboardDriver; +import org.netbeans.jemmy.drivers.text.SwingTextKeyboardDriver; +import org.netbeans.jemmy.drivers.trees.JTreeAPIDriver; +import org.netbeans.jemmy.drivers.windows.DefaultFrameDriver; +import org.netbeans.jemmy.drivers.windows.DefaultInternalFrameDriver; +import org.netbeans.jemmy.drivers.windows.DefaultWindowDriver; + +/** + * Installs all necessary drivers for Jemmy operators except low-level drivers + * which are installed by + * InputDriverInstaller. + * + * @author Alexandre Iline(alexandre.iline@oracle.com) + */ +public class APIDriverInstaller extends ArrayDriverInstaller { + + /** + * Constructs a DefaultDriverInstaller object. + * + * @param shortcutEvents Signals whether shortcut mode is used. + */ + public APIDriverInstaller(boolean shortcutEvents) { + super(new String[]{ + DriverManager.LIST_DRIVER_ID, + DriverManager.MULTISELLIST_DRIVER_ID, + DriverManager.TREE_DRIVER_ID, + DriverManager.TEXT_DRIVER_ID, + DriverManager.TEXT_DRIVER_ID, + DriverManager.SCROLL_DRIVER_ID, + DriverManager.SCROLL_DRIVER_ID, + DriverManager.SCROLL_DRIVER_ID, + DriverManager.SCROLL_DRIVER_ID, + DriverManager.SCROLL_DRIVER_ID, + DriverManager.SCROLL_DRIVER_ID, + DriverManager.BUTTON_DRIVER_ID, + DriverManager.LIST_DRIVER_ID, + DriverManager.LIST_DRIVER_ID, + DriverManager.MULTISELLIST_DRIVER_ID, + DriverManager.LIST_DRIVER_ID, + DriverManager.LIST_DRIVER_ID, + DriverManager.MULTISELLIST_DRIVER_ID, + DriverManager.TABLE_DRIVER_ID, + DriverManager.LIST_DRIVER_ID, + DriverManager.FRAME_DRIVER_ID, + DriverManager.WINDOW_DRIVER_ID, + DriverManager.FRAME_DRIVER_ID, + DriverManager.INTERNAL_FRAME_DRIVER_ID, + DriverManager.WINDOW_DRIVER_ID, + DriverManager.FOCUS_DRIVER_ID, + DriverManager.FOCUS_DRIVER_ID, + DriverManager.MENU_DRIVER_ID, + DriverManager.MENU_DRIVER_ID, + DriverManager.ORDEREDLIST_DRIVER_ID}, + new Object[]{ + new JTreeAPIDriver(), + new JTreeAPIDriver(), + new JTreeAPIDriver(), + new AWTTextKeyboardDriver(), + new SwingTextKeyboardDriver(), + new ScrollbarDriver(), + new ScrollPaneDriver(), + new JScrollBarAPIDriver(), + new JSplitPaneDriver(), + new JSliderAPIDriver(), + createSpinnerDriver(), + new ButtonMouseDriver(), + new JTabAPIDriver(), + new ListKeyboardDriver(), + new ListKeyboardDriver(), + new JComboMouseDriver(), + new JListMouseDriver(), + new JListMouseDriver(), + new JTableMouseDriver(), + new ChoiceDriver(), + new DefaultFrameDriver(), + new DefaultWindowDriver(), + new DefaultInternalFrameDriver(), + new DefaultInternalFrameDriver(), + new DefaultInternalFrameDriver(), + new APIFocusDriver(), + new MouseFocusDriver(), + (shortcutEvents ? new QueueJMenuDriver() : new DefaultJMenuDriver()), + ((System.getProperty("apple.laf.useScreenMenuBar") != null + && System.getProperty("apple.laf.useScreenMenuBar").equals("true")) ? new AppleMenuDriver() + : (shortcutEvents ? new QueueJMenuDriver() : new DefaultJMenuDriver())), + new JTableHeaderDriver()}); + } + + /** + * Constructs a DefaultDriverInstaller object with shortcut mode flag taken + * from {@code JemmyProperties}. + */ + public APIDriverInstaller() { + this((JemmyProperties.getCurrentDispatchingModel() + & JemmyProperties.SHORTCUT_MODEL_MASK) != 0); + } + + private static LightDriver createSpinnerDriver() { + if (System.getProperty("java.specification.version").compareTo("1.3") > 0) { + try { + return ((LightDriver) new ClassReference("org.netbeans.jemmy.drivers.scrolling.JSpinnerDriver"). + newInstance(null, null)); + } catch (ClassNotFoundException e) { + JemmyProperties.getCurrentOutput(). + printErrLine("ATTENTION! you are using Jemmy built by Java earlier then 1.4, under " + + "Java 1.4. \nImpossible to create JSpinnerDriver"); + return createEmptyDriver(); + } catch (Exception e) { + throw (new JemmyException("Impossible to create JSpinnerDriver although java version is " + + System.getProperty("java.version"), + e)); + } + } else { + return createEmptyDriver(); + } + } + + private static LightDriver createEmptyDriver() { + return (new LightDriver() { + @Override + public String[] getSupported() { + return new String[]{Object.class.getName()}; + } + }); + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/ArrayDriverInstaller.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/ArrayDriverInstaller.java new file mode 100644 index 00000000000..fa57275eb7f --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/ArrayDriverInstaller.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers; + +/** + * Auxiliary class making driver registration easier. + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + */ +public class ArrayDriverInstaller implements DriverInstaller { + + String[] ids; + Object[] drivers; + + /** + * Constructs an ArrayDriverInstaller object. Both parameter arrays mush + * have same length, {@code drivers} must keep instances of + * Driver or + * LightDriver implementations. + * + * @param ids an array of driver IDs + * @param drivers an array of drivers. + */ + public ArrayDriverInstaller(String[] ids, Object[] drivers) { + this.ids = ids; + this.drivers = drivers; + } + + /** + * Installs drivers from the array passed into constructor. + */ + @Override + public void install() { + for (int i = 0; i < ids.length; i++) { + DriverManager.setDriver(ids[i], drivers[i]); + } + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/ButtonDriver.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/ButtonDriver.java new file mode 100644 index 00000000000..befd9b1bc8a --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/ButtonDriver.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers; + +import org.netbeans.jemmy.operators.ComponentOperator; + +/** + * Defines how to work with buttons. + */ +public interface ButtonDriver { + + /** + * Presses a button. + * + * @param oper Button operator. + */ + public void press(ComponentOperator oper); + + /** + * Releases a button. + * + * @param oper Button operator. + */ + public void release(ComponentOperator oper); + + /** + * Pushes a button. + * + * @param oper Button operator. + */ + public void push(ComponentOperator oper); +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/DefaultDriverInstaller.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/DefaultDriverInstaller.java new file mode 100644 index 00000000000..15c8a5a3fec --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/DefaultDriverInstaller.java @@ -0,0 +1,166 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers; + +import org.netbeans.jemmy.ClassReference; +import org.netbeans.jemmy.JemmyException; +import org.netbeans.jemmy.JemmyProperties; +import org.netbeans.jemmy.drivers.buttons.ButtonMouseDriver; +import org.netbeans.jemmy.drivers.focus.APIFocusDriver; +import org.netbeans.jemmy.drivers.focus.MouseFocusDriver; +import org.netbeans.jemmy.drivers.lists.ChoiceDriver; +import org.netbeans.jemmy.drivers.lists.JComboMouseDriver; +import org.netbeans.jemmy.drivers.lists.JListMouseDriver; +import org.netbeans.jemmy.drivers.lists.JTabMouseDriver; +import org.netbeans.jemmy.drivers.lists.JTableHeaderDriver; +import org.netbeans.jemmy.drivers.lists.ListKeyboardDriver; +import org.netbeans.jemmy.drivers.menus.DefaultJMenuDriver; +import org.netbeans.jemmy.drivers.menus.QueueJMenuDriver; +import org.netbeans.jemmy.drivers.scrolling.JScrollBarDriver; +import org.netbeans.jemmy.drivers.scrolling.JSliderDriver; +import org.netbeans.jemmy.drivers.scrolling.JSplitPaneDriver; +import org.netbeans.jemmy.drivers.scrolling.ScrollPaneDriver; +import org.netbeans.jemmy.drivers.scrolling.ScrollbarDriver; +import org.netbeans.jemmy.drivers.tables.JTableMouseDriver; +import org.netbeans.jemmy.drivers.text.AWTTextKeyboardDriver; +import org.netbeans.jemmy.drivers.text.SwingTextKeyboardDriver; +import org.netbeans.jemmy.drivers.trees.JTreeMouseDriver; +import org.netbeans.jemmy.drivers.windows.DefaultFrameDriver; +import org.netbeans.jemmy.drivers.windows.DefaultInternalFrameDriver; +import org.netbeans.jemmy.drivers.windows.DefaultWindowDriver; + +/** + * Installs all necessary drivers for Jemmy operators except low-level drivers + * which are installed by + * InputDriverInstaller. + * + * @author Alexandre Iline(alexandre.iline@oracle.com) + */ +public class DefaultDriverInstaller extends ArrayDriverInstaller { + + /** + * Constructs a DefaultDriverInstaller object. + * + * @param shortcutEvents Signals whether shortcut mode is used. + */ + public DefaultDriverInstaller(boolean shortcutEvents) { + super(new String[]{ + DriverManager.LIST_DRIVER_ID, + DriverManager.MULTISELLIST_DRIVER_ID, + DriverManager.TREE_DRIVER_ID, + DriverManager.TEXT_DRIVER_ID, + DriverManager.TEXT_DRIVER_ID, + DriverManager.SCROLL_DRIVER_ID, + DriverManager.SCROLL_DRIVER_ID, + DriverManager.SCROLL_DRIVER_ID, + DriverManager.SCROLL_DRIVER_ID, + DriverManager.SCROLL_DRIVER_ID, + DriverManager.SCROLL_DRIVER_ID, + DriverManager.BUTTON_DRIVER_ID, + DriverManager.LIST_DRIVER_ID, + DriverManager.LIST_DRIVER_ID, + DriverManager.MULTISELLIST_DRIVER_ID, + DriverManager.LIST_DRIVER_ID, + DriverManager.LIST_DRIVER_ID, + DriverManager.MULTISELLIST_DRIVER_ID, + DriverManager.TABLE_DRIVER_ID, + DriverManager.LIST_DRIVER_ID, + DriverManager.FRAME_DRIVER_ID, + DriverManager.WINDOW_DRIVER_ID, + DriverManager.FRAME_DRIVER_ID, + DriverManager.INTERNAL_FRAME_DRIVER_ID, + DriverManager.WINDOW_DRIVER_ID, + DriverManager.FOCUS_DRIVER_ID, + DriverManager.FOCUS_DRIVER_ID, + DriverManager.MENU_DRIVER_ID, + DriverManager.ORDEREDLIST_DRIVER_ID}, + new Object[]{ + new JTreeMouseDriver(), + new JTreeMouseDriver(), + new JTreeMouseDriver(), + new AWTTextKeyboardDriver(), + new SwingTextKeyboardDriver(), + new ScrollbarDriver(), + new ScrollPaneDriver(), + new JScrollBarDriver(), + new JSplitPaneDriver(), + new JSliderDriver(), + createSpinnerDriver(), + new ButtonMouseDriver(), + new JTabMouseDriver(), + new ListKeyboardDriver(), + new ListKeyboardDriver(), + new JComboMouseDriver(), + new JListMouseDriver(), + new JListMouseDriver(), + new JTableMouseDriver(), + new ChoiceDriver(), + new DefaultFrameDriver(), + new DefaultWindowDriver(), + new DefaultInternalFrameDriver(), + new DefaultInternalFrameDriver(), + new DefaultInternalFrameDriver(), + new APIFocusDriver(), + new MouseFocusDriver(), + (shortcutEvents ? new QueueJMenuDriver() : new DefaultJMenuDriver()), + new JTableHeaderDriver()}); + } + + /** + * Constructs a DefaultDriverInstaller object with shortcut mode flag taken + * from {@code JemmyProperties}. + */ + public DefaultDriverInstaller() { + this((JemmyProperties.getCurrentDispatchingModel() + & JemmyProperties.SHORTCUT_MODEL_MASK) != 0); + } + + private static LightDriver createSpinnerDriver() { + if (System.getProperty("java.specification.version").compareTo("1.3") > 0) { + try { + return ((LightDriver) new ClassReference("org.netbeans.jemmy.drivers.scrolling.JSpinnerDriver"). + newInstance(null, null)); + } catch (ClassNotFoundException e) { + JemmyProperties.getCurrentOutput(). + printErrLine("ATTENTION! you are using Jemmy built by Java earlier then 1.4, under " + + "Java 1.4. \nImpossible to create JSpinnerDriver"); + return createEmptyDriver(); + } catch (Exception e) { + throw (new JemmyException("Impossible to create JSpinnerDriver although java version is " + + System.getProperty("java.version"), + e)); + } + } else { + return createEmptyDriver(); + } + } + + private static LightDriver createEmptyDriver() { + return (new LightDriver() { + @Override + public String[] getSupported() { + return new String[]{Object.class.getName()}; + } + }); + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/DescriptablePathChooser.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/DescriptablePathChooser.java new file mode 100644 index 00000000000..8c6ddcea6a8 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/DescriptablePathChooser.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers; + +/** + * Specifies an interface for objects defining path searching criteria. + * + * @author Alexandre Iline(alexandre.iline@oracle.com) + */ +public interface DescriptablePathChooser extends PathChooser { + + /** + * Gives path description. + */ + public String getDescription(); + +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/Driver.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/Driver.java new file mode 100644 index 00000000000..7ea0d174205 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/Driver.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers; + +/** + * Implements "heavy" model of driver because requires to load classes for all + * supported operator types. + * + * @see LightDriver + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + */ +public interface Driver { + + /** + * Returns an array of operator classes which are supported by this driver. + * + * @return an array of supported operators' classes. + */ + public Class[] getSupported(); +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/DriverInstaller.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/DriverInstaller.java new file mode 100644 index 00000000000..af50e0257cc --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/DriverInstaller.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers; + +/** + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + */ +public interface DriverInstaller { + + public void install(); +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/DriverManager.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/DriverManager.java new file mode 100644 index 00000000000..f38d836f81a --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/DriverManager.java @@ -0,0 +1,810 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers; + +import org.netbeans.jemmy.JemmyException; +import org.netbeans.jemmy.JemmyProperties; +import org.netbeans.jemmy.operators.ComponentOperator; + +/** + * Manages driver set. + */ +public class DriverManager { + + /** + * Symbolic constant - prefix for drivers names. + */ + public static final String DRIVER_ID = "drivers."; + /** + * Symbolic constant for tree drivers. + */ + public static final String TREE_DRIVER_ID = DRIVER_ID + "tree"; + /** + * Symbolic constant for text drivers. + */ + public static final String TEXT_DRIVER_ID = DRIVER_ID + "text"; + /** + * Symbolic constant for key drivers. + */ + public static final String KEY_DRIVER_ID = DRIVER_ID + "key"; + /** + * Symbolic constant for mouse drivers. + */ + public static final String MOUSE_DRIVER_ID = DRIVER_ID + "mouse"; + /** + * Symbolic constant for scroll drivers. + */ + public static final String SCROLL_DRIVER_ID = DRIVER_ID + "scroll"; + /** + * Symbolic constant for button drivers. + */ + public static final String BUTTON_DRIVER_ID = DRIVER_ID + "button"; + /** + * Symbolic constant for list drivers. + */ + public static final String LIST_DRIVER_ID = DRIVER_ID + "list"; + /** + * Symbolic constant for multiselection list drivers. + */ + public static final String MULTISELLIST_DRIVER_ID = DRIVER_ID + "multisellist"; + /** + * Symbolic constant for reorderable list drivers. + */ + public static final String ORDEREDLIST_DRIVER_ID = DRIVER_ID + "orderedlist"; + /** + * Symbolic constant for table drivers. + */ + public static final String TABLE_DRIVER_ID = DRIVER_ID + "table"; + /** + * Symbolic constant for window drivers. + */ + public static final String WINDOW_DRIVER_ID = DRIVER_ID + "window"; + /** + * Symbolic constant for window drivers. + */ + public static final String FRAME_DRIVER_ID = DRIVER_ID + "frame"; + /** + * Symbolic constant for window drivers. + */ + public static final String INTERNAL_FRAME_DRIVER_ID = DRIVER_ID + "internal_frame"; + /** + * Symbolic constant for frame drivers. + */ + public static final String FOCUS_DRIVER_ID = DRIVER_ID + "focus"; + /** + * Symbolic constant for menu drivers. + */ + public static final String MENU_DRIVER_ID = DRIVER_ID + "menu"; + + //cannot be instantiated! + private DriverManager() { + } + + /** + * Searches a driver. + * + * @param id Driver type id. + * @param operatorClass Class to get an driver for. + * @param props Instance to get driver from. + * @return a driver. + * @see #setDriver + */ + public static Object getDriver(String id, Class operatorClass, JemmyProperties props) { + Object result = getADriver(id, operatorClass, props); + if (result == null) { + return getDriver(id, operatorClass); + } else { + return result; + } + } + + /** + * Searches a driver. Uses {@code operator.getProperties()} to receive + * JemmyProperties instance. + * + * @param id Driver type id. + * @param operator Operator to get an driver for. + * @return a driver. + * @see #setDriver + */ + public static Object getDriver(String id, ComponentOperator operator) { + return getDriver(id, operator.getClass(), operator.getProperties()); + } + + /** + * Searches a driver. Uses current JemmyProperties. + * + * @param id Driver type id. + * @param operatorClass Class to get an driver for. + * @return a driver. + * @see #setDriver + */ + public static Object getDriver(String id, Class operatorClass) { + Object result = getADriver(id, operatorClass, JemmyProperties.getProperties()); + if (result == null) { + throw (new JemmyException("No \"" + id + "\" driver registered for " + + operatorClass.getName() + " class!")); + } else { + return result; + } + } + + /** + * Sets driver for an operator class. + * + * @param id Driver type id. + * @param driver A driver to be installed. + * @param operatorClass Class to set driver for. + * @see #getDriver + */ + public static void setDriver(String id, Object driver, Class operatorClass) { + JemmyProperties. + setCurrentProperty(makeID(id, operatorClass), driver); + if (Boolean.getBoolean(DRIVER_ID + "trace_output")) { + JemmyProperties.getCurrentOutput().printLine("Installing " + + driver.getClass().getName() + + " drifer for " + + operatorClass.getName() + + " operators."); + } + } + + /** + * Sets driver for an operator class name. + * + * @param id Driver type id. + * @param driver A driver to be installed. + * @param operatorClassName A name of operator class. + * @see #getDriver + */ + public static void setDriver(String id, Object driver, String operatorClassName) { + JemmyProperties. + setCurrentProperty(makeID(id, operatorClassName), driver); + if (Boolean.getBoolean(DRIVER_ID + "trace_output")) { + JemmyProperties.getCurrentOutput().printLine("Installing " + + driver.getClass().getName() + + " drifer for " + + operatorClassName + + " operators."); + } + } + + /** + * Sets driver for all classes supported by driver. + * + * @param id Driver type id. + * @param driver A driver to be installed. + * @see #getDriver + */ + public static void setDriver(String id, Driver driver) { + Class[] supported = driver.getSupported(); + for (Class aSupported : supported) { + setDriver(id, driver, aSupported); + } + } + + /** + * Sets driver for all classes supported by driver. + * + * @param id Driver type id. + * @param driver A driver to be installed. + * @see #getDriver + */ + public static void setDriver(String id, LightDriver driver) { + String[] supported = driver.getSupported(); + for (String aSupported : supported) { + setDriver(id, driver, aSupported); + } + } + + /** + * Removes driver for operator class. + * + * @param id Driver type to remove. + * @param operatorClass Class to remove driver for. + */ + public static void removeDriver(String id, Class operatorClass) { + JemmyProperties. + removeCurrentProperty(makeID(id, operatorClass)); + if (Boolean.getBoolean(DRIVER_ID + "trace_output")) { + JemmyProperties.getCurrentOutput().printLine("Uninstalling a drifer for " + + operatorClass.getName() + + " operators."); + } + } + + /** + * Removes driver for operator class. + * + * @param id Driver type to remove. + * @param operatorClassName A name of operator class. + */ + public static void removeDriver(String id, String operatorClassName) { + JemmyProperties. + removeCurrentProperty(makeID(id, operatorClassName)); + if (Boolean.getBoolean(DRIVER_ID + "trace_output")) { + JemmyProperties.getCurrentOutput().printLine("Uninstalling a drifer for " + + operatorClassName + + " operators."); + } + } + + /** + * Removes driver for operator classes. + * + * @param id Driver type to remove. + * @param operatorClasses Classes to remove driver for. + */ + public static void removeDriver(String id, Class[] operatorClasses) { + for (Class operatorClass : operatorClasses) { + removeDriver(id, operatorClass); + } + } + + /** + * Removes driver for operator classes. + * + * @param id Driver type to remove. + * @param operatorClassNames Names of operator classes. + */ + public static void removeDriver(String id, String[] operatorClassNames) { + for (String operatorClassName : operatorClassNames) { + removeDriver(id, operatorClassName); + } + } + + /** + * Removes driver for all supported classes. + * + * @param id Driver type to remove. + */ + public static void removeDrivers(String id) { + String[] keys = JemmyProperties.getCurrentKeys(); + for (String key : keys) { + if (key.startsWith(id)) { + JemmyProperties. + removeCurrentProperty(key); + } + } + } + + /** + * Returns {@code TREE_DRIVER_ID} driver. + * + * @param operatorClass Class to find driver for. + * @return a driver + * @see #setTreeDriver + */ + public static TreeDriver getTreeDriver(Class operatorClass) { + return (TreeDriver) getDriver(TREE_DRIVER_ID, operatorClass); + } + + /** + * Returns {@code TREE_DRIVER_ID} driver. + * + * @param operator Operator to find driver for. + * @return a driver + * @see #setTreeDriver + */ + public static TreeDriver getTreeDriver(ComponentOperator operator) { + return (TreeDriver) getDriver(TREE_DRIVER_ID, operator.getClass()); + } + + /** + * Defines {@code TREE_DRIVER_ID} driver. + * + * @param driver a driver + * @see #getTreeDriver + */ + public static void setTreeDriver(TreeDriver driver) { + setDriver(TREE_DRIVER_ID, driver); + } + + /** + * Returns {@code TEXT_DRIVER_ID} driver. + * + * @param operatorClass Class to find driver for. + * @return a driver + * @see #setTextDriver + */ + public static TextDriver getTextDriver(Class operatorClass) { + return (TextDriver) getDriver(TEXT_DRIVER_ID, operatorClass); + } + + /** + * Returns {@code TEXT_DRIVER_ID} driver. + * + * @param operator Operator to find driver for. + * @return a driver + * @see #setTextDriver + */ + public static TextDriver getTextDriver(ComponentOperator operator) { + return (TextDriver) getDriver(TEXT_DRIVER_ID, operator.getClass()); + } + + /** + * Defines {@code TEXT_DRIVER_ID} driver. + * + * @param driver a driver + * @see #getTextDriver + */ + public static void setTextDriver(TextDriver driver) { + setDriver(TEXT_DRIVER_ID, driver); + } + + /** + * Returns {@code KEY_DRIVER_ID} driver. + * + * @param operatorClass Class to find driver for. + * @return a driver + * @see #setKeyDriver + */ + public static KeyDriver getKeyDriver(Class operatorClass) { + return (KeyDriver) getDriver(KEY_DRIVER_ID, operatorClass); + } + + /** + * Returns {@code KEY_DRIVER_ID} driver. + * + * @param operator Operator to find driver for. + * @return a driver + * @see #setKeyDriver + */ + public static KeyDriver getKeyDriver(ComponentOperator operator) { + return (KeyDriver) getDriver(KEY_DRIVER_ID, operator.getClass()); + } + + /** + * Defines {@code KEY_DRIVER_ID} driver. + * + * @param driver a driver + * @see #getKeyDriver + */ + public static void setKeyDriver(KeyDriver driver) { + setDriver(KEY_DRIVER_ID, driver); + } + + /** + * Returns {@code MOUSE_DRIVER_ID} driver. + * + * @param operatorClass Class to find driver for. + * @return a driver + * @see #setMouseDriver + */ + public static MouseDriver getMouseDriver(Class operatorClass) { + return (MouseDriver) getDriver(MOUSE_DRIVER_ID, operatorClass); + } + + /** + * Returns {@code MOUSE_DRIVER_ID} driver. + * + * @param operator Operator to find driver for. + * @return a driver + * @see #setMouseDriver + */ + public static MouseDriver getMouseDriver(ComponentOperator operator) { + return (MouseDriver) getDriver(MOUSE_DRIVER_ID, operator.getClass()); + } + + /** + * Defines {@code MOUSE_DRIVER_ID} driver. + * + * @param driver a driver + * @see #getMouseDriver + */ + public static void setMouseDriver(MouseDriver driver) { + setDriver(MOUSE_DRIVER_ID, driver); + } + + /** + * Returns {@code SCROLL_DRIVER_ID} driver. + * + * @param operatorClass Class to find driver for. + * @return a driver + * @see #setScrollDriver + */ + public static ScrollDriver getScrollDriver(Class operatorClass) { + return (ScrollDriver) getDriver(SCROLL_DRIVER_ID, operatorClass); + } + + /** + * Returns {@code SCROLL_DRIVER_ID} driver. + * + * @param operator Operator to find driver for. + * @return a driver + * @see #setScrollDriver + */ + public static ScrollDriver getScrollDriver(ComponentOperator operator) { + return (ScrollDriver) getDriver(SCROLL_DRIVER_ID, operator.getClass()); + } + + /** + * Defines {@code SCROLL_DRIVER_ID} driver. + * + * @param driver a driver + * @see #getScrollDriver + */ + public static void setScrollDriver(ScrollDriver driver) { + setDriver(SCROLL_DRIVER_ID, driver); + } + + /** + * Returns {@code BUTTON_DRIVER_ID} driver. + * + * @param operatorClass Class to find driver for. + * @return a driver + * @see #setButtonDriver + */ + public static ButtonDriver getButtonDriver(Class operatorClass) { + return (ButtonDriver) getDriver(BUTTON_DRIVER_ID, operatorClass); + } + + /** + * Returns {@code BUTTON_DRIVER_ID} driver. + * + * @param operator Operator to find driver for. + * @return a driver + * @see #setButtonDriver + */ + public static ButtonDriver getButtonDriver(ComponentOperator operator) { + return (ButtonDriver) getDriver(BUTTON_DRIVER_ID, operator.getClass()); + } + + /** + * Defines {@code BUTTON_DRIVER_ID} driver. + * + * @param driver a driver + * @see #getButtonDriver + */ + public static void setButtonDriver(ButtonDriver driver) { + setDriver(BUTTON_DRIVER_ID, driver); + } + + /** + * Returns {@code LIST_DRIVER_ID} driver. + * + * @param operatorClass Class to find driver for. + * @return a driver + * @see #setListDriver + */ + public static ListDriver getListDriver(Class operatorClass) { + return (ListDriver) getDriver(LIST_DRIVER_ID, operatorClass); + } + + /** + * Returns {@code LIST_DRIVER_ID} driver. + * + * @param operator Operator to find driver for. + * @return a driver + * @see #setListDriver + */ + public static ListDriver getListDriver(ComponentOperator operator) { + return (ListDriver) getDriver(LIST_DRIVER_ID, operator.getClass()); + } + + /** + * Defines {@code LIST_DRIVER_ID} driver. + * + * @param driver a driver + * @see #getListDriver + */ + public static void setListDriver(ListDriver driver) { + setDriver(LIST_DRIVER_ID, driver); + } + + /** + * Returns {@code MULTISELLIST_DRIVER_ID} driver. + * + * @param operatorClass Class to find driver for. + * @return a driver + * @see #setMultiSelListDriver + */ + public static MultiSelListDriver getMultiSelListDriver(Class operatorClass) { + return (MultiSelListDriver) getDriver(MULTISELLIST_DRIVER_ID, operatorClass); + } + + /** + * Returns {@code MULTISELLIST_DRIVER_ID} driver. + * + * @param operator Operator to find driver for. + * @return a driver + * @see #setMultiSelListDriver + */ + public static MultiSelListDriver getMultiSelListDriver(ComponentOperator operator) { + return (MultiSelListDriver) getDriver(MULTISELLIST_DRIVER_ID, operator.getClass()); + } + + /** + * Defines {@code MULTISELLIST_DRIVER_ID} driver. + * + * @param driver a driver + * @see #getMultiSelListDriver + */ + public static void setMultiSelListDriver(MultiSelListDriver driver) { + setDriver(MULTISELLIST_DRIVER_ID, driver); + } + + /** + * Returns {@code ORDEREDLIST_DRIVER_ID} driver. + * + * @param operatorClass Class to find driver for. + * @return a driver + * @see #setOrderedListDriver + */ + public static OrderedListDriver getOrderedListDriver(Class operatorClass) { + return (OrderedListDriver) getDriver(ORDEREDLIST_DRIVER_ID, operatorClass); + } + + /** + * Returns {@code ORDEREDLIST_DRIVER_ID} driver. + * + * @param operator Operator to find driver for. + * @return a driver + * @see #setOrderedListDriver + */ + public static OrderedListDriver getOrderedListDriver(ComponentOperator operator) { + return (OrderedListDriver) getDriver(ORDEREDLIST_DRIVER_ID, operator.getClass()); + } + + /** + * Defines {@code ORDEREDLIST_DRIVER_ID} driver. + * + * @param driver a driver + * @see #getOrderedListDriver + */ + public static void setOrderedListDriver(OrderedListDriver driver) { + setDriver(ORDEREDLIST_DRIVER_ID, driver); + } + + /** + * Returns {@code TABLE_DRIVER_ID} driver. + * + * @param operatorClass Class to find driver for. + * @return a driver + * @see #setTableDriver + */ + public static TableDriver getTableDriver(Class operatorClass) { + return (TableDriver) getDriver(TABLE_DRIVER_ID, operatorClass); + } + + /** + * Returns {@code TABLE_DRIVER_ID} driver. + * + * @param operator Operator to find driver for. + * @return a driver + * @see #setTableDriver + */ + public static TableDriver getTableDriver(ComponentOperator operator) { + return (TableDriver) getDriver(TABLE_DRIVER_ID, operator.getClass()); + } + + /** + * Defines {@code TABLE_DRIVER_ID} driver. + * + * @param driver a driver + * @see #getTableDriver + */ + public static void setTableDriver(TableDriver driver) { + setDriver(TABLE_DRIVER_ID, driver); + } + + /** + * Returns {@code WINDOW_DRIVER_ID} driver. + * + * @param operatorClass Class to find driver for. + * @return a driver + * @see #setWindowDriver + */ + public static WindowDriver getWindowDriver(Class operatorClass) { + return (WindowDriver) getDriver(WINDOW_DRIVER_ID, operatorClass); + } + + /** + * Returns {@code WINDOW_DRIVER_ID} driver. + * + * @param operator Operator to find driver for. + * @return a driver + * @see #setWindowDriver + */ + public static WindowDriver getWindowDriver(ComponentOperator operator) { + return (WindowDriver) getDriver(WINDOW_DRIVER_ID, operator.getClass()); + } + + /** + * Defines {@code WINDOW_DRIVER_ID} driver. + * + * @param driver a driver + * @see #getWindowDriver + */ + public static void setWindowDriver(WindowDriver driver) { + setDriver(WINDOW_DRIVER_ID, driver); + } + + /** + * Returns {@code FRAME_DRIVER_ID} driver. + * + * @param operatorClass Class to find driver for. + * @return a driver + * @see #setFrameDriver + */ + public static FrameDriver getFrameDriver(Class operatorClass) { + return (FrameDriver) getDriver(FRAME_DRIVER_ID, operatorClass); + } + + /** + * Returns {@code FRAME_DRIVER_ID} driver. + * + * @param operator Operator to find driver for. + * @return a driver + * @see #setFrameDriver + */ + public static FrameDriver getFrameDriver(ComponentOperator operator) { + return (FrameDriver) getDriver(FRAME_DRIVER_ID, operator.getClass()); + } + + /** + * Defines {@code FRAME_DRIVER_ID} driver. + * + * @param driver a driver + * @see #getFrameDriver + */ + public static void setFrameDriver(FrameDriver driver) { + setDriver(FRAME_DRIVER_ID, driver); + } + + /** + * Returns {@code INTERNAL_FRAME_DRIVER_ID} driver. + * + * @param operatorClass Class to find driver for. + * @return a driver + * @see #setInternalFrameDriver + */ + public static InternalFrameDriver getInternalFrameDriver(Class operatorClass) { + return (InternalFrameDriver) getDriver(INTERNAL_FRAME_DRIVER_ID, operatorClass); + } + + /** + * Returns {@code INTERNAL_FRAME_DRIVER_ID} driver. + * + * @param operator Operator to find driver for. + * @return a driver + * @see #setInternalFrameDriver + */ + public static InternalFrameDriver getInternalFrameDriver(ComponentOperator operator) { + return (InternalFrameDriver) getDriver(INTERNAL_FRAME_DRIVER_ID, operator.getClass()); + } + + /** + * Defines {@code INTERNAL_FRAME_DRIVER_ID} driver. + * + * @param driver a driver + * @see #getInternalFrameDriver + */ + public static void setInternalFrameDriver(InternalFrameDriver driver) { + setDriver(INTERNAL_FRAME_DRIVER_ID, driver); + } + + /** + * Returns {@code FOCUS_DRIVER_ID} driver. + * + * @param operatorClass Class to find driver for. + * @return a driver + * @see #setFocusDriver + */ + public static FocusDriver getFocusDriver(Class operatorClass) { + return (FocusDriver) getDriver(FOCUS_DRIVER_ID, operatorClass); + } + + /** + * Returns {@code FOCUS_DRIVER_ID} driver. + * + * @param operator Operator to find driver for. + * @return a driver + * @see #setFocusDriver + */ + public static FocusDriver getFocusDriver(ComponentOperator operator) { + return (FocusDriver) getDriver(FOCUS_DRIVER_ID, operator.getClass()); + } + + /** + * Defines {@code FOCUS_DRIVER_ID} driver. + * + * @param driver a driver + * @see #getFocusDriver + */ + public static void setFocusDriver(FocusDriver driver) { + setDriver(FOCUS_DRIVER_ID, driver); + } + + /** + * Returns {@code MENU_DRIVER_ID} driver. + * + * @param operatorClass Class to find driver for. + * @return a driver + * @see #setMenuDriver + */ + public static MenuDriver getMenuDriver(Class operatorClass) { + return (MenuDriver) getDriver(MENU_DRIVER_ID, operatorClass); + } + + /** + * Returns {@code MENU_DRIVER_ID} driver. + * + * @param operator Operator to find driver for. + * @return a driver + * @see #setMenuDriver + */ + public static MenuDriver getMenuDriver(ComponentOperator operator) { + return (MenuDriver) getDriver(MENU_DRIVER_ID, operator.getClass()); + } + + /** + * Defines {@code MENU_DRIVER_ID} driver. + * + * @param driver a driver + * @see #getMenuDriver + */ + public static void setMenuDriver(MenuDriver driver) { + setDriver(MENU_DRIVER_ID, driver); + } + + static void setDriver(String id, Object driver) { + if (driver instanceof Driver) { + setDriver(id, (Driver) driver); + } else if (driver instanceof LightDriver) { + setDriver(id, (LightDriver) driver); + } else { + throw (new JemmyException("Driver is neither Driver nor LightDriver " + + driver.toString())); + } + } + + //creates driver id + private static String makeID(String id, Class operatorClass) { + return makeID(id, operatorClass.getName()); + } + + private static String makeID(String id, String operatorClassName) { + return id + "." + operatorClassName; + } + + //returns a driver + private static Object getADriver(String id, Class operatorClass, JemmyProperties props) { + Class superClass = operatorClass; + Object drvr; + do { + drvr = props. + getProperty(makeID(id, superClass)); + if (drvr != null) { + return drvr; + } + } while (ComponentOperator.class. + isAssignableFrom(superClass = superClass.getSuperclass())); + return null; + } + + static { + new InputDriverInstaller().install(); + new DefaultDriverInstaller().install(); + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/EditorDriver.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/EditorDriver.java new file mode 100644 index 00000000000..852790d5d49 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/EditorDriver.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers; + +import org.netbeans.jemmy.operators.ComponentOperator; + +/** + * Interface of objects to be used for value changing (editing). + * + * @author Alexandre Iline(alexandre.iline@oracle.com) + */ +public interface EditorDriver { + + /** + * Changes value. + * + * @param oper Operator to change value for. + * @param newValue a new value. + */ + public void enterNewValue(ComponentOperator oper, Object newValue); +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/FocusDriver.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/FocusDriver.java new file mode 100644 index 00000000000..7f87905fb7f --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/FocusDriver.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers; + +import org.netbeans.jemmy.operators.ComponentOperator; + +/** + * Defines how to manage focus. + */ +public interface FocusDriver { + + /** + * Makes a compoennt having focus. + * + * @param operator Component operator. + */ + public void giveFocus(ComponentOperator operator); +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/FrameDriver.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/FrameDriver.java new file mode 100644 index 00000000000..d0123d6bfb5 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/FrameDriver.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers; + +import org.netbeans.jemmy.operators.ComponentOperator; + +/** + * Defines how to work with frames. + */ +public interface FrameDriver { + + /** + * Iconifies a frame. + * + * @param oper Frame operator. + */ + public void iconify(ComponentOperator oper); + + /** + * Deiconifies a frame. + * + * @param oper Frame operator. + */ + public void deiconify(ComponentOperator oper); + + /** + * Maximizes a frame. + * + * @param oper Frame operator. + */ + public void maximize(ComponentOperator oper); + + /** + * Demaximizes a frame. + * + * @param oper Frame operator. + */ + public void demaximize(ComponentOperator oper); +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/InputDriverInstaller.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/InputDriverInstaller.java new file mode 100644 index 00000000000..c42d8d0bb83 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/InputDriverInstaller.java @@ -0,0 +1,158 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers; + +import org.netbeans.jemmy.EventDispatcher; +import org.netbeans.jemmy.JemmyException; +import org.netbeans.jemmy.JemmyProperties; +import org.netbeans.jemmy.Timeout; +import org.netbeans.jemmy.drivers.input.KeyEventDriver; +import org.netbeans.jemmy.drivers.input.KeyRobotDriver; +import org.netbeans.jemmy.drivers.input.MouseEventDriver; +import org.netbeans.jemmy.drivers.input.MouseRobotDriver; + +/** + * Installs drivers for low-level drivers. + * + * @author Alexandre Iline(alexandre.iline@oracle.com) + */ +public class InputDriverInstaller { + + Timeout robotAutoDelay; + boolean useEventDrivers; + boolean smooth = false; + + /** + * Constructs an InputDriverInstaller object. + * + * @param useEventDrivers Tells whether to use event drivers, otherwise + * robot drivers. + * @param robotAutoDelay Time for {@code Robot.setAutoDelay(long)} + * method. + */ + public InputDriverInstaller(boolean useEventDrivers, Timeout robotAutoDelay) { + this.robotAutoDelay = robotAutoDelay; + this.useEventDrivers = useEventDrivers; + } + + /** + * Constructs an InputDriverInstaller object. Takes autodelay time from + * JemmyProperties' timeouts. + * + * @param useEventDrivers Tells whether to use event drivers, otherwise + * robot drivers. + */ + public InputDriverInstaller(boolean useEventDrivers) { + this(useEventDrivers, + JemmyProperties.getCurrentTimeouts(). + create("EventDispatcher.RobotAutoDelay")); + } + + /** + * Constructs an InputDriverInstaller object. Takes autodelay time from + * JemmyProperties' timeouts. + * + * @param useEventDrivers Tells whether to use event drivers, otherwise + * robot drivers. + * @param smooth whether to move mouse smoothly. + */ + public InputDriverInstaller(boolean useEventDrivers, boolean smooth) { + this(useEventDrivers); + this.smooth = smooth; + } + + /** + * Constructs an InputDriverInstaller object. Uses event drivers. + * + * @param robotAutoDelay Time for {@code Robot.setAutoDelay(long)} + * method. + */ + public InputDriverInstaller(Timeout robotAutoDelay) { + this(true, + robotAutoDelay); + } + + /** + * Constructs an InputDriverInstaller object. Takes autodelay time from + * JemmyProperties' timeouts. Uses event drivers. + */ + public InputDriverInstaller() { + this(true); + } + + static { + EventDispatcher.performInit(); + } + + /** + * Installs input drivers. + */ + public void install() { + if (useEventDrivers) { + LightDriver keyE = new KeyEventDriver(); + LightDriver mouseE = new MouseEventDriver(); + DriverManager.removeDriver(DriverManager.KEY_DRIVER_ID, + keyE.getSupported()); + DriverManager.removeDriver(DriverManager.MOUSE_DRIVER_ID, + mouseE.getSupported()); + DriverManager.setDriver(DriverManager.KEY_DRIVER_ID, keyE); + DriverManager.setDriver(DriverManager.MOUSE_DRIVER_ID, mouseE); + try { + String[] awtOperators + = { + "org.netbeans.jemmy.operators.ButtonOperator", + "org.netbeans.jemmy.operators.CheckboxOperator", + "org.netbeans.jemmy.operators.ChoiceOperator", + "org.netbeans.jemmy.operators.LabelOperator", + "org.netbeans.jemmy.operators.ListOperator", + "org.netbeans.jemmy.operators.ScrollPaneOperator", + "org.netbeans.jemmy.operators.ScrollbarOperator", + "org.netbeans.jemmy.operators.TextAreaOperator", + "org.netbeans.jemmy.operators.TextComponentOperator", + "org.netbeans.jemmy.operators.TextFieldOperator" + }; + LightDriver keyR = new KeyRobotDriver(robotAutoDelay, awtOperators); + LightDriver mouseR = new MouseRobotDriver(robotAutoDelay, awtOperators); + DriverManager.removeDriver(DriverManager.KEY_DRIVER_ID, + keyR.getSupported()); + DriverManager.removeDriver(DriverManager.MOUSE_DRIVER_ID, + mouseR.getSupported()); + DriverManager.setDriver(DriverManager.KEY_DRIVER_ID, keyR); + DriverManager.setDriver(DriverManager.MOUSE_DRIVER_ID, mouseR); + } catch (JemmyException e) { + if (!(e.getInnerThrowable() instanceof ClassNotFoundException)) { + throw (e); + } + } + } else { + LightDriver keyR = new KeyRobotDriver(robotAutoDelay); + LightDriver mouseR = new MouseRobotDriver(robotAutoDelay, smooth); + DriverManager.removeDriver(DriverManager.KEY_DRIVER_ID, + keyR.getSupported()); + DriverManager.removeDriver(DriverManager.MOUSE_DRIVER_ID, + mouseR.getSupported()); + DriverManager.setDriver(DriverManager.KEY_DRIVER_ID, keyR); + DriverManager.setDriver(DriverManager.MOUSE_DRIVER_ID, mouseR); + } + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/InternalFrameDriver.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/InternalFrameDriver.java new file mode 100644 index 00000000000..cc297809e45 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/InternalFrameDriver.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers; + +import java.awt.Component; + +import org.netbeans.jemmy.operators.ComponentOperator; + +/** + * Defines the way to get a title pane. + */ +public interface InternalFrameDriver { + + /** + * Returns the title pane component. + * + * @param oper operator for an internal frame. + * @return a component - title pane. + */ + public Component getTitlePane(ComponentOperator oper); +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/KeyDriver.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/KeyDriver.java new file mode 100644 index 00000000000..f006050efe7 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/KeyDriver.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers; + +import org.netbeans.jemmy.Timeout; +import org.netbeans.jemmy.operators.ComponentOperator; + +/** + * Defines how to simulate keyboard operations. + */ +public interface KeyDriver { + + /** + * Presses a key. + * + * @param oper Component operator. + * @param keyCode Key code ({@code KeyEvent.VK_*} value) + * @param modifiers a combination of {@code InputEvent.*_MASK} fields. + */ + public void pressKey(ComponentOperator oper, int keyCode, int modifiers); + + /** + * Typed a key. + * + * @param oper Component operator. + * @param keyChar Symbol to be typed. + * @param modifiers a combination of {@code InputEvent.*_MASK} fields. + */ + public void typedKey(ComponentOperator oper, int keyCode, char keyChar, int modifiers); + + /** + * Releases a key. + * + * @param oper Component operator. + * @param keyCode Key code ({@code KeyEvent.VK_*} value) + * @param modifiers a combination of {@code InputEvent.*_MASK} fields. + */ + public void releaseKey(ComponentOperator oper, int keyCode, int modifiers); + + /** + * Pushes a key. + * + * @param oper Component operator. + * @param keyCode Key code ({@code KeyEvent.VK_*} value) + * @param modifiers a combination of {@code InputEvent.*_MASK} fields. + * @param pushTime Time between pressing and releasing. + */ + public void pushKey(ComponentOperator oper, int keyCode, int modifiers, Timeout pushTime); + + /** + * Types a symbol. + * + * @param oper Component operator. + * @param keyCode Key code ({@code KeyEvent.VK_*} value) + * @param keyChar Symbol to be typed. + * @param modifiers a combination of {@code InputEvent.*_MASK} fields. + * @param pushTime Time between pressing and releasing. + */ + public void typeKey(ComponentOperator oper, int keyCode, char keyChar, int modifiers, Timeout pushTime); +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/LightDriver.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/LightDriver.java new file mode 100644 index 00000000000..9e39c72ae36 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/LightDriver.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers; + +/** + * Implements "light" model of driver because does not require to load classes + * for all supported operator types. + * + * @see Driver + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + */ +public interface LightDriver { + + /** + * Returns array of operator classes which are supported by this driver. + * + * @return an array of supported operator classes' names. + */ + public String[] getSupported(); +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/LightSupportiveDriver.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/LightSupportiveDriver.java new file mode 100644 index 00000000000..06c2d737de7 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/LightSupportiveDriver.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers; + +import org.netbeans.jemmy.operators.ComponentOperator; + +/** + * Allows to declare supported operator classes. + */ +abstract public class LightSupportiveDriver implements LightDriver { + + private String[] supported; + + /** + * Creates an instance. + * + * @param supported Array of operator classes which are supported by this + * driver. + */ + public LightSupportiveDriver(String[] supported) { + this.supported = supported; + } + + /** + * Throws {@code UnsupportedOperatorException} exception if parameter's + * class is not in list of supported classes. + * + * @param oper Operator whose class should be checked. + * @throws UnsupportedOperatorException + */ + public void checkSupported(ComponentOperator oper) { + UnsupportedOperatorException.checkSupported(getClass(), supported, oper.getClass()); + } + + /** + * Returns array of operator classes which are supported by this driver. + */ + @Override + public String[] getSupported() { + return supported; + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/ListDriver.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/ListDriver.java new file mode 100644 index 00000000000..609ee44765b --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/ListDriver.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers; + +import org.netbeans.jemmy.operators.ComponentOperator; + +/** + * Defines how to work with lists. + */ +public interface ListDriver { + + /** + * Selects an item. + * + * @param oper List operator. + * @param index Item index. + */ + public void selectItem(ComponentOperator oper, int index); +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/MenuDriver.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/MenuDriver.java new file mode 100644 index 00000000000..b006a9ab48b --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/MenuDriver.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers; + +import org.netbeans.jemmy.operators.ComponentOperator; + +/** + * Defines how to work with menus. + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + */ +public interface MenuDriver { + + /** + * Pushes menu. + * + * @param oper Menu operator. + * @param chooser Object defining menupath. + * @return a result of menu pushing. It could be last pushed menuitem or + * anything else. + */ + public Object pushMenu(ComponentOperator oper, PathChooser chooser); +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/MouseDriver.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/MouseDriver.java new file mode 100644 index 00000000000..94738edac8a --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/MouseDriver.java @@ -0,0 +1,123 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers; + +import org.netbeans.jemmy.Timeout; +import org.netbeans.jemmy.operators.ComponentOperator; + +/** + * Defines how to simulate mouse operations. + */ +public interface MouseDriver { + + /** + * Presses mouse. + * + * @param oper Component operator. + * @param x Relative x coordinate. + * @param y Relative y coordinate. + * @param mouseButton mouse button ({@code InputEvent.BUTTON*_MASK} + * field) + * @param modifiers a combination of {@code InputEvent.*_MASK} fields. + */ + public void pressMouse(ComponentOperator oper, int x, int y, int mouseButton, int modifiers); + + /** + * Releases mouse. + * + * @param oper Component operator. + * @param x Relative x coordinate. + * @param y Relative y coordinate. + * @param mouseButton mouse button ({@code InputEvent.BUTTON*_MASK} + * field) + * @param modifiers a combination of {@code InputEvent.*_MASK} fields. + */ + public void releaseMouse(ComponentOperator oper, int x, int y, int mouseButton, int modifiers); + + /** + * Clicks mouse. + * + * @param oper Component operator. + * @param x Relative x coordinate. + * @param y Relative y coordinate. + * @param clickCount How many times to click. + * @param mouseButton mouse button ({@code InputEvent.BUTTON*_MASK} + * field) + * @param modifiers a combination of {@code InputEvent.*_MASK} fields. + * @param mouseClick Time between pressing and releasing mouse. + */ + public void clickMouse(ComponentOperator oper, int x, int y, int clickCount, int mouseButton, + int modifiers, Timeout mouseClick); + + /** + * Moves mouse. + * + * @param oper Component operator. + * @param x Relative x coordinate. + * @param y Relative y coordinate. + */ + public void moveMouse(ComponentOperator oper, int x, int y); + + /** + * Drags mouse. + * + * @param oper Component operator. + * @param x Relative x coordinate. + * @param y Relative y coordinate. + * @param mouseButton mouse button ({@code InputEvent.BUTTON*_MASK} + * field) + * @param modifiers a combination of {@code InputEvent.*_MASK} fields. + */ + public void dragMouse(ComponentOperator oper, int x, int y, int mouseButton, int modifiers); + + /** + * Performs drag'n'drop. + * + * @param oper Component operator. + * @param start_x Relative x coordinate of start point. + * @param start_y Relative y coordinate of start point. + * @param end_x Relative x coordinate of end point. + * @param end_y Relative y coordinate of end point. + * @param mouseButton mouse button ({@code InputEvent.BUTTON*_MASK} + * field) + * @param modifiers a combination of {@code InputEvent.*_MASK} fields. + * @param before Time to sleep after taking (before dragging) + * @param after Time to sleep before dropping (after dragging) + */ + public void dragNDrop(ComponentOperator oper, int start_x, int start_y, int end_x, int end_y, + int mouseButton, int modifiers, Timeout before, Timeout after); + + /** + * Moves mouse inside a component. + * + * @param oper Component operator. + */ + public void enterMouse(ComponentOperator oper); + + /** + * Moves mouse outside a component. + * + * @param oper Component operator. + */ + public void exitMouse(ComponentOperator oper); +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/MultiSelListDriver.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/MultiSelListDriver.java new file mode 100644 index 00000000000..eea518aa68a --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/MultiSelListDriver.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers; + +import org.netbeans.jemmy.operators.ComponentOperator; + +/** + * Defines how to work with lists allowing multiple selection. + */ +public interface MultiSelListDriver extends ListDriver { + + /** + * Selects some items. + * + * @param oper List operator. + * @param indices Item indices. + */ + public void selectItems(ComponentOperator oper, int[] indices); +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/OrderedListDriver.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/OrderedListDriver.java new file mode 100644 index 00000000000..db80dfa2687 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/OrderedListDriver.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers; + +import org.netbeans.jemmy.operators.ComponentOperator; + +/** + * Defines how to work with lists allowing items reordering. + */ +public interface OrderedListDriver extends MultiSelListDriver { + + /** + * Changes item index. + * + * @param oper List operator. + * @param itemIndex Current item index. + * @param newIndex Ne witem index. + */ + public void moveItem(ComponentOperator oper, int itemIndex, int newIndex); +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/PathChooser.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/PathChooser.java new file mode 100644 index 00000000000..876457f835c --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/PathChooser.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers; + +/** + * Specifies an interface for objects defining path searching criteria. + * + * @author Alexandre Iline(alexandre.iline@oracle.com) + */ +public interface PathChooser { + + /** + * Checks if {@code depth}'th path components fits the requirements. + * + * @param depth A depth of the component. + * @param component A path component to be checked. + * @return true if the component fits the requirements. + */ + public boolean checkPathComponent(int depth, Object component); + + /** + * Return requiered depth of the path. + * + * @return depth. + */ + public int getDepth(); +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/ScrollDriver.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/ScrollDriver.java new file mode 100644 index 00000000000..dfbcf057255 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/ScrollDriver.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers; + +import org.netbeans.jemmy.drivers.scrolling.ScrollAdjuster; +import org.netbeans.jemmy.operators.ComponentOperator; + +/** + * Defines how to work with scrollable components such as + * {@code javax.swing.JScrollBar}, {@code javax.swing.JScrollPane}, + * {@code javax.swing.JSlider}, ... + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + */ +public interface ScrollDriver { + + /** + * Changes value to a minimum. + * + * @param oper Scroller operator. + * @param orientation {@code java.awt.Adjustable.HORIZONTAL} or + * {@code java.awt.Adjustable.VERTICAL} + */ + public void scrollToMinimum(ComponentOperator oper, int orientation); + + /** + * Changes value to a maximum. + * + * @param oper Scroller operator. + * @param orientation {@code java.awt.Adjustable.HORIZONTAL} or + * {@code java.awt.Adjustable.VERTICAL} + */ + public void scrollToMaximum(ComponentOperator oper, int orientation); + + /** + * Changes value. + * + * @param oper Scroller operator. + * @param adj Object defines scroll position. + */ + public void scroll(ComponentOperator oper, ScrollAdjuster adj); +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/SupportiveDriver.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/SupportiveDriver.java new file mode 100644 index 00000000000..cc4ae68a165 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/SupportiveDriver.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers; + +import org.netbeans.jemmy.operators.ComponentOperator; + +/** + * Allows to declare supported operator classes. + */ +abstract public class SupportiveDriver implements Driver { + + private Class[] supported; + + /** + * Creates an instance. + * + * @param supported Array of operator classes which are supported by this + * driver. + */ + public SupportiveDriver(Class[] supported) { + this.supported = supported; + } + + /** + * Throws {@code UnsupportedOperatorException} exception if parameter's + * class is not in list of supported classes. + * + * @param oper Operator whose class should be checked. + * @throws UnsupportedOperatorException + */ + public void checkSupported(ComponentOperator oper) { + UnsupportedOperatorException.checkSupported(getClass(), supported, oper.getClass()); + } + + /** + * Returns array of operator classes which are supported by this driver. + */ + @Override + public Class[] getSupported() { + return supported; + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/TableDriver.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/TableDriver.java new file mode 100644 index 00000000000..5243666abdb --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/TableDriver.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers; + +import org.netbeans.jemmy.operators.ComponentOperator; + +/** + * Defines how to work with tables. + */ +public interface TableDriver { + + /** + * Selects a cell. + * + * @param oper Table operator. + * @param row Cell row index. + * @param column Cell column index. + */ + public void selectCell(ComponentOperator oper, int row, int column); + + /** + * Edits a cell. + * + * @param oper Table operator. + * @param row Cell row index. + * @param column Cell column index. + * @param value New value. + */ + public void editCell(ComponentOperator oper, int row, int column, Object value); +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/TextDriver.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/TextDriver.java new file mode 100644 index 00000000000..7866072fb75 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/TextDriver.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers; + +import org.netbeans.jemmy.operators.ComponentOperator; + +/** + * Defines how to work with text components. + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + */ +public interface TextDriver { + + /** + * Moves caret. + * + * @param oper Text component operator. + * @param position Position to move caret to. + */ + public void changeCaretPosition(ComponentOperator oper, int position); + + /** + * Selects text. + * + * @param oper Text component operator. + * @param startPosition a posistion of selction start + * @param finalPosition a posistion of selction end + */ + public void selectText(ComponentOperator oper, int startPosition, int finalPosition); + + /** + * Clears component text. + * + * @param oper Text component operator. + */ + public void clearText(ComponentOperator oper); + + /** + * Types new text. + * + * @param oper Text component operator. + * @param text New text to type. + * @param caretPosition Type text at that position. + */ + public void typeText(ComponentOperator oper, String text, int caretPosition); + + /** + * Replace component text. + * + * @param oper Text component operator. + * @param text New text to type. + */ + public void changeText(ComponentOperator oper, String text); + + /** + * Type text and push enter. + * + * @param oper Text component operator. + * @param text New text to type. + */ + public void enterText(ComponentOperator oper, String text); +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/TreeDriver.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/TreeDriver.java new file mode 100644 index 00000000000..fdb90c27d48 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/TreeDriver.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers; + +import org.netbeans.jemmy.Timeout; +import org.netbeans.jemmy.operators.ComponentOperator; + +/** + * Defines how to work with trees. + */ +public interface TreeDriver extends MultiSelListDriver { + + /** + * Expandes a node. + * + * @param oper Tree operator. + * @param index Node index. + */ + public void expandItem(ComponentOperator oper, int index); + + /** + * Collapses a node. + * + * @param oper Tree operator. + * @param index Node index. + */ + public void collapseItem(ComponentOperator oper, int index); + + /** + * Edits a node. + * + * @param oper Tree operator. + * @param index Node index. + * @param newValue New node value + * @param waitEditorTime Time to wait node editor. + */ + public void editItem(ComponentOperator oper, int index, Object newValue, Timeout waitEditorTime); + + /** + * Starts node editing. + * + * @param oper Tree operator. + * @param index Node index. + * @param waitEditorTime Time to wait node editor. + */ + public void startEditing(ComponentOperator oper, int index, Timeout waitEditorTime); +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/UnsupportedOperatorException.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/UnsupportedOperatorException.java new file mode 100644 index 00000000000..31522d0ebd7 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/UnsupportedOperatorException.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers; + +import org.netbeans.jemmy.JemmyException; + +/** + * Is thrown as a result of attempt to use driver for unsupported operator type. + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + */ +public class UnsupportedOperatorException extends JemmyException { + + private static final long serialVersionUID = 42L; + + /** + * Constructor. + * + * @param driver a driver + * @param operator an operator + */ + public UnsupportedOperatorException(Class driver, Class operator) { + super(driver.getName() + " operators are not supported by " + + operator.getName() + " driver!"); + } + + /** + * Checks if operator class is in the list of supported classes. + * + * @param driver Driver class + * @param supported Supported classes. + * @param operator Operator class. + * @throws UnsupportedOperatorException if class is not supported. + */ + public static void checkSupported(Class driver, Class[] supported, Class operator) { + for (Class aSupported : supported) { + if (aSupported.isAssignableFrom(operator)) { + return; + } + } + throw (new UnsupportedOperatorException(driver, operator)); + } + + /** + * Checks if operator class name is in the list of supported classes names. + * + * @param driver Driver class + * @param supported Supported classes names. + * @param operator Operator class. + * @throws UnsupportedOperatorException if class is not supported. + */ + public static void checkSupported(Class driver, String[] supported, Class operator) { + Class opClass = operator; + do { + for (String aSupported : supported) { + if (opClass.getName().equals(aSupported)) { + return; + } + } + } while ((opClass = opClass.getSuperclass()) != null); + throw (new UnsupportedOperatorException(driver, operator)); + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/WindowDriver.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/WindowDriver.java new file mode 100644 index 00000000000..1f554c68127 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/WindowDriver.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers; + +import org.netbeans.jemmy.operators.ComponentOperator; + +/** + * Defines how to work with windows. + */ +public interface WindowDriver { + + /** + * Activates a window. + * + * @param oper Window operator. + */ + public void activate(ComponentOperator oper); + + /** + * Requests a window to close. + * + * @param oper Window operator. + */ + public void requestClose(ComponentOperator oper); + + /** + * Closes a window by requesting it to close and then hiding it. + * + * @param oper Window operator. + */ + public void requestCloseAndThenHide(ComponentOperator oper); + + /** + * Closes a window by requesting it to close and then hiding it. + * + * @param oper Window operator. + * @deprecated Use requestClose(ComponentOperator) instead. + */ + @Deprecated + public void close(ComponentOperator oper); + + /** + * Change window location. + * + * @param oper Window operator. + * @param x New x coordinate + * @param y New y coordinate + */ + public void move(ComponentOperator oper, int x, int y); + + /** + * Change window size. + * + * @param oper Window operator. + * @param width New window width. + * @param height New window height. + */ + public void resize(ComponentOperator oper, int width, int height); +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/buttons/ButtonMouseDriver.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/buttons/ButtonMouseDriver.java new file mode 100644 index 00000000000..e546665face --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/buttons/ButtonMouseDriver.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers.buttons; + +import org.netbeans.jemmy.drivers.ButtonDriver; +import org.netbeans.jemmy.drivers.DriverManager; +import org.netbeans.jemmy.drivers.LightSupportiveDriver; +import org.netbeans.jemmy.drivers.MouseDriver; +import org.netbeans.jemmy.operators.ComponentOperator; +import org.netbeans.jemmy.operators.Operator; + +/** + * Driver to push a button by mouse click. + * + * @author Alexandre Iline(alexandre.iline@oracle.com) + */ +public class ButtonMouseDriver extends LightSupportiveDriver implements ButtonDriver { + + public ButtonMouseDriver() { + super(new String[]{"org.netbeans.jemmy.operators.ComponentOperator"}); + } + + @Override + public void press(ComponentOperator oper) { + MouseDriver mdriver = DriverManager.getMouseDriver(oper); + mdriver.moveMouse(oper, + oper.getCenterXForClick(), + oper.getCenterYForClick()); + mdriver.pressMouse(oper, + oper.getCenterXForClick(), + oper.getCenterYForClick(), + Operator.getDefaultMouseButton(), + 0); + } + + @Override + public void release(ComponentOperator oper) { + DriverManager. + getMouseDriver(oper). + releaseMouse(oper, + oper.getCenterXForClick(), + oper.getCenterYForClick(), + Operator.getDefaultMouseButton(), + 0); + } + + @Override + public void push(ComponentOperator oper) { + DriverManager. + getMouseDriver(oper). + clickMouse(oper, + oper.getCenterXForClick(), + oper.getCenterYForClick(), + 1, + Operator.getDefaultMouseButton(), + 0, + oper.getTimeouts(). + create("ComponentOperator.MouseClickTimeout")); + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/buttons/package-info.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/buttons/package-info.java new file mode 100644 index 00000000000..f937529f082 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/buttons/package-info.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +/** + *

Button drivers

+ * Different drivers to push a button.
+ * + * @since 4/17/2002 + *
+ */ +package org.netbeans.jemmy.drivers.buttons; diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/focus/APIFocusDriver.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/focus/APIFocusDriver.java new file mode 100644 index 00000000000..4f76f0731e1 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/focus/APIFocusDriver.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers.focus; + +import java.awt.event.FocusEvent; + +import org.netbeans.jemmy.drivers.FocusDriver; +import org.netbeans.jemmy.drivers.LightSupportiveDriver; +import org.netbeans.jemmy.drivers.input.EventDriver; +import org.netbeans.jemmy.operators.ComponentOperator; + +public class APIFocusDriver extends LightSupportiveDriver implements FocusDriver { + + EventDriver eDriver; + + public APIFocusDriver() { + super(new String[]{"org.netbeans.jemmy.operators.ComponentOperator"}); + eDriver = new EventDriver(); + } + + @Override + public void giveFocus(ComponentOperator operator) { + operator.requestFocus(); + eDriver.dispatchEvent(operator.getSource(), + new FocusEvent(operator.getSource(), + FocusEvent.FOCUS_GAINED)); + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/focus/MouseFocusDriver.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/focus/MouseFocusDriver.java new file mode 100644 index 00000000000..4eb21fd704d --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/focus/MouseFocusDriver.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers.focus; + +import org.netbeans.jemmy.QueueTool; +import org.netbeans.jemmy.drivers.DriverManager; +import org.netbeans.jemmy.drivers.FocusDriver; +import org.netbeans.jemmy.drivers.LightSupportiveDriver; +import org.netbeans.jemmy.operators.ComponentOperator; +import org.netbeans.jemmy.operators.Operator; + +public class MouseFocusDriver extends LightSupportiveDriver implements FocusDriver { + + private QueueTool queueTool; + + public MouseFocusDriver() { + super(new String[]{ + "org.netbeans.jemmy.operators.JListOperator", + "org.netbeans.jemmy.operators.JScrollBarOperator", + "org.netbeans.jemmy.operators.JSliderOperator", + "org.netbeans.jemmy.operators.JTableOperator", + "org.netbeans.jemmy.operators.JTextComponentOperator", + "org.netbeans.jemmy.operators.JTreeOperator", + "org.netbeans.jemmy.operators.ListOperator", + "org.netbeans.jemmy.operators.ScrollbarOperator", + "org.netbeans.jemmy.operators.TextAreaOperator", + "org.netbeans.jemmy.operators.TextComponentOperator", + "org.netbeans.jemmy.operators.TextFieldOperator"}); + queueTool = new QueueTool(); + } + + @Override + public void giveFocus(final ComponentOperator oper) { + if (!oper.hasFocus()) { + queueTool.invokeSmoothly(new QueueTool.QueueAction("Mouse click to get focus") { + @Override + public Void launch() { + DriverManager.getMouseDriver(oper). + clickMouse(oper, oper.getCenterXForClick(), oper.getCenterYForClick(), + 1, Operator.getDefaultMouseButton(), 0, + oper.getTimeouts().create("ComponentOperator.MouseClickTimeout")); + return null; + } + }); + oper.waitHasFocus(); + } + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/focus/package-info.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/focus/package-info.java new file mode 100644 index 00000000000..71f9b43be6a --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/focus/package-info.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +/** + *

Focus drivers.

+ * Different drivers to give input focus to a component.
+ *
+ */ +package org.netbeans.jemmy.drivers.focus; diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/input/EventDriver.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/input/EventDriver.java new file mode 100644 index 00000000000..b88b6c0b5f4 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/input/EventDriver.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers.input; + +import java.awt.AWTEvent; +import java.awt.Component; + +import org.netbeans.jemmy.ComponentIsNotVisibleException; +import org.netbeans.jemmy.QueueTool; +import org.netbeans.jemmy.drivers.LightSupportiveDriver; + +/** + * Superclass for all drivers using event dispatching. + * + * @author Alexandre Iline(alexandre.iline@oracle.com) + */ +public class EventDriver extends LightSupportiveDriver { + + /** + * Constructs an EventDriver object. + * + * @param supported an array of supported class names + */ + public EventDriver(String[] supported) { + super(supported); + } + + /** + * Constructs an EventDriver object suporting ComponentOperator. + */ + public EventDriver() { + this(new String[]{"org.netbeans.jemmy.operators.ComponentOperator"}); + } + + /** + * Dispatches an event to the component. + * + * @param comp Component to dispatch events to. + * @param event an event to dispatch. + */ + public void dispatchEvent(Component comp, AWTEvent event) { +// checkVisibility(comp); + QueueTool.processEvent(event); + } + + /** + * Checks component visibility. + * + * @param component a component. + */ + protected void checkVisibility(Component component) { + if (!component.isVisible()) { + throw (new ComponentIsNotVisibleException(component)); + } + } + + /** + * Class used fot execution of an event through the dispatching thread. + */ + protected class Dispatcher extends QueueTool.QueueAction { + + AWTEvent event; + Component component; + + /** + * Constructs an EventDriver$Dispatcher object. + * + * @param component a component to dispatch event to. + * @param e an event to dispatch. + */ + public Dispatcher(Component component, AWTEvent e) { + super(e.getClass().getName() + " event dispatching"); + this.component = component; + event = e; + } + + @Override + public Void launch() { + checkVisibility(component); + component.dispatchEvent(event); + return null; + } + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/input/KeyEventDriver.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/input/KeyEventDriver.java new file mode 100644 index 00000000000..ef6976f9106 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/input/KeyEventDriver.java @@ -0,0 +1,123 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers.input; + +import java.awt.Component; +import java.awt.event.KeyEvent; + +import org.netbeans.jemmy.Timeout; +import org.netbeans.jemmy.drivers.KeyDriver; +import org.netbeans.jemmy.operators.ComponentOperator; + +/** + * KeyDriver using event dispatching. + * + * @author Alexandre Iline(alexandre.iline@oracle.com) + */ +public class KeyEventDriver extends EventDriver implements KeyDriver { + + /** + * Constructs a KeyEventDriver object. + * + * @param supported an array of supported class names + */ + public KeyEventDriver(String[] supported) { + super(supported); + } + + /** + * Constructs an KeyEventDriver object suporting ComponentOperator. + */ + public KeyEventDriver() { + super(); + } + + @Override + public void pressKey(ComponentOperator oper, int keyCode, int modifiers) { + pressKey(findNativeParent(oper.getSource()), keyCode, modifiers); + } + + @Override + public void typedKey(ComponentOperator oper, int keyCode, char keyChar, int modifiers) { + typedKey(findNativeParent(oper.getSource()), keyChar, modifiers); + } + + @Override + public void releaseKey(ComponentOperator oper, int keyCode, int modifiers) { + releaseKey(findNativeParent(oper.getSource()), keyCode, modifiers); + } + + @Override + public void pushKey(ComponentOperator oper, int keyCode, int modifiers, Timeout pushTime) { + Component nativeContainer = findNativeParent(oper.getSource()); + pressKey(nativeContainer, keyCode, modifiers); + pushTime.sleep(); + releaseKey(nativeContainer, keyCode, modifiers); + } + + @Override + public void typeKey(ComponentOperator oper, int keyCode, char keyChar, int modifiers, Timeout pushTime) { + Component nativeContainer = findNativeParent(oper.getSource()); + pressKey(nativeContainer, keyCode, modifiers); + pushTime.sleep(); + typedKey(nativeContainer, keyChar, modifiers); + releaseKey(nativeContainer, keyCode, modifiers); + } + + @Deprecated + private void pressKey(Component nativeContainer, int keyCode, int modifiers) { + dispatchEvent(nativeContainer, + new KeyEvent(nativeContainer, + KeyEvent.KEY_PRESSED, + System.currentTimeMillis(), + modifiers, keyCode)); + } + + private void typedKey(Component nativeContainer, char keyChar, int modifiers) { + dispatchEvent(nativeContainer, + new KeyEvent(nativeContainer, + KeyEvent.KEY_TYPED, + System.currentTimeMillis(), + modifiers, KeyEvent.VK_UNDEFINED, keyChar)); + } + + @Deprecated + private void releaseKey(Component nativeContainer, int keyCode, int modifiers) { + dispatchEvent(nativeContainer, + new KeyEvent(nativeContainer, + KeyEvent.KEY_RELEASED, + System.currentTimeMillis(), + modifiers, keyCode)); + } + + private Component findNativeParent(Component source) { + Component nativeOne = source; + while (nativeOne != null) { + if (!nativeOne.isLightweight()) { + return nativeOne; + } + nativeOne = nativeOne.getParent(); + } + return source; + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/input/KeyRobotDriver.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/input/KeyRobotDriver.java new file mode 100644 index 00000000000..63d75f4bd8e --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/input/KeyRobotDriver.java @@ -0,0 +1,95 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers.input; + +import org.netbeans.jemmy.Timeout; +import org.netbeans.jemmy.drivers.KeyDriver; +import org.netbeans.jemmy.operators.ComponentOperator; + +/** + * KeyDriver using robot operations. + * + * @author Alexandre Iline(alexandre.iline@oracle.com) + */ +public class KeyRobotDriver extends RobotDriver implements KeyDriver { + + /** + * Constructs a KeyRobotDriver object. + * + * @param autoDelay Time for {@code Robot.setAutoDelay(long)} method. + */ + public KeyRobotDriver(Timeout autoDelay) { + super(autoDelay); + } + + /** + * Constructs a KeyRobotDriver object. + * + * @param autoDelay Time for {@code Robot.setAutoDelay(long)} method. + * @param supported an array of supported class names + */ + public KeyRobotDriver(Timeout autoDelay, String[] supported) { + super(autoDelay, supported); + } + + @Override + public void pushKey(ComponentOperator oper, int keyCode, int modifiers, Timeout pushTime) { + pressKey(oper, keyCode, modifiers); + pushTime.sleep(); + releaseKey(oper, keyCode, modifiers); + } + + @Override + public void typeKey(ComponentOperator oper, int keyCode, char keyChar, int modifiers, Timeout pushTime) { + pushKey(oper, keyCode, modifiers, pushTime); + } + + /** + * Presses a key. + * + * @param oper Operator to press a key on. + * @param keyCode Key code ({@code KeyEventVK_*} field. + * @param modifiers a combination of {@code InputEvent.*_MASK} fields. + */ + @Override + public void pressKey(ComponentOperator oper, int keyCode, int modifiers) { + pressKey(keyCode, modifiers); + } + + @Override + public void typedKey(ComponentOperator oper, int keyCode, char keyChar, int modifiers) { + releaseKey(oper, keyCode, modifiers); + } + + /** + * Releases a key. + * + * @param oper Operator to release a key on. + * @param keyCode Key code ({@code KeyEventVK_*} field. + * @param modifiers a combination of {@code InputEvent.*_MASK} fields. + */ + @Override + public void releaseKey(ComponentOperator oper, int keyCode, int modifiers) { + releaseKey(keyCode, modifiers); + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/input/MouseEventDriver.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/input/MouseEventDriver.java new file mode 100644 index 00000000000..705239b35be --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/input/MouseEventDriver.java @@ -0,0 +1,186 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers.input; + +import java.awt.Component; +import java.awt.event.MouseEvent; +import org.netbeans.jemmy.Timeout; +import org.netbeans.jemmy.drivers.MouseDriver; +import org.netbeans.jemmy.operators.ComponentOperator; +import org.netbeans.jemmy.operators.Operator; + +/** + * MouseDriver using event dispatching. + * + * @author Alexandre Iline(alexandre.iline@oracle.com) + */ +public class MouseEventDriver extends EventDriver implements MouseDriver { + + /** + * Constructs a MouseEventDriver object. + * + * @param supported an array of supported class names + */ + public MouseEventDriver(String[] supported) { + super(supported); + } + + /** + * Constructs a MouseEventDriver object. + */ + public MouseEventDriver() { + super(); + } + + @Override + public void pressMouse(ComponentOperator oper, int x, int y, int mouseButton, int modifiers) { + dispatchEvent(oper.getSource(), + MouseEvent.MOUSE_PRESSED, + modifiers, x, y, 1, + mouseButton); + } + + @Override + public void releaseMouse(ComponentOperator oper, int x, int y, int mouseButton, int modifiers) { + dispatchEvent(oper.getSource(), + MouseEvent.MOUSE_RELEASED, + modifiers, x, y, 1, + mouseButton); + } + + @Override + public void moveMouse(ComponentOperator oper, int x, int y) { + dispatchEvent(oper.getSource(), + MouseEvent.MOUSE_MOVED, + 0, x, y, 0, + Operator.getDefaultMouseButton()); + } + + @Override + public void clickMouse(ComponentOperator oper, int x, int y, int clickCount, int mouseButton, + int modifiers, Timeout mouseClick) { + + moveMouse(oper, x, y); + dispatchEvent(oper.getSource(), + MouseEvent.MOUSE_ENTERED, + 0, x, y, 0, + Operator.getDefaultMouseButton()); + dispatchEvent(oper.getSource(), + MouseEvent.MOUSE_PRESSED, + modifiers, x, y, 1, + mouseButton); + + for (int i = 1; i < clickCount; i++) { + dispatchEvent(oper.getSource(), + MouseEvent.MOUSE_RELEASED, + modifiers, x, y, i, + mouseButton); + dispatchEvent(oper.getSource(), + MouseEvent.MOUSE_CLICKED, + modifiers, x, y, i, + mouseButton); + dispatchEvent(oper.getSource(), + MouseEvent.MOUSE_PRESSED, + modifiers, x, y, i + 1, + mouseButton); + } + + mouseClick.sleep(); + dispatchEvent(oper.getSource(), + MouseEvent.MOUSE_RELEASED, + modifiers, x, y, clickCount, + mouseButton); + dispatchEvent(oper.getSource(), + MouseEvent.MOUSE_CLICKED, + modifiers, x, y, clickCount, + mouseButton); + exitMouse(oper); + } + + @Override + public void dragMouse(ComponentOperator oper, int x, int y, int mouseButton, int modifiers) { + dispatchEvent(oper.getSource(), + MouseEvent.MOUSE_DRAGGED, + modifiers, x, y, 1, + mouseButton); + } + + @Override + public void dragNDrop(ComponentOperator oper, int start_x, int start_y, int end_x, int end_y, + int mouseButton, int modifiers, Timeout before, Timeout after) { + + dispatchEvent(oper.getSource(), + MouseEvent.MOUSE_ENTERED, + 0, start_x, start_y, 0, + Operator.getDefaultMouseButton()); + dispatchEvent(oper.getSource(), + MouseEvent.MOUSE_PRESSED, + modifiers, start_x, start_y, 1, + mouseButton); + before.sleep(); + dragMouse(oper, end_x, end_y, mouseButton, modifiers); + after.sleep(); + dispatchEvent(oper.getSource(), + MouseEvent.MOUSE_RELEASED, + modifiers, end_x, end_y, 1, + mouseButton); + exitMouse(oper); + } + + @Override + public void enterMouse(ComponentOperator oper) { + dispatchEvent(oper.getSource(), + MouseEvent.MOUSE_ENTERED, + 0, oper.getCenterX(), oper.getCenterY(), 0, + Operator.getDefaultMouseButton()); + } + + @Override + public void exitMouse(ComponentOperator oper) { + dispatchEvent(oper.getSource(), + MouseEvent.MOUSE_EXITED, + 0, oper.getCenterX(), oper.getCenterY(), 0, + Operator.getDefaultMouseButton()); + } + + /** + * Dispatches a mouse event to the component. + * + * @param comp Component to dispatch events to. + * @param id an event id. + * @param modifiers a combination of {@code InputEvent.*_MASK} fields. + * @param x relative x coordinate of event point + * @param y relative y coordinate of event point + * @param clickCount click count + * @param mouseButton mouse button. + */ + protected void dispatchEvent(Component comp, int id, int modifiers, int x, int y, int clickCount, int mouseButton) { + dispatchEvent(comp, + new MouseEvent(comp, + id, + System.currentTimeMillis(), + modifiers | mouseButton, x, y, clickCount, + mouseButton == Operator.getPopupMouseButton() + && id == MouseEvent.MOUSE_PRESSED)); + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/input/MouseRobotDriver.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/input/MouseRobotDriver.java new file mode 100644 index 00000000000..be466f8951d --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/input/MouseRobotDriver.java @@ -0,0 +1,142 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers.input; + +import org.netbeans.jemmy.Timeout; +import org.netbeans.jemmy.drivers.MouseDriver; +import org.netbeans.jemmy.operators.ComponentOperator; + +/** + * MouseDriver using robot operations. + * + * @author Alexandre Iline(alexandre.iline@oracle.com) + */ +public class MouseRobotDriver extends RobotDriver implements MouseDriver { + + /** + * Constructs a MouseRobotDriver object. + * + * @param autoDelay Time for {@code Robot.setAutoDelay(long)} method. + */ + public MouseRobotDriver(Timeout autoDelay) { + super(autoDelay); + } + + /** + * Constructs a MouseRobotDriver object. + * + * @param autoDelay Time for {@code Robot.setAutoDelay(long)} method. + * @param smooth - whether to move mouse smooth from one ppoint to another. + */ + public MouseRobotDriver(Timeout autoDelay, boolean smooth) { + super(autoDelay, smooth); + } + + /** + * Constructs a MouseRobotDriver object. + * + * @param autoDelay Time for {@code Robot.setAutoDelay(long)} method. + * @param supported an array of supported class names + */ + public MouseRobotDriver(Timeout autoDelay, String[] supported) { + super(autoDelay, supported); + } + + /** + * Constructs a MouseRobotDriver object. + * + * @param autoDelay Time for {@code Robot.setAutoDelay(long)} method. + * @param supported an array of supported class names + * @param smooth - whether to move mouse smooth from one ppoint to another. + */ + public MouseRobotDriver(Timeout autoDelay, String[] supported, boolean smooth) { + super(autoDelay, supported, smooth); + } + + @Override + public void pressMouse(ComponentOperator oper, int x, int y, int mouseButton, int modifiers) { + pressMouse(mouseButton, modifiers); + } + + @Override + public void releaseMouse(ComponentOperator oper, int x, int y, int mouseButton, int modifiers) { + releaseMouse(mouseButton, modifiers); + } + + @Override + public void moveMouse(ComponentOperator oper, int x, int y) { + moveMouse(getAbsoluteX(oper, x), getAbsoluteY(oper, y)); + } + + @Override + public void clickMouse(ComponentOperator oper, int x, int y, int clickCount, int mouseButton, + int modifiers, Timeout mouseClick) { + clickMouse(getAbsoluteX(oper, x), getAbsoluteY(oper, y), clickCount, mouseButton, modifiers, mouseClick); + } + + @Override + public void dragMouse(ComponentOperator oper, int x, int y, int mouseButton, int modifiers) { + moveMouse(getAbsoluteX(oper, x), getAbsoluteY(oper, y)); + } + + @Override + public void dragNDrop(ComponentOperator oper, int start_x, int start_y, int end_x, int end_y, + int mouseButton, int modifiers, Timeout before, Timeout after) { + dragNDrop(getAbsoluteX(oper, start_x), getAbsoluteY(oper, start_y), getAbsoluteX(oper, end_x), getAbsoluteY(oper, end_y), mouseButton, modifiers, before, after); + } + + @Override + public void enterMouse(ComponentOperator oper) { + moveMouse(oper, oper.getCenterXForClick(), oper.getCenterYForClick()); + } + + @Override + public void exitMouse(ComponentOperator oper) { + //better not go anywhere + //exit will be executed during the next + //mouse move anyway. + // moveMouse(oper, -1, -1); + } + + /** + * Returns absolute x coordinate for relative x coordinate. + * + * @param oper an operator + * @param x a relative x coordinate. + * @return an absolute x coordinate. + */ + protected int getAbsoluteX(ComponentOperator oper, int x) { + return oper.getSource().getLocationOnScreen().x + x; + } + + /** + * Returns absolute y coordinate for relative y coordinate. + * + * @param oper an operator + * @param y a relative y coordinate. + * @return an absolute y coordinate. + */ + protected int getAbsoluteY(ComponentOperator oper, int y) { + return oper.getSource().getLocationOnScreen().y + y; + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/input/RobotDriver.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/input/RobotDriver.java new file mode 100644 index 00000000000..44d8638d76e --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/input/RobotDriver.java @@ -0,0 +1,340 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers.input; + +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.lang.reflect.InvocationTargetException; + +import org.netbeans.jemmy.ClassReference; +import org.netbeans.jemmy.JemmyException; +import org.netbeans.jemmy.JemmyProperties; +import org.netbeans.jemmy.QueueTool; +import org.netbeans.jemmy.TestOut; +import org.netbeans.jemmy.Timeout; +import org.netbeans.jemmy.drivers.LightSupportiveDriver; + +/** + * Superclass for all drivers using robot. + * + * @author Alexandre Iline(alexandre.iline@oracle.com) + */ +public class RobotDriver extends LightSupportiveDriver { + + private boolean haveOldPos; + private boolean smooth = false; + private double oldX; + private double oldY; + private static final double CONSTANT1 = 0.75; + private static final double CONSTANT2 = 12.0; + /** + * A reference to the robot instance. + */ + protected ClassReference robotReference = null; + + /** + * A QueueTool instance. + */ + protected QueueTool qtool; + + protected Timeout autoDelay; + + /** + * Constructs a RobotDriver object. + * + * @param autoDelay Time for {@code Robot.setAutoDelay(long)} method. + * @param supported an array of supported class names + */ + public RobotDriver(Timeout autoDelay, String[] supported) { + super(supported); + qtool = new QueueTool(); + qtool.setOutput(TestOut.getNullOutput()); + this.autoDelay = autoDelay; + } + + public RobotDriver(Timeout autoDelay, String[] supported, boolean smooth) { + this(autoDelay, supported); + this.smooth = smooth; + } + + /** + * Constructs a RobotDriver object. + * + * @param autoDelay Time for {@code Robot.setAutoDelay(long)} method. + */ + public RobotDriver(Timeout autoDelay) { + this(autoDelay, new String[]{"org.netbeans.jemmy.operators.ComponentOperator"}); + } + + public RobotDriver(Timeout autoDelay, boolean smooth) { + this(autoDelay); + this.smooth = smooth; + } + + public void pressMouse(int mouseButton, int modifiers) { + pressModifiers(modifiers); + makeAnOperation("mousePress", + new Object[]{mouseButton}, + new Class[]{Integer.TYPE}); + } + + public void releaseMouse(int mouseButton, int modifiers) { + makeAnOperation("mouseRelease", + new Object[]{mouseButton}, + new Class[]{Integer.TYPE}); + releaseModifiers(modifiers); + } + + public void moveMouse(int x, int y) { + if (!smooth) { + makeAnOperation("mouseMove", + new Object[]{x, y}, + new Class[]{Integer.TYPE, Integer.TYPE}); + } else { + double targetX = x; + double targetY = y; + if (haveOldPos) { + double currX = oldX; + double currY = oldY; + double vx = 0.0; + double vy = 0.0; + while (Math.round(currX) != Math.round(targetX) + || Math.round(currY) != Math.round(targetY)) { + vx = vx * CONSTANT1 + (targetX - currX) / CONSTANT2 * (1.0 - CONSTANT1); + vy = vy * CONSTANT1 + (targetY - currY) / CONSTANT2 * (1.0 - CONSTANT1); + currX += vx; + currY += vy; + makeAnOperation("mouseMove", new Object[]{ + (int) Math.round(currX), + (int) Math.round(currY)}, + new Class[]{Integer.TYPE, Integer.TYPE}); + } + } else { + makeAnOperation("mouseMove", new Object[]{ + (int) Math.round(targetX), + (int) Math.round(targetY)}, + new Class[]{Integer.TYPE, Integer.TYPE}); + } + haveOldPos = true; + oldX = targetX; + oldY = targetY; + } + } + + public void clickMouse(int x, int y, int clickCount, int mouseButton, + int modifiers, Timeout mouseClick) { + pressModifiers(modifiers); + moveMouse(x, y); + makeAnOperation("mousePress", new Object[]{mouseButton}, new Class[]{Integer.TYPE}); + for (int i = 1; i < clickCount; i++) { + makeAnOperation("mouseRelease", new Object[]{mouseButton}, new Class[]{Integer.TYPE}); + makeAnOperation("mousePress", new Object[]{mouseButton}, new Class[]{Integer.TYPE}); + } + mouseClick.sleep(); + makeAnOperation("mouseRelease", new Object[]{mouseButton}, new Class[]{Integer.TYPE}); + releaseModifiers(modifiers); + } + + public void dragMouse(int x, int y, int mouseButton, int modifiers) { + moveMouse(x, y); + } + + public void dragNDrop(int start_x, int start_y, int end_x, int end_y, + int mouseButton, int modifiers, Timeout before, Timeout after) { + moveMouse(start_x, start_y); + pressMouse(mouseButton, modifiers); + before.sleep(); + moveMouse(end_x, end_y); + after.sleep(); + releaseMouse(mouseButton, modifiers); + } + + /** + * Presses a key. + * + * @param keyCode Key code ({@code KeyEventVK_*} field. + * @param modifiers a combination of {@code InputEvent.*_MASK} fields. + */ + public void pressKey(int keyCode, int modifiers) { + pressModifiers(modifiers); + makeAnOperation("keyPress", + new Object[]{keyCode}, + new Class[]{Integer.TYPE}); + } + + /** + * Releases a key. + * + * @param keyCode Key code ({@code KeyEventVK_*} field. + * @param modifiers a combination of {@code InputEvent.*_MASK} fields. + */ + public void releaseKey(int keyCode, int modifiers) { + releaseModifiers(modifiers); + makeAnOperation("keyRelease", + new Object[]{keyCode}, + new Class[]{Integer.TYPE}); + } + + /** + * Performs a single operation. + * + * @param method a name of {@code java.awt.Robot} method. + * @param params method parameters + * @param paramClasses method parameters classes + */ + protected void makeAnOperation(final String method, final Object[] params, final Class[] paramClasses) { + if (robotReference == null) { + initRobot(); + } + try { + robotReference.invokeMethod(method, params, paramClasses); + synchronizeRobot(); + } catch (InvocationTargetException + | IllegalStateException + | NoSuchMethodException + | IllegalAccessException e) { + throw (new JemmyException("Exception during java.awt.Robot accessing", e)); + } + } + + /** + * Calls {@code java.awt.Robot.waitForIdle()} method. + */ + protected void synchronizeRobot() { + if (!QueueTool.isDispatchThread()) { + if ((JemmyProperties.getCurrentDispatchingModel() & JemmyProperties.QUEUE_MODEL_MASK) != 0) { + if (robotReference == null) { + initRobot(); + } + try { + robotReference.invokeMethod("waitForIdle", null, null); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } + + /** + * Presses modifiers keys by robot. + * + * @param modifiers a combination of {@code InputEvent.*_MASK} fields. + */ + protected void pressModifiers(int modifiers) { + if ((modifiers & InputEvent.SHIFT_MASK) != 0) { + pressKey(KeyEvent.VK_SHIFT, modifiers & ~InputEvent.SHIFT_MASK); + } else if ((modifiers & InputEvent.ALT_GRAPH_MASK) != 0) { + pressKey(KeyEvent.VK_ALT_GRAPH, modifiers & ~InputEvent.ALT_GRAPH_MASK); + } else if ((modifiers & InputEvent.ALT_MASK) != 0) { + pressKey(KeyEvent.VK_ALT, modifiers & ~InputEvent.ALT_MASK); + } else if ((modifiers & InputEvent.META_MASK) != 0) { + pressKey(KeyEvent.VK_META, modifiers & ~InputEvent.META_MASK); + } else if ((modifiers & InputEvent.CTRL_MASK) != 0) { + pressKey(KeyEvent.VK_CONTROL, modifiers & ~InputEvent.CTRL_MASK); + } + } + + /* + protected void pressModifiers(ComponentOperator oper, int modifiers) { + if ((modifiers & InputEvent.SHIFT_MASK) != 0) { + pressKey(oper, KeyEvent.VK_SHIFT, modifiers & ~InputEvent.SHIFT_MASK); + } else if((modifiers & InputEvent.ALT_GRAPH_MASK) != 0) { + pressKey(oper, KeyEvent.VK_ALT_GRAPH, modifiers & ~InputEvent.ALT_GRAPH_MASK); + } else if((modifiers & InputEvent.ALT_MASK) != 0) { + pressKey(oper, KeyEvent.VK_ALT, modifiers & ~InputEvent.ALT_MASK); + } else if((modifiers & InputEvent.META_MASK) != 0) { + pressKey(oper, KeyEvent.VK_META, modifiers & ~InputEvent.META_MASK); + } else if((modifiers & InputEvent.CTRL_MASK) != 0) { + pressKey(oper, KeyEvent.VK_CONTROL, modifiers & ~InputEvent.CTRL_MASK); + } + } + */ + /** + * Releases modifiers keys by robot. + * + * @param modifiers a combination of {@code InputEvent.*_MASK} fields. + */ + protected void releaseModifiers(int modifiers) { + if ((modifiers & InputEvent.SHIFT_MASK) != 0) { + releaseKey(KeyEvent.VK_SHIFT, modifiers & ~InputEvent.SHIFT_MASK); + } else if ((modifiers & InputEvent.ALT_GRAPH_MASK) != 0) { + releaseKey(KeyEvent.VK_ALT_GRAPH, modifiers & ~InputEvent.ALT_GRAPH_MASK); + } else if ((modifiers & InputEvent.ALT_MASK) != 0) { + releaseKey(KeyEvent.VK_ALT, modifiers & ~InputEvent.ALT_MASK); + } else if ((modifiers & InputEvent.META_MASK) != 0) { + releaseKey(KeyEvent.VK_META, modifiers & ~InputEvent.META_MASK); + } else if ((modifiers & InputEvent.CTRL_MASK) != 0) { + releaseKey(KeyEvent.VK_CONTROL, modifiers & ~InputEvent.CTRL_MASK); + } + } + + /* + protected void releaseModifiers(ComponentOperator oper, int modifiers) { + if ((modifiers & InputEvent.SHIFT_MASK) != 0) { + releaseKey(oper, KeyEvent.VK_SHIFT, modifiers & ~InputEvent.SHIFT_MASK); + } else if((modifiers & InputEvent.ALT_GRAPH_MASK) != 0) { + releaseKey(oper, KeyEvent.VK_ALT_GRAPH, modifiers & ~InputEvent.ALT_GRAPH_MASK); + } else if((modifiers & InputEvent.ALT_MASK) != 0) { + releaseKey(oper, KeyEvent.VK_ALT, modifiers & ~InputEvent.ALT_MASK); + } else if((modifiers & InputEvent.META_MASK) != 0) { + releaseKey(oper, KeyEvent.VK_META, modifiers & ~InputEvent.META_MASK); + } else if((modifiers & InputEvent.CTRL_MASK) != 0) { + releaseKey(oper, KeyEvent.VK_CONTROL, modifiers & ~InputEvent.CTRL_MASK); + } + } + */ + private void initRobot() { + // need to init Robot in dispatch thread because it hangs on Linux + // (see http://www.netbeans.org/issues/show_bug.cgi?id=37476) + if (QueueTool.isDispatchThread()) { + doInitRobot(); + } else { + qtool.invokeAndWait(new Runnable() { + @Override + public void run() { + doInitRobot(); + } + }); + } + } + + private void doInitRobot() { + try { + ClassReference robotClassReverence = new ClassReference("java.awt.Robot"); + robotReference = new ClassReference(robotClassReverence.newInstance(null, null)); + robotReference.invokeMethod("setAutoDelay", + new Object[]{(int) ((autoDelay != null) + ? autoDelay.getValue() + : 0)}, + new Class[]{Integer.TYPE}); + } catch (InvocationTargetException + | IllegalStateException + | NoSuchMethodException + | IllegalAccessException + | ClassNotFoundException + | InstantiationException e) { + throw (new JemmyException("Exception during java.awt.Robot accessing", e)); + } + } + +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/input/package-info.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/input/package-info.java new file mode 100644 index 00000000000..90d7263cdfb --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/input/package-info.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +/** + *

Mouse and keyboard drivers

+ * Contains low-level drivers to perform user input.
+ * + * @since 17 Apr 2002 + *
+ */ +package org.netbeans.jemmy.drivers.input; diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/lists/ChoiceDriver.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/lists/ChoiceDriver.java new file mode 100644 index 00000000000..a6fa8217f52 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/lists/ChoiceDriver.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers.lists; + +import java.awt.Point; +import java.awt.event.KeyEvent; + +import org.netbeans.jemmy.Timeout; +import org.netbeans.jemmy.drivers.DriverManager; +import org.netbeans.jemmy.drivers.KeyDriver; +import org.netbeans.jemmy.drivers.LightSupportiveDriver; +import org.netbeans.jemmy.drivers.ListDriver; +import org.netbeans.jemmy.operators.ChoiceOperator; +import org.netbeans.jemmy.operators.ComponentOperator; +import org.netbeans.jemmy.operators.Operator; + +/** + * List driver for java.awt.Choice component type. + * + * @author Alexandre Iline(alexandre.iline@oracle.com) + */ +public class ChoiceDriver extends LightSupportiveDriver implements ListDriver { + + private final static int RIGHT_INDENT = 10; + + /** + * Constructs a ChoiceDriver. + */ + public ChoiceDriver() { + super(new String[]{"org.netbeans.jemmy.operators.ChoiceOperator"}); + } + + @Override + public void selectItem(ComponentOperator oper, int index) { + ChoiceOperator coper = (ChoiceOperator) oper; + Point pointToClick = getClickPoint(oper); + DriverManager.getMouseDriver(oper). + clickMouse(oper, pointToClick.x, pointToClick.y, + 1, Operator.getDefaultMouseButton(), 0, + oper.getTimeouts().create("ComponentOperator.MouseClickTimeout")); + KeyDriver kdriver = DriverManager.getKeyDriver(oper); + Timeout pushTimeout = oper.getTimeouts().create("ComponentOperator.PushKeyTimeout"); + if (System.getProperty("java.specification.version").compareTo("1.3") > 0) { + while (coper.getSelectedIndex() != index) { + kdriver.pushKey(oper, (index > coper.getSelectedIndex()) ? KeyEvent.VK_DOWN : KeyEvent.VK_UP, 0, pushTimeout); + } + } else { + int current = ((ChoiceOperator) oper).getSelectedIndex(); + int diff = 0; + int key = 0; + if (index > current) { + diff = index - current; + key = KeyEvent.VK_DOWN; + } else { + diff = current - index; + key = KeyEvent.VK_UP; + } + for (int i = 0; i < diff; i++) { + kdriver.pushKey(oper, key, 0, pushTimeout); + } + } + kdriver.pushKey(oper, KeyEvent.VK_ENTER, 0, pushTimeout); + } + + private Point getClickPoint(ComponentOperator oper) { + return new Point(oper.getWidth() - RIGHT_INDENT, oper.getHeight() / 2); + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/lists/JComboMouseDriver.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/lists/JComboMouseDriver.java new file mode 100644 index 00000000000..8781721eae9 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/lists/JComboMouseDriver.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers.lists; + +import javax.swing.UIManager; + +import org.netbeans.jemmy.QueueTool; +import org.netbeans.jemmy.drivers.DriverManager; +import org.netbeans.jemmy.drivers.LightSupportiveDriver; +import org.netbeans.jemmy.drivers.ListDriver; +import org.netbeans.jemmy.operators.ComponentOperator; +import org.netbeans.jemmy.operators.JComboBoxOperator; +import org.netbeans.jemmy.operators.JListOperator; +import org.netbeans.jemmy.util.EmptyVisualizer; + +/** + * List driver for javax.swing.JCompoBox component type. + * + * @author Alexandre Iline(alexandre.iline@oracle.com) + */ +public class JComboMouseDriver extends LightSupportiveDriver implements ListDriver { + + /** + * Constructs a JComboMouseDriver. + */ + QueueTool queueTool; + + public JComboMouseDriver() { + super(new String[]{"org.netbeans.jemmy.operators.JComboBoxOperator"}); + queueTool = new QueueTool(); + } + + @Override + public void selectItem(ComponentOperator oper, int index) { + JComboBoxOperator coper = (JComboBoxOperator) oper; + //1.5 workaround + if (System.getProperty("java.specification.version").compareTo("1.4") > 0) { + queueTool.setOutput(oper.getOutput().createErrorOutput()); + queueTool.waitEmpty(10); + queueTool.waitEmpty(10); + queueTool.waitEmpty(10); + } + //end of 1.5 workaround + if (!coper.isPopupVisible()) { + if (UIManager.getLookAndFeel().getClass().getName().equals("com.sun.java.swing.plaf.motif.MotifLookAndFeel")) { + oper.clickMouse(oper.getWidth() - 2, oper.getHeight() / 2, 1); + } else { + DriverManager.getButtonDriver(coper.getButton()). + push(coper.getButton()); + } + } + JListOperator list = new JListOperator(coper.waitList()); + list.copyEnvironment(coper); + list.setVisualizer(new EmptyVisualizer()); + coper.getTimeouts().sleep("JComboBoxOperator.BeforeSelectingTimeout"); + DriverManager.getListDriver(list). + selectItem(list, index); + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/lists/JListMouseDriver.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/lists/JListMouseDriver.java new file mode 100644 index 00000000000..be02eabffd6 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/lists/JListMouseDriver.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers.lists; + +import java.awt.Rectangle; +import java.awt.event.InputEvent; + +import org.netbeans.jemmy.QueueTool; +import org.netbeans.jemmy.drivers.DriverManager; +import org.netbeans.jemmy.drivers.LightSupportiveDriver; +import org.netbeans.jemmy.drivers.MultiSelListDriver; +import org.netbeans.jemmy.operators.ComponentOperator; +import org.netbeans.jemmy.operators.JListOperator; +import org.netbeans.jemmy.operators.Operator; + +/** + * List driver for javax.swing.JList component type. + * + * @author Alexandre Iline(alexandre.iline@oracle.com) + */ +public class JListMouseDriver extends LightSupportiveDriver implements MultiSelListDriver { + + QueueTool queueTool; + + /** + * Constructs a JListMouseDriver. + */ + public JListMouseDriver() { + super(new String[]{"org.netbeans.jemmy.operators.JListOperator"}); + queueTool = new QueueTool(); + } + + @Override + public void selectItem(ComponentOperator oper, int index) { + clickOnItem((JListOperator) oper, index); + } + + @Override + public void selectItems(ComponentOperator oper, int[] indices) { + clickOnItem((JListOperator) oper, indices[0]); + for (int i = 1; i < indices.length; i++) { + clickOnItem((JListOperator) oper, indices[i], InputEvent.CTRL_MASK); + } + } + + /** + * Clicks on a list item. + * + * @param oper an operator to click on. + * @param index item index. + */ + protected void clickOnItem(JListOperator oper, int index) { + clickOnItem(oper, index, 0); + } + + /** + * Clicks on a list item. + * + * @param oper an operator to click on. + * @param index item index. + * @param modifiers a combination of {@code InputEvent.*_MASK} fields. + */ + protected void clickOnItem(final JListOperator oper, final int index, final int modifiers) { + if (!QueueTool.isDispatchThread()) { + oper.scrollToItem(index); + } + queueTool.invokeSmoothly(new QueueTool.QueueAction("Path selecting") { + @Override + public Void launch() { + Rectangle rect = oper.getCellBounds(index, index); + DriverManager.getMouseDriver(oper). + clickMouse(oper, + rect.x + rect.width / 2, + rect.y + rect.height / 2, + 1, Operator.getDefaultMouseButton(), modifiers, + oper.getTimeouts().create("ComponentOperator.MouseClickTimeout")); + return null; + } + }); + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/lists/JTabAPIDriver.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/lists/JTabAPIDriver.java new file mode 100644 index 00000000000..f9a9b1ba707 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/lists/JTabAPIDriver.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers.lists; + +import org.netbeans.jemmy.QueueTool; +import org.netbeans.jemmy.drivers.LightSupportiveDriver; +import org.netbeans.jemmy.drivers.ListDriver; +import org.netbeans.jemmy.operators.ComponentOperator; +import org.netbeans.jemmy.operators.JTabbedPaneOperator; + +/** + * List driver for javax.swing.JTabbedPane component type. + * + * @author Alexandre Iline(alexandre.iline@oracle.com) + */ +public class JTabAPIDriver extends LightSupportiveDriver implements ListDriver { + + private QueueTool queueTool; + + /** + * Constructs a JTabMouseDriver. + */ + public JTabAPIDriver() { + super(new String[]{"org.netbeans.jemmy.operators.JTabbedPaneOperator"}); + queueTool = new QueueTool(); + } + + @Override + public void selectItem(final ComponentOperator oper, final int index) { + if (index != -1) { + queueTool.invokeSmoothly(new QueueTool.QueueAction("Selecting tab " + index + " by setting selectedIndex") { + @Override + public Void launch() { + ((JTabbedPaneOperator) oper).setSelectedIndex(index); + return null; + } + }); + } + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/lists/JTabMouseDriver.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/lists/JTabMouseDriver.java new file mode 100644 index 00000000000..9cb1f85d596 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/lists/JTabMouseDriver.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers.lists; + +import java.awt.Rectangle; + +import javax.swing.JTabbedPane; + +import org.netbeans.jemmy.QueueTool; +import org.netbeans.jemmy.drivers.DriverManager; +import org.netbeans.jemmy.drivers.LightSupportiveDriver; +import org.netbeans.jemmy.drivers.ListDriver; +import org.netbeans.jemmy.operators.ComponentOperator; +import org.netbeans.jemmy.operators.JTabbedPaneOperator; +import org.netbeans.jemmy.operators.Operator; + +/** + * List driver for javax.swing.JTabbedPane component type. + * + * @author Alexandre Iline(alexandre.iline@oracle.com) + */ +public class JTabMouseDriver extends LightSupportiveDriver implements ListDriver { + + private QueueTool queueTool; + + /** + * Constructs a JTabMouseDriver. + */ + public JTabMouseDriver() { + super(new String[]{"org.netbeans.jemmy.operators.JTabbedPaneOperator"}); + queueTool = new QueueTool(); + } + + @Override + public void selectItem(final ComponentOperator oper, final int index) { + if (index != -1) { + queueTool.invokeSmoothly(new QueueTool.QueueAction("Selecting tab " + index + " using mouse") { + @Override + public Void launch() { + Rectangle rect = ((JTabbedPaneOperator) oper). + getUI(). + getTabBounds((JTabbedPane) oper.getSource(), + index); + DriverManager.getMouseDriver(oper). + clickMouse(oper, + (int) (rect.getX() + rect.getWidth() / 2), + (int) (rect.getY() + rect.getHeight() / 2), + 1, Operator.getDefaultMouseButton(), 0, + oper.getTimeouts().create("ComponentOperator.MouseClickTimeout")); + return null; + } + }); + } + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/lists/JTableHeaderDriver.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/lists/JTableHeaderDriver.java new file mode 100644 index 00000000000..e55dc8bf9c9 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/lists/JTableHeaderDriver.java @@ -0,0 +1,105 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers.lists; + +import java.awt.Point; +import java.awt.event.InputEvent; + +import org.netbeans.jemmy.QueueTool; +import org.netbeans.jemmy.drivers.DriverManager; +import org.netbeans.jemmy.drivers.LightSupportiveDriver; +import org.netbeans.jemmy.drivers.OrderedListDriver; +import org.netbeans.jemmy.operators.ComponentOperator; +import org.netbeans.jemmy.operators.JTableHeaderOperator; +import org.netbeans.jemmy.operators.Operator; + +/** + * List driver for javax.swing.table.JTableHeader component type. + * + * @author Alexandre Iline(alexandre.iline@oracle.com) + */ +public class JTableHeaderDriver extends LightSupportiveDriver implements OrderedListDriver { + + private QueueTool queueTool; + + /** + * Constructs a JTableHeaderDriver. + */ + public JTableHeaderDriver() { + super(new String[]{"org.netbeans.jemmy.operators.JTableHeaderOperator"}); + queueTool = new QueueTool(); + } + + @Override + public void selectItem(ComponentOperator oper, int index) { + clickOnHeader((JTableHeaderOperator) oper, index); + } + + @Override + public void selectItems(ComponentOperator oper, int[] indices) { + clickOnHeader((JTableHeaderOperator) oper, indices[0]); + for (int i = 1; i < indices.length; i++) { + clickOnHeader((JTableHeaderOperator) oper, indices[i], InputEvent.CTRL_MASK); + } + } + + @Override + public void moveItem(ComponentOperator oper, int moveColumn, int moveTo) { + Point start = ((JTableHeaderOperator) oper).getPointToClick(moveColumn); + Point end = ((JTableHeaderOperator) oper).getPointToClick(moveTo); + oper.dragNDrop(start.x, start.y, end.x, end.y); + } + + /** + * Clicks on a column header. + * + * @param oper an operator to click on. + * @param index column index. + */ + protected void clickOnHeader(JTableHeaderOperator oper, int index) { + clickOnHeader(oper, index, 0); + } + + /** + * Clicks on a column header. + * + * @param oper an operator to click on. + * @param index column index. + * @param modifiers a combination of {@code InputEvent.*_MASK} fields. + */ + protected void clickOnHeader(final JTableHeaderOperator oper, final int index, final int modifiers) { + queueTool.invokeSmoothly(new QueueTool.QueueAction("Column selecting") { + @Override + public Void launch() { + Point toClick = oper.getPointToClick(index); + DriverManager.getMouseDriver(oper). + clickMouse(oper, + toClick.x, + toClick.y, + 1, Operator.getDefaultMouseButton(), modifiers, + oper.getTimeouts().create("ComponentOperator.MouseClickTimeout")); + return null; + } + }); + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/lists/ListAPIDriver.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/lists/ListAPIDriver.java new file mode 100644 index 00000000000..bf464cfc67d --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/lists/ListAPIDriver.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers.lists; + +import org.netbeans.jemmy.drivers.LightSupportiveDriver; +import org.netbeans.jemmy.drivers.MultiSelListDriver; +import org.netbeans.jemmy.operators.ComponentOperator; +import org.netbeans.jemmy.operators.ListOperator; + +/** + * List driver for java.awt.List component type. Uses API calls. + * + * @author Alexandre Iline(alexandre.iline@oracle.com) + */ +public class ListAPIDriver extends LightSupportiveDriver implements MultiSelListDriver { + + /** + * Constructs a ListAPIDriver. + */ + public ListAPIDriver() { + super(new String[]{"org.netbeans.jemmy.operators.ListOperator"}); + } + + @Override + public void selectItem(ComponentOperator oper, int index) { + ListOperator loper = (ListOperator) oper; + clearSelection(loper); + loper.select(index); + } + + @Override + public void selectItems(ComponentOperator oper, int[] indices) { + ListOperator loper = (ListOperator) oper; + clearSelection(loper); + for (int indice : indices) { + loper.select(indice); + } + } + + private void clearSelection(ListOperator loper) { + for (int i = 0; i < loper.getItemCount(); i++) { + loper.deselect(i); + } + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/lists/ListKeyboardDriver.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/lists/ListKeyboardDriver.java new file mode 100644 index 00000000000..927d62a570d --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/lists/ListKeyboardDriver.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers.lists; + +import java.awt.event.KeyEvent; + +import org.netbeans.jemmy.Timeout; +import org.netbeans.jemmy.drivers.DriverManager; +import org.netbeans.jemmy.drivers.KeyDriver; +import org.netbeans.jemmy.drivers.MultiSelListDriver; +import org.netbeans.jemmy.operators.ComponentOperator; +import org.netbeans.jemmy.operators.ListOperator; + +/** + * List driver for java.awt.List component type. Uses keyboard and mouse. + * + * @author Alexandre Iline(alexandre.iline@oracle.com) + */ +public class ListKeyboardDriver extends ListAPIDriver implements MultiSelListDriver { + + /** + * Constructs a ListKeyboardDriver. + */ + public ListKeyboardDriver() { + super(); + } + + @Override + public void selectItem(ComponentOperator oper, int index) { + ListOperator loper = (ListOperator) oper; + if (loper.isMultipleMode()) { + super.selectItem(loper, index); + } + DriverManager.getFocusDriver(oper).giveFocus(oper); + KeyDriver kDriver = DriverManager.getKeyDriver(oper); + int current = loper.getSelectedIndex(); + int diff = 0; + int key = 0; + if (index > current) { + diff = index - current; + key = KeyEvent.VK_DOWN; + } else { + diff = current - index; + key = KeyEvent.VK_UP; + } + Timeout pushTime = oper.getTimeouts().create("ComponentOperator.PushKeyTimeout"); + for (int i = 0; i < diff; i++) { + kDriver.pushKey(oper, key, 0, pushTime); + } + kDriver.pushKey(oper, KeyEvent.VK_ENTER, 0, pushTime); + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/lists/package-info.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/lists/package-info.java new file mode 100644 index 00000000000..0187ea93b2a --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/lists/package-info.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +/** + *

List drivers

+ * Different drivers to perform a list operations.
+ * + * @since 4/17/2002 + *
+ */ +package org.netbeans.jemmy.drivers.lists; diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/menus/APIJMenuDriver.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/menus/APIJMenuDriver.java new file mode 100644 index 00000000000..8df8776d61e --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/menus/APIJMenuDriver.java @@ -0,0 +1,122 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers.menus; + +import java.awt.Component; + +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; + +import org.netbeans.jemmy.ComponentChooser; +import org.netbeans.jemmy.JemmyException; +import org.netbeans.jemmy.drivers.MenuDriver; +import org.netbeans.jemmy.drivers.PathChooser; +import org.netbeans.jemmy.operators.AbstractButtonOperator; +import org.netbeans.jemmy.operators.ComponentOperator; +import org.netbeans.jemmy.operators.JMenuItemOperator; +import org.netbeans.jemmy.operators.JMenuOperator; + +public class APIJMenuDriver extends DefaultJMenuDriver implements MenuDriver { + + public APIJMenuDriver() { + super(); + } + + protected Object push(ComponentOperator oper, JMenuBar menuBar, + PathChooser chooser, int depth, boolean pressMouse) { + try { + oper.waitComponentVisible(true); + oper.waitComponentEnabled(); + } catch (InterruptedException e) { + throw (new JemmyException("Interrupted!", e)); + } + if (depth > chooser.getDepth() - 1) { + if (oper instanceof JMenuOperator) { + if (((JMenuOperator) oper).isPopupMenuVisible()) { + ((JMenuOperator) oper).setPopupMenuVisible(false); + } + ((JMenuOperator) oper).setPopupMenuVisible(true); + waitPopupMenu(oper); + } + ((AbstractButtonOperator) oper).doClick(); + return oper.getSource(); + } else { + if (((JMenuOperator) oper).isPopupMenuVisible()) { + ((JMenuOperator) oper).setPopupMenuVisible(false); + } + ((JMenuOperator) oper).setPopupMenuVisible(true); + waitPopupMenu(oper); + } + oper.getTimeouts().sleep("JMenuOperator.WaitBeforePopupTimeout"); + JMenuItem item = waitItem(oper, waitPopupMenu(oper), chooser, depth); + if (item instanceof JMenu) { + JMenuOperator mo = new JMenuOperator((JMenu) item); + mo.copyEnvironment(oper); + Object result = push(mo, null, chooser, depth + 1, false); + if (result instanceof JMenu) { + org.netbeans.jemmy.JemmyProperties.getCurrentOutput().printLine("IN HERE" + ((JMenu) result).getText()); + org.netbeans.jemmy.JemmyProperties.getCurrentOutput().printLine("IN HERE" + Boolean.toString(((JMenu) result).isPopupMenuVisible())); + if (!((JMenu) result).isPopupMenuVisible()) { + ((JMenuOperator) oper).setPopupMenuVisible(false); + } + } else { + ((JMenuOperator) oper).setPopupMenuVisible(false); + waitNoPopupMenu(oper); + } + return result; + } else { + JMenuItemOperator mio = new JMenuItemOperator(item); + mio.copyEnvironment(oper); + try { + mio.waitComponentEnabled(); + } catch (InterruptedException e) { + throw (new JemmyException("Interrupted!", e)); + } + mio.doClick(); + ((JMenuOperator) oper).setPopupMenuVisible(false); + waitNoPopupMenu(oper); + return item; + } + } + + protected void waitNoPopupMenu(final ComponentOperator oper) { + oper.waitState(new ComponentChooser() { + @Override + public boolean checkComponent(Component comp) { + return !((JMenuOperator) oper).isPopupMenuVisible(); + } + + @Override + public String getDescription() { + return ((JMenuOperator) oper).getText() + "'s popup"; + } + + @Override + public String toString() { + return "waitNoPopupMenu.ComponentChooser{description = " + getDescription() + '}'; + } + }); + } + +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/menus/AppleMenuDriver.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/menus/AppleMenuDriver.java new file mode 100644 index 00000000000..1d6fcdd88a2 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/menus/AppleMenuDriver.java @@ -0,0 +1,140 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers.menus; + +import java.awt.event.KeyEvent; + +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.MenuElement; + +import org.netbeans.jemmy.JemmyException; +import org.netbeans.jemmy.Timeout; +import org.netbeans.jemmy.TimeoutExpiredException; +import org.netbeans.jemmy.drivers.DescriptablePathChooser; +import org.netbeans.jemmy.drivers.MenuDriver; +import org.netbeans.jemmy.drivers.PathChooser; +import org.netbeans.jemmy.drivers.input.RobotDriver; +import org.netbeans.jemmy.operators.ComponentOperator; +import org.netbeans.jemmy.operators.Operator; + +/** + * + * @author shura + */ +public class AppleMenuDriver extends RobotDriver implements MenuDriver { + + /** + * Creates a new instance of AppleMenuDriver + */ + public AppleMenuDriver() { + super(new Timeout("apple.system.menu.delay", 100), + new String[]{"org.netbeans.jemmy.operators.JMenuBarOperator"}); + } + + @Override + public Object pushMenu(ComponentOperator oper, PathChooser chooser) { + Timeout maxTime = oper.getTimeouts().create("ComponentOperator.WaitComponentTimeout"); + JMenuBar bar = (JMenuBar) (oper.getSource()); + activateMenu(bar); + MenuElement menuObject; + maxTime.start(); + while (!chooser.checkPathComponent(0, (menuObject = getSelectedElement(bar)))) { + pressKey(KeyEvent.VK_RIGHT, 0); + releaseKey(KeyEvent.VK_RIGHT, 0); + if (maxTime.expired()) { + throw (new TimeoutExpiredException("AppleMenuDriver: can not find an appropriate menu!")); + } + } + for (int depth = 1; depth < chooser.getDepth(); depth++) { + // TODO - wait for menu item + int elementIndex = getDesiredElementIndex(menuObject, chooser, depth); + if (elementIndex == -1) { + throw (new JemmyException("Unable to find menu (menuitem): " + ((DescriptablePathChooser) chooser).getDescription())); + } + for (int i = ((depth == 1) ? 0 : 1); i <= elementIndex; i++) { + pressKey(KeyEvent.VK_DOWN, 0); + releaseKey(KeyEvent.VK_DOWN, 0); + } + if (depth == chooser.getDepth() - 1) { + pressKey(KeyEvent.VK_ENTER, 0); + releaseKey(KeyEvent.VK_ENTER, 0); + return null; + } else { + pressKey(KeyEvent.VK_RIGHT, 0); + releaseKey(KeyEvent.VK_RIGHT, 0); + menuObject = menuObject.getSubElements()[0].getSubElements()[elementIndex]; + } + } + return menuObject; + } + + private void activateMenu(JMenuBar bar) { + if (getSelectedElement(bar) == null) { + tryToActivate(); + if (getSelectedElement(bar) == null) { + tryToActivate(); + } + } + } + + private void tryToActivate() { + moveMouse(0, 0); + pressMouse(Operator.getDefaultMouseButton(), 0); + releaseMouse(Operator.getDefaultMouseButton(), 0); + pressKey(KeyEvent.VK_RIGHT, 0); + releaseKey(KeyEvent.VK_RIGHT, 0); + pressKey(KeyEvent.VK_RIGHT, 0); + releaseKey(KeyEvent.VK_RIGHT, 0); + } + + private static MenuElement getSelectedElement(MenuElement bar) { + MenuElement[] subElements = bar.getSubElements(); + for (MenuElement subElement : subElements) { + if (subElement instanceof JMenu + && ((JMenu) subElement).isSelected()) { + return subElement; + } else if (subElement instanceof JMenuItem + && ((JMenuItem) subElement).isSelected()) { + return subElement; + } + } + return null; + } + + private static int getDesiredElementIndex(MenuElement bar, PathChooser chooser, int depth) { + MenuElement[] subElements = bar.getSubElements()[0].getSubElements(); + int realIndex = 0; + for (MenuElement subElement : subElements) { + // do not count invisible and disabled menu items + if (subElement instanceof JMenuItem && ((JMenuItem) subElement).isVisible() && ((JMenuItem) subElement).isEnabled()) { + if (chooser.checkPathComponent(depth, subElement)) { + return realIndex; + } + realIndex++; + } + } + return -1; + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/menus/DefaultJMenuDriver.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/menus/DefaultJMenuDriver.java new file mode 100644 index 00000000000..e4ef079ef60 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/menus/DefaultJMenuDriver.java @@ -0,0 +1,265 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers.menus; + +import java.awt.Component; + +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.JPopupMenu; +import javax.swing.MenuElement; + +import org.netbeans.jemmy.ComponentChooser; +import org.netbeans.jemmy.JemmyException; +import org.netbeans.jemmy.Waitable; +import org.netbeans.jemmy.Waiter; +import org.netbeans.jemmy.drivers.DriverManager; +import org.netbeans.jemmy.drivers.LightSupportiveDriver; +import org.netbeans.jemmy.drivers.MenuDriver; +import org.netbeans.jemmy.drivers.MouseDriver; +import org.netbeans.jemmy.drivers.PathChooser; +import org.netbeans.jemmy.operators.ComponentOperator; +import org.netbeans.jemmy.operators.JMenuBarOperator; +import org.netbeans.jemmy.operators.JMenuItemOperator; +import org.netbeans.jemmy.operators.JMenuOperator; +import org.netbeans.jemmy.operators.JPopupMenuOperator; + +public class DefaultJMenuDriver extends LightSupportiveDriver implements MenuDriver { + + public DefaultJMenuDriver() { + super(new String[]{"org.netbeans.jemmy.operators.JMenuOperator", + "org.netbeans.jemmy.operators.JMenuBarOperator", + "org.netbeans.jemmy.operators.JPopupMenuOperator"}); + } + + @Override + public Object pushMenu(ComponentOperator oper, PathChooser chooser) { + checkSupported(oper); + if (oper instanceof JMenuBarOperator + || oper instanceof JPopupMenuOperator) { + JMenuItem item; + if (oper instanceof JMenuBarOperator) { + item = waitItem(oper, + (JMenuBar) oper.getSource(), + chooser, 0); + } else { + item = waitItem(oper, + (JPopupMenu) oper.getSource(), + chooser, 0); + } + JMenuItemOperator itemOper; + if (item instanceof JMenu) { + itemOper = new JMenuOperator((JMenu) item); + } else { + itemOper = new JMenuItemOperator(item); + } + itemOper.copyEnvironment(oper); + return (push(itemOper, null, (oper instanceof JMenuBarOperator) ? ((JMenuBar) oper.getSource()) : null, + chooser, 1, true)); + } else { + return push(oper, null, null, chooser, 0, true); + } + } + + protected Object push(ComponentOperator oper, ComponentOperator lastItem, + JMenuBar menuBar, + PathChooser chooser, int depth, boolean pressMouse) { + try { + oper.waitComponentVisible(true); + oper.waitComponentEnabled(); + } catch (InterruptedException e) { + throw (new JemmyException("Interrupted!", e)); + } + MouseDriver mDriver = DriverManager.getMouseDriver(oper); + //mDriver.enterMouse(oper); + //use enhanced algorithm instead + smartMove(lastItem, oper); + if (depth > chooser.getDepth() - 1) { + if (oper instanceof JMenuOperator + && menuBar != null && getSelectedElement(menuBar) != null) { + //mDriver.enterMouse(oper); + } else { + DriverManager.getButtonDriver(oper).push(oper); + } + return oper.getSource(); + } + if (pressMouse && !((JMenuOperator) oper).isPopupMenuVisible() + && !(menuBar != null && getSelectedElement(menuBar) != null)) { + DriverManager.getButtonDriver(oper).push(oper); + } + oper.getTimeouts().sleep("JMenuOperator.WaitBeforePopupTimeout"); + JMenuItem item = waitItem(oper, waitPopupMenu(oper), chooser, depth); + mDriver.exitMouse(oper); + if (item instanceof JMenu) { + JMenuOperator mo = new JMenuOperator((JMenu) item); + mo.copyEnvironment(oper); + return push(mo, oper, null, chooser, depth + 1, false); + } else { + JMenuItemOperator mio = new JMenuItemOperator(item); + mio.copyEnvironment(oper); + try { + mio.waitComponentEnabled(); + } catch (InterruptedException e) { + throw (new JemmyException("Interrupted!", e)); + } + //move here first + smartMove(oper, mio); + DriverManager.getButtonDriver(oper).push(mio); + return item; + } + } + + private void smartMove(ComponentOperator last, ComponentOperator oper) { + if (last == null) { + oper.enterMouse(); + return; + } + //get all the coordinates first + //previous item + long lastXl, lastXr, lastYl, lastYr; + lastXl = (long) last.getSource().getLocationOnScreen().getX(); + lastXr = lastXl + last.getSource().getWidth(); + lastYl = (long) last.getSource().getLocationOnScreen().getY(); + lastYr = lastYl + last.getSource().getHeight(); + //this item + long operXl, operXr, operYl, operYr; + operXl = (long) oper.getSource().getLocationOnScreen().getX(); + operXr = operXl + oper.getSource().getWidth(); + operYl = (long) oper.getSource().getLocationOnScreen().getY(); + operYr = operYl + oper.getSource().getHeight(); + //get the overlap borders + long overXl, overXr, overYl, overYr; + overXl = (lastXl > operXl) ? lastXl : operXl; + overXr = (lastXr < operXr) ? lastXr : operXr; + overYl = (lastYl > operYl) ? lastYl : operYl; + overYr = (lastYr < operYr) ? lastYr : operYr; + //now, let's see ... + //what if it overlaps by x? + if (overXl < overXr) { + //good - move mose to the center of the overlap + last.moveMouse((int) ((overXr - overXl) / 2 - lastXl), + last.getCenterY()); + //move mouse inside + oper.moveMouse((int) ((overXr - overXl) / 2 - operXl), + oper.getCenterY()); + //done - now move to the center + oper.enterMouse(); + return; + } + //ok, what if it overlaps by y? + if (overYl < overYr) { + //good - move mose to the center of the overlap + last.moveMouse(last.getCenterX(), + (int) ((overYr - overYl) / 2 - lastYl)); + //move mouse inside + oper.moveMouse(last.getCenterX(), + (int) ((overYr - overYl) / 2 - operYl)); + //done - now move to the center + oper.enterMouse(); + return; + } + //well - can't help it + oper.enterMouse(); + } + + protected JPopupMenu waitPopupMenu(final ComponentOperator oper) { + return ((JPopupMenu) JPopupMenuOperator.waitJPopupMenu(new ComponentChooser() { + @Override + public boolean checkComponent(Component comp) { + return (comp == ((JMenuOperator) oper).getPopupMenu() + && comp.isShowing()); + } + + @Override + public String getDescription() { + return ((JMenuOperator) oper).getText() + "'s popup"; + } + + @Override + public String toString() { + return "waitPopupMenu.ComponentChooser{description = " + getDescription() + '}'; + } + }).getSource()); + } + + protected JMenuItem waitItem(ComponentOperator oper, MenuElement element, PathChooser chooser, int depth) { + Waiter waiter = new Waiter<>(new JMenuItemWaiter(element, chooser, depth)); + waiter.setOutput(oper.getOutput().createErrorOutput()); + waiter.setTimeouts(oper.getTimeouts()); + try { + return (JMenuItem) waiter.waitAction(null); + } catch (InterruptedException e) { + throw (new JemmyException("Waiting has been interrupted", e)); + } + } + + public static Object getSelectedElement(JMenuBar bar) { + MenuElement[] subElements = bar.getSubElements(); + for (MenuElement subElement : subElements) { + if (subElement instanceof JMenu + && ((JMenu) subElement).isPopupMenuVisible()) { + return subElement; + } + } + return null; + } + + private static class JMenuItemWaiter implements Waitable { + + MenuElement cont; + PathChooser chooser; + int depth; + + public JMenuItemWaiter(MenuElement cont, PathChooser chooser, int depth) { + this.cont = cont; + this.chooser = chooser; + this.depth = depth; + } + + @Override + public MenuElement actionProduced(Void obj) { + if (!((Component) cont).isShowing()) { + return null; + } + MenuElement[] subElements = cont.getSubElements(); + for (MenuElement subElement : subElements) { + if (((Component) subElement).isShowing() && ((Component) subElement).isEnabled() + && chooser.checkPathComponent(depth, subElement)) { + return subElement; + } + } + return null; + } + + @Override + public String getDescription() { + return ""; + } + + @Override + public String toString() { + return "JMenuItemWaiter{" + "cont=" + cont + ", chooser=" + chooser + ", depth=" + depth + '}'; + } + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/menus/QueueJMenuDriver.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/menus/QueueJMenuDriver.java new file mode 100644 index 00000000000..d327fb6e67b --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/menus/QueueJMenuDriver.java @@ -0,0 +1,325 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers.menus; + +import java.awt.Component; +import java.awt.Window; + +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.MenuElement; + +import org.netbeans.jemmy.ComponentChooser; +import org.netbeans.jemmy.JemmyException; +import org.netbeans.jemmy.QueueTool; +import org.netbeans.jemmy.Waitable; +import org.netbeans.jemmy.Waiter; +import org.netbeans.jemmy.drivers.DescriptablePathChooser; +import org.netbeans.jemmy.drivers.DriverManager; +import org.netbeans.jemmy.drivers.LightSupportiveDriver; +import org.netbeans.jemmy.drivers.MenuDriver; +import org.netbeans.jemmy.drivers.PathChooser; +import org.netbeans.jemmy.operators.ComponentOperator; +import org.netbeans.jemmy.operators.JMenuBarOperator; +import org.netbeans.jemmy.operators.JMenuItemOperator; +import org.netbeans.jemmy.operators.JMenuOperator; +import org.netbeans.jemmy.operators.JPopupMenuOperator; + +/** + * + * 100% stable menu driver. Tries to do next steps during one action executed + * through EventQueue:
+ * find showing window containing popup
+ * find showing popup
+ * find showing menuitem
+ * enter mouse into it
+ * + * Repeats this action as many times as "JMenuOperator.WaitPopupTimeout" timeout + * allows. + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + * + */ +public class QueueJMenuDriver extends LightSupportiveDriver implements MenuDriver { + + QueueTool queueTool; + + public QueueJMenuDriver() { + super(new String[]{"org.netbeans.jemmy.operators.JMenuOperator", + "org.netbeans.jemmy.operators.JMenuBarOperator", + "org.netbeans.jemmy.operators.JPopupMenuOperator"}); + queueTool = new QueueTool(); + } + + @Override + public Object pushMenu(final ComponentOperator oper, PathChooser chooser) { + queueTool.setOutput(oper.getOutput().createErrorOutput()); + checkSupported(oper); + JMenuItem result; + OneReleaseAction action; + if (oper instanceof JMenuBarOperator) { + action = new OneReleaseAction(chooser, 0, oper, false) { + @Override + protected void pushAlone(JMenuItemOperator subMenuOper) { + if (subMenuOper.getSource() instanceof JMenu + && isMenuBarSelected((JMenuBar) oper.getSource())) { + DriverManager.getMouseDriver(subMenuOper).enterMouse(subMenuOper); + } else { + DriverManager.getButtonDriver(subMenuOper).push(subMenuOper); + } + } + + @Override + protected boolean inTheMiddle(JMenuOperator subMenuOper, boolean mousePressed) { + if (isMenuBarSelected((JMenuBar) oper.getSource())) { + DriverManager.getMouseDriver(subMenuOper).enterMouse(subMenuOper); + return false; + } else { + return super.inTheMiddle(subMenuOper, mousePressed); + } + } + + @Override + protected void process(MenuElement element) { + super.process(element); + } + + @Override + public MenuElement getMenuElement() { + return (MenuElement) oper.getSource(); + } + }; + } else if (oper instanceof JPopupMenuOperator) { + action = new OneReleaseAction(chooser, 0, oper, false) { + @Override + public MenuElement getMenuElement() { + return (MenuElement) oper.getSource(); + } + }; + } else { + DriverManager.getButtonDriver(oper).press(oper); + action = new OneReleaseAction(chooser, 0, oper, false) { + @Override + public MenuElement launch() { + process((MenuElement) oper.getSource()); + return (MenuElement) oper.getSource(); + } + + @Override + public MenuElement getMenuElement() { + return null; + } + }; + } + queueTool.waitEmpty(10); + queueTool.waitEmpty(10); + queueTool.waitEmpty(10); + result = runAction(action, oper, + oper.getTimeouts().getTimeout("ComponentOperator.WaitComponentTimeout"), + (chooser instanceof DescriptablePathChooser) + ? ((DescriptablePathChooser) chooser).getDescription() + : "Menu pushing"); + if (result instanceof JMenu) { + for (int i = 1; i < chooser.getDepth(); i++) { + final JMenu menu = (JMenu) result; + final ComponentChooser popupChooser = new PopupMenuChooser(menu); + action = new OneReleaseAction(chooser, i, oper, action.mousePressed) { + @Override + public MenuElement getMenuElement() { + Window win = JPopupMenuOperator.findJPopupWindow(popupChooser); + if (win != null && win.isShowing()) { + return JPopupMenuOperator.findJPopupMenu(win, popupChooser); + } else { + return null; + } + } + }; + result = runAction(action, oper, + oper.getTimeouts().getTimeout("JMenuOperator.WaitPopupTimeout"), + (chooser instanceof DescriptablePathChooser) + ? ((DescriptablePathChooser) chooser).getDescription() + : "Menu pushing"); + } + } + return result; + } + + private JMenuItem runAction(final OneReleaseAction action, ComponentOperator env, long waitingTime, final String description) { + Waiter waiter = new Waiter<>(new Waitable() { + @Override + public MenuElement actionProduced(Void param) { + return queueTool.invokeSmoothly(action); + } + + @Override + public String getDescription() { + return description; + } + + @Override + public String toString() { + return "runAction.Waiter{description = " + getDescription() + '}'; + } + }); + waiter.setOutput(env.getOutput().createErrorOutput()); + waiter.setTimeouts(env.getTimeouts().cloneThis()); + waiter.getTimeouts().setTimeout("Waiter.WaitingTime", + waitingTime); + waiter.getTimeouts().setTimeout("Waiter.TimeDelta", 100); + //1.5 workaround + if (System.getProperty("java.specification.version").compareTo("1.4") > 0) { + queueTool.setOutput(env.getOutput().createErrorOutput()); + queueTool.waitEmpty(10); + queueTool.waitEmpty(10); + queueTool.waitEmpty(10); + } + //end of 1.5 workaround + try { + return (JMenuItem) waiter.waitAction(null); + } catch (InterruptedException e) { + action.stop(); + throw (new JemmyException("Waiting has been interrupted", e)); + } + } + + private boolean isMenuBarSelected(JMenuBar bar) { + MenuElement[] subElements = bar.getSubElements(); + for (MenuElement subElement : subElements) { + if (subElement instanceof JMenu + && ((JMenu) subElement).isPopupMenuVisible()) { + return true; + } + } + return false; + } + + private abstract class OneReleaseAction extends QueueTool.QueueAction { + + PathChooser chooser; + int depth; + ComponentOperator env; + boolean mousePressed = false; + private boolean stopped = false; + + public OneReleaseAction(PathChooser chooser, int depth, ComponentOperator env, boolean mousePressed) { + super("Menu pushing"); + this.chooser = chooser; + this.depth = depth; + this.env = env; + this.mousePressed = mousePressed; + } + + protected void pushAlone(JMenuItemOperator subMenuOper) { + DriverManager.getButtonDriver(subMenuOper).push(subMenuOper); + } + + protected void pushLast(JMenuItemOperator subMenuOper, boolean mousePressed) { + DriverManager.getMouseDriver(subMenuOper).enterMouse(subMenuOper); + DriverManager.getButtonDriver(subMenuOper).release(subMenuOper); + } + + protected boolean inTheMiddle(JMenuOperator subMenuOper, boolean mousePressed) { + if (!subMenuOper.isPopupMenuVisible()) { + if (!mousePressed) { + DriverManager.getMouseDriver(subMenuOper).enterMouse(subMenuOper); + DriverManager.getButtonDriver(subMenuOper).press(subMenuOper); + } else { + DriverManager.getMouseDriver(subMenuOper).enterMouse(subMenuOper); + } + return true; + } + return mousePressed; + } + + protected void process(MenuElement element) { + if (depth == chooser.getDepth() - 1) { + JMenuItemOperator subMenuOper = new JMenuItemOperator((JMenuItem) element); + subMenuOper.copyEnvironment(env); + if (depth == 0) { + pushAlone(subMenuOper); + } else { + pushLast(subMenuOper, mousePressed); + } + } else if (element instanceof JMenu) { + JMenuOperator subMenuOper = new JMenuOperator((JMenu) element); + subMenuOper.copyEnvironment(env); + mousePressed = inTheMiddle(subMenuOper, mousePressed); + } else { + throw (new JemmyException("Menu path too long")); + } + } + + @Override + public MenuElement launch() { + MenuElement element = getMenuElement(); + if (element != null) { + MenuElement[] subElements = element.getSubElements(); + for (MenuElement subElement : subElements) { + if (((Component) subElement).isShowing() + && ((Component) subElement).isEnabled() + && chooser.checkPathComponent(depth, subElement)) { + process(subElement); + return subElement; + } + if (stopped) { + return null; + } + } + } + return null; + } + + public abstract MenuElement getMenuElement(); + + private void stop() { + stopped = true; + } + } + + private static class PopupMenuChooser implements ComponentChooser { + + JMenu menu; + + public PopupMenuChooser(JMenu menu) { + this.menu = menu; + } + + @Override + public boolean checkComponent(Component comp) { + return (comp == menu.getPopupMenu() + && comp.isShowing() && comp.isEnabled()); + } + + @Override + public String getDescription() { + return menu.getText() + "'s popup"; + } + + @Override + public String toString() { + return "PopupMenuChooser{" + "menu=" + menu + '}'; + } + } + +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/menus/package-info.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/menus/package-info.java new file mode 100644 index 00000000000..071b87542c9 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/menus/package-info.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +/** + *

Menu drivers.

+ * Different drivers to perform a menu operations.
+ *
+ */ +package org.netbeans.jemmy.drivers.menus; diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/package-info.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/package-info.java new file mode 100644 index 00000000000..ab944add2b1 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/package-info.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +/** + *

Drivers interfaces

+ * Contains interfaces of "drivers".

+ * Driver is a class which actually implements action reproducing. There are + * different types of drivers (mouse driver, keyboard driver, button drivers, + * ...), each of them represented by interface (button driver - by ButtonDriver + * interface, ...)

+ * Package also contains some classes allowing to manage driver set.

+ * Subpackages contain driver implementations.

+ * Drivers is low-level API: they are not supposed to be used directly from + * test.
+ * + * @since 04/17/2002 + *
+ */ +package org.netbeans.jemmy.drivers; diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/scrolling/AWTScrollDriver.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/scrolling/AWTScrollDriver.java new file mode 100644 index 00000000000..3f8f254f8dd --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/scrolling/AWTScrollDriver.java @@ -0,0 +1,165 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers.scrolling; + +import java.awt.Point; + +import org.netbeans.jemmy.QueueTool; +import org.netbeans.jemmy.Timeout; +import org.netbeans.jemmy.drivers.DriverManager; +import org.netbeans.jemmy.drivers.MouseDriver; +import org.netbeans.jemmy.operators.ComponentOperator; +import org.netbeans.jemmy.operators.Operator; + +/** + * ScrollDriver for awt components. + * + * @author Alexandre Iline(alexandre.iline@oracle.com) + */ +public abstract class AWTScrollDriver extends AbstractScrollDriver { + + private QueueTool queueTool; + + /** + * Constructs a ChoiceDriver. + * + * @param supported an array of supported class names + */ + public AWTScrollDriver(String[] supported) { + super(supported); + queueTool = new QueueTool(); + } + + @Override + protected void step(final ComponentOperator oper, final ScrollAdjuster adj) { + if (adj.getScrollDirection() != ScrollAdjuster.DO_NOT_TOUCH_SCROLL_DIRECTION) { + queueTool.invokeSmoothly(new QueueTool.QueueAction("Scrolling by clicking with mouse") { + @Override + public Void launch() { + Point clickPoint = getClickPoint(oper, adj.getScrollDirection(), adj.getScrollOrientation()); + if (clickPoint != null) { + DriverManager.getMouseDriver(oper). + clickMouse(oper, clickPoint.x, clickPoint.y, 1, + Operator.getDefaultMouseButton(), + 0, + oper.getTimeouts(). + create("ComponentOperator.MouseClickTimeout")); + } + return null; + } + }); + } + } + + @Override + protected void jump(ComponentOperator oper, ScrollAdjuster adj) { + } + + @Override + protected void startPushAndWait(final ComponentOperator oper, final int direction, final int orientation) { + queueTool.invokeSmoothly(new QueueTool.QueueAction("Scrolling: pressing the mouse button and holding") { + @Override + public Void launch() { + Point clickPoint = getClickPoint(oper, direction, orientation); + if (clickPoint != null) { + MouseDriver mdriver = DriverManager.getMouseDriver(oper); + mdriver.moveMouse(oper, clickPoint.x, clickPoint.y); + mdriver.pressMouse(oper, clickPoint.x, clickPoint.y, + Operator.getDefaultMouseButton(), + 0); + } + return null; + } + }); + } + + @Override + protected void stopPushAndWait(final ComponentOperator oper, final int direction, final int orientation) { + queueTool.invokeSmoothly(new QueueTool.QueueAction("Scrolling: releasing the mouse button") { + @Override + public Void launch() { + Point clickPoint = getClickPoint(oper, direction, orientation); + if (clickPoint != null) { + MouseDriver mdriver = DriverManager.getMouseDriver(oper); + mdriver.releaseMouse(oper, clickPoint.x, clickPoint.y, + Operator.getDefaultMouseButton(), + 0); + } + return null; + } + }); + } + + @Override + protected Point startDragging(ComponentOperator oper) { + return null; + } + + @Override + protected void drop(ComponentOperator oper, Point pnt) { + } + + @Override + protected void drag(ComponentOperator oper, Point pnt) { + } + + @Override + protected Timeout getScrollDeltaTimeout(ComponentOperator oper) { + return (oper.getTimeouts(). + create("ScrollbarOperator.DragAndDropScrollingDelta")); + } + + @Override + protected boolean canDragAndDrop(ComponentOperator oper) { + return false; + } + + @Override + protected boolean canJump(ComponentOperator oper) { + return false; + } + + @Override + protected boolean canPushAndWait(ComponentOperator oper) { + return true; + } + + @Override + protected int getDragAndDropStepLength(ComponentOperator oper) { + return 1; + } + + /** + * Defines a click point which needs to be used in order to + * increase/decrease scroller value. + * + * @param oper an operator. + * @param direction - one of the ScrollAdjister.INCREASE_SCROLL_DIRECTION, + * ScrollAdjister.DECREASE_SCROLL_DIRECTION, + * ScrollAdjister.DO_NOT_TOUCH_SCROLL_DIRECTION values. + * @param orientation one of the Adjustable.HORIZONTAL or + * Adjustable.VERTICAL values. + * @return a point to click. + */ + protected abstract Point getClickPoint(ComponentOperator oper, int direction, int orientation); +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/scrolling/AbstractScrollDriver.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/scrolling/AbstractScrollDriver.java new file mode 100644 index 00000000000..e029fadf283 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/scrolling/AbstractScrollDriver.java @@ -0,0 +1,277 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers.scrolling; + +import java.awt.Adjustable; +import java.awt.Point; +import org.netbeans.jemmy.JemmyException; +import org.netbeans.jemmy.JemmyProperties; + +import org.netbeans.jemmy.Timeout; +import org.netbeans.jemmy.drivers.LightSupportiveDriver; +import org.netbeans.jemmy.drivers.ScrollDriver; +import org.netbeans.jemmy.operators.ComponentOperator; + +/** + * Superclass for all scroll drivers. Contains all the logic of scrolling. Tries + * allowed operations in this order: "jump", "drag'n'drop", "push'n'wait", + * "step". Repeats "step" scrolling while scroller value is not equal to the + * necessary value, but no more than {@code ADJUST_CLICK_COUNT}. + * + * @author Alexandre Iline(alexandre.iline@oracle.com) + */ +public abstract class AbstractScrollDriver extends LightSupportiveDriver implements ScrollDriver { + + /** + * Maximal number of attempts to reach required position by minimal + * scrolling operation. + */ + public static final int ADJUST_CLICK_COUNT = 10; + public static final String SCROLL_FREEZE_TIMEOUT = AbstractScrollDriver.class.getName() + ".freeze.timeout"; + + static { + JemmyProperties.getProperties().initTimeout(SCROLL_FREEZE_TIMEOUT, 1000); + } + + /** + * Constructs an AbstractScrollDriver. + * + * @param supported an array of supported class names + */ + public AbstractScrollDriver(String[] supported) { + super(supported); + } + + @Override + public void scroll(ComponentOperator oper, ScrollAdjuster adj) { + if (canJump(oper)) { + doJumps(oper, adj); + } + if (canDragAndDrop(oper)) { + doDragAndDrop(oper, adj); + } + if (canPushAndWait(oper)) { + if (!doPushAndWait(oper, adj, oper.getTimeouts().getTimeout(SCROLL_FREEZE_TIMEOUT))) { + throw new JemmyException("Scrolling stuck for more than " + + oper.getTimeouts().getTimeout(SCROLL_FREEZE_TIMEOUT) + + " on " + oper); + } + } + for (int i = 0; i < ADJUST_CLICK_COUNT; i++) { + doSteps(oper, adj); + } + } + + /** + * Performs minimal scrolling step. + * + * @param oper an operator. + * @param adj a scroll adjuster + */ + protected abstract void step(ComponentOperator oper, ScrollAdjuster adj); + + /** + * Performs maximal scroll step. + * + * @param oper an operator. + * @param adj a scroll adjuster + */ + protected abstract void jump(ComponentOperator oper, ScrollAdjuster adj); + + /** + * Presses something like a scroll button. + * + * @param oper an operator. + * @param direction - one of the ScrollAdjister.INCREASE_SCROLL_DIRECTION, + * ScrollAdjister.DECREASE_SCROLL_DIRECTION, + * ScrollAdjister.DO_NOT_TOUCH_SCROLL_DIRECTION values. + * @param orientation one of the Adjustable.HORIZONTAL or + * Adjustable.VERTICAL values. + */ + protected abstract void startPushAndWait(ComponentOperator oper, int direction, int orientation); + + /** + * Releases something like a scroll button. + * + * @param oper an operator. + * @param direction - one of the ScrollAdjister.INCREASE_SCROLL_DIRECTION, + * ScrollAdjister.DECREASE_SCROLL_DIRECTION, + * ScrollAdjister.DO_NOT_TOUCH_SCROLL_DIRECTION values. + * @param orientation one of the Adjustable.HORIZONTAL or + * Adjustable.VERTICAL values. + */ + protected abstract void stopPushAndWait(ComponentOperator oper, int direction, int orientation); + + /** + * Starts drag'n'drop scrolling. + * + * @param oper an operator. + * @return start drugging point. + */ + protected abstract Point startDragging(ComponentOperator oper); + + /** + * Drop at a specified point. + * + * @param oper an operator. + * @param pnt the point to drop. + */ + protected abstract void drop(ComponentOperator oper, Point pnt); + + /** + * Drag to a specified point. + * + * @param oper an operator. + * @param pnt the point to drag to. + */ + protected abstract void drag(ComponentOperator oper, Point pnt); + + /** + * Returns a timeout for sleeping between verifications during "push and + * wait" scrolling. + * + * @param oper an operator. + * @return a timeout + */ + protected abstract Timeout getScrollDeltaTimeout(ComponentOperator oper); + + /** + * Tells if this driver allows to perform drag'n'drop scrolling. + * + * @param oper an operator. + * @return true if this driver allows to drag'n'drop. + */ + protected abstract boolean canDragAndDrop(ComponentOperator oper); + + /** + * Tells if this driver allows to perform jumps. + * + * @param oper an operator. + * @return true if this driver allows to jump. + */ + protected abstract boolean canJump(ComponentOperator oper); + + /** + * Tells if this driver allows to perform "push and wait" scrolling. + * + * @param oper an operator. + * @return true if this driver allows to "push and wait". + */ + protected abstract boolean canPushAndWait(ComponentOperator oper); + + /** + * Returns a number of pixels in one drag and drop scrolling. + * + * @param oper an operator. + * @return drag'n'drop step length. + */ + protected abstract int getDragAndDropStepLength(ComponentOperator oper); + + /** + * Performs drag'n'drop scrolling till scroller's value does not cross + * required value. + * + * @param oper an operator. + * @param adj a scroll adjuster + */ + protected void doDragAndDrop(ComponentOperator oper, ScrollAdjuster adj) { + int direction = adj.getScrollDirection(); + if (direction != ScrollAdjuster.DO_NOT_TOUCH_SCROLL_DIRECTION) { + Point pnt = startDragging(oper); + while (adj.getScrollDirection() == direction) { + drag(oper, pnt = increasePoint(oper, pnt, adj, direction)); + } + drop(oper, pnt); + } + } + + /** + * Performs jump scrolling till scroller's value does not cross required + * value. + * + * @param oper an operator. + * @param adj a scroll adjuster + */ + protected void doJumps(ComponentOperator oper, ScrollAdjuster adj) { + int direction = adj.getScrollDirection(); + if (direction != ScrollAdjuster.DO_NOT_TOUCH_SCROLL_DIRECTION) { + while (adj.getScrollDirection() == direction) { + jump(oper, adj); + } + } + } + + protected abstract int position(ComponentOperator oper, int orientation); + + /** + * Performs "push and wait" scrolling till scroller's value does not cross + * required value. + * + * @param oper an operator. + * @param adj a scroll adjuster + */ + protected boolean doPushAndWait(ComponentOperator oper, ScrollAdjuster adj, long freezeTimeout) { + int direction = adj.getScrollDirection(); + int orientation = adj.getScrollOrientation(); + int position = position(oper, orientation); + long lastChanded = System.currentTimeMillis(); + if (direction != ScrollAdjuster.DO_NOT_TOUCH_SCROLL_DIRECTION) { + Timeout delta = getScrollDeltaTimeout(oper); + startPushAndWait(oper, direction, orientation); + while (adj.getScrollDirection() == direction) { + delta.sleep(); + int curPosition = position(oper, orientation); + if (curPosition != position) { + position = curPosition; + lastChanded = System.currentTimeMillis(); + } else if ((System.currentTimeMillis() - lastChanded) > freezeTimeout) { + return false; + } + } + stopPushAndWait(oper, direction, orientation); + } + return true; + } + + /** + * Performs minimal scrollings till scroller's value does not cross required + * value. + * + * @param oper an operator. + * @param adj a scroll adjuster + */ + protected void doSteps(ComponentOperator oper, ScrollAdjuster adj) { + int direction = adj.getScrollDirection(); + if (direction != ScrollAdjuster.DO_NOT_TOUCH_SCROLL_DIRECTION) { + while (adj.getScrollDirection() == direction) { + step(oper, adj); + } + } + } + + private Point increasePoint(ComponentOperator oper, Point pnt, ScrollAdjuster adj, int direction) { + return ((adj.getScrollOrientation() == Adjustable.HORIZONTAL) + ? new Point(pnt.x + ((direction == 1) ? 1 : -1) * getDragAndDropStepLength(oper), pnt.y) + : new Point(pnt.x, pnt.y + ((direction == 1) ? 1 : -1) * getDragAndDropStepLength(oper))); + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/scrolling/JScrollBarAPIDriver.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/scrolling/JScrollBarAPIDriver.java new file mode 100644 index 00000000000..c50f6f5832a --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/scrolling/JScrollBarAPIDriver.java @@ -0,0 +1,158 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers.scrolling; + +import java.awt.Point; + +import org.netbeans.jemmy.Timeout; +import org.netbeans.jemmy.operators.ComponentOperator; +import org.netbeans.jemmy.operators.JScrollBarOperator; + +/** + * ScrollDriver for javax.swing.JScrollBar component type. + * + * @author Alexandre Iline(alexandre.iline@oracle.com) + */ +public class JScrollBarAPIDriver extends AbstractScrollDriver { + + private final static int SMALL_INCREMENT = 1; + + /** + * Constructs a JScrollBarDriver. + */ + public JScrollBarAPIDriver() { + super(new String[]{"org.netbeans.jemmy.operators.JScrollBarOperator"}); + } + + @Override + protected int position(ComponentOperator oper, int orientation) { + return ((JScrollBarOperator) oper).getValue(); + } + + @Override + public void scrollToMinimum(ComponentOperator oper, int orientation) { + JScrollBarOperator scroll = (JScrollBarOperator) oper; + setValue(oper, scroll.getMinimum()); + } + + @Override + public void scrollToMaximum(ComponentOperator oper, int orientation) { + JScrollBarOperator scroll = (JScrollBarOperator) oper; + setValue(oper, scroll.getMaximum() - scroll.getVisibleAmount()); + } + + @Override + protected void step(ComponentOperator oper, ScrollAdjuster adj) { + JScrollBarOperator scroll = (JScrollBarOperator) oper; + int newValue = -1; + if (adj.getScrollDirection() == ScrollAdjuster.DECREASE_SCROLL_DIRECTION) { + newValue = (scroll.getValue() > scroll.getMinimum() + + scroll.getUnitIncrement()) + ? scroll.getValue() - scroll.getUnitIncrement() + : scroll.getMinimum(); + } else if (adj.getScrollDirection() == ScrollAdjuster.INCREASE_SCROLL_DIRECTION) { + newValue = (scroll.getValue() < scroll.getMaximum() + - scroll.getVisibleAmount() - scroll.getUnitIncrement()) + ? scroll.getValue() + scroll.getUnitIncrement() + : scroll.getMaximum(); + } + setValue(oper, newValue); + } + + private void setValue(ComponentOperator oper, int value) { + if (value != -1) { + ((JScrollBarOperator) oper).setValue(value); + } + } + + @Override + protected Timeout getScrollDeltaTimeout(ComponentOperator oper) { + return (oper.getTimeouts(). + create("JScrollBarOperator.DragAndDropScrollingDelta")); + } + + @Override + protected void jump(final ComponentOperator oper, final ScrollAdjuster adj) { + JScrollBarOperator scroll = (JScrollBarOperator) oper; + int newValue = -1; + if (adj.getScrollDirection() == ScrollAdjuster.DECREASE_SCROLL_DIRECTION) { + newValue = (scroll.getValue() > scroll.getMinimum() + + scroll.getBlockIncrement()) + ? scroll.getValue() - scroll.getBlockIncrement() + : scroll.getMinimum(); + } else if (adj.getScrollDirection() == ScrollAdjuster.INCREASE_SCROLL_DIRECTION) { + newValue = (scroll.getValue() < scroll.getMaximum() + - scroll.getVisibleAmount() - scroll.getBlockIncrement()) + ? scroll.getValue() + scroll.getBlockIncrement() + : scroll.getMaximum(); + } + setValue(oper, newValue); + } + + @Override + protected void startPushAndWait(ComponentOperator oper, int direction, int orientation) { + } + + @Override + protected void stopPushAndWait(ComponentOperator oper, int direction, int orientation) { + } + + @Override + protected Point startDragging(ComponentOperator oper) { + return null; + } + + @Override + protected void drop(ComponentOperator oper, Point pnt) { + } + + @Override + protected void drag(ComponentOperator oper, Point pnt) { + } + + @Override + protected boolean canDragAndDrop(ComponentOperator oper) { + return false; + } + + @Override + protected boolean canJump(ComponentOperator oper) { + return isSmallIncrement((JScrollBarOperator) oper); + } + + @Override + protected boolean canPushAndWait(ComponentOperator oper) { + return false; + } + + @Override + protected int getDragAndDropStepLength(ComponentOperator oper) { + return 1; + } + + private boolean isSmallIncrement(JScrollBarOperator oper) { + return (oper.getUnitIncrement(-1) <= SMALL_INCREMENT + && oper.getUnitIncrement(1) <= SMALL_INCREMENT); + } + +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/scrolling/JScrollBarDriver.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/scrolling/JScrollBarDriver.java new file mode 100644 index 00000000000..98e37595008 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/scrolling/JScrollBarDriver.java @@ -0,0 +1,252 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers.scrolling; + +import java.awt.Point; + +import javax.swing.JScrollBar; + +import org.netbeans.jemmy.QueueTool; +import org.netbeans.jemmy.Timeout; +import org.netbeans.jemmy.drivers.DriverManager; +import org.netbeans.jemmy.drivers.MouseDriver; +import org.netbeans.jemmy.operators.ComponentOperator; +import org.netbeans.jemmy.operators.JButtonOperator; +import org.netbeans.jemmy.operators.JScrollBarOperator; +import org.netbeans.jemmy.operators.Operator; + +/** + * ScrollDriver for javax.swing.JScrollBar component type. + * + * @author Alexandre Iline(alexandre.iline@oracle.com) + */ +public class JScrollBarDriver extends AbstractScrollDriver { + + private final static int SMALL_INCREMENT = 1; + private final static int MINIMAL_DRAGGER_SIZE = 5; + private final static int RELATIVE_DRAG_STEP_LENGTH = 20; + + private QueueTool queueTool; + + /** + * Constructs a JScrollBarDriver. + */ + public JScrollBarDriver() { + super(new String[]{"org.netbeans.jemmy.operators.JScrollBarOperator"}); + queueTool = new QueueTool(); + } + + @Override + protected int position(ComponentOperator oper, int orientation) { + return ((JScrollBarOperator) oper).getValue(); + } + + @Override + public void scrollToMinimum(ComponentOperator oper, int orientation) { + startDragging(oper); + Point pnt = new Point(0, 0); + drag(oper, pnt); + Timeout sleepTime = oper.getTimeouts().create("Waiter.TimeDelta"); + while (((JScrollBarOperator) oper).getValue() + > ((JScrollBarOperator) oper).getMinimum()) { + sleepTime.sleep(); + } + drop(oper, pnt); + } + + @Override + public void scrollToMaximum(ComponentOperator oper, int orientation) { + startDragging(oper); + Point pnt = new Point(oper.getWidth() - 1, oper.getHeight() - 1); + drag(oper, pnt); + Timeout sleepTime = oper.getTimeouts().create("Waiter.TimeDelta"); + while (((JScrollBarOperator) oper).getValue() + > (((JScrollBarOperator) oper).getMaximum() + - ((JScrollBarOperator) oper).getVisibleAmount())) { + sleepTime.sleep(); + } + drop(oper, pnt); + } + + @Override + protected void step(ComponentOperator oper, ScrollAdjuster adj) { + JButtonOperator boper = findAButton(oper, adj.getScrollDirection()); + DriverManager.getButtonDriver(boper).push(boper); + } + + @Override + protected void jump(final ComponentOperator oper, final ScrollAdjuster adj) { + final JButtonOperator lessButton = findAButton(oper, ScrollAdjuster.DECREASE_SCROLL_DIRECTION); + final JButtonOperator moreButton = findAButton(oper, ScrollAdjuster.INCREASE_SCROLL_DIRECTION); + queueTool.invokeSmoothly(new QueueTool.QueueAction("Scrolling by clicking with the mouse") { + @Override + public Void launch() { + if (adj.getScrollDirection() != ScrollAdjuster.DO_NOT_TOUCH_SCROLL_DIRECTION) { + int x, y; + switch (((JScrollBarOperator) oper).getOrientation()) { + case JScrollBar.HORIZONTAL: + switch (adj.getScrollDirection()) { + case ScrollAdjuster.INCREASE_SCROLL_DIRECTION: + x = moreButton.getX() - 1; + break; + case ScrollAdjuster.DECREASE_SCROLL_DIRECTION: + x = lessButton.getX() + lessButton.getWidth(); + break; + default: + return null; + } + y = lessButton.getHeight() / 2; + break; + case JScrollBar.VERTICAL: + switch (adj.getScrollDirection()) { + case ScrollAdjuster.INCREASE_SCROLL_DIRECTION: + y = moreButton.getY() - 1; + break; + case ScrollAdjuster.DECREASE_SCROLL_DIRECTION: + y = lessButton.getY() + lessButton.getHeight(); + break; + default: + return null; + } + x = lessButton.getWidth() / 2; + break; + default: + return null; + } + DriverManager.getMouseDriver(oper). + clickMouse(oper, x, y, 1, Operator.getDefaultMouseButton(), 0, new Timeout("", 0)); + } + return null; + } + }); + } + + @Override + protected void startPushAndWait(ComponentOperator oper, int direction, int orientation) { + JButtonOperator boper = findAButton(oper, direction); + DriverManager.getButtonDriver(boper).press(boper); + } + + @Override + protected void stopPushAndWait(ComponentOperator oper, int direction, int orientation) { + JButtonOperator boper = findAButton(oper, direction); + DriverManager.getButtonDriver(boper).release(boper); + } + + @Override + protected Point startDragging(ComponentOperator oper) { + JButtonOperator lessButton = findAButton(oper, ScrollAdjuster.DECREASE_SCROLL_DIRECTION); + JButtonOperator moreButton = findAButton(oper, ScrollAdjuster.INCREASE_SCROLL_DIRECTION); + Point pnt = getClickPoint((JScrollBarOperator) oper, lessButton, moreButton, ((JScrollBarOperator) oper).getValue()); + MouseDriver mdriver = DriverManager.getMouseDriver(oper); + mdriver.moveMouse(oper, pnt.x, pnt.y); + mdriver.pressMouse(oper, pnt.x, pnt.y, Operator.getDefaultMouseButton(), 0); + return pnt; + } + + @Override + protected void drop(ComponentOperator oper, Point pnt) { + DriverManager.getMouseDriver(oper). + releaseMouse(oper, pnt.x, pnt.y, Operator.getDefaultMouseButton(), 0); + } + + @Override + protected void drag(ComponentOperator oper, Point pnt) { + DriverManager.getMouseDriver(oper). + dragMouse(oper, pnt.x, pnt.y, Operator.getDefaultMouseButton(), 0); + } + + @Override + protected Timeout getScrollDeltaTimeout(ComponentOperator oper) { + return (oper.getTimeouts(). + create("ScrollbarOperator.DragAndDropScrollingDelta")); + } + + @Override + protected boolean canDragAndDrop(ComponentOperator oper) { + if (!isSmallIncrement((JScrollBarOperator) oper)) { + return false; + } + boolean result = false; + MouseDriver mdriver = DriverManager.getMouseDriver(oper); + JButtonOperator less = findAButton(oper, ScrollAdjuster.DECREASE_SCROLL_DIRECTION); + JButtonOperator more = findAButton(oper, ScrollAdjuster.INCREASE_SCROLL_DIRECTION); + Point pnt = getClickPoint((JScrollBarOperator) oper, less, more, ((JScrollBarOperator) oper).getValue()); + mdriver.moveMouse(oper, pnt.x, pnt.y); + mdriver.pressMouse(oper, pnt.x, pnt.y, Operator.getDefaultMouseButton(), 0); + result = ((JScrollBarOperator) oper).getValueIsAdjusting(); + mdriver.releaseMouse(oper, pnt.x, pnt.y, Operator.getDefaultMouseButton(), 0); + return result && isSmallIncrement((JScrollBarOperator) oper); + } + + @Override + protected boolean canJump(ComponentOperator oper) { + return isSmallIncrement((JScrollBarOperator) oper); + } + + @Override + protected boolean canPushAndWait(ComponentOperator oper) { + return isSmallIncrement((JScrollBarOperator) oper); + } + + @Override + protected int getDragAndDropStepLength(ComponentOperator oper) { + JButtonOperator less = findAButton(oper, ScrollAdjuster.DECREASE_SCROLL_DIRECTION); + JButtonOperator more = findAButton(oper, ScrollAdjuster.INCREASE_SCROLL_DIRECTION); + int width = oper.getWidth() - less.getWidth() - more.getWidth(); + int height = oper.getHeight() - less.getHeight() - more.getHeight(); + int max = (width > height) ? width : height; + if (max >= RELATIVE_DRAG_STEP_LENGTH * 2) { + return max / RELATIVE_DRAG_STEP_LENGTH; + } else { + return 1; + } + } + + private boolean isSmallIncrement(JScrollBarOperator oper) { + return (oper.getUnitIncrement(-1) <= SMALL_INCREMENT + && oper.getUnitIncrement(1) <= SMALL_INCREMENT); + } + + private Point getClickPoint(JScrollBarOperator oper, JButtonOperator lessButton, JButtonOperator moreButton, int value) { + int lenght = (oper.getOrientation() == JScrollBar.HORIZONTAL) + ? oper.getWidth() - lessButton.getWidth() - moreButton.getWidth() + : oper.getHeight() - lessButton.getHeight() - moreButton.getHeight(); + int subpos = (int) (((float) lenght / (oper.getMaximum() - oper.getMinimum())) * value); + if (oper.getOrientation() == JScrollBar.HORIZONTAL) { + subpos = subpos + lessButton.getWidth(); + } else { + subpos = subpos + lessButton.getHeight(); + } + subpos = subpos + MINIMAL_DRAGGER_SIZE / 2 + 1; + return ((oper.getOrientation() == JScrollBar.HORIZONTAL) + ? new Point(subpos, oper.getHeight() / 2) + : new Point(oper.getWidth() / 2, subpos)); + } + + private JButtonOperator findAButton(ComponentOperator oper, int direction) { + return ((direction == ScrollAdjuster.DECREASE_SCROLL_DIRECTION) + ? ((JScrollBarOperator) oper).getDecreaseButton() + : ((JScrollBarOperator) oper).getIncreaseButton()); + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/scrolling/JSliderAPIDriver.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/scrolling/JSliderAPIDriver.java new file mode 100644 index 00000000000..290c431f6f6 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/scrolling/JSliderAPIDriver.java @@ -0,0 +1,168 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers.scrolling; + +import java.awt.Point; + +import org.netbeans.jemmy.Timeout; +import org.netbeans.jemmy.operators.ComponentOperator; +import org.netbeans.jemmy.operators.JSliderOperator; + +/** + * A scroll driver serving JSlider component. + * + * @author Alexandre Iline(alexandre.iline@oracle.com) + */ +public class JSliderAPIDriver extends AbstractScrollDriver { + + private final static int SMALL_INCREMENT = 1; + + /** + * Constructs a JSliderDriver object. + */ + public JSliderAPIDriver() { + super(new String[]{"org.netbeans.jemmy.operators.JSliderOperator"}); + } + + @Override + protected int position(ComponentOperator oper, int orientation) { + return ((JSliderOperator) oper).getValue(); + } + + @Override + public void scrollToMinimum(final ComponentOperator oper, int orientation) { + ((JSliderOperator) oper).setValue(((JSliderOperator) oper).getMinimum()); + } + + @Override + public void scrollToMaximum(final ComponentOperator oper, int orientation) { + ((JSliderOperator) oper).setValue(((JSliderOperator) oper).getMaximum()); + } + + @Override + protected void step(ComponentOperator oper, ScrollAdjuster adj) { + JSliderOperator scroll = (JSliderOperator) oper; + int newValue = -1; + if (adj.getScrollDirection() == ScrollAdjuster.DECREASE_SCROLL_DIRECTION) { + newValue = (scroll.getValue() > scroll.getMinimum() + + getUnitIncrement(scroll)) + ? scroll.getValue() - getUnitIncrement(scroll) + : scroll.getMinimum(); + } else if (adj.getScrollDirection() == ScrollAdjuster.INCREASE_SCROLL_DIRECTION) { + newValue = (scroll.getValue() < scroll.getMaximum() + - getUnitIncrement(scroll)) + ? scroll.getValue() + getUnitIncrement(scroll) + : scroll.getMaximum(); + } + setValue(oper, newValue); + } + + private void setValue(ComponentOperator oper, int value) { + if (value != -1) { + ((JSliderOperator) oper).setValue(value); + } + } + + @Override + protected Timeout getScrollDeltaTimeout(ComponentOperator oper) { + return (oper.getTimeouts(). + create("JSliderOperator.ScrollingDelta")); + } + + @Override + protected void jump(final ComponentOperator oper, final ScrollAdjuster adj) { + JSliderOperator scroll = (JSliderOperator) oper; + int newValue = -1; + if (adj.getScrollDirection() == ScrollAdjuster.DECREASE_SCROLL_DIRECTION) { + newValue = (scroll.getValue() > scroll.getMinimum() + + getBlockIncrement(scroll)) + ? scroll.getValue() - getBlockIncrement(scroll) + : scroll.getMinimum(); + } else if (adj.getScrollDirection() == ScrollAdjuster.INCREASE_SCROLL_DIRECTION) { + newValue = (scroll.getValue() < scroll.getMaximum() + - getBlockIncrement(scroll)) + ? scroll.getValue() + getBlockIncrement(scroll) + : scroll.getMaximum(); + } + setValue(oper, newValue); + } + + @Override + protected void startPushAndWait(ComponentOperator oper, int direction, int orientation) { + } + + @Override + protected void stopPushAndWait(ComponentOperator oper, int direction, int orientation) { + } + + @Override + protected Point startDragging(ComponentOperator oper) { + return null; + } + + @Override + protected void drop(ComponentOperator oper, Point pnt) { + } + + @Override + protected void drag(ComponentOperator oper, Point pnt) { + } + + @Override + protected boolean canDragAndDrop(ComponentOperator oper) { + return false; + } + + @Override + protected boolean canJump(ComponentOperator oper) { + return isSmallIncrement((JSliderOperator) oper); + } + + @Override + protected boolean canPushAndWait(ComponentOperator oper) { + return false; + } + + @Override + protected int getDragAndDropStepLength(ComponentOperator oper) { + return 1; + } + + private int getUnitIncrement(JSliderOperator oper) { + return ((oper.getMinorTickSpacing() == 0) + ? 1 + : oper.getMinorTickSpacing()); + } + + private int getBlockIncrement(JSliderOperator oper) { + return ((oper.getMajorTickSpacing() == 0) + ? 1 + : oper.getMajorTickSpacing()); + } + + private boolean isSmallIncrement(JSliderOperator oper) { + return (oper.getMajorTickSpacing() <= SMALL_INCREMENT + && oper.getMajorTickSpacing() <= SMALL_INCREMENT); + } + +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/scrolling/JSliderDriver.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/scrolling/JSliderDriver.java new file mode 100644 index 00000000000..692f5fbbdd6 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/scrolling/JSliderDriver.java @@ -0,0 +1,259 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers.scrolling; + +import java.awt.Point; + +import javax.swing.JSlider; + +import org.netbeans.jemmy.QueueTool; +import org.netbeans.jemmy.Timeout; +import org.netbeans.jemmy.drivers.DriverManager; +import org.netbeans.jemmy.drivers.MouseDriver; +import org.netbeans.jemmy.operators.ComponentOperator; +import org.netbeans.jemmy.operators.JSliderOperator; +import org.netbeans.jemmy.operators.Operator; + +/** + * A scroll driver serving JSlider component. + * + * @author Alexandre Iline(alexandre.iline@oracle.com) + */ +public class JSliderDriver extends AbstractScrollDriver { + + private QueueTool queueTool; + + /** + * Constructs a JSliderDriver object. + */ + public JSliderDriver() { + super(new String[]{"org.netbeans.jemmy.operators.JSliderOperator"}); + queueTool = new QueueTool(); + } + + @Override + protected int position(ComponentOperator oper, int orientation) { + return ((JSliderOperator) oper).getValue(); + } + + @Override + public void scrollToMinimum(final ComponentOperator oper, int orientation) { + checkSupported(oper); + scroll(oper, + new ScrollAdjuster() { + @Override + public int getScrollDirection() { + return ((((JSliderOperator) oper).getMinimum() + < ((JSliderOperator) oper).getValue()) + ? DECREASE_SCROLL_DIRECTION + : DO_NOT_TOUCH_SCROLL_DIRECTION); + } + + @Override + public int getScrollOrientation() { + return ((JSliderOperator) oper).getOrientation(); + } + + @Override + public String getDescription() { + return "Scroll to minimum"; + } + + @Override + public String toString() { + return "scrollToMinimum.ScrollAdjuster{description = " + getDescription() + '}'; + } + }); + } + + @Override + public void scrollToMaximum(final ComponentOperator oper, int orientation) { + checkSupported(oper); + scroll(oper, + new ScrollAdjuster() { + @Override + public int getScrollDirection() { + return ((((JSliderOperator) oper).getMaximum() + > ((JSliderOperator) oper).getValue()) + ? INCREASE_SCROLL_DIRECTION + : DO_NOT_TOUCH_SCROLL_DIRECTION); + } + + @Override + public int getScrollOrientation() { + return ((JSliderOperator) oper).getOrientation(); + } + + @Override + public String getDescription() { + return "Scroll to maximum"; + } + + @Override + public String toString() { + return "scrollToMaximum.ScrollAdjuster{description = " + getDescription() + '}'; + } + }); + } + + @Override + protected void step(final ComponentOperator oper, final ScrollAdjuster adj) { + if (adj.getScrollDirection() != ScrollAdjuster.DO_NOT_TOUCH_SCROLL_DIRECTION) { + queueTool.invokeSmoothly(new QueueTool.QueueAction("Scrolling by clicking with the mouse") { + @Override + public Void launch() { + Point clickPoint = getClickPoint(oper, adj.getScrollDirection(), adj.getScrollOrientation()); + if (clickPoint != null) { + DriverManager.getMouseDriver(oper). + clickMouse(oper, clickPoint.x, clickPoint.y, 1, + Operator.getDefaultMouseButton(), + 0, + oper.getTimeouts(). + create("ComponentOperator.MouseClickTimeout")); + } + return null; + } + }); + } + } + + @Override + protected void jump(ComponentOperator oper, ScrollAdjuster adj) { + //cannot + } + + @Override + protected void startPushAndWait(final ComponentOperator oper, final int direction, final int orientation) { + queueTool.invokeSmoothly(new QueueTool.QueueAction("Start scrolling") { + @Override + public Void launch() { + Point clickPoint = getClickPoint(oper, direction, orientation); + if (clickPoint != null) { + MouseDriver mdriver = DriverManager.getMouseDriver(oper); + mdriver.moveMouse(oper, clickPoint.x, clickPoint.y); + mdriver.pressMouse(oper, clickPoint.x, clickPoint.y, + Operator.getDefaultMouseButton(), + 0); + } + return null; + } + }); + } + + @Override + protected void stopPushAndWait(final ComponentOperator oper, final int direction, final int orientation) { + queueTool.invokeSmoothly(new QueueTool.QueueAction("Stop scrolling") { + @Override + public Void launch() { + Point clickPoint = getClickPoint(oper, direction, orientation); + if (clickPoint != null) { + MouseDriver mdriver = DriverManager.getMouseDriver(oper); + mdriver.releaseMouse(oper, clickPoint.x, clickPoint.y, + Operator.getDefaultMouseButton(), + 0); + } + return null; + } + }); + } + + @Override + protected Point startDragging(ComponentOperator oper) { + //cannot + return null; + } + + @Override + protected void drop(ComponentOperator oper, Point pnt) { + //cannot + } + + @Override + protected void drag(ComponentOperator oper, Point pnt) { + //cannot + } + + @Override + protected Timeout getScrollDeltaTimeout(ComponentOperator oper) { + return oper.getTimeouts().create("JSliderOperator.ScrollingDelta"); + } + + @Override + protected boolean canDragAndDrop(ComponentOperator oper) { + return false; + } + + @Override + protected boolean canJump(ComponentOperator oper) { + return false; + } + + @Override + protected boolean canPushAndWait(ComponentOperator oper) { + return true; + } + + @Override + protected int getDragAndDropStepLength(ComponentOperator oper) { + return 0; + } + + private Point getClickPoint(ComponentOperator oper, int direction, int orientation) { + int x, y; + boolean inverted = ((JSliderOperator) oper).getInverted(); + int realDirection = ScrollAdjuster.DO_NOT_TOUCH_SCROLL_DIRECTION; + if (inverted) { + if (direction == ScrollAdjuster.INCREASE_SCROLL_DIRECTION) { + realDirection = ScrollAdjuster.DECREASE_SCROLL_DIRECTION; + } else if (direction == ScrollAdjuster.DECREASE_SCROLL_DIRECTION) { + realDirection = ScrollAdjuster.INCREASE_SCROLL_DIRECTION; + } else { + return null; + } + } else { + realDirection = direction; + } + if (orientation == JSlider.HORIZONTAL) { + if (realDirection == ScrollAdjuster.INCREASE_SCROLL_DIRECTION) { + x = oper.getWidth() - 1; + } else if (realDirection == ScrollAdjuster.DECREASE_SCROLL_DIRECTION) { + x = 0; + } else { + return null; + } + y = oper.getHeight() / 2; + } else if (orientation == JSlider.VERTICAL) { + if (realDirection == ScrollAdjuster.INCREASE_SCROLL_DIRECTION) { + y = 0; + } else if (realDirection == ScrollAdjuster.DECREASE_SCROLL_DIRECTION) { + y = oper.getHeight() - 1; + } else { + return null; + } + x = oper.getWidth() / 2; + } else { + return null; + } + return new Point(x, y); + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/scrolling/JSpinnerDriver.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/scrolling/JSpinnerDriver.java new file mode 100644 index 00000000000..9c7389d0100 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/scrolling/JSpinnerDriver.java @@ -0,0 +1,129 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers.scrolling; + +import javax.swing.SwingConstants; + +import org.netbeans.jemmy.drivers.LightSupportiveDriver; +import org.netbeans.jemmy.drivers.ScrollDriver; +import org.netbeans.jemmy.operators.ComponentOperator; +import org.netbeans.jemmy.operators.JButtonOperator; +import org.netbeans.jemmy.operators.JSpinnerOperator; + +/** + * A scroll driver serving JSpinner component. + * + * @author Alexandre Iline(alexandre.iline@oracle.com) + */ +public class JSpinnerDriver extends LightSupportiveDriver implements ScrollDriver { + + /** + * Constructs a JSpinnerDriver object. + */ + public JSpinnerDriver() { + super(new String[]{"org.netbeans.jemmy.operators.JSpinnerOperator"}); + } + + @Override + public void scrollToMinimum(final ComponentOperator oper, int orientation) { + Object minimum = ((JSpinnerOperator) oper).getMinimum(); + if (minimum == null) { + throw (new JSpinnerOperator.SpinnerModelException("Impossible to get a minimum of JSpinner model.", oper.getSource())); + } + scroll(oper, new ScrollAdjuster() { + @Override + public int getScrollOrientation() { + return SwingConstants.VERTICAL; + } + + @Override + public String getDescription() { + return "Spin to minimum"; + } + + @Override + public int getScrollDirection() { + if (((JSpinnerOperator) oper).getModel().getPreviousValue() != null) { + return ScrollAdjuster.DECREASE_SCROLL_DIRECTION; + } else { + return ScrollAdjuster.DO_NOT_TOUCH_SCROLL_DIRECTION; + } + } + + @Override + public String toString() { + return "scrollToMinimum.ScrollAdjuster{description = " + getDescription() + '}'; + } + }); + } + + @Override + public void scrollToMaximum(final ComponentOperator oper, int orientation) { + Object maximum = ((JSpinnerOperator) oper).getMaximum(); + if (maximum == null) { + throw (new JSpinnerOperator.SpinnerModelException("Impossible to get a maximum of JSpinner model.", oper.getSource())); + } + scroll(oper, new ScrollAdjuster() { + @Override + public int getScrollOrientation() { + return SwingConstants.VERTICAL; + } + + @Override + public String getDescription() { + return "Spin to maximum"; + } + + @Override + public int getScrollDirection() { + if (((JSpinnerOperator) oper).getModel().getNextValue() != null) { + return ScrollAdjuster.INCREASE_SCROLL_DIRECTION; + } else { + return ScrollAdjuster.DO_NOT_TOUCH_SCROLL_DIRECTION; + } + } + + @Override + public String toString() { + return "scrollToMaximum.ScrollAdjuster{description = " + getDescription() + '}'; + } + }); + } + + @Override + public void scroll(ComponentOperator oper, ScrollAdjuster adj) { + JButtonOperator increaseButton = ((JSpinnerOperator) oper).getIncreaseOperator(); + JButtonOperator decreaseButton = ((JSpinnerOperator) oper).getDecreaseOperator(); + if (adj.getScrollDirection() == ScrollAdjuster.DO_NOT_TOUCH_SCROLL_DIRECTION) { + return; + } + int originalDirection = adj.getScrollDirection(); + while (adj.getScrollDirection() == originalDirection) { + if (originalDirection == ScrollAdjuster.INCREASE_SCROLL_DIRECTION) { + increaseButton.push(); + } else { + decreaseButton.push(); + } + } + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/scrolling/JSplitPaneDriver.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/scrolling/JSplitPaneDriver.java new file mode 100644 index 00000000000..cc21e09bb5d --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/scrolling/JSplitPaneDriver.java @@ -0,0 +1,158 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers.scrolling; + +import javax.swing.JButton; +import javax.swing.JSplitPane; + +import org.netbeans.jemmy.ComponentSearcher; +import org.netbeans.jemmy.drivers.ButtonDriver; +import org.netbeans.jemmy.drivers.DriverManager; +import org.netbeans.jemmy.drivers.LightSupportiveDriver; +import org.netbeans.jemmy.drivers.ScrollDriver; +import org.netbeans.jemmy.operators.ComponentOperator; +import org.netbeans.jemmy.operators.ContainerOperator; +import org.netbeans.jemmy.operators.JButtonOperator; +import org.netbeans.jemmy.operators.JSplitPaneOperator; +import org.netbeans.jemmy.operators.Operator; + +/** + * ScrollDriver for javax.swing.JSplitPane component type. + * + * @author Alexandre Iline(alexandre.iline@oracle.com) + */ +public class JSplitPaneDriver extends LightSupportiveDriver implements ScrollDriver { + + /** + * Constructs a JSplitPaneDriver. + */ + public JSplitPaneDriver() { + super(new String[]{"org.netbeans.jemmy.operators.JSplitPaneOperator"}); + } + + @Override + public void scroll(ComponentOperator oper, ScrollAdjuster adj) { + moveDividerTo((JSplitPaneOperator) oper, adj); + } + + @Override + public void scrollToMinimum(ComponentOperator oper, int orientation) { + expandTo((JSplitPaneOperator) oper, 0); + } + + @Override + public void scrollToMaximum(ComponentOperator oper, int orientation) { + expandTo((JSplitPaneOperator) oper, 1); + } + + private void moveDividerTo(JSplitPaneOperator oper, ScrollAdjuster adj) { + ContainerOperator divOper = oper.getDivider(); + /* workaround */ + if (oper.getDividerLocation() == -1) { + moveTo(oper, divOper, divOper.getCenterX() - 1, divOper.getCenterY() - 1); + if (oper.getDividerLocation() == -1) { + moveTo(oper, divOper, divOper.getCenterX() + 1, divOper.getCenterY() + 1); + } + } + if (oper.getOrientation() == JSplitPane.HORIZONTAL_SPLIT) { + moveOnce(oper, divOper, adj, 0, oper.getWidth()); + } else { + moveOnce(oper, divOper, adj, 0, oper.getHeight()); + } + } + + private void moveOnce(JSplitPaneOperator oper, + ContainerOperator divOper, + ScrollAdjuster adj, + int leftPosition, + int rightPosition) { + int currentPosition = 0; + if (oper.getOrientation() == JSplitPane.HORIZONTAL_SPLIT) { + currentPosition = (int) (divOper.getLocationOnScreen().getX() + - oper.getLocationOnScreen().getX()); + } else { + currentPosition = (int) (divOper.getLocationOnScreen().getY() + - oper.getLocationOnScreen().getY()); + } + int nextPosition = 0; + if (adj.getScrollDirection() == ScrollAdjuster.DECREASE_SCROLL_DIRECTION) { + nextPosition = (currentPosition + leftPosition) / 2; + moveToPosition(oper, divOper, nextPosition - currentPosition); + if (currentPosition + == ((adj.getScrollOrientation() == JSplitPane.HORIZONTAL_SPLIT) + ? (int) (divOper.getLocationOnScreen().getX() + - oper.getLocationOnScreen().getX()) + : (int) (divOper.getLocationOnScreen().getY() + - oper.getLocationOnScreen().getY()))) { + return; + } + moveOnce(oper, divOper, adj, leftPosition, currentPosition); + } else if (adj.getScrollDirection() == ScrollAdjuster.INCREASE_SCROLL_DIRECTION) { + nextPosition = (currentPosition + rightPosition) / 2; + moveToPosition(oper, divOper, nextPosition - currentPosition); + if (currentPosition + == ((adj.getScrollOrientation() == JSplitPane.HORIZONTAL_SPLIT) + ? (int) (divOper.getLocationOnScreen().getX() + - oper.getLocationOnScreen().getX()) + : (int) (divOper.getLocationOnScreen().getY() + - oper.getLocationOnScreen().getY()))) { + return; + } + moveOnce(oper, divOper, adj, currentPosition, rightPosition); + } else { // (currentLocation == dividerLocation) - stop point + return; + } + } + + private void moveTo(JSplitPaneOperator oper, ComponentOperator divOper, int x, int y) { + DriverManager.getMouseDriver(divOper). + dragNDrop(divOper, divOper.getCenterX(), divOper.getCenterY(), x, y, + Operator.getDefaultMouseButton(), 0, + oper.getTimeouts().create("ComponentOperator.BeforeDragTimeout"), + oper.getTimeouts().create("ComponentOperator.AfterDragTimeout")); + } + + private void moveToPosition(JSplitPaneOperator oper, ComponentOperator divOper, int nextPosition) { + if (System.getProperty("java.version").startsWith("1.2")) { + oper.setDividerLocation(nextPosition); + } + if (oper.getOrientation() == JSplitPane.HORIZONTAL_SPLIT) { + moveTo(oper, divOper, divOper.getCenterX() + nextPosition, divOper.getCenterY()); + } else { + moveTo(oper, divOper, divOper.getCenterX(), divOper.getCenterY() + nextPosition); + } + } + + private void expandTo(JSplitPaneOperator oper, int index) { + ContainerOperator divOper = oper.getDivider(); + JButtonOperator bo + = new JButtonOperator((JButton) divOper. + waitSubComponent(new JButtonOperator.JButtonFinder(ComponentSearcher. + getTrueChooser("JButton")), + index)); + bo.copyEnvironment(divOper); + ButtonDriver bdriver = DriverManager.getButtonDriver(bo); + bdriver.push(bo); + bdriver.push(bo); + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/scrolling/KeyboardJSliderScrollDriver.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/scrolling/KeyboardJSliderScrollDriver.java new file mode 100644 index 00000000000..12522ee74c0 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/scrolling/KeyboardJSliderScrollDriver.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers.scrolling; + +import java.awt.Adjustable; +import java.awt.event.KeyEvent; +import org.netbeans.jemmy.operators.ComponentOperator; + +/** + * + * @author shura + */ +public class KeyboardJSliderScrollDriver extends JSliderDriver { + + private static int getButton(int direction, int orientation) { + if (direction == ScrollAdjuster.DECREASE_SCROLL_DIRECTION) { + return (orientation == Adjustable.HORIZONTAL) ? KeyEvent.VK_LEFT : KeyEvent.VK_DOWN; + } else { + return (orientation == Adjustable.HORIZONTAL) ? KeyEvent.VK_RIGHT : KeyEvent.VK_UP; + } + } + + @Override + protected boolean doPushAndWait(ComponentOperator oper, ScrollAdjuster adj, long freezeTimeout) { + super.doPushAndWait(oper, adj, freezeTimeout); + return true; + } + + @Override + protected void step(ComponentOperator oper, ScrollAdjuster adj) { + oper.pushKey(getButton(adj.getScrollDirection(), adj.getScrollOrientation())); + oper.getTimeouts().create("Waiter.TimeDelta").sleep(); + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/scrolling/ScrollAdjuster.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/scrolling/ScrollAdjuster.java new file mode 100644 index 00000000000..852b43515ef --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/scrolling/ScrollAdjuster.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers.scrolling; + +/** + * Specifies scrolling criteria. + * + * @author Alexandre Iline(alexandre.iline@oracle.com) + */ +public interface ScrollAdjuster { + + /** + * Increase scroll direction. + */ + public static final int INCREASE_SCROLL_DIRECTION = 1; + + /** + * Decrease scroll direction. + */ + public static final int DECREASE_SCROLL_DIRECTION = -1; + + /** + * Specifies that necessary value has been reached.. + */ + public static final int DO_NOT_TOUCH_SCROLL_DIRECTION = 0; + + /** + * Returns scroll direction to reach necessary scroller value. + * + * @return one of the values: INCREASE_SCROLL_DIRECTION, + * DECREASE_SCROLL_DIRECTION or DO_NOT_TOUCH_SCROLL_DIRECTION. + */ + public int getScrollDirection(); + + /** + * Returns scrolling orientation. + * + * @return one of the values: Adjustable.HORIZONTAL or Adjustable.VERTICAL. + */ + public int getScrollOrientation(); + + /** + * Returns a printable scrolling description. + * + * @return a description. + */ + public String getDescription(); +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/scrolling/ScrollPaneDriver.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/scrolling/ScrollPaneDriver.java new file mode 100644 index 00000000000..505157bf5fb --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/scrolling/ScrollPaneDriver.java @@ -0,0 +1,151 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers.scrolling; + +import java.awt.Adjustable; +import java.awt.Point; +import java.awt.Scrollbar; + +import org.netbeans.jemmy.operators.ComponentOperator; +import org.netbeans.jemmy.operators.ScrollPaneOperator; + +/** + * ScrollDriver for java.awt.ScrollPane component type. + * + * @author Alexandre Iline(alexandre.iline@oracle.com) + */ +public class ScrollPaneDriver extends AWTScrollDriver { + + private static final int CLICK_OFFSET = 5; + + /** + * Constructs a ScrollPaneDriver. + */ + public ScrollPaneDriver() { + super(new String[]{"org.netbeans.jemmy.operators.ScrollPaneOperator"}); + } + + @Override + protected int position(ComponentOperator oper, int orientation) { + return (orientation == Adjustable.HORIZONTAL) + ? ((ScrollPaneOperator) oper).getScrollPosition().x + : ((ScrollPaneOperator) oper).getScrollPosition().y; + } + + @Override + public void scrollToMinimum(ComponentOperator oper, final int orientation) { + final Adjustable adj + = (orientation == Scrollbar.HORIZONTAL) + ? ((ScrollPaneOperator) oper).getHAdjustable() + : ((ScrollPaneOperator) oper).getVAdjustable(); + scroll(oper, + new ScrollAdjuster() { + @Override + public int getScrollDirection() { + return ((adj.getMinimum() < adj.getValue()) + ? DECREASE_SCROLL_DIRECTION + : DO_NOT_TOUCH_SCROLL_DIRECTION); + } + + @Override + public int getScrollOrientation() { + return orientation; + } + + @Override + public String getDescription() { + return "Scroll to minimum"; + } + + @Override + public String toString() { + return "scrollToMinimum.ScrollAdjuster{description = " + getDescription() + '}'; + } + }); + } + + @Override + public void scrollToMaximum(ComponentOperator oper, final int orientation) { + final Adjustable adj + = (orientation == Scrollbar.HORIZONTAL) + ? ((ScrollPaneOperator) oper).getHAdjustable() + : ((ScrollPaneOperator) oper).getVAdjustable(); + scroll(oper, + new ScrollAdjuster() { + @Override + public int getScrollDirection() { + return (((adj.getMaximum() - adj.getVisibleAmount()) > adj.getValue()) + ? INCREASE_SCROLL_DIRECTION + : DO_NOT_TOUCH_SCROLL_DIRECTION); + } + + @Override + public int getScrollOrientation() { + return orientation; + } + + @Override + public String getDescription() { + return "Scroll to maximum"; + } + + @Override + public String toString() { + return "scrollToMaximum.ScrollAdjuster{description = " + getDescription() + '}'; + } + }); + } + + @Override + protected Point getClickPoint(ComponentOperator oper, int direction, int orientation) { + int x, y; + if (orientation == Scrollbar.HORIZONTAL) { + int offset = ((ScrollPaneOperator) oper). + isScrollbarVisible(Scrollbar.VERTICAL) + ? ((ScrollPaneOperator) oper).getVScrollbarWidth() : 0; + if (direction == ScrollAdjuster.INCREASE_SCROLL_DIRECTION) { + x = oper.getWidth() - 1 - CLICK_OFFSET - offset; + } else if (direction == ScrollAdjuster.DECREASE_SCROLL_DIRECTION) { + x = CLICK_OFFSET; + } else { + return null; + } + y = oper.getHeight() - ((ScrollPaneOperator) oper).getHScrollbarHeight() / 2; + } else if (orientation == Scrollbar.VERTICAL) { + int offset = ((ScrollPaneOperator) oper). + isScrollbarVisible(Scrollbar.HORIZONTAL) + ? ((ScrollPaneOperator) oper).getHScrollbarHeight() : 0; + if (direction == ScrollAdjuster.INCREASE_SCROLL_DIRECTION) { + y = oper.getHeight() - 1 - CLICK_OFFSET - offset; + } else if (direction == ScrollAdjuster.DECREASE_SCROLL_DIRECTION) { + y = CLICK_OFFSET; + } else { + return null; + } + x = oper.getWidth() - ((ScrollPaneOperator) oper).getVScrollbarWidth() / 2; + } else { + return null; + } + return new Point(x, y); + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/scrolling/ScrollbarDriver.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/scrolling/ScrollbarDriver.java new file mode 100644 index 00000000000..8d831302c0b --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/scrolling/ScrollbarDriver.java @@ -0,0 +1,137 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers.scrolling; + +import java.awt.Point; +import java.awt.Scrollbar; + +import org.netbeans.jemmy.operators.ComponentOperator; +import org.netbeans.jemmy.operators.ScrollbarOperator; + +/** + * ScrollDriver for java.awt.Scrollbar component type. + * + * @author Alexandre Iline(alexandre.iline@oracle.com) + */ +public class ScrollbarDriver extends AWTScrollDriver { + + private static final int CLICK_OFFSET = 5; + + /** + * Constructs a ScrollbarDriver. + */ + public ScrollbarDriver() { + super(new String[]{"org.netbeans.jemmy.operators.ScrollbarOperator"}); + } + + @Override + protected int position(ComponentOperator oper, int orientation) { + return ((ScrollbarOperator) oper).getValue(); + } + + @Override + public void scrollToMinimum(final ComponentOperator oper, final int orientation) { + scroll(oper, + new ScrollAdjuster() { + @Override + public int getScrollDirection() { + return ((((ScrollbarOperator) oper).getMinimum() + < ((ScrollbarOperator) oper).getValue()) + ? DECREASE_SCROLL_DIRECTION + : DO_NOT_TOUCH_SCROLL_DIRECTION); + } + + @Override + public int getScrollOrientation() { + return ((ScrollbarOperator) oper).getOrientation(); + } + + @Override + public String getDescription() { + return "Scroll to minimum"; + } + + @Override + public String toString() { + return "scrollToMinimum.ScrollAdjuster{description = " + getDescription() + '}'; + } + }); + } + + @Override + public void scrollToMaximum(final ComponentOperator oper, final int orientation) { + scroll(oper, + new ScrollAdjuster() { + @Override + public int getScrollDirection() { + return (((((ScrollbarOperator) oper).getMaximum() + - ((ScrollbarOperator) oper).getVisibleAmount()) + > ((ScrollbarOperator) oper).getValue()) + ? INCREASE_SCROLL_DIRECTION + : DO_NOT_TOUCH_SCROLL_DIRECTION); + } + + @Override + public int getScrollOrientation() { + return ((ScrollbarOperator) oper).getOrientation(); + } + + @Override + public String getDescription() { + return "Scroll to maximum"; + } + + @Override + public String toString() { + return "scrollToMaximum.ScrollAdjuster{description = " + getDescription() + '}'; + } + }); + } + + @Override + protected Point getClickPoint(ComponentOperator oper, int direction, int orientation) { + int x, y; + if (orientation == Scrollbar.HORIZONTAL) { + if (direction == ScrollAdjuster.INCREASE_SCROLL_DIRECTION) { + x = oper.getWidth() - 1 - CLICK_OFFSET; + } else if (direction == ScrollAdjuster.DECREASE_SCROLL_DIRECTION) { + x = CLICK_OFFSET; + } else { + return null; + } + y = oper.getHeight() / 2; + } else if (orientation == Scrollbar.VERTICAL) { + if (direction == ScrollAdjuster.INCREASE_SCROLL_DIRECTION) { + y = oper.getHeight() - 1 - CLICK_OFFSET; + } else if (direction == ScrollAdjuster.DECREASE_SCROLL_DIRECTION) { + y = CLICK_OFFSET; + } else { + return null; + } + x = oper.getWidth() / 2; + } else { + return null; + } + return new Point(x, y); + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/scrolling/package-info.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/scrolling/package-info.java new file mode 100644 index 00000000000..e3e930a658d --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/scrolling/package-info.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +/** + *

Scrolling drivers

+ * Different drivers to perform a scrolling operations.
+ * + * @since 17 Apr 2002 + *
+ */ +package org.netbeans.jemmy.drivers.scrolling; diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/tables/JTableMouseDriver.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/tables/JTableMouseDriver.java new file mode 100644 index 00000000000..cbf011393b1 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/tables/JTableMouseDriver.java @@ -0,0 +1,105 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers.tables; + +import java.awt.Point; +import java.awt.event.KeyEvent; + +import javax.swing.text.JTextComponent; + +import org.netbeans.jemmy.QueueTool; +import org.netbeans.jemmy.drivers.DriverManager; +import org.netbeans.jemmy.drivers.LightSupportiveDriver; +import org.netbeans.jemmy.drivers.TableDriver; +import org.netbeans.jemmy.drivers.TextDriver; +import org.netbeans.jemmy.operators.ComponentOperator; +import org.netbeans.jemmy.operators.JTableOperator; +import org.netbeans.jemmy.operators.JTextComponentOperator; +import org.netbeans.jemmy.operators.Operator; + +/** + * TableDriver for javax.swing.JTableDriver component type. + * + * @author Alexandre Iline(alexandre.iline@oracle.com) + */ +public class JTableMouseDriver extends LightSupportiveDriver implements TableDriver { + + QueueTool queueTool; + + /** + * Constructs a JTableMouseDriver. + */ + public JTableMouseDriver() { + super(new String[]{"org.netbeans.jemmy.operators.JTableOperator"}); + queueTool = new QueueTool(); + } + + @Override + public void selectCell(ComponentOperator oper, int row, int column) { + clickOnCell((JTableOperator) oper, row, column, 1); + } + + @Override + public void editCell(ComponentOperator oper, int row, int column, Object value) { + JTableOperator toper = (JTableOperator) oper; + toper.scrollToCell(row, column); + if (!toper.isEditing() + || toper.getEditingRow() != row + || toper.getEditingColumn() != column) { + clickOnCell((JTableOperator) oper, row, column, 2); + } + JTextComponentOperator textoper + = new JTextComponentOperator((JTextComponent) toper. + waitSubComponent(new JTextComponentOperator.JTextComponentFinder())); + TextDriver text = DriverManager.getTextDriver(JTextComponentOperator.class); + text.clearText(textoper); + text.typeText(textoper, value.toString(), 0); + DriverManager.getKeyDriver(oper). + pushKey(textoper, KeyEvent.VK_ENTER, 0, + oper.getTimeouts(). + create("ComponentOperator.PushKeyTimeout")); + } + + /** + * Clicks on JTable cell. + * + * @param oper Table operator. + * @param row Cell row index. + * @param column Cell column index. + * @param clickCount Count to click. + */ + protected void clickOnCell(final JTableOperator oper, final int row, final int column, final int clickCount) { + queueTool.invokeSmoothly(new QueueTool.QueueAction("Path selecting") { + @Override + public Void launch() { + Point point = oper.getPointToClick(row, column); + DriverManager.getMouseDriver(oper). + clickMouse(oper, point.x, point.y, clickCount, + Operator.getDefaultMouseButton(), + 0, + oper.getTimeouts().create("ComponentOperator.MouseClickTimeout")); + return null; + } + }); + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/tables/package-info.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/tables/package-info.java new file mode 100644 index 00000000000..6cbe4a1545a --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/tables/package-info.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +/** + *

Table drivers

+ * Different drivers to perform a table operations.
+ * + * @since 17 Apr 2002 + *
+ */ +package org.netbeans.jemmy.drivers.tables; diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/text/AWTTextAPIDriver.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/text/AWTTextAPIDriver.java new file mode 100644 index 00000000000..2d87edde851 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/text/AWTTextAPIDriver.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers.text; + +import org.netbeans.jemmy.operators.ComponentOperator; +import org.netbeans.jemmy.operators.TextComponentOperator; + +/** + * TextDriver for AWT component types. Uses API calls. + * + * @author Alexandre Iline(alexandre.iline@oracle.com) + */ +public class AWTTextAPIDriver extends TextAPIDriver { + + /** + * Constructs a AWTTextAPIDriver. + */ + public AWTTextAPIDriver() { + super(new String[]{"org.netbeans.jemmy.operators.TextComponentOperator"}); + } + + @Override + public String getText(ComponentOperator oper) { + return ((TextComponentOperator) oper).getText(); + } + + @Override + public int getCaretPosition(ComponentOperator oper) { + return ((TextComponentOperator) oper).getCaretPosition(); + } + + @Override + public int getSelectionStart(ComponentOperator oper) { + return ((TextComponentOperator) oper).getSelectionStart(); + } + + @Override + public int getSelectionEnd(ComponentOperator oper) { + return ((TextComponentOperator) oper).getSelectionEnd(); + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/text/AWTTextKeyboardDriver.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/text/AWTTextKeyboardDriver.java new file mode 100644 index 00000000000..b2a84dbc4ca --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/text/AWTTextKeyboardDriver.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers.text; + +import java.awt.event.KeyEvent; + +import org.netbeans.jemmy.Timeout; +import org.netbeans.jemmy.operators.ComponentOperator; +import org.netbeans.jemmy.operators.TextAreaOperator; +import org.netbeans.jemmy.operators.TextComponentOperator; + +/** + * TextDriver for AWT text component types. Uses keyboard operations. + * + * @author Alexandre Iline(alexandre.iline@oracle.com) + */ +public class AWTTextKeyboardDriver extends TextKeyboardDriver { + + /** + * Constructs a AWTTextKeyboardDriver. + */ + public AWTTextKeyboardDriver() { + super(new String[]{"org.netbeans.jemmy.operators.TextComponentOperator"}); + } + + @Override + public String getText(ComponentOperator oper) { + return ((TextComponentOperator) oper).getText(); + } + + @Override + public int getCaretPosition(ComponentOperator oper) { + return ((TextComponentOperator) oper).getCaretPosition(); + } + + @Override + public int getSelectionStart(ComponentOperator oper) { + return ((TextComponentOperator) oper).getSelectionStart(); + } + + @Override + public int getSelectionEnd(ComponentOperator oper) { + return ((TextComponentOperator) oper).getSelectionEnd(); + } + + @Override + public NavigationKey[] getKeys(ComponentOperator oper) { + boolean multiString = oper instanceof TextAreaOperator; + NavigationKey[] result = new NavigationKey[multiString ? 4 : 2]; + result[0] = new UpKey(KeyEvent.VK_LEFT, 0); + result[1] = new DownKey(KeyEvent.VK_RIGHT, 0); + ((UpKey) result[0]).setDownKey((DownKey) result[1]); + ((DownKey) result[1]).setUpKey((UpKey) result[0]); + if (multiString) { + result[2] = new UpKey(KeyEvent.VK_UP, 0); + result[3] = new DownKey(KeyEvent.VK_DOWN, 0); + ((UpKey) result[2]).setDownKey((DownKey) result[3]); + ((DownKey) result[3]).setUpKey((UpKey) result[2]); + } + return result; + } + + @Override + public Timeout getBetweenTimeout(ComponentOperator oper) { + return oper.getTimeouts().create("TextComponentOperator.BetweenKeysTimeout"); + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/text/DownKey.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/text/DownKey.java new file mode 100644 index 00000000000..cfe40cb4662 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/text/DownKey.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers.text; + +class DownKey extends GoAndBackKey { + + private UpKey backKey; + + public DownKey(int keyCode, int mods) { + super(keyCode, mods); + } + + public void setUpKey(UpKey key) { + backKey = key; + } + + @Override + public int getDirection() { + return 1; + } + + @Override + public GoAndBackKey getBackKey() { + return backKey; + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/text/EndKey.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/text/EndKey.java new file mode 100644 index 00000000000..9d7c8289cc7 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/text/EndKey.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers.text; + +import org.netbeans.jemmy.operators.ComponentOperator; + +class EndKey extends OffsetKey { + + TextKeyboardDriver cont; + ComponentOperator oper; + + public EndKey(int keyCode, int mods, TextKeyboardDriver cont, ComponentOperator oper) { + super(keyCode, mods); + this.cont = cont; + this.oper = oper; + } + + @Override + public int getDirection() { + return 1; + } + + @Override + public int getExpectedPosition() { + return cont.getText(oper).length(); + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/text/GoAndBackKey.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/text/GoAndBackKey.java new file mode 100644 index 00000000000..9de2fbf8904 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/text/GoAndBackKey.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers.text; + +abstract class GoAndBackKey extends NavigationKey { + + public GoAndBackKey(int keyCode, int mods) { + super(keyCode, mods); + } + + public abstract GoAndBackKey getBackKey(); +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/text/HomeKey.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/text/HomeKey.java new file mode 100644 index 00000000000..e34cca5c220 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/text/HomeKey.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers.text; + +class HomeKey extends OffsetKey { + + public HomeKey(int keyCode, int mods) { + super(keyCode, mods); + } + + @Override + public int getDirection() { + return -1; + } + + @Override + public int getExpectedPosition() { + return 0; + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/text/NavigationKey.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/text/NavigationKey.java new file mode 100644 index 00000000000..d4bd137282b --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/text/NavigationKey.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers.text; + +abstract class NavigationKey { + + private int keyCode; + private int mods; + + public NavigationKey(int keyCode, int mods) { + this.keyCode = keyCode; + this.mods = mods; + } + + public int getKeyCode() { + return keyCode; + } + + public int getModifiers() { + return mods; + } + + public abstract int getDirection(); +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/text/OffsetKey.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/text/OffsetKey.java new file mode 100644 index 00000000000..1c9a2ee2bc6 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/text/OffsetKey.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers.text; + +abstract class OffsetKey extends NavigationKey { + + public OffsetKey(int keyCode, int mods) { + super(keyCode, mods); + } + + public abstract int getExpectedPosition(); +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/text/SwingTextAPIDriver.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/text/SwingTextAPIDriver.java new file mode 100644 index 00000000000..723121d0272 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/text/SwingTextAPIDriver.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers.text; + +import org.netbeans.jemmy.operators.ComponentOperator; +import org.netbeans.jemmy.operators.JTextComponentOperator; + +/** + * TextDriver for swing component types. Uses API calls. + * + * @author Alexandre Iline(alexandre.iline@oracle.com) + */ +public class SwingTextAPIDriver extends TextAPIDriver { + + /** + * Constructs a SwingTextAPIDriver. + */ + public SwingTextAPIDriver() { + super(new String[]{"org.netbeans.jemmy.operators.JTextComponentOperator"}); + } + + @Override + public String getText(ComponentOperator oper) { + return ((JTextComponentOperator) oper).getDisplayedText(); + } + + @Override + public int getCaretPosition(ComponentOperator oper) { + return ((JTextComponentOperator) oper).getCaretPosition(); + } + + @Override + public int getSelectionStart(ComponentOperator oper) { + return ((JTextComponentOperator) oper).getSelectionStart(); + } + + @Override + public int getSelectionEnd(ComponentOperator oper) { + return ((JTextComponentOperator) oper).getSelectionEnd(); + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/text/SwingTextKeyboardDriver.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/text/SwingTextKeyboardDriver.java new file mode 100644 index 00000000000..e3e71e37fcd --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/text/SwingTextKeyboardDriver.java @@ -0,0 +1,116 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers.text; + +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +import org.netbeans.jemmy.Timeout; +import org.netbeans.jemmy.drivers.DriverManager; +import org.netbeans.jemmy.drivers.KeyDriver; +import org.netbeans.jemmy.operators.ComponentOperator; +import org.netbeans.jemmy.operators.JEditorPaneOperator; +import org.netbeans.jemmy.operators.JTextAreaOperator; +import org.netbeans.jemmy.operators.JTextComponentOperator; + +/** + * TextDriver for swing text component types. Uses keyboard operations. + * + * @author Alexandre Iline(alexandre.iline@oracle.com) + */ +public class SwingTextKeyboardDriver extends TextKeyboardDriver { + + /** + * Constructs a SwingTextKeyboardDriver. + */ + public SwingTextKeyboardDriver() { + super(new String[]{"org.netbeans.jemmy.operators.JTextComponentOperator"}); + } + + @Override + public void clearText(ComponentOperator oper) { + if (oper instanceof JTextAreaOperator + || oper instanceof JEditorPaneOperator) { + DriverManager.getFocusDriver(oper).giveFocus(oper); + KeyDriver kdriver = DriverManager.getKeyDriver(oper); + selectText(oper, 0, getText(oper).length()); + kdriver.pushKey(oper, KeyEvent.VK_DELETE, 0, + oper.getTimeouts().create("ComponentOperator.PushKeyTimeout")); + } else { + super.clearText(oper); + } + } + + @Override + public String getText(ComponentOperator oper) { + return ((JTextComponentOperator) oper).getDisplayedText(); + } + + @Override + public int getCaretPosition(ComponentOperator oper) { + return ((JTextComponentOperator) oper).getCaretPosition(); + } + + @Override + public int getSelectionStart(ComponentOperator oper) { + return ((JTextComponentOperator) oper).getSelectionStart(); + } + + @Override + public int getSelectionEnd(ComponentOperator oper) { + return ((JTextComponentOperator) oper).getSelectionEnd(); + } + + @Override + public NavigationKey[] getKeys(ComponentOperator oper) { + boolean multiString + = oper instanceof JTextAreaOperator + || oper instanceof JEditorPaneOperator; + NavigationKey[] result = new NavigationKey[multiString ? 8 : 4]; + result[0] = new UpKey(KeyEvent.VK_LEFT, 0); + result[1] = new DownKey(KeyEvent.VK_RIGHT, 0); + ((UpKey) result[0]).setDownKey((DownKey) result[1]); + ((DownKey) result[1]).setUpKey((UpKey) result[0]); + if (multiString) { + result[2] = new UpKey(KeyEvent.VK_UP, 0); + result[3] = new DownKey(KeyEvent.VK_DOWN, 0); + ((UpKey) result[2]).setDownKey((DownKey) result[3]); + ((DownKey) result[3]).setUpKey((UpKey) result[2]); + result[4] = new UpKey(KeyEvent.VK_PAGE_UP, 0); + result[5] = new DownKey(KeyEvent.VK_PAGE_DOWN, 0); + ((UpKey) result[4]).setDownKey((DownKey) result[5]); + ((DownKey) result[5]).setUpKey((UpKey) result[4]); + result[6] = new HomeKey(KeyEvent.VK_HOME, InputEvent.CTRL_MASK); + result[7] = new EndKey(KeyEvent.VK_END, InputEvent.CTRL_MASK, this, oper); + } else { + result[2] = new HomeKey(KeyEvent.VK_HOME, 0); + result[3] = new EndKey(KeyEvent.VK_END, 0, this, oper); + } + return result; + } + + @Override + public Timeout getBetweenTimeout(ComponentOperator oper) { + return oper.getTimeouts().create("TextComponentOperator.BetweenKeysTimeout"); + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/text/TextAPIDriver.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/text/TextAPIDriver.java new file mode 100644 index 00000000000..fdc626e45ab --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/text/TextAPIDriver.java @@ -0,0 +1,154 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers.text; + +import java.awt.event.KeyEvent; + +import org.netbeans.jemmy.Timeout; +import org.netbeans.jemmy.drivers.DriverManager; +import org.netbeans.jemmy.drivers.LightSupportiveDriver; +import org.netbeans.jemmy.drivers.TextDriver; +import org.netbeans.jemmy.operators.ComponentOperator; +import org.netbeans.jemmy.operators.JTextComponentOperator; +import org.netbeans.jemmy.operators.TextComponentOperator; + +/** + * Superclass for all TextDrivers using API calls. + * + * @author Alexandre Iline(alexandre.iline@oracle.com) + */ +public abstract class TextAPIDriver extends LightSupportiveDriver implements TextDriver { + + /** + * Constructs a ChoiceDriver. + * + * @param supported an array of supported class names + */ + public TextAPIDriver(String[] supported) { + super(supported); + } + + @Override + public void changeCaretPosition(ComponentOperator oper, int position) { + checkSupported(oper); + if (oper instanceof TextComponentOperator) { + ((TextComponentOperator) oper).setCaretPosition(position); + } else { + ((JTextComponentOperator) oper).setCaretPosition(position); + } + } + + @Override + public void selectText(ComponentOperator oper, int startPosition, int finalPosition) { + checkSupported(oper); + int start = (startPosition < finalPosition) ? startPosition : finalPosition; + int end = (startPosition > finalPosition) ? startPosition : finalPosition; + if (oper instanceof TextComponentOperator) { + TextComponentOperator toper = ((TextComponentOperator) oper); + toper.setSelectionStart(start); + toper.setSelectionEnd(end); + } else { + JTextComponentOperator toper = ((JTextComponentOperator) oper); + toper.setSelectionStart(start); + toper.setSelectionEnd(end); + } + } + + @Override + public void clearText(ComponentOperator oper) { + if (oper instanceof TextComponentOperator) { + ((TextComponentOperator) oper).setText(""); + } else { + ((JTextComponentOperator) oper).setText(""); + } + } + + @Override + public void typeText(ComponentOperator oper, String text, int caretPosition) { + checkSupported(oper); + String curtext = getText(oper); + int realPos = caretPosition; + if (getSelectionStart(oper) == realPos + || getSelectionEnd(oper) == realPos) { + if (getSelectionEnd(oper) == realPos) { + realPos = realPos - (getSelectionEnd(oper) - getSelectionStart(oper)); + } + curtext + = curtext.substring(0, getSelectionStart(oper)) + + curtext.substring(getSelectionEnd(oper)); + } + changeText(oper, + curtext.substring(0, realPos) + text + + curtext.substring(realPos)); + } + + @Override + public void changeText(ComponentOperator oper, String text) { + checkSupported(oper); + if (oper instanceof TextComponentOperator) { + ((TextComponentOperator) oper).setText(text); + } else { + ((JTextComponentOperator) oper).setText(text); + } + } + + @Override + public void enterText(ComponentOperator oper, String text) { + changeText(oper, text); + DriverManager.getKeyDriver(oper). + pushKey(oper, KeyEvent.VK_ENTER, 0, + new Timeout("", 0)); + } + + /** + * Returns operator's text. + * + * @param oper an operator. + * @return string representing component text. + */ + public abstract String getText(ComponentOperator oper); + + /** + * Returns current caret position. + * + * @param oper an operator. + * @return int represnting current operator's caret position. + */ + public abstract int getCaretPosition(ComponentOperator oper); + + /** + * Returns a caret position of selection start. + * + * @param oper an operator. + * @return int represnting index of operator's selection start. + */ + public abstract int getSelectionStart(ComponentOperator oper); + + /** + * Returns a caret position of selection end. + * + * @param oper an operator. + * @return int represnting index of operator's selection end. + */ + public abstract int getSelectionEnd(ComponentOperator oper); +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/text/TextKeyboardDriver.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/text/TextKeyboardDriver.java new file mode 100644 index 00000000000..d5121504c41 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/text/TextKeyboardDriver.java @@ -0,0 +1,223 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers.text; + +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +import org.netbeans.jemmy.CharBindingMap; +import org.netbeans.jemmy.QueueTool; +import org.netbeans.jemmy.Timeout; +import org.netbeans.jemmy.drivers.DriverManager; +import org.netbeans.jemmy.drivers.KeyDriver; +import org.netbeans.jemmy.drivers.LightSupportiveDriver; +import org.netbeans.jemmy.drivers.TextDriver; +import org.netbeans.jemmy.operators.ComponentOperator; + +/** + * Superclass for all TextDrivers using keyboard. + * + * @author Alexandre Iline(alexandre.iline@oracle.com) + */ +public abstract class TextKeyboardDriver extends LightSupportiveDriver implements TextDriver { + + /** + * Constructs a TextKeyboardDriver. + * + * @param supported an array of supported class names + */ + public TextKeyboardDriver(String[] supported) { + super(supported); + } + + @Override + public void changeCaretPosition(ComponentOperator oper, int position) { + DriverManager.getFocusDriver(oper).giveFocus(oper); + checkSupported(oper); + changeCaretPosition(oper, position, 0); + } + + @Override + public void selectText(ComponentOperator oper, int startPosition, int finalPosition) { + changeCaretPosition(oper, startPosition); + DriverManager.getKeyDriver(oper).pressKey(oper, KeyEvent.VK_SHIFT, 0); + changeCaretPosition(oper, finalPosition, InputEvent.SHIFT_MASK); + DriverManager.getKeyDriver(oper).releaseKey(oper, KeyEvent.VK_SHIFT, 0); + } + + @Override + public void clearText(ComponentOperator oper) { + DriverManager.getFocusDriver(oper).giveFocus(oper); + checkSupported(oper); + KeyDriver kdriver = DriverManager.getKeyDriver(oper); + Timeout pushTime = oper.getTimeouts().create("ComponentOperator.PushKeyTimeout"); + Timeout betweenTime = getBetweenTimeout(oper); + while (getCaretPosition(oper) > 0) { + kdriver.typeKey(oper, KeyEvent.VK_BACK_SPACE, (char) KeyEvent.VK_BACK_SPACE, 0, pushTime); + betweenTime.sleep(); + } + while (getText(oper).length() > 0) { + kdriver.pushKey(oper, KeyEvent.VK_DELETE, 0, pushTime); + betweenTime.sleep(); + } + } + + @Override + public void typeText(ComponentOperator oper, String text, int caretPosition) { + changeCaretPosition(oper, caretPosition); + KeyDriver kDriver = DriverManager.getKeyDriver(oper); + CharBindingMap map = oper.getCharBindingMap(); + Timeout pushTime = oper.getTimeouts().create("ComponentOperator.PushKeyTimeout"); + Timeout betweenTime = getBetweenTimeout(oper); + char[] crs = text.toCharArray(); + for (char cr : crs) { + kDriver.typeKey(oper, map.getCharKey(cr), cr, map.getCharModifiers(cr), pushTime); + betweenTime.sleep(); + } + } + + @Override + public void changeText(ComponentOperator oper, String text) { + clearText(oper); + typeText(oper, text, 0); + } + + @Override + public void enterText(ComponentOperator oper, String text) { + changeText(oper, text); + DriverManager.getKeyDriver(oper).pushKey(oper, KeyEvent.VK_ENTER, 0, + new Timeout("", 0)); + } + + /** + * Returns operator's text. + * + * @param oper an operator. + * @return string representing component text. + */ + public abstract String getText(ComponentOperator oper); + + /** + * Returns current caret position. + * + * @param oper an operator. + * @return int represnting current operator's caret position. + */ + public abstract int getCaretPosition(ComponentOperator oper); + + /** + * Returns a caret position of selection start. + * + * @param oper an operator. + * @return int represnting index of operator's selection start. + */ + public abstract int getSelectionStart(ComponentOperator oper); + + /** + * Returns a caret position of selection end. + * + * @param oper an operator. + * @return int represnting index of operator's selection end. + */ + public abstract int getSelectionEnd(ComponentOperator oper); + + /** + * Returns an array of navigation keys. + * + * @param oper an operator. + * @return an array on NavigationKey instances. + */ + public abstract NavigationKey[] getKeys(ComponentOperator oper); + + /** + * Returns a timeout to sleep between text typing and caret operations. + * + * @param oper an operator. + * @return a Timeout instance. + */ + public abstract Timeout getBetweenTimeout(ComponentOperator oper); + + /** + * Changes current caret position to specifyed. + * + * @param oper an operator. + * @param position new caret position + * @param preModifiers a modifiers (combination of + * {@code InputEvent.*_MASK} fields) pushed before caret moving (like + * shift during text selection). + */ + protected void changeCaretPosition(ComponentOperator oper, final int position, final int preModifiers) { + NavigationKey[] keys = getKeys(oper); + for (int i = keys.length - 1; i >= 0; i--) { + if (keys[i] instanceof OffsetKey) { + moveCaret(oper, (OffsetKey) keys[i], position, preModifiers); + } else { + moveCaret(oper, (GoAndBackKey) keys[i], position, preModifiers); + } + } + } + + private int difference(int one, int two) { + if (one >= two) { + return one - two; + } else { + return two - one; + } + } + + private void push(ComponentOperator oper, NavigationKey key, int preModifiers) { + DriverManager.getKeyDriver(oper). + pushKey(oper, key.getKeyCode(), key.getModifiers() | preModifiers, + oper.getTimeouts().create("ComponentOperator.PushKeyTimeout")); + getBetweenTimeout(oper).sleep(); + } + + private final void moveCaret(ComponentOperator oper, GoAndBackKey key, int position, int preModifiers) { + int newDiff = difference(position, getCaretPosition(oper)); + int oldDiff = newDiff; + QueueTool qTool = new QueueTool(); + qTool.setOutput(oper.getOutput().createErrorOutput()); + while (key.getDirection() * (position - getCaretPosition(oper)) > 0) { + oldDiff = newDiff; + push(oper, key, preModifiers); + qTool.waitEmpty(); + newDiff = difference(position, getCaretPosition(oper)); + if (newDiff == oldDiff) { + return; + } + } + if (newDiff > oldDiff) { + push(oper, key.getBackKey(), preModifiers); + } + } + + private final void moveCaret(ComponentOperator oper, OffsetKey key, int position, int preModifiers) { + if (gotToGo(oper, position, key.getExpectedPosition())) { + push(oper, key, preModifiers); + } + } + + private boolean gotToGo(ComponentOperator oper, int point, int offset) { + return difference(point, offset) < difference(point, getCaretPosition(oper)); + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/text/UpKey.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/text/UpKey.java new file mode 100644 index 00000000000..5bc5393bc25 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/text/UpKey.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers.text; + +class UpKey extends GoAndBackKey { + + private DownKey backKey; + + public UpKey(int keyCode, int mods) { + super(keyCode, mods); + } + + public void setDownKey(DownKey key) { + backKey = key; + } + + @Override + public int getDirection() { + return -1; + } + + @Override + public GoAndBackKey getBackKey() { + return backKey; + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/text/package-info.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/text/package-info.java new file mode 100644 index 00000000000..ecf61309846 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/text/package-info.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +/** + *

Text drivers

+ * Different drivers to perform a text operations.
+ * + * @since 17 Apr 2002 + *
+ */ +package org.netbeans.jemmy.drivers.text; diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/trees/JTreeAPIDriver.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/trees/JTreeAPIDriver.java new file mode 100644 index 00000000000..921d50985b0 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/trees/JTreeAPIDriver.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers.trees; + +import javax.swing.text.JTextComponent; + +import org.netbeans.jemmy.Timeout; +import org.netbeans.jemmy.drivers.DriverManager; +import org.netbeans.jemmy.drivers.LightSupportiveDriver; +import org.netbeans.jemmy.drivers.TextDriver; +import org.netbeans.jemmy.drivers.TreeDriver; +import org.netbeans.jemmy.operators.ComponentOperator; +import org.netbeans.jemmy.operators.JTextComponentOperator; +import org.netbeans.jemmy.operators.JTreeOperator; + +/** + * TreeDriver for javax.swing.JTree component type. Uses API calls. + * + * @author Alexandre Iline(alexandre.iline@oracle.com) + */ +public class JTreeAPIDriver extends LightSupportiveDriver implements TreeDriver { + + /** + * Constructs a JTreeAPIDriver. + */ + public JTreeAPIDriver() { + super(new String[]{"org.netbeans.jemmy.operators.JTreeOperator"}); + } + + @Override + public void selectItem(ComponentOperator oper, int index) { + selectItems(oper, new int[]{index}); + } + + @Override + public void selectItems(ComponentOperator oper, int[] indices) { + checkSupported(oper); + ((JTreeOperator) oper).clearSelection(); + ((JTreeOperator) oper).addSelectionRows(indices); + } + + @Override + public void expandItem(ComponentOperator oper, int index) { + checkSupported(oper); + ((JTreeOperator) oper).expandRow(index); + } + + @Override + public void collapseItem(ComponentOperator oper, int index) { + checkSupported(oper); + ((JTreeOperator) oper).collapseRow(index); + } + + @Override + public void editItem(ComponentOperator oper, int index, Object newValue, Timeout waitEditorTime) { + JTextComponentOperator textoper = startEditingAndReturnEditor(oper, index, waitEditorTime); + TextDriver text = DriverManager.getTextDriver(JTextComponentOperator.class); + text.clearText(textoper); + text.typeText(textoper, newValue.toString(), 0); + ((JTreeOperator) oper).stopEditing(); + } + + @Override + public void startEditing(ComponentOperator oper, int index, Timeout waitEditorTime) { + startEditingAndReturnEditor(oper, index, waitEditorTime); + } + + private JTextComponentOperator startEditingAndReturnEditor(ComponentOperator oper, int index, Timeout waitEditorTime) { + checkSupported(oper); + JTreeOperator toper = (JTreeOperator) oper; + toper.startEditingAtPath(toper.getPathForRow(index)); + toper.getTimeouts(). + setTimeout("ComponentOperator.WaitComponentTimeout", waitEditorTime.getValue()); + return (new JTextComponentOperator((JTextComponent) toper. + waitSubComponent(new JTextComponentOperator.JTextComponentFinder()))); + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/trees/JTreeMouseDriver.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/trees/JTreeMouseDriver.java new file mode 100644 index 00000000000..a0368477ee5 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/trees/JTreeMouseDriver.java @@ -0,0 +1,184 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers.trees; + +import java.awt.Point; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +import javax.swing.text.JTextComponent; + +import org.netbeans.jemmy.QueueTool; +import org.netbeans.jemmy.Timeout; +import org.netbeans.jemmy.drivers.DriverManager; +import org.netbeans.jemmy.drivers.LightSupportiveDriver; +import org.netbeans.jemmy.drivers.MouseDriver; +import org.netbeans.jemmy.drivers.TextDriver; +import org.netbeans.jemmy.drivers.TreeDriver; +import org.netbeans.jemmy.operators.ComponentOperator; +import org.netbeans.jemmy.operators.JTextComponentOperator; +import org.netbeans.jemmy.operators.JTreeOperator; +import org.netbeans.jemmy.operators.Operator; + +/** + * TreeDriver for javax.swing.JTree component type. Uses mouse operations. + * + * @author Alexandre Iline(alexandre.iline@oracle.com) + */ +public class JTreeMouseDriver extends LightSupportiveDriver implements TreeDriver { + + QueueTool queueTool; + + /** + * Constructs a JTreeMouseDriver. + */ + public JTreeMouseDriver() { + super(new String[]{"org.netbeans.jemmy.operators.JTreeOperator"}); + queueTool = new QueueTool(); + } + + @Override + public void selectItem(ComponentOperator oper, int index) { + selectItems(oper, new int[]{index}); + } + + @Override + public void selectItems(final ComponentOperator oper, int[] indices) { + ((JTreeOperator) oper).clearSelection(); + checkSupported(oper); + final MouseDriver mdriver = DriverManager.getMouseDriver(oper); + final JTreeOperator toper = (JTreeOperator) oper; + final Timeout clickTime = oper.getTimeouts().create("ComponentOperator.MouseClickTimeout"); + for (int i = 0; i < indices.length; i++) { + final int index = i; + if (!QueueTool.isDispatchThread()) { + toper.scrollToRow(indices[i]); + } + final Point p = toper.getPointToClick(indices[index]); + queueTool.invokeSmoothly(new QueueTool.QueueAction("Path selecting") { + @Override + public Void launch() { + mdriver.clickMouse(oper, p.x, p.y, 1, Operator.getDefaultMouseButton(), + (index == 0) ? 0 : InputEvent.CTRL_MASK, clickTime); + return null; + } + }); + } + //1.5 workaround + if (System.getProperty("java.specification.version").compareTo("1.4") > 0) { + if (!QueueTool.isDispatchThread()) { + queueTool.setOutput(oper.getOutput().createErrorOutput()); + queueTool.waitEmpty(10); + queueTool.waitEmpty(10); + queueTool.waitEmpty(10); + } + } + //end of 1.5 workaround + } + + @Override + public void expandItem(ComponentOperator oper, final int index) { + checkSupported(oper); + final JTreeOperator toper = (JTreeOperator) oper; + final MouseDriver mdriver = DriverManager.getMouseDriver(oper); + if (!toper.isExpanded(index)) { + queueTool.invokeSmoothly(new QueueTool.QueueAction("Path selecting") { + @Override + public Void launch() { + Point p = toper.getPointToClick(index); + mdriver.clickMouse(toper, p.x, p.y, 2, Operator.getDefaultMouseButton(), + 0, toper.getTimeouts(). + create("ComponentOperator.MouseClickTimeout")); + return null; + } + }); + } + } + + @Override + public void collapseItem(ComponentOperator oper, final int index) { + checkSupported(oper); + final JTreeOperator toper = (JTreeOperator) oper; + final MouseDriver mdriver = DriverManager.getMouseDriver(oper); + if (toper.isExpanded(index)) { + queueTool.invokeSmoothly(new QueueTool.QueueAction("Path selecting") { + @Override + public Void launch() { + Point p = toper.getPointToClick(index); + mdriver.clickMouse(toper, p.x, p.y, 2, Operator.getDefaultMouseButton(), + 0, toper.getTimeouts(). + create("ComponentOperator.MouseClickTimeout")); + return null; + } + }); + } + } + + @Override + public void editItem(ComponentOperator oper, int index, Object newValue, Timeout waitEditorTime) { + JTextComponentOperator textoper = startEditingAndReturnEditor(oper, index, waitEditorTime); + final TextDriver text = DriverManager.getTextDriver(JTextComponentOperator.class); + text.clearText(textoper); + text.typeText(textoper, newValue.toString(), 0); + DriverManager.getKeyDriver(oper). + pushKey(textoper, KeyEvent.VK_ENTER, 0, + oper.getTimeouts(). + create("ComponentOperator.PushKeyTimeout")); + } + + @Override + public void startEditing(ComponentOperator oper, int index, Timeout waitEditorTime) { + startEditingAndReturnEditor(oper, index, waitEditorTime); + } + + private JTextComponentOperator startEditingAndReturnEditor(ComponentOperator oper, final int index, Timeout waitEditorTime) { + checkSupported(oper); + final JTreeOperator toper = (JTreeOperator) oper; + final MouseDriver mdriver = DriverManager.getMouseDriver(oper); + queueTool.invokeSmoothly(new QueueTool.QueueAction("Path selecting") { + @Override + public Void launch() { + Point p = toper.getPointToClick(index); + mdriver.clickMouse(toper, p.x, p.y, 1, Operator.getDefaultMouseButton(), + 0, toper.getTimeouts(). + create("ComponentOperator.MouseClickTimeout")); + return null; + } + }); + oper.getTimeouts().sleep("JTreeOperator.BeforeEditTimeout"); + queueTool.invokeSmoothly(new QueueTool.QueueAction("Path selecting") { + @Override + public Void launch() { + Point p = toper.getPointToClick(index); + mdriver.clickMouse(toper, p.x, p.y, 1, Operator.getDefaultMouseButton(), + 0, toper.getTimeouts(). + create("ComponentOperator.MouseClickTimeout")); + return null; + } + }); + toper.getTimeouts(). + setTimeout("ComponentOperator.WaitComponentTimeout", waitEditorTime.getValue()); + return (new JTextComponentOperator((JTextComponent) toper. + waitSubComponent(new JTextComponentOperator.JTextComponentFinder()))); + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/trees/package-info.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/trees/package-info.java new file mode 100644 index 00000000000..64c6d892785 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/trees/package-info.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +/** + *

Tree drivers

+ * Different drivers to perform a tree operations.
+ * + * @since 17 Apr 2002 + *
+ */ +package org.netbeans.jemmy.drivers.trees; diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/windows/DefaultFrameDriver.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/windows/DefaultFrameDriver.java new file mode 100644 index 00000000000..055bc872f05 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/windows/DefaultFrameDriver.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers.windows; + +import java.awt.Dimension; +import java.awt.Frame; +import java.awt.Toolkit; +import java.awt.Window; +import java.awt.event.WindowEvent; + +import org.netbeans.jemmy.drivers.FrameDriver; +import org.netbeans.jemmy.drivers.LightSupportiveDriver; +import org.netbeans.jemmy.drivers.input.EventDriver; +import org.netbeans.jemmy.operators.ComponentOperator; +import org.netbeans.jemmy.operators.FrameOperator; + +public class DefaultFrameDriver extends LightSupportiveDriver implements FrameDriver { + + EventDriver eDriver; + + public DefaultFrameDriver() { + super(new String[]{"org.netbeans.jemmy.operators.FrameOperator"}); + eDriver = new EventDriver(); + } + + @Override + public void iconify(ComponentOperator oper) { + checkSupported(oper); + eDriver.dispatchEvent(oper.getSource(), + new WindowEvent((Window) oper.getSource(), + WindowEvent.WINDOW_ICONIFIED)); + ((FrameOperator) oper).setState(Frame.ICONIFIED); + } + + @Override + public void deiconify(ComponentOperator oper) { + checkSupported(oper); + eDriver.dispatchEvent(oper.getSource(), + new WindowEvent((Window) oper.getSource(), + WindowEvent.WINDOW_DEICONIFIED)); + ((FrameOperator) oper).setState(Frame.NORMAL); + } + + @Override + public void maximize(ComponentOperator oper) { + checkSupported(oper); + oper.setLocation(0, 0); + Dimension ssize = Toolkit.getDefaultToolkit().getScreenSize(); + oper.setSize(ssize.width, ssize.height); + } + + @Override + public void demaximize(ComponentOperator oper) { + checkSupported(oper); + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/windows/DefaultInternalFrameDriver.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/windows/DefaultInternalFrameDriver.java new file mode 100644 index 00000000000..27421ab9b4d --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/windows/DefaultInternalFrameDriver.java @@ -0,0 +1,150 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers.windows; + +import java.awt.Component; +import java.awt.Container; + +import javax.swing.plaf.basic.BasicInternalFrameTitlePane; + +import org.netbeans.jemmy.ComponentChooser; +import org.netbeans.jemmy.ComponentSearcher; +import org.netbeans.jemmy.drivers.FrameDriver; +import org.netbeans.jemmy.drivers.InternalFrameDriver; +import org.netbeans.jemmy.drivers.LightSupportiveDriver; +import org.netbeans.jemmy.drivers.WindowDriver; +import org.netbeans.jemmy.operators.ComponentOperator; +import org.netbeans.jemmy.operators.JInternalFrameOperator; + +public class DefaultInternalFrameDriver extends LightSupportiveDriver + implements WindowDriver, FrameDriver, InternalFrameDriver { + + public DefaultInternalFrameDriver() { + super(new String[]{"org.netbeans.jemmy.operators.JInternalFrameOperator"}); + } + + @Override + public void activate(ComponentOperator oper) { + checkSupported(oper); + ((JInternalFrameOperator) oper).moveToFront(); + ((JInternalFrameOperator) oper).getTitleOperator().clickMouse(); + } + + @Override + public void requestClose(ComponentOperator oper) { + checkSupported(oper); + ((JInternalFrameOperator) oper).moveToFront(); + ((JInternalFrameOperator) oper).getCloseButton().push(); + } + + @Override + public void requestCloseAndThenHide(ComponentOperator oper) { + throw new UnsupportedOperationException(); + } + + @Override + @Deprecated + public void close(ComponentOperator oper) { + requestClose(oper); + } + + @Override + public void move(ComponentOperator oper, int x, int y) { + checkSupported(oper); + ComponentOperator titleOperator = ((JInternalFrameOperator) oper).getTitleOperator(); + titleOperator.dragNDrop(titleOperator.getCenterY(), + titleOperator.getCenterY(), + x - oper.getX() + titleOperator.getCenterY(), + y - oper.getY() + titleOperator.getCenterY()); + } + + @Override + public void resize(ComponentOperator oper, int width, int height) { + checkSupported(oper); + oper. + dragNDrop(oper.getWidth() - 1, + oper.getHeight() - 1, + width - 1, + height - 1); + } + + @Override + public void iconify(ComponentOperator oper) { + checkSupported(oper); + ((JInternalFrameOperator) oper).getMinimizeButton().clickMouse(); + } + + @Override + public void deiconify(ComponentOperator oper) { + checkSupported(oper); + ((JInternalFrameOperator) oper).getIconOperator().pushButton(); + } + + @Override + public void maximize(ComponentOperator oper) { + checkSupported(oper); + if (!((JInternalFrameOperator) oper).isMaximum()) { + if (!((JInternalFrameOperator) oper).isSelected()) { + activate(oper); + } + ((JInternalFrameOperator) oper).getMaximizeButton().push(); + } + } + + @Override + public void demaximize(ComponentOperator oper) { + checkSupported(oper); + if (((JInternalFrameOperator) oper).isMaximum()) { + if (!((JInternalFrameOperator) oper).isSelected()) { + activate(oper); + } + ((JInternalFrameOperator) oper).getMaximizeButton().push(); + } + } + + @Override + public Component getTitlePane(ComponentOperator operator) { + ComponentSearcher cs = new ComponentSearcher((Container) operator.getSource()); + cs.setOutput(operator.getOutput().createErrorOutput()); + return (cs.findComponent(new ComponentChooser() { + @Override + public boolean checkComponent(Component comp) { + if (System.getProperty("java.version").startsWith("1.2")) { + return comp.getClass().getName().endsWith("InternalFrameTitlePane"); + } else { + return comp instanceof BasicInternalFrameTitlePane; + } + } + + @Override + public String getDescription() { + return "Title pane"; + } + + @Override + public String toString() { + return "getTitlePane.ComponentChooser{description = " + getDescription() + '}'; + } + })); + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/windows/DefaultWindowDriver.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/windows/DefaultWindowDriver.java new file mode 100644 index 00000000000..238d053ba2b --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/windows/DefaultWindowDriver.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.drivers.windows; + +import java.awt.Window; +import java.awt.event.ComponentEvent; +import java.awt.event.FocusEvent; +import java.awt.event.WindowEvent; + +import org.netbeans.jemmy.drivers.LightSupportiveDriver; +import org.netbeans.jemmy.drivers.WindowDriver; +import org.netbeans.jemmy.drivers.input.EventDriver; +import org.netbeans.jemmy.operators.ComponentOperator; +import org.netbeans.jemmy.operators.WindowOperator; + +public class DefaultWindowDriver extends LightSupportiveDriver implements WindowDriver { + + EventDriver eDriver; + + public DefaultWindowDriver() { + super(new String[]{"org.netbeans.jemmy.operators.WindowOperator"}); + eDriver = new EventDriver(); + } + + @Override + public void activate(ComponentOperator oper) { + checkSupported(oper); + if (((WindowOperator) oper).getFocusOwner() == null) { + ((WindowOperator) oper).toFront(); + } + eDriver.dispatchEvent(oper.getSource(), + new WindowEvent((Window) oper.getSource(), + WindowEvent.WINDOW_ACTIVATED)); + eDriver.dispatchEvent(oper.getSource(), + new FocusEvent(oper.getSource(), + FocusEvent.FOCUS_GAINED)); + } + + @Override + public void requestClose(ComponentOperator oper) { + checkSupported(oper); + eDriver.dispatchEvent(oper.getSource(), + new WindowEvent((Window) oper.getSource(), + WindowEvent.WINDOW_CLOSING)); + } + + @Override + public void requestCloseAndThenHide(ComponentOperator oper) { + requestClose(oper); + oper.setVisible(false); + } + + @Override + @Deprecated + public void close(ComponentOperator oper) { + requestClose(oper); + } + + @Override + public void move(ComponentOperator oper, int x, int y) { + checkSupported(oper); + oper.setLocation(x, y); + } + + @Override + public void resize(ComponentOperator oper, int width, int height) { + checkSupported(oper); + oper.setSize(width, height); + eDriver.dispatchEvent(oper.getSource(), + new ComponentEvent(oper.getSource(), + ComponentEvent.COMPONENT_RESIZED)); + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/windows/package-info.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/windows/package-info.java new file mode 100644 index 00000000000..0432f2e5e0e --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/drivers/windows/package-info.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +/** + *

Window drivers.

+ * Different drivers to perform a window operations.
+ *
+ */ +package org.netbeans.jemmy.drivers.windows; diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/explorer/GUIBrowser.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/explorer/GUIBrowser.java new file mode 100644 index 00000000000..0166f7e97d7 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/explorer/GUIBrowser.java @@ -0,0 +1,1619 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.explorer; + +import java.awt.AWTEvent; +import java.awt.AWTException; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Rectangle; +import java.awt.Robot; +import java.awt.Window; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.ComponentEvent; +import java.awt.event.ComponentListener; +import java.awt.event.WindowEvent; +import java.awt.event.WindowListener; +import java.awt.image.BufferedImage; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.Hashtable; +import java.util.Properties; +import java.util.Vector; + +import javax.swing.DefaultListModel; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JComboBox; +import javax.swing.JDialog; +import javax.swing.JFileChooser; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JSplitPane; +import javax.swing.JTabbedPane; +import javax.swing.JTable; +import javax.swing.JTextArea; +import javax.swing.JTextField; +import javax.swing.JTree; +import javax.swing.ListCellRenderer; +import javax.swing.border.BevelBorder; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; +import javax.swing.event.TreeModelListener; +import javax.swing.event.TreeSelectionEvent; +import javax.swing.event.TreeSelectionListener; +import javax.swing.filechooser.FileFilter; +import javax.swing.table.DefaultTableModel; +import javax.swing.tree.TreeCellRenderer; +import javax.swing.tree.TreeModel; +import javax.swing.tree.TreePath; + +import org.netbeans.jemmy.ClassReference; +import org.netbeans.jemmy.QueueTool; +import org.netbeans.jemmy.TestOut; +import org.netbeans.jemmy.operators.Operator; +import org.netbeans.jemmy.util.Dumper; + +/** + * An application allowing to explore a Java GUI application. Could be executed + * by command:
+ *
+ * java "application java options" \
+ *   org.netbeans.jemmy.explorer.GUIBrowser \
+ *   "application main class" \
+ *   "application parameters"
+ * 
or from java code by {@code GUIBrowser.showBrowser()} method + * using. + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + */ +public class GUIBrowser extends JFrame { + + private static String WINDOWS_TAB = "Subwindows"; + private static String COMPONENTS_TAB = "Hierarchy"; + private static String PROPERTIES_TAB = "Properties"; + private static String REFLECTION_TAB = "Reflection"; + private static String EVENT_TAB = "Events"; + private static String IMAGE_TAB = "Image"; + + boolean exit; + PropertyDialog propDialog; + RootNode root; + QueueTool qt; + JTextField refreshDelay; + JTree mainTree; + JLabel status; + JButton viewButton; + JButton expandButton; + JSplitPane split; + ByteArrayOutputStream dumpData; + PrintWriter dumpWriter; + + private GUIBrowser(boolean exitNecessary) { + super("GUI Browser"); + + dumpData = new ByteArrayOutputStream(); + dumpWriter = new PrintWriter(new OutputStreamWriter(dumpData)); + + exit = exitNecessary; + propDialog = new PropertyDialog(this); + qt = new QueueTool(); + qt.setOutput(TestOut.getNullOutput()); + root = new RootNode(); + + mainTree = new JTree(root.getWindowModel()); + mainTree.setCellRenderer(new WindowRenderer()); + mainTree.setEditable(false); + + refreshDelay = new JTextField(3); + refreshDelay.setText("0"); + + viewButton = new JButton("View"); + viewButton.setEnabled(false); + viewButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + new ComponentBrowser(getOwnr(), + (ComponentNode) mainTree.getSelectionPath(). + getLastPathComponent()). + setVisible(true); + } + }); + + expandButton = new JButton("Expand All"); + expandButton.setEnabled(false); + expandButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + expandAll(mainTree, + mainTree.getSelectionPath()); + } + }); + + JButton refreshButton = new JButton("Reload in ..."); + refreshButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + reload(Integer.parseInt(refreshDelay.getText())); + } + }); + + JButton dumpButton = new JButton("Dump"); + dumpButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + JFileChooser chooser + = new JFileChooser(System.getProperty("user.home")); + chooser.addChoosableFileFilter(new FileFilter() { + @Override + public boolean accept(File f) { + return f.getName().endsWith(".xml"); + } + + @Override + public String getDescription() { + return "xml files"; + } + + @Override + public String toString() { + return "dumpButton.ActionListener{description = " + getDescription() + '}'; + } + }); + chooser.showSaveDialog(GUIBrowser.this); + try { + File file = chooser.getSelectedFile(); + try (OutputStream output = new FileOutputStream(file)) { + output.write(dumpData.toByteArray()); + } + } catch (IOException ee) { + ee.printStackTrace(); + } + } + }); + + JPanel refreshPane = new JPanel(); + refreshPane.add(viewButton); + refreshPane.add(expandButton); + refreshPane.add(new JLabel("")); + refreshPane.add(new JLabel("")); + refreshPane.add(new JLabel("")); + refreshPane.add(new JLabel("")); + refreshPane.add(new JLabel("")); + refreshPane.add(new JLabel("")); + refreshPane.add(refreshButton); + refreshPane.add(refreshDelay); + refreshPane.add(new JLabel("seconds ")); + refreshPane.add(dumpButton); + + split = createUnderPane(mainTree); + + JPanel nonStatusPane = new JPanel(); + nonStatusPane.setLayout(new BorderLayout()); + nonStatusPane.add(refreshPane, BorderLayout.SOUTH); + nonStatusPane.add(split, BorderLayout.CENTER); + + split.setDividerLocation(0.8); + + status = new JLabel("Reloaded"); + + JPanel statusPane = new JPanel(); + statusPane.setLayout(new BorderLayout()); + statusPane.add(status, BorderLayout.CENTER); + statusPane.setBorder(new BevelBorder(BevelBorder.LOWERED)); + + JPanel bottomPane = new JPanel(); + bottomPane.setLayout(new BorderLayout()); + bottomPane.add(statusPane, BorderLayout.NORTH); + bottomPane.add(statusPane, BorderLayout.CENTER); + + getContentPane().setLayout(new BorderLayout()); + getContentPane().add(bottomPane, BorderLayout.SOUTH); + getContentPane().add(nonStatusPane, BorderLayout.CENTER); + + Component[] cpss = {viewButton, expandButton}; + mainTree.addTreeSelectionListener(new SelectionManager(cpss)); + + addWindowListener(new WindowListener() { + @Override + public void windowActivated(WindowEvent e) { + } + + @Override + public void windowClosed(WindowEvent e) { + setVisible(false); + if (exit) { + System.exit(0); + } + } + + @Override + public void windowClosing(WindowEvent e) { + } + + @Override + public void windowDeactivated(WindowEvent e) { + } + + @Override + public void windowDeiconified(WindowEvent e) { + } + + @Override + public void windowIconified(WindowEvent e) { + } + + @Override + public void windowOpened(WindowEvent e) { + } + }); + addComponentListener(new ComponentListener() { + @Override + public void componentHidden(ComponentEvent e) { + } + + @Override + public void componentMoved(ComponentEvent e) { + } + + @Override + public void componentResized(ComponentEvent e) { + split.setDividerLocation(0.8); + } + + @Override + public void componentShown(ComponentEvent e) { + } + }); + + setSize(800, 400); + } + + boolean shown = false; + + @Override + @Deprecated + public void show() { + super.show(); + viewButton.setEnabled(false); + if (!shown) { + split.setDividerLocation(0.8); + shown = true; + } + } + + /** + * Specifies a status text. + * + * @param st a status text. + */ + public void setStatus(String st) { + status.setText(st); + } + + /** + * Method to invoke GUIBrowser from java code. + */ + public static void showBrowser() { + showBrowser(new String[0], false); + } + + /** + * Method to invoke GUIBrowser as java application. + * + * @param argv Argument array. If not empty, first element should be
+ * main class of an aplication to be browsed.
+ * Other elements are application parameters. + */ + public static void main(String[] argv) { + showBrowser(argv, true); + } + + private static void showBrowser(String[] argv, boolean exitNecessary) { + if (argv.length >= 1) { + String[] newArgv = new String[argv.length - 1]; + System.arraycopy(argv, 1, newArgv, 0, argv.length - 1); + try { + new ClassReference(argv[0]).startApplication(newArgv); + } catch (ClassNotFoundException + | InvocationTargetException + | NoSuchMethodException e) { + e.printStackTrace(); + return; + } + } + new GUIBrowser(exitNecessary).show(); + } + + private void reload(final int delay) { + viewButton.setEnabled(false); + expandButton.setEnabled(false); + new Thread(new Runnable() { + @Override + public void run() { + try { + for (int i = delay - 1; i >= 0; i--) { + setStatus("Reloading after " + Integer.toString(i) + " second"); + Thread.sleep(1000); + } + setStatus("Reloading ..."); + Dumper.dumpAll(dumpWriter); + //qt.lock(); + root = new RootNode(); + //qt.unlock(); + mainTree.setModel(root.getWindowModel()); + setStatus("Reloaded"); + } catch (InterruptedException ignored) { + } + } + }).start(); + } + + private JFrame getOwnr() { + return this; + } + + private class ClassNode { + + Class clzz; + + protected ClassNode() { + clzz = null; + } + + public ClassNode(Class clzz) { + this.clzz = clzz; + } + + public ClassNode[] getSuperClasses() { + int count = 0; + Class parent = clzz; + while ((parent = parent.getSuperclass()) != null) { + count++; + } + Class[] interfaces = clzz.getInterfaces(); + ClassNode[] result = new ClassNode[count + interfaces.length + 1]; + result[0] = new SuperClassNode(clzz); + count = 1; + parent = clzz; + while ((parent = parent.getSuperclass()) != null) { + result[count] = new SuperClassNode(parent); + count++; + } + for (int i = count; i < count + interfaces.length; i++) { + result[i] = new InterfaceNode(interfaces[i - count]); + } + return result; + } + + public String toString() { + if (clzz.isArray()) { + return clzz.getComponentType().getName() + "[]"; + } else { + return clzz.getName(); + } + } + + public TreeModel getMethodsModel() { + return new ClassModel(this); + } + + public ClassNode[] getSubNodes() { + Vector res = new Vector<>(); + Field[] fields = clzz.getFields(); + Arrays.sort(fields, new Comparator() { + @Override + public int compare(Field f1, Field f2) { + return f1.getName().compareTo(f2.getName()); + } + }); + Method[] mtds = clzz.getMethods(); + Arrays.sort(mtds, new Comparator() { + @Override + public int compare(Method m1, Method m2) { + return m1.getName().compareTo(m2.getName()); + } + }); + for (Field field : fields) { + if (field.getDeclaringClass().getName().equals(clzz.getName())) { + res.add(new FieldNode(field)); + } + } + for (Method mtd : mtds) { + if (mtd.getDeclaringClass().getName().equals(clzz.getName())) { + res.add(new MethodNode(mtd)); + } + } + ClassNode[] result = new ClassNode[res.size()]; + for (int i = 0; i < result.length; i++) { + result[i] = res.get(i); + } + return result; + } + } + + private class SuperClassNode extends ClassNode { + + public SuperClassNode(Class clzz) { + super(clzz); + } + + public String toString() { + return "Class: " + super.toString(); + } + } + + private class InterfaceNode extends ClassNode { + + public InterfaceNode(Class clzz) { + super(clzz); + } + + public String toString() { + return "Interfac: " + super.toString(); + } + } + + private class FieldNode extends ClassNode { + + Field field; + + public FieldNode(Field fld) { + super(fld.getType()); + field = fld; + } + + public String toString() { + return ("Field: " + + Modifier.toString(field.getModifiers()) + " " + + super.toString() + " " + + field.getName()); + } + } + + private class MethodNode extends ClassNode { + + Method method; + + public MethodNode(Method mtd) { + super(mtd.getReturnType()); + method = mtd; + } + + public String toString() { + return ("Method: " + + Modifier.toString(method.getModifiers()) + " " + + super.toString() + " " + + method.getName()); + + } + + public ClassNode[] getParameters() { + Class[] ptps = method.getParameterTypes(); + ClassNode[] result = new ClassNode[ptps.length]; + for (int i = 0; i < ptps.length; i++) { + result[i] = new ClassNode(ptps[i]); + } + return result; + } + } + + private class ComponentNode extends ClassNode { + + protected Hashtable props; + protected String clss = ""; + protected String compToString = ""; + Component comp; + ComponentImageProvider image; + protected int x, y, w, h; + + protected ComponentNode() { + props = new Hashtable<>(); + } + + public ComponentNode(Component comp) { + super(comp.getClass()); + try { + props = Operator.createOperator(comp).getDump(); + } catch (Exception e) { + props = new Hashtable<>(); + } + clss = comp.getClass().getName(); + compToString = comp.toString(); + this.comp = comp; + x = comp.getLocationOnScreen().x; + y = comp.getLocationOnScreen().y; + w = comp.getWidth(); + h = comp.getHeight(); + } + + public String toString() { + return clss; + } + + public Hashtable getProperties() { + return props; + } + + public String getToString() { + return compToString; + } + + public void setComponentImageProvider(ComponentImageProvider provider) { + image = provider; + } + + public BufferedImage getImage() { + if (image != null) { + return image.getImage(x, y, w, h); + } else { + return null; + } + } + } + + private class ContainerNode extends ComponentNode { + + protected ComponentNode[] comps; + + protected ContainerNode() { + super(); + comps = new ComponentNode[0]; + } + + public ContainerNode(Container comp) { + super(comp); + Component[] cmps = comp.getComponents(); + Vector wwns = new Vector<>(); + for (Component cmp : cmps) { + if (cmp != null && (propDialog.showAll || cmp.isVisible())) { + if (cmp instanceof Container) { + wwns.add(new ContainerNode((Container) cmp)); + } else { + wwns.add(new ComponentNode(cmp)); + } + } + } + comps = new ComponentNode[wwns.size()]; + for (int i = 0; i < wwns.size(); i++) { + comps[i] = wwns.get(i); + } + } + + public ComponentNode[] getComponents() { + return comps; + } + + public int getComponentIndex(ComponentNode comp) { + for (int i = 0; i < comps.length; i++) { + if (comps[i].equals(comp)) { + return i; + } + } + return -1; + } + + @Override + public void setComponentImageProvider(ComponentImageProvider provider) { + super.setComponentImageProvider(provider); + for (ComponentNode comp1 : comps) { + comp1.setComponentImageProvider(provider); + } + } + + public ComponentModel getComponentModel() { + return new ComponentModel(this); + } + } + + private class WindowNode extends ContainerNode { + + protected WindowNode[] wins; + String title; + + protected WindowNode() { + super(); + wins = new WindowNode[0]; + title = "All Frames"; + clss = ""; + } + + public WindowNode(Window win) { + super(win); + Window[] wns = win.getOwnedWindows(); + Vector wwns = new Vector<>(); + for (Window wn : wns) { + if (propDialog.showAll || wn.isVisible()) { + wwns.add(new WindowNode(wn)); + } + } + wins = new WindowNode[wwns.size()]; + for (int i = 0; i < wwns.size(); i++) { + wins[i] = wwns.get(i); + } + title = win.toString(); + clss = win.getClass().getName(); + BufferedImage image = null; + try { + image = new Robot(). + createScreenCapture(new Rectangle(win.getLocationOnScreen(), + win.getSize())); + } catch (AWTException e) { + e.printStackTrace(); + } + setComponentImageProvider(new ComponentImageProvider(image, x, y)); + } + + public WindowNode[] getWindows() { + return wins; + } + + public int getWindowIndex(WindowNode node) { + for (int i = 0; i < wins.length; i++) { + if (wins[i].equals(node)) { + return i; + } + } + return -1; + } + + public WindowModel getWindowModel() { + return new WindowModel(this); + } + + public String toString() { + return clss + " \"" + title + "\""; + } + } + + private class RootNode extends WindowNode { + + public RootNode() { + super(); + Window[] wns = Frame.getFrames(); + wins = new WindowNode[wns.length]; + int count = 0; + for (Window wn1 : wns) { + if (propDialog.showAll || wn1.isVisible()) { + count++; + } + } + wins = new WindowNode[count]; + count = 0; + for (Window wn : wns) { + if (propDialog.showAll || wn.isVisible()) { + wins[count] = new WindowNode(wn); + count++; + } + } + } + } + + private static class ClassModel implements TreeModel { + + ClassNode clsn; + + ClassModel(ClassNode clsn) { + this.clsn = clsn; + } + + @Override + public void addTreeModelListener(TreeModelListener l) { + } + + @Override + public Object getChild(Object parent, int index) { + if (parent == clsn) { + return clsn.getSuperClasses()[index]; + } else if (parent instanceof SuperClassNode + || parent instanceof InterfaceNode) { + return ((ClassNode) parent).getSubNodes()[index]; + } else if (parent instanceof MethodNode) { + return ((MethodNode) parent).getParameters()[index]; + } + return null; + } + + @Override + public int getChildCount(Object parent) { + if (parent == clsn) { + return clsn.getSuperClasses().length; + } else if (parent instanceof SuperClassNode + || parent instanceof InterfaceNode) { + return ((ClassNode) parent).getSubNodes().length; + } else if (parent instanceof MethodNode) { + return ((MethodNode) parent).getParameters().length; + } + return 0; + } + + @Override + public int getIndexOfChild(Object parent, Object child) { + if (parent == clsn + || parent instanceof MethodNode + || parent instanceof SuperClassNode + || parent instanceof InterfaceNode) { + Object[] children; + if (parent instanceof SuperClassNode + || parent instanceof InterfaceNode) { + children = ((ClassNode) parent).getSuperClasses(); + } else if (parent instanceof MethodNode) { + children = ((MethodNode) parent).getParameters(); + } else { + children = clsn.getSuperClasses(); + } + for (int i = 0; i < children.length; i++) { + if (children.equals(child)) { + return i; + } + } + } + return 0; + } + + @Override + public Object getRoot() { + return clsn; + } + + @Override + public boolean isLeaf(Object node) { + return getChildCount(node) == 0; + } + + @Override + public void removeTreeModelListener(TreeModelListener l) { + } + + @Override + public void valueForPathChanged(TreePath path, Object newValue) { + } + } + + private static class WindowModel implements TreeModel { + + WindowNode win; + + WindowModel(WindowNode win) { + this.win = win; + } + + @Override + public void addTreeModelListener(TreeModelListener l) { + } + + @Override + public Object getChild(Object parent, int index) { + return ((WindowNode) parent).getWindows()[index]; + } + + @Override + public int getChildCount(Object parent) { + return ((WindowNode) parent).getWindows().length; + } + + @Override + public int getIndexOfChild(Object parent, Object child) { + return ((WindowNode) parent).getWindowIndex((WindowNode) child); + } + + @Override + public Object getRoot() { + return win; + } + + @Override + public boolean isLeaf(Object node) { + return ((WindowNode) node).getWindows().length == 0; + } + + @Override + public void removeTreeModelListener(TreeModelListener l) { + } + + @Override + public void valueForPathChanged(TreePath path, Object newValue) { + } + } + + private static class ComponentModel implements TreeModel { + + ContainerNode cont; + + ComponentModel(ContainerNode cont) { + this.cont = cont; + } + + @Override + public void addTreeModelListener(TreeModelListener l) { + } + + @Override + public Object getChild(Object parent, int index) { + if (parent instanceof ContainerNode) { + return ((ContainerNode) parent).getComponents()[index]; + } else { + return ""; + } + } + + @Override + public int getChildCount(Object parent) { + if (parent instanceof ContainerNode) { + return ((ContainerNode) parent).getComponents().length; + } else { + return 0; + } + } + + @Override + public int getIndexOfChild(Object parent, Object child) { + if (parent instanceof ContainerNode) { + return ((ContainerNode) parent).getComponentIndex((ComponentNode) child); + } else { + return -1; + } + } + + @Override + public Object getRoot() { + return cont; + } + + @Override + public boolean isLeaf(Object node) { + if (node instanceof ContainerNode) { + return ((ContainerNode) node).getComponents().length == 0; + } else { + return true; + } + } + + @Override + public void removeTreeModelListener(TreeModelListener l) { + } + + @Override + public void valueForPathChanged(TreePath path, Object newValue) { + } + } + + private static class WindowRenderer implements TreeCellRenderer, ListCellRenderer { + + public WindowRenderer() { + } + + @Override + public Component getTreeCellRendererComponent(JTree tree, + Object value, + boolean selected, + boolean expanded, + boolean leaf, + int row, + boolean hasFocus) { + return get((ClassNode) value, selected); + } + + @Override + public Component getListCellRendererComponent(JList list, + ClassNode value, + int index, + boolean isSelected, + boolean cellHasFocus) { + return get(value, isSelected); + } + + private Component get(ClassNode value, boolean selected) { + Component result = new JLabel(value.toString()); + if (selected) { + result.setBackground(Color.blue); + result.setForeground(Color.white); + JPanel resPane = new JPanel(); + resPane.setLayout(new BorderLayout()); + resPane.add(result, BorderLayout.CENTER); + return resPane; + } else { + return result; + } + } + } + + private static class PropertyDialog extends JDialog { + + public boolean showAll = false; + JComboBox visibleCombo; + JCheckBox showToString; + JCheckBox showReflection; + JCheckBox showEvents; + DefaultListModel viewTabs; + JList orderList; + JButton up; + JButton down; + Properties props; + File propFile; + + public PropertyDialog(JFrame owner) { + super(owner, "Properties", true); + + propFile = new File(System.getProperty("user.home") + + System.getProperty("file.separator") + + ".guibrowser"); + + props = new Properties(); + + String[] cpItems = {"Showing", "All"}; + visibleCombo = new JComboBox<>(cpItems); + visibleCombo.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + showAll = (visibleCombo.getSelectedIndex() == 1); + } + }); + + JPanel visiblePane = new JPanel(); + visiblePane.add(new JLabel("Show ")); + visiblePane.add(visibleCombo); + + showToString = new JCheckBox("Show toString() method result"); + showToString.setSelected(true); + + JPanel compTreePane = new JPanel(); + compTreePane.add(visiblePane); + + viewTabs = new DefaultListModel<>(); + viewTabs.addElement(WINDOWS_TAB); + viewTabs.addElement(COMPONENTS_TAB); + viewTabs.addElement(PROPERTIES_TAB); + viewTabs.addElement(REFLECTION_TAB); + viewTabs.addElement(IMAGE_TAB); + + orderList = new JList<>(viewTabs); + orderList.addListSelectionListener(new ListSelectionListener() { + @Override + public void valueChanged(ListSelectionEvent e) { + up.setEnabled(orderList.getSelectedIndex() > -1); + down.setEnabled(orderList.getSelectedIndex() > -1); + } + }); + + showReflection = new JCheckBox("Show reflection tab"); + showReflection.setSelected(true); + showReflection.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + if (showReflection.isSelected()) { + viewTabs.addElement(REFLECTION_TAB); + } else { + viewTabs.remove(viewTabs.indexOf(REFLECTION_TAB)); + } + up.setEnabled(orderList.getSelectedIndex() > -1); + down.setEnabled(orderList.getSelectedIndex() > -1); + } + }); + + showEvents = new JCheckBox("Show event tab"); + showEvents.setSelected(true); + showEvents.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + if (showEvents.isSelected()) { + viewTabs.addElement(EVENT_TAB); + } else { + viewTabs.remove(viewTabs.indexOf(EVENT_TAB)); + } + up.setEnabled(orderList.getSelectedIndex() > -1); + down.setEnabled(orderList.getSelectedIndex() > -1); + } + }); + + up = new JButton("Move Up"); + up.setEnabled(false); + up.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + int index = orderList.getSelectedIndex(); + if (index > 0) { + viewTabs.add(index - 1, + viewTabs.remove(index)); + orderList.setSelectedIndex(index - 1); + } + } + }); + + down = new JButton("Move Down"); + down.setEnabled(false); + down.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + int index = orderList.getSelectedIndex(); + if (index < viewTabs.size() - 1) { + viewTabs.add(index + 1, + viewTabs.remove(index)); + orderList.setSelectedIndex(index + 1); + } + } + }); + + JPanel movePane = new JPanel(); + movePane.add(showEvents); + movePane.add(showReflection); + movePane.add(up); + movePane.add(down); + + JPanel compViewPane = new JPanel(); + compViewPane.setLayout(new BorderLayout()); + compViewPane.add(new JLabel("Tab order:"), BorderLayout.NORTH); + compViewPane.add(movePane, BorderLayout.SOUTH); + compViewPane.add(orderList, BorderLayout.CENTER); + + JTabbedPane tbpn = new JTabbedPane(); + tbpn.add("Component Tree", compTreePane); + tbpn.add("Component View", compViewPane); + + JButton okBUtton = new JButton("OK"); + okBUtton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + save(); + setVisible(false); + } + }); + + JButton cnBUtton = new JButton("Cancel"); + cnBUtton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + setVisible(false); + load(); + } + }); + + JPanel pn = new JPanel(); + pn.add(okBUtton); + pn.add(cnBUtton); + + getContentPane().setLayout(new BorderLayout()); + getContentPane().add(pn, BorderLayout.SOUTH); + getContentPane().add(tbpn, BorderLayout.CENTER); + + load(); + + setSize(400, 400); + } + + private void save() { + try { + props.setProperty("guibrowser.showall", + showAll ? "on" : "off"); + for (int i = 0; i < viewTabs.size(); i++) { + props.setProperty("guibrowser.viewpage_" + Integer.toString(i), + viewTabs.elementAt(i)); + } + try (FileOutputStream fileOutputStream = new FileOutputStream(propFile)) { + props.store(fileOutputStream, + "Jemmy GUIBrowser"); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + + private void load() { + if (propFile.exists()) { + try { + try (FileInputStream fileInputStream = new FileInputStream(propFile)) { + props.load(fileInputStream); + } + showAll + = props.getProperty("guibrowser.showall") == null + || props.getProperty("guibrowser.showall").equals("") + || props.getProperty("guibrowser.showall").equals("on"); + visibleCombo.setSelectedIndex(showAll ? 1 : 0); + if (props.getProperty("guibrowser.viewpage_0") != null + && !props.getProperty("guibrowser.viewpage_0").equals("")) { + viewTabs.removeAllElements(); + viewTabs.addElement(props.getProperty("guibrowser.viewpage_0")); + viewTabs.addElement(props.getProperty("guibrowser.viewpage_1")); + viewTabs.addElement(props.getProperty("guibrowser.viewpage_2")); + if (props.getProperty("guibrowser.viewpage_3") != null + && !props.getProperty("guibrowser.viewpage_3").equals("")) { + viewTabs.addElement(props.getProperty("guibrowser.viewpage_3")); + } + if (props.getProperty("guibrowser.viewpage_4") != null + && !props.getProperty("guibrowser.viewpage_4").equals("")) { + viewTabs.addElement(props.getProperty("guibrowser.viewpage_4")); + } + } + } catch (IOException e) { + e.printStackTrace(); + } + showReflection.setSelected(viewTabs.indexOf(REFLECTION_TAB) > -1); + showEvents.setSelected(viewTabs.indexOf(EVENT_TAB) > -1); + } + } + } + + private class ComponentBrowser extends JFrame { + + JTree winTree; + JTree componentTree; + JTree methodTree; + ClassNode compNode; + JTabbedPane tbd; + JButton viewButton; + JButton expandButton; + JSplitPane winSplit = null; + JSplitPane componentSplit = null; + WindowRenderer renderer; + SelectionManager selManager; + JList eventList; + ListListener listListener; + DefaultListModel eventModel; + JCheckBox mouseEvents; + JCheckBox mouseMotionEvents; + JCheckBox keyEvents; + + public ComponentBrowser(JFrame owner, ClassNode componentNode) { + super("Component " + componentNode.toString()); + fill(componentNode); + } + + private void fill(ClassNode componentNode) { + compNode = componentNode; + + viewButton = new JButton("View"); + viewButton.setEnabled(false); + viewButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + new ComponentBrowser(getOwnr(), + getSelectedNode()). + setVisible(true); + } + }); + + expandButton = new JButton("Expand All"); + expandButton.setEnabled(false); + expandButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + expandAll(getSelectedTree(), + getSelectionPath()); + } + }); + + JPanel buttonPane = new JPanel(); + buttonPane.add(viewButton); + buttonPane.add(expandButton); + + Component[] cpss = {viewButton, expandButton}; + selManager = new SelectionManager(cpss); + renderer = new WindowRenderer(); + + tbd = new JTabbedPane(); + + for (int i = 0; i < propDialog.viewTabs.size(); i++) { + String next = propDialog.viewTabs.elementAt(i); + if (next.equals(PROPERTIES_TAB)) { + addPropertiesTab(); + } else if (next.equals(WINDOWS_TAB)) { + addWindowTab(); + } else if (next.equals(COMPONENTS_TAB)) { + addComponentTab(); + } else if (next.equals(REFLECTION_TAB)) { + addReflectionTab(); + } else if (next.equals(EVENT_TAB)) { + addEventTab(); + } else if (next.equals(IMAGE_TAB)) { + addImageTab(); + } + } + + tbd.addChangeListener(new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + viewButton.setEnabled(getSelectedNode() != null); + } + }); + + getContentPane().setLayout(new BorderLayout()); + getContentPane().add(buttonPane, BorderLayout.SOUTH); + getContentPane().add(tbd, BorderLayout.CENTER); + + addComponentListener(new ComponentListener() { + @Override + public void componentHidden(ComponentEvent e) { + } + + @Override + public void componentMoved(ComponentEvent e) { + } + + @Override + public void componentResized(ComponentEvent e) { + if (winSplit != null) { + winSplit.setDividerLocation(0.8); + } + if (componentSplit != null) { + componentSplit.setDividerLocation(0.8); + } + } + + @Override + public void componentShown(ComponentEvent e) { + } + }); + + setSize(800, 400); + } + + private void addImageTab() { + BufferedImage image = null; + if (compNode instanceof ComponentNode) { + image = ((ComponentNode) compNode).getImage(); + } + if (image != null) { + JPanel imagePane = new ImagePane(image); + imagePane.prepareImage(image, imagePane); + JScrollPane pane = new JScrollPane(imagePane); + tbd.add(IMAGE_TAB, pane); + } + } + + private void addWindowTab() { + if (compNode instanceof WindowNode + && ((WindowNode) compNode).getWindows().length > 0) { + winTree = new JTree(((WindowNode) compNode).getWindowModel()); + winTree.setCellRenderer(renderer); + winTree.setEditable(false); + winTree.addTreeSelectionListener(selManager); + winSplit = createUnderPane(winTree); + tbd.add(WINDOWS_TAB, winSplit); + } + + } + + private void addComponentTab() { + if (compNode instanceof ContainerNode + && ((ContainerNode) compNode).getComponents().length > 0) { + componentTree = new JTree(((ContainerNode) compNode).getComponentModel()); + componentTree.setCellRenderer(renderer); + componentTree.setEditable(false); + componentTree.addTreeSelectionListener(selManager); + componentSplit = createUnderPane(componentTree); + tbd.add(COMPONENTS_TAB, componentSplit); + } + + } + + private void addReflectionTab() { + methodTree = new JTree(compNode.getMethodsModel()); + methodTree.setCellRenderer(renderer); + methodTree.setEditable(false); + methodTree.addTreeSelectionListener(selManager); + tbd.add(REFLECTION_TAB, new JScrollPane(methodTree)); + } + + private void addPropertiesTab() { + if (compNode instanceof ComponentNode) { + Hashtable props = ((ContainerNode) compNode).getProperties(); + if (props.size() > 0) { + JTable propTable = new JTable(new MyModel(props)); + propTable.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN); + tbd.add(PROPERTIES_TAB, new JScrollPane(propTable)); + /* + propTable.addMouseListener(new MouseListener() { + public void mouseClicked(MouseEvent e) { + new ComponentBrowser(getOwnr(), + getSelectedNode()). + show(); + } + public void mouseExited(MouseEvent e) { + } + public void mouseEntered(MouseEvent e) { + } + public void mousePressed(MouseEvent e) { + } + public void mouseReleased(MouseEvent e) { + } + }); + */ + } + } + } + + private void addEventTab() { + if (compNode instanceof ComponentNode) { + eventModel = new DefaultListModel<>(); + eventList = new JList<>(eventModel); + listListener = new ListListener(eventModel, ((ComponentNode) compNode).comp); + mouseEvents = new JCheckBox("Mouse events"); + mouseEvents.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + if (mouseEvents.isSelected()) { + listListener.addMouseListener(); + } else { + listListener.removeMouseListener(); + } + } + }); + mouseMotionEvents = new JCheckBox("Mouse motion events"); + mouseMotionEvents.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + if (mouseMotionEvents.isSelected()) { + listListener.addMouseMotionListener(); + } else { + listListener.removeMouseMotionListener(); + } + } + }); + keyEvents = new JCheckBox("Key events"); + keyEvents.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + if (keyEvents.isSelected()) { + listListener.addKeyListener(); + } else { + listListener.removeKeyListener(); + } + } + }); + JButton clear = new JButton("Clear list"); + clear.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + eventModel.removeAllElements(); + } + }); + JPanel checkPane = new JPanel(); + checkPane.add(mouseEvents); + checkPane.add(mouseMotionEvents); + checkPane.add(keyEvents); + checkPane.add(clear); + JPanel subPane = new JPanel(); + subPane.setLayout(new BorderLayout()); + subPane.add(checkPane, BorderLayout.SOUTH); + subPane.add(new JScrollPane(eventList), BorderLayout.CENTER); + tbd.add(EVENT_TAB, subPane); + } + } + + private JFrame getOwnr() { + return this; + } + + private JTree getSelectedTree() { + String title = tbd.getTitleAt(tbd.getSelectedIndex()); + if (title.equals(WINDOWS_TAB)) { + return winTree; + } else if (title.equals(COMPONENTS_TAB)) { + return componentTree; + } else if (title.equals(REFLECTION_TAB)) { + return methodTree; + } + return null; + } + + private TreePath getSelectionPath() { + JTree tree = getSelectedTree(); + if (tree != null) { + return tree.getSelectionPath(); + } else { + return null; + } + } + + private ClassNode getSelectedNode() { + TreePath path = getSelectionPath(); + if (path != null) { + return (ClassNode) path.getLastPathComponent(); + } else { + return null; + } + } + + } + + private static class SelectionManager implements TreeSelectionListener, ListSelectionListener { + + Component[] comps; + + public SelectionManager(Component[] comps) { + this.comps = comps; + } + + @Override + public void valueChanged(TreeSelectionEvent e) { + for (Component comp : comps) { + comp.setEnabled(e.getPath() != null); + } + } + + @Override + public void valueChanged(ListSelectionEvent e) { + for (Component comp : comps) { + comp.setEnabled(e.getFirstIndex() != -1); + } + } + } + + private void expandAll(JTree tree, TreePath path) { + tree.expandPath(path); + TreeModel model = tree.getModel(); + Object lastComponent = path.getLastPathComponent(); + for (int i = 0; i < model.getChildCount(lastComponent); i++) { + expandAll(tree, + path.pathByAddingChild(model.getChild(lastComponent, i))); + } + } + + private static class ToStringListener implements TreeSelectionListener { + + JTextArea area; + + public ToStringListener(JTextArea area) { + this.area = area; + } + + @Override + public void valueChanged(TreeSelectionEvent e) { + if (e.getPath() != null + && e.getPath().getLastPathComponent() instanceof ComponentNode) { + area.setText("toString(): " + + ((ComponentNode) e.getPath().getLastPathComponent()). + getToString()); + } else { + area.setText(""); + } + } + } + + private JSplitPane createUnderPane(JTree tree) { + JTextArea toStringArea = new JTextArea(); + toStringArea.setLineWrap(true); + toStringArea.setEditable(false); + tree.addTreeSelectionListener(new ToStringListener(toStringArea)); + JSplitPane result = new JSplitPane(JSplitPane.VERTICAL_SPLIT, + new JScrollPane(tree), + new JScrollPane(toStringArea)); + result.setOneTouchExpandable(true); + result.setDividerSize(8); + result.setDividerLocation(0.8); + return result; + } + + private static class MyModel extends DefaultTableModel { + + private static final long serialVersionUID = 42L; + + @SuppressWarnings(value = "unchecked") + public MyModel(Hashtable props) { + super(); + Object[] keys = props.keySet().toArray(); + if (keys.length > 0) { + addColumn("Name"); + addColumn("Value"); + setNumRows(keys.length); + for (int i = 0; i < keys.length; i++) { + setValueAt(keys[i].toString(), i, 0); + setValueAt(props.get(keys[i]).toString(), i, 1); + } + Collections.sort((Vector>) (Vector) getDataVector(), new Comparator>() { + @Override + public int compare(Vector v1, Vector v2) { + return v1.get(0).toString().compareTo(v2.get(0).toString()); + } + }); + } + } + + @Override + public boolean isCellEditable(int x, int y) { + return false; + } + } + + private static class ListListener extends TrialListenerManager { + + DefaultListModel model; + + public ListListener(DefaultListModel m, Component comp) { + super(comp); + model = m; + } + + @Override + void printEvent(AWTEvent e) { + model.addElement(e); + } + } + + private static class ImagePane extends JPanel { + + private static final long serialVersionUID = 42L; + BufferedImage image; + + public ImagePane(BufferedImage image) { + super(); + this.image = image; + setPreferredSize(new Dimension(image.getWidth(), image.getHeight())); + } + + @Override + public void paint(Graphics g) { + g.drawImage(image, 0, 0, null); + } + } + + private static class ComponentImageProvider { + + BufferedImage image; + int x; + int y; + + public ComponentImageProvider(BufferedImage image, int x, int y) { + this.image = image; + this.x = x; + this.y = y; + } + + public BufferedImage getImage(int x, int y, int w, int h) { + /* + BufferedImage newImage = image.getSubimage(0, 0, image.getWidth(), image.getHeight()); + Graphics g = newImage.getGraphics(); + g.setColor(Color.RED); + g.drawRect(x - this.x, y - this.y, w, h); + return newImage; + */ + int realW = Math.min(image.getWidth() - x, w); + int realH = Math.min(image.getHeight() - y, h); + return image.getSubimage(x - this.x, y - this.y, realW, realH); + } + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/explorer/TrialListenerManager.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/explorer/TrialListenerManager.java new file mode 100644 index 00000000000..3c50b405cb5 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/explorer/TrialListenerManager.java @@ -0,0 +1,204 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.explorer; + +import java.awt.AWTEvent; +import java.awt.Component; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.event.MouseMotionListener; + +import org.netbeans.jemmy.JemmyProperties; +import org.netbeans.jemmy.Outputable; +import org.netbeans.jemmy.QueueTool; +import org.netbeans.jemmy.TestOut; + +/** + * Auxiliary class to find an event sequence which should be posted to reproduce + * user actions. + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + * + */ +public class TrialListenerManager implements Outputable { + + Component comp; + TrialMouseListener mListener; + TrialMouseMotionListener mmListener; + TrialKeyListener kListener; + TestOut output; + + /** + * Contructor. + * + * @param comp Component to display event sequence for. + */ + public TrialListenerManager(Component comp) { + this.comp = comp; + mListener = new TrialMouseListener(); + mmListener = new TrialMouseMotionListener(); + kListener = new TrialKeyListener(); + output = JemmyProperties.getCurrentOutput(); + } + + @Override + public void setOutput(TestOut output) { + this.output = output; + } + + @Override + public TestOut getOutput() { + return output; + } + + /** + * Removes mouse listener. + * + * @see #addMouseListener + */ + public void removeMouseListener() { + comp.removeMouseListener(mListener); + } + + /** + * Adds mouse listener. + * + * @see #removeMouseListener + */ + public void addMouseListener() { + removeMouseListener(); + comp.addMouseListener(mListener); + } + + /** + * Removes mouse motion listener. + * + * @see #addMouseMotionListener + */ + public void removeMouseMotionListener() { + comp.removeMouseMotionListener(mmListener); + } + + /** + * Adds mouse motion listener. + * + * @see #removeMouseMotionListener + */ + public void addMouseMotionListener() { + removeMouseMotionListener(); + comp.addMouseMotionListener(mmListener); + } + + /** + * Removes key listener. + * + * @see #addKeyListener + */ + public void removeKeyListener() { + comp.removeKeyListener(kListener); + } + + /** + * Adds key listener. + * + * @see #removeKeyListener + */ + public void addKeyListener() { + removeKeyListener(); + comp.addKeyListener(kListener); + } + + void printEvent(final AWTEvent event) { + // if event != null run toString in dispatch thread + String eventToString = new QueueTool().invokeSmoothly( + new QueueTool.QueueAction("event.toString()") { + @Override + public String launch() { + return event.toString(); + } + } + ); + output.printLine(eventToString); + } + + private class TrialMouseListener implements MouseListener { + + @Override + public void mouseClicked(MouseEvent e) { + printEvent(e); + } + + @Override + public void mouseEntered(MouseEvent e) { + printEvent(e); + } + + @Override + public void mouseExited(MouseEvent e) { + printEvent(e); + } + + @Override + public void mousePressed(MouseEvent e) { + printEvent(e); + } + + @Override + public void mouseReleased(MouseEvent e) { + printEvent(e); + } + } + + private class TrialMouseMotionListener implements MouseMotionListener { + + @Override + public void mouseDragged(MouseEvent e) { + printEvent(e); + } + + @Override + public void mouseMoved(MouseEvent e) { + printEvent(e); + } + } + + private class TrialKeyListener implements KeyListener { + + @Override + public void keyPressed(KeyEvent e) { + printEvent(e); + } + + @Override + public void keyReleased(KeyEvent e) { + printEvent(e); + } + + @Override + public void keyTyped(KeyEvent e) { + printEvent(e); + } + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/explorer/package-info.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/explorer/package-info.java new file mode 100644 index 00000000000..aa05f9aff1d --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/explorer/package-info.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +/** + *

Package org.netbeans.jemmy.explorer

+ * Contains auxiliary classes to explore tested application.
+ * + * @since 23 Feb 2002 + *
+ */ +package org.netbeans.jemmy.explorer; diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/image/ColorImageComparator.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/image/ColorImageComparator.java new file mode 100644 index 00000000000..1f4472cda4e --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/image/ColorImageComparator.java @@ -0,0 +1,179 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.image; + +import java.awt.image.BufferedImage; + +/** + * Compares two images with color mapping defined by {@code ColorModel} + * implementation. + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + */ +public class ColorImageComparator extends StrictImageComparator { + + ColorMap leftMap, rightMap; + ImageComparator comparator = null; + + /** + * Creates a comparator with a color maps. Object created by this + * constructor behaves like {@code StrictImageComparator}. Object + * created works faster because it does not create intermediate images for + * another comparator. + * + * @param map Map applied to both left and right images during comparision. + */ + public ColorImageComparator(ColorMap map) { + leftMap = map; + rightMap = map; + } + + /** + * Creates a comparator with {@code map} color mapping. Actual + * comparision perfomed by {@code comparator} parameter. + * + * @param map Map applied to both left and right images during comparision. + * @param subComparator comporator to perform a comparision of to images + * with mapped colors. + */ + public ColorImageComparator(ColorMap map, ImageComparator subComparator) { + this(map); + this.comparator = subComparator; + } + + /** + * Creates a comparator with two color maps. Object created by this + * constructor behaves like {@code StrictImageComparator}. Object + * created works faster because it does not create intermediate images for + * another comparator. + * + * @param leftMap Map applied to the left image during comparision. + * @param rightMap Map applied to the right image during comparision. + */ + public ColorImageComparator(ColorMap leftMap, ColorMap rightMap) { + this.leftMap = leftMap; + this.rightMap = rightMap; + } + + /** + * Creates a comparator with two color maps. Actual comparision perfomed by + * {@code comparator} parameter. + * + * @param leftMap Map applied to the left image during comparision. + * @param rightMap Map applied to the right image during comparision. + * @param subComparator comporator to perform a comparision of to images + * with mapped colors. + */ + public ColorImageComparator(ColorMap leftMap, ColorMap rightMap, ImageComparator subComparator) { + this(leftMap, rightMap); + this.comparator = subComparator; + } + + /** + * Compares images by {@code ImageComparator} passed into constructor, + * or itself if no {@code ImageComparator} was passed, processing both + * images by {@code ColorMap} instance before comparision. + */ + @Override + public boolean compare(BufferedImage image1, BufferedImage image2) { + if (comparator != null) { + return comparator.compare(recolor(image1, leftMap), recolor(image2, rightMap)); + } else { + return super.compare(image1, image2); + } + } + + private BufferedImage recolor(BufferedImage src, ColorMap map) { + BufferedImage result = new BufferedImage(src.getWidth(), src.getHeight(), src.getType()); + for (int x = 0; x < src.getWidth(); x++) { + for (int y = 0; y < src.getWidth(); y++) { + result.setRGB(x, y, map.mapColor(src.getRGB(x, y))); + } + } + return result; + } + + @Override + protected final boolean compareColors(int rgb1, int rgb2) { + return leftMap.mapColor(rgb1) == rightMap.mapColor(rgb2); + } + + /** + * Interface to map colors during the comparision. + */ + public static interface ColorMap { + + /** + * Maps one color into another. + * + * @param rgb an original color. + * @return a converted color. + */ + public int mapColor(int rgb); + } + + /** + * Turns {@code foreground} color to white, other - to black. + */ + public static class ForegroundColorMap implements ColorMap { + + int foreground; + + /** + * Constructs a ColorImageComparator$ForegroundColorMap object. + * + * @param foreground Foreground color. + */ + public ForegroundColorMap(int foreground) { + this.foreground = foreground; + } + + @Override + public int mapColor(int rgb) { + return (rgb == foreground) ? 0xffffff : 0; + } + } + + /** + * Turns {@code background} color to black, left others unchanged. + */ + public static class BackgroundColorMap implements ColorMap { + + int background; + + /** + * Constructs a ColorImageComparator$BackgroundColorMap object. + * + * @param background Background color. + */ + public BackgroundColorMap(int background) { + this.background = background; + } + + @Override + public int mapColor(int rgb) { + return (rgb == background) ? 0 : rgb; + } + } + +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/image/FileImageComparator.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/image/FileImageComparator.java new file mode 100644 index 00000000000..9898a875d56 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/image/FileImageComparator.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.image; + +import java.awt.image.BufferedImage; +import java.io.IOException; + +import org.netbeans.jemmy.JemmyException; + +/** + * Allowes compares images in memory to ones stored in files and compare such + * images one with another. + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + */ +public class FileImageComparator { + + ImageLoader loader; + ImageComparator comparator; + + /** + * Constructs a FileImageComparator object. + * + * @param comparator - ImageComparator to be used for image comparision. + * @param loader - ImageLoader to be used for image loading. + */ + public FileImageComparator(ImageComparator comparator, ImageLoader loader) { + this.loader = loader; + this.comparator = comparator; + } + + /** + * Compares an image with one stored in file. Comparision is performed by + * ImageComparator passed into constructor. Image is loaded by ImageLoader + * passed into constructor. + * + * @param image an image to compare. + * @param fileName a file containing an image to compare. + * @return true if images match each other. + */ + public boolean compare(BufferedImage image, String fileName) { + try { + return comparator.compare(image, loader.load(fileName)); + } catch (IOException e) { + throw (new JemmyException("IOException during image loading", e)); + } + } + + /** + * Compares two image stored in files.. Comparision is performed by + * ImageComparator passed into constructor. Images are loaded by ImageLoader + * passed into constructor. + * + * @param fileName1 a file containing an image to compare. + * @param fileName2 a file containing an image to compare. + * @return true if images match each other. + */ + public boolean compare(String fileName1, String fileName2) { + try { + return (comparator.compare(loader.load(fileName1), + loader.load(fileName2))); + } catch (IOException e) { + throw (new JemmyException("IOException during image loading", e)); + } + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/image/ImageComparator.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/image/ImageComparator.java new file mode 100644 index 00000000000..f3fbde9eaab --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/image/ImageComparator.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.image; + +import java.awt.image.BufferedImage; + +/** + * Interface for all classes performing image comparison. + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + */ +public interface ImageComparator { + + /** + * Should return true if images matches, false otherwise. + * + * @param image1 an image to compare. + * @param image2 an image to compare. + * @return true if images match each other. + */ + public boolean compare(BufferedImage image1, BufferedImage image2); +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/image/ImageFinder.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/image/ImageFinder.java new file mode 100644 index 00000000000..9a218994526 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/image/ImageFinder.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.image; + +import java.awt.Point; +import java.awt.image.BufferedImage; + +/** + * Interface for all classes performing image lookup. + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + */ +public interface ImageFinder { + + /** + * Should return location if image lays inside an image represented by this + * object. + * + * @param image an image to search. + * @param index an ordinal image location index. If equal to 1, for example, + * second appropriate location will be found. + * @return Image location coordinates if image was found, null otherwise. + */ + public Point findImage(BufferedImage image, int index); +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/image/ImageLoader.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/image/ImageLoader.java new file mode 100644 index 00000000000..b3e4bab9408 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/image/ImageLoader.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.image; + +import java.awt.image.BufferedImage; +import java.io.IOException; + +/** + * Interface for all classes performing image loading. + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + */ +public interface ImageLoader { + + /** + * Loads an image from file. + * + * @param fileName a file to load image from. + * @return a loaded image. + * @throws IOException + */ + public BufferedImage load(String fileName) throws IOException; +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/image/ImageSaver.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/image/ImageSaver.java new file mode 100644 index 00000000000..07f45d4d9d1 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/image/ImageSaver.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.image; + +import java.awt.image.BufferedImage; +import java.io.IOException; + +/** + * Interface for classes performing image saving. + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + */ +public interface ImageSaver { + + /** + * Should save image into file. + * + * @param image an image to be saved. + * @param fileName a file to load image from. + * @throws IOException + */ + public void save(BufferedImage image, String fileName) throws IOException; +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/image/ImageTool.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/image/ImageTool.java new file mode 100644 index 00000000000..cca418332b4 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/image/ImageTool.java @@ -0,0 +1,199 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.image; + +import java.awt.AWTException; +import java.awt.Color; +import java.awt.Component; +import java.awt.Rectangle; +import java.awt.Robot; +import java.awt.Toolkit; +import java.awt.image.BufferedImage; + +import org.netbeans.jemmy.JemmyException; + +/** + * Contains util methods to work with images. + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + */ +public class ImageTool { + + /** + * Gets an image from a rectange on screen. + * + * @param rect a rectangle on screen in absolute screen coordinates. + * @return a captured image. + */ + public static BufferedImage getImage(Rectangle rect) { + try { + return new Robot().createScreenCapture(rect); + } catch (AWTException e) { + throw (new JemmyException("Exception during screen capturing", e)); + } + } + + /** + * Gets an image from a component. + * + * @param comp a visible component. + * @return a captured image. + */ + public static BufferedImage getImage(Component comp) { + return (getImage(new Rectangle(comp.getLocationOnScreen(), + comp.getSize()))); + } + + /** + * Gets the whole screen image. + * + * @return a captured image. + */ + public static BufferedImage getImage() { + return getImage(new Rectangle(Toolkit.getDefaultToolkit().getScreenSize())); + } + + /** + * Increases image. + * + * @param image an image to enlarge. + * @param zoom A scale. + * @return a result image. + */ + public static BufferedImage enlargeImage(BufferedImage image, int zoom) { + int wight = image.getWidth(); + int height = image.getHeight(); + BufferedImage result = new BufferedImage(wight * zoom, + height * zoom, + image.getType()); + int rgb; + for (int x = 0; x < wight; x++) { + for (int y = 0; y < height; y++) { + rgb = image.getRGB(x, y); + for (int i = 0; i < zoom; i++) { + for (int j = 0; j < zoom; j++) { + result.setRGB(x * zoom + i, + y * zoom + j, + rgb); + } + } + } + } + return result; + } + + /** + * @deprecated Use subtractImage(BufferedImage, BufferedImage) instead. + * @param minuend an image to subtract from. + * @param deduction an image to subtract. + * @return a result image. + */ + @Deprecated + public static BufferedImage substractImage(BufferedImage minuend, BufferedImage deduction) { + return subtractImage(minuend, deduction); + } + + /** + * Subtracts second image from first one. Could be used to save file + * difference for future analysis. + * + * @param minuend an image to subtract from. + * @param deduction an image to subtract. + * @return a result image. + */ + public static BufferedImage subtractImage(BufferedImage minuend, BufferedImage deduction) { + return subtractImage(minuend, deduction, 0, 0); + } + + /** + * @deprecated Use subtractImage(BufferedImage, BufferedImage, int, int) + * instead. + * @param minuend an image to subtract from. + * @param deduction an image to subtract. + * @return a result image. + */ + @Deprecated + public static BufferedImage substractImage(BufferedImage minuend, BufferedImage deduction, int relativeX, int relativeY) { + return subtractImage(minuend, deduction, relativeX, relativeY); + } + + /** + * Subtracts subimage from image. Could be used to save file difference for + * future analysis. + * + * @param minuend an image to subtract from. + * @param deduction an image to subtract. + * @param relativeX - deduction-in-minuend X coordinate + * @param relativeY - deduction-in-minuend Y coordinate + * @return a result image. + */ + public static BufferedImage subtractImage(BufferedImage minuend, BufferedImage deduction, int relativeX, int relativeY) { + int mWidth = minuend.getWidth(); + int mHeight = minuend.getHeight(); + int dWidth = deduction.getWidth(); + int dHeight = deduction.getHeight(); + + int maxWidth = (mWidth > relativeX + dWidth) ? mWidth : (relativeX + dWidth); + int maxHeight = (mHeight > relativeY + dHeight) ? mHeight : (relativeY + dHeight); + + BufferedImage result = new BufferedImage(maxWidth, maxHeight, BufferedImage.TYPE_INT_RGB); + int mColor, dColor; + for (int x = 0; x < maxWidth; x++) { + for (int y = 0; y < maxHeight; y++) { + if (x >= mWidth + || y >= mHeight) { + mColor = 0; + } else { + mColor = minuend.getRGB(x, y); + } + if (x >= dWidth + relativeX + || y >= dHeight + relativeY + || x < relativeX + || y < relativeY) { + dColor = 0; + } else { + dColor = deduction.getRGB(x - relativeX, y - relativeY); + } + result.setRGB(x, y, subtractColors(mColor, dColor)); + } + } + return result; + } + + private static int subtractColors(int mRGB, int dRGB) { + Color mColor = new Color(mRGB); + Color dColor = new Color(dRGB); + int red = subtractColor(mColor.getRed(), dColor.getRed()); + int green = subtractColor(mColor.getGreen(), dColor.getGreen()); + int blue = subtractColor(mColor.getBlue(), dColor.getBlue()); + return new Color(red, green, blue).getRGB(); + } + + private static int subtractColor(int mColor, int dColor) { + if (mColor >= dColor) { + return mColor - dColor; + } else { + return mColor - dColor + 0Xff; + } + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/image/PNGImageLoader.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/image/PNGImageLoader.java new file mode 100644 index 00000000000..935f90df8b3 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/image/PNGImageLoader.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.image; + +import java.awt.image.BufferedImage; +import java.io.FileInputStream; +import java.io.IOException; + +import org.netbeans.jemmy.util.PNGDecoder; + +/** + * Allowes to process PNF image format. + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + */ +public class PNGImageLoader implements ImageLoader { + + /** + * Loads an image from a PNG image file. + */ + @Override + public BufferedImage load(String fileName) throws IOException { + return new PNGDecoder(new FileInputStream(fileName)).decode(); + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/image/PNGImageSaver.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/image/PNGImageSaver.java new file mode 100644 index 00000000000..d2fae52470d --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/image/PNGImageSaver.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.image; + +import java.awt.image.BufferedImage; +import java.io.BufferedOutputStream; +import java.io.FileOutputStream; +import java.io.IOException; + +import org.netbeans.jemmy.util.PNGEncoder; + +/** + * Allowes to process PNF image format. + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + */ +public class PNGImageSaver implements ImageSaver { + + /** + * Saves an image into a PNG image file. + */ + @Override + public void save(BufferedImage image, String fileName) throws IOException { + new PNGEncoder(new BufferedOutputStream(new FileOutputStream(fileName)), + PNGEncoder.COLOR_MODE). + encode(image); + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/image/RoughImageComparator.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/image/RoughImageComparator.java new file mode 100644 index 00000000000..cdf8ddd9ae5 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/image/RoughImageComparator.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.image; + +import java.awt.image.BufferedImage; + +/** + * Compares two images roughly (i.e. not all of the pixel colors should match). + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + */ +public class RoughImageComparator implements ImageComparator { + + double roughness = .0; + + /** + * Creates a comparator with {@code roughness} allowed roughness. + * + * @param roughness Allowed comparision roughness. + */ + public RoughImageComparator(double roughness) { + this.roughness = roughness; + } + + /** + * Compares two images with allowed roughness. + * + * @param image1 an image to compare. + * @param image2 an image to compare. + * @return true if images have the same sizes and number of unmatching + * pixels less or equal to image1.getWidth() * image1.getHeight() * roughness + */ + @Override + public boolean compare(BufferedImage image1, BufferedImage image2) { + if (image1.getWidth() != image2.getWidth() + || image1.getHeight() != image2.getHeight()) { + return false; + } + double maxRoughPixels = (double) (image1.getWidth() * image1.getHeight()) * roughness; + int errorCount = 0; + for (int x = 0; x < image1.getWidth(); x++) { + for (int y = 0; y < image1.getHeight(); y++) { + if (image1.getRGB(x, y) != image2.getRGB(x, y)) { + errorCount++; + if (errorCount > maxRoughPixels) { + return false; + } + } + } + } + return true; + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/image/RoughImageFinder.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/image/RoughImageFinder.java new file mode 100644 index 00000000000..6848fc825c3 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/image/RoughImageFinder.java @@ -0,0 +1,103 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.image; + +import java.awt.Point; +import java.awt.image.BufferedImage; + +/** + * Performs "rough" image search. + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + */ +public class RoughImageFinder implements ImageFinder { + + double roughness = .0; + int bigWidth, bigHeight; + int[][] bigPixels; + + /** + * Creates an instance allowing to find an image inside the one passed as + * parameter with some "roughness". + * + * @param area - Image to search in. + * @param roughness - Allowed + */ + public RoughImageFinder(BufferedImage area, double roughness) { + this.roughness = roughness; + bigWidth = area.getWidth(); + bigHeight = area.getHeight(); + bigPixels = new int[bigWidth][bigHeight]; + for (int x = 0; x < bigWidth; x++) { + for (int y = 0; y < bigHeight; y++) { + bigPixels[x][y] = area.getRGB(x, y); + } + } + } + + /** + * Performs "rough" search. + * + * @param image an image to search. + * @param index an ordinal image location index. + * @return Point where number of unmatching pixels less or equal to image1.getWidth() * image1.getHeight() * roughness + */ + @Override + public Point findImage(BufferedImage image, int index) { + int smallWidth = image.getWidth(); + int smallHeight = image.getHeight(); + int[][] smallPixels = new int[smallWidth][smallHeight]; + for (int x = 0; x < smallWidth; x++) { + for (int y = 0; y < smallHeight; y++) { + smallPixels[x][y] = image.getRGB(x, y); + } + } + double maxRoughPixels = (double) (smallWidth * smallHeight) * roughness; + int count = 0; + for (int X = 0; X <= bigWidth - smallWidth; X++) { + for (int Y = 0; Y <= bigHeight - smallHeight; Y++) { + int roughPixels = 0; + for (int x = 0; x < smallWidth; x++) { + for (int y = 0; y < smallHeight; y++) { + if (smallPixels[x][y] != bigPixels[X + x][Y + y]) { + roughPixels++; + if (roughPixels > maxRoughPixels) { + break; + } + } + } + if (roughPixels > maxRoughPixels) { + break; + } + } + if (roughPixels <= maxRoughPixels) { + if (count == index) { + return new Point(X, Y); + } + count++; + } + } + } + return null; + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/image/StrictImageComparator.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/image/StrictImageComparator.java new file mode 100644 index 00000000000..dce51c568f7 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/image/StrictImageComparator.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.image; + +import java.awt.image.BufferedImage; + +/** + * Compares two images strictly (i.e. all the pixel colors should match). + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + */ +public class StrictImageComparator implements ImageComparator { + + /** + * Checks images sizes and pixels. Compares one pixel after another untill + * one will be different. + * + * @param image1 an image to compare. + * @param image2 an image to compare. + * @return True if all the pixels match, false otherwise. + */ + @Override + public boolean compare(BufferedImage image1, BufferedImage image2) { + if (image1.getWidth() != image2.getWidth() + || image1.getHeight() != image2.getHeight()) { + return false; + } + for (int x = 0; x < image1.getWidth(); x++) { + for (int y = 0; y < image1.getHeight(); y++) { + if (!compareColors(image1.getRGB(x, y), image2.getRGB(x, y))) { + return false; + } + } + } + return true; + } + + /** + * Could be used to override the way of comparing colors. + * + * @param rgb1 a color to compare. + * @param rgb2 a color to compare. + * @return true if colors are equal. + */ + protected boolean compareColors(int rgb1, int rgb2) { + return rgb1 == rgb2; + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/image/StrictImageFinder.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/image/StrictImageFinder.java new file mode 100644 index 00000000000..674e550c122 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/image/StrictImageFinder.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.image; + +import java.awt.Point; +import java.awt.image.BufferedImage; + +/** + * Performs "strict" (i.e. based on all pixels matching) image search. + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + */ +public class StrictImageFinder implements ImageFinder { + + int bigWidth, bigHeight; + int[][] bigPixels; + + /** + * Creates an instance searching subimages insige a parameter image. + * + * @param area - Image to search in. + */ + public StrictImageFinder(BufferedImage area) { + bigWidth = area.getWidth(); + bigHeight = area.getHeight(); + bigPixels = new int[bigWidth][bigHeight]; + for (int x = 0; x < bigWidth; x++) { + for (int y = 0; y < bigHeight; y++) { + bigPixels[x][y] = area.getRGB(x, y); + } + } + } + + /** + * Searchs for an image inside image passed into constructor. + * + * @param image an image to search. + * @param index an ordinal image location index. If equal to 1, for example, + * second appropriate location will be found. + * @return Left-up corner coordinates of image location. + */ + @Override + public Point findImage(BufferedImage image, int index) { + int smallWidth = image.getWidth(); + int smallHeight = image.getHeight(); + int[][] smallPixels = new int[smallWidth][smallHeight]; + for (int x = 0; x < smallWidth; x++) { + for (int y = 0; y < smallHeight; y++) { + smallPixels[x][y] = image.getRGB(x, y); + } + } + boolean good; + int count = 0; + for (int X = 0; X <= bigWidth - smallWidth; X++) { + for (int Y = 0; Y <= bigHeight - smallHeight; Y++) { + good = true; + for (int x = 0; x < smallWidth; x++) { + for (int y = 0; y < smallHeight; y++) { + if (smallPixels[x][y] != bigPixels[X + x][Y + y]) { + good = false; + break; + } + } + if (!good) { + break; + } + } + if (good) { + if (count == index) { + return new Point(X, Y); + } + count++; + } + } + } + return null; + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/image/package-info.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/image/package-info.java new file mode 100644 index 00000000000..f1987a4caf4 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/image/package-info.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +/** + *

Image library

+ * Contains classes allowing to compare two images and to find one image inside + * another.

+ * + * @since 9 Nov 2002 + *
+ */ +package org.netbeans.jemmy.image; diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/AbstractButtonOperator.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/AbstractButtonOperator.java new file mode 100644 index 00000000000..577ce4fb95d --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/AbstractButtonOperator.java @@ -0,0 +1,1213 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.operators; + +import java.awt.Component; +import java.awt.Container; +import java.awt.Insets; +import java.awt.event.ActionListener; +import java.awt.event.ItemListener; +import java.util.Hashtable; + +import javax.swing.AbstractButton; +import javax.swing.ButtonModel; +import javax.swing.Icon; +import javax.swing.event.ChangeListener; +import javax.swing.plaf.ButtonUI; + +import org.netbeans.jemmy.ComponentChooser; +import org.netbeans.jemmy.JemmyException; +import org.netbeans.jemmy.Outputable; +import org.netbeans.jemmy.TestOut; +import org.netbeans.jemmy.TimeoutExpiredException; +import org.netbeans.jemmy.Timeoutable; +import org.netbeans.jemmy.Timeouts; +import org.netbeans.jemmy.drivers.ButtonDriver; +import org.netbeans.jemmy.drivers.DriverManager; + +/** + * + *

Timeouts used:
+ * AbstractButtonOperator.PushButtonTimeout - time between button pressing and + * releasing
+ * ComponentOperator.WaitComponentTimeout - time to wait button displayed
+ * ComponentOperator.WaitComponentEnabledTimeout - time to wait button enabled + *
+ * ComponentOperator.WaitStateTimeout - time to wait for text
. + * + * @see org.netbeans.jemmy.Timeouts + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + * + */ +public class AbstractButtonOperator extends JComponentOperator + implements Timeoutable, Outputable { + + /** + * Identifier for a text property. + * + * @see #getDump + */ + public static final String TEXT_DPROP = "Text"; + + /** + * Identifier for a selected text property. + * + * @see #getDump + */ + public static final String IS_SELECTED_DPROP = "Selected"; + + /** + * Default value for AbstractButtonOperator.PushButtonTimeout timeout. + */ + private final static long PUSH_BUTTON_TIMEOUT = 0; + + private Timeouts timeouts; + private TestOut output; + + ButtonDriver driver; + + /** + * Constructor. + * + * @param b The {@code java.awt.AbstractButton} managed by this + * instance. + */ + public AbstractButtonOperator(AbstractButton b) { + super(b); + driver = DriverManager.getButtonDriver(getClass()); + } + + /** + * Constructs an AbstractButtonOperator object. + * + * @param cont container + * @param chooser a component chooser specifying searching criteria. + * @param index an index between appropriate ones. + */ + public AbstractButtonOperator(ContainerOperator cont, ComponentChooser chooser, int index) { + this((AbstractButton) cont. + waitSubComponent(new AbstractButtonFinder(chooser), + index)); + copyEnvironment(cont); + } + + /** + * Constructs an AbstractButtonOperator object. + * + * @param cont container + * @param chooser a component chooser specifying searching criteria. + */ + public AbstractButtonOperator(ContainerOperator cont, ComponentChooser chooser) { + this(cont, chooser, 0); + } + + /** + * Constructor. Waits for a component in a container to show. The component + * is identified as the {@code index+1}'th + * {@code javax.swing.AbstractButton} that shows, lies below the + * container in the display containment hierarchy, and that has the desired + * text. Uses cont's timeout and output for waiting and to init this + * operator. + * + * @param cont The operator for a container containing the sought for + * button. + * @param text Button text. + * @param index Ordinal component index. The first component has + * {@code index} 0. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public AbstractButtonOperator(ContainerOperator cont, String text, int index) { + this((AbstractButton) waitComponent(cont, + new AbstractButtonByLabelFinder(text, + cont.getComparator()), + index)); + copyEnvironment(cont); + } + + /** + * Constructor. Waits for a component in a container to show. The component + * is identified as the first {@code javax.swing.AbstractButton} that + * shows, lies below the container in the display containment hierarchy, and + * that has the desired text. Uses cont's timeout and output for waiting and + * to init this operator. + * + * @param cont The operator for a container containing the sought for + * button. + * @param text Button text. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public AbstractButtonOperator(ContainerOperator cont, String text) { + this(cont, text, 0); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont The operator for a container containing the sought for + * button. + * @param index Ordinal component index. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public AbstractButtonOperator(ContainerOperator cont, int index) { + this((AbstractButton) waitComponent(cont, + new AbstractButtonFinder(), + index)); + copyEnvironment(cont); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont The operator for a container containing the sought for + * button. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public AbstractButtonOperator(ContainerOperator cont) { + this(cont, 0); + } + + /** + * Searches AbstractButton in a container. + * + * @param cont Container in which to search for the component. The container + * lies above the component in the display containment hierarchy. The + * containment need not be direct. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation, + * defining and applying search criteria. + * @param index Ordinal component index. The first {@code index} is 0. + * @return AbstractButton instance or null if component was not found. + */ + public static AbstractButton findAbstractButton(Container cont, ComponentChooser chooser, int index) { + return (AbstractButton) findComponent(cont, new AbstractButtonFinder(chooser), index); + } + + /** + * Searches for the first AbstractButton in a container. + * + * @param cont Container in which to search for the component. The container + * lies above the component in the display containment hierarchy. The + * containment need not be direct. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation, + * defining and applying search criteria. + * @return AbstractButton instance or null if component was not found. + */ + public static AbstractButton findAbstractButton(Container cont, ComponentChooser chooser) { + return findAbstractButton(cont, chooser, 0); + } + + /** + * Searches AbstractButton by text. + * + * @param cont Container to search component in. + * @param text Button text. If null, contents is not checked. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @param index Ordinal component index. + * @return AbstractButton instance or null if component was not found. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + */ + public static AbstractButton findAbstractButton(Container cont, String text, boolean ce, boolean ccs, int index) { + return findAbstractButton(cont, new AbstractButtonByLabelFinder(text, new DefaultStringComparator(ce, ccs)), index); + } + + /** + * Searches AbstractButton by text. + * + * @param cont Container to search component in. + * @param text Button text. If null, contents is not checked. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @return AbstractButton instance or null if component was not found. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + */ + public static AbstractButton findAbstractButton(Container cont, String text, boolean ce, boolean ccs) { + return findAbstractButton(cont, text, ce, ccs, 0); + } + + /** + * Waits AbstractButton in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @param index Ordinal component index. + * @return AbstractButton instance. + * @throws TimeoutExpiredException + */ + public static AbstractButton waitAbstractButton(Container cont, ComponentChooser chooser, int index) { + return (AbstractButton) waitComponent(cont, new AbstractButtonFinder(chooser), index); + } + + /** + * Waits 0'th AbstractButton in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @return AbstractButton instance. + * @throws TimeoutExpiredException + */ + public static AbstractButton waitAbstractButton(Container cont, ComponentChooser chooser) { + return waitAbstractButton(cont, chooser, 0); + } + + /** + * Waits AbstractButton by text. + * + * @param cont Container to search component in. + * @param text Button text. If null, contents is not checked. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @param index Ordinal component index. + * @return AbstractButton instance. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public static AbstractButton waitAbstractButton(Container cont, String text, boolean ce, boolean ccs, int index) { + return waitAbstractButton(cont, new AbstractButtonByLabelFinder(text, new DefaultStringComparator(ce, ccs)), index); + } + + /** + * Waits AbstractButton by text. + * + * @param cont Container to search component in. + * @param text Button text. If null, contents is not checked. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @return AbstractButton instance. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public static AbstractButton waitAbstractButton(Container cont, String text, boolean ce, boolean ccs) { + return waitAbstractButton(cont, text, ce, ccs, 0); + } + + static { + Timeouts.initDefault("AbstractButtonOperator.PushButtonTimeout", PUSH_BUTTON_TIMEOUT); + } + + @Override + public void setTimeouts(Timeouts timeouts) { + super.setTimeouts(timeouts); + this.timeouts = timeouts; + } + + @Override + public Timeouts getTimeouts() { + return timeouts; + } + + @Override + public void setOutput(TestOut out) { + output = out; + super.setOutput(output.createErrorOutput()); + } + + @Override + public TestOut getOutput() { + return output; + } + + @Override + public void copyEnvironment(Operator anotherOperator) { + super.copyEnvironment(anotherOperator); + driver = DriverManager.getButtonDriver(this); + } + + /** + * Pushs the button using a ButtonDriver registered for this operator. + */ + public void push() { + output.printLine("Push button\n :" + toStringSource()); + output.printGolden("Push button"); + makeComponentVisible(); + try { + waitComponentEnabled(); + } catch (InterruptedException e) { + throw (new JemmyException("Interrupted", e)); + } + driver.push(this); + } + + /** + * Runs {@code push()} method in a separate thread. + */ + public void pushNoBlock() { + produceNoBlocking(new NoBlockingAction("Button pushing") { + @Override + public Void doAction(Void param) { + push(); + return null; + } + }); + } + + /** + * Changes selection if necessary. Uses {@code push()} method in order + * to do so. + * + * @param selected a button selection. + */ + public void changeSelection(boolean selected) { + if (isSelected() != selected) { + push(); + } + if (getVerification()) { + waitSelected(selected); + } + } + + /** + * Runs {@code changeSelection(boolean)} method in a separate thread. + * + * @param selected a button selection. + */ + public void changeSelectionNoBlock(boolean selected) { + produceNoBlocking(new NoBlockingAction("Button selection changing") { + @Override + public Void doAction(Boolean param) { + changeSelection(param); + return null; + } + }, selected ? Boolean.TRUE : Boolean.FALSE); + } + + /** + * Press the button by mouse. + * + * @throws TimeoutExpiredException + */ + public void press() { + output.printLine("Press button\n :" + toStringSource()); + output.printGolden("Press button"); + makeComponentVisible(); + try { + waitComponentEnabled(); + } catch (InterruptedException e) { + throw (new JemmyException("Interrupted", e)); + } + driver.press(this); + } + + /** + * Releases the button by mouse. + * + * @throws TimeoutExpiredException + */ + public void release() { + output.printLine("Release button\n :" + toStringSource()); + output.printGolden("Release button"); + try { + waitComponentEnabled(); + } catch (InterruptedException e) { + throw (new JemmyException("Interrupted", e)); + } + driver.release(this); + } + + /** + * Waits for button to be selected. + * + * @param selected a button selection. + */ + public void waitSelected(final boolean selected) { + getOutput().printLine("Wait button to be selected \n : " + + toStringSource()); + getOutput().printGolden("Wait button to be selected"); + waitState(new ComponentChooser() { + @Override + public boolean checkComponent(Component comp) { + return isSelected() == selected; + } + + @Override + public String getDescription() { + return ("Items has been " + + (selected ? "" : "un") + "selected"); + } + + @Override + public String toString() { + return "waitSelected.ComponentChooser{description = " + getDescription() + '}'; + } + }); + } + + /** + * Waits for text. Uses getComparator() comparator. + * + * @param text Text to wait for. + */ + public void waitText(String text) { + getOutput().printLine("Wait \"" + text + "\" text in component \n : " + + toStringSource()); + getOutput().printGolden("Wait \"" + text + "\" text"); + waitState(new AbstractButtonByLabelFinder(text, getComparator())); + } + + /** + * Returns information about component. + */ + @Override + public Hashtable getDump() { + Hashtable result = super.getDump(); + if (((AbstractButton) getSource()).getText() != null) { + result.put(TEXT_DPROP, ((AbstractButton) getSource()).getText()); + } + result.put(IS_SELECTED_DPROP, ((AbstractButton) getSource()).isSelected() ? "true" : "false"); + return result; + } + + //////////////////////////////////////////////////////// + //Mapping // + /** + * Maps {@code AbstractButton.addActionListener(ActionListener)} + * through queue + */ + public void addActionListener(final ActionListener actionListener) { + runMapping(new MapVoidAction("addActionListener") { + @Override + public void map() { + ((AbstractButton) getSource()).addActionListener(actionListener); + } + }); + } + + /** + * Maps {@code AbstractButton.addChangeListener(ChangeListener)} + * through queue + */ + public void addChangeListener(final ChangeListener changeListener) { + runMapping(new MapVoidAction("addChangeListener") { + @Override + public void map() { + ((AbstractButton) getSource()).addChangeListener(changeListener); + } + }); + } + + /** + * Maps {@code AbstractButton.addItemListener(ItemListener)} through queue + */ + public void addItemListener(final ItemListener itemListener) { + runMapping(new MapVoidAction("addItemListener") { + @Override + public void map() { + ((AbstractButton) getSource()).addItemListener(itemListener); + } + }); + } + + /** + * Maps {@code AbstractButton.doClick()} through queue + */ + public void doClick() { + runMapping(new MapVoidAction("doClick") { + @Override + public void map() { + ((AbstractButton) getSource()).doClick(); + } + }); + } + + /** + * Maps {@code AbstractButton.doClick(int)} through queue + */ + public void doClick(final int i) { + runMapping(new MapVoidAction("doClick") { + @Override + public void map() { + ((AbstractButton) getSource()).doClick(i); + } + }); + } + + /** + * Maps {@code AbstractButton.getActionCommand()} through queue + */ + public String getActionCommand() { + return (runMapping(new MapAction("getActionCommand") { + @Override + public String map() { + return ((AbstractButton) getSource()).getActionCommand(); + } + })); + } + + /** + * Maps {@code AbstractButton.getDisabledIcon()} through queue + */ + public Icon getDisabledIcon() { + return (runMapping(new MapAction("getDisabledIcon") { + @Override + public Icon map() { + return ((AbstractButton) getSource()).getDisabledIcon(); + } + })); + } + + /** + * Maps {@code AbstractButton.getDisabledSelectedIcon()} through queue + */ + public Icon getDisabledSelectedIcon() { + return (runMapping(new MapAction("getDisabledSelectedIcon") { + @Override + public Icon map() { + return ((AbstractButton) getSource()).getDisabledSelectedIcon(); + } + })); + } + + /** + * Maps {@code AbstractButton.getHorizontalAlignment()} through queue + */ + public int getHorizontalAlignment() { + return (runMapping(new MapIntegerAction("getHorizontalAlignment") { + @Override + public int map() { + return ((AbstractButton) getSource()).getHorizontalAlignment(); + } + })); + } + + /** + * Maps {@code AbstractButton.getHorizontalTextPosition()} through queue + */ + public int getHorizontalTextPosition() { + return (runMapping(new MapIntegerAction("getHorizontalTextPosition") { + @Override + public int map() { + return ((AbstractButton) getSource()).getHorizontalTextPosition(); + } + })); + } + + /** + * Maps {@code AbstractButton.getIcon()} through queue + */ + public Icon getIcon() { + return (runMapping(new MapAction("getIcon") { + @Override + public Icon map() { + return ((AbstractButton) getSource()).getIcon(); + } + })); + } + + /** + * Maps {@code AbstractButton.getMargin()} through queue + */ + public Insets getMargin() { + return (runMapping(new MapAction("getMargin") { + @Override + public Insets map() { + return ((AbstractButton) getSource()).getMargin(); + } + })); + } + + /** + * Maps {@code AbstractButton.getMnemonic()} through queue + */ + public int getMnemonic() { + return (runMapping(new MapIntegerAction("getMnemonic") { + @Override + public int map() { + return ((AbstractButton) getSource()).getMnemonic(); + } + })); + } + + /** + * Maps {@code AbstractButton.getModel()} through queue + */ + public ButtonModel getModel() { + return (runMapping(new MapAction("getModel") { + @Override + public ButtonModel map() { + return ((AbstractButton) getSource()).getModel(); + } + })); + } + + /** + * Maps {@code AbstractButton.getPressedIcon()} through queue + */ + public Icon getPressedIcon() { + return (runMapping(new MapAction("getPressedIcon") { + @Override + public Icon map() { + return ((AbstractButton) getSource()).getPressedIcon(); + } + })); + } + + /** + * Maps {@code AbstractButton.getRolloverIcon()} through queue + */ + public Icon getRolloverIcon() { + return (runMapping(new MapAction("getRolloverIcon") { + @Override + public Icon map() { + return ((AbstractButton) getSource()).getRolloverIcon(); + } + })); + } + + /** + * Maps {@code AbstractButton.getRolloverSelectedIcon()} through queue + */ + public Icon getRolloverSelectedIcon() { + return (runMapping(new MapAction("getRolloverSelectedIcon") { + @Override + public Icon map() { + return ((AbstractButton) getSource()).getRolloverSelectedIcon(); + } + })); + } + + /** + * Maps {@code AbstractButton.getSelectedIcon()} through queue + */ + public Icon getSelectedIcon() { + return (runMapping(new MapAction("getSelectedIcon") { + @Override + public Icon map() { + return ((AbstractButton) getSource()).getSelectedIcon(); + } + })); + } + + /** + * Maps {@code AbstractButton.getSelectedObjects()} through queue + */ + public Object[] getSelectedObjects() { + return ((Object[]) runMapping(new MapAction("getSelectedObjects") { + @Override + public Object map() { + return ((AbstractButton) getSource()).getSelectedObjects(); + } + })); + } + + /** + * Maps {@code AbstractButton.getText()} through queue + */ + public String getText() { + return (runMapping(new MapAction("getText") { + @Override + public String map() { + return ((AbstractButton) getSource()).getText(); + } + })); + } + + /** + * Maps {@code AbstractButton.getUI()} through queue + */ + public ButtonUI getUI() { + return (runMapping(new MapAction("getUI") { + @Override + public ButtonUI map() { + return ((AbstractButton) getSource()).getUI(); + } + })); + } + + /** + * Maps {@code AbstractButton.getVerticalAlignment()} through queue + */ + public int getVerticalAlignment() { + return (runMapping(new MapIntegerAction("getVerticalAlignment") { + @Override + public int map() { + return ((AbstractButton) getSource()).getVerticalAlignment(); + } + })); + } + + /** + * Maps {@code AbstractButton.getVerticalTextPosition()} through queue + */ + public int getVerticalTextPosition() { + return (runMapping(new MapIntegerAction("getVerticalTextPosition") { + @Override + public int map() { + return ((AbstractButton) getSource()).getVerticalTextPosition(); + } + })); + } + + /** + * Maps {@code AbstractButton.isBorderPainted()} through queue + */ + public boolean isBorderPainted() { + return (runMapping(new MapBooleanAction("isBorderPainted") { + @Override + public boolean map() { + return ((AbstractButton) getSource()).isBorderPainted(); + } + })); + } + + /** + * Maps {@code AbstractButton.isContentAreaFilled()} through queue + */ + public boolean isContentAreaFilled() { + return (runMapping(new MapBooleanAction("isContentAreaFilled") { + @Override + public boolean map() { + return ((AbstractButton) getSource()).isContentAreaFilled(); + } + })); + } + + /** + * Maps {@code AbstractButton.isFocusPainted()} through queue + */ + public boolean isFocusPainted() { + return (runMapping(new MapBooleanAction("isFocusPainted") { + @Override + public boolean map() { + return ((AbstractButton) getSource()).isFocusPainted(); + } + })); + } + + /** + * Maps {@code AbstractButton.isRolloverEnabled()} through queue + */ + public boolean isRolloverEnabled() { + return (runMapping(new MapBooleanAction("isRolloverEnabled") { + @Override + public boolean map() { + return ((AbstractButton) getSource()).isRolloverEnabled(); + } + })); + } + + /** + * Maps {@code AbstractButton.isSelected()} through queue + */ + public boolean isSelected() { + return (runMapping(new MapBooleanAction("isSelected") { + @Override + public boolean map() { + return ((AbstractButton) getSource()).isSelected(); + } + })); + } + + /** + * Maps {@code AbstractButton.removeActionListener(ActionListener)} + * through queue + */ + public void removeActionListener(final ActionListener actionListener) { + runMapping(new MapVoidAction("removeActionListener") { + @Override + public void map() { + ((AbstractButton) getSource()).removeActionListener(actionListener); + } + }); + } + + /** + * Maps {@code AbstractButton.removeChangeListener(ChangeListener)} + * through queue + */ + public void removeChangeListener(final ChangeListener changeListener) { + runMapping(new MapVoidAction("removeChangeListener") { + @Override + public void map() { + ((AbstractButton) getSource()).removeChangeListener(changeListener); + } + }); + } + + /** + * Maps {@code AbstractButton.removeItemListener(ItemListener)} through queue + */ + public void removeItemListener(final ItemListener itemListener) { + runMapping(new MapVoidAction("removeItemListener") { + @Override + public void map() { + ((AbstractButton) getSource()).removeItemListener(itemListener); + } + }); + } + + /** + * Maps {@code AbstractButton.setActionCommand(String)} through queue + */ + public void setActionCommand(final String string) { + runMapping(new MapVoidAction("setActionCommand") { + @Override + public void map() { + ((AbstractButton) getSource()).setActionCommand(string); + } + }); + } + + /** + * Maps {@code AbstractButton.setBorderPainted(boolean)} through queue + */ + public void setBorderPainted(final boolean b) { + runMapping(new MapVoidAction("setBorderPainted") { + @Override + public void map() { + ((AbstractButton) getSource()).setBorderPainted(b); + } + }); + } + + /** + * Maps {@code AbstractButton.setContentAreaFilled(boolean)} through queue + */ + public void setContentAreaFilled(final boolean b) { + runMapping(new MapVoidAction("setContentAreaFilled") { + @Override + public void map() { + ((AbstractButton) getSource()).setContentAreaFilled(b); + } + }); + } + + /** + * Maps {@code AbstractButton.setDisabledIcon(Icon)} through queue + */ + public void setDisabledIcon(final Icon icon) { + runMapping(new MapVoidAction("setDisabledIcon") { + @Override + public void map() { + ((AbstractButton) getSource()).setDisabledIcon(icon); + } + }); + } + + /** + * Maps {@code AbstractButton.setDisabledSelectedIcon(Icon)} through queue + */ + public void setDisabledSelectedIcon(final Icon icon) { + runMapping(new MapVoidAction("setDisabledSelectedIcon") { + @Override + public void map() { + ((AbstractButton) getSource()).setDisabledSelectedIcon(icon); + } + }); + } + + /** + * Maps {@code AbstractButton.setFocusPainted(boolean)} through queue + */ + public void setFocusPainted(final boolean b) { + runMapping(new MapVoidAction("setFocusPainted") { + @Override + public void map() { + ((AbstractButton) getSource()).setFocusPainted(b); + } + }); + } + + /** + * Maps {@code AbstractButton.setHorizontalAlignment(int)} through queue + */ + public void setHorizontalAlignment(final int i) { + runMapping(new MapVoidAction("setHorizontalAlignment") { + @Override + public void map() { + ((AbstractButton) getSource()).setHorizontalAlignment(i); + } + }); + } + + /** + * Maps {@code AbstractButton.setHorizontalTextPosition(int)} through queue + */ + public void setHorizontalTextPosition(final int i) { + runMapping(new MapVoidAction("setHorizontalTextPosition") { + @Override + public void map() { + ((AbstractButton) getSource()).setHorizontalTextPosition(i); + } + }); + } + + /** + * Maps {@code AbstractButton.setIcon(Icon)} through queue + */ + public void setIcon(final Icon icon) { + runMapping(new MapVoidAction("setIcon") { + @Override + public void map() { + ((AbstractButton) getSource()).setIcon(icon); + } + }); + } + + /** + * Maps {@code AbstractButton.setMargin(Insets)} through queue + */ + public void setMargin(final Insets insets) { + runMapping(new MapVoidAction("setMargin") { + @Override + public void map() { + ((AbstractButton) getSource()).setMargin(insets); + } + }); + } + + /** + * Maps {@code AbstractButton.setMnemonic(char)} through queue + */ + public void setMnemonic(final char c) { + runMapping(new MapVoidAction("setMnemonic") { + @Override + public void map() { + ((AbstractButton) getSource()).setMnemonic(c); + } + }); + } + + /** + * Maps {@code AbstractButton.setMnemonic(int)} through queue + */ + public void setMnemonic(final int i) { + runMapping(new MapVoidAction("setMnemonic") { + @Override + public void map() { + ((AbstractButton) getSource()).setMnemonic(i); + } + }); + } + + /** + * Maps {@code AbstractButton.setModel(ButtonModel)} through queue + */ + public void setModel(final ButtonModel buttonModel) { + runMapping(new MapVoidAction("setModel") { + @Override + public void map() { + ((AbstractButton) getSource()).setModel(buttonModel); + } + }); + } + + /** + * Maps {@code AbstractButton.setPressedIcon(Icon)} through queue + */ + public void setPressedIcon(final Icon icon) { + runMapping(new MapVoidAction("setPressedIcon") { + @Override + public void map() { + ((AbstractButton) getSource()).setPressedIcon(icon); + } + }); + } + + /** + * Maps {@code AbstractButton.setRolloverEnabled(boolean)} through queue + */ + public void setRolloverEnabled(final boolean b) { + runMapping(new MapVoidAction("setRolloverEnabled") { + @Override + public void map() { + ((AbstractButton) getSource()).setRolloverEnabled(b); + } + }); + } + + /** + * Maps {@code AbstractButton.setRolloverIcon(Icon)} through queue + */ + public void setRolloverIcon(final Icon icon) { + runMapping(new MapVoidAction("setRolloverIcon") { + @Override + public void map() { + ((AbstractButton) getSource()).setRolloverIcon(icon); + } + }); + } + + /** + * Maps {@code AbstractButton.setRolloverSelectedIcon(Icon)} through queue + */ + public void setRolloverSelectedIcon(final Icon icon) { + runMapping(new MapVoidAction("setRolloverSelectedIcon") { + @Override + public void map() { + ((AbstractButton) getSource()).setRolloverSelectedIcon(icon); + } + }); + } + + /** + * Maps {@code AbstractButton.setSelected(boolean)} through queue + */ + public void setSelected(final boolean b) { + runMapping(new MapVoidAction("setSelected") { + @Override + public void map() { + ((AbstractButton) getSource()).setSelected(b); + } + }); + } + + /** + * Maps {@code AbstractButton.setSelectedIcon(Icon)} through queue + */ + public void setSelectedIcon(final Icon icon) { + runMapping(new MapVoidAction("setSelectedIcon") { + @Override + public void map() { + ((AbstractButton) getSource()).setSelectedIcon(icon); + } + }); + } + + /** + * Maps {@code AbstractButton.setText(String)} through queue + */ + public void setText(final String string) { + runMapping(new MapVoidAction("setText") { + @Override + public void map() { + ((AbstractButton) getSource()).setText(string); + } + }); + } + + /** + * Maps {@code AbstractButton.setUI(ButtonUI)} through queue + */ + public void setUI(final ButtonUI buttonUI) { + runMapping(new MapVoidAction("setUI") { + @Override + public void map() { + ((AbstractButton) getSource()).setUI(buttonUI); + } + }); + } + + /** + * Maps {@code AbstractButton.setVerticalAlignment(int)} through queue + */ + public void setVerticalAlignment(final int i) { + runMapping(new MapVoidAction("setVerticalAlignment") { + @Override + public void map() { + ((AbstractButton) getSource()).setVerticalAlignment(i); + } + }); + } + + /** + * Maps {@code AbstractButton.setVerticalTextPosition(int)} through queue + */ + public void setVerticalTextPosition(final int i) { + runMapping(new MapVoidAction("setVerticalTextPosition") { + @Override + public void map() { + ((AbstractButton) getSource()).setVerticalTextPosition(i); + } + }); + } + + //End of mapping // + //////////////////////////////////////////////////////// + /** + * Allows to find component by text. + */ + public static class AbstractButtonByLabelFinder implements ComponentChooser { + + String label; + StringComparator comparator; + + /** + * Constructs AbstractButtonByLabelFinder. + * + * @param lb a text pattern + * @param comparator specifies string comparision algorithm. + */ + public AbstractButtonByLabelFinder(String lb, StringComparator comparator) { + label = lb; + this.comparator = comparator; + } + + /** + * Constructs AbstractButtonByLabelFinder. + * + * @param lb a text pattern + */ + public AbstractButtonByLabelFinder(String lb) { + this(lb, Operator.getDefaultStringComparator()); + } + + @Override + public boolean checkComponent(Component comp) { + if (comp instanceof AbstractButton) { + if (((AbstractButton) comp).getText() != null) { + return (comparator.equals(((AbstractButton) comp).getText(), + label)); + } + } + return false; + } + + @Override + public String getDescription() { + return "AbstractButton with text \"" + label + "\""; + } + + @Override + public String toString() { + return "AbstractButtonByLabelFinder{" + "label=" + label + ", comparator=" + comparator + '}'; + } + } + + /** + * Checks component type. + */ + public static class AbstractButtonFinder extends Finder { + + /** + * Constructs AbstractButtonFinder. + * + * @param sf other searching criteria. + */ + public AbstractButtonFinder(ComponentChooser sf) { + super(AbstractButton.class, sf); + } + + /** + * Constructs AbstractButtonFinder. + */ + public AbstractButtonFinder() { + super(AbstractButton.class); + } + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/ButtonOperator.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/ButtonOperator.java new file mode 100644 index 00000000000..e1af1f252a0 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/ButtonOperator.java @@ -0,0 +1,530 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.operators; + +import java.awt.Button; +import java.awt.Component; +import java.awt.Container; +import java.awt.event.ActionListener; +import java.util.Hashtable; + +import org.netbeans.jemmy.ComponentChooser; +import org.netbeans.jemmy.Outputable; +import org.netbeans.jemmy.TestOut; +import org.netbeans.jemmy.TimeoutExpiredException; +import org.netbeans.jemmy.Timeoutable; +import org.netbeans.jemmy.Timeouts; +import org.netbeans.jemmy.drivers.ButtonDriver; +import org.netbeans.jemmy.drivers.DriverManager; + +/** + * + *

Timeouts used:
+ * ButtonOperator.PushButtonTimeout - time between button pressing and + * releasing
+ * ComponentOperator.WaitComponentTimeout - time to wait button displayed
+ * ComponentOperator.WaitComponentEnabledTimeout - time to wait button enabled + *
. + * + * @see org.netbeans.jemmy.Timeouts + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + * + */ +public class ButtonOperator extends ComponentOperator + implements Timeoutable, Outputable { + + /** + * Identifier for a label property. + * + * @see #getDump + */ + public static final String TEXT_DPROP = "Label"; + + private final static long PUSH_BUTTON_TIMEOUT = 0; + + private Timeouts timeouts; + private TestOut output; + + ButtonDriver driver; + + /** + * Constructor. + * + * @param b The {@code java.awt.Button} managed by this instance. + */ + public ButtonOperator(Button b) { + super(b); + driver = DriverManager.getButtonDriver(getClass()); + } + + /** + * Constructs a ButtonOperator object. + * + * @param cont container + * @param chooser a component chooser specifying searching criteria. + * @param index an index between appropriate ones. + */ + public ButtonOperator(ContainerOperator cont, ComponentChooser chooser, int index) { + this((Button) cont. + waitSubComponent(new ButtonFinder(chooser), + index)); + copyEnvironment(cont); + } + + /** + * Constructs a ButtonOperator object. + * + * @param cont container + * @param chooser a component chooser specifying searching criteria. + */ + public ButtonOperator(ContainerOperator cont, ComponentChooser chooser) { + this(cont, chooser, 0); + } + + /** + * Constructor. Waits for a component in a container to show. The component + * is identified as the {@code index+1}'th {@code java.awt.Button} + * that shows, lies below the container in the display containment + * hierarchy, and that has the desired text. Uses cont's timeout and output + * for waiting and to init this operator. + * + * @param cont The operator for a container containing the sought for + * button. + * @param text Button text. + * @param index Ordinal component index. The first component has + * {@code index} 0. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public ButtonOperator(ContainerOperator cont, String text, int index) { + this((Button) waitComponent(cont, + new ButtonByLabelFinder(text, + cont.getComparator()), + index)); + copyEnvironment(cont); + } + + /** + * Constructor. Waits for a component in a container to show. The component + * is identified as the first {@code java.awt.Button} that shows, lies + * below the container in the display containment hierarchy, and that has + * the desired text. Uses cont's timeout and output for waiting and to init + * this operator. + * + * @param cont The operator for a container containing the sought for + * button. + * @param text Button text. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public ButtonOperator(ContainerOperator cont, String text) { + this(cont, text, 0); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont The operator for a container containing the sought for + * button. + * @param index Ordinal component index. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public ButtonOperator(ContainerOperator cont, int index) { + this((Button) waitComponent(cont, + new ButtonFinder(), + index)); + copyEnvironment(cont); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont The operator for a container containing the sought for + * button. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public ButtonOperator(ContainerOperator cont) { + this(cont, 0); + } + + /** + * Searches Button in a container. + * + * @param cont Container in which to search for the component. The container + * lies above the component in the display containment hierarchy. The + * containment need not be direct. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation, + * defining and applying search criteria. + * @param index Ordinal component index. The first {@code index} is 0. + * @return Button instance or null if component was not found. + */ + public static Button findButton(Container cont, ComponentChooser chooser, int index) { + return (Button) findComponent(cont, new ButtonFinder(chooser), index); + } + + /** + * Searches for the first Button in a container. + * + * @param cont Container in which to search for the component. The container + * lies above the component in the display containment hierarchy. The + * containment need not be direct. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation, + * defining and applying search criteria. + * @return Button instance or null if component was not found. + */ + public static Button findButton(Container cont, ComponentChooser chooser) { + return findButton(cont, chooser, 0); + } + + /** + * Searches Button by text. + * + * @param cont Container to search component in. + * @param text Button text. If null, contents is not checked. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @param index Ordinal component index. + * @return Button instance or null if component was not found. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + */ + public static Button findButton(Container cont, String text, boolean ce, boolean ccs, int index) { + return findButton(cont, new ButtonByLabelFinder(text, new DefaultStringComparator(ce, ccs)), index); + } + + /** + * Searches Button by text. + * + * @param cont Container to search component in. + * @param text Button text. If null, contents is not checked. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @return Button instance or null if component was not found. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + */ + public static Button findButton(Container cont, String text, boolean ce, boolean ccs) { + return findButton(cont, text, ce, ccs, 0); + } + + /** + * Waits Button in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @param index Ordinal component index. + * @return Button instance. + * @throws TimeoutExpiredException + */ + public static Button waitButton(Container cont, ComponentChooser chooser, int index) { + return (Button) waitComponent(cont, new ButtonFinder(chooser), index); + } + + /** + * Waits 0'th Button in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @return Button instance. + * @throws TimeoutExpiredException + */ + public static Button waitButton(Container cont, ComponentChooser chooser) { + return waitButton(cont, chooser, 0); + } + + /** + * Waits Button by text. + * + * @param cont Container to search component in. + * @param text Button text. If null, contents is not checked. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @param index Ordinal component index. + * @return Button instance. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public static Button waitButton(Container cont, String text, boolean ce, boolean ccs, int index) { + return waitButton(cont, new ButtonByLabelFinder(text, new DefaultStringComparator(ce, ccs)), index); + } + + /** + * Waits Button by text. + * + * @param cont Container to search component in. + * @param text Button text. If null, contents is not checked. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @return Button instance. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public static Button waitButton(Container cont, String text, boolean ce, boolean ccs) { + return waitButton(cont, text, ce, ccs, 0); + } + + static { + Timeouts.initDefault("ButtonOperator.PushButtonTimeout", PUSH_BUTTON_TIMEOUT); + } + + @Override + public void setTimeouts(Timeouts timeouts) { + super.setTimeouts(timeouts); + this.timeouts = timeouts; + } + + @Override + public Timeouts getTimeouts() { + return timeouts; + } + + @Override + public void setOutput(TestOut out) { + output = out; + super.setOutput(output.createErrorOutput()); + } + + @Override + public TestOut getOutput() { + return output; + } + + @Override + public void copyEnvironment(Operator anotherOperator) { + super.copyEnvironment(anotherOperator); + driver + = (ButtonDriver) DriverManager. + getDriver(DriverManager.BUTTON_DRIVER_ID, + getClass(), + anotherOperator.getProperties()); + } + + /** + * Pushes the button by mouse click. + * + * @throws TimeoutExpiredException + */ + public void push() { + output.printLine("Push button\n :" + toStringSource()); + output.printGolden("Push button"); + driver.push(this); + } + + /** + * Runs {@code push()} method in a separate thread. + */ + public void pushNoBlock() { + produceNoBlocking(new NoBlockingAction("Button pushing") { + @Override + public Void doAction(Void param) { + push(); + return null; + } + }); + } + + /** + * Press the button by mouse. + * + * @throws TimeoutExpiredException + */ + public void press() { + output.printLine("Press button\n :" + toStringSource()); + output.printGolden("Press button"); + driver.press(this); + } + + /** + * Releases the button by mouse. + * + * @throws TimeoutExpiredException + */ + public void release() { + output.printLine("Release button\n :" + toStringSource()); + output.printGolden("Release button"); + driver.press(this); + } + + /** + * Returns information about component. + */ + @Override + public Hashtable getDump() { + Hashtable result = super.getDump(); + if (((Button) getSource()).getLabel() != null) { + result.put(TEXT_DPROP, ((Button) getSource()).getLabel()); + } + return result; + } + + //////////////////////////////////////////////////////// + //Mapping // + /** + * Maps {@code Button.addActionListener(ActionListener)} through queue + */ + public void addActionListener(final ActionListener actionListener) { + runMapping(new MapVoidAction("addActionListener") { + @Override + public void map() { + ((Button) getSource()).addActionListener(actionListener); + } + }); + } + + /** + * Maps {@code Button.getActionCommand()} through queue + */ + public String getActionCommand() { + return (runMapping(new MapAction("getActionCommand") { + @Override + public String map() { + return ((Button) getSource()).getActionCommand(); + } + })); + } + + /** + * Maps {@code Button.getLabel()} through queue + */ + public String getLabel() { + return (runMapping(new MapAction("getLabel") { + @Override + public String map() { + return ((Button) getSource()).getLabel(); + } + })); + } + + /** + * Maps {@code Button.removeActionListener(ActionListener)} through queue + */ + public void removeActionListener(final ActionListener actionListener) { + runMapping(new MapVoidAction("removeActionListener") { + @Override + public void map() { + ((Button) getSource()).removeActionListener(actionListener); + } + }); + } + + /** + * Maps {@code Button.setActionCommand(String)} through queue + */ + public void setActionCommand(final String string) { + runMapping(new MapVoidAction("setActionCommand") { + @Override + public void map() { + ((Button) getSource()).setActionCommand(string); + } + }); + } + + /** + * Maps {@code Button.setLabel(String)} through queue + */ + public void setLabel(final String string) { + runMapping(new MapVoidAction("setLabel") { + @Override + public void map() { + ((Button) getSource()).setLabel(string); + } + }); + } + + //End of mapping // + //////////////////////////////////////////////////////// + /** + * Allows to find component by label. + */ + public static class ButtonByLabelFinder implements ComponentChooser { + + String label; + StringComparator comparator; + + /** + * Constructs ButtonByLabelFinder. + * + * @param lb a text pattern + * @param comparator specifies string comparision algorithm. + */ + public ButtonByLabelFinder(String lb, StringComparator comparator) { + label = lb; + this.comparator = comparator; + } + + /** + * Constructs ButtonByLabelFinder. + * + * @param lb a text pattern + */ + public ButtonByLabelFinder(String lb) { + this(lb, Operator.getDefaultStringComparator()); + } + + @Override + public boolean checkComponent(Component comp) { + if (comp instanceof Button) { + if (((Button) comp).getLabel() != null) { + return (comparator.equals(((Button) comp).getLabel(), + label)); + } + } + return false; + } + + @Override + public String getDescription() { + return "Button with label \"" + label + "\""; + } + + @Override + public String toString() { + return "ButtonByLabelFinder{" + "label=" + label + ", comparator=" + comparator + '}'; + } + } + + /** + * Checks component type. + */ + public static class ButtonFinder extends Finder { + + /** + * Constructs AbstractButtonFinder. + * + * @param sf other searching criteria. + */ + public ButtonFinder(ComponentChooser sf) { + super(Button.class, sf); + } + + /** + * Constructs AbstractButtonFinder. + */ + public ButtonFinder() { + super(Button.class); + } + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/CheckboxOperator.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/CheckboxOperator.java new file mode 100644 index 00000000000..bdda5e63683 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/CheckboxOperator.java @@ -0,0 +1,540 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.operators; + +import java.awt.Checkbox; +import java.awt.CheckboxGroup; +import java.awt.Component; +import java.awt.Container; +import java.awt.event.ItemListener; +import java.util.Hashtable; + +import org.netbeans.jemmy.ComponentChooser; +import org.netbeans.jemmy.JemmyException; +import org.netbeans.jemmy.Outputable; +import org.netbeans.jemmy.TestOut; +import org.netbeans.jemmy.TimeoutExpiredException; +import org.netbeans.jemmy.drivers.ButtonDriver; +import org.netbeans.jemmy.drivers.DriverManager; + +/** + * + *

Timeouts used:
+ * ButtonOperator.PushButtonTimeout - time between checkbox pressing and + * releasing
+ * ComponentOperator.WaitComponentTimeout - time to wait checkbox displayed
+ * ComponentOperator.WaitComponentEnabledTimeout - time to wait checkbox enabled + *
. + * + * @see org.netbeans.jemmy.Timeouts + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + * + */ +public class CheckboxOperator extends ComponentOperator implements Outputable { + + /** + * Identifier for a label property. + * + * @see #getDump + */ + public static final String TEXT_DPROP = "Label"; + + private TestOut output; + ButtonDriver driver; + + /** + * Constructor. + * + * @param b a component + */ + public CheckboxOperator(Checkbox b) { + super(b); + driver = DriverManager.getButtonDriver(getClass()); + } + + /** + * Constructs an CheckboxOperator object. + * + * @param cont container + * @param chooser a component chooser specifying searching criteria. + * @param index an index between appropriate ones. + */ + public CheckboxOperator(ContainerOperator cont, ComponentChooser chooser, int index) { + this((Checkbox) cont. + waitSubComponent(new CheckboxFinder(chooser), + index)); + copyEnvironment(cont); + } + + /** + * Constructs an CheckboxOperator object. + * + * @param cont container + * @param chooser a component chooser specifying searching criteria. + */ + public CheckboxOperator(ContainerOperator cont, ComponentChooser chooser) { + this(cont, chooser, 0); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont container + * @param text Checkbox text. + * @param index Ordinal component index. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public CheckboxOperator(ContainerOperator cont, String text, int index) { + this((Checkbox) waitComponent(cont, + new CheckboxByLabelFinder(text, + cont.getComparator()), + index)); + copyEnvironment(cont); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont container + * @param text Checkbox text. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public CheckboxOperator(ContainerOperator cont, String text) { + this(cont, text, 0); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont container + * @param index Ordinal component index. + * @throws TimeoutExpiredException + */ + public CheckboxOperator(ContainerOperator cont, int index) { + this((Checkbox) waitComponent(cont, + new CheckboxFinder(), + index)); + copyEnvironment(cont); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont container + * @throws TimeoutExpiredException + */ + public CheckboxOperator(ContainerOperator cont) { + this(cont, 0); + } + + /** + * Searches Checkbox in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @param index Ordinal component index. + * @return Checkbox instance or null if component was not found. + */ + public static Checkbox findCheckbox(Container cont, ComponentChooser chooser, int index) { + return (Checkbox) findComponent(cont, new CheckboxFinder(chooser), index); + } + + /** + * Searches 0'th Checkbox in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @return Checkbox instance or null if component was not found. + */ + public static Checkbox findCheckbox(Container cont, ComponentChooser chooser) { + return findCheckbox(cont, chooser, 0); + } + + /** + * Searches Checkbox by text. + * + * @param cont Container to search component in. + * @param text Checkbox text. If null, contents is not checked. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @param index Ordinal component index. + * @return Checkbox instance or null if component was not found. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + */ + public static Checkbox findCheckbox(Container cont, String text, boolean ce, boolean ccs, int index) { + return (findCheckbox(cont, + new CheckboxByLabelFinder(text, + new DefaultStringComparator(ce, ccs)), + index)); + } + + /** + * Searches Checkbox by text. + * + * @param cont Container to search component in. + * @param text Checkbox text. If null, contents is not checked. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @return Checkbox instance or null if component was not found. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + */ + public static Checkbox findCheckbox(Container cont, String text, boolean ce, boolean ccs) { + return findCheckbox(cont, text, ce, ccs, 0); + } + + /** + * Waits Checkbox in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @param index Ordinal component index. + * @return Checkbox instance. + * @throws TimeoutExpiredException + */ + public static Checkbox waitCheckbox(Container cont, ComponentChooser chooser, int index) { + return (Checkbox) waitComponent(cont, new CheckboxFinder(chooser), index); + } + + /** + * Waits 0'th Checkbox in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @return Checkbox instance. + * @throws TimeoutExpiredException + */ + public static Checkbox waitCheckbox(Container cont, ComponentChooser chooser) { + return waitCheckbox(cont, chooser, 0); + } + + /** + * Waits Checkbox by text. + * + * @param cont Container to search component in. + * @param text Checkbox text. If null, contents is not checked. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @param index Ordinal component index. + * @return Checkbox instance. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public static Checkbox waitCheckbox(Container cont, String text, boolean ce, boolean ccs, int index) { + return (waitCheckbox(cont, + new CheckboxByLabelFinder(text, + new DefaultStringComparator(ce, ccs)), + index)); + } + + /** + * Waits Checkbox by text. + * + * @param cont Container to search component in. + * @param text Checkbox text. If null, contents is not checked. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @return Checkbox instance. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public static Checkbox waitCheckbox(Container cont, String text, boolean ce, boolean ccs) { + return waitCheckbox(cont, text, ce, ccs, 0); + } + + @Override + public void setOutput(TestOut out) { + output = out; + super.setOutput(output.createErrorOutput()); + } + + @Override + public TestOut getOutput() { + return output; + } + + @Override + public void copyEnvironment(Operator anotherOperator) { + super.copyEnvironment(anotherOperator); + driver + = (ButtonDriver) DriverManager. + getDriver(DriverManager.BUTTON_DRIVER_ID, + getClass(), + anotherOperator.getProperties()); + } + + /** + * Changes selection if necessary. Uses a ButtonDriver registered for this + * operator. + * + * @param newValue a button selection. + */ + public void changeSelection(boolean newValue) { + makeComponentVisible(); + if (getState() != newValue) { + try { + waitComponentEnabled(); + } catch (InterruptedException e) { + throw (new JemmyException("Interrupted!", e)); + } + output.printLine("Change checkbox selection to " + (newValue ? "true" : "false") + + "\n :" + toStringSource()); + output.printGolden("Change checkbox selection to " + (newValue ? "true" : "false")); + driver.push(this); + if (getVerification()) { + waitSelected(newValue); + } + } + } + + /** + * Runs {@code changeSelection(boolean)} method in a separate thread. + * + * @param selected a button selection. + */ + public void changeSelectionNoBlock(boolean selected) { + produceNoBlocking(new NoBlockingAction("Button selection changing") { + @Override + public Void doAction(Boolean param) { + changeSelection(param); + return null; + } + }, selected ? Boolean.TRUE : Boolean.FALSE); + } + + /** + * Waits for button to be selected. + * + * @param selected selection. + */ + public void waitSelected(final boolean selected) { + getOutput().printLine("Wait button to be selected \n : " + + toStringSource()); + getOutput().printGolden("Wait button to be selected"); + waitState(new ComponentChooser() { + @Override + public boolean checkComponent(Component comp) { + return getState() == selected; + } + + @Override + public String getDescription() { + return ("Items has been " + + (selected ? "" : "un") + "selected"); + } + + @Override + public String toString() { + return "CheckboxOperator.waitSelected.ComponentChooser{description = " + getDescription() + '}'; + } + }); + } + + /** + * Returns information about component. + */ + @Override + public Hashtable getDump() { + Hashtable result = super.getDump(); + result.put(TEXT_DPROP, ((Checkbox) getSource()).getLabel()); + return result; + } + + //////////////////////////////////////////////////////// + //Mapping // + /** + * Maps {@code Checkbox.addItemListener(ItemListener)} through queue + */ + public void addItemListener(final ItemListener itemListener) { + runMapping(new MapVoidAction("addItemListener") { + @Override + public void map() { + ((Checkbox) getSource()).addItemListener(itemListener); + } + }); + } + + /** + * Maps {@code Checkbox.getCheckboxGroup()} through queue + */ + public CheckboxGroup getCheckboxGroup() { + return (runMapping(new MapAction("getCheckboxGroup") { + @Override + public CheckboxGroup map() { + return ((Checkbox) getSource()).getCheckboxGroup(); + } + })); + } + + /** + * Maps {@code Checkbox.getLabel()} through queue + */ + public String getLabel() { + return (runMapping(new MapAction("getLabel") { + @Override + public String map() { + return ((Checkbox) getSource()).getLabel(); + } + })); + } + + /** + * Maps {@code Checkbox.getState()} through queue + */ + public boolean getState() { + return (runMapping(new MapBooleanAction("getState") { + @Override + public boolean map() { + return ((Checkbox) getSource()).getState(); + } + })); + } + + /** + * Maps {@code Checkbox.removeItemListener(ItemListener)} through queue + */ + public void removeItemListener(final ItemListener itemListener) { + runMapping(new MapVoidAction("removeItemListener") { + @Override + public void map() { + ((Checkbox) getSource()).removeItemListener(itemListener); + } + }); + } + + /** + * Maps {@code Checkbox.setCheckboxGroup(CheckboxGroup)} through queue + */ + public void setCheckboxGroup(final CheckboxGroup grp) { + runMapping(new MapVoidAction("setCheckboxGroup") { + @Override + public void map() { + ((Checkbox) getSource()).setCheckboxGroup(grp); + } + }); + } + + /** + * Maps {@code Checkbox.setLabel(String)} through queue + */ + public void setLabel(final String string) { + runMapping(new MapVoidAction("setLabel") { + @Override + public void map() { + ((Checkbox) getSource()).setLabel(string); + } + }); + } + + /** + * Maps {@code Checkbox.setState(boolean)} through queue + */ + public void setState(final boolean state) { + runMapping(new MapVoidAction("setState") { + @Override + public void map() { + ((Checkbox) getSource()).setState(state); + } + }); + } + + //End of mapping // + //////////////////////////////////////////////////////// + /** + * Allows to find component by label. + */ + public static class CheckboxByLabelFinder implements ComponentChooser { + + String label; + StringComparator comparator; + + /** + * Constructs CheckboxByLabelFinder. + * + * @param lb a label pattern + * @param comparator specifies string comparision algorithm. + */ + public CheckboxByLabelFinder(String lb, StringComparator comparator) { + label = lb; + this.comparator = comparator; + } + + /** + * Constructs CheckboxByLabelFinder. + * + * @param lb a label pattern + */ + public CheckboxByLabelFinder(String lb) { + this(lb, Operator.getDefaultStringComparator()); + } + + @Override + public boolean checkComponent(Component comp) { + if (comp instanceof Checkbox) { + if (((Checkbox) comp).getLabel() != null) { + return (comparator.equals(((Checkbox) comp).getLabel(), + label)); + } + } + return false; + } + + @Override + public String getDescription() { + return "Checkbox with label \"" + label + "\""; + } + + @Override + public String toString() { + return "CheckboxByLabelFinder{" + "label=" + label + ", comparator=" + comparator + '}'; + } + } + + /** + * Checks component type. + */ + public static class CheckboxFinder extends Finder { + + /** + * Constructs CheckboxFinder. + * + * @param sf other searching criteria. + */ + public CheckboxFinder(ComponentChooser sf) { + super(Checkbox.class, sf); + } + + /** + * Constructs CheckboxFinder. + */ + public CheckboxFinder() { + super(Checkbox.class); + } + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/ChoiceOperator.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/ChoiceOperator.java new file mode 100644 index 00000000000..0f22ac00441 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/ChoiceOperator.java @@ -0,0 +1,667 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.operators; + +import java.awt.Choice; +import java.awt.Component; +import java.awt.Container; +import java.awt.event.ItemListener; +import java.util.Hashtable; + +import org.netbeans.jemmy.ComponentChooser; +import org.netbeans.jemmy.JemmyException; +import org.netbeans.jemmy.Outputable; +import org.netbeans.jemmy.TestOut; +import org.netbeans.jemmy.TimeoutExpiredException; +import org.netbeans.jemmy.drivers.DriverManager; +import org.netbeans.jemmy.drivers.ListDriver; + +/** + * + *

Timeouts used:
+ * ButtonOperator.PushButtonTimeout - time between choice pressing and + * releasing
+ * ComponentOperator.WaitComponentTimeout - time to wait choice displayed
+ * ComponentOperator.WaitComponentEnabledTimeout - time to wait choice enabled + *
. + * + * @see org.netbeans.jemmy.Timeouts + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + * + */ +public class ChoiceOperator extends ComponentOperator implements Outputable { + + /** + * Identifier for a selected item property. + * + * @see #getDump + */ + public static final String SELECTED_ITEM_DPROP = "Selected item"; + + /** + * Identifier for a items properties. + * + * @see #getDump + */ + public static final String ITEM_PREFIX_DPROP = "Item"; + + private TestOut output; + private ListDriver driver; + + /** + * Constructor. + * + * @param b a component + */ + public ChoiceOperator(Choice b) { + super(b); + driver = DriverManager.getListDriver(getClass()); + } + + /** + * Constructs a ChoiceOperator object. + * + * @param cont container + * @param chooser a component chooser specifying searching criteria. + * @param index an index between appropriate ones. + */ + public ChoiceOperator(ContainerOperator cont, ComponentChooser chooser, int index) { + this((Choice) cont. + waitSubComponent(new ChoiceFinder(chooser), + index)); + copyEnvironment(cont); + } + + /** + * Constructs a ChoiceOperator object. + * + * @param cont container + * @param chooser a component chooser specifying searching criteria. + */ + public ChoiceOperator(ContainerOperator cont, ComponentChooser chooser) { + this(cont, chooser, 0); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont container + * @param text Choice text. + * @param index Ordinal component index. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public ChoiceOperator(ContainerOperator cont, String text, int index) { + this((Choice) waitComponent(cont, + new ChoiceBySelectedItemFinder(text, + cont.getComparator()), + index)); + copyEnvironment(cont); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont container + * @param text Choice text. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public ChoiceOperator(ContainerOperator cont, String text) { + this(cont, text, 0); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont container + * @param index Ordinal component index. + * @throws TimeoutExpiredException + */ + public ChoiceOperator(ContainerOperator cont, int index) { + this((Choice) waitComponent(cont, + new ChoiceFinder(), + index)); + copyEnvironment(cont); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont container + * @throws TimeoutExpiredException + */ + public ChoiceOperator(ContainerOperator cont) { + this(cont, 0); + } + + /** + * Searches Choice in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @param index Ordinal component index. + * @return Choice instance or null if component was not found. + */ + public static Choice findChoice(Container cont, ComponentChooser chooser, int index) { + return (Choice) findComponent(cont, new ChoiceFinder(chooser), index); + } + + /** + * Searches 0'th Choice in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @return Choice instance or null if component was not found. + */ + public static Choice findChoice(Container cont, ComponentChooser chooser) { + return findChoice(cont, chooser, 0); + } + + /** + * Searches Choice by text. + * + * @param cont Container to search component in. + * @param text Choice text. If null, contents is not checked. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @param index Ordinal component index. + * @return Choice instance or null if component was not found. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + */ + public static Choice findChoice(Container cont, String text, boolean ce, boolean ccs, int index) { + return (findChoice(cont, + new ChoiceBySelectedItemFinder(text, + new DefaultStringComparator(ce, ccs)), + index)); + } + + /** + * Searches Choice by text. + * + * @param cont Container to search component in. + * @param text Choice text. If null, contents is not checked. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @return Choice instance or null if component was not found. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + */ + public static Choice findChoice(Container cont, String text, boolean ce, boolean ccs) { + return findChoice(cont, text, ce, ccs, 0); + } + + /** + * Waits Choice in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @param index Ordinal component index. + * @return Choice instance. + * @throws TimeoutExpiredException + */ + public static Choice waitChoice(Container cont, ComponentChooser chooser, int index) { + return (Choice) waitComponent(cont, new ChoiceFinder(chooser), index); + } + + /** + * Waits 0'th Choice in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @return Choice instance. + * @throws TimeoutExpiredException + */ + public static Choice waitChoice(Container cont, ComponentChooser chooser) { + return waitChoice(cont, chooser, 0); + } + + /** + * Waits Choice by text. + * + * @param cont Container to search component in. + * @param text Choice text. If null, contents is not checked. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @param index Ordinal component index. + * @return Choice instance. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public static Choice waitChoice(Container cont, String text, boolean ce, boolean ccs, int index) { + return (waitChoice(cont, + new ChoiceBySelectedItemFinder(text, + new DefaultStringComparator(ce, ccs)), + index)); + } + + /** + * Waits Choice by text. + * + * @param cont Container to search component in. + * @param text Choice text. If null, contents is not checked. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @return Choice instance. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public static Choice waitChoice(Container cont, String text, boolean ce, boolean ccs) { + return waitChoice(cont, text, ce, ccs, 0); + } + + @Override + public void setOutput(TestOut out) { + output = out; + super.setOutput(output.createErrorOutput()); + } + + @Override + public TestOut getOutput() { + return output; + } + + @Override + public void copyEnvironment(Operator anotherOperator) { + super.copyEnvironment(anotherOperator); + driver + = (ListDriver) DriverManager. + getDriver(DriverManager.LIST_DRIVER_ID, + getClass(), + anotherOperator.getProperties()); + } + + /** + * Finds an item between choice items. + * + * @param item a text pattern. + * @param index an ordinal index between appropriate items. + * @return an item index. + */ + public int findItemIndex(String item, int index) { + return findItemIndex(item, getComparator(), index); + } + + /** + * Finds an item between choice items. + * + * @param item a text pattern. + * @return an item index. + */ + public int findItemIndex(String item) { + return findItemIndex(item, 0); + } + + /** + * Selects an item by text. + * + * @param item a text pattern. + * @param index an ordinal index between appropriate items. + */ + public void selectItem(String item, int index) { + selectItem(item, getComparator(), index); + } + + /** + * Selects an item by text. + * + * @param item a text pattern. + */ + public void selectItem(String item) { + selectItem(item, 0); + } + + /** + * Selects an item by index. + * + * @param index an item index. + */ + public void selectItem(int index) { + output.printLine("Select " + Integer.toString(index) + "`th item in combobox\n : " + + toStringSource()); + output.printGolden("Select " + Integer.toString(index) + "`th item in combobox"); + makeComponentVisible(); + try { + waitComponentEnabled(); + } catch (InterruptedException e) { + throw (new JemmyException("Interrupted!", e)); + } + driver.selectItem(this, index); + if (getVerification()) { + waitItemSelected(index); + } + } + + /** + * Waits for item to be selected. + * + * @param index Item index. + */ + public void waitItemSelected(final int index) { + getOutput().printLine("Wait " + Integer.toString(index) + + "'th item to be selected in component \n : " + + toStringSource()); + getOutput().printGolden("Wait " + Integer.toString(index) + + "'th item to be selected"); + waitState(new ComponentChooser() { + @Override + public boolean checkComponent(Component comp) { + return getSelectedIndex() == index; + } + + @Override + public String getDescription() { + return "Has " + Integer.toString(index) + "'th item selected"; + } + + @Override + public String toString() { + return "ChoiceOperator.waitItemSelected.ComponentChooser{description = " + getDescription() + '}'; + } + }); + } + + /** + * Returns information about component. + */ + @Override + public Hashtable getDump() { + Hashtable result = super.getDump(); + if (((Choice) getSource()).getSelectedItem() != null) { + result.put(SELECTED_ITEM_DPROP, ((Choice) getSource()).getSelectedItem()); + } + String[] items = new String[((Choice) getSource()).getItemCount()]; + for (int i = 0; i < ((Choice) getSource()).getItemCount(); i++) { + items[i] = ((Choice) getSource()).getItem(i); + } + addToDump(result, ITEM_PREFIX_DPROP, items); + return result; + } + + //////////////////////////////////////////////////////// + //Mapping // + /** + * Maps {@code Choice.add(String)} through queue + */ + public void add(final String item) { + runMapping(new MapVoidAction("add") { + @Override + public void map() { + ((Choice) getSource()).add(item); + } + }); + } + + /** + * Maps {@code Choice.addItemListener(ItemListener)} through queue + */ + public void addItemListener(final ItemListener itemListener) { + runMapping(new MapVoidAction("addItemListener") { + @Override + public void map() { + ((Choice) getSource()).addItemListener(itemListener); + } + }); + } + + /** + * Maps {@code Choice.addNotify()} through queue + */ + @Override + public void addNotify() { + runMapping(new MapVoidAction("addNotify") { + @Override + public void map() { + getSource().addNotify(); + } + }); + } + + /** + * Maps {@code Choice.getItem(int)} through queue + */ + public String getItem(final int index) { + return (runMapping(new MapAction("getItem") { + @Override + public String map() { + return ((Choice) getSource()).getItem(index); + } + })); + } + + /** + * Maps {@code Choice.getItemCount()} through queue + */ + public int getItemCount() { + return (runMapping(new MapIntegerAction("getItemCount") { + @Override + public int map() { + return ((Choice) getSource()).getItemCount(); + } + })); + } + + /** + * Maps {@code Choice.getSelectedIndex()} through queue + */ + public int getSelectedIndex() { + return (runMapping(new MapIntegerAction("getSelectedIndex") { + @Override + public int map() { + return ((Choice) getSource()).getSelectedIndex(); + } + })); + } + + /** + * Maps {@code Choice.getSelectedItem()} through queue + */ + public String getSelectedItem() { + return (runMapping(new MapAction("getSelectedItem") { + @Override + public String map() { + return ((Choice) getSource()).getSelectedItem(); + } + })); + } + + /** + * Maps {@code Choice.insert(String)} through queue + */ + public void insert(final String item, final int index) { + runMapping(new MapVoidAction("insert") { + @Override + public void map() { + ((Choice) getSource()).insert(item, index); + } + }); + } + + /** + * Maps {@code Choice.remove(int)} through queue + */ + public void remove(final int position) { + runMapping(new MapVoidAction("remove") { + @Override + public void map() { + ((Choice) getSource()).remove(position); + } + }); + } + + /** + * Maps {@code Choice.remove(String)} through queue + */ + public void remove(final String item) { + runMapping(new MapVoidAction("remove") { + @Override + public void map() { + ((Choice) getSource()).remove(item); + } + }); + } + + /** + * Maps {@code Choice.removeAll()} through queue + */ + public void removeAll() { + runMapping(new MapVoidAction("removeAll") { + @Override + public void map() { + ((Choice) getSource()).removeAll(); + } + }); + } + + /** + * Maps {@code Choice.removeItemListener(ItemListener)} through queue + */ + public void removeItemListener(final ItemListener itemListener) { + runMapping(new MapVoidAction("removeItemListener") { + @Override + public void map() { + ((Choice) getSource()).removeItemListener(itemListener); + } + }); + } + + /** + * Maps {@code Choice.select(int)} through queue + */ + public void select(final int pos) { + runMapping(new MapVoidAction("select") { + @Override + public void map() { + ((Choice) getSource()).select(pos); + } + }); + } + + /** + * Maps {@code Choice.select(String)} through queue + */ + public void setState(final String str) { + runMapping(new MapVoidAction("select") { + @Override + public void map() { + ((Choice) getSource()).select(str); + } + }); + } + + //End of mapping // + //////////////////////////////////////////////////////// + private int findItemIndex(String item, StringComparator comparator, int index) { + int count = 0; + for (int i = 0; i < getItemCount(); i++) { + if (comparator.equals(getItem(i), item)) { + if (count == index) { + return i; + } else { + count++; + } + } + } + return -1; + } + + private void selectItem(String item, StringComparator comparator, int index) { + selectItem(findItemIndex(item, comparator, index)); + } + + /** + * Allows to find component by label. + */ + public static class ChoiceBySelectedItemFinder implements ComponentChooser { + + String label; + StringComparator comparator; + + /** + * Constructs ChoiceBySelectedItemFinder. + * + * @param lb a text pattern + * @param comparator specifies string comparision algorithm. + */ + public ChoiceBySelectedItemFinder(String lb, StringComparator comparator) { + label = lb; + this.comparator = comparator; + } + + /** + * Constructs ChoiceBySelectedItemFinder. + * + * @param lb a text pattern + */ + public ChoiceBySelectedItemFinder(String lb) { + this(lb, Operator.getDefaultStringComparator()); + } + + @Override + public boolean checkComponent(Component comp) { + if (comp instanceof Choice) { + if (((Choice) comp).getSelectedItem() != null) { + return (comparator.equals(((Choice) comp).getSelectedItem(), + label)); + } + } + return false; + } + + @Override + public String getDescription() { + return "Choice with label \"" + label + "\""; + } + + @Override + public String toString() { + return "ChoiceBySelectedItemFinder{" + "label=" + label + ", comparator=" + comparator + '}'; + } + } + + /** + * Checks component type. + */ + public static class ChoiceFinder extends Finder { + + /** + * Constructs ChoiceFinder. + * + * @param sf other searching criteria. + */ + public ChoiceFinder(ComponentChooser sf) { + super(Choice.class, sf); + } + + /** + * Constructs ChoiceFinder. + */ + public ChoiceFinder() { + super(Choice.class); + } + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/ComponentOperator.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/ComponentOperator.java new file mode 100644 index 00000000000..804ad6e4449 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/ComponentOperator.java @@ -0,0 +1,2535 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.operators; + +import java.awt.AWTEvent; +import java.awt.Color; +import java.awt.Component; +import java.awt.ComponentOrientation; +import java.awt.Container; +import java.awt.Cursor; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Graphics; +import java.awt.Image; +import java.awt.MenuComponent; +import java.awt.Point; +import java.awt.PopupMenu; +import java.awt.Rectangle; +import java.awt.Toolkit; +import java.awt.Window; +import java.awt.dnd.DropTarget; +import java.awt.event.ComponentListener; +import java.awt.event.FocusListener; +import java.awt.event.InputMethodListener; +import java.awt.event.KeyListener; +import java.awt.event.MouseListener; +import java.awt.event.MouseMotionListener; +import java.awt.im.InputContext; +import java.awt.im.InputMethodRequests; +import java.awt.image.ColorModel; +import java.awt.image.ImageObserver; +import java.awt.image.ImageProducer; +import java.beans.PropertyChangeListener; +import java.io.PrintStream; +import java.io.PrintWriter; +import java.util.Hashtable; +import java.util.Locale; + +import org.netbeans.jemmy.CharBindingMap; +import org.netbeans.jemmy.ComponentChooser; +import org.netbeans.jemmy.ComponentSearcher; +import org.netbeans.jemmy.EventDispatcher; +import org.netbeans.jemmy.JemmyException; +import org.netbeans.jemmy.JemmyProperties; +import org.netbeans.jemmy.Outputable; +import org.netbeans.jemmy.QueueTool; +import org.netbeans.jemmy.TestOut; +import org.netbeans.jemmy.TimeoutExpiredException; +import org.netbeans.jemmy.Timeoutable; +import org.netbeans.jemmy.Timeouts; +import org.netbeans.jemmy.Waitable; +import org.netbeans.jemmy.Waiter; +import org.netbeans.jemmy.drivers.DriverManager; +import org.netbeans.jemmy.drivers.FocusDriver; +import org.netbeans.jemmy.drivers.KeyDriver; +import org.netbeans.jemmy.drivers.MouseDriver; + +/** + * Root class for all component operators. + * + * Provides basic methods to operate with mouse and keyboard.
+ *
+ * Almost all input methods can throw JemmyInputException or its subclass.
+ * + * ComponentOperator and its subclasses has a lot of methods which name and + * parameters just like consistent component has. In this case operator class + * just invokes consistent component method through AWT Event Queue + * (invokeAndWait method). + * + *

Timeouts used:
+ * ComponentOperator.PushKeyTimeout - time between key pressing and releasing + *
+ * ComponentOperator.MouseClickTimeout - time between mouse pressing and + * releasing
+ * ComponentOperator.WaitComponentTimeout - time to wait component displayed + *
+ * ComponentOperator.WaitComponentEnabledTimeout - time to wait component + * enabled
+ * ComponentOperator.BeforeDragTimeout - time to sleep before grag'n'drop + * operations
+ * ComponentOperator.AfterDragTimeout - time to sleep after grag'n'drop + * operations
+ * ComponentOperator.WaitFocusTimeout - time to wait component focus
+ * ComponentOperator.WaitStateTimeout- time to wait component to be in some + * state. Typically used from methods like + * {@code Operator.wait"something happened"(*)}
. + * + * @see org.netbeans.jemmy.Timeouts + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + */ +public class ComponentOperator extends Operator + implements Timeoutable, Outputable { + + /** + * Identifier for a name property. + * + * @see #getDump + */ + public static final String NAME_DPROP = "Name:"; + + /** + * Identifier for a visible property. + * + * @see #getDump + */ + public static final String IS_VISIBLE_DPROP = "Visible"; + + /** + * Identifier for a showing property. + * + * @see #getDump + */ + public static final String IS_SHOWING_DPROP = "Showing"; + + /** + * Identifier for a x coordinate property. + * + * @see #getDump + */ + public static final String X_DPROP = "X"; + + /** + * Identifier for a y coordinate property. + * + * @see #getDump + */ + public static final String Y_DPROP = "Y"; + + /** + * Identifier for a width property. + * + * @see #getDump + */ + public static final String WIDTH_DPROP = "Width"; + + /** + * Identifier for a height property. + * + * @see #getDump + */ + public static final String HEIGHT_DPROP = "Height"; + + private static final long PUSH_KEY_TIMEOUT = 0; + private static final long MOUSE_CLICK_TIMEOUT = 0; + private static final long BEFORE_DRAG_TIMEOUT = 0; + private static final long AFTER_DRAG_TIMEOUT = 0; + private static final long WAIT_COMPONENT_TIMEOUT = 60000; + private static final long WAIT_COMPONENT_ENABLED_TIMEOUT = 60000; + private static final long WAIT_FOCUS_TIMEOUT = 60000; + private static final long WAIT_STATE_TIMEOUT = 60000; + + private final Component source; + private volatile Timeouts timeouts; // used in invokeSmoothly in clickMouse + private volatile TestOut output; // used in QueueTool.Locker + private volatile EventDispatcher dispatcher; // used in JInternalFrameByTitleFinder.checkComponent + private KeyDriver kDriver; + private MouseDriver mDriver; + private FocusDriver fDriver; + + /** + * Constructor. + * + * @param comp a component + */ + public ComponentOperator(Component comp) { + super(); + source = comp; + kDriver = DriverManager.getKeyDriver(getClass()); + mDriver = DriverManager.getMouseDriver(getClass()); + fDriver = DriverManager.getFocusDriver(getClass()); + setEventDispatcher(new EventDispatcher(comp)); + } + + /** + * Constructs a ComponentOperator object. + * + * @param cont container + * @param chooser a component chooser specifying searching criteria. + * @param index an index between appropriate ones. + */ + public ComponentOperator(ContainerOperator cont, ComponentChooser chooser, int index) { + this(waitComponent((Container) cont.getSource(), + chooser, + index, cont.getTimeouts(), cont.getOutput())); + copyEnvironment(cont); + } + + /** + * Constructs a ComponentOperator object. + * + * @param cont container + * @param chooser a component chooser specifying searching criteria. + */ + public ComponentOperator(ContainerOperator cont, ComponentChooser chooser) { + this(cont, chooser, 0); + } + + /** + * Constructor. Waits for a component in a container to show. The component + * is iis the {@code index+1}'th {@code java.awt.Component} that + * shows and that lies below the container in the display containment + * hierarchy. Uses cont's timeout and output for waiting and to init + * operator. + * + * @param cont Operator for a java.awt.Container. + * @param index an index between appropriate ones. + * @throws TimeoutExpiredException + */ + public ComponentOperator(ContainerOperator cont, int index) { + this(cont, ComponentSearcher.getTrueChooser("Any component"), index); + } + + /** + * Constructor. Waits for a component in a container to show. The component + * is is the first {@code java.awt.Component} that shows and that lies + * below the container in the display containment hierarchy. Uses cont's + * timeout and output for waiting and to init operator. + * + * @param cont Operator for a java.awt.Container. + * @throws TimeoutExpiredException + */ + public ComponentOperator(ContainerOperator cont) { + this(cont, 0); + } + + /** + * Searches Component in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @param index Ordinal component index. + * @return Component instance or null if component was not found. + */ + public static Component findComponent(Container cont, ComponentChooser chooser, int index) { + return findComponent(cont, chooser, index, false); + } + + /** + * Searches Component in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @return Component instance or null if component was not found. + */ + public static Component findComponent(Container cont, ComponentChooser chooser) { + return findComponent(cont, chooser, 0); + } + + /** + * Waits Component in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @param index Ordinal component index. + * @return Component instance or null if component was not found. + * @throws TimeoutExpiredException + */ + public static Component waitComponent(Container cont, ComponentChooser chooser, int index) { + return (waitComponent(cont, chooser, index, + JemmyProperties.getCurrentTimeouts(), + JemmyProperties.getCurrentOutput())); + } + + /** + * Waits Component in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @return Component instance or null if component was not found. + * @throws TimeoutExpiredException + */ + public static Component waitComponent(Container cont, ComponentChooser chooser) { + return waitComponent(cont, chooser, 0); + } + + /** + * A method to be used from subclasses. Uses {@code contOper}'s + * timeouts and output during the waiting. + * + * @param contOper Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @param index Ordinal component index. + * @return Component instance or null if component was not found. + * @throws TimeoutExpiredException + */ + protected static Component waitComponent(ContainerOperator contOper, + ComponentChooser chooser, int index) { + return (waitComponent((Container) contOper.getSource(), + chooser, index, + contOper.getTimeouts(), + contOper.getOutput())); + } + + /** + * A method to be used from subclasses. Uses timeouts and output passed as + * parameters during the waiting. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @param index Ordinal component index. + * @param timeouts timeouts to be used during the waiting. + * @param output an output to be used during the waiting. + * @return Component instance or null if component was not found. + * @throws TimeoutExpiredException + */ + protected static Component waitComponent(final Container cont, + final ComponentChooser chooser, + final int index, + Timeouts timeouts, final TestOut output) { + try { + Waiter waiter = new Waiter<>(new Waitable() { + @Override + public Component actionProduced(Void obj) { + return findComponent(cont, new VisibleComponentFinder(chooser), index, + output.createErrorOutput()); + } + + @Override + public String getDescription() { + return "Wait " + chooser.getDescription() + " loaded"; + } + + @Override + public String toString() { + return "ComponentOperator.waitComponent.Waitable{description = " + getDescription() + '}'; + } + }); + waiter.setTimeoutsToCloneOf(timeouts, "ComponentOperator.WaitComponentTimeout"); + waiter.setOutput(output); + return waiter.waitAction(null); + } catch (InterruptedException e) { + return null; + } + } + + /** + * Searches Components in container. + * + * @param cont Container to search components in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @return Component array or empty array if component was not found. + */ + public static Component[] findComponents(Container cont, ComponentChooser chooser) { + ComponentSearcher searcher = new ComponentSearcher(cont); + return searcher.findComponents(new VisibleComponentFinder(chooser)); + } + + private static Component findComponent(Container cont, ComponentChooser chooser, int index, TestOut output) { + ComponentSearcher searcher = new ComponentSearcher(cont); + searcher.setOutput(output); + return searcher.findComponent(new VisibleComponentFinder(chooser), index); + } + + private static Component findComponent(Container cont, ComponentChooser chooser, int index, boolean supressOutout) { + return findComponent(cont, chooser, index, JemmyProperties.getCurrentOutput().createErrorOutput()); + } + + static { + Timeouts.initDefault("ComponentOperator.PushKeyTimeout", PUSH_KEY_TIMEOUT); + Timeouts.initDefault("ComponentOperator.MouseClickTimeout", MOUSE_CLICK_TIMEOUT); + Timeouts.initDefault("ComponentOperator.BeforeDragTimeout", BEFORE_DRAG_TIMEOUT); + Timeouts.initDefault("ComponentOperator.AfterDragTimeout", AFTER_DRAG_TIMEOUT); + Timeouts.initDefault("ComponentOperator.WaitComponentTimeout", WAIT_COMPONENT_TIMEOUT); + Timeouts.initDefault("ComponentOperator.WaitComponentEnabledTimeout", WAIT_COMPONENT_ENABLED_TIMEOUT); + Timeouts.initDefault("ComponentOperator.WaitStateTimeout", WAIT_STATE_TIMEOUT); + Timeouts.initDefault("ComponentOperator.WaitFocusTimeout", WAIT_FOCUS_TIMEOUT); + } + + /** + * Returns component. + */ + @Override + public Component getSource() { + return source; + } + + /** + * Returns org.netbeans.jemmy.EventDispatcher instance which is used to + * dispatch events. + * + * @return the dispatcher. + * @see org.netbeans.jemmy.EventDispatcher + */ + public EventDispatcher getEventDispatcher() { + return dispatcher; + } + + //////////////////////////////////////////////////////// + //Environment // + //////////////////////////////////////////////////////// + @Override + public void setOutput(TestOut out) { + super.setOutput(out); + this.output = out; + if (dispatcher != null) { + dispatcher.setOutput(output.createErrorOutput()); + } + } + + @Override + public TestOut getOutput() { + return output; + } + + @Override + public void setTimeouts(Timeouts timeouts) { + super.setTimeouts(timeouts); + this.timeouts = timeouts; + if (dispatcher != null) { + dispatcher.setTimeouts(getTimeouts()); + } + } + + @Override + public Timeouts getTimeouts() { + return timeouts; + } + + @Override + public void copyEnvironment(Operator anotherOperator) { + super.copyEnvironment(anotherOperator); + kDriver = (KeyDriver) DriverManager. + getDriver(DriverManager.KEY_DRIVER_ID, + getClass(), + anotherOperator.getProperties()); + mDriver = (MouseDriver) DriverManager. + getDriver(DriverManager.MOUSE_DRIVER_ID, + getClass(), + anotherOperator.getProperties()); + fDriver = (FocusDriver) DriverManager. + getDriver(DriverManager.FOCUS_DRIVER_ID, + getClass(), + anotherOperator.getProperties()); + } + + //////////////////////////////////////////////////////// + //Mouse operations + //////////////////////////////////////////////////////// + /** + * Makes mouse click. + * + * @param x Horizontal click coordinate + * @param y Vertical click coordinate + * @param clickCount Click count + * @param mouseButton Mouse button (InputEvent.BUTTON1/2/3_MASK value) + * @param modifiers Modifiers (combination of InputEvent.*_MASK values) + * @param forPopup signals that click is intended to call popup. + */ + public void clickMouse(final int x, final int y, final int clickCount, final int mouseButton, + final int modifiers, final boolean forPopup) { + getQueueTool().invokeSmoothly(new QueueTool.QueueAction("Path selecting") { + @Override + public Void launch() { + mDriver.clickMouse(ComponentOperator.this, x, y, clickCount, mouseButton, modifiers, + timeouts.create("ComponentOperator.MouseClickTimeout")); + return null; + } + }); + } + + /** + * Makes mouse click. + * + * @param x Horizontal click coordinate + * @param y Vertical click coordinate + * @param clickCount Click count + * @param mouseButton Mouse button (InputEvent.BUTTON1/2/3_MASK value) + * @param modifiers Modifiers (combination of InputEvent.*_MASK values) + * @see #clickMouse(int, int, int, int, int, boolean) + */ + public void clickMouse(int x, int y, int clickCount, int mouseButton, int modifiers) { + clickMouse(x, y, clickCount, mouseButton, modifiers, false); + } + + /** + * Makes mouse click with 0 modifiers. + * + * @param x Horizontal click coordinate + * @param y Vertical click coordinate + * @param clickCount Click count + * @param mouseButton Mouse button (InputEvent.BUTTON1/2/3_MASK value) + * @see #clickMouse(int, int, int, int, int) + */ + public void clickMouse(int x, int y, int clickCount, int mouseButton) { + clickMouse(x, y, clickCount, mouseButton, 0); + } + + /** + * Makes mouse click by default mouse button with 0 modifiers. + * + * @param x Horizontal click coordinate + * @param y Vertical click coordinate + * @param clickCount Click count + * @see #clickMouse(int, int, int, int) + * @see #getDefaultMouseButton() + */ + public void clickMouse(int x, int y, int clickCount) { + clickMouse(x, y, clickCount, getDefaultMouseButton()); + } + + /** + * Press mouse. + * + * @param x Horizontal click coordinate + * @param y Vertical click coordinate + */ + public void pressMouse(int x, int y) { + mDriver.pressMouse(this, x, y, getDefaultMouseButton(), 0); + } + + /** + * Releases mouse. + * + * @param x Horizontal click coordinate + * @param y Vertical click coordinate + */ + public void releaseMouse(int x, int y) { + mDriver.releaseMouse(this, x, y, getDefaultMouseButton(), 0); + } + + /** + * Move mouse over the component. + * + * @param x Horisontal destination coordinate. + * @param y Vertical destination coordinate. + */ + public void moveMouse(int x, int y) { + mDriver.moveMouse(this, x, y); + } + + /** + * Drag mouse over the component. + * + * @param x Horisontal destination coordinate. + * @param y Vertical destination coordinate. + * @param mouseButton Mouse button + * @param modifiers Modifiers + */ + public void dragMouse(int x, int y, int mouseButton, int modifiers) { + mDriver.dragMouse(this, x, y, getDefaultMouseButton(), 0); + } + + /** + * Drag mouse over the component with 0 modifiers. + * + * @param x Horisontal destination coordinate. + * @param y Vertical destination coordinate. + * @param mouseButton Mouse button + * @see #dragMouse(int, int, int, int) + */ + public void dragMouse(int x, int y, int mouseButton) { + dragMouse(x, y, mouseButton, 0); + } + + /** + * Drag mouse over the component with 0 modifiers and default mose button + * pressed. + * + * @param x Horisontal destination coordinate. + * @param y Vertical destination coordinate. + * @see #dragMouse(int, int, int) + * @see #getDefaultMouseButton() + */ + public void dragMouse(int x, int y) { + dragMouse(x, y, getDefaultMouseButton()); + } + + /** + * Makes drag'n'drop operation. + * + * @param start_x Start horizontal coordinate + * @param start_y Start vertical coordinate + * @param end_x End horizontal coordinate + * @param end_y End vertical coordinate + * @param mouseButton Mouse button + * @param modifiers Modifiers + */ + public void dragNDrop(int start_x, int start_y, int end_x, int end_y, int mouseButton, int modifiers) { + mDriver.dragNDrop(this, start_x, start_y, end_x, end_y, mouseButton, modifiers, + timeouts.create("ComponentOperator.BeforeDragTimeout"), + timeouts.create("ComponentOperator.AfterDragTimeout")); + } + + /** + * Makes drag'n'drop operation with 0 modifiers. + * + * @param start_x Start horizontal coordinate + * @param start_y Start vertical coordinate + * @param end_x End horizontal coordinate + * @param end_y End vertical coordinate + * @param mouseButton Mouse button + * @see #dragNDrop(int, int, int, int, int, int) + */ + public void dragNDrop(int start_x, int start_y, int end_x, int end_y, int mouseButton) { + dragNDrop(start_x, start_y, end_x, end_y, mouseButton, 0); + } + + /** + * Makes drag'n'drop operation by default mouse buttons with 0 modifiers. + * + * @param start_x Start horizontal coordinate + * @param start_y Start vertical coordinate + * @param end_x End horizontal coordinate + * @param end_y End vertical coordinate + * @see #dragNDrop(int, int, int, int, int) + * @see #getDefaultMouseButton() + */ + public void dragNDrop(int start_x, int start_y, int end_x, int end_y) { + dragNDrop(start_x, start_y, end_x, end_y, getDefaultMouseButton(), 0); + } + + /** + * Clicks for popup. + * + * @param x Horizontal click coordinate. + * @param y Vertical click coordinate. + * @param mouseButton Mouse button. + * @see #clickMouse(int, int, int, int, int, boolean) + */ + public void clickForPopup(int x, int y, int mouseButton) { + makeComponentVisible(); + clickMouse(x, y, 1, mouseButton, 0, true); + } + + /** + * Clicks for popup by popup mouse button. + * + * @param x Horizontal click coordinate. + * @param y Vertical click coordinate. + * @see #clickForPopup(int, int, int) + * @see #getPopupMouseButton() + */ + public void clickForPopup(int x, int y) { + clickForPopup(x, y, getPopupMouseButton()); + } + + /** + * Makes mouse click on the component center with 0 modifiers. + * + * @param clickCount Click count + * @param mouseButton Mouse button (InputEvent.BUTTON1/2/3_MASK value) + * @see #clickMouse(int, int, int, int) + */ + public void clickMouse(final int clickCount, final int mouseButton) { + getQueueTool().invokeSmoothly(new QueueTool.QueueAction("Clicking the mouse button") { + @Override + public Void launch() { + clickMouse(getCenterXForClick(), getCenterYForClick(), clickCount, mouseButton); + return null; + } + }); + } + + /** + * Makes mouse click on the component center by default mouse button with 0 + * modifiers. + * + * @param clickCount Click count + * @see #clickMouse(int, int) + * @see #getDefaultMouseButton() + */ + public void clickMouse(int clickCount) { + clickMouse(clickCount, getDefaultMouseButton()); + } + + /** + * Makes siple mouse click on the component center by default mouse button + * with 0 modifiers. + * + * @see #clickMouse(int) + * @see #getDefaultMouseButton() + */ + public void clickMouse() { + clickMouse(1); + } + + /** + * Move mouse inside the component. + */ + public void enterMouse() { + mDriver.enterMouse(this); + } + + /** + * Move mouse outside the component. + */ + public void exitMouse() { + mDriver.exitMouse(this); + } + + /** + * Press mouse. + */ + public void pressMouse() { + getQueueTool().invokeSmoothly(new QueueTool.QueueAction("Pressing the mouse button") { + @Override + public Void launch() { + pressMouse(getCenterXForClick(), getCenterYForClick()); + return null; + } + }); + } + + /** + * Releases mouse. + */ + public void releaseMouse() { + getQueueTool().invokeSmoothly(new QueueTool.QueueAction("Releasing the mouse button") { + @Override + public Void launch() { + releaseMouse(getCenterXForClick(), getCenterYForClick()); + return null; + } + }); + } + + /** + * Clicks for popup at the component center. + * + * @param mouseButton Mouse button. + * @see #clickForPopup(int, int) + */ + public void clickForPopup(int mouseButton) { + clickForPopup(getCenterXForClick(), getCenterYForClick(), mouseButton); + } + + /** + * Clicks for popup by popup mouse button at the component center. + * + * @see #clickForPopup(int) + * @see #getPopupMouseButton() + */ + public void clickForPopup() { + clickForPopup(getPopupMouseButton()); + } + + //////////////////////////////////////////////////////// + //Keyboard operations + //////////////////////////////////////////////////////// + /** + * Press key. + * + * @param keyCode Key code (KeyEvent.VK_* value) + * @param modifiers Modifiers (combination of InputEvent.*_MASK fields) + */ + public void pressKey(int keyCode, int modifiers) { + kDriver.pressKey(this, keyCode, modifiers); + } + + /** + * Press key with no modifiers. + * + * @param keyCode Key code (KeyEvent.VK_* value) + */ + public void pressKey(int keyCode) { + pressKey(keyCode, 0); + } + + /** + * Typed key. + * + * @param keyChar Char to be typed. + * @param modifiers Modifiers (combination of InputEvent.*_MASK fields) + */ + public void typedKey(char keyChar, int modifiers) { + kDriver.typedKey(this, getCharBindingMap().getCharKey(keyChar), keyChar, modifiers); + } + + /** + * Releases key. + * + * @param keyCode Key code (KeyEvent.VK_* value) + * @param modifiers Modifiers (combination of InputEvent.*_MASK fields) + */ + public void releaseKey(int keyCode, int modifiers) { + kDriver.releaseKey(this, keyCode, modifiers); + } + + /** + * Releases key with no modifiers. + * + * @param keyCode Key code (KeyEvent.VK_* value) + */ + public void releaseKey(int keyCode) { + releaseKey(keyCode, 0); + } + + /** + * Pushs key. + * + * @param keyCode Key code (KeyEvent.VK_* value) + * @param modifiers Modifiers (combination of InputEvent.*_MASK fields) + */ + public void pushKey(int keyCode, int modifiers) { + kDriver.pushKey(this, keyCode, modifiers, timeouts.create("ComponentOperator.PushKeyTimeout")); + } + + /** + * Pushs key. + * + * @param keyCode Key code (KeyEvent.VK_* value) + */ + public void pushKey(int keyCode) { + pushKey(keyCode, 0); + } + + /** + * Types one char. + * + * @param keyCode Key code (KeyEvent.VK_* value) + * @param keyChar Char to be typed. + * @param modifiers Modifiers (combination of InputEvent.*_MASK fields) + */ + public void typeKey(int keyCode, char keyChar, int modifiers) { + kDriver.typeKey(this, keyCode, keyChar, modifiers, timeouts.create("ComponentOperator.PushKeyTimeout")); + } + + /** + * Types one char. Uses map defined by setCharBindingMap(CharBindingMap) + * method to find a key should be pressed. + * + * @param keyChar Char to be typed. + * @param modifiers Modifiers (combination of InputEvent.*_MASK fields) + * @see org.netbeans.jemmy.CharBindingMap + * @see #setCharBindingMap(CharBindingMap) + * @see #typeKey(int, char, int) + */ + public void typeKey(char keyChar, int modifiers) { + typeKey(getCharKey(keyChar), keyChar, modifiers | getCharModifiers(keyChar)); + } + + /** + * Types one char. Uses map defined by setCharBindingMap(CharBindingMap) + * method to find a key and modifiers should be pressed. + * + * @param keyChar Char to be typed. + * @see #setCharBindingMap(CharBindingMap) + * @see #typeKey(char, int) + */ + public void typeKey(char keyChar) { + typeKey(keyChar, 0); + } + + //////////////////////////////////////////////////////// + //Util + //////////////////////////////////////////////////////// + /** + * Activates component's window. + * + * @deprecated Use makeComponentVisible() instead. + * @see #makeComponentVisible() + */ + @Deprecated + public void activateWindow() { + getVisualizer().makeVisible(this); + } + + /** + * Prepares component for user input. Uses visualizer defined by + * setVisualiser() method. + */ + public void makeComponentVisible() { + getVisualizer().makeVisible(this); + /* + final ComponentOperator compOper = (ComponentOperator)this; + runMapping(new MapVoidAction("add") { + public void map() { + getVisualizer().makeVisible(compOper); + } + }); + */ + } + + /** + * Gives input focus to the component. + */ + public void getFocus() { + fDriver.giveFocus(this); + } + + /** + * Return the center x coordinate. + * + * @return the center x coordinate. + */ + public int getCenterX() { + return getWidth() / 2; + } + + /** + * Return the center y coordinate. + * + * @return the center y coordinate. + */ + public int getCenterY() { + return getHeight() / 2; + } + + /** + * Return the x coordinate which should be used for mouse operations by + * default. + * + * @return the center x coordinate of the visible component part. + */ + public int getCenterXForClick() { + return getCenterX(); + } + + /** + * Return the y coordinate which should be used for mouse operations by + * default. + * + * @return the center y coordinate of the visible component part. + */ + public int getCenterYForClick() { + return getCenterY(); + } + + /** + * Waits for the component to be enabled. + * + * @throws TimeoutExpiredException + * @throws InterruptedException + */ + public void waitComponentEnabled() throws InterruptedException { + Waiter waiter = new Waiter<>(new Waitable() { + @Override + public Component actionProduced(Component obj) { + if (obj.isEnabled()) { + return obj; + } else { + return null; + } + } + + @Override + public String getDescription() { + return ("Component enabled: " + + getSource().getClass().toString()); + } + + @Override + public String toString() { + return "ComponentOperator.waitComponentEnabled.Waitable{description = " + getDescription() + '}'; + } + }); + waiter.setOutput(output); + waiter.setTimeoutsToCloneOf(timeouts, "ComponentOperator.WaitComponentEnabledTimeout"); + waiter.waitAction(getSource()); + } + + /** + * Waits for the component to be enabled. per request: 37831 + * + * @throws TimeoutExpiredException + */ + public void wtComponentEnabled() { + try { + waitComponentEnabled(); + } catch (InterruptedException e) { + throw (new JemmyException("Interrupted!", e)); + } + } + + /** + * Returns an array of containers for this component. + * + * @return an array of containers + */ + public Container[] getContainers() { + int counter = 0; + Container cont = getSource().getParent(); + if (cont == null) { + return new Container[0]; + } + do { + counter++; + } while ((cont = cont.getParent()) != null); + Container[] res = new Container[counter]; + cont = getSource().getParent(); + counter = 0; + do { + counter++; + res[counter - 1] = cont; + } while ((cont = cont.getParent()) != null); + return res; + } + + /** + * Searches a container. + * + * @param chooser a chooser specifying the searching criteria. + * @return a containers specified by searching criteria. + */ + public Container getContainer(ComponentChooser chooser) { + int counter = 0; + Container cont = getSource().getParent(); + if (cont == null) { + return null; + } + do { + if (chooser.checkComponent(cont)) { + return cont; + } + counter++; + } while ((cont = cont.getParent()) != null); + return null; + } + + /** + * Searches the window under component. + * + * @return the component window. + */ + public Window getWindow() { + if (getSource() instanceof Window) { + return (Window) getSource(); + } + Window window = (Window) getContainer(new ComponentChooser() { + @Override + public boolean checkComponent(Component comp) { + return comp instanceof Window; + } + + @Override + public String getDescription() { + return ""; + } + + @Override + public String toString() { + return "ComponentOperator.getWindow.ComponentChooser{description = " + getDescription() + '}'; + } + }); + if (window == null && getSource() instanceof Window) { + return (Window) getSource(); + } else { + return window; + } + } + + /** + * Waits for this Component has the keyboard focus. + * + * @throws TimeoutExpiredException + */ + public void waitHasFocus() { + Waiter focusWaiter = new Waiter<>(new Waitable() { + @Override + public String actionProduced(Void obj) { + return hasFocus() ? "" : null; + } + + @Override + public String getDescription() { + return "Wait component has focus"; + } + + @Override + public String toString() { + return "ComponentOperator.waitHasFocus.Waitable{description = " + getDescription() + '}'; + } + }); + focusWaiter.setTimeoutsToCloneOf(timeouts, "ComponentOperator.WaitFocusTimeout"); + focusWaiter.setOutput(output.createErrorOutput()); + try { + focusWaiter.waitAction(null); + } catch (InterruptedException e) { + output.printStackTrace(e); + } + } + + /** + * Waits for the component to be visible or unvisible. + * + * @param visibility required visiblity. + * @throws TimeoutExpiredException + */ + public void waitComponentVisible(final boolean visibility) { + waitState(new ComponentChooser() { + @Override + public boolean checkComponent(Component comp) { + return isVisible() == visibility; + } + + @Override + public String getDescription() { + return "Component is " + (visibility ? "" : " not ") + "visible"; + } + + @Override + public String toString() { + return "ComponentOperator.waitComponentVisible.ComponentChooser{description = " + getDescription() + '}'; + } + }); + } + + public void waitComponentShowing(final boolean visibility) { + waitState(new ComponentChooser() { + @Override + public boolean checkComponent(Component comp) { + return isShowing() == visibility; + } + + @Override + public String getDescription() { + return "Component is " + (visibility ? "" : " not ") + "showing"; + } + + @Override + public String toString() { + return "ComponentOperator.waitComponentShowing.ComponentChooser{description = " + getDescription() + '}'; + } + }); + } + + /** + * Returns information about component. + */ + @Override + public Hashtable getDump() { + Hashtable result = super.getDump(); + if (getSource().getName() != null) { + result.put(NAME_DPROP, getSource().getName()); + } + result.put(IS_VISIBLE_DPROP, getSource().isVisible() ? "true" : "false"); + result.put(IS_SHOWING_DPROP, getSource().isShowing() ? "true" : "false"); + result.put(X_DPROP, Integer.toString(getSource().getX())); + result.put(Y_DPROP, Integer.toString(getSource().getY())); + result.put(WIDTH_DPROP, Integer.toString(getSource().getWidth())); + result.put(HEIGHT_DPROP, Integer.toString(getSource().getHeight())); + return result; + } + + //////////////////////////////////////////////////////// + //Mapping // + /** + * Maps {@code Component.add(PopupMenu)} through queue + */ + public void add(final PopupMenu popupMenu) { + runMapping(new MapVoidAction("add") { + @Override + public void map() { + getSource().add(popupMenu); + } + }); + } + + /** + * Maps {@code Component.addComponentListener(ComponentListener)} + * through queue + */ + public void addComponentListener(final ComponentListener componentListener) { + runMapping(new MapVoidAction("addComponentListener") { + @Override + public void map() { + getSource().addComponentListener(componentListener); + } + }); + } + + /** + * Maps {@code Component.addFocusListener(FocusListener)} through queue + */ + public void addFocusListener(final FocusListener focusListener) { + runMapping(new MapVoidAction("addFocusListener") { + @Override + public void map() { + getSource().addFocusListener(focusListener); + } + }); + } + + /** + * Maps {@code Component.addInputMethodListener(InputMethodListener)} + * through queue + */ + public void addInputMethodListener(final InputMethodListener inputMethodListener) { + runMapping(new MapVoidAction("addInputMethodListener") { + @Override + public void map() { + getSource().addInputMethodListener(inputMethodListener); + } + }); + } + + /** + * Maps {@code Component.addKeyListener(KeyListener)} through queue + */ + public void addKeyListener(final KeyListener keyListener) { + runMapping(new MapVoidAction("addKeyListener") { + @Override + public void map() { + getSource().addKeyListener(keyListener); + } + }); + } + + /** + * Maps {@code Component.addMouseListener(MouseListener)} through queue + */ + public void addMouseListener(final MouseListener mouseListener) { + runMapping(new MapVoidAction("addMouseListener") { + @Override + public void map() { + getSource().addMouseListener(mouseListener); + } + }); + } + + /** + * Maps {@code Component.addMouseMotionListener(MouseMotionListener)} + * through queue + */ + public void addMouseMotionListener(final MouseMotionListener mouseMotionListener) { + runMapping(new MapVoidAction("addMouseMotionListener") { + @Override + public void map() { + getSource().addMouseMotionListener(mouseMotionListener); + } + }); + } + + /** + * Maps {@code Component.addNotify()} through queue + */ + public void addNotify() { + runMapping(new MapVoidAction("addNotify") { + @Override + public void map() { + getSource().addNotify(); + } + }); + } + + /** + * Maps + * {@code Component.addPropertyChangeListener(PropertyChangeListener)} + * through queue + */ + public void addPropertyChangeListener(final PropertyChangeListener propertyChangeListener) { + runMapping(new MapVoidAction("addPropertyChangeListener") { + @Override + public void map() { + getSource().addPropertyChangeListener(propertyChangeListener); + } + }); + } + + /** + * Maps + * {@code Component.addPropertyChangeListener(String, PropertyChangeListener)} + * through queue + */ + public void addPropertyChangeListener(final String string, final PropertyChangeListener propertyChangeListener) { + runMapping(new MapVoidAction("addPropertyChangeListener") { + @Override + public void map() { + getSource().addPropertyChangeListener(string, propertyChangeListener); + } + }); + } + + /** + * Maps {@code Component.checkImage(Image, int, int, ImageObserver)} + * through queue + */ + public int checkImage(final Image image, final int i, final int i1, final ImageObserver imageObserver) { + return (runMapping(new MapIntegerAction("checkImage") { + @Override + public int map() { + return getSource().checkImage(image, i, i1, imageObserver); + } + })); + } + + /** + * Maps {@code Component.checkImage(Image, ImageObserver)} through queue + */ + public int checkImage(final Image image, final ImageObserver imageObserver) { + return (runMapping(new MapIntegerAction("checkImage") { + @Override + public int map() { + return getSource().checkImage(image, imageObserver); + } + })); + } + + /** + * Maps {@code Component.contains(int, int)} through queue + */ + public boolean contains(final int i, final int i1) { + return (runMapping(new MapBooleanAction("contains") { + @Override + public boolean map() { + return getSource().contains(i, i1); + } + })); + } + + /** + * Maps {@code Component.contains(Point)} through queue + */ + public boolean contains(final Point point) { + return (runMapping(new MapBooleanAction("contains") { + @Override + public boolean map() { + return getSource().contains(point); + } + })); + } + + /** + * Maps {@code Component.createImage(int, int)} through queue + */ + public Image createImage(final int i, final int i1) { + return (runMapping(new MapAction("createImage") { + @Override + public Image map() { + return getSource().createImage(i, i1); + } + })); + } + + /** + * Maps {@code Component.createImage(ImageProducer)} through queue + */ + public Image createImage(final ImageProducer imageProducer) { + return (runMapping(new MapAction("createImage") { + @Override + public Image map() { + return getSource().createImage(imageProducer); + } + })); + } + + /** + * Maps {@code Component.dispatchEvent(AWTEvent)} through queue + */ + public void dispatchEvent(final AWTEvent aWTEvent) { + runMapping(new MapVoidAction("dispatchEvent") { + @Override + public void map() { + getSource().dispatchEvent(aWTEvent); + } + }); + } + + /** + * Maps {@code Component.doLayout()} through queue + */ + public void doLayout() { + runMapping(new MapVoidAction("doLayout") { + @Override + public void map() { + getSource().doLayout(); + } + }); + } + + /** + * Maps {@code Component.enableInputMethods(boolean)} through queue + */ + public void enableInputMethods(final boolean b) { + runMapping(new MapVoidAction("enableInputMethods") { + @Override + public void map() { + getSource().enableInputMethods(b); + } + }); + } + + /** + * Maps {@code Component.getAlignmentX()} through queue + */ + public float getAlignmentX() { + return (runMapping(new MapFloatAction("getAlignmentX") { + @Override + public float map() { + return getSource().getAlignmentX(); + } + })); + } + + /** + * Maps {@code Component.getAlignmentY()} through queue + */ + public float getAlignmentY() { + return (runMapping(new MapFloatAction("getAlignmentY") { + @Override + public float map() { + return getSource().getAlignmentY(); + } + })); + } + + /** + * Maps {@code Component.getBackground()} through queue + */ + public Color getBackground() { + return (runMapping(new MapAction("getBackground") { + @Override + public Color map() { + return getSource().getBackground(); + } + })); + } + + /** + * Maps {@code Component.getBounds()} through queue + */ + public Rectangle getBounds() { + return (runMapping(new MapAction("getBounds") { + @Override + public Rectangle map() { + return getSource().getBounds(); + } + })); + } + + /** + * Maps {@code Component.getBounds(Rectangle)} through queue + */ + public Rectangle getBounds(final Rectangle rectangle) { + return (runMapping(new MapAction("getBounds") { + @Override + public Rectangle map() { + return getSource().getBounds(rectangle); + } + })); + } + + /** + * Maps {@code Component.getColorModel()} through queue + */ + public ColorModel getColorModel() { + return (runMapping(new MapAction("getColorModel") { + @Override + public ColorModel map() { + return getSource().getColorModel(); + } + })); + } + + /** + * Maps {@code Component.getComponentAt(int, int)} through queue + */ + public Component getComponentAt(final int i, final int i1) { + return (runMapping(new MapAction("getComponentAt") { + @Override + public Component map() { + return getSource().getComponentAt(i, i1); + } + })); + } + + /** + * Maps {@code Component.getComponentAt(Point)} through queue + */ + public Component getComponentAt(final Point point) { + return (runMapping(new MapAction("getComponentAt") { + @Override + public Component map() { + return getSource().getComponentAt(point); + } + })); + } + + /** + * Maps {@code Component.getComponentOrientation()} through queue + */ + public ComponentOrientation getComponentOrientation() { + return (runMapping(new MapAction("getComponentOrientation") { + @Override + public ComponentOrientation map() { + return getSource().getComponentOrientation(); + } + })); + } + + /** + * Maps {@code Component.getCursor()} through queue + */ + public Cursor getCursor() { + return (runMapping(new MapAction("getCursor") { + @Override + public Cursor map() { + return getSource().getCursor(); + } + })); + } + + /** + * Maps {@code Component.getDropTarget()} through queue + */ + public DropTarget getDropTarget() { + return (runMapping(new MapAction("getDropTarget") { + @Override + public DropTarget map() { + return getSource().getDropTarget(); + } + })); + } + + /** + * Maps {@code Component.getFont()} through queue + */ + public Font getFont() { + return (runMapping(new MapAction("getFont") { + @Override + public Font map() { + return getSource().getFont(); + } + })); + } + + /** + * Maps {@code Component.getFontMetrics(Font)} through queue + */ + public FontMetrics getFontMetrics(final Font font) { + return (runMapping(new MapAction("getFontMetrics") { + @Override + public FontMetrics map() { + return getSource().getFontMetrics(font); + } + })); + } + + /** + * Maps {@code Component.getForeground()} through queue + */ + public Color getForeground() { + return (runMapping(new MapAction("getForeground") { + @Override + public Color map() { + return getSource().getForeground(); + } + })); + } + + /** + * Maps {@code Component.getGraphics()} through queue + */ + public Graphics getGraphics() { + return (runMapping(new MapAction("getGraphics") { + @Override + public Graphics map() { + return getSource().getGraphics(); + } + })); + } + + /** + * Maps {@code Component.getHeight()} through queue + */ + public int getHeight() { + return (runMapping(new MapIntegerAction("getHeight") { + @Override + public int map() { + return getSource().getHeight(); + } + })); + } + + /** + * Maps {@code Component.getInputContext()} through queue + */ + public InputContext getInputContext() { + return (runMapping(new MapAction("getInputContext") { + @Override + public InputContext map() { + return getSource().getInputContext(); + } + })); + } + + /** + * Maps {@code Component.getInputMethodRequests()} through queue + */ + public InputMethodRequests getInputMethodRequests() { + return (runMapping(new MapAction("getInputMethodRequests") { + @Override + public InputMethodRequests map() { + return getSource().getInputMethodRequests(); + } + })); + } + + /** + * Maps {@code Component.getLocale()} through queue + */ + public Locale getLocale() { + return (runMapping(new MapAction("getLocale") { + @Override + public Locale map() { + return getSource().getLocale(); + } + })); + } + + /** + * Maps {@code Component.getLocation()} through queue + */ + public Point getLocation() { + return (runMapping(new MapAction("getLocation") { + @Override + public Point map() { + return getSource().getLocation(); + } + })); + } + + /** + * Maps {@code Component.getLocation(Point)} through queue + */ + public Point getLocation(final Point point) { + return (runMapping(new MapAction("getLocation") { + @Override + public Point map() { + return getSource().getLocation(point); + } + })); + } + + /** + * Maps {@code Component.getLocationOnScreen()} through queue + */ + public Point getLocationOnScreen() { + return (runMapping(new MapAction("getLocationOnScreen") { + @Override + public Point map() { + return getSource().getLocationOnScreen(); + } + })); + } + + /** + * Maps {@code Component.getMaximumSize()} through queue + */ + public Dimension getMaximumSize() { + return (runMapping(new MapAction("getMaximumSize") { + @Override + public Dimension map() { + return getSource().getMaximumSize(); + } + })); + } + + /** + * Maps {@code Component.getMinimumSize()} through queue + */ + public Dimension getMinimumSize() { + return (runMapping(new MapAction("getMinimumSize") { + @Override + public Dimension map() { + return getSource().getMinimumSize(); + } + })); + } + + /** + * Maps {@code Component.getName()} through queue + */ + public String getName() { + return (runMapping(new MapAction("getName") { + @Override + public String map() { + return getSource().getName(); + } + })); + } + + /** + * Maps {@code Component.getParent()} through queue + */ + public Container getParent() { + return (runMapping(new MapAction("getParent") { + @Override + public Container map() { + return getSource().getParent(); + } + })); + } + + /** + * Maps {@code Component.getPreferredSize()} through queue + */ + public Dimension getPreferredSize() { + return (runMapping(new MapAction("getPreferredSize") { + @Override + public Dimension map() { + return getSource().getPreferredSize(); + } + })); + } + + /** + * Maps {@code Component.getSize()} through queue + */ + public Dimension getSize() { + return (runMapping(new MapAction("getSize") { + @Override + public Dimension map() { + return getSource().getSize(); + } + })); + } + + /** + * Maps {@code Component.getSize(Dimension)} through queue + */ + public Dimension getSize(final Dimension dimension) { + return (runMapping(new MapAction("getSize") { + @Override + public Dimension map() { + return getSource().getSize(dimension); + } + })); + } + + /** + * Maps {@code Component.getToolkit()} through queue + */ + public Toolkit getToolkit() { + return (runMapping(new MapAction("getToolkit") { + @Override + public Toolkit map() { + return getSource().getToolkit(); + } + })); + } + + /** + * Maps {@code Component.getTreeLock()} through queue + */ + public Object getTreeLock() { + return (runMapping(new MapAction("getTreeLock") { + @Override + public Object map() { + return getSource().getTreeLock(); + } + })); + } + + /** + * Maps {@code Component.getWidth()} through queue + */ + public int getWidth() { + return (runMapping(new MapIntegerAction("getWidth") { + @Override + public int map() { + return getSource().getWidth(); + } + })); + } + + /** + * Maps {@code Component.getX()} through queue + */ + public int getX() { + return (runMapping(new MapIntegerAction("getX") { + @Override + public int map() { + return getSource().getX(); + } + })); + } + + /** + * Maps {@code Component.getY()} through queue + */ + public int getY() { + return (runMapping(new MapIntegerAction("getY") { + @Override + public int map() { + return getSource().getY(); + } + })); + } + + /** + * Maps {@code Component.hasFocus()} through queue + */ + public boolean hasFocus() { + return (runMapping(new MapBooleanAction("hasFocus") { + @Override + public boolean map() { + return getSource().hasFocus(); + } + })); + } + + /** + * Maps {@code Component.imageUpdate(Image, int, int, int, int, int)} + * through queue + */ + public boolean imageUpdate(final Image image, final int i, final int i1, final int i2, final int i3, final int i4) { + return (runMapping(new MapBooleanAction("imageUpdate") { + @Override + public boolean map() { + return getSource().imageUpdate(image, i, i1, i2, i3, i4); + } + })); + } + + /** + * Maps {@code Component.invalidate()} through queue + */ + public void invalidate() { + runMapping(new MapVoidAction("invalidate") { + @Override + public void map() { + getSource().invalidate(); + } + }); + } + + /** + * Maps {@code Component.isDisplayable()} through queue + */ + public boolean isDisplayable() { + return (runMapping(new MapBooleanAction("isDisplayable") { + @Override + public boolean map() { + return getSource().isDisplayable(); + } + })); + } + + /** + * Maps {@code Component.isDoubleBuffered()} through queue + */ + public boolean isDoubleBuffered() { + return (runMapping(new MapBooleanAction("isDoubleBuffered") { + @Override + public boolean map() { + return getSource().isDoubleBuffered(); + } + })); + } + + /** + * Maps {@code Component.isEnabled()} through queue + */ + public boolean isEnabled() { + return (runMapping(new MapBooleanAction("isEnabled") { + @Override + public boolean map() { + return getSource().isEnabled(); + } + })); + } + + /** + * Maps {@code Component.isFocusTraversable()} through queue + */ + @Deprecated + public boolean isFocusTraversable() { + return (runMapping(new MapBooleanAction("isFocusTraversable") { + @Override + public boolean map() { + return getSource().isFocusTraversable(); + } + })); + } + + /** + * Maps {@code Component.isLightweight()} through queue + */ + public boolean isLightweight() { + return (runMapping(new MapBooleanAction("isLightweight") { + @Override + public boolean map() { + return getSource().isLightweight(); + } + })); + } + + /** + * Maps {@code Component.isOpaque()} through queue + */ + public boolean isOpaque() { + return (runMapping(new MapBooleanAction("isOpaque") { + @Override + public boolean map() { + return getSource().isOpaque(); + } + })); + } + + /** + * Maps {@code Component.isShowing()} through queue + */ + public boolean isShowing() { + return (runMapping(new MapBooleanAction("isShowing") { + @Override + public boolean map() { + return getSource().isShowing(); + } + })); + } + + /** + * Maps {@code Component.isValid()} through queue + */ + public boolean isValid() { + return (runMapping(new MapBooleanAction("isValid") { + @Override + public boolean map() { + return getSource().isValid(); + } + })); + } + + /** + * Maps {@code Component.isVisible()} through queue + */ + public boolean isVisible() { + return (runMapping(new MapBooleanAction("isVisible") { + @Override + public boolean map() { + return getSource().isVisible(); + } + })); + } + + /** + * Maps {@code Component.list()} through queue + */ + public void list() { + runMapping(new MapVoidAction("list") { + @Override + public void map() { + getSource().list(); + } + }); + } + + /** + * Maps {@code Component.list(PrintStream)} through queue + */ + public void list(final PrintStream printStream) { + runMapping(new MapVoidAction("list") { + @Override + public void map() { + getSource().list(printStream); + } + }); + } + + /** + * Maps {@code Component.list(PrintStream, int)} through queue + */ + public void list(final PrintStream printStream, final int i) { + runMapping(new MapVoidAction("list") { + @Override + public void map() { + getSource().list(printStream, i); + } + }); + } + + /** + * Maps {@code Component.list(PrintWriter)} through queue + */ + public void list(final PrintWriter printWriter) { + runMapping(new MapVoidAction("list") { + @Override + public void map() { + getSource().list(printWriter); + } + }); + } + + /** + * Maps {@code Component.list(PrintWriter, int)} through queue + */ + public void list(final PrintWriter printWriter, final int i) { + runMapping(new MapVoidAction("list") { + @Override + public void map() { + getSource().list(printWriter, i); + } + }); + } + + /** + * Maps {@code Component.paint(Graphics)} through queue + */ + public void paint(final Graphics graphics) { + runMapping(new MapVoidAction("paint") { + @Override + public void map() { + getSource().paint(graphics); + } + }); + } + + /** + * Maps {@code Component.paintAll(Graphics)} through queue + */ + public void paintAll(final Graphics graphics) { + runMapping(new MapVoidAction("paintAll") { + @Override + public void map() { + getSource().paintAll(graphics); + } + }); + } + + /** + * Maps {@code Component.prepareImage(Image, int, int, ImageObserver)} + * through queue + */ + public boolean prepareImage(final Image image, final int i, final int i1, final ImageObserver imageObserver) { + return (runMapping(new MapBooleanAction("prepareImage") { + @Override + public boolean map() { + return getSource().prepareImage(image, i, i1, imageObserver); + } + })); + } + + /** + * Maps {@code Component.prepareImage(Image, ImageObserver)} through queue + */ + public boolean prepareImage(final Image image, final ImageObserver imageObserver) { + return (runMapping(new MapBooleanAction("prepareImage") { + @Override + public boolean map() { + return getSource().prepareImage(image, imageObserver); + } + })); + } + + /** + * Maps {@code Component.print(Graphics)} through queue + */ + public void print(final Graphics graphics) { + runMapping(new MapVoidAction("print") { + @Override + public void map() { + getSource().print(graphics); + } + }); + } + + /** + * Maps {@code Component.printAll(Graphics)} through queue + */ + public void printAll(final Graphics graphics) { + runMapping(new MapVoidAction("printAll") { + @Override + public void map() { + getSource().printAll(graphics); + } + }); + } + + /** + * Maps {@code Component.remove(MenuComponent)} through queue + */ + public void remove(final MenuComponent menuComponent) { + runMapping(new MapVoidAction("remove") { + @Override + public void map() { + getSource().remove(menuComponent); + } + }); + } + + /** + * Maps {@code Component.removeComponentListener(ComponentListener)} + * through queue + */ + public void removeComponentListener(final ComponentListener componentListener) { + runMapping(new MapVoidAction("removeComponentListener") { + @Override + public void map() { + getSource().removeComponentListener(componentListener); + } + }); + } + + /** + * Maps {@code Component.removeFocusListener(FocusListener)} through queue + */ + public void removeFocusListener(final FocusListener focusListener) { + runMapping(new MapVoidAction("removeFocusListener") { + @Override + public void map() { + getSource().removeFocusListener(focusListener); + } + }); + } + + /** + * Maps + * {@code Component.removeInputMethodListener(InputMethodListener)} + * through queue + */ + public void removeInputMethodListener(final InputMethodListener inputMethodListener) { + runMapping(new MapVoidAction("removeInputMethodListener") { + @Override + public void map() { + getSource().removeInputMethodListener(inputMethodListener); + } + }); + } + + /** + * Maps {@code Component.removeKeyListener(KeyListener)} through queue + */ + public void removeKeyListener(final KeyListener keyListener) { + runMapping(new MapVoidAction("removeKeyListener") { + @Override + public void map() { + getSource().removeKeyListener(keyListener); + } + }); + } + + /** + * Maps {@code Component.removeMouseListener(MouseListener)} through queue + */ + public void removeMouseListener(final MouseListener mouseListener) { + runMapping(new MapVoidAction("removeMouseListener") { + @Override + public void map() { + getSource().removeMouseListener(mouseListener); + } + }); + } + + /** + * Maps + * {@code Component.removeMouseMotionListener(MouseMotionListener)} + * through queue + */ + public void removeMouseMotionListener(final MouseMotionListener mouseMotionListener) { + runMapping(new MapVoidAction("removeMouseMotionListener") { + @Override + public void map() { + getSource().removeMouseMotionListener(mouseMotionListener); + } + }); + } + + /** + * Maps {@code Component.removeNotify()} through queue + */ + public void removeNotify() { + runMapping(new MapVoidAction("removeNotify") { + @Override + public void map() { + getSource().removeNotify(); + } + }); + } + + /** + * Maps + * {@code Component.removePropertyChangeListener(PropertyChangeListener)} + * through queue + */ + public void removePropertyChangeListener(final PropertyChangeListener propertyChangeListener) { + runMapping(new MapVoidAction("removePropertyChangeListener") { + @Override + public void map() { + getSource().removePropertyChangeListener(propertyChangeListener); + } + }); + } + + /** + * Maps + * {@code Component.removePropertyChangeListener(String, PropertyChangeListener)} + * through queue + */ + public void removePropertyChangeListener(final String string, final PropertyChangeListener propertyChangeListener) { + runMapping(new MapVoidAction("removePropertyChangeListener") { + @Override + public void map() { + getSource().removePropertyChangeListener(string, propertyChangeListener); + } + }); + } + + /** + * Maps {@code Component.repaint()} through queue + */ + public void repaint() { + runMapping(new MapVoidAction("repaint") { + @Override + public void map() { + getSource().repaint(); + } + }); + } + + /** + * Maps {@code Component.repaint(int, int, int, int)} through queue + */ + public void repaint(final int i, final int i1, final int i2, final int i3) { + runMapping(new MapVoidAction("repaint") { + @Override + public void map() { + getSource().repaint(i, i1, i2, i3); + } + }); + } + + /** + * Maps {@code Component.repaint(long)} through queue + */ + public void repaint(final long l) { + runMapping(new MapVoidAction("repaint") { + @Override + public void map() { + getSource().repaint(l); + } + }); + } + + /** + * Maps {@code Component.repaint(long, int, int, int, int)} through queue + */ + public void repaint(final long l, final int i, final int i1, final int i2, final int i3) { + runMapping(new MapVoidAction("repaint") { + @Override + public void map() { + getSource().repaint(l, i, i1, i2, i3); + } + }); + } + + /** + * Maps {@code Component.requestFocus()} through queue + */ + public void requestFocus() { + runMapping(new MapVoidAction("requestFocus") { + @Override + public void map() { + getSource().requestFocus(); + } + }); + } + + /** + * Maps {@code Component.setBackground(Color)} through queue + */ + public void setBackground(final Color color) { + runMapping(new MapVoidAction("setBackground") { + @Override + public void map() { + getSource().setBackground(color); + } + }); + } + + /** + * Maps {@code Component.setBounds(int, int, int, int)} through queue + */ + public void setBounds(final int i, final int i1, final int i2, final int i3) { + runMapping(new MapVoidAction("setBounds") { + @Override + public void map() { + getSource().setBounds(i, i1, i2, i3); + } + }); + } + + /** + * Maps {@code Component.setBounds(Rectangle)} through queue + */ + public void setBounds(final Rectangle rectangle) { + runMapping(new MapVoidAction("setBounds") { + @Override + public void map() { + getSource().setBounds(rectangle); + } + }); + } + + /** + * Maps {@code Component.setComponentOrientation(ComponentOrientation)} + * through queue + */ + public void setComponentOrientation(final ComponentOrientation componentOrientation) { + runMapping(new MapVoidAction("setComponentOrientation") { + @Override + public void map() { + getSource().setComponentOrientation(componentOrientation); + } + }); + } + + /** + * Maps {@code Component.setCursor(Cursor)} through queue + */ + public void setCursor(final Cursor cursor) { + runMapping(new MapVoidAction("setCursor") { + @Override + public void map() { + getSource().setCursor(cursor); + } + }); + } + + /** + * Maps {@code Component.setDropTarget(DropTarget)} through queue + */ + public void setDropTarget(final DropTarget dropTarget) { + runMapping(new MapVoidAction("setDropTarget") { + @Override + public void map() { + getSource().setDropTarget(dropTarget); + } + }); + } + + /** + * Maps {@code Component.setEnabled(boolean)} through queue + */ + public void setEnabled(final boolean b) { + runMapping(new MapVoidAction("setEnabled") { + @Override + public void map() { + getSource().setEnabled(b); + } + }); + } + + /** + * Maps {@code Component.setFont(Font)} through queue + */ + public void setFont(final Font font) { + runMapping(new MapVoidAction("setFont") { + @Override + public void map() { + getSource().setFont(font); + } + }); + } + + /** + * Maps {@code Component.setForeground(Color)} through queue + */ + public void setForeground(final Color color) { + runMapping(new MapVoidAction("setForeground") { + @Override + public void map() { + getSource().setForeground(color); + } + }); + } + + /** + * Maps {@code Component.setLocale(Locale)} through queue + */ + public void setLocale(final Locale locale) { + runMapping(new MapVoidAction("setLocale") { + @Override + public void map() { + getSource().setLocale(locale); + } + }); + } + + /** + * Maps {@code Component.setLocation(int, int)} through queue + */ + public void setLocation(final int i, final int i1) { + runMapping(new MapVoidAction("setLocation") { + @Override + public void map() { + getSource().setLocation(i, i1); + } + }); + } + + /** + * Maps {@code Component.setLocation(Point)} through queue + */ + public void setLocation(final Point point) { + runMapping(new MapVoidAction("setLocation") { + @Override + public void map() { + getSource().setLocation(point); + } + }); + } + + /** + * Maps {@code Component.setName(String)} through queue + */ + public void setName(final String string) { + runMapping(new MapVoidAction("setName") { + @Override + public void map() { + getSource().setName(string); + } + }); + } + + /** + * Maps {@code Component.setSize(int, int)} through queue + */ + public void setSize(final int i, final int i1) { + runMapping(new MapVoidAction("setSize") { + @Override + public void map() { + getSource().setSize(i, i1); + } + }); + } + + /** + * Maps {@code Component.setSize(Dimension)} through queue + */ + public void setSize(final Dimension dimension) { + runMapping(new MapVoidAction("setSize") { + @Override + public void map() { + getSource().setSize(dimension); + } + }); + } + + /** + * Maps {@code Component.setVisible(boolean)} through queue + */ + public void setVisible(final boolean b) { + runMapping(new MapVoidAction("setVisible") { + @Override + public void map() { + getSource().setVisible(b); + } + }); + } + + /** + * Maps {@code Component.transferFocus()} through queue + */ + public void transferFocus() { + runMapping(new MapVoidAction("transferFocus") { + @Override + public void map() { + getSource().transferFocus(); + } + }); + } + + /** + * Maps {@code Component.update(Graphics)} through queue + */ + public void update(final Graphics graphics) { + runMapping(new MapVoidAction("update") { + @Override + public void map() { + getSource().update(graphics); + } + }); + } + + /** + * Maps {@code Component.validate()} through queue + */ + public void validate() { + runMapping(new MapVoidAction("validate") { + @Override + public void map() { + getSource().validate(); + } + }); + } + + //End of mapping // + //////////////////////////////////////////////////////// + private void setEventDispatcher(EventDispatcher dispatcher) { + dispatcher.setOutput(getOutput().createErrorOutput()); + dispatcher.setTimeouts(getTimeouts()); + this.dispatcher = dispatcher; + } + + static class VisibleComponentFinder implements ComponentChooser { + + ComponentChooser subFinder; + + public VisibleComponentFinder(ComponentChooser sf) { + subFinder = sf; + } + + @Override + public boolean checkComponent(Component comp) { + if (comp.isShowing()) { + return subFinder.checkComponent(comp); + } + return false; + } + + @Override + public String getDescription() { + return subFinder.getDescription(); + } + + @Override + public String toString() { + return "VisibleComponentFinder{" + "subFinder=" + subFinder + '}'; + } + } + +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/ContainerOperator.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/ContainerOperator.java new file mode 100644 index 00000000000..37ea08f0695 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/ContainerOperator.java @@ -0,0 +1,636 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.operators; + +import java.awt.Component; +import java.awt.Container; +import java.awt.Graphics; +import java.awt.Insets; +import java.awt.LayoutManager; +import java.awt.Point; +import java.awt.event.ContainerListener; + +import org.netbeans.jemmy.ComponentChooser; +import org.netbeans.jemmy.ComponentSearcher; +import org.netbeans.jemmy.JemmyException; +import org.netbeans.jemmy.Outputable; +import org.netbeans.jemmy.TestOut; +import org.netbeans.jemmy.TimeoutExpiredException; +import org.netbeans.jemmy.Timeoutable; +import org.netbeans.jemmy.Timeouts; +import org.netbeans.jemmy.Waitable; +import org.netbeans.jemmy.Waiter; + +/** + *

Timeouts used:
+ * ComponentOperator.WaitComponentTimeout - time to wait container displayed + *
. + * + * @see org.netbeans.jemmy.Timeouts + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + * + */ +public class ContainerOperator extends ComponentOperator + implements Timeoutable, Outputable { + + private final static long WAIT_SUBCOMPONENT_TIMEOUT = 60000; + + private ComponentSearcher searcher; + private Timeouts timeouts; + private TestOut output; + + /** + * Constructor. + * + * @param b Container component. + */ + public ContainerOperator(Container b) { + super(b); + searcher = new ComponentSearcher(b); + searcher.setOutput(TestOut.getNullOutput()); + } + + /** + * Constructs a ContainerOperator object. + * + * @param cont container + * @param chooser a component chooser specifying searching criteria. + * @param index an index between appropriate ones. + */ + public ContainerOperator(ContainerOperator cont, ComponentChooser chooser, int index) { + this((Container) cont. + waitSubComponent(new ContainerFinder(chooser), + index)); + copyEnvironment(cont); + } + + /** + * Constructs a ContainerOperator object. + * + * @param cont container + * @param chooser a component chooser specifying searching criteria. + */ + public ContainerOperator(ContainerOperator cont, ComponentChooser chooser) { + this(cont, chooser, 0); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont Operator pointing a container to search component in. + * @param index Ordinal component index. + * @throws TimeoutExpiredException + */ + public ContainerOperator(ContainerOperator cont, int index) { + this((Container) waitComponent(cont, + new ContainerFinder(), + index)); + copyEnvironment(cont); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont Operator pointing a container to search component in. + * @throws TimeoutExpiredException + */ + public ContainerOperator(ContainerOperator cont) { + this(cont, 0); + } + + /** + * Searches Container in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @param index Ordinal component index. + * @return Container instance or null if component was not found. + */ + public static Container findContainer(Container cont, ComponentChooser chooser, int index) { + return (Container) findComponent(cont, new ContainerFinder(chooser), index); + } + + /** + * Searches 0'th Container in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @return Container instance or null if component was not found. + */ + public static Container findContainer(Container cont, ComponentChooser chooser) { + return findContainer(cont, chooser, 0); + } + + /** + * Searches Container in container. + * + * @param cont Container to search component in. + * @param index Ordinal component index. + * @return Container instance or null if component was not found. + */ + public static Container findContainer(Container cont, int index) { + return findContainer(cont, ComponentSearcher.getTrueChooser(Integer.toString(index) + "'th Container instance"), index); + } + + /** + * Searches 0'th Container in container. + * + * @param cont Container to search component in. + * @return Container instance or null if component was not found. + */ + public static Container findContainer(Container cont) { + return findContainer(cont, 0); + } + + /** + * Searches Container object which component lies on. + * + * @param comp Component to find Container under. + * @param chooser a chooser specifying searching criteria. + * @return Container instance or null if component was not found. + */ + public static Container findContainerUnder(Component comp, ComponentChooser chooser) { + return (new ComponentOperator(comp). + getContainer(new ContainerFinder(chooser))); + } + + /** + * Searches Container object which component lies on. + * + * @param comp Component to find Container under. + * @return Container instance or null if component was not found. + */ + public static Container findContainerUnder(Component comp) { + return findContainerUnder(comp, ComponentSearcher.getTrueChooser("Container")); + } + + /** + * Waits Container in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @param index Ordinal component index. + * @return Container instance. + * @throws TimeoutExpiredException + */ + public static Container waitContainer(Container cont, ComponentChooser chooser, int index) { + return (Container) waitComponent(cont, new ContainerFinder(chooser), index); + } + + /** + * Waits 0'th Container in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @return Container instance. + * @throws TimeoutExpiredException + */ + public static Container waitContainer(Container cont, ComponentChooser chooser) { + return waitContainer(cont, chooser, 0); + } + + /** + * Waits Container in container. + * + * @param cont Container to search component in. + * @param index Ordinal component index. + * @return Container instance. + * @throws TimeoutExpiredException + */ + public static Container waitContainer(Container cont, int index) { + return waitContainer(cont, ComponentSearcher.getTrueChooser(Integer.toString(index) + "'th Container instance"), index); + } + + /** + * Waits 0'th Container in container. + * + * @param cont Container to search component in. + * @return Container instance. + * @throws TimeoutExpiredException + */ + public static Container waitContainer(Container cont) { + return waitContainer(cont, 0); + } + + static { + Timeouts.initDefault("ComponentOperator.WaitComponentTimeout", WAIT_SUBCOMPONENT_TIMEOUT); + } + + @Override + public void setTimeouts(Timeouts timeouts) { + super.setTimeouts(timeouts); + this.timeouts = timeouts; + } + + @Override + public Timeouts getTimeouts() { + return timeouts; + } + + @Override + public void setOutput(TestOut out) { + output = out; + super.setOutput(output.createErrorOutput()); + } + + @Override + public TestOut getOutput() { + return output; + } + + /** + * Searches for a subcomponent. + * + * @param chooser a chooser specifying searching criteria. + * @param index Ordinal component index. + * @return Component instance. + */ + public Component findSubComponent(ComponentChooser chooser, int index) { + getOutput().printLine("Looking for \"" + chooser.getDescription() + + "\" subcomponent"); + return searcher.findComponent(chooser, index); + } + + /** + * Searches for a subcomponent. + * + * @param chooser a chooser specifying searching criteria. + * @return Component instance. + */ + public Component findSubComponent(ComponentChooser chooser) { + return findSubComponent(chooser, 0); + } + + /** + * Waits for a subcomponent. + * + * @param chooser a chooser specifying searching criteria. + * @param index Ordinal component index. + * @return Component instance. + */ + public Component waitSubComponent(final ComponentChooser chooser, final int index) { + getOutput().printLine("Waiting for \"" + chooser.getDescription() + + "\" subcomponent"); + final ComponentSearcher searcher = new ComponentSearcher((Container) getSource()); + searcher.setOutput(getOutput().createErrorOutput()); + Waiter waiter = new Waiter<>(new Waitable() { + @Override + public Component actionProduced(Void obj) { + return searcher.findComponent(chooser, index); + } + + @Override + public String getDescription() { + return ("Wait for \"" + chooser.getDescription() + + "\" subcomponent to be displayed"); + } + + @Override + public String toString() { + return "ContainerOperator.waitSubComponent.Waitable{description = " + getDescription() + '}'; + } + }); + waiter.setTimeoutsToCloneOf(getTimeouts(), "ComponentOperator.WaitComponentTimeout"); + waiter.setOutput(getOutput()); + try { + return waiter.waitAction(null); + } catch (InterruptedException e) { + throw (new JemmyException("Waiting for \"" + chooser.getDescription() + + "\" component has been interrupted", e)); + } + } + + /** + * Waits for a subcomponent. + * + * @param chooser a chooser specifying searching criteria. + * @return Component instance. + */ + public Component waitSubComponent(ComponentChooser chooser) { + return waitSubComponent(chooser, 0); + } + + /** + * Waits for a subcomponent and creates an operator. + * + * @param chooser a chooser specifying searching criteria. + * @param index Ordinal component index. + * @return Component instance. + */ + public ComponentOperator createSubOperator(ComponentChooser chooser, int index) { + return createOperator(waitSubComponent(chooser, index)); + } + + /** + * Waits for a subcomponent and creates an operator. + * + * @param chooser a chooser specifying searching criteria. + * @return Component instance. + */ + public ComponentOperator createSubOperator(ComponentChooser chooser) { + return createSubOperator(chooser, 0); + } + + //////////////////////////////////////////////////////// + //Mapping // + /** + * Maps {@code Container.add(Component)} through queue + */ + public Component add(final Component component) { + return (runMapping(new MapAction("add") { + @Override + public Component map() { + return ((Container) getSource()).add(component); + } + })); + } + + /** + * Maps {@code Container.add(Component, int)} through queue + */ + public Component add(final Component component, final int i) { + return (runMapping(new MapAction("add") { + @Override + public Component map() { + return ((Container) getSource()).add(component, i); + } + })); + } + + /** + * Maps {@code Container.add(Component, Object)} through queue + */ + public void add(final Component component, final Object object) { + runMapping(new MapVoidAction("add") { + @Override + public void map() { + ((Container) getSource()).add(component, object); + } + }); + } + + /** + * Maps {@code Container.add(Component, Object, int)} through queue + */ + public void add(final Component component, final Object object, final int i) { + runMapping(new MapVoidAction("add") { + @Override + public void map() { + ((Container) getSource()).add(component, object, i); + } + }); + } + + /** + * Maps {@code Container.add(String, Component)} through queue + */ + public Component add(final String string, final Component component) { + return (runMapping(new MapAction("add") { + @Override + public Component map() { + return ((Container) getSource()).add(string, component); + } + })); + } + + /** + * Maps {@code Container.addContainerListener(ContainerListener)} + * through queue + */ + public void addContainerListener(final ContainerListener containerListener) { + runMapping(new MapVoidAction("addContainerListener") { + @Override + public void map() { + ((Container) getSource()).addContainerListener(containerListener); + } + }); + } + + /** + * Maps {@code Container.findComponentAt(int, int)} through queue + */ + public Component findComponentAt(final int i, final int i1) { + return (runMapping(new MapAction("findComponentAt") { + @Override + public Component map() { + return ((Container) getSource()).findComponentAt(i, i1); + } + })); + } + + /** + * Maps {@code Container.findComponentAt(Point)} through queue + */ + public Component findComponentAt(final Point point) { + return (runMapping(new MapAction("findComponentAt") { + @Override + public Component map() { + return ((Container) getSource()).findComponentAt(point); + } + })); + } + + /** + * Maps {@code Container.getComponent(int)} through queue + */ + public Component getComponent(final int i) { + return (runMapping(new MapAction("getComponent") { + @Override + public Component map() { + return ((Container) getSource()).getComponent(i); + } + })); + } + + /** + * Maps {@code Container.getComponentCount()} through queue + */ + public int getComponentCount() { + return (runMapping(new MapIntegerAction("getComponentCount") { + @Override + public int map() { + return ((Container) getSource()).getComponentCount(); + } + })); + } + + /** + * Maps {@code Container.getComponents()} through queue + */ + public Component[] getComponents() { + return ((Component[]) runMapping(new MapAction("getComponents") { + @Override + public Object map() { + return ((Container) getSource()).getComponents(); + } + })); + } + + /** + * Maps {@code Container.getInsets()} through queue + */ + public Insets getInsets() { + return (runMapping(new MapAction("getInsets") { + @Override + public Insets map() { + return ((Container) getSource()).getInsets(); + } + })); + } + + /** + * Maps {@code Container.getLayout()} through queue + */ + public LayoutManager getLayout() { + return (runMapping(new MapAction("getLayout") { + @Override + public LayoutManager map() { + return ((Container) getSource()).getLayout(); + } + })); + } + + /** + * Maps {@code Container.isAncestorOf(Component)} through queue + */ + public boolean isAncestorOf(final Component component) { + return (runMapping(new MapBooleanAction("isAncestorOf") { + @Override + public boolean map() { + return ((Container) getSource()).isAncestorOf(component); + } + })); + } + + /** + * Maps {@code Container.paintComponents(Graphics)} through queue + */ + public void paintComponents(final Graphics graphics) { + runMapping(new MapVoidAction("paintComponents") { + @Override + public void map() { + ((Container) getSource()).paintComponents(graphics); + } + }); + } + + /** + * Maps {@code Container.printComponents(Graphics)} through queue + */ + public void printComponents(final Graphics graphics) { + runMapping(new MapVoidAction("printComponents") { + @Override + public void map() { + ((Container) getSource()).printComponents(graphics); + } + }); + } + + /** + * Maps {@code Container.remove(int)} through queue + */ + public void remove(final int i) { + runMapping(new MapVoidAction("remove") { + @Override + public void map() { + ((Container) getSource()).remove(i); + } + }); + } + + /** + * Maps {@code Container.remove(Component)} through queue + */ + public void remove(final Component component) { + runMapping(new MapVoidAction("remove") { + @Override + public void map() { + ((Container) getSource()).remove(component); + } + }); + } + + /** + * Maps {@code Container.removeAll()} through queue + */ + public void removeAll() { + runMapping(new MapVoidAction("removeAll") { + @Override + public void map() { + ((Container) getSource()).removeAll(); + } + }); + } + + /** + * Maps {@code Container.removeContainerListener(ContainerListener)} + * through queue + */ + public void removeContainerListener(final ContainerListener containerListener) { + runMapping(new MapVoidAction("removeContainerListener") { + @Override + public void map() { + ((Container) getSource()).removeContainerListener(containerListener); + } + }); + } + + /** + * Maps {@code Container.setLayout(LayoutManager)} through queue + */ + public void setLayout(final LayoutManager layoutManager) { + runMapping(new MapVoidAction("setLayout") { + @Override + public void map() { + ((Container) getSource()).setLayout(layoutManager); + } + }); + } + + //End of mapping // + //////////////////////////////////////////////////////// + /** + * Checks component type. + */ + public static class ContainerFinder extends Finder { + + /** + * Constructs ContainerFinder. + * + * @param sf other searching criteria. + */ + public ContainerFinder(ComponentChooser sf) { + super(Container.class, sf); + } + + /** + * Constructs ContainerFinder. + */ + public ContainerFinder() { + super(Container.class); + } + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/DialogOperator.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/DialogOperator.java new file mode 100644 index 00000000000..a15ebc5356d --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/DialogOperator.java @@ -0,0 +1,511 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.operators; + +import java.awt.Component; +import java.awt.Dialog; +import java.awt.Window; +import java.util.Hashtable; + +import org.netbeans.jemmy.ComponentChooser; +import org.netbeans.jemmy.DialogWaiter; +import org.netbeans.jemmy.JemmyProperties; +import org.netbeans.jemmy.TestOut; +import org.netbeans.jemmy.TimeoutExpiredException; +import org.netbeans.jemmy.Timeouts; + +/** + *

Timeouts used:
+ * DialogWaiter.WaitDialogTimeout - time to wait dialog displayed
+ * DialogWaiter.AfterDialogTimeout - time to sleep after dialog has been + * dispayed
+ * ComponentOperator.WaitStateTimeout - time to wait for title
. + * + * @see org.netbeans.jemmy.Timeouts + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + * + */ +public class DialogOperator extends WindowOperator { + + /** + * Identifier for a title property. + * + * @see #getDump + */ + public static final String TITLE_DPROP = "Title"; + + /** + * Identifier for a modal property. + * + * @see #getDump + */ + public static final String IS_MODAL_DPROP = "Modal"; + + /** + * Identifier for a resizable property. + * + * @see #getDump + */ + public static final String IS_RESIZABLE_DPROP = "Resizable"; + + /** + * Constructs a DialogOperator object. + * + * @param w window + */ + public DialogOperator(Dialog w) { + super(w); + } + + /** + * Constructs a DialogOperator object. + * + * @param chooser a component chooser specifying searching criteria. + * @param index an index between appropriate ones. + * @param env an operator to copy environment from. + */ + public DialogOperator(ComponentChooser chooser, int index, Operator env) { + this(waitDialog(new DialogFinder(chooser), + index, + env.getTimeouts(), + env.getOutput())); + copyEnvironment(env); + } + + /** + * Constructs a DialogOperator object. + * + * @param chooser a component chooser specifying searching criteria. + * @param index an index between appropriate ones. + */ + public DialogOperator(ComponentChooser chooser, int index) { + this(chooser, index, Operator.getEnvironmentOperator()); + } + + /** + * Constructs a DialogOperator object. + * + * @param chooser a component chooser specifying searching criteria. + */ + public DialogOperator(ComponentChooser chooser) { + this(chooser, 0); + } + + /** + * Constructs a DialogOperator object. + * + * @param owner window - owner + * @param chooser a component chooser specifying searching criteria. + * @param index an index between appropriate ones. + */ + public DialogOperator(WindowOperator owner, ComponentChooser chooser, int index) { + this((Dialog) owner. + waitSubWindow(new DialogFinder(chooser), + index)); + copyEnvironment(owner); + } + + /** + * Constructs a DialogOperator object. + * + * @param owner window - owner + * @param chooser a component chooser specifying searching criteria. + */ + public DialogOperator(WindowOperator owner, ComponentChooser chooser) { + this(owner, chooser, 0); + } + + /** + * Constructor. Waits for a dialog to show. The dialog is identified as the + * {@code index+1}'th {@code java.awt.Dialog} that shows, is owned + * by the window managed by the {@code WindowOperator} + * {@code owner}, and that has the desired title. Uses owner's timeout + * and output for waiting and to init this operator. + * + * @param owner Operator pointing to a window owner. + * @param title The desired title. + * @param index Ordinal index. The first dialog has {@code index} 0. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public DialogOperator(WindowOperator owner, String title, int index) { + this(waitDialog(owner, + new DialogByTitleFinder(title, + owner.getComparator()), + index)); + copyEnvironment(owner); + } + + /** + * Uses owner's timeout and output for waiting and to init operator. Waits + * for a dialog to show. The dialog is identified as the first + * {@code java.awt.Dialog} that shows, is owned by the window managed + * by the {@code WindowOperator} {@code owner}, and that has the + * desired title. Uses owner's timeout and output for waiting and to init + * this operator. + * + * @param owner Operator pointing to a window owner. + * @param title The desired title. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public DialogOperator(WindowOperator owner, String title) { + this(owner, title, 0); + } + + /** + * Constructor. Waits for the index'th dialog between owner's children. Uses + * owner'th timeout and output for waiting and to init operator. + * + * @param owner Operator pointing to a window owner. + * @param index Ordinal component index. + * @throws TimeoutExpiredException + */ + public DialogOperator(WindowOperator owner, int index) { + this(waitDialog(owner, + new DialogFinder(), + index)); + copyEnvironment(owner); + } + + /** + * Constructor. Waits for the first dialog between owner's children. Uses + * owner'th timeout and output for waiting and to init operator. + * + * @param owner Operator pointing to a window owner. + * @throws TimeoutExpiredException + */ + public DialogOperator(WindowOperator owner) { + this(owner, 0); + } + + /** + * Constructor. Waits for the dialog with "title" subtitle. Constructor can + * be used in complicated cases when output or timeouts should differ from + * default. + * + * @param title a window title + * @param index Ordinal component index. + * @param env an operator to copy environment from. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public DialogOperator(String title, int index, Operator env) { + this(new DialogByTitleFinder(title, + env.getComparator()), + index, + env); + } + + /** + * Constructor. Waits for the dialog with "title" subtitle. Uses current + * timeouts and output values. + * + * @param title a window title + * @param index Ordinal component index. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @see JemmyProperties#getCurrentTimeouts() + * @see JemmyProperties#getCurrentOutput() + * @throws TimeoutExpiredException + */ + public DialogOperator(String title, int index) { + this(title, index, + ComponentOperator.getEnvironmentOperator()); + } + + /** + * Constructor. Waits for the dialog with "title" subtitle. Uses current + * timeouts and output values. + * + * @param title a window title + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @see JemmyProperties#getCurrentTimeouts() + * @see JemmyProperties#getCurrentOutput() + * @throws TimeoutExpiredException + */ + public DialogOperator(String title) { + this(title, 0); + } + + /** + * Constructor. Waits for the index'th dialog. Uses current timeout and + * output for waiting and to init operator. + * + * @param index Ordinal component index. + * @throws TimeoutExpiredException + */ + public DialogOperator(int index) { + this(waitDialog(new DialogFinder(), + index, + ComponentOperator.getEnvironmentOperator().getTimeouts(), + ComponentOperator.getEnvironmentOperator().getOutput())); + copyEnvironment(ComponentOperator.getEnvironmentOperator()); + } + + /** + * Constructor. Waits for the first dialog. Uses current timeout and output + * for waiting and to init operator. + * + * @throws TimeoutExpiredException + */ + public DialogOperator() { + this(0); + } + + /** + * Waits for title. Uses getComparator() comparator. + * + * @param title Title to wait for. + */ + public void waitTitle(final String title) { + getOutput().printLine("Wait \"" + title + "\" title of dialog \n : " + + toStringSource()); + getOutput().printGolden("Wait \"" + title + "\" title"); + waitState(new DialogByTitleFinder(title, getComparator())); + } + + /** + * Returns information about component. + */ + @Override + public Hashtable getDump() { + Hashtable result = super.getDump(); + if (((Dialog) getSource()).getTitle() != null) { + result.put(TITLE_DPROP, ((Dialog) getSource()).getTitle()); + } + result.put(IS_MODAL_DPROP, ((Dialog) getSource()).isModal() ? "true" : "false"); + result.put(IS_RESIZABLE_DPROP, ((Dialog) getSource()).isResizable() ? "true" : "false"); + return result; + } + + //////////////////////////////////////////////////////// + //Mapping // + /** + * Maps {@code Dialog.getTitle()} through queue + */ + public String getTitle() { + return (runMapping(new MapAction("getTitle") { + @Override + public String map() { + return ((Dialog) getSource()).getTitle(); + } + })); + } + + /** + * Maps {@code Dialog.isModal()} through queue + */ + public boolean isModal() { + return (runMapping(new MapBooleanAction("isModal") { + @Override + public boolean map() { + return ((Dialog) getSource()).isModal(); + } + })); + } + + /** + * Maps {@code Dialog.isResizable()} through queue + */ + public boolean isResizable() { + return (runMapping(new MapBooleanAction("isResizable") { + @Override + public boolean map() { + return ((Dialog) getSource()).isResizable(); + } + })); + } + + /** + * Maps {@code Dialog.setModal(boolean)} through queue + */ + public void setModal(final boolean b) { + runMapping(new MapVoidAction("setModal") { + @Override + public void map() { + ((Dialog) getSource()).setModal(b); + } + }); + } + + /** + * Maps {@code Dialog.setResizable(boolean)} through queue + */ + public void setResizable(final boolean b) { + runMapping(new MapVoidAction("setResizable") { + @Override + public void map() { + ((Dialog) getSource()).setResizable(b); + } + }); + } + + /** + * Maps {@code Dialog.setTitle(String)} through queue + */ + public void setTitle(final String string) { + runMapping(new MapVoidAction("setTitle") { + @Override + public void map() { + ((Dialog) getSource()).setTitle(string); + } + }); + } + + //End of mapping // + //////////////////////////////////////////////////////// + /** + * A method to be used from subclasses. Uses timeouts and output passed as + * parameters during the waiting. + * + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @param index Ordinal component index. + * @param timeouts timeouts to be used during the waiting. + * @param output an output to be used during the waiting. + * @return Component instance or null if component was not found. + */ + protected static Dialog waitDialog(ComponentChooser chooser, int index, + Timeouts timeouts, TestOut output) { + try { + DialogWaiter waiter = new DialogWaiter(); + waiter.setTimeouts(timeouts); + waiter.setOutput(output); + return waiter.waitDialog(new DialogFinder(chooser), index); + } catch (InterruptedException e) { + output.printStackTrace(e); + return null; + } + } + + /** + * A method to be used from subclasses. Uses {@code owner}'s timeouts + * and output during the waiting. + * + * @param owner a window - dialog owner. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @param index Ordinal component index. + * @return Component instance or null if component was not found. + */ + protected static Dialog waitDialog(WindowOperator owner, ComponentChooser chooser, int index) { + return (waitDialog((Window) owner.getSource(), + chooser, index, + owner.getTimeouts(), owner.getOutput())); + } + + /** + * A method to be used from subclasses. Uses timeouts and output passed as + * parameters during the waiting. + * + * @param owner a window - dialog owner. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @param index Ordinal component index. + * @param timeouts timeouts to be used during the waiting. + * @param output an output to be used during the waiting. + * @return Component instance or null if component was not found. + */ + protected static Dialog waitDialog(Window owner, ComponentChooser chooser, int index, + Timeouts timeouts, TestOut output) { + try { + DialogWaiter waiter = new DialogWaiter(); + waiter.setTimeouts(timeouts); + waiter.setOutput(output); + return (waiter. + waitDialog(owner, new DialogFinder(chooser), index)); + } catch (InterruptedException e) { + JemmyProperties.getCurrentOutput().printStackTrace(e); + return null; + } + } + + /** + * Checks component type. + */ + public static class DialogFinder extends Finder { + + /** + * Constructs DialogFinder. + * + * @param sf other searching criteria. + */ + public DialogFinder(ComponentChooser sf) { + super(Dialog.class, sf); + } + + /** + * Constructs DialogFinder. + */ + public DialogFinder() { + super(Dialog.class); + } + } + + /** + * Allows to find component by title. + */ + public static class DialogByTitleFinder implements ComponentChooser { + + String title; + StringComparator comparator; + + /** + * Constructs DialogByTitleFinder. + * + * @param t a text pattern + * @param comparator specifies string comparision algorithm. + */ + public DialogByTitleFinder(String t, StringComparator comparator) { + title = t; + this.comparator = comparator; + } + + /** + * Constructs DialogByTitleFinder. + * + * @param t a text pattern + */ + public DialogByTitleFinder(String t) { + this(t, Operator.getDefaultStringComparator()); + } + + @Override + public boolean checkComponent(Component comp) { + if (comp instanceof Dialog) { + if (comp.isShowing() && ((Dialog) comp).getTitle() != null) { + return comparator.equals(((Dialog) comp).getTitle(), title); + } + } + return false; + } + + @Override + public String getDescription() { + return "Dialog with title \"" + title + "\""; + } + + @Override + public String toString() { + return "DialogByTitleFinder{" + "title=" + title + ", comparator=" + comparator + '}'; + } + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/FrameOperator.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/FrameOperator.java new file mode 100644 index 00000000000..d66a8e9c211 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/FrameOperator.java @@ -0,0 +1,558 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.operators; + +import java.awt.Component; +import java.awt.Frame; +import java.awt.Image; +import java.awt.MenuBar; +import java.util.Hashtable; + +import org.netbeans.jemmy.ComponentChooser; +import org.netbeans.jemmy.FrameWaiter; +import org.netbeans.jemmy.JemmyProperties; +import org.netbeans.jemmy.Outputable; +import org.netbeans.jemmy.TestOut; +import org.netbeans.jemmy.TimeoutExpiredException; +import org.netbeans.jemmy.Timeouts; +import org.netbeans.jemmy.drivers.DriverManager; +import org.netbeans.jemmy.drivers.FrameDriver; + +/** + *

Timeouts used:
+ * FrameWaiter.WaitFrameTimeout - time to wait frame displayed
+ * FrameWaiter.AfterFrameTimeout - time to sleep after frame has been dispayed + *
+ * ComponentOperator.WaitStateTimeout - time to wait for text
. + * + * @see org.netbeans.jemmy.Timeouts + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + * + */ +public class FrameOperator extends WindowOperator implements Outputable { + + /** + * Identifier for a title property. + * + * @see #getDump + */ + public static final String TITLE_DPROP = "Title"; + + /** + * Identifier for a state property. + * + * @see #getDump + */ + public static final String STATE_DPROP = "State"; + + /** + * Identifier for a "normal state" state property value. + * + * @see #getDump + */ + public static final String STATE_NORMAL_DPROP_VALUE = "NORMAL"; + + /** + * Identifier for a "iconified state" state property value. + * + * @see #getDump + */ + public static final String STATE_ICONIFIED_DPROP_VALUE = "ICONIFIED"; + + /** + * Identifier for a resizable property. + * + * @see #getDump + */ + public static final String IS_RESIZABLE_DPROP = "Resizable"; + + TestOut output; + FrameDriver driver; + + /** + * Constructs a FrameOperator object. + * + * @param w window + */ + public FrameOperator(Frame w) { + super(w); + driver = DriverManager.getFrameDriver(getClass()); + } + + /** + * Constructs a FrameOperator object. + * + * @param chooser a component chooser specifying searching criteria. + * @param index an index between appropriate ones. + * @param env an operator to copy environment from. + */ + public FrameOperator(ComponentChooser chooser, int index, Operator env) { + this(waitFrame(new FrameFinder(chooser), + index, + env.getTimeouts(), + env.getOutput())); + copyEnvironment(env); + } + + /** + * Constructs a FrameOperator object. + * + * @param chooser a component chooser specifying searching criteria. + * @param index an index between appropriate ones. + */ + public FrameOperator(ComponentChooser chooser, int index) { + this(chooser, index, Operator.getEnvironmentOperator()); + } + + /** + * Constructs a FrameOperator object. + * + * @param chooser a component chooser specifying searching criteria. + */ + public FrameOperator(ComponentChooser chooser) { + this(chooser, 0); + } + + /** + * Constructor. Waits for the frame with "title" subtitle. Constructor can + * be used in complicated cases when output or timeouts should differ from + * default. + * + * @param title a window title + * @param index Ordinal component index. + * @param env an operator to copy environment from. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public FrameOperator(String title, int index, Operator env) { + this(waitFrame(new FrameByTitleFinder(title, + env.getComparator()), + index, + env.getTimeouts(), + env.getOutput())); + copyEnvironment(env); + } + + /** + * Constructor. Waits for the frame with "title" subtitle. Uses current + * timeouts and output values. + * + * @param title a window title + * @param index Ordinal component index. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @see JemmyProperties#getCurrentTimeouts() + * @see JemmyProperties#getCurrentOutput() + * @throws TimeoutExpiredException + */ + public FrameOperator(String title, int index) { + this(title, index, + ComponentOperator.getEnvironmentOperator()); + } + + /** + * Constructor. Waits for the frame with "title" subtitle. Uses current + * timeouts and output values. + * + * @param title a window title + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @see JemmyProperties#getCurrentTimeouts() + * @see JemmyProperties#getCurrentOutput() + * @throws TimeoutExpiredException + */ + public FrameOperator(String title) { + this(title, 0); + } + + /** + * Constructor. Waits for the index'th frame. Uses current timeout and + * output for waiting and to init operator. + * + * @param index Ordinal component index. + * @throws TimeoutExpiredException + */ + public FrameOperator(int index) { + this(waitFrame(new FrameFinder(), + index, + ComponentOperator.getEnvironmentOperator().getTimeouts(), + ComponentOperator.getEnvironmentOperator().getOutput())); + copyEnvironment(ComponentOperator.getEnvironmentOperator()); + } + + /** + * Constructor. Waits for the first frame. Uses current timeout and output + * for waiting and to init operator. + * + * @throws TimeoutExpiredException + */ + public FrameOperator() { + this(0); + } + + @Override + public void setOutput(TestOut out) { + super.setOutput(out); + output = out; + } + + @Override + public TestOut getOutput() { + return output; + } + + @Override + public void copyEnvironment(Operator anotherOperator) { + super.copyEnvironment(anotherOperator); + driver + = (FrameDriver) DriverManager. + getDriver(DriverManager.FRAME_DRIVER_ID, + getClass(), + anotherOperator.getProperties()); + } + + /** + * Waits for title. Uses getComparator() comparator. + * + * @param title Title to wait for. + */ + public void waitTitle(final String title) { + getOutput().printLine("Wait \"" + title + "\" title of frame \n : " + + toStringSource()); + getOutput().printGolden("Wait \"" + title + "\" title"); + waitState(new FrameByTitleFinder(title, getComparator())); + } + + /** + * Iconifies the frame. + */ + public void iconify() { + output.printLine("Iconifying frame\n " + toStringSource()); + output.printGolden("Iconifying frame"); + driver.iconify(this); + if (getVerification()) { + waitState(Frame.ICONIFIED); + } + } + + /** + * Deiconifies the frame. + */ + public void deiconify() { + output.printLine("Deiconifying frame\n " + toStringSource()); + output.printGolden("Deiconifying frame"); + driver.deiconify(this); + if (getVerification()) { + waitState(Frame.NORMAL); + } + } + + /** + * Maximizes the frame. + */ + public void maximize() { + output.printLine("Maximizing frame\n " + toStringSource()); + output.printGolden("Maximizing frame"); + driver.maximize(this); + if (getVerification()) { + waitState(Frame.NORMAL); + } + } + + /** + * Demaximizes the frame. + */ + public void demaximize() { + output.printLine("Demaximizing frame\n " + toStringSource()); + output.printGolden("Demaximizing frame"); + driver.demaximize(this); + if (getVerification()) { + waitState(Frame.NORMAL); + } + } + + /** + * Waits for the frame to have a specified state. + * + * @param state a state for the frame to have. + */ + public void waitState(final int state) { + getOutput().printLine("Wait frame to have " + + Integer.toString(state) + + " state \n : " + + toStringSource()); + getOutput().printGolden("Wait frame to have " + + Integer.toString(state) + + " state"); + waitState(new ComponentChooser() { + @Override + public boolean checkComponent(Component comp) { + return ((Frame) comp).getState() == state; + } + + @Override + public String getDescription() { + return Integer.toString(state) + " state"; + } + + @Override + public String toString() { + return "FrameOperator.waitState.ComponentChooser{description = " + getDescription() + '}'; + } + }); + } + + /** + * Returns information about component. + */ + @Override + public Hashtable getDump() { + Hashtable result = super.getDump(); + if (((Frame) getSource()).getTitle() != null) { + result.put(TITLE_DPROP, ((Frame) getSource()).getTitle()); + } + result.put(STATE_DPROP, + (((Frame) getSource()).getState() == Frame.ICONIFIED) + ? STATE_ICONIFIED_DPROP_VALUE : STATE_NORMAL_DPROP_VALUE); + result.put(IS_RESIZABLE_DPROP, ((Frame) getSource()).isResizable() ? "true" : "false"); + return result; + } + + //////////////////////////////////////////////////////// + //Mapping // + /** + * Maps {@code Frame.getIconImage()} through queue + */ + public Image getIconImage() { + return (runMapping(new MapAction("getIconImage") { + @Override + public Image map() { + return ((Frame) getSource()).getIconImage(); + } + })); + } + + /** + * Maps {@code Frame.getMenuBar()} through queue + */ + public MenuBar getMenuBar() { + return (runMapping(new MapAction("getMenuBar") { + @Override + public MenuBar map() { + return ((Frame) getSource()).getMenuBar(); + } + })); + } + + /** + * Maps {@code Frame.getState()} through queue + */ + public int getState() { + return (runMapping(new MapIntegerAction("getState") { + @Override + public int map() { + return ((Frame) getSource()).getState(); + } + })); + } + + /** + * Maps {@code Frame.getTitle()} through queue + */ + public String getTitle() { + return (runMapping(new MapAction("getTitle") { + @Override + public String map() { + return ((Frame) getSource()).getTitle(); + } + })); + } + + /** + * Maps {@code Frame.isResizable()} through queue + */ + public boolean isResizable() { + return (runMapping(new MapBooleanAction("isResizable") { + @Override + public boolean map() { + return ((Frame) getSource()).isResizable(); + } + })); + } + + /** + * Maps {@code Frame.setIconImage(Image)} through queue + */ + public void setIconImage(final Image image) { + runMapping(new MapVoidAction("setIconImage") { + @Override + public void map() { + ((Frame) getSource()).setIconImage(image); + } + }); + } + + /** + * Maps {@code Frame.setMenuBar(MenuBar)} through queue + */ + public void setMenuBar(final MenuBar menuBar) { + runMapping(new MapVoidAction("setMenuBar") { + @Override + public void map() { + ((Frame) getSource()).setMenuBar(menuBar); + } + }); + } + + /** + * Maps {@code Frame.setResizable(boolean)} through queue + */ + public void setResizable(final boolean b) { + runMapping(new MapVoidAction("setResizable") { + @Override + public void map() { + ((Frame) getSource()).setResizable(b); + } + }); + } + + /** + * Maps {@code Frame.setState(int)} through queue + */ + public void setState(final int i) { + runMapping(new MapVoidAction("setState") { + @Override + public void map() { + ((Frame) getSource()).setState(i); + } + }); + } + + /** + * Maps {@code Frame.setTitle(String)} through queue + */ + public void setTitle(final String string) { + runMapping(new MapVoidAction("setTitle") { + @Override + public void map() { + ((Frame) getSource()).setTitle(string); + } + }); + } + + //End of mapping // + //////////////////////////////////////////////////////// + /** + * A method to be used from subclasses. Uses timeouts and output passed as + * parameters during the waiting. + * + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @param index Ordinal component index. + * @param timeouts timeouts to be used during the waiting. + * @param output an output to be used during the waiting. + * @return Component instance or null if component was not found. + * @throws TimeoutExpiredException + */ + protected static Frame waitFrame(ComponentChooser chooser, int index, + Timeouts timeouts, TestOut output) { + try { + FrameWaiter waiter = new FrameWaiter(); + waiter.setTimeouts(timeouts); + waiter.setOutput(output); + return waiter.waitFrame(new FrameFinder(chooser), index); + } catch (InterruptedException e) { + output.printStackTrace(e); + return null; + } + } + + /** + * Checks component type. + */ + public static class FrameFinder extends Finder { + + /** + * Constructs FrameFinder. + * + * @param sf other searching criteria. + */ + public FrameFinder(ComponentChooser sf) { + super(Frame.class, sf); + } + + /** + * Constructs FrameFinder. + */ + public FrameFinder() { + super(Frame.class); + } + } + + /** + * Allows to find component by title. + */ + public static class FrameByTitleFinder implements ComponentChooser { + + String title; + StringComparator comparator; + + /** + * Constructs FrameByTitleFinder. + * + * @param t a text pattern + * @param comparator specifies string comparision algorithm. + */ + public FrameByTitleFinder(String t, StringComparator comparator) { + title = t; + this.comparator = comparator; + } + + /** + * Constructs FrameByTitleFinder. + * + * @param t a text pattern + */ + public FrameByTitleFinder(String t) { + this(t, Operator.getDefaultStringComparator()); + } + + @Override + public boolean checkComponent(Component comp) { + if (comp instanceof Frame) { + if (comp.isShowing() && ((Frame) comp).getTitle() != null) { + return comparator.equals(((Frame) comp).getTitle(), title); + } + } + return false; + } + + @Override + public String getDescription() { + return "Frame with title \"" + title + "\""; + } + + @Override + public String toString() { + return "FrameByTitleFinder{" + "title=" + title + ", comparator=" + comparator + '}'; + } + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JButtonOperator.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JButtonOperator.java new file mode 100644 index 00000000000..5880960b4c4 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JButtonOperator.java @@ -0,0 +1,339 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.operators; + +import java.awt.Container; +import java.util.Hashtable; + +import javax.swing.JButton; + +import org.netbeans.jemmy.ComponentChooser; +import org.netbeans.jemmy.TimeoutExpiredException; + +/** + * + *

Timeouts used:
+ * AbstractButtonOperator.PushButtonTimeout - time between button pressing and + * releasing
+ * ComponentOperator.WaitComponentTimeout - time to wait button displayed
+ * ComponentOperator.WaitComponentEnabledTimeout - time to wait button enabled + *
. + * + * @see org.netbeans.jemmy.Timeouts + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + * + */ +public class JButtonOperator extends AbstractButtonOperator { + + /** + * Identifier for a "default button" property. + * + * @see #getDump + */ + public static final String IS_DEFAULT_DPROP = "Default button"; + + /** + * Constructor. + * + * @param b a component + */ + public JButtonOperator(JButton b) { + super(b); + } + + /** + * Constructs a JButtonOperator object. + * + * @param cont container + * @param chooser a component chooser specifying searching criteria. + * @param index an index between appropriate ones. + */ + public JButtonOperator(ContainerOperator cont, ComponentChooser chooser, int index) { + this((JButton) cont. + waitSubComponent(new JButtonFinder(chooser), + index)); + copyEnvironment(cont); + } + + /** + * Constructs a JButtonOperator object. + * + * @param cont container + * @param chooser a component chooser specifying searching criteria. + */ + public JButtonOperator(ContainerOperator cont, ComponentChooser chooser) { + this(cont, chooser, 0); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont container + * @param text Button text. + * @param index Ordinal component index. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public JButtonOperator(ContainerOperator cont, String text, int index) { + this((JButton) waitComponent(cont, + new JButtonFinder(new AbstractButtonOperator.AbstractButtonByLabelFinder(text, + cont.getComparator())), + index)); + copyEnvironment(cont); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont container + * @param text Button text. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public JButtonOperator(ContainerOperator cont, String text) { + this(cont, text, 0); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont container + * @param index Ordinal component index. + * @throws TimeoutExpiredException + */ + public JButtonOperator(ContainerOperator cont, int index) { + this((JButton) waitComponent(cont, + new JButtonFinder(), + index)); + copyEnvironment(cont); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont container + * @throws TimeoutExpiredException + */ + public JButtonOperator(ContainerOperator cont) { + this(cont, 0); + } + + /** + * Searches JButton in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @param index Ordinal component index. + * @return JButton instance or null if component was not found. + */ + public static JButton findJButton(Container cont, ComponentChooser chooser, int index) { + return (JButton) findAbstractButton(cont, new JButtonFinder(chooser), index); + } + + /** + * Searches 0'th JButton in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @return JButton instance or null if component was not found. + */ + public static JButton findJButton(Container cont, ComponentChooser chooser) { + return findJButton(cont, chooser, 0); + } + + /** + * Searches JButton by text. + * + * @param cont Container to search component in. + * @param text Button text. If null, contents is not checked. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @param index Ordinal component index. + * @return JButton instance or null if component was not found. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + */ + public static JButton findJButton(Container cont, String text, boolean ce, boolean ccs, int index) { + return (findJButton(cont, + new JButtonFinder(new AbstractButtonOperator.AbstractButtonByLabelFinder(text, + new DefaultStringComparator(ce, ccs))), + index)); + } + + /** + * Searches JButton by text. + * + * @param cont Container to search component in. + * @param text Button text. If null, contents is not checked. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @return JButton instance or null if component was not found. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + */ + public static JButton findJButton(Container cont, String text, boolean ce, boolean ccs) { + return findJButton(cont, text, ce, ccs, 0); + } + + /** + * Waits JButton in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @param index Ordinal component index. + * @return JButton instance. + * @throws TimeoutExpiredException + */ + public static JButton waitJButton(Container cont, ComponentChooser chooser, int index) { + return (JButton) waitAbstractButton(cont, new JButtonFinder(chooser), index); + } + + /** + * Waits 0'th JButton in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @return JButton instance. + * @throws TimeoutExpiredException + */ + public static JButton waitJButton(Container cont, ComponentChooser chooser) { + return waitJButton(cont, chooser, 0); + } + + /** + * Waits JButton by text. + * + * @param cont Container to search component in. + * @param text Button text. If null, contents is not checked. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @param index Ordinal component index. + * @return JButton instance. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public static JButton waitJButton(Container cont, String text, boolean ce, boolean ccs, int index) { + return (waitJButton(cont, + new JButtonFinder(new AbstractButtonOperator.AbstractButtonByLabelFinder(text, + new DefaultStringComparator(ce, ccs))), + index)); + } + + /** + * Waits JButton by text. + * + * @param cont Container to search component in. + * @param text Button text. If null, contents is not checked. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @return JButton instance. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public static JButton waitJButton(Container cont, String text, boolean ce, boolean ccs) { + return waitJButton(cont, text, ce, ccs, 0); + } + + /** + * Returns information about component. + */ + @Override + public Hashtable getDump() { + Hashtable result = super.getDump(); + result.remove(AbstractButtonOperator.IS_SELECTED_DPROP); + result.put(IS_DEFAULT_DPROP, ((JButton) getSource()).isDefaultButton() ? "true" : "false"); + return result; + } + + //////////////////////////////////////////////////////// + //Mapping // + /** + * Maps {@code JButton.isDefaultButton()} through queue + */ + public boolean isDefaultButton() { + return (runMapping(new MapBooleanAction("isDefaultButton") { + @Override + public boolean map() { + return ((JButton) getSource()).isDefaultButton(); + } + })); + } + + /** + * Maps {@code JButton.isDefaultCapable()} through queue + */ + public boolean isDefaultCapable() { + return (runMapping(new MapBooleanAction("isDefaultCapable") { + @Override + public boolean map() { + return ((JButton) getSource()).isDefaultCapable(); + } + })); + } + + /** + * Maps {@code JButton.setDefaultCapable(boolean)} through queue + */ + public void setDefaultCapable(final boolean b) { + runMapping(new MapVoidAction("setDefaultCapable") { + @Override + public void map() { + ((JButton) getSource()).setDefaultCapable(b); + } + }); + } + + //End of mapping // + //////////////////////////////////////////////////////// + /** + * Prepares the button to click. + */ + protected void prepareToClick() { + makeComponentVisible(); + } + + /** + * Checks component type. + */ + public static class JButtonFinder extends Finder { + + /** + * Constructs JButtonFinder. + * + * @param sf other searching criteria. + */ + public JButtonFinder(ComponentChooser sf) { + super(JButton.class, sf); + } + + /** + * Constructs JButtonFinder. + */ + public JButtonFinder() { + super(JButton.class); + } + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JCheckBoxMenuItemOperator.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JCheckBoxMenuItemOperator.java new file mode 100644 index 00000000000..8331629e792 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JCheckBoxMenuItemOperator.java @@ -0,0 +1,236 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.operators; + +import java.awt.Component; + +import javax.swing.JCheckBoxMenuItem; + +import org.netbeans.jemmy.ComponentChooser; +import org.netbeans.jemmy.JemmyProperties; + +/** + * + *

Timeouts used:
+ * JMenuItemOperator.PushMenuTimeout - time between button pressing and + * releasing
+ * ComponentOperator.WaitComponentTimeout - time to wait button displayed
+ * ComponentOperator.WaitComponentEnabledTimeout - time to wait button enabled + *
. + * + * @see org.netbeans.jemmy.Timeouts + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + * + */ +public class JCheckBoxMenuItemOperator extends JMenuItemOperator { + + /** + * Constructor. + * + * @param item a component. + */ + public JCheckBoxMenuItemOperator(JCheckBoxMenuItem item) { + super(item); + setTimeouts(JemmyProperties.getProperties().getTimeouts()); + setOutput(JemmyProperties.getProperties().getOutput()); + } + + /** + * Constructs a JCheckBoxMenuItemOperator object. + * + * @param cont a container + * @param chooser a component chooser specifying searching criteria. + * @param index an index between appropriate ones. + */ + public JCheckBoxMenuItemOperator(ContainerOperator cont, ComponentChooser chooser, int index) { + this((JCheckBoxMenuItem) cont. + waitSubComponent(new JCheckBoxMenuItemFinder(chooser), + index)); + copyEnvironment(cont); + } + + /** + * Constructs a JCheckBoxMenuItemOperator object. + * + * @param cont a container + * @param chooser a component chooser specifying searching criteria. + */ + public JCheckBoxMenuItemOperator(ContainerOperator cont, ComponentChooser chooser) { + this(cont, chooser, 0); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont a container + * @param text Button text. + * @param index Ordinal component index. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + */ + public JCheckBoxMenuItemOperator(ContainerOperator cont, String text, int index) { + this((JCheckBoxMenuItem) waitComponent(cont, + new JCheckBoxMenuItemByLabelFinder(text, + cont.getComparator()), + index)); + setTimeouts(cont.getTimeouts()); + setOutput(cont.getOutput()); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont a container + * @param text Button text. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + */ + public JCheckBoxMenuItemOperator(ContainerOperator cont, String text) { + this(cont, text, 0); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont a container + * @param index Ordinal component index. + */ + public JCheckBoxMenuItemOperator(ContainerOperator cont, int index) { + this((JCheckBoxMenuItem) waitComponent(cont, + new JCheckBoxMenuItemFinder(), + index)); + copyEnvironment(cont); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont a container + */ + public JCheckBoxMenuItemOperator(ContainerOperator cont) { + this(cont, 0); + } + + //////////////////////////////////////////////////////// + //Mapping // + /** + * Maps {@code JCheckBoxMenuItem.getState()} through queue + */ + public boolean getState() { + return (runMapping(new MapBooleanAction("getState") { + @Override + public boolean map() { + return ((JCheckBoxMenuItem) getSource()).getState(); + } + })); + } + + /** + * Maps {@code JCheckBoxMenuItem.setState(boolean)} through queue + */ + public void setState(final boolean b) { + runMapping(new MapVoidAction("setState") { + @Override + public void map() { + ((JCheckBoxMenuItem) getSource()).setState(b); + } + }); + } + + //End of mapping // + //////////////////////////////////////////////////////// + /** + * Allows to find component by text. + */ + public static class JCheckBoxMenuItemByLabelFinder implements ComponentChooser { + + String label; + StringComparator comparator; + + /** + * Constructs JCheckBoxMenuItemByLabelFinder. + * + * @param lb a text pattern + * @param comparator specifies string comparision algorithm. + */ + public JCheckBoxMenuItemByLabelFinder(String lb, StringComparator comparator) { + label = lb; + this.comparator = comparator; + } + + /** + * Constructs JCheckBoxMenuItemByLabelFinder. + * + * @param lb a text pattern + */ + public JCheckBoxMenuItemByLabelFinder(String lb) { + this(lb, Operator.getDefaultStringComparator()); + } + + @Override + public boolean checkComponent(Component comp) { + if (comp instanceof JCheckBoxMenuItem) { + if (((JCheckBoxMenuItem) comp).getText() != null) { + return (comparator.equals(((JCheckBoxMenuItem) comp).getText(), + label)); + } + } + return false; + } + + @Override + public String getDescription() { + return "JCheckBoxMenuItem with text \"" + label + "\""; + } + + @Override + public String toString() { + return "JCheckBoxMenuItemByLabelFinder{" + "label=" + label + ", comparator=" + comparator + '}'; + } + } + + /** + * Checks component type. + */ + public static class JCheckBoxMenuItemFinder extends Finder { + + /** + * Constructs JCheckBoxMenuItemFinder. + * + * @param sf other searching criteria. + */ + public JCheckBoxMenuItemFinder(ComponentChooser sf) { + super(JCheckBoxMenuItem.class, sf); + } + + /** + * Constructs JCheckBoxMenuItemFinder. + */ + public JCheckBoxMenuItemFinder() { + super(JCheckBoxMenuItem.class); + } + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JCheckBoxOperator.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JCheckBoxOperator.java new file mode 100644 index 00000000000..a4a641f32b0 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JCheckBoxOperator.java @@ -0,0 +1,273 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.operators; + +import java.awt.Container; + +import javax.swing.JCheckBox; + +import org.netbeans.jemmy.ComponentChooser; +import org.netbeans.jemmy.TimeoutExpiredException; + +/** + * + *

Timeouts used:
+ * AbstractButtonOperator.PushButtonTimeout - time between button pressing and + * releasing
+ * ComponentOperator.WaitComponentTimeout - time to wait button displayed
+ * ComponentOperator.WaitComponentEnabledTimeout - time to wait button enabled + *
. + * + * @see org.netbeans.jemmy.Timeouts + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + * + */ +public class JCheckBoxOperator extends JToggleButtonOperator { + + /** + * Constructor. + * + * @param b a component + */ + public JCheckBoxOperator(JCheckBox b) { + super(b); + } + + /** + * Constructs a JCheckBoxOperator object. + * + * @param cont a container + * @param chooser a component chooser specifying searching criteria. + * @param index an index between appropriate ones. + */ + public JCheckBoxOperator(ContainerOperator cont, ComponentChooser chooser, int index) { + this((JCheckBox) cont. + waitSubComponent(new JCheckBoxFinder(chooser), + index)); + copyEnvironment(cont); + } + + /** + * Constructs a JCheckBoxOperator object. + * + * @param cont a container + * @param chooser a component chooser specifying searching criteria. + */ + public JCheckBoxOperator(ContainerOperator cont, ComponentChooser chooser) { + this(cont, chooser, 0); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont a container + * @param text Button text. + * @param index Ordinal component index. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public JCheckBoxOperator(ContainerOperator cont, String text, int index) { + this((JCheckBox) waitComponent(cont, + new JCheckBoxFinder(new AbstractButtonOperator.AbstractButtonByLabelFinder(text, + cont.getComparator())), + index)); + copyEnvironment(cont); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont a container + * @param text Button text. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public JCheckBoxOperator(ContainerOperator cont, String text) { + this(cont, text, 0); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont a container + * @param index Ordinal component index. + * @throws TimeoutExpiredException + */ + public JCheckBoxOperator(ContainerOperator cont, int index) { + this((JCheckBox) waitComponent(cont, + new JCheckBoxFinder(), + index)); + copyEnvironment(cont); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont a container + * @throws TimeoutExpiredException + */ + public JCheckBoxOperator(ContainerOperator cont) { + this(cont, 0); + } + + /** + * Searches JCheckBox in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @param index Ordinal component index. + * @return JCheckBox instance or null if component was not found. + */ + public static JCheckBox findJCheckBox(Container cont, ComponentChooser chooser, int index) { + return (JCheckBox) findJToggleButton(cont, new JCheckBoxFinder(chooser), index); + } + + /** + * Searches 0'th JCheckBox in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @return JCheckBox instance or null if component was not found. + */ + public static JCheckBox findJCheckBox(Container cont, ComponentChooser chooser) { + return findJCheckBox(cont, chooser, 0); + } + + /** + * Searches JCheckBox by text. + * + * @param cont Container to search component in. + * @param text Button text. If null, contents is not checked. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @param index Ordinal component index. + * @return JCheckBox instance or null if component was not found. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + */ + public static JCheckBox findJCheckBox(Container cont, String text, boolean ce, boolean ccs, int index) { + return (findJCheckBox(cont, + new JCheckBoxFinder(new AbstractButtonOperator.AbstractButtonByLabelFinder(text, + new DefaultStringComparator(ce, ccs))), + index)); + } + + /** + * Searches JCheckBox by text. + * + * @param cont Container to search component in. + * @param text Button text. If null, contents is not checked. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @return JCheckBox instance or null if component was not found. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + */ + public static JCheckBox findJCheckBox(Container cont, String text, boolean ce, boolean ccs) { + return findJCheckBox(cont, text, ce, ccs, 0); + } + + /** + * Waits JCheckBox in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @param index Ordinal component index. + * @return JCheckBox instance. + * @throws TimeoutExpiredException + */ + public static JCheckBox waitJCheckBox(Container cont, ComponentChooser chooser, int index) { + return (JCheckBox) waitJToggleButton(cont, new JCheckBoxFinder(chooser), index); + } + + /** + * Waits 0'th JCheckBox in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @return JCheckBox instance. + * @throws TimeoutExpiredException + */ + public static JCheckBox waitJCheckBox(Container cont, ComponentChooser chooser) { + return waitJCheckBox(cont, chooser, 0); + } + + /** + * Waits JCheckBox by text. + * + * @param cont Container to search component in. + * @param text Button text. If null, contents is not checked. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @param index Ordinal component index. + * @return JCheckBox instance. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public static JCheckBox waitJCheckBox(Container cont, String text, boolean ce, boolean ccs, int index) { + return (waitJCheckBox(cont, + new JCheckBoxFinder(new AbstractButtonOperator.AbstractButtonByLabelFinder(text, + new DefaultStringComparator(ce, ccs))), + index)); + } + + /** + * Waits JCheckBox by text. + * + * @param cont Container to search component in. + * @param text Button text. If null, contents is not checked. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @return JCheckBox instance. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public static JCheckBox waitJCheckBox(Container cont, String text, boolean ce, boolean ccs) { + return waitJCheckBox(cont, text, ce, ccs, 0); + } + + /** + * Checks component type. + */ + public static class JCheckBoxFinder extends Finder { + + /** + * Constructs JCheckBoxFinder. + * + * @param sf other searching criteria. + */ + public JCheckBoxFinder(ComponentChooser sf) { + super(JCheckBox.class, sf); + } + + /** + * Constructs JCheckBoxFinder. + */ + public JCheckBoxFinder() { + super(JCheckBox.class); + } + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JColorChooserOperator.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JColorChooserOperator.java new file mode 100644 index 00000000000..ac0fee85abf --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JColorChooserOperator.java @@ -0,0 +1,548 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.operators; + +import java.awt.Color; +import java.awt.Container; +import java.util.Hashtable; + +import javax.swing.JColorChooser; +import javax.swing.JComponent; +import javax.swing.JTabbedPane; +import javax.swing.colorchooser.AbstractColorChooserPanel; +import javax.swing.colorchooser.ColorSelectionModel; +import javax.swing.plaf.ColorChooserUI; + +import org.netbeans.jemmy.ComponentChooser; +import org.netbeans.jemmy.ComponentSearcher; +import org.netbeans.jemmy.JemmyProperties; +import org.netbeans.jemmy.Outputable; +import org.netbeans.jemmy.TestOut; + +/** + * + * Class provides methods to cover main JColorChooser component functionality. + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + * + */ +public class JColorChooserOperator extends JComponentOperator + implements Outputable { + + /** + * Identifier for a "color" property. + * + * @see #getDump + */ + public static final String COLOR_DPROP = "Color"; + + /** + * Identifier for a "selected page" property. + * + * @see #getDump + */ + public static final String SELECTED_PAGE_DPROP = "Selected page"; + + private static final String RGB_TITLE = "RGB"; + + private TestOut output; + private JTabbedPaneOperator tabbed; + private JTextFieldOperator red; + private JTextFieldOperator green; + private JTextFieldOperator blue; + + /** + * Constructor. + * + * @param comp a component + */ + public JColorChooserOperator(JColorChooser comp) { + super(comp); + setTimeouts(JemmyProperties.getProperties().getTimeouts()); + setOutput(JemmyProperties.getProperties().getOutput()); + tabbed = new JTabbedPaneOperator(this); + } + + /** + * Constructs a JColorChooserOperator object. + * + * @param cont a container + * @param chooser a component chooser specifying searching criteria. + * @param index an index between appropriate ones. + */ + public JColorChooserOperator(ContainerOperator cont, ComponentChooser chooser, int index) { + this((JColorChooser) cont. + waitSubComponent(new JColorChooserFinder(chooser), + index)); + copyEnvironment(cont); + } + + /** + * Constructs a JColorChooserOperator object. + * + * @param cont a container + * @param chooser a component chooser specifying searching criteria. + */ + public JColorChooserOperator(ContainerOperator cont, ComponentChooser chooser) { + this(cont, chooser, 0); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont Operator pointing a container to search component in. + * @param index Ordinal component index. + * + */ + public JColorChooserOperator(ContainerOperator cont, int index) { + this((JColorChooser) waitComponent(cont, + new JColorChooserFinder(), + index)); + copyEnvironment(cont); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont Operator pointing a container to search component in. + * + */ + public JColorChooserOperator(ContainerOperator cont) { + this(cont, 0); + } + + /** + * Searches JColorChooser in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @param index Ordinal component index. + * @return JColorChooser instance or null if component was not found. + */ + public static JColorChooser findJColorChooser(Container cont, ComponentChooser chooser, int index) { + return (JColorChooser) findComponent(cont, new JColorChooserFinder(chooser), index); + } + + /** + * Searches 0'th JColorChooser in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @return JColorChooser instance or null if component was not found. + */ + public static JColorChooser findJColorChooser(Container cont, ComponentChooser chooser) { + return findJColorChooser(cont, chooser, 0); + } + + /** + * Searches JColorChooser in container. + * + * @param cont Container to search component in. + * @param index Ordinal component index. + * @return JColorChooser instance or null if component was not found. + */ + public static JColorChooser findJColorChooser(Container cont, int index) { + return findJColorChooser(cont, ComponentSearcher.getTrueChooser(Integer.toString(index) + "'th JColorChooser instance"), index); + } + + /** + * Searches 0'th JColorChooser in container. + * + * @param cont Container to search component in. + * @return JColorChooser instance or null if component was not found. + */ + public static JColorChooser findJColorChooser(Container cont) { + return findJColorChooser(cont, 0); + } + + /** + * Waits JColorChooser in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @param index Ordinal component index. + * @return JColorChooser instance or null if component was not displayed. + * + */ + public static JColorChooser waitJColorChooser(Container cont, ComponentChooser chooser, int index) { + return (JColorChooser) waitComponent(cont, new JColorChooserFinder(chooser), index); + } + + /** + * Waits 0'th JColorChooser in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @return JColorChooser instance or null if component was not displayed. + * + */ + public static JColorChooser waitJColorChooser(Container cont, ComponentChooser chooser) { + return waitJColorChooser(cont, chooser, 0); + } + + /** + * Waits JColorChooser in container. + * + * @param cont Container to search component in. + * @param index Ordinal component index. + * @return JColorChooser instance or null if component was not displayed. + * + */ + public static JColorChooser waitJColorChooser(Container cont, int index) { + return waitJColorChooser(cont, ComponentSearcher.getTrueChooser(Integer.toString(index) + "'th JColorChooser instance"), index); + } + + /** + * Waits 0'th JColorChooser in container. + * + * @param cont Container to search component in. + * @return JColorChooser instance or null if component was not displayed. + * + */ + public static JColorChooser waitJColorChooser(Container cont) { + return waitJColorChooser(cont, 0); + } + + @Override + public void setOutput(TestOut out) { + output = out; + super.setOutput(output.createErrorOutput()); + } + + @Override + public TestOut getOutput() { + return output; + } + + /** + * Switches tab to "RGB" page. + */ + public void switchToRGB() { + if (!tabbed.getTitleAt(tabbed.getSelectedIndex()). + equals(RGB_TITLE)) { + tabbed.selectPage(RGB_TITLE); + } + blue = new JTextFieldOperator(this, 2); + green = new JTextFieldOperator(this, 1); + red = new JTextFieldOperator(this, 0); + } + + /** + * Enters red color component value. Switches to "RGB" page first. + * + * @param value red color component + * @see #switchToRGB() + * @see #enterColor(int, int, int) + * @see #enterColor(java.awt.Color) + * @see #enterColor(int) + */ + public void enterRed(int value) { + switchToRGB(); + red.setText(Integer.toString(value)); + } + + /** + * Enters green color component value. Switches to "RGB" page first. + * + * @param value green color component + * @see #switchToRGB() + * @see #enterColor(int, int, int) + * @see #enterColor(java.awt.Color) + * @see #enterColor(int) + */ + public void enterGreen(int value) { + switchToRGB(); + green.setText(Integer.toString(value)); + } + + /** + * Enters blue color component value. Switches to "RGB" page first. + * + * @param value blue color component + * @see #switchToRGB() + * @see #enterColor(int, int, int) + * @see #enterColor(java.awt.Color) + * @see #enterColor(int) + */ + public void enterBlue(int value) { + switchToRGB(); + blue.setText(Integer.toString(value)); + } + + /** + * Enters all color components values. Switches to "RGB" page first. + * + * @param red red color component + * @param green green color component + * @param blue blue color component + * @see #switchToRGB() + * @see #enterColor(java.awt.Color) + * @see #enterColor(int) + */ + public void enterColor(int red, int green, int blue) { + switchToRGB(); + enterRed(red); + enterGreen(green); + enterBlue(blue); + } + + /** + * Enters color. Switches to "RGB" page first. + * + * @param color a color + * @see #switchToRGB() + * @see #enterColor(int, int, int) + * @see #enterColor(int) + */ + public void enterColor(Color color) { + enterColor(color.getRed(), color.getGreen(), color.getBlue()); + } + + /** + * Enters color. Switches to "RGB" page first. + * + * @param color a color + * @see #switchToRGB() + * @see #enterColor(int, int, int) + * @see #enterColor(java.awt.Color) + */ + public void enterColor(int color) { + enterColor(new Color(color)); + } + + /** + * Returns information about component. + */ + @Override + public Hashtable getDump() { + Hashtable result = super.getDump(); + result.put(COLOR_DPROP, ((JColorChooser) getSource()).getColor().toString()); + JTabbedPane tb = (JTabbedPane) tabbed.getSource(); + result.put(SELECTED_PAGE_DPROP, tb.getTitleAt(tb.getSelectedIndex())); + return result; + } + + //////////////////////////////////////////////////////// + //Mapping // + /** + * Maps + * {@code JColorChooser.addChooserPanel(AbstractColorChooserPanel)} + * through queue + */ + public void addChooserPanel(final AbstractColorChooserPanel abstractColorChooserPanel) { + runMapping(new MapVoidAction("addChooserPanel") { + @Override + public void map() { + ((JColorChooser) getSource()).addChooserPanel(abstractColorChooserPanel); + } + }); + } + + /** + * Maps {@code JColorChooser.getChooserPanels()} through queue + */ + public AbstractColorChooserPanel[] getChooserPanels() { + return ((AbstractColorChooserPanel[]) runMapping(new MapAction("getChooserPanels") { + @Override + public Object map() { + return ((JColorChooser) getSource()).getChooserPanels(); + } + })); + } + + /** + * Maps {@code JColorChooser.getColor()} through queue + */ + public Color getColor() { + return (runMapping(new MapAction("getColor") { + @Override + public Color map() { + return ((JColorChooser) getSource()).getColor(); + } + })); + } + + /** + * Maps {@code JColorChooser.getPreviewPanel()} through queue + */ + public JComponent getPreviewPanel() { + return (runMapping(new MapAction("getPreviewPanel") { + @Override + public JComponent map() { + return ((JColorChooser) getSource()).getPreviewPanel(); + } + })); + } + + /** + * Maps {@code JColorChooser.getSelectionModel()} through queue + */ + public ColorSelectionModel getSelectionModel() { + return (runMapping(new MapAction("getSelectionModel") { + @Override + public ColorSelectionModel map() { + return ((JColorChooser) getSource()).getSelectionModel(); + } + })); + } + + /** + * Maps {@code JColorChooser.getUI()} through queue + */ + public ColorChooserUI getUI() { + return (runMapping(new MapAction("getUI") { + @Override + public ColorChooserUI map() { + return ((JColorChooser) getSource()).getUI(); + } + })); + } + + /** + * Maps + * {@code JColorChooser.removeChooserPanel(AbstractColorChooserPanel)} + * through queue + */ + public AbstractColorChooserPanel removeChooserPanel(final AbstractColorChooserPanel abstractColorChooserPanel) { + return (runMapping(new MapAction("removeChooserPanel") { + @Override + public AbstractColorChooserPanel map() { + return ((JColorChooser) getSource()).removeChooserPanel(abstractColorChooserPanel); + } + })); + } + + /** + * Maps + * {@code JColorChooser.setChooserPanels(AbstractColorChooserPanel[])} + * through queue + */ + public void setChooserPanels(final AbstractColorChooserPanel[] abstractColorChooserPanel) { + runMapping(new MapVoidAction("setChooserPanels") { + @Override + public void map() { + ((JColorChooser) getSource()).setChooserPanels(abstractColorChooserPanel); + } + }); + } + + /** + * Maps {@code JColorChooser.setColor(int)} through queue + */ + public void setColor(final int i) { + runMapping(new MapVoidAction("setColor") { + @Override + public void map() { + ((JColorChooser) getSource()).setColor(i); + } + }); + } + + /** + * Maps {@code JColorChooser.setColor(int, int, int)} through queue + */ + public void setColor(final int i, final int i1, final int i2) { + runMapping(new MapVoidAction("setColor") { + @Override + public void map() { + ((JColorChooser) getSource()).setColor(i, i1, i2); + } + }); + } + + /** + * Maps {@code JColorChooser.setColor(Color)} through queue + */ + public void setColor(final Color color) { + runMapping(new MapVoidAction("setColor") { + @Override + public void map() { + ((JColorChooser) getSource()).setColor(color); + } + }); + } + + /** + * Maps {@code JColorChooser.setPreviewPanel(JComponent)} through queue + */ + public void setPreviewPanel(final JComponent jComponent) { + runMapping(new MapVoidAction("setPreviewPanel") { + @Override + public void map() { + ((JColorChooser) getSource()).setPreviewPanel(jComponent); + } + }); + } + + /** + * Maps {@code JColorChooser.setSelectionModel(ColorSelectionModel)} + * through queue + */ + public void setSelectionModel(final ColorSelectionModel colorSelectionModel) { + runMapping(new MapVoidAction("setSelectionModel") { + @Override + public void map() { + ((JColorChooser) getSource()).setSelectionModel(colorSelectionModel); + } + }); + } + + /** + * Maps {@code JColorChooser.setUI(ColorChooserUI)} through queue + */ + public void setUI(final ColorChooserUI colorChooserUI) { + runMapping(new MapVoidAction("setUI") { + @Override + public void map() { + ((JColorChooser) getSource()).setUI(colorChooserUI); + } + }); + } + + //End of mapping // + //////////////////////////////////////////////////////// + /** + * Checks component type. + */ + public static class JColorChooserFinder extends Finder { + + /** + * Constructs JColorChooserFinder. + * + * @param sf other searching criteria. + */ + public JColorChooserFinder(ComponentChooser sf) { + super(JColorChooser.class, sf); + } + + /** + * Constructs JColorChooserFinder. + */ + public JColorChooserFinder() { + super(JColorChooser.class); + } + } + +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JComboBoxOperator.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JComboBoxOperator.java new file mode 100644 index 00000000000..91a4eec633f --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JComboBoxOperator.java @@ -0,0 +1,1452 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.operators; + +import java.awt.Component; +import java.awt.Container; +import java.awt.Window; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.ItemListener; +import java.awt.event.KeyEvent; +import java.util.Hashtable; + +import javax.swing.ComboBoxEditor; +import javax.swing.ComboBoxModel; +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JList; +import javax.swing.JTextField; +import javax.swing.ListCellRenderer; +import javax.swing.JComboBox.KeySelectionManager; +import javax.swing.event.ListDataEvent; +import javax.swing.plaf.ComboBoxUI; +import javax.swing.plaf.basic.ComboPopup; + +import org.netbeans.jemmy.ComponentChooser; +import org.netbeans.jemmy.ComponentSearcher; +import org.netbeans.jemmy.JemmyException; +import org.netbeans.jemmy.Outputable; +import org.netbeans.jemmy.TestOut; +import org.netbeans.jemmy.TimeoutExpiredException; +import org.netbeans.jemmy.Timeoutable; +import org.netbeans.jemmy.Timeouts; +import org.netbeans.jemmy.Waiter; +import org.netbeans.jemmy.WindowWaiter; +import org.netbeans.jemmy.drivers.DriverManager; +import org.netbeans.jemmy.drivers.ListDriver; +import org.netbeans.jemmy.util.EmptyVisualizer; + +/** + *

Timeouts used:
+ * JComboBoxOperator.BeforeSelectingTimeout - time to sleep after list opened + * and before item selected
+ * JComboBoxOperator.WaitListTimeout - time to wait list opened
+ * ComponentOperator.WaitComponentTimeout - time to wait component displayed + *
+ * ComponentOperator.WaitComponentEnabledTimeout - time to wait component + * enabled
+ * ComponentOperator.WaitStateTimeout - time to wait for item to be selected + *
+ * AbstractButtonOperator.PushButtonTimeout - time between combo button pressing + * and releasing
+ * ComponentOperator.MouseClickTimeout - time between mouse pressing and + * releasing during item selecting
+ * JTextComponentOperator.PushKeyTimeout - time between key pressing and + * releasing during text typing
+ * JTextComponentOperator.BetweenKeysTimeout - time to sleep between two chars + * typing
+ * JTextComponentOperator.ChangeCaretPositionTimeout - maximum time to chenge + * caret position
+ * JTextComponentOperator.TypeTextTimeout - maximum time to type text
. + * + * @see Timeouts + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + * + */ +public class JComboBoxOperator extends JComponentOperator + implements Timeoutable, Outputable { + + /** + * Identifier for a "text" property. + * + * @see #getDump + */ + public static final String TEXT_DPROP = "Text"; + + /** + * Identifier for a "item" property values. + * + * @see #getDump + */ + public static final String ITEM_PREFIX_DPROP = "Item"; + + private final static long BEFORE_SELECTING_TIMEOUT = 0; + private final static long WAIT_LIST_TIMEOUT = 60000; + + private TestOut output; + private Timeouts timeouts; + + private JButtonOperator button; + private JTextFieldOperator text; + + ListDriver driver; + + /** + * Constructs a JComboBoxOperator object. + * + * @param b a component + */ + public JComboBoxOperator(JComboBox b) { + super(b); + driver = DriverManager.getListDriver(getClass()); + } + + /** + * Constructs a JComboBoxOperator object. + * + * @param cont a container + * @param chooser a component chooser specifying searching criteria. + * @param index an index between appropriate ones. + */ + public JComboBoxOperator(ContainerOperator cont, ComponentChooser chooser, int index) { + this((JComboBox) cont. + waitSubComponent(new JComboBoxFinder(chooser), + index)); + copyEnvironment(cont); + } + + /** + * Constructs a JComboBoxOperator object. + * + * @param cont a container + * @param chooser a component chooser specifying searching criteria. + */ + public JComboBoxOperator(ContainerOperator cont, ComponentChooser chooser) { + this(cont, chooser, 0); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont a container + * @param text Text of item which is currently selected. + * @param index Ordinal component index. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public JComboBoxOperator(ContainerOperator cont, String text, int index) { + this((JComboBox) waitComponent(cont, + new JComboBoxByItemFinder(text, -1, + cont.getComparator()), + index)); + copyEnvironment(cont); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont a container + * @param text Text of item which is currently selected. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public JComboBoxOperator(ContainerOperator cont, String text) { + this(cont, text, 0); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont a container + * @param index Ordinal component index. + * @throws TimeoutExpiredException + */ + public JComboBoxOperator(ContainerOperator cont, int index) { + this((JComboBox) waitComponent(cont, + new JComboBoxFinder(), + index)); + copyEnvironment(cont); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont a container + * @throws TimeoutExpiredException + */ + public JComboBoxOperator(ContainerOperator cont) { + this(cont, 0); + } + + /** + * Searches JComboBox in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @param index Ordinal component index. + * @return JComboBox instance or null if component was not found. + */ + public static JComboBox findJComboBox(Container cont, ComponentChooser chooser, int index) { + return (JComboBox) findComponent(cont, new JComboBoxFinder(chooser), index); + } + + /** + * Searches 0'th JComboBox in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @return JComboBox instance or null if component was not found. + */ + public static JComboBox findJComboBox(Container cont, ComponentChooser chooser) { + return findJComboBox(cont, chooser, 0); + } + + /** + * Searches JComboBox by item. + * + * @param cont Container to search component in. + * @param text Item text. If null, contents is not checked. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @param itemIndex Index of item to compare text. If -1, selected item is + * checked. + * @param index Ordinal component index. + * @return JComboBox instance or null if component was not found. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + */ + public static JComboBox findJComboBox(Container cont, String text, boolean ce, boolean ccs, int itemIndex, int index) { + return (findJComboBox(cont, + new JComboBoxByItemFinder(text, + itemIndex, + new DefaultStringComparator(ce, ccs)), + index)); + } + + /** + * Searches JComboBox by item. + * + * @param cont Container to search component in. + * @param text Item text. If null, contents is not checked. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @param itemIndex Index of item to compare text. If -1, selected item is + * checked. + * @return JComboBox instance or null if component was not found. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + */ + public static JComboBox findJComboBox(Container cont, String text, boolean ce, boolean ccs, int itemIndex) { + return findJComboBox(cont, text, ce, ccs, itemIndex, 0); + } + + /** + * Waits JComboBox in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @param index Ordinal component index. + * @return JComboBox instance or null if component was not found. + * @throws TimeoutExpiredException + */ + public static JComboBox waitJComboBox(Container cont, ComponentChooser chooser, int index) { + return (JComboBox) waitComponent(cont, new JComboBoxFinder(chooser), index); + } + + /** + * Waits 0'th JComboBox in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @return JComboBox instance or null if component was not found. + * @throws TimeoutExpiredException + */ + public static JComboBox waitJComboBox(Container cont, ComponentChooser chooser) { + return waitJComboBox(cont, chooser, 0); + } + + /** + * Waits JComboBox by item. + * + * @param cont Container to search component in. + * @param text Item text. If null, contents is not checked. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @param itemIndex Index of item to compare text. If -1, selected item is + * checked. + * @param index Ordinal component index. + * @return JComboBox instance or null if component was not found. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public static JComboBox waitJComboBox(Container cont, String text, boolean ce, boolean ccs, int itemIndex, int index) { + return (waitJComboBox(cont, + new JComboBoxByItemFinder(text, + itemIndex, + new DefaultStringComparator(ce, ccs)), + index)); + } + + /** + * Waits JComboBox by item. + * + * @param cont Container to search component in. + * @param text Item text. If null, contents is not checked. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @param itemIndex Index of item to compare text. If -1, selected item is + * checked. + * @return JComboBox instance or null if component was not found. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public static JComboBox waitJComboBox(Container cont, String text, boolean ce, boolean ccs, int itemIndex) { + return waitJComboBox(cont, text, ce, ccs, itemIndex, 0); + } + + static { + Timeouts.initDefault("JComboBoxOperator.BeforeSelectingTimeout", BEFORE_SELECTING_TIMEOUT); + Timeouts.initDefault("JComboBoxOperator.WaitListTimeout", WAIT_LIST_TIMEOUT); + } + + @Override + public void setTimeouts(Timeouts timeouts) { + super.setTimeouts(timeouts); + this.timeouts = timeouts; + } + + @Override + public Timeouts getTimeouts() { + return timeouts; + } + + @Override + public void setOutput(TestOut output) { + super.setOutput(output); + this.output = output; + } + + @Override + public TestOut getOutput() { + return output; + } + + @Override + public void copyEnvironment(Operator anotherOperator) { + super.copyEnvironment(anotherOperator); + driver + = (ListDriver) DriverManager. + getDriver(DriverManager.LIST_DRIVER_ID, + getClass(), + anotherOperator.getProperties()); + } + + /** + * Searches JButton inside component. + * + * @return JButton which is used to expand this JComboBox. + */ + public JButton findJButton() { + return ((JButton) waitSubComponent(new ComponentChooser() { + @Override + public boolean checkComponent(Component comp) { + return comp instanceof JButton; + } + + @Override + public String getDescription() { + return "Button for combobox popup menu opening"; + } + + @Override + public String toString() { + return "JComboBoxOperator.findJButton.ComponentChooser{description = " + getDescription() + '}'; + } + })); + } + + /** + * Searches JTextField inside component. + * + * @return JTextField if JComboBox is editable, null otherwise. + */ + public JTextField findJTextField() { + return ((JTextField) waitSubComponent(new ComponentChooser() { + @Override + public boolean checkComponent(Component comp) { + return comp instanceof JTextField; + } + + @Override + public String getDescription() { + return "ComboBox's text field"; + } + + @Override + public String toString() { + return "JComboBoxOperator.findJTextField.ComponentChooser{description = " + getDescription() + '}'; + } + })); + } + + /** + * Creates an operator for button returned by {@code findJButton()} + * method. + * + * @return new JButtonOperator instance. + */ + public JButtonOperator getButton() { + if (button == null) { + button = new JButtonOperator(findJButton()); + button.copyEnvironment(this); + button.setOutput(getOutput().createErrorOutput()); + } + return button; + } + + /** + * Creates an operator for button returned by {@code findJTextField()} + * method. + * + * @return new JTextField instance. + */ + public JTextFieldOperator getTextField() { + if (((JComboBox) getSource()).isEditable()) { + text = new JTextFieldOperator(findJTextField()); + text.copyEnvironment(this); + text.setOutput(getOutput().createErrorOutput()); + } + return text; + } + + /** + * Waits combobox's list to be displayed. + * + * @return JList object if it was displayed in + * JComboBoxOperator.WaitListTimeout milliseconds, null otherwise. + * @throws TimeoutExpiredException + */ + public JList waitList() { + ListWater pw = new ListWater(); + pw.setOutput(output.createErrorOutput()); + pw.setTimeoutsToCloneOf(timeouts, "JComboBoxOperator.WaitListTimeout"); + try { + return (JList) pw.waitAction(null); + } catch (InterruptedException e) { + output.printStackTrace(e); + } + return null; + } + + /** + * Push combobox's button to expand or collapse combobox. + * + * @throws TimeoutExpiredException + */ + public void pushComboButton() { + makeComponentVisible(); + getButton().push(); + } + + /** + * Finds an item between list items. + * + * @param item a text pattern. + * @param comparator a searching criteria. + * @return an item index. + */ + public int findItemIndex(String item, StringComparator comparator) { + ComboBoxModel model = getModel(); + for (int i = 0; i < model.getSize(); i++) { + if (comparator.equals(model.getElementAt(i).toString(), item)) { + return i; + } + } + return -1; + } + + /** + * Waits for an item available between list items. + * + * @param item a text pattern. + * @param comparator a searching criteria. + * @return an item index or throws TimeoutExpiredException if item not + * found. + */ + public int waitItem(final String item, final StringComparator comparator) { + getOutput().printLine("Wait item \"" + item + "\" available in combo box \n : " + + toStringSource()); + getOutput().printGolden("Wait item \"" + item + "\" available in combo box."); + waitState(new ComponentChooser() { + @Override + public boolean checkComponent(Component comp) { + return findItemIndex(item, comparator) > -1; + } + + @Override + public String getDescription() { + return "Item \"" + item + "\" available in combo box."; + } + + @Override + public String toString() { + return "JComboBoxOperator.waitItem.ComponentChooser{description = " + getDescription() + '}'; + } + }); + return findItemIndex(item, comparator); + } + + /** + * Waits for an item of given index available between list items. + * + * @param itemIndex index of desired item + * @return an item index or throws TimeoutExpiredException if item not + * found. + */ + public int waitItem(final int itemIndex) { + getOutput().printLine("Wait item of index \"" + itemIndex + "\" available in combo box \n : " + + toStringSource()); + getOutput().printGolden("Wait item of index \"" + itemIndex + "\" available in combo box."); + waitState(new ComponentChooser() { + @Override + public boolean checkComponent(Component comp) { + // given itemIndex is within size of combo box + return getModel().getSize() > itemIndex; + } + + @Override + public String getDescription() { + return "Item \"" + itemIndex + "\" available in combo box."; + } + + @Override + public String toString() { + return "JComboBoxOperator.waitItem.ComponentChooser{description = " + getDescription() + '}'; + } + }); + return itemIndex; + } + + /** + * Selects an item by text. + * + * @param item a text pattern. + * @param comparator a searching criteria. + */ + public void selectItem(String item, StringComparator comparator) { + output.printLine("Select \"" + item + "\" item in combobox\n : " + + toStringSource()); + output.printGolden("Select \"" + item + "\" item in combobox"); + selectItem(waitItem(item, comparator)); + } + + /** + * Selects combobox item. + * + * @param item Item text. + * @param ce Compare exactly. + * @param cc Compare case sensitivelly. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + * @deprecated Use selectItem(String) or selectItem(String, + * StringComparator) + */ + @Deprecated + public void selectItem(String item, boolean ce, boolean cc) { + selectItem(item, new DefaultStringComparator(ce, cc)); + } + + /** + * Selects combobox item. Uses StringComparator assigned to this object. + * + * @param item Item text. + * @throws TimeoutExpiredException + */ + public void selectItem(String item) { + selectItem(item, getComparator()); + } + + /** + * Selects combobox item. If verification mode is on, checks that right item + * has been selected. + * + * @param index Item index. + * @throws TimeoutExpiredException + */ + public void selectItem(int index) { + output.printLine("Select " + Integer.toString(index) + "\'th item in combobox\n : " + + toStringSource()); + output.printGolden("Select " + Integer.toString(index) + "\'th item in combobox"); + try { + waitComponentEnabled(); + } catch (InterruptedException e) { + throw new JemmyException("Interrupted", e); + } + + driver.selectItem(this, waitItem(index)); + + if (getVerification()) { + waitItemSelected(index); + } + } + + /** + * Types text in the editable combobox. If combobox has no focus, does + * simple mouse click on it first. + * + * @param text text to type. + * @throws TimeoutExpiredException + */ + public void typeText(String text) { + makeComponentVisible(); + JTextFieldOperator tfo = getTextField(); + tfo.copyEnvironment(this); + tfo.setVisualizer(new EmptyVisualizer()); + tfo.typeText(text); + } + + /** + * Clears text in the editable combobox using left-arrow and delete keys. If + * combobox has no focus, does simple mouse click on it first. + * + * @throws TimeoutExpiredException + */ + public void clearText() { + makeComponentVisible(); + JTextFieldOperator tfo = getTextField(); + tfo.copyEnvironment(this); + tfo.setVisualizer(new EmptyVisualizer()); + tfo.clearText(); + } + + /** + * Requests a focus, clears text, types new one and pushes Enter. + * + * @param text New text value. Shouln't include final '\n'. + * @throws TimeoutExpiredException + */ + public void enterText(String text) { + makeComponentVisible(); + JTextFieldOperator tfo = getTextField(); + tfo.copyEnvironment(this); + tfo.setVisualizer(new EmptyVisualizer()); + tfo.enterText(text); + } + + /** + * Waits for item to be selected. + * + * @param index Item index. + */ + public void waitItemSelected(final int index) { + getOutput().printLine("Wait " + Integer.toString(index) + + "'th item to be selected in component \n : " + + toStringSource()); + getOutput().printGolden("Wait " + Integer.toString(index) + + "'th item to be selected"); + waitState(new ComponentChooser() { + @Override + public boolean checkComponent(Component comp) { + return getSelectedIndex() == index; + } + + @Override + public String getDescription() { + return "Has " + Integer.toString(index) + "'th item selected"; + } + + @Override + public String toString() { + return "JComboBoxOperator.waitItemSelected.ComponentChooser{description = " + getDescription() + '}'; + } + }); + } + + /** + * Waits for item to be selected. Uses getComparator() comparator. + * + * @param item wait an item to be selected. + */ + public void waitItemSelected(final String item) { + getOutput().printLine("Wait \"" + item + + "\" item to be selected in component \n : " + + toStringSource()); + getOutput().printGolden("WaitWait \"" + item + + "\" item to be selected"); + waitState(new JComboBoxByItemFinder(item, -1, getComparator())); + + } + + /** + * Returns information about component. + */ + @Override + public Hashtable getDump() { + Hashtable result = super.getDump(); + JComboBox jComboBox = (JComboBox) getSource(); + Object selectedItem = jComboBox.getSelectedItem(); + if (selectedItem != null) { + result.put(TEXT_DPROP, selectedItem.toString()); + } + int itemCount = jComboBox.getItemCount(); + String[] items = new String[itemCount]; + for (int i = 0; i < itemCount; i++) { + if (jComboBox.getItemAt(i) != null) { + items[i] = jComboBox.getItemAt(i).toString(); + } + } + addToDump(result, ITEM_PREFIX_DPROP, items); + return result; + } + + //////////////////////////////////////////////////////// + //Mapping // + /** + * Maps {@code JComboBox.actionPerformed(ActionEvent)} through queue + */ + public void actionPerformed(final ActionEvent actionEvent) { + runMapping(new MapVoidAction("actionPerformed") { + @Override + public void map() { + ((JComboBox) getSource()).actionPerformed(actionEvent); + } + }); + } + + /** + * Maps {@code JComboBox.addActionListener(ActionListener)} through queue + */ + public void addActionListener(final ActionListener actionListener) { + runMapping(new MapVoidAction("addActionListener") { + @Override + public void map() { + ((JComboBox) getSource()).addActionListener(actionListener); + } + }); + } + + /** + * Maps {@code JComboBox.addItem(Object)} through queue + */ + @SuppressWarnings(value = "unchecked") + public void addItem(final Object object) { + runMapping(new MapVoidAction("addItem") { + @Override + public void map() { + ((JComboBox) getSource()).addItem(object); + } + }); + } + + /** + * Maps {@code JComboBox.addItemListener(ItemListener)} through queue + */ + public void addItemListener(final ItemListener itemListener) { + runMapping(new MapVoidAction("addItemListener") { + @Override + public void map() { + ((JComboBox) getSource()).addItemListener(itemListener); + } + }); + } + + /** + * Maps {@code JComboBox.configureEditor(ComboBoxEditor, Object)} + * through queue + */ + public void configureEditor(final ComboBoxEditor comboBoxEditor, final Object object) { + runMapping(new MapVoidAction("configureEditor") { + @Override + public void map() { + ((JComboBox) getSource()).configureEditor(comboBoxEditor, object); + } + }); + } + + /** + * Maps {@code JComboBox.contentsChanged(ListDataEvent)} through queue + */ + public void contentsChanged(final ListDataEvent listDataEvent) { + runMapping(new MapVoidAction("contentsChanged") { + @Override + public void map() { + ((JComboBox) getSource()).contentsChanged(listDataEvent); + } + }); + } + + /** + * Maps {@code JComboBox.getActionCommand()} through queue + */ + public String getActionCommand() { + return (runMapping(new MapAction("getActionCommand") { + @Override + public String map() { + return ((JComboBox) getSource()).getActionCommand(); + } + })); + } + + /** + * Maps {@code JComboBox.getEditor()} through queue + */ + public ComboBoxEditor getEditor() { + return (runMapping(new MapAction("getEditor") { + @Override + public ComboBoxEditor map() { + return ((JComboBox) getSource()).getEditor(); + } + })); + } + + /** + * Maps {@code JComboBox.getItemAt(int)} through queue + */ + public Object getItemAt(final int i) { + return (runMapping(new MapAction("getItemAt") { + @Override + public Object map() { + return ((JComboBox) getSource()).getItemAt(i); + } + })); + } + + /** + * Maps {@code JComboBox.getItemCount()} through queue + */ + public int getItemCount() { + return (runMapping(new MapIntegerAction("getItemCount") { + @Override + public int map() { + return ((JComboBox) getSource()).getItemCount(); + } + })); + } + + /** + * Maps {@code JComboBox.getKeySelectionManager()} through queue + */ + public KeySelectionManager getKeySelectionManager() { + return (runMapping(new MapAction("getKeySelectionManager") { + @Override + public KeySelectionManager map() { + return ((JComboBox) getSource()).getKeySelectionManager(); + } + })); + } + + /** + * Maps {@code JComboBox.getMaximumRowCount()} through queue + */ + public int getMaximumRowCount() { + return (runMapping(new MapIntegerAction("getMaximumRowCount") { + @Override + public int map() { + return ((JComboBox) getSource()).getMaximumRowCount(); + } + })); + } + + /** + * Maps {@code JComboBox.getModel()} through queue + */ + public ComboBoxModel getModel() { + return (runMapping(new MapAction>("getModel") { + @Override + public ComboBoxModel map() { + return ((JComboBox) getSource()).getModel(); + } + })); + } + + /** + * Maps {@code JComboBox.getRenderer()} through queue + */ + public ListCellRenderer getRenderer() { + return (runMapping(new MapAction>("getRenderer") { + @Override + public ListCellRenderer map() { + return ((JComboBox) getSource()).getRenderer(); + } + })); + } + + /** + * Maps {@code JComboBox.getSelectedIndex()} through queue + */ + public int getSelectedIndex() { + return (runMapping(new MapIntegerAction("getSelectedIndex") { + @Override + public int map() { + return ((JComboBox) getSource()).getSelectedIndex(); + } + })); + } + + /** + * Maps {@code JComboBox.getSelectedItem()} through queue + */ + public Object getSelectedItem() { + return (runMapping(new MapAction("getSelectedItem") { + @Override + public Object map() { + return ((JComboBox) getSource()).getSelectedItem(); + } + })); + } + + /** + * Maps {@code JComboBox.getSelectedObjects()} through queue + */ + public Object[] getSelectedObjects() { + return ((Object[]) runMapping(new MapAction("getSelectedObjects") { + @Override + public Object map() { + return ((JComboBox) getSource()).getSelectedObjects(); + } + })); + } + + /** + * Maps {@code JComboBox.getUI()} through queue + */ + public ComboBoxUI getUI() { + return (runMapping(new MapAction("getUI") { + @Override + public ComboBoxUI map() { + return ((JComboBox) getSource()).getUI(); + } + })); + } + + /** + * Maps {@code JComboBox.hidePopup()} through queue + */ + public void hidePopup() { + runMapping(new MapVoidAction("hidePopup") { + @Override + public void map() { + ((JComboBox) getSource()).hidePopup(); + } + }); + } + + /** + * Maps {@code JComboBox.insertItemAt(Object, int)} through queue + */ + @SuppressWarnings(value = "unchecked") + public void insertItemAt(final Object object, final int i) { + runMapping(new MapVoidAction("insertItemAt") { + @Override + public void map() { + ((JComboBox) getSource()).insertItemAt(object, i); + } + }); + } + + /** + * Maps {@code JComboBox.intervalAdded(ListDataEvent)} through queue + */ + public void intervalAdded(final ListDataEvent listDataEvent) { + runMapping(new MapVoidAction("intervalAdded") { + @Override + public void map() { + ((JComboBox) getSource()).intervalAdded(listDataEvent); + } + }); + } + + /** + * Maps {@code JComboBox.intervalRemoved(ListDataEvent)} through queue + */ + public void intervalRemoved(final ListDataEvent listDataEvent) { + runMapping(new MapVoidAction("intervalRemoved") { + @Override + public void map() { + ((JComboBox) getSource()).intervalRemoved(listDataEvent); + } + }); + } + + /** + * Maps {@code JComboBox.isEditable()} through queue + */ + public boolean isEditable() { + return (runMapping(new MapBooleanAction("isEditable") { + @Override + public boolean map() { + return ((JComboBox) getSource()).isEditable(); + } + })); + } + + /** + * Maps {@code JComboBox.isLightWeightPopupEnabled()} through queue + */ + public boolean isLightWeightPopupEnabled() { + return (runMapping(new MapBooleanAction("isLightWeightPopupEnabled") { + @Override + public boolean map() { + return ((JComboBox) getSource()).isLightWeightPopupEnabled(); + } + })); + } + + /** + * Maps {@code JComboBox.isPopupVisible()} through queue + */ + public boolean isPopupVisible() { + return (runMapping(new MapBooleanAction("isPopupVisible") { + @Override + public boolean map() { + return ((JComboBox) getSource()).isPopupVisible(); + } + })); + } + + /** + * Maps {@code JComboBox.processKeyEvent(KeyEvent)} through queue + */ + public void processKeyEvent(final KeyEvent keyEvent) { + runMapping(new MapVoidAction("processKeyEvent") { + @Override + public void map() { + ((JComboBox) getSource()).processKeyEvent(keyEvent); + } + }); + } + + /** + * Maps {@code JComboBox.removeActionListener(ActionListener)} through queue + */ + public void removeActionListener(final ActionListener actionListener) { + runMapping(new MapVoidAction("removeActionListener") { + @Override + public void map() { + ((JComboBox) getSource()).removeActionListener(actionListener); + } + }); + } + + /** + * Maps {@code JComboBox.removeAllItems()} through queue + */ + public void removeAllItems() { + runMapping(new MapVoidAction("removeAllItems") { + @Override + public void map() { + ((JComboBox) getSource()).removeAllItems(); + } + }); + } + + /** + * Maps {@code JComboBox.removeItem(Object)} through queue + */ + public void removeItem(final Object object) { + runMapping(new MapVoidAction("removeItem") { + @Override + public void map() { + ((JComboBox) getSource()).removeItem(object); + } + }); + } + + /** + * Maps {@code JComboBox.removeItemAt(int)} through queue + */ + public void removeItemAt(final int i) { + runMapping(new MapVoidAction("removeItemAt") { + @Override + public void map() { + ((JComboBox) getSource()).removeItemAt(i); + } + }); + } + + /** + * Maps {@code JComboBox.removeItemListener(ItemListener)} through queue + */ + public void removeItemListener(final ItemListener itemListener) { + runMapping(new MapVoidAction("removeItemListener") { + @Override + public void map() { + ((JComboBox) getSource()).removeItemListener(itemListener); + } + }); + } + + /** + * Maps {@code JComboBox.selectWithKeyChar(char)} through queue + */ + public boolean selectWithKeyChar(final char c) { + return (runMapping(new MapBooleanAction("selectWithKeyChar") { + @Override + public boolean map() { + return ((JComboBox) getSource()).selectWithKeyChar(c); + } + })); + } + + /** + * Maps {@code JComboBox.setActionCommand(String)} through queue + */ + public void setActionCommand(final String string) { + runMapping(new MapVoidAction("setActionCommand") { + @Override + public void map() { + ((JComboBox) getSource()).setActionCommand(string); + } + }); + } + + /** + * Maps {@code JComboBox.setEditable(boolean)} through queue + */ + public void setEditable(final boolean b) { + runMapping(new MapVoidAction("setEditable") { + @Override + public void map() { + ((JComboBox) getSource()).setEditable(b); + } + }); + } + + /** + * Maps {@code JComboBox.setEditor(ComboBoxEditor)} through queue + */ + public void setEditor(final ComboBoxEditor comboBoxEditor) { + runMapping(new MapVoidAction("setEditor") { + @Override + public void map() { + ((JComboBox) getSource()).setEditor(comboBoxEditor); + } + }); + } + + /** + * Maps {@code JComboBox.setKeySelectionManager(KeySelectionManager)} + * through queue + */ + public void setKeySelectionManager(final KeySelectionManager keySelectionManager) { + runMapping(new MapVoidAction("setKeySelectionManager") { + @Override + public void map() { + ((JComboBox) getSource()).setKeySelectionManager(keySelectionManager); + } + }); + } + + /** + * Maps {@code JComboBox.setLightWeightPopupEnabled(boolean)} through queue + */ + public void setLightWeightPopupEnabled(final boolean b) { + runMapping(new MapVoidAction("setLightWeightPopupEnabled") { + @Override + public void map() { + ((JComboBox) getSource()).setLightWeightPopupEnabled(b); + } + }); + } + + /** + * Maps {@code JComboBox.setMaximumRowCount(int)} through queue + */ + public void setMaximumRowCount(final int i) { + runMapping(new MapVoidAction("setMaximumRowCount") { + @Override + public void map() { + ((JComboBox) getSource()).setMaximumRowCount(i); + } + }); + } + + /** + * Maps {@code JComboBox.setModel(ComboBoxModel)} through queue + */ + @SuppressWarnings(value = "unchecked") + public void setModel(final ComboBoxModel comboBoxModel) { + runMapping(new MapVoidAction("setModel") { + @Override + public void map() { + ((JComboBox) getSource()).setModel(comboBoxModel); + } + }); + } + + /** + * Maps {@code JComboBox.setPopupVisible(boolean)} through queue + */ + public void setPopupVisible(final boolean b) { + runMapping(new MapVoidAction("setPopupVisible") { + @Override + public void map() { + ((JComboBox) getSource()).setPopupVisible(b); + } + }); + } + + /** + * Maps {@code JComboBox.setRenderer(ListCellRenderer)} through queue + */ + @SuppressWarnings(value = "unchecked") + public void setRenderer(final ListCellRenderer listCellRenderer) { + runMapping(new MapVoidAction("setRenderer") { + @Override + public void map() { + ((JComboBox) getSource()).setRenderer(listCellRenderer); + } + }); + } + + /** + * Maps {@code JComboBox.setSelectedIndex(int)} through queue + */ + public void setSelectedIndex(final int i) { + runMapping(new MapVoidAction("setSelectedIndex") { + @Override + public void map() { + ((JComboBox) getSource()).setSelectedIndex(i); + } + }); + } + + /** + * Maps {@code JComboBox.setSelectedItem(Object)} through queue + */ + public void setSelectedItem(final Object object) { + runMapping(new MapVoidAction("setSelectedItem") { + @Override + public void map() { + ((JComboBox) getSource()).setSelectedItem(object); + } + }); + } + + /** + * Maps {@code JComboBox.setUI(ComboBoxUI)} through queue + */ + public void setUI(final ComboBoxUI comboBoxUI) { + runMapping(new MapVoidAction("setUI") { + @Override + public void map() { + ((JComboBox) getSource()).setUI(comboBoxUI); + } + }); + } + + /** + * Maps {@code JComboBox.showPopup()} through queue + */ + public void showPopup() { + runMapping(new MapVoidAction("showPopup") { + @Override + public void map() { + ((JComboBox) getSource()).showPopup(); + } + }); + } + + //End of mapping // + //////////////////////////////////////////////////////// + /** + * Allows to find component by an item. + */ + public static class JComboBoxByItemFinder implements ComponentChooser { + + String label; + int itemIndex; + StringComparator comparator; + + /** + * Constructs JComboBoxByItemFinder. + * + * @param lb a text pattern + * @param ii item index to check. If equal to -1, selected item is + * checked. + * @param comparator specifies string comparision algorithm. + */ + public JComboBoxByItemFinder(String lb, int ii, StringComparator comparator) { + label = lb; + itemIndex = ii; + this.comparator = comparator; + } + + /** + * Constructs JComboBoxByItemFinder. + * + * @param lb a text pattern + * @param ii item index to check. If equal to -1, selected item is + * checked. + */ + public JComboBoxByItemFinder(String lb, int ii) { + this(lb, ii, Operator.getDefaultStringComparator()); + } + + @Override + public boolean checkComponent(Component comp) { + if (comp instanceof JComboBox) { + if (label == null) { + return true; + } + if (((JComboBox) comp).getModel().getSize() > itemIndex) { + int ii = itemIndex; + if (ii == -1) { + ii = ((JComboBox) comp).getSelectedIndex(); + if (ii == -1) { + return false; + } + } + return (comparator.equals(((JComboBox) comp).getModel().getElementAt(ii).toString(), + label)); + } + } + return false; + } + + @Override + public String getDescription() { + return ("JComboBox with text \"" + label + "\" in " + + itemIndex + "'th item"); + } + + @Override + public String toString() { + return "JComboBoxByItemFinder{" + "label=" + label + ", itemIndex=" + itemIndex + ", comparator=" + comparator + '}'; + } + } + + /** + * Checks component type. + */ + public static class JComboBoxFinder extends Finder { + + /** + * Constructs JComboBoxFinder. + * + * @param sf other searching criteria. + */ + public JComboBoxFinder(ComponentChooser sf) { + super(JComboBox.class, sf); + } + + /** + * Constructs JComboBoxFinder. + */ + public JComboBoxFinder() { + super(JComboBox.class); + } + } + + private static class PopupWindowChooser implements ComponentChooser { + + ComponentChooser pChooser; + + public PopupWindowChooser(ComponentChooser pChooser) { + this.pChooser = pChooser; + } + + @Override + public boolean checkComponent(Component comp) { + ComponentSearcher cs = new ComponentSearcher((Container) comp); + cs.setOutput(TestOut.getNullOutput()); + return cs.findComponent(pChooser) != null; + } + + @Override + public String getDescription() { + return "Popup window"; + } + + @Override + public String toString() { + return "PopupWindowChooser{" + "pChooser=" + pChooser + '}'; + } + } + + private class ListWater extends Waiter { + + ComponentChooser cChooser; + ComponentChooser pChooser; + + public ListWater() { + super(); + cChooser = new ComponentChooser() { + @Override + public boolean checkComponent(Component comp) { + if (comp instanceof JList) { + Container cont = (Container) comp; + while ((cont = cont.getParent()) != null) { + if (cont instanceof ComboPopup) { + return true; + } + } + } + return false; + } + + @Override + public String getDescription() { + return "Popup menu"; + } + + @Override + public String toString() { + return "JComboBoxOperator.ListWater.ComponentChooser{description = " + getDescription() + '}'; + } + }; + pChooser = new PopupWindowChooser(cChooser); + } + + @Override + public Component actionProduced(Void obj) { + Window popupWindow = null; + if (pChooser.checkComponent(getWindow())) { + popupWindow = getWindow(); + } else { + popupWindow = WindowWaiter.getWindow(getWindow(), pChooser); + } + if (popupWindow != null) { + ComponentSearcher sc = new ComponentSearcher(popupWindow); + sc.setOutput(TestOut.getNullOutput()); + return sc.findComponent(cChooser); + } else { + return null; + } + } + + @Override + public String getDescription() { + return "Wait popup expanded"; + } + + @Override + public String toString() { + return "ListWater{" + "cChooser=" + cChooser + ", pChooser=" + pChooser + '}'; + } + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JComponentOperator.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JComponentOperator.java new file mode 100644 index 00000000000..dc59739d513 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JComponentOperator.java @@ -0,0 +1,1286 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.operators; + +import java.awt.Component; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.Insets; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Window; +import java.awt.event.ActionListener; +import java.awt.event.MouseEvent; +import java.beans.VetoableChangeListener; +import java.util.Hashtable; + +import javax.accessibility.AccessibleContext; +import javax.swing.JComponent; +import javax.swing.JInternalFrame; +import javax.swing.JRootPane; +import javax.swing.JToolTip; +import javax.swing.KeyStroke; +import javax.swing.border.Border; +import javax.swing.event.AncestorListener; + +import org.netbeans.jemmy.ComponentChooser; +import org.netbeans.jemmy.ComponentSearcher; +import org.netbeans.jemmy.JemmyProperties; +import org.netbeans.jemmy.Outputable; +import org.netbeans.jemmy.TestOut; +import org.netbeans.jemmy.TimeoutExpiredException; +import org.netbeans.jemmy.Timeoutable; +import org.netbeans.jemmy.Timeouts; + +/** + *

Timeouts used:
+ * JComponentOperator.WaitToolTipTimeout - time to wait tool tip displayed
+ * JComponentOperator.ShowToolTipTimeout - time to show tool tip
+ * ComponentOperator.WaitComponentTimeout - time to wait component displayed + *
. + * + * @see org.netbeans.jemmy.Timeouts + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + * + */ +public class JComponentOperator extends ContainerOperator + implements Timeoutable, Outputable { + + /** + * Identifier for a "tooltip text" property. + * + * @see #getDump + */ + public static final String TOOLTIP_TEXT_DPROP = "Tooltip text"; + public static final String A11Y_DATA = "Accessible data (yes/no)"; + public static final String A11Y_NAME_DPROP = "Accessible name"; + public static final String A11Y_DESCRIPTION_DPROP = "Accessible decription"; + + private final static long WAIT_TOOL_TIP_TIMEOUT = 10000; + private final static long SHOW_TOOL_TIP_TIMEOUT = 0; + + private Timeouts timeouts; + private TestOut output; + + /** + * Constructor. + * + * @param b a component + */ + public JComponentOperator(JComponent b) { + super(b); + } + + /** + * Constructs a JComponentOperator object. + * + * @param cont a container + * @param chooser a component chooser specifying searching criteria. + * @param index an index between appropriate ones. + */ + public JComponentOperator(ContainerOperator cont, ComponentChooser chooser, int index) { + this((JComponent) cont. + waitSubComponent(new JComponentFinder(chooser), + index)); + copyEnvironment(cont); + } + + /** + * Constructs a JComponentOperator object. + * + * @param cont a container + * @param chooser a component chooser specifying searching criteria. + */ + public JComponentOperator(ContainerOperator cont, ComponentChooser chooser) { + this(cont, chooser, 0); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont Operator pointing a container to search component in. + * @param index Ordinal component index. + * @throws TimeoutExpiredException + */ + public JComponentOperator(ContainerOperator cont, int index) { + this((JComponent) waitComponent(cont, + new JComponentFinder(ComponentSearcher.getTrueChooser("Any JComponent")), + index)); + copyEnvironment(cont); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont Operator pointing a container to search component in. + * @throws TimeoutExpiredException + */ + public JComponentOperator(ContainerOperator cont) { + this(cont, 0); + } + + /** + * Searches JComponent in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @param index Ordinal component index. + * @return JComponent instance or null if component was not found. + */ + public static JComponent findJComponent(Container cont, ComponentChooser chooser, int index) { + return (JComponent) findComponent(cont, new JComponentFinder(chooser), index); + } + + /** + * Searches 0'th JComponent in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @return JComponent instance or null if component was not found. + */ + public static JComponent findJComponent(Container cont, ComponentChooser chooser) { + return findJComponent(cont, chooser, 0); + } + + /** + * Searches JComponent by tooltip text. + * + * @param cont Container to search component in. + * @param toolTipText Tooltip text. If null, contents is not checked. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @param index Ordinal component index. + * @return JComponent instance or null if component was not found. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + */ + public static JComponent findJComponent(Container cont, String toolTipText, boolean ce, boolean ccs, int index) { + return findJComponent(cont, new JComponentByTipFinder(toolTipText, new DefaultStringComparator(ce, ccs)), index); + } + + /** + * Searches JComponent by tooltip text. + * + * @param cont Container to search component in. + * @param toolTipText Tooltip text. If null, contents is not checked. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @return JComponent instance or null if component was not found. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + */ + public static JComponent findJComponent(Container cont, String toolTipText, boolean ce, boolean ccs) { + return findJComponent(cont, toolTipText, ce, ccs, 0); + } + + /** + * Waits JComponent in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @param index Ordinal component index. + * @return JComponent instance or null if component was not found. + * @throws TimeoutExpiredException + */ + public static JComponent waitJComponent(Container cont, ComponentChooser chooser, final int index) { + return (JComponent) waitComponent(cont, new JComponentFinder(chooser), index); + } + + /** + * Waits 0'th JComponent in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @return JComponent instance or null if component was not found. + * @throws TimeoutExpiredException + */ + public static JComponent waitJComponent(Container cont, ComponentChooser chooser) { + return waitJComponent(cont, chooser, 0); + } + + /** + * Waits JComponent by tooltip text. + * + * @param cont Container to search component in. + * @param toolTipText Tooltip text. If null, contents is not checked. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @param index Ordinal component index. + * @return JComponent instance or null if component was not found. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public static JComponent waitJComponent(Container cont, String toolTipText, boolean ce, boolean ccs, int index) { + return waitJComponent(cont, new JComponentByTipFinder(toolTipText, new DefaultStringComparator(ce, ccs)), index); + } + + /** + * Waits JComponent by tooltip text. + * + * @param cont Container to search component in. + * @param toolTipText Tooltip text. If null, contents is not checked. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @return JComponent instance or null if component was not found. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public static JComponent waitJComponent(Container cont, String toolTipText, boolean ce, boolean ccs) { + return waitJComponent(cont, toolTipText, ce, ccs, 0); + } + + static { + Timeouts.initDefault("JComponentOperator.WaitToolTipTimeout", WAIT_TOOL_TIP_TIMEOUT); + Timeouts.initDefault("JComponentOperator.ShowToolTipTimeout", SHOW_TOOL_TIP_TIMEOUT); + } + + @Override + public void setTimeouts(Timeouts timeouts) { + super.setTimeouts(timeouts); + this.timeouts = timeouts; + } + + @Override + public Timeouts getTimeouts() { + return timeouts; + } + + @Override + public void setOutput(TestOut out) { + output = out; + super.setOutput(output.createErrorOutput()); + } + + @Override + public TestOut getOutput() { + return output; + } + + @Override + public int getCenterXForClick() { + Rectangle rect = getVisibleRect(); + return ((int) rect.getX() + + (int) rect.getWidth() / 2); + } + + @Override + public int getCenterYForClick() { + Rectangle rect = getVisibleRect(); + return ((int) rect.getY() + + (int) rect.getHeight() / 2); + } + + /** + * Showes tool tip. + * + * @return JToolTip component. + * @throws TimeoutExpiredException + */ + public JToolTip showToolTip() { + enterMouse(); + moveMouse(getCenterXForClick(), + getCenterYForClick()); + return waitToolTip(); + } + + public JToolTip waitToolTip() { + return ((JToolTip) waitComponent(WindowOperator. + waitWindow(new JToolTipWindowFinder(), + 0, + getTimeouts(), + getOutput()), + new JToolTipFinder(), + 0, + getTimeouts(), + getOutput())); + } + + /** + * Looks for a first window-like container. + * + * @return either WindowOperator of JInternalFrameOperator + */ + public ContainerOperator getWindowContainerOperator() { + Component resultComp; + if (getSource() instanceof Window) { + resultComp = getSource(); + } else { + resultComp = getContainer(new ComponentChooser() { + @Override + public boolean checkComponent(Component comp) { + return (comp instanceof Window + || comp instanceof JInternalFrame); + } + + @Override + public String getDescription() { + return ""; + } + }); + } + ContainerOperator result; + if (resultComp instanceof Window) { + result = new WindowOperator((Window) resultComp); + } else { + result = new ContainerOperator<>((Container) resultComp); + } + result.copyEnvironment(this); + return result; + } + + @Override + public Hashtable getDump() { + Hashtable result = super.getDump(); + if (getToolTipText() != null) { + result.put(TOOLTIP_TEXT_DPROP, getToolTipText()); + } + //System.out.println("Dump a11y = " + System.getProperty("jemmy.dump.a11y")); + if (System.getProperty("jemmy.dump.a11y") != null + && System.getProperty("jemmy.dump.a11y").equals("on")) { + AccessibleContext a11y = getSource().getAccessibleContext(); + if (a11y != null) { + result.put(A11Y_DATA, "yes"); + String accName = (a11y.getAccessibleName() == null) ? "null" : a11y.getAccessibleName(); + String accDesc = (a11y.getAccessibleDescription() == null) ? "null" : a11y.getAccessibleDescription(); + result.put(A11Y_NAME_DPROP, accName); + result.put(A11Y_DESCRIPTION_DPROP, accDesc); + } else { + result.put(A11Y_DATA, "no"); + } + } + return result; + } + + //////////////////////////////////////////////////////// + //Mapping // + /** + * Maps {@code JComponent.addAncestorListener(AncestorListener)} + * through queue + */ + public void addAncestorListener(final AncestorListener ancestorListener) { + runMapping(new MapVoidAction("addAncestorListener") { + @Override + public void map() { + ((JComponent) getSource()).addAncestorListener(ancestorListener); + } + }); + } + + /** + * Maps + * {@code JComponent.addVetoableChangeListener(VetoableChangeListener)} + * through queue + */ + public void addVetoableChangeListener(final VetoableChangeListener vetoableChangeListener) { + runMapping(new MapVoidAction("addVetoableChangeListener") { + @Override + public void map() { + ((JComponent) getSource()).addVetoableChangeListener(vetoableChangeListener); + } + }); + } + + /** + * Maps {@code JComponent.computeVisibleRect(Rectangle)} through queue + */ + public void computeVisibleRect(final Rectangle rectangle) { + runMapping(new MapVoidAction("computeVisibleRect") { + @Override + public void map() { + ((JComponent) getSource()).computeVisibleRect(rectangle); + } + }); + } + + /** + * Maps {@code JComponent.createToolTip()} through queue + */ + public JToolTip createToolTip() { + return (runMapping(new MapAction("createToolTip") { + @Override + public JToolTip map() { + return ((JComponent) getSource()).createToolTip(); + } + })); + } + + /** + * Maps {@code JComponent.firePropertyChange(String, byte, byte)} + * through queue + */ + public void firePropertyChange(final String string, final byte b, final byte b1) { + runMapping(new MapVoidAction("firePropertyChange") { + @Override + public void map() { + getSource().firePropertyChange(string, b, b1); + } + }); + } + + /** + * Maps {@code JComponent.firePropertyChange(String, char, char)} + * through queue + */ + public void firePropertyChange(final String string, final char c, final char c1) { + runMapping(new MapVoidAction("firePropertyChange") { + @Override + public void map() { + getSource().firePropertyChange(string, c, c1); + } + }); + } + + /** + * Maps {@code JComponent.firePropertyChange(String, double, double)} + * through queue + */ + public void firePropertyChange(final String string, final double d, final double d1) { + runMapping(new MapVoidAction("firePropertyChange") { + @Override + public void map() { + getSource().firePropertyChange(string, d, d1); + } + }); + } + + /** + * Maps {@code JComponent.firePropertyChange(String, float, float)} + * through queue + */ + public void firePropertyChange(final String string, final float f, final float f1) { + runMapping(new MapVoidAction("firePropertyChange") { + @Override + public void map() { + getSource().firePropertyChange(string, f, f1); + } + }); + } + + /** + * Maps {@code JComponent.firePropertyChange(String, int, int)} through queue + */ + public void firePropertyChange(final String string, final int i, final int i1) { + runMapping(new MapVoidAction("firePropertyChange") { + @Override + public void map() { + ((JComponent) getSource()).firePropertyChange(string, i, i1); + } + }); + } + + /** + * Maps {@code JComponent.firePropertyChange(String, long, long)} + * through queue + */ + public void firePropertyChange(final String string, final long l, final long l1) { + runMapping(new MapVoidAction("firePropertyChange") { + @Override + public void map() { + getSource().firePropertyChange(string, l, l1); + } + }); + } + + /** + * Maps {@code JComponent.firePropertyChange(String, short, short)} + * through queue + */ + public void firePropertyChange(final String string, final short s, final short s1) { + runMapping(new MapVoidAction("firePropertyChange") { + @Override + public void map() { + getSource().firePropertyChange(string, s, s1); + } + }); + } + + /** + * Maps {@code JComponent.firePropertyChange(String, boolean, boolean)} + * through queue + */ + public void firePropertyChange(final String string, final boolean b, final boolean b1) { + runMapping(new MapVoidAction("firePropertyChange") { + @Override + public void map() { + ((JComponent) getSource()).firePropertyChange(string, b, b1); + } + }); + } + + /** + * Maps {@code JComponent.getAccessibleContext()} through queue + */ + public AccessibleContext getAccessibleContext() { + return (runMapping(new MapAction("getAccessibleContext") { + @Override + public AccessibleContext map() { + return getSource().getAccessibleContext(); + } + })); + } + + /** + * Maps {@code JComponent.getActionForKeyStroke(KeyStroke)} through queue + */ + public ActionListener getActionForKeyStroke(final KeyStroke keyStroke) { + return (runMapping(new MapAction("getActionForKeyStroke") { + @Override + public ActionListener map() { + return ((JComponent) getSource()).getActionForKeyStroke(keyStroke); + } + })); + } + + /** + * Maps {@code JComponent.getAutoscrolls()} through queue + */ + public boolean getAutoscrolls() { + return (runMapping(new MapBooleanAction("getAutoscrolls") { + @Override + public boolean map() { + return ((JComponent) getSource()).getAutoscrolls(); + } + })); + } + + /** + * Maps {@code JComponent.getBorder()} through queue + */ + public Border getBorder() { + return (runMapping(new MapAction("getBorder") { + @Override + public Border map() { + return ((JComponent) getSource()).getBorder(); + } + })); + } + + /** + * Maps {@code JComponent.getClientProperty(Object)} through queue + */ + public Object getClientProperty(final Object object) { + return (runMapping(new MapAction("getClientProperty") { + @Override + public Object map() { + return ((JComponent) getSource()).getClientProperty(object); + } + })); + } + + /** + * Maps {@code JComponent.getConditionForKeyStroke(KeyStroke)} through queue + */ + public int getConditionForKeyStroke(final KeyStroke keyStroke) { + return (runMapping(new MapIntegerAction("getConditionForKeyStroke") { + @Override + public int map() { + return ((JComponent) getSource()).getConditionForKeyStroke(keyStroke); + } + })); + } + + /** + * Maps {@code JComponent.getDebugGraphicsOptions()} through queue + */ + public int getDebugGraphicsOptions() { + return (runMapping(new MapIntegerAction("getDebugGraphicsOptions") { + @Override + public int map() { + return ((JComponent) getSource()).getDebugGraphicsOptions(); + } + })); + } + + /** + * Maps {@code JComponent.getInsets(Insets)} through queue + */ + public Insets getInsets(final Insets insets) { + return (runMapping(new MapAction("getInsets") { + @Override + public Insets map() { + return ((JComponent) getSource()).getInsets(insets); + } + })); + } + + /** + * Maps {@code JComponent.getNextFocusableComponent()} through queue + */ + @Deprecated + public Component getNextFocusableComponent() { + return (runMapping(new MapAction("getNextFocusableComponent") { + @Override + public Component map() { + return ((JComponent) getSource()).getNextFocusableComponent(); + } + })); + } + + /** + * Maps {@code JComponent.getRegisteredKeyStrokes()} through queue + */ + public KeyStroke[] getRegisteredKeyStrokes() { + return ((KeyStroke[]) runMapping(new MapAction("getRegisteredKeyStrokes") { + @Override + public Object map() { + return ((JComponent) getSource()).getRegisteredKeyStrokes(); + } + })); + } + + /** + * Maps {@code JComponent.getRootPane()} through queue + */ + public JRootPane getRootPane() { + return (runMapping(new MapAction("getRootPane") { + @Override + public JRootPane map() { + return ((JComponent) getSource()).getRootPane(); + } + })); + } + + /** + * Maps {@code JComponent.getToolTipLocation(MouseEvent)} through queue + */ + public Point getToolTipLocation(final MouseEvent mouseEvent) { + return (runMapping(new MapAction("getToolTipLocation") { + @Override + public Point map() { + return ((JComponent) getSource()).getToolTipLocation(mouseEvent); + } + })); + } + + /** + * Maps {@code JComponent.getToolTipText()} through queue + */ + public String getToolTipText() { + return (runMapping(new MapAction("getToolTipText") { + @Override + public String map() { + return ((JComponent) getSource()).getToolTipText(); + } + })); + } + + /** + * Maps {@code JComponent.getToolTipText(MouseEvent)} through queue + */ + public String getToolTipText(final MouseEvent mouseEvent) { + return (runMapping(new MapAction("getToolTipText") { + @Override + public String map() { + return ((JComponent) getSource()).getToolTipText(mouseEvent); + } + })); + } + + /** + * Maps {@code JComponent.getTopLevelAncestor()} through queue + */ + public Container getTopLevelAncestor() { + return (runMapping(new MapAction("getTopLevelAncestor") { + @Override + public Container map() { + return ((JComponent) getSource()).getTopLevelAncestor(); + } + })); + } + + /** + * Maps {@code JComponent.getUIClassID()} through queue + */ + public String getUIClassID() { + return (runMapping(new MapAction("getUIClassID") { + @Override + public String map() { + return ((JComponent) getSource()).getUIClassID(); + } + })); + } + + /** + * Maps {@code JComponent.getVisibleRect()} through queue + */ + public Rectangle getVisibleRect() { + return (runMapping(new MapAction("getVisibleRect") { + @Override + public Rectangle map() { + return ((JComponent) getSource()).getVisibleRect(); + } + })); + } + + /** + * Maps {@code JComponent.grabFocus()} through queue + */ + public void grabFocus() { + runMapping(new MapVoidAction("grabFocus") { + @Override + public void map() { + ((JComponent) getSource()).grabFocus(); + } + }); + } + + /** + * Maps {@code JComponent.isFocusCycleRoot()} through queue + */ + public boolean isFocusCycleRoot() { + return (runMapping(new MapBooleanAction("isFocusCycleRoot") { + @Override + public boolean map() { + return ((JComponent) getSource()).isFocusCycleRoot(); + } + })); + } + + /** + * Maps {@code JComponent.isManagingFocus()} through queue + */ + @Deprecated + public boolean isManagingFocus() { + return (runMapping(new MapBooleanAction("isManagingFocus") { + @Override + public boolean map() { + return ((JComponent) getSource()).isManagingFocus(); + } + })); + } + + /** + * Maps {@code JComponent.isOptimizedDrawingEnabled()} through queue + */ + public boolean isOptimizedDrawingEnabled() { + return (runMapping(new MapBooleanAction("isOptimizedDrawingEnabled") { + @Override + public boolean map() { + return ((JComponent) getSource()).isOptimizedDrawingEnabled(); + } + })); + } + + /** + * Maps {@code JComponent.isPaintingTile()} through queue + */ + public boolean isPaintingTile() { + return (runMapping(new MapBooleanAction("isPaintingTile") { + @Override + public boolean map() { + return ((JComponent) getSource()).isPaintingTile(); + } + })); + } + + /** + * Maps {@code JComponent.isRequestFocusEnabled()} through queue + */ + public boolean isRequestFocusEnabled() { + return (runMapping(new MapBooleanAction("isRequestFocusEnabled") { + @Override + public boolean map() { + return ((JComponent) getSource()).isRequestFocusEnabled(); + } + })); + } + + /** + * Maps {@code JComponent.isValidateRoot()} through queue + */ + public boolean isValidateRoot() { + return (runMapping(new MapBooleanAction("isValidateRoot") { + @Override + public boolean map() { + return ((JComponent) getSource()).isValidateRoot(); + } + })); + } + + /** + * Maps {@code JComponent.paintImmediately(int, int, int, int)} through queue + */ + public void paintImmediately(final int i, final int i1, final int i2, final int i3) { + runMapping(new MapVoidAction("paintImmediately") { + @Override + public void map() { + ((JComponent) getSource()).paintImmediately(i, i1, i2, i3); + } + }); + } + + /** + * Maps {@code JComponent.paintImmediately(Rectangle)} through queue + */ + public void paintImmediately(final Rectangle rectangle) { + runMapping(new MapVoidAction("paintImmediately") { + @Override + public void map() { + ((JComponent) getSource()).paintImmediately(rectangle); + } + }); + } + + /** + * Maps {@code JComponent.putClientProperty(Object, Object)} through queue + */ + public void putClientProperty(final Object object, final Object object1) { + runMapping(new MapVoidAction("putClientProperty") { + @Override + public void map() { + ((JComponent) getSource()).putClientProperty(object, object1); + } + }); + } + + /** + * Maps + * {@code JComponent.registerKeyboardAction(ActionListener, String, KeyStroke, int)} + * through queue + */ + public void registerKeyboardAction(final ActionListener actionListener, final String string, final KeyStroke keyStroke, final int i) { + runMapping(new MapVoidAction("registerKeyboardAction") { + @Override + public void map() { + ((JComponent) getSource()).registerKeyboardAction(actionListener, string, keyStroke, i); + } + }); + } + + /** + * Maps + * {@code JComponent.registerKeyboardAction(ActionListener, KeyStroke, int)} + * through queue + */ + public void registerKeyboardAction(final ActionListener actionListener, final KeyStroke keyStroke, final int i) { + runMapping(new MapVoidAction("registerKeyboardAction") { + @Override + public void map() { + ((JComponent) getSource()).registerKeyboardAction(actionListener, keyStroke, i); + } + }); + } + + /** + * Maps {@code JComponent.removeAncestorListener(AncestorListener)} + * through queue + */ + public void removeAncestorListener(final AncestorListener ancestorListener) { + runMapping(new MapVoidAction("removeAncestorListener") { + @Override + public void map() { + ((JComponent) getSource()).removeAncestorListener(ancestorListener); + } + }); + } + + /** + * Maps + * {@code JComponent.removeVetoableChangeListener(VetoableChangeListener)} + * through queue + */ + public void removeVetoableChangeListener(final VetoableChangeListener vetoableChangeListener) { + runMapping(new MapVoidAction("removeVetoableChangeListener") { + @Override + public void map() { + ((JComponent) getSource()).removeVetoableChangeListener(vetoableChangeListener); + } + }); + } + + /** + * Maps {@code JComponent.repaint(Rectangle)} through queue + */ + public void repaint(final Rectangle rectangle) { + runMapping(new MapVoidAction("repaint") { + @Override + public void map() { + ((JComponent) getSource()).repaint(rectangle); + } + }); + } + + /** + * Maps {@code JComponent.requestDefaultFocus()} through queue + */ + @Deprecated + public boolean requestDefaultFocus() { + return (runMapping(new MapBooleanAction("requestDefaultFocus") { + @Override + public boolean map() { + return ((JComponent) getSource()).requestDefaultFocus(); + } + })); + } + + /** + * Maps {@code JComponent.resetKeyboardActions()} through queue + */ + public void resetKeyboardActions() { + runMapping(new MapVoidAction("resetKeyboardActions") { + @Override + public void map() { + ((JComponent) getSource()).resetKeyboardActions(); + } + }); + } + + /** + * Maps {@code JComponent.revalidate()} through queue + */ + public void revalidate() { + runMapping(new MapVoidAction("revalidate") { + @Override + public void map() { + getSource().revalidate(); + } + }); + } + + /** + * Maps {@code JComponent.scrollRectToVisible(Rectangle)} through queue + */ + public void scrollRectToVisible(final Rectangle rectangle) { + runMapping(new MapVoidAction("scrollRectToVisible") { + @Override + public void map() { + ((JComponent) getSource()).scrollRectToVisible(rectangle); + } + }); + } + + /** + * Maps {@code JComponent.setAlignmentX(float)} through queue + */ + public void setAlignmentX(final float f) { + runMapping(new MapVoidAction("setAlignmentX") { + @Override + public void map() { + ((JComponent) getSource()).setAlignmentX(f); + } + }); + } + + /** + * Maps {@code JComponent.setAlignmentY(float)} through queue + */ + public void setAlignmentY(final float f) { + runMapping(new MapVoidAction("setAlignmentY") { + @Override + public void map() { + ((JComponent) getSource()).setAlignmentY(f); + } + }); + } + + /** + * Maps {@code JComponent.setAutoscrolls(boolean)} through queue + */ + public void setAutoscrolls(final boolean b) { + runMapping(new MapVoidAction("setAutoscrolls") { + @Override + public void map() { + ((JComponent) getSource()).setAutoscrolls(b); + } + }); + } + + /** + * Maps {@code JComponent.setBorder(Border)} through queue + */ + public void setBorder(final Border border) { + runMapping(new MapVoidAction("setBorder") { + @Override + public void map() { + ((JComponent) getSource()).setBorder(border); + } + }); + } + + /** + * Maps {@code JComponent.setDebugGraphicsOptions(int)} through queue + */ + public void setDebugGraphicsOptions(final int i) { + runMapping(new MapVoidAction("setDebugGraphicsOptions") { + @Override + public void map() { + ((JComponent) getSource()).setDebugGraphicsOptions(i); + } + }); + } + + /** + * Maps {@code JComponent.setDoubleBuffered(boolean)} through queue + */ + public void setDoubleBuffered(final boolean b) { + runMapping(new MapVoidAction("setDoubleBuffered") { + @Override + public void map() { + ((JComponent) getSource()).setDoubleBuffered(b); + } + }); + } + + /** + * Maps {@code JComponent.setMaximumSize(Dimension)} through queue + */ + public void setMaximumSize(final Dimension dimension) { + runMapping(new MapVoidAction("setMaximumSize") { + @Override + public void map() { + getSource().setMaximumSize(dimension); + } + }); + } + + /** + * Maps {@code JComponent.setMinimumSize(Dimension)} through queue + */ + public void setMinimumSize(final Dimension dimension) { + runMapping(new MapVoidAction("setMinimumSize") { + @Override + public void map() { + getSource().setMinimumSize(dimension); + } + }); + } + + /** + * Maps {@code JComponent.setNextFocusableComponent(Component)} through queue + */ + @Deprecated + public void setNextFocusableComponent(final Component component) { + runMapping(new MapVoidAction("setNextFocusableComponent") { + @Override + public void map() { + ((JComponent) getSource()).setNextFocusableComponent(component); + } + }); + } + + /** + * Maps {@code JComponent.setOpaque(boolean)} through queue + */ + public void setOpaque(final boolean b) { + runMapping(new MapVoidAction("setOpaque") { + @Override + public void map() { + ((JComponent) getSource()).setOpaque(b); + } + }); + } + + /** + * Maps {@code JComponent.setPreferredSize(Dimension)} through queue + */ + public void setPreferredSize(final Dimension dimension) { + runMapping(new MapVoidAction("setPreferredSize") { + @Override + public void map() { + getSource().setPreferredSize(dimension); + } + }); + } + + /** + * Maps {@code JComponent.setRequestFocusEnabled(boolean)} through queue + */ + public void setRequestFocusEnabled(final boolean b) { + runMapping(new MapVoidAction("setRequestFocusEnabled") { + @Override + public void map() { + ((JComponent) getSource()).setRequestFocusEnabled(b); + } + }); + } + + /** + * Maps {@code JComponent.setToolTipText(String)} through queue + */ + public void setToolTipText(final String string) { + runMapping(new MapVoidAction("setToolTipText") { + @Override + public void map() { + ((JComponent) getSource()).setToolTipText(string); + } + }); + } + + /** + * Maps {@code JComponent.unregisterKeyboardAction(KeyStroke)} through queue + */ + public void unregisterKeyboardAction(final KeyStroke keyStroke) { + runMapping(new MapVoidAction("unregisterKeyboardAction") { + @Override + public void map() { + ((JComponent) getSource()).unregisterKeyboardAction(keyStroke); + } + }); + } + + /** + * Maps {@code JComponent.updateUI()} through queue + */ + public void updateUI() { + runMapping(new MapVoidAction("updateUI") { + @Override + public void map() { + ((JComponent) getSource()).updateUI(); + } + }); + } + + //End of mapping // + //////////////////////////////////////////////////////// + /** + * Allows to find component by tooltip. + */ + public static class JComponentByTipFinder implements ComponentChooser { + + String label; + + StringComparator comparator; + + /** + * Constructs JComponentByTipFinder. + * + * @param lb a text pattern + * @param comparator specifies string comparision algorithm. + */ + public JComponentByTipFinder(String lb, StringComparator comparator) { + label = lb; + this.comparator = comparator; + } + + /** + * Constructs JComponentByTipFinder. + * + * @param lb a text pattern + */ + public JComponentByTipFinder(String lb) { + this(lb, Operator.getDefaultStringComparator()); + } + + @Override + public boolean checkComponent(Component comp) { + if (comp instanceof JComponent) { + if (((JComponent) comp).getToolTipText() != null) { + return (comparator.equals(((JComponent) comp).getToolTipText(), + label)); + } + } + return false; + } + + @Override + public String getDescription() { + return "JComponent with tool tip \"" + label + "\""; + } + + @Override + public String toString() { + return "JComponentByTipFinder{" + "label=" + label + ", comparator=" + comparator + '}'; + } + } + + /** + * Checks component type. + */ + public static class JComponentFinder extends Finder { + + /** + * Constructs JComponentFinder. + * + * @param sf other searching criteria. + */ + public JComponentFinder(ComponentChooser sf) { + super(JComponent.class, sf); + } + + /** + * Constructs JComponentFinder. + */ + public JComponentFinder() { + super(JComponent.class); + } + } + + static class JToolTipWindowFinder implements ComponentChooser { + + ComponentChooser ppFinder; + + public JToolTipWindowFinder() { + ppFinder = new ComponentChooser() { + @Override + public boolean checkComponent(Component comp) { + return (comp.isShowing() + && comp.isVisible() + && comp instanceof JToolTip); + } + + @Override + public String getDescription() { + return "A tool tip"; + } + + @Override + public String toString() { + return "JComponentOperator.JToolTipWindowFinder.ComponentChooser{description = " + getDescription() + '}'; + } + }; + } + + @Override + public boolean checkComponent(Component comp) { + if (comp.isShowing() && comp instanceof Window) { + ComponentSearcher cs = new ComponentSearcher((Container) comp); + cs.setOutput(JemmyProperties.getCurrentOutput().createErrorOutput()); + return (cs.findComponent(ppFinder) + != null); + } + return false; + } + + @Override + public String getDescription() { + return "A tool tip window"; + } + + @Override + public String toString() { + return "JToolTipWindowFinder{" + "ppFinder=" + ppFinder + '}'; + } + } + + class JToolTipFinder extends Finder { + + public JToolTipFinder(ComponentChooser sf) { + super(JToolTip.class, sf); + } + + public JToolTipFinder() { + super(JToolTip.class); + } + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JDialogOperator.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JDialogOperator.java new file mode 100644 index 00000000000..883369265ea --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JDialogOperator.java @@ -0,0 +1,748 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.operators; + +import java.awt.Component; +import java.awt.Container; +import java.awt.Dialog; +import java.awt.Window; + +import javax.accessibility.AccessibleContext; +import javax.swing.JDialog; +import javax.swing.JLayeredPane; +import javax.swing.JMenuBar; +import javax.swing.JRootPane; + +import org.netbeans.jemmy.ComponentChooser; +import org.netbeans.jemmy.DialogWaiter; +import org.netbeans.jemmy.JemmyProperties; +import org.netbeans.jemmy.TestOut; +import org.netbeans.jemmy.TimeoutExpiredException; +import org.netbeans.jemmy.Timeouts; + +/** + *

Timeouts used:
+ * DialogWaiter.WaitDialogTimeout - time to wait dialog displayed
+ * DialogWaiter.AfterDialogTimeout - time to sleep after dialog has been + * dispayed
. + * + * @see org.netbeans.jemmy.Timeouts + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + * + */ +public class JDialogOperator extends DialogOperator { + + /** + * Constructor. + * + * @param w a component + */ + public JDialogOperator(JDialog w) { + super(w); + } + + /** + * Constructs a JDialogOperator object. + * + * @param chooser a component chooser specifying searching criteria. + * @param index an index between appropriate ones. + * @param env an operator to copy environment from. + */ + public JDialogOperator(ComponentChooser chooser, int index, Operator env) { + this(waitJDialog(new JDialogFinder(chooser), + index, + env.getTimeouts(), + env.getOutput())); + copyEnvironment(env); + } + + /** + * Constructs a JDialogOperator object. + * + * @param chooser a component chooser specifying searching criteria. + * @param index an index between appropriate ones. + */ + public JDialogOperator(ComponentChooser chooser, int index) { + this(chooser, index, Operator.getEnvironmentOperator()); + } + + /** + * Constructs a JDialogOperator object. + * + * @param chooser a component chooser specifying searching criteria. + */ + public JDialogOperator(ComponentChooser chooser) { + this(chooser, 0); + } + + /** + * Constructs a JDialogOperator object. + * + * @param owner window - owner + * @param chooser a component chooser specifying searching criteria. + * @param index an index between appropriate ones. + */ + public JDialogOperator(WindowOperator owner, ComponentChooser chooser, int index) { + this((JDialog) owner. + waitSubWindow(new JDialogFinder(chooser), + index)); + copyEnvironment(owner); + } + + /** + * Constructs a JDialogOperator object. + * + * @param owner window - owner + * @param chooser a component chooser specifying searching criteria. + */ + public JDialogOperator(WindowOperator owner, ComponentChooser chooser) { + this(owner, chooser, 0); + } + + /** + * Constructor. Waits for the dialog with "title" subtitle. Uses owner's + * timeout and output for waiting and to init operator. + * + * @param owner Operator pointing to a window owner. + * @param title The desired title. + * @param index Ordinal index. The first dialog has {@code index} 0. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * + */ + public JDialogOperator(WindowOperator owner, String title, int index) { + this(waitJDialog(owner, + new JDialogFinder(new DialogByTitleFinder(title, + owner.getComparator())), + index)); + copyEnvironment(owner); + } + + /** + * Constructor. Waits for the dialog with "title" subtitle. Uses owner's + * timeout and output for waiting and to init operator. + * + * @param owner Operator pointing to a window owner. + * @param title The desired title. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * + */ + public JDialogOperator(WindowOperator owner, String title) { + this(owner, title, 0); + } + + /** + * Constructor. Waits for the index'th dialog between owner's children. Uses + * owner'th timeout and output for waiting and to init operator. + * + * @param owner Operator pointing to a window owner. + * @param index Ordinal component index. + * + */ + public JDialogOperator(WindowOperator owner, int index) { + this(waitJDialog(owner, + new JDialogFinder(), + index)); + copyEnvironment(owner); + } + + /** + * Constructor. Waits for the first dialog between owner's children. Uses + * owner'th timeout and output for waiting and to init operator. + * + * @param owner Operator pointing to a window owner. + * + */ + public JDialogOperator(WindowOperator owner) { + this(owner, 0); + } + + /** + * Constructor. Waits for the dialog with "title" subtitle. Constructor can + * be used in complicated cases when output or timeouts should differ from + * default. + * + * @param title a window title + * @param index Ordinal component index. + * @param env an operator to copy environment from. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * + */ + public JDialogOperator(String title, int index, Operator env) { + this(new JDialogFinder(new DialogByTitleFinder(title, + env.getComparator())), + index, env); + } + + /** + * Constructor. Waits for the dialog with "title" subtitle. Uses current + * timeouts and output values. + * + * @param title a window title + * @param index Ordinal component index. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @see JemmyProperties#getCurrentTimeouts() + * @see JemmyProperties#getCurrentOutput() + * + */ + public JDialogOperator(String title, int index) { + this(title, index, + ComponentOperator.getEnvironmentOperator()); + } + + /** + * Constructor. Waits for the dialog with "title" subtitle. Uses current + * timeouts and output values. + * + * @param title a window title + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @see JemmyProperties#getCurrentTimeouts() + * @see JemmyProperties#getCurrentOutput() + * + */ + public JDialogOperator(String title) { + this(title, 0); + } + + /** + * Constructor. Waits for the index'th dialog. Uses current timeout and + * output for waiting and to init operator. + * + * @param index Ordinal component index. + * + */ + public JDialogOperator(int index) { + this(waitJDialog(new JDialogFinder(), + index, + ComponentOperator.getEnvironmentOperator().getTimeouts(), + ComponentOperator.getEnvironmentOperator().getOutput())); + copyEnvironment(ComponentOperator.getEnvironmentOperator()); + } + + /** + * Constructor. Waits for the first dialog. Uses current timeout and output + * for waiting and to init operator. + * + */ + public JDialogOperator() { + this(0); + } + + /** + * Searches an index'th dialog. + * + * @param chooser a component chooser specifying searching criteria. + * @param index an index between appropriate ones. + * @return JDialog instance or null if component was not found. + */ + public static JDialog findJDialog(ComponentChooser chooser, int index) { + return (JDialog) DialogWaiter.getDialog(new JDialogFinder(chooser), index); + } + + /** + * Searches a dialog. + * + * @param chooser a component chooser specifying searching criteria. + * @return JDialog instance or null if component was not found. + */ + public static JDialog findJDialog(ComponentChooser chooser) { + return findJDialog(chooser, 0); + } + + /** + * Searches an index'th dialog by title. + * + * @param title Dialog title + * @param ce Compare exactly. If true, text can be a substring of caption. + * @param cc Compare case sensitively. If true, both text and caption are + * @param index an index between appropriate ones. + * @return JDialog instance or null if component was not found. + */ + public static JDialog findJDialog(String title, boolean ce, boolean cc, int index) { + return ((JDialog) DialogWaiter. + getDialog(new JDialogFinder(new DialogByTitleFinder(title, + new DefaultStringComparator(ce, cc))), + index)); + } + + /** + * Searches a dialog by title. + * + * @param title Dialog title + * @param ce Compare exactly. If true, text can be a substring of caption. + * @param cc Compare case sensitively. If true, both text and caption are + * @return JDialog instance or null if component was not found. + */ + public static JDialog findJDialog(String title, boolean ce, boolean cc) { + return findJDialog(title, ce, cc, 0); + } + + /** + * Searches an index'th dialog between owner's owned windows. + * + * @param owner Window - dialog owner. + * @param chooser a component chooser specifying searching criteria. + * @param index an index between appropriate ones. + * @return JDialog instance or null if component was not found. + */ + public static JDialog findJDialog(Window owner, ComponentChooser chooser, int index) { + return (JDialog) DialogWaiter.getDialog(owner, new JDialogFinder(chooser), index); + } + + /** + * Searches a dialog between owner's owned windows. + * + * @param owner Window - dialog owner. + * @param chooser a component chooser specifying searching criteria. + * @return JDialog instance or null if component was not found. + */ + public static JDialog findJDialog(Window owner, ComponentChooser chooser) { + return findJDialog(owner, chooser, 0); + } + + /** + * Searches an index'th dialog by title between owner's owned windows. + * + * @param owner Window - dialog owner. + * @param title Dialog title + * @param ce Compare exactly. If true, text can be a substring of caption. + * @param cc Compare case sensitively. If true, both text and caption are + * @param index an index between appropriate ones. + * @return JDialog instance or null if component was not found. + */ + public static JDialog findJDialog(Window owner, String title, boolean ce, boolean cc, int index) { + return ((JDialog) DialogWaiter. + getDialog(owner, + new JDialogFinder(new DialogByTitleFinder(title, + new DefaultStringComparator(ce, cc))), + index)); + } + + /** + * Searches a dialog by title between owner's owned windows. + * + * @param owner Window - dialog owner. + * @param title Dialog title + * @param ce Compare exactly. If true, text can be a substring of caption. + * @param cc Compare case sensitively. If true, both text and caption are + * @return JDialog instance or null if component was not found. + */ + public static JDialog findJDialog(Window owner, String title, boolean ce, boolean cc) { + return findJDialog(owner, title, ce, cc, 0); + } + + /** + * Waits an index'th dialog. + * + * @param chooser a component chooser specifying searching criteria. + * @param index an index between appropriate ones. + * @return JDialog instance or null if component was not found. + * + */ + public static JDialog waitJDialog(ComponentChooser chooser, int index) { + return (waitJDialog(chooser, index, + JemmyProperties.getCurrentTimeouts(), + JemmyProperties.getCurrentOutput())); + } + + /** + * Waits a dialog. + * + * @param chooser a component chooser specifying searching criteria. + * @return JDialog instance or null if component was not found. + * + */ + public static JDialog waitJDialog(ComponentChooser chooser) { + return waitJDialog(chooser, 0); + } + + /** + * Waits an index'th dialog by title. + * + * @param title Dialog title + * @param ce Compare exactly. If true, text can be a substring of caption. + * @param cc Compare case sensitively. If true, both text and caption are + * @param index an index between appropriate ones. + * @return JDialog instance or null if component was not found. + * + */ + public static JDialog waitJDialog(String title, boolean ce, boolean cc, int index) { + return (waitJDialog(new JDialogFinder(new DialogByTitleFinder(title, + new DefaultStringComparator(ce, cc))), + index)); + } + + /** + * Waits a dialog by title. + * + * @param title Dialog title + * @param ce Compare exactly. If true, text can be a substring of caption. + * @param cc Compare case sensitively. If true, both text and caption are + * @return JDialog instance or null if component was not found. + * + */ + public static JDialog waitJDialog(String title, boolean ce, boolean cc) { + return waitJDialog(title, ce, cc, 0); + } + + /** + * Waits an index'th dialog between owner's owned windows. + * + * @param owner Window - dialog owner. + * @param chooser a component chooser specifying searching criteria. + * @param index an index between appropriate ones. + * @return JDialog instance or null if component was not found. + * + */ + public static JDialog waitJDialog(Window owner, ComponentChooser chooser, int index) { + return (waitJDialog(owner, chooser, index, + JemmyProperties.getCurrentTimeouts(), + JemmyProperties.getCurrentOutput())); + } + + /** + * Waits a dialog between owner's owned windows. + * + * @param owner Window - dialog owner. + * @param chooser a component chooser specifying searching criteria. + * @return JDialog instance or null if component was not found. + * + */ + public static JDialog waitJDialog(Window owner, ComponentChooser chooser) { + return waitJDialog(owner, chooser, 0); + } + + /** + * Waits an index'th dialog by title between owner's owned windows. + * + * @param owner Window - dialog owner. + * @param title Dialog title + * @param ce Compare exactly. If true, text can be a substring of caption. + * @param cc Compare case sensitively. If true, both text and caption are + * @param index an index between appropriate ones. + * @return JDialog instance or null if component was not found. + * + */ + public static JDialog waitJDialog(Window owner, String title, boolean ce, boolean cc, int index) { + return (waitJDialog(owner, new JDialogFinder(new DialogByTitleFinder(title, + new DefaultStringComparator(ce, cc))), + index)); + } + + /** + * Waits a dialog by title between owner's owned windows. + * + * @param owner Window - dialog owner. + * @param title Dialog title + * @param ce Compare exactly. If true, text can be a substring of caption. + * @param cc Compare case sensitively. If true, both text and caption are + * @return JDialog instance or null if component was not found. + * + */ + public static JDialog waitJDialog(Window owner, String title, boolean ce, boolean cc) { + return waitJDialog(owner, title, ce, cc, 0); + } + + /** + * Searhs for modal dialog currently staying on top. + * + * @return dialog or null if no modal dialog is currently displayed. + */ + public static Dialog getTopModalDialog() { + return (DialogWaiter.getDialog(new ComponentChooser() { + @Override + public boolean checkComponent(Component comp) { + if (comp instanceof Dialog) { + Dialog dialog = (Dialog) comp; + if (dialog.isModal()) { + Window[] ow = dialog.getOwnedWindows(); + for (Window anOw : ow) { + if (anOw.isVisible()) { + return false; + } + } + return true; + } + } + return false; + } + + @Override + public String getDescription() { + return "Upper modal dialog"; + } + + @Override + public String toString() { + return "JDialogOperator.getTopModalDialog.ComponentChooser{description = " + getDescription() + '}'; + } + })); + } + + //////////////////////////////////////////////////////// + //Mapping // + /** + * Maps {@code JDialog.getAccessibleContext()} through queue + */ + public AccessibleContext getAccessibleContext() { + return (runMapping(new MapAction("getAccessibleContext") { + @Override + public AccessibleContext map() { + return getSource().getAccessibleContext(); + } + })); + } + + /** + * Maps {@code JDialog.getContentPane()} through queue + */ + public Container getContentPane() { + return (runMapping(new MapAction("getContentPane") { + @Override + public Container map() { + return ((JDialog) getSource()).getContentPane(); + } + })); + } + + /** + * Maps {@code JDialog.getDefaultCloseOperation()} through queue + */ + public int getDefaultCloseOperation() { + return (runMapping(new MapIntegerAction("getDefaultCloseOperation") { + @Override + public int map() { + return ((JDialog) getSource()).getDefaultCloseOperation(); + } + })); + } + + /** + * Maps {@code JDialog.getGlassPane()} through queue + */ + public Component getGlassPane() { + return (runMapping(new MapAction("getGlassPane") { + @Override + public Component map() { + return ((JDialog) getSource()).getGlassPane(); + } + })); + } + + /** + * Maps {@code JDialog.getJMenuBar()} through queue + */ + public JMenuBar getJMenuBar() { + return (runMapping(new MapAction("getJMenuBar") { + @Override + public JMenuBar map() { + return ((JDialog) getSource()).getJMenuBar(); + } + })); + } + + /** + * Maps {@code JDialog.getLayeredPane()} through queue + */ + public JLayeredPane getLayeredPane() { + return (runMapping(new MapAction("getLayeredPane") { + @Override + public JLayeredPane map() { + return ((JDialog) getSource()).getLayeredPane(); + } + })); + } + + /** + * Maps {@code JDialog.getRootPane()} through queue + */ + public JRootPane getRootPane() { + return (runMapping(new MapAction("getRootPane") { + @Override + public JRootPane map() { + return ((JDialog) getSource()).getRootPane(); + } + })); + } + + /** + * Maps {@code JDialog.setContentPane(Container)} through queue + */ + public void setContentPane(final Container container) { + runMapping(new MapVoidAction("setContentPane") { + @Override + public void map() { + ((JDialog) getSource()).setContentPane(container); + } + }); + } + + /** + * Maps {@code JDialog.setDefaultCloseOperation(int)} through queue + */ + public void setDefaultCloseOperation(final int i) { + runMapping(new MapVoidAction("setDefaultCloseOperation") { + @Override + public void map() { + ((JDialog) getSource()).setDefaultCloseOperation(i); + } + }); + } + + /** + * Maps {@code JDialog.setGlassPane(Component)} through queue + */ + public void setGlassPane(final Component component) { + runMapping(new MapVoidAction("setGlassPane") { + @Override + public void map() { + ((JDialog) getSource()).setGlassPane(component); + } + }); + } + + /** + * Maps {@code JDialog.setJMenuBar(JMenuBar)} through queue + */ + public void setJMenuBar(final JMenuBar jMenuBar) { + runMapping(new MapVoidAction("setJMenuBar") { + @Override + public void map() { + ((JDialog) getSource()).setJMenuBar(jMenuBar); + } + }); + } + + /** + * Maps {@code JDialog.setLayeredPane(JLayeredPane)} through queue + */ + public void setLayeredPane(final JLayeredPane jLayeredPane) { + runMapping(new MapVoidAction("setLayeredPane") { + @Override + public void map() { + ((JDialog) getSource()).setLayeredPane(jLayeredPane); + } + }); + } + + /** + * Maps {@code JDialog.setLocationRelativeTo(Component)} through queue + */ + public void setLocationRelativeTo(final Component component) { + runMapping(new MapVoidAction("setLocationRelativeTo") { + @Override + public void map() { + ((JDialog) getSource()).setLocationRelativeTo(component); + } + }); + } + + //End of mapping // + //////////////////////////////////////////////////////// + /** + * A method to be used from subclasses. Uses timeouts and output passed as + * parameters during the waiting. + * + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @param index Ordinal component index. + * @param timeouts timeouts to be used during the waiting. + * @param output an output to be used during the waiting. + * @return Component instance or null if component was not found. + */ + protected static JDialog waitJDialog(ComponentChooser chooser, int index, + Timeouts timeouts, TestOut output) { + try { + DialogWaiter waiter = new DialogWaiter(); + waiter.setTimeouts(timeouts); + waiter.setOutput(output); + return ((JDialog) waiter. + waitDialog(new JDialogFinder(chooser), index)); + } catch (InterruptedException e) { + output.printStackTrace(e); + return null; + } + } + + /** + * A method to be used from subclasses. Uses {@code owner}'s timeouts + * and output during the waiting. + * + * @param owner a window - dialog owner. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @param index Ordinal component index. + * @return Component instance or null if component was not found. + * @throws TimeoutExpiredException + */ + protected static JDialog waitJDialog(WindowOperator owner, ComponentChooser chooser, int index) { + return (waitJDialog((Window) owner.getSource(), + chooser, index, + owner.getTimeouts(), owner.getOutput())); + } + + /** + * A method to be used from subclasses. Uses timeouts and output passed as + * parameters during the waiting. + * + * @param owner a window - dialog owner. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @param index Ordinal component index. + * @param timeouts timeouts to be used during the waiting. + * @param output an output to be used during the waiting. + * @return Component instance or null if component was not found. + */ + protected static JDialog waitJDialog(Window owner, ComponentChooser chooser, int index, + Timeouts timeouts, TestOut output) { + try { + DialogWaiter waiter = new DialogWaiter(); + waiter.setTimeouts(timeouts); + waiter.setOutput(output); + return ((JDialog) waiter. + waitDialog(owner, new JDialogFinder(chooser), index)); + } catch (InterruptedException e) { + JemmyProperties.getCurrentOutput().printStackTrace(e); + return null; + } + } + + /** + * Checks component type. + */ + public static class JDialogFinder extends Finder { + + /** + * Constructs JDialogFinder. + * + * @param sf other searching criteria. + */ + public JDialogFinder(ComponentChooser sf) { + super(JDialog.class, sf); + } + + /** + * Constructs JDialogFinder. + */ + public JDialogFinder() { + super(JDialog.class); + } + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JEditorPaneOperator.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JEditorPaneOperator.java new file mode 100644 index 00000000000..63f536ade64 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JEditorPaneOperator.java @@ -0,0 +1,481 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.operators; + +import java.awt.Container; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Hashtable; + +import javax.swing.JEditorPane; +import javax.swing.event.HyperlinkEvent; +import javax.swing.event.HyperlinkListener; +import javax.swing.text.EditorKit; + +import org.netbeans.jemmy.ComponentChooser; +import org.netbeans.jemmy.TimeoutExpiredException; + +/** + * Class provides basic functions to operate with JEditorPane (selection, + * typing, deleting) + * + *

Timeouts used:
+ * JTextComponentOperator.PushKeyTimeout - time between key pressing and + * releasing during text typing
+ * JTextComponentOperator.BetweenKeysTimeout - time to sleep between two chars + * typing
+ * JTextComponentOperator.ChangeCaretPositionTimeout - maximum time to change + * caret position
+ * JTextComponentOperator.TypeTextTimeout - maximum time to type text
+ * ComponentOperator.WaitComponentTimeout - time to wait component displayed + *
+ * ComponentOperator.WaitFocusTimeout - time to wait component focus
+ * JScrollBarOperator.OneScrollClickTimeout - time for one scroll click
+ * JScrollBarOperator.WholeScrollTimeout - time for the whole scrolling
. + * + * @see org.netbeans.jemmy.Timeouts + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + */ +public class JEditorPaneOperator extends JTextComponentOperator { + + /** + * Identifier for a "content type" property. + * + * @see #getDump + */ + public static final String CONTENT_TYPE_DPROP = "Content type"; + + /** + * Constructor. + * + * @param b a component + */ + public JEditorPaneOperator(JEditorPane b) { + super(b); + } + + /** + * Constructs a JEditorPaneOperator object. + * + * @param cont a container + * @param chooser a component chooser specifying searching criteria. + * @param index an index between appropriate ones. + */ + public JEditorPaneOperator(ContainerOperator cont, ComponentChooser chooser, int index) { + this((JEditorPane) cont. + waitSubComponent(new JEditorPaneFinder(chooser), + index)); + copyEnvironment(cont); + } + + /** + * Constructs a JEditorPaneOperator object. + * + * @param cont a container + * @param chooser a component chooser specifying searching criteria. + */ + public JEditorPaneOperator(ContainerOperator cont, ComponentChooser chooser) { + this(cont, chooser, 0); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont a container + * @param text Button text. + * @param index Ordinal component index. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public JEditorPaneOperator(ContainerOperator cont, String text, int index) { + this((JEditorPane) waitComponent(cont, + new JEditorPaneFinder(new JTextComponentOperator.JTextComponentByTextFinder(text, + cont.getComparator())), + index)); + copyEnvironment(cont); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont a container + * @param text Button text. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public JEditorPaneOperator(ContainerOperator cont, String text) { + this(cont, text, 0); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont a container + * @param index Ordinal component index. + * @throws TimeoutExpiredException + */ + public JEditorPaneOperator(ContainerOperator cont, int index) { + this((JEditorPane) waitComponent(cont, + new JEditorPaneFinder(), + index)); + copyEnvironment(cont); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont a container + * @throws TimeoutExpiredException + */ + public JEditorPaneOperator(ContainerOperator cont) { + this(cont, 0); + } + + /** + * Searches JEditorPane in container. + * + * @param cont Container to search component in. + * @param chooser a component chooser specifying searching criteria. + * @param index Ordinal component index. + * @return JEditorPane instance or null if component was not found. + */ + public static JEditorPane findJEditorPane(Container cont, ComponentChooser chooser, int index) { + return (JEditorPane) findJTextComponent(cont, new JEditorPaneFinder(chooser), index); + } + + /** + * Searches JEditorPane in container. + * + * @param cont Container to search component in. + * @param chooser a component chooser specifying searching criteria. + * @return JEditorPane instance or null if component was not found. + */ + public static JEditorPane findJEditorPane(Container cont, ComponentChooser chooser) { + return findJEditorPane(cont, chooser, 0); + } + + /** + * Searches JEditorPane by text. + * + * @param cont Container to search component in. + * @param text Component text. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @param index Ordinal component index. + * @return JEditorPane instance or null if component was not found. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + */ + public static JEditorPane findJEditorPane(Container cont, String text, boolean ce, boolean ccs, int index) { + return (findJEditorPane(cont, + new JEditorPaneFinder(new JTextComponentOperator.JTextComponentByTextFinder(text, + new DefaultStringComparator(ce, ccs))), + index)); + } + + /** + * Searches JEditorPane by text. + * + * @param cont Container to search component in. + * @param text Component text. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @return JEditorPane instance or null if component was not found. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + */ + public static JEditorPane findJEditorPane(Container cont, String text, boolean ce, boolean ccs) { + return findJEditorPane(cont, text, ce, ccs, 0); + } + + /** + * Waits JEditorPane in container. + * + * @param cont Container to search component in. + * @param chooser a component chooser specifying searching criteria. + * @param index Ordinal component index. + * @return JEditorPane instance. + * @throws TimeoutExpiredException + */ + public static JEditorPane waitJEditorPane(Container cont, ComponentChooser chooser, int index) { + return (JEditorPane) waitJTextComponent(cont, new JEditorPaneFinder(chooser), index); + } + + /** + * Waits JEditorPane in container. + * + * @param cont Container to search component in. + * @param chooser a component chooser specifying searching criteria. + * @return JEditorPane instance. + * @throws TimeoutExpiredException + */ + public static JEditorPane waitJEditorPane(Container cont, ComponentChooser chooser) { + return waitJEditorPane(cont, chooser, 0); + } + + /** + * Waits JEditorPane by text. + * + * @param cont Container to search component in. + * @param text Component text. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @param index Ordinal component index. + * @return JEditorPane instance. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public static JEditorPane waitJEditorPane(Container cont, String text, boolean ce, boolean ccs, int index) { + return (waitJEditorPane(cont, + new JEditorPaneFinder(new JTextComponentOperator.JTextComponentByTextFinder(text, + new DefaultStringComparator(ce, ccs))), + index)); + } + + /** + * Waits JEditorPane by text. + * + * @param cont Container to search component in. + * @param text Component text. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @return JEditorPane instance. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public static JEditorPane waitJEditorPane(Container cont, String text, boolean ce, boolean ccs) { + return waitJEditorPane(cont, text, ce, ccs, 0); + } + + /** + * Notifies whether "PageUp" and "PageDown" should be used to change caret + * position. If can be useful if text takes some pages. + * + * @param yesOrNo whether to use "PageUp" and "PageDown" + * @deprecated vlue set by this method is not used anymore: all navigating + * is performed by TextDriver. + */ + @Deprecated + public void usePageNavigationKeys(boolean yesOrNo) { + } + + /** + * Returns information about component. + */ + @Override + public Hashtable getDump() { + Hashtable result = super.getDump(); + result.put(CONTENT_TYPE_DPROP, ((JEditorPane) getSource()).getContentType()); + return result; + } + + //////////////////////////////////////////////////////// + //Mapping // + /** + * Maps {@code JEditorPane.addHyperlinkListener(HyperlinkListener)} + * through queue + */ + public void addHyperlinkListener(final HyperlinkListener hyperlinkListener) { + runMapping(new MapVoidAction("addHyperlinkListener") { + @Override + public void map() { + ((JEditorPane) getSource()).addHyperlinkListener(hyperlinkListener); + } + }); + } + + /** + * Maps {@code JEditorPane.fireHyperlinkUpdate(HyperlinkEvent)} through queue + */ + public void fireHyperlinkUpdate(final HyperlinkEvent hyperlinkEvent) { + runMapping(new MapVoidAction("fireHyperlinkUpdate") { + @Override + public void map() { + ((JEditorPane) getSource()).fireHyperlinkUpdate(hyperlinkEvent); + } + }); + } + + /** + * Maps {@code JEditorPane.getContentType()} through queue + */ + public String getContentType() { + return (runMapping(new MapAction("getContentType") { + @Override + public String map() { + return ((JEditorPane) getSource()).getContentType(); + } + })); + } + + /** + * Maps {@code JEditorPane.getEditorKit()} through queue + */ + public EditorKit getEditorKit() { + return (runMapping(new MapAction("getEditorKit") { + @Override + public EditorKit map() { + return ((JEditorPane) getSource()).getEditorKit(); + } + })); + } + + /** + * Maps {@code JEditorPane.getEditorKitForContentType(String)} through queue + */ + public EditorKit getEditorKitForContentType(final String string) { + return (runMapping(new MapAction("getEditorKitForContentType") { + @Override + public EditorKit map() { + return ((JEditorPane) getSource()).getEditorKitForContentType(string); + } + })); + } + + /** + * Maps {@code JEditorPane.getPage()} through queue + */ + public URL getPage() { + return (runMapping(new MapAction("getPage") { + @Override + public URL map() { + return ((JEditorPane) getSource()).getPage(); + } + })); + } + + /** + * Maps {@code JEditorPane.read(InputStream, Object)} through queue + */ + public void read(final InputStream inputStream, final Object object) { + runMapping(new MapVoidAction("read") { + @Override + public void map() throws IOException { + ((JEditorPane) getSource()).read(inputStream, object); + } + }); + } + + /** + * Maps {@code JEditorPane.removeHyperlinkListener(HyperlinkListener)} + * through queue + */ + public void removeHyperlinkListener(final HyperlinkListener hyperlinkListener) { + runMapping(new MapVoidAction("removeHyperlinkListener") { + @Override + public void map() { + ((JEditorPane) getSource()).removeHyperlinkListener(hyperlinkListener); + } + }); + } + + /** + * Maps {@code JEditorPane.setContentType(String)} through queue + */ + public void setContentType(final String string) { + runMapping(new MapVoidAction("setContentType") { + @Override + public void map() { + ((JEditorPane) getSource()).setContentType(string); + } + }); + } + + /** + * Maps {@code JEditorPane.setEditorKit(EditorKit)} through queue + */ + public void setEditorKit(final EditorKit editorKit) { + runMapping(new MapVoidAction("setEditorKit") { + @Override + public void map() { + ((JEditorPane) getSource()).setEditorKit(editorKit); + } + }); + } + + /** + * Maps + * {@code JEditorPane.setEditorKitForContentType(String, EditorKit)} + * through queue + */ + public void setEditorKitForContentType(final String string, final EditorKit editorKit) { + runMapping(new MapVoidAction("setEditorKitForContentType") { + @Override + public void map() { + ((JEditorPane) getSource()).setEditorKitForContentType(string, editorKit); + } + }); + } + + /** + * Maps {@code JEditorPane.setPage(String)} through queue + */ + public void setPage(final String string) { + runMapping(new MapVoidAction("setPage") { + @Override + public void map() throws IOException { + ((JEditorPane) getSource()).setPage(string); + } + }); + } + + /** + * Maps {@code JEditorPane.setPage(URL)} through queue + */ + public void setPage(final URL uRL) { + runMapping(new MapVoidAction("setPage") { + @Override + public void map() throws IOException { + ((JEditorPane) getSource()).setPage(uRL); + } + }); + } + + //End of mapping // + //////////////////////////////////////////////////////// + /** + * Checks component type. + */ + public static class JEditorPaneFinder extends Finder { + + /** + * Constructs JEditorPaneFinder. + * + * @param sf other searching criteria. + */ + public JEditorPaneFinder(ComponentChooser sf) { + super(JEditorPane.class, sf); + } + + /** + * Constructs JEditorPaneFinder. + */ + public JEditorPaneFinder() { + super(JEditorPane.class); + } + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JFileChooserOperator.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JFileChooserOperator.java new file mode 100644 index 00000000000..fbe0d70f15f --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JFileChooserOperator.java @@ -0,0 +1,1739 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.operators; + +import java.awt.Component; +import java.awt.Container; +import java.awt.Rectangle; +import java.awt.Window; +import java.awt.event.ActionListener; +import java.io.File; + +import javax.swing.ComboBoxModel; +import javax.swing.Icon; +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JComponent; +import javax.swing.JDialog; +import javax.swing.JFileChooser; +import javax.swing.JList; +import javax.swing.JTextField; +import javax.swing.JToggleButton; +import javax.swing.ListModel; +import javax.swing.filechooser.FileFilter; +import javax.swing.filechooser.FileSystemView; +import javax.swing.filechooser.FileView; +import javax.swing.plaf.FileChooserUI; + +import org.netbeans.jemmy.ComponentChooser; +import org.netbeans.jemmy.ComponentSearcher; +import org.netbeans.jemmy.JemmyException; +import org.netbeans.jemmy.JemmyProperties; +import org.netbeans.jemmy.Outputable; +import org.netbeans.jemmy.TestOut; +import org.netbeans.jemmy.Timeoutable; +import org.netbeans.jemmy.Timeouts; +import org.netbeans.jemmy.Waitable; +import org.netbeans.jemmy.Waiter; + +/** + * + * Class provides methods to cover main JFileChooser component functionality. + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + * + */ +public class JFileChooserOperator extends JComponentOperator + implements Timeoutable, Outputable { + + private final static long WAIT_LIST_PAINTED_TIMEOUT = 60000; + + private Timeouts timeouts; + private TestOut output; + private ComponentSearcher innerSearcher; + + /** + * Constructor. + * + * @param comp a component + */ + public JFileChooserOperator(JFileChooser comp) { + super(comp); + innerSearcher = new ComponentSearcher(comp); + setTimeouts(JemmyProperties.getProperties().getTimeouts()); + setOutput(JemmyProperties.getProperties().getOutput()); + } + + /** + * Constructor. Waits component first. Constructor can be used in + * complicated cases when output or timeouts should differ from default. + * + * @param env an operator to get environment from. + */ + public JFileChooserOperator(Operator env) { + this((JFileChooser) waitComponent(JDialogOperator. + waitJDialog(new JFileChooserJDialogFinder(env.getOutput()), + 0, + env.getTimeouts(), + env.getOutput()), + new JFileChooserFinder(), + 0, + env.getTimeouts(), + env.getOutput())); + copyEnvironment(env); + } + + /** + * Constructor. Waits component first. + */ + public JFileChooserOperator() { + this(getEnvironmentOperator()); + } + + /** + * Searches currently opened JDilog with JFileChooser inside. + * + * @return a component instance + */ + public static JDialog findJFileChooserDialog() { + return (JDialogOperator. + findJDialog(new JFileChooserJDialogFinder(JemmyProperties. + getCurrentOutput()))); + } + + /** + * Waits currently opened JDilog with JFileChooser inside. + * + * @return a component instance + */ + public static JDialog waitJFileChooserDialog() { + return (JDialogOperator. + waitJDialog(new JFileChooserJDialogFinder(JemmyProperties. + getCurrentOutput()))); + } + + /** + * Searches JFileChooser in container. + * + * @param cont a container + * @return a component instance + */ + public static JFileChooser findJFileChooser(Container cont) { + return (JFileChooser) findComponent(cont, new JFileChooserFinder()); + } + + /** + * Searches JFileChooser in container. + * + * @param cont a container + * @return a component instance + */ + public static JFileChooser waitJFileChooser(Container cont) { + return (JFileChooser) waitComponent(cont, new JFileChooserFinder()); + } + + /** + * Searches currently opened JFileChooser. + * + * @return a component instance + */ + public static JFileChooser findJFileChooser() { + return findJFileChooser(findJFileChooserDialog()); + } + + /** + * Waits currently opened JFileChooser. + * + * @return a component instance + */ + public static JFileChooser waitJFileChooser() { + return waitJFileChooser(waitJFileChooserDialog()); + } + + static { + Timeouts.initDefault("JFileChooserOperator.WaitListPaintedTimeout", WAIT_LIST_PAINTED_TIMEOUT); + } + + @Override + public void setTimeouts(Timeouts timeouts) { + super.setTimeouts(timeouts); + this.timeouts = timeouts; + } + + @Override + public Timeouts getTimeouts() { + return timeouts; + } + + @Override + public void setOutput(TestOut out) { + output = out; + super.setOutput(output.createErrorOutput()); + if (innerSearcher != null) { + innerSearcher.setOutput(output.createErrorOutput()); + } + } + + @Override + public TestOut getOutput() { + return output; + } + + /** + * Returns combo box containing path (upper). + * + * @return JComboBox being used to show directories. + */ + public JComboBox getPathCombo() { + return getCombo(0); + } + + /** + * Returns combo box containing file types (lower). + * + * @return JComboBox being used to show file types. + */ + public JComboBox getFileTypesCombo() { + return getCombo(1); + } + + /** + * Returns approve button. + * + * @return an approve button. + */ + public JButton getApproveButton() { + String aText = getApproveButtonText(); + if (aText == null) { + aText = getUI().getApproveButtonText((JFileChooser) getSource()); + } + if (aText != null) { + return ((JButton) innerSearcher. + findComponent(new ButtonFinder(aText))); + } else { + throw (new JemmyException("JFileChooser.getApproveButtonText() " + + "and getUI().getApproveButtonText " + + "return null")); + } + } + + /** + * Returns cancel button. + * + * @return a cancel button. + */ + public JButton getCancelButton() { + return ((JButton) innerSearcher. + findComponent(new ComponentChooser() { + @Override + public boolean checkComponent(Component comp) { + return (comp != null + && comp instanceof JButton + && comp.getParent() != null + && !(comp.getParent() instanceof JComboBox) + && ((JButton) comp).getText() != null + && ((JButton) comp).getText().length() != 0); + } + + @Override + public String getDescription() { + return "JButton"; + } + + @Override + public String toString() { + return "JFileChooserOperator.getCancelButton.ComponentChooser{description = " + getDescription() + '}'; + } + }, 1)); + } + + /** + * Returns "Home" button. + * + * @return a "home" button. + */ + public JButton getHomeButton() { + return getNoTextButton(1); + } + + /** + * Returns "Up One Level" button. + * + * @return a "Up One Level" button. + */ + public JButton getUpLevelButton() { + return getNoTextButton(0); + } + + /** + * Returns a toggle button being used to switch to list view. + * + * @return a "list mode" button. + */ + public JToggleButton getListToggleButton() { + return getToggleButton(0); + } + + /** + * Returns a toggle button being used to switch to detals view. + * + * @return a "list mode" button. + */ + public JToggleButton getDetailsToggleButton() { + return getToggleButton(1); + } + + /** + * Returns field which can be used to type path. + * + * @return a text field being used for path typing. + */ + public JTextField getPathField() { + return ((JTextField) innerSearcher. + findComponent(new ComponentChooser() { + @Override + public boolean checkComponent(Component comp) { + return (comp != null + && comp instanceof JTextField); + } + + @Override + public String getDescription() { + return "JTextField"; + } + + @Override + public String toString() { + return "JFileChooserOperator.getPathField.ComponentChooser{description = " + getDescription() + '}'; + } + })); + } + + /** + * Returns file list. + * + * @return a list being used to display directory content. + */ + public JList getFileList() { + return ((JList) innerSearcher. + findComponent(new ComponentChooser() { + @Override + public boolean checkComponent(Component comp) { + return (comp != null + && comp instanceof JList); + } + + @Override + public String getDescription() { + return "JList"; + } + + @Override + public String toString() { + return "JFileChooserOperator.getFileList.ComponentChooser{description = " + getDescription() + '}'; + } + })); + } + + /** + * Pushes approve button. + */ + public void approve() { + getQueueTool().waitEmpty(); + output.printTrace("Push approve button in JFileChooser\n : " + + toStringSource()); + JButtonOperator approveOper = new JButtonOperator(getApproveButton()); + approveOper.copyEnvironment(this); + approveOper.setOutput(output.createErrorOutput()); + approveOper.push(); + } + + /** + * Pushes cancel button. + */ + public void cancel() { + output.printTrace("Push cancel button in JFileChooser\n : " + + toStringSource()); + JButtonOperator cancelOper = new JButtonOperator(getCancelButton()); + cancelOper.copyEnvironment(this); + cancelOper.setOutput(output.createErrorOutput()); + cancelOper.push(); + } + + /** + * Types file name into text field and pushes approve button. + * + * @param fileName a file to choose. + */ + public void chooseFile(String fileName) { + getQueueTool().waitEmpty(); + output.printTrace("Choose file by JFileChooser\n : " + fileName + + "\n : " + toStringSource()); + JTextFieldOperator fieldOper = new JTextFieldOperator(getPathField()); + fieldOper.copyEnvironment(this); + fieldOper.setOutput(output.createErrorOutput()); + //workaround + fieldOper.setText(fileName); + //fieldOper.clearText(); + //fieldOper.typeText(fileName); + //approveSelection(); + approve(); + } + + /** + * Pushes "Up One Level" button. + * + * @return new current directory + */ + public File goUpLevel() { + getQueueTool().waitEmpty(); + output.printTrace("Go up level in JFileChooser\n : " + + toStringSource()); + //workaround + setCurrentDirectory(getCurrentDirectory().getParentFile()); + //JButtonOperator upOper = new JButtonOperator(getUpLevelButton()); + //upOper.copyEnvironment(this); + //upOper.setOutput(output.createErrorOutput()); + //upOper.push(); + waitPainted(-1); + return getCurrentDirectory(); + } + + /** + * Pushes "Home" button. + * + * @return new current directory + */ + public File goHome() { + getQueueTool().waitEmpty(); + output.printTrace("Go home in JFileChooser\n : " + + toStringSource()); + JButtonOperator homeOper = new JButtonOperator(getHomeButton()); + homeOper.copyEnvironment(this); + homeOper.setOutput(output.createErrorOutput()); + homeOper.push(); + waitPainted(-1); + return getCurrentDirectory(); + } + + /** + * Clicks on file in the list. + * + * @param index Ordinal file index. + * @param clickCount click count + */ + public void clickOnFile(int index, int clickCount) { + getQueueTool().waitEmpty(); + output.printTrace("Click " + Integer.toString(clickCount) + + "times on " + Integer.toString(index) + + "`th file in JFileChooser\n : " + + toStringSource()); + JListOperator listOper = new JListOperator(getFileList()); + waitPainted(index); + listOper.copyEnvironment(this); + listOper.setOutput(output.createErrorOutput()); + listOper.clickOnItem(index, clickCount); + } + + /** + * Clicks on file in the list. + * + * @param file File name (foo.c). Do not use full path (/tmp/foo.c) here. + * @param comparator a comparator defining string comparision criteria + * @param clickCount click count + */ + public void clickOnFile(String file, StringComparator comparator, int clickCount) { + output.printTrace("Click " + Integer.toString(clickCount) + + "times on \"" + file + + "\" file in JFileChooser\n : " + + toStringSource()); + clickOnFile(findFileIndex(file, comparator), clickCount); + } + + /** + * Clicks on file in the list. + * + * @param file File name (foo.c). Do not use full path (/tmp/foo.c) here. + * @param ce Compare exactly. If true, text can be a substring of caption. + * @param cc Compare case sensitively. If true, both text and caption are + * @param clickCount click count + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @deprecated Use clickOnFile(String, int) or clickOnFile(String, + * StringComparator, int) + */ + @Deprecated + public void clickOnFile(String file, boolean ce, boolean cc, int clickCount) { + clickOnFile(file, new DefaultStringComparator(ce, cc), clickCount); + } + + /** + * Clicks on file in the list. + * + * @param file File name (foo.c). Do not use full path (/tmp/foo.c) here. + * @param clickCount click count + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + */ + public void clickOnFile(String file, int clickCount) { + clickOnFile(file, getComparator(), clickCount); + } + + /** + * Clicks on file in the list. + * + * @param file File name (foo.c). Do not use full path (/tmp/foo.c) here. + * @param comparator a comparator defining string comparision criteria + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + */ + public void clickOnFile(String file, StringComparator comparator) { + clickOnFile(file, comparator, 1); + } + + /** + * Clicks 1 time on file in the list. + * + * @param file File name (foo.c). Do not use full path (/tmp/foo.c) here. + * @param ce Compare exactly. If true, text can be a substring of caption. + * @param cc Compare case sensitively. If true, both text and caption are + * @see #clickOnFile + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @deprecated Use clickOnFile(String) or clickOnFile(String, + * StringComparator) + */ + @Deprecated + public void clickOnFile(String file, boolean ce, boolean cc) { + clickOnFile(file, ce, cc, 1); + } + + /** + * Clicks 1 time on file in the list. + * + * @param file File name (foo.c). Do not use full path (/tmp/foo.c) here. + * @see #clickOnFile + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + */ + public void clickOnFile(String file) { + clickOnFile(file, 1); + } + + /** + * Enters into subdirectory. + * + * @param dir A directory to enter into. + * @param comparator a comparator defining string comparision criteria + * @return new current directory + */ + public File enterSubDir(String dir, StringComparator comparator) { + getQueueTool().waitEmpty(); + selectFile(dir, comparator); + int index = findFileIndex(dir, comparator); + waitPainted(index); + setCurrentDirectory(getSelectedFile()); + return getCurrentDirectory(); + } + + /** + * Enters into subdir curently displayed in the list. + * + * @param dir Directory name (tmp1). Do not use full path (/tmp/tmp1) here. + * @param ce Compare exactly. If true, text can be a substring of caption. + * @param cc Compare case sensitively. If true, both text and caption are + * @return new current directory + * @see #clickOnFile + * @deprecated Use enterSubDir(String) or enterSubDir(String, + * StringComparator) + */ + @Deprecated + public File enterSubDir(String dir, boolean ce, boolean cc) { + return enterSubDir(dir, new DefaultStringComparator(ce, cc)); + } + + /** + * Enters into subdir curently displayed in the list. + * + * @param dir Directory name (tmp1). Do not use full path (/tmp/tmp1) here. + * @return new current directory + * @see #clickOnFile + */ + public File enterSubDir(String dir) { + return enterSubDir(dir, getComparator()); + } + + /** + * Selects a file curently in the list. + * + * @param file File name (foo.c). Do not use full path (/tmp/foo.c) here. + * @param comparator a comparator defining string comparision criteria + * @see #clickOnFile + */ + public void selectFile(String file, StringComparator comparator) { + getQueueTool().waitEmpty(); + int index = findFileIndex(file, comparator); + JListOperator listOper = new JListOperator(getFileList()); + waitPainted(index); + listOper.copyEnvironment(this); + listOper.setOutput(output.createErrorOutput()); + listOper.setSelectedIndex(index); + } + + /** + * Selects a file curently in the list. + * + * @param file File name (foo.c). Do not use full path (/tmp/foo.c) here. + * @param ce Compare exactly. If true, text can be a substring of caption. + * @param cc Compare case sensitively. If true, both text and caption are + * @see #clickOnFile + * @deprecated Use selectFile(String) or selectFile(String, + * StringComparator) + */ + @Deprecated + public void selectFile(String file, boolean ce, boolean cc) { + clickOnFile(file, ce, cc); + } + + /** + * Selects a file curently in the list. + * + * @param file File name (foo.c). Do not use full path (/tmp/foo.c) here. + * @see #clickOnFile + */ + public void selectFile(String file) { + clickOnFile(file); + } + + /** + * Selects directory from the combo box above. + * + * @param dir Directory name (tmp1). Do not use full path (/tmp/tmp1) here. + * @param comparator a comparator defining string comparision criteria + */ + public void selectPathDirectory(String dir, StringComparator comparator) { + getQueueTool().waitEmpty(); + output.printTrace("Select \"" + dir + "\" directory in JFileChooser\n : " + + toStringSource()); + JComboBoxOperator comboOper = new JComboBoxOperator(getPathCombo()); + comboOper.copyEnvironment(this); + comboOper.setOutput(output.createErrorOutput()); + //workaround + comboOper.setSelectedIndex(findDirIndex(dir, comparator)); + //comboOper.selectItem(findDirIndex(dir, comparator)); + waitPainted(-1); + } + + /** + * Selects directory from the combo box above. + * + * @param dir Directory name (tmp1). Do not use full path (/tmp/tmp1) here. + * @param ce Compare exactly. If true, text can be a substring of caption. + * @param cc Compare case sensitively. If true, both text and caption are + * @deprecated Use selectPathDirectory(String) or + * selectPathDirectory(String, StringComparator) + */ + @Deprecated + public void selectPathDirectory(String dir, boolean ce, boolean cc) { + selectPathDirectory(dir, new DefaultStringComparator(ce, cc)); + } + + /** + * Selects directory from the combo box above. + * + * @param dir Directory name (tmp1). Do not use full path (/tmp/tmp1) here. + */ + public void selectPathDirectory(String dir) { + selectPathDirectory(dir, getComparator()); + } + + /** + * Selects file type from the combo box below. + * + * @param filter a pattern for choosing a file type. + * @param comparator a comparator defining string comparision criteria + */ + public void selectFileType(String filter, StringComparator comparator) { + getQueueTool().waitEmpty(); + output.printTrace("Select \"" + filter + "\" file type in JFileChooser\n : " + + toStringSource()); + JComboBoxOperator comboOper = new JComboBoxOperator(getFileTypesCombo()); + comboOper.copyEnvironment(this); + comboOper.setOutput(output.createErrorOutput()); + //workaround + comboOper.setSelectedIndex(findFileTypeIndex(filter, comparator)); + // comboOper.selectItem(findFileTypeIndex(filter, comparator)); + waitPainted(-1); + } + + /** + * Selects file type from the combo box below. + * + * @param filter a pattern for choosing a file type. + * @param ce Compare exactly. If true, text can be a substring of caption. + * @param cc Compare case sensitively. If true, both text and caption are + * @deprecated Use selectFileType(String) or selectFileType(String, + * StringComparator) + */ + @Deprecated + public void selectFileType(String filter, boolean ce, boolean cc) { + selectFileType(filter, new DefaultStringComparator(ce, cc)); + } + + /** + * Selects file type from the combo box below. + * + * @param filter a pattern for choosing a file type. + */ + public void selectFileType(String filter) { + selectFileType(filter, getComparator()); + } + + /** + * Checks if file is currently displayed in the list. + * + * @param file File name (foo.c). Do not use full path (/tmp/foo.c) here. + * @param comparator a comparator defining string comparision criteria + * @return true if file is displayed. + */ + public boolean checkFileDisplayed(String file, StringComparator comparator) { + waitPainted(-1); + return findFileIndex(file, comparator) != -1; + } + + /** + * Checks if file is currently displayed in the list. + * + * @param file File name (foo.c). Do not use full path (/tmp/foo.c) here. + * @param ce Compare exactly. If true, text can be a substring of caption. + * @param cc Compare case sensitively. If true, both text and caption are + * @return true if file is displayed. + * @deprecated Use checkFileDisplayed(String) or checkFileDisplayed(String, + * StringComparator) + */ + @Deprecated + public boolean checkFileDisplayed(String file, boolean ce, boolean cc) { + return checkFileDisplayed(file, new DefaultStringComparator(ce, cc)); + } + + /** + * Checks if file is currently displayed in the list. + * + * @param file File name (foo.c). Do not use full path (/tmp/foo.c) here. + * @return true if file is displayed. + */ + public boolean checkFileDisplayed(String file) { + return checkFileDisplayed(file, getComparator()); + } + + /** + * Return count of files currently displayed. + * + * @return a number of items in the file list. + */ + public int getFileCount() { + waitPainted(-1); + return getFileList().getModel().getSize(); + } + + /** + * Return files currently displayed. + * + * @return an array of items from the file list. + */ + public File[] getFiles() { + waitPainted(-1); + ListModel listModel = getFileList().getModel(); + File[] result = new File[listModel.getSize()]; + for (int i = 0; i < listModel.getSize(); i++) { + result[i] = (File) listModel.getElementAt(i); + } + return result; + } + + /** + * Waits for the file list to have required number of items. + * + * @param count Number of files to wait. + */ + public void waitFileCount(final int count) { + waitState(new ComponentChooser() { + @Override + public boolean checkComponent(Component comp) { + return getFileCount() == count; + } + + @Override + public String getDescription() { + return ("Count of files to be equal " + + Integer.toString(count)); + } + + @Override + public String toString() { + return "JFileChooserOperator.waitFileCount.ComponentChooser{description = " + getDescription() + '}'; + } + }); + } + + /** + * Waits for a file to be displayed in the file list. + * + * @param fileName a file to wait. + */ + public void waitFileDisplayed(final String fileName) { + waitState(new ComponentChooser() { + @Override + public boolean checkComponent(Component comp) { + return checkFileDisplayed(fileName); + } + + @Override + public String getDescription() { + return "\"" + fileName + "\"file to be displayed"; + } + + @Override + public String toString() { + return "JFileChooserOperator.waitFileDisplayed.ComponentChooser{description = " + getDescription() + '}'; + } + }); + } + + //////////////////////////////////////////////////////// + //Mapping // + /** + * Maps {@code JFileChooser.accept(File)} through queue + */ + public boolean accept(final File file) { + return (runMapping(new MapBooleanAction("accept") { + @Override + public boolean map() { + return ((JFileChooser) getSource()).accept(file); + } + })); + } + + /** + * Maps {@code JFileChooser.addActionListener(ActionListener)} through queue + */ + public void addActionListener(final ActionListener actionListener) { + runMapping(new MapVoidAction("addActionListener") { + @Override + public void map() { + ((JFileChooser) getSource()).addActionListener(actionListener); + } + }); + } + + /** + * Maps {@code JFileChooser.addChoosableFileFilter(FileFilter)} through queue + */ + public void addChoosableFileFilter(final FileFilter fileFilter) { + runMapping(new MapVoidAction("addChoosableFileFilter") { + @Override + public void map() { + ((JFileChooser) getSource()).addChoosableFileFilter(fileFilter); + } + }); + } + + /** + * Maps {@code JFileChooser.approveSelection()} through queue + */ + public void approveSelection() { + runMapping(new MapVoidAction("approveSelection") { + @Override + public void map() { + ((JFileChooser) getSource()).approveSelection(); + } + }); + } + + /** + * Maps {@code JFileChooser.cancelSelection()} through queue + */ + public void cancelSelection() { + runMapping(new MapVoidAction("cancelSelection") { + @Override + public void map() { + ((JFileChooser) getSource()).cancelSelection(); + } + }); + } + + /** + * Maps {@code JFileChooser.changeToParentDirectory()} through queue + */ + public void changeToParentDirectory() { + runMapping(new MapVoidAction("changeToParentDirectory") { + @Override + public void map() { + ((JFileChooser) getSource()).changeToParentDirectory(); + } + }); + } + + /** + * Maps {@code JFileChooser.ensureFileIsVisible(File)} through queue + */ + public void ensureFileIsVisible(final File file) { + runMapping(new MapVoidAction("ensureFileIsVisible") { + @Override + public void map() { + ((JFileChooser) getSource()).ensureFileIsVisible(file); + } + }); + } + + /** + * Maps {@code JFileChooser.getAcceptAllFileFilter()} through queue + */ + public FileFilter getAcceptAllFileFilter() { + return (runMapping(new MapAction("getAcceptAllFileFilter") { + @Override + public FileFilter map() { + return ((JFileChooser) getSource()).getAcceptAllFileFilter(); + } + })); + } + + /** + * Maps {@code JFileChooser.getAccessory()} through queue + */ + public JComponent getAccessory() { + return (runMapping(new MapAction("getAccessory") { + @Override + public JComponent map() { + return ((JFileChooser) getSource()).getAccessory(); + } + })); + } + + /** + * Maps {@code JFileChooser.getApproveButtonMnemonic()} through queue + */ + public int getApproveButtonMnemonic() { + return (runMapping(new MapIntegerAction("getApproveButtonMnemonic") { + @Override + public int map() { + return ((JFileChooser) getSource()).getApproveButtonMnemonic(); + } + })); + } + + /** + * Maps {@code JFileChooser.getApproveButtonText()} through queue + */ + public String getApproveButtonText() { + return (runMapping(new MapAction("getApproveButtonText") { + @Override + public String map() { + return ((JFileChooser) getSource()).getApproveButtonText(); + } + })); + } + + /** + * Maps {@code JFileChooser.getApproveButtonToolTipText()} through queue + */ + public String getApproveButtonToolTipText() { + return (runMapping(new MapAction("getApproveButtonToolTipText") { + @Override + public String map() { + return ((JFileChooser) getSource()).getApproveButtonToolTipText(); + } + })); + } + + /** + * Maps {@code JFileChooser.getChoosableFileFilters()} through queue + */ + public FileFilter[] getChoosableFileFilters() { + return ((FileFilter[]) runMapping(new MapAction("getChoosableFileFilters") { + @Override + public Object map() { + return ((JFileChooser) getSource()).getChoosableFileFilters(); + } + })); + } + + /** + * Maps {@code JFileChooser.getCurrentDirectory()} through queue + */ + public File getCurrentDirectory() { + return (runMapping(new MapAction("getCurrentDirectory") { + @Override + public File map() { + return ((JFileChooser) getSource()).getCurrentDirectory(); + } + })); + } + + /** + * Maps {@code JFileChooser.getDescription(File)} through queue + */ + public String getDescription(final File file) { + return (runMapping(new MapAction("getDescription") { + @Override + public String map() { + return ((JFileChooser) getSource()).getDescription(file); + } + })); + } + + /** + * Maps {@code JFileChooser.getDialogTitle()} through queue + */ + public String getDialogTitle() { + return (runMapping(new MapAction("getDialogTitle") { + @Override + public String map() { + return ((JFileChooser) getSource()).getDialogTitle(); + } + })); + } + + /** + * Maps {@code JFileChooser.getDialogType()} through queue + */ + public int getDialogType() { + return (runMapping(new MapIntegerAction("getDialogType") { + @Override + public int map() { + return ((JFileChooser) getSource()).getDialogType(); + } + })); + } + + /** + * Maps {@code JFileChooser.getFileFilter()} through queue + */ + public FileFilter getFileFilter() { + return (runMapping(new MapAction("getFileFilter") { + @Override + public FileFilter map() { + return ((JFileChooser) getSource()).getFileFilter(); + } + })); + } + + /** + * Maps {@code JFileChooser.getFileSelectionMode()} through queue + */ + public int getFileSelectionMode() { + return (runMapping(new MapIntegerAction("getFileSelectionMode") { + @Override + public int map() { + return ((JFileChooser) getSource()).getFileSelectionMode(); + } + })); + } + + /** + * Maps {@code JFileChooser.getFileSystemView()} through queue + */ + public FileSystemView getFileSystemView() { + return (runMapping(new MapAction("getFileSystemView") { + @Override + public FileSystemView map() { + return ((JFileChooser) getSource()).getFileSystemView(); + } + })); + } + + /** + * Maps {@code JFileChooser.getFileView()} through queue + */ + public FileView getFileView() { + return (runMapping(new MapAction("getFileView") { + @Override + public FileView map() { + return ((JFileChooser) getSource()).getFileView(); + } + })); + } + + /** + * Maps {@code JFileChooser.getIcon(File)} through queue + */ + public Icon getIcon(final File file) { + return (runMapping(new MapAction("getIcon") { + @Override + public Icon map() { + return ((JFileChooser) getSource()).getIcon(file); + } + })); + } + + /** + * Maps {@code JFileChooser.getName(File)} through queue + */ + public String getName(final File file) { + return (runMapping(new MapAction("getName") { + @Override + public String map() { + return ((JFileChooser) getSource()).getName(file); + } + })); + } + + /** + * Maps {@code JFileChooser.getSelectedFile()} through queue + */ + public File getSelectedFile() { + return (runMapping(new MapAction("getSelectedFile") { + @Override + public File map() { + return ((JFileChooser) getSource()).getSelectedFile(); + } + })); + } + + /** + * Maps {@code JFileChooser.getSelectedFiles()} through queue + */ + public File[] getSelectedFiles() { + return ((File[]) runMapping(new MapAction("getSelectedFiles") { + @Override + public Object map() { + return ((JFileChooser) getSource()).getSelectedFiles(); + } + })); + } + + /** + * Maps {@code JFileChooser.getTypeDescription(File)} through queue + */ + public String getTypeDescription(final File file) { + return (runMapping(new MapAction("getTypeDescription") { + @Override + public String map() { + return ((JFileChooser) getSource()).getTypeDescription(file); + } + })); + } + + /** + * Maps {@code JFileChooser.getUI()} through queue + */ + public FileChooserUI getUI() { + return (runMapping(new MapAction("getUI") { + @Override + public FileChooserUI map() { + return ((JFileChooser) getSource()).getUI(); + } + })); + } + + /** + * Maps {@code JFileChooser.isDirectorySelectionEnabled()} through queue + */ + public boolean isDirectorySelectionEnabled() { + return (runMapping(new MapBooleanAction("isDirectorySelectionEnabled") { + @Override + public boolean map() { + return ((JFileChooser) getSource()).isDirectorySelectionEnabled(); + } + })); + } + + /** + * Maps {@code JFileChooser.isFileHidingEnabled()} through queue + */ + public boolean isFileHidingEnabled() { + return (runMapping(new MapBooleanAction("isFileHidingEnabled") { + @Override + public boolean map() { + return ((JFileChooser) getSource()).isFileHidingEnabled(); + } + })); + } + + /** + * Maps {@code JFileChooser.isFileSelectionEnabled()} through queue + */ + public boolean isFileSelectionEnabled() { + return (runMapping(new MapBooleanAction("isFileSelectionEnabled") { + @Override + public boolean map() { + return ((JFileChooser) getSource()).isFileSelectionEnabled(); + } + })); + } + + /** + * Maps {@code JFileChooser.isMultiSelectionEnabled()} through queue + */ + public boolean isMultiSelectionEnabled() { + return (runMapping(new MapBooleanAction("isMultiSelectionEnabled") { + @Override + public boolean map() { + return ((JFileChooser) getSource()).isMultiSelectionEnabled(); + } + })); + } + + /** + * Maps {@code JFileChooser.isTraversable(File)} through queue + */ + public boolean isTraversable(final File file) { + return (runMapping(new MapBooleanAction("isTraversable") { + @Override + public boolean map() { + return ((JFileChooser) getSource()).isTraversable(file); + } + })); + } + + /** + * Maps {@code JFileChooser.removeActionListener(ActionListener)} + * through queue + */ + public void removeActionListener(final ActionListener actionListener) { + runMapping(new MapVoidAction("removeActionListener") { + @Override + public void map() { + ((JFileChooser) getSource()).removeActionListener(actionListener); + } + }); + } + + /** + * Maps {@code JFileChooser.removeChoosableFileFilter(FileFilter)} + * through queue + */ + public boolean removeChoosableFileFilter(final FileFilter fileFilter) { + return (runMapping(new MapBooleanAction("removeChoosableFileFilter") { + @Override + public boolean map() { + return ((JFileChooser) getSource()).removeChoosableFileFilter(fileFilter); + } + })); + } + + /** + * Maps {@code JFileChooser.rescanCurrentDirectory()} through queue + */ + public void rescanCurrentDirectory() { + runMapping(new MapVoidAction("rescanCurrentDirectory") { + @Override + public void map() { + ((JFileChooser) getSource()).rescanCurrentDirectory(); + } + }); + } + + /** + * Maps {@code JFileChooser.resetChoosableFileFilters()} through queue + */ + public void resetChoosableFileFilters() { + runMapping(new MapVoidAction("resetChoosableFileFilters") { + @Override + public void map() { + ((JFileChooser) getSource()).resetChoosableFileFilters(); + } + }); + } + + /** + * Maps {@code JFileChooser.setAccessory(JComponent)} through queue + */ + public void setAccessory(final JComponent jComponent) { + runMapping(new MapVoidAction("setAccessory") { + @Override + public void map() { + ((JFileChooser) getSource()).setAccessory(jComponent); + } + }); + } + + /** + * Maps {@code JFileChooser.setApproveButtonMnemonic(char)} through queue + */ + public void setApproveButtonMnemonic(final char c) { + runMapping(new MapVoidAction("setApproveButtonMnemonic") { + @Override + public void map() { + ((JFileChooser) getSource()).setApproveButtonMnemonic(c); + } + }); + } + + /** + * Maps {@code JFileChooser.setApproveButtonMnemonic(int)} through queue + */ + public void setApproveButtonMnemonic(final int i) { + runMapping(new MapVoidAction("setApproveButtonMnemonic") { + @Override + public void map() { + ((JFileChooser) getSource()).setApproveButtonMnemonic(i); + } + }); + } + + /** + * Maps {@code JFileChooser.setApproveButtonText(String)} through queue + */ + public void setApproveButtonText(final String string) { + runMapping(new MapVoidAction("setApproveButtonText") { + @Override + public void map() { + ((JFileChooser) getSource()).setApproveButtonText(string); + } + }); + } + + /** + * Maps {@code JFileChooser.setApproveButtonToolTipText(String)} + * through queue + */ + public void setApproveButtonToolTipText(final String string) { + runMapping(new MapVoidAction("setApproveButtonToolTipText") { + @Override + public void map() { + ((JFileChooser) getSource()).setApproveButtonToolTipText(string); + } + }); + } + + /** + * Maps {@code JFileChooser.setCurrentDirectory(File)} through queue + */ + public void setCurrentDirectory(final File file) { + runMapping(new MapVoidAction("setCurrentDirectory") { + @Override + public void map() { + ((JFileChooser) getSource()).setCurrentDirectory(file); + } + }); + } + + /** + * Maps {@code JFileChooser.setDialogTitle(String)} through queue + */ + public void setDialogTitle(final String string) { + runMapping(new MapVoidAction("setDialogTitle") { + @Override + public void map() { + ((JFileChooser) getSource()).setDialogTitle(string); + } + }); + } + + /** + * Maps {@code JFileChooser.setDialogType(int)} through queue + */ + public void setDialogType(final int i) { + runMapping(new MapVoidAction("setDialogType") { + @Override + public void map() { + ((JFileChooser) getSource()).setDialogType(i); + } + }); + } + + /** + * Maps {@code JFileChooser.setFileFilter(FileFilter)} through queue + */ + public void setFileFilter(final FileFilter fileFilter) { + runMapping(new MapVoidAction("setFileFilter") { + @Override + public void map() { + ((JFileChooser) getSource()).setFileFilter(fileFilter); + } + }); + } + + /** + * Maps {@code JFileChooser.setFileHidingEnabled(boolean)} through queue + */ + public void setFileHidingEnabled(final boolean b) { + runMapping(new MapVoidAction("setFileHidingEnabled") { + @Override + public void map() { + ((JFileChooser) getSource()).setFileHidingEnabled(b); + } + }); + } + + /** + * Maps {@code JFileChooser.setFileSelectionMode(int)} through queue + */ + public void setFileSelectionMode(final int i) { + runMapping(new MapVoidAction("setFileSelectionMode") { + @Override + public void map() { + ((JFileChooser) getSource()).setFileSelectionMode(i); + } + }); + } + + /** + * Maps {@code JFileChooser.setFileSystemView(FileSystemView)} through queue + */ + public void setFileSystemView(final FileSystemView fileSystemView) { + runMapping(new MapVoidAction("setFileSystemView") { + @Override + public void map() { + ((JFileChooser) getSource()).setFileSystemView(fileSystemView); + } + }); + } + + /** + * Maps {@code JFileChooser.setFileView(FileView)} through queue + */ + public void setFileView(final FileView fileView) { + runMapping(new MapVoidAction("setFileView") { + @Override + public void map() { + ((JFileChooser) getSource()).setFileView(fileView); + } + }); + } + + /** + * Maps {@code JFileChooser.setMultiSelectionEnabled(boolean)} through queue + */ + public void setMultiSelectionEnabled(final boolean b) { + runMapping(new MapVoidAction("setMultiSelectionEnabled") { + @Override + public void map() { + ((JFileChooser) getSource()).setMultiSelectionEnabled(b); + } + }); + } + + /** + * Maps {@code JFileChooser.setSelectedFile(File)} through queue + */ + public void setSelectedFile(final File file) { + runMapping(new MapVoidAction("setSelectedFile") { + @Override + public void map() { + ((JFileChooser) getSource()).setSelectedFile(file); + } + }); + } + + /** + * Maps {@code JFileChooser.setSelectedFiles(File[])} through queue + */ + public void setSelectedFiles(final File[] file) { + runMapping(new MapVoidAction("setSelectedFiles") { + @Override + public void map() { + ((JFileChooser) getSource()).setSelectedFiles(file); + } + }); + } + + /** + * Maps {@code JFileChooser.showDialog(Component, String)} through queue + */ + public int showDialog(final Component component, final String string) { + return (runMapping(new MapIntegerAction("showDialog") { + @Override + public int map() { + return ((JFileChooser) getSource()).showDialog(component, string); + } + })); + } + + /** + * Maps {@code JFileChooser.showOpenDialog(Component)} through queue + */ + public int showOpenDialog(final Component component) { + return (runMapping(new MapIntegerAction("showOpenDialog") { + @Override + public int map() { + return ((JFileChooser) getSource()).showOpenDialog(component); + } + })); + } + + /** + * Maps {@code JFileChooser.showSaveDialog(Component)} through queue + */ + public int showSaveDialog(final Component component) { + return (runMapping(new MapIntegerAction("showSaveDialog") { + @Override + public int map() { + return ((JFileChooser) getSource()).showSaveDialog(component); + } + })); + } + + //End of mapping // + //////////////////////////////////////////////////////// + private void waitPainted(int index) { + Waiter drawingWaiter = new Waiter<>(new Waitable() { + @Override + public Rectangle actionProduced(Integer param) { + JList list = getFileList(); + int last_one = list.getModel().getSize() - 1; + if (last_one == -1) { + return new Rectangle(); + } + int current = (param != null) ? param : 0; + try { + if (list.getCellBounds(current, current) != null) { + return list.getCellBounds(last_one, last_one); + } else { + return null; + } + } catch (NullPointerException e) { + //sometimes thrown from list.getCellBounds when item exists but not painted + return null; + } + } + + @Override + public String getDescription() { + return "List drawed"; + } + + @Override + public String toString() { + return "JFileChooserOperator.waitPainted.Waitable{description = " + getDescription() + '}'; + } + }); + drawingWaiter.setTimeoutsToCloneOf(getTimeouts(), "JFileChooserOperator.WaitListPaintedTimeout"); + drawingWaiter.setOutput(getOutput().createErrorOutput()); + try { + drawingWaiter.waitAction((index != -1) ? index : null); + } catch (InterruptedException e) { + output.printStackTrace(e); + } + } + + private JComboBox getCombo(int index) { + return ((JComboBox) innerSearcher. + findComponent(new ComponentChooser() { + @Override + public boolean checkComponent(Component comp) { + return (comp != null + && comp instanceof JComboBox); + } + + @Override + public String getDescription() { + return "JComboBox"; + } + + @Override + public String toString() { + return "JFileChooserOperator.getCombo.ComponentChooser{description = " + getDescription() + '}'; + } + }, index)); + } + + private JButton getNoTextButton(int index) { + return ((JButton) innerSearcher. + findComponent(new ComponentChooser() { + @Override + public boolean checkComponent(Component comp) { + return (comp != null + && comp instanceof JButton + && !(comp.getParent() instanceof JComboBox) + && (((JButton) comp).getText() == null + || ((JButton) comp).getText().length() == 0)); + } + + @Override + public String getDescription() { + return "JButton"; + } + + @Override + public String toString() { + return "JFileChooserOperator.getNoTextButton.ComponentChooser{description = " + getDescription() + '}'; + } + }, index)); + } + + private JToggleButton getToggleButton(int index) { + return ((JToggleButton) innerSearcher. + findComponent(new ComponentChooser() { + @Override + public boolean checkComponent(Component comp) { + return (comp != null + && comp instanceof JToggleButton); + } + + @Override + public String getDescription() { + return "JToggleButton"; + } + + @Override + public String toString() { + return "JFileChooserOperator.getToggleButton.ComponentChooser{description = " + getDescription() + '}'; + } + }, index)); + } + + private int findFileIndex(final String file, final StringComparator comparator) { + Waiter fileWaiter = new Waiter<>(new Waitable() { + @Override + public Integer actionProduced(Void obj) { + File[] files = getFiles(); + for (int i = 0; i < files.length; i++) { + if (comparator.equals(files[i].getName(), + file)) { + return i; + } + } + return null; + } + + @Override + public String getDescription() { + return "\"" + file + "\" file to be displayed"; + } + + @Override + public String toString() { + return "JFileChooserOperator.findFileIndex.Waitable{description = " + getDescription() + '}'; + } + }); + fileWaiter.setOutput(getOutput().createErrorOutput()); + fileWaiter.setTimeoutsToCloneOf(getTimeouts(), "JFileChooserOperator.WaitListPaintedTimeout"); + try { + return fileWaiter.waitAction(null); + } catch (InterruptedException e) { + throw (new JemmyException("Waiting has been interrupted!")); + } + } + + private int findDirIndex(String dir, StringComparator comparator) { + ComboBoxModel cbModel = getPathCombo().getModel(); + for (int i = cbModel.getSize() - 1; i >= 0; i--) { + if (comparator.equals(((File) cbModel.getElementAt(i)).getName(), + dir)) { + return i; + } + } + return -1; + } + + private int findFileTypeIndex(String fileType, StringComparator comparator) { + ComboBoxModel cbModel = getFileTypesCombo().getModel(); + for (int i = 0; i < cbModel.getSize(); i++) { + if (comparator.equals(((FileFilter) cbModel.getElementAt(i)).getDescription(), + fileType)) { + return i; + } + } + return -1; + } + + /** + * Allows to find a dialog containing JFileChooser. + */ + public static class JFileChooserJDialogFinder implements ComponentChooser { + + TestOut output; + ComponentChooser subChooser; + + /** + * Constructs JFileChooserJDialogFinder. + * + * @param output an output to put searching message into. + */ + public JFileChooserJDialogFinder(TestOut output) { + this.output = output; + subChooser = new JFileChooserFinder(); + } + + @Override + public boolean checkComponent(Component comp) { + if (comp != null + && comp instanceof Window + && comp.isVisible()) { + ComponentSearcher searcher + = new ComponentSearcher((Container) comp); + searcher.setOutput(output); + return searcher.findComponent(subChooser) != null; + } else { + return false; + } + } + + @Override + public String getDescription() { + return "JFileChooser's window"; + } + + @Override + public String toString() { + return "JFileChooserJDialogFinder{" + "subChooser=" + subChooser + '}'; + } + } + + /** + * Checks component type. + */ + public static class JFileChooserFinder extends Finder { + + /** + * Constructs JFileChooserFinder. + * + * @param sf other searching criteria. + */ + public JFileChooserFinder(ComponentChooser sf) { + super(JFileChooser.class, sf); + } + + /** + * Constructs JFileChooserFinder. + */ + public JFileChooserFinder() { + super(JFileChooser.class); + } + } + + private static class ButtonFinder implements ComponentChooser { + + String text; + + public ButtonFinder(String text) { + this.text = text; + } + + @Override + public boolean checkComponent(Component comp) { + return (comp != null + && comp instanceof JButton + && ((JButton) comp).getText() != null + && ((JButton) comp).getText().equals(text)); + } + + @Override + public String getDescription() { + return "\"" + text + "\" button"; + } + + @Override + public String toString() { + return "ButtonFinder{" + "text=" + text + '}'; + } + } + +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JFrameOperator.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JFrameOperator.java new file mode 100644 index 00000000000..02672f85785 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JFrameOperator.java @@ -0,0 +1,443 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.operators; + +import java.awt.Component; +import java.awt.Container; + +import javax.accessibility.AccessibleContext; +import javax.swing.JFrame; +import javax.swing.JLayeredPane; +import javax.swing.JMenuBar; +import javax.swing.JRootPane; + +import org.netbeans.jemmy.ComponentChooser; +import org.netbeans.jemmy.FrameWaiter; +import org.netbeans.jemmy.JemmyProperties; + +/** + *

Timeouts used:
+ * FrameWaiter.WaitFrameTimeout - time to wait frame displayed
+ * FrameWaiter.AfterFrameTimeout - time to sleep after frame has been dispayed + *
. + * + * @see org.netbeans.jemmy.Timeouts + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + * + */ +public class JFrameOperator extends FrameOperator { + + /** + * Constructor. + * + * @param w window + */ + public JFrameOperator(JFrame w) { + super(w); + } + + /** + * Constructs a JFrameOperator object. + * + * @param chooser a component chooser specifying searching criteria. + * @param index an index between appropriate ones. + * @param env an operator to copy environment from. + */ + public JFrameOperator(ComponentChooser chooser, int index, Operator env) { + this((JFrame) waitFrame(new JFrameFinder(chooser), + index, + env.getTimeouts(), + env.getOutput())); + copyEnvironment(env); + } + + /** + * Constructs a JFrameOperator object. + * + * @param chooser a component chooser specifying searching criteria. + * @param index an index between appropriate ones. + */ + public JFrameOperator(ComponentChooser chooser, int index) { + this(chooser, index, Operator.getEnvironmentOperator()); + } + + /** + * Constructs a JFrameOperator object. + * + * @param chooser a component chooser specifying searching criteria. + */ + public JFrameOperator(ComponentChooser chooser) { + this(chooser, 0); + } + + /** + * Constructor. Waits for the frame with "title" subtitle. Constructor can + * be used in complicated cases when output or timeouts should differ from + * default. + * + * @param title a window title + * @param index Ordinal component index. + * @param env an operator to copy environment from. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * + */ + public JFrameOperator(String title, int index, Operator env) { + this(new JFrameFinder(new FrameByTitleFinder(title, + env.getComparator())), + index, env); + } + + /** + * Constructor. Waits for the frame with "title" subtitle. Uses current + * timeouts and output values. + * + * @param title a window title + * @param index Ordinal component index. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @see JemmyProperties#getCurrentTimeouts() + * @see JemmyProperties#getCurrentOutput() + * + */ + public JFrameOperator(String title, int index) { + this(title, index, + ComponentOperator.getEnvironmentOperator()); + } + + /** + * Constructor. Waits for the frame with "title" subtitle. Uses current + * timeouts and output values. + * + * @param title a window title + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @see JemmyProperties#getCurrentTimeouts() + * @see JemmyProperties#getCurrentOutput() + * + */ + public JFrameOperator(String title) { + this(title, 0); + } + + /** + * Constructor. Waits for the index'th frame. Uses current timeout and + * output for waiting and to init operator. + * + * @param index Ordinal component index. + * + */ + public JFrameOperator(int index) { + this((JFrame) waitFrame(new JFrameFinder(), + index, + ComponentOperator.getEnvironmentOperator().getTimeouts(), + ComponentOperator.getEnvironmentOperator().getOutput())); + copyEnvironment(ComponentOperator.getEnvironmentOperator()); + } + + /** + * Constructor. Waits for the first frame. Uses current timeout and output + * for waiting and to init operator. + * + */ + public JFrameOperator() { + this(0); + } + + /** + * Searches an index'th frame. + * + * @param chooser a component chooser specifying searching criteria. + * @param index an index between appropriate ones. + * @return JFrame instance or null if component was not found. + */ + public static JFrame findJFrame(ComponentChooser chooser, int index) { + return (JFrame) FrameWaiter.getFrame(new JFrameFinder(chooser), index); + } + + /** + * Searches a frame. + * + * @param chooser a component chooser specifying searching criteria. + * @return JFrame instance or null if component was not found. + */ + public static JFrame findJFrame(ComponentChooser chooser) { + return findJFrame(chooser, 0); + } + + /** + * Searches an index'th frame by title. + * + * @param title Frame title + * @param ce Compare exactly. If true, text can be a substring of caption. + * @param cc Compare case sensitively. If true, both text and caption are + * @param index an index between appropriate ones. + * @return JFrame instance or null if component was not found. + */ + public static JFrame findJFrame(String title, boolean ce, boolean cc, int index) { + return ((JFrame) FrameWaiter. + getFrame(new JFrameFinder(new FrameByTitleFinder(title, + new DefaultStringComparator(ce, cc))), + index)); + } + + /** + * Searches a frame by title. + * + * @param title Frame title + * @param ce Compare exactly. If true, text can be a substring of caption. + * @param cc Compare case sensitively. If true, both text and caption are + * @return JFrame instance or null if component was not found. + */ + public static JFrame findJFrame(String title, boolean ce, boolean cc) { + return findJFrame(title, ce, cc, 0); + } + + /** + * Waits an index'th frame. + * + * @param chooser a component chooser specifying searching criteria. + * @param index an index between appropriate ones. + * @return JFrame instance or null if component was not found. + * + */ + public static JFrame waitJFrame(ComponentChooser chooser, int index) { + return ((JFrame) waitFrame(new JFrameFinder(chooser), index, + JemmyProperties.getCurrentTimeouts(), + JemmyProperties.getCurrentOutput())); + } + + /** + * Waits a frame. + * + * @param chooser a component chooser specifying searching criteria. + * @return JFrame instance or null if component was not found. + * + */ + public static JFrame waitJFrame(ComponentChooser chooser) { + return waitJFrame(chooser, 0); + } + + /** + * Waits an index'th frame by title. + * + * @param title Frame title + * @param ce Compare exactly. If true, text can be a substring of caption. + * @param cc Compare case sensitively. If true, both text and caption are + * @param index an index between appropriate ones. + * @return JFrame instance or null if component was not found. + * + */ + public static JFrame waitJFrame(String title, boolean ce, boolean cc, int index) { + try { + return ((JFrame) (new FrameWaiter()). + waitFrame(new JFrameFinder(new FrameByTitleFinder(title, + new DefaultStringComparator(ce, cc))), + index)); + } catch (InterruptedException e) { + JemmyProperties.getCurrentOutput().printStackTrace(e); + return null; + } + } + + /** + * Waits a frame by title. + * + * @param title Frame title + * @param ce Compare exactly. If true, text can be a substring of caption. + * @param cc Compare case sensitively. If true, both text and caption are + * @return JFrame instance or null if component was not found. + * + */ + public static JFrame waitJFrame(String title, boolean ce, boolean cc) { + return waitJFrame(title, ce, cc, 0); + } + + //////////////////////////////////////////////////////// + //Mapping // + /** + * Maps {@code JFrame.getAccessibleContext()} through queue + */ + public AccessibleContext getAccessibleContext() { + return (runMapping(new MapAction("getAccessibleContext") { + @Override + public AccessibleContext map() { + return getSource().getAccessibleContext(); + } + })); + } + + /** + * Maps {@code JFrame.getContentPane()} through queue + */ + public Container getContentPane() { + return (runMapping(new MapAction("getContentPane") { + @Override + public Container map() { + return ((JFrame) getSource()).getContentPane(); + } + })); + } + + /** + * Maps {@code JFrame.getDefaultCloseOperation()} through queue + */ + public int getDefaultCloseOperation() { + return (runMapping(new MapIntegerAction("getDefaultCloseOperation") { + @Override + public int map() { + return ((JFrame) getSource()).getDefaultCloseOperation(); + } + })); + } + + /** + * Maps {@code JFrame.getGlassPane()} through queue + */ + public Component getGlassPane() { + return (runMapping(new MapAction("getGlassPane") { + @Override + public Component map() { + return ((JFrame) getSource()).getGlassPane(); + } + })); + } + + /** + * Maps {@code JFrame.getJMenuBar()} through queue + */ + public JMenuBar getJMenuBar() { + return (runMapping(new MapAction("getJMenuBar") { + @Override + public JMenuBar map() { + return ((JFrame) getSource()).getJMenuBar(); + } + })); + } + + /** + * Maps {@code JFrame.getLayeredPane()} through queue + */ + public JLayeredPane getLayeredPane() { + return (runMapping(new MapAction("getLayeredPane") { + @Override + public JLayeredPane map() { + return ((JFrame) getSource()).getLayeredPane(); + } + })); + } + + /** + * Maps {@code JFrame.getRootPane()} through queue + */ + public JRootPane getRootPane() { + return (runMapping(new MapAction("getRootPane") { + @Override + public JRootPane map() { + return ((JFrame) getSource()).getRootPane(); + } + })); + } + + /** + * Maps {@code JFrame.setContentPane(Container)} through queue + */ + public void setContentPane(final Container container) { + runMapping(new MapVoidAction("setContentPane") { + @Override + public void map() { + ((JFrame) getSource()).setContentPane(container); + } + }); + } + + /** + * Maps {@code JFrame.setDefaultCloseOperation(int)} through queue + */ + public void setDefaultCloseOperation(final int i) { + runMapping(new MapVoidAction("setDefaultCloseOperation") { + @Override + public void map() { + ((JFrame) getSource()).setDefaultCloseOperation(i); + } + }); + } + + /** + * Maps {@code JFrame.setGlassPane(Component)} through queue + */ + public void setGlassPane(final Component component) { + runMapping(new MapVoidAction("setGlassPane") { + @Override + public void map() { + ((JFrame) getSource()).setGlassPane(component); + } + }); + } + + /** + * Maps {@code JFrame.setJMenuBar(JMenuBar)} through queue + */ + public void setJMenuBar(final JMenuBar jMenuBar) { + runMapping(new MapVoidAction("setJMenuBar") { + @Override + public void map() { + ((JFrame) getSource()).setJMenuBar(jMenuBar); + } + }); + } + + /** + * Maps {@code JFrame.setLayeredPane(JLayeredPane)} through queue + */ + public void setLayeredPane(final JLayeredPane jLayeredPane) { + runMapping(new MapVoidAction("setLayeredPane") { + @Override + public void map() { + ((JFrame) getSource()).setLayeredPane(jLayeredPane); + } + }); + } + + //End of mapping // + //////////////////////////////////////////////////////// + /** + * Checks component type. + */ + public static class JFrameFinder extends Finder { + + /** + * Constructs JFrameFinder. + * + * @param sf other searching criteria. + */ + public JFrameFinder(ComponentChooser sf) { + super(JFrame.class, sf); + } + + /** + * Constructs JFrameFinder. + */ + public JFrameFinder() { + super(JFrame.class); + } + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JInternalFrameOperator.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JInternalFrameOperator.java new file mode 100644 index 00000000000..9feff1dcaee --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JInternalFrameOperator.java @@ -0,0 +1,1552 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.operators; + +import java.awt.Component; +import java.awt.Container; +import java.awt.Rectangle; +import java.beans.PropertyVetoException; +import java.util.Hashtable; + +import javax.swing.Icon; +import javax.swing.JDesktopPane; +import javax.swing.JInternalFrame; +import javax.swing.JLayeredPane; +import javax.swing.JMenuBar; +import javax.swing.JScrollPane; +import javax.swing.JInternalFrame.JDesktopIcon; +import javax.swing.event.InternalFrameListener; +import javax.swing.plaf.InternalFrameUI; + +import org.netbeans.jemmy.ComponentChooser; +import org.netbeans.jemmy.ComponentSearcher; +import org.netbeans.jemmy.JemmyInputException; +import org.netbeans.jemmy.JemmyProperties; +import org.netbeans.jemmy.Outputable; +import org.netbeans.jemmy.TestOut; +import org.netbeans.jemmy.TimeoutExpiredException; +import org.netbeans.jemmy.Timeoutable; +import org.netbeans.jemmy.Timeouts; +import org.netbeans.jemmy.drivers.DriverManager; +import org.netbeans.jemmy.drivers.FrameDriver; +import org.netbeans.jemmy.drivers.InternalFrameDriver; +import org.netbeans.jemmy.drivers.WindowDriver; +import org.netbeans.jemmy.util.EmptyVisualizer; + +/** + * Class provides necessary functionality to operate with + * javax.swing.JInternalFrame component. + * + * Some methods can throw WrongInternalFrameStateException exception. + * + *

Timeouts used:
+ * ComponentOperator.WaitComponentTimeout - time to wait component displayed + *
+ * ComponentOperator.MouseClickTimeout - time between mouse pressing and + * releasing
+ * AbstractButtonOperator.PushButtonTimeout - time between button pressing and + * releasing
+ * JScrollBarOperator.WholeScrollTimeout - time for the whole scrolling
. + * + * @see org.netbeans.jemmy.Timeouts + * @see WrongInternalFrameStateException + * @author Alexandre Iline (alexandre.iline@oracle.com) + */ +public class JInternalFrameOperator extends JComponentOperator + implements Outputable, Timeoutable { + + /** + * Identifier for a "title" property. + * + * @see #getDump + */ + public static final String TITLE_DPROP = "Title"; + + /** + * Identifier for a "state" property. + * + * @see #getDump + */ + public static final String STATE_DPROP = "State"; + + /** + * Identifier for a "normal" value of "state" property. + * + * @see #getDump + */ + public static final String STATE_NORMAL_DPROP_VALUE = "NORMAL"; + + /** + * Identifier for a "closed" value of "state" property. + * + * @see #getDump + */ + public static final String STATE_CLOSED_DPROP_VALUE = "CLOSED"; + + /** + * Identifier for a "iconified" value of "state" property. + * + * @see #getDump + */ + public static final String STATE_ICONIFIED_DPROP_VALUE = "ICONIFIED"; + + /** + * Identifier for a "maximized" value of "state" property. + * + * @see #getDump + */ + public static final String STATE_MAXIMAZED_DPROP_VALUE = "MAXIMIZED"; + + /** + * Identifier for a "resizable" property. + * + * @see #getDump + */ + public static final String IS_RESIZABLE_DPROP = "Resizable"; + + /** + * Identifier for a "selected" property. + * + * @see #getDump + */ + public static final String IS_SELECTED_DPROP = "Selected"; + + /** + * A minimizing button. + */ + protected JButtonOperator minOper = null; + + /** + * A maximizing button. + */ + protected JButtonOperator maxOper = null; + + /** + * A close button. + */ + protected JButtonOperator closeOper = null; + + /** + * A title operator. + */ + protected ContainerOperator titleOperator = null; + private TestOut output; + private Timeouts timeouts; + private JDesktopIconOperator iconOperator; + + WindowDriver wDriver; + FrameDriver fDriver; + InternalFrameDriver iDriver; + + /** + * Constructor. + * + * @param b a component + */ + public JInternalFrameOperator(JInternalFrame b) { + super(b); + wDriver = DriverManager.getWindowDriver(getClass()); + fDriver = DriverManager.getFrameDriver(getClass()); + iDriver = DriverManager.getInternalFrameDriver(getClass()); + } + + /** + * Constructs a JInternalFrameOperator object. + * + * @param cont a container + * @param chooser a component chooser specifying searching criteria. + * @param index an index between appropriate ones. + */ + public JInternalFrameOperator(ContainerOperator cont, ComponentChooser chooser, int index) { + this((JInternalFrame) cont. + waitSubComponent(new JInternalFrameFinder(chooser), + index)); + copyEnvironment(cont); + } + + /** + * Constructs a JInternalFrameOperator object. + * + * @param cont a container + * @param chooser a component chooser specifying searching criteria. + */ + public JInternalFrameOperator(ContainerOperator cont, ComponentChooser chooser) { + this(cont, chooser, 0); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont a container + * @param text Button text. + * @param index Ordinal component index. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * + */ + public JInternalFrameOperator(ContainerOperator cont, String text, int index) { + this(findOne(cont, text, index)); + copyEnvironment(cont); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont a container + * @param text Button text. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * + */ + public JInternalFrameOperator(ContainerOperator cont, String text) { + this(cont, text, 0); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont a container + * @param index Ordinal component index. + * + */ + public JInternalFrameOperator(ContainerOperator cont, int index) { + this((JInternalFrame) waitComponent(cont, + new JInternalFrameFinder(), + index)); + copyEnvironment(cont); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont a container + * + */ + public JInternalFrameOperator(ContainerOperator cont) { + this(cont, 0); + } + + /** + * Searches JInternalframe in container. + * + * @param cont Container to search component in. + * @param chooser a component chooser specifying searching criteria. + * @param index Ordinal component index. + * @return JInternalframe instance or null if component was not found. + */ + public static JInternalFrame findJInternalFrame(Container cont, ComponentChooser chooser, int index) { + Component res = findComponent(cont, new JInternalFrameFinder(chooser), index); + if (res instanceof JInternalFrame) { + return (JInternalFrame) res; + } else if (res instanceof JInternalFrame.JDesktopIcon) { + return ((JInternalFrame.JDesktopIcon) res).getInternalFrame(); + } else { + return null; + } + } + + /** + * Searches JInternalframe in container. + * + * @param cont Container to search component in. + * @param chooser a component chooser specifying searching criteria. + * @return JInternalframe instance or null if component was not found. + */ + public static JInternalFrame findJInternalFrame(Container cont, ComponentChooser chooser) { + return findJInternalFrame(cont, chooser, 0); + } + + /** + * Searches JInternalframe by title. + * + * @param cont Container to search component in. + * @param text Component text. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @param index Ordinal component index. + * @return JInternalframe instance or null if component was not found. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + */ + public static JInternalFrame findJInternalFrame(Container cont, String text, boolean ce, boolean ccs, int index) { + return (findJInternalFrame(cont, + new JInternalFrameByTitleFinder(text, + new DefaultStringComparator(ce, ccs)), + index)); + } + + /** + * Searches JInternalframe by title. + * + * @param cont Container to search component in. + * @param text Component text. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @return JInternalframe instance or null if component was not found. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + */ + public static JInternalFrame findJInternalFrame(Container cont, String text, boolean ce, boolean ccs) { + return findJInternalFrame(cont, text, ce, ccs, 0); + } + + /** + * Searches JInternalFrame object which component lies on. + * + * @param comp Component to find JInternalFrame under. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @return JInternalFrame instance or null if component was not found. + */ + public static JInternalFrame findJInternalFrameUnder(Component comp, ComponentChooser chooser) { + return (JInternalFrame) findContainerUnder(comp, new JInternalFrameFinder(chooser)); + } + + /** + * Searches JInternalFrame object which component lies on. + * + * @param comp Component to find JInternalFrame under. + * @return JInternalFrame instance or null if component was not found. + */ + public static JInternalFrame findJInternalFrameUnder(Component comp) { + return findJInternalFrameUnder(comp, new JInternalFrameFinder()); + } + + /** + * Waits JInternalframe in container. + * + * @param cont Container to search component in. + * @param chooser a component chooser specifying searching criteria. + * @param index Ordinal component index. + * @return JInternalframe instance. + * + */ + public static JInternalFrame waitJInternalFrame(final Container cont, final ComponentChooser chooser, final int index) { + Component res = waitComponent(cont, new JInternalFrameFinder(chooser), index); + if (res instanceof JInternalFrame) { + return (JInternalFrame) res; + } else if (res instanceof JInternalFrame.JDesktopIcon) { + return ((JInternalFrame.JDesktopIcon) res).getInternalFrame(); + } else { + throw (new TimeoutExpiredException(chooser.getDescription())); + } + } + + /** + * Waits JInternalframe in container. + * + * @param cont Container to search component in. + * @param chooser a component chooser specifying searching criteria. + * @return JInternalframe instance. + * + */ + public static JInternalFrame waitJInternalFrame(Container cont, ComponentChooser chooser) { + return waitJInternalFrame(cont, chooser, 0); + } + + /** + * Waits JInternalframe by title. + * + * @param cont Container to search component in. + * @param text Component text. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @param index Ordinal component index. + * @return JInternalframe instance. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * + */ + public static JInternalFrame waitJInternalFrame(Container cont, String text, boolean ce, boolean ccs, int index) { + return (waitJInternalFrame(cont, + new JInternalFrameByTitleFinder(text, + new DefaultStringComparator(ce, ccs)), + index)); + } + + /** + * Waits JInternalframe by title. + * + * @param cont Container to search component in. + * @param text Component text. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @return JInternalframe instance. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * + */ + public static JInternalFrame waitJInternalFrame(Container cont, String text, boolean ce, boolean ccs) { + return waitJInternalFrame(cont, text, ce, ccs, 0); + } + + @Override + public void setOutput(TestOut out) { + output = out; + super.setOutput(output.createErrorOutput()); + } + + @Override + public TestOut getOutput() { + return output; + } + + @Override + public void setTimeouts(Timeouts times) { + timeouts = times; + super.setTimeouts(timeouts); + } + + @Override + public Timeouts getTimeouts() { + return timeouts; + } + + /** + * Iconifies frame. Note: frame should not be iconified and should be + * iconifiable. + * + * @throws WrongInternalFrameStateException + * + */ + public void iconify() { + output.printLine("Iconify JInternalFrame\n : " + toStringSource()); + output.printGolden("Iconify JInternalFrame \"" + getTitle() + "\""); + checkIconified(false); + makeComponentVisible(); + fDriver.iconify(this); + if (getVerification()) { + waitIcon(true); + } + } + + /** + * Deiconifies frame. Note: frame should be iconified. + * + * @throws WrongInternalFrameStateException + * + */ + public void deiconify() { + output.printLine("Deiconify JInternalFrame\n : " + toStringSource()); + output.printGolden("Deiconify JInternalFrame \"" + getTitle() + "\""); + checkIconified(true); + fDriver.deiconify(this); + if (getVerification()) { + waitIcon(false); + } + } + + /** + * Maximizes frame. Note: frame should not be iconified. + * + * @throws WrongInternalFrameStateException + */ + public void maximize() { + output.printLine("Maximize JInternalFrame\n : " + toStringSource()); + output.printGolden("Maximize JInternalFrame \"" + getTitle() + "\""); + checkIconified(false); + makeComponentVisible(); + fDriver.maximize(this); + if (getVerification()) { + waitMaximum(true); + } + } + + /** + * Demaximizes frame. Note: frame should not be iconified. + * + * @throws WrongInternalFrameStateException + */ + public void demaximize() { + output.printLine("Demaximize JInternalFrame\n : " + toStringSource()); + output.printGolden("Demaximize JInternalFrame \"" + getTitle() + "\""); + checkIconified(false); + makeComponentVisible(); + fDriver.demaximize(this); + if (getVerification()) { + waitMaximum(false); + } + } + + /** + * Moves frame to new location. Note: frame should not be iconified. + * + * @param x X coordinate of a new frame location. + * @param y Y coordinate of a new frame location. + * @throws WrongInternalFrameStateException + */ + public void move(int x, int y) { + checkIconified(false); + output.printLine("Move JInternalFrame to (" + + Integer.toString(x) + "," + + Integer.toString(y) + ")" + + " position\n : " + toStringSource()); + output.printGolden("Move " + getTitle() + + " JInternalFrame to (" + + Integer.toString(x) + "," + + Integer.toString(y) + ")" + + " position"); + checkIconified(false); + wDriver.move(this, x, y); + } + + /** + * Resizes frame. Note: frame should not be iconified. + * + * @param width New frame width. + * @param height New frame height. + * @throws WrongInternalFrameStateException + */ + public void resize(int width, int height) { + output.printLine("Resize JInternalFrame to (" + + Integer.toString(width) + "," + + Integer.toString(height) + ")" + + " size\n : " + toStringSource()); + output.printGolden("Resize " + getTitle() + + " JInternalFrame to (" + + Integer.toString(width) + "," + + Integer.toString(height) + ")" + + " size"); + checkIconified(false); + wDriver.resize(this, width, height); + } + + /** + * Activates frame. Note: frame should not be iconified. + * + * @throws WrongInternalFrameStateException + */ + public void activate() { + checkIconified(false); + wDriver.activate(this); + } + + /** + * Closes the frame. + */ + public void close() { + checkIconified(false); + wDriver.requestClose(this); + } + + /** + * Scrolls to internal frame's rectangle. + * + * @param x Horizontal rectangle coordinate + * @param y Vertical rectangle coordinate + * @param width rectangle width + * @param height rectangle height + * + */ + public void scrollToRectangle(int x, int y, int width, int height) { + output.printTrace("Scroll desktop pane to make \"" + getTitle() + "\" internal frame visible"); + output.printGolden("Scroll desktop pane to make \"" + getTitle() + "\" internal frame visible"); + makeComponentVisible(); + //try to find JScrollPane under. + JScrollPane scroll; + if (isIcon()) { + scroll + = (JScrollPane) iconOperator.getContainer(new JScrollPaneOperator.JScrollPaneFinder(ComponentSearcher. + getTrueChooser("JScrollPane"))); + } else { + scroll + = (JScrollPane) getContainer(new JScrollPaneOperator.JScrollPaneFinder(ComponentSearcher. + getTrueChooser("JScrollPane"))); + } + if (scroll == null) { + return; + } + JScrollPaneOperator scroller = new JScrollPaneOperator(scroll); + scroller.copyEnvironment(this); + scroller.setVisualizer(new EmptyVisualizer()); + scroller.scrollToComponentRectangle(isIcon() ? iconOperator.getSource() : getSource(), + x, y, width, height); + } + + /** + * Scrolls to internal frame's rectangle. + * + * @param rect a rectangle to scroll to. + */ + public void scrollToRectangle(Rectangle rect) { + scrollToRectangle(rect.x, rect.y, rect.width, rect.height); + } + + /** + * Scrolls to internal frame. + * + */ + public void scrollToFrame() { + if (isIcon()) { + scrollToRectangle(0, 0, iconOperator.getWidth(), iconOperator.getHeight()); + } else { + scrollToRectangle(0, 0, getWidth(), getHeight()); + } + } + + /** + * Waits for a minimize button inside the title pane. + * + * @return a button operator + */ + public JButtonOperator getMinimizeButton() { + initOperators(); + return minOper; + } + + /** + * Waits for a maximize button inside the title pane. + * + * @return a button operator + */ + public JButtonOperator getMaximizeButton() { + initOperators(); + return maxOper; + } + + /** + * Waits for a close button inside the title pane. + * + * @return a button operator + */ + public JButtonOperator getCloseButton() { + initOperators(); + return closeOper; + } + + /** + * Waits for the title pane. + * + * @return a button operator + */ + public ContainerOperator getTitleOperator() { + initOperators(); + return titleOperator; + } + + /** + * Creates an operator for an desktop icon. + * + * @return an icon operator. + */ + public JDesktopIconOperator getIconOperator() { + initOperators(); + return iconOperator; + } + + /** + * Waits for the frame to be iconified or deiconified. + * + * @param icon whether the frame needs to be iconified. + */ + public void waitIcon(final boolean icon) { + waitState(new ComponentChooser() { + @Override + public boolean checkComponent(Component comp) { + return ((JInternalFrame) comp).isIcon() == icon; + } + + @Override + public String getDescription() { + return "Iconified JInternalFrame"; + } + + @Override + public String toString() { + return "JInternalFrameOperator.waitIcon.ComponentChooser{description = " + getDescription() + '}'; + } + }); + } + + /** + * Waits for the frame to be maximized or demaximized. + * + * @param maximum whether the frame needs to be maximized. + */ + public void waitMaximum(final boolean maximum) { + waitState(new ComponentChooser() { + @Override + public boolean checkComponent(Component comp) { + return ((JInternalFrame) comp).isMaximum() == maximum; + } + + @Override + public String getDescription() { + return "Maximizied JInternalFrame"; + } + + @Override + public String toString() { + return "JInternalFrameOperator.waitMaximum.ComponentChooser{description = " + getDescription() + '}'; + } + }); + } + + /** + * Returns information about component. + */ + @Override + public Hashtable getDump() { + Hashtable result = super.getDump(); + result.put(TITLE_DPROP, ((JInternalFrame) getSource()).getTitle()); + String state = STATE_NORMAL_DPROP_VALUE; + if (((JInternalFrame) getSource()).isClosed()) { + state = STATE_CLOSED_DPROP_VALUE; + } else if (((JInternalFrame) getSource()).isIcon()) { + state = STATE_ICONIFIED_DPROP_VALUE; + } else if (((JInternalFrame) getSource()).isMaximum()) { + state = STATE_MAXIMAZED_DPROP_VALUE; + } + result.put(STATE_DPROP, state); + result.put(IS_RESIZABLE_DPROP, ((JInternalFrame) getSource()).isResizable() ? "true" : "false"); + result.put(IS_SELECTED_DPROP, ((JInternalFrame) getSource()).isSelected() ? "true" : "false"); + return result; + } + + //////////////////////////////////////////////////////// + //Mapping // + /** + * Maps + * {@code JInternalFrame.addInternalFrameListener(InternalFrameListener)} + * through queue + */ + public void addInternalFrameListener(final InternalFrameListener internalFrameListener) { + runMapping(new MapVoidAction("addInternalFrameListener") { + @Override + public void map() { + ((JInternalFrame) getSource()).addInternalFrameListener(internalFrameListener); + } + }); + } + + /** + * Maps {@code JInternalFrame.dispose()} through queue + */ + public void dispose() { + runMapping(new MapVoidAction("dispose") { + @Override + public void map() { + ((JInternalFrame) getSource()).dispose(); + } + }); + } + + /** + * Maps {@code JInternalFrame.getContentPane()} through queue + */ + public Container getContentPane() { + return (runMapping(new MapAction("getContentPane") { + @Override + public Container map() { + return ((JInternalFrame) getSource()).getContentPane(); + } + })); + } + + /** + * Maps {@code JInternalFrame.getDefaultCloseOperation()} through queue + */ + public int getDefaultCloseOperation() { + return (runMapping(new MapIntegerAction("getDefaultCloseOperation") { + @Override + public int map() { + return ((JInternalFrame) getSource()).getDefaultCloseOperation(); + } + })); + } + + /** + * Maps {@code JInternalFrame.getDesktopIcon()} through queue + */ + public JDesktopIcon getDesktopIcon() { + return (runMapping(new MapAction("getDesktopIcon") { + @Override + public JDesktopIcon map() { + return ((JInternalFrame) getSource()).getDesktopIcon(); + } + })); + } + + /** + * Maps {@code JInternalFrame.getDesktopPane()} through queue + */ + public JDesktopPane getDesktopPane() { + return (runMapping(new MapAction("getDesktopPane") { + @Override + public JDesktopPane map() { + return ((JInternalFrame) getSource()).getDesktopPane(); + } + })); + } + + /** + * Maps {@code JInternalFrame.getFrameIcon()} through queue + */ + public Icon getFrameIcon() { + return (runMapping(new MapAction("getFrameIcon") { + @Override + public Icon map() { + return ((JInternalFrame) getSource()).getFrameIcon(); + } + })); + } + + /** + * Maps {@code JInternalFrame.getGlassPane()} through queue + */ + public Component getGlassPane() { + return (runMapping(new MapAction("getGlassPane") { + @Override + public Component map() { + return ((JInternalFrame) getSource()).getGlassPane(); + } + })); + } + + /** + * Maps {@code JInternalFrame.getJMenuBar()} through queue + */ + public JMenuBar getJMenuBar() { + return (runMapping(new MapAction("getJMenuBar") { + @Override + public JMenuBar map() { + return ((JInternalFrame) getSource()).getJMenuBar(); + } + })); + } + + /** + * Maps {@code JInternalFrame.getLayer()} through queue + */ + public int getLayer() { + return (runMapping(new MapIntegerAction("getLayer") { + @Override + public int map() { + return ((JInternalFrame) getSource()).getLayer(); + } + })); + } + + /** + * Maps {@code JInternalFrame.getLayeredPane()} through queue + */ + public JLayeredPane getLayeredPane() { + return (runMapping(new MapAction("getLayeredPane") { + @Override + public JLayeredPane map() { + return ((JInternalFrame) getSource()).getLayeredPane(); + } + })); + } + + /** + * Maps {@code JInternalFrame.getTitle()} through queue + */ + public String getTitle() { + return (runMapping(new MapAction("getTitle") { + @Override + public String map() { + return ((JInternalFrame) getSource()).getTitle(); + } + })); + } + + /** + * Maps {@code JInternalFrame.getUI()} through queue + */ + public InternalFrameUI getUI() { + return (runMapping(new MapAction("getUI") { + @Override + public InternalFrameUI map() { + return ((JInternalFrame) getSource()).getUI(); + } + })); + } + + /** + * Maps {@code JInternalFrame.getWarningString()} through queue + */ + public String getWarningString() { + return (runMapping(new MapAction("getWarningString") { + @Override + public String map() { + return ((JInternalFrame) getSource()).getWarningString(); + } + })); + } + + /** + * Maps {@code JInternalFrame.isClosable()} through queue + */ + public boolean isClosable() { + return (runMapping(new MapBooleanAction("isClosable") { + @Override + public boolean map() { + return ((JInternalFrame) getSource()).isClosable(); + } + })); + } + + /** + * Maps {@code JInternalFrame.isClosed()} through queue + */ + public boolean isClosed() { + return (runMapping(new MapBooleanAction("isClosed") { + @Override + public boolean map() { + return ((JInternalFrame) getSource()).isClosed(); + } + })); + } + + /** + * Maps {@code JInternalFrame.isIcon()} through queue + */ + public boolean isIcon() { + return (runMapping(new MapBooleanAction("isIcon") { + @Override + public boolean map() { + return ((JInternalFrame) getSource()).isIcon(); + } + })); + } + + /** + * Maps {@code JInternalFrame.isIconifiable()} through queue + */ + public boolean isIconifiable() { + return (runMapping(new MapBooleanAction("isIconifiable") { + @Override + public boolean map() { + return ((JInternalFrame) getSource()).isIconifiable(); + } + })); + } + + /** + * Maps {@code JInternalFrame.isMaximizable()} through queue + */ + public boolean isMaximizable() { + return (runMapping(new MapBooleanAction("isMaximizable") { + @Override + public boolean map() { + return ((JInternalFrame) getSource()).isMaximizable(); + } + })); + } + + /** + * Maps {@code JInternalFrame.isMaximum()} through queue + */ + public boolean isMaximum() { + return (runMapping(new MapBooleanAction("isMaximum") { + @Override + public boolean map() { + return ((JInternalFrame) getSource()).isMaximum(); + } + })); + } + + /** + * Maps {@code JInternalFrame.isResizable()} through queue + */ + public boolean isResizable() { + return (runMapping(new MapBooleanAction("isResizable") { + @Override + public boolean map() { + return ((JInternalFrame) getSource()).isResizable(); + } + })); + } + + /** + * Maps {@code JInternalFrame.isSelected()} through queue + */ + public boolean isSelected() { + return (runMapping(new MapBooleanAction("isSelected") { + @Override + public boolean map() { + return ((JInternalFrame) getSource()).isSelected(); + } + })); + } + + /** + * Maps {@code JInternalFrame.moveToBack()} through queue + */ + public void moveToBack() { + runMapping(new MapVoidAction("moveToBack") { + @Override + public void map() { + ((JInternalFrame) getSource()).moveToBack(); + } + }); + } + + /** + * Maps {@code JInternalFrame.moveToFront()} through queue + */ + public void moveToFront() { + runMapping(new MapVoidAction("moveToFront") { + @Override + public void map() { + ((JInternalFrame) getSource()).moveToFront(); + } + }); + } + + /** + * Maps {@code JInternalFrame.pack()} through queue + */ + public void pack() { + runMapping(new MapVoidAction("pack") { + @Override + public void map() { + ((JInternalFrame) getSource()).pack(); + } + }); + } + + /** + * Maps + * {@code JInternalFrame.removeInternalFrameListener(InternalFrameListener)} + * through queue + */ + public void removeInternalFrameListener(final InternalFrameListener internalFrameListener) { + runMapping(new MapVoidAction("removeInternalFrameListener") { + @Override + public void map() { + ((JInternalFrame) getSource()).removeInternalFrameListener(internalFrameListener); + } + }); + } + + /** + * Maps {@code JInternalFrame.setClosable(boolean)} through queue + */ + public void setClosable(final boolean b) { + runMapping(new MapVoidAction("setClosable") { + @Override + public void map() { + ((JInternalFrame) getSource()).setClosable(b); + } + }); + } + + /** + * Maps {@code JInternalFrame.setClosed(boolean)} through queue + */ + public void setClosed(final boolean b) { + runMapping(new MapVoidAction("setClosed") { + @Override + public void map() throws PropertyVetoException { + ((JInternalFrame) getSource()).setClosed(b); + } + }); + } + + /** + * Maps {@code JInternalFrame.setContentPane(Container)} through queue + */ + public void setContentPane(final Container container) { + runMapping(new MapVoidAction("setContentPane") { + @Override + public void map() { + ((JInternalFrame) getSource()).setContentPane(container); + } + }); + } + + /** + * Maps {@code JInternalFrame.setDefaultCloseOperation(int)} through queue + */ + public void setDefaultCloseOperation(final int i) { + runMapping(new MapVoidAction("setDefaultCloseOperation") { + @Override + public void map() { + ((JInternalFrame) getSource()).setDefaultCloseOperation(i); + } + }); + } + + /** + * Maps {@code JInternalFrame.setDesktopIcon(JDesktopIcon)} through queue + */ + public void setDesktopIcon(final JDesktopIcon jDesktopIcon) { + runMapping(new MapVoidAction("setDesktopIcon") { + @Override + public void map() { + ((JInternalFrame) getSource()).setDesktopIcon(jDesktopIcon); + } + }); + } + + /** + * Maps {@code JInternalFrame.setFrameIcon(Icon)} through queue + */ + public void setFrameIcon(final Icon icon) { + runMapping(new MapVoidAction("setFrameIcon") { + @Override + public void map() { + ((JInternalFrame) getSource()).setFrameIcon(icon); + } + }); + } + + /** + * Maps {@code JInternalFrame.setGlassPane(Component)} through queue + */ + public void setGlassPane(final Component component) { + runMapping(new MapVoidAction("setGlassPane") { + @Override + public void map() { + ((JInternalFrame) getSource()).setGlassPane(component); + } + }); + } + + /** + * Maps {@code JInternalFrame.setIcon(boolean)} through queue + */ + public void setIcon(final boolean b) { + runMapping(new MapVoidAction("setIcon") { + @Override + public void map() throws PropertyVetoException { + ((JInternalFrame) getSource()).setIcon(b); + } + }); + } + + /** + * Maps {@code JInternalFrame.setIconifiable(boolean)} through queue + */ + public void setIconifiable(final boolean b) { + runMapping(new MapVoidAction("setIconifiable") { + @Override + public void map() { + ((JInternalFrame) getSource()).setIconifiable(b); + } + }); + } + + /** + * Maps {@code JInternalFrame.setJMenuBar(JMenuBar)} through queue + */ + public void setJMenuBar(final JMenuBar jMenuBar) { + runMapping(new MapVoidAction("setJMenuBar") { + @Override + public void map() { + ((JInternalFrame) getSource()).setJMenuBar(jMenuBar); + } + }); + } + + /** + * Maps {@code JInternalFrame.setLayer(Integer)} through queue + */ + public void setLayer(final Integer integer) { + runMapping(new MapVoidAction("setLayer") { + @Override + public void map() { + ((JInternalFrame) getSource()).setLayer(integer); + } + }); + } + + /** + * Maps {@code JInternalFrame.setLayeredPane(JLayeredPane)} through queue + */ + public void setLayeredPane(final JLayeredPane jLayeredPane) { + runMapping(new MapVoidAction("setLayeredPane") { + @Override + public void map() { + ((JInternalFrame) getSource()).setLayeredPane(jLayeredPane); + } + }); + } + + /** + * Maps {@code JInternalFrame.setMaximizable(boolean)} through queue + */ + public void setMaximizable(final boolean b) { + runMapping(new MapVoidAction("setMaximizable") { + @Override + public void map() { + ((JInternalFrame) getSource()).setMaximizable(b); + } + }); + } + + /** + * Maps {@code JInternalFrame.setMaximum(boolean)} through queue + */ + public void setMaximum(final boolean b) { + runMapping(new MapVoidAction("setMaximum") { + @Override + public void map() throws PropertyVetoException { + ((JInternalFrame) getSource()).setMaximum(b); + } + }); + } + + /** + * Maps {@code JInternalFrame.setResizable(boolean)} through queue + */ + public void setResizable(final boolean b) { + runMapping(new MapVoidAction("setResizable") { + @Override + public void map() { + ((JInternalFrame) getSource()).setResizable(b); + } + }); + } + + /** + * Maps {@code JInternalFrame.setSelected(boolean)} through queue + */ + public void setSelected(final boolean b) { + runMapping(new MapVoidAction("setSelected") { + @Override + public void map() throws PropertyVetoException { + ((JInternalFrame) getSource()).setSelected(b); + } + }); + } + + /** + * Maps {@code JInternalFrame.setTitle(String)} through queue + */ + public void setTitle(final String string) { + runMapping(new MapVoidAction("setTitle") { + @Override + public void map() { + ((JInternalFrame) getSource()).setTitle(string); + } + }); + } + + /** + * Maps {@code JInternalFrame.setUI(InternalFrameUI)} through queue + */ + public void setUI(final InternalFrameUI internalFrameUI) { + runMapping(new MapVoidAction("setUI") { + @Override + public void map() { + ((JInternalFrame) getSource()).setUI(internalFrameUI); + } + }); + } + + /** + * Maps {@code JInternalFrame.toBack()} through queue + */ + public void toBack() { + runMapping(new MapVoidAction("toBack") { + @Override + public void map() { + ((JInternalFrame) getSource()).toBack(); + } + }); + } + + /** + * Maps {@code JInternalFrame.toFront()} through queue + */ + public void toFront() { + runMapping(new MapVoidAction("toFront") { + @Override + public void map() { + ((JInternalFrame) getSource()).toFront(); + } + }); + } + + //End of mapping // + //////////////////////////////////////////////////////// + /** + * Uses InternalframeDriver to get a title pane. + * + * @return a title pane. + */ + protected Container findTitlePane() { + return (Container) iDriver.getTitlePane(this); + } + + /** + * Initiaites suboperators. + */ + protected void initOperators() { + iconOperator = new JDesktopIconOperator(((JInternalFrame) getSource()).getDesktopIcon()); + iconOperator.copyEnvironment(this); + Container titlePane = findTitlePane(); + if (!isIcon() && titlePane != null) { + if (titleOperator == null) { + titleOperator = new ContainerOperator<>(titlePane); + int bttCount = 0; + if (getContainer(new ComponentChooser() { + @Override + public boolean checkComponent(Component comp) { + return comp instanceof JDesktopPane; + } + + @Override + public String getDescription() { + return "Desctop pane"; + } + + @Override + public String toString() { + return "JInternalFrameOperator.initOperators.ComponentChooser{description = " + getDescription() + '}'; + } + }) != null) { + minOper = new JButtonOperator(titleOperator, bttCount); + bttCount++; + if (((JInternalFrame) getSource()).isMaximizable()) { + maxOper = new JButtonOperator(titleOperator, bttCount); + bttCount++; + } else { + maxOper = null; + } + } else { + minOper = null; + maxOper = null; + } + if (isClosable()) { + closeOper = new JButtonOperator(titleOperator, bttCount); + } else { + closeOper = null; + } + } + } else { + titleOperator = null; + minOper = null; + maxOper = null; + closeOper = null; + } + } + + //throw exception if state is wrong + private void checkIconified(boolean shouldBeIconified) { + if (shouldBeIconified && !isIcon() + || !shouldBeIconified && isIcon()) { + throw (new WrongInternalFrameStateException("JInternal frame should " + + (shouldBeIconified ? "" : "not") + + " be iconified to produce this operation", + getSource())); + } + } + + private static JInternalFrame findOne(ContainerOperator cont, String text, int index) { + Component source = waitComponent(cont, + new JInternalFrameByTitleFinder(text, + cont.getComparator()), + index); + if (source instanceof JInternalFrame) { + return (JInternalFrame) source; + } else if (source instanceof JInternalFrame.JDesktopIcon) { + return ((JInternalFrame.JDesktopIcon) source).getInternalFrame(); + } else { + throw (new TimeoutExpiredException("No internal frame was found")); + } + } + + /** + * Exception can be throwht if as a result of an attempt to produce + * operation for the frame in incorrect state. Like activate iconified + * frame, for example. + */ + public static class WrongInternalFrameStateException extends JemmyInputException { + + private static final long serialVersionUID = 42L; + + /** + * Constructs a JInternalFrameOperator$WrongInternalFrameStateException + * object. + * + * @param message an exception message. + * @param comp an internal frame. + */ + public WrongInternalFrameStateException(String message, Component comp) { + super(message, comp); + } + } + + /** + * Allows to find component by title. + */ + public static class JInternalFrameByTitleFinder implements ComponentChooser { + + String label; + StringComparator comparator; + + /** + * Constructs JInternalFrameByTitleFinder. + * + * @param lb a text pattern + * @param comparator specifies string comparision algorithm. + */ + public JInternalFrameByTitleFinder(String lb, StringComparator comparator) { + label = lb; + this.comparator = comparator; + } + + /** + * Constructs JInternalFrameByTitleFinder. + * + * @param lb a text pattern + */ + public JInternalFrameByTitleFinder(String lb) { + this(lb, Operator.getDefaultStringComparator()); + } + + @Override + public boolean checkComponent(Component comp) { + if (comp instanceof JInternalFrame || comp instanceof JInternalFrame.JDesktopIcon) { + JInternalFrame frame = null; + if (comp instanceof JInternalFrame) { + frame = (JInternalFrame) comp; + } else { + JDesktopIconOperator io = new JDesktopIconOperator((JInternalFrame.JDesktopIcon) comp); + frame = io.getInternalFrame(); + } + if (frame.getTitle() != null) { + return (comparator.equals(frame.getTitle(), + label)); + } + } + return false; + } + + @Override + public String getDescription() { + return "JInternalFrame with title \"" + label + "\""; + } + + @Override + public String toString() { + return "JInternalFrameByTitleFinder{" + "label=" + label + ", comparator=" + comparator + '}'; + } + } + + /** + * Class to operate with javax.swing.JInternalFrame.JDesktopIconOperator + * component. + */ + public static class JDesktopIconOperator extends JComponentOperator + implements Outputable, Timeoutable { + + private TestOut output; + private Timeouts timeouts; + + /** + * Constructs JDesktopIconOperator. + * + * @param b a component + */ + public JDesktopIconOperator(JInternalFrame.JDesktopIcon b) { + super(b); + setOutput(JemmyProperties.getCurrentOutput()); + setTimeouts(JemmyProperties.getCurrentTimeouts()); + } + + @Override + public void setOutput(TestOut out) { + output = out; + super.setOutput(output.createErrorOutput()); + } + + @Override + public TestOut getOutput() { + return output; + } + + @Override + public void setTimeouts(Timeouts times) { + timeouts = times; + super.setTimeouts(timeouts); + } + + @Override + public Timeouts getTimeouts() { + return timeouts; + } + + /** + * Creates an operator for the correspondent intenal frame. + * + * @return an operator. + */ + public JInternalFrame getInternalFrame() { + return ((JInternalFrame) getEventDispatcher(). + invokeExistingMethod("getInternalFrame", + null, + null, + output)); + } + + /** + * Pushs the deiconifying button. + */ + public void pushButton() { + new JButtonOperator(this).push(); + } + } + + /** + * Checks component type. + */ + public static class JInternalFrameFinder implements ComponentChooser { + + ComponentChooser sf = null; + + /** + * Constructs JInternalFrameFinder. + * + * @param sf other searching criteria. + */ + public JInternalFrameFinder(ComponentChooser sf) { + this.sf = sf; + } + + /** + * Constructs JInternalFrameFinder. + */ + public JInternalFrameFinder() { + this(ComponentSearcher.getTrueChooser("JInternalFrame or JInternalFrame.JDesktopIcon")); + } + + @Override + public boolean checkComponent(Component comp) { + return ((comp instanceof JInternalFrame || comp instanceof JInternalFrame.JDesktopIcon) + && sf.checkComponent(comp)); + } + + @Override + public String getDescription() { + return sf.getDescription(); + } + + @Override + public String toString() { + return "JInternalFrameFinder{" + "sf=" + sf + '}'; + } + } + +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JLabelOperator.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JLabelOperator.java new file mode 100644 index 00000000000..c3c0005c2e0 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JLabelOperator.java @@ -0,0 +1,629 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.operators; + +import java.awt.Component; +import java.awt.Container; +import java.util.Hashtable; + +import javax.swing.Icon; +import javax.swing.JLabel; +import javax.swing.plaf.LabelUI; + +import org.netbeans.jemmy.ComponentChooser; + +/** + *

Timeouts used:
+ * ComponentOperator.WaitComponentTimeout - time to wait component displayed + *
+ * ComponentOperator.WaitStateTimeout - time to wait for text
. + * + * @see org.netbeans.jemmy.Timeouts + * @author Alexandre Iline (alexandre.iline@oracle.com) + * + */ +public class JLabelOperator extends JComponentOperator { + + /** + * Identifier for a "text" property. + * + * @see #getDump + */ + public static final String TEXT_DPROP = "Text"; + + /** + * Constructor. + * + * @param b a component + */ + public JLabelOperator(JLabel b) { + super(b); + } + + /** + * Constructs a JLabelOperator object. + * + * @param cont a container + * @param chooser a component chooser specifying searching criteria. + * @param index an index between appropriate ones. + */ + public JLabelOperator(ContainerOperator cont, ComponentChooser chooser, int index) { + this((JLabel) cont. + waitSubComponent(new JLabelFinder(chooser), + index)); + copyEnvironment(cont); + } + + /** + * Constructs a JLabelOperator object. + * + * @param cont a container + * @param chooser a component chooser specifying searching criteria. + */ + public JLabelOperator(ContainerOperator cont, ComponentChooser chooser) { + this(cont, chooser, 0); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont a container + * @param text Button text. + * @param index Ordinal component index. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * + */ + public JLabelOperator(ContainerOperator cont, String text, int index) { + this((JLabel) waitComponent(cont, + new JLabelByLabelFinder(text, + cont.getComparator()), + index)); + copyEnvironment(cont); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont a container + * @param text Button text. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * + */ + public JLabelOperator(ContainerOperator cont, String text) { + this(cont, text, 0); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont a container + * @param index Ordinal component index. + * + */ + public JLabelOperator(ContainerOperator cont, int index) { + this((JLabel) waitComponent(cont, + new JLabelFinder(), + index)); + copyEnvironment(cont); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont a container + * + */ + public JLabelOperator(ContainerOperator cont) { + this(cont, 0); + } + + /** + * Searches JLabel in container. + * + * @param cont Container to search component in. + * @param chooser a component chooser specifying searching criteria. + * @param index Ordinal component index. + * @return JLabel instance or null if component was not found. + */ + public static JLabel findJLabel(Container cont, ComponentChooser chooser, int index) { + return (JLabel) findComponent(cont, new JLabelFinder(chooser), index); + } + + /** + * Searches JLabel in container. + * + * @param cont Container to search component in. + * @param chooser a component chooser specifying searching criteria. + * @return JLabel instance or null if component was not found. + */ + public static JLabel findJLabel(Container cont, ComponentChooser chooser) { + return findJLabel(cont, chooser, 0); + } + + /** + * Searches JLabel by text. + * + * @param cont Container to search component in. + * @param text Component text. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @param index Ordinal component index. + * @return JLabel instance or null if component was not found. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + */ + public static JLabel findJLabel(Container cont, String text, boolean ce, boolean ccs, int index) { + return findJLabel(cont, new JLabelByLabelFinder(text, new DefaultStringComparator(ce, ccs)), index); + } + + /** + * Searches JLabel by text. + * + * @param cont Container to search component in. + * @param text Component text. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @return JLabel instance or null if component was not found. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + */ + public static JLabel findJLabel(Container cont, String text, boolean ce, boolean ccs) { + return findJLabel(cont, text, ce, ccs, 0); + } + + /** + * Waits JLabel in container. + * + * @param cont Container to search component in. + * @param chooser a component chooser specifying searching criteria. + * @param index Ordinal component index. + * @return JLabel instance. + * + */ + public static JLabel waitJLabel(final Container cont, final ComponentChooser chooser, final int index) { + return (JLabel) waitComponent(cont, new JLabelFinder(chooser), index); + } + + /** + * Waits JLabel in container. + * + * @param cont Container to search component in. + * @param chooser a component chooser specifying searching criteria. + * @return JLabel instance. + * + */ + public static JLabel waitJLabel(Container cont, ComponentChooser chooser) { + return waitJLabel(cont, chooser, 0); + } + + /** + * Waits JLabel by text. + * + * @param cont Container to search component in. + * @param text Component text. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @param index Ordinal component index. + * @return JLabel instance. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * + */ + public static JLabel waitJLabel(Container cont, String text, boolean ce, boolean ccs, int index) { + return waitJLabel(cont, new JLabelByLabelFinder(text, new DefaultStringComparator(ce, ccs)), index); + } + + /** + * Waits JLabel by text. + * + * @param cont Container to search component in. + * @param text Component text. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @return JLabel instance. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * + */ + public static JLabel waitJLabel(Container cont, String text, boolean ce, boolean ccs) { + return waitJLabel(cont, text, ce, ccs, 0); + } + + /** + * Waits for text. Uses getComparator() comparator. + * + * @param text Text to wait for. + */ + public void waitText(String text) { + getOutput().printLine("Wait \"" + text + "\" text in component \n : " + + toStringSource()); + getOutput().printGolden("Wait \"" + text + "\" text"); + waitState(new JLabelByLabelFinder(text, getComparator())); + } + + /** + * Returns information about component. + */ + @Override + public Hashtable getDump() { + Hashtable result = super.getDump(); + if (((JLabel) getSource()).getText() != null) { + result.put(TEXT_DPROP, ((JLabel) getSource()).getText()); + } else { + result.put(TEXT_DPROP, "null"); + } + return result; + } + + //////////////////////////////////////////////////////// + //Mapping // + /** + * Maps {@code JLabel.getDisabledIcon()} through queue + */ + public Icon getDisabledIcon() { + return (runMapping(new MapAction("getDisabledIcon") { + @Override + public Icon map() { + return ((JLabel) getSource()).getDisabledIcon(); + } + })); + } + + /** + * Maps {@code JLabel.getDisplayedMnemonic()} through queue + */ + public int getDisplayedMnemonic() { + return (runMapping(new MapIntegerAction("getDisplayedMnemonic") { + @Override + public int map() { + return ((JLabel) getSource()).getDisplayedMnemonic(); + } + })); + } + + /** + * Maps {@code JLabel.getHorizontalAlignment()} through queue + */ + public int getHorizontalAlignment() { + return (runMapping(new MapIntegerAction("getHorizontalAlignment") { + @Override + public int map() { + return ((JLabel) getSource()).getHorizontalAlignment(); + } + })); + } + + /** + * Maps {@code JLabel.getHorizontalTextPosition()} through queue + */ + public int getHorizontalTextPosition() { + return (runMapping(new MapIntegerAction("getHorizontalTextPosition") { + @Override + public int map() { + return ((JLabel) getSource()).getHorizontalTextPosition(); + } + })); + } + + /** + * Maps {@code JLabel.getIcon()} through queue + */ + public Icon getIcon() { + return (runMapping(new MapAction("getIcon") { + @Override + public Icon map() { + return ((JLabel) getSource()).getIcon(); + } + })); + } + + /** + * Maps {@code JLabel.getIconTextGap()} through queue + */ + public int getIconTextGap() { + return (runMapping(new MapIntegerAction("getIconTextGap") { + @Override + public int map() { + return ((JLabel) getSource()).getIconTextGap(); + } + })); + } + + /** + * Maps {@code JLabel.getLabelFor()} through queue + */ + public Component getLabelFor() { + return (runMapping(new MapAction("getLabelFor") { + @Override + public Component map() { + return ((JLabel) getSource()).getLabelFor(); + } + })); + } + + /** + * Maps {@code JLabel.getText()} through queue + */ + public String getText() { + return (runMapping(new MapAction("getText") { + @Override + public String map() { + return ((JLabel) getSource()).getText(); + } + })); + } + + /** + * Maps {@code JLabel.getUI()} through queue + */ + public LabelUI getUI() { + return (runMapping(new MapAction("getUI") { + @Override + public LabelUI map() { + return ((JLabel) getSource()).getUI(); + } + })); + } + + /** + * Maps {@code JLabel.getVerticalAlignment()} through queue + */ + public int getVerticalAlignment() { + return (runMapping(new MapIntegerAction("getVerticalAlignment") { + @Override + public int map() { + return ((JLabel) getSource()).getVerticalAlignment(); + } + })); + } + + /** + * Maps {@code JLabel.getVerticalTextPosition()} through queue + */ + public int getVerticalTextPosition() { + return (runMapping(new MapIntegerAction("getVerticalTextPosition") { + @Override + public int map() { + return ((JLabel) getSource()).getVerticalTextPosition(); + } + })); + } + + /** + * Maps {@code JLabel.setDisabledIcon(Icon)} through queue + */ + public void setDisabledIcon(final Icon icon) { + runMapping(new MapVoidAction("setDisabledIcon") { + @Override + public void map() { + ((JLabel) getSource()).setDisabledIcon(icon); + } + }); + } + + /** + * Maps {@code JLabel.setDisplayedMnemonic(char)} through queue + */ + public void setDisplayedMnemonic(final char c) { + runMapping(new MapVoidAction("setDisplayedMnemonic") { + @Override + public void map() { + ((JLabel) getSource()).setDisplayedMnemonic(c); + } + }); + } + + /** + * Maps {@code JLabel.setDisplayedMnemonic(int)} through queue + */ + public void setDisplayedMnemonic(final int i) { + runMapping(new MapVoidAction("setDisplayedMnemonic") { + @Override + public void map() { + ((JLabel) getSource()).setDisplayedMnemonic(i); + } + }); + } + + /** + * Maps {@code JLabel.setHorizontalAlignment(int)} through queue + */ + public void setHorizontalAlignment(final int i) { + runMapping(new MapVoidAction("setHorizontalAlignment") { + @Override + public void map() { + ((JLabel) getSource()).setHorizontalAlignment(i); + } + }); + } + + /** + * Maps {@code JLabel.setHorizontalTextPosition(int)} through queue + */ + public void setHorizontalTextPosition(final int i) { + runMapping(new MapVoidAction("setHorizontalTextPosition") { + @Override + public void map() { + ((JLabel) getSource()).setHorizontalTextPosition(i); + } + }); + } + + /** + * Maps {@code JLabel.setIcon(Icon)} through queue + */ + public void setIcon(final Icon icon) { + runMapping(new MapVoidAction("setIcon") { + @Override + public void map() { + ((JLabel) getSource()).setIcon(icon); + } + }); + } + + /** + * Maps {@code JLabel.setIconTextGap(int)} through queue + */ + public void setIconTextGap(final int i) { + runMapping(new MapVoidAction("setIconTextGap") { + @Override + public void map() { + ((JLabel) getSource()).setIconTextGap(i); + } + }); + } + + /** + * Maps {@code JLabel.setLabelFor(Component)} through queue + */ + public void setLabelFor(final Component component) { + runMapping(new MapVoidAction("setLabelFor") { + @Override + public void map() { + ((JLabel) getSource()).setLabelFor(component); + } + }); + } + + /** + * Maps {@code JLabel.setText(String)} through queue + */ + public void setText(final String string) { + runMapping(new MapVoidAction("setText") { + @Override + public void map() { + ((JLabel) getSource()).setText(string); + } + }); + } + + /** + * Maps {@code JLabel.setUI(LabelUI)} through queue + */ + public void setUI(final LabelUI labelUI) { + runMapping(new MapVoidAction("setUI") { + @Override + public void map() { + ((JLabel) getSource()).setUI(labelUI); + } + }); + } + + /** + * Maps {@code JLabel.setVerticalAlignment(int)} through queue + */ + public void setVerticalAlignment(final int i) { + runMapping(new MapVoidAction("setVerticalAlignment") { + @Override + public void map() { + ((JLabel) getSource()).setVerticalAlignment(i); + } + }); + } + + /** + * Maps {@code JLabel.setVerticalTextPosition(int)} through queue + */ + public void setVerticalTextPosition(final int i) { + runMapping(new MapVoidAction("setVerticalTextPosition") { + @Override + public void map() { + ((JLabel) getSource()).setVerticalTextPosition(i); + } + }); + } + + //End of mapping // + //////////////////////////////////////////////////////// + /** + * Allows to find component by text. + */ + public static class JLabelByLabelFinder implements ComponentChooser { + + String label; + StringComparator comparator; + + /** + * Constructs JLabelByLabelFinder. + * + * @param lb a text pattern + * @param comparator specifies string comparision algorithm. + */ + public JLabelByLabelFinder(String lb, StringComparator comparator) { + label = lb; + this.comparator = comparator; + } + + /** + * Constructs JLabelByLabelFinder. + * + * @param lb a text pattern + */ + public JLabelByLabelFinder(String lb) { + this(lb, Operator.getDefaultStringComparator()); + } + + @Override + public boolean checkComponent(Component comp) { + if (comp instanceof JLabel) { + if (((JLabel) comp).getText() != null) { + return (comparator.equals(((JLabel) comp).getText(), + label)); + } + } + return false; + } + + @Override + public String getDescription() { + return "JLabel with text \"" + label + "\""; + } + + @Override + public String toString() { + return "JLabelByLabelFinder{" + "label=" + label + ", comparator=" + comparator + '}'; + } + } + + /** + * Checks component type. + */ + public static class JLabelFinder extends Finder { + + /** + * Constructs JLabelFinder. + * + * @param sf other searching criteria. + */ + public JLabelFinder(ComponentChooser sf) { + super(JLabel.class, sf); + } + + /** + * Constructs JLabelFinder. + */ + public JLabelFinder() { + super(JLabel.class); + } + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JListOperator.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JListOperator.java new file mode 100644 index 00000000000..e8184f8dd85 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JListOperator.java @@ -0,0 +1,1735 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.operators; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.Point; +import java.awt.Rectangle; +import java.util.Hashtable; +import java.util.Vector; + +import javax.swing.JList; +import javax.swing.JScrollPane; +import javax.swing.ListCellRenderer; +import javax.swing.ListModel; +import javax.swing.ListSelectionModel; +import javax.swing.event.ListSelectionListener; +import javax.swing.plaf.ListUI; + +import org.netbeans.jemmy.ComponentChooser; +import org.netbeans.jemmy.ComponentSearcher; +import org.netbeans.jemmy.JemmyInputException; +import org.netbeans.jemmy.Outputable; +import org.netbeans.jemmy.QueueTool; +import org.netbeans.jemmy.TestOut; +import org.netbeans.jemmy.TimeoutExpiredException; +import org.netbeans.jemmy.drivers.DriverManager; +import org.netbeans.jemmy.drivers.MultiSelListDriver; +import org.netbeans.jemmy.util.EmptyVisualizer; + +/** + *

Timeouts used:
+ * ComponentOperator.WaitComponentTimeout - time to wait component displayed + *
+ * ComponentOperator.WaitStateTimeout - time to wait for item, and for item to + * be selected
+ * JScrollBarOperator.OneScrollClickTimeout - time for one scroll click
+ * JScrollBarOperator.WholeScrollTimeout - time for the whole scrolling
. + * + * @see org.netbeans.jemmy.Timeouts + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + * + */ +public class JListOperator extends JComponentOperator + implements Outputable { + + /** + * Identifier for a "item" properties. + * + * @see #getDump + */ + public static final String ITEM_PREFIX_DPROP = "Item"; + + /** + * Identifier for a "selected item" property. + * + * @see #getDump + */ + public static final String SELECTED_ITEM_PREFIX_DPROP = "SelectedItem"; + + private TestOut output; + private MultiSelListDriver driver; + + /** + * Constructor. + * + * @param b a component + */ + public JListOperator(JList b) { + super(b); + driver = DriverManager.getMultiSelListDriver(getClass()); + } + + /** + * Constructs a JListOperator object. + * + * @param cont a container + * @param chooser a component chooser specifying searching criteria. + * @param index an index between appropriate ones. + */ + public JListOperator(ContainerOperator cont, ComponentChooser chooser, int index) { + this((JList) cont. + waitSubComponent(new JListFinder(chooser), + index)); + copyEnvironment(cont); + } + + /** + * Constructs a JListOperator object. + * + * @param cont a container + * @param chooser a component chooser specifying searching criteria. + */ + public JListOperator(ContainerOperator cont, ComponentChooser chooser) { + this(cont, chooser, 0); + } + + /** + * Constructor. Waits item text first. Uses cont's timeout and output for + * waiting and to init operator. + * + * @param cont a container + * @param text Text of item which is currently selected. + * @param itemIndex Item index. + * @param index Ordinal component index. + * + */ + public JListOperator(ContainerOperator cont, String text, int itemIndex, int index) { + this((JList) waitComponent(cont, + new JListByItemFinder(text, itemIndex, + cont.getComparator()), + index)); + copyEnvironment(cont); + } + + /** + * Constructor. Waits component by selected item text first. Uses cont's + * timeout and output for waiting and to init operator. + * + * @param cont a container + * @param text Text of item which is currently selected. + * @param index Ordinal component index. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * + */ + public JListOperator(ContainerOperator cont, String text, int index) { + this(cont, text, -1, index); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont a container + * @param text Text of item which is currently selected. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * + */ + public JListOperator(ContainerOperator cont, String text) { + this(cont, text, 0); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont a container + * @param index Ordinal component index. + * + */ + public JListOperator(ContainerOperator cont, int index) { + this((JList) waitComponent(cont, + new JListFinder(), + index)); + copyEnvironment(cont); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont a container + * + */ + public JListOperator(ContainerOperator cont) { + this(cont, 0); + } + + /** + * Searches JList in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @param index Ordinal component index. + * @return JList instance or null if component was not found. + */ + public static JList findJList(Container cont, ComponentChooser chooser, int index) { + return (JList) findComponent(cont, new JListFinder(chooser), index); + } + + /** + * Searches 0'th JList in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @return JList instance or null if component was not found. + */ + public static JList findJList(Container cont, ComponentChooser chooser) { + return findJList(cont, chooser, 0); + } + + /** + * Searches JList by item. + * + * @param cont Container to search component in. + * @param text Item text. If null, contents is not checked. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @param itemIndex Index of item to compare text. If -1, selected item is + * checked. + * @param index Ordinal component index. + * @return JList instance or null if component was not found. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + */ + public static JList findJList(Container cont, String text, boolean ce, boolean ccs, int itemIndex, int index) { + return findJList(cont, new JListByItemFinder(text, itemIndex, new DefaultStringComparator(ce, ccs)), index); + } + + /** + * Searches JList by item. + * + * @param cont Container to search component in. + * @param text Item text. If null, contents is not checked. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @param itemIndex Index of item to compare text. If -1, selected item is + * checked. + * @return JList instance or null if component was not found. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + */ + public static JList findJList(Container cont, String text, boolean ce, boolean ccs, int itemIndex) { + return findJList(cont, text, ce, ccs, itemIndex, 0); + } + + /** + * Waits JList in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @param index Ordinal component index. + * @return JList instance or null if component was not found. + * + */ + public static JList waitJList(Container cont, ComponentChooser chooser, int index) { + return (JList) waitComponent(cont, new JListFinder(chooser), index); + } + + /** + * Waits 0'th JList in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @return JList instance or null if component was not found. + * + */ + public static JList waitJList(Container cont, ComponentChooser chooser) { + return waitJList(cont, chooser, 0); + } + + /** + * Waits JList by item. + * + * @param cont Container to search component in. + * @param text Item text. If null, contents is not checked. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @param itemIndex Index of item to compare text. If -1, selected item is + * checked. + * @param index Ordinal component index. + * @return JList instance or null if component was not found. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * + */ + public static JList waitJList(Container cont, String text, boolean ce, boolean ccs, int itemIndex, int index) { + return waitJList(cont, new JListByItemFinder(text, itemIndex, new DefaultStringComparator(ce, ccs)), index); + } + + /** + * Waits JList by item. + * + * @param cont Container to search component in. + * @param text Item text. If null, contents is not checked. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @param itemIndex Index of item to compare text. If -1, selected item is + * checked. + * @return JList instance or null if component was not found. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * + */ + public static JList waitJList(Container cont, String text, boolean ce, boolean ccs, int itemIndex) { + return waitJList(cont, text, ce, ccs, itemIndex, 0); + } + + @Override + public void setOutput(TestOut output) { + super.setOutput(output.createErrorOutput()); + this.output = output; + } + + @Override + public TestOut getOutput() { + return output; + } + + @Override + public void copyEnvironment(Operator anotherOperator) { + super.copyEnvironment(anotherOperator); + driver + = (MultiSelListDriver) DriverManager. + getDriver(DriverManager.MULTISELLIST_DRIVER_ID, + getClass(), + anotherOperator.getProperties()); + } + + /** + * Gets point to click on itemIndex'th item. + * + * @param itemIndex an index of an item to click. + * @return a Point in component's coordinate system. + */ + public Point getClickPoint(int itemIndex) { + Rectangle rect = getCellBounds(itemIndex, itemIndex); + return (new Point(rect.x + rect.width / 2, + rect.y + rect.height / 2)); + } + + /** + * Ask renderer for component to be displayed. + * + * @param itemIndex Item index. + * @param isSelected True if the specified cell was selected. + * @param cellHasFocus True if the specified cell has the focus. + * @return Component to be displayed. + */ + @SuppressWarnings(value = "unchecked") + public Component getRenderedComponent(int itemIndex, boolean isSelected, boolean cellHasFocus) { + return (((ListCellRenderer) getCellRenderer()). + getListCellRendererComponent((JList) getSource(), + getModel().getElementAt(itemIndex), + itemIndex, + isSelected, + cellHasFocus)); + } + + /** + * Ask renderer for component to be displayed. Uses + * isSelectedIndex(itemIndex) to determine whether item is selected. + * Supposes item do not have focus. + * + * @param itemIndex Item index. + * @return Component to be displayed. + */ + public Component getRenderedComponent(int itemIndex) { + return getRenderedComponent(itemIndex, isSelectedIndex(itemIndex), false); + } + + /** + * Searches for index'th item good from chooser's point of view. + * + * @param chooser Item verifying object. + * @param index Ordinal item index. + * @return Item index or -1 if search was insuccessful. + */ + public int findItemIndex(ListItemChooser chooser, int index) { + ListModel model = getModel(); + int count = 0; + for (int i = 0; i < model.getSize(); i++) { + if (chooser.checkItem(this, i)) { + if (count == index) { + return i; + } else { + count++; + } + } + } + return -1; + } + + /** + * Searches for an item good from chooser's point of view. + * + * @param chooser Item verifying object. + * @return Item index or -1 if serch was insuccessful. + * @see #findItemIndex(JListOperator.ListItemChooser, int) + * @see #findItemIndex(String, boolean, boolean) + */ + public int findItemIndex(ListItemChooser chooser) { + return findItemIndex(chooser, 0); + } + + /** + * Searches for an item good from chooser's point of view. + * + * @param item a text pattern + * @param comparator a string comparision algorithm + * @param index Ordinal item index. + * @return Item index or -1 if serch was insuccessful. + * @see #findItemIndex(JListOperator.ListItemChooser, int) + * @see #findItemIndex(String, boolean, boolean) + */ + public int findItemIndex(String item, StringComparator comparator, int index) { + return findItemIndex(new BySubStringListItemChooser(item, comparator), index); + } + + /** + * Searched for index'th item by text. + * + * @param item a text pattern + * @param ce Compare text exactly. + * @param cc Compare text case sensitively. + * @param index Ordinal item index. + * @return Item index or -1 if serch was insuccessful. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @deprecated Use findItemIndex(String, int) or findItemIndex(String, + * StringComparator, int) + */ + @Deprecated + public int findItemIndex(String item, boolean ce, boolean cc, int index) { + return findItemIndex(item, new DefaultStringComparator(ce, cc), index); + } + + /** + * Searched for index'th item by text. Uses StringComparator assigned to + * this object. + * + * @param item a text pattern + * @param index Ordinal item index. + * @return Item index or -1 if search was insuccessful. + */ + public int findItemIndex(String item, int index) { + return findItemIndex(item, getComparator(), index); + } + + /** + * Searches for an item good from chooser's point of view. + * + * @param item a text pattern + * @param comparator a string comparision algorithm + * @return Item index or -1 if serch was insuccessful. + * @see #findItemIndex(JListOperator.ListItemChooser, int) + * @see #findItemIndex(String, boolean, boolean) + */ + public int findItemIndex(String item, StringComparator comparator) { + return findItemIndex(item, comparator, 0); + } + + /** + * Searched item by text. + * + * @param item a text pattern + * @param ce Compare text exactly. + * @param cc Compare text case sensitively. + * @return Item index or -1 if search was insuccessful. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @deprecated Use findItemIndex(String) or findItemIndex(String, + * StringComparator) + */ + @Deprecated + public int findItemIndex(String item, boolean ce, boolean cc) { + return findItemIndex(item, ce, cc, 0); + } + + /** + * Searched for first item by text. Uses StringComparator assigned to this + * object. + * + * @param item a text pattern + * @return Item index or -1 if search was insuccessful. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + */ + public int findItemIndex(String item) { + return findItemIndex(item, 0); + } + + /** + * Searches for index'th item by rendered component. + * + * @param chooser Component verifying object. + * @param index Ordinal item index. + * @return Item index or -1 if serch was insuccessful. + * @see #getRenderedComponent(int, boolean, boolean) + */ + public int findItemIndex(ComponentChooser chooser, int index) { + return findItemIndex(new ByRenderedComponentListItemChooser(chooser), index); + } + + /** + * Searches for an item by rendered component. + * + * @param chooser Component verifying object. + * @return Item index or -1 if serch was insuccessful. + * @see #getRenderedComponent(int, boolean, boolean) + */ + public int findItemIndex(ComponentChooser chooser) { + return findItemIndex(chooser, 0); + } + + /** + * Clicks on item by item index. + * + * @param itemIndex Item index. + * @param clickCount count click. + * @return Click point or null if list does not contains itemIndex'th item. + * @throws NoSuchItemException + */ + public Object clickOnItem(final int itemIndex, final int clickCount) { + output.printLine("Click " + Integer.toString(clickCount) + + " times on JList\n : " + toStringSource()); + output.printGolden("Click " + Integer.toString(clickCount) + + " times on JList"); + checkIndex(itemIndex); + try { + scrollToItem(itemIndex); + } catch (TimeoutExpiredException e) { + output.printStackTrace(e); + } + if (((JList) getSource()).getModel().getSize() <= itemIndex) { + output.printErrLine("JList " + toStringSource() + " does not contain " + + Integer.toString(itemIndex) + "'th item"); + return null; + } + if (((JList) getSource()).getAutoscrolls()) { + ((JList) getSource()).ensureIndexIsVisible(itemIndex); + } + return (getQueueTool().invokeSmoothly(new QueueTool.QueueAction("Path selecting") { + @Override + public Object launch() { + Rectangle rect = getCellBounds(itemIndex, itemIndex); + if (rect == null) { + output.printErrLine("Impossible to determine click point for " + + Integer.toString(itemIndex) + "'th item"); + return null; + } + Point point = new Point((int) (rect.getX() + rect.getWidth() / 2), + (int) (rect.getY() + rect.getHeight() / 2)); + Object result = getModel().getElementAt(itemIndex); + clickMouse(point.x, point.y, clickCount); + return result; + } + })); + } + + /** + * Finds item by item text, and do mouse click on it. + * + * @param item Item text. + * @param comparator a string comparision algorithm + * @param clickCount count click. + * @return Click point or null if list does not contains itemIndex'th item. + * @throws NoSuchItemException + */ + public Object clickOnItem(final String item, final StringComparator comparator, final int clickCount) { + scrollToItem(findItemIndex(item, comparator, 0)); + return (getQueueTool().invokeSmoothly(new QueueTool.QueueAction("Path selecting") { + @Override + public Object launch() { + int index = findItemIndex(item, comparator, 0); + if (index != -1) { + return clickOnItem(index, clickCount); + } else { + throw (new NoSuchItemException(item)); + } + } + })); + } + + /** + * Finds item by item text, and do mouse click on it. + * + * @param item Item text. + * @param ce Compare exactly. + * @param cc Compare case sensitively. + * @param clickCount count click. + * @return Click point or null if list does not contains itemIndex'th item. + * @throws NoSuchItemException + * @deprecated Use clickOnItem(String, int) or clickOnItem(String, + * StringComparator, int) + */ + @Deprecated + public Object clickOnItem(String item, boolean ce, boolean cc, int clickCount) { + return clickOnItem(item, new DefaultStringComparator(ce, cc), clickCount); + } + + /** + * Finds item by item text, and do mouse click on it. Uses StringComparator + * assigned to this object. + * + * @param item Item text. + * @param clickCount count click. + * @return Click point or null if list does not contains itemIndex'th item. + * @throws NoSuchItemException + */ + public Object clickOnItem(String item, int clickCount) { + return clickOnItem(item, getComparator(), clickCount); + } + + /** + * Finds item by item text, and do simple mouse click on it. Uses + * StringComparator assigned to this object. + * + * @param item Item text. + * @param comparator a string comparision algorithm + * @return Click point or null if list does not contains itemIndex'th item. + * @throws NoSuchItemException + */ + public Object clickOnItem(String item, StringComparator comparator) { + return clickOnItem(item, comparator, 1); + } + + /** + * Finds item by item text, and do simple mouse click on it. + * + * @param item Item text. + * @param ce Compare exactly. + * @param cc Compare case sensitively. + * @return Click point or null if list does not contains itemIndex'th item. + * @throws NoSuchItemException + * @deprecated Use clickOnItem(String) or clickOnItem(String, + * StringComparator) + */ + @Deprecated + public Object clickOnItem(String item, boolean ce, boolean cc) { + return clickOnItem(item, ce, cc, 1); + } + + /** + * Finds item by item text, and do simple mouse click on it. Uses + * StringComparator assigned to this object. + * + * @param item Item text. + * @return Click point or null if list does not contains itemIndex'th item. + * @throws NoSuchItemException + */ + public Object clickOnItem(String item) { + return clickOnItem(item, 0); + } + + /** + * Scrolls to an item if the list is on a JScrollPane component. + * + * @param itemIndex an item index. + * @see #scrollToItem(String, boolean, boolean) + * + * @throws NoSuchItemException + */ + public void scrollToItem(int itemIndex) { + output.printTrace("Scroll JList to " + Integer.toString(itemIndex) + "'th item\n : " + + toStringSource()); + output.printGolden("Scroll JList to " + Integer.toString(itemIndex) + "'th item"); + checkIndex(itemIndex); + makeComponentVisible(); + //try to find JScrollPane under. + JScrollPane scroll = (JScrollPane) getContainer(new JScrollPaneOperator.JScrollPaneFinder(ComponentSearcher. + getTrueChooser("JScrollPane"))); + if (scroll == null) { + return; + } + JScrollPaneOperator scroller = new JScrollPaneOperator(scroll); + scroller.copyEnvironment(this); + scroller.setVisualizer(new EmptyVisualizer()); + Rectangle rect = getCellBounds(itemIndex, itemIndex); + scroller.scrollToComponentRectangle(getSource(), + (int) rect.getX(), + (int) rect.getY(), + (int) rect.getWidth(), + (int) rect.getHeight()); + } + + /** + * Scrolls to an item if the list is on a JScrollPane component. + * + * @param item Item text + * @param comparator a string comparision algorithm + * @see #scrollToItem(String, boolean, boolean) + * + */ + public void scrollToItem(String item, StringComparator comparator) { + scrollToItem(findItemIndex(item, comparator)); + } + + /** + * Scrolls to an item if the list is on a JScrollPane component. + * + * @param item Item text + * @param ce Compare exactly. + * @param cc Compare case sensitively. + * @see #scrollToItem(String, boolean, boolean) + * + * @deprecated Use scrollToItem(String) or scrollToItem(String, + * StringComparator) + */ + @Deprecated + public void scrollToItem(String item, boolean ce, boolean cc) { + scrollToItem(findItemIndex(item, ce, cc)); + } + + /** + * Selects an item by index. + * + * @param index an item index. + */ + public void selectItem(int index) { + checkIndex(index); + driver.selectItem(this, index); + if (getVerification()) { + waitItemSelection(index, true); + } + } + + /** + * Selects an item by text. + * + * @param item an item text. + */ + public void selectItem(final String item) { + scrollToItem(findItemIndex(item)); + getQueueTool().invokeSmoothly(new QueueTool.QueueAction("Path selecting") { + @Override + public Void launch() { + driver.selectItem(JListOperator.this, findItemIndex(item)); + return null; + } + }); + } + + /** + * Selects items by indices. + * + * @param indices item indices. + */ + public void selectItems(int[] indices) { + checkIndices(indices); + driver.selectItems(this, indices); + if (getVerification()) { + waitItemsSelection(indices, true); + } + } + + /** + * Selects items by texts. + * + * @param items item texts. + */ + public void selectItem(String[] items) { + int[] indices = new int[items.length]; + for (int i = 0; i < items.length; i++) { + indices[i] = findItemIndex(items[i]); + } + selectItems(indices); + } + + /** + * Waits for items to be selected. + * + * @param itemIndices item indices to be selected + * @param selected Selected (true) or unselected (false). + */ + public void waitItemsSelection(final int[] itemIndices, final boolean selected) { + getOutput().printLine("Wait items to be " + + (selected ? "" : "un") + "selected in component \n : " + + toStringSource()); + getOutput().printGolden("Wait items to be " + + (selected ? "" : "un") + "selected"); + waitState(new ComponentChooser() { + @Override + public boolean checkComponent(Component comp) { + int[] indices = getSelectedIndices(); + for (int i = 0; i < indices.length; i++) { + if (indices[i] != itemIndices[i]) { + return false; + } + } + return true; + } + + @Override + public String getDescription() { + return ("Item has been " + + (selected ? "" : "un") + "selected"); + } + + @Override + public String toString() { + return "JListOperator.waitItemsSelection.ComponentChooser{description = " + getDescription() + '}'; + } + }); + } + + /** + * Waits for item to be selected. + * + * @param itemIndex an item needs to be selected + * @param selected Selected (true) or unselected (false). + */ + public void waitItemSelection(final int itemIndex, final boolean selected) { + waitItemsSelection(new int[]{itemIndex}, selected); + } + + /** + * Waits for item. Uses getComparator() comparator. + * + * @param item an item text + * @param itemIndex Index of item to check or -1 to check selected item. + */ + public void waitItem(String item, int itemIndex) { + getOutput().printLine("Wait \"" + item + "\" at the " + Integer.toString(itemIndex) + + " position in component \n : " + + toStringSource()); + getOutput().printGolden("Wait \"" + item + "\" at the " + Integer.toString(itemIndex) + + " position"); + waitState(new JListByItemFinder(item, itemIndex, getComparator())); + } + + /** + * Returns information about component. + */ + @Override + public Hashtable getDump() { + Hashtable result = super.getDump(); + String[] items = new String[((JList) getSource()).getModel().getSize()]; + for (int i = 0; i < ((JList) getSource()).getModel().getSize(); i++) { + items[i] = ((JList) getSource()).getModel().getElementAt(i).toString(); + } + int[] selectedIndices = ((JList) getSource()).getSelectedIndices(); + String[] selectedItems = new String[selectedIndices.length]; + for (int i = 0; i < selectedIndices.length; i++) { + selectedItems[i] = items[selectedIndices[i]]; + } + addToDump(result, ITEM_PREFIX_DPROP, items); + addToDump(result, SELECTED_ITEM_PREFIX_DPROP, selectedItems); + return result; + } + + //////////////////////////////////////////////////////// + //Mapping // + /** + * Maps {@code JList.addListSelectionListener(ListSelectionListener)} + * through queue + */ + public void addListSelectionListener(final ListSelectionListener listSelectionListener) { + runMapping(new MapVoidAction("addListSelectionListener") { + @Override + public void map() { + ((JList) getSource()).addListSelectionListener(listSelectionListener); + } + }); + } + + /** + * Maps {@code JList.addSelectionInterval(int, int)} through queue + */ + public void addSelectionInterval(final int i, final int i1) { + runMapping(new MapVoidAction("addSelectionInterval") { + @Override + public void map() { + ((JList) getSource()).addSelectionInterval(i, i1); + } + }); + } + + /** + * Maps {@code JList.clearSelection()} through queue + */ + public void clearSelection() { + runMapping(new MapVoidAction("clearSelection") { + @Override + public void map() { + ((JList) getSource()).clearSelection(); + } + }); + } + + /** + * Maps {@code JList.ensureIndexIsVisible(int)} through queue + */ + public void ensureIndexIsVisible(final int i) { + runMapping(new MapVoidAction("ensureIndexIsVisible") { + @Override + public void map() { + ((JList) getSource()).ensureIndexIsVisible(i); + } + }); + } + + /** + * Maps {@code JList.getAnchorSelectionIndex()} through queue + */ + public int getAnchorSelectionIndex() { + return (runMapping(new MapIntegerAction("getAnchorSelectionIndex") { + @Override + public int map() { + return ((JList) getSource()).getAnchorSelectionIndex(); + } + })); + } + + /** + * Maps {@code JList.getCellBounds(int, int)} through queue + */ + public Rectangle getCellBounds(final int i, final int i1) { + return (runMapping(new MapAction("getCellBounds") { + @Override + public Rectangle map() { + return ((JList) getSource()).getCellBounds(i, i1); + } + })); + } + + /** + * Maps {@code JList.getCellRenderer()} through queue + */ + public ListCellRenderer getCellRenderer() { + return (runMapping(new MapAction>("getCellRenderer") { + @Override + public ListCellRenderer map() { + return ((JList) getSource()).getCellRenderer(); + } + })); + } + + /** + * Maps {@code JList.getFirstVisibleIndex()} through queue + */ + public int getFirstVisibleIndex() { + return (runMapping(new MapIntegerAction("getFirstVisibleIndex") { + @Override + public int map() { + return ((JList) getSource()).getFirstVisibleIndex(); + } + })); + } + + /** + * Maps {@code JList.getFixedCellHeight()} through queue + */ + public int getFixedCellHeight() { + return (runMapping(new MapIntegerAction("getFixedCellHeight") { + @Override + public int map() { + return ((JList) getSource()).getFixedCellHeight(); + } + })); + } + + /** + * Maps {@code JList.getFixedCellWidth()} through queue + */ + public int getFixedCellWidth() { + return (runMapping(new MapIntegerAction("getFixedCellWidth") { + @Override + public int map() { + return ((JList) getSource()).getFixedCellWidth(); + } + })); + } + + /** + * Maps {@code JList.getLastVisibleIndex()} through queue + */ + public int getLastVisibleIndex() { + return (runMapping(new MapIntegerAction("getLastVisibleIndex") { + @Override + public int map() { + return ((JList) getSource()).getLastVisibleIndex(); + } + })); + } + + /** + * Maps {@code JList.getLeadSelectionIndex()} through queue + */ + public int getLeadSelectionIndex() { + return (runMapping(new MapIntegerAction("getLeadSelectionIndex") { + @Override + public int map() { + return ((JList) getSource()).getLeadSelectionIndex(); + } + })); + } + + /** + * Maps {@code JList.getMaxSelectionIndex()} through queue + */ + public int getMaxSelectionIndex() { + return (runMapping(new MapIntegerAction("getMaxSelectionIndex") { + @Override + public int map() { + return ((JList) getSource()).getMaxSelectionIndex(); + } + })); + } + + /** + * Maps {@code JList.getMinSelectionIndex()} through queue + */ + public int getMinSelectionIndex() { + return (runMapping(new MapIntegerAction("getMinSelectionIndex") { + @Override + public int map() { + return ((JList) getSource()).getMinSelectionIndex(); + } + })); + } + + /** + * Maps {@code JList.getModel()} through queue + */ + public ListModel getModel() { + return (runMapping(new MapAction>("getModel") { + @Override + public ListModel map() { + return ((JList) getSource()).getModel(); + } + })); + } + + /** + * Maps {@code JList.getPreferredScrollableViewportSize()} through queue + */ + public Dimension getPreferredScrollableViewportSize() { + return (runMapping(new MapAction("getPreferredScrollableViewportSize") { + @Override + public Dimension map() { + return ((JList) getSource()).getPreferredScrollableViewportSize(); + } + })); + } + + /** + * Maps {@code JList.getPrototypeCellValue()} through queue + */ + public Object getPrototypeCellValue() { + return (runMapping(new MapAction("getPrototypeCellValue") { + @Override + public Object map() { + return ((JList) getSource()).getPrototypeCellValue(); + } + })); + } + + /** + * Maps {@code JList.getScrollableBlockIncrement(Rectangle, int, int)} + * through queue + */ + public int getScrollableBlockIncrement(final Rectangle rectangle, final int i, final int i1) { + return (runMapping(new MapIntegerAction("getScrollableBlockIncrement") { + @Override + public int map() { + return ((JList) getSource()).getScrollableBlockIncrement(rectangle, i, i1); + } + })); + } + + /** + * Maps {@code JList.getScrollableTracksViewportHeight()} through queue + */ + public boolean getScrollableTracksViewportHeight() { + return (runMapping(new MapBooleanAction("getScrollableTracksViewportHeight") { + @Override + public boolean map() { + return ((JList) getSource()).getScrollableTracksViewportHeight(); + } + })); + } + + /** + * Maps {@code JList.getScrollableTracksViewportWidth()} through queue + */ + public boolean getScrollableTracksViewportWidth() { + return (runMapping(new MapBooleanAction("getScrollableTracksViewportWidth") { + @Override + public boolean map() { + return ((JList) getSource()).getScrollableTracksViewportWidth(); + } + })); + } + + /** + * Maps {@code JList.getScrollableUnitIncrement(Rectangle, int, int)} + * through queue + */ + public int getScrollableUnitIncrement(final Rectangle rectangle, final int i, final int i1) { + return (runMapping(new MapIntegerAction("getScrollableUnitIncrement") { + @Override + public int map() { + return ((JList) getSource()).getScrollableUnitIncrement(rectangle, i, i1); + } + })); + } + + /** + * Maps {@code JList.getSelectedIndex()} through queue + */ + public int getSelectedIndex() { + return (runMapping(new MapIntegerAction("getSelectedIndex") { + @Override + public int map() { + return ((JList) getSource()).getSelectedIndex(); + } + })); + } + + /** + * Maps {@code JList.getSelectedIndices()} through queue + */ + public int[] getSelectedIndices() { + return ((int[]) runMapping(new MapAction("getSelectedIndices") { + @Override + public Object map() { + return ((JList) getSource()).getSelectedIndices(); + } + })); + } + + /** + * Maps {@code JList.getSelectedValue()} through queue + */ + public Object getSelectedValue() { + return (runMapping(new MapAction("getSelectedValue") { + @Override + public Object map() { + return ((JList) getSource()).getSelectedValue(); + } + })); + } + + /** + * Maps {@code JList.getSelectedValues()} through queue + */ + @Deprecated + public Object[] getSelectedValues() { + return ((Object[]) runMapping(new MapAction("getSelectedValues") { + @Override + public Object map() { + return ((JList) getSource()).getSelectedValues(); + } + })); + } + + /** + * Maps {@code JList.getSelectionBackground()} through queue + */ + public Color getSelectionBackground() { + return (runMapping(new MapAction("getSelectionBackground") { + @Override + public Color map() { + return ((JList) getSource()).getSelectionBackground(); + } + })); + } + + /** + * Maps {@code JList.getSelectionForeground()} through queue + */ + public Color getSelectionForeground() { + return (runMapping(new MapAction("getSelectionForeground") { + @Override + public Color map() { + return ((JList) getSource()).getSelectionForeground(); + } + })); + } + + /** + * Maps {@code JList.getSelectionMode()} through queue + */ + public int getSelectionMode() { + return (runMapping(new MapIntegerAction("getSelectionMode") { + @Override + public int map() { + return ((JList) getSource()).getSelectionMode(); + } + })); + } + + /** + * Maps {@code JList.getSelectionModel()} through queue + */ + public ListSelectionModel getSelectionModel() { + return (runMapping(new MapAction("getSelectionModel") { + @Override + public ListSelectionModel map() { + return ((JList) getSource()).getSelectionModel(); + } + })); + } + + /** + * Maps {@code JList.getUI()} through queue + */ + public ListUI getUI() { + return (runMapping(new MapAction("getUI") { + @Override + public ListUI map() { + return ((JList) getSource()).getUI(); + } + })); + } + + /** + * Maps {@code JList.getValueIsAdjusting()} through queue + */ + public boolean getValueIsAdjusting() { + return (runMapping(new MapBooleanAction("getValueIsAdjusting") { + @Override + public boolean map() { + return ((JList) getSource()).getValueIsAdjusting(); + } + })); + } + + /** + * Maps {@code JList.getVisibleRowCount()} through queue + */ + public int getVisibleRowCount() { + return (runMapping(new MapIntegerAction("getVisibleRowCount") { + @Override + public int map() { + return ((JList) getSource()).getVisibleRowCount(); + } + })); + } + + /** + * Maps {@code JList.indexToLocation(int)} through queue + */ + public Point indexToLocation(final int i) { + return (runMapping(new MapAction("indexToLocation") { + @Override + public Point map() { + return ((JList) getSource()).indexToLocation(i); + } + })); + } + + /** + * Maps {@code JList.isSelectedIndex(int)} through queue + */ + public boolean isSelectedIndex(final int i) { + return (runMapping(new MapBooleanAction("isSelectedIndex") { + @Override + public boolean map() { + return ((JList) getSource()).isSelectedIndex(i); + } + })); + } + + /** + * Maps {@code JList.isSelectionEmpty()} through queue + */ + public boolean isSelectionEmpty() { + return (runMapping(new MapBooleanAction("isSelectionEmpty") { + @Override + public boolean map() { + return ((JList) getSource()).isSelectionEmpty(); + } + })); + } + + /** + * Maps {@code JList.locationToIndex(Point)} through queue + */ + public int locationToIndex(final Point point) { + return (runMapping(new MapIntegerAction("locationToIndex") { + @Override + public int map() { + return ((JList) getSource()).locationToIndex(point); + } + })); + } + + /** + * Maps + * {@code JList.removeListSelectionListener(ListSelectionListener)} + * through queue + */ + public void removeListSelectionListener(final ListSelectionListener listSelectionListener) { + runMapping(new MapVoidAction("removeListSelectionListener") { + @Override + public void map() { + ((JList) getSource()).removeListSelectionListener(listSelectionListener); + } + }); + } + + /** + * Maps {@code JList.removeSelectionInterval(int, int)} through queue + */ + public void removeSelectionInterval(final int i, final int i1) { + runMapping(new MapVoidAction("removeSelectionInterval") { + @Override + public void map() { + ((JList) getSource()).removeSelectionInterval(i, i1); + } + }); + } + + /** + * Maps {@code JList.setCellRenderer(ListCellRenderer)} through queue + */ + @SuppressWarnings(value = "unchecked") + public void setCellRenderer(final ListCellRenderer listCellRenderer) { + runMapping(new MapVoidAction("setCellRenderer") { + @Override + public void map() { + ((JList) getSource()).setCellRenderer(listCellRenderer); + } + }); + } + + /** + * Maps {@code JList.setFixedCellHeight(int)} through queue + */ + public void setFixedCellHeight(final int i) { + runMapping(new MapVoidAction("setFixedCellHeight") { + @Override + public void map() { + ((JList) getSource()).setFixedCellHeight(i); + } + }); + } + + /** + * Maps {@code JList.setFixedCellWidth(int)} through queue + */ + public void setFixedCellWidth(final int i) { + runMapping(new MapVoidAction("setFixedCellWidth") { + @Override + public void map() { + ((JList) getSource()).setFixedCellWidth(i); + } + }); + } + + /** + * Maps {@code JList.setListData(Vector)} through queue + */ + @SuppressWarnings(value = "unchecked") + public void setListData(final Vector vector) { + runMapping(new MapVoidAction("setListData") { + @Override + public void map() { + ((JList) getSource()).setListData(vector); + } + }); + } + + /** + * Maps {@code JList.setListData(Object[])} through queue + */ + @SuppressWarnings(value = "unchecked") + public void setListData(final Object[] object) { + runMapping(new MapVoidAction("setListData") { + @Override + public void map() { + ((JList) getSource()).setListData(object); + } + }); + } + + /** + * Maps {@code JList.setModel(ListModel)} through queue + */ + @SuppressWarnings(value = "unchecked") + public void setModel(final ListModel listModel) { + runMapping(new MapVoidAction("setModel") { + @Override + public void map() { + ((JList) getSource()).setModel(listModel); + } + }); + } + + /** + * Maps {@code JList.setPrototypeCellValue(Object)} through queue + */ + @SuppressWarnings(value = "unchecked") + public void setPrototypeCellValue(final Object object) { + runMapping(new MapVoidAction("setPrototypeCellValue") { + @Override + public void map() { + ((JList) getSource()).setPrototypeCellValue(object); + } + }); + } + + /** + * Maps {@code JList.setSelectedIndex(int)} through queue + */ + public void setSelectedIndex(final int i) { + runMapping(new MapVoidAction("setSelectedIndex") { + @Override + public void map() { + ((JList) getSource()).setSelectedIndex(i); + } + }); + } + + /** + * Maps {@code JList.setSelectedIndices(int[])} through queue + */ + public void setSelectedIndices(final int[] i) { + runMapping(new MapVoidAction("setSelectedIndices") { + @Override + public void map() { + ((JList) getSource()).setSelectedIndices(i); + } + }); + } + + /** + * Maps {@code JList.setSelectedValue(Object, boolean)} through queue + */ + public void setSelectedValue(final Object object, final boolean b) { + runMapping(new MapVoidAction("setSelectedValue") { + @Override + public void map() { + ((JList) getSource()).setSelectedValue(object, b); + } + }); + } + + /** + * Maps {@code JList.setSelectionBackground(Color)} through queue + */ + public void setSelectionBackground(final Color color) { + runMapping(new MapVoidAction("setSelectionBackground") { + @Override + public void map() { + ((JList) getSource()).setSelectionBackground(color); + } + }); + } + + /** + * Maps {@code JList.setSelectionForeground(Color)} through queue + */ + public void setSelectionForeground(final Color color) { + runMapping(new MapVoidAction("setSelectionForeground") { + @Override + public void map() { + ((JList) getSource()).setSelectionForeground(color); + } + }); + } + + /** + * Maps {@code JList.setSelectionInterval(int, int)} through queue + */ + public void setSelectionInterval(final int i, final int i1) { + runMapping(new MapVoidAction("setSelectionInterval") { + @Override + public void map() { + ((JList) getSource()).setSelectionInterval(i, i1); + } + }); + } + + /** + * Maps {@code JList.setSelectionMode(int)} through queue + */ + public void setSelectionMode(final int i) { + runMapping(new MapVoidAction("setSelectionMode") { + @Override + public void map() { + ((JList) getSource()).setSelectionMode(i); + } + }); + } + + /** + * Maps {@code JList.setSelectionModel(ListSelectionModel)} through queue + */ + public void setSelectionModel(final ListSelectionModel listSelectionModel) { + runMapping(new MapVoidAction("setSelectionModel") { + @Override + public void map() { + ((JList) getSource()).setSelectionModel(listSelectionModel); + } + }); + } + + /** + * Maps {@code JList.setUI(ListUI)} through queue + */ + public void setUI(final ListUI listUI) { + runMapping(new MapVoidAction("setUI") { + @Override + public void map() { + ((JList) getSource()).setUI(listUI); + } + }); + } + + /** + * Maps {@code JList.setValueIsAdjusting(boolean)} through queue + */ + public void setValueIsAdjusting(final boolean b) { + runMapping(new MapVoidAction("setValueIsAdjusting") { + @Override + public void map() { + ((JList) getSource()).setValueIsAdjusting(b); + } + }); + } + + /** + * Maps {@code JList.setVisibleRowCount(int)} through queue + */ + public void setVisibleRowCount(final int i) { + runMapping(new MapVoidAction("setVisibleRowCount") { + @Override + public void map() { + ((JList) getSource()).setVisibleRowCount(i); + } + }); + } + + //End of mapping // + //////////////////////////////////////////////////////// + private void checkIndex(int index) { + if (index < 0 + || index >= getModel().getSize()) { + throw (new NoSuchItemException(index)); + } + } + + private void checkIndices(int[] indices) { + for (int indice : indices) { + checkIndex(indice); + } + } + + /** + * Iterface to choose list item. + */ + public interface ListItemChooser { + + /** + * Should be true if item is good. + * + * @param oper Operator used to search item. + * @param index Index of an item be checked. + * @return true if the item fits the criteria + */ + public boolean checkItem(JListOperator oper, int index); + + /** + * Item description. + * + * @return a description. + */ + public String getDescription(); + } + + /** + * Can be thrown during item selecting if list does not have item requested. + */ + public class NoSuchItemException extends JemmyInputException { + + private static final long serialVersionUID = 42L; + + /** + * Constructor. + * + * @param item an item's text + */ + public NoSuchItemException(String item) { + super("No such item as \"" + item + "\"", getSource()); + } + + /** + * Constructor. + * + * @param index an item's index + */ + public NoSuchItemException(int index) { + super("List does not contain " + index + "'th item", getSource()); + } + } + + private static class BySubStringListItemChooser implements ListItemChooser { + + String subString; + StringComparator comparator; + + public BySubStringListItemChooser(String subString, StringComparator comparator) { + this.subString = subString; + this.comparator = comparator; + } + + @Override + public boolean checkItem(JListOperator oper, int index) { + return (comparator.equals(oper.getModel().getElementAt(index).toString(), + subString)); + } + + @Override + public String getDescription() { + return "Item containing \"" + subString + "\" string"; + } + + @Override + public String toString() { + return "BySubStringListItemChooser{" + "subString=" + subString + ", comparator=" + comparator + '}'; + } + } + + private static class ByRenderedComponentListItemChooser implements ListItemChooser { + + ComponentChooser chooser; + + public ByRenderedComponentListItemChooser(ComponentChooser chooser) { + this.chooser = chooser; + } + + @Override + public boolean checkItem(JListOperator oper, int index) { + return chooser.checkComponent(oper.getRenderedComponent(index)); + } + + @Override + public String getDescription() { + return chooser.getDescription(); + } + + @Override + public String toString() { + return "ByRenderedComponentListItemChooser{" + "chooser=" + chooser + '}'; + } + } + + /** + * Allows to find component by an item. + */ + public static class JListByItemFinder implements ComponentChooser { + + String label; + int itemIndex; + StringComparator comparator; + + /** + * Constructs JListByItemFinder. + * + * @param lb a text pattern + * @param ii item index to check. If equal to -1, selected item is + * checked. + * @param comparator specifies string comparision algorithm. + */ + public JListByItemFinder(String lb, int ii, StringComparator comparator) { + label = lb; + itemIndex = ii; + this.comparator = comparator; + } + + /** + * Constructs JListByItemFinder. + * + * @param lb a text pattern + * @param ii item index to check. If equal to -1, selected item is + * checked. + */ + public JListByItemFinder(String lb, int ii) { + this(lb, ii, Operator.getDefaultStringComparator()); + } + + @Override + public boolean checkComponent(Component comp) { + if (comp instanceof JList) { + if (label == null) { + return true; + } + if (((JList) comp).getModel().getSize() > itemIndex) { + int ii = itemIndex; + if (ii == -1) { + ii = ((JList) comp).getSelectedIndex(); + if (ii == -1) { + return false; + } + } + return (comparator.equals(((JList) comp).getModel().getElementAt(ii).toString(), + label)); + } + } + return false; + } + + @Override + public String getDescription() { + return ("JList with text \"" + label + "\" in " + + itemIndex + "'th item"); + } + + @Override + public String toString() { + return "JListByItemFinder{" + "label=" + label + ", itemIndex=" + itemIndex + ", comparator=" + comparator + '}'; + } + } + + /** + * Checks component type. + */ + public static class JListFinder extends Finder { + + /** + * Constructs JListFinder. + * + * @param sf other searching criteria. + */ + public JListFinder(ComponentChooser sf) { + super(JList.class, sf); + } + + /** + * Constructs JListFinder. + */ + public JListFinder() { + super(JList.class); + } + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JMenuBarOperator.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JMenuBarOperator.java new file mode 100644 index 00000000000..234fafdb621 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JMenuBarOperator.java @@ -0,0 +1,992 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.operators; + +import java.awt.Component; +import java.awt.Container; +import java.awt.Insets; +import java.awt.event.KeyEvent; +import java.awt.event.MouseEvent; +import java.util.Hashtable; + +import javax.swing.JDialog; +import javax.swing.JFrame; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.MenuElement; +import javax.swing.MenuSelectionManager; +import javax.swing.SingleSelectionModel; +import javax.swing.plaf.MenuBarUI; + +import org.netbeans.jemmy.Action; +import org.netbeans.jemmy.ComponentChooser; +import org.netbeans.jemmy.ComponentSearcher; +import org.netbeans.jemmy.Outputable; +import org.netbeans.jemmy.TestOut; +import org.netbeans.jemmy.TimeoutExpiredException; +import org.netbeans.jemmy.Timeoutable; +import org.netbeans.jemmy.Timeouts; +import org.netbeans.jemmy.drivers.DriverManager; +import org.netbeans.jemmy.drivers.MenuDriver; + +/** + *

Timeouts used:
+ * JMenuOperator.WaitBeforePopupTimeout - time to sleep before popup expanding + *
+ * JMenuOperator.WaitPopupTimeout - time to wait popup displayed
+ * ComponentOperator.WaitComponentTimeout - time to wait button displayed
. + * + * @see org.netbeans.jemmy.Timeouts + * @author Alexandre Iline (alexandre.iline@oracle.com) + * + */ +public class JMenuBarOperator extends JComponentOperator + implements Outputable, Timeoutable { + + /** + * Identifier for a "submenu" properties. + * + * @see #getDump + */ + public static final String SUBMENU_PREFIX_DPROP = "Submenu"; + + private TestOut output; + private Timeouts timeouts; + private MenuDriver driver; + + /** + * Constructor. + * + * @param b a component + */ + public JMenuBarOperator(JMenuBar b) { + super(b); + driver = DriverManager.getMenuDriver(getClass()); + } + + /** + * Constructs a JMenuBarOperator object. + * + * @param cont a container + * @param chooser a component chooser specifying searching criteria. + * @param index an index between appropriate ones. + */ + public JMenuBarOperator(ContainerOperator cont, ComponentChooser chooser, int index) { + this((JMenuBar) cont. + waitSubComponent(new JMenuBarFinder(chooser), + index)); + copyEnvironment(cont); + } + + /** + * Constructs a JMenuBarOperator object. + * + * @param cont a container + * @param chooser a component chooser specifying searching criteria. + */ + public JMenuBarOperator(ContainerOperator cont, ComponentChooser chooser) { + this(cont, chooser, 0); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont Operator pointing a container to search component in. + * @throws TimeoutExpiredException + */ + public JMenuBarOperator(ContainerOperator cont) { + this((JMenuBar) waitComponent(cont, + new JMenuBarFinder(), + 0)); + copyEnvironment(cont); + } + + /** + * Searches JMenuBar in frame. + * + * @param frame a container + * @return found JMenuBar + */ + public static JMenuBar findJMenuBar(JFrame frame) { + return findJMenuBar((Container) frame); + } + + /** + * Searches JMenuBar in dialog. + * + * @param dialog a container + * @return found JMenuBar + */ + public static JMenuBar findJMenuBar(JDialog dialog) { + return findJMenuBar((Container) dialog); + } + + /** + * Searches JMenuBar in container. + * + * @param cont a container + * @return found JMenuBar + * @throws TimeoutExpiredException + */ + public static JMenuBar waitJMenuBar(Container cont) { + return (JMenuBar) waitComponent(cont, new JMenuBarFinder()); + } + + /** + * Waits JMenuBar in frame. + * + * @param frame a container + * @return found JMenuBar + * @throws TimeoutExpiredException + */ + public static JMenuBar waitJMenuBar(JFrame frame) { + return waitJMenuBar((Container) frame); + } + + /** + * Waits JMenuBar in dialog. + * + * @param dialog a container + * @return found JMenuBar + * @throws TimeoutExpiredException + */ + public static JMenuBar waitJMenuBar(JDialog dialog) { + return waitJMenuBar((Container) dialog); + } + + /** + * Waits JMenuBar in container. + * + * @param cont a container + * @return found JMenuBar + */ + public static JMenuBar findJMenuBar(Container cont) { + return (JMenuBar) findComponent(cont, new JMenuBarFinder()); + } + + static { + //necessary to init timeouts + JMenuOperator.performInit(); + } + + @Override + public void setOutput(TestOut out) { + super.setOutput(out); + output = out; + } + + @Override + public TestOut getOutput() { + return output; + } + + @Override + public void setTimeouts(Timeouts times) { + super.setTimeouts(times); + timeouts = times; + } + + @Override + public Timeouts getTimeouts() { + return timeouts; + } + + @Override + public void copyEnvironment(Operator anotherOperator) { + super.copyEnvironment(anotherOperator); + driver = DriverManager.getMenuDriver(this); + } + + /** + * Pushes menu. + * + * @param choosers Array of choosers to find menuItems to push. + * @return Last pushed JMenuItem. + * @throws TimeoutExpiredException + */ + public JMenuItem pushMenu(final ComponentChooser[] choosers) { + makeComponentVisible(); + return ((JMenuItem) produceTimeRestricted(new Action() { + @Override + public Object launch(Void obj) { + //TDB 1.5 menu workaround + getQueueTool().waitEmpty(); + Object result = driver.pushMenu(JMenuBarOperator.this, + JMenuOperator.converChoosers(choosers)); + getQueueTool().waitEmpty(); + return result; + } + + @Override + public String getDescription() { + return JMenuOperator.createDescription(choosers); + } + + @Override + public String toString() { + return "JMenuBarOperator.pushMenu.Action{description = " + getDescription() + '}'; + } + }, "JMenuOperator.PushMenuTimeout")); + } + + /** + * Executes {@code pushMenu(choosers)} in a separate thread. + * + * @param choosers Array of choosers to find menuItems to push. + * @see #pushMenu(ComponentChooser[]) + */ + public void pushMenuNoBlock(final ComponentChooser[] choosers) { + makeComponentVisible(); + produceNoBlocking(new NoBlockingAction("Menu pushing") { + @Override + public Object doAction(Void param) { + //TDB 1.5 menu workaround + getQueueTool().waitEmpty(); + Object result = driver.pushMenu(JMenuBarOperator.this, + JMenuOperator.converChoosers(choosers)); + getQueueTool().waitEmpty(); + return result; + } + }); + } + + /** + * Pushes menu. + * + * @param names an array of menu texts. + * @param comparator a string comparision algorithm + * @return Last pushed JMenuItem. + * @throws TimeoutExpiredException + */ + public JMenuItem pushMenu(String[] names, StringComparator comparator) { + return pushMenu(JMenuItemOperator.createChoosers(names, comparator)); + } + + /** + * Pushes menu. + * + * @param names Menu items texts. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @return Last pushed JMenuItem. + * @throws TimeoutExpiredException + * @deprecated Use pushMenu(String[]) or pushMenu(String[], + * StringComparator) + */ + @Deprecated + public JMenuItem pushMenu(String[] names, boolean ce, boolean ccs) { + return pushMenu(names, new DefaultStringComparator(ce, ccs)); + } + + /** + * Executes {@code pushMenu(names, ce, ccs)} in a separate thread. + * + * @param names an array of menu texts. + * @param comparator a string comparision algorithm + */ + public void pushMenuNoBlock(String[] names, StringComparator comparator) { + pushMenuNoBlock(JMenuItemOperator.createChoosers(names, comparator)); + } + + /** + * Executes {@code pushMenu(names, ce, ccs)} in a separate thread. + * + * @param names Menu items texts. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @see #pushMenu(String[], boolean,boolean) + * @deprecated Use pushMenuNoBlock(String[]) or pushMenuNoBlock(String[], + * StringComparator) + */ + @Deprecated + public void pushMenuNoBlock(String[] names, boolean ce, boolean ccs) { + pushMenuNoBlock(names, new DefaultStringComparator(ce, ccs)); + } + + /** + * Pushes menu. + * + * @param names Menu items texts. + * @return Last pushed JMenuItem. + * @throws TimeoutExpiredException + */ + public JMenuItem pushMenu(String[] names) { + return pushMenu(names, getComparator()); + } + + /** + * Executes {@code pushMenu(names)} in a separate thread. + * + * @param names Menu items texts. + * @see #pushMenu(String[]) + */ + public void pushMenuNoBlock(String[] names) { + pushMenuNoBlock(names, getComparator()); + } + + /** + * Pushes menu. + * + * @param path a menu path. + * @param delim a path delimiter. + * @param comparator a string comparision algorithm + * @return Last pushed JMenuItem. + * @throws TimeoutExpiredException + */ + public JMenuItem pushMenu(String path, String delim, StringComparator comparator) { + return pushMenu(parseString(path, delim), comparator); + } + + /** + * Pushes menu. Uses PathParser assigned to this operator. + * + * @param path a menu path. + * @param comparator a string comparision algorithm + * @return Last pushed JMenuItem. + * @throws TimeoutExpiredException + */ + public JMenuItem pushMenu(String path, StringComparator comparator) { + return pushMenu(parseString(path), comparator); + } + + /** + * Pushes menu. + * + * @param path String menupath representation ("File/New", for example). + * @param delim String menupath divider ("/"). + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @return Last pushed JMenuItem. + * @throws TimeoutExpiredException + * @deprecated Use pushMenu(String, String) or pushMenu(String, String, + * StringComparator) + */ + @Deprecated + public JMenuItem pushMenu(String path, String delim, boolean ce, boolean ccs) { + return pushMenu(parseString(path, delim), ce, ccs); + } + + /** + * Executes {@code pushMenu(names, delim, comparator)} in a separate + * thread. + * + * @param path a menu path. + * @param delim a path delimiter. + * @param comparator a string comparision algorithm + */ + public void pushMenuNoBlock(String path, String delim, StringComparator comparator) { + pushMenuNoBlock(parseString(path, delim), comparator); + } + + /** + * Executes {@code pushMenu(names, comparator)} in a separate thread. + * Uses PathParser assigned to this operator. + * + * @param path a menu path. + * @param comparator a string comparision algorithm + */ + public void pushMenuNoBlock(String path, StringComparator comparator) { + pushMenuNoBlock(parseString(path), comparator); + } + + /** + * Executes {@code pushMenu(path, delim, ce, ccs)} in a separate + * thread. + * + * @param path String menupath representation ("File/New", for example). + * @param delim String menupath divider ("/"). + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @see #pushMenu + * @deprecated Use pushMenuNoBlock(String, String) or + * pushMenuNoBlock(String, String, StringComparator) + */ + @Deprecated + public void pushMenuNoBlock(String path, String delim, boolean ce, boolean ccs) { + pushMenuNoBlock(parseString(path, delim), ce, ccs); + } + + /** + * Pushes menu. + * + * @param path String menupath representation ("File/New", for example). + * @param delim String menupath divider ("/"). + * @return Last pushed JMenuItem. + * @throws TimeoutExpiredException + */ + public JMenuItem pushMenu(String path, String delim) { + return pushMenu(parseString(path, delim)); + } + + /** + * Pushes menu. Uses PathParser assigned to this operator. + * + * @param path String menupath representation ("File/New", for example). + * @return Last pushed JMenuItem. + * @throws TimeoutExpiredException + */ + public JMenuItem pushMenu(String path) { + return pushMenu(parseString(path)); + } + + /** + * Executes {@code pushMenu(path, delim)} in a separate thread. + * + * @param path String menupath representation ("File/New", for example). + * @param delim String menupath divider ("/"). + */ + public void pushMenuNoBlock(String path, String delim) { + pushMenuNoBlock(parseString(path, delim)); + } + + /** + * Executes {@code pushMenu(path)} in a separate thread. + * + * @param path String menupath representation ("File/New", for example). + */ + public void pushMenuNoBlock(String path) { + pushMenuNoBlock(parseString(path)); + } + + public JMenuItemOperator[] showMenuItems(ComponentChooser[] choosers) { + if (choosers == null || choosers.length == 0) { + return JMenuItemOperator.getMenuItems((MenuElement) getSource(), this); + } else { + return JMenuItemOperator.getMenuItems((JMenu) pushMenu(choosers), this); + } + } + + /** + * Shows submenu of menu specified by a {@code path} parameter. + * + * @param path an array of menu texts. + * @param comparator a string comparision algorithm + * @return an array of operators created tor items from the submenu. + * @throws TimeoutExpiredException + */ + public JMenuItemOperator[] showMenuItems(String[] path, StringComparator comparator) { + if (path == null || path.length == 0) { + return JMenuItemOperator.getMenuItems((MenuElement) getSource(), this); + } else { + return JMenuItemOperator.getMenuItems((JMenu) pushMenu(path, comparator), this); + } + } + + /** + * Shows submenu of menu specified by a {@code path} parameter. Uses + * StringComparator assigned to the operator. + * + * @param path an array of menu texts. + * @return an array of operators created tor items from the submenu. + * @throws TimeoutExpiredException + */ + public JMenuItemOperator[] showMenuItems(String[] path) { + return showMenuItems(path, getComparator()); + } + + /** + * Shows submenu of menu specified by a {@code path} parameter. + * + * @param path a string identifying the menu path. + * @param delim a path delimiter. + * @param comparator a string comparision algorithm + * @return an array of operators created tor items from the submenu. + * @throws TimeoutExpiredException + */ + public JMenuItemOperator[] showMenuItems(String path, String delim, StringComparator comparator) { + return showMenuItems(parseString(path, delim), comparator); + } + + /** + * Shows submenu of menu specified by a {@code path} parameter. Uses + * PathParser assigned to this operator. + * + * @param path a string identifying the menu path. + * @param comparator a string comparision algorithm + * @return an array of operators created tor items from the submenu. + * @throws TimeoutExpiredException + */ + public JMenuItemOperator[] showMenuItems(String path, StringComparator comparator) { + return showMenuItems(parseString(path), comparator); + } + + /** + * Shows submenu of menu specified by a {@code path} parameter. Uses + * StringComparator assigned to the operator. + * + * @param path a string identifying the menu path. + * @param delim a path delimiter. + * @return an array of operators created tor items from the submenu. + * @throws TimeoutExpiredException + */ + public JMenuItemOperator[] showMenuItems(String path, String delim) { + return showMenuItems(path, delim, getComparator()); + } + + /** + * Shows submenu of menu specified by a {@code path} parameter. Uses + * PathParser assigned to this operator. Uses StringComparator assigned to + * the operator. + * + * @param path a string identifying the menu path. + * @return an array of operators created tor items from the submenu. + * @throws TimeoutExpiredException + */ + public JMenuItemOperator[] showMenuItems(String path) { + return showMenuItems(path, getComparator()); + } + + public JMenuItemOperator showMenuItem(ComponentChooser[] choosers) { + ComponentChooser[] parentPath = getParentPath(choosers); + JMenu menu; + ContainerOperator menuCont; + if (parentPath.length > 0) { + menu = (JMenu) pushMenu(getParentPath(choosers)); + menuCont = new ContainerOperator<>(menu.getPopupMenu()); + menuCont.copyEnvironment(this); + } else { + menuCont = this; + } + JMenuItemOperator result = new JMenuItemOperator(menuCont, choosers[choosers.length - 1]); + result.copyEnvironment(this); + return result; + } + + /** + * Expends all menus to show menu item specified by a {@code path} + * parameter. + * + * @param path an array of menu texts. + * @param comparator a string comparision algorithm + * @return an operator for the last menu item in path. + * @throws TimeoutExpiredException + */ + public JMenuItemOperator showMenuItem(String[] path, StringComparator comparator) { + String[] parentPath = getParentPath(path); + JMenu menu; + ContainerOperator menuCont; + if (parentPath.length > 0) { + menu = (JMenu) pushMenu(getParentPath(path), comparator); + menuCont = new ContainerOperator<>(menu.getPopupMenu()); + menuCont.copyEnvironment(this); + } else { + menuCont = this; + } + JMenuItemOperator result; + // isVisible() on items returns false on mac, so we need a special searcher. + if (System.getProperty("os.name").toLowerCase().indexOf("mac") > -1) { // NOI18N + ComponentSearcher searcher = new ComponentSearcher((Container) menuCont.getSource()); + searcher.setOutput(output); + Component c = searcher.findComponent(new JMenuItemOperator.JMenuItemByLabelFinder(path[path.length - 1], getComparator())); + result = new JMenuItemOperator((JMenuItem) c); + } else { + result = new JMenuItemOperator(menuCont, path[path.length - 1]); + } + result.copyEnvironment(this); + return result; + } + + /** + * Expands all menus to show menu item specified by a {@code path} + * parameter. + * + * @param path an array of menu texts. + * @return an operator for the last menu item in path. + * @throws TimeoutExpiredException + */ + public JMenuItemOperator showMenuItem(String[] path) { + return showMenuItem(path, getComparator()); + } + + /** + * Expands all menus to show menu item specified by a {@code path} + * parameter. + * + * @param path a string identifying the menu path. + * @param delim a path delimiter. + * @param comparator a string comparision algorithm + * @return an operator for the last menu item in path. + * @throws TimeoutExpiredException + */ + public JMenuItemOperator showMenuItem(String path, String delim, StringComparator comparator) { + return showMenuItem(parseString(path, delim), comparator); + } + + /** + * Expands all menus to show menu item specified by a {@code path} + * parameter. Uses PathParser assigned to this operator. + * + * @param path a string identifying the menu path. + * @param comparator a string comparision algorithm + * @return an operator for the last menu item in path. + * @throws TimeoutExpiredException + */ + public JMenuItemOperator showMenuItem(String path, StringComparator comparator) { + return showMenuItem(parseString(path), comparator); + } + + /** + * Expands all menus to show menu item specified by a {@code path} + * parameter. Uses StringComparator assigned to the operator. + * + * @param path a string identifying the menu path. + * @param delim a path delimiter. + * @return an operator for the last menu item in path. + * @throws TimeoutExpiredException + */ + public JMenuItemOperator showMenuItem(String path, String delim) { + return showMenuItem(path, delim, getComparator()); + } + + /** + * Expands all menus to show menu item specified by a {@code path} + * parameter. Uses PathParser assigned to this operator. Uses + * StringComparator assigned to the operator. + * + * @param path a string identifying the menu path. + * @return an array of operators created tor items from the submenu. + * @throws TimeoutExpiredException + */ + public JMenuItemOperator showMenuItem(String path) { + return showMenuItem(path, getComparator()); + } + + /** + * Closes all expanded submenus. + */ + public void closeSubmenus() { + JMenu menu = (JMenu) findSubComponent(new ComponentChooser() { + @Override + public boolean checkComponent(Component comp) { + return (comp instanceof JMenu + && ((JMenu) comp).isPopupMenuVisible()); + } + + @Override + public String getDescription() { + return "Expanded JMenu"; + } + + @Override + public String toString() { + return "JMenuBarOperator.closeSubmenus.ComponentChooser{description = " + getDescription() + '}'; + } + }); + if (menu != null) { + JMenuOperator oper = new JMenuOperator(menu); + oper.copyEnvironment(this); + oper.push(); + } + } + + @Override + public Hashtable getDump() { + Hashtable result = super.getDump(); + String[] items = new String[((JMenuBar) getSource()).getMenuCount()]; + for (int i = 0; i < ((JMenuBar) getSource()).getMenuCount(); i++) { + if (((JMenuBar) getSource()).getMenu(i) != null) { + items[i] = ((JMenuBar) getSource()).getMenu(i).getText(); + } else { + items[i] = "null"; + } + } + addToDump(result, SUBMENU_PREFIX_DPROP, items); + return result; + } + + //////////////////////////////////////////////////////// + //Mapping // + /** + * Maps {@code JMenuBar.add(JMenu)} through queue + */ + public JMenu add(final JMenu jMenu) { + return (runMapping(new MapAction("add") { + @Override + public JMenu map() { + return ((JMenuBar) getSource()).add(jMenu); + } + })); + } + + /** + * Maps {@code JMenuBar.getComponentIndex(Component)} through queue + */ + public int getComponentIndex(final Component component) { + return (runMapping(new MapIntegerAction("getComponentIndex") { + @Override + public int map() { + return ((JMenuBar) getSource()).getComponentIndex(component); + } + })); + } + + /** + * Maps {@code JMenuBar.getHelpMenu()} through queue + */ + public JMenu getHelpMenu() { + return (runMapping(new MapAction("getHelpMenu") { + @Override + public JMenu map() { + return ((JMenuBar) getSource()).getHelpMenu(); + } + })); + } + + /** + * Maps {@code JMenuBar.getMargin()} through queue + */ + public Insets getMargin() { + return (runMapping(new MapAction("getMargin") { + @Override + public Insets map() { + return ((JMenuBar) getSource()).getMargin(); + } + })); + } + + /** + * Maps {@code JMenuBar.getMenu(int)} through queue + */ + public JMenu getMenu(final int i) { + return (runMapping(new MapAction("getMenu") { + @Override + public JMenu map() { + return ((JMenuBar) getSource()).getMenu(i); + } + })); + } + + /** + * Maps {@code JMenuBar.getMenuCount()} through queue + */ + public int getMenuCount() { + return (runMapping(new MapIntegerAction("getMenuCount") { + @Override + public int map() { + return ((JMenuBar) getSource()).getMenuCount(); + } + })); + } + + /** + * Maps {@code JMenuBar.getSelectionModel()} through queue + */ + public SingleSelectionModel getSelectionModel() { + return (runMapping(new MapAction("getSelectionModel") { + @Override + public SingleSelectionModel map() { + return ((JMenuBar) getSource()).getSelectionModel(); + } + })); + } + + /** + * Maps {@code JMenuBar.getSubElements()} through queue + */ + public MenuElement[] getSubElements() { + return ((MenuElement[]) runMapping(new MapAction("getSubElements") { + @Override + public Object map() { + return ((JMenuBar) getSource()).getSubElements(); + } + })); + } + + /** + * Maps {@code JMenuBar.getUI()} through queue + */ + public MenuBarUI getUI() { + return (runMapping(new MapAction("getUI") { + @Override + public MenuBarUI map() { + return ((JMenuBar) getSource()).getUI(); + } + })); + } + + /** + * Maps {@code JMenuBar.isBorderPainted()} through queue + */ + public boolean isBorderPainted() { + return (runMapping(new MapBooleanAction("isBorderPainted") { + @Override + public boolean map() { + return ((JMenuBar) getSource()).isBorderPainted(); + } + })); + } + + /** + * Maps {@code JMenuBar.isSelected()} through queue + */ + public boolean isSelected() { + return (runMapping(new MapBooleanAction("isSelected") { + @Override + public boolean map() { + return ((JMenuBar) getSource()).isSelected(); + } + })); + } + + /** + * Maps {@code JMenuBar.menuSelectionChanged(boolean)} through queue + */ + public void menuSelectionChanged(final boolean b) { + runMapping(new MapVoidAction("menuSelectionChanged") { + @Override + public void map() { + ((JMenuBar) getSource()).menuSelectionChanged(b); + } + }); + } + + /** + * Maps + * {@code JMenuBar.processKeyEvent(KeyEvent, MenuElement[], MenuSelectionManager)} + * through queue + */ + public void processKeyEvent(final KeyEvent keyEvent, final MenuElement[] menuElement, final MenuSelectionManager menuSelectionManager) { + runMapping(new MapVoidAction("processKeyEvent") { + @Override + public void map() { + ((JMenuBar) getSource()).processKeyEvent(keyEvent, menuElement, menuSelectionManager); + } + }); + } + + /** + * Maps + * {@code JMenuBar.processMouseEvent(MouseEvent, MenuElement[], MenuSelectionManager)} + * through queue + */ + public void processMouseEvent(final MouseEvent mouseEvent, final MenuElement[] menuElement, final MenuSelectionManager menuSelectionManager) { + runMapping(new MapVoidAction("processMouseEvent") { + @Override + public void map() { + ((JMenuBar) getSource()).processMouseEvent(mouseEvent, menuElement, menuSelectionManager); + } + }); + } + + /** + * Maps {@code JMenuBar.setBorderPainted(boolean)} through queue + */ + public void setBorderPainted(final boolean b) { + runMapping(new MapVoidAction("setBorderPainted") { + @Override + public void map() { + ((JMenuBar) getSource()).setBorderPainted(b); + } + }); + } + + /** + * Maps {@code JMenuBar.setHelpMenu(JMenu)} through queue + */ + public void setHelpMenu(final JMenu jMenu) { + runMapping(new MapVoidAction("setHelpMenu") { + @Override + public void map() { + ((JMenuBar) getSource()).setHelpMenu(jMenu); + } + }); + } + + /** + * Maps {@code JMenuBar.setMargin(Insets)} through queue + */ + public void setMargin(final Insets insets) { + runMapping(new MapVoidAction("setMargin") { + @Override + public void map() { + ((JMenuBar) getSource()).setMargin(insets); + } + }); + } + + /** + * Maps {@code JMenuBar.setSelected(Component)} through queue + */ + public void setSelected(final Component component) { + runMapping(new MapVoidAction("setSelected") { + @Override + public void map() { + ((JMenuBar) getSource()).setSelected(component); + } + }); + } + + /** + * Maps {@code JMenuBar.setSelectionModel(SingleSelectionModel)} + * through queue + */ + public void setSelectionModel(final SingleSelectionModel singleSelectionModel) { + runMapping(new MapVoidAction("setSelectionModel") { + @Override + public void map() { + ((JMenuBar) getSource()).setSelectionModel(singleSelectionModel); + } + }); + } + + /** + * Maps {@code JMenuBar.setUI(MenuBarUI)} through queue + */ + public void setUI(final MenuBarUI menuBarUI) { + runMapping(new MapVoidAction("setUI") { + @Override + public void map() { + ((JMenuBar) getSource()).setUI(menuBarUI); + } + }); + } + + //End of mapping // + //////////////////////////////////////////////////////// + /** + * Checks component type. + */ + public static class JMenuBarFinder extends Finder { + + /** + * Constructs JMenuBarFinder. + * + * @param sf other searching criteria. + */ + public JMenuBarFinder(ComponentChooser sf) { + super(JMenuBar.class, sf); + } + + /** + * Constructs JMenuBarFinder. + */ + public JMenuBarFinder() { + super(JMenuBar.class); + } + } + +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JMenuItemOperator.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JMenuItemOperator.java new file mode 100644 index 00000000000..69b3dd9b6ab --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JMenuItemOperator.java @@ -0,0 +1,657 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.operators; + +import java.awt.Component; +import java.awt.Container; +import java.awt.event.KeyEvent; +import java.awt.event.MouseEvent; +import java.util.Hashtable; + +import javax.swing.JMenu; +import javax.swing.JMenuItem; +import javax.swing.KeyStroke; +import javax.swing.MenuElement; +import javax.swing.MenuSelectionManager; +import javax.swing.event.MenuDragMouseEvent; +import javax.swing.event.MenuDragMouseListener; +import javax.swing.event.MenuKeyEvent; +import javax.swing.event.MenuKeyListener; +import javax.swing.plaf.MenuItemUI; + +import org.netbeans.jemmy.ComponentChooser; +import org.netbeans.jemmy.JemmyProperties; +import org.netbeans.jemmy.Outputable; +import org.netbeans.jemmy.TestOut; +import org.netbeans.jemmy.TimeoutExpiredException; +import org.netbeans.jemmy.Timeoutable; +import org.netbeans.jemmy.Timeouts; +import org.netbeans.jemmy.util.EmptyVisualizer; + +/** + * + *

Timeouts used:
+ * JMenuItemOperator.PushMenuTimeout - time between button pressing and + * releasing
+ * ComponentOperator.WaitComponentTimeout - time to wait button displayed
+ * ComponentOperator.WaitComponentEnabledTimeout - time to wait button enabled + *
. + * + * @see org.netbeans.jemmy.Timeouts + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + * + */ +public class JMenuItemOperator extends AbstractButtonOperator + implements Timeoutable, Outputable { + + private final static long PUSH_MENU_TIMEOUT = 0; + + private Timeouts timeouts; + private TestOut output; + + /** + * Constructor. + * + * @param item a component + */ + public JMenuItemOperator(JMenuItem item) { + super(item); + setTimeouts(JemmyProperties.getProperties().getTimeouts()); + setOutput(JemmyProperties.getProperties().getOutput()); + } + + /** + * Constructs a JMenuItemOperator object. + * + * @param cont a container + * @param chooser a component chooser specifying searching criteria. + * @param index an index between appropriate ones. + */ + public JMenuItemOperator(ContainerOperator cont, ComponentChooser chooser, int index) { + this((JMenuItem) cont. + waitSubComponent(new JMenuItemFinder(chooser), + index)); + copyEnvironment(cont); + } + + /** + * Constructs a JMenuItemOperator object. + * + * @param cont a container + * @param chooser a component chooser specifying searching criteria. + */ + public JMenuItemOperator(ContainerOperator cont, ComponentChooser chooser) { + this(cont, chooser, 0); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont a container + * @param text Button text. + * @param index Ordinal component index. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public JMenuItemOperator(ContainerOperator cont, String text, int index) { + this((JMenuItem) waitComponent(cont, + new JMenuItemByLabelFinder(text, + cont.getComparator()), + index)); + setTimeouts(cont.getTimeouts()); + setOutput(cont.getOutput()); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont a container + * @param text Button text. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public JMenuItemOperator(ContainerOperator cont, String text) { + this(cont, text, 0); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont a container + * @param index Ordinal component index. + * @throws TimeoutExpiredException + */ + public JMenuItemOperator(ContainerOperator cont, int index) { + this((JMenuItem) waitComponent(cont, + new JMenuItemFinder(), + index)); + copyEnvironment(cont); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont a container + * @throws TimeoutExpiredException + */ + public JMenuItemOperator(ContainerOperator cont) { + this(cont, 0); + } + + /** + * Searches JMenuItem in container. + * + * @param menu Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @param index Ordinal component index. + * @return JMenuItem instance or null if component was not found. + */ + public static JMenuItem findJMenuItem(Container menu, ComponentChooser chooser, int index) { + return (JMenuItem) findComponent(menu, new JMenuItemFinder(chooser), index); + } + + /** + * Searches 0'th JMenuItem in container. + * + * @param menu Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @return JMenuItem instance or null if component was not found. + */ + public static JMenuItem findJMenuItem(Container menu, ComponentChooser chooser) { + return findJMenuItem(menu, chooser, 0); + } + + /** + * Searches JMenuItem by text. + * + * @param menu Container to search component in. + * @param text Button text. If null, contents is not checked. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @param index Ordinal component index. + * @return JMenuItem instance or null if component was not found. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + */ + public static JMenuItem findJMenuItem(Container menu, String text, boolean ce, boolean ccs, int index) { + return (findJMenuItem(menu, + new JMenuItemByLabelFinder(text, + new DefaultStringComparator(ce, + ccs)), + index)); + } + + /** + * Searches JMenuItem by text. + * + * @param menu Container to search component in. + * @param text Button text. If null, contents is not checked. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @return JMenuItem instance or null if component was not found. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + */ + public static JMenuItem findJMenuItem(Container menu, String text, boolean ce, boolean ccs) { + return findJMenuItem(menu, text, ce, ccs, 0); + } + + /** + * Waits JMenuItem in container. + * + * @param menu Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @param index Ordinal component index. + * @return JMenuItem instance. + * @throws TimeoutExpiredException + */ + public static JMenuItem waitJMenuItem(Container menu, ComponentChooser chooser, int index) { + return (JMenuItem) waitComponent(menu, new JMenuItemFinder(chooser), index); + } + + /** + * Waits 0'th JMenuItem in container. + * + * @param menu Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @return JMenuItem instance. + * @throws TimeoutExpiredException + */ + public static JMenuItem waitJMenuItem(Container menu, ComponentChooser chooser) { + return waitJMenuItem(menu, chooser, 0); + } + + /** + * Waits JMenuItem by text. + * + * @param menu Container to search component in. + * @param text Button text. If null, contents is not checked. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @param index Ordinal component index. + * @return JMenuItem instance. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public static JMenuItem waitJMenuItem(Container menu, String text, boolean ce, boolean ccs, int index) { + return (waitJMenuItem(menu, + new JMenuItemByLabelFinder(text, + new DefaultStringComparator(ce, ccs)), + index)); + } + + /** + * Waits JMenuItem by text. + * + * @param menu Container to search component in. + * @param text Button text. If null, contents is not checked. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @return JMenuItem instance. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public static JMenuItem waitJMenuItem(Container menu, String text, boolean ce, boolean ccs) { + return waitJMenuItem(menu, text, ce, ccs, 0); + } + + static { + Timeouts.initDefault("JMenuItemOperator.PushMenuTimeout", PUSH_MENU_TIMEOUT); + } + + @Override + public void setTimeouts(Timeouts timeouts) { + super.setTimeouts(timeouts); + this.timeouts = timeouts; + } + + @Override + public Timeouts getTimeouts() { + return timeouts; + } + + @Override + public void setOutput(TestOut out) { + super.setOutput(out); + output = out; + } + + @Override + public TestOut getOutput() { + return output; + } + + @Override + public Hashtable getDump() { + Hashtable result = super.getDump(); + result.remove(AbstractButtonOperator.IS_SELECTED_DPROP); + return result; + } + + /** + * Push this menu item. + */ + @Override + public void push() { + setVisualizer(new EmptyVisualizer()); + super.push(); + } + + /** + * Push this menu item and no block further execution. + */ + @Override + public void pushNoBlock() { + setVisualizer(new EmptyVisualizer()); + super.pushNoBlock(); + } + + //////////////////////////////////////////////////////// + //Mapping // + /** + * Maps + * {@code JMenuItem.addMenuDragMouseListener(MenuDragMouseListener)} + * through queue + */ + public void addMenuDragMouseListener(final MenuDragMouseListener menuDragMouseListener) { + runMapping(new MapVoidAction("addMenuDragMouseListener") { + @Override + public void map() { + ((JMenuItem) getSource()).addMenuDragMouseListener(menuDragMouseListener); + } + }); + } + + /** + * Maps {@code JMenuItem.addMenuKeyListener(MenuKeyListener)} through queue + */ + public void addMenuKeyListener(final MenuKeyListener menuKeyListener) { + runMapping(new MapVoidAction("addMenuKeyListener") { + @Override + public void map() { + ((JMenuItem) getSource()).addMenuKeyListener(menuKeyListener); + } + }); + } + + /** + * Maps {@code JMenuItem.getAccelerator()} through queue + */ + public KeyStroke getAccelerator() { + return (runMapping(new MapAction("getAccelerator") { + @Override + public KeyStroke map() { + return ((JMenuItem) getSource()).getAccelerator(); + } + })); + } + + /** + * Maps {@code JMenuItem.getComponent()} through queue + */ + public Component getComponent() { + return (runMapping(new MapAction("getComponent") { + @Override + public Component map() { + return ((JMenuItem) getSource()).getComponent(); + } + })); + } + + /** + * Maps {@code JMenuItem.getSubElements()} through queue + */ + public MenuElement[] getSubElements() { + return ((MenuElement[]) runMapping(new MapAction("getSubElements") { + @Override + public Object map() { + return ((JMenuItem) getSource()).getSubElements(); + } + })); + } + + /** + * Maps {@code JMenuItem.isArmed()} through queue + */ + public boolean isArmed() { + return (runMapping(new MapBooleanAction("isArmed") { + @Override + public boolean map() { + return ((JMenuItem) getSource()).isArmed(); + } + })); + } + + /** + * Maps {@code JMenuItem.menuSelectionChanged(boolean)} through queue + */ + public void menuSelectionChanged(final boolean b) { + runMapping(new MapVoidAction("menuSelectionChanged") { + @Override + public void map() { + ((JMenuItem) getSource()).menuSelectionChanged(b); + } + }); + } + + /** + * Maps + * {@code JMenuItem.processKeyEvent(KeyEvent, MenuElement[], MenuSelectionManager)} + * through queue + */ + public void processKeyEvent(final KeyEvent keyEvent, final MenuElement[] menuElement, final MenuSelectionManager menuSelectionManager) { + runMapping(new MapVoidAction("processKeyEvent") { + @Override + public void map() { + ((JMenuItem) getSource()).processKeyEvent(keyEvent, menuElement, menuSelectionManager); + } + }); + } + + /** + * Maps {@code JMenuItem.processMenuDragMouseEvent(MenuDragMouseEvent)} + * through queue + */ + public void processMenuDragMouseEvent(final MenuDragMouseEvent menuDragMouseEvent) { + runMapping(new MapVoidAction("processMenuDragMouseEvent") { + @Override + public void map() { + ((JMenuItem) getSource()).processMenuDragMouseEvent(menuDragMouseEvent); + } + }); + } + + /** + * Maps {@code JMenuItem.processMenuKeyEvent(MenuKeyEvent)} through queue + */ + public void processMenuKeyEvent(final MenuKeyEvent menuKeyEvent) { + runMapping(new MapVoidAction("processMenuKeyEvent") { + @Override + public void map() { + ((JMenuItem) getSource()).processMenuKeyEvent(menuKeyEvent); + } + }); + } + + /** + * Maps + * {@code JMenuItem.processMouseEvent(MouseEvent, MenuElement[], MenuSelectionManager)} + * through queue + */ + public void processMouseEvent(final MouseEvent mouseEvent, final MenuElement[] menuElement, final MenuSelectionManager menuSelectionManager) { + runMapping(new MapVoidAction("processMouseEvent") { + @Override + public void map() { + ((JMenuItem) getSource()).processMouseEvent(mouseEvent, menuElement, menuSelectionManager); + } + }); + } + + /** + * Maps + * {@code JMenuItem.removeMenuDragMouseListener(MenuDragMouseListener)} + * through queue + */ + public void removeMenuDragMouseListener(final MenuDragMouseListener menuDragMouseListener) { + runMapping(new MapVoidAction("removeMenuDragMouseListener") { + @Override + public void map() { + ((JMenuItem) getSource()).removeMenuDragMouseListener(menuDragMouseListener); + } + }); + } + + /** + * Maps {@code JMenuItem.removeMenuKeyListener(MenuKeyListener)} + * through queue + */ + public void removeMenuKeyListener(final MenuKeyListener menuKeyListener) { + runMapping(new MapVoidAction("removeMenuKeyListener") { + @Override + public void map() { + ((JMenuItem) getSource()).removeMenuKeyListener(menuKeyListener); + } + }); + } + + /** + * Maps {@code JMenuItem.setAccelerator(KeyStroke)} through queue + */ + public void setAccelerator(final KeyStroke keyStroke) { + runMapping(new MapVoidAction("setAccelerator") { + @Override + public void map() { + ((JMenuItem) getSource()).setAccelerator(keyStroke); + } + }); + } + + /** + * Maps {@code JMenuItem.setArmed(boolean)} through queue + */ + public void setArmed(final boolean b) { + runMapping(new MapVoidAction("setArmed") { + @Override + public void map() { + ((JMenuItem) getSource()).setArmed(b); + } + }); + } + + /** + * Maps {@code JMenuItem.setUI(MenuItemUI)} through queue + */ + public void setUI(final MenuItemUI menuItemUI) { + runMapping(new MapVoidAction("setUI") { + @Override + public void map() { + ((JMenuItem) getSource()).setUI(menuItemUI); + } + }); + } + + //End of mapping // + //////////////////////////////////////////////////////// + /** + * Prepares the button to click. + */ + protected void prepareToClick() { + output.printLine("Push menu item\n :" + toStringSource()); + output.printGolden("Push menu item"); + Timeouts times = timeouts.cloneThis(); + times.setTimeout("AbstractButtonOperator.PushButtonTimeout", + timeouts.getTimeout("JMenuItemOperator.PushMenuTimeout")); + super.setTimeouts(times); + super.setOutput(output.createErrorOutput()); + } + + static JMenuItemOperator[] getMenuItems(Object[] elements, Operator env) { + int size = 0; + for (Object element1 : elements) { + if (element1 instanceof JMenuItem) { + size++; + } + } + JMenuItemOperator[] result = new JMenuItemOperator[size]; + int index = 0; + for (Object element : elements) { + if (element instanceof JMenuItem) { + result[index] = new JMenuItemOperator((JMenuItem) element); + result[index].copyEnvironment(env); + index++; + } + } + return result; + } + + static JMenuItemOperator[] getMenuItems(MenuElement parent, Operator env) { + return getMenuItems(parent.getSubElements(), env); + } + + static JMenuItemOperator[] getMenuItems(JMenu parent, Operator env) { + return getMenuItems(parent.getMenuComponents(), env); + } + + static ComponentChooser[] createChoosers(String[] names, StringComparator comparator) { + ComponentChooser[] choosers = new ComponentChooser[names.length]; + for (int i = 0; i < choosers.length; i++) { + choosers[i] = new JMenuItemOperator.JMenuItemByLabelFinder(names[i], comparator); + } + return choosers; + } + + /** + * Allows to find component by text. + */ + public static class JMenuItemByLabelFinder implements ComponentChooser { + + String label; + StringComparator comparator; + + /** + * Constructs JMenuItemByLabelFinder. + * + * @param lb a text pattern + * @param comparator specifies string comparision algorithm. + */ + public JMenuItemByLabelFinder(String lb, StringComparator comparator) { + label = lb; + this.comparator = comparator; + } + + /** + * Constructs JMenuItemByLabelFinder. + * + * @param lb a text pattern + */ + public JMenuItemByLabelFinder(String lb) { + this(lb, Operator.getDefaultStringComparator()); + } + + @Override + public boolean checkComponent(Component comp) { + if (comp instanceof JMenuItem) { + if (((JMenuItem) comp).getText() != null) { + return (comparator.equals(((JMenuItem) comp).getText(), + label)); + } + } + return false; + } + + @Override + public String getDescription() { + return "JMenuItem with text \"" + label + "\""; + } + + @Override + public String toString() { + return "JMenuItemByLabelFinder{" + "label=" + label + ", comparator=" + comparator + '}'; + } + } + + /** + * Checks component type. + */ + public static class JMenuItemFinder extends Finder { + + /** + * Constructs JMenuItemFinder. + * + * @param sf other searching criteria. + */ + public JMenuItemFinder(ComponentChooser sf) { + super(JMenuItem.class, sf); + } + + /** + * Constructs JMenuItemFinder. + */ + public JMenuItemFinder() { + super(JMenuItem.class); + } + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JMenuOperator.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JMenuOperator.java new file mode 100644 index 00000000000..50e53488d72 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JMenuOperator.java @@ -0,0 +1,1202 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.operators; + +import java.awt.Component; +import java.awt.Container; +import java.util.Hashtable; + +import javax.swing.JMenu; +import javax.swing.JMenuItem; +import javax.swing.JPopupMenu; +import javax.swing.event.MenuListener; + +import org.netbeans.jemmy.Action; +import org.netbeans.jemmy.ComponentChooser; +import org.netbeans.jemmy.Outputable; +import org.netbeans.jemmy.TestOut; +import org.netbeans.jemmy.TimeoutExpiredException; +import org.netbeans.jemmy.Timeoutable; +import org.netbeans.jemmy.Timeouts; +import org.netbeans.jemmy.drivers.DescriptablePathChooser; +import org.netbeans.jemmy.drivers.DriverManager; +import org.netbeans.jemmy.drivers.MenuDriver; + +/** + *

Timeouts used:
+ * JMenuOperator.WaitBeforePopupTimeout - time to sleep before popup expanding + *
+ * JMenuOperator.WaitPopupTimeout - time to wait popup displayed
+ * JMenuOperator.PushMenuTimeout - time for the whole menu operation
+ * JMenuItemOperator.PushMenuTimeout - time between button pressing and + * releasing
+ * ComponentOperator.WaitComponentTimeout - time to wait button displayed
+ * ComponentOperator.WaitComponentEnabledTimeout - time to wait button enabled + *
. + * + * @see org.netbeans.jemmy.Timeouts + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + * + */ +public class JMenuOperator extends JMenuItemOperator + implements Outputable, Timeoutable { + + /** + * Identifier for a "submenu" properties. + * + * @see #getDump + */ + public static final String SUBMENU_PREFIX_DPROP = "Submenu"; + + private final static long WAIT_POPUP_TIMEOUT = 60000; + private final static long WAIT_BEFORE_POPUP_TIMEOUT = 0; + private final static long PUSH_MENU_TIMEOUT = 60000; + + private Timeouts timeouts; + private TestOut output; + private MenuDriver driver; + + /** + * Constructor. + * + * @param menu a component + */ + public JMenuOperator(JMenu menu) { + super(menu); + driver = DriverManager.getMenuDriver(this); + } + + /** + * Constructs a JMenuOperator object. + * + * @param cont a container + * @param chooser a component chooser specifying searching criteria. + * @param index an index between appropriate ones. + */ + public JMenuOperator(ContainerOperator cont, ComponentChooser chooser, int index) { + this((JMenu) cont. + waitSubComponent(new JMenuFinder(chooser), + index)); + copyEnvironment(cont); + } + + /** + * Constructs a JMenuOperator object. + * + * @param cont a container + * @param chooser a component chooser specifying searching criteria. + */ + public JMenuOperator(ContainerOperator cont, ComponentChooser chooser) { + this(cont, chooser, 0); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont a container + * @param text Button text. + * @param index Ordinal component index. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public JMenuOperator(ContainerOperator cont, String text, int index) { + this((JMenu) waitComponent(cont, + new JMenuByLabelFinder(text, + cont.getComparator()), + index)); + copyEnvironment(cont); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont a container + * @param text Button text. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public JMenuOperator(ContainerOperator cont, String text) { + this(cont, text, 0); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont a container + * @param index Ordinal component index. + * @throws TimeoutExpiredException + */ + public JMenuOperator(ContainerOperator cont, int index) { + this((JMenu) waitComponent(cont, + new JMenuFinder(), + index)); + copyEnvironment(cont); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont a container + * @throws TimeoutExpiredException + */ + public JMenuOperator(ContainerOperator cont) { + this(cont, 0); + } + + /** + * Searches JMenu in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @param index Ordinal component index. + * @return JMenu instance or null if component was not found. + */ + public static JMenu findJMenu(Container cont, ComponentChooser chooser, int index) { + return (JMenu) findComponent(cont, new JMenuFinder(chooser), index); + } + + /** + * Searches 0'th JMenu in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @return JMenu instance or null if component was not found. + */ + public static JMenu findJMenu(Container cont, ComponentChooser chooser) { + return findJMenu(cont, chooser, 0); + } + + /** + * Searches JMenu by text. + * + * @param cont Container to search component in. + * @param text Button text. If null, contents is not checked. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @param index Ordinal component index. + * @return JMenu instance or null if component was not found. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + */ + public static JMenu findJMenu(Container cont, String text, boolean ce, boolean ccs, int index) { + return (findJMenu(cont, + new JMenuByLabelFinder(text, + new DefaultStringComparator(ce, ccs)), + index)); + } + + /** + * Searches JMenu by text. + * + * @param cont Container to search component in. + * @param text Button text. If null, contents is not checked. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @return JMenu instance or null if component was not found. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + */ + public static JMenu findJMenu(Container cont, String text, boolean ce, boolean ccs) { + return findJMenu(cont, text, ce, ccs, 0); + } + + /** + * Waits JMenu in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @param index Ordinal component index. + * @return JMenu instance. + * @throws TimeoutExpiredException + */ + public static JMenu waitJMenu(final Container cont, final ComponentChooser chooser, final int index) { + return (JMenu) waitComponent(cont, new JMenuFinder(chooser), index); + } + + /** + * Waits 0'th JMenu in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @return JMenu instance. + * @throws TimeoutExpiredException + */ + public static JMenu waitJMenu(Container cont, ComponentChooser chooser) { + return waitJMenu(cont, chooser, 0); + } + + /** + * Waits JMenu by text. + * + * @param cont Container to search component in. + * @param text Button text. If null, contents is not checked. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @param index Ordinal component index. + * @return JMenu instance. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public static JMenu waitJMenu(Container cont, String text, boolean ce, boolean ccs, int index) { + return (waitJMenu(cont, + new JMenuByLabelFinder(text, + new DefaultStringComparator(ce, ccs)), + index)); + } + + /** + * Waits JMenu by text. + * + * @param cont Container to search component in. + * @param text Button text. If null, contents is not checked. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @return JMenu instance. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public static JMenu waitJMenu(Container cont, String text, boolean ce, boolean ccs) { + return waitJMenu(cont, text, ce, ccs, 0); + } + + public static void performInit() { + Timeouts.initDefault("JMenuOperator.WaitBeforePopupTimeout", WAIT_BEFORE_POPUP_TIMEOUT); + Timeouts.initDefault("JMenuOperator.WaitPopupTimeout", WAIT_POPUP_TIMEOUT); + Timeouts.initDefault("JMenuOperator.PushMenuTimeout", PUSH_MENU_TIMEOUT); + } + + static { + performInit(); + } + + @Override + public void setTimeouts(Timeouts timeouts) { + super.setTimeouts(timeouts); + this.timeouts = timeouts; + } + + @Override + public Timeouts getTimeouts() { + return timeouts; + } + + @Override + public void setOutput(TestOut out) { + super.setOutput(out); + output = out; + } + + @Override + public TestOut getOutput() { + return output; + } + + @Override + public void copyEnvironment(Operator anotherOperator) { + super.copyEnvironment(anotherOperator); + driver = DriverManager.getMenuDriver(this); + } + + /** + * Pushes menu. + * + * @param choosers Array of choosers to find menuItems to push. + * @return Last pushed JMenuItem. + * @throws TimeoutExpiredException + */ + public JMenuItem pushMenu(final ComponentChooser[] choosers) { + return ((JMenuItem) produceTimeRestricted(new Action() { + @Override + public Object launch(Void obj) { + //TDB 1.5 menu workaround + getQueueTool().waitEmpty(); + Object result = driver.pushMenu(JMenuOperator.this, converChoosers(choosers)); + getQueueTool().waitEmpty(); + return result; + } + + @Override + public String getDescription() { + return createDescription(choosers); + } + + @Override + public String toString() { + return "JMenuOperator.pushMenu.Action{description = " + getDescription() + '}'; + } + }, "JMenuOperator.PushMenuTimeout")); + } + + /** + * Executes {@code pushMenu(choosers)} in a separate thread. + * + * @param choosers Array of choosers to find menuItems to push. + * @see #pushMenu(ComponentChooser[]) + */ + public void pushMenuNoBlock(final ComponentChooser[] choosers) { + produceNoBlocking(new NoBlockingAction("Menu pushing") { + @Override + public Object doAction(Void param) { + //TDB 1.5 menu workaround + getQueueTool().waitEmpty(); + Object result = driver.pushMenu(JMenuOperator.this, converChoosers(choosers)); + getQueueTool().waitEmpty(); + return result; + } + }); + } + + /** + * Pushes menu. + * + * @param names an array of menu texts. + * @param comparator a string comparision algorithm + * @return Last pushed JMenuItem. + * @throws TimeoutExpiredException + */ + public JMenuItem pushMenu(String[] names, StringComparator comparator) { + return pushMenu(JMenuItemOperator.createChoosers(names, comparator)); + } + + /** + * Pushes menu. + * + * @param names Menu items texts. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + * @return Last pushed JMenuItem. + * @deprecated Use pushMenu(String[]) or pushMenu(String[], + * StringComparator) + */ + @Deprecated + public JMenuItem pushMenu(String[] names, boolean ce, boolean ccs) { + return pushMenu(names, new DefaultStringComparator(ce, ccs)); + } + + /** + * Executes {@code pushMenu(names, ce, ccs)} in a separate thread. + * + * @param names an array of menu texts. + * @param comparator a string comparision algorithm + */ + public void pushMenuNoBlock(String[] names, StringComparator comparator) { + pushMenuNoBlock(JMenuItemOperator.createChoosers(names, comparator)); + } + + /** + * Executes {@code pushMenu(names, ce, ccs)} in a separate thread. + * + * @param names Menu items texts. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @see #pushMenu(String[], boolean,boolean) + * @deprecated Use pushMenuNoBlock(String[]) or pushMenuNoBlock(String[], + * StringComparator) + */ + @Deprecated + public void pushMenuNoBlock(String[] names, boolean ce, boolean ccs) { + pushMenuNoBlock(names, new DefaultStringComparator(ce, ccs)); + } + + /** + * Pushes menu. Uses StringComparator assigned to this object, + * + * @param names Menu items texts. + * @return Last pushed JMenuItem. + * @throws TimeoutExpiredException + */ + public JMenuItem pushMenu(String[] names) { + return pushMenu(names, getComparator()); + } + + /** + * Executes {@code pushMenu(names)} in a separate thread. + * + * @param names Menu items texts. + * @see #pushMenu(String[]) + */ + public void pushMenuNoBlock(String[] names) { + pushMenuNoBlock(names, getComparator()); + } + + /** + * Pushes menu. + * + * @param path a menu path. + * @param delim a path delimiter. + * @param comparator a string comparision algorithm + * @return Last pushed JMenuItem. + * @throws TimeoutExpiredException + */ + public JMenuItem pushMenu(String path, String delim, StringComparator comparator) { + output.printLine("Pushing " + path + " menu in \n " + toStringSource()); + output.printGolden("Pushing " + path + " menu in \n " + toStringSource()); + return pushMenu(parseString(path, delim), comparator); + } + + /** + * Pushes menu. Uses PathParser assigned to this operator. + * + * @param path a menu path. + * @param comparator a string comparision algorithm + * @return Last pushed JMenuItem. + * @throws TimeoutExpiredException + */ + public JMenuItem pushMenu(String path, StringComparator comparator) { + output.printLine("Pushing " + path + " menu in \n " + toStringSource()); + output.printGolden("Pushing " + path + " menu in \n " + toStringSource()); + return pushMenu(parseString(path), comparator); + } + + /** + * Pushes menu. + * + * @param path String menupath representation ("File/New", for example). + * @param delim String menupath divider ("/"). + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @return Last pushed JMenuItem. + * @throws TimeoutExpiredException + * @deprecated Use pushMenuNoBlock(String) or pushMenuNoBlock(String, + * StringComparator) + */ + @Deprecated + public JMenuItem pushMenu(String path, String delim, boolean ce, boolean ccs) { + return pushMenu(path, delim, new DefaultStringComparator(ce, ccs)); + } + + /** + * Executes {@code pushMenu(names, delim, comparator)} in a separate + * thread. + * + * @param path a menu path. + * @param delim a path delimiter. + * @param comparator a string comparision algorithm + */ + public void pushMenuNoBlock(String path, String delim, StringComparator comparator) { + output.printLine("Pushing " + path + " menu in \n " + toStringSource()); + output.printGolden("Pushing " + path + " menu in \n " + toStringSource()); + pushMenuNoBlock(parseString(path, delim), comparator); + } + + /** + * Executes {@code pushMenu(names, comparator)} in a separate thread. + * Uses PathParser assigned to this operator. + * + * @param path a menu path. + * @param comparator a string comparision algorithm + */ + public void pushMenuNoBlock(String path, StringComparator comparator) { + output.printLine("Pushing " + path + " menu in \n " + toStringSource()); + output.printGolden("Pushing " + path + " menu in \n " + toStringSource()); + pushMenuNoBlock(parseString(path), comparator); + } + + /** + * Executes {@code pushMenu(path, delim, ce, ccs)} in a separate + * thread. + * + * @param path String menupath representation ("File/New", for example). + * @param delim String menupath divider ("/"). + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @see #pushMenu + * @deprecated Use pushMenuNoBlock(String, String) or + * pushMenuNoBlock(String, String, StringComparator) + */ + @Deprecated + public void pushMenuNoBlock(String path, String delim, boolean ce, boolean ccs) { + pushMenuNoBlock(parseString(path, delim), new DefaultStringComparator(ce, ccs)); + } + + /** + * Pushes menu. Uses StringComparator assigned to this object, + * + * @param path String menupath representation ("File/New", for example). + * @param delim String menupath divider ("/"). + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @return Last pushed JMenuItem. + * @throws TimeoutExpiredException + */ + public JMenuItem pushMenu(String path, String delim) { + output.printLine("Pushing " + path + " menu in \n " + toStringSource()); + output.printGolden("Pushing " + path + " menu in \n " + toStringSource()); + return pushMenu(parseString(path, delim)); + } + + /** + * Pushes menu. Uses PathParser assigned to this operator. + * + * @param path String menupath representation ("File/New", for example). + * @return Last pushed JMenuItem. + * @throws TimeoutExpiredException + */ + public JMenuItem pushMenu(String path) { + output.printLine("Pushing " + path + " menu in \n " + toStringSource()); + output.printGolden("Pushing " + path + " menu in \n " + toStringSource()); + return pushMenu(parseString(path)); + } + + /** + * Executes {@code pushMenu(path, delim)} in a separate thread. + * + * @param path String menupath representation ("File/New", for example). + * @param delim String menupath divider ("/"). + */ + public void pushMenuNoBlock(String path, String delim) { + output.printLine("Pushing " + path + " menu in \n " + toStringSource()); + output.printGolden("Pushing " + path + " menu in \n " + toStringSource()); + pushMenuNoBlock(parseString(path, delim)); + } + + /** + * Executes {@code pushMenu(path)} in a separate thread. + * + * @param path String menupath representation ("File/New", for example). + */ + public void pushMenuNoBlock(String path) { + output.printLine("Pushing " + path + " menu in \n " + toStringSource()); + output.printGolden("Pushing " + path + " menu in \n " + toStringSource()); + pushMenuNoBlock(parseString(path)); + } + + public JMenuItemOperator[] showMenuItems(ComponentChooser[] choosers) { + return JMenuItemOperator.getMenuItems((JMenu) pushMenu(choosers), this); + } + + /** + * Shows submenu of menu specified by a {@code path} parameter. + * + * @param path an array of menu texts. + * @param comparator a string comparision algorithm + * @return an array of operators created tor items from the submenu. + * @throws TimeoutExpiredException + */ + public JMenuItemOperator[] showMenuItems(String[] path, StringComparator comparator) { + return showMenuItems(JMenuItemOperator.createChoosers(path, comparator)); + } + + /** + * Shows submenu of menu specified by a {@code path} parameter. Uses + * StringComparator assigned to the operator. + * + * @param path an array of menu texts. + * @return an array of operators created tor items from the submenu. + * @throws TimeoutExpiredException + */ + public JMenuItemOperator[] showMenuItems(String[] path) { + return showMenuItems(path, getComparator()); + } + + /** + * Shows submenu of menu specified by a {@code path} parameter. + * + * @param path a string identifying the menu path. + * @param delim a path delimiter. + * @param comparator a string comparision algorithm + * @return an array of operators created tor items from the submenu. + * @throws TimeoutExpiredException + */ + public JMenuItemOperator[] showMenuItems(String path, String delim, StringComparator comparator) { + return showMenuItems(parseString(path, delim), comparator); + } + + /** + * Shows submenu of menu specified by a {@code path} parameter. Uses + * StringComparator assigned to the operator. + * + * @param path a string identifying the menu path. + * @param delim a path delimiter. + * @return an array of operators created tor items from the submenu. + * @throws TimeoutExpiredException + */ + public JMenuItemOperator[] showMenuItems(String path, String delim) { + return showMenuItems(path, delim, getComparator()); + } + + /** + * Shows submenu of menu specified by a {@code path} parameter. Uses + * PathParser assigned to this operator. + * + * @param path a string identifying the menu path. + * @param comparator a string comparision algorithm + * @return an array of operators created tor items from the submenu. + * @throws TimeoutExpiredException + */ + public JMenuItemOperator[] showMenuItems(String path, StringComparator comparator) { + return showMenuItems(parseString(path), comparator); + } + + /** + * Shows submenu of menu specified by a {@code path} parameter. Uses + * PathParser assigned to this operator. Uses StringComparator assigned to + * the operator. + * + * @param path a string identifying the menu path. + * @return an array of operators created tor items from the submenu. + * @throws TimeoutExpiredException + */ + public JMenuItemOperator[] showMenuItems(String path) { + return showMenuItems(path, getComparator()); + } + + public JMenuItemOperator showMenuItem(ComponentChooser[] choosers) { + ComponentChooser[] parentPath = getParentPath(choosers); + JMenu menu; + if (parentPath.length > 0) { + menu = (JMenu) pushMenu(parentPath); + } else { + push(); + menu = (JMenu) getSource(); + } + JPopupMenuOperator popup = new JPopupMenuOperator(menu.getPopupMenu()); + popup.copyEnvironment(this); + JMenuItemOperator result = new JMenuItemOperator(popup, choosers[choosers.length - 1]); + result.copyEnvironment(this); + return result; + } + + /** + * Expends all menus to show menu item specified by a {@code path} + * parameter. + * + * @param path an array of menu texts. + * @param comparator a string comparision algorithm + * @return an operator for the last menu item in path. + * @throws TimeoutExpiredException + */ + public JMenuItemOperator showMenuItem(String[] path, StringComparator comparator) { + String[] parentPath = getParentPath(path); + JMenu menu; + if (parentPath.length > 0) { + menu = (JMenu) pushMenu(parentPath, comparator); + } else { + push(); + menu = (JMenu) getSource(); + } + JPopupMenuOperator popup = new JPopupMenuOperator(menu.getPopupMenu()); + popup.copyEnvironment(this); + JMenuItemOperator result = new JMenuItemOperator(popup, path[path.length - 1]); + result.copyEnvironment(this); + return result; + } + + /** + * Expands all menus to show menu item specified by a {@code path} + * parameter. + * + * @param path an array of menu texts. + * @return an operator for the last menu item in path. + * @throws TimeoutExpiredException + */ + public JMenuItemOperator showMenuItem(String[] path) { + return showMenuItem(path, getComparator()); + } + + /** + * Expands all menus to show menu item specified by a {@code path} + * parameter. + * + * @param path a string identifying the menu path. + * @param delim a path delimiter. + * @param comparator a string comparision algorithm + * @return an operator for the last menu item in path. + * @throws TimeoutExpiredException + */ + public JMenuItemOperator showMenuItem(String path, String delim, StringComparator comparator) { + return showMenuItem(parseString(path, delim), comparator); + } + + /** + * Expands all menus to show menu item specified by a {@code path} + * parameter. Uses StringComparator assigned to the operator. + * + * @param path a string identifying the menu path. + * @param delim a path delimiter. + * @return an operator for the last menu item in path. + * @throws TimeoutExpiredException + */ + public JMenuItemOperator showMenuItem(String path, String delim) { + return showMenuItem(path, delim, getComparator()); + } + + /** + * Expands all menus to show menu item specified by a {@code path} + * parameter. Uses PathParser assigned to this operator. + * + * @param path a string identifying the menu path. + * @param comparator a string comparision algorithm + * @return an operator for the last menu item in path. + * @throws TimeoutExpiredException + */ + public JMenuItemOperator showMenuItem(String path, StringComparator comparator) { + return showMenuItem(parseString(path), comparator); + } + + /** + * Expands all menus to show menu item specified by a {@code path} + * parameter. Uses PathParser assigned to this operator. Uses + * StringComparator assigned to the operator. + * + * @param path a string identifying the menu path. + * @return an array of operators created tor items from the submenu. + * @throws TimeoutExpiredException + */ + public JMenuItemOperator showMenuItem(String path) { + return showMenuItem(path, getComparator()); + } + + @Override + public Hashtable getDump() { + Hashtable result = super.getDump(); + String[] items = new String[((JMenu) getSource()).getItemCount()]; + for (int i = 0; i < ((JMenu) getSource()).getItemCount(); i++) { + if (((JMenu) getSource()).getItem(i) != null + && ((JMenu) getSource()).getItem(i).getText() != null) { + items[i] = ((JMenu) getSource()).getItem(i).getText(); + } else { + items[i] = "null"; + } + } + addToDump(result, SUBMENU_PREFIX_DPROP, items); + return result; + } + + //////////////////////////////////////////////////////// + //Mapping // + /** + * Maps {@code JMenu.add(String)} through queue + */ + public JMenuItem add(final String string) { + return (runMapping(new MapAction("add") { + @Override + public JMenuItem map() { + return ((JMenu) getSource()).add(string); + } + })); + } + + /** + * Maps {@code JMenu.add(Action)} through queue + */ + public JMenuItem add(final javax.swing.Action action) { + return (runMapping(new MapAction("add") { + @Override + public JMenuItem map() { + return ((JMenu) getSource()).add(action); + } + })); + } + + /** + * Maps {@code JMenu.add(JMenuItem)} through queue + */ + public JMenuItem add(final JMenuItem jMenuItem) { + return (runMapping(new MapAction("add") { + @Override + public JMenuItem map() { + return ((JMenu) getSource()).add(jMenuItem); + } + })); + } + + /** + * Maps {@code JMenu.addMenuListener(MenuListener)} through queue + */ + public void addMenuListener(final MenuListener menuListener) { + runMapping(new MapVoidAction("addMenuListener") { + @Override + public void map() { + ((JMenu) getSource()).addMenuListener(menuListener); + } + }); + } + + /** + * Maps {@code JMenu.addSeparator()} through queue + */ + public void addSeparator() { + runMapping(new MapVoidAction("addSeparator") { + @Override + public void map() { + ((JMenu) getSource()).addSeparator(); + } + }); + } + + /** + * Maps {@code JMenu.getDelay()} through queue + */ + public int getDelay() { + return (runMapping(new MapIntegerAction("getDelay") { + @Override + public int map() { + return ((JMenu) getSource()).getDelay(); + } + })); + } + + /** + * Maps {@code JMenu.getItem(int)} through queue + */ + public JMenuItem getItem(final int i) { + return (runMapping(new MapAction("getItem") { + @Override + public JMenuItem map() { + return ((JMenu) getSource()).getItem(i); + } + })); + } + + /** + * Maps {@code JMenu.getItemCount()} through queue + */ + public int getItemCount() { + return (runMapping(new MapIntegerAction("getItemCount") { + @Override + public int map() { + return ((JMenu) getSource()).getItemCount(); + } + })); + } + + /** + * Maps {@code JMenu.getMenuComponent(int)} through queue + */ + public Component getMenuComponent(final int i) { + return (runMapping(new MapAction("getMenuComponent") { + @Override + public Component map() { + return ((JMenu) getSource()).getMenuComponent(i); + } + })); + } + + /** + * Maps {@code JMenu.getMenuComponentCount()} through queue + */ + public int getMenuComponentCount() { + return (runMapping(new MapIntegerAction("getMenuComponentCount") { + @Override + public int map() { + return ((JMenu) getSource()).getMenuComponentCount(); + } + })); + } + + /** + * Maps {@code JMenu.getMenuComponents()} through queue + */ + public Component[] getMenuComponents() { + return ((Component[]) runMapping(new MapAction("getMenuComponents") { + @Override + public Object map() { + return ((JMenu) getSource()).getMenuComponents(); + } + })); + } + + /** + * Maps {@code JMenu.getPopupMenu()} through queue + */ + public JPopupMenu getPopupMenu() { + return (runMapping(new MapAction("getPopupMenu") { + @Override + public JPopupMenu map() { + return ((JMenu) getSource()).getPopupMenu(); + } + })); + } + + /** + * Maps {@code JMenu.insert(String, int)} through queue + */ + public void insert(final String string, final int i) { + runMapping(new MapVoidAction("insert") { + @Override + public void map() { + ((JMenu) getSource()).insert(string, i); + } + }); + } + + /** + * Maps {@code JMenu.insert(Action, int)} through queue + */ + public JMenuItem insert(final javax.swing.Action action, final int i) { + return (runMapping(new MapAction("insert") { + @Override + public JMenuItem map() { + return ((JMenu) getSource()).insert(action, i); + } + })); + } + + /** + * Maps {@code JMenu.insert(JMenuItem, int)} through queue + */ + public JMenuItem insert(final JMenuItem jMenuItem, final int i) { + return (runMapping(new MapAction("insert") { + @Override + public JMenuItem map() { + return ((JMenu) getSource()).insert(jMenuItem, i); + } + })); + } + + /** + * Maps {@code JMenu.insertSeparator(int)} through queue + */ + public void insertSeparator(final int i) { + runMapping(new MapVoidAction("insertSeparator") { + @Override + public void map() { + ((JMenu) getSource()).insertSeparator(i); + } + }); + } + + /** + * Maps {@code JMenu.isMenuComponent(Component)} through queue + */ + public boolean isMenuComponent(final Component component) { + return (runMapping(new MapBooleanAction("isMenuComponent") { + @Override + public boolean map() { + return ((JMenu) getSource()).isMenuComponent(component); + } + })); + } + + /** + * Maps {@code JMenu.isPopupMenuVisible()} through queue + */ + public boolean isPopupMenuVisible() { + return (runMapping(new MapBooleanAction("isPopupMenuVisible") { + @Override + public boolean map() { + return ((JMenu) getSource()).isPopupMenuVisible(); + } + })); + } + + /** + * Maps {@code JMenu.isTearOff()} through queue + */ + public boolean isTearOff() { + return (runMapping(new MapBooleanAction("isTearOff") { + @Override + public boolean map() { + return ((JMenu) getSource()).isTearOff(); + } + })); + } + + /** + * Maps {@code JMenu.isTopLevelMenu()} through queue + */ + public boolean isTopLevelMenu() { + return (runMapping(new MapBooleanAction("isTopLevelMenu") { + @Override + public boolean map() { + return ((JMenu) getSource()).isTopLevelMenu(); + } + })); + } + + /** + * Maps {@code JMenu.remove(JMenuItem)} through queue + */ + public void remove(final JMenuItem jMenuItem) { + runMapping(new MapVoidAction("remove") { + @Override + public void map() { + ((JMenu) getSource()).remove(jMenuItem); + } + }); + } + + /** + * Maps {@code JMenu.removeMenuListener(MenuListener)} through queue + */ + public void removeMenuListener(final MenuListener menuListener) { + runMapping(new MapVoidAction("removeMenuListener") { + @Override + public void map() { + ((JMenu) getSource()).removeMenuListener(menuListener); + } + }); + } + + /** + * Maps {@code JMenu.setDelay(int)} through queue + */ + public void setDelay(final int i) { + runMapping(new MapVoidAction("setDelay") { + @Override + public void map() { + ((JMenu) getSource()).setDelay(i); + } + }); + } + + /** + * Maps {@code JMenu.setMenuLocation(int, int)} through queue + */ + public void setMenuLocation(final int i, final int i1) { + runMapping(new MapVoidAction("setMenuLocation") { + @Override + public void map() { + ((JMenu) getSource()).setMenuLocation(i, i1); + } + }); + } + + /** + * Maps {@code JMenu.setPopupMenuVisible(boolean)} through queue + */ + public void setPopupMenuVisible(final boolean b) { + runMapping(new MapVoidAction("setPopupMenuVisible") { + @Override + public void map() { + ((JMenu) getSource()).setPopupMenuVisible(b); + } + }); + } + + //End of mapping // + //////////////////////////////////////////////////////// + static String createDescription(ComponentChooser[] choosers) { + StringBuilder description = new StringBuilder("("); + for (int i = 0; i < choosers.length; i++) { + if (i > 0) { + description.append(", "); + } + description.append(choosers[i].getDescription()); + } + description.append(")"); + return "Menu pushing: " + description.toString(); + } + + static DescriptablePathChooser converChoosers(final ComponentChooser[] choosers) { + return (new DescriptablePathChooser() { + @Override + public boolean checkPathComponent(int depth, Object component) { + return choosers[depth].checkComponent((Component) component); + } + + @Override + public int getDepth() { + return choosers.length; + } + + @Override + public String getDescription() { + return createDescription(choosers); + } + + @Override + public String toString() { + return "JMenuOperator.converChoosers.DescriptablePathChooser{description = " + getDescription() + '}'; + } + }); + } + + /** + * Allows to find component by text. + */ + public static class JMenuByLabelFinder implements ComponentChooser { + + String label; + StringComparator comparator; + + /** + * Constructs JMenuByLabelFinder. + * + * @param lb a text pattern + * @param comparator specifies string comparision algorithm. + */ + public JMenuByLabelFinder(String lb, StringComparator comparator) { + label = lb; + this.comparator = comparator; + } + + /** + * Constructs JMenuByLabelFinder. + * + * @param lb a text pattern + */ + public JMenuByLabelFinder(String lb) { + this(lb, Operator.getDefaultStringComparator()); + } + + @Override + public boolean checkComponent(Component comp) { + if (comp instanceof JMenu) { + if (((JMenu) comp).getText() != null) { + return (comparator.equals(((JMenu) comp).getText(), + label)); + } + } + return false; + } + + @Override + public String getDescription() { + return "JMenu with text \"" + label + "\""; + } + + @Override + public String toString() { + return "JMenuByLabelFinder{" + "label=" + label + ", comparator=" + comparator + '}'; + } + } + + /** + * Checks component type. + */ + public static class JMenuFinder extends Finder { + + /** + * Constructs JMenuFinder. + * + * @param sf other searching criteria. + */ + public JMenuFinder(ComponentChooser sf) { + super(JMenu.class, sf); + } + + /** + * Constructs JMenuFinder. + */ + public JMenuFinder() { + super(JMenu.class); + } + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JPasswordFieldOperator.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JPasswordFieldOperator.java new file mode 100644 index 00000000000..2b39f265934 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JPasswordFieldOperator.java @@ -0,0 +1,347 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.operators; + +import java.awt.Container; +import java.util.Hashtable; + +import javax.swing.JPasswordField; + +import org.netbeans.jemmy.ComponentChooser; +import org.netbeans.jemmy.TimeoutExpiredException; + +/** + *

Timeouts used:
+ * JTextComponentOperator.PushKeyTimeout - time between key pressing and + * releasing during text typing
+ * JTextComponentOperator.BetweenKeysTimeout - time to sleep between two chars + * typing
+ * JTextComponentOperator.ChangeCaretPositionTimeout - maximum time to chenge + * caret position
+ * JTextComponentOperator.TypeTextTimeout - maximum time to type text
+ * ComponentOperator.WaitComponentTimeout - time to wait component displayed + *
+ * ComponentOperator.WaitFocusTimeout - time to wait component focus
+ * JScrollBarOperator.OneScrollClickTimeout - time for one scroll click
+ * JScrollBarOperator.WholeScrollTimeout - time for the whole scrolling
. + * + * @see org.netbeans.jemmy.Timeouts + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + * + */ +public class JPasswordFieldOperator extends JTextFieldOperator { + + /** + * Identifier for a "echo char" property. + * + * @see #getDump + */ + public static final String ECHO_CHAR_DPROP = "Echo char"; + + /** + * Constructor. + * + * @param b a component + */ + public JPasswordFieldOperator(JPasswordField b) { + super(b); + } + + /** + * Constructs a JPasswordFieldOperator object. + * + * @param cont a container + * @param chooser a component chooser specifying searching criteria. + * @param index an index between appropriate ones. + */ + public JPasswordFieldOperator(ContainerOperator cont, ComponentChooser chooser, int index) { + this((JPasswordField) cont. + waitSubComponent(new JPasswordFieldFinder(chooser), + index)); + copyEnvironment(cont); + } + + /** + * Constructs a JPasswordFieldOperator object. + * + * @param cont a container + * @param chooser a component chooser specifying searching criteria. + */ + public JPasswordFieldOperator(ContainerOperator cont, ComponentChooser chooser) { + this(cont, chooser, 0); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont a container + * @param text Button text. + * @param index Ordinal component index. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public JPasswordFieldOperator(ContainerOperator cont, String text, int index) { + this((JPasswordField) waitComponent(cont, + new JPasswordFieldFinder(new JTextComponentOperator.JTextComponentByTextFinder(text, + cont.getComparator())), + index)); + copyEnvironment(cont); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont a container + * @param text Button text. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public JPasswordFieldOperator(ContainerOperator cont, String text) { + this(cont, text, 0); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont a container + * @param index Ordinal component index. + * @throws TimeoutExpiredException + */ + public JPasswordFieldOperator(ContainerOperator cont, int index) { + this((JPasswordField) waitComponent(cont, + new JPasswordFieldFinder(), + index)); + copyEnvironment(cont); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont a container + * @throws TimeoutExpiredException + */ + public JPasswordFieldOperator(ContainerOperator cont) { + this(cont, 0); + } + + /** + * Searches JPasswordField in container. + * + * @param cont Container to search component in. + * @param chooser a component chooser specifying searching criteria. + * @param index Ordinal component index. + * @return JPasswordField instance or null if component was not found. + */ + public static JPasswordField findJPasswordField(Container cont, ComponentChooser chooser, int index) { + return (JPasswordField) findJTextComponent(cont, new JPasswordFieldFinder(chooser), index); + } + + /** + * Searches JPasswordField in container. + * + * @param cont Container to search component in. + * @param chooser a component chooser specifying searching criteria. + * @return JPasswordField instance or null if component was not found. + */ + public static JPasswordField findJPasswordField(Container cont, ComponentChooser chooser) { + return findJPasswordField(cont, chooser, 0); + } + + /** + * Searches JPasswordField by text. + * + * @param cont Container to search component in. + * @param text Component text. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @param index Ordinal component index. + * @return JPasswordField instance or null if component was not found. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + */ + public static JPasswordField findJPasswordField(Container cont, String text, boolean ce, boolean ccs, int index) { + return (findJPasswordField(cont, + new JPasswordFieldFinder(new JTextComponentOperator.JTextComponentByTextFinder(text, + new DefaultStringComparator(ce, ccs))), + index)); + } + + /** + * Searches JPasswordField by text. + * + * @param cont Container to search component in. + * @param text Component text. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @return JPasswordField instance or null if component was not found. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + */ + public static JPasswordField findJPasswordField(Container cont, String text, boolean ce, boolean ccs) { + return findJPasswordField(cont, text, ce, ccs, 0); + } + + /** + * Waits JPasswordField in container. + * + * @param cont Container to search component in. + * @param chooser a component chooser specifying searching criteria. + * @param index Ordinal component index. + * @return JPasswordField instance. + * @throws TimeoutExpiredException + */ + public static JPasswordField waitJPasswordField(Container cont, ComponentChooser chooser, int index) { + return (JPasswordField) waitJTextComponent(cont, new JPasswordFieldFinder(chooser), index); + } + + /** + * Waits JPasswordField in container. + * + * @param cont Container to search component in. + * @param chooser a component chooser specifying searching criteria. + * @return JPasswordField instance. + * @throws TimeoutExpiredException + */ + public static JPasswordField waitJPasswordField(Container cont, ComponentChooser chooser) { + return waitJPasswordField(cont, chooser, 0); + } + + /** + * Waits JPasswordField by text. + * + * @param cont Container to search component in. + * @param text Component text. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @param index Ordinal component index. + * @return JPasswordField instance. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public static JPasswordField waitJPasswordField(Container cont, String text, boolean ce, boolean ccs, int index) { + return (waitJPasswordField(cont, + new JPasswordFieldFinder(new JTextComponentOperator.JTextComponentByTextFinder(text, + new DefaultStringComparator(ce, ccs))), + index)); + } + + /** + * Waits JPasswordField by text. + * + * @param cont Container to search component in. + * @param text Component text. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @return JPasswordField instance. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public static JPasswordField waitJPasswordField(Container cont, String text, boolean ce, boolean ccs) { + return waitJPasswordField(cont, text, ce, ccs, 0); + } + + @Override + public Hashtable getDump() { + Hashtable result = super.getDump(); + result.put(ECHO_CHAR_DPROP, + Character.toString(((JPasswordField) getSource()).getEchoChar())); + return result; + } + + //////////////////////////////////////////////////////// + //Mapping // + /** + * Maps {@code JPasswordField.echoCharIsSet()} through queue + */ + public boolean echoCharIsSet() { + return (runMapping(new MapBooleanAction("echoCharIsSet") { + @Override + public boolean map() { + return ((JPasswordField) getSource()).echoCharIsSet(); + } + })); + } + + /** + * Maps {@code JPasswordField.getEchoChar()} through queue + */ + public char getEchoChar() { + return (runMapping(new MapCharacterAction("getEchoChar") { + @Override + public char map() { + return ((JPasswordField) getSource()).getEchoChar(); + } + })); + } + + /** + * Maps {@code JPasswordField.getPassword()} through queue + */ + public char[] getPassword() { + return ((char[]) runMapping(new MapAction("getPassword") { + @Override + public Object map() { + return ((JPasswordField) getSource()).getPassword(); + } + })); + } + + /** + * Maps {@code JPasswordField.setEchoChar(char)} through queue + */ + public void setEchoChar(final char c) { + runMapping(new MapVoidAction("setEchoChar") { + @Override + public void map() { + ((JPasswordField) getSource()).setEchoChar(c); + } + }); + } + + //End of mapping // + //////////////////////////////////////////////////////// + /** + * Checks component type. + */ + public static class JPasswordFieldFinder extends Finder { + + /** + * Constructs JPasswordFieldFinder. + * + * @param sf other searching criteria. + */ + public JPasswordFieldFinder(ComponentChooser sf) { + super(JPasswordField.class, sf); + } + + /** + * Constructs JPasswordFieldFinder. + */ + public JPasswordFieldFinder() { + super(JPasswordField.class); + } + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JPopupMenuOperator.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JPopupMenuOperator.java new file mode 100644 index 00000000000..2b4af3db3c2 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JPopupMenuOperator.java @@ -0,0 +1,1384 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.operators; + +import java.awt.Component; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.Insets; +import java.awt.Window; +import java.awt.event.KeyEvent; +import java.awt.event.MouseEvent; +import java.util.Hashtable; + +import javax.swing.JMenu; +import javax.swing.JMenuItem; +import javax.swing.JPopupMenu; +import javax.swing.MenuElement; +import javax.swing.MenuSelectionManager; +import javax.swing.SingleSelectionModel; +import javax.swing.event.PopupMenuListener; +import javax.swing.plaf.PopupMenuUI; + +import org.netbeans.jemmy.Action; +import org.netbeans.jemmy.ComponentChooser; +import org.netbeans.jemmy.ComponentSearcher; +import org.netbeans.jemmy.JemmyException; +import org.netbeans.jemmy.JemmyProperties; +import org.netbeans.jemmy.Outputable; +import org.netbeans.jemmy.QueueTool; +import org.netbeans.jemmy.TestOut; +import org.netbeans.jemmy.TimeoutExpiredException; +import org.netbeans.jemmy.Timeoutable; +import org.netbeans.jemmy.Timeouts; +import org.netbeans.jemmy.WindowWaiter; +import org.netbeans.jemmy.drivers.DriverManager; +import org.netbeans.jemmy.drivers.MenuDriver; + +/** + *

Timeouts used:
+ * JMenuOperator.WaitBeforePopupTimeout - time to sleep before popup expanding + *
+ * JMenuOperator.WaitPopupTimeout - time to wait popup displayed
+ * ComponentOperator.WaitComponentTimeout - time to wait button displayed
+ * WindowWaiter.WaitWindowTimeout - time to wait popup window displayed
+ * WindowWaiter.AfterWindowTimeout - time to sleep after popup window has been + * dispayed
. + * + * @see org.netbeans.jemmy.Timeouts + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + * + */ +public class JPopupMenuOperator extends JComponentOperator + implements Outputable, Timeoutable { + + /** + * Identifier for a "label" property. + * + * @see #getDump + */ + public static final String LABEL_DPROP = "Label"; + + private TestOut output; + private Timeouts timeouts; + private MenuDriver driver; + + /** + * Constructor. + * + * @param popup a component + */ + public JPopupMenuOperator(JPopupMenu popup) { + super(popup); + driver = DriverManager.getMenuDriver(getClass()); + } + + /** + * Constructs a JPopupMenuOperator object. + * + * @param cont a container + * @param chooser a component chooser specifying searching criteria. + * @param index an index between appropriate ones. + */ + public JPopupMenuOperator(ContainerOperator cont, ComponentChooser chooser, int index) { + this((JPopupMenu) cont. + waitSubComponent(new JPopupMenuFinder(chooser), + index)); + copyEnvironment(cont); + } + + /** + * Constructs a JPopupMenuOperator object. + * + * @param cont a container + * @param chooser a component chooser specifying searching criteria. + */ + public JPopupMenuOperator(ContainerOperator cont, ComponentChooser chooser) { + this(cont, chooser, 0); + } + + /** + * Constructor. Waits component first. Constructor can be used in + * complicated cases when output or timeouts should differ from default. + * + * @param env an operator to copy environment from. + * @throws TimeoutExpiredException + */ + public JPopupMenuOperator(Operator env) { + this((JPopupMenu) waitComponent(WindowOperator. + waitWindow(new JPopupWindowFinder(), + 0, + env.getTimeouts(), + env.getOutput()), + new JPopupMenuFinder(), + 0, + env.getTimeouts(), + env.getOutput())); + copyEnvironment(env); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont a container + * @throws TimeoutExpiredException + */ + public JPopupMenuOperator(ContainerOperator cont) { + this((JPopupMenu) waitComponent(cont, + new JPopupMenuFinder(), + 0)); + copyEnvironment(cont); + } + + /** + * Constructor. Waits component first. + * + * @throws TimeoutExpiredException + */ + public JPopupMenuOperator() { + this(Operator.getEnvironmentOperator()); + } + + /** + * Searches JPopupMenu in container. + * + * @param cont Container to search component in. + * @param chooser a component chooser specifying searching criteria. + * @param index Ordinal component index. + * @return JPopupMenu instance or null if component was not found. + */ + public static JPopupMenu findJPopupMenu(Container cont, ComponentChooser chooser, int index) { + return (JPopupMenu) findComponent(cont, new JPopupMenuFinder(chooser), index); + } + + /** + * Searches JPopupMenu in container. + * + * @param cont Container to search component in. + * @param chooser a component chooser specifying searching criteria. + * @return JPopupMenu instance or null if component was not found. + */ + public static JPopupMenu findJPopupMenu(Container cont, ComponentChooser chooser) { + return findJPopupMenu(cont, chooser, 0); + } + + /** + * Waits JPopupMenu in container. + * + * @param cont Container to search component in. + * @param chooser a component chooser specifying searching criteria. + * @param index Ordinal component index. + * @return JPopupMenu instance. + * @throws TimeoutExpiredException + */ + public static JPopupMenu waitJPopupMenu(Container cont, ComponentChooser chooser, int index) { + return (JPopupMenu) waitComponent(cont, new JPopupMenuFinder(chooser), index); + } + + /** + * Waits JPopupMenu in container. + * + * @param cont Container to search component in. + * @param chooser a component chooser specifying searching criteria. + * @return JPopupMenu instance. + * @throws TimeoutExpiredException + */ + public static JPopupMenu waitJPopupMenu(Container cont, ComponentChooser chooser) { + return waitJPopupMenu(cont, chooser, 0); + } + + /** + * Searches for a window which contains JPopupMenu. + * + * @param chooser a component chooser specifying criteria for JPopupMenu. + * @return a window containing JPopupMenu. + */ + public static Window findJPopupWindow(ComponentChooser chooser) { + return WindowWaiter.getWindow(new JPopupWindowFinder(chooser)); + } + + /** + * Waits for a window which contains JPopupMenu. + * + * @param chooser a component chooser specifying criteria for JPopupMenu. + * @return a window containing JPopupMenu. + * @throws TimeoutExpiredException + */ + public static Window waitJPopupWindow(ComponentChooser chooser) { + try { + return (new WindowWaiter()).waitWindow(new JPopupWindowFinder(chooser)); + } catch (InterruptedException e) { + return null; + } + } + + /** + * Waits popup defined by {@code popupChooser} parameter. + * + * @param popupChooser a component chooser specifying criteria for + * JPopupMenu. + * @return a JPopupMenu fitting the criteria. + */ + public static JPopupMenuOperator waitJPopupMenu(final ComponentChooser popupChooser) { + try { + WindowOperator wind = new WindowOperator(new WindowWaiter().waitWindow(new ComponentChooser() { + @Override + public boolean checkComponent(Component comp) { + ComponentSearcher searcher = new ComponentSearcher((Container) comp); + searcher.setOutput(JemmyProperties.getCurrentOutput().createErrorOutput()); + return searcher.findComponent(popupChooser) != null; + } + + @Override + public String getDescription() { + return "Window containing \"" + popupChooser.getDescription() + "\" popup"; + } + + @Override + public String toString() { + return "JPopupMenuOperator.waitJPopupMenu.ComponentChooser{description = " + getDescription() + '}'; + } + })); + return new JPopupMenuOperator(wind); + } catch (InterruptedException e) { + throw (new JemmyException("Popup waiting has been interrupted", e)); + } + } + + /** + * Waits popup containing menu item with {@code menuItemText} text. + * + * @param menuItemText a text of a menu item which supposed to be displayed + * inside the popup. + * @return a JPopupMenu fitting the criteria. + */ + public static JPopupMenuOperator waitJPopupMenu(final String menuItemText) { + return (waitJPopupMenu(new ComponentChooser() { + @Override + public boolean checkComponent(Component comp) { + if (comp instanceof JPopupMenu) { + ComponentSearcher searcher = new ComponentSearcher((Container) comp); + searcher.setOutput(JemmyProperties.getCurrentOutput().createErrorOutput()); + return (searcher.findComponent(new JMenuItemOperator.JMenuItemByLabelFinder(menuItemText, + Operator.getDefaultStringComparator())) + != null); + } else { + return false; + } + } + + @Override + public String getDescription() { + return "Popup containing \"" + menuItemText + "\" menu item"; + } + + @Override + public String toString() { + return "JPopupMenuOperator.waitJPopupMenu.ComponentChooser{description = " + getDescription() + '}'; + } + })); + } + + /** + * Calls popup by clicking on (x, y) point in component. + * + * @param oper Component operator to call popup on. + * @param x X coordinate of click point in the component coordinate system. + * @param y Y coordinate of click point in the component coordinate system. + * @param mouseButton Mouse button mask to call popup. + * @return an opened JPopupMenu + * @throws TimeoutExpiredException + */ + public static JPopupMenu callPopup(final ComponentOperator oper, int x, int y, int mouseButton) { + oper.makeComponentVisible(); + //1.5 workaround + if (System.getProperty("java.specification.version").compareTo("1.4") > 0) { + QueueTool qt = new QueueTool(); + qt.setOutput(oper.getOutput().createErrorOutput()); + qt.waitEmpty(10); + qt.waitEmpty(10); + qt.waitEmpty(10); + } + //end of 1.5 workaround + oper.clickForPopup(x, y, mouseButton); + oper.getTimeouts().sleep("JMenuOperator.WaitBeforePopupTimeout"); + return (waitJPopupMenu(waitJPopupWindow(new ComponentChooser() { + @Override + public boolean checkComponent(Component cmp) { + Component invoker = ((JPopupMenu) cmp).getInvoker(); + return (invoker == oper.getSource() + || (invoker instanceof Container + && ((Container) invoker).isAncestorOf(oper.getSource())) + || (oper.getSource() instanceof Container + && ((Container) oper.getSource()).isAncestorOf(invoker))); + } + + @Override + public String getDescription() { + return "Popup menu"; + } + + @Override + public String toString() { + return "JPopupMenuOperator.callPopup.ComponentChooser{description = " + getDescription() + '}'; + } + }), + ComponentSearcher.getTrueChooser("Popup menu"))); + } + + /** + * Calls popup by clicking on (x, y) point in component. + * + * @param oper Component operator to call popup on. + * @param x X coordinate of click point in the component coordinate system. + * @param y Y coordinate of click point in the component coordinate system. + * @return an opened JPopupMenu + * @see ComponentOperator#getPopupMouseButton() + * @throws TimeoutExpiredException + */ + public static JPopupMenu callPopup(ComponentOperator oper, int x, int y) { + return callPopup(oper, x, y, getPopupMouseButton()); + } + + /** + * Calls popup by clicking on (x, y) point in component. + * + * @param comp Component to call popup on. + * @param x X coordinate of click point in the component coordinate system. + * @param y Y coordinate of click point in the component coordinate system. + * @param mouseButton Mouse button mask to call popup. + * @return an opened JPopupMenu + * @throws TimeoutExpiredException + */ + public static JPopupMenu callPopup(Component comp, int x, int y, int mouseButton) { + return callPopup(new ComponentOperator(comp), x, y, mouseButton); + } + + /** + * Calls popup by clicking on (x, y) point in component. + * + * @param comp Component to call popup on. + * @param x X coordinate of click point in the component coordinate system. + * @param y Y coordinate of click point in the component coordinate system. + * @return an opened JPopupMenu + * @see ComponentOperator#getPopupMouseButton() + * @throws TimeoutExpiredException + */ + public static JPopupMenu callPopup(Component comp, int x, int y) { + return callPopup(comp, x, y, getPopupMouseButton()); + } + + /** + * Calls popup by clicking component center. + * + * @param comp Component to call popup on. + * @param mouseButton Mouse button mask to call popup. + * @return an opened JPopupMenu + * @throws TimeoutExpiredException + */ + public static JPopupMenu callPopup(Component comp, int mouseButton) { + ComponentOperator co = new ComponentOperator(comp); + co.makeComponentVisible(); + co.clickForPopup(mouseButton); + return (findJPopupMenu(waitJPopupWindow(ComponentSearcher.getTrueChooser("Popup menu window")), + ComponentSearcher.getTrueChooser("Popup menu"))); + } + + /** + * Calls popup by clicking component center. + * + * @param comp Component to call popup on. + * @return an opened JPopupMenu + * @see ComponentOperator#getPopupMouseButton() + * @throws TimeoutExpiredException + */ + public static JPopupMenu callPopup(Component comp) { + return callPopup(comp, getPopupMouseButton()); + } + + static { + //necessary to init timeouts + JMenuOperator.performInit(); + } + + @Override + public void setOutput(TestOut out) { + super.setOutput(out); + output = out; + } + + @Override + public TestOut getOutput() { + return output; + } + + @Override + public void setTimeouts(Timeouts times) { + super.setTimeouts(times); + timeouts = times; + } + + @Override + public Timeouts getTimeouts() { + return timeouts; + } + + @Override + public void copyEnvironment(Operator anotherOperator) { + super.copyEnvironment(anotherOperator); + driver = DriverManager.getMenuDriver(this); + } + + /** + * Pushes menu. + * + * @param choosers Array of choosers to find menuItems to push. + * @return Last pushed JMenuItem. + * @throws TimeoutExpiredException + */ + public JMenuItem pushMenu(final ComponentChooser[] choosers) { + return ((JMenuItem) produceTimeRestricted(new Action() { + @Override + public Object launch(Void obj) { + //TDB 1.5 menu workaround + getQueueTool().waitEmpty(); + Object result = driver.pushMenu(JPopupMenuOperator.this, + JMenuOperator.converChoosers(choosers)); + getQueueTool().waitEmpty(); + return result; + } + + @Override + public String getDescription() { + return JMenuOperator.createDescription(choosers); + } + + @Override + public String toString() { + return "JPopupMenuOperator.pushMenu.ComponentChooser{description = " + getDescription() + '}'; + } + }, "JMenuOperator.PushMenuTimeout")); + } + + /** + * Executes {@code pushMenu(choosers)} in a separate thread. + * + * @param choosers Array of choosers to find menuItems to push. + * @see #pushMenu(ComponentChooser[]) + */ + public void pushMenuNoBlock(final ComponentChooser[] choosers) { + produceNoBlocking(new NoBlockingAction("Menu pushing") { + @Override + public Object doAction(Void param) { + //TDB 1.5 menu workaround + getQueueTool().waitEmpty(); + Object result = driver.pushMenu(JPopupMenuOperator.this, + JMenuOperator.converChoosers(choosers)); + getQueueTool().waitEmpty(); + return result; + } + }); + } + + /** + * Pushes menu. + * + * @param names an array of menu texts. + * @param comparator a string comparision algorithm + * @return Last pushed JMenuItem. + * @throws TimeoutExpiredException + */ + public JMenuItem pushMenu(String[] names, StringComparator comparator) { + return pushMenu(JMenuItemOperator.createChoosers(names, comparator)); + } + + /** + * Pushes menu. + * + * @param names Menu items texts. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @return Last pushed JMenuItem. + * @throws TimeoutExpiredException + * @deprecated Use pushMenu(String[]) or pushMenu(String[], + * StringComparator) + */ + @Deprecated + public JMenuItem pushMenu(String[] names, boolean ce, boolean ccs) { + return pushMenu(names, new DefaultStringComparator(ce, ccs)); + } + + /** + * Executes {@code pushMenu(names, ce, ccs)} in a separate thread. + * + * @param names an array of menu texts. + * @param comparator a string comparision algorithm + */ + public void pushMenuNoBlock(String[] names, StringComparator comparator) { + pushMenuNoBlock(JMenuItemOperator.createChoosers(names, comparator)); + } + + /** + * Executes {@code pushMenu(names, ce, ccs)} in a separate thread. + * + * @param names Menu items texts. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @see #pushMenu(String[], boolean,boolean) + * @deprecated Use pushMenuNoBlock(String[]) or pushMenuNoBlock(String[], + * StringComparator) + */ + @Deprecated + public void pushMenuNoBlock(String[] names, boolean ce, boolean ccs) { + pushMenuNoBlock(names, new DefaultStringComparator(ce, ccs)); + } + + /** + * Pushes menu. + * + * @param names Menu items texts. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @return Last pushed JMenuItem. + * @throws TimeoutExpiredException + */ + public JMenuItem pushMenu(String[] names) { + return pushMenu(names, getComparator()); + } + + /** + * Executes {@code pushMenu(names)} in a separate thread. + * + * @param names Menu items texts. + * @see #pushMenu(String[]) + */ + public void pushMenuNoBlock(String[] names) { + pushMenuNoBlock(names, getComparator()); + } + + /** + * Pushes menu. + * + * @param path a menu path. + * @param delim a path delimiter. + * @param comparator a string comparision algorithm + * @return Last pushed JMenuItem. + * @throws TimeoutExpiredException + */ + public JMenuItem pushMenu(String path, String delim, StringComparator comparator) { + return pushMenu(parseString(path, delim), comparator); + } + + /** + * Pushes menu. Uses PathParser assigned to this operator. + * + * @param path a menu path. + * @param comparator a string comparision algorithm + * @return Last pushed JMenuItem. + * @throws TimeoutExpiredException + */ + public JMenuItem pushMenu(String path, StringComparator comparator) { + return pushMenu(parseString(path), comparator); + } + + /** + * Pushes menu. + * + * @param path String menupath representation ("File/New", for example). + * @param delim String menupath divider ("/"). + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @return Last pushed JMenuItem. + * @throws TimeoutExpiredException + * @deprecated Use pushMenu(String, String) or pushMenu(String, String, + * StringComparator) + */ + @Deprecated + public JMenuItem pushMenu(String path, String delim, boolean ce, boolean ccs) { + return pushMenu(parseString(path, delim), ce, ccs); + } + + /** + * Executes {@code pushMenu(names, delim, comparator)} in a separate + * thread. + * + * @param path a menu path. + * @param delim a path delimiter. + * @param comparator a string comparision algorithm + */ + public void pushMenuNoBlock(String path, String delim, StringComparator comparator) { + pushMenuNoBlock(parseString(path, delim), comparator); + } + + /** + * Executes {@code pushMenu(names, comparator)} in a separate thread. + * Uses PathParser assigned to this operator. + * + * @param path a menu path. + * @param comparator a string comparision algorithm + */ + public void pushMenuNoBlock(String path, StringComparator comparator) { + pushMenuNoBlock(parseString(path), comparator); + } + + /** + * Executes {@code pushMenu(path, delim, ce, ccs)} in a separate + * thread. + * + * @param path String menupath representation ("File/New", for example). + * @param delim String menupath divider ("/"). + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @see #pushMenu + * @deprecated Use pushMenuNoBlock(String, String) or + * pushMenuNoBlock(String, String, StringComparator) + */ + @Deprecated + public void pushMenuNoBlock(String path, String delim, boolean ce, boolean ccs) { + pushMenuNoBlock(parseString(path, delim), ce, ccs); + } + + /** + * Pushes menu. + * + * @param path String menupath representation ("File/New", for example). + * @param delim String menupath divider ("/"). + * @return Last pushed JMenuItem. + * @throws TimeoutExpiredException + */ + public JMenuItem pushMenu(String path, String delim) { + return pushMenu(parseString(path, delim)); + } + + /** + * Pushes menu. Uses PathParser assigned to this operator. + * + * @param path String menupath representation ("File/New", for example). + * @return Last pushed JMenuItem. + * @throws TimeoutExpiredException + */ + public JMenuItem pushMenu(String path) { + return pushMenu(parseString(path)); + } + + /** + * Executes {@code pushMenu(path, delim)} in a separate thread. + * + * @param path String menupath representation ("File/New", for example). + * @param delim String menupath divider ("/"). + */ + public void pushMenuNoBlock(String path, String delim) { + pushMenuNoBlock(parseString(path, delim)); + } + + /** + * Executes {@code pushMenu(path)} in a separate thread. + * + * @param path String menupath representation ("File/New", for example). + */ + public void pushMenuNoBlock(String path) { + pushMenuNoBlock(parseString(path)); + } + + public JMenuItemOperator[] showMenuItems(ComponentChooser[] choosers) { + if (choosers == null || choosers.length == 0) { + return JMenuItemOperator.getMenuItems((MenuElement) getSource(), this); + } else { + return JMenuItemOperator.getMenuItems((JMenu) pushMenu(choosers), this); + } + } + + /** + * Shows submenu of menu specified by a {@code path} parameter. + * + * @param path an array of menu texts. + * @param comparator a string comparision algorithm + * @return an array of operators created tor items from the submenu. + * @throws TimeoutExpiredException + */ + public JMenuItemOperator[] showMenuItems(String[] path, StringComparator comparator) { + if (path == null || path.length == 0) { + return JMenuItemOperator.getMenuItems((MenuElement) getSource(), this); + } else { + return JMenuItemOperator.getMenuItems((JMenu) pushMenu(path, comparator), this); + } + } + + /** + * Shows submenu of menu specified by a {@code path} parameter. Uses + * StringComparator assigned to the operator. + * + * @param path an array of menu texts. + * @return an array of operators created tor items from the submenu. + * @throws TimeoutExpiredException + */ + public JMenuItemOperator[] showMenuItems(String[] path) { + return showMenuItems(path, getComparator()); + } + + /** + * Shows submenu of menu specified by a {@code path} parameter. + * + * @param path a string identifying the menu path. + * @param delim a path delimiter. + * @param comparator a string comparision algorithm + * @return an array of operators created tor items from the submenu. + * @throws TimeoutExpiredException + */ + public JMenuItemOperator[] showMenuItems(String path, String delim, StringComparator comparator) { + return showMenuItems(parseString(path, delim), comparator); + } + + /** + * Shows submenu of menu specified by a {@code path} parameter. Uses + * PathParser assigned to this operator. + * + * @param path a string identifying the menu path. + * @param comparator a string comparision algorithm + * @return an array of operators created tor items from the submenu. + * @throws TimeoutExpiredException + */ + public JMenuItemOperator[] showMenuItems(String path, StringComparator comparator) { + return showMenuItems(parseString(path), comparator); + } + + /** + * Shows submenu of menu specified by a {@code path} parameter. Uses + * StringComparator assigned to the operator. + * + * @param path a string identifying the menu path. + * @param delim a path delimiter. + * @return an array of operators created tor items from the submenu. + * @throws TimeoutExpiredException + */ + public JMenuItemOperator[] showMenuItems(String path, String delim) { + return showMenuItems(path, delim, getComparator()); + } + + /** + * Shows submenu of menu specified by a {@code path} parameter. Uses + * PathParser assigned to this operator. Uses StringComparator assigned to + * the operator. + * + * @param path a string identifying the menu path. + * @return an array of operators created tor items from the submenu. + * @throws TimeoutExpiredException + */ + public JMenuItemOperator[] showMenuItems(String path) { + return showMenuItems(path, getComparator()); + } + + public JMenuItemOperator showMenuItem(ComponentChooser[] choosers) { + ComponentChooser[] parentPath = getParentPath(choosers); + JMenu menu; + ContainerOperator menuCont; + if (parentPath.length > 0) { + menu = (JMenu) pushMenu(getParentPath(choosers)); + menuCont = new ContainerOperator<>(menu.getPopupMenu()); + menuCont.copyEnvironment(this); + } else { + menuCont = this; + } + JMenuItemOperator result = new JMenuItemOperator(menuCont, choosers[choosers.length - 1]); + result.copyEnvironment(this); + return result; + } + + /** + * Expends all menus to show menu item specified by a {@code path} + * parameter. + * + * @param path an array of menu texts. + * @param comparator a string comparision algorithm + * @return an operator for the last menu item in path. + * @throws TimeoutExpiredException + */ + public JMenuItemOperator showMenuItem(String[] path, StringComparator comparator) { + String[] parentPath = getParentPath(path); + JMenu menu; + ContainerOperator menuCont; + if (parentPath.length > 0) { + menu = (JMenu) pushMenu(getParentPath(path), comparator); + menuCont = new ContainerOperator<>(menu.getPopupMenu()); + menuCont.copyEnvironment(this); + } else { + menuCont = this; + } + JMenuItemOperator result = new JMenuItemOperator(menuCont, path[path.length - 1]); + result.copyEnvironment(this); + return result; + } + + /** + * Expands all menus to show menu item specified by a {@code path} + * parameter. + * + * @param path an array of menu texts. + * @return an operator for the last menu item in path. + * @throws TimeoutExpiredException + */ + public JMenuItemOperator showMenuItem(String[] path) { + return showMenuItem(path, getComparator()); + } + + /** + * Expands all menus to show menu item specified by a {@code path} + * parameter. + * + * @param path a string identifying the menu path. + * @param delim a path delimiter. + * @param comparator a string comparision algorithm + * @return an operator for the last menu item in path. + * @throws TimeoutExpiredException + */ + public JMenuItemOperator showMenuItem(String path, String delim, StringComparator comparator) { + return showMenuItem(parseString(path, delim), comparator); + } + + /** + * Expands all menus to show menu item specified by a {@code path} + * parameter. Uses PathParser assigned to this operator. + * + * @param path a string identifying the menu path. + * @param comparator a string comparision algorithm + * @return an operator for the last menu item in path. + * @throws TimeoutExpiredException + */ + public JMenuItemOperator showMenuItem(String path, StringComparator comparator) { + return showMenuItem(parseString(path), comparator); + } + + /** + * Expands all menus to show menu item specified by a {@code path} + * parameter. Uses StringComparator assigned to the operator. + * + * @param path a string identifying the menu path. + * @param delim a path delimiter. + * @return an operator for the last menu item in path. + * @throws TimeoutExpiredException + */ + public JMenuItemOperator showMenuItem(String path, String delim) { + return showMenuItem(path, delim, getComparator()); + } + + /** + * Expands all menus to show menu item specified by a {@code path} + * parameter. Uses PathParser assigned to this operator. Uses + * StringComparator assigned to the operator. + * + * @param path a string identifying the menu path. + * @return an array of operators created tor items from the submenu. + * @throws TimeoutExpiredException + */ + public JMenuItemOperator showMenuItem(String path) { + return showMenuItem(path, getComparator()); + } + + @Override + public Hashtable getDump() { + Hashtable result = super.getDump(); + if (((JPopupMenu) getSource()).getLabel() != null) { + result.put(LABEL_DPROP, ((JPopupMenu) getSource()).getLabel()); + } + return result; + } + + //////////////////////////////////////////////////////// + //Mapping // + /** + * Maps {@code JPopupMenu.add(String)} through queue + */ + public JMenuItem add(final String string) { + return (runMapping(new MapAction("add") { + @Override + public JMenuItem map() { + return ((JPopupMenu) getSource()).add(string); + } + })); + } + + /** + * Maps {@code JPopupMenu.add(Action)} through queue + */ + public JMenuItem add(final javax.swing.Action action) { + return (runMapping(new MapAction("add") { + @Override + public JMenuItem map() { + return ((JPopupMenu) getSource()).add(action); + } + })); + } + + /** + * Maps {@code JPopupMenu.add(JMenuItem)} through queue + */ + public JMenuItem add(final JMenuItem jMenuItem) { + return (runMapping(new MapAction("add") { + @Override + public JMenuItem map() { + return ((JPopupMenu) getSource()).add(jMenuItem); + } + })); + } + + /** + * Maps {@code JPopupMenu.addPopupMenuListener(PopupMenuListener)} + * through queue + */ + public void addPopupMenuListener(final PopupMenuListener popupMenuListener) { + runMapping(new MapVoidAction("addPopupMenuListener") { + @Override + public void map() { + ((JPopupMenu) getSource()).addPopupMenuListener(popupMenuListener); + } + }); + } + + /** + * Maps {@code JPopupMenu.addSeparator()} through queue + */ + public void addSeparator() { + runMapping(new MapVoidAction("addSeparator") { + @Override + public void map() { + ((JPopupMenu) getSource()).addSeparator(); + } + }); + } + + /** + * Maps {@code JPopupMenu.getComponentIndex(Component)} through queue + */ + public int getComponentIndex(final Component component) { + return (runMapping(new MapIntegerAction("getComponentIndex") { + @Override + public int map() { + return ((JPopupMenu) getSource()).getComponentIndex(component); + } + })); + } + + /** + * Maps {@code JPopupMenu.getInvoker()} through queue + */ + public Component getInvoker() { + return (runMapping(new MapAction("getInvoker") { + @Override + public Component map() { + return ((JPopupMenu) getSource()).getInvoker(); + } + })); + } + + /** + * Maps {@code JPopupMenu.getLabel()} through queue + */ + public String getLabel() { + return (runMapping(new MapAction("getLabel") { + @Override + public String map() { + return ((JPopupMenu) getSource()).getLabel(); + } + })); + } + + /** + * Maps {@code JPopupMenu.getMargin()} through queue + */ + public Insets getMargin() { + return (runMapping(new MapAction("getMargin") { + @Override + public Insets map() { + return ((JPopupMenu) getSource()).getMargin(); + } + })); + } + + /** + * Maps {@code JPopupMenu.getSelectionModel()} through queue + */ + public SingleSelectionModel getSelectionModel() { + return (runMapping(new MapAction("getSelectionModel") { + @Override + public SingleSelectionModel map() { + return ((JPopupMenu) getSource()).getSelectionModel(); + } + })); + } + + /** + * Maps {@code JPopupMenu.getSubElements()} through queue + */ + public MenuElement[] getSubElements() { + return ((MenuElement[]) runMapping(new MapAction("getSubElements") { + @Override + public Object map() { + return ((JPopupMenu) getSource()).getSubElements(); + } + })); + } + + /** + * Maps {@code JPopupMenu.getUI()} through queue + */ + public PopupMenuUI getUI() { + return (runMapping(new MapAction("getUI") { + @Override + public PopupMenuUI map() { + return ((JPopupMenu) getSource()).getUI(); + } + })); + } + + /** + * Maps {@code JPopupMenu.insert(Component, int)} through queue + */ + public void insert(final Component component, final int i) { + runMapping(new MapVoidAction("insert") { + @Override + public void map() { + ((JPopupMenu) getSource()).insert(component, i); + } + }); + } + + /** + * Maps {@code JPopupMenu.insert(Action, int)} through queue + */ + public void insert(final javax.swing.Action action, final int i) { + runMapping(new MapVoidAction("insert") { + @Override + public void map() { + ((JPopupMenu) getSource()).insert(action, i); + } + }); + } + + /** + * Maps {@code JPopupMenu.isBorderPainted()} through queue + */ + public boolean isBorderPainted() { + return (runMapping(new MapBooleanAction("isBorderPainted") { + @Override + public boolean map() { + return ((JPopupMenu) getSource()).isBorderPainted(); + } + })); + } + + /** + * Maps {@code JPopupMenu.isLightWeightPopupEnabled()} through queue + */ + public boolean isLightWeightPopupEnabled() { + return (runMapping(new MapBooleanAction("isLightWeightPopupEnabled") { + @Override + public boolean map() { + return ((JPopupMenu) getSource()).isLightWeightPopupEnabled(); + } + })); + } + + /** + * Maps {@code JPopupMenu.menuSelectionChanged(boolean)} through queue + */ + public void menuSelectionChanged(final boolean b) { + runMapping(new MapVoidAction("menuSelectionChanged") { + @Override + public void map() { + ((JPopupMenu) getSource()).menuSelectionChanged(b); + } + }); + } + + /** + * Maps {@code JPopupMenu.pack()} through queue + */ + public void pack() { + runMapping(new MapVoidAction("pack") { + @Override + public void map() { + ((JPopupMenu) getSource()).pack(); + } + }); + } + + /** + * Maps + * {@code JPopupMenu.processKeyEvent(KeyEvent, MenuElement[], MenuSelectionManager)} + * through queue + */ + public void processKeyEvent(final KeyEvent keyEvent, final MenuElement[] menuElement, final MenuSelectionManager menuSelectionManager) { + runMapping(new MapVoidAction("processKeyEvent") { + @Override + public void map() { + ((JPopupMenu) getSource()).processKeyEvent(keyEvent, menuElement, menuSelectionManager); + } + }); + } + + /** + * Maps + * {@code JPopupMenu.processMouseEvent(MouseEvent, MenuElement[], MenuSelectionManager)} + * through queue + */ + public void processMouseEvent(final MouseEvent mouseEvent, final MenuElement[] menuElement, final MenuSelectionManager menuSelectionManager) { + runMapping(new MapVoidAction("processMouseEvent") { + @Override + public void map() { + ((JPopupMenu) getSource()).processMouseEvent(mouseEvent, menuElement, menuSelectionManager); + } + }); + } + + /** + * Maps {@code JPopupMenu.removePopupMenuListener(PopupMenuListener)} + * through queue + */ + public void removePopupMenuListener(final PopupMenuListener popupMenuListener) { + runMapping(new MapVoidAction("removePopupMenuListener") { + @Override + public void map() { + ((JPopupMenu) getSource()).removePopupMenuListener(popupMenuListener); + } + }); + } + + /** + * Maps {@code JPopupMenu.setBorderPainted(boolean)} through queue + */ + public void setBorderPainted(final boolean b) { + runMapping(new MapVoidAction("setBorderPainted") { + @Override + public void map() { + ((JPopupMenu) getSource()).setBorderPainted(b); + } + }); + } + + /** + * Maps {@code JPopupMenu.setInvoker(Component)} through queue + */ + public void setInvoker(final Component component) { + runMapping(new MapVoidAction("setInvoker") { + @Override + public void map() { + ((JPopupMenu) getSource()).setInvoker(component); + } + }); + } + + /** + * Maps {@code JPopupMenu.setLabel(String)} through queue + */ + public void setLabel(final String string) { + runMapping(new MapVoidAction("setLabel") { + @Override + public void map() { + ((JPopupMenu) getSource()).setLabel(string); + } + }); + } + + /** + * Maps {@code JPopupMenu.setLightWeightPopupEnabled(boolean)} through queue + */ + public void setLightWeightPopupEnabled(final boolean b) { + runMapping(new MapVoidAction("setLightWeightPopupEnabled") { + @Override + public void map() { + ((JPopupMenu) getSource()).setLightWeightPopupEnabled(b); + } + }); + } + + /** + * Maps {@code JPopupMenu.setPopupSize(int, int)} through queue + */ + public void setPopupSize(final int i, final int i1) { + runMapping(new MapVoidAction("setPopupSize") { + @Override + public void map() { + ((JPopupMenu) getSource()).setPopupSize(i, i1); + } + }); + } + + /** + * Maps {@code JPopupMenu.setPopupSize(Dimension)} through queue + */ + public void setPopupSize(final Dimension dimension) { + runMapping(new MapVoidAction("setPopupSize") { + @Override + public void map() { + ((JPopupMenu) getSource()).setPopupSize(dimension); + } + }); + } + + /** + * Maps {@code JPopupMenu.setSelected(Component)} through queue + */ + public void setSelected(final Component component) { + runMapping(new MapVoidAction("setSelected") { + @Override + public void map() { + ((JPopupMenu) getSource()).setSelected(component); + } + }); + } + + /** + * Maps {@code JPopupMenu.setSelectionModel(SingleSelectionModel)} + * through queue + */ + public void setSelectionModel(final SingleSelectionModel singleSelectionModel) { + runMapping(new MapVoidAction("setSelectionModel") { + @Override + public void map() { + ((JPopupMenu) getSource()).setSelectionModel(singleSelectionModel); + } + }); + } + + /** + * Maps {@code JPopupMenu.setUI(PopupMenuUI)} through queue + */ + public void setUI(final PopupMenuUI popupMenuUI) { + runMapping(new MapVoidAction("setUI") { + @Override + public void map() { + ((JPopupMenu) getSource()).setUI(popupMenuUI); + } + }); + } + + /** + * Maps {@code JPopupMenu.show(Component, int, int)} through queue + */ + public void show(final Component component, final int i, final int i1) { + runMapping(new MapVoidAction("show") { + @Override + public void map() { + ((JPopupMenu) getSource()).show(component, i, i1); + } + }); + } + + //End of mapping // + //////////////////////////////////////////////////////// + /** + * Checks component type. + */ + public static class JPopupMenuFinder extends Finder { + + /** + * Constructs JPopupMenuFinder. + * + * @param sf other searching criteria. + */ + public JPopupMenuFinder(ComponentChooser sf) { + super(JPopupMenu.class, sf); + } + + /** + * Constructs JPopupMenuFinder. + */ + public JPopupMenuFinder() { + super(JPopupMenu.class); + } + + @Override + public boolean checkComponent(Component comp) { + return (comp.isShowing() + && super.checkComponent(comp)); + } + } + + /** + * Allwos to find a window containing JPopupMenu. + */ + public static class JPopupWindowFinder implements ComponentChooser { + + ComponentChooser subFinder; + ComponentChooser ppFinder; + + /** + * Constructs JPopupWindowFinder. + * + * @param sf searching criteria for a JPopupMenu inside the window.. + */ + public JPopupWindowFinder(ComponentChooser sf) { + subFinder = new JPopupMenuFinder(sf); + ppFinder = new ComponentChooser() { + @Override + public boolean checkComponent(Component comp) { + return (comp.isShowing() + && comp.isVisible() + && subFinder.checkComponent(comp)); + } + + @Override + public String getDescription() { + return subFinder.getDescription(); + } + + @Override + public String toString() { + return "JPopupMenuOperator.JPopupWindowFinder.ComponentChooser{description = " + getDescription() + '}'; + } + }; + } + + /** + * Constructs JPopupWindowFinder. + */ + public JPopupWindowFinder() { + this(ComponentSearcher.getTrueChooser("Any JPopupWindow")); + } + + @Override + public boolean checkComponent(Component comp) { + if (comp.isShowing() && comp instanceof Window) { + ComponentSearcher cs = new ComponentSearcher((Container) comp); + cs.setOutput(JemmyProperties.getCurrentOutput().createErrorOutput()); + return (cs.findComponent(ppFinder) + != null); + } + return false; + } + + @Override + public String getDescription() { + return subFinder.getDescription(); + } + + @Override + public String toString() { + return "JPopupWindowFinder{" + "subFinder=" + subFinder + ", ppFinder=" + ppFinder + '}'; + } + } + +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JProgressBarOperator.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JProgressBarOperator.java new file mode 100644 index 00000000000..951eef05ee9 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JProgressBarOperator.java @@ -0,0 +1,679 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.operators; + +import java.awt.Component; +import java.awt.Container; +import java.util.Hashtable; + +import javax.swing.BoundedRangeModel; +import javax.swing.JProgressBar; +import javax.swing.event.ChangeListener; +import javax.swing.plaf.ProgressBarUI; + +import org.netbeans.jemmy.ComponentChooser; +import org.netbeans.jemmy.ComponentSearcher; +import org.netbeans.jemmy.JemmyException; +import org.netbeans.jemmy.Outputable; +import org.netbeans.jemmy.TestOut; +import org.netbeans.jemmy.TimeoutExpiredException; +import org.netbeans.jemmy.Timeoutable; +import org.netbeans.jemmy.Timeouts; +import org.netbeans.jemmy.Waitable; +import org.netbeans.jemmy.Waiter; + +/** + * + * Operator is supposed to be used to operate with an instance of + * javax.swing.JProgressBar class. + * + *

Timeouts used:
+ * JProgressBarOperator.WaitValueTimeout - used from waitValue() method
. + * + * @see org.netbeans.jemmy.Timeouts + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + */ +public class JProgressBarOperator extends JComponentOperator + implements Timeoutable, Outputable { + + /** + * Identifier for a "minimum" property. + * + * @see #getDump + */ + public static final String MINIMUM_DPROP = "Minimum"; + + /** + * Identifier for a "maximum" property. + * + * @see #getDump + */ + public static final String MAXIMUM_DPROP = "Maximum"; + + /** + * Identifier for a "value" property. + * + * @see #getDump + */ + public static final String VALUE_DPROP = "Value"; + + private static long WAIT_VALUE_TIMEOUT = 60000; + + private Timeouts timeouts; + private TestOut output; + + /** + * Constructor. + * + * @param b JProgressBar component. + */ + public JProgressBarOperator(JProgressBar b) { + super(b); + } + + /** + * Constructs a JProgressBarOperator object. + * + * @param cont a container + * @param chooser a component chooser specifying searching criteria. + * @param index an index between appropriate ones. + */ + public JProgressBarOperator(ContainerOperator cont, ComponentChooser chooser, int index) { + this((JProgressBar) cont. + waitSubComponent(new JProgressBarFinder(chooser), + index)); + copyEnvironment(cont); + } + + /** + * Constructs a JProgressBarOperator object. + * + * @param cont a container + * @param chooser a component chooser specifying searching criteria. + */ + public JProgressBarOperator(ContainerOperator cont, ComponentChooser chooser) { + this(cont, chooser, 0); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont Operator pointing a container to search component in. + * @param index Ordinal component index. + * @throws TimeoutExpiredException + */ + public JProgressBarOperator(ContainerOperator cont, int index) { + this((JProgressBar) waitComponent(cont, + new JProgressBarFinder(), + index)); + copyEnvironment(cont); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont Operator pointing a container to search component in. + * @throws TimeoutExpiredException + */ + public JProgressBarOperator(ContainerOperator cont) { + this(cont, 0); + } + + /** + * Searches JProgressBar in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @param index Ordinal component index. + * @return JProgressBar instance or null if component was not found. + */ + public static JProgressBar findJProgressBar(Container cont, ComponentChooser chooser, int index) { + return (JProgressBar) findComponent(cont, new JProgressBarFinder(chooser), index); + } + + /** + * Searches 0'th JProgressBar in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @return JProgressBar instance or null if component was not found. + */ + public static JProgressBar findJProgressBar(Container cont, ComponentChooser chooser) { + return findJProgressBar(cont, chooser, 0); + } + + /** + * Searches JProgressBar in container. + * + * @param cont Container to search component in. + * @param index Ordinal component index. + * @return JProgressBar instance or null if component was not found. + */ + public static JProgressBar findJProgressBar(Container cont, int index) { + return findJProgressBar(cont, ComponentSearcher.getTrueChooser(Integer.toString(index) + "'th JProgressBar instance"), index); + } + + /** + * Searches 0'th JProgressBar in container. + * + * @param cont Container to search component in. + * @return JProgressBar instance or null if component was not found. + */ + public static JProgressBar findJProgressBar(Container cont) { + return findJProgressBar(cont, 0); + } + + /** + * Waits JProgressBar in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @param index Ordinal component index. + * @return JProgressBar instance or null if component was not displayed. + * @throws TimeoutExpiredException + */ + public static JProgressBar waitJProgressBar(Container cont, ComponentChooser chooser, int index) { + return (JProgressBar) waitComponent(cont, new JProgressBarFinder(chooser), index); + } + + /** + * Waits 0'th JProgressBar in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @return JProgressBar instance or null if component was not displayed. + * @throws TimeoutExpiredException + */ + public static JProgressBar waitJProgressBar(Container cont, ComponentChooser chooser) { + return waitJProgressBar(cont, chooser, 0); + } + + /** + * Waits JProgressBar in container. + * + * @param cont Container to search component in. + * @param index Ordinal component index. + * @return JProgressBar instance or null if component was not displayed. + * @throws TimeoutExpiredException + */ + public static JProgressBar waitJProgressBar(Container cont, int index) { + return waitJProgressBar(cont, ComponentSearcher.getTrueChooser(Integer.toString(index) + "'th JProgressBar instance"), index); + } + + /** + * Waits 0'th JProgressBar in container. + * + * @param cont Container to search component in. + * @return JProgressBar instance or null if component was not displayed. + * @throws TimeoutExpiredException + */ + public static JProgressBar waitJProgressBar(Container cont) { + return waitJProgressBar(cont, 0); + } + + static { + Timeouts.initDefault("JProgressBarOperator.WaitValueTimeout", WAIT_VALUE_TIMEOUT); + } + + @Override + public void setTimeouts(Timeouts timeouts) { + this.timeouts = timeouts; + super.setTimeouts(timeouts); + } + + @Override + public Timeouts getTimeouts() { + return timeouts; + } + + @Override + public void setOutput(TestOut out) { + output = out; + super.setOutput(output.createErrorOutput()); + } + + @Override + public TestOut getOutput() { + return output; + } + + /** + * Waits for criteria defined by {@code chooser} to be reached. + * + * @param chooser an object specifying waiting criteria. + * @see #waitValue(int) + * @deprecated Use waitState(ComponentChooser) instead. + */ + @Deprecated + public void waitValue(final ValueChooser chooser) { + output.printLine("Wait \"" + chooser.getDescription() + + "\" value in progressbar\n : " + + toStringSource()); + output.printGolden("Wait \"" + chooser.getDescription() + + "\" value in progressbar"); + Waiter wt = new Waiter<>(new Waitable() { + @Override + public String actionProduced(Void obj) { + return (chooser.checkValue(((JProgressBar) getSource()).getValue()) + ? "" : null); + } + + @Override + public String getDescription() { + return "\"" + chooser.getDescription() + "\" value"; + } + + @Override + public String toString() { + return "JProgressBarOperator.waitValue.Waitable{description = " + getDescription() + '}'; + } + }); + wt.setTimeoutsToCloneOf(timeouts, "JProgressBarOperator.WaitValueTimeout"); + wt.setOutput(output.createErrorOutput()); + try { + wt.waitAction(null); + } catch (InterruptedException e) { + throw (new JemmyException("Exception during progressbar value waiting", e)); + } + } + + /** + * Waits progress bar value to be less or equal to {@code value} + * parameter. Can be used for typical progress bar (when value is + * increasing). + * + * @param value a value to reach. + * @see Operator#waitState(ComponentChooser) + */ + public void waitValue(final int value) { + output.printLine("Wait \"" + value + + "\" value in progressbar\n : " + + toStringSource()); + output.printGolden("Wait \"" + value + + "\" value in progressbar"); + waitState(new ComponentChooser() { + @Override + public boolean checkComponent(Component comp) { + return ((JProgressBar) comp).getValue() >= value; + } + + @Override + public String getDescription() { + return "greater then " + Integer.toString(value); + } + + @Override + public String toString() { + return "JProgressBarOperator.waitValue.ComponentChooser{description = " + getDescription() + '}'; + } + }); + } + + /** + * Waits progress bar string to match {@code value} parameter. + * + * @param value a string value. + * @see Operator#waitState(ComponentChooser) + */ + public void waitValue(final String value) { + output.printLine("Wait \"" + value + + "\" string in progressbar\n : " + + toStringSource()); + output.printGolden("Wait \"" + value + + "\" string in progressbar"); + waitState(new ComponentChooser() { + @Override + public boolean checkComponent(Component comp) { + return getComparator().equals(((JProgressBar) comp).getString(), value); + } + + @Override + public String getDescription() { + return "'" + value + "' string"; + } + + @Override + public String toString() { + return "JProgressBarOperator.waitValue.ComponentChooser{description = " + getDescription() + '}'; + } + }); + } + + @Override + public Hashtable getDump() { + Hashtable result = super.getDump(); + result.put(MINIMUM_DPROP, Integer.toString(((JProgressBar) getSource()).getMinimum())); + result.put(MAXIMUM_DPROP, Integer.toString(((JProgressBar) getSource()).getMaximum())); + result.put(VALUE_DPROP, Integer.toString(((JProgressBar) getSource()).getValue())); + return result; + } + + //////////////////////////////////////////////////////// + //Mapping // + /** + * Maps {@code JProgressBar.addChangeListener(ChangeListener)} through queue + */ + public void addChangeListener(final ChangeListener changeListener) { + runMapping(new MapVoidAction("addChangeListener") { + @Override + public void map() { + ((JProgressBar) getSource()).addChangeListener(changeListener); + } + }); + } + + /** + * Maps {@code JProgressBar.getMaximum()} through queue + */ + public int getMaximum() { + return (runMapping(new MapIntegerAction("getMaximum") { + @Override + public int map() { + return ((JProgressBar) getSource()).getMaximum(); + } + })); + } + + /** + * Maps {@code JProgressBar.getMinimum()} through queue + */ + public int getMinimum() { + return (runMapping(new MapIntegerAction("getMinimum") { + @Override + public int map() { + return ((JProgressBar) getSource()).getMinimum(); + } + })); + } + + /** + * Maps {@code JProgressBar.getModel()} through queue + */ + public BoundedRangeModel getModel() { + return (runMapping(new MapAction("getModel") { + @Override + public BoundedRangeModel map() { + return ((JProgressBar) getSource()).getModel(); + } + })); + } + + /** + * Maps {@code JProgressBar.getOrientation()} through queue + */ + public int getOrientation() { + return (runMapping(new MapIntegerAction("getOrientation") { + @Override + public int map() { + return ((JProgressBar) getSource()).getOrientation(); + } + })); + } + + /** + * Maps {@code JProgressBar.getPercentComplete()} through queue + */ + public double getPercentComplete() { + return (runMapping(new MapDoubleAction("getPercentComplete") { + @Override + public double map() { + return ((JProgressBar) getSource()).getPercentComplete(); + } + })); + } + + /** + * Maps {@code JProgressBar.getString()} through queue + */ + public String getString() { + return (runMapping(new MapAction("getString") { + @Override + public String map() { + return ((JProgressBar) getSource()).getString(); + } + })); + } + + /** + * Maps {@code JProgressBar.getUI()} through queue + */ + public ProgressBarUI getUI() { + return (runMapping(new MapAction("getUI") { + @Override + public ProgressBarUI map() { + return ((JProgressBar) getSource()).getUI(); + } + })); + } + + /** + * Maps {@code JProgressBar.getValue()} through queue + */ + public int getValue() { + return (runMapping(new MapIntegerAction("getValue") { + @Override + public int map() { + return ((JProgressBar) getSource()).getValue(); + } + })); + } + + /** + * Maps {@code JProgressBar.isBorderPainted()} through queue + */ + public boolean isBorderPainted() { + return (runMapping(new MapBooleanAction("isBorderPainted") { + @Override + public boolean map() { + return ((JProgressBar) getSource()).isBorderPainted(); + } + })); + } + + /** + * Maps {@code JProgressBar.isStringPainted()} through queue + */ + public boolean isStringPainted() { + return (runMapping(new MapBooleanAction("isStringPainted") { + @Override + public boolean map() { + return ((JProgressBar) getSource()).isStringPainted(); + } + })); + } + + /** + * Maps {@code JProgressBar.removeChangeListener(ChangeListener)} + * through queue + */ + public void removeChangeListener(final ChangeListener changeListener) { + runMapping(new MapVoidAction("removeChangeListener") { + @Override + public void map() { + ((JProgressBar) getSource()).removeChangeListener(changeListener); + } + }); + } + + /** + * Maps {@code JProgressBar.setBorderPainted(boolean)} through queue + */ + public void setBorderPainted(final boolean b) { + runMapping(new MapVoidAction("setBorderPainted") { + @Override + public void map() { + ((JProgressBar) getSource()).setBorderPainted(b); + } + }); + } + + /** + * Maps {@code JProgressBar.setMaximum(int)} through queue + */ + public void setMaximum(final int i) { + runMapping(new MapVoidAction("setMaximum") { + @Override + public void map() { + ((JProgressBar) getSource()).setMaximum(i); + } + }); + } + + /** + * Maps {@code JProgressBar.setMinimum(int)} through queue + */ + public void setMinimum(final int i) { + runMapping(new MapVoidAction("setMinimum") { + @Override + public void map() { + ((JProgressBar) getSource()).setMinimum(i); + } + }); + } + + /** + * Maps {@code JProgressBar.setModel(BoundedRangeModel)} through queue + */ + public void setModel(final BoundedRangeModel boundedRangeModel) { + runMapping(new MapVoidAction("setModel") { + @Override + public void map() { + ((JProgressBar) getSource()).setModel(boundedRangeModel); + } + }); + } + + /** + * Maps {@code JProgressBar.setOrientation(int)} through queue + */ + public void setOrientation(final int i) { + runMapping(new MapVoidAction("setOrientation") { + @Override + public void map() { + ((JProgressBar) getSource()).setOrientation(i); + } + }); + } + + /** + * Maps {@code JProgressBar.setString(String)} through queue + */ + public void setString(final String string) { + runMapping(new MapVoidAction("setString") { + @Override + public void map() { + ((JProgressBar) getSource()).setString(string); + } + }); + } + + /** + * Maps {@code JProgressBar.setStringPainted(boolean)} through queue + */ + public void setStringPainted(final boolean b) { + runMapping(new MapVoidAction("setStringPainted") { + @Override + public void map() { + ((JProgressBar) getSource()).setStringPainted(b); + } + }); + } + + /** + * Maps {@code JProgressBar.setUI(ProgressBarUI)} through queue + */ + public void setUI(final ProgressBarUI progressBarUI) { + runMapping(new MapVoidAction("setUI") { + @Override + public void map() { + ((JProgressBar) getSource()).setUI(progressBarUI); + } + }); + } + + /** + * Maps {@code JProgressBar.setValue(int)} through queue + */ + public void setValue(final int i) { + runMapping(new MapVoidAction("setValue") { + @Override + public void map() { + ((JProgressBar) getSource()).setValue(i); + } + }); + } + + //End of mapping // + //////////////////////////////////////////////////////// + /** + * Interface to define criteria for {@code waitValue(ValueChooser)} + * method. + * + * @see #waitValue(int) + * @deprecated Use waitState(ComponentChooser) instead. + */ + @Deprecated + public interface ValueChooser { + + /** + * Check if criteria jave been reached. + * + * @param value current value. + * @return true if criteria reached. + */ + public boolean checkValue(int value); + + /** + * A description. + * + * @return a description. + */ + public String getDescription(); + } + + /** + * Checks component type. + */ + public static class JProgressBarFinder extends Finder { + + /** + * Constructs JProgressBarFinder. + * + * @param sf other searching criteria. + */ + public JProgressBarFinder(ComponentChooser sf) { + super(JProgressBar.class, sf); + } + + /** + * Constructs JProgressBarFinder. + */ + public JProgressBarFinder() { + super(JProgressBar.class); + } + } + +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JRadioButtonMenuItemOperator.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JRadioButtonMenuItemOperator.java new file mode 100644 index 00000000000..b8d499ccdc4 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JRadioButtonMenuItemOperator.java @@ -0,0 +1,212 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.operators; + +import java.awt.Component; + +import javax.swing.JRadioButtonMenuItem; + +import org.netbeans.jemmy.ComponentChooser; +import org.netbeans.jemmy.JemmyProperties; + +/** + * + *

Timeouts used:
+ * JMenuItemOperator.PushMenuTimeout - time between button pressing and + * releasing
+ * ComponentOperator.WaitComponentTimeout - time to wait button displayed
+ * ComponentOperator.WaitComponentEnabledTimeout - time to wait button enabled + *
. + * + * @see org.netbeans.jemmy.Timeouts + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + * + */ +public class JRadioButtonMenuItemOperator extends JMenuItemOperator { + + /** + * Constructor. + * + * @param item a component + */ + public JRadioButtonMenuItemOperator(JRadioButtonMenuItem item) { + super(item); + setTimeouts(JemmyProperties.getProperties().getTimeouts()); + setOutput(JemmyProperties.getProperties().getOutput()); + } + + /** + * Constructs a JRadioButtonMenuItemOperator object. + * + * @param cont a container + * @param chooser a component chooser specifying searching criteria. + * @param index an index between appropriate ones. + */ + public JRadioButtonMenuItemOperator(ContainerOperator cont, ComponentChooser chooser, int index) { + this((JRadioButtonMenuItem) cont. + waitSubComponent(new JRadioButtonMenuItemFinder(chooser), + index)); + copyEnvironment(cont); + } + + /** + * Constructs a JRadioButtonMenuItemOperator object. + * + * @param cont a container + * @param chooser a component chooser specifying searching criteria. + */ + public JRadioButtonMenuItemOperator(ContainerOperator cont, ComponentChooser chooser) { + this(cont, chooser, 0); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont a container + * @param text Button text. + * @param index Ordinal component index. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + */ + public JRadioButtonMenuItemOperator(ContainerOperator cont, String text, int index) { + this((JRadioButtonMenuItem) waitComponent(cont, + new JRadioButtonMenuItemByLabelFinder(text, + cont.getComparator()), + index)); + setTimeouts(cont.getTimeouts()); + setOutput(cont.getOutput()); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont a container + * @param text Button text. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + */ + public JRadioButtonMenuItemOperator(ContainerOperator cont, String text) { + this(cont, text, 0); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont a container + * @param index Ordinal component index. + */ + public JRadioButtonMenuItemOperator(ContainerOperator cont, int index) { + this((JRadioButtonMenuItem) waitComponent(cont, + new JRadioButtonMenuItemFinder(), + index)); + copyEnvironment(cont); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont a container + */ + public JRadioButtonMenuItemOperator(ContainerOperator cont) { + this(cont, 0); + } + + //////////////////////////////////////////////////////// + //Mapping // + //End of mapping // + //////////////////////////////////////////////////////// + /** + * Allows to find component by text. + */ + public static class JRadioButtonMenuItemByLabelFinder implements ComponentChooser { + + String label; + StringComparator comparator; + + /** + * Constructs JRadioButtonMenuItemByLabelFinder. + * + * @param lb a text pattern + * @param comparator specifies string comparision algorithm. + */ + public JRadioButtonMenuItemByLabelFinder(String lb, StringComparator comparator) { + label = lb; + this.comparator = comparator; + } + + /** + * Constructs JRadioButtonMenuItemByLabelFinder. + * + * @param lb a text pattern + */ + public JRadioButtonMenuItemByLabelFinder(String lb) { + this(lb, Operator.getDefaultStringComparator()); + } + + @Override + public boolean checkComponent(Component comp) { + if (comp instanceof JRadioButtonMenuItem) { + if (((JRadioButtonMenuItem) comp).getText() != null) { + return (comparator.equals(((JRadioButtonMenuItem) comp).getText(), + label)); + } + } + return false; + } + + @Override + public String getDescription() { + return "JRadioButtonMenuItem with text \"" + label + "\""; + } + + @Override + public String toString() { + return "JRadioButtonMenuItemByLabelFinder{" + "label=" + label + ", comparator=" + comparator + '}'; + } + } + + /** + * Checks component type. + */ + public static class JRadioButtonMenuItemFinder extends Finder { + + /** + * Constructs JRadioButtonMenuItemFinder. + * + * @param sf other searching criteria. + */ + public JRadioButtonMenuItemFinder(ComponentChooser sf) { + super(JRadioButtonMenuItem.class, sf); + } + + /** + * Constructs JRadioButtonMenuItemFinder. + */ + public JRadioButtonMenuItemFinder() { + super(JRadioButtonMenuItem.class); + } + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JRadioButtonOperator.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JRadioButtonOperator.java new file mode 100644 index 00000000000..c25b4656a88 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JRadioButtonOperator.java @@ -0,0 +1,273 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.operators; + +import java.awt.Container; + +import javax.swing.JRadioButton; + +import org.netbeans.jemmy.ComponentChooser; +import org.netbeans.jemmy.TimeoutExpiredException; + +/** + * + *

Timeouts used:
+ * AbstractButtonOperator.PushButtonTimeout - time between button pressing and + * releasing
+ * ComponentOperator.WaitComponentTimeout - time to wait button displayed
+ * ComponentOperator.WaitComponentEnabledTimeout - time to wait button enabled + *
. + * + * @see org.netbeans.jemmy.Timeouts + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + * + */ +public class JRadioButtonOperator extends JToggleButtonOperator { + + /** + * Constructor. + * + * @param b a component + */ + public JRadioButtonOperator(JRadioButton b) { + super(b); + } + + /** + * Constructs a JRadioButtonOperator object. + * + * @param cont a container + * @param chooser a component chooser specifying searching criteria. + * @param index an index between appropriate ones. + */ + public JRadioButtonOperator(ContainerOperator cont, ComponentChooser chooser, int index) { + this((JRadioButton) cont. + waitSubComponent(new JRadioButtonFinder(chooser), + index)); + copyEnvironment(cont); + } + + /** + * Constructs a JRadioButtonOperator object. + * + * @param cont a container + * @param chooser a component chooser specifying searching criteria. + */ + public JRadioButtonOperator(ContainerOperator cont, ComponentChooser chooser) { + this(cont, chooser, 0); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont a container + * @param text Button text. + * @param index Ordinal component index. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public JRadioButtonOperator(ContainerOperator cont, String text, int index) { + this((JRadioButton) waitComponent(cont, + new JRadioButtonFinder(new AbstractButtonOperator.AbstractButtonByLabelFinder(text, + cont.getComparator())), + index)); + copyEnvironment(cont); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont a container + * @param text Button text. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public JRadioButtonOperator(ContainerOperator cont, String text) { + this(cont, text, 0); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont a container + * @param index Ordinal component index. + * @throws TimeoutExpiredException + */ + public JRadioButtonOperator(ContainerOperator cont, int index) { + this((JRadioButton) waitComponent(cont, + new JRadioButtonFinder(), + index)); + copyEnvironment(cont); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont a container + * @throws TimeoutExpiredException + */ + public JRadioButtonOperator(ContainerOperator cont) { + this(cont, 0); + } + + /** + * Searches JRadioButton in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @param index Ordinal component index. + * @return JRadioButton instance or null if component was not found. + */ + public static JRadioButton findJRadioButton(Container cont, ComponentChooser chooser, int index) { + return (JRadioButton) findJToggleButton(cont, new JRadioButtonFinder(chooser), index); + } + + /** + * Searches 0'th JRadioButton in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @return JRadioButton instance or null if component was not found. + */ + public static JRadioButton findJRadioButton(Container cont, ComponentChooser chooser) { + return findJRadioButton(cont, chooser, 0); + } + + /** + * Searches JRadioButton by text. + * + * @param cont Container to search component in. + * @param text Button text. If null, contents is not checked. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @param index Ordinal component index. + * @return JRadioButton instance or null if component was not found. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + */ + public static JRadioButton findJRadioButton(Container cont, String text, boolean ce, boolean ccs, int index) { + return (findJRadioButton(cont, + new JRadioButtonFinder(new AbstractButtonOperator.AbstractButtonByLabelFinder(text, + new DefaultStringComparator(ce, ccs))), + index)); + } + + /** + * Searches JRadioButton by text. + * + * @param cont Container to search component in. + * @param text Button text. If null, contents is not checked. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @return JRadioButton instance or null if component was not found. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + */ + public static JRadioButton findJRadioButton(Container cont, String text, boolean ce, boolean ccs) { + return findJRadioButton(cont, text, ce, ccs, 0); + } + + /** + * Waits JRadioButton in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @param index Ordinal component index. + * @return JRadioButton instance. + * @throws TimeoutExpiredException + */ + public static JRadioButton waitJRadioButton(Container cont, ComponentChooser chooser, int index) { + return (JRadioButton) waitJToggleButton(cont, new JRadioButtonFinder(chooser), index); + } + + /** + * Waits 0'th JRadioButton in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @return JRadioButton instance. + * @throws TimeoutExpiredException + */ + public static JRadioButton waitJRadioButton(Container cont, ComponentChooser chooser) { + return waitJRadioButton(cont, chooser, 0); + } + + /** + * Waits JRadioButton by text. + * + * @param cont Container to search component in. + * @param text Button text. If null, contents is not checked. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @param index Ordinal component index. + * @return JRadioButton instance. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public static JRadioButton waitJRadioButton(Container cont, String text, boolean ce, boolean ccs, int index) { + return (waitJRadioButton(cont, + new JRadioButtonFinder(new AbstractButtonOperator.AbstractButtonByLabelFinder(text, + new DefaultStringComparator(ce, ccs))), + index)); + } + + /** + * Waits JRadioButton by text. + * + * @param cont Container to search component in. + * @param text Button text. If null, contents is not checked. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @return JRadioButton instance. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public static JRadioButton waitJRadioButton(Container cont, String text, boolean ce, boolean ccs) { + return waitJRadioButton(cont, text, ce, ccs, 0); + } + + /** + * Checks component type. + */ + public static class JRadioButtonFinder extends Finder { + + /** + * Constructs JRadioButtonFinder. + * + * @param sf other searching criteria. + */ + public JRadioButtonFinder(ComponentChooser sf) { + super(JRadioButton.class, sf); + } + + /** + * Constructs JRadioButtonFinder. + */ + public JRadioButtonFinder() { + super(JRadioButton.class); + } + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JScrollBarOperator.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JScrollBarOperator.java new file mode 100644 index 00000000000..912611abd6c --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JScrollBarOperator.java @@ -0,0 +1,1025 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.operators; + +import java.awt.Component; +import java.awt.Container; +import java.awt.event.AdjustmentListener; +import java.util.Hashtable; + +import javax.swing.BoundedRangeModel; +import javax.swing.JButton; +import javax.swing.JScrollBar; +import javax.swing.plaf.ScrollBarUI; + +import org.netbeans.jemmy.Action; +import org.netbeans.jemmy.ComponentChooser; +import org.netbeans.jemmy.ComponentSearcher; +import org.netbeans.jemmy.Outputable; +import org.netbeans.jemmy.TestOut; +import org.netbeans.jemmy.TimeoutExpiredException; +import org.netbeans.jemmy.Timeoutable; +import org.netbeans.jemmy.Timeouts; +import org.netbeans.jemmy.Waitable; +import org.netbeans.jemmy.drivers.DriverManager; +import org.netbeans.jemmy.drivers.ScrollDriver; +import org.netbeans.jemmy.drivers.scrolling.ScrollAdjuster; +import org.netbeans.jemmy.util.EmptyVisualizer; + +/** + * + * Operator is supposed to be used to operate with an instance of + * javax.swing.JScrollBar class.

+ * + * + *

Timeouts used:
+ * JScrollBarOperator.OneScrollClickTimeout - time for one scroll click
+ * JScrollBarOperator.WholeScrollTimeout - time for the whole scrolling
+ * JScrollBarOperator.BeforeDropTimeout - to sleep before drop + * JScrollBarOperator.DragAndDropScrollingDelta - to sleep before drag steps + * ComponentOperator.WaitComponentTimeout - time to wait component displayed + *
. + * + * @see org.netbeans.jemmy.Timeouts + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + */ +public class JScrollBarOperator extends JComponentOperator + implements Timeoutable, Outputable { + + /** + * Identifier for a "minimum" property. + * + * @see #getDump + */ + public static final String MINIMUM_DPROP = "Minimum"; + + /** + * Identifier for a "maximum" property. + * + * @see #getDump + */ + public static final String MAXIMUM_DPROP = "Maximum"; + + /** + * Identifier for a "value" property. + * + * @see #getDump + */ + public static final String VALUE_DPROP = "Value"; + + /** + * Identifier for a "orientation" property. + * + * @see #getDump + */ + public static final String ORIENTATION_DPROP = "Orientation"; + + /** + * Identifier for a "HORIZONTAL" value of "orientation" property. + * + * @see #getDump + */ + public static final String HORIZONTAL_ORIENTATION_DPROP_VALUE = "HORIZONTAL"; + + /** + * Identifier for a "VERTICAL" value of "orientation" property. + * + * @see #getDump + */ + public static final String VERTICAL_ORIENTATION_DPROP_VALUE = "VERTICAL"; + + private final static long ONE_SCROLL_CLICK_TIMEOUT = 0; + private final static long WHOLE_SCROLL_TIMEOUT = 60000; + private final static long BEFORE_DROP_TIMEOUT = 0; + private final static long DRAG_AND_DROP_SCROLLING_DELTA = 0; + + private Timeouts timeouts; + private TestOut output; + private JButtonOperator minButtOperator; + private JButtonOperator maxButtOperator; + + private ScrollDriver driver; + + /** + * Constructor. + * + * @param b JScrollBar component. + */ + public JScrollBarOperator(JScrollBar b) { + super(b); + driver = DriverManager.getScrollDriver(getClass()); + } + + /** + * Constructs a JScrollBarOperator object. + * + * @param cont a container + * @param chooser a component chooser specifying searching criteria. + * @param index an index between appropriate ones. + */ + public JScrollBarOperator(ContainerOperator cont, ComponentChooser chooser, int index) { + this((JScrollBar) cont. + waitSubComponent(new JScrollBarFinder(chooser), + index)); + copyEnvironment(cont); + } + + /** + * Constructs a JScrollBarOperator object. + * + * @param cont a container + * @param chooser a component chooser specifying searching criteria. + */ + public JScrollBarOperator(ContainerOperator cont, ComponentChooser chooser) { + this(cont, chooser, 0); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont Operator pointing a container to search component in. + * @param index Ordinal component index. + * @throws TimeoutExpiredException + */ + public JScrollBarOperator(ContainerOperator cont, int index) { + this((JScrollBar) waitComponent(cont, + new JScrollBarFinder(), + index)); + copyEnvironment(cont); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont Operator pointing a container to search component in. + * @throws TimeoutExpiredException + */ + public JScrollBarOperator(ContainerOperator cont) { + this(cont, 0); + } + + /** + * Searches JScrollBar in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @param index Ordinal component index. + * @return JScrollBar instance or null if component was not found. + */ + public static JScrollBar findJScrollBar(Container cont, ComponentChooser chooser, int index) { + return (JScrollBar) findComponent(cont, new JScrollBarFinder(chooser), index); + } + + /** + * Searches 0'th JScrollBar in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @return JScrollBar instance or null if component was not found. + */ + public static JScrollBar findJScrollBar(Container cont, ComponentChooser chooser) { + return findJScrollBar(cont, chooser, 0); + } + + /** + * Searches JScrollBar in container. + * + * @param cont Container to search component in. + * @param index Ordinal component index. + * @return JScrollBar instance or null if component was not found. + */ + public static JScrollBar findJScrollBar(Container cont, int index) { + return findJScrollBar(cont, ComponentSearcher.getTrueChooser(Integer.toString(index) + "'th JScrollBar instance"), index); + } + + /** + * Searches 0'th JScrollBar in container. + * + * @param cont Container to search component in. + * @return JScrollBar instance or null if component was not found. + */ + public static JScrollBar findJScrollBar(Container cont) { + return findJScrollBar(cont, 0); + } + + /** + * Waits JScrollBar in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @param index Ordinal component index. + * @return JScrollBar instance or null if component was not displayed. + * @throws TimeoutExpiredException + */ + public static JScrollBar waitJScrollBar(Container cont, ComponentChooser chooser, int index) { + return (JScrollBar) waitComponent(cont, new JScrollBarFinder(chooser), index); + } + + /** + * Waits 0'th JScrollBar in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @return JScrollBar instance or null if component was not displayed. + * @throws TimeoutExpiredException + */ + public static JScrollBar waitJScrollBar(Container cont, ComponentChooser chooser) { + return waitJScrollBar(cont, chooser, 0); + } + + /** + * Waits JScrollBar in container. + * + * @param cont Container to search component in. + * @param index Ordinal component index. + * @return JScrollBar instance or null if component was not displayed. + * @throws TimeoutExpiredException + */ + public static JScrollBar waitJScrollBar(Container cont, int index) { + return waitJScrollBar(cont, ComponentSearcher.getTrueChooser(Integer.toString(index) + "'th JScrollBar instance"), index); + } + + /** + * Waits 0'th JScrollBar in container. + * + * @param cont Container to search component in. + * @return JScrollBar instance or null if component was not displayed. + * @throws TimeoutExpiredException + */ + public static JScrollBar waitJScrollBar(Container cont) { + return waitJScrollBar(cont, 0); + } + + static { + Timeouts.initDefault("JScrollBarOperator.OneScrollClickTimeout", ONE_SCROLL_CLICK_TIMEOUT); + Timeouts.initDefault("JScrollBarOperator.WholeScrollTimeout", WHOLE_SCROLL_TIMEOUT); + Timeouts.initDefault("JScrollBarOperator.BeforeDropTimeout", BEFORE_DROP_TIMEOUT); + Timeouts.initDefault("JScrollBarOperator.DragAndDropScrollingDelta", DRAG_AND_DROP_SCROLLING_DELTA); + } + + @Override + public void setOutput(TestOut out) { + output = out; + super.setOutput(output.createErrorOutput()); + } + + @Override + public TestOut getOutput() { + return output; + } + + @Override + public void setTimeouts(Timeouts timeouts) { + this.timeouts = timeouts; + super.setTimeouts(timeouts); + } + + @Override + public Timeouts getTimeouts() { + return timeouts; + } + + @Override + public void copyEnvironment(Operator anotherOperator) { + super.copyEnvironment(anotherOperator); + driver + = (ScrollDriver) DriverManager. + getDriver(DriverManager.SCROLL_DRIVER_ID, + getClass(), + anotherOperator.getProperties()); + } + + /** + * Does simple scroll click. + * + * @param increase a scrolling direction. + * @throws TimeoutExpiredException + * @deprecated All scrolling is done through a ScrollDriver registered to + * this operator type. + */ + @Deprecated + public void scroll(boolean increase) { + scrollToValue(getValue() + (increase ? 1 : -1)); + } + + /** + * Scrolls scrollbar to the position defined by w parameter. Uses + * ScrollDriver registered to this operator type. + * + * @param w Scrolling is stopped when w.actionProduced(waiterParam) != null + * @param waiterParam a waiting parameter. + * @param increase a scrolling direction. + * @see #scrollTo(JScrollBarOperator.ScrollChecker) + * @throws TimeoutExpiredException + */ + public

void scrollTo(Waitable w, P waiterParam, boolean increase) { + scrollTo(new WaitableChecker<>(w, waiterParam, increase, this)); + } + + /** + * Scrolls scrollbar to the position defined by a ScrollChecker + * implementation. + * + * @param checker ScrollChecker implementation defining scrolling direction, + * and so on. + * @see ScrollChecker + * @throws TimeoutExpiredException + */ + public void scrollTo(ScrollChecker checker) { + scrollTo(new CheckerAdjustable(checker, this)); + } + + /** + * Scrolls scrollbar to the position defined by a ScrollAdjuster + * implementation. + * + * @param adj defines scrolling direction, and so on. + * @throws TimeoutExpiredException + */ + public void scrollTo(final ScrollAdjuster adj) { + initOperators(); + produceTimeRestricted(new Action() { + @Override + public Void launch(Void obj) { + driver.scroll(JScrollBarOperator.this, adj); + return null; + } + + @Override + public String getDescription() { + return "Scrolling"; + } + + @Override + public String toString() { + return "JScrollBarOperator.scrollTo.Action{description = " + getDescription() + '}'; + } + }, "JScrollBarOperator.WholeScrollTimeout"); + } + + /** + * Scrolls scroll bar to necessary value. + * + * @param value Scroll bar value to scroll to. + * @throws TimeoutExpiredException + */ + public void scrollToValue(int value) { + output.printTrace("Scroll JScrollBar to " + Integer.toString(value) + + " value\n" + toStringSource()); + output.printGolden("Scroll JScrollBar to " + Integer.toString(value) + " value"); + scrollTo(new ValueScrollAdjuster(value)); + } + + /** + * Scrolls scroll bar to necessary proportional value. + * + * @param proportionalValue Proportional scroll to. Must be >= 0 and <= 1. + * @throws TimeoutExpiredException + */ + public void scrollToValue(double proportionalValue) { + output.printTrace("Scroll JScrollBar to " + Double.toString(proportionalValue) + + " proportional value\n" + toStringSource()); + output.printGolden("Scroll JScrollBar to " + Double.toString(proportionalValue) + " proportional value"); + scrollTo(new ValueScrollAdjuster((int) (getMinimum() + + (getMaximum() + - getVisibleAmount() + - getMinimum()) * proportionalValue))); + } + + /** + * Scrolls to minimum value. + * + * @throws TimeoutExpiredException + */ + public void scrollToMinimum() { + output.printTrace("Scroll JScrollBar to minimum value\n" + + toStringSource()); + output.printGolden("Scroll JScrollBar to minimum value"); + initOperators(); + produceTimeRestricted(new Action() { + @Override + public Void launch(Void obj) { + driver.scrollToMinimum(JScrollBarOperator.this, getOrientation()); + return null; + } + + @Override + public String getDescription() { + return "Scrolling"; + } + + @Override + public String toString() { + return "JScrollBarOperator.scrollToMinimum.Action{description = " + getDescription() + '}'; + } + }, "JScrollBarOperator.WholeScrollTimeout"); + } + + /** + * Scrolls to maximum value. + * + * @throws TimeoutExpiredException + */ + public void scrollToMaximum() { + output.printTrace("Scroll JScrollBar to maximum value\n" + + toStringSource()); + output.printGolden("Scroll JScrollBar to maximum value"); + initOperators(); + produceTimeRestricted(new Action() { + @Override + public Void launch(Void obj) { + driver.scrollToMaximum(JScrollBarOperator.this, getOrientation()); + return null; + } + + @Override + public String getDescription() { + return "Scrolling"; + } + + @Override + public String toString() { + return "JScrollBarOperator.scrollToMaximum.Action{description = " + getDescription() + '}'; + } + }, "JScrollBarOperator.WholeScrollTimeout"); + } + + /** + * Returns a button responsible for value decreasing. + * + * @return an operator for the decrease button. + */ + public JButtonOperator getDecreaseButton() { + initOperators(); + return minButtOperator; + } + + /** + * Returns a button responsible for value increasing. + * + * @return an operator for the increase button. + */ + public JButtonOperator getIncreaseButton() { + initOperators(); + return maxButtOperator; + } + + @Override + public Hashtable getDump() { + Hashtable result = super.getDump(); + result.put(MINIMUM_DPROP, Integer.toString(((JScrollBar) getSource()).getMinimum())); + result.put(MAXIMUM_DPROP, Integer.toString(((JScrollBar) getSource()).getMaximum())); + result.put(ORIENTATION_DPROP, (((JScrollBar) getSource()).getOrientation() == JScrollBar.HORIZONTAL) + ? HORIZONTAL_ORIENTATION_DPROP_VALUE + : VERTICAL_ORIENTATION_DPROP_VALUE); + result.put(VALUE_DPROP, Integer.toString(((JScrollBar) getSource()).getValue())); + return result; + } + + //////////////////////////////////////////////////////// + //Mapping // + /** + * Maps {@code JScrollBar.addAdjustmentListener(AdjustmentListener)} + * through queue + */ + public void addAdjustmentListener(final AdjustmentListener adjustmentListener) { + runMapping(new MapVoidAction("addAdjustmentListener") { + @Override + public void map() { + ((JScrollBar) getSource()).addAdjustmentListener(adjustmentListener); + } + }); + } + + /** + * Maps {@code JScrollBar.getBlockIncrement()} through queue + */ + public int getBlockIncrement() { + return (runMapping(new MapIntegerAction("getBlockIncrement") { + @Override + public int map() { + return ((JScrollBar) getSource()).getBlockIncrement(); + } + })); + } + + /** + * Maps {@code JScrollBar.getBlockIncrement(int)} through queue + */ + public int getBlockIncrement(final int i) { + return (runMapping(new MapIntegerAction("getBlockIncrement") { + @Override + public int map() { + return ((JScrollBar) getSource()).getBlockIncrement(i); + } + })); + } + + /** + * Maps {@code JScrollBar.getMaximum()} through queue + */ + public int getMaximum() { + return (runMapping(new MapIntegerAction("getMaximum") { + @Override + public int map() { + return ((JScrollBar) getSource()).getMaximum(); + } + })); + } + + /** + * Maps {@code JScrollBar.getMinimum()} through queue + */ + public int getMinimum() { + return (runMapping(new MapIntegerAction("getMinimum") { + @Override + public int map() { + return ((JScrollBar) getSource()).getMinimum(); + } + })); + } + + /** + * Maps {@code JScrollBar.getModel()} through queue + */ + public BoundedRangeModel getModel() { + return (runMapping(new MapAction("getModel") { + @Override + public BoundedRangeModel map() { + return ((JScrollBar) getSource()).getModel(); + } + })); + } + + /** + * Maps {@code JScrollBar.getOrientation()} through queue + */ + public int getOrientation() { + return (runMapping(new MapIntegerAction("getOrientation") { + @Override + public int map() { + return ((JScrollBar) getSource()).getOrientation(); + } + })); + } + + /** + * Maps {@code JScrollBar.getUI()} through queue + */ + public ScrollBarUI getUI() { + return (runMapping(new MapAction("getUI") { + @Override + public ScrollBarUI map() { + return ((JScrollBar) getSource()).getUI(); + } + })); + } + + /** + * Maps {@code JScrollBar.getUnitIncrement()} through queue + */ + public int getUnitIncrement() { + return (runMapping(new MapIntegerAction("getUnitIncrement") { + @Override + public int map() { + return ((JScrollBar) getSource()).getUnitIncrement(); + } + })); + } + + /** + * Maps {@code JScrollBar.getUnitIncrement(int)} through queue + */ + public int getUnitIncrement(final int i) { + return (runMapping(new MapIntegerAction("getUnitIncrement") { + @Override + public int map() { + return ((JScrollBar) getSource()).getUnitIncrement(i); + } + })); + } + + /** + * Maps {@code JScrollBar.getValue()} through queue + */ + public int getValue() { + return (runMapping(new MapIntegerAction("getValue") { + @Override + public int map() { + return ((JScrollBar) getSource()).getValue(); + } + })); + } + + /** + * Maps {@code JScrollBar.getValueIsAdjusting()} through queue + */ + public boolean getValueIsAdjusting() { + return (runMapping(new MapBooleanAction("getValueIsAdjusting") { + @Override + public boolean map() { + return ((JScrollBar) getSource()).getValueIsAdjusting(); + } + })); + } + + /** + * Maps {@code JScrollBar.getVisibleAmount()} through queue + */ + public int getVisibleAmount() { + return (runMapping(new MapIntegerAction("getVisibleAmount") { + @Override + public int map() { + return ((JScrollBar) getSource()).getVisibleAmount(); + } + })); + } + + /** + * Maps {@code JScrollBar.removeAdjustmentListener(AdjustmentListener)} + * through queue + */ + public void removeAdjustmentListener(final AdjustmentListener adjustmentListener) { + runMapping(new MapVoidAction("removeAdjustmentListener") { + @Override + public void map() { + ((JScrollBar) getSource()).removeAdjustmentListener(adjustmentListener); + } + }); + } + + /** + * Maps {@code JScrollBar.setBlockIncrement(int)} through queue + */ + public void setBlockIncrement(final int i) { + runMapping(new MapVoidAction("setBlockIncrement") { + @Override + public void map() { + ((JScrollBar) getSource()).setBlockIncrement(i); + } + }); + } + + /** + * Maps {@code JScrollBar.setMaximum(int)} through queue + */ + public void setMaximum(final int i) { + runMapping(new MapVoidAction("setMaximum") { + @Override + public void map() { + ((JScrollBar) getSource()).setMaximum(i); + } + }); + } + + /** + * Maps {@code JScrollBar.setMinimum(int)} through queue + */ + public void setMinimum(final int i) { + runMapping(new MapVoidAction("setMinimum") { + @Override + public void map() { + ((JScrollBar) getSource()).setMinimum(i); + } + }); + } + + /** + * Maps {@code JScrollBar.setModel(BoundedRangeModel)} through queue + */ + public void setModel(final BoundedRangeModel boundedRangeModel) { + runMapping(new MapVoidAction("setModel") { + @Override + public void map() { + ((JScrollBar) getSource()).setModel(boundedRangeModel); + } + }); + } + + /** + * Maps {@code JScrollBar.setOrientation(int)} through queue + */ + public void setOrientation(final int i) { + runMapping(new MapVoidAction("setOrientation") { + @Override + public void map() { + ((JScrollBar) getSource()).setOrientation(i); + } + }); + } + + /** + * Maps {@code JScrollBar.setUnitIncrement(int)} through queue + */ + public void setUnitIncrement(final int i) { + runMapping(new MapVoidAction("setUnitIncrement") { + @Override + public void map() { + ((JScrollBar) getSource()).setUnitIncrement(i); + } + }); + } + + /** + * Maps {@code JScrollBar.setValue(int)} through queue + */ + public void setValue(final int i) { + runMapping(new MapVoidAction("setValue") { + @Override + public void map() { + ((JScrollBar) getSource()).setValue(i); + } + }); + } + + /** + * Maps {@code JScrollBar.setValueIsAdjusting(boolean)} through queue + */ + public void setValueIsAdjusting(final boolean b) { + runMapping(new MapVoidAction("setValueIsAdjusting") { + @Override + public void map() { + ((JScrollBar) getSource()).setValueIsAdjusting(b); + } + }); + } + + /** + * Maps {@code JScrollBar.setValues(int, int, int, int)} through queue + */ + public void setValues(final int i, final int i1, final int i2, final int i3) { + runMapping(new MapVoidAction("setValues") { + @Override + public void map() { + ((JScrollBar) getSource()).setValues(i, i1, i2, i3); + } + }); + } + + /** + * Maps {@code JScrollBar.setVisibleAmount(int)} through queue + */ + public void setVisibleAmount(final int i) { + runMapping(new MapVoidAction("setVisibleAmount") { + @Override + public void map() { + ((JScrollBar) getSource()).setVisibleAmount(i); + } + }); + } + + //End of mapping // + //////////////////////////////////////////////////////// + private void initOperators() { + if (minButtOperator != null + && maxButtOperator != null) { + return; + } + ComponentChooser chooser = new ComponentChooser() { + @Override + public boolean checkComponent(Component comp) { + return comp instanceof JButton; + } + + @Override + public String getDescription() { + return ""; + } + + @Override + public String toString() { + return "JScrollBarOperator.initOperators.ComponentChooser{description = " + getDescription() + '}'; + } + }; + ComponentSearcher searcher = new ComponentSearcher((Container) getSource()); + searcher.setOutput(output.createErrorOutput()); + JButton butt0 = (JButton) searcher.findComponent(chooser, 0); + JButton butt1 = (JButton) searcher.findComponent(chooser, 1); + + if (butt0 == null || butt1 == null) { + minButtOperator = null; + maxButtOperator = null; + return; + } + + JButton minButt, maxButt; + + if (((JScrollBar) getSource()).getOrientation() == JScrollBar.HORIZONTAL) { + if (butt0.getX() < butt1.getX()) { + minButt = butt0; + maxButt = butt1; + } else { + minButt = butt1; + maxButt = butt0; + } + } else if (butt0.getY() < butt1.getY()) { + minButt = butt0; + maxButt = butt1; + } else { + minButt = butt1; + maxButt = butt0; + } + minButtOperator = new JButtonOperator(minButt); + maxButtOperator = new JButtonOperator(maxButt); + + minButtOperator.copyEnvironment(this); + maxButtOperator.copyEnvironment(this); + + minButtOperator.setOutput(output.createErrorOutput()); + maxButtOperator.setOutput(output.createErrorOutput()); + + Timeouts times = timeouts.cloneThis(); + times.setTimeout("AbstractButtonOperator.PushButtonTimeout", + times.getTimeout("JScrollBarOperator.OneScrollClickTimeout")); + + minButtOperator.setTimeouts(times); + maxButtOperator.setTimeouts(times); + + minButtOperator.setVisualizer(new EmptyVisualizer()); + maxButtOperator.setVisualizer(new EmptyVisualizer()); + } + + /** + * Interface can be used to define some kind of complicated scrolling rules. + */ + public interface ScrollChecker { + + /** + * Defines a scrolling direction. + * + * @param oper an operator + * @see + * org.netbeans.jemmy.drivers.scrolling.ScrollAdjuster#INCREASE_SCROLL_DIRECTION + * @see + * org.netbeans.jemmy.drivers.scrolling.ScrollAdjuster#DECREASE_SCROLL_DIRECTION + * @see + * org.netbeans.jemmy.drivers.scrolling.ScrollAdjuster#DO_NOT_TOUCH_SCROLL_DIRECTION + * @return one of the following values:
+ * ScrollAdjuster.INCREASE_SCROLL_DIRECTION
+ * ScrollAdjuster.DECREASE_SCROLL_DIRECTION
+ * ScrollAdjuster.DO_NOT_TOUCH_SCROLL_DIRECTION
+ */ + public int getScrollDirection(JScrollBarOperator oper); + + /** + * Scrolling rules decription. + * + * @return a description + */ + public String getDescription(); + } + + private class ValueScrollAdjuster implements ScrollAdjuster { + + int value; + + public ValueScrollAdjuster(int value) { + this.value = value; + } + + @Override + public int getScrollDirection() { + if (getValue() == value) { + return ScrollAdjuster.DO_NOT_TOUCH_SCROLL_DIRECTION; + } else { + return ((getValue() < value) + ? ScrollAdjuster.INCREASE_SCROLL_DIRECTION + : ScrollAdjuster.DECREASE_SCROLL_DIRECTION); + } + } + + @Override + public int getScrollOrientation() { + return getOrientation(); + } + + @Override + public String getDescription() { + return "Scroll to " + Integer.toString(value) + " value"; + } + + @Override + public String toString() { + return "ValueScrollAdjuster{" + "value=" + value + '}'; + } + } + + private class WaitableChecker

implements ScrollAdjuster { + + Waitable w; + P waitParam; + boolean increase; + boolean reached = false; + + public WaitableChecker(Waitable w, P waitParam, boolean increase, JScrollBarOperator oper) { + this.w = w; + this.waitParam = waitParam; + this.increase = increase; + } + + @Override + public int getScrollDirection() { + if (!reached && w.actionProduced(waitParam) != null) { + reached = true; + } + if (reached) { + return ScrollAdjuster.DO_NOT_TOUCH_SCROLL_DIRECTION; + } else { + return (increase + ? ScrollAdjuster.INCREASE_SCROLL_DIRECTION + : ScrollAdjuster.DECREASE_SCROLL_DIRECTION); + } + } + + @Override + public int getScrollOrientation() { + return getOrientation(); + } + + @Override + public String getDescription() { + return w.getDescription(); + } + + @Override + public String toString() { + return "WaitableChecker{" + "w=" + w + ", waitParam=" + waitParam + ", increase=" + increase + ", reached=" + reached + '}'; + } + } + + private class CheckerAdjustable implements ScrollAdjuster { + + ScrollChecker checker; + JScrollBarOperator oper; + + public CheckerAdjustable(ScrollChecker checker, JScrollBarOperator oper) { + this.checker = checker; + this.oper = oper; + } + + @Override + public int getScrollDirection() { + return checker.getScrollDirection(oper); + } + + @Override + public int getScrollOrientation() { + return getOrientation(); + } + + @Override + public String getDescription() { + return checker.getDescription(); + } + + @Override + public String toString() { + return "CheckerAdjustable{" + "checker=" + checker + ", oper=" + oper + '}'; + } + } + + /** + * Checks component type. + */ + public static class JScrollBarFinder extends Finder { + + /** + * Constructs JScrollBarFinder. + * + * @param sf other searching criteria. + */ + public JScrollBarFinder(ComponentChooser sf) { + super(JScrollBar.class, sf); + } + + /** + * Constructs JScrollBarFinder. + */ + public JScrollBarFinder() { + super(JScrollBar.class); + } + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JScrollPaneOperator.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JScrollPaneOperator.java new file mode 100644 index 00000000000..c69d9615902 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JScrollPaneOperator.java @@ -0,0 +1,961 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.operators; + +import java.awt.Component; +import java.awt.Container; +import java.awt.Point; +import java.awt.Rectangle; + +import javax.swing.JScrollBar; +import javax.swing.JScrollPane; +import javax.swing.JViewport; +import javax.swing.SwingUtilities; +import javax.swing.border.Border; +import javax.swing.plaf.ScrollPaneUI; + +import org.netbeans.jemmy.ComponentChooser; +import org.netbeans.jemmy.ComponentSearcher; +import org.netbeans.jemmy.Outputable; +import org.netbeans.jemmy.TestOut; +import org.netbeans.jemmy.TimeoutExpiredException; +import org.netbeans.jemmy.Timeoutable; +import org.netbeans.jemmy.Timeouts; +import org.netbeans.jemmy.drivers.scrolling.ScrollAdjuster; +import org.netbeans.jemmy.util.EmptyVisualizer; + +/** + *

Timeouts used:
+ * JScrollBarOperator.OneScrollClickTimeout - time for one scroll click
+ * JScrollBarOperator.WholeScrollTimeout - time for the whole scrolling
+ * ComponentOperator.WaitComponentTimeout - time to wait component displayed + *
. + * + * @see org.netbeans.jemmy.Timeouts + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + * + */ +public class JScrollPaneOperator extends JComponentOperator + implements Timeoutable, Outputable { + + private static int X_POINT_RECT_SIZE = 6; + private static int Y_POINT_RECT_SIZE = 4; + + private Timeouts timeouts; + private TestOut output; + private JScrollBarOperator hScrollBarOper = null; + private JScrollBarOperator vScrollBarOper = null; + + /** + * Constructor. + * + * @param b JScrollPane component. + */ + public JScrollPaneOperator(JScrollPane b) { + super(b); + } + + /** + * Constructs a JScrollPaneOperator object. + * + * @param cont a container + * @param chooser a component chooser specifying searching criteria. + * @param index an index between appropriate ones. + */ + public JScrollPaneOperator(ContainerOperator cont, ComponentChooser chooser, int index) { + this((JScrollPane) cont. + waitSubComponent(new JScrollPaneFinder(chooser), + index)); + copyEnvironment(cont); + } + + /** + * Constructs a JScrollPaneOperator object. + * + * @param cont a container + * @param chooser a component chooser specifying searching criteria. + */ + public JScrollPaneOperator(ContainerOperator cont, ComponentChooser chooser) { + this(cont, chooser, 0); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont Operator pointing a container to search component in. + * @param index Ordinal component index. + * @throws TimeoutExpiredException + */ + public JScrollPaneOperator(ContainerOperator cont, int index) { + this((JScrollPane) waitComponent(cont, + new JScrollPaneFinder(), + index)); + copyEnvironment(cont); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont Operator pointing a container to search component in. + * @throws TimeoutExpiredException + */ + public JScrollPaneOperator(ContainerOperator cont) { + this(cont, 0); + } + + /** + * Searches JScrollPane in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @param index Ordinal component index. + * @return JScrollPane instance or null if component was not found. + */ + public static JScrollPane findJScrollPane(Container cont, ComponentChooser chooser, int index) { + return (JScrollPane) findComponent(cont, new JScrollPaneFinder(chooser), index); + } + + /** + * Searches 0'th JScrollPane in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @return JScrollPane instance or null if component was not found. + */ + public static JScrollPane findJScrollPane(Container cont, ComponentChooser chooser) { + return findJScrollPane(cont, chooser, 0); + } + + /** + * Searches JScrollPane in container. + * + * @param cont Container to search component in. + * @param index Ordinal component index. + * @return JScrollPane instance or null if component was not found. + */ + public static JScrollPane findJScrollPane(Container cont, int index) { + return findJScrollPane(cont, ComponentSearcher.getTrueChooser(Integer.toString(index) + "'th JScrollPane instance"), index); + } + + /** + * Searches 0'th JScrollPane in container. + * + * @param cont Container to search component in. + * @return JScrollPane instance or null if component was not found. + */ + public static JScrollPane findJScrollPane(Container cont) { + return findJScrollPane(cont, 0); + } + + /** + * Searches JScrollPane object which component lies on. + * + * @param comp Component to find JScrollPane under. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @return JScrollPane instance or null if component was not found. + */ + public static JScrollPane findJScrollPaneUnder(Component comp, ComponentChooser chooser) { + return (JScrollPane) findContainerUnder(comp, new JScrollPaneFinder(chooser)); + } + + /** + * Searches JScrollPane object which component lies on. + * + * @param comp Component to find JScrollPane under. + * @return JScrollPane instance or null if component was not found. + */ + public static JScrollPane findJScrollPaneUnder(Component comp) { + return findJScrollPaneUnder(comp, new JScrollPaneFinder()); + } + + /** + * Waits JScrollPane in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @param index Ordinal component index. + * @return JScrollPane instance or null if component was not displayed. + * @throws TimeoutExpiredException + */ + public static JScrollPane waitJScrollPane(Container cont, ComponentChooser chooser, int index) { + return (JScrollPane) waitComponent(cont, new JScrollPaneFinder(chooser), index); + } + + /** + * Waits 0'th JScrollPane in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @return JScrollPane instance or null if component was not displayed. + * @throws TimeoutExpiredException + */ + public static JScrollPane waitJScrollPane(Container cont, ComponentChooser chooser) { + return waitJScrollPane(cont, chooser, 0); + } + + /** + * Waits JScrollPane in container. + * + * @param cont Container to search component in. + * @param index Ordinal component index. + * @return JScrollPane instance or null if component was not displayed. + * @throws TimeoutExpiredException + */ + public static JScrollPane waitJScrollPane(Container cont, int index) { + return waitJScrollPane(cont, ComponentSearcher.getTrueChooser(Integer.toString(index) + "'th JScrollPane instance"), index); + } + + /** + * Waits 0'th JScrollPane in container. + * + * @param cont Container to search component in. + * @return JScrollPane instance or null if component was not displayed. + * @throws TimeoutExpiredException + */ + public static JScrollPane waitJScrollPane(Container cont) { + return waitJScrollPane(cont, 0); + } + + /** + * Sets values for both JScrollBars. + * + * @param hValue a value for the horizontal scrollbar. + * @param vValue a value for the vertical scrollbar. + */ + public void setValues(int hValue, int vValue) { + initOperators(); + hScrollBarOper.setValue(hValue); + vScrollBarOper.setValue(vValue); + } + + @Override + public void setTimeouts(Timeouts timeouts) { + super.setTimeouts(timeouts); + this.timeouts = timeouts; + } + + @Override + public Timeouts getTimeouts() { + return timeouts; + } + + @Override + public void setOutput(TestOut out) { + output = out; + super.setOutput(output.createErrorOutput()); + } + + @Override + public TestOut getOutput() { + return output; + } + + /** + * Scrolls horizontal scroll bar. + * + * @param value Value to scroll horizontal scroll bar to. + * @throws TimeoutExpiredException + */ + public void scrollToHorizontalValue(int value) { + output.printTrace("Scroll JScrollPane to " + Integer.toString(value) + " horizontal value \n" + + toStringSource()); + output.printGolden("Scroll JScrollPane to " + Integer.toString(value) + " horizontal value"); + initOperators(); + makeComponentVisible(); + if (hScrollBarOper != null && hScrollBarOper.getSource().isVisible()) { + hScrollBarOper.scrollToValue(value); + } + } + + /** + * Scrolls horizontal scroll bar. + * + * @param proportionalValue Proportional value to scroll horizontal scroll + * bar to. + * @throws TimeoutExpiredException + */ + public void scrollToHorizontalValue(double proportionalValue) { + output.printTrace("Scroll JScrollPane to " + Double.toString(proportionalValue) + " proportional horizontal value \n" + + toStringSource()); + output.printGolden("Scroll JScrollPane to " + Double.toString(proportionalValue) + " proportional horizontal value"); + initOperators(); + makeComponentVisible(); + if (hScrollBarOper != null && hScrollBarOper.getSource().isVisible()) { + hScrollBarOper.scrollToValue(proportionalValue); + } + } + + /** + * Scrolls vertical scroll bar. + * + * @param value Value to scroll vertical scroll bar to. + * @throws TimeoutExpiredException + */ + public void scrollToVerticalValue(int value) { + output.printTrace("Scroll JScrollPane to " + Integer.toString(value) + " vertical value \n" + + toStringSource()); + output.printGolden("Scroll JScrollPane to " + Integer.toString(value) + " vertical value"); + initOperators(); + makeComponentVisible(); + if (vScrollBarOper != null && vScrollBarOper.getSource().isVisible()) { + vScrollBarOper.scrollToValue(value); + } + } + + /** + * Scrolls vertical scroll bar. + * + * @param proportionalValue Value to scroll vertical scroll bar to. + * @throws TimeoutExpiredException + */ + public void scrollToVerticalValue(double proportionalValue) { + output.printTrace("Scroll JScrollPane to " + Double.toString(proportionalValue) + " proportional vertical value \n" + + toStringSource()); + output.printGolden("Scroll JScrollPane to " + Double.toString(proportionalValue) + " proportional vertical value"); + initOperators(); + makeComponentVisible(); + if (vScrollBarOper != null && vScrollBarOper.getSource().isVisible()) { + vScrollBarOper.scrollToValue(proportionalValue); + } + } + + /** + * Scrolls both scroll bars. + * + * @param valueX Value to scroll horizontal scroll bar to. + * @param valueY Value to scroll vertical scroll bar to. + * @throws TimeoutExpiredException + */ + public void scrollToValues(int valueX, int valueY) { + scrollToVerticalValue(valueX); + scrollToHorizontalValue(valueX); + } + + /** + * Scrolls both scroll bars. + * + * @param proportionalValueX Value to scroll horizontal scroll bar to. + * @param proportionalValueY Value to scroll vertical scroll bar to. + * @throws TimeoutExpiredException + */ + public void scrollToValues(double proportionalValueX, double proportionalValueY) { + scrollToVerticalValue(proportionalValueX); + scrollToHorizontalValue(proportionalValueY); + } + + /** + * Scrolls pane to top. + * + * @throws TimeoutExpiredException + */ + public void scrollToTop() { + output.printTrace("Scroll JScrollPane to top\n" + + toStringSource()); + output.printGolden("Scroll JScrollPane to top"); + initOperators(); + makeComponentVisible(); + if (vScrollBarOper != null && vScrollBarOper.getSource().isVisible()) { + vScrollBarOper.scrollToMinimum(); + } + } + + /** + * Scrolls pane to bottom. + * + * @throws TimeoutExpiredException + */ + public void scrollToBottom() { + output.printTrace("Scroll JScrollPane to bottom\n" + + toStringSource()); + output.printGolden("Scroll JScrollPane to bottom"); + initOperators(); + makeComponentVisible(); + if (vScrollBarOper != null && vScrollBarOper.getSource().isVisible()) { + vScrollBarOper.scrollToMaximum(); + } + } + + /** + * Scrolls pane to left. + * + * @throws TimeoutExpiredException + */ + public void scrollToLeft() { + output.printTrace("Scroll JScrollPane to left\n" + + toStringSource()); + output.printGolden("Scroll JScrollPane to left"); + initOperators(); + makeComponentVisible(); + if (hScrollBarOper != null && hScrollBarOper.getSource().isVisible()) { + hScrollBarOper.scrollToMinimum(); + } + } + + /** + * Scrolls pane to right. + * + * @throws TimeoutExpiredException + */ + public void scrollToRight() { + output.printTrace("Scroll JScrollPane to right\n" + + toStringSource()); + output.printGolden("Scroll JScrollPane to right"); + initOperators(); + makeComponentVisible(); + if (hScrollBarOper != null && hScrollBarOper.getSource().isVisible()) { + hScrollBarOper.scrollToMaximum(); + } + } + + /** + * Scrolls pane to rectangle of a component. + * + * @param comp a subcomponent defining coordinate system. + * @param x coordinate + * @param y coordinate + * @param width rectangle width + * @param height rectangle height + * @throws TimeoutExpiredException + */ + public void scrollToComponentRectangle(Component comp, int x, int y, int width, int height) { + initOperators(); + makeComponentVisible(); + if (hScrollBarOper != null && hScrollBarOper.getSource().isVisible()) { + hScrollBarOper.scrollTo(new ComponentRectChecker(comp, x, y, width, height, JScrollBar.HORIZONTAL)); + } + if (vScrollBarOper != null && vScrollBarOper.getSource().isVisible()) { + vScrollBarOper.scrollTo(new ComponentRectChecker(comp, x, y, width, height, JScrollBar.VERTICAL)); + } + } + + /** + * Scrolls pane to point. + * + * @param comp a subcomponent defining coordinate system. + * @param x coordinate + * @param y coordinate + * @throws TimeoutExpiredException + */ + public void scrollToComponentPoint(Component comp, int x, int y) { + scrollToComponentRectangle(comp, + x - X_POINT_RECT_SIZE, + y - Y_POINT_RECT_SIZE, + 2 * X_POINT_RECT_SIZE, + 2 * Y_POINT_RECT_SIZE); + } + + /** + * Scrolls pane to component on this pane. Component should lay on the + * JScrollPane view. + * + * @param comp Component to scroll to. + * @throws TimeoutExpiredException + */ + public void scrollToComponent(final Component comp) { + String componentToString = runMapping( + new Operator.MapAction("comp.toString()") { + @Override + public String map() { + return comp.toString(); + } + } + ); + output.printTrace("Scroll JScrollPane " + toStringSource() + + "\nto component " + componentToString); + output.printGolden("Scroll JScrollPane to " + comp.getClass().getName() + " component."); + scrollToComponentRectangle(comp, 0, 0, comp.getWidth(), comp.getHeight()); + } + + /** + * Returns operator used for horizontal scrollbar. + * + * @return an operator for the horizontal scrollbar. + */ + public JScrollBarOperator getHScrollBarOperator() { + initOperators(); + return hScrollBarOper; + } + + /** + * Returns operator used for vertical scrollbar. + * + * @return an operator for the vertical scrollbar. + */ + public JScrollBarOperator getVScrollBarOperator() { + initOperators(); + return vScrollBarOper; + } + + /** + * Checks if component's rectangle is inside view port (no scrolling + * necessary). + * + * @param comp a subcomponent defining coordinate system. + * @param x coordinate + * @param y coordinate + * @param width rectangle width + * @param height rectangle height + * @return true if pointed subcomponent rectangle is inside the scrolling + * area. + */ + public boolean checkInside(Component comp, int x, int y, int width, int height) { + Component view = getViewport().getView(); + Point toPoint = SwingUtilities. + convertPoint(comp, x, y, getViewport().getView()); + initOperators(); + if (hScrollBarOper != null && hScrollBarOper.getSource().isVisible()) { + if (toPoint.x < hScrollBarOper.getValue()) { + return false; + } + if (comp.getWidth() > view.getWidth()) { + return toPoint.x > 0; + } else { + return (toPoint.x + comp.getWidth() + > hScrollBarOper.getValue() + view.getWidth()); + } + } + if (vScrollBarOper != null && vScrollBarOper.getSource().isVisible()) { + if (toPoint.y < vScrollBarOper.getValue()) { + return false; + } + if (comp.getHeight() > view.getHeight()) { + return toPoint.y > 0; + } else { + return (toPoint.y + comp.getHeight() + > vScrollBarOper.getValue() + view.getHeight()); + } + } + return true; + } + + /** + * Checks if component is inside view port (no scrolling necessary). + * + * @param comp a subcomponent + * @return true if pointed subcomponent is inside the scrolling area. + */ + public boolean checkInside(Component comp) { + return checkInside(comp, 0, 0, comp.getWidth(), comp.getHeight()); + } + + //////////////////////////////////////////////////////// + //Mapping // + /** + * Maps {@code JScrollPane.createHorizontalScrollBar()} through queue + */ + public JScrollBar createHorizontalScrollBar() { + return (runMapping(new MapAction("createHorizontalScrollBar") { + @Override + public JScrollBar map() { + return ((JScrollPane) getSource()).createHorizontalScrollBar(); + } + })); + } + + /** + * Maps {@code JScrollPane.createVerticalScrollBar()} through queue + */ + public JScrollBar createVerticalScrollBar() { + return (runMapping(new MapAction("createVerticalScrollBar") { + @Override + public JScrollBar map() { + return ((JScrollPane) getSource()).createVerticalScrollBar(); + } + })); + } + + /** + * Maps {@code JScrollPane.getColumnHeader()} through queue + */ + public JViewport getColumnHeader() { + return (runMapping(new MapAction("getColumnHeader") { + @Override + public JViewport map() { + return ((JScrollPane) getSource()).getColumnHeader(); + } + })); + } + + /** + * Maps {@code JScrollPane.getCorner(String)} through queue + */ + public Component getCorner(final String string) { + return (runMapping(new MapAction("getCorner") { + @Override + public Component map() { + return ((JScrollPane) getSource()).getCorner(string); + } + })); + } + + /** + * Maps {@code JScrollPane.getHorizontalScrollBar()} through queue + */ + public JScrollBar getHorizontalScrollBar() { + return (runMapping(new MapAction("getHorizontalScrollBar") { + @Override + public JScrollBar map() { + return ((JScrollPane) getSource()).getHorizontalScrollBar(); + } + })); + } + + /** + * Maps {@code JScrollPane.getHorizontalScrollBarPolicy()} through queue + */ + public int getHorizontalScrollBarPolicy() { + return (runMapping(new MapIntegerAction("getHorizontalScrollBarPolicy") { + @Override + public int map() { + return ((JScrollPane) getSource()).getHorizontalScrollBarPolicy(); + } + })); + } + + /** + * Maps {@code JScrollPane.getRowHeader()} through queue + */ + public JViewport getRowHeader() { + return (runMapping(new MapAction("getRowHeader") { + @Override + public JViewport map() { + return ((JScrollPane) getSource()).getRowHeader(); + } + })); + } + + /** + * Maps {@code JScrollPane.getUI()} through queue + */ + public ScrollPaneUI getUI() { + return (runMapping(new MapAction("getUI") { + @Override + public ScrollPaneUI map() { + return ((JScrollPane) getSource()).getUI(); + } + })); + } + + /** + * Maps {@code JScrollPane.getVerticalScrollBar()} through queue + */ + public JScrollBar getVerticalScrollBar() { + return (runMapping(new MapAction("getVerticalScrollBar") { + @Override + public JScrollBar map() { + return ((JScrollPane) getSource()).getVerticalScrollBar(); + } + })); + } + + /** + * Maps {@code JScrollPane.getVerticalScrollBarPolicy()} through queue + */ + public int getVerticalScrollBarPolicy() { + return (runMapping(new MapIntegerAction("getVerticalScrollBarPolicy") { + @Override + public int map() { + return ((JScrollPane) getSource()).getVerticalScrollBarPolicy(); + } + })); + } + + /** + * Maps {@code JScrollPane.getViewport()} through queue + */ + public JViewport getViewport() { + return (runMapping(new MapAction("getViewport") { + @Override + public JViewport map() { + return ((JScrollPane) getSource()).getViewport(); + } + })); + } + + /** + * Maps {@code JScrollPane.getViewportBorder()} through queue + */ + public Border getViewportBorder() { + return (runMapping(new MapAction("getViewportBorder") { + @Override + public Border map() { + return ((JScrollPane) getSource()).getViewportBorder(); + } + })); + } + + /** + * Maps {@code JScrollPane.getViewportBorderBounds()} through queue + */ + public Rectangle getViewportBorderBounds() { + return (runMapping(new MapAction("getViewportBorderBounds") { + @Override + public Rectangle map() { + return ((JScrollPane) getSource()).getViewportBorderBounds(); + } + })); + } + + /** + * Maps {@code JScrollPane.setColumnHeader(JViewport)} through queue + */ + public void setColumnHeader(final JViewport jViewport) { + runMapping(new MapVoidAction("setColumnHeader") { + @Override + public void map() { + ((JScrollPane) getSource()).setColumnHeader(jViewport); + } + }); + } + + /** + * Maps {@code JScrollPane.setColumnHeaderView(Component)} through queue + */ + public void setColumnHeaderView(final Component component) { + runMapping(new MapVoidAction("setColumnHeaderView") { + @Override + public void map() { + ((JScrollPane) getSource()).setColumnHeaderView(component); + } + }); + } + + /** + * Maps {@code JScrollPane.setCorner(String, Component)} through queue + */ + public void setCorner(final String string, final Component component) { + runMapping(new MapVoidAction("setCorner") { + @Override + public void map() { + ((JScrollPane) getSource()).setCorner(string, component); + } + }); + } + + /** + * Maps {@code JScrollPane.setHorizontalScrollBar(JScrollBar)} through queue + */ + public void setHorizontalScrollBar(final JScrollBar jScrollBar) { + runMapping(new MapVoidAction("setHorizontalScrollBar") { + @Override + public void map() { + ((JScrollPane) getSource()).setHorizontalScrollBar(jScrollBar); + } + }); + } + + /** + * Maps {@code JScrollPane.setHorizontalScrollBarPolicy(int)} through queue + */ + public void setHorizontalScrollBarPolicy(final int i) { + runMapping(new MapVoidAction("setHorizontalScrollBarPolicy") { + @Override + public void map() { + ((JScrollPane) getSource()).setHorizontalScrollBarPolicy(i); + } + }); + } + + /** + * Maps {@code JScrollPane.setRowHeader(JViewport)} through queue + */ + public void setRowHeader(final JViewport jViewport) { + runMapping(new MapVoidAction("setRowHeader") { + @Override + public void map() { + ((JScrollPane) getSource()).setRowHeader(jViewport); + } + }); + } + + /** + * Maps {@code JScrollPane.setRowHeaderView(Component)} through queue + */ + public void setRowHeaderView(final Component component) { + runMapping(new MapVoidAction("setRowHeaderView") { + @Override + public void map() { + ((JScrollPane) getSource()).setRowHeaderView(component); + } + }); + } + + /** + * Maps {@code JScrollPane.setUI(ScrollPaneUI)} through queue + */ + public void setUI(final ScrollPaneUI scrollPaneUI) { + runMapping(new MapVoidAction("setUI") { + @Override + public void map() { + ((JScrollPane) getSource()).setUI(scrollPaneUI); + } + }); + } + + /** + * Maps {@code JScrollPane.setVerticalScrollBar(JScrollBar)} through queue + */ + public void setVerticalScrollBar(final JScrollBar jScrollBar) { + runMapping(new MapVoidAction("setVerticalScrollBar") { + @Override + public void map() { + ((JScrollPane) getSource()).setVerticalScrollBar(jScrollBar); + } + }); + } + + /** + * Maps {@code JScrollPane.setVerticalScrollBarPolicy(int)} through queue + */ + public void setVerticalScrollBarPolicy(final int i) { + runMapping(new MapVoidAction("setVerticalScrollBarPolicy") { + @Override + public void map() { + ((JScrollPane) getSource()).setVerticalScrollBarPolicy(i); + } + }); + } + + /** + * Maps {@code JScrollPane.setViewport(JViewport)} through queue + */ + public void setViewport(final JViewport jViewport) { + runMapping(new MapVoidAction("setViewport") { + @Override + public void map() { + ((JScrollPane) getSource()).setViewport(jViewport); + } + }); + } + + /** + * Maps {@code JScrollPane.setViewportBorder(Border)} through queue + */ + public void setViewportBorder(final Border border) { + runMapping(new MapVoidAction("setViewportBorder") { + @Override + public void map() { + ((JScrollPane) getSource()).setViewportBorder(border); + } + }); + } + + /** + * Maps {@code JScrollPane.setViewportView(Component)} through queue + */ + public void setViewportView(final Component component) { + runMapping(new MapVoidAction("setViewportView") { + @Override + public void map() { + ((JScrollPane) getSource()).setViewportView(component); + } + }); + } + + //End of mapping // + //////////////////////////////////////////////////////// + private void initOperators() { + if (hScrollBarOper == null && getHorizontalScrollBar() != null && getHorizontalScrollBar().isVisible()) { + hScrollBarOper = new JScrollBarOperator(getHorizontalScrollBar()); + hScrollBarOper.copyEnvironment(this); + hScrollBarOper.setVisualizer(new EmptyVisualizer()); + } + if (vScrollBarOper == null && getVerticalScrollBar() != null && getVerticalScrollBar().isVisible()) { + vScrollBarOper = new JScrollBarOperator(getVerticalScrollBar()); + vScrollBarOper.copyEnvironment(this); + vScrollBarOper.setVisualizer(new EmptyVisualizer()); + } + } + + private class ComponentRectChecker implements JScrollBarOperator.ScrollChecker { + + Component comp; + int x; + int y; + int width; + int height; + int orientation; + + public ComponentRectChecker(Component comp, int x, int y, int width, int height, int orientation) { + this.comp = comp; + this.x = x; + this.y = y; + this.width = width; + this.height = height; + this.orientation = orientation; + } + + @Override + public int getScrollDirection(JScrollBarOperator oper) { + Point toPoint = SwingUtilities. + convertPoint(comp, x, y, getViewport().getView()); + int to = (orientation == JScrollBar.HORIZONTAL) ? toPoint.x : toPoint.y; + int ln = (orientation == JScrollBar.HORIZONTAL) ? width : height; + int lv = (orientation == JScrollBar.HORIZONTAL) ? getViewport().getWidth() : getViewport().getHeight(); + int vl = oper.getValue(); + if (to < vl) { + return ScrollAdjuster.DECREASE_SCROLL_DIRECTION; + } else if ((to + ln - 1) > (vl + lv) + && to > vl) { + return ScrollAdjuster.INCREASE_SCROLL_DIRECTION; + } else { + return ScrollAdjuster.DO_NOT_TOUCH_SCROLL_DIRECTION; + } + } + + @Override + public String getDescription() { + return ""; + } + + @Override + public String toString() { + return "ComponentRectChecker{" + "comp=" + comp + ", x=" + x + ", y=" + y + ", width=" + width + ", height=" + height + ", orientation=" + orientation + '}'; + } + } + + /** + * Checks component type. + */ + public static class JScrollPaneFinder extends Finder { + + /** + * Constructs JScrollPaneFinder. + * + * @param sf other searching criteria. + */ + public JScrollPaneFinder(ComponentChooser sf) { + super(JScrollPane.class, sf); + } + + /** + * Constructs JScrollPaneFinder. + */ + public JScrollPaneFinder() { + super(JScrollPane.class); + } + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JSliderOperator.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JSliderOperator.java new file mode 100644 index 00000000000..e16516fa197 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JSliderOperator.java @@ -0,0 +1,917 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.operators; + +import java.awt.Container; +import java.util.Dictionary; +import java.util.Hashtable; + +import javax.swing.BoundedRangeModel; +import javax.swing.JSlider; +import javax.swing.event.ChangeListener; +import javax.swing.plaf.SliderUI; + +import org.netbeans.jemmy.Action; +import org.netbeans.jemmy.ComponentChooser; +import org.netbeans.jemmy.ComponentSearcher; +import org.netbeans.jemmy.Outputable; +import org.netbeans.jemmy.TestOut; +import org.netbeans.jemmy.TimeoutExpiredException; +import org.netbeans.jemmy.Timeoutable; +import org.netbeans.jemmy.Timeouts; +import org.netbeans.jemmy.drivers.DriverManager; +import org.netbeans.jemmy.drivers.ScrollDriver; +import org.netbeans.jemmy.drivers.scrolling.ScrollAdjuster; + +/** + * Covers {@code javax.swing.JSlider} component. + * + *

Timeouts used:
+ * JSliderOperator.WholeScrollTimeout - time for the whole scrolling.
+ * JSliderOperator.ScrollingDelta - time delta to verify result durong + * scrolling.
+ * + * @author Alexandre Iline (alexandre.iline@oracle.com) + */ +public class JSliderOperator extends JComponentOperator + implements Timeoutable, Outputable { + + private final static long ONE_SCROLL_CLICK_TIMEOUT = 0; + private final static long WHOLE_SCROLL_TIMEOUT = 60000; + private final static long SCROLLING_DELTA = 0; + + private ScrollDriver driver; + + /** + * Identifier for a "minimum" property. + * + * @see #getDump + */ + public static final String MINIMUM_DPROP = "Minimum"; + + /** + * Identifier for a "maximum" property. + * + * @see #getDump + */ + public static final String MAXIMUM_DPROP = "Maximum"; + + /** + * Identifier for a "value" property. + * + * @see #getDump + */ + public static final String VALUE_DPROP = "Value"; + + /** + * Identifier for a "orientation" property. + * + * @see #getDump + */ + public static final String ORIENTATION_DPROP = "Orientation"; + + /** + * Identifier for a "HORIZONTAL" value of "orientation" property. + * + * @see #getDump + */ + public static final String HORIZONTAL_ORIENTATION_DPROP_VALUE = "HORIZONTAL"; + + /** + * Identifier for a "VERTICAL" value of "orientation" property. + * + * @see #getDump + */ + public static final String VERTICAL_ORIENTATION_DPROP_VALUE = "VERTICAL"; + + /** + * Identifier for a "inverted" property. + */ + public static final String IS_INVERTED_DPROP = "Inverted"; + + /** + * Scrolling model. + * + * @see #setScrollModel(int) + * @deprecated All actions are prformed throw a {@code ScrollDriver} + * registered for this component, So this field is useless. + */ + @Deprecated + public static final int CLICK_SCROLL_MODEL = 1; + + /** + * Push and wait scroll model. Mouse is pressed, and released after + * necessary position reached. + * + * @see #setScrollModel(int) + * @deprecated All actions are prformed throw a {@code ScrollDriver} + * registered for this component, So this field is useless. + */ + @Deprecated + public static final int PUSH_AND_WAIT_SCROLL_MODEL = 2; + + private Timeouts timeouts; + private TestOut output; + private int scrollModel = CLICK_SCROLL_MODEL; + + /** + * Constructor. + * + * @param b JSlider component. + */ + public JSliderOperator(JSlider b) { + super(b); + driver = DriverManager.getScrollDriver(getClass()); + } + + /** + * Constructs a JSliderOperator object. + * + * @param cont a container + * @param chooser a component chooser specifying searching criteria. + * @param index an index between appropriate ones. + */ + public JSliderOperator(ContainerOperator cont, ComponentChooser chooser, int index) { + this((JSlider) cont. + waitSubComponent(new JSliderFinder(chooser), + index)); + copyEnvironment(cont); + } + + /** + * Constructs a JSliderOperator object. + * + * @param cont a container + * @param chooser a component chooser specifying searching criteria. + */ + public JSliderOperator(ContainerOperator cont, ComponentChooser chooser) { + this(cont, chooser, 0); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont Operator pointing a container to search component in. + * @param index Ordinal component index. + * @throws TimeoutExpiredException + */ + public JSliderOperator(ContainerOperator cont, int index) { + this((JSlider) waitComponent(cont, + new JSliderFinder(), + index)); + copyEnvironment(cont); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont Operator pointing a container to search component in. + * @throws TimeoutExpiredException + */ + public JSliderOperator(ContainerOperator cont) { + this(cont, 0); + } + + /** + * Searches JSlider in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @param index Ordinal component index. + * @return JSlider instance or null if component was not found. + */ + public static JSlider findJSlider(Container cont, ComponentChooser chooser, int index) { + return (JSlider) findComponent(cont, new JSliderFinder(chooser), index); + } + + /** + * Searches 0'th JSlider in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @return JSlider instance or null if component was not found. + */ + public static JSlider findJSlider(Container cont, ComponentChooser chooser) { + return findJSlider(cont, chooser, 0); + } + + /** + * Searches JSlider in container. + * + * @param cont Container to search component in. + * @param index Ordinal component index. + * @return JSlider instance or null if component was not found. + */ + public static JSlider findJSlider(Container cont, int index) { + return findJSlider(cont, ComponentSearcher.getTrueChooser(Integer.toString(index) + "'th JSlider instance"), index); + } + + /** + * Searches 0'th JSlider in container. + * + * @param cont Container to search component in. + * @return JSlider instance or null if component was not found. + */ + public static JSlider findJSlider(Container cont) { + return findJSlider(cont, 0); + } + + /** + * Waits JSlider in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @param index Ordinal component index. + * @return JSlider instance or null if component was not displayed. + * @throws TimeoutExpiredException + */ + public static JSlider waitJSlider(Container cont, ComponentChooser chooser, int index) { + return (JSlider) waitComponent(cont, new JSliderFinder(chooser), index); + } + + /** + * Waits 0'th JSlider in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @return JSlider instance or null if component was not displayed. + * @throws TimeoutExpiredException + */ + public static JSlider waitJSlider(Container cont, ComponentChooser chooser) { + return waitJSlider(cont, chooser, 0); + } + + /** + * Waits JSlider in container. + * + * @param cont Container to search component in. + * @param index Ordinal component index. + * @return JSlider instance or null if component was not displayed. + * @throws TimeoutExpiredException + */ + public static JSlider waitJSlider(Container cont, int index) { + return waitJSlider(cont, ComponentSearcher.getTrueChooser(Integer.toString(index) + "'th JSlider instance"), index); + } + + /** + * Waits 0'th JSlider in container. + * + * @param cont Container to search component in. + * @return JSlider instance or null if component was not displayed. + * @throws TimeoutExpiredException + */ + public static JSlider waitJSlider(Container cont) { + return waitJSlider(cont, 0); + } + + static { + Timeouts.initDefault("JSliderOperator.OneScrollClickTimeout", ONE_SCROLL_CLICK_TIMEOUT); + Timeouts.initDefault("JSliderOperator.WholeScrollTimeout", WHOLE_SCROLL_TIMEOUT); + Timeouts.initDefault("JSliderOperator.ScrollingDelta", SCROLLING_DELTA); + } + + /** + * Defines scroll model. Default model value - CLICK_SCROLL_MODEL. + * + * @param model New scroll model value. + * @see #CLICK_SCROLL_MODEL + * @see #PUSH_AND_WAIT_SCROLL_MODEL + * @see #getScrollModel() + * @see #scrollToValue(int) + * @deprecated All actions are prformed throw a {@code ScrollDriver} + * registered for this component, so value set by this method is ignored. + */ + @Deprecated + public void setScrollModel(int model) { + scrollModel = model; + } + + /** + * Specifies the scroll model. + * + * @return Current scroll model value. + * @see #setScrollModel(int) + * @deprecated All actions are prformed throw a {@code ScrollDriver} + * registered for this component, so value returned by this method is + * ignored. + */ + @Deprecated + public int getScrollModel() { + return scrollModel; + } + + @Override + public void setOutput(TestOut out) { + output = out; + super.setOutput(output.createErrorOutput()); + } + + @Override + public TestOut getOutput() { + return output; + } + + @Override + public void setTimeouts(Timeouts timeouts) { + this.timeouts = timeouts; + super.setTimeouts(timeouts); + } + + @Override + public Timeouts getTimeouts() { + return timeouts; + } + + /** + * Scrolls slider to the position defined by a ScrollAdjuster + * implementation. + * + * @param adj defines scrolling direction, and so on. + * @throws TimeoutExpiredException + */ + public void scrollTo(final ScrollAdjuster adj) { + makeComponentVisible(); + produceTimeRestricted(new Action() { + @Override + public Void launch(Void obj) { + driver.scroll(JSliderOperator.this, adj); + return null; + } + + @Override + public String getDescription() { + return "Scrolling"; + } + + @Override + public String toString() { + return "JSliderOperator.scrollTo.Action{description = " + getDescription() + '}'; + } + }, "JSliderOperator.WholeScrollTimeout"); + } + + /** + * Moves slider to the necessary value. + * + * @param value Value to move slider to. + * @throws TimeoutExpiredException + */ + public void scrollToValue(int value) { + output.printTrace("Move JSlider to " + Integer.toString(value) + + " value\n" + toStringSource()); + output.printGolden("Move JSlider to " + Integer.toString(value) + " value"); + scrollTo(new ValueScrollAdjuster(value)); + } + + /** + * Moves slider to the maximal value. + * + * @throws TimeoutExpiredException + */ + public void scrollToMaximum() { + output.printTrace("Move JSlider to maximum value\n" + + toStringSource()); + output.printGolden("Move JSlider to maximum value"); + scrollToValue(getMaximum()); + } + + /** + * Moves slider to the minimal value. + * + * @throws TimeoutExpiredException + */ + public void scrollToMinimum() { + output.printTrace("Move JSlider to minimum value\n" + + toStringSource()); + output.printGolden("Move JSlider to minimum value"); + scrollToValue(getMinimum()); + } + + @Override + public Hashtable getDump() { + Hashtable result = super.getDump(); + result.put(MINIMUM_DPROP, Integer.toString(((JSlider) getSource()).getMinimum())); + result.put(MAXIMUM_DPROP, Integer.toString(((JSlider) getSource()).getMaximum())); + result.put(ORIENTATION_DPROP, (((JSlider) getSource()).getOrientation() == JSlider.HORIZONTAL) + ? HORIZONTAL_ORIENTATION_DPROP_VALUE + : VERTICAL_ORIENTATION_DPROP_VALUE); + result.put(IS_INVERTED_DPROP, ((JSlider) getSource()).getInverted() ? "true" : "false"); + result.put(VALUE_DPROP, Integer.toString(((JSlider) getSource()).getValue())); + return result; + } + + //////////////////////////////////////////////////////// + //Mapping // + /** + * Maps {@code JSlider.addChangeListener(ChangeListener)} through queue + */ + public void addChangeListener(final ChangeListener changeListener) { + runMapping(new MapVoidAction("addChangeListener") { + @Override + public void map() { + ((JSlider) getSource()).addChangeListener(changeListener); + } + }); + } + + /** + * Maps {@code JSlider.createStandardLabels(int)} through queue + */ + public Hashtable createStandardLabels(final int i) { + return ((Hashtable) runMapping(new MapAction("createStandardLabels") { + @Override + public Object map() { + return ((JSlider) getSource()).createStandardLabels(i); + } + })); + } + + /** + * Maps {@code JSlider.createStandardLabels(int, int)} through queue + */ + public Hashtable createStandardLabels(final int i, final int i1) { + return ((Hashtable) runMapping(new MapAction("createStandardLabels") { + @Override + public Object map() { + return ((JSlider) getSource()).createStandardLabels(i, i1); + } + })); + } + + /** + * Maps {@code JSlider.getExtent()} through queue + */ + public int getExtent() { + return (runMapping(new MapIntegerAction("getExtent") { + @Override + public int map() { + return ((JSlider) getSource()).getExtent(); + } + })); + } + + /** + * Maps {@code JSlider.getInverted()} through queue + */ + public boolean getInverted() { + return (runMapping(new MapBooleanAction("getInverted") { + @Override + public boolean map() { + return ((JSlider) getSource()).getInverted(); + } + })); + } + + /** + * Maps {@code JSlider.getLabelTable()} through queue + */ + public Dictionary getLabelTable() { + return (runMapping(new MapAction>("getLabelTable") { + @Override + public Dictionary map() { + return ((JSlider) getSource()).getLabelTable(); + } + })); + } + + /** + * Maps {@code JSlider.getMajorTickSpacing()} through queue + */ + public int getMajorTickSpacing() { + return (runMapping(new MapIntegerAction("getMajorTickSpacing") { + @Override + public int map() { + return ((JSlider) getSource()).getMajorTickSpacing(); + } + })); + } + + /** + * Maps {@code JSlider.getMaximum()} through queue + */ + public int getMaximum() { + return (runMapping(new MapIntegerAction("getMaximum") { + @Override + public int map() { + return ((JSlider) getSource()).getMaximum(); + } + })); + } + + /** + * Maps {@code JSlider.getMinimum()} through queue + */ + public int getMinimum() { + return (runMapping(new MapIntegerAction("getMinimum") { + @Override + public int map() { + return ((JSlider) getSource()).getMinimum(); + } + })); + } + + /** + * Maps {@code JSlider.getMinorTickSpacing()} through queue + */ + public int getMinorTickSpacing() { + return (runMapping(new MapIntegerAction("getMinorTickSpacing") { + @Override + public int map() { + return ((JSlider) getSource()).getMinorTickSpacing(); + } + })); + } + + /** + * Maps {@code JSlider.getModel()} through queue + */ + public BoundedRangeModel getModel() { + return (runMapping(new MapAction("getModel") { + @Override + public BoundedRangeModel map() { + return ((JSlider) getSource()).getModel(); + } + })); + } + + /** + * Maps {@code JSlider.getOrientation()} through queue + */ + public int getOrientation() { + return (runMapping(new MapIntegerAction("getOrientation") { + @Override + public int map() { + return ((JSlider) getSource()).getOrientation(); + } + })); + } + + /** + * Maps {@code JSlider.getPaintLabels()} through queue + */ + public boolean getPaintLabels() { + return (runMapping(new MapBooleanAction("getPaintLabels") { + @Override + public boolean map() { + return ((JSlider) getSource()).getPaintLabels(); + } + })); + } + + /** + * Maps {@code JSlider.getPaintTicks()} through queue + */ + public boolean getPaintTicks() { + return (runMapping(new MapBooleanAction("getPaintTicks") { + @Override + public boolean map() { + return ((JSlider) getSource()).getPaintTicks(); + } + })); + } + + /** + * Maps {@code JSlider.getPaintTrack()} through queue + */ + public boolean getPaintTrack() { + return (runMapping(new MapBooleanAction("getPaintTrack") { + @Override + public boolean map() { + return ((JSlider) getSource()).getPaintTrack(); + } + })); + } + + /** + * Maps {@code JSlider.getSnapToTicks()} through queue + */ + public boolean getSnapToTicks() { + return (runMapping(new MapBooleanAction("getSnapToTicks") { + @Override + public boolean map() { + return ((JSlider) getSource()).getSnapToTicks(); + } + })); + } + + /** + * Maps {@code JSlider.getUI()} through queue + */ + public SliderUI getUI() { + return (runMapping(new MapAction("getUI") { + @Override + public SliderUI map() { + return ((JSlider) getSource()).getUI(); + } + })); + } + + /** + * Maps {@code JSlider.getValue()} through queue + */ + public int getValue() { + return (runMapping(new MapIntegerAction("getValue") { + @Override + public int map() { + return ((JSlider) getSource()).getValue(); + } + })); + } + + /** + * Maps {@code JSlider.getValueIsAdjusting()} through queue + */ + public boolean getValueIsAdjusting() { + return (runMapping(new MapBooleanAction("getValueIsAdjusting") { + @Override + public boolean map() { + return ((JSlider) getSource()).getValueIsAdjusting(); + } + })); + } + + /** + * Maps {@code JSlider.removeChangeListener(ChangeListener)} through queue + */ + public void removeChangeListener(final ChangeListener changeListener) { + runMapping(new MapVoidAction("removeChangeListener") { + @Override + public void map() { + ((JSlider) getSource()).removeChangeListener(changeListener); + } + }); + } + + /** + * Maps {@code JSlider.setExtent(int)} through queue + */ + public void setExtent(final int i) { + runMapping(new MapVoidAction("setExtent") { + @Override + public void map() { + ((JSlider) getSource()).setExtent(i); + } + }); + } + + /** + * Maps {@code JSlider.setInverted(boolean)} through queue + */ + public void setInverted(final boolean b) { + runMapping(new MapVoidAction("setInverted") { + @Override + public void map() { + ((JSlider) getSource()).setInverted(b); + } + }); + } + + /** + * Maps {@code JSlider.setLabelTable(Dictionary)} through queue + */ + public void setLabelTable(final Dictionary dictionary) { + runMapping(new MapVoidAction("setLabelTable") { + @Override + public void map() { + ((JSlider) getSource()).setLabelTable(dictionary); + } + }); + } + + /** + * Maps {@code JSlider.setMajorTickSpacing(int)} through queue + */ + public void setMajorTickSpacing(final int i) { + runMapping(new MapVoidAction("setMajorTickSpacing") { + @Override + public void map() { + ((JSlider) getSource()).setMajorTickSpacing(i); + } + }); + } + + /** + * Maps {@code JSlider.setMaximum(int)} through queue + */ + public void setMaximum(final int i) { + runMapping(new MapVoidAction("setMaximum") { + @Override + public void map() { + ((JSlider) getSource()).setMaximum(i); + } + }); + } + + /** + * Maps {@code JSlider.setMinimum(int)} through queue + */ + public void setMinimum(final int i) { + runMapping(new MapVoidAction("setMinimum") { + @Override + public void map() { + ((JSlider) getSource()).setMinimum(i); + } + }); + } + + /** + * Maps {@code JSlider.setMinorTickSpacing(int)} through queue + */ + public void setMinorTickSpacing(final int i) { + runMapping(new MapVoidAction("setMinorTickSpacing") { + @Override + public void map() { + ((JSlider) getSource()).setMinorTickSpacing(i); + } + }); + } + + /** + * Maps {@code JSlider.setModel(BoundedRangeModel)} through queue + */ + public void setModel(final BoundedRangeModel boundedRangeModel) { + runMapping(new MapVoidAction("setModel") { + @Override + public void map() { + ((JSlider) getSource()).setModel(boundedRangeModel); + } + }); + } + + /** + * Maps {@code JSlider.setOrientation(int)} through queue + */ + public void setOrientation(final int i) { + runMapping(new MapVoidAction("setOrientation") { + @Override + public void map() { + ((JSlider) getSource()).setOrientation(i); + } + }); + } + + /** + * Maps {@code JSlider.setPaintLabels(boolean)} through queue + */ + public void setPaintLabels(final boolean b) { + runMapping(new MapVoidAction("setPaintLabels") { + @Override + public void map() { + ((JSlider) getSource()).setPaintLabels(b); + } + }); + } + + /** + * Maps {@code JSlider.setPaintTicks(boolean)} through queue + */ + public void setPaintTicks(final boolean b) { + runMapping(new MapVoidAction("setPaintTicks") { + @Override + public void map() { + ((JSlider) getSource()).setPaintTicks(b); + } + }); + } + + /** + * Maps {@code JSlider.setPaintTrack(boolean)} through queue + */ + public void setPaintTrack(final boolean b) { + runMapping(new MapVoidAction("setPaintTrack") { + @Override + public void map() { + ((JSlider) getSource()).setPaintTrack(b); + } + }); + } + + /** + * Maps {@code JSlider.setSnapToTicks(boolean)} through queue + */ + public void setSnapToTicks(final boolean b) { + runMapping(new MapVoidAction("setSnapToTicks") { + @Override + public void map() { + ((JSlider) getSource()).setSnapToTicks(b); + } + }); + } + + /** + * Maps {@code JSlider.setUI(SliderUI)} through queue + */ + public void setUI(final SliderUI sliderUI) { + runMapping(new MapVoidAction("setUI") { + @Override + public void map() { + ((JSlider) getSource()).setUI(sliderUI); + } + }); + } + + /** + * Maps {@code JSlider.setValue(int)} through queue + */ + public void setValue(final int i) { + runMapping(new MapVoidAction("setValue") { + @Override + public void map() { + ((JSlider) getSource()).setValue(i); + } + }); + } + + /** + * Maps {@code JSlider.setValueIsAdjusting(boolean)} through queue + */ + public void setValueIsAdjusting(final boolean b) { + runMapping(new MapVoidAction("setValueIsAdjusting") { + @Override + public void map() { + ((JSlider) getSource()).setValueIsAdjusting(b); + } + }); + } + + //End of mapping // + //////////////////////////////////////////////////////// + /** + * Checks component type. + */ + public static class JSliderFinder extends Finder { + + /** + * Constructs JSliderFinder. + * + * @param sf other searching criteria. + */ + public JSliderFinder(ComponentChooser sf) { + super(JSlider.class, sf); + } + + /** + * Constructs JSliderFinder. + */ + public JSliderFinder() { + super(JSlider.class); + } + } + + private class ValueScrollAdjuster implements ScrollAdjuster { + + int value; + + public ValueScrollAdjuster(int value) { + this.value = value; + } + + @Override + public int getScrollDirection() { + if (getValue() == value) { + return ScrollAdjuster.DO_NOT_TOUCH_SCROLL_DIRECTION; + } else { + return ((getValue() < value) + ? ScrollAdjuster.INCREASE_SCROLL_DIRECTION + : ScrollAdjuster.DECREASE_SCROLL_DIRECTION); + } + } + + @Override + public int getScrollOrientation() { + return getOrientation(); + } + + @Override + public String getDescription() { + return "Scroll to " + Integer.toString(value) + " value"; + } + + @Override + public String toString() { + return "ValueScrollAdjuster{" + "value=" + value + '}'; + } + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JSpinnerOperator.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JSpinnerOperator.java new file mode 100644 index 00000000000..1cbf118c594 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JSpinnerOperator.java @@ -0,0 +1,1296 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.operators; + +import java.awt.Component; +import java.awt.Container; +import java.text.ParseException; +import java.util.Date; +import java.util.Hashtable; +import java.util.List; + +import javax.swing.JComponent; +import javax.swing.JSpinner; +import javax.swing.SpinnerDateModel; +import javax.swing.SpinnerListModel; +import javax.swing.SpinnerModel; +import javax.swing.SpinnerNumberModel; +import javax.swing.SwingConstants; +import javax.swing.event.ChangeListener; +import javax.swing.plaf.SpinnerUI; + +import org.netbeans.jemmy.Action; +import org.netbeans.jemmy.ComponentChooser; +import org.netbeans.jemmy.ComponentSearcher; +import org.netbeans.jemmy.JemmyException; +import org.netbeans.jemmy.Outputable; +import org.netbeans.jemmy.TestOut; +import org.netbeans.jemmy.TimeoutExpiredException; +import org.netbeans.jemmy.Timeoutable; +import org.netbeans.jemmy.Timeouts; +import org.netbeans.jemmy.drivers.DriverManager; +import org.netbeans.jemmy.drivers.ScrollDriver; +import org.netbeans.jemmy.drivers.scrolling.ScrollAdjuster; + +/** + * Provides methods to work with {@code javax.swing.JSpinner} component + *
+ * + * @see NumberSpinnerOperator + * @see ListSpinnerOperator + * @see DateSpinnerOperator + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + */ +public class JSpinnerOperator extends JComponentOperator + implements Timeoutable, Outputable { + + /** + * Identifier for a "value" property. + * + * @see #getDump + */ + public static final String VALUE_DPROP = "Value"; + + private final static long WHOLE_SCROLL_TIMEOUT = 60000; + + private Timeouts timeouts; + private TestOut output; + + private ScrollDriver driver; + + private JButtonOperator increaseOperator = null; + private JButtonOperator decreaseOperator = null; + + /** + * Constructor. + * + * @param b JSpinner component. + */ + public JSpinnerOperator(JSpinner b) { + super(b); + driver = DriverManager.getScrollDriver(getClass()); + } + + /** + * Constructs a JSpinnerOperator object. + * + * @param cont a container + * @param chooser a component chooser specifying searching criteria. + * @param index an index between appropriate ones. + * @throws TimeoutExpiredException + */ + public JSpinnerOperator(ContainerOperator cont, ComponentChooser chooser, int index) { + this((JSpinner) cont. + waitSubComponent(new JSpinnerFinder(chooser), + index)); + copyEnvironment(cont); + } + + /** + * Constructs a JSpinnerOperator object. + * + * @param cont a container + * @param chooser a component chooser specifying searching criteria. + * @throws TimeoutExpiredException + */ + public JSpinnerOperator(ContainerOperator cont, ComponentChooser chooser) { + this(cont, chooser, 0); + } + + /** + * Constructs a JSpinnerOperator object. + * + * @param cont The operator for a container containing the sought for + * button. + * @param text toString() representation of the current spinner value. + * @param index Ordinal component index. The first component has + * {@code index} 0. + * @throws TimeoutExpiredException + */ + public JSpinnerOperator(ContainerOperator cont, String text, int index) { + this((JSpinner) waitComponent(cont, + new JSpinnerByTextFinder(text, + cont.getComparator()), + index)); + copyEnvironment(cont); + } + + /** + * Constructs a JSpinnerOperator object. + * + * @param cont The operator for a container containing the sought for + * button. + * @param text toString() representation of the current spinner value. + * @throws TimeoutExpiredException + */ + public JSpinnerOperator(ContainerOperator cont, String text) { + this(cont, text, 0); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont Operator pointing a container to search component in. + * @param index Ordinal component index. + * @throws TimeoutExpiredException + */ + public JSpinnerOperator(ContainerOperator cont, int index) { + this((JSpinner) waitComponent(cont, + new JSpinnerFinder(), + index)); + copyEnvironment(cont); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont Operator pointing a container to search component in. + * @throws TimeoutExpiredException + */ + public JSpinnerOperator(ContainerOperator cont) { + this(cont, 0); + } + + /** + * Searches JSpinner in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @param index Ordinal component index. + * @return JSpinner instance or null if component was not found. + */ + public static JSpinner findJSpinner(Container cont, ComponentChooser chooser, int index) { + return (JSpinner) findComponent(cont, new JSpinnerFinder(chooser), index); + } + + /** + * Searches 0'th JSpinner in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @return JSpinner instance or null if component was not found. + */ + public static JSpinner findJSpinner(Container cont, ComponentChooser chooser) { + return findJSpinner(cont, chooser, 0); + } + + /** + * Searches JSpinner in container. + * + * @param cont Container to search component in. + * @param index Ordinal component index. + * @return JSpinner instance or null if component was not found. + */ + public static JSpinner findJSpinner(Container cont, int index) { + return findJSpinner(cont, ComponentSearcher.getTrueChooser(Integer.toString(index) + "'th JSpinner instance"), index); + } + + /** + * Searches 0'th JSpinner in container. + * + * @param cont Container to search component in. + * @return JSpinner instance or null if component was not found. + */ + public static JSpinner findJSpinner(Container cont) { + return findJSpinner(cont, 0); + } + + /** + * Waits JSpinner in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @param index Ordinal component index. + * @return JSpinner instance or null if component was not displayed. + * @throws TimeoutExpiredException + */ + public static JSpinner waitJSpinner(Container cont, ComponentChooser chooser, int index) { + return (JSpinner) waitComponent(cont, new JSpinnerFinder(chooser), index); + } + + /** + * Waits 0'th JSpinner in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @return JSpinner instance or null if component was not displayed. + * @throws TimeoutExpiredException + */ + public static JSpinner waitJSpinner(Container cont, ComponentChooser chooser) { + return waitJSpinner(cont, chooser, 0); + } + + /** + * Waits JSpinner in container. + * + * @param cont Container to search component in. + * @param index Ordinal component index. + * @return JSpinner instance or null if component was not displayed. + * @throws TimeoutExpiredException + */ + public static JSpinner waitJSpinner(Container cont, int index) { + return waitJSpinner(cont, ComponentSearcher.getTrueChooser(Integer.toString(index) + "'th JSpinner instance"), index); + } + + /** + * Waits 0'th JSpinner in container. + * + * @param cont Container to search component in. + * @return JSpinner instance or null if component was not displayed. + * @throws TimeoutExpiredException + */ + public static JSpinner waitJSpinner(Container cont) { + return waitJSpinner(cont, 0); + } + + /** + * Checks operator's model type. + * + * @param oper an operator to check model + * @param modelClass a model class. + * @throws SpinnerModelException if an operator's model is not an instance + * of specified class. + */ + public static void checkModel(JSpinnerOperator oper, Class modelClass) { + if (!modelClass.isInstance(oper.getModel())) { + throw (new SpinnerModelException("JSpinner model is not a " + modelClass.getName(), + oper.getSource())); + } + } + + static { + Timeouts.initDefault("JSpinnerOperator.WholeScrollTimeout", WHOLE_SCROLL_TIMEOUT); + } + + @Override + public void setOutput(TestOut out) { + output = out; + super.setOutput(output.createErrorOutput()); + } + + @Override + public TestOut getOutput() { + return output; + } + + @Override + public void setTimeouts(Timeouts timeouts) { + this.timeouts = timeouts; + super.setTimeouts(timeouts); + } + + @Override + public Timeouts getTimeouts() { + return timeouts; + } + + /** + * Returns an instance of {@code NumberSpinnerOperator} operator, the + * operator used for {@code JSpinner} having + * {@code SpinnerNumberModel} model. + * + * @return a {@code NumberSpinnerOperator} created for the same + * {@code JSpinner} as this operator. + * @throws SpinnerModelException if an operator's model is not an instance + * of {@code SpinnerNumberModel} + */ + public NumberSpinnerOperator getNumberSpinner() { + return new NumberSpinnerOperator(this); + } + + /** + * Returns an instance of {@code ListSpinnerOperator} operator, the + * operator used for {@code JSpinner} having + * {@code SpinnerListModel} model. + * + * @return a {@code ListSpinnerOperator} created for the same + * {@code JSpinner} as this operator. + * @throws SpinnerModelException if an operator's model is not an instance + * of {@code SpinnerListModel} + */ + public ListSpinnerOperator getListSpinner() { + return new ListSpinnerOperator(this); + } + + /** + * Returns an instance of {@code DateSpinnerOperator} operator, the + * operator used for {@code JSpinner} having + * {@code SpinnerDateModel} model. + * + * @return a {@code DateSpinnerOperator} created for the same + * {@code JSpinner} as this operator. + * @throws SpinnerModelException if an operator's model is not an instance + * of {@code SpinnerDateModel} + */ + public DateSpinnerOperator getDateSpinner() { + return new DateSpinnerOperator(this); + } + + /** + * Scrolls to reach a condition specified by {@code ScrollAdjuster} + * + * @param adj scrolling criteria. + */ + public void scrollTo(final ScrollAdjuster adj) { + produceTimeRestricted(new Action() { + @Override + public Void launch(Void obj) { + driver.scroll(JSpinnerOperator.this, adj); + return null; + } + + @Override + public String getDescription() { + return "Scrolling"; + } + + @Override + public String toString() { + return "JSpinnerOperator.scrollTo.Action{description = " + getDescription() + '}'; + } + }, "JSpinnerOperator.WholeScrollTimeout"); + } + + /** + * Scrolls to maximum value. + * + * @throws SpinnerModelException if an operator's model does not have a + * maximum value. + */ + public void scrollToMaximum() { + produceTimeRestricted(new Action() { + @Override + public Void launch(Void obj) { + driver.scrollToMaximum(JSpinnerOperator.this, SwingConstants.VERTICAL); + return null; + } + + @Override + public String getDescription() { + return "Scrolling"; + } + + @Override + public String toString() { + return "JSpinnerOperator.scrollToMaximum.Action{description = " + getDescription() + '}'; + } + }, "JSpinnerOperator.WholeScrollTimeout"); + } + + /** + * Scrolls to minimum value. + * + * @throws SpinnerModelException if an operator's model does not have a + * minimum value. + */ + public void scrollToMinimum() { + produceTimeRestricted(new Action() { + @Override + public Void launch(Void obj) { + driver.scrollToMinimum(JSpinnerOperator.this, SwingConstants.VERTICAL); + return null; + } + + @Override + public String getDescription() { + return "Scrolling"; + } + + @Override + public String toString() { + return "JSpinnerOperator.scrollToMinimum.Action{description = " + getDescription() + '}'; + } + }, "JSpinnerOperator.WholeScrollTimeout"); + } + + /** + * Scrolls to exact match of a spinner value to the specified value. + * + * @param value an value to scroll to. + * @param direction a scrolling direction - one of + * {@code ScrollAdjuster.*_SCROLL_DIRECTION} fields. + */ + public void scrollToObject(Object value, int direction) { + scrollTo(new ExactScrollAdjuster(this, value, direction)); + } + + /** + * Scrolls to matching of getValue().toString() with the pattern. + * + * @param pattern a pattern to compare with + * @param comparator a string comparision criteria + * @param direction a scrolling direction - one of + * {@code ScrollAdjuster.*_SCROLL_DIRECTION} fields. + */ + public void scrollToString(String pattern, StringComparator comparator, int direction) { + scrollTo(new ToStringScrollAdjuster(this, pattern, comparator, direction)); + } + + /** + * Scrolls to matching of {@code getValue().toString()} with the + * pattern. Uses {@code StringComparator} assigned to the operator. + * + * @param pattern a pattern to compare with + * @param direction a scrolling direction - one of + * {@code ScrollAdjuster.*_SCROLL_DIRECTION} fields. + */ + public void scrollToString(String pattern, int direction) { + scrollToString(pattern, getComparator(), direction); + } + + /** + * Returns an operator for a button used for value increasing. + * + * @return an operator for a first JButton inside this spinner. + */ + public JButtonOperator getIncreaseOperator() { + if (increaseOperator == null) { + increaseOperator = (JButtonOperator) createSubOperator(new JButtonOperator.JButtonFinder(), 0); + increaseOperator.copyEnvironment(this); + increaseOperator.setOutput(getOutput().createErrorOutput()); + } + return increaseOperator; + } + + /** + * Returns an operator for a button used for value decreasing. + * + * @return an operator for a second JButton inside this spinner. + */ + public JButtonOperator getDecreaseOperator() { + if (decreaseOperator == null) { + decreaseOperator = (JButtonOperator) createSubOperator(new JButtonOperator.JButtonFinder(), 1); + decreaseOperator.copyEnvironment(this); + decreaseOperator.setOutput(getOutput().createErrorOutput()); + } + return decreaseOperator; + } + + /** + * Returns a minimal value. Returns null if model is not one of the + * following: {@code javax.swing.SpinnerDateModel}, + * {@code javax.swing.SpinnerListModel}, + * {@code javax.swing.SpinnerNumberModel}. Also, returns null if the + * model does not have a minimal value. + * + * @return a minimal value. + */ + public Object getMinimum() { + SpinnerModel model = getModel(); + if (model instanceof SpinnerNumberModel) { + return ((SpinnerNumberModel) model).getMinimum(); + } else if (model instanceof SpinnerDateModel) { + return ((SpinnerDateModel) model).getEnd(); + } else if (model instanceof SpinnerListModel) { + List list = ((SpinnerListModel) model).getList(); + return list.get(list.size() - 1); + } else { + return null; + } + } + + /** + * Returns a maximal value. Returns null if model is not one of the + * following: {@code javax.swing.SpinnerDateModel}, + * {@code javax.swing.SpinnerListModel}, + * {@code javax.swing.SpinnerNumberModel}. Also, returns null if the + * model does not have a maximal value. + * + * @return a maximal value. + */ + public Object getMaximum() { + SpinnerModel model = getModel(); + if (model instanceof SpinnerNumberModel) { + return ((SpinnerNumberModel) model).getMaximum(); + } else if (model instanceof SpinnerDateModel) { + return ((SpinnerDateModel) model).getEnd(); + } else if (model instanceof SpinnerListModel) { + List list = ((SpinnerListModel) model).getList(); + return list.get(list.size() - 1); + } else { + return null; + } + } + + @Override + public Hashtable getDump() { + Hashtable result = super.getDump(); + result.put(VALUE_DPROP, ((JSpinner) getSource()).getValue().toString()); + return result; + } + + //////////////////////////////////////////////////////// + //Mapping // + /** + * Maps {@code JSpinner.getValue()} through queue + */ + public Object getValue() { + return (runMapping(new MapAction("getValue") { + @Override + public Object map() { + return ((JSpinner) getSource()).getValue(); + } + })); + } + + /** + * Maps {@code JSpinner.setValue(Object)} through queue + */ + public void setValue(final Object object) { + runMapping(new MapVoidAction("setValue") { + @Override + public void map() { + ((JSpinner) getSource()).setValue(object); + } + }); + } + + /** + * Maps {@code JSpinner.getUI()} through queue + */ + public SpinnerUI getUI() { + return (runMapping(new MapAction("getUI") { + @Override + public SpinnerUI map() { + return ((JSpinner) getSource()).getUI(); + } + })); + } + + /** + * Maps {@code JSpinner.setUI(SpinnerUI)} through queue + */ + public void setUI(final SpinnerUI spinnerUI) { + runMapping(new MapVoidAction("setUI") { + @Override + public void map() { + ((JSpinner) getSource()).setUI(spinnerUI); + } + }); + } + + /** + * Maps {@code JSpinner.setModel(SpinnerModel)} through queue + */ + public void setModel(final SpinnerModel spinnerModel) { + runMapping(new MapVoidAction("setModel") { + @Override + public void map() { + ((JSpinner) getSource()).setModel(spinnerModel); + } + }); + } + + /** + * Maps {@code JSpinner.getModel()} through queue + */ + public SpinnerModel getModel() { + return (runMapping(new MapAction("getModel") { + @Override + public SpinnerModel map() { + return ((JSpinner) getSource()).getModel(); + } + })); + } + + /** + * Maps {@code JSpinner.getNextValue()} through queue + */ + public Object getNextValue() { + return (runMapping(new MapAction("getNextValue") { + @Override + public Object map() { + return ((JSpinner) getSource()).getNextValue(); + } + })); + } + + /** + * Maps {@code JSpinner.addChangeListener(ChangeListener)} through queue + */ + public void addChangeListener(final ChangeListener changeListener) { + runMapping(new MapVoidAction("addChangeListener") { + @Override + public void map() { + ((JSpinner) getSource()).addChangeListener(changeListener); + } + }); + } + + /** + * Maps {@code JSpinner.removeChangeListener(ChangeListener)} through queue + */ + public void removeChangeListener(final ChangeListener changeListener) { + runMapping(new MapVoidAction("removeChangeListener") { + @Override + public void map() { + ((JSpinner) getSource()).removeChangeListener(changeListener); + } + }); + } + + /** + * Maps {@code JSpinner.getChangeListeners()} through queue + */ + public ChangeListener[] getChangeListeners() { + return ((ChangeListener[]) runMapping(new MapAction("getChangeListeners") { + @Override + public Object map() { + return ((JSpinner) getSource()).getChangeListeners(); + } + })); + } + + /** + * Maps {@code JSpinner.getPreviousValue()} through queue + */ + public Object getPreviousValue() { + return (runMapping(new MapAction("getPreviousValue") { + @Override + public Object map() { + return ((JSpinner) getSource()).getPreviousValue(); + } + })); + } + + /** + * Maps {@code JSpinner.setEditor(JComponent)} through queue + */ + public void setEditor(final JComponent jComponent) { + runMapping(new MapVoidAction("setEditor") { + @Override + public void map() { + ((JSpinner) getSource()).setEditor(jComponent); + } + }); + } + + /** + * Maps {@code JSpinner.getEditor()} through queue + */ + public JComponent getEditor() { + return (runMapping(new MapAction("getEditor") { + @Override + public JComponent map() { + return ((JSpinner) getSource()).getEditor(); + } + })); + } + + /** + * Maps {@code JSpinner.commitEdit()} through queue + */ + public void commitEdit() { + runMapping(new MapVoidAction("commitEdit") { + @Override + public void map() throws ParseException { + ((JSpinner) getSource()).commitEdit(); + } + }); + } + + //End of mapping // + //////////////////////////////////////////////////////// + /** + * Allows to find component by text. + */ + public static class JSpinnerByTextFinder implements ComponentChooser { + + String label; + StringComparator comparator; + + /** + * Constructs JSpinnerByTextFinder. + * + * @param lb a text pattern + * @param comparator specifies string comparision algorithm. + */ + public JSpinnerByTextFinder(String lb, StringComparator comparator) { + label = lb; + this.comparator = comparator; + } + + /** + * Constructs JSpinnerByTextFinder. + * + * @param lb a text pattern + */ + public JSpinnerByTextFinder(String lb) { + this(lb, Operator.getDefaultStringComparator()); + } + + @Override + public boolean checkComponent(Component comp) { + if (comp instanceof JSpinner) { + if (((JSpinner) comp).getValue() != null) { + return (comparator.equals(((JSpinner) comp).getValue().toString(), + label)); + } + } + return false; + } + + @Override + public String getDescription() { + return "JSpinner with text \"" + label + "\""; + } + + @Override + public String toString() { + return "JSpinnerByTextFinder{" + "label=" + label + ", comparator=" + comparator + '}'; + } + } + + /** + * Checks component type. + */ + public static class JSpinnerFinder extends Finder { + + /** + * Constructs JSpinnerFinder. + * + * @param sf other searching criteria. + */ + public JSpinnerFinder(ComponentChooser sf) { + super(JSpinner.class, sf); + } + + /** + * Constructs JSpinnerFinder. + */ + public JSpinnerFinder() { + super(JSpinner.class); + } + } + + /** + * A {@code ScrollAdjuster} to be used for {@code JSpinner} + * component having {@code SpinnerNumberModel} model. + * + * @see NumberSpinnerOperator + */ + public static class NumberScrollAdjuster implements ScrollAdjuster { + + SpinnerNumberModel model; + double value; + + /** + * Constructs a {@code NumberScrollAdjuster} object. + * + * @param oper an operator to work with. + * @param value a value to scroll to. + */ + public NumberScrollAdjuster(JSpinnerOperator oper, double value) { + this.value = value; + checkModel(oper, SpinnerNumberModel.class); + model = (SpinnerNumberModel) oper.getModel(); + } + + /** + * Constructs a {@code NumberScrollAdjuster} object. + * + * @param oper an operator to work with. + * @param value a value to scroll to. + */ + public NumberScrollAdjuster(JSpinnerOperator oper, Number value) { + this(oper, value.doubleValue()); + } + + @Override + public int getScrollDirection() { + if (value > model.getNumber().doubleValue()) { + return ScrollAdjuster.INCREASE_SCROLL_DIRECTION; + } else if (value < model.getNumber().doubleValue()) { + return ScrollAdjuster.DECREASE_SCROLL_DIRECTION; + } else { + return ScrollAdjuster.DO_NOT_TOUCH_SCROLL_DIRECTION; + } + } + + @Override + public int getScrollOrientation() { + return SwingConstants.VERTICAL; + } + + @Override + public String getDescription() { + return "Spin to " + value + " value"; + } + + @Override + public String toString() { + return "NumberScrollAdjuster{" + "model=" + model + ", value=" + value + '}'; + } + } + + /** + * A {@code ScrollAdjuster} to be used for {@code JSpinner} + * component having {@code SpinnerListModel} model. + * + * @see ListSpinnerOperator + */ + public static class ListScrollAdjuster implements ScrollAdjuster { + + SpinnerListModel model; + int itemIndex; + List elements; + + private ListScrollAdjuster(JSpinnerOperator oper) { + checkModel(oper, SpinnerListModel.class); + model = (SpinnerListModel) oper.getModel(); + elements = model.getList(); + } + + /** + * Constructs a {@code ListScrollAdjuster} object. + * + * @param oper an operator to work with. + * @param value a value to scroll to. + */ + public ListScrollAdjuster(JSpinnerOperator oper, Object value) { + this(oper); + this.itemIndex = elements.indexOf(value); + } + + /** + * Constructs a {@code ListScrollAdjuster} object. + * + * @param oper an operator to work with. + * @param itemIndex an item index to scroll to. + */ + public ListScrollAdjuster(JSpinnerOperator oper, int itemIndex) { + this(oper); + this.itemIndex = itemIndex; + } + + @Override + public int getScrollDirection() { + int curIndex = elements.indexOf(model.getValue()); + if (itemIndex > curIndex) { + return ScrollAdjuster.INCREASE_SCROLL_DIRECTION; + } else if (itemIndex < curIndex) { + return ScrollAdjuster.DECREASE_SCROLL_DIRECTION; + } else { + return ScrollAdjuster.DO_NOT_TOUCH_SCROLL_DIRECTION; + } + } + + @Override + public int getScrollOrientation() { + return SwingConstants.VERTICAL; + } + + @Override + public String getDescription() { + return "Spin to " + Integer.toString(itemIndex) + "'th item"; + } + + @Override + public String toString() { + return "ListScrollAdjuster{" + "model=" + model + ", itemIndex=" + itemIndex + ", elements=" + elements + '}'; + } + } + + /** + * A {@code ScrollAdjuster} to be used for {@code JSpinner} + * component having {@code SpinnerDateModel} model. + * + * @see DateSpinnerOperator + */ + public static class DateScrollAdjuster implements ScrollAdjuster { + + Date date; + SpinnerDateModel model; + + /** + * Constructs a {@code DateScrollAdjuster} object. + * + * @param oper an operator to work with. + * @param date a date to scroll to. + */ + public DateScrollAdjuster(JSpinnerOperator oper, Date date) { + this.date = date; + checkModel(oper, SpinnerDateModel.class); + model = (SpinnerDateModel) oper.getModel(); + } + + @Override + public int getScrollDirection() { + if (date.after(model.getDate())) { + return ScrollAdjuster.INCREASE_SCROLL_DIRECTION; + } else if (date.before(model.getDate())) { + return ScrollAdjuster.DECREASE_SCROLL_DIRECTION; + } else { + return ScrollAdjuster.DO_NOT_TOUCH_SCROLL_DIRECTION; + } + } + + @Override + public int getScrollOrientation() { + return SwingConstants.VERTICAL; + } + + @Override + public String getDescription() { + return "Spin to " + date.toString() + " date"; + } + + @Override + public String toString() { + return "DateScrollAdjuster{" + "date=" + date + ", model=" + model + '}'; + } + } + + /** + * Abstract class for a scrolling of a spinner having unknown model type. A + * subclass needs to override {@code reached(Object)} method to specify a + * criteria of successful scrolling. + */ + public abstract static class ObjectScrollAdjuster implements ScrollAdjuster { + + SpinnerModel model; + int direction; + + /** + * Constructs a {@code ObjectScrollAdjuster} object. + * + * @param oper an operator to work with. + * @param direction a scrolling direction - one of + * {@code ScrollAdjuster.*_SCROLL_DIRECTION} fields. + */ + public ObjectScrollAdjuster(JSpinnerOperator oper, int direction) { + this.direction = direction; + model = oper.getModel(); + } + + @Override + public int getScrollDirection() { + if (reached(model.getValue())) { + return ScrollAdjuster.DO_NOT_TOUCH_SCROLL_DIRECTION; + } else if (direction == ScrollAdjuster.INCREASE_SCROLL_DIRECTION + && model.getNextValue() != null + || direction == ScrollAdjuster.DECREASE_SCROLL_DIRECTION + && model.getPreviousValue() != null) { + return direction; + } else { + return ScrollAdjuster.DO_NOT_TOUCH_SCROLL_DIRECTION; + } + } + + public abstract boolean reached(Object curvalue); + + @Override + public int getScrollOrientation() { + return SwingConstants.VERTICAL; + } + } + + /** + * Class for a scrolling of a spinner having unknown model type. Checks + * spinner value for exact equality with a specified value. + */ + public static class ExactScrollAdjuster extends ObjectScrollAdjuster { + + Object obj; + + /** + * Constructs a {@code ExactScrollAdjuster} object. + * + * @param oper an operator to work with. + * @param direction a scrolling direction - one of + * {@code ScrollAdjuster.*_SCROLL_DIRECTION} fields. + */ + public ExactScrollAdjuster(JSpinnerOperator oper, Object obj, int direction) { + super(oper, direction); + this.obj = obj; + } + + @Override + public boolean reached(Object curvalue) { + return curvalue != null && curvalue.equals(obj); + } + + @Override + public String getDescription() { + return "Spin to " + obj.toString() + " value"; + } + + @Override + public String toString() { + return "ExactScrollAdjuster{" + "obj=" + obj + '}'; + } + + @Override + public int getScrollOrientation() { + return SwingConstants.VERTICAL; + } + } + + /** + * Class for a scrolling of a spinner having unknown model type. Checks + * spinner value's toString() reprsentation to match a string pattern. + */ + public static class ToStringScrollAdjuster extends ObjectScrollAdjuster { + + String pattern; + StringComparator comparator; + + /** + * Constructs a {@code ToStringScrollAdjuster} object. + * + * @param oper an operator to work with. + * @param pattern a pattern to compare with + * @param comparator specifies string comparision algorithm. + * @param direction a scrolling direction - one of + * {@code ScrollAdjuster.*_SCROLL_DIRECTION} fields. + */ + public ToStringScrollAdjuster(JSpinnerOperator oper, String pattern, StringComparator comparator, int direction) { + super(oper, direction); + this.pattern = pattern; + this.comparator = comparator; + } + + /** + * Constructs a {@code ToStringScrollAdjuster} object. Uses + * {@code StringComparator} assigned to the operator. + * + * @param oper an operator to work with. + * @param pattern a pattern to compare with + * @param direction a scrolling direction - one of + * {@code ScrollAdjuster.*_SCROLL_DIRECTION} fields. + */ + public ToStringScrollAdjuster(JSpinnerOperator oper, String pattern, int direction) { + this(oper, pattern, oper.getComparator(), direction); + } + + @Override + public boolean reached(Object curvalue) { + return curvalue != null && comparator.equals(curvalue.toString(), pattern); + } + + @Override + public String getDescription() { + return "Spin to \"" + pattern + "\" value"; + } + + @Override + public String toString() { + return "ToStringScrollAdjuster{" + "pattern=" + pattern + ", comparator=" + comparator + '}'; + } + + @Override + public int getScrollOrientation() { + return SwingConstants.VERTICAL; + } + } + + /** + * Provides some specific functionality for {@code JSpinner} components + * having {@code SpinnerNumberModel} model. Constructor of this object + * is private - it cannot be received only from another JSpinnerOperator + * instance. + * + * @see #getNumberSpinner + */ + public static class NumberSpinnerOperator extends JSpinnerOperator { + + private NumberSpinnerOperator(JSpinnerOperator spinner) { + super((JSpinner) spinner.getSource()); + copyEnvironment(spinner); + checkModel(this, SpinnerNumberModel.class); + } + + /** + * Costs spinner's model to SpinnerNumberModel. + * + * @return a spinner model. + */ + public SpinnerNumberModel getNumberModel() { + return (SpinnerNumberModel) getModel(); + } + + /** + * Scrolls to a double value. + * + * @param value a value to scroll to. + */ + public void scrollToValue(double value) { + scrollTo(new NumberScrollAdjuster(this, value)); + } + + /** + * Scrolls to a number value. + * + * @param value a value to scroll to. + */ + public void scrollToValue(Number value) { + scrollTo(new NumberScrollAdjuster(this, value)); + } + } + + /** + * Provides some specific functionality for {@code JSpinner} components + * having {@code SpinnerListModel} model. Constructor of this object is + * private - it cannot be received only from another JSpinnerOperator + * instance. + * + * @see #getListSpinner + */ + public static class ListSpinnerOperator extends JSpinnerOperator { + + private ListSpinnerOperator(JSpinnerOperator spinner) { + super((JSpinner) spinner.getSource()); + copyEnvironment(spinner); + checkModel(this, SpinnerListModel.class); + } + + /** + * Costs spinner's model to SpinnerListModel. + * + * @return a spinner model. + */ + public SpinnerListModel getListModel() { + return (SpinnerListModel) getModel(); + } + + /** + * Looks for an index of an item having {@code toString()} matching + * a specified pattern. + * + * @param pattern a string pattern + * @param comparator a string comparision criteria. + */ + public int findItem(String pattern, StringComparator comparator) { + List list = getListModel().getList(); + for (int i = 0; i < list.size(); i++) { + if (comparator.equals(list.get(i).toString(), pattern)) { + return i; + } + } + return -1; + } + + /** + * Looks for an index of an item having {@code toString()} matching + * a specified pattern. Uses a {@code StringComparator} assigned to + * the operator. + * + * @param pattern a string pattern + */ + public int findItem(String pattern) { + return findItem(pattern, getComparator()); + } + + /** + * Scrolls to an item having specified instance. + * + * @param index an index to scroll to. + */ + public void scrollToIndex(int index) { + scrollTo(new ListScrollAdjuster(this, index)); + } + + /** + * Scrolls to {@code getValue().toString()} match a specified + * pattern. + * + * @param pattern a string pattern + * @param comparator a string comparision criteria. + */ + public void scrollToString(String pattern, StringComparator comparator) { + int index = findItem(pattern, comparator); + if (index != -1) { + scrollToIndex(index); + } else { + throw (new JemmyException("No \"" + pattern + "\" item in JSpinner", getSource())); + } + } + + /** + * Scrolls to {@code getValue().toString()} match a specified + * pattern. Uses a {@code StringComparator} assigned to the + * operator. + * + * @param pattern a string pattern + */ + public void scrollToString(String pattern) { + scrollToString(pattern, getComparator()); + } + } + + /** + * Provides some specific functionality for {@code JSpinner} components + * having {@code SpinnerDateModel} model. Constructor of this object is + * private - it cannot be received only from another JSpinnerOperator + * instance. + * + * @see #getDateSpinner + */ + public static class DateSpinnerOperator extends JSpinnerOperator { + + private DateSpinnerOperator(JSpinnerOperator spinner) { + super((JSpinner) spinner.getSource()); + copyEnvironment(spinner); + checkModel(this, SpinnerDateModel.class); + } + + /** + * Costs spinner's model to SpinnerDateModel. + * + * @return a spinner model. + */ + public SpinnerDateModel getDateModel() { + return (SpinnerDateModel) getModel(); + } + + /** + * Scrolls to a date. + * + * @param date a date to scroll to. + */ + public void scrollToDate(Date date) { + scrollTo(new DateScrollAdjuster(this, date)); + } + } + + /** + * Exception is thown whenever spinner model is threated wrong. + */ + public static class SpinnerModelException extends JemmyException { + + private static final long serialVersionUID = 42L; + + /** + * Constructs a {@code SpinnerModelException} object. + * + * @param message error message. + * @param comp a spinner which model cased the exception. + */ + public SpinnerModelException(String message, Component comp) { + super(message, comp); + } + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JSplitPaneOperator.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JSplitPaneOperator.java new file mode 100644 index 00000000000..d9c25b5cfbc --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JSplitPaneOperator.java @@ -0,0 +1,914 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.operators; + +import java.awt.Component; +import java.awt.Container; +import java.util.Hashtable; + +import javax.swing.JButton; +import javax.swing.JSplitPane; +import javax.swing.plaf.SplitPaneUI; +import javax.swing.plaf.basic.BasicSplitPaneDivider; + +import org.netbeans.jemmy.Action; +import org.netbeans.jemmy.ComponentChooser; +import org.netbeans.jemmy.ComponentSearcher; +import org.netbeans.jemmy.Outputable; +import org.netbeans.jemmy.TestOut; +import org.netbeans.jemmy.TimeoutExpiredException; +import org.netbeans.jemmy.Timeoutable; +import org.netbeans.jemmy.Timeouts; +import org.netbeans.jemmy.drivers.DriverManager; +import org.netbeans.jemmy.drivers.ScrollDriver; +import org.netbeans.jemmy.drivers.scrolling.ScrollAdjuster; +import org.netbeans.jemmy.util.EmptyVisualizer; + +/** + *

Timeouts used:
+ * JSplitPaneOperator.ScrollClickTimeout - time for simple scroll click
+ * JSplitPaneOperator.BetweenClickTimeout - time to sleep between scroll clicks + *
+ * JSplitPaneOperator.WholeScrollTimeout - time for the whole scrolling
+ * ComponentOperator.WaitComponentTimeout - time to wait component displayed + *
. + * + * @see org.netbeans.jemmy.Timeouts + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + * + * Class to operate with javax.swing.JSplitPane component + * + */ +public class JSplitPaneOperator extends JComponentOperator + implements Timeoutable, Outputable { + + /** + * Identifier for a "minimum" property. + * + * @see #getDump + */ + public static final String MINIMUM_DPROP = "Minimum"; + + /** + * Identifier for a "maximum" property. + * + * @see #getDump + */ + public static final String MAXIMUM_DPROP = "Maximum"; + + /** + * Identifier for a "value" property. + * + * @see #getDump + */ + public static final String VALUE_DPROP = "Value"; + + /** + * Identifier for a "orientation" property. + * + * @see #getDump + */ + public static final String ORIENTATION_DPROP = "Orientation"; + + /** + * Identifier for a "HORIZONTAL" value of "orientation" property. + * + * @see #getDump + */ + public static final String HORIZONTAL_ORIENTATION_DPROP_VALUE = "HORIZONTAL"; + + /** + * Identifier for a "VERTICAL" value of "orientation" property. + * + * @see #getDump + */ + public static final String VERTICAL_ORIENTATION_DPROP_VALUE = "VERTICAL"; + + /** + * Identifier for a "one touch expendable" property. + * + * @see #getDump + */ + public static final String IS_ONE_TOUCH_EXPANDABLE_DPROP = "One touch expandable"; + + private final static long SCROLL_CLICK_TIMEOUT = 0; + private final static long BETWEEN_CLICK_TIMEOUT = 0; + private final static long WHOLE_SCROLL_TIMEOUT = 60000; + + private Timeouts timeouts; + private TestOut output; + private ContainerOperator divider; + private ScrollDriver driver; + + /** + * Constructor. + * + * @param b JSplitPane component. + */ + public JSplitPaneOperator(JSplitPane b) { + super(b); + driver = DriverManager.getScrollDriver(getClass()); + } + + /** + * Constructs a JSplitPaneOperator object. + * + * @param cont a container + * @param chooser a component chooser specifying searching criteria. + * @param index an index between appropriate ones. + */ + public JSplitPaneOperator(ContainerOperator cont, ComponentChooser chooser, int index) { + this((JSplitPane) cont. + waitSubComponent(new JSplitPaneFinder(chooser), + index)); + copyEnvironment(cont); + } + + /** + * Constructs a JSplitPaneOperator object. + * + * @param cont a container + * @param chooser a component chooser specifying searching criteria. + */ + public JSplitPaneOperator(ContainerOperator cont, ComponentChooser chooser) { + this(cont, chooser, 0); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont Operator pointing a container to search component in. + * @param index Ordinal component index. + * @throws TimeoutExpiredException + */ + public JSplitPaneOperator(ContainerOperator cont, int index) { + this((JSplitPane) waitComponent(cont, + new JSplitPaneFinder(), + index)); + copyEnvironment(cont); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont Operator pointing a container to search component in. + * @throws TimeoutExpiredException + */ + public JSplitPaneOperator(ContainerOperator cont) { + this(cont, 0); + } + + /** + * Searches JSplitPane in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @param index Ordinal component index. + * @return JSplitPane instance or null if component was not found. + */ + public static JSplitPane findJSplitPane(Container cont, ComponentChooser chooser, int index) { + return (JSplitPane) findComponent(cont, new JSplitPaneFinder(chooser), index); + } + + /** + * Searches 0'th JSplitPane in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @return JSplitPane instance or null if component was not found. + */ + public static JSplitPane findJSplitPane(Container cont, ComponentChooser chooser) { + return findJSplitPane(cont, chooser, 0); + } + + /** + * Searches JSplitPane in container. + * + * @param cont Container to search component in. + * @param index Ordinal component index. + * @return JSplitPane instance or null if component was not found. + */ + public static JSplitPane findJSplitPane(Container cont, int index) { + return findJSplitPane(cont, ComponentSearcher.getTrueChooser(Integer.toString(index) + "'th JSplitPane instance"), index); + } + + /** + * Searches 0'th JSplitPane in container. + * + * @param cont Container to search component in. + * @return JSplitPane instance or null if component was not found. + */ + public static JSplitPane findJSplitPane(Container cont) { + return findJSplitPane(cont, 0); + } + + /** + * Searches JSplitPane object which component lies on. + * + * @param comp Component to find JSplitPane under. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @return JSplitPane instance or null if component was not found. + */ + public static JSplitPane findJSplitPaneUnder(Component comp, ComponentChooser chooser) { + return (JSplitPane) findContainerUnder(comp, new JSplitPaneFinder(chooser)); + } + + /** + * Searches JSplitPane object which component lies on. + * + * @param comp Component to find JSplitPane under. + * @return JSplitPane instance or null if component was not found. + */ + public static JSplitPane findJSplitPaneUnder(Component comp) { + return findJSplitPaneUnder(comp, new JSplitPaneFinder()); + } + + /** + * Waits JSplitPane in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @param index Ordinal component index. + * @return JSplitPane instance or null if component was not displayed. + * @throws TimeoutExpiredException + */ + public static JSplitPane waitJSplitPane(Container cont, ComponentChooser chooser, int index) { + return (JSplitPane) waitComponent(cont, new JSplitPaneFinder(chooser), index); + } + + /** + * Waits 0'th JSplitPane in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @return JSplitPane instance or null if component was not displayed. + * @throws TimeoutExpiredException + */ + public static JSplitPane waitJSplitPane(Container cont, ComponentChooser chooser) { + return waitJSplitPane(cont, chooser, 0); + } + + /** + * Waits JSplitPane in container. + * + * @param cont Container to search component in. + * @param index Ordinal component index. + * @return JSplitPane instance or null if component was not displayed. + * @throws TimeoutExpiredException + */ + public static JSplitPane waitJSplitPane(Container cont, int index) { + return waitJSplitPane(cont, ComponentSearcher.getTrueChooser(Integer.toString(index) + "'th JSplitPane instance"), index); + } + + /** + * Waits 0'th JSplitPane in container. + * + * @param cont Container to search component in. + * @return JSplitPane instance or null if component was not displayed. + * @throws TimeoutExpiredException + */ + public static JSplitPane waitJSplitPane(Container cont) { + return waitJSplitPane(cont, 0); + } + + static { + Timeouts.initDefault("JSplitPaneOperator.ScrollClickTimeout", SCROLL_CLICK_TIMEOUT); + Timeouts.initDefault("JSplitPaneOperator.BetweenClickTimeout", BETWEEN_CLICK_TIMEOUT); + Timeouts.initDefault("JSplitPaneOperator.WholeScrollTimeout", WHOLE_SCROLL_TIMEOUT); + } + + @Override + public void setTimeouts(Timeouts timeouts) { + this.timeouts = timeouts; + Timeouts times = timeouts; + times.setTimeout("ComponentOperator.BeforeDragTimeout", + 0); + times.setTimeout("ComponentOperator.AfterDragTimeout", + times.getTimeout("JSplitPaneOperator.ScrollClickTimeout")); + super.setTimeouts(times); + } + + @Override + public Timeouts getTimeouts() { + return timeouts; + } + + @Override + public void setOutput(TestOut out) { + output = out; + super.setOutput(output.createErrorOutput()); + } + + @Override + public TestOut getOutput() { + return output; + } + + @Override + public void copyEnvironment(Operator anotherOperator) { + super.copyEnvironment(anotherOperator); + driver + = (ScrollDriver) DriverManager. + getDriver(DriverManager.SCROLL_DRIVER_ID, + getClass(), + anotherOperator.getProperties()); + } + + /** + * Searches divider inside split pane. + * + * @return an operator for the divider. + */ + public BasicSplitPaneDivider findDivider() { + return ((BasicSplitPaneDivider) waitSubComponent(new ComponentChooser() { + @Override + public boolean checkComponent(Component comp) { + return comp instanceof BasicSplitPaneDivider; + } + + @Override + public String getDescription() { + return ""; + } + + @Override + public String toString() { + return "JSplitPaneOperator.findDivider.ComponentChooser{description = " + getDescription() + '}'; + } + })); + } + + /** + * Searches divider inside split pane. + * + * @return an operator for the divider. + */ + public ContainerOperator getDivider() { + if (divider == null) { + divider = new ContainerOperator<>(findDivider()); + divider.copyEnvironment(this); + divider.setOutput(getOutput().createErrorOutput()); + } + return divider; + } + + /** + * Scrolls to the position defined by a ScrollAdjuster implementation. + * + * @param adj defines scrolling direction, and so on. + * @throws TimeoutExpiredException + */ + public void scrollTo(final ScrollAdjuster adj) { + produceTimeRestricted(new Action() { + @Override + public Void launch(Void obj) { + driver.scroll(JSplitPaneOperator.this, adj); + return null; + } + + @Override + public String getDescription() { + return "Moving a divider"; + } + + @Override + public String toString() { + return "JSplitPaneOperator.scrollTo.Action{description = " + getDescription() + '}'; + } + }, "JSplitPaneOperator.WholeScrollTimeout"); + } + + /** + * Changes divider location. + * + * @param dividerLocation location to move divider to. + */ + public void moveDivider(int dividerLocation) { + output.printTrace("Move JSplitPane divider to " + Integer.toString(dividerLocation) + + " location. JSplitPane : \n" + toStringSource()); + output.printGolden("Move JSplitPane divider to " + Integer.toString(dividerLocation) + + " location"); + scrollTo(new ValueScrollAdjuster(dividerLocation)); + } + + /** + * Changes divider location. + * + * @param proportionalLocation Proportional location. Should be great then 0 + * and less then 1. + */ + public void moveDivider(double proportionalLocation) { + output.printTrace("Move JSplitPane divider to " + Double.toString(proportionalLocation) + + " proportional location. JSplitPane : \n" + toStringSource()); + output.printGolden("Move JSplitPane divider to " + Double.toString(proportionalLocation) + + " proportional location"); + scrollTo(new ValueScrollAdjuster(getMinimumDividerLocation() + + (int) (proportionalLocation + * (getMaximumDividerLocation() - getMinimumDividerLocation())))); + } + + /** + * Moves the divider all the way to the left/top. + */ + public void moveToMinimum() { + output.printTrace("Scroll JSplitPane to minimum. JSplitPane : \n" + toStringSource()); + output.printGolden("Scroll JSplitPane to minimum."); + produceTimeRestricted(new Action() { + @Override + public Void launch(Void obj) { + driver.scrollToMinimum(JSplitPaneOperator.this, getOrientation()); + return null; + } + + @Override + public String getDescription() { + return "Scrolling"; + } + + @Override + public String toString() { + return "JSplitPaneOperator.moveToMinimum.Action{description = " + getDescription() + '}'; + } + }, "JSplitPaneOperator.WholeScrollTimeout"); + } + + /** + * Moves the divider all the way to the right/bottom. + */ + public void moveToMaximum() { + output.printTrace("Scroll JSplitPane to maximum. JSplitPane : \n" + toStringSource()); + output.printGolden("Scroll JSplitPane to maximum."); + produceTimeRestricted(new Action() { + @Override + public Void launch(Void obj) { + driver.scrollToMaximum(JSplitPaneOperator.this, getOrientation()); + return null; + } + + @Override + public String getDescription() { + return "Scrolling"; + } + + @Override + public String toString() { + return "JSplitPaneOperator.moveToMaximum.Action{description = " + getDescription() + '}'; + } + }, "JSplitPaneOperator.WholeScrollTimeout"); + } + + /** + * Pushes one time right(bottom) expand button. + * + * @throws TimeoutExpiredException + */ + public void expandRight() { + String mess = "Expand "; + if (getOrientation() == JSplitPane.HORIZONTAL_SPLIT) { + mess = mess + "right"; + } else { + mess = mess + "bottom"; + } + output.printTrace(mess + " JSplitPane side. JSplitPane : \n" + toStringSource()); + output.printGolden(mess + " JSplitPane side."); + expandTo(0); + } + + /** + * Pushes one time left(top) expand button. + * + * @throws TimeoutExpiredException + */ + public void expandLeft() { + String mess = "Expand "; + if (getOrientation() == JSplitPane.HORIZONTAL_SPLIT) { + mess = mess + "left"; + } else { + mess = mess + "top"; + } + output.printTrace(mess + " JSplitPane side. JSplitPane : \n" + toStringSource()); + output.printGolden(mess + " JSplitPane side."); + expandTo(1); + } + + @Override + public Hashtable getDump() { + Hashtable result = super.getDump(); + result.put(MINIMUM_DPROP, Integer.toString(((JSplitPane) getSource()).getMinimumDividerLocation())); + result.put(MAXIMUM_DPROP, Integer.toString(((JSplitPane) getSource()).getMaximumDividerLocation())); + result.put(ORIENTATION_DPROP, (((JSplitPane) getSource()).getOrientation() == JSplitPane.HORIZONTAL_SPLIT) + ? HORIZONTAL_ORIENTATION_DPROP_VALUE + : VERTICAL_ORIENTATION_DPROP_VALUE); + result.put(VALUE_DPROP, Integer.toString(((JSplitPane) getSource()).getDividerLocation())); + result.put(IS_ONE_TOUCH_EXPANDABLE_DPROP, ((JSplitPane) getSource()).isOneTouchExpandable() ? "true" : "false"); + return result; + } + + //////////////////////////////////////////////////////// + //Mapping // + /** + * Maps {@code JSplitPane.getBottomComponent()} through queue + */ + public Component getBottomComponent() { + return (runMapping(new MapAction("getBottomComponent") { + @Override + public Component map() { + return ((JSplitPane) getSource()).getBottomComponent(); + } + })); + } + + /** + * Maps {@code JSplitPane.getDividerLocation()} through queue + */ + public int getDividerLocation() { + return (runMapping(new MapIntegerAction("getDividerLocation") { + @Override + public int map() { + return ((JSplitPane) getSource()).getDividerLocation(); + } + })); + } + + /** + * Maps {@code JSplitPane.getDividerSize()} through queue + */ + public int getDividerSize() { + return (runMapping(new MapIntegerAction("getDividerSize") { + @Override + public int map() { + return ((JSplitPane) getSource()).getDividerSize(); + } + })); + } + + /** + * Maps {@code JSplitPane.getLastDividerLocation()} through queue + */ + public int getLastDividerLocation() { + return (runMapping(new MapIntegerAction("getLastDividerLocation") { + @Override + public int map() { + return ((JSplitPane) getSource()).getLastDividerLocation(); + } + })); + } + + /** + * Maps {@code JSplitPane.getLeftComponent()} through queue + */ + public Component getLeftComponent() { + return (runMapping(new MapAction("getLeftComponent") { + @Override + public Component map() { + return ((JSplitPane) getSource()).getLeftComponent(); + } + })); + } + + /** + * Maps {@code JSplitPane.getMaximumDividerLocation()} through queue + */ + public int getMaximumDividerLocation() { + return (runMapping(new MapIntegerAction("getMaximumDividerLocation") { + @Override + public int map() { + return ((JSplitPane) getSource()).getMaximumDividerLocation(); + } + })); + } + + /** + * Maps {@code JSplitPane.getMinimumDividerLocation()} through queue + */ + public int getMinimumDividerLocation() { + return (runMapping(new MapIntegerAction("getMinimumDividerLocation") { + @Override + public int map() { + return ((JSplitPane) getSource()).getMinimumDividerLocation(); + } + })); + } + + /** + * Maps {@code JSplitPane.getOrientation()} through queue + */ + public int getOrientation() { + return (runMapping(new MapIntegerAction("getOrientation") { + @Override + public int map() { + return ((JSplitPane) getSource()).getOrientation(); + } + })); + } + + /** + * Maps {@code JSplitPane.getRightComponent()} through queue + */ + public Component getRightComponent() { + return (runMapping(new MapAction("getRightComponent") { + @Override + public Component map() { + return ((JSplitPane) getSource()).getRightComponent(); + } + })); + } + + /** + * Maps {@code JSplitPane.getTopComponent()} through queue + */ + public Component getTopComponent() { + return (runMapping(new MapAction("getTopComponent") { + @Override + public Component map() { + return ((JSplitPane) getSource()).getTopComponent(); + } + })); + } + + /** + * Maps {@code JSplitPane.getUI()} through queue + */ + public SplitPaneUI getUI() { + return (runMapping(new MapAction("getUI") { + @Override + public SplitPaneUI map() { + return ((JSplitPane) getSource()).getUI(); + } + })); + } + + /** + * Maps {@code JSplitPane.isContinuousLayout()} through queue + */ + public boolean isContinuousLayout() { + return (runMapping(new MapBooleanAction("isContinuousLayout") { + @Override + public boolean map() { + return ((JSplitPane) getSource()).isContinuousLayout(); + } + })); + } + + /** + * Maps {@code JSplitPane.isOneTouchExpandable()} through queue + */ + public boolean isOneTouchExpandable() { + return (runMapping(new MapBooleanAction("isOneTouchExpandable") { + @Override + public boolean map() { + return ((JSplitPane) getSource()).isOneTouchExpandable(); + } + })); + } + + /** + * Maps {@code JSplitPane.resetToPreferredSizes()} through queue + */ + public void resetToPreferredSizes() { + runMapping(new MapVoidAction("resetToPreferredSizes") { + @Override + public void map() { + ((JSplitPane) getSource()).resetToPreferredSizes(); + } + }); + } + + /** + * Maps {@code JSplitPane.setBottomComponent(Component)} through queue + */ + public void setBottomComponent(final Component component) { + runMapping(new MapVoidAction("setBottomComponent") { + @Override + public void map() { + ((JSplitPane) getSource()).setBottomComponent(component); + } + }); + } + + /** + * Maps {@code JSplitPane.setContinuousLayout(boolean)} through queue + */ + public void setContinuousLayout(final boolean b) { + runMapping(new MapVoidAction("setContinuousLayout") { + @Override + public void map() { + ((JSplitPane) getSource()).setContinuousLayout(b); + } + }); + } + + /** + * Maps {@code JSplitPane.setDividerLocation(double)} through queue + */ + public void setDividerLocation(final double d) { + runMapping(new MapVoidAction("setDividerLocation") { + @Override + public void map() { + ((JSplitPane) getSource()).setDividerLocation(d); + } + }); + } + + /** + * Maps {@code JSplitPane.setDividerLocation(int)} through queue + */ + public void setDividerLocation(final int i) { + runMapping(new MapVoidAction("setDividerLocation") { + @Override + public void map() { + ((JSplitPane) getSource()).setDividerLocation(i); + } + }); + } + + /** + * Maps {@code JSplitPane.setDividerSize(int)} through queue + */ + public void setDividerSize(final int i) { + runMapping(new MapVoidAction("setDividerSize") { + @Override + public void map() { + ((JSplitPane) getSource()).setDividerSize(i); + } + }); + } + + /** + * Maps {@code JSplitPane.setLastDividerLocation(int)} through queue + */ + public void setLastDividerLocation(final int i) { + runMapping(new MapVoidAction("setLastDividerLocation") { + @Override + public void map() { + ((JSplitPane) getSource()).setLastDividerLocation(i); + } + }); + } + + /** + * Maps {@code JSplitPane.setLeftComponent(Component)} through queue + */ + public void setLeftComponent(final Component component) { + runMapping(new MapVoidAction("setLeftComponent") { + @Override + public void map() { + ((JSplitPane) getSource()).setLeftComponent(component); + } + }); + } + + /** + * Maps {@code JSplitPane.setOneTouchExpandable(boolean)} through queue + */ + public void setOneTouchExpandable(final boolean b) { + runMapping(new MapVoidAction("setOneTouchExpandable") { + @Override + public void map() { + ((JSplitPane) getSource()).setOneTouchExpandable(b); + } + }); + } + + /** + * Maps {@code JSplitPane.setOrientation(int)} through queue + */ + public void setOrientation(final int i) { + runMapping(new MapVoidAction("setOrientation") { + @Override + public void map() { + ((JSplitPane) getSource()).setOrientation(i); + } + }); + } + + /** + * Maps {@code JSplitPane.setRightComponent(Component)} through queue + */ + public void setRightComponent(final Component component) { + runMapping(new MapVoidAction("setRightComponent") { + @Override + public void map() { + ((JSplitPane) getSource()).setRightComponent(component); + } + }); + } + + /** + * Maps {@code JSplitPane.setTopComponent(Component)} through queue + */ + public void setTopComponent(final Component component) { + runMapping(new MapVoidAction("setTopComponent") { + @Override + public void map() { + ((JSplitPane) getSource()).setTopComponent(component); + } + }); + } + + /** + * Maps {@code JSplitPane.setUI(SplitPaneUI)} through queue + */ + public void setUI(final SplitPaneUI splitPaneUI) { + runMapping(new MapVoidAction("setUI") { + @Override + public void map() { + ((JSplitPane) getSource()).setUI(splitPaneUI); + } + }); + } + + //End of mapping // + //////////////////////////////////////////////////////// + private void expandTo(int index) { + makeComponentVisible(); + JButtonOperator bo + = new JButtonOperator((JButton) getDivider(). + waitSubComponent(new JButtonOperator.JButtonFinder(ComponentSearcher. + getTrueChooser("JButton")), + index)); + bo.copyEnvironment(getDivider()); + bo.setVisualizer(new EmptyVisualizer()); + bo.push(); + } + + /** + * Checks component type. + */ + public static class JSplitPaneFinder extends Finder { + + /** + * Constructs JSplitPaneFinder. + * + * @param sf other searching criteria. + */ + public JSplitPaneFinder(ComponentChooser sf) { + super(JSplitPane.class, sf); + } + + /** + * Constructs JSplitPaneFinder. + */ + public JSplitPaneFinder() { + super(JSplitPane.class); + } + } + + private class ValueScrollAdjuster implements ScrollAdjuster { + + int value; + + public ValueScrollAdjuster(int value) { + this.value = value; + } + + @Override + public int getScrollDirection() { + if (getDividerLocation() == value) { + return ScrollAdjuster.DO_NOT_TOUCH_SCROLL_DIRECTION; + } else { + return ((getDividerLocation() < value) + ? ScrollAdjuster.INCREASE_SCROLL_DIRECTION + : ScrollAdjuster.DECREASE_SCROLL_DIRECTION); + } + } + + @Override + public int getScrollOrientation() { + return getOrientation(); + } + + @Override + public String getDescription() { + return "Scroll to " + Integer.toString(value) + " value"; + } + + @Override + public String toString() { + return "ValueScrollAdjuster{" + "value=" + value + '}'; + } + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JTabbedPaneOperator.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JTabbedPaneOperator.java new file mode 100644 index 00000000000..7e7b6b49115 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JTabbedPaneOperator.java @@ -0,0 +1,1168 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.operators; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Container; +import java.awt.Rectangle; +import java.util.Hashtable; + +import javax.swing.Icon; +import javax.swing.JTabbedPane; +import javax.swing.SingleSelectionModel; +import javax.swing.event.ChangeListener; +import javax.swing.plaf.TabbedPaneUI; + +import org.netbeans.jemmy.ComponentChooser; +import org.netbeans.jemmy.JemmyInputException; +import org.netbeans.jemmy.Outputable; +import org.netbeans.jemmy.TestOut; +import org.netbeans.jemmy.TimeoutExpiredException; +import org.netbeans.jemmy.drivers.DriverManager; +import org.netbeans.jemmy.drivers.ListDriver; + +/** + *

Timeouts used:
+ * ComponentOperator.WaitComponentTimeout - time to wait component displayed + *
. + * + * @see org.netbeans.jemmy.Timeouts + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + * + */ +public class JTabbedPaneOperator extends JComponentOperator + implements Outputable { + + /** + * Identifier for a "selected page" property. + * + * @see #getDump + */ + public static final String SELECTED_PAGE_DPROP = "Selected"; + + /** + * Identifier for a "page" properties. + * + * @see #getDump + */ + public static final String PAGE_PREFIX_DPROP = "Page"; + + private TestOut output; + private ListDriver driver; + + /** + * Constructor. + * + * @param b a component + */ + public JTabbedPaneOperator(JTabbedPane b) { + super(b); + driver = DriverManager.getListDriver(getClass()); + } + + /** + * Constructs a JTabbedPaneOperator object. + * + * @param cont a container + * @param chooser a component chooser specifying searching criteria. + * @param index an index between appropriate ones. + */ + public JTabbedPaneOperator(ContainerOperator cont, ComponentChooser chooser, int index) { + this((JTabbedPane) cont. + waitSubComponent(new JTabbedPaneFinder(chooser), + index)); + copyEnvironment(cont); + } + + /** + * Constructs a JTabbedPaneOperator object. + * + * @param cont a container + * @param chooser a component chooser specifying searching criteria. + */ + public JTabbedPaneOperator(ContainerOperator cont, ComponentChooser chooser) { + this(cont, chooser, 0); + } + + /** + * Constructor. Waits component by tab title first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont a container + * @param text Tab title. + * @param tabIndex a page index to check. if equal to -1, selected page is + * checked. + * @param index Ordinal component index. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public JTabbedPaneOperator(ContainerOperator cont, String text, int tabIndex, int index) { + this((JTabbedPane) waitComponent(cont, + new JTabbedPaneByItemFinder(text, tabIndex, + cont.getComparator()), + index)); + copyEnvironment(cont); + } + + /** + * Constructor. Waits component by activetab title first. Uses cont's + * timeout and output for waiting and to init operator. + * + * @param cont a container + * @param text Title of tab which is currently selected. + * @param index Ordinal component index. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public JTabbedPaneOperator(ContainerOperator cont, String text, int index) { + this(cont, text, -1, index); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont a container + * @param text Title of tab which is currently selected. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public JTabbedPaneOperator(ContainerOperator cont, String text) { + this(cont, text, 0); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont a container + * @param index Ordinal component index. + * @throws TimeoutExpiredException + */ + public JTabbedPaneOperator(ContainerOperator cont, int index) { + this((JTabbedPane) waitComponent(cont, + new JTabbedPaneFinder(), + index)); + copyEnvironment(cont); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont a container + * @throws TimeoutExpiredException + */ + public JTabbedPaneOperator(ContainerOperator cont) { + this(cont, 0); + } + + /** + * Searches JTabbedPane in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @param index Ordinal component index. + * @return JTabbedPane instance or null if component was not found. + */ + public static JTabbedPane findJTabbedPane(Container cont, ComponentChooser chooser, int index) { + return (JTabbedPane) findComponent(cont, new JTabbedPaneFinder(chooser), index); + } + + /** + * Searches 0'th JTabbedPane in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @return JTabbedPane instance or null if component was not found. + */ + public static JTabbedPane findJTabbedPane(Container cont, ComponentChooser chooser) { + return findJTabbedPane(cont, chooser, 0); + } + + /** + * Searches JTabbedPane by tab title. + * + * @param cont Container to search component in. + * @param text Tooltip text. If null, contents is not checked. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @param itemIndex Tab index. if -1 selected one is checked. + * @param index Ordinal component index. + * @return JTabbedPane instance or null if component was not found. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + */ + public static JTabbedPane findJTabbedPane(Container cont, String text, boolean ce, boolean ccs, int itemIndex, int index) { + return findJTabbedPane(cont, new JTabbedPaneByItemFinder(text, itemIndex, new DefaultStringComparator(ce, ccs)), index); + } + + /** + * Searches JTabbedPane by tab title. + * + * @param cont Container to search component in. + * @param text Tooltip text. If null, contents is not checked. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @param itemIndex Tab index. if -1 selected one is checked. + * @return JTabbedPane instance or null if component was not found. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + */ + public static JTabbedPane findJTabbedPane(Container cont, String text, boolean ce, boolean ccs, int itemIndex) { + return findJTabbedPane(cont, text, ce, ccs, itemIndex, 0); + } + + /** + * Searches JTabbedPane object which component lies on. + * + * @param comp Component to find JTabbedPane under. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @return JTabbedPane instance or null if component was not found. + */ + public static JTabbedPane findJTabbedPaneUnder(Component comp, ComponentChooser chooser) { + return (JTabbedPane) findContainerUnder(comp, new JTabbedPaneFinder(chooser)); + } + + /** + * Searches JTabbedPane object which component lies on. + * + * @param comp Component to find JTabbedPane under. + * @return JTabbedPane instance or null if component was not found. + */ + public static JTabbedPane findJTabbedPaneUnder(Component comp) { + return findJTabbedPaneUnder(comp, new JTabbedPaneFinder()); + } + + /** + * Waits JTabbedPane in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @param index Ordinal component index. + * @return JTabbedPane instance. + * @throws TimeoutExpiredException + */ + public static JTabbedPane waitJTabbedPane(Container cont, ComponentChooser chooser, int index) { + return (JTabbedPane) waitComponent(cont, new JTabbedPaneFinder(chooser), index); + } + + /** + * Waits 0'th JTabbedPane in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @return JTabbedPane instance. + * @throws TimeoutExpiredException + */ + public static JTabbedPane waitJTabbedPane(Container cont, ComponentChooser chooser) { + return waitJTabbedPane(cont, chooser, 0); + } + + /** + * Waits JTabbedPane by tab title. + * + * @param cont Container to search component in. + * @param text Tooltip text. If null, contents is not checked. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @param itemIndex Tab index. if -1 selected one is checked. + * @param index Ordinal component index. + * @return JTabbedPane instance. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public static JTabbedPane waitJTabbedPane(Container cont, String text, boolean ce, boolean ccs, int itemIndex, int index) { + return waitJTabbedPane(cont, new JTabbedPaneByItemFinder(text, itemIndex, new DefaultStringComparator(ce, ccs)), index); + } + + /** + * Waits JTabbedPane by tab title. + * + * @param cont Container to search component in. + * @param text Tooltip text. If null, contents is not checked. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @param itemIndex Tab index. if -1 selected one is checked. + * @return JTabbedPane instance. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public static JTabbedPane waitJTabbedPane(Container cont, String text, boolean ce, boolean ccs, int itemIndex) { + return waitJTabbedPane(cont, text, ce, ccs, itemIndex, 0); + } + + @Override + public void setOutput(TestOut output) { + super.setOutput(output.createErrorOutput()); + this.output = output; + } + + @Override + public TestOut getOutput() { + return output; + } + + @Override + public void copyEnvironment(Operator anotherOperator) { + super.copyEnvironment(anotherOperator); + driver + = (ListDriver) DriverManager. + getDriver(DriverManager.LIST_DRIVER_ID, + getClass(), + anotherOperator.getProperties()); + } + + /** + * Searches tab index by tab title. + * + * @param chooser page searching criteria + * @return a page index. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @deprecated Use findPage(String) or findPage(String, StringComparator) + */ + @Deprecated + public int findPage(TabPageChooser chooser) { + for (int i = 0; i < getTabCount(); i++) { + if (chooser.checkPage(this, i)) { + return i; + } + } + return -1; + } + + /** + * Searches tab index by tab title. + * + * @param title a page title. + * @param comparator a string comparision algorithm + * @return a page index. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @deprecated Use findPage(String) or findPage(String, StringComparator) + */ + @Deprecated + public int findPage(String title, StringComparator comparator) { + return findPage(new BySubStringTabPageChooser(title, comparator)); + } + + /** + * Searches tab index by tab title. isCaptionEqual method is used to compare + * page title with match. + * + * @param title a page title. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @return a page index. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @deprecated Use findPage(String) or findPage(String, StringComparator) + */ + @Deprecated + public int findPage(String title, boolean ce, boolean ccs) { + return findPage(title, new DefaultStringComparator(ce, ccs)); + } + + /** + * Searches tab index by tab title. isCaptionEqual method is used to compare + * page title with match. Uses StringComparator assigned to this object. + * + * @param title a page title. + * @return a page index. + */ + public int findPage(String title) { + return findPage(title, getComparator()); + } + + /** + * Selects tab. + * + * @param index a page ordinal index. + * @return a root corresponding to the page. + */ + public Component selectPage(int index) { + output.printLine("Selecting " + index + "'th page in tabbed pane\n :" + toStringSource()); + makeComponentVisible(); + driver.selectItem(this, index); + if (getVerification()) { + waitSelected(index); + } + return getComponentAt(index); + } + + /** + * Selects tab. + * + * @param chooser page searching criteria + * @return a root corresponding to the page. + */ + public Component selectPage(TabPageChooser chooser) { + output.printLine("Selecting \"" + chooser.getDescription() + + "\" page in tabbed pane\n :" + toStringSource()); + return selectPage(waitPage(chooser)); + } + + /** + * Selects tab. + * + * @param title a page title. + * @param comparator a string comparision algorithm + * @return a root corresponding to the page. + */ + public Component selectPage(String title, StringComparator comparator) { + return selectPage(new BySubStringTabPageChooser(title, comparator)); + } + + /** + * Selects tab by tab title. + * + * @param title a page title. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @deprecated Use selectPage(String) or selectPage(String, + * StringComparator) + * @return a root corresponding to the page. + */ + @Deprecated + public Component selectPage(String title, boolean ce, boolean ccs) { + return selectPage(title, new DefaultStringComparator(ce, ccs)); + } + + /** + * Selects tab by tab title. Uses StringComparator assigned to this object. + * + * @param title a page title. + * @return a root corresponding to the page. + */ + public Component selectPage(String title) { + return selectPage(title, getComparator()); + } + + /** + * Wait for a page to exist. + * + * @param chooser page searching criteria + * @return a page index. + */ + public int waitPage(final TabPageChooser chooser) { + waitState(new ComponentChooser() { + @Override + public boolean checkComponent(Component comp) { + return findPage(chooser) > -1; + } + + @Override + public String getDescription() { + return "Tabbed with " + chooser.getDescription() + " page."; + } + + @Override + public String toString() { + return "JTabbedPaneOperator.waitPage.Action{description = " + getDescription() + '}'; + } + }); + return findPage(chooser); + } + + /** + * Wait for a page to exist. + * + * @param title a page title. + * @param comparator a string comparision algorithm + * @return a page index. + */ + public int waitPage(String title, StringComparator comparator) { + return waitPage(new BySubStringTabPageChooser(title, comparator)); + } + + /** + * Wait for a page to exist. + * + * @param title a page title. + * @return a page index. + */ + public int waitPage(String title) { + return waitPage(title, getComparator()); + } + + /** + * Waits for a page to be selected. + * + * @param pageIndex an index of a page to be selected. + */ + public void waitSelected(final int pageIndex) { + getOutput().printLine("Wait " + Integer.toString(pageIndex) + "'th page to be " + + " selected in component \n : " + + toStringSource()); + getOutput().printGolden("Wait " + Integer.toString(pageIndex) + "'th page to be " + + " selected"); + waitState(new ComponentChooser() { + @Override + public boolean checkComponent(Component comp) { + return getSelectedIndex() == pageIndex; + } + + @Override + public String getDescription() { + return Integer.toString(pageIndex) + "'th page has been selected"; + } + + @Override + public String toString() { + return "JTabbedPaneOperator.waitSelected.ComponentChooser{description = " + getDescription() + '}'; + } + }); + } + + /** + * Waits for a page to be selected. + * + * @param pageTitle a title of a page to be selected. + */ + public void waitSelected(final String pageTitle) { + waitSelected(findPage(pageTitle)); + } + + @Override + public Hashtable getDump() { + Hashtable result = super.getDump(); + if (((JTabbedPane) getSource()).getSelectedIndex() != -1) { + result.put(SELECTED_PAGE_DPROP, ((JTabbedPane) getSource()). + getTitleAt(((JTabbedPane) getSource()).getSelectedIndex())); + } + String[] pages = new String[((JTabbedPane) getSource()).getTabCount()]; + for (int i = 0; i < ((JTabbedPane) getSource()).getTabCount(); i++) { + pages[i] = ((JTabbedPane) getSource()).getTitleAt(i); + } + addToDump(result, PAGE_PREFIX_DPROP, pages); + return result; + } + + //////////////////////////////////////////////////////// + //Mapping // + /** + * Maps {@code JTabbedPane.addChangeListener(ChangeListener)} through queue + */ + public void addChangeListener(final ChangeListener changeListener) { + runMapping(new MapVoidAction("addChangeListener") { + @Override + public void map() { + ((JTabbedPane) getSource()).addChangeListener(changeListener); + } + }); + } + + /** + * Maps {@code JTabbedPane.addTab(String, Component)} through queue + */ + public void addTab(final String string, final Component component) { + runMapping(new MapVoidAction("addTab") { + @Override + public void map() { + ((JTabbedPane) getSource()).addTab(string, component); + } + }); + } + + /** + * Maps {@code JTabbedPane.addTab(String, Icon, Component)} through queue + */ + public void addTab(final String string, final Icon icon, final Component component) { + runMapping(new MapVoidAction("addTab") { + @Override + public void map() { + ((JTabbedPane) getSource()).addTab(string, icon, component); + } + }); + } + + /** + * Maps {@code JTabbedPane.addTab(String, Icon, Component, String)} + * through queue + */ + public void addTab(final String string, final Icon icon, final Component component, final String string1) { + runMapping(new MapVoidAction("addTab") { + @Override + public void map() { + ((JTabbedPane) getSource()).addTab(string, icon, component, string1); + } + }); + } + + /** + * Maps {@code JTabbedPane.getBackgroundAt(int)} through queue + */ + public Color getBackgroundAt(final int i) { + return (runMapping(new MapAction("getBackgroundAt") { + @Override + public Color map() { + return ((JTabbedPane) getSource()).getBackgroundAt(i); + } + })); + } + + /** + * Maps {@code JTabbedPane.getBoundsAt(int)} through queue + */ + public Rectangle getBoundsAt(final int i) { + return (runMapping(new MapAction("getBoundsAt") { + @Override + public Rectangle map() { + return ((JTabbedPane) getSource()).getBoundsAt(i); + } + })); + } + + /** + * Maps {@code JTabbedPane.getComponentAt(int)} through queue + */ + public Component getComponentAt(final int i) { + return (runMapping(new MapAction("getComponentAt") { + @Override + public Component map() { + return ((JTabbedPane) getSource()).getComponentAt(i); + } + })); + } + + /** + * Maps {@code JTabbedPane.getDisabledIconAt(int)} through queue + */ + public Icon getDisabledIconAt(final int i) { + return (runMapping(new MapAction("getDisabledIconAt") { + @Override + public Icon map() { + return ((JTabbedPane) getSource()).getDisabledIconAt(i); + } + })); + } + + /** + * Maps {@code JTabbedPane.getForegroundAt(int)} through queue + */ + public Color getForegroundAt(final int i) { + return (runMapping(new MapAction("getForegroundAt") { + @Override + public Color map() { + return ((JTabbedPane) getSource()).getForegroundAt(i); + } + })); + } + + /** + * Maps {@code JTabbedPane.getIconAt(int)} through queue + */ + public Icon getIconAt(final int i) { + return (runMapping(new MapAction("getIconAt") { + @Override + public Icon map() { + return ((JTabbedPane) getSource()).getIconAt(i); + } + })); + } + + /** + * Maps {@code JTabbedPane.getModel()} through queue + */ + public SingleSelectionModel getModel() { + return (runMapping(new MapAction("getModel") { + @Override + public SingleSelectionModel map() { + return ((JTabbedPane) getSource()).getModel(); + } + })); + } + + /** + * Maps {@code JTabbedPane.getSelectedComponent()} through queue + */ + public Component getSelectedComponent() { + return (runMapping(new MapAction("getSelectedComponent") { + @Override + public Component map() { + return ((JTabbedPane) getSource()).getSelectedComponent(); + } + })); + } + + /** + * Maps {@code JTabbedPane.getSelectedIndex()} through queue + */ + public int getSelectedIndex() { + return (runMapping(new MapIntegerAction("getSelectedIndex") { + @Override + public int map() { + return ((JTabbedPane) getSource()).getSelectedIndex(); + } + })); + } + + /** + * Maps {@code JTabbedPane.getTabCount()} through queue + */ + public int getTabCount() { + return (runMapping(new MapIntegerAction("getTabCount") { + @Override + public int map() { + return ((JTabbedPane) getSource()).getTabCount(); + } + })); + } + + /** + * Maps {@code JTabbedPane.getTabPlacement()} through queue + */ + public int getTabPlacement() { + return (runMapping(new MapIntegerAction("getTabPlacement") { + @Override + public int map() { + return ((JTabbedPane) getSource()).getTabPlacement(); + } + })); + } + + /** + * Maps {@code JTabbedPane.getTabRunCount()} through queue + */ + public int getTabRunCount() { + return (runMapping(new MapIntegerAction("getTabRunCount") { + @Override + public int map() { + return ((JTabbedPane) getSource()).getTabRunCount(); + } + })); + } + + /** + * Maps {@code JTabbedPane.getTitleAt(int)} through queue + */ + public String getTitleAt(final int i) { + return (runMapping(new MapAction("getTitleAt") { + @Override + public String map() { + return ((JTabbedPane) getSource()).getTitleAt(i); + } + })); + } + + /** + * Maps {@code JTabbedPane.getUI()} through queue + */ + public TabbedPaneUI getUI() { + return (runMapping(new MapAction("getUI") { + @Override + public TabbedPaneUI map() { + return ((JTabbedPane) getSource()).getUI(); + } + })); + } + + /** + * Maps {@code JTabbedPane.indexOfComponent(Component)} through queue + */ + public int indexOfComponent(final Component component) { + return (runMapping(new MapIntegerAction("indexOfComponent") { + @Override + public int map() { + return ((JTabbedPane) getSource()).indexOfComponent(component); + } + })); + } + + /** + * Maps {@code JTabbedPane.indexOfTab(String)} through queue + */ + public int indexOfTab(final String string) { + return (runMapping(new MapIntegerAction("indexOfTab") { + @Override + public int map() { + return ((JTabbedPane) getSource()).indexOfTab(string); + } + })); + } + + /** + * Maps {@code JTabbedPane.indexOfTab(Icon)} through queue + */ + public int indexOfTab(final Icon icon) { + return (runMapping(new MapIntegerAction("indexOfTab") { + @Override + public int map() { + return ((JTabbedPane) getSource()).indexOfTab(icon); + } + })); + } + + /** + * Maps + * {@code JTabbedPane.insertTab(String, Icon, Component, String, int)} + * through queue + */ + public void insertTab(final String string, final Icon icon, final Component component, final String string1, final int i) { + runMapping(new MapVoidAction("insertTab") { + @Override + public void map() { + ((JTabbedPane) getSource()).insertTab(string, icon, component, string1, i); + } + }); + } + + /** + * Maps {@code JTabbedPane.isEnabledAt(int)} through queue + */ + public boolean isEnabledAt(final int i) { + return (runMapping(new MapBooleanAction("isEnabledAt") { + @Override + public boolean map() { + return ((JTabbedPane) getSource()).isEnabledAt(i); + } + })); + } + + /** + * Maps {@code JTabbedPane.removeChangeListener(ChangeListener)} + * through queue + */ + public void removeChangeListener(final ChangeListener changeListener) { + runMapping(new MapVoidAction("removeChangeListener") { + @Override + public void map() { + ((JTabbedPane) getSource()).removeChangeListener(changeListener); + } + }); + } + + /** + * Maps {@code JTabbedPane.removeTabAt(int)} through queue + */ + public void removeTabAt(final int i) { + runMapping(new MapVoidAction("removeTabAt") { + @Override + public void map() { + ((JTabbedPane) getSource()).removeTabAt(i); + } + }); + } + + /** + * Maps {@code JTabbedPane.setBackgroundAt(int, Color)} through queue + */ + public void setBackgroundAt(final int i, final Color color) { + runMapping(new MapVoidAction("setBackgroundAt") { + @Override + public void map() { + ((JTabbedPane) getSource()).setBackgroundAt(i, color); + } + }); + } + + /** + * Maps {@code JTabbedPane.setComponentAt(int, Component)} through queue + */ + public void setComponentAt(final int i, final Component component) { + runMapping(new MapVoidAction("setComponentAt") { + @Override + public void map() { + ((JTabbedPane) getSource()).setComponentAt(i, component); + } + }); + } + + /** + * Maps {@code JTabbedPane.setDisabledIconAt(int, Icon)} through queue + */ + public void setDisabledIconAt(final int i, final Icon icon) { + runMapping(new MapVoidAction("setDisabledIconAt") { + @Override + public void map() { + ((JTabbedPane) getSource()).setDisabledIconAt(i, icon); + } + }); + } + + /** + * Maps {@code JTabbedPane.setEnabledAt(int, boolean)} through queue + */ + public void setEnabledAt(final int i, final boolean b) { + runMapping(new MapVoidAction("setEnabledAt") { + @Override + public void map() { + ((JTabbedPane) getSource()).setEnabledAt(i, b); + } + }); + } + + /** + * Maps {@code JTabbedPane.setForegroundAt(int, Color)} through queue + */ + public void setForegroundAt(final int i, final Color color) { + runMapping(new MapVoidAction("setForegroundAt") { + @Override + public void map() { + ((JTabbedPane) getSource()).setForegroundAt(i, color); + } + }); + } + + /** + * Maps {@code JTabbedPane.setIconAt(int, Icon)} through queue + */ + public void setIconAt(final int i, final Icon icon) { + runMapping(new MapVoidAction("setIconAt") { + @Override + public void map() { + ((JTabbedPane) getSource()).setIconAt(i, icon); + } + }); + } + + /** + * Maps {@code JTabbedPane.setModel(SingleSelectionModel)} through queue + */ + public void setModel(final SingleSelectionModel singleSelectionModel) { + runMapping(new MapVoidAction("setModel") { + @Override + public void map() { + ((JTabbedPane) getSource()).setModel(singleSelectionModel); + } + }); + } + + /** + * Maps {@code JTabbedPane.setSelectedComponent(Component)} through queue + */ + public void setSelectedComponent(final Component component) { + runMapping(new MapVoidAction("setSelectedComponent") { + @Override + public void map() { + ((JTabbedPane) getSource()).setSelectedComponent(component); + } + }); + } + + /** + * Maps {@code JTabbedPane.setSelectedIndex(int)} through queue + */ + public void setSelectedIndex(final int i) { + runMapping(new MapVoidAction("setSelectedIndex") { + @Override + public void map() { + ((JTabbedPane) getSource()).setSelectedIndex(i); + } + }); + } + + /** + * Maps {@code JTabbedPane.setTabPlacement(int)} through queue + */ + public void setTabPlacement(final int i) { + runMapping(new MapVoidAction("setTabPlacement") { + @Override + public void map() { + ((JTabbedPane) getSource()).setTabPlacement(i); + } + }); + } + + /** + * Maps {@code JTabbedPane.setTitleAt(int, String)} through queue + */ + public void setTitleAt(final int i, final String string) { + runMapping(new MapVoidAction("setTitleAt") { + @Override + public void map() { + ((JTabbedPane) getSource()).setTitleAt(i, string); + } + }); + } + + /** + * Maps {@code JTabbedPane.setUI(TabbedPaneUI)} through queue + */ + public void setUI(final TabbedPaneUI tabbedPaneUI) { + runMapping(new MapVoidAction("setUI") { + @Override + public void map() { + ((JTabbedPane) getSource()).setUI(tabbedPaneUI); + } + }); + } + + //End of mapping // + //////////////////////////////////////////////////////// + /** + * Specifies criteria for a tab page searching. + */ + public interface TabPageChooser { + + /** + * Should be true if a page is good. + * + * @param oper Operator used to search item. + * @param index Index of a page be checked. + * @return true if a page fits the criteria. + */ + public boolean checkPage(JTabbedPaneOperator oper, int index); + + /** + * Page description. + * + * @return a description. + */ + public String getDescription(); + } + + /** + * Exception is thrown if a nonexistent page is trying to be selected. + */ + public class NoSuchPageException extends JemmyInputException { + + private static final long serialVersionUID = 42L; + + /** + * Constructor. + * + * @param item nonexistent page title. + */ + public NoSuchPageException(String item) { + super("No such page as \"" + item + "\"", getSource()); + } + } + + /** + * Allows to find component by page title. + */ + public static class JTabbedPaneByItemFinder implements ComponentChooser { + + String title; + int itemIndex; + StringComparator comparator; + + /** + * Constructs JTabbedPaneByItemFinder. + * + * @param lb a text pattern + * @param ii page index to check. If equal to -1, selected page is + * checked. + * @param comparator specifies string comparision algorithm. + */ + public JTabbedPaneByItemFinder(String lb, int ii, StringComparator comparator) { + title = lb; + itemIndex = ii; + this.comparator = comparator; + } + + /** + * Constructs JTabbedPaneByItemFinder. + * + * @param lb a text pattern + * @param ii page index to check. If equal to -1, selected page is + * checked. + */ + public JTabbedPaneByItemFinder(String lb, int ii) { + this(lb, ii, Operator.getDefaultStringComparator()); + } + + @Override + public boolean checkComponent(Component comp) { + if (comp instanceof JTabbedPane) { + if (title == null) { + return true; + } + JTabbedPaneOperator tpo = new JTabbedPaneOperator((JTabbedPane) comp); + if (tpo.getTabCount() > itemIndex) { + int ii = itemIndex; + if (ii == -1) { + ii = tpo.getSelectedIndex(); + if (ii == -1) { + return false; + } + } + return (comparator.equals(tpo.getTitleAt(ii), + title)); + } + } + return false; + } + + @Override + public String getDescription() { + return ("JTabbedPane with text \"" + title + "\" in " + + itemIndex + "'th item"); + } + + @Override + public String toString() { + return "JTabbedPaneByItemFinder{" + "title=" + title + ", itemIndex=" + itemIndex + ", comparator=" + comparator + '}'; + } + } + + /** + * Checks component type. + */ + public static class JTabbedPaneFinder extends Finder { + + /** + * Constructs JTabbedPaneFinder. + * + * @param sf other searching criteria. + */ + public JTabbedPaneFinder(ComponentChooser sf) { + super(JTabbedPane.class, sf); + } + + /** + * Constructs JTabbedPaneFinder. + */ + public JTabbedPaneFinder() { + super(JTabbedPane.class); + } + } + + private static class BySubStringTabPageChooser implements TabPageChooser { + + String title; + StringComparator comparator; + + public BySubStringTabPageChooser(String title, StringComparator comparator) { + this.title = title; + this.comparator = comparator; + } + + @Override + public boolean checkPage(JTabbedPaneOperator oper, int index) { + return (comparator.equals(oper.getTitleAt(index), + title)); + } + + @Override + public String getDescription() { + return "Page having \"" + title + "\" title."; + } + + @Override + public String toString() { + return "BySubStringTabPageChooser{" + "title=" + title + ", comparator=" + comparator + '}'; + } + } + +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JTableHeaderOperator.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JTableHeaderOperator.java new file mode 100644 index 00000000000..72d4c846db9 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JTableHeaderOperator.java @@ -0,0 +1,571 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.operators; + +import java.awt.Component; +import java.awt.Point; +import java.awt.Rectangle; + +import javax.swing.JTable; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.TableColumnModelEvent; +import javax.swing.plaf.TableHeaderUI; +import javax.swing.table.JTableHeader; +import javax.swing.table.TableCellRenderer; +import javax.swing.table.TableColumn; +import javax.swing.table.TableColumnModel; + +import org.netbeans.jemmy.ComponentChooser; +import org.netbeans.jemmy.ComponentSearcher; +import org.netbeans.jemmy.Outputable; +import org.netbeans.jemmy.TestOut; +import org.netbeans.jemmy.Timeoutable; +import org.netbeans.jemmy.Timeouts; +import org.netbeans.jemmy.drivers.DriverManager; +import org.netbeans.jemmy.drivers.OrderedListDriver; + +/** + * ComponentOperator.BeforeDragTimeout - time to sleep before column moving
+ * ComponentOperator.AfterDragTimeout - time to sleep after column moving
+ * ComponentOperator.WaitComponentTimeout - time to wait component displayed + *
. + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + * + */ +public class JTableHeaderOperator extends JComponentOperator + implements Outputable, Timeoutable { + + private TestOut output; + private Timeouts timeouts; + + private OrderedListDriver driver; + + /** + * Constructor. + * + * @param b a component + */ + public JTableHeaderOperator(JTableHeader b) { + super(b); + driver = DriverManager.getOrderedListDriver(getClass()); + } + + /** + * Constructs a JTableHeaderOperator object. + * + * @param cont a container + * @param chooser a component chooser specifying searching criteria. + * @param index an index between appropriate ones. + */ + public JTableHeaderOperator(ContainerOperator cont, ComponentChooser chooser, int index) { + this((JTableHeader) cont. + waitSubComponent(new JTableHeaderFinder(chooser), + index)); + copyEnvironment(cont); + } + + /** + * Constructs a JTableHeaderOperator object. + * + * @param cont a container + * @param chooser a component chooser specifying searching criteria. + */ + public JTableHeaderOperator(ContainerOperator cont, ComponentChooser chooser) { + this(cont, chooser, 0); + } + + /** + * Constructs a JTableHeaderOperator object. + * + * @param cont a container + * @param index an index between appropriate ones. + */ + public JTableHeaderOperator(ContainerOperator cont, int index) { + this((JTableHeader) waitComponent(cont, + new JTableHeaderFinder(ComponentSearcher. + getTrueChooser("Any JTableHeader")), + index)); + copyEnvironment(cont); + } + + /** + * Constructs a JTableHeaderOperator object. + * + * @param cont a container + */ + public JTableHeaderOperator(ContainerOperator cont) { + this(cont, 0); + } + + @Override + public void setTimeouts(Timeouts times) { + this.timeouts = times; + super.setTimeouts(timeouts); + } + + @Override + public Timeouts getTimeouts() { + return timeouts; + } + + @Override + public void setOutput(TestOut out) { + output = out; + super.setOutput(output); + } + + @Override + public TestOut getOutput() { + return output; + } + + /** + * Selects a column. + * + * @param columnIndex an index of a column to select. + */ + public void selectColumn(int columnIndex) { + driver.selectItem(this, columnIndex); + } + + /** + * Selects some columns. + * + * @param columnIndices indices of columns to select. + */ + public void selectColumns(int[] columnIndices) { + driver.selectItems(this, columnIndices); + } + + /** + * Moves a column to a different location. + * + * @param moveColumn an original column index. + * @param moveTo a desctination column index. + */ + public void moveColumn(int moveColumn, int moveTo) { + driver.moveItem(this, moveColumn, moveTo); + } + + /** + * Return a point to click on column header. + * + * @param columnIndex an index of a column to click on. + * @return the point to click. + */ + public Point getPointToClick(int columnIndex) { + Rectangle rect = getHeaderRect(columnIndex); + return (new Point(rect.x + rect.width / 2, + rect.y + rect.height / 2)); + } + + @Override + public void copyEnvironment(Operator anotherOperator) { + super.copyEnvironment(anotherOperator); + driver + = (OrderedListDriver) DriverManager. + getDriver(DriverManager.ORDEREDLIST_DRIVER_ID, + getClass(), + anotherOperator.getProperties()); + } + + //////////////////////////////////////////////////////// + //Mapping // + /** + * Maps {@code JTableHeader.setTable(JTable)} through queue + */ + public void setTable(final JTable jTable) { + runMapping(new MapVoidAction("setTable") { + @Override + public void map() { + ((JTableHeader) getSource()).setTable(jTable); + } + }); + } + + /** + * Maps {@code JTableHeader.getTable()} through queue + */ + public JTable getTable() { + return (runMapping(new MapAction("getTable") { + @Override + public JTable map() { + return ((JTableHeader) getSource()).getTable(); + } + })); + } + + /** + * Maps {@code JTableHeader.setReorderingAllowed(boolean)} through queue + */ + public void setReorderingAllowed(final boolean b) { + runMapping(new MapVoidAction("setReorderingAllowed") { + @Override + public void map() { + ((JTableHeader) getSource()).setReorderingAllowed(b); + } + }); + } + + /** + * Maps {@code JTableHeader.getReorderingAllowed()} through queue + */ + public boolean getReorderingAllowed() { + return (runMapping(new MapBooleanAction("getReorderingAllowed") { + @Override + public boolean map() { + return ((JTableHeader) getSource()).getReorderingAllowed(); + } + })); + } + + /** + * Maps {@code JTableHeader.setResizingAllowed(boolean)} through queue + */ + public void setResizingAllowed(final boolean b) { + runMapping(new MapVoidAction("setResizingAllowed") { + @Override + public void map() { + ((JTableHeader) getSource()).setResizingAllowed(b); + } + }); + } + + /** + * Maps {@code JTableHeader.getResizingAllowed()} through queue + */ + public boolean getResizingAllowed() { + return (runMapping(new MapBooleanAction("getResizingAllowed") { + @Override + public boolean map() { + return ((JTableHeader) getSource()).getResizingAllowed(); + } + })); + } + + /** + * Maps {@code JTableHeader.getDraggedColumn()} through queue + */ + public TableColumn getDraggedColumn() { + return (runMapping(new MapAction("getDraggedColumn") { + @Override + public TableColumn map() { + return ((JTableHeader) getSource()).getDraggedColumn(); + } + })); + } + + /** + * Maps {@code JTableHeader.getDraggedDistance()} through queue + */ + public int getDraggedDistance() { + return (runMapping(new MapIntegerAction("getDraggedDistance") { + @Override + public int map() { + return ((JTableHeader) getSource()).getDraggedDistance(); + } + })); + } + + /** + * Maps {@code JTableHeader.getResizingColumn()} through queue + */ + public TableColumn getResizingColumn() { + return (runMapping(new MapAction("getResizingColumn") { + @Override + public TableColumn map() { + return ((JTableHeader) getSource()).getResizingColumn(); + } + })); + } + + /** + * Maps {@code JTableHeader.setUpdateTableInRealTime(boolean)} through queue + */ + public void setUpdateTableInRealTime(final boolean b) { + runMapping(new MapVoidAction("setUpdateTableInRealTime") { + @Override + public void map() { + ((JTableHeader) getSource()).setUpdateTableInRealTime(b); + } + }); + } + + /** + * Maps {@code JTableHeader.getUpdateTableInRealTime()} through queue + */ + public boolean getUpdateTableInRealTime() { + return (runMapping(new MapBooleanAction("getUpdateTableInRealTime") { + @Override + public boolean map() { + return ((JTableHeader) getSource()).getUpdateTableInRealTime(); + } + })); + } + + /** + * Maps {@code JTableHeader.setDefaultRenderer(TableCellRenderer)} + * through queue + */ + public void setDefaultRenderer(final TableCellRenderer tableCellRenderer) { + runMapping(new MapVoidAction("setDefaultRenderer") { + @Override + public void map() { + ((JTableHeader) getSource()).setDefaultRenderer(tableCellRenderer); + } + }); + } + + /** + * Maps {@code JTableHeader.getDefaultRenderer()} through queue + */ + public TableCellRenderer getDefaultRenderer() { + return (runMapping(new MapAction("getDefaultRenderer") { + @Override + public TableCellRenderer map() { + return ((JTableHeader) getSource()).getDefaultRenderer(); + } + })); + } + + /** + * Maps {@code JTableHeader.columnAtPoint(Point)} through queue + */ + public int columnAtPoint(final Point point) { + return (runMapping(new MapIntegerAction("columnAtPoint") { + @Override + public int map() { + return ((JTableHeader) getSource()).columnAtPoint(point); + } + })); + } + + /** + * Maps {@code JTableHeader.getHeaderRect(int)} through queue + */ + public Rectangle getHeaderRect(final int i) { + return (runMapping(new MapAction("getHeaderRect") { + @Override + public Rectangle map() { + return ((JTableHeader) getSource()).getHeaderRect(i); + } + })); + } + + /** + * Maps {@code JTableHeader.getUI()} through queue + */ + public TableHeaderUI getUI() { + return (runMapping(new MapAction("getUI") { + @Override + public TableHeaderUI map() { + return ((JTableHeader) getSource()).getUI(); + } + })); + } + + /** + * Maps {@code JTableHeader.setUI(TableHeaderUI)} through queue + */ + public void setUI(final TableHeaderUI tableHeaderUI) { + runMapping(new MapVoidAction("setUI") { + @Override + public void map() { + ((JTableHeader) getSource()).setUI(tableHeaderUI); + } + }); + } + + /** + * Maps {@code JTableHeader.setColumnModel(TableColumnModel)} through queue + */ + public void setColumnModel(final TableColumnModel tableColumnModel) { + runMapping(new MapVoidAction("setColumnModel") { + @Override + public void map() { + ((JTableHeader) getSource()).setColumnModel(tableColumnModel); + } + }); + } + + /** + * Maps {@code JTableHeader.getColumnModel()} through queue + */ + public TableColumnModel getColumnModel() { + return (runMapping(new MapAction("getColumnModel") { + @Override + public TableColumnModel map() { + return ((JTableHeader) getSource()).getColumnModel(); + } + })); + } + + /** + * Maps {@code JTableHeader.columnAdded(TableColumnModelEvent)} through queue + */ + public void columnAdded(final TableColumnModelEvent tableColumnModelEvent) { + runMapping(new MapVoidAction("columnAdded") { + @Override + public void map() { + ((JTableHeader) getSource()).columnAdded(tableColumnModelEvent); + } + }); + } + + /** + * Maps {@code JTableHeader.columnRemoved(TableColumnModelEvent)} + * through queue + */ + public void columnRemoved(final TableColumnModelEvent tableColumnModelEvent) { + runMapping(new MapVoidAction("columnRemoved") { + @Override + public void map() { + ((JTableHeader) getSource()).columnRemoved(tableColumnModelEvent); + } + }); + } + + /** + * Maps {@code JTableHeader.columnMoved(TableColumnModelEvent)} through queue + */ + public void columnMoved(final TableColumnModelEvent tableColumnModelEvent) { + runMapping(new MapVoidAction("columnMoved") { + @Override + public void map() { + ((JTableHeader) getSource()).columnMoved(tableColumnModelEvent); + } + }); + } + + /** + * Maps {@code JTableHeader.columnMarginChanged(ChangeEvent)} through queue + */ + public void columnMarginChanged(final ChangeEvent changeEvent) { + runMapping(new MapVoidAction("columnMarginChanged") { + @Override + public void map() { + ((JTableHeader) getSource()).columnMarginChanged(changeEvent); + } + }); + } + + /** + * Maps {@code JTableHeader.columnSelectionChanged(ListSelectionEvent)} + * through queue + */ + public void columnSelectionChanged(final ListSelectionEvent listSelectionEvent) { + runMapping(new MapVoidAction("columnSelectionChanged") { + @Override + public void map() { + ((JTableHeader) getSource()).columnSelectionChanged(listSelectionEvent); + } + }); + } + + /** + * Maps {@code JTableHeader.resizeAndRepaint()} through queue + */ + public void resizeAndRepaint() { + runMapping(new MapVoidAction("resizeAndRepaint") { + @Override + public void map() { + ((JTableHeader) getSource()).resizeAndRepaint(); + } + }); + } + + /** + * Maps {@code JTableHeader.setDraggedColumn(TableColumn)} through queue + */ + public void setDraggedColumn(final TableColumn tableColumn) { + runMapping(new MapVoidAction("setDraggedColumn") { + @Override + public void map() { + ((JTableHeader) getSource()).setDraggedColumn(tableColumn); + } + }); + } + + /** + * Maps {@code JTableHeader.setDraggedDistance(int)} through queue + */ + public void setDraggedDistance(final int i) { + runMapping(new MapVoidAction("setDraggedDistance") { + @Override + public void map() { + ((JTableHeader) getSource()).setDraggedDistance(i); + } + }); + } + + /** + * Maps {@code JTableHeader.setResizingColumn(TableColumn)} through queue + */ + public void setResizingColumn(final TableColumn tableColumn) { + runMapping(new MapVoidAction("setResizingColumn") { + @Override + public void map() { + ((JTableHeader) getSource()).setResizingColumn(tableColumn); + } + }); + } + + //End of mapping // + //////////////////////////////////////////////////////// + /** + * Checks component type. + */ + public static class JTableHeaderFinder implements ComponentChooser { + + ComponentChooser subFinder; + + /** + * Constructs JTableHeaderFinder. + * + * @param sf other searching criteria. + */ + public JTableHeaderFinder(ComponentChooser sf) { + subFinder = sf; + } + + @Override + public boolean checkComponent(Component comp) { + if (comp instanceof JTableHeader) { + return subFinder.checkComponent(comp); + } + return false; + } + + @Override + public String getDescription() { + return subFinder.getDescription(); + } + + @Override + public String toString() { + return "JTableHeaderFinder{" + "subFinder=" + subFinder + '}'; + } + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JTableOperator.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JTableOperator.java new file mode 100644 index 00000000000..13e3e5a8776 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JTableOperator.java @@ -0,0 +1,2760 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.operators; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.Point; +import java.awt.Rectangle; +import java.util.EventObject; +import java.util.Hashtable; + +import javax.swing.JPopupMenu; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.ListSelectionModel; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.TableColumnModelEvent; +import javax.swing.event.TableModelEvent; +import javax.swing.plaf.TableUI; +import javax.swing.table.JTableHeader; +import javax.swing.table.TableCellEditor; +import javax.swing.table.TableCellRenderer; +import javax.swing.table.TableColumn; +import javax.swing.table.TableColumnModel; +import javax.swing.table.TableModel; + +import org.netbeans.jemmy.ComponentChooser; +import org.netbeans.jemmy.ComponentSearcher; +import org.netbeans.jemmy.JemmyException; +import org.netbeans.jemmy.Outputable; +import org.netbeans.jemmy.QueueTool; +import org.netbeans.jemmy.TestOut; +import org.netbeans.jemmy.TimeoutExpiredException; +import org.netbeans.jemmy.Timeoutable; +import org.netbeans.jemmy.Timeouts; +import org.netbeans.jemmy.Waiter; +import org.netbeans.jemmy.drivers.DriverManager; +import org.netbeans.jemmy.drivers.TableDriver; +import org.netbeans.jemmy.util.EmptyVisualizer; + +/** + *

Timeouts used:
+ * JTableOperator.WaitEditingTimeout - time to wait cell editing
+ * ComponentOperator.WaitComponentTimeout - time to wait component displayed + *
+ * ComponentOperator.WaitStateTimeout - time to wait for cell contents
+ * JTextComponentOperator.ChangeCaretPositionTimeout - maximum time to chenge + * caret position
+ * JTextComponentOperator.TypeTextTimeout - maximum time to type text
+ * JScrollBarOperator.WholeScrollTimeout - time for the whole scrolling
. + * + * @see Timeouts + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + * + */ +public class JTableOperator extends JComponentOperator + implements Outputable, Timeoutable { + + /** + * Identifier for a "cell" property. + * + * @see #getDump + */ + public static final String CELL_PREFIX_DPROP = "Cell"; + + /** + * Identifier for a "column" property. + * + * @see #getDump + */ + public static final String COLUMN_PREFIX_DPROP = "Column"; + + /** + * Identifier for a "selected column" property. + * + * @see #getDump + */ + public static final String SELECTED_COLUMN_PREFIX_DPROP = "SelectedColumn"; + + /** + * Identifier for a "selected row" property. + * + * @see #getDump + */ + public static final String SELECTED_ROW_PREFIX_DPROP = "SelectedRow"; + + /** + * Identifier for a "column count" property. + * + * @see #getDump + */ + public static final String COLUMN_COUNT_DPROP = "Column count"; + + /** + * Identifier for a "row count" property. + * + * @see #getDump + */ + public static final String ROW_COUNT_DPROP = "Row count"; + + private final static long WAIT_EDITING_TIMEOUT = 60000; + + private TestOut output; + private Timeouts timeouts; + + TableDriver driver; + + /** + * Constructor. + * + * @param b a component + */ + public JTableOperator(JTable b) { + super(b); + driver = DriverManager.getTableDriver(getClass()); + } + + /** + * Constructs a JTableOperator object. + * + * @param cont a container + * @param chooser a component chooser specifying searching criteria. + * @param index an index between appropriate ones. + */ + public JTableOperator(ContainerOperator cont, ComponentChooser chooser, int index) { + this((JTable) cont. + waitSubComponent(new JTableFinder(chooser), + index)); + copyEnvironment(cont); + } + + /** + * Constructs a JTableOperator object. + * + * @param cont a container + * @param chooser a component chooser specifying searching criteria. + */ + public JTableOperator(ContainerOperator cont, ComponentChooser chooser) { + this(cont, chooser, 0); + } + + /** + * Constructor. Waits by cell text first. Uses cont's timeout and output for + * waiting and to init operator. + * + * @param cont a container + * @param text Text of a cell which is currently selected. + * @param row a cell row to check. + * @param column a cell row to check. + * @param index Ordinal component index. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public JTableOperator(ContainerOperator cont, String text, int row, int column, int index) { + this((JTable) waitComponent(cont, + new JTableByCellFinder(text, row, column, + cont.getComparator()), + index)); + copyEnvironment(cont); + } + + /** + * Constructor. Waits by cell text first. Uses cont's timeout and output for + * waiting and to init operator. + * + * @param cont a container + * @param text Text of a cell which is currently selected. + * @param row a cell row to check. + * @param column a cell row to check. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public JTableOperator(ContainerOperator cont, String text, int row, int column) { + this(cont, text, row, column, 0); + } + + /** + * Constructor. Waits by text in selected cell first. Uses cont's timeout + * and output for waiting and to init operator. + * + * @param cont a container + * @param text Text of a cell which is currently selected. + * @param index Ordinal component index. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public JTableOperator(ContainerOperator cont, String text, int index) { + this(cont, text, -1, -1, index); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont a container + * @param text Text of a cell which is currently selected. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public JTableOperator(ContainerOperator cont, String text) { + this(cont, text, 0); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont a container + * @param index Ordinal component index. + * @throws TimeoutExpiredException + */ + public JTableOperator(ContainerOperator cont, int index) { + this((JTable) waitComponent(cont, + new JTableFinder(), + index)); + copyEnvironment(cont); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont a container + * @throws TimeoutExpiredException + */ + public JTableOperator(ContainerOperator cont) { + this(cont, 0); + } + + //////////////////////////////////////////////////////// + //Static finds // + //////////////////////////////////////////////////////// + /** + * Searches JTable in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @param index Ordinal component index. + * @return JTable instance or null if component was not found. + */ + public static JTable findJTable(Container cont, ComponentChooser chooser, int index) { + return (JTable) findComponent(cont, new JTableFinder(chooser), index); + } + + /** + * Searches 0'th JTable in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @return JTable instance or null if component was not found. + */ + public static JTable findJTable(Container cont, ComponentChooser chooser) { + return findJTable(cont, chooser, 0); + } + + /** + * Searches JTable by cell. + * + * @param cont Container to search component in. + * @param text Item text. If null, contents is not checked. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @param row Index of row to compare text. If -1, selected row is checked. + * @param column Index of column to compare text. If -1, selected column is + * checked. + * @param index Ordinal component index. + * @return JTable instance or null if component was not found. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + */ + public static JTable findJTable(Container cont, String text, boolean ce, boolean ccs, int row, int column, int index) { + return findJTable(cont, new JTableByCellFinder(text, row, column, new DefaultStringComparator(ce, ccs)), index); + } + + /** + * Searches JTable by cell. + * + * @param cont Container to search component in. + * @param text Item text. If null, contents is not checked. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @param row Index of row to compare text. If -1, selected row is checked. + * @param column Index of column to compare text. If -1, selected column is + * checked. + * @return JTable instance or null if component was not found. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + */ + public static JTable findJTable(Container cont, String text, boolean ce, boolean ccs, int row, int column) { + return findJTable(cont, text, ce, ccs, row, column, 0); + } + + /** + * Waits JTable in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @param index Ordinal component index. + * @return JTable instance or null if component was not found. + * @throws TimeoutExpiredException + */ + public static JTable waitJTable(Container cont, ComponentChooser chooser, int index) { + return (JTable) waitComponent(cont, new JTableFinder(chooser), index); + } + + /** + * Waits 0'th JTable in container. + * + * @param cont Container to search component in. + * @param chooser org.netbeans.jemmy.ComponentChooser implementation. + * @return JTable instance or null if component was not found. + * @throws TimeoutExpiredException + */ + public static JTable waitJTable(Container cont, ComponentChooser chooser) { + return waitJTable(cont, chooser, 0); + } + + /** + * Waits JTable by cell. + * + * @param cont Container to search component in. + * @param text Item text. If null, contents is not checked. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @param row Index of row to compare text. If -1, selected row is checked. + * @param column Index of column to compare text. If -1, selected column is + * checked. + * @param index Ordinal component index. + * @return JTable instance or null if component was not found. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public static JTable waitJTable(Container cont, String text, boolean ce, boolean ccs, int row, int column, int index) { + return waitJTable(cont, new JTableByCellFinder(text, row, column, new DefaultStringComparator(ce, ccs)), index); + } + + /** + * Waits JTable by cell. + * + * @param cont Container to search component in. + * @param text Item text. If null, contents is not checked. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @param row Index of row to compare text. If -1, selected row is checked. + * @param column Index of column to compare text. If -1, selected column is + * checked. + * @return JTable instance or null if component was not found. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public static JTable waitJTable(Container cont, String text, boolean ce, boolean ccs, int row, int column) { + return waitJTable(cont, text, ce, ccs, row, column, 0); + } + + static { + Timeouts.initDefault("JTableOperator.WaitEditingTimeout", WAIT_EDITING_TIMEOUT); + } + + //////////////////////////////////////////////////////// + //Environment // + //////////////////////////////////////////////////////// + @Override + public void setTimeouts(Timeouts times) { + this.timeouts = times; + super.setTimeouts(timeouts); + } + + @Override + public Timeouts getTimeouts() { + return timeouts; + } + + @Override + public void setOutput(TestOut out) { + output = out; + super.setOutput(output); + } + + @Override + public TestOut getOutput() { + return output; + } + + @Override + public void copyEnvironment(Operator anotherOperator) { + super.copyEnvironment(anotherOperator); + driver + = (TableDriver) DriverManager. + getDriver(DriverManager.TABLE_DRIVER_ID, + getClass(), + anotherOperator.getProperties()); + } + + //////////////////////////////////////////////////////// + //Find methods // + //////////////////////////////////////////////////////// + //text, comparator and index + /** + * Searches cell coordinates. + * + * @param text a text pattern + * @param comparator a string comparision algorithm + * @param index an ordinal cell index + * @return Point indicating coordinates (x - column, y - row) + */ + public Point findCell(String text, StringComparator comparator, int index) { + return (findCell(new BySubStringTableCellChooser(text, + comparator), + index)); + } + + /** + * Searches cell coordinates in the specified rows and columns. + * + * @param text a text pattern + * @param comparator a string comparision algorithm + * @param rows rows to search in + * @param columns columns to search in + * @param index an ordinal cell index + * @return Point indicating coordinates (x - column, y - row) + */ + public Point findCell(String text, StringComparator comparator, int[] rows, int[] columns, int index) { + return findCell(new BySubStringTableCellChooser(text, comparator), rows, columns, index); + } + + /** + * Searches cell row index. + * + * @param text a text pattern + * @param comparator a string comparision algorithm + * @param index an ordinal cell index + * @return a row index. + */ + public int findCellRow(String text, StringComparator comparator, int index) { + return findCell(text, comparator, index).y; + } + + /** + * Searches cell row index. Searching is performed between cells in one + * column. + * + * @param text a text pattern + * @param comparator a string comparision algorithm + * @param column a column index to search in + * @param index an ordinal cell index + * @return a row index. + */ + public int findCellRow(String text, StringComparator comparator, int column, int index) { + return findCell(text, comparator, null, new int[]{column}, index).y; + } + + /** + * Searches cell column visible index. + * + * @param text a text pattern + * @param comparator a string comparision algorithm + * @param index an ordinal cell index + * @return a column index. + */ + public int findCellColumn(String text, StringComparator comparator, int index) { + return findCell(text, comparator, index).x; + } + + /** + * Searches cell column index. Searching is performed between cells in one + * row. + * + * @param text a text pattern + * @param comparator a string comparision algorithm + * @param row a row index to search in + * @param index an ordinal cell index + * @return a column index. + */ + public int findCellColumn(String text, StringComparator comparator, int row, int index) { + return findCell(text, comparator, new int[]{row}, null, index).x; + } + + //booleans - deprecated + /** + * Searches cell row by cell text. + * + * @param text Text to search by. + * @param ce Compare exactly. + * @param ccs Compare case sensitively. + * @param index Ordinal index in suitable cells. + * @return a row index. + * @see #findCellRow(String, int) + * @deprecated Use findCellRow(String, int) or findCellRow(String, + * StringComparator, int) + */ + @Deprecated + public int findCellRow(String text, boolean ce, boolean ccs, int index) { + return findCell(text, ce, ccs, index).y; + } + + /** + * Searches cell column by cell text. + * + * @param text Text to search by. + * @param ce Compare exactly. + * @param ccs Compare case sensitively. + * @param index Ordinal index in suitable cells. + * @return a column index. + * @see #findCellColumn(String, int) + * @deprecated Use findCellColumn(String, int) or findCellColumn(String, + * StringComparator, int) + */ + @Deprecated + public int findCellColumn(String text, boolean ce, boolean ccs, int index) { + return findCell(text, ce, ccs, index).x; + } + + /** + * Searches first cell row by cell text. + * + * @param text Text to search by. + * @param ce Compare exactly. + * @param ccs Compare case sensitively. + * @return a row index. + * @see #findCellRow(String) + * @deprecated Use findCellRow(String) or findCellRow(String, + * StringComparator) + */ + @Deprecated + public int findCellRow(String text, boolean ce, boolean ccs) { + return findCellRow(text, ce, ccs, 0); + } + + /** + * Searches first cell column by cell text. + * + * @param text Text to search by. + * @param ce Compare exactly. + * @param ccs Compare case sensitively. + * @return a column index. + * @see #findCellColumn(String) + * @deprecated Use findCellColumn(String) or findCellColumn(String, + * StringComparator) + */ + @Deprecated + public int findCellColumn(String text, boolean ce, boolean ccs) { + return findCellColumn(text, ce, ccs, 0); + } + + //text and comparator only + /** + * Searches cell row index. + * + * @param text a text pattern + * @param comparator a string comparision algorithm + * @return a row index. + */ + public int findCellRow(String text, StringComparator comparator) { + return findCellRow(text, comparator, 0); + } + + /** + * Searches cell column visible index. + * + * @param text a text pattern + * @param comparator a string comparision algorithm + * @return a column index. + */ + public int findCellColumn(String text, StringComparator comparator) { + return findCellColumn(text, comparator, 0); + } + + //text and index + /** + * Searches cell row by cell text. + * + * @param text Text to search by. + * @param index Ordinal index in suitable cells. + * @return a row index. + */ + public int findCellRow(String text, int index) { + return findCell(text, index).y; + } + + /** + * Searches cell row index. Searching is performed between cells in one + * column. + * + * @param text a text pattern + * @param column a column index to search in + * @param index Ordinal index in suitable cells. + * @return a row index. + */ + public int findCellRow(String text, int column, int index) { + return findCell(text, null, new int[]{column}, index).y; + } + + /** + * Searches cell column by cell text. + * + * @param text Text to search by. + * @param index Ordinal index in suitable cells. + * @return a column index. + */ + public int findCellColumn(String text, int index) { + return findCell(text, index).x; + } + + /** + * Searches cell column index. Searching is performed between cells in one + * row. + * + * @param text a text pattern + * @param row a row index to search in + * @param index Ordinal index in suitable cells. + * @return a column index. + */ + public int findCellColumn(String text, int row, int index) { + return findCell(text, new int[]{row}, null, index).x; + } + + /** + * Searches cell coordinates. + * + * @param text a text pattern + * @param index an ordinal cell index + * @return Point indicating coordinates (x - column, y - row) + */ + public Point findCell(String text, int index) { + return findCell(text, getComparator(), index); + } + + /** + * Searches cell coordinates in the specified rows and columns. + * + * @param text a text pattern + * @param rows rows to search in + * @param columns columns to search in + * @param index an ordinal cell index + * @return Point indicating coordinates (x - column, y - row) + */ + public Point findCell(String text, int[] rows, int[] columns, int index) { + return findCell(new BySubStringTableCellChooser(text, getComparator()), rows, columns, index); + } + + //text only + /** + * Searches first cell row by cell text. + * + * @param text Text to search by. + * @return a row index. + */ + public int findCellRow(String text) { + return findCellRow(text, 0); + } + + /** + * Searches first cell column by cell text. + * + * @param text Text to search by. + * @return a column index. + */ + public int findCellColumn(String text) { + return findCellColumn(text, 0); + } + + //component chooser and index + /** + * Searches cell row by rendered component. + * + * @param chooser Component verifying object. + * @param index Ordinal index in suitable cells. + * @return a row index. + */ + public int findCellRow(ComponentChooser chooser, int index) { + return findCell(chooser, index).y; + } + + /** + * Searches cell row index. Searching is performed between cells in one + * column. + * + * @param chooser Component verifying object. + * @param column a column index to search in + * @param index Ordinal index in suitable cells. + * @return a row index. + */ + public int findCellRow(ComponentChooser chooser, int column, int index) { + return findCell(chooser, null, new int[]{column}, index).y; + } + + /** + * Searches cell column by rendered component. + * + * @param chooser Component verifying object. + * @param index Ordinal index in suitable cells. + * @return a column index. + */ + public int findCellColumn(ComponentChooser chooser, int index) { + return findCell(chooser, index).x; + } + + /** + * Searches cell column index. Searching is performed between cells in one + * row. + * + * @param chooser Component verifying object. + * @param row a row index to search in + * @param index Ordinal index in suitable cells. + * @return a column index. + */ + public int findCellColumn(ComponentChooser chooser, int row, int index) { + return findCell(chooser, new int[]{row}, null, index).x; + } + + /** + * Searches cell coordinates. + * + * @param chooser Component verifying object. + * @param index an ordinal cell index + * @return Point indicating coordinates (x - column, y - row) + */ + public Point findCell(ComponentChooser chooser, int index) { + return findCell(new ByRenderedComponentTableCellChooser(chooser), index); + } + + /** + * Searches cell coordinates. + * + * @param chooser Component verifying object. + * @param rows rows to search in + * @param columns columns to search in + * @param index an ordinal cell index + * @return Point indicating coordinates (x - column, y - row) + */ + public Point findCell(ComponentChooser chooser, int[] rows, int[] columns, int index) { + return findCell(new ByRenderedComponentTableCellChooser(chooser), rows, columns, index); + } + + //component chooser only + /** + * Searches cell row by rendered component. + * + * @param chooser Component verifying object. + * @return a row index. + */ + public int findCellRow(ComponentChooser chooser) { + return findCellRow(chooser, 0); + } + + /** + * Searches cell column by rendered component. + * + * @param chooser Component verifying object. + * @return a column index. + */ + public int findCellColumn(ComponentChooser chooser) { + return findCellColumn(chooser, 0); + } + + /** + * Searches cell coordinates. + * + * @param chooser Component verifying object. + * @return Point indicating coordinates (x - column, y - row) + */ + public Point findCell(ComponentChooser chooser) { + return findCell(chooser, 0); + } + + //cell chooser and index + /** + * Searches cell row by TableCellChooser. + * + * @param chooser Cell verifying object. + * @param index Ordinal index in suitable cells. + * @return a row index. + */ + public int findCellRow(TableCellChooser chooser, int index) { + return findCell(chooser, index).y; + } + + /** + * Searches cell row index. Searching is performed between cells in one + * column. + * + * @param chooser Cell verifying object. + * @param column a column index to search in + * @param index Ordinal index in suitable cells. + * @return a row index. + */ + public int findCellRow(TableCellChooser chooser, int column, int index) { + return findCell(chooser, null, new int[]{column}, index).y; + } + + /** + * Searches cell column by TableCellChooser. + * + * @param chooser ComponentCell verifying object. + * @param index Ordinal index in suitable cells. + * @return a column index. + */ + public int findCellColumn(TableCellChooser chooser, int index) { + return findCell(chooser, index).x; + } + + /** + * Searches cell column index. Searching is performed between cells in one + * row. + * + * @param chooser Cell verifying object. + * @param row a row index to search in + * @param index Ordinal index in suitable cells. + * @return a column index. + */ + public int findCellColumn(TableCellChooser chooser, int row, int index) { + return findCell(chooser, new int[]{row}, null, index).x; + } + + /** + * Searches cell coordinates. + * + * @param chooser cell verifying object. + * @param index an ordinal cell index + * @return Point indicating coordinates (x - column, y - row) + */ + public Point findCell(TableCellChooser chooser, int index) { + return findCell(chooser, null, null, index); + } + + /** + * Searches cell coordinates in the specified rows and columns. + * + * @param chooser cell verifying object. + * @param rows rows to search in + * @param columns columns to search in + * @param index an ordinal cell index + * @return Point indicating coordinates (x - column, y - row) + */ + public Point findCell(TableCellChooser chooser, int[] rows, int[] columns, int index) { + TableModel model = getModel(); + int[] realRows; + if (rows != null) { + realRows = rows; + } else { + realRows = new int[model.getRowCount()]; + for (int i = 0; i < model.getRowCount(); i++) { + realRows[i] = i; + } + } + int[] realColumns; + if (columns != null) { + realColumns = columns; + } else { + realColumns = new int[model.getColumnCount()]; + for (int i = 0; i < model.getColumnCount(); i++) { + realColumns[i] = i; + } + } + int count = 0; + for (int realRow : realRows) { + for (int realColumn : realColumns) { + if (chooser.checkCell(this, realRow, realColumn)) { + if (count == index) { + return new Point(realColumn, realRow); + } else { + count++; + } + } + } + } + return new Point(-1, -1); + } + + //cell chooser only + /** + * Searches cell row by TableCellChooser. + * + * @param chooser Cell verifying object. + * @return a row index. + */ + public int findCellRow(TableCellChooser chooser) { + return findCellRow(chooser, 0); + } + + /** + * Searches cell column by TableCellChooser. + * + * @param chooser Cell verifying object. + * @return a column index. + */ + public int findCellColumn(TableCellChooser chooser) { + return findCellColumn(chooser, 0); + } + + /** + * Searches cell coordinates. + * + * @param chooser cell verifying object. + * @return Point indicating coordinates (x - column, y - row) + */ + public Point findCell(TableCellChooser chooser) { + return findCell(chooser, 0); + } + + //////////////////////////////////////////////////////// + //Actions // + //////////////////////////////////////////////////////// + /** + * Does mouse click on the cell. + * + * @param row a row index + * @param column a column index + * @param clickCount a number of clicks + * @param button a mouse button (value of InputEvent.BUTTON*_MASK field) + * @param modifiers Modifiers (combination of InputEvent.*_MASK values) + */ + public void clickOnCell(final int row, final int column, final int clickCount, final int button, final int modifiers) { + output.printLine("Click on (" + + Integer.toString(row) + ", " + + Integer.toString(column) + + ") cell"); + output.printGolden("Click on cell"); + makeComponentVisible(); + scrollToCell(row, column); + getQueueTool().invokeSmoothly(new QueueTool.QueueAction("Path selecting") { + @Override + public Void launch() { + Point point = getPointToClick(row, column); + clickMouse(point.x, point.y, clickCount, button, modifiers); + return null; + } + }); + } + + /** + * Does mouse click on the cell with no modifiers. + * + * @param row a row index + * @param column a column index + * @param clickCount a number of clicks + * @param button a mouse button (value of InputEvent.BUTTON*_MASK field) + */ + public void clickOnCell(int row, int column, int clickCount, int button) { + clickOnCell(row, column, clickCount, button, 0); + } + + /** + * Does mouse click on the cell by default mouse button with no modifiers. + * + * @param row a row index + * @param column a column index + * @param clickCount a number of clicks + */ + public void clickOnCell(int row, int column, int clickCount) { + clickOnCell(row, column, clickCount, getDefaultMouseButton()); + } + + /** + * Does single mouse click on the cell. + * + * @param row a row index + * @param column a column index + */ + public void clickOnCell(int row, int column) { + clickOnCell(row, column, 1); + } + + /** + * Double clicks on cell to turns it to the editing mode. + * + * @param row a row index + * @param column a column index + * @throws TimeoutExpiredException + */ + public void clickForEdit(int row, int column) { + clickOnCell(row, column, 2); + } + + /** + * Changes text of the cell pointed by row and column indexes. + * + * @param row a row index + * @param column a column index + * @param newText a text to enter + * @deprecated Use changeCellObject(int, int, Object) instead. + * @see #changeCellObject(int, int, Object) + * @throws TimeoutExpiredException + */ + @Deprecated + public void changeCellText(int row, int column, String newText) { + changeCellObject(row, column, newText); + } + + /** + * Changes value of the cell pointed by row and column indexes. uses editor + * defined by setCellEditor method. + * + * @param row a row index + * @param column a column index + * @param newValue an object to chenge cell value to. + * @throws TimeoutExpiredException + */ + public void changeCellObject(int row, int column, Object newValue) { + driver.editCell(this, row, column, newValue); + } + + /** + * Scrolls to a cell if the table lies on a JScrollPane component. + * + * @param row a row index + * @param column a column index + * @throws TimeoutExpiredException + */ + public void scrollToCell(int row, int column) { + output.printTrace("Scroll JTable to (" + Integer.toString(row) + + "," + Integer.toString(column) + ") cell\n : " + + toStringSource()); + output.printGolden("Scroll JTable to (" + Integer.toString(row) + + "," + Integer.toString(column) + ")"); + makeComponentVisible(); + //try to find JScrollPane under. + JScrollPane scroll = (JScrollPane) getContainer(new JScrollPaneOperator.JScrollPaneFinder(ComponentSearcher. + getTrueChooser("JScrollPane"))); + if (scroll == null) { + return; + } + JScrollPaneOperator scroller = new JScrollPaneOperator(scroll); + scroller.copyEnvironment(this); + scroller.setVisualizer(new EmptyVisualizer()); + Rectangle rect = getCellRect(row, column, false); + scroller.scrollToComponentRectangle(getSource(), + (int) rect.getX(), + (int) rect.getY(), + (int) rect.getWidth(), + (int) rect.getHeight()); + } + + /** + * Selects a specified cell. + * + * @param row a row index + * @param column a column index + */ + public void selectCell(int row, int column) { + driver.selectCell(this, row, column); + } + + /** + * Searches a column by name. + * + * @param name a text pattern + * @param comparator a string comparision algorithm + * @return a column index + */ + public int findColumn(String name, StringComparator comparator) { + int columnCount = getColumnCount(); + for (int i = 0; i < columnCount; i++) { + if (comparator.equals(getColumnName(i), + name)) { + return i; + } + } + return -1; + } + + /** + * Searches a column by name. + * + * @param name a text pattern + * @return a column index + */ + public int findColumn(String name) { + return findColumn(name, getComparator()); + } + + /** + * Calls popup menu on specified cell. + * + * @param row a row index + * @param column a column index + * @return the menu + */ + public JPopupMenu callPopupOnCell(int row, int column) { + output.printLine("Call popup on (" + row + ", " + column + ") cell"); + output.printGolden("Call popup on cell"); + makeComponentVisible(); + Point point = getPointToClick(row, column); + return (JPopupMenuOperator.callPopup(getSource(), + (int) point.getX(), + (int) point.getY(), + getPopupMouseButton())); + } + + //////////////////////////////////////////////////////// + //Gets // + //////////////////////////////////////////////////////// + /** + * Ask renderer for component to be displayed. + * + * @param row cell row index. + * @param column cell column visible index. + * @param isSelected True if the specified cell was selected. + * @param cellHasFocus True if the specified cell has the focus. + * @return Component to be displayed. + */ + public Component getRenderedComponent(int row, int column, boolean isSelected, boolean cellHasFocus) { + return (getCellRenderer(row, column). + getTableCellRendererComponent((JTable) getSource(), + getValueAt(row, column), + isSelected, + cellHasFocus, + row, + column)); + } + + /** + * Ask renderer for component to be displayed. Uses + * isCellSelected(itemIndex) to determine whether cell is selected. Supposes + * item do not have focus. + * + * @param row cell row index. + * @param column cell column visible index. + * @return Component to be displayed. + */ + public Component getRenderedComponent(int row, int column) { + return (getRenderedComponent(row, column, isCellSelected(row, column), + false)); + } + + /** + * Returns a point at the center of the cell rectangle. + * + * @param row a row index + * @param column a column index + * @return a Point in component's coordinate system. + */ + public Point getPointToClick(int row, int column) { + Rectangle rect = getCellRect(row, column, false); + return (new Point((int) (rect.getX() + rect.getWidth() / 2), + (int) (rect.getY() + rect.getHeight() / 2))); + } + + /** + * Creates an operator for a teble header assigned to this table. + * + * @return an JTableHeaderOperator operator + */ + public JTableHeaderOperator getHeaderOperator() { + return new JTableHeaderOperator(getTableHeader()); + } + + /** + * Waits for an editor. + * + * @param chooser a chhoser specifying criteria to distinguish cell editor. + * @param row a row index + * @param column a column index + * @return a component displayed over the cell and fitting the criteria + * specified by {@code chooser} + */ + public Component waitCellComponent(ComponentChooser chooser, int row, int column) { + CellComponentWaiter waiter = new CellComponentWaiter(chooser, row, column); + waiter.setOutput(getOutput()); + waiter.setTimeoutsToCloneOf(getTimeouts(), "JTableOperator.WaitEditingTimeout"); + try { + return waiter.waitAction(null); + } catch (InterruptedException e) { + throw (new JemmyException("Waiting has been interrupted", e)); + } + } + + /** + * Waits for certain cell contents. + * + * @param cellText Text comparing to cell text by + * {@code getComparator()} comparator. + * @param row cell row index. If -1, selected one is checked. + * @param column cell column visible index. If -1, selected one is checked. + */ + public void waitCell(String cellText, int row, int column) { + getOutput().printLine("Wait \"" + cellText + "\" text at (" + + Integer.toString(row) + "," + + Integer.toString(column) + ")" + + " position in component \n : " + + toStringSource()); + getOutput().printGolden("Wait \"" + cellText + "\" text at (" + + Integer.toString(row) + "," + + Integer.toString(column) + ")" + + " position"); + waitState(new JTableByCellFinder(cellText, row, column, getComparator())); + } + + /** + * Returns information about component. + */ + @Override + public Hashtable getDump() { + Hashtable result = super.getDump(); + TableModel model = ((JTable) getSource()).getModel(); + int colCount = model.getColumnCount(); + int rowCount = model.getRowCount(); + String[][] items = new String[rowCount][colCount]; + for (int i = 0; i < rowCount; i++) { + for (int j = 0; j < colCount; j++) { + if (model.getValueAt(i, j) != null) { + items[i][j] = model.getValueAt(i, j).toString(); + } else { + items[i][j] = "null"; + } + } + } + addToDump(result, CELL_PREFIX_DPROP, items); + String[] columns = new String[colCount]; + for (int j = 0; j < colCount; j++) { + columns[j] = ((JTable) getSource()).getColumnName(j); + } + addToDump(result, COLUMN_PREFIX_DPROP, columns); + int[] selColNums = ((JTable) getSource()).getSelectedColumns(); + String[] selColumns = new String[selColNums.length]; + for (int j = 0; j < selColNums.length; j++) { + selColumns[j] = Integer.toString(selColNums[j]); + } + addToDump(result, SELECTED_COLUMN_PREFIX_DPROP, selColumns); + int[] selRowNums = ((JTable) getSource()).getSelectedRows(); + String[] selRows = new String[selRowNums.length]; + for (int i = 0; i < selRowNums.length; i++) { + selRows[i] = Integer.toString(selRowNums[i]); + } + addToDump(result, SELECTED_ROW_PREFIX_DPROP, selRows); + result.put(COLUMN_COUNT_DPROP, Integer.toString(colCount)); + result.put(ROW_COUNT_DPROP, Integer.toString(rowCount)); + return result; + } + + //////////////////////////////////////////////////////// + //Mapping // + /** + * Maps {@code JTable.addColumn(TableColumn)} through queue + */ + public void addColumn(final TableColumn tableColumn) { + runMapping(new MapVoidAction("addColumn") { + @Override + public void map() { + ((JTable) getSource()).addColumn(tableColumn); + } + }); + } + + /** + * Maps {@code JTable.addColumnSelectionInterval(int, int)} through queue + */ + public void addColumnSelectionInterval(final int i, final int i1) { + runMapping(new MapVoidAction("addColumnSelectionInterval") { + @Override + public void map() { + ((JTable) getSource()).addColumnSelectionInterval(i, i1); + } + }); + } + + /** + * Maps {@code JTable.addRowSelectionInterval(int, int)} through queue + */ + public void addRowSelectionInterval(final int i, final int i1) { + runMapping(new MapVoidAction("addRowSelectionInterval") { + @Override + public void map() { + ((JTable) getSource()).addRowSelectionInterval(i, i1); + } + }); + } + + /** + * Maps {@code JTable.clearSelection()} through queue + */ + public void clearSelection() { + runMapping(new MapVoidAction("clearSelection") { + @Override + public void map() { + ((JTable) getSource()).clearSelection(); + } + }); + } + + /** + * Maps {@code JTable.columnAdded(TableColumnModelEvent)} through queue + */ + public void columnAdded(final TableColumnModelEvent tableColumnModelEvent) { + runMapping(new MapVoidAction("columnAdded") { + @Override + public void map() { + ((JTable) getSource()).columnAdded(tableColumnModelEvent); + } + }); + } + + /** + * Maps {@code JTable.columnAtPoint(Point)} through queue + */ + public int columnAtPoint(final Point point) { + return (runMapping(new MapIntegerAction("columnAtPoint") { + @Override + public int map() { + return ((JTable) getSource()).columnAtPoint(point); + } + })); + } + + /** + * Maps {@code JTable.columnMarginChanged(ChangeEvent)} through queue + */ + public void columnMarginChanged(final ChangeEvent changeEvent) { + runMapping(new MapVoidAction("columnMarginChanged") { + @Override + public void map() { + ((JTable) getSource()).columnMarginChanged(changeEvent); + } + }); + } + + /** + * Maps {@code JTable.columnMoved(TableColumnModelEvent)} through queue + */ + public void columnMoved(final TableColumnModelEvent tableColumnModelEvent) { + runMapping(new MapVoidAction("columnMoved") { + @Override + public void map() { + ((JTable) getSource()).columnMoved(tableColumnModelEvent); + } + }); + } + + /** + * Maps {@code JTable.columnRemoved(TableColumnModelEvent)} through queue + */ + public void columnRemoved(final TableColumnModelEvent tableColumnModelEvent) { + runMapping(new MapVoidAction("columnRemoved") { + @Override + public void map() { + ((JTable) getSource()).columnRemoved(tableColumnModelEvent); + } + }); + } + + /** + * Maps {@code JTable.columnSelectionChanged(ListSelectionEvent)} + * through queue + */ + public void columnSelectionChanged(final ListSelectionEvent listSelectionEvent) { + runMapping(new MapVoidAction("columnSelectionChanged") { + @Override + public void map() { + ((JTable) getSource()).columnSelectionChanged(listSelectionEvent); + } + }); + } + + /** + * Maps {@code JTable.convertColumnIndexToModel(int)} through queue + */ + public int convertColumnIndexToModel(final int i) { + return (runMapping(new MapIntegerAction("convertColumnIndexToModel") { + @Override + public int map() { + return ((JTable) getSource()).convertColumnIndexToModel(i); + } + })); + } + + /** + * Maps {@code JTable.convertColumnIndexToView(int)} through queue + */ + public int convertColumnIndexToView(final int i) { + return (runMapping(new MapIntegerAction("convertColumnIndexToView") { + @Override + public int map() { + return ((JTable) getSource()).convertColumnIndexToView(i); + } + })); + } + + /** + * Maps {@code JTable.createDefaultColumnsFromModel()} through queue + */ + public void createDefaultColumnsFromModel() { + runMapping(new MapVoidAction("createDefaultColumnsFromModel") { + @Override + public void map() { + ((JTable) getSource()).createDefaultColumnsFromModel(); + } + }); + } + + /** + * Maps {@code JTable.editCellAt(int, int)} through queue + */ + public boolean editCellAt(final int i, final int i1) { + return (runMapping(new MapBooleanAction("editCellAt") { + @Override + public boolean map() { + return ((JTable) getSource()).editCellAt(i, i1); + } + })); + } + + /** + * Maps {@code JTable.editCellAt(int, int, EventObject)} through queue + */ + public boolean editCellAt(final int i, final int i1, final EventObject eventObject) { + return (runMapping(new MapBooleanAction("editCellAt") { + @Override + public boolean map() { + return ((JTable) getSource()).editCellAt(i, i1, eventObject); + } + })); + } + + /** + * Maps {@code JTable.editingCanceled(ChangeEvent)} through queue + */ + public void editingCanceled(final ChangeEvent changeEvent) { + runMapping(new MapVoidAction("editingCanceled") { + @Override + public void map() { + ((JTable) getSource()).editingCanceled(changeEvent); + } + }); + } + + /** + * Maps {@code JTable.editingStopped(ChangeEvent)} through queue + */ + public void editingStopped(final ChangeEvent changeEvent) { + runMapping(new MapVoidAction("editingStopped") { + @Override + public void map() { + ((JTable) getSource()).editingStopped(changeEvent); + } + }); + } + + /** + * Maps {@code JTable.getAutoCreateColumnsFromModel()} through queue + */ + public boolean getAutoCreateColumnsFromModel() { + return (runMapping(new MapBooleanAction("getAutoCreateColumnsFromModel") { + @Override + public boolean map() { + return ((JTable) getSource()).getAutoCreateColumnsFromModel(); + } + })); + } + + /** + * Maps {@code JTable.getAutoResizeMode()} through queue + */ + public int getAutoResizeMode() { + return (runMapping(new MapIntegerAction("getAutoResizeMode") { + @Override + public int map() { + return ((JTable) getSource()).getAutoResizeMode(); + } + })); + } + + /** + * Maps {@code JTable.getCellEditor()} through queue + */ + public TableCellEditor getCellEditor() { + return (runMapping(new MapAction("getCellEditor") { + @Override + public TableCellEditor map() { + return ((JTable) getSource()).getCellEditor(); + } + })); + } + + /** + * Maps {@code JTable.getCellEditor(int, int)} through queue + */ + public TableCellEditor getCellEditor(final int i, final int i1) { + return (runMapping(new MapAction("getCellEditor") { + @Override + public TableCellEditor map() { + return ((JTable) getSource()).getCellEditor(i, i1); + } + })); + } + + /** + * Maps {@code JTable.getCellRect(int, int, boolean)} through queue + */ + public Rectangle getCellRect(final int i, final int i1, final boolean b) { + return (runMapping(new MapAction("getCellRect") { + @Override + public Rectangle map() { + return ((JTable) getSource()).getCellRect(i, i1, b); + } + })); + } + + /** + * Maps {@code JTable.getCellRenderer(int, int)} through queue + */ + public TableCellRenderer getCellRenderer(final int i, final int i1) { + return (runMapping(new MapAction("getCellRenderer") { + @Override + public TableCellRenderer map() { + return ((JTable) getSource()).getCellRenderer(i, i1); + } + })); + } + + /** + * Maps {@code JTable.getCellSelectionEnabled()} through queue + */ + public boolean getCellSelectionEnabled() { + return (runMapping(new MapBooleanAction("getCellSelectionEnabled") { + @Override + public boolean map() { + return ((JTable) getSource()).getCellSelectionEnabled(); + } + })); + } + + /** + * Maps {@code JTable.getColumn(Object)} through queue + */ + public TableColumn getColumn(final Object object) { + return (runMapping(new MapAction("getColumn") { + @Override + public TableColumn map() { + return ((JTable) getSource()).getColumn(object); + } + })); + } + + /** + * Maps {@code JTable.getColumnClass(int)} through queue + */ + public Class getColumnClass(final int i) { + return (runMapping(new MapAction>("getColumnClass") { + @Override + public Class map() { + return ((JTable) getSource()).getColumnClass(i); + } + })); + } + + /** + * Maps {@code JTable.getColumnCount()} through queue + */ + public int getColumnCount() { + return (runMapping(new MapIntegerAction("getColumnCount") { + @Override + public int map() { + return ((JTable) getSource()).getColumnCount(); + } + })); + } + + /** + * Maps {@code JTable.getColumnModel()} through queue + */ + public TableColumnModel getColumnModel() { + return (runMapping(new MapAction("getColumnModel") { + @Override + public TableColumnModel map() { + return ((JTable) getSource()).getColumnModel(); + } + })); + } + + /** + * Maps {@code JTable.getColumnName(int)} through queue + */ + public String getColumnName(final int i) { + return (runMapping(new MapAction("getColumnName") { + @Override + public String map() { + return ((JTable) getSource()).getColumnName(i); + } + })); + } + + /** + * Maps {@code JTable.getColumnSelectionAllowed()} through queue + */ + public boolean getColumnSelectionAllowed() { + return (runMapping(new MapBooleanAction("getColumnSelectionAllowed") { + @Override + public boolean map() { + return ((JTable) getSource()).getColumnSelectionAllowed(); + } + })); + } + + /** + * Maps {@code JTable.getDefaultEditor(Class)} through queue + */ + public TableCellEditor getDefaultEditor(final Class clss) { + return (runMapping(new MapAction("getDefaultEditor") { + @Override + public TableCellEditor map() { + return ((JTable) getSource()).getDefaultEditor(clss); + } + })); + } + + /** + * Maps {@code JTable.getDefaultRenderer(Class)} through queue + */ + public TableCellRenderer getDefaultRenderer(final Class clss) { + return (runMapping(new MapAction("getDefaultRenderer") { + @Override + public TableCellRenderer map() { + return ((JTable) getSource()).getDefaultRenderer(clss); + } + })); + } + + /** + * Maps {@code JTable.getEditingColumn()} through queue + */ + public int getEditingColumn() { + return (runMapping(new MapIntegerAction("getEditingColumn") { + @Override + public int map() { + return ((JTable) getSource()).getEditingColumn(); + } + })); + } + + /** + * Maps {@code JTable.getEditingRow()} through queue + */ + public int getEditingRow() { + return (runMapping(new MapIntegerAction("getEditingRow") { + @Override + public int map() { + return ((JTable) getSource()).getEditingRow(); + } + })); + } + + /** + * Maps {@code JTable.getEditorComponent()} through queue + */ + public Component getEditorComponent() { + return (runMapping(new MapAction("getEditorComponent") { + @Override + public Component map() { + return ((JTable) getSource()).getEditorComponent(); + } + })); + } + + /** + * Maps {@code JTable.getGridColor()} through queue + */ + public Color getGridColor() { + return (runMapping(new MapAction("getGridColor") { + @Override + public Color map() { + return ((JTable) getSource()).getGridColor(); + } + })); + } + + /** + * Maps {@code JTable.getIntercellSpacing()} through queue + */ + public Dimension getIntercellSpacing() { + return (runMapping(new MapAction("getIntercellSpacing") { + @Override + public Dimension map() { + return ((JTable) getSource()).getIntercellSpacing(); + } + })); + } + + /** + * Maps {@code JTable.getModel()} through queue + */ + public TableModel getModel() { + return (runMapping(new MapAction("getModel") { + @Override + public TableModel map() { + return ((JTable) getSource()).getModel(); + } + })); + } + + /** + * Maps {@code JTable.getPreferredScrollableViewportSize()} through queue + */ + public Dimension getPreferredScrollableViewportSize() { + return (runMapping(new MapAction("getPreferredScrollableViewportSize") { + @Override + public Dimension map() { + return ((JTable) getSource()).getPreferredScrollableViewportSize(); + } + })); + } + + /** + * Maps {@code JTable.getRowCount()} through queue + */ + public int getRowCount() { + return (runMapping(new MapIntegerAction("getRowCount") { + @Override + public int map() { + return ((JTable) getSource()).getRowCount(); + } + })); + } + + /** + * Maps {@code JTable.getRowHeight()} through queue + */ + public int getRowHeight() { + return (runMapping(new MapIntegerAction("getRowHeight") { + @Override + public int map() { + return ((JTable) getSource()).getRowHeight(); + } + })); + } + + /** + * Maps {@code JTable.getRowMargin()} through queue + */ + public int getRowMargin() { + return (runMapping(new MapIntegerAction("getRowMargin") { + @Override + public int map() { + return ((JTable) getSource()).getRowMargin(); + } + })); + } + + /** + * Maps {@code JTable.getRowSelectionAllowed()} through queue + */ + public boolean getRowSelectionAllowed() { + return (runMapping(new MapBooleanAction("getRowSelectionAllowed") { + @Override + public boolean map() { + return ((JTable) getSource()).getRowSelectionAllowed(); + } + })); + } + + /** + * Maps {@code JTable.getScrollableBlockIncrement(Rectangle, int, int)} + * through queue + */ + public int getScrollableBlockIncrement(final Rectangle rectangle, final int i, final int i1) { + return (runMapping(new MapIntegerAction("getScrollableBlockIncrement") { + @Override + public int map() { + return ((JTable) getSource()).getScrollableBlockIncrement(rectangle, i, i1); + } + })); + } + + /** + * Maps {@code JTable.getScrollableTracksViewportHeight()} through queue + */ + public boolean getScrollableTracksViewportHeight() { + return (runMapping(new MapBooleanAction("getScrollableTracksViewportHeight") { + @Override + public boolean map() { + return ((JTable) getSource()).getScrollableTracksViewportHeight(); + } + })); + } + + /** + * Maps {@code JTable.getScrollableTracksViewportWidth()} through queue + */ + public boolean getScrollableTracksViewportWidth() { + return (runMapping(new MapBooleanAction("getScrollableTracksViewportWidth") { + @Override + public boolean map() { + return ((JTable) getSource()).getScrollableTracksViewportWidth(); + } + })); + } + + /** + * Maps {@code JTable.getScrollableUnitIncrement(Rectangle, int, int)} + * through queue + */ + public int getScrollableUnitIncrement(final Rectangle rectangle, final int i, final int i1) { + return (runMapping(new MapIntegerAction("getScrollableUnitIncrement") { + @Override + public int map() { + return ((JTable) getSource()).getScrollableUnitIncrement(rectangle, i, i1); + } + })); + } + + /** + * Maps {@code JTable.getSelectedColumn()} through queue + */ + public int getSelectedColumn() { + return (runMapping(new MapIntegerAction("getSelectedColumn") { + @Override + public int map() { + return ((JTable) getSource()).getSelectedColumn(); + } + })); + } + + /** + * Maps {@code JTable.getSelectedColumnCount()} through queue + */ + public int getSelectedColumnCount() { + return (runMapping(new MapIntegerAction("getSelectedColumnCount") { + @Override + public int map() { + return ((JTable) getSource()).getSelectedColumnCount(); + } + })); + } + + /** + * Maps {@code JTable.getSelectedColumns()} through queue + */ + public int[] getSelectedColumns() { + return ((int[]) runMapping(new MapAction("getSelectedColumns") { + @Override + public Object map() { + return ((JTable) getSource()).getSelectedColumns(); + } + })); + } + + /** + * Maps {@code JTable.getSelectedRow()} through queue + */ + public int getSelectedRow() { + return (runMapping(new MapIntegerAction("getSelectedRow") { + @Override + public int map() { + return ((JTable) getSource()).getSelectedRow(); + } + })); + } + + /** + * Maps {@code JTable.getSelectedRowCount()} through queue + */ + public int getSelectedRowCount() { + return (runMapping(new MapIntegerAction("getSelectedRowCount") { + @Override + public int map() { + return ((JTable) getSource()).getSelectedRowCount(); + } + })); + } + + /** + * Maps {@code JTable.getSelectedRows()} through queue + */ + public int[] getSelectedRows() { + return ((int[]) runMapping(new MapAction("getSelectedRows") { + @Override + public Object map() { + return ((JTable) getSource()).getSelectedRows(); + } + })); + } + + /** + * Maps {@code JTable.getSelectionBackground()} through queue + */ + public Color getSelectionBackground() { + return (runMapping(new MapAction("getSelectionBackground") { + @Override + public Color map() { + return ((JTable) getSource()).getSelectionBackground(); + } + })); + } + + /** + * Maps {@code JTable.getSelectionForeground()} through queue + */ + public Color getSelectionForeground() { + return (runMapping(new MapAction("getSelectionForeground") { + @Override + public Color map() { + return ((JTable) getSource()).getSelectionForeground(); + } + })); + } + + /** + * Maps {@code JTable.getSelectionModel()} through queue + */ + public ListSelectionModel getSelectionModel() { + return (runMapping(new MapAction("getSelectionModel") { + @Override + public ListSelectionModel map() { + return ((JTable) getSource()).getSelectionModel(); + } + })); + } + + /** + * Maps {@code JTable.getShowHorizontalLines()} through queue + */ + public boolean getShowHorizontalLines() { + return (runMapping(new MapBooleanAction("getShowHorizontalLines") { + @Override + public boolean map() { + return ((JTable) getSource()).getShowHorizontalLines(); + } + })); + } + + /** + * Maps {@code JTable.getShowVerticalLines()} through queue + */ + public boolean getShowVerticalLines() { + return (runMapping(new MapBooleanAction("getShowVerticalLines") { + @Override + public boolean map() { + return ((JTable) getSource()).getShowVerticalLines(); + } + })); + } + + /** + * Maps {@code JTable.getTableHeader()} through queue + */ + public JTableHeader getTableHeader() { + return (runMapping(new MapAction("getTableHeader") { + @Override + public JTableHeader map() { + return ((JTable) getSource()).getTableHeader(); + } + })); + } + + /** + * Maps {@code JTable.getUI()} through queue + */ + public TableUI getUI() { + return (runMapping(new MapAction("getUI") { + @Override + public TableUI map() { + return ((JTable) getSource()).getUI(); + } + })); + } + + /** + * Maps {@code JTable.getValueAt(int, int)} through queue + */ + public Object getValueAt(final int i, final int i1) { + return (runMapping(new MapAction("getValueAt") { + @Override + public Object map() { + return ((JTable) getSource()).getValueAt(i, i1); + } + })); + } + + /** + * Maps {@code JTable.isCellEditable(int, int)} through queue + */ + public boolean isCellEditable(final int i, final int i1) { + return (runMapping(new MapBooleanAction("isCellEditable") { + @Override + public boolean map() { + return ((JTable) getSource()).isCellEditable(i, i1); + } + })); + } + + /** + * Maps {@code JTable.isCellSelected(int, int)} through queue + */ + public boolean isCellSelected(final int i, final int i1) { + return (runMapping(new MapBooleanAction("isCellSelected") { + @Override + public boolean map() { + return ((JTable) getSource()).isCellSelected(i, i1); + } + })); + } + + /** + * Maps {@code JTable.isColumnSelected(int)} through queue + */ + public boolean isColumnSelected(final int i) { + return (runMapping(new MapBooleanAction("isColumnSelected") { + @Override + public boolean map() { + return ((JTable) getSource()).isColumnSelected(i); + } + })); + } + + /** + * Maps {@code JTable.isEditing()} through queue + */ + public boolean isEditing() { + return (runMapping(new MapBooleanAction("isEditing") { + @Override + public boolean map() { + return ((JTable) getSource()).isEditing(); + } + })); + } + + /** + * Maps {@code JTable.isRowSelected(int)} through queue + */ + public boolean isRowSelected(final int i) { + return (runMapping(new MapBooleanAction("isRowSelected") { + @Override + public boolean map() { + return ((JTable) getSource()).isRowSelected(i); + } + })); + } + + /** + * Maps {@code JTable.moveColumn(int, int)} through queue + */ + public void moveColumn(final int i, final int i1) { + runMapping(new MapVoidAction("moveColumn") { + @Override + public void map() { + ((JTable) getSource()).moveColumn(i, i1); + } + }); + } + + /** + * Maps {@code JTable.prepareEditor(TableCellEditor, int, int)} through queue + */ + public Component prepareEditor(final TableCellEditor tableCellEditor, final int i, final int i1) { + return (runMapping(new MapAction("prepareEditor") { + @Override + public Component map() { + return ((JTable) getSource()).prepareEditor(tableCellEditor, i, i1); + } + })); + } + + /** + * Maps {@code JTable.prepareRenderer(TableCellRenderer, int, int)} + * through queue + */ + public Component prepareRenderer(final TableCellRenderer tableCellRenderer, final int i, final int i1) { + return (runMapping(new MapAction("prepareRenderer") { + @Override + public Component map() { + return ((JTable) getSource()).prepareRenderer(tableCellRenderer, i, i1); + } + })); + } + + /** + * Maps {@code JTable.removeColumn(TableColumn)} through queue + */ + public void removeColumn(final TableColumn tableColumn) { + runMapping(new MapVoidAction("removeColumn") { + @Override + public void map() { + ((JTable) getSource()).removeColumn(tableColumn); + } + }); + } + + /** + * Maps {@code JTable.removeColumnSelectionInterval(int, int)} through queue + */ + public void removeColumnSelectionInterval(final int i, final int i1) { + runMapping(new MapVoidAction("removeColumnSelectionInterval") { + @Override + public void map() { + ((JTable) getSource()).removeColumnSelectionInterval(i, i1); + } + }); + } + + /** + * Maps {@code JTable.removeEditor()} through queue + */ + public void removeEditor() { + runMapping(new MapVoidAction("removeEditor") { + @Override + public void map() { + ((JTable) getSource()).removeEditor(); + } + }); + } + + /** + * Maps {@code JTable.removeRowSelectionInterval(int, int)} through queue + */ + public void removeRowSelectionInterval(final int i, final int i1) { + runMapping(new MapVoidAction("removeRowSelectionInterval") { + @Override + public void map() { + ((JTable) getSource()).removeRowSelectionInterval(i, i1); + } + }); + } + + /** + * Maps {@code JTable.rowAtPoint(Point)} through queue + */ + public int rowAtPoint(final Point point) { + return (runMapping(new MapIntegerAction("rowAtPoint") { + @Override + public int map() { + return ((JTable) getSource()).rowAtPoint(point); + } + })); + } + + /** + * Maps {@code JTable.selectAll()} through queue + */ + public void selectAll() { + runMapping(new MapVoidAction("selectAll") { + @Override + public void map() { + ((JTable) getSource()).selectAll(); + } + }); + } + + /** + * Maps {@code JTable.setAutoCreateColumnsFromModel(boolean)} through queue + */ + public void setAutoCreateColumnsFromModel(final boolean b) { + runMapping(new MapVoidAction("setAutoCreateColumnsFromModel") { + @Override + public void map() { + ((JTable) getSource()).setAutoCreateColumnsFromModel(b); + } + }); + } + + /** + * Maps {@code JTable.setAutoResizeMode(int)} through queue + */ + public void setAutoResizeMode(final int i) { + runMapping(new MapVoidAction("setAutoResizeMode") { + @Override + public void map() { + ((JTable) getSource()).setAutoResizeMode(i); + } + }); + } + + /** + * Maps {@code JTable.setCellEditor(TableCellEditor)} through queue + */ + public void setCellEditor(final TableCellEditor tableCellEditor) { + runMapping(new MapVoidAction("setCellEditor") { + @Override + public void map() { + ((JTable) getSource()).setCellEditor(tableCellEditor); + } + }); + } + + /** + * Maps {@code JTable.setCellSelectionEnabled(boolean)} through queue + */ + public void setCellSelectionEnabled(final boolean b) { + runMapping(new MapVoidAction("setCellSelectionEnabled") { + @Override + public void map() { + ((JTable) getSource()).setCellSelectionEnabled(b); + } + }); + } + + /** + * Maps {@code JTable.setColumnModel(TableColumnModel)} through queue + */ + public void setColumnModel(final TableColumnModel tableColumnModel) { + runMapping(new MapVoidAction("setColumnModel") { + @Override + public void map() { + ((JTable) getSource()).setColumnModel(tableColumnModel); + } + }); + } + + /** + * Maps {@code JTable.setColumnSelectionAllowed(boolean)} through queue + */ + public void setColumnSelectionAllowed(final boolean b) { + runMapping(new MapVoidAction("setColumnSelectionAllowed") { + @Override + public void map() { + ((JTable) getSource()).setColumnSelectionAllowed(b); + } + }); + } + + /** + * Maps {@code JTable.setColumnSelectionInterval(int, int)} through queue + */ + public void setColumnSelectionInterval(final int i, final int i1) { + runMapping(new MapVoidAction("setColumnSelectionInterval") { + @Override + public void map() { + ((JTable) getSource()).setColumnSelectionInterval(i, i1); + } + }); + } + + /** + * Maps {@code JTable.setDefaultEditor(Class, TableCellEditor)} through queue + */ + public void setDefaultEditor(final Class clss, final TableCellEditor tableCellEditor) { + runMapping(new MapVoidAction("setDefaultEditor") { + @Override + public void map() { + ((JTable) getSource()).setDefaultEditor(clss, tableCellEditor); + } + }); + } + + /** + * Maps {@code JTable.setDefaultRenderer(Class, TableCellRenderer)} + * through queue + */ + public void setDefaultRenderer(final Class clss, final TableCellRenderer tableCellRenderer) { + runMapping(new MapVoidAction("setDefaultRenderer") { + @Override + public void map() { + ((JTable) getSource()).setDefaultRenderer(clss, tableCellRenderer); + } + }); + } + + /** + * Maps {@code JTable.setEditingColumn(int)} through queue + */ + public void setEditingColumn(final int i) { + runMapping(new MapVoidAction("setEditingColumn") { + @Override + public void map() { + ((JTable) getSource()).setEditingColumn(i); + } + }); + } + + /** + * Maps {@code JTable.setEditingRow(int)} through queue + */ + public void setEditingRow(final int i) { + runMapping(new MapVoidAction("setEditingRow") { + @Override + public void map() { + ((JTable) getSource()).setEditingRow(i); + } + }); + } + + /** + * Maps {@code JTable.setGridColor(Color)} through queue + */ + public void setGridColor(final Color color) { + runMapping(new MapVoidAction("setGridColor") { + @Override + public void map() { + ((JTable) getSource()).setGridColor(color); + } + }); + } + + /** + * Maps {@code JTable.setIntercellSpacing(Dimension)} through queue + */ + public void setIntercellSpacing(final Dimension dimension) { + runMapping(new MapVoidAction("setIntercellSpacing") { + @Override + public void map() { + ((JTable) getSource()).setIntercellSpacing(dimension); + } + }); + } + + /** + * Maps {@code JTable.setModel(TableModel)} through queue + */ + public void setModel(final TableModel tableModel) { + runMapping(new MapVoidAction("setModel") { + @Override + public void map() { + ((JTable) getSource()).setModel(tableModel); + } + }); + } + + /** + * Maps {@code JTable.setPreferredScrollableViewportSize(Dimension)} + * through queue + */ + public void setPreferredScrollableViewportSize(final Dimension dimension) { + runMapping(new MapVoidAction("setPreferredScrollableViewportSize") { + @Override + public void map() { + ((JTable) getSource()).setPreferredScrollableViewportSize(dimension); + } + }); + } + + /** + * Maps {@code JTable.setRowHeight(int)} through queue + */ + public void setRowHeight(final int i) { + runMapping(new MapVoidAction("setRowHeight") { + @Override + public void map() { + ((JTable) getSource()).setRowHeight(i); + } + }); + } + + /** + * Maps {@code JTable.setRowMargin(int)} through queue + */ + public void setRowMargin(final int i) { + runMapping(new MapVoidAction("setRowMargin") { + @Override + public void map() { + ((JTable) getSource()).setRowMargin(i); + } + }); + } + + /** + * Maps {@code JTable.setRowSelectionAllowed(boolean)} through queue + */ + public void setRowSelectionAllowed(final boolean b) { + runMapping(new MapVoidAction("setRowSelectionAllowed") { + @Override + public void map() { + ((JTable) getSource()).setRowSelectionAllowed(b); + } + }); + } + + /** + * Maps {@code JTable.setRowSelectionInterval(int, int)} through queue + */ + public void setRowSelectionInterval(final int i, final int i1) { + runMapping(new MapVoidAction("setRowSelectionInterval") { + @Override + public void map() { + ((JTable) getSource()).setRowSelectionInterval(i, i1); + } + }); + } + + /** + * Maps {@code JTable.setSelectionBackground(Color)} through queue + */ + public void setSelectionBackground(final Color color) { + runMapping(new MapVoidAction("setSelectionBackground") { + @Override + public void map() { + ((JTable) getSource()).setSelectionBackground(color); + } + }); + } + + /** + * Maps {@code JTable.setSelectionForeground(Color)} through queue + */ + public void setSelectionForeground(final Color color) { + runMapping(new MapVoidAction("setSelectionForeground") { + @Override + public void map() { + ((JTable) getSource()).setSelectionForeground(color); + } + }); + } + + /** + * Maps {@code JTable.setSelectionMode(int)} through queue + */ + public void setSelectionMode(final int i) { + runMapping(new MapVoidAction("setSelectionMode") { + @Override + public void map() { + ((JTable) getSource()).setSelectionMode(i); + } + }); + } + + /** + * Maps {@code JTable.setSelectionModel(ListSelectionModel)} through queue + */ + public void setSelectionModel(final ListSelectionModel listSelectionModel) { + runMapping(new MapVoidAction("setSelectionModel") { + @Override + public void map() { + ((JTable) getSource()).setSelectionModel(listSelectionModel); + } + }); + } + + /** + * Maps {@code JTable.setShowGrid(boolean)} through queue + */ + public void setShowGrid(final boolean b) { + runMapping(new MapVoidAction("setShowGrid") { + @Override + public void map() { + ((JTable) getSource()).setShowGrid(b); + } + }); + } + + /** + * Maps {@code JTable.setShowHorizontalLines(boolean)} through queue + */ + public void setShowHorizontalLines(final boolean b) { + runMapping(new MapVoidAction("setShowHorizontalLines") { + @Override + public void map() { + ((JTable) getSource()).setShowHorizontalLines(b); + } + }); + } + + /** + * Maps {@code JTable.setShowVerticalLines(boolean)} through queue + */ + public void setShowVerticalLines(final boolean b) { + runMapping(new MapVoidAction("setShowVerticalLines") { + @Override + public void map() { + ((JTable) getSource()).setShowVerticalLines(b); + } + }); + } + + /** + * Maps {@code JTable.setTableHeader(JTableHeader)} through queue + */ + public void setTableHeader(final JTableHeader jTableHeader) { + runMapping(new MapVoidAction("setTableHeader") { + @Override + public void map() { + ((JTable) getSource()).setTableHeader(jTableHeader); + } + }); + } + + /** + * Maps {@code JTable.setUI(TableUI)} through queue + */ + public void setUI(final TableUI tableUI) { + runMapping(new MapVoidAction("setUI") { + @Override + public void map() { + ((JTable) getSource()).setUI(tableUI); + } + }); + } + + /** + * Maps {@code JTable.setValueAt(Object, int, int)} through queue + */ + public void setValueAt(final Object object, final int i, final int i1) { + runMapping(new MapVoidAction("setValueAt") { + @Override + public void map() { + ((JTable) getSource()).setValueAt(object, i, i1); + } + }); + } + + /** + * Maps {@code JTable.tableChanged(TableModelEvent)} through queue + */ + public void tableChanged(final TableModelEvent tableModelEvent) { + runMapping(new MapVoidAction("tableChanged") { + @Override + public void map() { + ((JTable) getSource()).tableChanged(tableModelEvent); + } + }); + } + + /** + * Maps {@code JTable.valueChanged(ListSelectionEvent)} through queue + */ + public void valueChanged(final ListSelectionEvent listSelectionEvent) { + runMapping(new MapVoidAction("valueChanged") { + @Override + public void map() { + ((JTable) getSource()).valueChanged(listSelectionEvent); + } + }); + } + + //End of mapping // + //////////////////////////////////////////////////////// + private Point findCell(String text, boolean ce, boolean ccs, int index) { + return findCell(text, new DefaultStringComparator(ce, ccs), index); + } + + /** + * Iterface to choose table cell. + */ + public interface TableCellChooser { + + /** + * Should be true if item is good. + * + * @param oper Operator used to search item. + * @param row Row be checked. + * @param column Column be checked. + * @return true if cell fits the criteria + */ + public boolean checkCell(JTableOperator oper, int row, int column); + + /** + * Item description. + * + * @return the description. + */ + public String getDescription(); + } + + private static class BySubStringTableCellChooser implements TableCellChooser { + + String subString; + StringComparator comparator; + + public BySubStringTableCellChooser(String subString, StringComparator comparator) { + this.subString = subString; + this.comparator = comparator; + } + + @Override + public boolean checkCell(JTableOperator oper, int row, int column) { + Object value = ((JTable) oper.getSource()).getModel().getValueAt(row, column); + return (comparator.equals((value != null) + ? value.toString() + : null, + subString)); + } + + @Override + public String getDescription() { + return "Cell containing \"" + subString + "\" string"; + } + + @Override + public String toString() { + return "BySubStringTableCellChooser{" + "subString=" + subString + ", comparator=" + comparator + '}'; + } + } + + private static class ByRenderedComponentTableCellChooser implements TableCellChooser { + + ComponentChooser chooser; + + public ByRenderedComponentTableCellChooser(ComponentChooser chooser) { + this.chooser = chooser; + } + + @Override + public boolean checkCell(JTableOperator oper, int row, int column) { + return chooser.checkComponent(oper.getRenderedComponent(row, column)); + } + + @Override + public String getDescription() { + return chooser.getDescription(); + } + + @Override + public String toString() { + return "ByRenderedComponentTableCellChooser{" + "chooser=" + chooser + '}'; + } + } + + /** + * Allows to find component by cell text. + */ + public static class JTableByCellFinder implements ComponentChooser { + + String label; + int row; + int column; + StringComparator comparator; + + /** + * Constructs JTableByCellFinder. + * + * @param lb a text pattern + * @param r a row index to look in. If equal to -1, selected row is + * checked. + * @param c a column index to look in. If equal to -1, selected column + * is checked. + * @param comparator specifies string comparision algorithm. + */ + public JTableByCellFinder(String lb, int r, int c, StringComparator comparator) { + label = lb; + row = r; + column = c; + this.comparator = comparator; + } + + /** + * Constructs JTableByCellFinder. + * + * @param lb a text pattern + * @param r a row index to look in. If equal to -1, selected row is + * checked. + * @param c a column index to look in. If equal to -1, selected column + * is checked. + */ + public JTableByCellFinder(String lb, int r, int c) { + this(lb, r, c, Operator.getDefaultStringComparator()); + } + + @Override + public boolean checkComponent(Component comp) { + if (comp instanceof JTable) { + if (label == null) { + return true; + } + if (((JTable) comp).getRowCount() > row && ((JTable) comp).getColumnCount() > column) { + int r = row; + if (r == -1) { + int[] rows = ((JTable) comp).getSelectedRows(); + if (rows.length != 0) { + r = rows[0]; + } else { + return false; + } + } + int c = column; + if (c == -1) { + int[] columns = ((JTable) comp).getSelectedColumns(); + if (columns.length != 0) { + c = columns[0]; + } else { + return false; + } + } + Object value = ((JTable) comp).getValueAt(r, c); + if (value == null) { + return false; + } + return (comparator.equals(value.toString(), + label)); + } + } + return false; + } + + @Override + public String getDescription() { + return ("JTable with text \"" + label + "\" in (" + + row + ", " + + column + ") cell"); + } + + @Override + public String toString() { + return "JTableByCellFinder{" + "label=" + label + ", row=" + row + ", column=" + column + ", comparator=" + comparator + '}'; + } + } + + /** + * Checks component type. + */ + public static class JTableFinder extends Finder { + + /** + * Constructs JTableFinder. + * + * @param sf other searching criteria. + */ + public JTableFinder(ComponentChooser sf) { + super(JTable.class, sf); + } + + /** + * Constructs JTableFinder. + */ + public JTableFinder() { + super(JTable.class); + } + } + + private class CellComponentWaiter extends Waiter { + + private ComponentChooser chooser; + private int row, column; + + public CellComponentWaiter(ComponentChooser chooser, int row, int column) { + this.chooser = chooser; + this.row = row; + this.column = column; + } + + @Override + public Component actionProduced(Void obj) { + Point pnt = getPointToClick(row, column); + Component comp = getComponentAt(pnt.x, pnt.y); + if (comp != null + && chooser.checkComponent(comp)) { + return comp; + } else { + return null; + } + } + + @Override + public String getDescription() { + return chooser.getDescription(); + } + + @Override + public String toString() { + return "CellComponentWaiter{" + "chooser=" + chooser + ", row=" + row + ", column=" + column + '}'; + } + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JTextAreaOperator.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JTextAreaOperator.java new file mode 100644 index 00000000000..dae8f4c8da7 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JTextAreaOperator.java @@ -0,0 +1,658 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.operators; + +import java.awt.Container; +import java.util.Hashtable; + +import javax.swing.JTextArea; +import javax.swing.text.BadLocationException; + +import org.netbeans.jemmy.ComponentChooser; +import org.netbeans.jemmy.JemmyException; +import org.netbeans.jemmy.Outputable; +import org.netbeans.jemmy.TestOut; +import org.netbeans.jemmy.TimeoutExpiredException; +import org.netbeans.jemmy.Timeoutable; +import org.netbeans.jemmy.Timeouts; + +/** + * + * Class provides basic functions to operate with JTextArea (selection, typing, + * deleting) + * + *

Timeouts used:
+ * JTextComponentOperator.PushKeyTimeout - time between key pressing and + * releasing during text typing
+ * JTextComponentOperator.BetweenKeysTimeout - time to sleep between two chars + * typing
+ * JTextComponentOperator.ChangeCaretPositionTimeout - maximum time to chenge + * caret position
+ * JTextComponentOperator.TypeTextTimeout - maximum time to type text
+ * ComponentOperator.WaitComponentTimeout - time to wait component displayed + *
+ * ComponentOperator.WaitFocusTimeout - time to wait component focus
+ * JScrollBarOperator.OneScrollClickTimeout - time for one scroll click
+ * JScrollBarOperator.WholeScrollTimeout - time for the whole scrolling
. + * + * @see org.netbeans.jemmy.Timeouts + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + */ +public class JTextAreaOperator extends JTextComponentOperator + implements Timeoutable, Outputable { + + /** + * Identifier for a "column count" property. + * + * @see #getDump + */ + public static final String COLUMN_COUNT_DPROP = "Column count"; + + /** + * Identifier for a "row count" property. + * + * @see #getDump + */ + public static final String ROW_COUNT_DPROP = "Row count"; + + private Timeouts timeouts; + private TestOut output; + + /** + * Constructor. + * + * @param b a component + */ + public JTextAreaOperator(JTextArea b) { + super(b); + } + + /** + * Constructs a JTextAreaOperator object. + * + * @param cont a container + * @param chooser a component chooser specifying searching criteria. + * @param index an index between appropriate ones. + */ + public JTextAreaOperator(ContainerOperator cont, ComponentChooser chooser, int index) { + this((JTextArea) cont. + waitSubComponent(new JTextAreaFinder(chooser), + index)); + copyEnvironment(cont); + } + + /** + * Constructs a JTextAreaOperator object. + * + * @param cont a container + * @param chooser a component chooser specifying searching criteria. + */ + public JTextAreaOperator(ContainerOperator cont, ComponentChooser chooser) { + this(cont, chooser, 0); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont a container + * @param text Button text. + * @param index Ordinal component index. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public JTextAreaOperator(ContainerOperator cont, String text, int index) { + this((JTextArea) waitComponent(cont, + new JTextAreaFinder(new JTextComponentOperator.JTextComponentByTextFinder(text, + cont.getComparator())), + index)); + copyEnvironment(cont); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont a container + * @param text Button text. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public JTextAreaOperator(ContainerOperator cont, String text) { + this(cont, text, 0); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont a container + * @param index Ordinal component index. + * @throws TimeoutExpiredException + */ + public JTextAreaOperator(ContainerOperator cont, int index) { + this((JTextArea) waitComponent(cont, + new JTextAreaFinder(), + index)); + copyEnvironment(cont); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont a container + * @throws TimeoutExpiredException + */ + public JTextAreaOperator(ContainerOperator cont) { + this(cont, 0); + } + + /** + * Searches JTextArea in container. + * + * @param cont Container to search component in. + * @param chooser a component chooser specifying searching criteria. + * @param index Ordinal component index. + * @return JTextArea instance or null if component was not found. + */ + public static JTextArea findJTextArea(Container cont, ComponentChooser chooser, int index) { + return (JTextArea) findJTextComponent(cont, new JTextAreaFinder(chooser), index); + } + + /** + * Searches JTextArea in container. + * + * @param cont Container to search component in. + * @param chooser a component chooser specifying searching criteria. + * @return JTextArea instance or null if component was not found. + */ + public static JTextArea findJTextArea(Container cont, ComponentChooser chooser) { + return findJTextArea(cont, chooser, 0); + } + + /** + * Searches JTextArea by text. + * + * @param cont Container to search component in. + * @param text Component text. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @param index Ordinal component index. + * @return JTextArea instance or null if component was not found. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + */ + public static JTextArea findJTextArea(Container cont, String text, boolean ce, boolean ccs, int index) { + return (findJTextArea(cont, + new JTextAreaFinder(new JTextComponentOperator.JTextComponentByTextFinder(text, + new DefaultStringComparator(ce, ccs))), + index)); + } + + /** + * Searches JTextArea by text. + * + * @param cont Container to search component in. + * @param text Component text. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @return JTextArea instance or null if component was not found. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + */ + public static JTextArea findJTextArea(Container cont, String text, boolean ce, boolean ccs) { + return findJTextArea(cont, text, ce, ccs, 0); + } + + /** + * Waits JTextArea in container. + * + * @param cont Container to search component in. + * @param chooser a component chooser specifying searching criteria. + * @param index Ordinal component index. + * @return JTextArea instance. + * @throws TimeoutExpiredException + */ + public static JTextArea waitJTextArea(Container cont, ComponentChooser chooser, int index) { + return (JTextArea) waitJTextComponent(cont, new JTextAreaFinder(chooser), index); + } + + /** + * Waits JTextArea in container. + * + * @param cont Container to search component in. + * @param chooser a component chooser specifying searching criteria. + * @return JTextArea instance. + * @throws TimeoutExpiredException + */ + public static JTextArea waitJTextArea(Container cont, ComponentChooser chooser) { + return waitJTextArea(cont, chooser, 0); + } + + /** + * Waits JTextArea by text. + * + * @param cont Container to search component in. + * @param text Component text. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @param index Ordinal component index. + * @return JTextArea instance. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public static JTextArea waitJTextArea(Container cont, String text, boolean ce, boolean ccs, int index) { + return (waitJTextArea(cont, + new JTextAreaFinder(new JTextComponentOperator.JTextComponentByTextFinder(text, + new DefaultStringComparator(ce, ccs))), + index)); + } + + /** + * Waits JTextArea by text. + * + * @param cont Container to search component in. + * @param text Component text. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @return JTextArea instance. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public static JTextArea waitJTextArea(Container cont, String text, boolean ce, boolean ccs) { + return waitJTextArea(cont, text, ce, ccs, 0); + } + + @Override + public void setTimeouts(Timeouts times) { + timeouts = times; + super.setTimeouts(timeouts); + } + + @Override + public Timeouts getTimeouts() { + return timeouts; + } + + @Override + public void setOutput(TestOut out) { + output = out; + super.setOutput(output.createErrorOutput()); + } + + @Override + public TestOut getOutput() { + return output; + } + + /** + * Notifies whether "PageUp" and "PageDown" should be used to change caret + * position. If can be useful if text takes some pages. + * + * @param yesOrNo if page navigation keys need to be used. + * @deprecated All text operations are performed by TextDriver regitered for + * this operator type. + */ + @Deprecated + public void usePageNavigationKeys(boolean yesOrNo) { + } + + /** + * Moves caret to line. + * + * @param row Line to move caret to. + * @see JTextComponentOperator#changeCaretPosition(int) + * @see #changeCaretPosition(int) + * @see #changeCaretPosition(int, int) + * @throws TimeoutExpiredException + */ + public void changeCaretRow(int row) { + changeCaretPosition(row, getCaretPosition() + - getLineStartOffset(getLineOfOffset(getCaretPosition()))); + } + + /** + * Moves caret. + * + * @param row Line to move caret to. + * @param column Column to move caret to. + * @see JTextComponentOperator#changeCaretPosition(int) + * @see #changeCaretRow(int) + * @see #changeCaretPosition(int, int) + * @throws TimeoutExpiredException + */ + public void changeCaretPosition(int row, int column) { + int startOffset = getLineStartOffset(row); + int endOffset = getLineEndOffset(row); + super.changeCaretPosition(getLineStartOffset(row) + + ((column <= (endOffset - startOffset)) + ? column + : (endOffset - startOffset))); + } + + /** + * Types text. + * + * @param text Text to be typed. + * @param row Line to type text in. + * @param column Column to type text from. + * @see JTextComponentOperator#typeText(String, int) + * @throws TimeoutExpiredException + */ + public void typeText(String text, int row, int column) { + if (!hasFocus()) { + makeComponentVisible(); + } + changeCaretPosition(row, column); + typeText(text); + } + + /** + * Select a part of text. + * + * @param startRow Start position row. + * @param startColumn Start position column. + * @param endRow End position row. + * @param endColumn End position column. + * @see JTextComponentOperator#selectText(int, int) + * @see #selectLines(int, int) + * @throws TimeoutExpiredException + */ + public void selectText(int startRow, int startColumn, + int endRow, int endColumn) { + int startPos = 0; + try { + startPos = getLineStartOffset(startRow) + startColumn; + } catch (JemmyException e) { + if (!(e.getInnerThrowable() instanceof BadLocationException)) { + throw (e); + } + } + int endPos = getText().length(); + try { + endPos = getLineStartOffset(endRow) + endColumn; + } catch (JemmyException e) { + if (!(e.getInnerThrowable() instanceof BadLocationException)) { + throw (e); + } + } + selectText(startPos, endPos); + } + + /** + * Select some text lines. + * + * @param startLine start selection + * @param endLine end selection + * @see JTextComponentOperator#selectText(int, int) + * @see #selectText(int, int, int, int) + * @throws TimeoutExpiredException + */ + public void selectLines(int startLine, int endLine) { + if (!hasFocus()) { + makeComponentVisible(); + } + selectText(startLine, 0, endLine + 1, 0); + } + + /** + * Returns information about component. + */ + @Override + public Hashtable getDump() { + Hashtable result = super.getDump(); + result.put(COLUMN_COUNT_DPROP, Integer.toString(((JTextArea) getSource()).getRows())); + result.put(ROW_COUNT_DPROP, Integer.toString(((JTextArea) getSource()).getColumns())); + return result; + } + + //////////////////////////////////////////////////////// + //Mapping // + /** + * Maps {@code JTextArea.append(String)} through queue + */ + public void append(final String string) { + runMapping(new MapVoidAction("append") { + @Override + public void map() { + ((JTextArea) getSource()).append(string); + } + }); + } + + /** + * Maps {@code JTextArea.getColumns()} through queue + */ + public int getColumns() { + return (runMapping(new MapIntegerAction("getColumns") { + @Override + public int map() { + return ((JTextArea) getSource()).getColumns(); + } + })); + } + + /** + * Maps {@code JTextArea.getLineCount()} through queue + */ + public int getLineCount() { + return (runMapping(new MapIntegerAction("getLineCount") { + @Override + public int map() { + return ((JTextArea) getSource()).getLineCount(); + } + })); + } + + /** + * Maps {@code JTextArea.getLineEndOffset(int)} through queue + */ + public int getLineEndOffset(final int i) { + return (runMapping(new MapIntegerAction("getLineEndOffset") { + @Override + public int map() throws BadLocationException { + return ((JTextArea) getSource()).getLineEndOffset(i); + } + })); + } + + /** + * Maps {@code JTextArea.getLineOfOffset(int)} through queue + */ + public int getLineOfOffset(final int i) { + return (runMapping(new MapIntegerAction("getLineOfOffset") { + @Override + public int map() throws BadLocationException { + return ((JTextArea) getSource()).getLineOfOffset(i); + } + })); + } + + /** + * Maps {@code JTextArea.getLineStartOffset(int)} through queue + */ + public int getLineStartOffset(final int i) { + return (runMapping(new MapIntegerAction("getLineStartOffset") { + @Override + public int map() throws BadLocationException { + return ((JTextArea) getSource()).getLineStartOffset(i); + } + })); + } + + /** + * Maps {@code JTextArea.getLineWrap()} through queue + */ + public boolean getLineWrap() { + return (runMapping(new MapBooleanAction("getLineWrap") { + @Override + public boolean map() { + return ((JTextArea) getSource()).getLineWrap(); + } + })); + } + + /** + * Maps {@code JTextArea.getRows()} through queue + */ + public int getRows() { + return (runMapping(new MapIntegerAction("getRows") { + @Override + public int map() { + return ((JTextArea) getSource()).getRows(); + } + })); + } + + /** + * Maps {@code JTextArea.getTabSize()} through queue + */ + public int getTabSize() { + return (runMapping(new MapIntegerAction("getTabSize") { + @Override + public int map() { + return ((JTextArea) getSource()).getTabSize(); + } + })); + } + + /** + * Maps {@code JTextArea.getWrapStyleWord()} through queue + */ + public boolean getWrapStyleWord() { + return (runMapping(new MapBooleanAction("getWrapStyleWord") { + @Override + public boolean map() { + return ((JTextArea) getSource()).getWrapStyleWord(); + } + })); + } + + /** + * Maps {@code JTextArea.insert(String, int)} through queue + */ + public void insert(final String string, final int i) { + runMapping(new MapVoidAction("insert") { + @Override + public void map() { + ((JTextArea) getSource()).insert(string, i); + } + }); + } + + /** + * Maps {@code JTextArea.replaceRange(String, int, int)} through queue + */ + public void replaceRange(final String string, final int i, final int i1) { + runMapping(new MapVoidAction("replaceRange") { + @Override + public void map() { + ((JTextArea) getSource()).replaceRange(string, i, i1); + } + }); + } + + /** + * Maps {@code JTextArea.setColumns(int)} through queue + */ + public void setColumns(final int i) { + runMapping(new MapVoidAction("setColumns") { + @Override + public void map() { + ((JTextArea) getSource()).setColumns(i); + } + }); + } + + /** + * Maps {@code JTextArea.setLineWrap(boolean)} through queue + */ + public void setLineWrap(final boolean b) { + runMapping(new MapVoidAction("setLineWrap") { + @Override + public void map() { + ((JTextArea) getSource()).setLineWrap(b); + } + }); + } + + /** + * Maps {@code JTextArea.setRows(int)} through queue + */ + public void setRows(final int i) { + runMapping(new MapVoidAction("setRows") { + @Override + public void map() { + ((JTextArea) getSource()).setRows(i); + } + }); + } + + /** + * Maps {@code JTextArea.setTabSize(int)} through queue + */ + public void setTabSize(final int i) { + runMapping(new MapVoidAction("setTabSize") { + @Override + public void map() { + ((JTextArea) getSource()).setTabSize(i); + } + }); + } + + /** + * Maps {@code JTextArea.setWrapStyleWord(boolean)} through queue + */ + public void setWrapStyleWord(final boolean b) { + runMapping(new MapVoidAction("setWrapStyleWord") { + @Override + public void map() { + ((JTextArea) getSource()).setWrapStyleWord(b); + } + }); + } + + //End of mapping // + //////////////////////////////////////////////////////// + /** + * Checks component type. + */ + public static class JTextAreaFinder extends Finder { + + /** + * Constructs JTextAreaFinder. + * + * @param sf other searching criteria. + */ + public JTextAreaFinder(ComponentChooser sf) { + super(JTextArea.class, sf); + } + + /** + * Constructs JTextAreaFinder. + */ + public JTextAreaFinder() { + super(JTextArea.class); + } + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JTextComponentOperator.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JTextComponentOperator.java new file mode 100644 index 00000000000..1b2c6562eef --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JTextComponentOperator.java @@ -0,0 +1,1599 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.operators; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.Insets; +import java.awt.Point; +import java.awt.Rectangle; +import java.io.IOException; +import java.io.Reader; +import java.io.Writer; +import java.util.Hashtable; + +import javax.swing.JScrollPane; +import javax.swing.event.CaretListener; +import javax.swing.plaf.TextUI; +import javax.swing.text.BadLocationException; +import javax.swing.text.Caret; +import javax.swing.text.Document; +import javax.swing.text.Highlighter; +import javax.swing.text.JTextComponent; +import javax.swing.text.Keymap; + +import org.netbeans.jemmy.Action; +import org.netbeans.jemmy.ComponentChooser; +import org.netbeans.jemmy.ComponentSearcher; +import org.netbeans.jemmy.JemmyException; +import org.netbeans.jemmy.JemmyInputException; +import org.netbeans.jemmy.Outputable; +import org.netbeans.jemmy.TestOut; +import org.netbeans.jemmy.TimeoutExpiredException; +import org.netbeans.jemmy.Timeoutable; +import org.netbeans.jemmy.Timeouts; +import org.netbeans.jemmy.drivers.DriverManager; +import org.netbeans.jemmy.drivers.TextDriver; +import org.netbeans.jemmy.util.EmptyVisualizer; + +/** + * + * Class provides basic functions to operate with JTextComponent (selection, + * typing, deleting) + * + *

Timeouts used:
+ * JTextComponentOperator.PushKeyTimeout - time between key pressing and + * releasing during text typing
+ * JTextComponentOperator.BetweenKeysTimeout - time to sleep between two chars + * typing
+ * JTextComponentOperator.ChangeCaretPositionTimeout - maximum time to chenge + * caret position
+ * JTextComponentOperator.TypeTextTimeout - maximum time to type text
+ * ComponentOperator.WaitComponentTimeout - time to wait component displayed + *
+ * ComponentOperator.WaitFocusTimeout - time to wait component focus
+ * ComponentOperator.WaitStateTimeout - time to wait for text
+ * JScrollBarOperator.OneScrollClickTimeout - time for one scroll click
+ * JScrollBarOperator.WholeScrollTimeout - time for the whole scrolling
. + * + * @see org.netbeans.jemmy.Timeouts + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + */ +public class JTextComponentOperator extends JComponentOperator + implements Timeoutable, Outputable { + + /** + * Identifier for a "text" property. + * + * @see #getDump + */ + public static final String TEXT_DPROP = "Text"; + + /** + * Identifier for a "selected text" property. + * + * @see #getDump + */ + public static final String SELECTED_TEXT_DPROP = "Selected text"; + + /** + * Identifier for a "editable" property. + * + * @see #getDump + */ + public static final String IS_EDITABLE_DPROP = "Editable"; + + private final static long PUSH_KEY_TIMEOUT = 0; + private final static long BETWEEN_KEYS_TIMEOUT = 0; + private final static long CHANGE_CARET_POSITION_TIMEOUT = 60000; + private final static long TYPE_TEXT_TIMEOUT = 60000; + + private Timeouts timeouts; + private TestOut output; + + /** + * Notifies what modifiers are pressed. + * + * @deprecated All text operations are performed by TextDriver regitered for + * this operator type. + */ + @Deprecated + protected int modifiersPressed = 0; + + private TextDriver driver; + + /** + * Constructor. + * + * @param b Component to operate with. + */ + public JTextComponentOperator(JTextComponent b) { + super(b); + driver = DriverManager.getTextDriver(getClass()); + } + + /** + * Constructs a JTextComponentOperator object. + * + * @param cont a container + * @param chooser a component chooser specifying searching criteria. + * @param index an index between appropriate ones. + */ + public JTextComponentOperator(ContainerOperator cont, ComponentChooser chooser, int index) { + this((JTextComponent) cont. + waitSubComponent(new JTextComponentFinder(chooser), + index)); + copyEnvironment(cont); + } + + /** + * Constructs a JTextComponentOperator object. + * + * @param cont a container + * @param chooser a component chooser specifying searching criteria. + */ + public JTextComponentOperator(ContainerOperator cont, ComponentChooser chooser) { + this(cont, chooser, 0); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont a container + * @param text Button text. + * @param index Ordinal component index. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public JTextComponentOperator(ContainerOperator cont, String text, int index) { + this((JTextComponent) waitComponent(cont, + new JTextComponentByTextFinder(text, + cont.getComparator()), + index)); + copyEnvironment(cont); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont a container + * @param text Button text. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public JTextComponentOperator(ContainerOperator cont, String text) { + this(cont, text, 0); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont a container + * @param index Ordinal component index. + * @throws TimeoutExpiredException + */ + public JTextComponentOperator(ContainerOperator cont, int index) { + this((JTextComponent) waitComponent(cont, + new JTextComponentFinder(), + index)); + copyEnvironment(cont); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont a container + * @throws TimeoutExpiredException + */ + public JTextComponentOperator(ContainerOperator cont) { + this(cont, 0); + } + + static { + Timeouts.initDefault("JTextComponentOperator.PushKeyTimeout", PUSH_KEY_TIMEOUT); + Timeouts.initDefault("JTextComponentOperator.BetweenKeysTimeout", BETWEEN_KEYS_TIMEOUT); + Timeouts.initDefault("JTextComponentOperator.ChangeCaretPositionTimeout", CHANGE_CARET_POSITION_TIMEOUT); + Timeouts.initDefault("JTextComponentOperator.TypeTextTimeout", TYPE_TEXT_TIMEOUT); + } + + /** + * Searches JTextComponent in container. + * + * @param cont Container to search component in. + * @param chooser a component chooser specifying searching criteria. + * @param index Ordinal component index. + * @return JTextComponent instance or null if component was not found. + */ + public static JTextComponent findJTextComponent(Container cont, ComponentChooser chooser, int index) { + return (JTextComponent) findComponent(cont, new JTextComponentFinder(chooser), index); + } + + /** + * Searches JTextComponent in container. + * + * @param cont Container to search component in. + * @param chooser a component chooser specifying searching criteria. + * @return JTextComponent instance or null if component was not found. + */ + public static JTextComponent findJTextComponent(Container cont, ComponentChooser chooser) { + return findJTextComponent(cont, chooser, 0); + } + + /** + * Searches JTextComponent by text. + * + * @param cont Container to search component in. + * @param text Component text. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @param index Ordinal component index. + * @return JTextComponent instance or null if component was not found. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + */ + public static JTextComponent findJTextComponent(Container cont, String text, boolean ce, boolean ccs, int index) { + return findJTextComponent(cont, new JTextComponentByTextFinder(text, new DefaultStringComparator(ce, ccs)), index); + } + + /** + * Searches JTextComponent by text. + * + * @param cont Container to search component in. + * @param text Component text. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @return JTextComponent instance or null if component was not found. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + */ + public static JTextComponent findJTextComponent(Container cont, String text, boolean ce, boolean ccs) { + return findJTextComponent(cont, text, ce, ccs, 0); + } + + /** + * Waits JTextComponent in container. + * + * @param cont Container to search component in. + * @param chooser a component chooser specifying searching criteria. + * @param index Ordinal component index. + * @return JTextComponent instance. + * @throws TimeoutExpiredException + */ + public static JTextComponent waitJTextComponent(final Container cont, final ComponentChooser chooser, final int index) { + return (JTextComponent) waitComponent(cont, new JTextComponentFinder(chooser), index); + } + + /** + * Waits JTextComponent in container. + * + * @param cont Container to search component in. + * @param chooser a component chooser specifying searching criteria. + * @return JTextComponent instance. + * @throws TimeoutExpiredException + */ + public static JTextComponent waitJTextComponent(Container cont, ComponentChooser chooser) { + return waitJTextComponent(cont, chooser, 0); + } + + /** + * Waits JTextComponent by text. + * + * @param cont Container to search component in. + * @param text Component text. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @param index Ordinal component index. + * @return JTextComponent instance. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public static JTextComponent waitJTextComponent(Container cont, String text, boolean ce, boolean ccs, int index) { + return waitJTextComponent(cont, new JTextComponentByTextFinder(text, new DefaultStringComparator(ce, ccs)), index); + } + + /** + * Waits JTextComponent by text. + * + * @param cont Container to search component in. + * @param text Component text. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @return JTextComponent instance. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public static JTextComponent waitJTextComponent(Container cont, String text, boolean ce, boolean ccs) { + return waitJTextComponent(cont, text, ce, ccs, 0); + } + + @Override + public void setTimeouts(Timeouts times) { + timeouts = times; + timeouts.setTimeout("ComponentOperator.PushKeyTimeout", + timeouts.getTimeout("JTextComponentOperator.PushKeyTimeout")); + super.setTimeouts(timeouts); + } + + @Override + public Timeouts getTimeouts() { + return timeouts; + } + + @Override + public void setOutput(TestOut out) { + output = out; + super.setOutput(output.createErrorOutput()); + } + + @Override + public TestOut getOutput() { + return output; + } + + @Override + public void copyEnvironment(Operator anotherOperator) { + super.copyEnvironment(anotherOperator); + driver + = (TextDriver) DriverManager. + getDriver(DriverManager.TEXT_DRIVER_ID, + getClass(), + anotherOperator.getProperties()); + } + + /** + * Finds start text position. + * + * @param text Text to be searched. + * @param tChooser Additional search criteria. + * @param index Index of text instance (first instance has index 0) + * @return Caret position correspondent to text start. + * @see JTextComponentOperator.TextChooser + */ + public int getPositionByText(String text, TextChooser tChooser, int index) { + output.printLine("Find " + tChooser.getDescription() + "\"" + text + + "\" text in text component\n : " + + toStringSource()); + output.printGolden("Find " + tChooser.getDescription() + "\"" + text + + "\" text in text component"); + String allText = getDisplayedText(); + Document doc = getDocument(); + int position = 0; + int ind = 0; + while ((position = allText.indexOf(text, position)) >= 0) { + if (tChooser.checkPosition(doc, position)) { + if (ind == index) { + return position; + } else { + ind++; + } + } + position = position + text.length(); + } + return -1; + } + + /** + * Finds start text position. + * + * @param text Text to be searched. + * @param tChooser Additional search criteria. + * @return Caret position correspondent to text start. + */ + public int getPositionByText(String text, TextChooser tChooser) { + return getPositionByText(text, tChooser, 0); + } + + /** + * Finds start text position. + * + * @param text Text to be searched. + * @param index Index of text instance (first instance has index 0) + * @return Caret position correspondent to text start. + */ + public int getPositionByText(String text, int index) { + return (getPositionByText(text, new TextChooser() { + @Override + public boolean checkPosition(Document doc, int offset) { + return true; + } + + @Override + public String getDescription() { + return "any"; + } + + @Override + public String toString() { + return "JTextComponentOperator.getPositionByText.TextChooser{description = " + getDescription() + '}'; + } + }, index)); + } + + /** + * Finds start text position. + * + * @param text Text to be searched. + * @return Caret position correspondent to text start. + */ + public int getPositionByText(String text) { + return getPositionByText(text, 0); + } + + /** + * Requests a focus, clears text, types new one and pushes Enter. + * + * @param text New text value. Shouldn't include final '\n'. + * @throws TimeoutExpiredException + */ + public void enterText(final String text) { + makeComponentVisible(); + produceTimeRestricted(new Action() { + @Override + public Void launch(Void obj) { + driver.enterText(JTextComponentOperator.this, text); + return null; + } + + @Override + public String getDescription() { + return "Text entering"; + } + + @Override + public String toString() { + return "JTextComponentOperator.enterText.Action{description = " + getDescription() + '}'; + } + }, "JTextComponentOperator.TypeTextTimeout"); + } + + /** + * Changes caret position. + * + * @param position Position to move caret to. + * @see #changeCaretPosition(String, int, boolean) + * @throws TimeoutExpiredException + */ + public void changeCaretPosition(final int position) { + output.printLine("Change caret position to " + Integer.toString(position)); + makeComponentVisible(); + produceTimeRestricted(new Action() { + @Override + public Void launch(Void obj) { + driver.changeCaretPosition(JTextComponentOperator.this, position); + return null; + } + + @Override + public String getDescription() { + return "Caret moving"; + } + + @Override + public String toString() { + return "JTextComponentOperator.changeCaretPosition.Action{description = " + getDescription() + '}'; + } + }, "JTextComponentOperator.ChangeCaretPositionTimeout"); + if (getVerification()) { + waitCaretPosition(position); + } + } + + /** + * Puts caret before or after text. + * + * @param text Text to be searched. + * @param index Index of text instance (first instance has index 0) + * @param before If true put caret before text, otherwise after. + * @see #changeCaretPosition(int) + * @see #getPositionByText(String, int) + * @throws TimeoutExpiredException + * @throws NoSuchTextException + */ + public void changeCaretPosition(String text, int index, boolean before) { + output.printLine("Put caret " + + (before ? "before" : "after") + " " + + Integer.toString(index) + "'th instance of \"" + + text + "\" text"); + makeComponentVisible(); + int offset = getPositionByText(text, index); + if (offset == -1) { + throw (new NoSuchTextException(text)); + } + offset = before ? offset : offset + text.length(); + changeCaretPosition(offset); + } + + /** + * Puts caret before or after text. + * + * @param text Text to be searched. + * @param before If true put caret before text, otherwise after. + * @see #changeCaretPosition(int) + * @see #getPositionByText(String, int) + * @throws TimeoutExpiredException + * @throws NoSuchTextException + */ + public void changeCaretPosition(String text, boolean before) { + changeCaretPosition(text, 0, before); + } + + /** + * Types text starting from known position. If verification mode is on, + * checks that right text has been typed and caret has been moved to right + * position. + * + * @param text Text to be typed. + * @param caretPosition Position to start type text + * @see #typeText(String) + * @throws TimeoutExpiredException + * @throws NoSuchTextException + */ + public void typeText(final String text, final int caretPosition) { + output.printLine("Typing text \"" + text + "\" from " + + Integer.toString(caretPosition) + " position " + + "in text component\n : " + + toStringSource()); + output.printGolden("Typing text \"" + text + "\" in text component"); + makeComponentVisible(); + produceTimeRestricted(new Action() { + @Override + public Void launch(Void obj) { + driver.typeText(JTextComponentOperator.this, text, caretPosition); + return null; + } + + @Override + public String getDescription() { + return "Text typing"; + } + + @Override + public String toString() { + return "JTextComponentOperator.typeText.Action{description = " + getDescription() + '}'; + } + }, "JTextComponentOperator.TypeTextTimeout"); + if (getVerification()) { + waitText(text, -1); + } + } + + /** + * Types text starting from the current position. + * + * @param text Text to be typed. + * @see #typeText(String, int) + * @throws TimeoutExpiredException + */ + public void typeText(String text) { + typeText(text, getCaretPosition()); + } + + /** + * Selects a part of text. + * + * @param startPosition Start caret position + * @param finalPosition Final caret position + * @see #selectText(String, int) + * @see #selectText(String) + * @throws TimeoutExpiredException + */ + public void selectText(final int startPosition, final int finalPosition) { + output.printLine("Select text from " + + Integer.toString(startPosition) + " to " + + Integer.toString(finalPosition) + + " in text component\n : " + + toStringSource()); + makeComponentVisible(); + produceTimeRestricted(new Action() { + @Override + public Void launch(Void obj) { + driver.selectText(JTextComponentOperator.this, startPosition, finalPosition); + return null; + } + + @Override + public String getDescription() { + return "Text selecting"; + } + + @Override + public String toString() { + return "JTextComponentOperator.selectText.Action{description = " + getDescription() + '}'; + } + }, "JTextComponentOperator.TypeTextTimeout"); + } + + /** + * Selects a part of text. + * + * @param text Text to be selected + * @param index Index of text instance (first instance has index 0) + * @see #selectText(int, int) + * @see #selectText(String) + * @see #getPositionByText(String, int) + * @throws TimeoutExpiredException + * @throws NoSuchTextException + */ + public void selectText(String text, int index) { + output.printLine("Select " + + Integer.toString(index) + "'th instance of \"" + + text + "\" text in component\n : " + + toStringSource()); + makeComponentVisible(); + int start = getPositionByText(text, index); + if (start == -1) { + throw (new NoSuchTextException(text)); + } + selectText(start, start + text.length()); + } + + /** + * Selects a part of text. + * + * @param text Text to be selected + * @see #selectText(String, int) + * @see #selectText(int, int) + * @throws TimeoutExpiredException + * @throws NoSuchTextException + */ + public void selectText(String text) { + selectText(text, 0); + } + + /** + * Clears text. + * + * @throws TimeoutExpiredException + */ + public void clearText() { + output.printLine("Clearing text in text component\n : " + + toStringSource()); + output.printGolden("Clearing text in text component"); + makeComponentVisible(); + produceTimeRestricted(new Action() { + @Override + public Void launch(Void obj) { + driver.clearText(JTextComponentOperator.this); + return null; + } + + @Override + public String getDescription() { + return "Text clearing"; + } + + @Override + public String toString() { + return "JTextComponentOperator.clearText.Action{description = " + getDescription() + '}'; + } + }, "JTextComponentOperator.TypeTextTimeout"); + } + + /** + * Scrolls to a text poistion. + * + * @param position a position to scroll. + * @throws TimeoutExpiredException + */ + public void scrollToPosition(int position) { + output.printTrace("Scroll JTextComponent to " + Integer.toString(position) + " position\n : " + + toStringSource()); + output.printGolden("Scroll JTextComponent to " + Integer.toString(position) + " position"); + makeComponentVisible(); + //try to find JScrollPane under. + JScrollPane scroll = (JScrollPane) getContainer(new JScrollPaneOperator.JScrollPaneFinder(ComponentSearcher. + getTrueChooser("JScrollPane"))); + if (scroll == null) { + return; + } + JScrollPaneOperator scroller = new JScrollPaneOperator(scroll); + scroller.copyEnvironment(this); + scroller.setVisualizer(new EmptyVisualizer()); + Rectangle rect = modelToView(position); + scroller.scrollToComponentRectangle(getSource(), + (int) rect.getX(), + (int) rect.getY(), + (int) rect.getWidth(), + (int) rect.getHeight()); + } + + /** + * Returns text which is really displayed. Results returned by + * {@code getText()} and {@code getDisplayedText()} are different + * if text component is used to display + * {@code javax.swing.text.StyledDocument} + * + * @return the text which is displayed. + */ + public String getDisplayedText() { + try { + Document doc = getDocument(); + return doc.getText(0, doc.getLength()); + } catch (BadLocationException e) { + throw (new JemmyException("Exception during text operation with\n : " + + toStringSource(), e)); + } + } + + /** + * Wait for text to be displayed starting from certain position. + * + * @param text text to wait. + * @param position starting text position. + */ + public void waitText(final String text, final int position) { + getOutput().printLine("Wait \"" + text + "\" text starting from " + + Integer.toString(position) + " position in component \n : " + + toStringSource()); + getOutput().printGolden("Wait \"" + text + "\" text starting from " + + Integer.toString(position) + " position"); + waitState(new ComponentChooser() { + @Override + public boolean checkComponent(Component comp) { + String alltext = getDisplayedText(); + if (position >= 0) { + if (position + text.length() <= alltext.length()) { + return (alltext.substring(position, position + text.length()). + equals(text)); + } else { + return false; + } + } else { + return alltext.indexOf(text) >= 0; + } + } + + @Override + public String getDescription() { + return ("Has \"" + text + "\" text starting from " + + Integer.toString(position) + " position"); + } + + @Override + public String toString() { + return "JTextComponentOperator.waitText.ComponentChooser{description = " + getDescription() + '}'; + } + }); + } + + /** + * Waits for certain text. + * + * @param text Text to be compared by getComparator() comparator. + */ + public void waitText(String text) { + getOutput().printLine("Wait \"" + text + "\" text in component \n : " + + toStringSource()); + getOutput().printGolden("Wait \"" + text + "\" text"); + waitState(new JTextComponentByTextFinder(text, getComparator())); + } + + /** + * Wait for caret to be moved to certain position. + * + * @param position a position which caret supposed to be moved to. + */ + public void waitCaretPosition(final int position) { + getOutput().printLine("Wait caret to be at \"" + Integer.toString(position) + + " position in component \n : " + + toStringSource()); + getOutput().printGolden("Wait caret to be at \"" + Integer.toString(position) + + " position"); + waitState(new ComponentChooser() { + @Override + public boolean checkComponent(Component comp) { + return getCaretPosition() == position; + } + + @Override + public String getDescription() { + return "Has caret at " + Integer.toString(position) + " position"; + } + + @Override + public String toString() { + return "JTextComponentOperator.waitCaretPosition.ComponentChooser{description = " + getDescription() + '}'; + } + }); + } + + @Override + public Hashtable getDump() { + Hashtable result = super.getDump(); + result.put(TEXT_DPROP, ((JTextComponent) getSource()).getText()); + if (((JTextComponent) getSource()).getSelectedText() != null + && !((JTextComponent) getSource()).getSelectedText().equals("")) { + result.put(SELECTED_TEXT_DPROP, ((JTextComponent) getSource()).getSelectedText()); + } + result.put(IS_EDITABLE_DPROP, ((JTextComponent) getSource()).isEditable() ? "true" : "false"); + return result; + } + + //////////////////////////////////////////////////////// + //Mapping // + /** + * Maps {@code JTextComponent.addCaretListener(CaretListener)} through queue + */ + public void addCaretListener(final CaretListener caretListener) { + runMapping(new MapVoidAction("addCaretListener") { + @Override + public void map() { + ((JTextComponent) getSource()).addCaretListener(caretListener); + } + }); + } + + /** + * Maps {@code JTextComponent.copy()} through queue + */ + public void copy() { + runMapping(new MapVoidAction("copy") { + @Override + public void map() { + ((JTextComponent) getSource()).copy(); + } + }); + } + + /** + * Maps {@code JTextComponent.cut()} through queue + */ + public void cut() { + runMapping(new MapVoidAction("cut") { + @Override + public void map() { + ((JTextComponent) getSource()).cut(); + } + }); + } + + /** + * Maps {@code JTextComponent.getActions()} through queue + */ + public javax.swing.Action[] getActions() { + return ((javax.swing.Action[]) runMapping(new MapAction("getActions") { + @Override + public Object map() { + return ((JTextComponent) getSource()).getActions(); + } + })); + } + + /** + * Maps {@code JTextComponent.getCaret()} through queue + */ + public Caret getCaret() { + return (runMapping(new MapAction("getCaret") { + @Override + public Caret map() { + return ((JTextComponent) getSource()).getCaret(); + } + })); + } + + /** + * Maps {@code JTextComponent.getCaretColor()} through queue + */ + public Color getCaretColor() { + return (runMapping(new MapAction("getCaretColor") { + @Override + public Color map() { + return ((JTextComponent) getSource()).getCaretColor(); + } + })); + } + + /** + * Maps {@code JTextComponent.getCaretPosition()} through queue + */ + public int getCaretPosition() { + return (runMapping(new MapIntegerAction("getCaretPosition") { + @Override + public int map() { + return ((JTextComponent) getSource()).getCaretPosition(); + } + })); + } + + /** + * Maps {@code JTextComponent.getDisabledTextColor()} through queue + */ + public Color getDisabledTextColor() { + return (runMapping(new MapAction("getDisabledTextColor") { + @Override + public Color map() { + return ((JTextComponent) getSource()).getDisabledTextColor(); + } + })); + } + + /** + * Maps {@code JTextComponent.getDocument()} through queue + */ + public Document getDocument() { + return (runMapping(new MapAction("getDocument") { + @Override + public Document map() { + return ((JTextComponent) getSource()).getDocument(); + } + })); + } + + /** + * Maps {@code JTextComponent.getFocusAccelerator()} through queue + */ + public char getFocusAccelerator() { + return (runMapping(new MapCharacterAction("getFocusAccelerator") { + @Override + public char map() { + return ((JTextComponent) getSource()).getFocusAccelerator(); + } + })); + } + + /** + * Maps {@code JTextComponent.getHighlighter()} through queue + */ + public Highlighter getHighlighter() { + return (runMapping(new MapAction("getHighlighter") { + @Override + public Highlighter map() { + return ((JTextComponent) getSource()).getHighlighter(); + } + })); + } + + /** + * Maps {@code JTextComponent.getKeymap()} through queue + */ + public Keymap getKeymap() { + return (runMapping(new MapAction("getKeymap") { + @Override + public Keymap map() { + return ((JTextComponent) getSource()).getKeymap(); + } + })); + } + + /** + * Maps {@code JTextComponent.getMargin()} through queue + */ + public Insets getMargin() { + return (runMapping(new MapAction("getMargin") { + @Override + public Insets map() { + return ((JTextComponent) getSource()).getMargin(); + } + })); + } + + /** + * Maps {@code JTextComponent.getPreferredScrollableViewportSize()} + * through queue + */ + public Dimension getPreferredScrollableViewportSize() { + return (runMapping(new MapAction("getPreferredScrollableViewportSize") { + @Override + public Dimension map() { + return ((JTextComponent) getSource()).getPreferredScrollableViewportSize(); + } + })); + } + + /** + * Maps + * {@code JTextComponent.getScrollableBlockIncrement(Rectangle, int, int)} + * through queue + */ + public int getScrollableBlockIncrement(final Rectangle rectangle, final int i, final int i1) { + return (runMapping(new MapIntegerAction("getScrollableBlockIncrement") { + @Override + public int map() { + return ((JTextComponent) getSource()).getScrollableBlockIncrement(rectangle, i, i1); + } + })); + } + + /** + * Maps {@code JTextComponent.getScrollableTracksViewportHeight()} + * through queue + */ + public boolean getScrollableTracksViewportHeight() { + return (runMapping(new MapBooleanAction("getScrollableTracksViewportHeight") { + @Override + public boolean map() { + return ((JTextComponent) getSource()).getScrollableTracksViewportHeight(); + } + })); + } + + /** + * Maps {@code JTextComponent.getScrollableTracksViewportWidth()} + * through queue + */ + public boolean getScrollableTracksViewportWidth() { + return (runMapping(new MapBooleanAction("getScrollableTracksViewportWidth") { + @Override + public boolean map() { + return ((JTextComponent) getSource()).getScrollableTracksViewportWidth(); + } + })); + } + + /** + * Maps + * {@code JTextComponent.getScrollableUnitIncrement(Rectangle, int, int)} + * through queue + */ + public int getScrollableUnitIncrement(final Rectangle rectangle, final int i, final int i1) { + return (runMapping(new MapIntegerAction("getScrollableUnitIncrement") { + @Override + public int map() { + return ((JTextComponent) getSource()).getScrollableUnitIncrement(rectangle, i, i1); + } + })); + } + + /** + * Maps {@code JTextComponent.getSelectedText()} through queue + */ + public String getSelectedText() { + return (runMapping(new MapAction("getSelectedText") { + @Override + public String map() { + return ((JTextComponent) getSource()).getSelectedText(); + } + })); + } + + /** + * Maps {@code JTextComponent.getSelectedTextColor()} through queue + */ + public Color getSelectedTextColor() { + return (runMapping(new MapAction("getSelectedTextColor") { + @Override + public Color map() { + return ((JTextComponent) getSource()).getSelectedTextColor(); + } + })); + } + + /** + * Maps {@code JTextComponent.getSelectionColor()} through queue + */ + public Color getSelectionColor() { + return (runMapping(new MapAction("getSelectionColor") { + @Override + public Color map() { + return ((JTextComponent) getSource()).getSelectionColor(); + } + })); + } + + /** + * Maps {@code JTextComponent.getSelectionEnd()} through queue + */ + public int getSelectionEnd() { + return (runMapping(new MapIntegerAction("getSelectionEnd") { + @Override + public int map() { + return ((JTextComponent) getSource()).getSelectionEnd(); + } + })); + } + + /** + * Maps {@code JTextComponent.getSelectionStart()} through queue + */ + public int getSelectionStart() { + return (runMapping(new MapIntegerAction("getSelectionStart") { + @Override + public int map() { + return ((JTextComponent) getSource()).getSelectionStart(); + } + })); + } + + /** + * Maps {@code JTextComponent.getText()} through queue + */ + public String getText() { + return (runMapping(new MapAction("getText") { + @Override + public String map() { + return ((JTextComponent) getSource()).getText(); + } + })); + } + + /** + * Maps {@code JTextComponent.getText(int, int)} through queue + */ + public String getText(final int i, final int i1) { + return (runMapping(new MapAction("getText") { + @Override + public String map() throws BadLocationException { + return ((JTextComponent) getSource()).getText(i, i1); + } + })); + } + + /** + * Maps {@code JTextComponent.getUI()} through queue + */ + public TextUI getUI() { + return (runMapping(new MapAction("getUI") { + @Override + public TextUI map() { + return ((JTextComponent) getSource()).getUI(); + } + })); + } + + /** + * Maps {@code JTextComponent.isEditable()} through queue + */ + public boolean isEditable() { + return (runMapping(new MapBooleanAction("isEditable") { + @Override + public boolean map() { + return ((JTextComponent) getSource()).isEditable(); + } + })); + } + + /** + * Maps {@code JTextComponent.modelToView(int)} through queue + */ + public Rectangle modelToView(final int i) { + return (runMapping(new MapAction("modelToView") { + @Override + public Rectangle map() throws BadLocationException { + return ((JTextComponent) getSource()).modelToView(i); + } + })); + } + + /** + * Maps {@code JTextComponent.moveCaretPosition(int)} through queue + */ + public void moveCaretPosition(final int i) { + runMapping(new MapVoidAction("moveCaretPosition") { + @Override + public void map() { + ((JTextComponent) getSource()).moveCaretPosition(i); + } + }); + } + + /** + * Maps {@code JTextComponent.paste()} through queue + */ + public void paste() { + runMapping(new MapVoidAction("paste") { + @Override + public void map() { + ((JTextComponent) getSource()).paste(); + } + }); + } + + /** + * Maps {@code JTextComponent.read(Reader, Object)} through queue + */ + public void read(final Reader reader, final Object object) { + runMapping(new MapVoidAction("read") { + @Override + public void map() throws IOException { + ((JTextComponent) getSource()).read(reader, object); + } + }); + } + + /** + * Maps {@code JTextComponent.removeCaretListener(CaretListener)} + * through queue + */ + public void removeCaretListener(final CaretListener caretListener) { + runMapping(new MapVoidAction("removeCaretListener") { + @Override + public void map() { + ((JTextComponent) getSource()).removeCaretListener(caretListener); + } + }); + } + + /** + * Maps {@code JTextComponent.replaceSelection(String)} through queue + */ + public void replaceSelection(final String string) { + runMapping(new MapVoidAction("replaceSelection") { + @Override + public void map() { + ((JTextComponent) getSource()).replaceSelection(string); + } + }); + } + + /** + * Maps {@code JTextComponent.select(int, int)} through queue + */ + public void select(final int i, final int i1) { + runMapping(new MapVoidAction("select") { + @Override + public void map() { + ((JTextComponent) getSource()).select(i, i1); + } + }); + } + + /** + * Maps {@code JTextComponent.selectAll()} through queue + */ + public void selectAll() { + runMapping(new MapVoidAction("selectAll") { + @Override + public void map() { + ((JTextComponent) getSource()).selectAll(); + } + }); + } + + /** + * Maps {@code JTextComponent.setCaret(Caret)} through queue + */ + public void setCaret(final Caret caret) { + runMapping(new MapVoidAction("setCaret") { + @Override + public void map() { + ((JTextComponent) getSource()).setCaret(caret); + } + }); + } + + /** + * Maps {@code JTextComponent.setCaretColor(Color)} through queue + */ + public void setCaretColor(final Color color) { + runMapping(new MapVoidAction("setCaretColor") { + @Override + public void map() { + ((JTextComponent) getSource()).setCaretColor(color); + } + }); + } + + /** + * Maps {@code JTextComponent.setCaretPosition(int)} through queue + */ + public void setCaretPosition(final int i) { + runMapping(new MapVoidAction("setCaretPosition") { + @Override + public void map() { + ((JTextComponent) getSource()).setCaretPosition(i); + } + }); + } + + /** + * Maps {@code JTextComponent.setDisabledTextColor(Color)} through queue + */ + public void setDisabledTextColor(final Color color) { + runMapping(new MapVoidAction("setDisabledTextColor") { + @Override + public void map() { + ((JTextComponent) getSource()).setDisabledTextColor(color); + } + }); + } + + /** + * Maps {@code JTextComponent.setDocument(Document)} through queue + */ + public void setDocument(final Document document) { + runMapping(new MapVoidAction("setDocument") { + @Override + public void map() { + ((JTextComponent) getSource()).setDocument(document); + } + }); + } + + /** + * Maps {@code JTextComponent.setEditable(boolean)} through queue + */ + public void setEditable(final boolean b) { + runMapping(new MapVoidAction("setEditable") { + @Override + public void map() { + ((JTextComponent) getSource()).setEditable(b); + } + }); + } + + /** + * Maps {@code JTextComponent.setFocusAccelerator(char)} through queue + */ + public void setFocusAccelerator(final char c) { + runMapping(new MapVoidAction("setFocusAccelerator") { + @Override + public void map() { + ((JTextComponent) getSource()).setFocusAccelerator(c); + } + }); + } + + /** + * Maps {@code JTextComponent.setHighlighter(Highlighter)} through queue + */ + public void setHighlighter(final Highlighter highlighter) { + runMapping(new MapVoidAction("setHighlighter") { + @Override + public void map() { + ((JTextComponent) getSource()).setHighlighter(highlighter); + } + }); + } + + /** + * Maps {@code JTextComponent.setKeymap(Keymap)} through queue + */ + public void setKeymap(final Keymap keymap) { + runMapping(new MapVoidAction("setKeymap") { + @Override + public void map() { + ((JTextComponent) getSource()).setKeymap(keymap); + } + }); + } + + /** + * Maps {@code JTextComponent.setMargin(Insets)} through queue + */ + public void setMargin(final Insets insets) { + runMapping(new MapVoidAction("setMargin") { + @Override + public void map() { + ((JTextComponent) getSource()).setMargin(insets); + } + }); + } + + /** + * Maps {@code JTextComponent.setSelectedTextColor(Color)} through queue + */ + public void setSelectedTextColor(final Color color) { + runMapping(new MapVoidAction("setSelectedTextColor") { + @Override + public void map() { + ((JTextComponent) getSource()).setSelectedTextColor(color); + } + }); + } + + /** + * Maps {@code JTextComponent.setSelectionColor(Color)} through queue + */ + public void setSelectionColor(final Color color) { + runMapping(new MapVoidAction("setSelectionColor") { + @Override + public void map() { + ((JTextComponent) getSource()).setSelectionColor(color); + } + }); + } + + /** + * Maps {@code JTextComponent.setSelectionEnd(int)} through queue + */ + public void setSelectionEnd(final int i) { + runMapping(new MapVoidAction("setSelectionEnd") { + @Override + public void map() { + ((JTextComponent) getSource()).setSelectionEnd(i); + } + }); + } + + /** + * Maps {@code JTextComponent.setSelectionStart(int)} through queue + */ + public void setSelectionStart(final int i) { + runMapping(new MapVoidAction("setSelectionStart") { + @Override + public void map() { + ((JTextComponent) getSource()).setSelectionStart(i); + } + }); + } + + /** + * Maps {@code JTextComponent.setText(String)} through queue + */ + public void setText(final String string) { + runMapping(new MapVoidAction("setText") { + @Override + public void map() { + ((JTextComponent) getSource()).setText(string); + } + }); + } + + /** + * Maps {@code JTextComponent.setUI(TextUI)} through queue + */ + public void setUI(final TextUI textUI) { + runMapping(new MapVoidAction("setUI") { + @Override + public void map() { + ((JTextComponent) getSource()).setUI(textUI); + } + }); + } + + /** + * Maps {@code JTextComponent.viewToModel(Point)} through queue + */ + public int viewToModel(final Point point) { + return (runMapping(new MapIntegerAction("viewToModel") { + @Override + public int map() { + return ((JTextComponent) getSource()).viewToModel(point); + } + })); + } + + /** + * Maps {@code JTextComponent.write(Writer)} through queue + */ + public void write(final Writer writer) { + runMapping(new MapVoidAction("write") { + @Override + public void map() throws IOException { + ((JTextComponent) getSource()).write(writer); + } + }); + } + + //End of mapping // + //////////////////////////////////////////////////////// + /** + * Can be throught during a text operation if text has not been found in the + * component. + */ + public class NoSuchTextException extends JemmyInputException { + + private static final long serialVersionUID = 42L; + + /** + * Constructor. + * + * @param text a nonexistent text. + */ + public NoSuchTextException(String text) { + super("No such text as \"" + text + "\"", getSource()); + } + } + + /** + * Interface defining additional text cearch criteria. + * + * @see #getPositionByText(java.lang.String, + * JTextComponentOperator.TextChooser) + */ + public interface TextChooser { + + /** + * Checkes if position fits the criteria. + * + * @param document a document to be checked. + * @param offset a checked position + * @return true if the position fits the criteria. + */ + public boolean checkPosition(Document document, int offset); + + /** + * Returns a printable description of the criteria. + * + * @return a description of this chooser. + */ + public String getDescription(); + } + + /** + * Allows to find component by text. + */ + public static class JTextComponentByTextFinder implements ComponentChooser { + + String label; + StringComparator comparator; + + /** + * Constructs JTextComponentByTextFinder. + * + * @param lb a text pattern + * @param comparator specifies string comparision algorithm. + */ + public JTextComponentByTextFinder(String lb, StringComparator comparator) { + label = lb; + this.comparator = comparator; + } + + /** + * Constructs JTextComponentByTextFinder. + * + * @param lb a text pattern + */ + public JTextComponentByTextFinder(String lb) { + this(lb, Operator.getDefaultStringComparator()); + } + + @Override + public boolean checkComponent(Component comp) { + if (comp instanceof JTextComponent) { + if (((JTextComponent) comp).getText() != null) { + return (comparator.equals(((JTextComponent) comp).getText(), + label)); + } + } + return false; + } + + @Override + public String getDescription() { + return "JTextComponent with text \"" + label + "\""; + } + + @Override + public String toString() { + return "JTextComponentByTextFinder{" + "label=" + label + ", comparator=" + comparator + '}'; + } + } + + /** + * Checks component type. + */ + public static class JTextComponentFinder extends Finder { + + /** + * Constructs JTextComponentFinder. + * + * @param sf other searching criteria. + */ + public JTextComponentFinder(ComponentChooser sf) { + super(JTextComponent.class, sf); + } + + /** + * Constructs JTextComponentFinder. + */ + public JTextComponentFinder() { + super(JTextComponent.class); + } + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JTextFieldOperator.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JTextFieldOperator.java new file mode 100644 index 00000000000..00e70220869 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JTextFieldOperator.java @@ -0,0 +1,451 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.operators; + +import java.awt.Container; +import java.awt.event.ActionListener; + +import javax.swing.BoundedRangeModel; +import javax.swing.JTextField; + +import org.netbeans.jemmy.ComponentChooser; +import org.netbeans.jemmy.TimeoutExpiredException; + +/** + *

Timeouts used:
+ * JTextComponentOperator.PushKeyTimeout - time between key pressing and + * releasing during text typing
+ * JTextComponentOperator.BetweenKeysTimeout - time to sleep between two chars + * typing
+ * JTextComponentOperator.ChangeCaretPositionTimeout - maximum time to chenge + * caret position
+ * JTextComponentOperator.TypeTextTimeout - maximum time to type text
+ * ComponentOperator.WaitComponentTimeout - time to wait component displayed + *
+ * ComponentOperator.WaitFocusTimeout - time to wait component focus
+ * JScrollBarOperator.OneScrollClickTimeout - time for one scroll click
+ * JScrollBarOperator.WholeScrollTimeout - time for the whole scrolling
. + * + * @see org.netbeans.jemmy.Timeouts + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + * + */ +public class JTextFieldOperator extends JTextComponentOperator { + + /** + * Constructor. + * + * @param b a component + */ + public JTextFieldOperator(JTextField b) { + super(b); + } + + /** + * Constructs a JTextFieldOperator object. + * + * @param cont a container + * @param chooser a component chooser specifying searching criteria. + * @param index an index between appropriate ones. + */ + public JTextFieldOperator(ContainerOperator cont, ComponentChooser chooser, int index) { + this((JTextField) cont. + waitSubComponent(new JTextFieldFinder(chooser), + index)); + copyEnvironment(cont); + } + + /** + * Constructs a JTextFieldOperator object. + * + * @param cont a container + * @param chooser a component chooser specifying searching criteria. + */ + public JTextFieldOperator(ContainerOperator cont, ComponentChooser chooser) { + this(cont, chooser, 0); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont a container + * @param text Button text. + * @param index Ordinal component index. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public JTextFieldOperator(ContainerOperator cont, String text, int index) { + this((JTextField) waitComponent(cont, + new JTextFieldFinder(new JTextComponentOperator.JTextComponentByTextFinder(text, + cont.getComparator())), + index)); + copyEnvironment(cont); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont a container + * @param text Button text. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public JTextFieldOperator(ContainerOperator cont, String text) { + this(cont, text, 0); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont a container + * @param index Ordinal component index. + * @throws TimeoutExpiredException + */ + public JTextFieldOperator(ContainerOperator cont, int index) { + this((JTextField) waitComponent(cont, + new JTextFieldFinder(), + index)); + copyEnvironment(cont); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont a container + * @throws TimeoutExpiredException + */ + public JTextFieldOperator(ContainerOperator cont) { + this(cont, 0); + } + + /** + * Searches JTextField in container. + * + * @param cont Container to search component in. + * @param chooser a component chooser specifying searching criteria. + * @param index Ordinal component index. + * @return JTextField instance or null if component was not found. + */ + public static JTextField findJTextField(Container cont, ComponentChooser chooser, int index) { + return (JTextField) findJTextComponent(cont, new JTextFieldFinder(chooser), index); + } + + /** + * Searches JTextField in container. + * + * @param cont Container to search component in. + * @param chooser a component chooser specifying searching criteria. + * @return JTextField instance or null if component was not found. + */ + public static JTextField findJTextField(Container cont, ComponentChooser chooser) { + return findJTextField(cont, chooser, 0); + } + + /** + * Searches JTextField by text. + * + * @param cont Container to search component in. + * @param text Component text. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @param index Ordinal component index. + * @return JTextField instance or null if component was not found. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + */ + public static JTextField findJTextField(Container cont, String text, boolean ce, boolean ccs, int index) { + return (findJTextField(cont, + new JTextFieldFinder(new JTextComponentOperator.JTextComponentByTextFinder(text, + new DefaultStringComparator(ce, ccs))), + index)); + } + + /** + * Searches JTextField by text. + * + * @param cont Container to search component in. + * @param text Component text. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @return JTextField instance or null if component was not found. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + */ + public static JTextField findJTextField(Container cont, String text, boolean ce, boolean ccs) { + return findJTextField(cont, text, ce, ccs, 0); + } + + /** + * Waits JTextField in container. + * + * @param cont Container to search component in. + * @param chooser a component chooser specifying searching criteria. + * @param index Ordinal component index. + * @return JTextField instance. + * @throws TimeoutExpiredException + */ + public static JTextField waitJTextField(Container cont, ComponentChooser chooser, int index) { + return (JTextField) waitJTextComponent(cont, new JTextFieldFinder(chooser), index); + } + + /** + * Waits JTextField in container. + * + * @param cont Container to search component in. + * @param chooser a component chooser specifying searching criteria. + * @return JTextField instance. + * @throws TimeoutExpiredException + */ + public static JTextField waitJTextField(Container cont, ComponentChooser chooser) { + return waitJTextField(cont, chooser, 0); + } + + /** + * Waits JTextField by text. + * + * @param cont Container to search component in. + * @param text Component text. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @param index Ordinal component index. + * @return JTextField instance. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public static JTextField waitJTextField(Container cont, String text, boolean ce, boolean ccs, int index) { + return (waitJTextField(cont, + new JTextFieldFinder(new JTextComponentOperator.JTextComponentByTextFinder(text, + new DefaultStringComparator(ce, ccs))), + index)); + } + + /** + * Waits JTextField by text. + * + * @param cont Container to search component in. + * @param text Component text. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @return JTextField instance. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + * @throws TimeoutExpiredException + */ + public static JTextField waitJTextField(Container cont, String text, boolean ce, boolean ccs) { + return waitJTextField(cont, text, ce, ccs, 0); + } + + /** + * Wait some text to be displayed starting from certain position. + * + * @param text a text to wait. + * @param position start position. + */ + @Override + public void waitText(String text, int position) { + super.waitText(removeNewLines(text), position); + } + + /** + * Wait some text to be displayed. + * + * @param text a text to wait. + */ + @Override + public void waitText(String text) { + super.waitText(removeNewLines(text)); + } + + //////////////////////////////////////////////////////// + //Mapping // + /** + * Maps {@code JTextField.addActionListener(ActionListener)} through queue + */ + public void addActionListener(final ActionListener actionListener) { + runMapping(new MapVoidAction("addActionListener") { + @Override + public void map() { + ((JTextField) getSource()).addActionListener(actionListener); + } + }); + } + + /** + * Maps {@code JTextField.getColumns()} through queue + */ + public int getColumns() { + return (runMapping(new MapIntegerAction("getColumns") { + @Override + public int map() { + return ((JTextField) getSource()).getColumns(); + } + })); + } + + /** + * Maps {@code JTextField.getHorizontalAlignment()} through queue + */ + public int getHorizontalAlignment() { + return (runMapping(new MapIntegerAction("getHorizontalAlignment") { + @Override + public int map() { + return ((JTextField) getSource()).getHorizontalAlignment(); + } + })); + } + + /** + * Maps {@code JTextField.getHorizontalVisibility()} through queue + */ + public BoundedRangeModel getHorizontalVisibility() { + return (runMapping(new MapAction("getHorizontalVisibility") { + @Override + public BoundedRangeModel map() { + return ((JTextField) getSource()).getHorizontalVisibility(); + } + })); + } + + /** + * Maps {@code JTextField.getScrollOffset()} through queue + */ + public int getScrollOffset() { + return (runMapping(new MapIntegerAction("getScrollOffset") { + @Override + public int map() { + return ((JTextField) getSource()).getScrollOffset(); + } + })); + } + + /** + * Maps {@code JTextField.postActionEvent()} through queue + */ + public void postActionEvent() { + runMapping(new MapVoidAction("postActionEvent") { + @Override + public void map() { + ((JTextField) getSource()).postActionEvent(); + } + }); + } + + /** + * Maps {@code JTextField.removeActionListener(ActionListener)} through queue + */ + public void removeActionListener(final ActionListener actionListener) { + runMapping(new MapVoidAction("removeActionListener") { + @Override + public void map() { + ((JTextField) getSource()).removeActionListener(actionListener); + } + }); + } + + /** + * Maps {@code JTextField.setActionCommand(String)} through queue + */ + public void setActionCommand(final String string) { + runMapping(new MapVoidAction("setActionCommand") { + @Override + public void map() { + ((JTextField) getSource()).setActionCommand(string); + } + }); + } + + /** + * Maps {@code JTextField.setColumns(int)} through queue + */ + public void setColumns(final int i) { + runMapping(new MapVoidAction("setColumns") { + @Override + public void map() { + ((JTextField) getSource()).setColumns(i); + } + }); + } + + /** + * Maps {@code JTextField.setHorizontalAlignment(int)} through queue + */ + public void setHorizontalAlignment(final int i) { + runMapping(new MapVoidAction("setHorizontalAlignment") { + @Override + public void map() { + ((JTextField) getSource()).setHorizontalAlignment(i); + } + }); + } + + /** + * Maps {@code JTextField.setScrollOffset(int)} through queue + */ + public void setScrollOffset(final int i) { + runMapping(new MapVoidAction("setScrollOffset") { + @Override + public void map() { + ((JTextField) getSource()).setScrollOffset(i); + } + }); + } + + //End of mapping // + //////////////////////////////////////////////////////// + private String removeNewLines(String text) { + StringBuffer buff = new StringBuffer(text); + int i = 0; + while (i < buff.length()) { + if (buff.charAt(i) != '\n') { + i++; + } else { + buff.deleteCharAt(i); + } + } + return buff.toString(); + } + + /** + * Checks component type. + */ + public static class JTextFieldFinder extends Finder { + + /** + * Constructs JTextFieldFinder. + * + * @param sf other searching criteria. + */ + public JTextFieldFinder(ComponentChooser sf) { + super(JTextField.class, sf); + } + + /** + * Constructs JTextFieldFinder. + */ + public JTextFieldFinder() { + super(JTextField.class); + } + } +} diff --git a/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JTextPaneOperator.java b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JTextPaneOperator.java new file mode 100644 index 00000000000..0e7f3a3c8b8 --- /dev/null +++ b/jdk/test/sanity/client/lib/jemmy/src/org/netbeans/jemmy/operators/JTextPaneOperator.java @@ -0,0 +1,450 @@ +/* + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.netbeans.jemmy.operators; + +import java.awt.Component; +import java.awt.Container; + +import javax.swing.Icon; +import javax.swing.JTextPane; +import javax.swing.text.AttributeSet; +import javax.swing.text.MutableAttributeSet; +import javax.swing.text.Style; +import javax.swing.text.StyledDocument; + +import org.netbeans.jemmy.ComponentChooser; + +/** + *

Timeouts used:
+ * JTextComponentOperator.PushKeyTimeout - time between key pressing and + * releasing during text typing
+ * JTextComponentOperator.BetweenKeysTimeout - time to sleep between two chars + * typing
+ * JTextComponentOperator.ChangeCaretPositionTimeout - maximum time to chenge + * caret position
+ * JTextComponentOperator.TypeTextTimeout - maximum time to type text
+ * ComponentOperator.WaitComponentTimeout - time to wait component displayed + *
+ * ComponentOperator.WaitFocusTimeout - time to wait component focus
+ * JScrollBarOperator.OneScrollClickTimeout - time for one scroll click
+ * JScrollBarOperator.WholeScrollTimeout - time for the whole scrolling
. + * + * @see org.netbeans.jemmy.Timeouts + * + * @author Alexandre Iline (alexandre.iline@oracle.com) + * + */ +public class JTextPaneOperator extends JEditorPaneOperator { + + /** + * Constructor. + * + * @param b a component + */ + public JTextPaneOperator(JTextPane b) { + super(b); + } + + /** + * Constructs a JTextPaneOperator object. + * + * @param cont a container + * @param chooser a component chooser specifying searching criteria. + * @param index an index between appropriate ones. + */ + public JTextPaneOperator(ContainerOperator cont, ComponentChooser chooser, int index) { + this((JTextPane) cont. + waitSubComponent(new JTextPaneFinder(chooser), + index)); + copyEnvironment(cont); + } + + /** + * Constructs a JTextPaneOperator object. + * + * @param cont a container + * @param chooser a component chooser specifying searching criteria. + */ + public JTextPaneOperator(ContainerOperator cont, ComponentChooser chooser) { + this(cont, chooser, 0); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont a container + * @param text Button text. + * @param index Ordinal component index. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + */ + public JTextPaneOperator(ContainerOperator cont, String text, int index) { + this((JTextPane) waitComponent(cont, + new JTextPaneFinder(new JTextComponentOperator.JTextComponentByTextFinder(text, + cont.getComparator())), + index)); + copyEnvironment(cont); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont a container + * @param text Button text. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + */ + public JTextPaneOperator(ContainerOperator cont, String text) { + this(cont, text, 0); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont a container + * @param index Ordinal component index. + */ + public JTextPaneOperator(ContainerOperator cont, int index) { + this((JTextPane) waitComponent(cont, + new JTextPaneFinder(), + index)); + copyEnvironment(cont); + } + + /** + * Constructor. Waits component in container first. Uses cont's timeout and + * output for waiting and to init operator. + * + * @param cont a container + */ + public JTextPaneOperator(ContainerOperator cont) { + this(cont, 0); + } + + /** + * Searches JTextPane in container. + * + * @param cont Container to search component in. + * @param chooser a component chooser specifying searching criteria. + * @param index Ordinal component index. + * @return JTextPane instance or null if component was not found. + */ + public static JTextPane findJTextPane(Container cont, ComponentChooser chooser, int index) { + return (JTextPane) findJTextComponent(cont, new JTextPaneFinder(chooser), index); + } + + /** + * Searches JTextPane in container. + * + * @param cont Container to search component in. + * @param chooser a component chooser specifying searching criteria. + * @return JTextPane instance or null if component was not found. + */ + public static JTextPane findJTextPane(Container cont, ComponentChooser chooser) { + return findJTextPane(cont, chooser, 0); + } + + /** + * Searches JTextPane by text. + * + * @param cont Container to search component in. + * @param text Component text. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @param index Ordinal component index. + * @return JTextPane instance or null if component was not found. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + */ + public static JTextPane findJTextPane(Container cont, String text, boolean ce, boolean ccs, int index) { + return (findJTextPane(cont, + new JTextPaneFinder(new JTextComponentOperator.JTextComponentByTextFinder(text, + new DefaultStringComparator(ce, ccs))), + index)); + } + + /** + * Searches JTextPane by text. + * + * @param cont Container to search component in. + * @param text Component text. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @return JTextPane instance or null if component was not found. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + */ + public static JTextPane findJTextPane(Container cont, String text, boolean ce, boolean ccs) { + return findJTextPane(cont, text, ce, ccs, 0); + } + + /** + * Waits JTextPane in container. + * + * @param cont Container to search component in. + * @param chooser a component chooser specifying searching criteria. + * @param index Ordinal component index. + * @return JTextPane instance. + */ + public static JTextPane waitJTextPane(Container cont, ComponentChooser chooser, int index) { + return (JTextPane) waitJTextComponent(cont, new JTextPaneFinder(chooser), index); + } + + /** + * Waits JTextPane in container. + * + * @param cont Container to search component in. + * @param chooser a component chooser specifying searching criteria. + * @return JTextPane instance. + */ + public static JTextPane waitJTextPane(Container cont, ComponentChooser chooser) { + return waitJTextPane(cont, chooser, 0); + } + + /** + * Waits JTextPane by text. + * + * @param cont Container to search component in. + * @param text Component text. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @param index Ordinal component index. + * @return JTextPane instance. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + */ + public static JTextPane waitJTextPane(Container cont, String text, boolean ce, boolean ccs, int index) { + return (waitJTextPane(cont, + new JTextPaneFinder(new JTextComponentOperator.JTextComponentByTextFinder(text, + new DefaultStringComparator(ce, ccs))), + index)); + } + + /** + * Waits JTextPane by text. + * + * @param cont Container to search component in. + * @param text Component text. + * @param ce Compare text exactly. + * @param ccs Compare text case sensitively. + * @return JTextPane instance. + * @see ComponentOperator#isCaptionEqual(String, String, boolean, boolean) + */ + public static JTextPane waitJTextPane(Container cont, String text, boolean ce, boolean ccs) { + return waitJTextPane(cont, text, ce, ccs, 0); + } + + //////////////////////////////////////////////////////// + //Mapping // + /** + * Maps {@code JTextPane.addStyle(String, Style)} through queue + */ + public Style addStyle(final String string, final Style style) { + return (runMapping(new MapAction

+ Show how Swing's top-level container, + JWindow, can be used to create areas on the screen which + do not have system-supplied borders and therefore cannot be moved or resized + by the user like frames or dialogs. JWindow is commonly used to implement menus, + tooltips, and popups. +