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