1. /*
  2. * @(#)SpinnerNumberModel.java 1.12 04/05/12
  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 <code>SpinnerModel</code> for sequences of numbers.
  12. * The upper and lower bounds of the sequence are defined
  13. * by properties called <code>minimum</code> and
  14. * <code>maximum</code>. The size of the increase or decrease
  15. * computed by the <code>nextValue</code> and
  16. * <code>previousValue</code> methods is defined by a property called
  17. * <code>stepSize</code>. The <code>minimum</code> and
  18. * <code>maximum</code> properties can be <code>null</code>
  19. * to indicate that the sequence has no lower or upper limit.
  20. * All of the properties in this class are defined in terms of two
  21. * generic types: <code>Number</code> and
  22. * <code>Comparable</code>, so that all Java numeric types
  23. * may be accommodated. Internally, there's only support for
  24. * values whose type is one of the primitive <code>Number</code> types:
  25. * <code>Double</code>, <code>Float</code>, <code>Long</code>,
  26. * <code>Integer</code>, <code>Short</code>, or <code>Byte</code>.
  27. * <p>
  28. * To create a <code>SpinnerNumberModel</code> for the integer
  29. * range zero to one hundred, with
  30. * fifty as the initial value, one could write:
  31. * <pre>
  32. * Integer value = new Integer(50);
  33. * Integer min = new Integer(0);
  34. * Integer max = new Integer(100);
  35. * Integer step = new Integer(1);
  36. * SpinnerNumberModel model = new SpinnerNumberModel(value, min, max, step);
  37. * int fifty = model.getNumber().intValue();
  38. * </pre>
  39. * <p>
  40. * Spinners for integers and doubles are common, so special constructors
  41. * for these cases are provided. For example to create the model in
  42. * the previous example, one could also write:
  43. * <pre>
  44. * SpinnerNumberModel model = new SpinnerNumberModel(50, 0, 100, 1);
  45. * </pre>
  46. * <p>
  47. * This model inherits a <code>ChangeListener</code>.
  48. * The <code>ChangeListeners</code> are notified
  49. * whenever the model's <code>value</code>, <code>stepSize</code>,
  50. * <code>minimum</code>, or <code>maximum</code> properties changes.
  51. *
  52. * @see JSpinner
  53. * @see SpinnerModel
  54. * @see AbstractSpinnerModel
  55. * @see SpinnerListModel
  56. * @see SpinnerDateModel
  57. *
  58. * @version 1.12 05/12/04
  59. * @author Hans Muller
  60. * @since 1.4
  61. */
  62. public class SpinnerNumberModel extends AbstractSpinnerModel implements Serializable
  63. {
  64. private Number stepSize, value;
  65. private Comparable minimum, maximum;
  66. /**
  67. * Constructs a <code>SpinnerModel</code> that represents
  68. * a closed sequence of
  69. * numbers from <code>minimum</code> to <code>maximum</code>. The
  70. * <code>nextValue</code> and <code>previousValue</code> methods
  71. * compute elements of the sequence by adding or subtracting
  72. * <code>stepSize</code> respectively. All of the parameters
  73. * must be mutually <code>Comparable</code>, <code>value</code>
  74. * and <code>stepSize</code> must be instances of <code>Integer</code>
  75. * <code>Long</code>, <code>Float</code>, or <code>Double</code>.
  76. * <p>
  77. * The <code>minimum</code> and <code>maximum</code> parameters
  78. * can be <code>null</code> to indicate that the range doesn't
  79. * have an upper or lower bound.
  80. * If <code>value</code> or <code>stepSize</code> is <code>null</code>,
  81. * or if both <code>minimum</code> and <code>maximum</code>
  82. * are specified and <code>mininum > maximum</code> then an
  83. * <code>IllegalArgumentException</code> is thrown.
  84. * Similarly if <code>(minimum <= value <= maximum</code>) is false,
  85. * an <code>IllegalArgumentException</code> is thrown.
  86. *
  87. * @param value the current (non <code>null</code>) value of the model
  88. * @param minimum the first number in the sequence or <code>null</code>
  89. * @param maximum the last number in the sequence or <code>null</code>
  90. * @param stepSize the difference between elements of the sequence
  91. *
  92. * @throws IllegalArgumentException if stepSize or value is
  93. * <code>null</code> or if the following expression is false:
  94. * <code>minimum <= value <= maximum</code>
  95. */
  96. public SpinnerNumberModel(Number value, Comparable minimum, Comparable maximum, Number stepSize) {
  97. if ((value == null) || (stepSize == null)) {
  98. throw new IllegalArgumentException("value and stepSize must be non-null");
  99. }
  100. if (!(((minimum == null) || (minimum.compareTo(value) <= 0)) &&
  101. ((maximum == null) || (maximum.compareTo(value) >= 0)))) {
  102. throw new IllegalArgumentException("(minimum <= value <= maximum) is false");
  103. }
  104. this.value = value;
  105. this.minimum = minimum;
  106. this.maximum = maximum;
  107. this.stepSize = stepSize;
  108. }
  109. /**
  110. * Constructs a <code>SpinnerNumberModel</code> with the specified
  111. * <code>value</code>, <code>minimum</code>/<code>maximum</code> bounds,
  112. * and <code>stepSize</code>.
  113. *
  114. * @param value the current value of the model
  115. * @param minimum the first number in the sequence
  116. * @param maximum the last number in the sequence
  117. * @param stepSize the difference between elements of the sequence
  118. * @throws IllegalArgumentException if the following expression is false:
  119. * <code>minimum <= value <= maximum</code>
  120. */
  121. public SpinnerNumberModel(int value, int minimum, int maximum, int stepSize) {
  122. this(new Integer(value), new Integer(minimum), new Integer(maximum), new Integer(stepSize));
  123. }
  124. /**
  125. * Constructs a <code>SpinnerNumberModel</code> with the specified
  126. * <code>value</code>, <code>minimum</code>/<code>maximum</code> bounds,
  127. * and <code>stepSize</code>.
  128. *
  129. * @param value the current value of the model
  130. * @param minimum the first number in the sequence
  131. * @param maximum the last number in the sequence
  132. * @param stepSize the difference between elements of the sequence
  133. * @throws IllegalArgumentException if the following expression is false:
  134. * <code>minimum <= value <= maximum</code>
  135. */
  136. public SpinnerNumberModel(double value, double minimum, double maximum, double stepSize) {
  137. this(new Double(value), new Double(minimum), new Double(maximum), new Double(stepSize));
  138. }
  139. /**
  140. * Constructs a <code>SpinnerNumberModel</code> with no
  141. * <code>minimum</code> or <code>maximum</code> value,
  142. * <code>stepSize</code> equal to one, and an initial value of zero.
  143. */
  144. public SpinnerNumberModel() {
  145. this(new Integer(0), null, null, new Integer(1));
  146. }
  147. /**
  148. * Changes the lower bound for numbers in this sequence.
  149. * If <code>minimum</code> is <code>null</code>,
  150. * then there is no lower bound. No bounds checking is done here;
  151. * the new <code>minimum</code> value may invalidate the
  152. * <code>(minimum <= value <= maximum)</code>
  153. * invariant enforced by the constructors. This is to simplify updating
  154. * the model, naturally one should ensure that the invariant is true
  155. * before calling the <code>getNextValue</code>,
  156. * <code>getPreviousValue</code>, or <code>setValue</code> methods.
  157. * <p>
  158. * Typically this property is a <code>Number</code> of the same type
  159. * as the <code>value</code> however it's possible to use any
  160. * <code>Comparable</code> with a <code>compareTo</code>
  161. * method for a <code>Number</code> with the same type as the value.
  162. * For example if value was a <code>Long</code>,
  163. * <code>minimum</code> might be a Date subclass defined like this:
  164. * <pre>
  165. * MyDate extends Date { // Date already implements Comparable
  166. * public int compareTo(Long o) {
  167. * long t = getTime();
  168. * return (t < o.longValue() ? -1 : (t == o.longValue() ? 0 : 1));
  169. * }
  170. * }
  171. * </pre>
  172. * <p>
  173. * This method fires a <code>ChangeEvent</code>
  174. * if the <code>minimum</code> has changed.
  175. *
  176. * @param minimum a <code>Comparable</code> that has a
  177. * <code>compareTo</code> method for <code>Number</code>s with
  178. * the same type as <code>value</code>
  179. * @see #getMinimum
  180. * @see #setMaximum
  181. * @see SpinnerModel#addChangeListener
  182. */
  183. public void setMinimum(Comparable minimum) {
  184. if ((minimum == null) ? (this.minimum != null) : !minimum.equals(this.minimum)) {
  185. this.minimum = minimum;
  186. fireStateChanged();
  187. }
  188. }
  189. /**
  190. * Returns the first number in this sequence.
  191. *
  192. * @return the value of the <code>minimum</code> property
  193. * @see #setMinimum
  194. */
  195. public Comparable getMinimum() {
  196. return minimum;
  197. }
  198. /**
  199. * Changes the upper bound for numbers in this sequence.
  200. * If <code>maximum</code> is <code>null</code>, then there
  201. * is no upper bound. No bounds checking is done here; the new
  202. * <code>maximum</code> value may invalidate the
  203. * <code>(minimum <= value < maximum)</code>
  204. * invariant enforced by the constructors. This is to simplify updating
  205. * the model, naturally one should ensure that the invariant is true
  206. * before calling the <code>next</code>, <code>previous</code>,
  207. * or <code>setValue</code> methods.
  208. * <p>
  209. * Typically this property is a <code>Number</code> of the same type
  210. * as the <code>value</code> however it's possible to use any
  211. * <code>Comparable</code> with a <code>compareTo</code>
  212. * method for a <code>Number</code> with the same type as the value.
  213. * See <a href="#setMinimum(java.lang.Comparable)">
  214. * <code>setMinimum</code></a> for an example.
  215. * <p>
  216. * This method fires a <code>ChangeEvent</code> if the
  217. * <code>maximum</code> has changed.
  218. *
  219. * @param maximum a <code>Comparable</code> that has a
  220. * <code>compareTo</code> method for <code>Number</code>s with
  221. * the same type as <code>value</code>
  222. * @see #getMaximum
  223. * @see #setMinimum
  224. * @see SpinnerModel#addChangeListener
  225. */
  226. public void setMaximum(Comparable maximum) {
  227. if ((maximum == null) ? (this.maximum != null) : !maximum.equals(this.maximum)) {
  228. this.maximum = maximum;
  229. fireStateChanged();
  230. }
  231. }
  232. /**
  233. * Returns the last number in the sequence.
  234. *
  235. * @return the value of the <code>maximum</code> property
  236. * @see #setMaximum
  237. */
  238. public Comparable getMaximum() {
  239. return maximum;
  240. }
  241. /**
  242. * Changes the size of the value change computed by the
  243. * <code>getNextValue</code> and <code>getPreviousValue</code>
  244. * methods. An <code>IllegalArgumentException</code>
  245. * is thrown if <code>stepSize</code> is <code>null</code>.
  246. * <p>
  247. * This method fires a <code>ChangeEvent</code> if the
  248. * <code>stepSize</code> has changed.
  249. *
  250. * @param stepSize the size of the value change computed by the
  251. * <code>getNextValue</code> and <code>getPreviousValue</code> methods
  252. * @see #getNextValue
  253. * @see #getPreviousValue
  254. * @see #getStepSize
  255. * @see SpinnerModel#addChangeListener
  256. */
  257. public void setStepSize(Number stepSize) {
  258. if (stepSize == null) {
  259. throw new IllegalArgumentException("null stepSize");
  260. }
  261. if (!stepSize.equals(this.stepSize)) {
  262. this.stepSize = stepSize;
  263. fireStateChanged();
  264. }
  265. }
  266. /**
  267. * Returns the size of the value change computed by the
  268. * <code>getNextValue</code>
  269. * and <code>getPreviousValue</code> methods.
  270. *
  271. * @return the value of the <code>stepSize</code> property
  272. * @see #setStepSize
  273. */
  274. public Number getStepSize() {
  275. return stepSize;
  276. }
  277. private Number incrValue(int dir)
  278. {
  279. Number newValue;
  280. if ((value instanceof Float) || (value instanceof Double)) {
  281. double v = value.doubleValue() + (stepSize.doubleValue() * (double)dir);
  282. if (value instanceof Double) {
  283. newValue = new Double(v);
  284. }
  285. else {
  286. newValue = new Float(v);
  287. }
  288. }
  289. else {
  290. long v = value.longValue() + (stepSize.longValue() * (long)dir);
  291. if (value instanceof Long) {
  292. newValue = new Long(v);
  293. }
  294. else if (value instanceof Integer) {
  295. newValue = new Integer((int)v);
  296. }
  297. else if (value instanceof Short) {
  298. newValue = new Short((short)v);
  299. }
  300. else {
  301. newValue = new Byte((byte)v);
  302. }
  303. }
  304. if ((maximum != null) && (maximum.compareTo(newValue) < 0)) {
  305. return null;
  306. }
  307. if ((minimum != null) && (minimum.compareTo(newValue) > 0)) {
  308. return null;
  309. }
  310. else {
  311. return newValue;
  312. }
  313. }
  314. /**
  315. * Returns the next number in the sequence.
  316. *
  317. * @return <code>value + stepSize</code> or <code>null</code> if the sum
  318. * exceeds <code>maximum</code>.
  319. *
  320. * @see SpinnerModel#getNextValue
  321. * @see #getPreviousValue
  322. * @see #setStepSize
  323. */
  324. public Object getNextValue() {
  325. return incrValue(+1);
  326. }
  327. /**
  328. * Returns the previous number in the sequence.
  329. *
  330. * @return <code>value - stepSize</code>, or
  331. * <code>null</code> if the sum is less
  332. * than <code>minimum</code>.
  333. *
  334. * @see SpinnerModel#getPreviousValue
  335. * @see #getNextValue
  336. * @see #setStepSize
  337. */
  338. public Object getPreviousValue() {
  339. return incrValue(-1);
  340. }
  341. /**
  342. * Returns the value of the current element of the sequence.
  343. *
  344. * @return the value property
  345. * @see #setValue
  346. */
  347. public Number getNumber() {
  348. return value;
  349. }
  350. /**
  351. * Returns the value of the current element of the sequence.
  352. *
  353. * @return the value property
  354. * @see #setValue
  355. * @see #getNumber
  356. */
  357. public Object getValue() {
  358. return value;
  359. }
  360. /**
  361. * Sets the current value for this sequence. If <code>value</code> is
  362. * <code>null</code>, or not a <code>Number</code>, an
  363. * <code>IllegalArgumentException</code> is thrown. No
  364. * bounds checking is done here; the new value may invalidate the
  365. * <code>(minimum <= value <= maximum)</code>
  366. * invariant enforced by the constructors. It's also possible to set
  367. * the value to be something that wouldn't naturally occur in the sequence,
  368. * i.e. a value that's not modulo the <code>stepSize</code>.
  369. * This is to simplify updating the model, and to accommodate
  370. * spinners that don't want to restrict values that have been
  371. * directly entered by the user. Naturally, one should ensure that the
  372. * <code>(minimum <= value <= maximum)</code> invariant is true
  373. * before calling the <code>next</code>, <code>previous</code>, or
  374. * <code>setValue</code> methods.
  375. * <p>
  376. * This method fires a <code>ChangeEvent</code> if the value has changed.
  377. *
  378. * @param value the current (non <code>null</code>) <code>Number</code>
  379. * for this sequence
  380. * @throws IllegalArgumentException if <code>value</code> is
  381. * <code>null</code> or not a <code>Number</code>
  382. * @see #getNumber
  383. * @see #getValue
  384. * @see SpinnerModel#addChangeListener
  385. */
  386. public void setValue(Object value) {
  387. if ((value == null) || !(value instanceof Number)) {
  388. throw new IllegalArgumentException("illegal value");
  389. }
  390. if (!value.equals(this.value)) {
  391. this.value = (Number)value;
  392. fireStateChanged();
  393. }
  394. }
  395. }