1 /* 2 * Copyright (c) 2000, 2013, 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 = 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</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 public class SpinnerNumberModel extends AbstractSpinnerModel implements Serializable 84 { 85 private Number stepSize, value; 86 private Comparable minimum, maximum; 87 88 89 /** 90 * Constructs a <code>SpinnerModel</code> that represents 91 * a closed sequence of 92 * numbers from <code>minimum</code> to <code>maximum</code>. The 93 * <code>nextValue</code> and <code>previousValue</code> methods 94 * compute elements of the sequence by adding or subtracting 95 * <code>stepSize</code> respectively. All of the parameters 96 * must be mutually <code>Comparable</code>, <code>value</code> 97 * and <code>stepSize</code> must be instances of <code>Integer</code> 98 * <code>Long</code>, <code>Float</code>, or <code>Double</code>. 99 * <p> 100 * The <code>minimum</code> and <code>maximum</code> parameters 101 * can be <code>null</code> to indicate that the range doesn't 102 * have an upper or lower bound. 103 * If <code>value</code> or <code>stepSize</code> is <code>null</code>, 104 * or if both <code>minimum</code> and <code>maximum</code> 105 * are specified and <code>minimum > maximum</code> then an 106 * <code>IllegalArgumentException</code> is thrown. 107 * Similarly if <code>(minimum <= value <= maximum</code>) is false, 108 * an <code>IllegalArgumentException</code> is thrown. 109 * 110 * @param value the current (non <code>null</code>) value of the model 111 * @param minimum the first number in the sequence or <code>null</code> 112 * @param maximum the last number in the sequence or <code>null</code> 113 * @param stepSize the difference between elements of the sequence 114 * 115 * @throws IllegalArgumentException if stepSize or value is 116 * <code>null</code> or if the following expression is false: 117 * <code>minimum <= value <= maximum</code> 118 */ 119 public SpinnerNumberModel(Number value, Comparable minimum, Comparable maximum, Number stepSize) { 120 if ((value == null) || (stepSize == null)) { 121 throw new IllegalArgumentException("value and stepSize must be non-null"); 122 } 123 if (!(((minimum == null) || (minimum.compareTo(value) <= 0)) && 124 ((maximum == null) || (maximum.compareTo(value) >= 0)))) { 125 throw new IllegalArgumentException("(minimum <= value <= maximum) is false"); 126 } 127 this.value = value; 128 this.minimum = minimum; 129 this.maximum = maximum; 130 this.stepSize = stepSize; 131 } 132 133 134 /** 135 * Constructs a <code>SpinnerNumberModel</code> with the specified 136 * <code>value</code>, <code>minimum</code>/<code>maximum</code> bounds, 137 * and <code>stepSize</code>. 138 * 139 * @param value the current value of the model 140 * @param minimum the first number in the sequence 141 * @param maximum the last number in the sequence 142 * @param stepSize the difference between elements of the sequence 143 * @throws IllegalArgumentException if the following expression is false: 144 * <code>minimum <= value <= maximum</code> 145 */ 146 public SpinnerNumberModel(int value, int minimum, int maximum, int stepSize) { 147 this(Integer.valueOf(value), Integer.valueOf(minimum), Integer.valueOf(maximum), Integer.valueOf(stepSize)); 148 } 149 150 151 /** 152 * Constructs a <code>SpinnerNumberModel</code> with the specified 153 * <code>value</code>, <code>minimum</code>/<code>maximum</code> bounds, 154 * and <code>stepSize</code>. 155 * 156 * @param value the current value of the model 157 * @param minimum the first number in the sequence 158 * @param maximum the last number in the sequence 159 * @param stepSize the difference between elements of the sequence 160 * @throws IllegalArgumentException if the following expression is false: 161 * <code>minimum <= value <= maximum</code> 162 */ 163 public SpinnerNumberModel(double value, double minimum, double maximum, double stepSize) { 164 this(new Double(value), new Double(minimum), new Double(maximum), new Double(stepSize)); 165 } 166 167 168 /** 169 * Constructs a <code>SpinnerNumberModel</code> with no 170 * <code>minimum</code> or <code>maximum</code> value, 171 * <code>stepSize</code> equal to one, and an initial value of zero. 172 */ 173 public SpinnerNumberModel() { 174 this(Integer.valueOf(0), null, null, Integer.valueOf(1)); 175 } 176 177 178 /** 179 * Changes the lower bound for numbers in this sequence. 180 * If <code>minimum</code> is <code>null</code>, 181 * then there is no lower bound. No bounds checking is done here; 182 * the new <code>minimum</code> value may invalidate the 183 * <code>(minimum <= value <= maximum)</code> 184 * invariant enforced by the constructors. This is to simplify updating 185 * the model, naturally one should ensure that the invariant is true 186 * before calling the <code>getNextValue</code>, 187 * <code>getPreviousValue</code>, or <code>setValue</code> methods. 188 * <p> 189 * Typically this property is a <code>Number</code> of the same type 190 * as the <code>value</code> however it's possible to use any 191 * <code>Comparable</code> with a <code>compareTo</code> 192 * method for a <code>Number</code> with the same type as the value. 193 * For example if value was a <code>Long</code>, 194 * <code>minimum</code> might be a Date subclass defined like this: 195 * <pre> 196 * MyDate extends Date { // Date already implements Comparable 197 * public int compareTo(Long o) { 198 * long t = getTime(); 199 * return (t < o.longValue() ? -1 : (t == o.longValue() ? 0 : 1)); 200 * } 201 * } 202 * </pre> 203 * <p> 204 * This method fires a <code>ChangeEvent</code> 205 * if the <code>minimum</code> has changed. 206 * 207 * @param minimum a <code>Comparable</code> that has a 208 * <code>compareTo</code> method for <code>Number</code>s with 209 * the same type as <code>value</code> 210 * @see #getMinimum 211 * @see #setMaximum 212 * @see SpinnerModel#addChangeListener 213 */ 214 public void setMinimum(Comparable minimum) { 215 if ((minimum == null) ? (this.minimum != null) : !minimum.equals(this.minimum)) { 216 this.minimum = minimum; 217 fireStateChanged(); 218 } 219 } 220 221 222 /** 223 * Returns the first number in this sequence. 224 * 225 * @return the value of the <code>minimum</code> property 226 * @see #setMinimum 227 */ 228 public Comparable getMinimum() { 229 return minimum; 230 } 231 232 233 /** 234 * Changes the upper bound for numbers in this sequence. 235 * If <code>maximum</code> is <code>null</code>, then there 236 * is no upper bound. No bounds checking is done here; the new 237 * <code>maximum</code> value may invalidate the 238 * <code>(minimum <= value < maximum)</code> 239 * invariant enforced by the constructors. This is to simplify updating 240 * the model, naturally one should ensure that the invariant is true 241 * before calling the <code>next</code>, <code>previous</code>, 242 * or <code>setValue</code> methods. 243 * <p> 244 * Typically this property is a <code>Number</code> of the same type 245 * as the <code>value</code> however it's possible to use any 246 * <code>Comparable</code> with a <code>compareTo</code> 247 * method for a <code>Number</code> with the same type as the value. 248 * See <a href="#setMinimum(java.lang.Comparable)"> 249 * <code>setMinimum</code></a> for an example. 250 * <p> 251 * This method fires a <code>ChangeEvent</code> if the 252 * <code>maximum</code> has changed. 253 * 254 * @param maximum a <code>Comparable</code> that has a 255 * <code>compareTo</code> method for <code>Number</code>s with 256 * the same type as <code>value</code> 257 * @see #getMaximum 258 * @see #setMinimum 259 * @see SpinnerModel#addChangeListener 260 */ 261 public void setMaximum(Comparable maximum) { 262 if ((maximum == null) ? (this.maximum != null) : !maximum.equals(this.maximum)) { 263 this.maximum = maximum; 264 fireStateChanged(); 265 } 266 } 267 268 269 /** 270 * Returns the last number in the sequence. 271 * 272 * @return the value of the <code>maximum</code> property 273 * @see #setMaximum 274 */ 275 public Comparable getMaximum() { 276 return maximum; 277 } 278 279 280 /** 281 * Changes the size of the value change computed by the 282 * <code>getNextValue</code> and <code>getPreviousValue</code> 283 * methods. An <code>IllegalArgumentException</code> 284 * is thrown if <code>stepSize</code> is <code>null</code>. 285 * <p> 286 * This method fires a <code>ChangeEvent</code> if the 287 * <code>stepSize</code> has changed. 288 * 289 * @param stepSize the size of the value change computed by the 290 * <code>getNextValue</code> and <code>getPreviousValue</code> methods 291 * @see #getNextValue 292 * @see #getPreviousValue 293 * @see #getStepSize 294 * @see SpinnerModel#addChangeListener 295 */ 296 public void setStepSize(Number stepSize) { 297 if (stepSize == null) { 298 throw new IllegalArgumentException("null stepSize"); 299 } 300 if (!stepSize.equals(this.stepSize)) { 301 this.stepSize = stepSize; 302 fireStateChanged(); 303 } 304 } 305 306 307 /** 308 * Returns the size of the value change computed by the 309 * <code>getNextValue</code> 310 * and <code>getPreviousValue</code> methods. 311 * 312 * @return the value of the <code>stepSize</code> property 313 * @see #setStepSize 314 */ 315 public Number getStepSize() { 316 return stepSize; 317 } 318 319 320 private Number incrValue(int dir) 321 { 322 Number newValue; 323 if ((value instanceof Float) || (value instanceof Double)) { 324 double v = value.doubleValue() + (stepSize.doubleValue() * (double)dir); 325 if (value instanceof Double) { 326 newValue = new Double(v); 327 } 328 else { 329 newValue = new Float(v); 330 } 331 } 332 else { 333 long v = value.longValue() + (stepSize.longValue() * (long)dir); 334 335 if (value instanceof Long) { 336 newValue = Long.valueOf(v); 337 } 338 else if (value instanceof Integer) { 339 newValue = Integer.valueOf((int)v); 340 } 341 else if (value instanceof Short) { 342 newValue = Short.valueOf((short)v); 343 } 344 else { 345 newValue = Byte.valueOf((byte)v); 346 } 347 } 348 349 if ((maximum != null) && (maximum.compareTo(newValue) < 0)) { 350 return null; 351 } 352 if ((minimum != null) && (minimum.compareTo(newValue) > 0)) { 353 return null; 354 } 355 else { 356 return newValue; 357 } 358 } 359 360 361 /** 362 * Returns the next number in the sequence. 363 * 364 * @return <code>value + stepSize</code> or <code>null</code> if the sum 365 * exceeds <code>maximum</code>. 366 * 367 * @see SpinnerModel#getNextValue 368 * @see #getPreviousValue 369 * @see #setStepSize 370 */ 371 public Object getNextValue() { 372 return incrValue(+1); 373 } 374 375 376 /** 377 * Returns the previous number in the sequence. 378 * 379 * @return <code>value - stepSize</code>, or 380 * <code>null</code> if the sum is less 381 * than <code>minimum</code>. 382 * 383 * @see SpinnerModel#getPreviousValue 384 * @see #getNextValue 385 * @see #setStepSize 386 */ 387 public Object getPreviousValue() { 388 return incrValue(-1); 389 } 390 391 392 /** 393 * Returns the value of the current element of the sequence. 394 * 395 * @return the value property 396 * @see #setValue 397 */ 398 public Number getNumber() { 399 return value; 400 } 401 402 403 /** 404 * Returns the value of the current element of the sequence. 405 * 406 * @return the value property 407 * @see #setValue 408 * @see #getNumber 409 */ 410 public Object getValue() { 411 return value; 412 } 413 414 415 /** 416 * Sets the current value for this sequence. If <code>value</code> is 417 * <code>null</code>, or not a <code>Number</code>, an 418 * <code>IllegalArgumentException</code> is thrown. No 419 * bounds checking is done here; the new value may invalidate the 420 * <code>(minimum <= value <= maximum)</code> 421 * invariant enforced by the constructors. It's also possible to set 422 * the value to be something that wouldn't naturally occur in the sequence, 423 * i.e. a value that's not modulo the <code>stepSize</code>. 424 * This is to simplify updating the model, and to accommodate 425 * spinners that don't want to restrict values that have been 426 * directly entered by the user. Naturally, one should ensure that the 427 * <code>(minimum <= value <= maximum)</code> invariant is true 428 * before calling the <code>next</code>, <code>previous</code>, or 429 * <code>setValue</code> methods. 430 * <p> 431 * This method fires a <code>ChangeEvent</code> if the value has changed. 432 * 433 * @param value the current (non <code>null</code>) <code>Number</code> 434 * for this sequence 435 * @throws IllegalArgumentException if <code>value</code> is 436 * <code>null</code> or not a <code>Number</code> 437 * @see #getNumber 438 * @see #getValue 439 * @see SpinnerModel#addChangeListener 440 */ 441 public void setValue(Object value) { 442 if ((value == null) || !(value instanceof Number)) { 443 throw new IllegalArgumentException("illegal value"); 444 } 445 if (!value.equals(this.value)) { 446 this.value = (Number)value; 447 fireStateChanged(); 448 } 449 } 450 }