1 /* 2 * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 package org.netbeans.jemmy.operators; 24 25 import java.awt.Component; 26 import java.awt.Container; 27 import java.text.ParseException; 28 import java.util.Date; 29 import java.util.Hashtable; 30 import java.util.List; 31 32 import javax.swing.JComponent; 33 import javax.swing.JSpinner; 34 import javax.swing.SpinnerDateModel; 35 import javax.swing.SpinnerListModel; 36 import javax.swing.SpinnerModel; 37 import javax.swing.SpinnerNumberModel; 38 import javax.swing.SwingConstants; 39 import javax.swing.event.ChangeListener; 40 import javax.swing.plaf.SpinnerUI; 41 42 import org.netbeans.jemmy.Action; 43 import org.netbeans.jemmy.ComponentChooser; 44 import org.netbeans.jemmy.ComponentSearcher; 45 import org.netbeans.jemmy.JemmyException; 46 import org.netbeans.jemmy.Outputable; 47 import org.netbeans.jemmy.TestOut; 48 import org.netbeans.jemmy.TimeoutExpiredException; 49 import org.netbeans.jemmy.Timeoutable; 50 import org.netbeans.jemmy.Timeouts; 51 import org.netbeans.jemmy.drivers.DriverManager; 52 import org.netbeans.jemmy.drivers.ScrollDriver; 53 import org.netbeans.jemmy.drivers.scrolling.ScrollAdjuster; 54 55 /** 56 * Provides methods to work with {@code javax.swing.JSpinner} component 57 * <br> 58 * 59 * @see NumberSpinnerOperator 60 * @see ListSpinnerOperator 61 * @see DateSpinnerOperator 62 * 63 * @author Alexandre Iline (alexandre.iline@oracle.com) 64 */ 65 public class JSpinnerOperator extends JComponentOperator 66 implements Timeoutable, Outputable { 67 68 /** 69 * Identifier for a "value" property. 70 * 71 * @see #getDump 72 */ 73 public static final String VALUE_DPROP = "Value"; 74 75 private final static long WHOLE_SCROLL_TIMEOUT = 60000; 76 77 private Timeouts timeouts; 78 private TestOut output; 79 80 private ScrollDriver driver; 81 82 private JButtonOperator increaseOperator = null; 83 private JButtonOperator decreaseOperator = null; 84 85 /** 86 * Constructor. 87 * 88 * @param b JSpinner component. 89 */ 90 public JSpinnerOperator(JSpinner b) { 91 super(b); 92 driver = DriverManager.getScrollDriver(getClass()); 93 } 94 95 /** 96 * Constructs a JSpinnerOperator object. 97 * 98 * @param cont a container 99 * @param chooser a component chooser specifying searching criteria. 100 * @param index an index between appropriate ones. 101 * @throws TimeoutExpiredException 102 */ 103 public JSpinnerOperator(ContainerOperator<?> cont, ComponentChooser chooser, int index) { 104 this((JSpinner) cont. 105 waitSubComponent(new JSpinnerFinder(chooser), 106 index)); 107 copyEnvironment(cont); 108 } 109 110 /** 111 * Constructs a JSpinnerOperator object. 112 * 113 * @param cont a container 114 * @param chooser a component chooser specifying searching criteria. 115 * @throws TimeoutExpiredException 116 */ 117 public JSpinnerOperator(ContainerOperator<?> cont, ComponentChooser chooser) { 118 this(cont, chooser, 0); 119 } 120 121 /** 122 * Constructs a JSpinnerOperator object. 123 * 124 * @param cont The operator for a container containing the sought for 125 * button. 126 * @param text toString() representation of the current spinner value. 127 * @param index Ordinal component index. The first component has 128 * {@code index} 0. 129 * @throws TimeoutExpiredException 130 */ 131 public JSpinnerOperator(ContainerOperator<?> cont, String text, int index) { 132 this((JSpinner) waitComponent(cont, 133 new JSpinnerByTextFinder(text, 134 cont.getComparator()), 135 index)); 136 copyEnvironment(cont); 137 } 138 139 /** 140 * Constructs a JSpinnerOperator object. 141 * 142 * @param cont The operator for a container containing the sought for 143 * button. 144 * @param text toString() representation of the current spinner value. 145 * @throws TimeoutExpiredException 146 */ 147 public JSpinnerOperator(ContainerOperator<?> cont, String text) { 148 this(cont, text, 0); 149 } 150 151 /** 152 * Constructor. Waits component in container first. Uses cont's timeout and 153 * output for waiting and to init operator. 154 * 155 * @param cont Operator pointing a container to search component in. 156 * @param index Ordinal component index. 157 * @throws TimeoutExpiredException 158 */ 159 public JSpinnerOperator(ContainerOperator<?> cont, int index) { 160 this((JSpinner) waitComponent(cont, 161 new JSpinnerFinder(), 162 index)); 163 copyEnvironment(cont); 164 } 165 166 /** 167 * Constructor. Waits component in container first. Uses cont's timeout and 168 * output for waiting and to init operator. 169 * 170 * @param cont Operator pointing a container to search component in. 171 * @throws TimeoutExpiredException 172 */ 173 public JSpinnerOperator(ContainerOperator<?> cont) { 174 this(cont, 0); 175 } 176 177 /** 178 * Searches JSpinner in container. 179 * 180 * @param cont Container to search component in. 181 * @param chooser org.netbeans.jemmy.ComponentChooser implementation. 182 * @param index Ordinal component index. 183 * @return JSpinner instance or null if component was not found. 184 */ 185 public static JSpinner findJSpinner(Container cont, ComponentChooser chooser, int index) { 186 return (JSpinner) findComponent(cont, new JSpinnerFinder(chooser), index); 187 } 188 189 /** 190 * Searches 0'th JSpinner in container. 191 * 192 * @param cont Container to search component in. 193 * @param chooser org.netbeans.jemmy.ComponentChooser implementation. 194 * @return JSpinner instance or null if component was not found. 195 */ 196 public static JSpinner findJSpinner(Container cont, ComponentChooser chooser) { 197 return findJSpinner(cont, chooser, 0); 198 } 199 200 /** 201 * Searches JSpinner in container. 202 * 203 * @param cont Container to search component in. 204 * @param index Ordinal component index. 205 * @return JSpinner instance or null if component was not found. 206 */ 207 public static JSpinner findJSpinner(Container cont, int index) { 208 return findJSpinner(cont, ComponentSearcher.getTrueChooser(Integer.toString(index) + "'th JSpinner instance"), index); 209 } 210 211 /** 212 * Searches 0'th JSpinner in container. 213 * 214 * @param cont Container to search component in. 215 * @return JSpinner instance or null if component was not found. 216 */ 217 public static JSpinner findJSpinner(Container cont) { 218 return findJSpinner(cont, 0); 219 } 220 221 /** 222 * Waits JSpinner in container. 223 * 224 * @param cont Container to search component in. 225 * @param chooser org.netbeans.jemmy.ComponentChooser implementation. 226 * @param index Ordinal component index. 227 * @return JSpinner instance or null if component was not displayed. 228 * @throws TimeoutExpiredException 229 */ 230 public static JSpinner waitJSpinner(Container cont, ComponentChooser chooser, int index) { 231 return (JSpinner) waitComponent(cont, new JSpinnerFinder(chooser), index); 232 } 233 234 /** 235 * Waits 0'th JSpinner in container. 236 * 237 * @param cont Container to search component in. 238 * @param chooser org.netbeans.jemmy.ComponentChooser implementation. 239 * @return JSpinner instance or null if component was not displayed. 240 * @throws TimeoutExpiredException 241 */ 242 public static JSpinner waitJSpinner(Container cont, ComponentChooser chooser) { 243 return waitJSpinner(cont, chooser, 0); 244 } 245 246 /** 247 * Waits JSpinner in container. 248 * 249 * @param cont Container to search component in. 250 * @param index Ordinal component index. 251 * @return JSpinner instance or null if component was not displayed. 252 * @throws TimeoutExpiredException 253 */ 254 public static JSpinner waitJSpinner(Container cont, int index) { 255 return waitJSpinner(cont, ComponentSearcher.getTrueChooser(Integer.toString(index) + "'th JSpinner instance"), index); 256 } 257 258 /** 259 * Waits 0'th JSpinner in container. 260 * 261 * @param cont Container to search component in. 262 * @return JSpinner instance or null if component was not displayed. 263 * @throws TimeoutExpiredException 264 */ 265 public static JSpinner waitJSpinner(Container cont) { 266 return waitJSpinner(cont, 0); 267 } 268 269 /** 270 * Checks operator's model type. 271 * 272 * @param oper an operator to check model 273 * @param modelClass a model class. 274 * @throws SpinnerModelException if an operator's model is not an instance 275 * of specified class. 276 */ 277 public static void checkModel(JSpinnerOperator oper, Class<?> modelClass) { 278 if (!modelClass.isInstance(oper.getModel())) { 279 throw (new SpinnerModelException("JSpinner model is not a " + modelClass.getName(), 280 oper.getSource())); 281 } 282 } 283 284 static { 285 Timeouts.initDefault("JSpinnerOperator.WholeScrollTimeout", WHOLE_SCROLL_TIMEOUT); 286 } 287 288 @Override 289 public void setOutput(TestOut out) { 290 output = out; 291 super.setOutput(output.createErrorOutput()); 292 } 293 294 @Override 295 public TestOut getOutput() { 296 return output; 297 } 298 299 @Override 300 public void setTimeouts(Timeouts timeouts) { 301 this.timeouts = timeouts; 302 super.setTimeouts(timeouts); 303 } 304 305 @Override 306 public Timeouts getTimeouts() { 307 return timeouts; 308 } 309 310 /** 311 * Returns an instance of {@code NumberSpinnerOperator} operator, the 312 * operator used for {@code JSpinner} having 313 * {@code SpinnerNumberModel} model. 314 * 315 * @return a {@code NumberSpinnerOperator} created for the same 316 * {@code JSpinner} as this operator. 317 * @throws SpinnerModelException if an operator's model is not an instance 318 * of {@code SpinnerNumberModel} 319 */ 320 public NumberSpinnerOperator getNumberSpinner() { 321 return new NumberSpinnerOperator(this); 322 } 323 324 /** 325 * Returns an instance of {@code ListSpinnerOperator} operator, the 326 * operator used for {@code JSpinner} having 327 * {@code SpinnerListModel} model. 328 * 329 * @return a {@code ListSpinnerOperator} created for the same 330 * {@code JSpinner} as this operator. 331 * @throws SpinnerModelException if an operator's model is not an instance 332 * of {@code SpinnerListModel} 333 */ 334 public ListSpinnerOperator getListSpinner() { 335 return new ListSpinnerOperator(this); 336 } 337 338 /** 339 * Returns an instance of {@code DateSpinnerOperator} operator, the 340 * operator used for {@code JSpinner} having 341 * {@code SpinnerDateModel} model. 342 * 343 * @return a {@code DateSpinnerOperator} created for the same 344 * {@code JSpinner} as this operator. 345 * @throws SpinnerModelException if an operator's model is not an instance 346 * of {@code SpinnerDateModel} 347 */ 348 public DateSpinnerOperator getDateSpinner() { 349 return new DateSpinnerOperator(this); 350 } 351 352 /** 353 * Scrolls to reach a condition specified by {@code ScrollAdjuster} 354 * 355 * @param adj scrolling criteria. 356 */ 357 public void scrollTo(final ScrollAdjuster adj) { 358 produceTimeRestricted(new Action<Void, Void>() { 359 @Override 360 public Void launch(Void obj) { 361 driver.scroll(JSpinnerOperator.this, adj); 362 return null; 363 } 364 365 @Override 366 public String getDescription() { 367 return "Scrolling"; 368 } 369 370 @Override 371 public String toString() { 372 return "JSpinnerOperator.scrollTo.Action{description = " + getDescription() + '}'; 373 } 374 }, "JSpinnerOperator.WholeScrollTimeout"); 375 } 376 377 /** 378 * Scrolls to maximum value. 379 * 380 * @throws SpinnerModelException if an operator's model does not have a 381 * maximum value. 382 */ 383 public void scrollToMaximum() { 384 produceTimeRestricted(new Action<Void, Void>() { 385 @Override 386 public Void launch(Void obj) { 387 driver.scrollToMaximum(JSpinnerOperator.this, SwingConstants.VERTICAL); 388 return null; 389 } 390 391 @Override 392 public String getDescription() { 393 return "Scrolling"; 394 } 395 396 @Override 397 public String toString() { 398 return "JSpinnerOperator.scrollToMaximum.Action{description = " + getDescription() + '}'; 399 } 400 }, "JSpinnerOperator.WholeScrollTimeout"); 401 } 402 403 /** 404 * Scrolls to minimum value. 405 * 406 * @throws SpinnerModelException if an operator's model does not have a 407 * minimum value. 408 */ 409 public void scrollToMinimum() { 410 produceTimeRestricted(new Action<Void, Void>() { 411 @Override 412 public Void launch(Void obj) { 413 driver.scrollToMinimum(JSpinnerOperator.this, SwingConstants.VERTICAL); 414 return null; 415 } 416 417 @Override 418 public String getDescription() { 419 return "Scrolling"; 420 } 421 422 @Override 423 public String toString() { 424 return "JSpinnerOperator.scrollToMinimum.Action{description = " + getDescription() + '}'; 425 } 426 }, "JSpinnerOperator.WholeScrollTimeout"); 427 } 428 429 /** 430 * Scrolls to exact match of a spinner value to the specified value. 431 * 432 * @param value an value to scroll to. 433 * @param direction a scrolling direction - one of 434 * {@code ScrollAdjuster.*_SCROLL_DIRECTION} fields. 435 */ 436 public void scrollToObject(Object value, int direction) { 437 scrollTo(new ExactScrollAdjuster(this, value, direction)); 438 } 439 440 /** 441 * Scrolls to matching of <code>getValue().toString() with the pattern. 442 * 443 * @param pattern a pattern to compare with 444 * @param comparator a string comparision criteria 445 * @param direction a scrolling direction - one of 446 * {@code ScrollAdjuster.*_SCROLL_DIRECTION} fields. 447 */ 448 public void scrollToString(String pattern, StringComparator comparator, int direction) { 449 scrollTo(new ToStringScrollAdjuster(this, pattern, comparator, direction)); 450 } 451 452 /** 453 * Scrolls to matching of {@code getValue().toString()} with the 454 * pattern. Uses {@code StringComparator} assigned to the operator. 455 * 456 * @param pattern a pattern to compare with 457 * @param direction a scrolling direction - one of 458 * {@code ScrollAdjuster.*_SCROLL_DIRECTION} fields. 459 */ 460 public void scrollToString(String pattern, int direction) { 461 scrollToString(pattern, getComparator(), direction); 462 } 463 464 /** 465 * Returns an operator for a button used for value increasing. 466 * 467 * @return an operator for a first <code>JButton<code> inside this spinner. 468 */ 469 public JButtonOperator getIncreaseOperator() { 470 if (increaseOperator == null) { 471 increaseOperator = (JButtonOperator) createSubOperator(new JButtonOperator.JButtonFinder(), 0); 472 increaseOperator.copyEnvironment(this); 473 increaseOperator.setOutput(getOutput().createErrorOutput()); 474 } 475 return increaseOperator; 476 } 477 478 /** 479 * Returns an operator for a button used for value decreasing. 480 * 481 * @return an operator for a second <code>JButton<code> inside this spinner. 482 */ 483 public JButtonOperator getDecreaseOperator() { 484 if (decreaseOperator == null) { 485 decreaseOperator = (JButtonOperator) createSubOperator(new JButtonOperator.JButtonFinder(), 1); 486 decreaseOperator.copyEnvironment(this); 487 decreaseOperator.setOutput(getOutput().createErrorOutput()); 488 } 489 return decreaseOperator; 490 } 491 492 /** 493 * Returns a minimal value. Returns null if model is not one of the 494 * following: {@code javax.swing.SpinnerDateModel}, 495 * {@code javax.swing.SpinnerListModel}, 496 * {@code javax.swing.SpinnerNumberModel}. Also, returns null if the 497 * model does not have a minimal value. 498 * 499 * @return a minimal value. 500 */ 501 public Object getMinimum() { 502 SpinnerModel model = getModel(); 503 if (model instanceof SpinnerNumberModel) { 504 return ((SpinnerNumberModel) model).getMinimum(); 505 } else if (model instanceof SpinnerDateModel) { 506 return ((SpinnerDateModel) model).getEnd(); 507 } else if (model instanceof SpinnerListModel) { 508 List<?> list = ((SpinnerListModel) model).getList(); 509 return list.get(list.size() - 1); 510 } else { 511 return null; 512 } 513 } 514 515 /** 516 * Returns a maximal value. Returns null if model is not one of the 517 * following: {@code javax.swing.SpinnerDateModel}, 518 * {@code javax.swing.SpinnerListModel}, 519 * {@code javax.swing.SpinnerNumberModel}. Also, returns null if the 520 * model does not have a maximal value. 521 * 522 * @return a maximal value. 523 */ 524 public Object getMaximum() { 525 SpinnerModel model = getModel(); 526 if (model instanceof SpinnerNumberModel) { 527 return ((SpinnerNumberModel) model).getMaximum(); 528 } else if (model instanceof SpinnerDateModel) { 529 return ((SpinnerDateModel) model).getEnd(); 530 } else if (model instanceof SpinnerListModel) { 531 List<?> list = ((SpinnerListModel) model).getList(); 532 return list.get(list.size() - 1); 533 } else { 534 return null; 535 } 536 } 537 538 @Override 539 public Hashtable<String, Object> getDump() { 540 Hashtable<String, Object> result = super.getDump(); 541 result.put(VALUE_DPROP, ((JSpinner) getSource()).getValue().toString()); 542 return result; 543 } 544 545 //////////////////////////////////////////////////////// 546 //Mapping // 547 /** 548 * Maps {@code JSpinner.getValue()} through queue 549 */ 550 public Object getValue() { 551 return (runMapping(new MapAction<Object>("getValue") { 552 @Override 553 public Object map() { 554 return ((JSpinner) getSource()).getValue(); 555 } 556 })); 557 } 558 559 /** 560 * Maps {@code JSpinner.setValue(Object)} through queue 561 */ 562 public void setValue(final Object object) { 563 runMapping(new MapVoidAction("setValue") { 564 @Override 565 public void map() { 566 ((JSpinner) getSource()).setValue(object); 567 } 568 }); 569 } 570 571 /** 572 * Maps {@code JSpinner.getUI()} through queue 573 */ 574 public SpinnerUI getUI() { 575 return (runMapping(new MapAction<SpinnerUI>("getUI") { 576 @Override 577 public SpinnerUI map() { 578 return ((JSpinner) getSource()).getUI(); 579 } 580 })); 581 } 582 583 /** 584 * Maps {@code JSpinner.setUI(SpinnerUI)} through queue 585 */ 586 public void setUI(final SpinnerUI spinnerUI) { 587 runMapping(new MapVoidAction("setUI") { 588 @Override 589 public void map() { 590 ((JSpinner) getSource()).setUI(spinnerUI); 591 } 592 }); 593 } 594 595 /** 596 * Maps {@code JSpinner.setModel(SpinnerModel)} through queue 597 */ 598 public void setModel(final SpinnerModel spinnerModel) { 599 runMapping(new MapVoidAction("setModel") { 600 @Override 601 public void map() { 602 ((JSpinner) getSource()).setModel(spinnerModel); 603 } 604 }); 605 } 606 607 /** 608 * Maps {@code JSpinner.getModel()} through queue 609 */ 610 public SpinnerModel getModel() { 611 return (runMapping(new MapAction<SpinnerModel>("getModel") { 612 @Override 613 public SpinnerModel map() { 614 return ((JSpinner) getSource()).getModel(); 615 } 616 })); 617 } 618 619 /** 620 * Maps {@code JSpinner.getNextValue()} through queue 621 */ 622 public Object getNextValue() { 623 return (runMapping(new MapAction<Object>("getNextValue") { 624 @Override 625 public Object map() { 626 return ((JSpinner) getSource()).getNextValue(); 627 } 628 })); 629 } 630 631 /** 632 * Maps {@code JSpinner.addChangeListener(ChangeListener)} through queue 633 */ 634 public void addChangeListener(final ChangeListener changeListener) { 635 runMapping(new MapVoidAction("addChangeListener") { 636 @Override 637 public void map() { 638 ((JSpinner) getSource()).addChangeListener(changeListener); 639 } 640 }); 641 } 642 643 /** 644 * Maps {@code JSpinner.removeChangeListener(ChangeListener)} through queue 645 */ 646 public void removeChangeListener(final ChangeListener changeListener) { 647 runMapping(new MapVoidAction("removeChangeListener") { 648 @Override 649 public void map() { 650 ((JSpinner) getSource()).removeChangeListener(changeListener); 651 } 652 }); 653 } 654 655 /** 656 * Maps {@code JSpinner.getChangeListeners()} through queue 657 */ 658 public ChangeListener[] getChangeListeners() { 659 return ((ChangeListener[]) runMapping(new MapAction<Object>("getChangeListeners") { 660 @Override 661 public Object map() { 662 return ((JSpinner) getSource()).getChangeListeners(); 663 } 664 })); 665 } 666 667 /** 668 * Maps {@code JSpinner.getPreviousValue()} through queue 669 */ 670 public Object getPreviousValue() { 671 return (runMapping(new MapAction<Object>("getPreviousValue") { 672 @Override 673 public Object map() { 674 return ((JSpinner) getSource()).getPreviousValue(); 675 } 676 })); 677 } 678 679 /** 680 * Maps {@code JSpinner.setEditor(JComponent)} through queue 681 */ 682 public void setEditor(final JComponent jComponent) { 683 runMapping(new MapVoidAction("setEditor") { 684 @Override 685 public void map() { 686 ((JSpinner) getSource()).setEditor(jComponent); 687 } 688 }); 689 } 690 691 /** 692 * Maps {@code JSpinner.getEditor()} through queue 693 */ 694 public JComponent getEditor() { 695 return (runMapping(new MapAction<JComponent>("getEditor") { 696 @Override 697 public JComponent map() { 698 return ((JSpinner) getSource()).getEditor(); 699 } 700 })); 701 } 702 703 /** 704 * Maps {@code JSpinner.commitEdit()} through queue 705 */ 706 public void commitEdit() { 707 runMapping(new MapVoidAction("commitEdit") { 708 @Override 709 public void map() throws ParseException { 710 ((JSpinner) getSource()).commitEdit(); 711 } 712 }); 713 } 714 715 //End of mapping // 716 //////////////////////////////////////////////////////// 717 /** 718 * Allows to find component by text. 719 */ 720 public static class JSpinnerByTextFinder implements ComponentChooser { 721 722 String label; 723 StringComparator comparator; 724 725 /** 726 * Constructs JSpinnerByTextFinder. 727 * 728 * @param lb a text pattern 729 * @param comparator specifies string comparision algorithm. 730 */ 731 public JSpinnerByTextFinder(String lb, StringComparator comparator) { 732 label = lb; 733 this.comparator = comparator; 734 } 735 736 /** 737 * Constructs JSpinnerByTextFinder. 738 * 739 * @param lb a text pattern 740 */ 741 public JSpinnerByTextFinder(String lb) { 742 this(lb, Operator.getDefaultStringComparator()); 743 } 744 745 @Override 746 public boolean checkComponent(Component comp) { 747 if (comp instanceof JSpinner) { 748 if (((JSpinner) comp).getValue() != null) { 749 return (comparator.equals(((JSpinner) comp).getValue().toString(), 750 label)); 751 } 752 } 753 return false; 754 } 755 756 @Override 757 public String getDescription() { 758 return "JSpinner with text \"" + label + "\""; 759 } 760 761 @Override 762 public String toString() { 763 return "JSpinnerByTextFinder{" + "label=" + label + ", comparator=" + comparator + '}'; 764 } 765 } 766 767 /** 768 * Checks component type. 769 */ 770 public static class JSpinnerFinder extends Finder { 771 772 /** 773 * Constructs JSpinnerFinder. 774 * 775 * @param sf other searching criteria. 776 */ 777 public JSpinnerFinder(ComponentChooser sf) { 778 super(JSpinner.class, sf); 779 } 780 781 /** 782 * Constructs JSpinnerFinder. 783 */ 784 public JSpinnerFinder() { 785 super(JSpinner.class); 786 } 787 } 788 789 /** 790 * A {@code ScrollAdjuster} to be used for {@code JSpinner} 791 * component having {@code SpinnerNumberModel} model. 792 * 793 * @see NumberSpinnerOperator 794 */ 795 public static class NumberScrollAdjuster implements ScrollAdjuster { 796 797 SpinnerNumberModel model; 798 double value; 799 800 /** 801 * Constructs a {@code NumberScrollAdjuster} object. 802 * 803 * @param oper an operator to work with. 804 * @param value a value to scroll to. 805 */ 806 public NumberScrollAdjuster(JSpinnerOperator oper, double value) { 807 this.value = value; 808 checkModel(oper, SpinnerNumberModel.class); 809 model = (SpinnerNumberModel) oper.getModel(); 810 } 811 812 /** 813 * Constructs a {@code NumberScrollAdjuster} object. 814 * 815 * @param oper an operator to work with. 816 * @param value a value to scroll to. 817 */ 818 public NumberScrollAdjuster(JSpinnerOperator oper, Number value) { 819 this(oper, value.doubleValue()); 820 } 821 822 @Override 823 public int getScrollDirection() { 824 if (value > model.getNumber().doubleValue()) { 825 return ScrollAdjuster.INCREASE_SCROLL_DIRECTION; 826 } else if (value < model.getNumber().doubleValue()) { 827 return ScrollAdjuster.DECREASE_SCROLL_DIRECTION; 828 } else { 829 return ScrollAdjuster.DO_NOT_TOUCH_SCROLL_DIRECTION; 830 } 831 } 832 833 @Override 834 public int getScrollOrientation() { 835 return SwingConstants.VERTICAL; 836 } 837 838 @Override 839 public String getDescription() { 840 return "Spin to " + value + " value"; 841 } 842 843 @Override 844 public String toString() { 845 return "NumberScrollAdjuster{" + "model=" + model + ", value=" + value + '}'; 846 } 847 } 848 849 /** 850 * A {@code ScrollAdjuster} to be used for {@code JSpinner} 851 * component having {@code SpinnerListModel} model. 852 * 853 * @see ListSpinnerOperator 854 */ 855 public static class ListScrollAdjuster implements ScrollAdjuster { 856 857 SpinnerListModel model; 858 int itemIndex; 859 List<?> elements; 860 861 private ListScrollAdjuster(JSpinnerOperator oper) { 862 checkModel(oper, SpinnerListModel.class); 863 model = (SpinnerListModel) oper.getModel(); 864 elements = model.getList(); 865 } 866 867 /** 868 * Constructs a {@code ListScrollAdjuster} object. 869 * 870 * @param oper an operator to work with. 871 * @param value a value to scroll to. 872 */ 873 public ListScrollAdjuster(JSpinnerOperator oper, Object value) { 874 this(oper); 875 this.itemIndex = elements.indexOf(value); 876 } 877 878 /** 879 * Constructs a {@code ListScrollAdjuster} object. 880 * 881 * @param oper an operator to work with. 882 * @param itemIndex an item index to scroll to. 883 */ 884 public ListScrollAdjuster(JSpinnerOperator oper, int itemIndex) { 885 this(oper); 886 this.itemIndex = itemIndex; 887 } 888 889 @Override 890 public int getScrollDirection() { 891 int curIndex = elements.indexOf(model.getValue()); 892 if (itemIndex > curIndex) { 893 return ScrollAdjuster.INCREASE_SCROLL_DIRECTION; 894 } else if (itemIndex < curIndex) { 895 return ScrollAdjuster.DECREASE_SCROLL_DIRECTION; 896 } else { 897 return ScrollAdjuster.DO_NOT_TOUCH_SCROLL_DIRECTION; 898 } 899 } 900 901 @Override 902 public int getScrollOrientation() { 903 return SwingConstants.VERTICAL; 904 } 905 906 @Override 907 public String getDescription() { 908 return "Spin to " + Integer.toString(itemIndex) + "'th item"; 909 } 910 911 @Override 912 public String toString() { 913 return "ListScrollAdjuster{" + "model=" + model + ", itemIndex=" + itemIndex + ", elements=" + elements + '}'; 914 } 915 } 916 917 /** 918 * A {@code ScrollAdjuster} to be used for {@code JSpinner} 919 * component having {@code SpinnerDateModel} model. 920 * 921 * @see DateSpinnerOperator 922 */ 923 public static class DateScrollAdjuster implements ScrollAdjuster { 924 925 Date date; 926 SpinnerDateModel model; 927 928 /** 929 * Constructs a {@code DateScrollAdjuster} object. 930 * 931 * @param oper an operator to work with. 932 * @param date a date to scroll to. 933 */ 934 public DateScrollAdjuster(JSpinnerOperator oper, Date date) { 935 this.date = date; 936 checkModel(oper, SpinnerDateModel.class); 937 model = (SpinnerDateModel) oper.getModel(); 938 } 939 940 @Override 941 public int getScrollDirection() { 942 if (date.after(model.getDate())) { 943 return ScrollAdjuster.INCREASE_SCROLL_DIRECTION; 944 } else if (date.before(model.getDate())) { 945 return ScrollAdjuster.DECREASE_SCROLL_DIRECTION; 946 } else { 947 return ScrollAdjuster.DO_NOT_TOUCH_SCROLL_DIRECTION; 948 } 949 } 950 951 @Override 952 public int getScrollOrientation() { 953 return SwingConstants.VERTICAL; 954 } 955 956 @Override 957 public String getDescription() { 958 return "Spin to " + date.toString() + " date"; 959 } 960 961 @Override 962 public String toString() { 963 return "DateScrollAdjuster{" + "date=" + date + ", model=" + model + '}'; 964 } 965 } 966 967 /** 968 * Abstract class for a scrolling of a spinner having unknown model type. A 969 * subclass needs to override {@code reached(Object)} method to specify a 970 * criteria of successful scrolling. 971 */ 972 public abstract static class ObjectScrollAdjuster implements ScrollAdjuster { 973 974 SpinnerModel model; 975 int direction; 976 977 /** 978 * Constructs a {@code ObjectScrollAdjuster} object. 979 * 980 * @param oper an operator to work with. 981 * @param direction a scrolling direction - one of 982 * {@code ScrollAdjuster.*_SCROLL_DIRECTION} fields. 983 */ 984 public ObjectScrollAdjuster(JSpinnerOperator oper, int direction) { 985 this.direction = direction; 986 model = oper.getModel(); 987 } 988 989 @Override 990 public int getScrollDirection() { 991 if (reached(model.getValue())) { 992 return ScrollAdjuster.DO_NOT_TOUCH_SCROLL_DIRECTION; 993 } else if (direction == ScrollAdjuster.INCREASE_SCROLL_DIRECTION 994 && model.getNextValue() != null 995 || direction == ScrollAdjuster.DECREASE_SCROLL_DIRECTION 996 && model.getPreviousValue() != null) { 997 return direction; 998 } else { 999 return ScrollAdjuster.DO_NOT_TOUCH_SCROLL_DIRECTION; 1000 } 1001 } 1002 1003 public abstract boolean reached(Object curvalue); 1004 1005 @Override 1006 public int getScrollOrientation() { 1007 return SwingConstants.VERTICAL; 1008 } 1009 } 1010 1011 /** 1012 * Class for a scrolling of a spinner having unknown model type. Checks 1013 * spinner value for exact equality with a specified value. 1014 */ 1015 public static class ExactScrollAdjuster extends ObjectScrollAdjuster { 1016 1017 Object obj; 1018 1019 /** 1020 * Constructs a {@code ExactScrollAdjuster} object. 1021 * 1022 * @param oper an operator to work with. 1023 * @param direction a scrolling direction - one of 1024 * {@code ScrollAdjuster.*_SCROLL_DIRECTION} fields. 1025 */ 1026 public ExactScrollAdjuster(JSpinnerOperator oper, Object obj, int direction) { 1027 super(oper, direction); 1028 this.obj = obj; 1029 } 1030 1031 @Override 1032 public boolean reached(Object curvalue) { 1033 return curvalue != null && curvalue.equals(obj); 1034 } 1035 1036 @Override 1037 public String getDescription() { 1038 return "Spin to " + obj.toString() + " value"; 1039 } 1040 1041 @Override 1042 public String toString() { 1043 return "ExactScrollAdjuster{" + "obj=" + obj + '}'; 1044 } 1045 1046 @Override 1047 public int getScrollOrientation() { 1048 return SwingConstants.VERTICAL; 1049 } 1050 } 1051 1052 /** 1053 * Class for a scrolling of a spinner having unknown model type. Checks 1054 * spinner value's toString() reprsentation to match a string pattern. 1055 */ 1056 public static class ToStringScrollAdjuster extends ObjectScrollAdjuster { 1057 1058 String pattern; 1059 StringComparator comparator; 1060 1061 /** 1062 * Constructs a {@code ToStringScrollAdjuster} object. 1063 * 1064 * @param oper an operator to work with. 1065 * @param pattern a pattern to compare with 1066 * @param comparator specifies string comparision algorithm. 1067 * @param direction a scrolling direction - one of 1068 * {@code ScrollAdjuster.*_SCROLL_DIRECTION} fields. 1069 */ 1070 public ToStringScrollAdjuster(JSpinnerOperator oper, String pattern, StringComparator comparator, int direction) { 1071 super(oper, direction); 1072 this.pattern = pattern; 1073 this.comparator = comparator; 1074 } 1075 1076 /** 1077 * Constructs a {@code ToStringScrollAdjuster} object. Uses 1078 * {@code StringComparator} assigned to the operator. 1079 * 1080 * @param oper an operator to work with. 1081 * @param pattern a pattern to compare with 1082 * @param direction a scrolling direction - one of 1083 * {@code ScrollAdjuster.*_SCROLL_DIRECTION} fields. 1084 */ 1085 public ToStringScrollAdjuster(JSpinnerOperator oper, String pattern, int direction) { 1086 this(oper, pattern, oper.getComparator(), direction); 1087 } 1088 1089 @Override 1090 public boolean reached(Object curvalue) { 1091 return curvalue != null && comparator.equals(curvalue.toString(), pattern); 1092 } 1093 1094 @Override 1095 public String getDescription() { 1096 return "Spin to \"" + pattern + "\" value"; 1097 } 1098 1099 @Override 1100 public String toString() { 1101 return "ToStringScrollAdjuster{" + "pattern=" + pattern + ", comparator=" + comparator + '}'; 1102 } 1103 1104 @Override 1105 public int getScrollOrientation() { 1106 return SwingConstants.VERTICAL; 1107 } 1108 } 1109 1110 /** 1111 * Provides some specific functionality for {@code JSpinner} components 1112 * having {@code SpinnerNumberModel} model. Constructor of this object 1113 * is private - it cannot be received only from another JSpinnerOperator 1114 * instance. 1115 * 1116 * @see #getNumberSpinner 1117 */ 1118 public static class NumberSpinnerOperator extends JSpinnerOperator { 1119 1120 private NumberSpinnerOperator(JSpinnerOperator spinner) { 1121 super((JSpinner) spinner.getSource()); 1122 copyEnvironment(spinner); 1123 checkModel(this, SpinnerNumberModel.class); 1124 } 1125 1126 /** 1127 * Costs spinner's model to <code>SpinnerNumberModel<code>. 1128 * 1129 * @return a spinner model. 1130 */ 1131 public SpinnerNumberModel getNumberModel() { 1132 return (SpinnerNumberModel) getModel(); 1133 } 1134 1135 /** 1136 * Scrolls to a double value. 1137 * 1138 * @param value a value to scroll to. 1139 */ 1140 public void scrollToValue(double value) { 1141 scrollTo(new NumberScrollAdjuster(this, value)); 1142 } 1143 1144 /** 1145 * Scrolls to a number value. 1146 * 1147 * @param value a value to scroll to. 1148 */ 1149 public void scrollToValue(Number value) { 1150 scrollTo(new NumberScrollAdjuster(this, value)); 1151 } 1152 } 1153 1154 /** 1155 * Provides some specific functionality for {@code JSpinner} components 1156 * having {@code SpinnerListModel} model. Constructor of this object is 1157 * private - it cannot be received only from another JSpinnerOperator 1158 * instance. 1159 * 1160 * @see #getListSpinner 1161 */ 1162 public static class ListSpinnerOperator extends JSpinnerOperator { 1163 1164 private ListSpinnerOperator(JSpinnerOperator spinner) { 1165 super((JSpinner) spinner.getSource()); 1166 copyEnvironment(spinner); 1167 checkModel(this, SpinnerListModel.class); 1168 } 1169 1170 /** 1171 * Costs spinner's model to <code>SpinnerListModel<code>. 1172 * 1173 * @return a spinner model. 1174 */ 1175 public SpinnerListModel getListModel() { 1176 return (SpinnerListModel) getModel(); 1177 } 1178 1179 /** 1180 * Looks for an index of an item having {@code toString()} matching 1181 * a specified pattern. 1182 * 1183 * @param pattern a string pattern 1184 * @param comparator a string comparision criteria. 1185 */ 1186 public int findItem(String pattern, StringComparator comparator) { 1187 List<?> list = getListModel().getList(); 1188 for (int i = 0; i < list.size(); i++) { 1189 if (comparator.equals(list.get(i).toString(), pattern)) { 1190 return i; 1191 } 1192 } 1193 return -1; 1194 } 1195 1196 /** 1197 * Looks for an index of an item having {@code toString()} matching 1198 * a specified pattern. Uses a {@code StringComparator} assigned to 1199 * the operator. 1200 * 1201 * @param pattern a string pattern 1202 */ 1203 public int findItem(String pattern) { 1204 return findItem(pattern, getComparator()); 1205 } 1206 1207 /** 1208 * Scrolls to an item having specified instance. 1209 * 1210 * @param index an index to scroll to. 1211 */ 1212 public void scrollToIndex(int index) { 1213 scrollTo(new ListScrollAdjuster(this, index)); 1214 } 1215 1216 /** 1217 * Scrolls to {@code getValue().toString()} match a specified 1218 * pattern. 1219 * 1220 * @param pattern a string pattern 1221 * @param comparator a string comparision criteria. 1222 */ 1223 public void scrollToString(String pattern, StringComparator comparator) { 1224 int index = findItem(pattern, comparator); 1225 if (index != -1) { 1226 scrollToIndex(index); 1227 } else { 1228 throw (new JemmyException("No \"" + pattern + "\" item in JSpinner", getSource())); 1229 } 1230 } 1231 1232 /** 1233 * Scrolls to {@code getValue().toString()} match a specified 1234 * pattern. Uses a {@code StringComparator} assigned to the 1235 * operator. 1236 * 1237 * @param pattern a string pattern 1238 */ 1239 public void scrollToString(String pattern) { 1240 scrollToString(pattern, getComparator()); 1241 } 1242 } 1243 1244 /** 1245 * Provides some specific functionality for {@code JSpinner} components 1246 * having {@code SpinnerDateModel} model. Constructor of this object is 1247 * private - it cannot be received only from another JSpinnerOperator 1248 * instance. 1249 * 1250 * @see #getDateSpinner 1251 */ 1252 public static class DateSpinnerOperator extends JSpinnerOperator { 1253 1254 private DateSpinnerOperator(JSpinnerOperator spinner) { 1255 super((JSpinner) spinner.getSource()); 1256 copyEnvironment(spinner); 1257 checkModel(this, SpinnerDateModel.class); 1258 } 1259 1260 /** 1261 * Costs spinner's model to <code>SpinnerDateModel<code>. 1262 * 1263 * @return a spinner model. 1264 */ 1265 public SpinnerDateModel getDateModel() { 1266 return (SpinnerDateModel) getModel(); 1267 } 1268 1269 /** 1270 * Scrolls to a date. 1271 * 1272 * @param date a date to scroll to. 1273 */ 1274 public void scrollToDate(Date date) { 1275 scrollTo(new DateScrollAdjuster(this, date)); 1276 } 1277 } 1278 1279 /** 1280 * Exception is thown whenever spinner model is threated wrong. 1281 */ 1282 public static class SpinnerModelException extends JemmyException { 1283 1284 private static final long serialVersionUID = 42L; 1285 1286 /** 1287 * Constructs a {@code SpinnerModelException} object. 1288 * 1289 * @param message error message. 1290 * @param comp a spinner which model cased the exception. 1291 */ 1292 public SpinnerModelException(String message, Component comp) { 1293 super(message, comp); 1294 } 1295 } 1296 }