KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > javax > swing > SpinnerDateModel


1 /*
2  * @(#)SpinnerDateModel.java 1.11 04/05/12
3  *
4  * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7
8 package javax.swing;
9
10 import java.util.*;
11 import java.io.Serializable JavaDoc;
12
13
14 /**
15  * A <code>SpinnerModel</code> for sequences of <code>Date</code>s.
16  * The upper and lower bounds of the sequence are defined by properties called
17  * <code>start</code> and <code>end</code> and the size
18  * of the increase or decrease computed by the <code>nextValue</code>
19  * and <code>previousValue</code> methods is defined by a property
20  * called <code>calendarField</code>. The <code>start</code>
21  * and <code>end</code> properties can be <code>null</code> to
22  * indicate that the sequence has no lower or upper limit.
23  * <p>
24  * The value of the <code>calendarField</code> property must be one of the
25  * <code>java.util.Calendar</code> constants that specify a field
26  * within a <code>Calendar</code>. The <code>getNextValue</code>
27  * and <code>getPreviousValue</code>
28  * methods change the date forward or backwards by this amount.
29  * For example, if <code>calendarField</code> is <code>Calendar.DAY_OF_WEEK</code>,
30  * then <code>nextValue</code> produces a <code>Date</code> that's 24
31  * hours after the current <code>value</code>, and <code>previousValue</code>
32  * produces a <code>Date</code> that's 24 hours earlier.
33  * <p>
34  * The legal values for <code>calendarField</code> are:
35  * <ul>
36  * <li><code>Calendar.ERA</code>
37  * <li><code>Calendar.YEAR</code>
38  * <li><code>Calendar.MONTH</code>
39  * <li><code>Calendar.WEEK_OF_YEAR</code>
40  * <li><code>Calendar.WEEK_OF_MONTH</code>
41  * <li><code>Calendar.DAY_OF_MONTH</code>
42  * <li><code>Calendar.DAY_OF_YEAR</code>
43  * <li><code>Calendar.DAY_OF_WEEK</code>
44  * <li><code>Calendar.DAY_OF_WEEK_IN_MONTH</code>
45  * <li><code>Calendar.AM_PM</code>
46  * <li><code>Calendar.HOUR</code>
47  * <li><code>Calendar.HOUR_OF_DAY</code>
48  * <li><code>Calendar.MINUTE</code>
49  * <li><code>Calendar.SECOND</code>
50  * <li><code>Calendar.MILLISECOND</code>
51  * </ul>
52  * However some UIs may set the calendarField before commiting the edit
53  * to spin the field under the cursor. If you only want one field to
54  * spin you can subclass and ignore the setCalendarField calls.
55  * <p>
56  * This model inherits a <code>ChangeListener</code>. The
57  * <code>ChangeListeners</code> are notified whenever the models
58  * <code>value</code>, <code>calendarField</code>,
59  * <code>start</code>, or <code>end</code> properties changes.
60  *
61  * @see JSpinner
62  * @see SpinnerModel
63  * @see AbstractSpinnerModel
64  * @see SpinnerListModel
65  * @see SpinnerNumberModel
66  * @see Calendar#add
67  *
68  * @version 1.11 05/12/04
69  * @author Hans Muller
70  * @since 1.4
71  */

72 public class SpinnerDateModel extends AbstractSpinnerModel JavaDoc implements Serializable JavaDoc
73 {
74     private Comparable JavaDoc start, end;
75     private Calendar value;
76     private int calendarField;
77
78
79     private boolean calendarFieldOK(int calendarField) {
80     switch(calendarField) {
81     case Calendar.ERA:
82     case Calendar.YEAR:
83     case Calendar.MONTH:
84     case Calendar.WEEK_OF_YEAR:
85     case Calendar.WEEK_OF_MONTH:
86     case Calendar.DAY_OF_MONTH:
87     case Calendar.DAY_OF_YEAR:
88     case Calendar.DAY_OF_WEEK:
89     case Calendar.DAY_OF_WEEK_IN_MONTH:
90     case Calendar.AM_PM:
91     case Calendar.HOUR:
92     case Calendar.HOUR_OF_DAY:
93     case Calendar.MINUTE:
94     case Calendar.SECOND:
95     case Calendar.MILLISECOND:
96         return true;
97     default:
98         return false;
99     }
100     }
101     
102
103     /**
104      * Creates a <code>SpinnerDateModel</code> that represents a sequence of dates
105      * between <code>start</code> and <code>end</code>. The
106      * <code>nextValue</code> and <code>previousValue</code> methods
107      * compute elements of the sequence by advancing or reversing
108      * the current date <code>value</code> by the
109      * <code>calendarField</code> time unit. For a precise description
110      * of what it means to increment or decrement a <code>Calendar</code>
111      * <code>field</code>, see the <code>add</code> method in
112      * <code>java.util.Calendar</code>.
113      * <p>
114      * The <code>start</code> and <code>end</code> parameters can be
115      * <code>null</code> to indicate that the range doesn't have an
116      * upper or lower bound. If <code>value</code> or
117      * <code>calendarField</code> is <code>null</code>, or if both
118      * <code>start</code> and <code>end</code> are specified and
119      * <code>mininum &gt; maximum</code> then an
120      * <code>IllegalArgumentException</code> is thrown.
121      * Similarly if <code>(minimum &lt;= value &lt;= maximum)</code> is false,
122      * an IllegalArgumentException is thrown.
123      *
124      * @param value the current (non <code>null</code>) value of the model
125      * @param start the first date in the sequence or <code>null</code>
126      * @param end the last date in the sequence or <code>null</code>
127      * @param calendarField one of
128      * <ul>
129      * <li><code>Calendar.ERA</code>
130      * <li><code>Calendar.YEAR</code>
131      * <li><code>Calendar.MONTH</code>
132      * <li><code>Calendar.WEEK_OF_YEAR</code>
133      * <li><code>Calendar.WEEK_OF_MONTH</code>
134      * <li><code>Calendar.DAY_OF_MONTH</code>
135      * <li><code>Calendar.DAY_OF_YEAR</code>
136      * <li><code>Calendar.DAY_OF_WEEK</code>
137      * <li><code>Calendar.DAY_OF_WEEK_IN_MONTH</code>
138      * <li><code>Calendar.AM_PM</code>
139      * <li><code>Calendar.HOUR</code>
140      * <li><code>Calendar.HOUR_OF_DAY</code>
141      * <li><code>Calendar.MINUTE</code>
142      * <li><code>Calendar.SECOND</code>
143      * <li><code>Calendar.MILLISECOND</code>
144      * </ul>
145      *
146      * @throws IllegalArgumentException if <code>value</code> or
147      * <code>calendarField</code> are <code>null</code>,
148      * if <code>calendarField</code> isn't valid,
149      * or if the following expression is
150      * false: <code>(start &lt;= value &lt;= end)</code>.
151      *
152      * @see Calendar#add
153      * @see #setValue
154      * @see #setStart
155      * @see #setEnd
156      * @see #setCalendarField
157      */

158     public SpinnerDateModel(Date value, Comparable JavaDoc start, Comparable JavaDoc end, int calendarField) {
159     if (value == null) {
160         throw new IllegalArgumentException JavaDoc("value is null");
161     }
162     if (!calendarFieldOK(calendarField)) {
163         throw new IllegalArgumentException JavaDoc("invalid calendarField");
164     }
165     if (!(((start == null) || (start.compareTo(value) <= 0)) &&
166           ((end == null) || (end.compareTo(value) >= 0)))) {
167         throw new IllegalArgumentException JavaDoc("(start <= value <= end) is false");
168     }
169     this.value = Calendar.getInstance();
170     this.start = start;
171     this.end = end;
172     this.calendarField = calendarField;
173
174     this.value.setTime(value);
175     }
176
177
178     /**
179      * Constructs a <code>SpinnerDateModel</code> whose initial
180      * <code>value</code> is the current date, <code>calendarField</code>
181      * is equal to <code>Calendar.DAY_OF_MONTH</code>, and for which
182      * there are no <code>start</code>/<code>end</code> limits.
183      */

184     public SpinnerDateModel() {
185     this(new Date(), null, null, Calendar.DAY_OF_MONTH);
186     }
187
188
189     /**
190      * Changes the lower limit for Dates in this sequence.
191      * If <code>start</code> is <code>null</code>,
192      * then there is no lower limit. No bounds checking is done here:
193      * the new start value may invalidate the
194      * <code>(start &lt;= value &lt;= end)</code>
195      * invariant enforced by the constructors. This is to simplify updating
196      * the model. Naturally one should ensure that the invariant is true
197      * before calling the <code>nextValue</code>, <code>previousValue</code>,
198      * or <code>setValue</code> methods.
199      * <p>
200      * Typically this property is a <code>Date</code> however it's possible to use
201      * a <code>Comparable</code> with a <code>compareTo</code> method for Dates.
202      * For example <code>start</code> might be an instance of a class like this:
203      * <pre>
204      * MyStartDate implements Comparable {
205      * long t = 12345;
206      * public int compareTo(Date d) {
207      * return (t < d.getTime() ? -1 : (t == d.getTime() ? 0 : 1));
208      * }
209      * public int compareTo(Object o) {
210      * return compareTo((Date)o);
211      * }
212      * }
213      * </pre>
214      * Note that the above example will throw a <code>ClassCastException</code>
215      * if the <code>Object</code> passed to <code>compareTo(Object)</code>
216      * is not a <code>Date</code>.
217      * <p>
218      * This method fires a <code>ChangeEvent</code> if the
219      * <code>start</code> has changed.
220      *
221      * @param start defines the first date in the sequence
222      * @see #getStart
223      * @see #setEnd
224      * @see #addChangeListener
225      */

226     public void setStart(Comparable JavaDoc start) {
227     if ((start == null) ? (this.start != null) : !start.equals(this.start)) {
228         this.start = start;
229         fireStateChanged();
230     }
231     }
232
233
234     /**
235      * Returns the first <code>Date</code> in the sequence.
236      *
237      * @return the value of the <code>start</code> property
238      * @see #setStart
239      */

240     public Comparable JavaDoc getStart() {
241     return start;
242     }
243
244
245     /**
246      * Changes the upper limit for <code>Date</code>s in this sequence.
247      * If <code>start</code> is <code>null</code>, then there is no upper
248      * limit. No bounds checking is done here: the new
249      * start value may invalidate the <code>(start &lt;= value &lt;= end)</code>
250      * invariant enforced by the constructors. This is to simplify updating
251      * the model. Naturally, one should ensure that the invariant is true
252      * before calling the <code>nextValue</code>, <code>previousValue</code>,
253      * or <code>setValue</code> methods.
254      * <p>
255      * Typically this property is a <code>Date</code> however it's possible to use
256      * <code>Comparable</code> with a <code>compareTo</code> method for
257      * <code>Date</code>s. See <code>setStart</code> for an example.
258      * <p>
259      * This method fires a <code>ChangeEvent</code> if the <code>end</code>
260      * has changed.
261      *
262      * @param end defines the last date in the sequence
263      * @see #getEnd
264      * @see #setStart
265      * @see #addChangeListener
266      */

267     public void setEnd(Comparable JavaDoc end) {
268     if ((end == null) ? (this.end != null) : !end.equals(this.end)) {
269         this.end = end;
270         fireStateChanged();
271     }
272     }
273
274     
275     /**
276      * Returns the last <code>Date</code> in the sequence.
277      *
278      * @return the value of the <code>end</code> property
279      * @see #setEnd
280      */

281     public Comparable JavaDoc getEnd() {
282     return end;
283     }
284
285
286     /**
287      * Changes the size of the date value change computed
288      * by the <code>nextValue</code> and <code>previousValue</code> methods.
289      * The <code>calendarField</code> parameter must be one of the
290      * <code>Calendar</code> field constants like <code>Calendar.MONTH</code>
291      * or <code>Calendar.MINUTE</code>.
292      * The <code>nextValue</code> and <code>previousValue</code> methods
293      * simply move the specified <code>Calendar</code> field forward or backward
294      * by one unit with the <code>Calendar.add</code> method.
295      * You should use this method with care as some UIs may set the
296      * calendarField before commiting the edit to spin the field under
297      * the cursor. If you only want one field to spin you can subclass
298      * and ignore the setCalendarField calls.
299      *
300      * @param calendarField one of
301      * <ul>
302      * <li><code>Calendar.ERA</code>
303      * <li><code>Calendar.YEAR</code>
304      * <li><code>Calendar.MONTH</code>
305      * <li><code>Calendar.WEEK_OF_YEAR</code>
306      * <li><code>Calendar.WEEK_OF_MONTH</code>
307      * <li><code>Calendar.DAY_OF_MONTH</code>
308      * <li><code>Calendar.DAY_OF_YEAR</code>
309      * <li><code>Calendar.DAY_OF_WEEK</code>
310      * <li><code>Calendar.DAY_OF_WEEK_IN_MONTH</code>
311      * <li><code>Calendar.AM_PM</code>
312      * <li><code>Calendar.HOUR</code>
313      * <li><code>Calendar.HOUR_OF_DAY</code>
314      * <li><code>Calendar.MINUTE</code>
315      * <li><code>Calendar.SECOND</code>
316      * <li><code>Calendar.MILLISECOND</code>
317      * </ul>
318      * <p>
319      * This method fires a <code>ChangeEvent</code> if the
320      * <code>calendarField</code> has changed.
321      *
322      * @see #getCalendarField
323      * @see #getNextValue
324      * @see #getPreviousValue
325      * @see Calendar#add
326      * @see #addChangeListener
327      */

328     public void setCalendarField(int calendarField) {
329     if (!calendarFieldOK(calendarField)) {
330         throw new IllegalArgumentException JavaDoc("invalid calendarField");
331     }
332     if (calendarField != this.calendarField) {
333         this.calendarField = calendarField;
334         fireStateChanged();
335     }
336     }
337
338
339     /**
340      * Returns the <code>Calendar</code> field that is added to or subtracted from
341      * by the <code>nextValue</code> and <code>previousValue</code> methods.
342      *
343      * @return the value of the <code>calendarField</code> property
344      * @see #setCalendarField
345      */

346     public int getCalendarField() {
347     return calendarField;
348     }
349
350
351     /**
352      * Returns the next <code>Date</code> in the sequence, or <code>null</code> if
353      * the next date is after <code>end</code>.
354      *
355      * @return the next <code>Date</code> in the sequence, or <code>null</code> if
356      * the next date is after <code>end</code>.
357      *
358      * @see SpinnerModel#getNextValue
359      * @see #getPreviousValue
360      * @see #setCalendarField
361      */

362     public Object JavaDoc getNextValue() {
363     Calendar cal = Calendar.getInstance();
364     cal.setTime(value.getTime());
365     cal.add(calendarField, 1);
366     Date next = cal.getTime();
367     return ((end == null) || (end.compareTo(next) >= 0)) ? next : null;
368     }
369
370
371     /**
372      * Returns the previous <code>Date</code> in the sequence, or <code>null</code>
373      * if the previous date is before <code>start</code>.
374      *
375      * @return the previous <code>Date</code> in the sequence, or
376      * <code>null</code> if the previous date
377      * is before <code>start</code>
378      *
379      * @see SpinnerModel#getPreviousValue
380      * @see #getNextValue
381      * @see #setCalendarField
382      */

383     public Object JavaDoc getPreviousValue() {
384     Calendar cal = Calendar.getInstance();
385     cal.setTime(value.getTime());
386     cal.add(calendarField, -1);
387     Date prev = cal.getTime();
388     return ((start == null) || (start.compareTo(prev) <= 0)) ? prev : null;
389     }
390
391
392     /**
393      * Returns the current element in this sequence of <code>Date</code>s.
394      * This method is equivalent to <code>(Date)getValue</code>.
395      *
396      * @return the <code>value</code> property
397      * @see #setValue
398      */

399     public Date getDate() {
400     return value.getTime();
401     }
402
403
404     /**
405      * Returns the current element in this sequence of <code>Date</code>s.
406      *
407      * @return the <code>value</code> property
408      * @see #setValue
409      * @see #getDate
410      */

411     public Object JavaDoc getValue() {
412     return value.getTime();
413     }
414
415
416     /**
417      * Sets the current <code>Date</code> for this sequence.
418      * If <code>value</code> is <code>null</code>,
419      * an <code>IllegalArgumentException</code> is thrown. No bounds
420      * checking is done here:
421      * the new value may invalidate the <code>(start &lt= value &lt end)</code>
422      * invariant enforced by the constructors. Naturally, one should ensure
423      * that the <code>(start &lt;= value &lt;= maximum)</code> invariant is true
424      * before calling the <code>nextValue</code>, <code>previousValue</code>,
425      * or <code>setValue</code> methods.
426      * <p>
427      * This method fires a <code>ChangeEvent</code> if the
428      * <code>value</code> has changed.
429      *
430      * @param value the current (non <code>null</code>)
431      * <code>Date</code> for this sequence
432      * @throws IllegalArgumentException if value is <code>null</code>
433      * or not a <code>Date</code>
434      * @see #getDate
435      * @see #getValue
436      * @see #addChangeListener
437      */

438     public void setValue(Object JavaDoc value) {
439     if ((value == null) || !(value instanceof Date)) {
440         throw new IllegalArgumentException JavaDoc("null value");
441     }
442     if (!value.equals(this.value.getTime())) {
443         this.value.setTime((Date)value);
444         fireStateChanged();
445     }
446     }
447 }
448
Popular Tags