# `JSpinner` - A Simple Sequence Container

This document describes the new Swing component, JSpinner.

One of the most commonly requested new Swing components is a spinner - a single line input field that lets the user select an number or an object value from an ordered set. Spinners typically provide a pair of tiny arrow buttons for stepping through the possible values, the up/down arrow keys also cycle through the values. The user may also be allowed to type a (legal) value directly into the spinner. Although combo boxes provide similar functionality, spinners are sometimes preferred because they don't require a drop down list that can obscure important data.

The bugtraq report that corresponds to this change is: 4290529.

## A Short Catalog of Spinners

Spinners are common in modern GUIs. Here are some examples from some popular look and feels and from OpenWindows:

Windows | CDE/Motif | JLF | Mac Aqua | OpenWindows

All of the spinners deliver very simple behavior: clicking on the arrow buttons changes the fields value and, when the spinner has the focus, the keyboard up and down arrow keys do the same. In some applications the spinners up or down arrow button is disabled when the upper or lower limit of the spinner has been reached, in other cases the spinner just resets its value to the opposite extreme.

## `JSpinner`, `SpinnerModel`, `SpinnerListModel`

The `JSpinner` class is a simple Swing container that manages three children: two arrow buttons and a single component, called the editor that displays the spinner's value. The value displayed by the spinner is encapsulated by model of a sequence of objects called a `SpinnerModel`.

    public interface SpinnerModel {
        Object getValue();
        void setValue(Object);
        Object getNextValue();
        Object getPreviousValue();
        void addChangeListener(ChangeListener x);
        void removeChangeListener(ChangeListener x);}

The `SpinnerModel` interface is similar to `ListModel` - they both represent a sequence a values - however there are some important differences:

- The model doesn't support random access to sequence elements. Only three sequence values are accessible at a time: current, next and previous.
- The number of sequence elements isn't necessarily bounded so there's no sequence length returning `size` method.
- The model's value property is read/write, unlike the elements of a `ListModels` sequence, which are read-only. This difference reflects the different roles of `JList` and `JSpinner`. The former was designed to simplify selecting one or more items from a list and the latter for entering a single value directly.

The relationship between the `JSpinner` and its model is simple. The editor component monitors the model with a `ChangeListener` and always displays the object returned by `SpinnerModel.getValue()`. The up and down arrow buttons update the value by calling `setValue(getNextValue())` or `setValue(getPreviousValue())` respectively. The `getNextValue` and `getPreviousValue` methods return `null` when the end or the beginning of the sequence has been reached, so the arrow button actions have to check for `null` before updating the models value. If the editor is a writable field of some kind, it's responsible for honoring the constraints defined by the model or handling the `IllegalArgumentException` thrown by `setValue` for invalid values.

The `SpinnerListModel` provides support for two common mutable sequence types: `java.util.List`, and an array of objects. For example to create a `JSpinner` that lets the user choose a day of the week in the default locale one could write:

        String[] days = new DateFormatSymbols().getWeekdays();
        SpinnerModel model = new SpinnerListModel(days);
        JSpinner spinner = new JSpinner(model);

In addition to initializing the `model` property of the spinner, these constructors create an editor component that displays the `SpinnerModel`'s `value` and can be used to change it. The protected `JSpinner.createEditor` method is used for this and, by default, it creates a `JFormattedTextField` that has been configured to display the model.

To find or initialize a spinners current value one can either use the models `value` property or use the convenience `JSpinner` `value` property which just delegates to the model. For example, using the spinner configured in the example above, the following two statements are equivalent:

        String selectedDay = spinner.getModel().getValue().toString();
        String selectedDay = spinner.getValue().toString();

Setting the spinner's value is similar. Attempts to set the `SpinnerModel` value to an object that the model doesn't support cause an `IllegalArgumentException` to be thrown.

Dates and numbers are two of the most common applications for a spinner component. To simplify spinning these types, two additional `SpinnerModel` implementation classes are provided: `SpinnerDateModel` and `SpinnerNumberModel`.

## `SpinnerDateModel`

One of the most common uses of a spinner is to compactly present an editable date. Here's a simple example of creating a `JSpinner` that allows the user to enter a (fully localized) date:

        SpinnerDateModel model = new SpinnerDateModel();
        JSpinner spinner = new JSpinner(model);
        Date value = model.getDate();

In this example, the `JSpinner` constructor has created a `JFormattedTextField` editor that's configured for editing dates and it has added a `ChangeListener` to the `SpinnerDateModel` to keep the `editor` and the model in sync.

Here's the `SpinnerDateModel` API. We've added three new read/write properties: `start`, `end`, and `stepSize` and a read-only `date` property that returns the models value cast to a `Date`.

    public class SpinnerDateModel extends AbstractSpinnerModel {
        public SpinnerDateModel(Date value, Comparable start, Comparable end, int stepSize)
        public SpinnerDateModel()
        public void setStart(Comparable start)
        public Comparable getStart()
        public void setEnd(Comparable end)
        public Comparable getEnd()
        public Object getNextValue()
        public Object getPreviousValue()
        public Date getDate()
        public Object getValue()
        public void setValue(Object value)}

The `startDate` and `endDate` properties can be `null` to indicate that there is no lower or upper limit. The no-arguments `SpinnerDateModel` constructor initializes both the start and end date to `null`, the initial value of the model is the current date.

The value of the `stepSize` property must be one of the `java.util.Calendar` constants that specify a field within a `Calendar`. The `getNextValue` and `getPreviousValue` methods change the date forward or backwards by this amount. For example, if `stepSize` is `Calendar.DAY_OF_WEEK`, then `nextValue` produces a `Date` that's 24 hours after the current `value`, and `previousValue` produces a `Date` that's 24 hours earlier.

The legal values for `stepSize` are:

- `Calendar.ERA`
- `Calendar.YEAR`
- `Calendar.MONTH`
- `Calendar.WEEK_OF_YEAR`
- `Calendar.WEEK_OF_MONTH`
- `Calendar.DAY_OF_MONTH`
- `Calendar.DAY_OF_YEAR`
- `Calendar.DAY_OF_WEEK`
- `Calendar.DAY_OF_WEEK_IN_MONTH`
- `Calendar.AM_PM`
- `Calendar.HOUR`
- `Calendar.HOUR_OF_DAY`
- `Calendar.MINUTE`
- `Calendar.SECOND`
- `Calendar.MILLISECOND`

The default `SpinnerDateModel` `editor` adjusts the `stepSize` property based on the text cursor position. For example if the cursor moves into the editor's month subfield then the `incrementSize` would be changed to `Calendar.DAY_OF_MONTH`.

### SpinnerNumberModel

Spinners are often used to present editable integers and real numbers that represent everything from temperature to stock prices. The `SpinnerNumberModel` provides basic support for all of the basic Java `Number` types, from `Byte` to `Double`.

To create a spinner that allows the user to pick a real multiple of 1/8 between 0.0 and 1000.0, with an initial value of 500.0, one could write:

        SpinnerNumberModel model = new SpinnerNumberModel(500.0, 0.0, 1000.0, 0.625);
        JSpinner spinner = new JSpinner(model);
        double value = model.getNumber().doubleValue();

In this example, the `JSpinner` constructor has created a `JFormattedTextField` editor that's configured for editing real numbers and it has added a `ChangeListener` to the `SpinnerNumberModel` to keep the `editor` and the model in sync.

Here's a summary of the `SpinnerNumberModel` API. We've added three new read/write properties: `minimum`, `maximum`, and `stepSize` and a read-only `number` property that returns the models `value` cast to a `Number`.

    public class SpinnerNumberModel extends AbstractSpinnerModel {
        public SpinnerNumberModel(Number value, Comparable minimum, Comparable maximum, Number stepSize)
        public SpinnerNumberModel(int value, int minimum, int maximum, int stepSize)
        public SpinnerNumberModel(double value, double minimum, double maximum, double stepSize)
        public SpinnerNumberModel()
        public void setMinimum(Comparable minimum)
        public Comparable getMinimum()
        public void setMaximum(Comparable maximum)
        public Comparable getMaximum()
        public void setStepSize(Number stepSize)
        public Number getStepSize()
        public Object getNextValue()
        public Object getPreviousValue()
        public Number getNumber()
        public Object getValue()
        public void setValue(Object value)}

As with the `SpinnerDateModel`, the `minimum` and `maximum` properties can be `null` to indicate that there is no lower or upper limit. The `stepSize` property just specifies how much to add or subtract from the `value` to compute the `nextValue` or the `previousValue`.

## Summary of the Spinner APIs

Support for spinners added six classes and one interface (`SpinnerModel`) to the `javax.swing` package:

- `JSpinner`
- `SpinnerListModel`
- `AbstractSpinnerModel`
- `SpinnerDateModel`
- `SpinnerModel`
- `SpinnerNumberModel`

Additionally, `SpinnerUI` has been added to the `javax.swing.plaf` package and `BasicSpinnerUI` to the `javax.swing.plaf.basic` package:

- `SpinnerUI`
- `BasicSpinnerUI`