Source for javax.swing.SpinnerListModel

   1: /* SpinnerListModel.java -- A spinner model backed by a list or an array.
   2:    Copyright (C) 2004, 2005  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: package javax.swing;
  39: 
  40: import java.io.Serializable;
  41: import java.util.ArrayList;
  42: import java.util.Arrays;
  43: import java.util.List;
  44: 
  45: /**
  46:  * An implementation of <code>SpinnerModel</code> which uses the values
  47:  * contained within a list or an array.  The backing list or array is
  48:  * only stored as a reference within the class.  As a result, changes
  49:  * made elsewhere to the members of the list or array are reflected by
  50:  * this model.
  51:  * <p>
  52:  *
  53:  * The model itself inherits a list of <code>ChangeListener</code>s from
  54:  * <code>AbstractSpinnerModel</code>.  As this code is unaware of changes
  55:  * made to the backing list or array, it is the responsibility of the
  56:  * application using the model to invoke <code>fireStateChanged()</code>,
  57:  * in order to notify any <code>ChangeListener</code>s, when the list or array
  58:  * changes.  The model handles notification when the reference itself
  59:  * is changed via <code>setList()</code> or when the current value is
  60:  * set directly using <code>setValue()</code>.
  61:  *
  62:  * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
  63:  * @see SpinnerModel
  64:  * @see AbstractSpinnerModel
  65:  * @see JSpinner
  66:  * @since 1.4
  67:  */
  68: 
  69: public class SpinnerListModel
  70:     extends AbstractSpinnerModel
  71:     implements Serializable
  72: {
  73:     /**
  74:      * For compatability with Sun's JDK
  75:      */
  76:     private static final long serialVersionUID = 3358804052191994516L;
  77: 
  78:     /**
  79:      * The backing list for this model.
  80:      */
  81:     private List list;
  82: 
  83:     /**
  84:      * The current index in the list.
  85:      */
  86:     private transient int index;
  87: 
  88:     /**
  89:      * Constructs a default <code>SpinnerListModel</code>.  This
  90:      * is a model backed by a list containing only the single
  91:      * <code>String</code> element, "empty".
  92:      */
  93:     public SpinnerListModel()
  94:     {
  95:     List defaultList;
  96: 
  97:     /* Create an empty list */
  98:     defaultList = new ArrayList();
  99:     /* Add the string "empty" */
 100:     defaultList.add("empty");
 101:     /* Set the list */
 102:     setList(defaultList);
 103:     }
 104: 
 105:     /**
 106:      * Constructs a <code>SpinnerListModel</code> using the supplied list.
 107:      * The model maintains a reference to this list, and returns
 108:      * consecutive elements in response to calls to <code>getNextValue()</code>.
 109:      * The initial value is that at position 0, so an initial call
 110:      * to <code>getValue()</code> returns the same as <code>list.get(0)</code>.
 111:      *
 112:      * @param list The list to use for this model.
 113:      * @throws IllegalArgumentException if the list is null or contains no
 114:      *         elements.
 115:      * @see SpinnerListModel#getNextValue()
 116:      * @see SpinnerListModel#getValue()
 117:      */ 
 118:     public SpinnerListModel(List list)
 119:     {
 120:     /* Retain a reference to the valid list */
 121:         setList(list);
 122:     }
 123: 
 124:     /**
 125:      * Constructs a <code>SpinnerListModel</code> using the supplied array.
 126:      * The model stores a reference to the wrapper list returned by
 127:      * <code>Arrays.asList()</code>.  The wrapper list reflects modifications
 128:      * in the underlying array, so these changes will also be reflected
 129:      * by the model.  The model produces consecutive elements from the array
 130:      * in response to calls to <code>getNextValue()</code>.  The initial
 131:      * value returned by <code>getValue()</code> is the same as
 132:      * <code>array[0]</code>.
 133:      *
 134:      * @param array The array to use for this model.
 135:      * @throws IllegalArgumentException if the array is null or contains
 136:      *         no elements.
 137:      * @see Arrays#asList(Object[])
 138:      * @see SpinnerListModel#getNextValue()
 139:      * @see SpinnerListModel#getValue()
 140:      */
 141:     public SpinnerListModel(Object[] array)
 142:     {
 143:     /* Check for a null or zero-sized array */
 144:     if (array == null || array.length == 0)
 145:         {
 146:         throw new IllegalArgumentException("The supplied array was invalid.");
 147:         }
 148:     /* 
 149:        Retain a reference to a wrapper around the valid array 
 150:        The array, in list form, will be tested again here, but we can't really
 151:        avoid this -- a null value to Arrays.asList will throw a NullPointerException
 152:     */ 
 153:     setList(Arrays.asList(array));
 154:     }
 155: 
 156:     /**
 157:      * Returns the backing list for this model.
 158:      *
 159:      * @return The backing list.
 160:      */
 161:     public List getList()
 162:     {
 163:     return list;
 164:     }
 165:     
 166:     /**
 167:      * Returns the next value from the list, which is the same as the element
 168:      * stored at the current index + 1.  Null is returned if there are no more
 169:      * values to be returned (the end of the list has been reached).  An
 170:      * ambiguity can occur here, as null may also be returned as a valid list
 171:      * element.  This operation does not change the current value.
 172:      *
 173:      * @return The next value from the list or null.
 174:      */
 175:     public Object getNextValue()
 176:     {
 177:     /* Check for a next value */
 178:     if (index < (list.size() - 1))
 179:         {
 180:         /* Return the element at the next index */
 181:         return list.get(index + 1);
 182:         }
 183:     else
 184:         {
 185:         /* Return null as this is the end of the list */
 186:         return null;
 187:         }
 188:     }
 189: 
 190:     /**
 191:      * Returns the previous value from the list, which is the same as the element
 192:      * stored at the current index - 1.  Null is returned if there are no more
 193:      * values to be returned (the start of the list has been reached).  An
 194:      * ambiguity can occur here, as null may also be returned as a valid list
 195:      * element.  This operation does not change the current value.
 196:      *
 197:      * @return The previous value from the list or null.
 198:      */
 199:     public Object getPreviousValue()
 200:     {
 201:     /* Check for a previous value. */
 202:     if (index > 0) 
 203:         {
 204:         /* Return the element at the previous position */
 205:         return list.get(index - 1);
 206:         }
 207:     else
 208:         {
 209:         /* Return null as this is the start of the list */
 210:         return null;
 211:         }
 212:     }
 213: 
 214:     /**
 215:      * Returns the current value of the model.  Initially, this will
 216:      * be the element at position 0.  On later invocations, this will
 217:      * be the last element returned by <code>getNextValue()</code>
 218:      * or <code>getPreviousValue()</code>.
 219:      *
 220:      * @return The current value.
 221:      * @see SpinnerListModel#getPreviousValue()
 222:      * @see SpinnerListModel#getNextValue()
 223:      */
 224:     public Object getValue()
 225:     {
 226:     return list.get(index);
 227:     }
 228: 
 229:     /**
 230:      * Changes the backing list for this model.  The model only stores
 231:      * a reference to the list, so any changes made to the list elsewhere
 232:      * will be reflected in the values returned by the model.  A
 233:      * <code>ChangeEvent</code> is fired if the list being used actually
 234:      * changes (i.e. the new list is not referentially equal (!=) to the
 235:      * old one).
 236:      *
 237:      * @param list The new list to use.
 238:      * @throws IllegalArgumentException if the list is null or contains
 239:      *         no elements.
 240:      * @see ChangeEvent
 241:      */
 242:     public void setList(List list)
 243:     {
 244:     /* Check for null or zero size list */
 245:     if (list == null || list.size() == 0)
 246:         {
 247:         throw new IllegalArgumentException("The supplied list was invalid.");
 248:         }
 249:     /* Check for a change of referenced list */
 250:     if (this.list != list)
 251:         {
 252:         /* Store the new list */
 253:         this.list = list;
 254:         /* Notify listeners of a change */
 255:         fireStateChanged();
 256:         }
 257:     /* We reset the other values in either case */
 258:     /* Set the index to 0 */
 259:     index = 0;
 260:     }
 261: 
 262:     /**
 263:      * Sets the current value of the model to be the one supplied.
 264:      * The value must exist within the backing list in order for
 265:      * the change to take place.  Otherwise, an exception is thrown.
 266:      * The value used is the first occurrence of the value within
 267:      * the backing list.  Listeners are notified of this change.
 268:      * Following the change, <code>getNextValue()</code> and
 269:      * <code>getPreviousValue()</code> return the objects following
 270:      * and prior to the supplied value, respectively. 
 271:      *
 272:      * @param value The requested new value of the list.
 273:      * @throws IllegalArgumentException if the supplied value does
 274:      *         not exist in the backing list.
 275:      * @see SpinnerListModel#getPreviousValue()
 276:      * @see SpinnerListModel#getNextValue()
 277:      */
 278:     public void setValue(Object value)
 279:     {
 280:     int valueIndex;
 281: 
 282:     /* Search for the value in the list */
 283:     valueIndex = list.indexOf(value);
 284:     /* Check for the value being found */
 285:     if (valueIndex == -1)
 286:         {
 287:         throw new IllegalArgumentException("The supplied value does not "
 288:                            + "exist in this list");
 289:         }
 290:     /* Make the indices match */
 291:     index = valueIndex;
 292:     /* Notify the listeners */
 293:     fireStateChanged();
 294:     }
 295: 
 296: }