1 /* 2 * Copyright (c) 2000, 2014, 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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package javax.swing; 27 28 import java.util.*; 29 import java.io.Serializable; 30 31 32 /** 33 * A <code>SpinnerModel</code> for sequences of numbers. 34 * The upper and lower bounds of the sequence are defined 35 * by properties called <code>minimum</code> and 36 * <code>maximum</code>. The size of the increase or decrease 37 * computed by the <code>nextValue</code> and 38 * <code>previousValue</code> methods is defined by a property called 39 * <code>stepSize</code>. The <code>minimum</code> and 40 * <code>maximum</code> properties can be <code>null</code> 41 * to indicate that the sequence has no lower or upper limit. 42 * All of the properties in this class are defined in terms of two 43 * generic types: <code>Number</code> and 44 * <code>Comparable</code>, so that all Java numeric types 45 * may be accommodated. Internally, there's only support for 46 * values whose type is one of the primitive <code>Number</code> types: 47 * <code>Double</code>, <code>Float</code>, <code>Long</code>, 48 * <code>Integer</code>, <code>Short</code>, or <code>Byte</code>. 49 * <p> 50 * To create a <code>SpinnerNumberModel</code> for the integer 51 * range zero to one hundred, with 52 * fifty as the initial value, one could write: 53 * <pre> 54 * Integer value = Integer.valueOf(50); 55 * Integer min = Integer.valueOf(0); 56 * Integer max = Integer.valueOf(100); 57 * Integer step = Integer.valueOf(1); 58 * SpinnerNumberModel model = new SpinnerNumberModel(value, min, max, step); 59 * int fifty = model.getNumber().intValue(); 60 * </pre> 61 * <p> 62 * Spinners for integers and doubles are common, so special constructors 63 * for these cases are provided. For example to create the model in 64 * the previous example, one could also write: 65 * <pre> 66 * SpinnerNumberModel model = new SpinnerNumberModel(50, 0, 100, 1); 67 * </pre> 68 * <p> 69 * This model inherits a <code>ChangeListener</code>. 70 * The <code>ChangeListeners</code> are notified 71 * whenever the model's <code>value</code>, <code>stepSize</code>, 72 * <code>minimum</code>, or <code>maximum</code> properties changes. 73 * 74 * @see JSpinner 75 * @see SpinnerModel 76 * @see AbstractSpinnerModel 77 * @see SpinnerListModel 78 * @see SpinnerDateModel 79 * 80 * @author Hans Muller 81 * @since 1.4 82 */ 83 @SuppressWarnings("serial") // Superclass is not serializable across versions 84 public class SpinnerNumberModel extends AbstractSpinnerModel implements Serializable 85 { 86 private Number stepSize, value; 87 // Both minimum and maximum are logically Comparable<? extends 88 // Number>, but that type is awkward to use since different 89 // instances of Number are not naturally Comparable. For example, 90 // a Double implements Comparable<Double> and an Integer 91 // implements Comparable<Integer>. Neither Integer nor Double will 92 // have a bridge method for Comparable<Number>. However, it safe 93 // to cast Comparable<?> to Comparable<Object> since all 94 // Comparables will have a compare(Object> method, possibly as a 95 // bridge. 96 private Comparable<?> minimum, maximum; 97 98 99 /** 100 * Constructs a <code>SpinnerModel</code> that represents 101 * a closed sequence of 102 * numbers from <code>minimum</code> to <code>maximum</code>. The 103 * <code>nextValue</code> and <code>previousValue</code> methods 104 * compute elements of the sequence by adding or subtracting 105 * <code>stepSize</code> respectively. All of the parameters 106 * must be mutually <code>Comparable</code>, <code>value</code> 107 * and <code>stepSize</code> must be instances of <code>Integer</code> 108 * <code>Long</code>, <code>Float</code>, or <code>Double</code>. 109 * <p> 110 * The <code>minimum</code> and <code>maximum</code> parameters 111 * can be <code>null</code> to indicate that the range doesn't 112 * have an upper or lower bound. 113 * If <code>value</code> or <code>stepSize</code> is <code>null</code>, 114 * or if both <code>minimum</code> and <code>maximum</code> 115 * are specified and <code>minimum > maximum</code> then an 116 * <code>IllegalArgumentException</code> is thrown. 117 * Similarly if <code>(minimum <= value <= maximum</code>) is false, 118 * an <code>IllegalArgumentException</code> is thrown. 119 * 120 * @param value the current (non <code>null</code>) value of the model 121 * @param minimum the first number in the sequence or <code>null</code> 122 * @param maximum the last number in the sequence or <code>null</code> 123 * @param stepSize the difference between elements of the sequence 124 * 125 * @throws IllegalArgumentException if stepSize or value is 126 * <code>null</code> or if the following expression is false: 127 * <code>minimum <= value <= maximum</code> 128 */ 129 @SuppressWarnings("unchecked") // Casts to Comparable<Object> SpinnerNumberModel(Number value, Comparable<?> minimum, Comparable<?> maximum, Number stepSize)130 public SpinnerNumberModel(Number value, 131 Comparable<?> minimum, 132 Comparable<?> maximum, 133 Number stepSize) { 134 if ((value == null) || (stepSize == null)) { 135 throw new IllegalArgumentException("value and stepSize must be non-null"); 136 } 137 if (!(((minimum == null) || (((Comparable<Object>)minimum).compareTo(value) <= 0)) && 138 ((maximum == null) || (((Comparable<Object>)maximum).compareTo(value) >= 0)))) { 139 throw new IllegalArgumentException("(minimum <= value <= maximum) is false"); 140 } 141 this.value = value; 142 this.minimum = minimum; 143 this.maximum = maximum; 144 this.stepSize = stepSize; 145 } 146 147 148 /** 149 * Constructs a <code>SpinnerNumberModel</code> with the specified 150 * <code>value</code>, <code>minimum</code>/<code>maximum</code> bounds, 151 * and <code>stepSize</code>. 152 * 153 * @param value the current value of the model 154 * @param minimum the first number in the sequence 155 * @param maximum the last number in the sequence 156 * @param stepSize the difference between elements of the sequence 157 * @throws IllegalArgumentException if the following expression is false: 158 * <code>minimum <= value <= maximum</code> 159 */ SpinnerNumberModel(int value, int minimum, int maximum, int stepSize)160 public SpinnerNumberModel(int value, int minimum, int maximum, int stepSize) { 161 this(Integer.valueOf(value), Integer.valueOf(minimum), Integer.valueOf(maximum), Integer.valueOf(stepSize)); 162 } 163 164 165 /** 166 * Constructs a <code>SpinnerNumberModel</code> with the specified 167 * <code>value</code>, <code>minimum</code>/<code>maximum</code> bounds, 168 * and <code>stepSize</code>. 169 * 170 * @param value the current value of the model 171 * @param minimum the first number in the sequence 172 * @param maximum the last number in the sequence 173 * @param stepSize the difference between elements of the sequence 174 * @throws IllegalArgumentException if the following expression is false: 175 * <code>minimum <= value <= maximum</code> 176 */ SpinnerNumberModel(double value, double minimum, double maximum, double stepSize)177 public SpinnerNumberModel(double value, double minimum, double maximum, double stepSize) { 178 this(Double.valueOf(value), Double.valueOf(minimum), 179 Double.valueOf(maximum), Double.valueOf(stepSize)); 180 } 181 182 183 /** 184 * Constructs a <code>SpinnerNumberModel</code> with no 185 * <code>minimum</code> or <code>maximum</code> value, 186 * <code>stepSize</code> equal to one, and an initial value of zero. 187 */ SpinnerNumberModel()188 public SpinnerNumberModel() { 189 this(Integer.valueOf(0), null, null, Integer.valueOf(1)); 190 } 191 192 193 /** 194 * Changes the lower bound for numbers in this sequence. 195 * If <code>minimum</code> is <code>null</code>, 196 * then there is no lower bound. No bounds checking is done here; 197 * the new <code>minimum</code> value may invalidate the 198 * <code>(minimum <= value <= maximum)</code> 199 * invariant enforced by the constructors. This is to simplify updating 200 * the model, naturally one should ensure that the invariant is true 201 * before calling the <code>getNextValue</code>, 202 * <code>getPreviousValue</code>, or <code>setValue</code> methods. 203 * <p> 204 * Typically this property is a <code>Number</code> of the same type 205 * as the <code>value</code> however it's possible to use any 206 * <code>Comparable</code> with a <code>compareTo</code> 207 * method for a <code>Number</code> with the same type as the value. 208 * For example if value was a <code>Long</code>, 209 * <code>minimum</code> might be a Date subclass defined like this: 210 * <pre> 211 * MyDate extends Date { // Date already implements Comparable 212 * public int compareTo(Long o) { 213 * long t = getTime(); 214 * return (t < o.longValue() ? -1 : (t == o.longValue() ? 0 : 1)); 215 * } 216 * } 217 * </pre> 218 * <p> 219 * This method fires a <code>ChangeEvent</code> 220 * if the <code>minimum</code> has changed. 221 * 222 * @param minimum a <code>Comparable</code> that has a 223 * <code>compareTo</code> method for <code>Number</code>s with 224 * the same type as <code>value</code> 225 * @see #getMinimum 226 * @see #setMaximum 227 * @see SpinnerModel#addChangeListener 228 */ setMinimum(Comparable<?> minimum)229 public void setMinimum(Comparable<?> minimum) { 230 if ((minimum == null) ? (this.minimum != null) : !minimum.equals(this.minimum)) { 231 this.minimum = minimum; 232 fireStateChanged(); 233 } 234 } 235 236 237 /** 238 * Returns the first number in this sequence. 239 * 240 * @return the value of the <code>minimum</code> property 241 * @see #setMinimum 242 */ getMinimum()243 public Comparable<?> getMinimum() { 244 return minimum; 245 } 246 247 248 /** 249 * Changes the upper bound for numbers in this sequence. 250 * If <code>maximum</code> is <code>null</code>, then there 251 * is no upper bound. No bounds checking is done here; the new 252 * <code>maximum</code> value may invalidate the 253 * <code>(minimum <= value < maximum)</code> 254 * invariant enforced by the constructors. This is to simplify updating 255 * the model, naturally one should ensure that the invariant is true 256 * before calling the <code>next</code>, <code>previous</code>, 257 * or <code>setValue</code> methods. 258 * <p> 259 * Typically this property is a <code>Number</code> of the same type 260 * as the <code>value</code> however it's possible to use any 261 * <code>Comparable</code> with a <code>compareTo</code> 262 * method for a <code>Number</code> with the same type as the value. 263 * See {@link #setMinimum(Comparable)} for an example. 264 * <p> 265 * This method fires a <code>ChangeEvent</code> if the 266 * <code>maximum</code> has changed. 267 * 268 * @param maximum a <code>Comparable</code> that has a 269 * <code>compareTo</code> method for <code>Number</code>s with 270 * the same type as <code>value</code> 271 * @see #getMaximum 272 * @see #setMinimum 273 * @see SpinnerModel#addChangeListener 274 */ setMaximum(Comparable<?> maximum)275 public void setMaximum(Comparable<?> maximum) { 276 if ((maximum == null) ? (this.maximum != null) : !maximum.equals(this.maximum)) { 277 this.maximum = maximum; 278 fireStateChanged(); 279 } 280 } 281 282 283 /** 284 * Returns the last number in the sequence. 285 * 286 * @return the value of the <code>maximum</code> property 287 * @see #setMaximum 288 */ getMaximum()289 public Comparable<?> getMaximum() { 290 return maximum; 291 } 292 293 294 /** 295 * Changes the size of the value change computed by the 296 * <code>getNextValue</code> and <code>getPreviousValue</code> 297 * methods. An <code>IllegalArgumentException</code> 298 * is thrown if <code>stepSize</code> is <code>null</code>. 299 * <p> 300 * This method fires a <code>ChangeEvent</code> if the 301 * <code>stepSize</code> has changed. 302 * 303 * @param stepSize the size of the value change computed by the 304 * <code>getNextValue</code> and <code>getPreviousValue</code> methods 305 * @see #getNextValue 306 * @see #getPreviousValue 307 * @see #getStepSize 308 * @see SpinnerModel#addChangeListener 309 */ setStepSize(Number stepSize)310 public void setStepSize(Number stepSize) { 311 if (stepSize == null) { 312 throw new IllegalArgumentException("null stepSize"); 313 } 314 if (!stepSize.equals(this.stepSize)) { 315 this.stepSize = stepSize; 316 fireStateChanged(); 317 } 318 } 319 320 321 /** 322 * Returns the size of the value change computed by the 323 * <code>getNextValue</code> 324 * and <code>getPreviousValue</code> methods. 325 * 326 * @return the value of the <code>stepSize</code> property 327 * @see #setStepSize 328 */ getStepSize()329 public Number getStepSize() { 330 return stepSize; 331 } 332 333 @SuppressWarnings("unchecked") // Casts to Comparable<Object> incrValue(int dir)334 private Number incrValue(int dir) 335 { 336 Number newValue; 337 if ((value instanceof Float) || (value instanceof Double)) { 338 double v = value.doubleValue() + (stepSize.doubleValue() * (double)dir); 339 if (value instanceof Double) { 340 newValue = Double.valueOf(v); 341 } 342 else { 343 newValue = Float.valueOf((float)v); 344 } 345 } else { 346 long v = value.longValue() + (stepSize.longValue() * (long)dir); 347 348 if (value instanceof Long) { 349 newValue = Long.valueOf(v); 350 } 351 else if (value instanceof Integer) { 352 newValue = Integer.valueOf((int)v); 353 } 354 else if (value instanceof Short) { 355 newValue = Short.valueOf((short)v); 356 } 357 else { 358 newValue = Byte.valueOf((byte)v); 359 } 360 } 361 362 if ((maximum != null) && (((Comparable<Object>)maximum).compareTo(newValue) < 0)) { 363 return null; 364 } 365 if ((minimum != null) && (((Comparable<Object>)minimum).compareTo(newValue) > 0)) { 366 return null; 367 } 368 else { 369 return newValue; 370 } 371 } 372 373 374 /** 375 * Returns the next number in the sequence. 376 * 377 * @return <code>value + stepSize</code> or <code>null</code> if the sum 378 * exceeds <code>maximum</code>. 379 * 380 * @see SpinnerModel#getNextValue 381 * @see #getPreviousValue 382 * @see #setStepSize 383 */ getNextValue()384 public Object getNextValue() { 385 return incrValue(+1); 386 } 387 388 389 /** 390 * Returns the previous number in the sequence. 391 * 392 * @return <code>value - stepSize</code>, or 393 * <code>null</code> if the sum is less 394 * than <code>minimum</code>. 395 * 396 * @see SpinnerModel#getPreviousValue 397 * @see #getNextValue 398 * @see #setStepSize 399 */ getPreviousValue()400 public Object getPreviousValue() { 401 return incrValue(-1); 402 } 403 404 405 /** 406 * Returns the value of the current element of the sequence. 407 * 408 * @return the value property 409 * @see #setValue 410 */ getNumber()411 public Number getNumber() { 412 return value; 413 } 414 415 416 /** 417 * Returns the value of the current element of the sequence. 418 * 419 * @return the value property 420 * @see #setValue 421 * @see #getNumber 422 */ getValue()423 public Object getValue() { 424 return value; 425 } 426 427 428 /** 429 * Sets the current value for this sequence. If <code>value</code> is 430 * <code>null</code>, or not a <code>Number</code>, an 431 * <code>IllegalArgumentException</code> is thrown. No 432 * bounds checking is done here; the new value may invalidate the 433 * <code>(minimum <= value <= maximum)</code> 434 * invariant enforced by the constructors. It's also possible to set 435 * the value to be something that wouldn't naturally occur in the sequence, 436 * i.e. a value that's not modulo the <code>stepSize</code>. 437 * This is to simplify updating the model, and to accommodate 438 * spinners that don't want to restrict values that have been 439 * directly entered by the user. Naturally, one should ensure that the 440 * <code>(minimum <= value <= maximum)</code> invariant is true 441 * before calling the <code>next</code>, <code>previous</code>, or 442 * <code>setValue</code> methods. 443 * <p> 444 * This method fires a <code>ChangeEvent</code> if the value has changed. 445 * 446 * @param value the current (non <code>null</code>) <code>Number</code> 447 * for this sequence 448 * @throws IllegalArgumentException if <code>value</code> is 449 * <code>null</code> or not a <code>Number</code> 450 * @see #getNumber 451 * @see #getValue 452 * @see SpinnerModel#addChangeListener 453 */ setValue(Object value)454 public void setValue(Object value) { 455 if ((value == null) || !(value instanceof Number)) { 456 throw new IllegalArgumentException("illegal value"); 457 } 458 if (!value.equals(this.value)) { 459 this.value = (Number)value; 460 fireStateChanged(); 461 } 462 } 463 } 464