-
Notifications
You must be signed in to change notification settings - Fork 3
/
SpinnerTemporalModel.java
237 lines (216 loc) · 6.48 KB
/
SpinnerTemporalModel.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
/**
* @(#)SpinnerTemporalModel.java 1.0 2014/12/05
*/
package darrylbu.model;
import darrylbu.editor.SpinnerTemporalEditor;
import java.time.temporal.ChronoUnit;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalAdjuster;
import java.time.temporal.UnsupportedTemporalTypeException;
import javax.swing.AbstractSpinnerModel;
/**
* SpinnerTemporalModel in conjunction with {@link SpinnerTemporalEditor} allows using a JSpinner
* with temporal classes of the java.time package. Generics and additional methods provide
* compile-time type safety for accessing and mutating the temporal value.
* <P>
* <B>Note that compiling or using this class requires Java 8</B>
*
* @author Darryl
* @see Temporal
* @param <T> The type of value, usually one of LocalDate, LocalDateTime, LocalTime, MonthDay, Year
* and YearMonth.
*/
public class SpinnerTemporalModel<T extends Temporal & TemporalAdjuster & Comparable>
extends AbstractSpinnerModel {
private T value;
private T min;
private T max;
private ChronoUnit step;
/**
* Constructs a SpinnerTemporalModel with the specified value, minimum (earliest) / maximum
* (latest) bounds, and step.
*
* @param value the current value of the model
* @param min the earliest element in the sequence, or null for no limit.
* @param max the latest element in the sequence, or null for no limit.
* @param step the temporal period between elements of the sequence
*
* @throws IllegalArgumentException if value is null or either or both of min/max are non-null and
* (minimum <= value <= maximum) is false. @throws UnsupportedTemporalTypeException if the unit of
* the step is not supported by the generic type T.
*/
public SpinnerTemporalModel(T value, T min, T max, ChronoUnit step) {
if (value == null) {
throw new IllegalArgumentException("value is null");
}
if (!step.isSupportedBy(value)) {
throw new UnsupportedTemporalTypeException("Unit " + step.name()
+ " is not supported for type " + value.getClass().getSimpleName());
}
// min <= value <= max
if ((min != null && min.compareTo(value) > 0)
|| max != null && max.compareTo(value) < 0) {
throw new IllegalArgumentException("(start <= value <= end) is false");
}
this.value = value;
this.min = min;
this.max = max;
this.step = step;
}
/**
* This method provides compile-time type safety. Client code should call this method rather than
* getValue(), which is retained for compatibility with core Swing code.
*
* @return
*/
public T getTemporalValue() {
return value;
}
/**
* This method provides compile-time type safety. Client code should call this method rather than
* setValue(Object), which is retained for compatibility with core Swing code.
*
* @param value
*/
public void setTemporalValue(T value) {
setValue(value);
}
/**
* {@inheritDoc }
*
* @deprecated Client code should use getTemporalValue which provides compile-time type safety.
*/
@Override
@Deprecated
public Object getValue() {
return value;
}
/**
* {@inheritDoc }
*
* @param value The value to set
* @throws IllegalArgumentException if value is null or cannot be cast to the requisite type.
*
* @deprecated Client code should use setTemporalValue which provides compile-time type safety.
*/
@Override
@Deprecated
public void setValue(Object value) {
if (value == null) {
throw new IllegalArgumentException("value is null");
}
T t;
try {
t = (T) value;
} catch (ClassCastException cce) {
throw new IllegalArgumentException("illegal value", cce);
}
if (!t.equals(this.value)) {
this.value = t;
fireStateChanged();
}
}
/**
* {@inheritDoc }
*/
@Override
public Object getNextValue() {
if (max != null && max.equals(value)) {
return null;
}
T newValue = step.addTo(value, 1);
return max == null || max.compareTo(newValue) > 0 ? newValue : max;
}
/**
* {@inheritDoc }
*/
@Override
public Object getPreviousValue() {
if (min != null && min.equals(value)) {
return null;
}
T newValue = step.addTo(value, -1);
return min == null || min.compareTo(newValue) < 0 ? newValue : min;
}
/**
* Returns the minimum (earliest) value.
*
* @return The minimum value
*/
public T getMin() {
return min;
}
/**
* Sets the minimum (earliest) value.
*
* @param min the earliest value
*/
public void setTemporalMin(T min) {
setMin(min);
}
/**
* Sets the minimum (earliest) value.
*
* @param min the minimum value
*
* @deprecated Client code should use setTemporalMin which provides compile-time type safety.
*/
@Deprecated
public void setMin(Comparable min) {
if ((min == null) ? (this.min != null) : !min.equals(this.min)) {
this.min = (T) min;
fireStateChanged();
}
}
/**
* Returns the maximum (latest) value.
*
* @return The maximum value
*/
public T getMax() {
return max;
}
/**
* Sets the maximum (latest) value.
*
* @param max the latest value
*/
public void setTemporalMax(T max) {
setMax(max);
}
/**
* Sets the maximum (latest) value.
*
* @param max
*
* @deprecated Client code should use setTemporalMax which provides compile-time type safety.
*/
@Deprecated
public void setMax(Comparable max) {
if ((max == null) ? (this.max != null) : !max.equals(this.max)) {
this.max = (T) max;
fireStateChanged();
}
}
/**
* Returns the size of the value change computed by the getNextValue and getPreviousValue methods.
*
* @return The value of the step property.
*/
public ChronoUnit getStep() {
return step;
}
/**
* Changes the size of the value change computed by the getNextValue and getPreviousValue methods.
*
* @param step the step to set
* @throws UnsupportedTemporalTypeException if the step is not supported by the temporal type e.g.
* attempting to set ChronoUnit.DAYS when the type T is YearMonth.
*/
public void setStep(ChronoUnit step) {
if (!step.isSupportedBy(value)) {
throw new UnsupportedTemporalTypeException("Unsupported unit: " + step.name());
}
this.step = step;
}
}