1. /*
  2. * @(#)SpinnerListModel.java 1.11 04/05/05
  3. *
  4. * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package javax.swing;
  8. import java.util.*;
  9. import java.io.Serializable;
  10. /**
  11. * A simple implementation of <code>SpinnerModel</code> whose
  12. * values are defined by an array or a <code>List</code>.
  13. * For example to create a model defined by
  14. * an array of the names of the days of the week:
  15. * <pre>
  16. * String[] days = new DateFormatSymbols().getWeekdays();
  17. * SpinnerModel model = new SpinnerListModel(Arrays.asList(days).subList(1, 8));
  18. * </pre>
  19. * This class only stores a reference to the array or <code>List</code>
  20. * so if an element of the underlying sequence changes, it's up
  21. * to the application to notify the <code>ChangeListeners</code> by calling
  22. * <code>fireStateChanged</code>.
  23. * <p>
  24. * This model inherits a <code>ChangeListener</code>.
  25. * The <code>ChangeListener</code>s are notified whenever the
  26. * model's <code>value</code> or <code>list</code> properties changes.
  27. *
  28. * @see JSpinner
  29. * @see SpinnerModel
  30. * @see AbstractSpinnerModel
  31. * @see SpinnerNumberModel
  32. * @see SpinnerDateModel
  33. *
  34. * @version 1.11 05/05/04
  35. * @author Hans Muller
  36. * @since 1.4
  37. */
  38. public class SpinnerListModel extends AbstractSpinnerModel implements Serializable
  39. {
  40. private List list;
  41. private int index;
  42. /**
  43. * Constructs a <code>SpinnerModel</code> whose sequence of
  44. * values is defined by the specified <code>List</code>.
  45. * The initial value (<i>current element</i>)
  46. * of the model will be <code>values.get(0)</code>.
  47. * If <code>values</code> is <code>null</code> or has zero
  48. * size, an <code>IllegalArugmentException</code> is thrown.
  49. *
  50. * @param values the sequence this model represents
  51. * @throws IllegalArugmentException if <code>values</code> is
  52. * <code>null</code> or zero size
  53. */
  54. public SpinnerListModel(List<?> values) {
  55. if (values == null || values.size() == 0) {
  56. throw new IllegalArgumentException("SpinnerListModel(List) expects non-null non-empty List");
  57. }
  58. this.list = values;
  59. this.index = 0;
  60. }
  61. /**
  62. * Constructs a <code>SpinnerModel</code> whose sequence of values
  63. * is defined by the specified array. The initial value of the model
  64. * will be <code>values[0]</code>. If <code>values</code> is
  65. * <code>null</code> or has zero length, an
  66. * <code>IllegalArugmentException</code> is thrown.
  67. *
  68. * @param values the sequence this model represents
  69. * @throws IllegalArugmentException if <code>values</code> is
  70. * <code>null</code> or zero length
  71. */
  72. public SpinnerListModel(Object[] values) {
  73. if (values == null || values.length == 0) {
  74. throw new IllegalArgumentException("SpinnerListModel(Object[]) expects non-null non-empty Object[]");
  75. }
  76. this.list = Arrays.asList(values);
  77. this.index = 0;
  78. }
  79. /**
  80. * Constructs an effectively empty <code>SpinnerListModel</code>.
  81. * The model's list will contain a single
  82. * <code>"empty"</code> string element.
  83. */
  84. public SpinnerListModel() {
  85. this(new Object[]{"empty"});
  86. }
  87. /**
  88. * Returns the <code>List</code> that defines the sequence for this model.
  89. *
  90. * @return the value of the <code>list</code> property
  91. * @see #setList
  92. */
  93. public List<?> getList() {
  94. return list;
  95. }
  96. /**
  97. * Changes the list that defines this sequence and resets the index
  98. * of the models <code>value</code> to zero. Note that <code>list</code>
  99. * is not copied, the model just stores a reference to it.
  100. * <p>
  101. * This method fires a <code>ChangeEvent</code> if <code>list</code> is
  102. * not equal to the current list.
  103. *
  104. * @param list the sequence that this model represents
  105. * @throws IllegalArgumentException if <code>list</code> is
  106. * <code>null</code> or zero length
  107. * @see #getList
  108. */
  109. public void setList(List<?> list) {
  110. if ((list == null) || (list.size() == 0)) {
  111. throw new IllegalArgumentException("invalid list");
  112. }
  113. if (!list.equals(this.list)) {
  114. this.list = list;
  115. index = 0;
  116. fireStateChanged();
  117. }
  118. }
  119. /**
  120. * Returns the current element of the sequence.
  121. *
  122. * @return the <code>value</code> property
  123. * @see SpinnerModel#getValue
  124. * @see #setValue
  125. */
  126. public Object getValue() {
  127. return list.get(index);
  128. }
  129. /**
  130. * Changes the current element of the sequence and notifies
  131. * <code>ChangeListeners</code>. If the specified
  132. * value is not equal to an element of the underlying sequence
  133. * then an <code>IllegalArgumentException</code> is thrown.
  134. * In the following example the <code>setValue</code> call
  135. * would cause an exception to be thrown:
  136. * <pre>
  137. * String[] values = {"one", "two", "free", "four"};
  138. * SpinnerModel model = new SpinnerListModel(values);
  139. * model.setValue("TWO");
  140. * </pre>
  141. *
  142. * @param elt the sequence element that will be model's current value
  143. * @throws IllegalArgumentException if the specified value isn't allowed
  144. * @see SpinnerModel#setValue
  145. * @see #getValue
  146. */
  147. public void setValue(Object elt) {
  148. int index = list.indexOf(elt);
  149. if (index == -1) {
  150. throw new IllegalArgumentException("invalid sequence element");
  151. }
  152. else if (index != this.index) {
  153. this.index = index;
  154. fireStateChanged();
  155. }
  156. }
  157. /**
  158. * Returns the next legal value of the underlying sequence or
  159. * <code>null</code> if value is already the last element.
  160. *
  161. * @return the next legal value of the underlying sequence or
  162. * <code>null</code> if value is already the last element
  163. * @see SpinnerModel#getNextValue
  164. * @see #getPreviousValue
  165. */
  166. public Object getNextValue() {
  167. return (index >= (list.size() - 1)) ? null : list.get(index + 1);
  168. }
  169. /**
  170. * Returns the previous element of the underlying sequence or
  171. * <code>null</code> if value is already the first element.
  172. *
  173. * @return the previous element of the underlying sequence or
  174. * <code>null</code> if value is already the first element
  175. * @see SpinnerModel#getPreviousValue
  176. * @see #getNextValue
  177. */
  178. public Object getPreviousValue() {
  179. return (index <= 0) ? null : list.get(index - 1);
  180. }
  181. /**
  182. * Returns the next object that starts with <code>substring</code>.
  183. *
  184. * @param substring the string to be matched
  185. * @return the match
  186. */
  187. Object findNextMatch(String substring) {
  188. int max = list.size();
  189. if (max == 0) {
  190. return null;
  191. }
  192. int counter = index;
  193. do {
  194. Object value = list.get(counter);
  195. String string = value.toString();
  196. if (string != null && string.startsWith(substring)) {
  197. return value;
  198. }
  199. counter = (counter + 1) % max;
  200. } while (counter != index);
  201. return null;
  202. }
  203. }