001/* SpinnerNumberModel.java --
002   Copyright (C) 2002, 2004, 2006 Free Software Foundation, Inc.
003
004This file is part of GNU Classpath.
005
006GNU Classpath is free software; you can redistribute it and/or modify
007it under the terms of the GNU General Public License as published by
008the Free Software Foundation; either version 2, or (at your option)
009any later version.
010
011GNU Classpath is distributed in the hope that it will be useful, but
012WITHOUT ANY WARRANTY; without even the implied warranty of
013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014General Public License for more details.
015
016You should have received a copy of the GNU General Public License
017along with GNU Classpath; see the file COPYING.  If not, write to the
018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
01902110-1301 USA.
020
021Linking this library statically or dynamically with other modules is
022making a combined work based on this library.  Thus, the terms and
023conditions of the GNU General Public License cover the whole
024combination.
025
026As a special exception, the copyright holders of this library give you
027permission to link this library with independent modules to produce an
028executable, regardless of the license terms of these independent
029modules, and to copy and distribute the resulting executable under
030terms of your choice, provided that you also meet, for each linked
031independent module, the terms and conditions of the license of that
032module.  An independent module is a module which is not derived from
033or based on this library.  If you modify this library, you may extend
034this exception to your version of the library, but you are not
035obligated to do so.  If you do not wish to do so, delete this
036exception statement from your version. */
037
038package javax.swing;
039
040import java.io.Serializable;
041
042import javax.swing.event.ChangeEvent;
043
044/**
045 * A model used by the {@link JSpinner} component.
046 *
047 * @author Ka-Hing Cheung
048 * @since 1.4
049 */
050public class SpinnerNumberModel extends AbstractSpinnerModel
051  implements Serializable
052{
053  /**
054   * For compatability with Sun's JDK
055   */
056  private static final long serialVersionUID = 7279176385485777821L;
057
058  /** The current value. */
059  private Number value;
060
061  /** The minimum value (or <code>null</code>). */
062  private Comparable minimum;
063
064  /** The maximum value (or <code>null</code>). */
065  private Comparable maximum;
066
067  /** The step size. */
068  private Number stepSize;
069
070  /**
071   * Creates a <code>SpinnerNumberModel</code> with initial value 0, step 1,
072   * and no maximum nor minimum.
073   */
074  public SpinnerNumberModel()
075  {
076    this(new Integer(0), null, null, new Integer(1));
077  }
078
079  /**
080   * Creates a <code>SpinnerNumberModel</code> with double precision.
081   *
082   * @param value the initial value
083   * @param minimum the minimum value
084   * @param maximum the maximum value
085   * @param stepSize the step size
086   * @throws IllegalArgumentException if minimum &lt;= value &lt;= maximum does 
087   *         not hold.
088   */
089  public SpinnerNumberModel(double value, double minimum, double maximum,
090                            double stepSize)
091  {
092    this(new Double(value), new Double(minimum), new Double(maximum),
093         new Double(stepSize));
094  }
095
096  /**
097   * Creates a <code>SpinnerNumberModel</code> with integer precision.
098   *
099   * @param value the initial value
100   * @param minimum the minimum value
101   * @param maximum the maximum value
102   * @param stepSize the step size
103   * @throws IllegalArgumentException if minimum &lt;= value &lt;= maximum does 
104   *         not hold.
105   */
106  public SpinnerNumberModel(int value, int minimum, int maximum, int stepSize)
107  {
108    this(new Integer(value), new Integer(minimum), new Integer(maximum),
109         new Integer(stepSize));
110  }
111
112  /**
113   * Creates a <code>SpinnerNumberModel</code> with the given attributes.  The
114   * caller should ensure that both <code>minimum</code> and 
115   * <code>maximum</code> are serializable.
116   *
117   * @param value the initial value (<code>null</code> not permitted).
118   * @param minimum the minimum value (<code>null</code> permitted).
119   * @param maximum the maximum value (<code>null</code> permitted).
120   * @param stepSize the step size  (<code>null</code> not permitted).
121   *
122   * @throws IllegalArgumentException if minimum &lt;= value &lt;= maximum
123   *         does not hold
124   * @throws IllegalArgumentException if <code>value</code> is 
125   *         <code>null</code>.
126   * @throws IllegalArgumentException if <code>stepSize</code> is 
127   *         <code>null</code>.
128   */
129  public SpinnerNumberModel(Number value, Comparable minimum,
130                            Comparable maximum, Number stepSize)
131  {
132    if (stepSize == null)
133      throw new IllegalArgumentException("stepSize may not be null");
134    if (value == null)
135      throw new IllegalArgumentException("value may not be null");
136    if (minimum != null)
137      {
138        if (minimum.compareTo(value) > 0)
139          throw new IllegalArgumentException("minimum is not <= value");
140      }
141    if (maximum != null)
142      {
143        if (maximum.compareTo(value) < 0)
144          throw new IllegalArgumentException("maximum is not >= value");
145      }
146
147    this.value = value;
148    this.stepSize = stepSize;
149    this.minimum = minimum;
150    this.maximum = maximum;
151  }
152
153  /**
154   * Sets the current value and, if the new value is different to the old
155   * value, sends a {@link ChangeEvent} to all registered listeners.
156   *
157   * @param value the new value (<code>null</code> not permitted, must be an
158   *              instance of <code>Number</code>).
159   *
160   * @throws IllegalArgumentException if <code>value</code> is not an instance
161   *         of <code>Number</code>.
162   */
163  public void setValue(Object value)
164  {
165    if (! (value instanceof Number))
166      throw new IllegalArgumentException("value must be a Number");
167
168    if (!this.value.equals(value)) 
169      {
170        this.value = (Number) value;
171        fireStateChanged();
172      }
173  }
174
175  /**
176   * Returns the current value, which for this class is always an instance of
177   * {@link Number}.
178   *
179   * @return The current value.
180   * 
181   * @see #getNumber()
182   */
183  public Object getValue()
184  {
185    return value;
186  }
187
188  /**
189   * Returns the next value, or <code>null</code> if adding the step size to
190   * the current value results in a value greater than the maximum value.  
191   * The current value is not changed.
192   *
193   * @return The next value, or <code>null</code> if the current value is the 
194   *         maximum value represented by this model.
195   */
196  public Object getNextValue()
197  {
198    Number num;
199
200    if (value instanceof Double)
201      num = new Double(value.doubleValue() + stepSize.doubleValue());
202    else if (value instanceof Float)
203      num = new Double(value.floatValue() + stepSize.floatValue());
204    else if (value instanceof Long)
205      num = new Long(value.longValue() + stepSize.longValue());
206    else if (value instanceof Integer)
207      num = new Integer(value.intValue() + stepSize.intValue());
208    else if (value instanceof Short)
209      num = new Short((short) (value.shortValue() + stepSize.shortValue()));
210    else
211      num = new Byte((byte) (value.byteValue() + stepSize.byteValue()));
212    
213    // check upper bound if set
214    if ((maximum != null) && maximum.compareTo(num) < 0)
215      num = null;
216    
217    return num;
218  }
219
220  /**
221   * Returns the previous value, or <code>null</code> if subtracting the
222   * step size from the current value results in a value less than the minimum
223   * value.  The current value is not changed.
224   *
225   * @return The previous value, or <code>null</code> if the current value
226   *         is the minimum value represented by this model.
227   */
228  public Object getPreviousValue()
229  {
230    Number num;
231
232    if (value instanceof Double)
233      num = new Double(value.doubleValue() - stepSize.doubleValue());
234    else if (value instanceof Float)
235      num = new Double(value.floatValue() - stepSize.floatValue());
236    else if (value instanceof Long)
237      num = new Long(value.longValue() - stepSize.longValue());
238    else if (value instanceof Integer)
239      num = new Integer(value.intValue() - stepSize.intValue());
240    else if (value instanceof Short)
241      num = new Short((short) (value.shortValue() - stepSize.shortValue()));
242    else
243      num = new Byte((byte) (value.byteValue() - stepSize.byteValue()));
244    
245    // check lower bound if set
246    if ((minimum != null) && minimum.compareTo(num) > 0)
247      num = null;
248
249    return num;
250  }
251
252  /**
253   * Returns the current value.
254   *
255   * @return The current value.
256   */
257  public Number getNumber()
258  {
259    return value;
260  }
261
262  /**
263   * Returns the minimum value, or <code>null</code> if there is no minimum.
264   * 
265   * @return The minimum value.
266   * 
267   * @see #setMinimum(Comparable)
268   */
269  public Comparable getMinimum()
270  {
271    return minimum;
272  }
273
274  /**
275   * Sets the minimum value and, if the new value is different to the old
276   * value, sends a {@link ChangeEvent} to all registered listeners.  A 
277   * <code>null</code> value is interpreted as "no minimum value".  No check
278   * is made to ensure that the new minimum is less than or equal to the 
279   * current value, the caller is responsible for ensuring that this 
280   * relationship holds.  In addition, the caller should ensure that
281   * <code>newMinimum</code> is {@link Serializable}.
282   * 
283   * @param newMinimum  the new minimum value (<code>null</code> permitted).
284   * 
285   * @see #getMinimum()
286   */
287  public void setMinimum(Comparable newMinimum)
288  {
289    if (minimum != null ? !minimum.equals(newMinimum) : newMinimum != null)
290      {
291        minimum = newMinimum;
292        fireStateChanged();
293      }
294  }
295
296  /**
297   * Returns the maximum value, or <code>null</code> if there is no maximum.
298   * 
299   * @return The maximum value.
300   * 
301   * @see #getMinimum()
302   * @see #setMaximum(Comparable)
303   */
304  public Comparable getMaximum()
305  {
306    return maximum;
307  }
308
309  /**
310   * Sets the maximum value and, if the new value is different to the old
311   * value, sends a {@link ChangeEvent} to all registered listeners.  A 
312   * <code>null</code> value is interpreted as "no maximum value".  No check
313   * is made to ensure that the new maximum is greater than or equal to the 
314   * current value, the caller is responsible for ensuring that this 
315   * relationship holds. In addition, the caller should ensure that
316   * <code>newMaximum</code> is {@link Serializable}.
317   * 
318   * @param newMaximum  the new maximum (<code>null</code> permitted).
319   * 
320   * @see #getMaximum()
321   */
322  public void setMaximum(Comparable newMaximum)
323  {
324    if (maximum != null ? !maximum.equals(newMaximum) : newMaximum != null)
325      {
326        maximum = newMaximum;
327        fireStateChanged();
328      }
329  }
330
331  /**
332   * Returns the step size.
333   * 
334   * @return The step size (never <code>null</code>).
335   */
336  public Number getStepSize()
337  {
338    return stepSize;
339  }
340
341  /**
342   * Sets the step size and, if the new step size is different to the old
343   * step size, sends a {@link ChangeEvent} to all registered listeners.
344   * 
345   * @param newStepSize  the new step size (<code>null</code> not permitted).
346   * 
347   * @throws IllegalArgumentException if <code>newStepSize</code> is 
348   *         <code>null</code>.
349   */
350  public void setStepSize(Number newStepSize)
351  {
352    if (newStepSize == null)
353      throw new IllegalArgumentException();
354
355    if (!stepSize.equals(newStepSize))
356      {
357        stepSize = newStepSize;
358        fireStateChanged();
359      }
360  }
361}