1 /* 2 * Copyright (c) 2000, 2013, 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 public class SpinnerListModel extends AbstractSpinnerModel implements Serializable 60 { 61 private List list; 62 private int index; 63 64 65 /** 66 * Constructs a <code>SpinnerModel</code> whose sequence of 67 * values is defined by the specified <code>List</code>. 68 * The initial value (<i>current element</i>) 69 * of the model will be <code>values.get(0)</code>. 70 * If <code>values</code> is <code>null</code> or has zero 71 * size, an <code>IllegalArugmentException</code> is thrown. 72 * 73 * @param values the sequence this model represents 74 * @throws IllegalArgumentException if <code>values</code> is 75 * <code>null</code> or zero size 76 */ 77 public SpinnerListModel(List<?> values) { 78 if (values == null || values.size() == 0) { 79 throw new IllegalArgumentException("SpinnerListModel(List) expects non-null non-empty List"); 80 } 81 this.list = values; 82 this.index = 0; 83 } 84 85 86 /** 87 * Constructs a <code>SpinnerModel</code> whose sequence of values 88 * is defined by the specified array. The initial value of the model 89 * will be <code>values[0]</code>. If <code>values</code> is 90 * <code>null</code> or has zero length, an 91 * <code>IllegalArgumentException</code> is thrown. 92 * 93 * @param values the sequence this model represents 94 * @throws IllegalArgumentException if <code>values</code> is 95 * <code>null</code> or zero length 96 */ 97 public SpinnerListModel(Object[] values) { 98 if (values == null || values.length == 0) { 99 throw new IllegalArgumentException("SpinnerListModel(Object[]) expects non-null non-empty Object[]"); 100 } 101 this.list = Arrays.asList(values); 102 this.index = 0; 103 } 104 105 106 /** 107 * Constructs an effectively empty <code>SpinnerListModel</code>. 108 * The model's list will contain a single 109 * <code>"empty"</code> string element. 110 */ 111 public SpinnerListModel() { 112 this(new Object[]{"empty"}); 113 } 114 115 116 /** 117 * Returns the <code>List</code> that defines the sequence for this model. 118 * 119 * @return the value of the <code>list</code> property 120 * @see #setList 121 */ 122 public List<?> getList() { 123 return list; 124 } 125 126 127 /** 128 * Changes the list that defines this sequence and resets the index 129 * of the models <code>value</code> to zero. Note that <code>list</code> 130 * is not copied, the model just stores a reference to it. 131 * <p> 132 * This method fires a <code>ChangeEvent</code> if <code>list</code> is 133 * not equal to the current list. 134 * 135 * @param list the sequence that this model represents 136 * @throws IllegalArgumentException if <code>list</code> is 137 * <code>null</code> or zero length 138 * @see #getList 139 */ 140 public void setList(List<?> list) { 141 if ((list == null) || (list.size() == 0)) { 142 throw new IllegalArgumentException("invalid list"); 143 } 144 if (!list.equals(this.list)) { 145 this.list = list; 146 index = 0; 147 fireStateChanged(); 148 } 149 } 150 151 152 /** 153 * Returns the current element of the sequence. 154 * 155 * @return the <code>value</code> property 156 * @see SpinnerModel#getValue 157 * @see #setValue 158 */ 159 public Object getValue() { 160 return list.get(index); 161 } 162 163 164 /** 165 * Changes the current element of the sequence and notifies 166 * <code>ChangeListeners</code>. If the specified 167 * value is not equal to an element of the underlying sequence 168 * then an <code>IllegalArgumentException</code> is thrown. 169 * In the following example the <code>setValue</code> call 170 * would cause an exception to be thrown: 171 * <pre> 172 * String[] values = {"one", "two", "free", "four"}; 173 * SpinnerModel model = new SpinnerListModel(values); 174 * model.setValue("TWO"); 175 * </pre> 176 * 177 * @param elt the sequence element that will be model's current value 178 * @throws IllegalArgumentException if the specified value isn't allowed 179 * @see SpinnerModel#setValue 180 * @see #getValue 181 */ 182 public void setValue(Object elt) { 183 int index = list.indexOf(elt); 184 if (index == -1) { 185 throw new IllegalArgumentException("invalid sequence element"); 186 } 187 else if (index != this.index) { 188 this.index = index; 189 fireStateChanged(); 190 } 191 } 192 193 194 /** 195 * Returns the next legal value of the underlying sequence or 196 * <code>null</code> if value is already the last element. 197 * 198 * @return the next legal value of the underlying sequence or 199 * <code>null</code> if value is already the last element 200 * @see SpinnerModel#getNextValue 201 * @see #getPreviousValue 202 */ 203 public Object getNextValue() { 204 return (index >= (list.size() - 1)) ? null : list.get(index + 1); 205 } 206 207 208 /** 209 * Returns the previous element of the underlying sequence or 210 * <code>null</code> if value is already the first element. 211 * 212 * @return the previous element of the underlying sequence or 213 * <code>null</code> if value is already the first element 214 * @see SpinnerModel#getPreviousValue 215 * @see #getNextValue 216 */ 217 public Object getPreviousValue() { 218 return (index <= 0) ? null : list.get(index - 1); 219 } 220 221 222 /** 223 * Returns the next object that starts with <code>substring</code>. 224 * 225 * @param substring the string to be matched 226 * @return the match 227 */ 228 Object findNextMatch(String substring) { 229 int max = list.size(); 230 231 if (max == 0) { 232 return null; 233 } 234 int counter = index; 235 236 do { 237 Object value = list.get(counter); 238 String string = value.toString(); 239 240 if (string != null && string.startsWith(substring)) { 241 return value; 242 } 243 counter = (counter + 1) % max; 244 } while (counter != index); 245 return null; 246 } 247 }