001 /*
002 * Copyright 2000-2004 Sun Microsystems, Inc. All Rights Reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation. Sun designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Sun in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022 * CA 95054 USA or visit www.sun.com if you need additional information or
023 * have any questions.
024 */
025
026 package javax.swing;
027
028 import java.util.*;
029 import java.io.Serializable;
030
031 /**
032 * A simple implementation of <code>SpinnerModel</code> whose
033 * values are defined by an array or a <code>List</code>.
034 * For example to create a model defined by
035 * an array of the names of the days of the week:
036 * <pre>
037 * String[] days = new DateFormatSymbols().getWeekdays();
038 * SpinnerModel model = new SpinnerListModel(Arrays.asList(days).subList(1, 8));
039 * </pre>
040 * This class only stores a reference to the array or <code>List</code>
041 * so if an element of the underlying sequence changes, it's up
042 * to the application to notify the <code>ChangeListeners</code> by calling
043 * <code>fireStateChanged</code>.
044 * <p>
045 * This model inherits a <code>ChangeListener</code>.
046 * The <code>ChangeListener</code>s are notified whenever the
047 * model's <code>value</code> or <code>list</code> properties changes.
048 *
049 * @see JSpinner
050 * @see SpinnerModel
051 * @see AbstractSpinnerModel
052 * @see SpinnerNumberModel
053 * @see SpinnerDateModel
054 *
055 * @version 1.18 05/05/07
056 * @author Hans Muller
057 * @since 1.4
058 */
059 public class SpinnerListModel extends AbstractSpinnerModel implements
060 Serializable {
061 private List list;
062 private int index;
063
064 /**
065 * Constructs a <code>SpinnerModel</code> whose sequence of
066 * values is defined by the specified <code>List</code>.
067 * The initial value (<i>current element</i>)
068 * of the model will be <code>values.get(0)</code>.
069 * If <code>values</code> is <code>null</code> or has zero
070 * size, an <code>IllegalArugmentException</code> is thrown.
071 *
072 * @param values the sequence this model represents
073 * @throws IllegalArugmentException if <code>values</code> is
074 * <code>null</code> or zero size
075 */
076 public SpinnerListModel(List<?> values) {
077 if (values == null || values.size() == 0) {
078 throw new IllegalArgumentException(
079 "SpinnerListModel(List) expects non-null non-empty List");
080 }
081 this .list = values;
082 this .index = 0;
083 }
084
085 /**
086 * Constructs a <code>SpinnerModel</code> whose sequence of values
087 * is defined by the specified array. The initial value of the model
088 * will be <code>values[0]</code>. If <code>values</code> is
089 * <code>null</code> or has zero length, an
090 * <code>IllegalArugmentException</code> is thrown.
091 *
092 * @param values the sequence this model represents
093 * @throws IllegalArugmentException if <code>values</code> is
094 * <code>null</code> or zero length
095 */
096 public SpinnerListModel(Object[] values) {
097 if (values == null || values.length == 0) {
098 throw new IllegalArgumentException(
099 "SpinnerListModel(Object[]) expects non-null non-empty Object[]");
100 }
101 this .list = Arrays.asList(values);
102 this .index = 0;
103 }
104
105 /**
106 * Constructs an effectively empty <code>SpinnerListModel</code>.
107 * The model's list will contain a single
108 * <code>"empty"</code> string element.
109 */
110 public SpinnerListModel() {
111 this (new Object[] { "empty" });
112 }
113
114 /**
115 * Returns the <code>List</code> that defines the sequence for this model.
116 *
117 * @return the value of the <code>list</code> property
118 * @see #setList
119 */
120 public List<?> getList() {
121 return list;
122 }
123
124 /**
125 * Changes the list that defines this sequence and resets the index
126 * of the models <code>value</code> to zero. Note that <code>list</code>
127 * is not copied, the model just stores a reference to it.
128 * <p>
129 * This method fires a <code>ChangeEvent</code> if <code>list</code> is
130 * not equal to the current list.
131 *
132 * @param list the sequence that this model represents
133 * @throws IllegalArgumentException if <code>list</code> is
134 * <code>null</code> or zero length
135 * @see #getList
136 */
137 public void setList(List<?> list) {
138 if ((list == null) || (list.size() == 0)) {
139 throw new IllegalArgumentException("invalid list");
140 }
141 if (!list.equals(this .list)) {
142 this .list = list;
143 index = 0;
144 fireStateChanged();
145 }
146 }
147
148 /**
149 * Returns the current element of the sequence.
150 *
151 * @return the <code>value</code> property
152 * @see SpinnerModel#getValue
153 * @see #setValue
154 */
155 public Object getValue() {
156 return list.get(index);
157 }
158
159 /**
160 * Changes the current element of the sequence and notifies
161 * <code>ChangeListeners</code>. If the specified
162 * value is not equal to an element of the underlying sequence
163 * then an <code>IllegalArgumentException</code> is thrown.
164 * In the following example the <code>setValue</code> call
165 * would cause an exception to be thrown:
166 * <pre>
167 * String[] values = {"one", "two", "free", "four"};
168 * SpinnerModel model = new SpinnerListModel(values);
169 * model.setValue("TWO");
170 * </pre>
171 *
172 * @param elt the sequence element that will be model's current value
173 * @throws IllegalArgumentException if the specified value isn't allowed
174 * @see SpinnerModel#setValue
175 * @see #getValue
176 */
177 public void setValue(Object elt) {
178 int index = list.indexOf(elt);
179 if (index == -1) {
180 throw new IllegalArgumentException(
181 "invalid sequence element");
182 } else if (index != this .index) {
183 this .index = index;
184 fireStateChanged();
185 }
186 }
187
188 /**
189 * Returns the next legal value of the underlying sequence or
190 * <code>null</code> if value is already the last element.
191 *
192 * @return the next legal value of the underlying sequence or
193 * <code>null</code> if value is already the last element
194 * @see SpinnerModel#getNextValue
195 * @see #getPreviousValue
196 */
197 public Object getNextValue() {
198 return (index >= (list.size() - 1)) ? null : list
199 .get(index + 1);
200 }
201
202 /**
203 * Returns the previous element of the underlying sequence or
204 * <code>null</code> if value is already the first element.
205 *
206 * @return the previous element of the underlying sequence or
207 * <code>null</code> if value is already the first element
208 * @see SpinnerModel#getPreviousValue
209 * @see #getNextValue
210 */
211 public Object getPreviousValue() {
212 return (index <= 0) ? null : list.get(index - 1);
213 }
214
215 /**
216 * Returns the next object that starts with <code>substring</code>.
217 *
218 * @param substring the string to be matched
219 * @return the match
220 */
221 Object findNextMatch(String substring) {
222 int max = list.size();
223
224 if (max == 0) {
225 return null;
226 }
227 int counter = index;
228
229 do {
230 Object value = list.get(counter);
231 String string = value.toString();
232
233 if (string != null && string.startsWith(substring)) {
234 return value;
235 }
236 counter = (counter + 1) % max;
237 } while (counter != index);
238 return null;
239 }
240 }
|