1 /* 2 * Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package javax.swing; 27 28 import java.util.*; 29 import java.io.Serializable; 30 31 32 /** 33 * A simple implementation of <code>SpinnerModel</code> whose 34 * values are defined by an array or a <code>List</code>. 35 * For example to create a model defined by 36 * an array of the names of the days of the week: 37 * <pre> 38 * String[] days = new DateFormatSymbols().getWeekdays(); 39 * SpinnerModel model = new SpinnerListModel(Arrays.asList(days).subList(1, 8)); 40 * </pre> 41 * This class only stores a reference to the array or <code>List</code> 42 * so if an element of the underlying sequence changes, it's up 43 * to the application to notify the <code>ChangeListeners</code> by calling 44 * <code>fireStateChanged</code>. 45 * <p> 46 * This model inherits a <code>ChangeListener</code>. 47 * The <code>ChangeListener</code>s are notified whenever the 48 * model's <code>value</code> or <code>list</code> properties changes. 49 * 50 * @see JSpinner 51 * @see SpinnerModel 52 * @see AbstractSpinnerModel 53 * @see SpinnerNumberModel 54 * @see SpinnerDateModel 55 * 56 * @author Hans Muller 57 * @since 1.4 58 */ 59 @SuppressWarnings("serial") // Superclass is not serializable across versions 60 public class SpinnerListModel extends AbstractSpinnerModel implements Serializable 61 { 62 private List<?> list; 63 private int index; 64 65 66 /** 67 * Constructs a <code>SpinnerModel</code> whose sequence of 68 * values is defined by the specified <code>List</code>. 69 * The initial value (<i>current element</i>) 70 * of the model will be <code>values.get(0)</code>. 71 * If <code>values</code> is <code>null</code> or has zero 72 * size, an <code>IllegalArugmentException</code> is thrown. 73 * 74 * @param values the sequence this model represents 75 * @throws IllegalArgumentException if <code>values</code> is 76 * <code>null</code> or zero size 77 */ 78 public SpinnerListModel(List<?> values) { 79 if (values == null || values.size() == 0) { 80 throw new IllegalArgumentException("SpinnerListModel(List) expects non-null non-empty List"); 81 } 82 this.list = values; 83 this.index = 0; 84 } 85 86 87 /** 88 * Constructs a <code>SpinnerModel</code> whose sequence of values 89 * is defined by the specified array. The initial value of the model 90 * will be <code>values[0]</code>. If <code>values</code> is 91 * <code>null</code> or has zero length, an 92 * <code>IllegalArgumentException</code> is thrown. 93 * 94 * @param values the sequence this model represents 95 * @throws IllegalArgumentException if <code>values</code> is 96 * <code>null</code> or zero length 97 */ 98 public SpinnerListModel(Object[] values) { 99 if (values == null || values.length == 0) { 100 throw new IllegalArgumentException("SpinnerListModel(Object[]) expects non-null non-empty Object[]"); 101 } 102 this.list = Arrays.asList(values); 103 this.index = 0; 104 } 105 106 107 /** 108 * Constructs an effectively empty <code>SpinnerListModel</code>. 109 * The model's list will contain a single 110 * <code>"empty"</code> string element. 111 */ 112 public SpinnerListModel() { 113 this(new Object[]{"empty"}); 114 } 115 116 117 /** 118 * Returns the <code>List</code> that defines the sequence for this model. 119 * 120 * @return the value of the <code>list</code> property 121 * @see #setList 122 */ 123 public List<?> getList() { 124 return list; 125 } 126 127 128 /** 129 * Changes the list that defines this sequence and resets the index 130 * of the models <code>value</code> to zero. Note that <code>list</code> 131 * is not copied, the model just stores a reference to it. 132 * <p> 133 * This method fires a <code>ChangeEvent</code> if <code>list</code> is 134 * not equal to the current list. 135 * 136 * @param list the sequence that this model represents 137 * @throws IllegalArgumentException if <code>list</code> is 138 * <code>null</code> or zero length 139 * @see #getList 140 */ 141 public void setList(List<?> list) { 142 if ((list == null) || (list.size() == 0)) { 143 throw new IllegalArgumentException("invalid list"); 144 } 145 if (!list.equals(this.list)) { 146 this.list = list; 147 index = 0; 148 fireStateChanged(); 149 } 150 } 151 152 153 /** 154 * Returns the current element of the sequence. 155 * 156 * @return the <code>value</code> property 157 * @see SpinnerModel#getValue 158 * @see #setValue 159 */ 160 public Object getValue() { 161 return list.get(index); 162 } 163 164 165 /** 166 * Changes the current element of the sequence and notifies 167 * <code>ChangeListeners</code>. If the specified 168 * value is not equal to an element of the underlying sequence 169 * then an <code>IllegalArgumentException</code> is thrown. 170 * In the following example the <code>setValue</code> call 171 * would cause an exception to be thrown: 172 * <pre> 173 * String[] values = {"one", "two", "free", "four"}; 174 * SpinnerModel model = new SpinnerListModel(values); 175 * model.setValue("TWO"); 176 * </pre> 177 * 178 * @param elt the sequence element that will be model's current value 179 * @throws IllegalArgumentException if the specified value isn't allowed 180 * @see SpinnerModel#setValue 181 * @see #getValue 182 */ 183 public void setValue(Object elt) { 184 int index = list.indexOf(elt); 185 if (index == -1) { 186 throw new IllegalArgumentException("invalid sequence element"); 187 } 188 else if (index != this.index) { 189 this.index = index; 190 fireStateChanged(); 191 } 192 } 193 194 195 /** 196 * Returns the next legal value of the underlying sequence or 197 * <code>null</code> if value is already the last element. 198 * 199 * @return the next legal value of the underlying sequence or 200 * <code>null</code> if value is already the last element 201 * @see SpinnerModel#getNextValue 202 * @see #getPreviousValue 203 */ 204 public Object getNextValue() { 205 return (index >= (list.size() - 1)) ? null : list.get(index + 1); 206 } 207 208 209 /** 210 * Returns the previous element of the underlying sequence or 211 * <code>null</code> if value is already the first element. 212 * 213 * @return the previous element of the underlying sequence or 214 * <code>null</code> if value is already the first element 215 * @see SpinnerModel#getPreviousValue 216 * @see #getNextValue 217 */ 218 public Object getPreviousValue() { 219 return (index <= 0) ? null : list.get(index - 1); 220 } 221 222 223 /** 224 * Returns the next object that starts with <code>substring</code>. 225 * 226 * @param substring the string to be matched 227 * @return the match 228 */ 229 Object findNextMatch(String substring) { 230 int max = list.size(); 231 232 if (max == 0) { 233 return null; 234 } 235 int counter = index; 236 237 do { 238 Object value = list.get(counter); 239 String string = value.toString(); 240 241 if (string != null && string.startsWith(substring)) { 242 return value; 243 } 244 counter = (counter + 1) % max; 245 } while (counter != index); 246 return null; 247 } 248 }