Source for javax.swing.SpinnerDateModel

   1: /* SpinnerDateModel.java --
   2:    Copyright (C) 2002, 2004, 2006 Free Software Foundation, Inc.
   3: 
   4: This file is part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2, or (at your option)
   9: any later version.
  10: 
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; see the file COPYING.  If not, write to the
  18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19: 02110-1301 USA.
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version. */
  37: 
  38: 
  39: package javax.swing;
  40: 
  41: import java.io.Serializable;
  42: import java.util.Calendar;
  43: import java.util.Date;
  44: 
  45: import javax.swing.event.ChangeEvent;
  46: 
  47: /**
  48:  * A date model used by the {@link JSpinner} component.  This implements a 
  49:  * spinner model for dates, rotating a calendar field such as  month, year, 
  50:  * day, week, hour, minute.
  51:  *
  52:  * @author Sven de Marothy
  53:  * @since 1.4
  54:  */
  55: public class SpinnerDateModel extends AbstractSpinnerModel
  56:   implements Serializable
  57: {
  58:   /** The current date. */
  59:   private Calendar date;
  60:   
  61:   /** 
  62:    * A constraint on the start or earliest permitted date (<code>null</code> 
  63:    * for no minimum). 
  64:    */
  65:   private Comparable start;
  66: 
  67:   /** 
  68:    * A constraint on the end or latest permitted date (<code>null</code> for no 
  69:    * maximum). 
  70:    */
  71:   private Comparable end;
  72:   
  73:   /**
  74:    * The calendar field used to calculate the previous or next date. 
  75:    */
  76:   private int calendarField;
  77: 
  78:   /**
  79:    * For compatability with Sun's JDK
  80:    */
  81:   private static final long serialVersionUID = -4802518107105940612L;
  82: 
  83:   /**
  84:    * Constructs a <code>SpinnerDateModel</code> using the current date,
  85:    * no start or end limit, and {@link Calendar#DAY_OF_MONTH} as the calendar 
  86:    * field.
  87:    */
  88:   public SpinnerDateModel()
  89:   {
  90:     this(new Date(), null, null, Calendar.DAY_OF_MONTH);
  91:   }
  92: 
  93:   /**
  94:    * Constructs a <code>SpinnerDateModel</code> with the specified value, lower
  95:    * and upper bounds, and which spins the specified calendar field. 
  96:    * <p>
  97:    * The <code>start</code> and <code>end</code> limits must have a
  98:    * <code>compareTo</code> method that supports instances of {@link Date}, but
  99:    * do not themselves need to be instances of {@link Date} (although typically
 100:    * they are).
 101:    * 
 102:    * @param value  the initial value/date (<code>null</code> not permitted).
 103:    * @param start  a constraint that specifies the earliest permitted date 
 104:    *     value, or <code>null</code> for no lower limit.
 105:    * @param end  a constraint that specifies the latest permitted date value,
 106:    *     or <code>null</code> for no upper limit.
 107:    * @param calendarField  the <code>Calendar</code> field to spin,
 108:    *     (Calendar.ZONE_OFFSET and Calendar.DST_OFFSET are invalid)
 109:    */
 110:   public SpinnerDateModel(Date value, Comparable start, Comparable end,
 111:                           int calendarField)
 112:   {
 113:     if (value == null) 
 114:       throw new IllegalArgumentException("Null 'value' argument.");
 115:     if (start != null && start.compareTo(value) > 0)
 116:       throw new IllegalArgumentException("Require value on or after start.");
 117:     if (end != null && end.compareTo(value) < 0)
 118:       throw new IllegalArgumentException("Require value on or before end.");
 119:     date = Calendar.getInstance();
 120:     date.setTime(value);
 121:     this.start = start;
 122:     this.end = end;
 123:     setCalendarField(calendarField);
 124:   }
 125: 
 126:   /**
 127:    * Returns the {@link Calendar} field used to calculate the previous and 
 128:    * next dates in the sequence.
 129:    * 
 130:    * @return The date field code.
 131:    */
 132:   public int getCalendarField()
 133:   {
 134:     return calendarField;
 135:   }
 136: 
 137:   /**
 138:    * Returns the current date/time.
 139:    * 
 140:    * @return The current date/time (never <code>null</code>).
 141:    * 
 142:    * @see #getValue()
 143:    */
 144:   public Date getDate()
 145:   {
 146:     return date.getTime();
 147:   }
 148: 
 149:   /**
 150:    * Returns the lower limit on the date/time value, or <code>null</code> if 
 151:    * there is no minimum date/time.
 152:    * 
 153:    * @return The lower limit.
 154:    * 
 155:    * @see #setStart(Comparable)
 156:    */
 157:   public Comparable getStart()
 158:   {
 159:     return start;
 160:   }
 161: 
 162:   /**
 163:    * Returns the upper limit on the date/time value, or <code>null</code> if 
 164:    * there is no maximum date/time.
 165:    * 
 166:    * @return The upper limit.
 167:    * 
 168:    * @see #setEnd(Comparable)
 169:    */
 170:   public Comparable getEnd()
 171:   {
 172:     return end;
 173:   }
 174: 
 175:   /**
 176:    * Returns the current date in the sequence (this method returns the same as 
 177:    * {@link #getDate()}).
 178:    * 
 179:    * @return The current date (never <code>null</code>).
 180:    */
 181:   public Object getValue()
 182:   {
 183:     return date.getTime();
 184:   }
 185: 
 186:   /**
 187:    * Returns the next date in the sequence, or <code>null</code> if the
 188:    * next date is past the upper limit (if one is specified).  The current date 
 189:    * is not changed.
 190:    * 
 191:    * @return The next date, or <code>null</code> if the current value is
 192:    *         the latest date represented by the model.
 193:    *         
 194:    * @see #getEnd()
 195:    */
 196:   public Object getNextValue()
 197:   {
 198:     Calendar nextCal = Calendar.getInstance();
 199:     nextCal.setTime(date.getTime());
 200:     nextCal.roll(calendarField, true);
 201:     Date nextDate = nextCal.getTime();
 202:     if (end != null)
 203:       if (end.compareTo(nextDate) < 0)
 204:         return null;
 205:     return nextDate;
 206:   }
 207: 
 208:   /**
 209:    * Returns the previous date in the sequence, or <code>null</code> if the
 210:    * previous date is prior to the lower limit (if one is specified).  The 
 211:    * current date is not changed.
 212:    * 
 213:    * @return The previous date, or <code>null</code> if the current value is
 214:    *         the earliest date represented by the model.
 215:    *         
 216:    * @see #getStart()
 217:    */
 218:   public Object getPreviousValue()
 219:   {
 220:     Calendar prevCal = Calendar.getInstance();
 221:     prevCal.setTime(date.getTime());
 222:     prevCal.roll(calendarField, false);
 223:     Date prevDate = prevCal.getTime();
 224:     if (start != null)
 225:       if (start.compareTo(prevDate) > 0)
 226:         return null;
 227:     return prevDate;
 228:   }
 229: 
 230:   /**
 231:    * Sets the date field to change when calculating the next and previous 
 232:    * values. It must be a valid {@link Calendar} field, excluding 
 233:    * {@link Calendar#ZONE_OFFSET} and {@link Calendar#DST_OFFSET}.
 234:    * 
 235:    * @param calendarField  the calendar field to set.
 236:    * 
 237:    * @throws IllegalArgumentException if <code>calendarField</code> is not 
 238:    *         a valid code.
 239:    */
 240:   public void setCalendarField(int calendarField)
 241:   {
 242:     if (calendarField < 0 || calendarField >= Calendar.FIELD_COUNT
 243:         || calendarField == Calendar.ZONE_OFFSET
 244:         || calendarField == Calendar.DST_OFFSET)
 245:       throw new IllegalArgumentException("Illegal calendarField");
 246: 
 247:     if (this.calendarField != calendarField)
 248:       {
 249:         this.calendarField = calendarField;
 250:         fireStateChanged();
 251:       }
 252:   }
 253: 
 254:   /**
 255:    * Sets the lower limit for the date/time value and, if the new limit is 
 256:    * different to the old limit, sends a {@link ChangeEvent} to all registered 
 257:    * listeners.  A <code>null</code> value is interpreted as "no lower limit".  
 258:    * No check is made to ensure that the current date/time is on or after the 
 259:    * new lower limit - the caller is responsible for ensuring that this 
 260:    * relationship holds.  In addition, the caller should ensure that
 261:    * <code>start</code> is {@link Serializable}.
 262:    * 
 263:    * @param start  the new lower limit for the date/time value 
 264:    *     (<code>null</code> permitted).
 265:    */
 266:   public void setStart(Comparable start)
 267:   {
 268:     if (this.start != start)
 269:       {
 270:         this.start = start;
 271:         fireStateChanged();
 272:       }
 273:   }
 274: 
 275:   /**
 276:    * Sets the upper limit for the date/time value and, if the new limit is 
 277:    * different to the old limit, sends a {@link ChangeEvent} to all registered 
 278:    * listeners.  A <code>null</code> value is interpreted as "no upper limit".  
 279:    * No check is made to ensure that the current date/time is on or before the 
 280:    * new upper limit - the caller is responsible for ensuring that this 
 281:    * relationship holds.  In addition, the caller should ensure that
 282:    * <code>end</code> is {@link Serializable}.
 283:    * 
 284:    * @param end  the new upper limit for the date/time value (<code>null</code>
 285:    *     permitted).
 286:    */
 287:   public void setEnd(Comparable end)
 288:   {
 289:     if (this.end != end)
 290:       {
 291:         this.end = end;
 292:         fireStateChanged();
 293:       }
 294:   }
 295: 
 296:   /**
 297:    * Sets the current date and, if the new value is different to the old
 298:    * value, sends a {@link ChangeEvent} to all registered listeners.
 299:    *
 300:    * @param value  the new date (<code>null</code> not permitted, must be an 
 301:    *               instance of <code>Date</code>).
 302:    *               
 303:    * @throws IllegalArgumentException if <code>value</code> is not an instance 
 304:    *         of <code>Date</code>.
 305:    */
 306:   public void setValue(Object value)
 307:   {
 308:     if (! (value instanceof Date) || value == null)
 309:       throw new IllegalArgumentException("Value not a date.");
 310:     
 311:     if (!date.getTime().equals(value)) 
 312:       {
 313:         date.setTime((Date) value);
 314:         fireStateChanged();
 315:       }
 316:   }
 317: }