001    /* SpinnerNumberModel.java --
002       Copyright (C) 2002, 2004, 2006 Free Software Foundation, Inc.
003    
004    This file is part of GNU Classpath.
005    
006    GNU Classpath is free software; you can redistribute it and/or modify
007    it under the terms of the GNU General Public License as published by
008    the Free Software Foundation; either version 2, or (at your option)
009    any later version.
010    
011    GNU Classpath is distributed in the hope that it will be useful, but
012    WITHOUT ANY WARRANTY; without even the implied warranty of
013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014    General Public License for more details.
015    
016    You should have received a copy of the GNU General Public License
017    along with GNU Classpath; see the file COPYING.  If not, write to the
018    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019    02110-1301 USA.
020    
021    Linking this library statically or dynamically with other modules is
022    making a combined work based on this library.  Thus, the terms and
023    conditions of the GNU General Public License cover the whole
024    combination.
025    
026    As a special exception, the copyright holders of this library give you
027    permission to link this library with independent modules to produce an
028    executable, regardless of the license terms of these independent
029    modules, and to copy and distribute the resulting executable under
030    terms of your choice, provided that you also meet, for each linked
031    independent module, the terms and conditions of the license of that
032    module.  An independent module is a module which is not derived from
033    or based on this library.  If you modify this library, you may extend
034    this exception to your version of the library, but you are not
035    obligated to do so.  If you do not wish to do so, delete this
036    exception statement from your version. */
037    
038    package javax.swing;
039    
040    import java.io.Serializable;
041    
042    import 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     */
050    public 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    }