001 /*
002 * Copyright 2000-2004 Sun Microsystems, Inc. All Rights Reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation. Sun designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Sun in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022 * CA 95054 USA or visit www.sun.com if you need additional information or
023 * have any questions.
024 */
025
026 package javax.swing;
027
028 import java.util.*;
029 import java.io.Serializable;
030
031 /**
032 * A <code>SpinnerModel</code> for sequences of numbers.
033 * The upper and lower bounds of the sequence are defined
034 * by properties called <code>minimum</code> and
035 * <code>maximum</code>. The size of the increase or decrease
036 * computed by the <code>nextValue</code> and
037 * <code>previousValue</code> methods is defined by a property called
038 * <code>stepSize</code>. The <code>minimum</code> and
039 * <code>maximum</code> properties can be <code>null</code>
040 * to indicate that the sequence has no lower or upper limit.
041 * All of the properties in this class are defined in terms of two
042 * generic types: <code>Number</code> and
043 * <code>Comparable</code>, so that all Java numeric types
044 * may be accommodated. Internally, there's only support for
045 * values whose type is one of the primitive <code>Number</code> types:
046 * <code>Double</code>, <code>Float</code>, <code>Long</code>,
047 * <code>Integer</code>, <code>Short</code>, or <code>Byte</code>.
048 * <p>
049 * To create a <code>SpinnerNumberModel</code> for the integer
050 * range zero to one hundred, with
051 * fifty as the initial value, one could write:
052 * <pre>
053 * Integer value = new Integer(50);
054 * Integer min = new Integer(0);
055 * Integer max = new Integer(100);
056 * Integer step = new Integer(1);
057 * SpinnerNumberModel model = new SpinnerNumberModel(value, min, max, step);
058 * int fifty = model.getNumber().intValue();
059 * </pre>
060 * <p>
061 * Spinners for integers and doubles are common, so special constructors
062 * for these cases are provided. For example to create the model in
063 * the previous example, one could also write:
064 * <pre>
065 * SpinnerNumberModel model = new SpinnerNumberModel(50, 0, 100, 1);
066 * </pre>
067 * <p>
068 * This model inherits a <code>ChangeListener</code>.
069 * The <code>ChangeListeners</code> are notified
070 * whenever the model's <code>value</code>, <code>stepSize</code>,
071 * <code>minimum</code>, or <code>maximum</code> properties changes.
072 *
073 * @see JSpinner
074 * @see SpinnerModel
075 * @see AbstractSpinnerModel
076 * @see SpinnerListModel
077 * @see SpinnerDateModel
078 *
079 * @version 1.19 05/05/07
080 * @author Hans Muller
081 * @since 1.4
082 */
083 public class SpinnerNumberModel extends AbstractSpinnerModel implements
084 Serializable {
085 private Number stepSize, value;
086 private Comparable minimum, maximum;
087
088 /**
089 * Constructs a <code>SpinnerModel</code> that represents
090 * a closed sequence of
091 * numbers from <code>minimum</code> to <code>maximum</code>. The
092 * <code>nextValue</code> and <code>previousValue</code> methods
093 * compute elements of the sequence by adding or subtracting
094 * <code>stepSize</code> respectively. All of the parameters
095 * must be mutually <code>Comparable</code>, <code>value</code>
096 * and <code>stepSize</code> must be instances of <code>Integer</code>
097 * <code>Long</code>, <code>Float</code>, or <code>Double</code>.
098 * <p>
099 * The <code>minimum</code> and <code>maximum</code> parameters
100 * can be <code>null</code> to indicate that the range doesn't
101 * have an upper or lower bound.
102 * If <code>value</code> or <code>stepSize</code> is <code>null</code>,
103 * or if both <code>minimum</code> and <code>maximum</code>
104 * are specified and <code>mininum > maximum</code> then an
105 * <code>IllegalArgumentException</code> is thrown.
106 * Similarly if <code>(minimum <= value <= maximum</code>) is false,
107 * an <code>IllegalArgumentException</code> is thrown.
108 *
109 * @param value the current (non <code>null</code>) value of the model
110 * @param minimum the first number in the sequence or <code>null</code>
111 * @param maximum the last number in the sequence or <code>null</code>
112 * @param stepSize the difference between elements of the sequence
113 *
114 * @throws IllegalArgumentException if stepSize or value is
115 * <code>null</code> or if the following expression is false:
116 * <code>minimum <= value <= maximum</code>
117 */
118 public SpinnerNumberModel(Number value, Comparable minimum,
119 Comparable maximum, Number stepSize) {
120 if ((value == null) || (stepSize == null)) {
121 throw new IllegalArgumentException(
122 "value and stepSize must be non-null");
123 }
124 if (!(((minimum == null) || (minimum.compareTo(value) <= 0)) && ((maximum == null) || (maximum
125 .compareTo(value) >= 0)))) {
126 throw new IllegalArgumentException(
127 "(minimum <= value <= maximum) is false");
128 }
129 this .value = value;
130 this .minimum = minimum;
131 this .maximum = maximum;
132 this .stepSize = stepSize;
133 }
134
135 /**
136 * Constructs a <code>SpinnerNumberModel</code> with the specified
137 * <code>value</code>, <code>minimum</code>/<code>maximum</code> bounds,
138 * and <code>stepSize</code>.
139 *
140 * @param value the current value of the model
141 * @param minimum the first number in the sequence
142 * @param maximum the last number in the sequence
143 * @param stepSize the difference between elements of the sequence
144 * @throws IllegalArgumentException if the following expression is false:
145 * <code>minimum <= value <= maximum</code>
146 */
147 public SpinnerNumberModel(int value, int minimum, int maximum,
148 int stepSize) {
149 this (new Integer(value), new Integer(minimum), new Integer(
150 maximum), new Integer(stepSize));
151 }
152
153 /**
154 * Constructs a <code>SpinnerNumberModel</code> with the specified
155 * <code>value</code>, <code>minimum</code>/<code>maximum</code> bounds,
156 * and <code>stepSize</code>.
157 *
158 * @param value the current value of the model
159 * @param minimum the first number in the sequence
160 * @param maximum the last number in the sequence
161 * @param stepSize the difference between elements of the sequence
162 * @throws IllegalArgumentException if the following expression is false:
163 * <code>minimum <= value <= maximum</code>
164 */
165 public SpinnerNumberModel(double value, double minimum,
166 double maximum, double stepSize) {
167 this (new Double(value), new Double(minimum),
168 new Double(maximum), new Double(stepSize));
169 }
170
171 /**
172 * Constructs a <code>SpinnerNumberModel</code> with no
173 * <code>minimum</code> or <code>maximum</code> value,
174 * <code>stepSize</code> equal to one, and an initial value of zero.
175 */
176 public SpinnerNumberModel() {
177 this (new Integer(0), null, null, new Integer(1));
178 }
179
180 /**
181 * Changes the lower bound for numbers in this sequence.
182 * If <code>minimum</code> is <code>null</code>,
183 * then there is no lower bound. No bounds checking is done here;
184 * the new <code>minimum</code> value may invalidate the
185 * <code>(minimum <= value <= maximum)</code>
186 * invariant enforced by the constructors. This is to simplify updating
187 * the model, naturally one should ensure that the invariant is true
188 * before calling the <code>getNextValue</code>,
189 * <code>getPreviousValue</code>, or <code>setValue</code> methods.
190 * <p>
191 * Typically this property is a <code>Number</code> of the same type
192 * as the <code>value</code> however it's possible to use any
193 * <code>Comparable</code> with a <code>compareTo</code>
194 * method for a <code>Number</code> with the same type as the value.
195 * For example if value was a <code>Long</code>,
196 * <code>minimum</code> might be a Date subclass defined like this:
197 * <pre>
198 * MyDate extends Date { // Date already implements Comparable
199 * public int compareTo(Long o) {
200 * long t = getTime();
201 * return (t < o.longValue() ? -1 : (t == o.longValue() ? 0 : 1));
202 * }
203 * }
204 * </pre>
205 * <p>
206 * This method fires a <code>ChangeEvent</code>
207 * if the <code>minimum</code> has changed.
208 *
209 * @param minimum a <code>Comparable</code> that has a
210 * <code>compareTo</code> method for <code>Number</code>s with
211 * the same type as <code>value</code>
212 * @see #getMinimum
213 * @see #setMaximum
214 * @see SpinnerModel#addChangeListener
215 */
216 public void setMinimum(Comparable minimum) {
217 if ((minimum == null) ? (this .minimum != null) : !minimum
218 .equals(this .minimum)) {
219 this .minimum = minimum;
220 fireStateChanged();
221 }
222 }
223
224 /**
225 * Returns the first number in this sequence.
226 *
227 * @return the value of the <code>minimum</code> property
228 * @see #setMinimum
229 */
230 public Comparable getMinimum() {
231 return minimum;
232 }
233
234 /**
235 * Changes the upper bound for numbers in this sequence.
236 * If <code>maximum</code> is <code>null</code>, then there
237 * is no upper bound. No bounds checking is done here; the new
238 * <code>maximum</code> value may invalidate the
239 * <code>(minimum <= value < maximum)</code>
240 * invariant enforced by the constructors. This is to simplify updating
241 * the model, naturally one should ensure that the invariant is true
242 * before calling the <code>next</code>, <code>previous</code>,
243 * or <code>setValue</code> methods.
244 * <p>
245 * Typically this property is a <code>Number</code> of the same type
246 * as the <code>value</code> however it's possible to use any
247 * <code>Comparable</code> with a <code>compareTo</code>
248 * method for a <code>Number</code> with the same type as the value.
249 * See <a href="#setMinimum(java.lang.Comparable)">
250 * <code>setMinimum</code></a> for an example.
251 * <p>
252 * This method fires a <code>ChangeEvent</code> if the
253 * <code>maximum</code> has changed.
254 *
255 * @param maximum a <code>Comparable</code> that has a
256 * <code>compareTo</code> method for <code>Number</code>s with
257 * the same type as <code>value</code>
258 * @see #getMaximum
259 * @see #setMinimum
260 * @see SpinnerModel#addChangeListener
261 */
262 public void setMaximum(Comparable maximum) {
263 if ((maximum == null) ? (this .maximum != null) : !maximum
264 .equals(this .maximum)) {
265 this .maximum = maximum;
266 fireStateChanged();
267 }
268 }
269
270 /**
271 * Returns the last number in the sequence.
272 *
273 * @return the value of the <code>maximum</code> property
274 * @see #setMaximum
275 */
276 public Comparable getMaximum() {
277 return maximum;
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 * Returns the size of the value change computed by the
308 * <code>getNextValue</code>
309 * and <code>getPreviousValue</code> methods.
310 *
311 * @return the value of the <code>stepSize</code> property
312 * @see #setStepSize
313 */
314 public Number getStepSize() {
315 return stepSize;
316 }
317
318 private Number incrValue(int dir) {
319 Number newValue;
320 if ((value instanceof Float) || (value instanceof Double)) {
321 double v = value.doubleValue()
322 + (stepSize.doubleValue() * (double) dir);
323 if (value instanceof Double) {
324 newValue = new Double(v);
325 } else {
326 newValue = new Float(v);
327 }
328 } else {
329 long v = value.longValue()
330 + (stepSize.longValue() * (long) dir);
331
332 if (value instanceof Long) {
333 newValue = new Long(v);
334 } else if (value instanceof Integer) {
335 newValue = new Integer((int) v);
336 } else if (value instanceof Short) {
337 newValue = new Short((short) v);
338 } else {
339 newValue = new Byte((byte) v);
340 }
341 }
342
343 if ((maximum != null) && (maximum.compareTo(newValue) < 0)) {
344 return null;
345 }
346 if ((minimum != null) && (minimum.compareTo(newValue) > 0)) {
347 return null;
348 } else {
349 return newValue;
350 }
351 }
352
353 /**
354 * Returns the next number in the sequence.
355 *
356 * @return <code>value + stepSize</code> or <code>null</code> if the sum
357 * exceeds <code>maximum</code>.
358 *
359 * @see SpinnerModel#getNextValue
360 * @see #getPreviousValue
361 * @see #setStepSize
362 */
363 public Object getNextValue() {
364 return incrValue(+1);
365 }
366
367 /**
368 * Returns the previous number in the sequence.
369 *
370 * @return <code>value - stepSize</code>, or
371 * <code>null</code> if the sum is less
372 * than <code>minimum</code>.
373 *
374 * @see SpinnerModel#getPreviousValue
375 * @see #getNextValue
376 * @see #setStepSize
377 */
378 public Object getPreviousValue() {
379 return incrValue(-1);
380 }
381
382 /**
383 * Returns the value of the current element of the sequence.
384 *
385 * @return the value property
386 * @see #setValue
387 */
388 public Number getNumber() {
389 return value;
390 }
391
392 /**
393 * Returns the value of the current element of the sequence.
394 *
395 * @return the value property
396 * @see #setValue
397 * @see #getNumber
398 */
399 public Object getValue() {
400 return value;
401 }
402
403 /**
404 * Sets the current value for this sequence. If <code>value</code> is
405 * <code>null</code>, or not a <code>Number</code>, an
406 * <code>IllegalArgumentException</code> is thrown. No
407 * bounds checking is done here; the new value may invalidate the
408 * <code>(minimum <= value <= maximum)</code>
409 * invariant enforced by the constructors. It's also possible to set
410 * the value to be something that wouldn't naturally occur in the sequence,
411 * i.e. a value that's not modulo the <code>stepSize</code>.
412 * This is to simplify updating the model, and to accommodate
413 * spinners that don't want to restrict values that have been
414 * directly entered by the user. Naturally, one should ensure that the
415 * <code>(minimum <= value <= maximum)</code> invariant is true
416 * before calling the <code>next</code>, <code>previous</code>, or
417 * <code>setValue</code> methods.
418 * <p>
419 * This method fires a <code>ChangeEvent</code> if the value has changed.
420 *
421 * @param value the current (non <code>null</code>) <code>Number</code>
422 * for this sequence
423 * @throws IllegalArgumentException if <code>value</code> is
424 * <code>null</code> or not a <code>Number</code>
425 * @see #getNumber
426 * @see #getValue
427 * @see SpinnerModel#addChangeListener
428 */
429 public void setValue(Object value) {
430 if ((value == null) || !(value instanceof Number)) {
431 throw new IllegalArgumentException("illegal value");
432 }
433 if (!value.equals(this .value)) {
434 this .value = (Number) value;
435 fireStateChanged();
436 }
437 }
438 }
|