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} for sequences of numbers. 34 * The upper and lower bounds of the sequence are defined 35 * by properties called {@code minimum} and 36 * {@code maximum}. The size of the increase or decrease 37 * computed by the {@code nextValue} and 38 * {@code previousValue} methods is defined by a property called 39 * {@code stepSize}. The {@code minimum} and 40 * {@code maximum} properties can be {@code null} 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} and 44 * {@code Comparable}, 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} types: 47 * {@code Double}, {@code Float}, {@code Long}, 48 * {@code Integer}, {@code Short}, or {@code Byte}. 49 * <p> 50 * To create a {@code SpinnerNumberModel} for the integer 51 * range zero to one hundred, with 52 * fifty as the initial value, one could write: 53 * <pre> 54 * Integer value = new Integer(50); 55 * Integer min = new Integer(0); 56 * Integer max = new Integer(100); 57 * Integer step = new Integer(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}. 70 * The {@code ChangeListeners} are notified 71 * whenever the model's {@code value}, {@code stepSize}, 72 * {@code minimum}, or {@code maximum} 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} that represents 101 * a closed sequence of 102 * numbers from {@code minimum} to {@code maximum}. The 103 * {@code nextValue} and {@code previousValue} methods 104 * compute elements of the sequence by adding or subtracting 105 * {@code stepSize} respectively. All of the parameters 106 * must be mutually {@code Comparable}, {@code value} 107 * and {@code stepSize} must be instances of {@code Integer} 108 * {@code Long}, {@code Float}, or {@code Double}. 109 * <p> 110 * The {@code minimum} and {@code maximum} parameters 111 * can be {@code null} to indicate that the range doesn't 112 * have an upper or lower bound. 113 * If {@code value} or {@code stepSize} is {@code null}, 114 * or if both {@code minimum} and {@code maximum} 115 * are specified and {@code minimum > maximum} then an 116 * {@code IllegalArgumentException} is thrown. 117 * Similarly if {@code (minimum <= value <= maximum}) is false, 118 * an {@code IllegalArgumentException} is thrown. 119 * 120 * @param value the current (non {@code null}) value of the model 121 * @param minimum the first number in the sequence or {@code null} 122 * @param maximum the last number in the sequence or {@code null} 123 * @param stepSize the difference between elements of the sequence 124 * 125 * @throws IllegalArgumentException if stepSize or value is 126 * {@code null} or if the following expression is false: 127 * {@code minimum <= value <= maximum} 128 */ 129 @SuppressWarnings("unchecked") // Casts to Comparable<Object> 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} with the specified 150 * {@code value}, {@code minimum}/{@code maximum} bounds, 151 * and {@code stepSize}. 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} 159 */ 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} with the specified 167 * {@code value}, {@code minimum}/{@code maximum} bounds, 168 * and {@code stepSize}. 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} 176 */ 177 public SpinnerNumberModel(double value, double minimum, double maximum, double stepSize) { 178 this(new Double(value), new Double(minimum), new Double(maximum), new Double(stepSize)); 179 } 180 181 182 /** 183 * Constructs a {@code SpinnerNumberModel} with no 184 * {@code minimum} or {@code maximum} value, 185 * {@code stepSize} equal to one, and an initial value of zero. 186 */ 187 public SpinnerNumberModel() { 188 this(Integer.valueOf(0), null, null, Integer.valueOf(1)); 189 } 190 191 192 /** 193 * Changes the lower bound for numbers in this sequence. 194 * If {@code minimum} is {@code null}, 195 * then there is no lower bound. No bounds checking is done here; 196 * the new {@code minimum} value may invalidate the 197 * {@code (minimum <= value <= maximum)} 198 * invariant enforced by the constructors. This is to simplify updating 199 * the model, naturally one should ensure that the invariant is true 200 * before calling the {@code getNextValue}, 201 * {@code getPreviousValue}, or {@code setValue} methods. 202 * <p> 203 * Typically this property is a {@code Number} of the same type 204 * as the {@code value} however it's possible to use any 205 * {@code Comparable} with a {@code compareTo} 206 * method for a {@code Number} with the same type as the value. 207 * For example if value was a {@code Long}, 208 * {@code minimum} might be a Date subclass defined like this: 209 * <pre> 210 * MyDate extends Date { // Date already implements Comparable 211 * public int compareTo(Long o) { 212 * long t = getTime(); 213 * return (t < o.longValue() ? -1 : (t == o.longValue() ? 0 : 1)); 214 * } 215 * } 216 * </pre> 217 * <p> 218 * This method fires a {@code ChangeEvent} 219 * if the {@code minimum} has changed. 220 * 221 * @param minimum a {@code Comparable} that has a 222 * {@code compareTo} method for {@code Number}s with 223 * the same type as {@code value} 224 * @see #getMinimum 225 * @see #setMaximum 226 * @see SpinnerModel#addChangeListener 227 */ 228 public void setMinimum(Comparable<?> minimum) { 229 if ((minimum == null) ? (this.minimum != null) : !minimum.equals(this.minimum)) { 230 this.minimum = minimum; 231 fireStateChanged(); 232 } 233 } 234 235 236 /** 237 * Returns the first number in this sequence. 238 * 239 * @return the value of the {@code minimum} property 240 * @see #setMinimum 241 */ 242 public Comparable<?> getMinimum() { 243 return minimum; 244 } 245 246 247 /** 248 * Changes the upper bound for numbers in this sequence. 249 * If {@code maximum} is {@code null}, then there 250 * is no upper bound. No bounds checking is done here; the new 251 * {@code maximum} value may invalidate the 252 * {@code (minimum <= value < maximum)} 253 * invariant enforced by the constructors. This is to simplify updating 254 * the model, naturally one should ensure that the invariant is true 255 * before calling the {@code next}, {@code previous}, 256 * or {@code setValue} methods. 257 * <p> 258 * Typically this property is a {@code Number} of the same type 259 * as the {@code value} however it's possible to use any 260 * {@code Comparable} with a {@code compareTo} 261 * method for a {@code Number} with the same type as the value. 262 * See <a href="#setMinimum(java.lang.Comparable)"> 263 * {@code setMinimum}</a> for an example. 264 * <p> 265 * This method fires a {@code ChangeEvent} if the 266 * {@code maximum} has changed. 267 * 268 * @param maximum a {@code Comparable} that has a 269 * {@code compareTo} method for {@code Number}s with 270 * the same type as {@code value} 271 * @see #getMaximum 272 * @see #setMinimum 273 * @see SpinnerModel#addChangeListener 274 */ 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} property 287 * @see #setMaximum 288 */ 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} and {@code getPreviousValue} 297 * methods. An {@code IllegalArgumentException} 298 * is thrown if {@code stepSize} is {@code null}. 299 * <p> 300 * This method fires a {@code ChangeEvent} if the 301 * {@code stepSize} has changed. 302 * 303 * @param stepSize the size of the value change computed by the 304 * {@code getNextValue} and {@code getPreviousValue} methods 305 * @see #getNextValue 306 * @see #getPreviousValue 307 * @see #getStepSize 308 * @see SpinnerModel#addChangeListener 309 */ 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} 324 * and {@code getPreviousValue} methods. 325 * 326 * @return the value of the {@code stepSize} property 327 * @see #setStepSize 328 */ 329 public Number getStepSize() { 330 return stepSize; 331 } 332 333 @SuppressWarnings("unchecked") // Casts to Comparable<Object> 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 = new Double(v); 341 } 342 else { 343 newValue = new 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} or {@code null} if the sum 378 * exceeds {@code maximum}. 379 * 380 * @see SpinnerModel#getNextValue 381 * @see #getPreviousValue 382 * @see #setStepSize 383 */ 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}, or 393 * {@code null} if the sum is less 394 * than {@code minimum}. 395 * 396 * @see SpinnerModel#getPreviousValue 397 * @see #getNextValue 398 * @see #setStepSize 399 */ 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 */ 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 */ 423 public Object getValue() { 424 return value; 425 } 426 427 428 /** 429 * Sets the current value for this sequence. If {@code value} is 430 * {@code null}, or not a {@code Number}, an 431 * {@code IllegalArgumentException} is thrown. No 432 * bounds checking is done here; the new value may invalidate the 433 * {@code (minimum <= value <= maximum)} 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}. 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)} invariant is true 441 * before calling the {@code next}, {@code previous}, or 442 * {@code setValue} methods. 443 * <p> 444 * This method fires a {@code ChangeEvent} if the value has changed. 445 * 446 * @param value the current (non {@code null}) {@code Number} 447 * for this sequence 448 * @throws IllegalArgumentException if {@code value} is 449 * {@code null} or not a {@code Number} 450 * @see #getNumber 451 * @see #getValue 452 * @see SpinnerModel#addChangeListener 453 */ 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 } --- EOF ---