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