1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.validator.routines;
18
19 import java.text.DateFormatSymbols;
20 import java.text.Format;
21 import java.text.DateFormat;
22 import java.text.SimpleDateFormat;
23 import java.util.Calendar;
24 import java.util.Locale;
25 import java.util.TimeZone;
26
27 /***
28 * <p>Abstract class for Date/Time/Calendar validation.</p>
29 *
30 * <p>This is a <i>base</i> class for building Date / Time
31 * Validators using format parsing.</p>
32 *
33 * @version $Revision: 478334 $ $Date: 2006-11-22 21:31:54 +0000 (Wed, 22 Nov 2006) $
34 * @since Validator 1.3.0
35 */
36 public abstract class AbstractCalendarValidator extends AbstractFormatValidator {
37
38 private int dateStyle = -1;
39
40 private int timeStyle = -1;
41
42 /***
43 * Construct an instance with the specified <i>strict</i>,
44 * <i>time</i> and <i>date</i> style parameters.
45 *
46 * @param strict <code>true</code> if strict
47 * <code>Format</code> parsing should be used.
48 * @param dateStyle the date style to use for Locale validation.
49 * @param timeStyle the time style to use for Locale validation.
50 */
51 public AbstractCalendarValidator(boolean strict, int dateStyle, int timeStyle) {
52 super(strict);
53 this.dateStyle = dateStyle;
54 this.timeStyle = timeStyle;
55 }
56
57 /***
58 * <p>Validate using the specified <code>Locale</code>.
59 *
60 * @param value The value validation is being performed on.
61 * @param pattern The pattern used to format the value.
62 * @param locale The locale to use for the Format, defaults to the default
63 * @return <code>true</code> if the value is valid.
64 */
65 public boolean isValid(String value, String pattern, Locale locale) {
66 Object parsedValue = parse(value, pattern, locale, (TimeZone)null);
67 return (parsedValue == null ? false : true);
68 }
69
70 /***
71 * <p>Format an object into a <code>String</code> using
72 * the default Locale.</p>
73 *
74 * @param value The value validation is being performed on.
75 * @param timeZone The Time Zone used to format the date,
76 * system default if null (unless value is a <code>Calendar</code>.
77 * @return The value formatted as a <code>String</code>.
78 */
79 public String format(Object value, TimeZone timeZone) {
80 return format(value, (String)null, (Locale)null, timeZone);
81 }
82
83 /***
84 * <p>Format an object into a <code>String</code> using
85 * the specified pattern.</p>
86 *
87 * @param value The value validation is being performed on.
88 * @param pattern The pattern used to format the value.
89 * @param timeZone The Time Zone used to format the date,
90 * system default if null (unless value is a <code>Calendar</code>.
91 * @return The value formatted as a <code>String</code>.
92 */
93 public String format(Object value, String pattern, TimeZone timeZone) {
94 return format(value, pattern, (Locale)null, timeZone);
95 }
96
97 /***
98 * <p>Format an object into a <code>String</code> using
99 * the specified Locale.</p>
100 *
101 * @param value The value validation is being performed on.
102 * @param locale The locale to use for the Format.
103 * @param timeZone The Time Zone used to format the date,
104 * system default if null (unless value is a <code>Calendar</code>.
105 * @return The value formatted as a <code>String</code>.
106 */
107 public String format(Object value, Locale locale, TimeZone timeZone) {
108 return format(value, (String)null, locale, timeZone);
109 }
110
111 /***
112 * <p>Format an object using the specified pattern and/or
113 * <code>Locale</code>.
114 *
115 * @param value The value validation is being performed on.
116 * @param pattern The pattern used to format the value.
117 * @param locale The locale to use for the Format.
118 * @return The value formatted as a <code>String</code>.
119 */
120 public String format(Object value, String pattern, Locale locale) {
121 return format(value, pattern, locale, (TimeZone)null);
122 }
123
124 /***
125 * <p>Format an object using the specified pattern and/or
126 * <code>Locale</code>.
127 *
128 * @param value The value validation is being performed on.
129 * @param pattern The pattern used to format the value.
130 * @param locale The locale to use for the Format.
131 * @param timeZone The Time Zone used to format the date,
132 * system default if null (unless value is a <code>Calendar</code>.
133 * @return The value formatted as a <code>String</code>.
134 */
135 public String format(Object value, String pattern, Locale locale, TimeZone timeZone) {
136 DateFormat formatter = (DateFormat)getFormat(pattern, locale);
137 if (timeZone != null) {
138 formatter.setTimeZone(timeZone);
139 } else if (value instanceof Calendar) {
140 formatter.setTimeZone(((Calendar)value).getTimeZone());
141 }
142 return format(value, formatter);
143 }
144
145 /***
146 * <p>Format a value with the specified <code>DateFormat</code>.</p>
147 *
148 * @param value The value to be formatted.
149 * @param formatter The Format to use.
150 * @return The formatted value.
151 */
152 protected String format(Object value, Format formatter) {
153 if (value == null) {
154 return null;
155 } else if (value instanceof Calendar) {
156 value = ((Calendar)value).getTime();
157 }
158 return formatter.format(value);
159 }
160
161 /***
162 * <p>Checks if the value is valid against a specified pattern.</p>
163 *
164 * @param value The value validation is being performed on.
165 * @param pattern The pattern used to validate the value against, or the
166 * default for the <code>Locale</code> if <code>null</code>.
167 * @param locale The locale to use for the date format, system default if null.
168 * @param timeZone The Time Zone used to parse the date, system default if null.
169 * @return The parsed value if valid or <code>null</code> if invalid.
170 */
171 protected Object parse(String value, String pattern, Locale locale, TimeZone timeZone) {
172
173 value = (value == null ? null : value.trim());
174 if (value == null || value.length() == 0) {
175 return null;
176 }
177 DateFormat formatter = (DateFormat)getFormat(pattern, locale);
178 if (timeZone != null) {
179 formatter.setTimeZone(timeZone);
180 }
181 return parse(value, formatter);
182
183 }
184
185 /***
186 * <p>Process the parsed value, performing any further validation
187 * and type conversion required.</p>
188 *
189 * @param value The parsed object created.
190 * @param formatter The Format used to parse the value with.
191 * @return The parsed value converted to the appropriate type
192 * if valid or <code>null</code> if invalid.
193 */
194 protected abstract Object processParsedValue(Object value, Format formatter);
195
196 /***
197 * <p>Returns a <code>DateFormat</code> for the specified <i>pattern</i>
198 * and/or <code>Locale</code>.</p>
199 *
200 * @param pattern The pattern used to validate the value against or
201 * <code>null</code> to use the default for the <code>Locale</code>.
202 * @param locale The locale to use for the currency format, system default if null.
203 * @return The <code>DateFormat</code> to created.
204 */
205 protected Format getFormat(String pattern, Locale locale) {
206 DateFormat formatter = null;
207 boolean usePattern = (pattern != null && pattern.length() > 0);
208 if (!usePattern) {
209 formatter = (DateFormat)getFormat(locale);
210 } else if (locale == null) {
211 formatter = new SimpleDateFormat(pattern);
212 } else {
213 DateFormatSymbols symbols = new DateFormatSymbols(locale);
214 formatter = new SimpleDateFormat(pattern, symbols);
215 }
216 formatter.setLenient(false);
217 return formatter;
218 }
219
220 /***
221 * <p>Returns a <code>DateFormat</code> for the specified Locale.</p>
222 *
223 * @param locale The locale a <code>DateFormat</code> is required for,
224 * system default if null.
225 * @return The <code>DateFormat</code> to created.
226 */
227 protected Format getFormat(Locale locale) {
228
229 DateFormat formatter = null;
230 if (dateStyle >= 0 && timeStyle >= 0) {
231 if (locale == null) {
232 formatter = DateFormat.getDateTimeInstance(dateStyle, timeStyle);
233 } else {
234 formatter = DateFormat.getDateTimeInstance(dateStyle, timeStyle, locale);
235 }
236 } else if (timeStyle >= 0) {
237 if (locale == null) {
238 formatter = DateFormat.getTimeInstance(timeStyle);
239 } else {
240 formatter = DateFormat.getTimeInstance(timeStyle, locale);
241 }
242 } else {
243 int useDateStyle = dateStyle >= 0 ? dateStyle : DateFormat.SHORT;
244 if (locale == null) {
245 formatter = DateFormat.getDateInstance(useDateStyle);
246 } else {
247 formatter = DateFormat.getDateInstance(useDateStyle, locale);
248 }
249 }
250 formatter.setLenient(false);
251 return formatter;
252
253 }
254
255 /***
256 * <p>Compares a calendar value to another, indicating whether it is
257 * equal, less then or more than at a specified level.</p>
258 *
259 * @param value The Calendar value.
260 * @param compare The <code>Calendar</code> to check the value against.
261 * @param field The field <i>level</i> to compare to - e.g. specifying
262 * <code>Calendar.MONTH</code> will compare the year and month
263 * portions of the calendar.
264 * @return Zero if the first value is equal to the second, -1
265 * if it is less than the second or +1 if it is greater than the second.
266 */
267 protected int compare(Calendar value, Calendar compare, int field) {
268
269 int result = 0;
270
271
272 result = calculateCompareResult(value, compare, Calendar.YEAR);
273 if (result != 0 || field == Calendar.YEAR) {
274 return result;
275 }
276
277
278 if (field == Calendar.WEEK_OF_YEAR) {
279 return calculateCompareResult(value, compare, Calendar.WEEK_OF_YEAR);
280 }
281
282
283 if (field == Calendar.DAY_OF_YEAR) {
284 return calculateCompareResult(value, compare, Calendar.DAY_OF_YEAR);
285 }
286
287
288 result = calculateCompareResult(value, compare, Calendar.MONTH);
289 if (result != 0 || field == Calendar.MONTH) {
290 return result;
291 }
292
293
294 if (field == Calendar.WEEK_OF_MONTH) {
295 return calculateCompareResult(value, compare, Calendar.WEEK_OF_MONTH);
296 }
297
298
299 result = calculateCompareResult(value, compare, Calendar.DATE);
300 if (result != 0 || (field == Calendar.DATE ||
301 field == Calendar.DAY_OF_MONTH ||
302 field == Calendar.DAY_OF_WEEK ||
303 field == Calendar.DAY_OF_WEEK_IN_MONTH)) {
304 return result;
305 }
306
307
308 return compareTime(value, compare, field);
309
310 }
311
312 /***
313 * <p>Compares a calendar time value to another, indicating whether it is
314 * equal, less then or more than at a specified level.</p>
315 *
316 * @param value The Calendar value.
317 * @param compare The <code>Calendar</code> to check the value against.
318 * @param field The field <i>level</i> to compare to - e.g. specifying
319 * <code>Calendar.MINUTE</code> will compare the hours and minutes
320 * portions of the calendar.
321 * @return Zero if the first value is equal to the second, -1
322 * if it is less than the second or +1 if it is greater than the second.
323 */
324 protected int compareTime(Calendar value, Calendar compare, int field) {
325
326 int result = 0;
327
328
329 result = calculateCompareResult(value, compare, Calendar.HOUR_OF_DAY);
330 if (result != 0 || (field == Calendar.HOUR || field == Calendar.HOUR_OF_DAY)) {
331 return result;
332 }
333
334
335 result = calculateCompareResult(value, compare, Calendar.MINUTE);
336 if (result != 0 || field == Calendar.MINUTE) {
337 return result;
338 }
339
340
341 result = calculateCompareResult(value, compare, Calendar.SECOND);
342 if (result != 0 || field == Calendar.SECOND) {
343 return result;
344 }
345
346
347 if (field == Calendar.MILLISECOND) {
348 return calculateCompareResult(value, compare, Calendar.MILLISECOND);
349 }
350
351 throw new IllegalArgumentException("Invalid field: " + field);
352
353 }
354
355 /***
356 * <p>Compares a calendar's quarter value to another, indicating whether it is
357 * equal, less then or more than the specified quarter.</p>
358 *
359 * @param value The Calendar value.
360 * @param compare The <code>Calendar</code> to check the value against.
361 * @param monthOfFirstQuarter The month that the first quarter starts.
362 * @return Zero if the first quarter is equal to the second, -1
363 * if it is less than the second or +1 if it is greater than the second.
364 */
365 protected int compareQuarters(Calendar value, Calendar compare, int monthOfFirstQuarter) {
366 int valueQuarter = calculateQuarter(value, monthOfFirstQuarter);
367 int compareQuarter = calculateQuarter(compare, monthOfFirstQuarter);
368 if (valueQuarter < compareQuarter) {
369 return -1;
370 } else if (valueQuarter > compareQuarter) {
371 return 1;
372 } else {
373 return 0;
374 }
375 }
376
377 /***
378 * <p>Calculate the quarter for the specified Calendar.</p>
379 *
380 * @param calendar The Calendar value.
381 * @param monthOfFirstQuarter The month that the first quarter starts.
382 * @return The calculated quarter.
383 */
384 private int calculateQuarter(Calendar calendar, int monthOfFirstQuarter) {
385
386 int year = calendar.get(Calendar.YEAR);
387
388 int month = (calendar.get(Calendar.MONTH) + 1);
389 int relativeMonth = (month >= monthOfFirstQuarter)
390 ? (month - monthOfFirstQuarter)
391 : (month + (12 - monthOfFirstQuarter));
392 int quarter = ((relativeMonth / 3) + 1);
393
394 if (month < monthOfFirstQuarter) {
395 --year;
396 }
397 return (year * 10) + quarter;
398 }
399
400 /***
401 * <p>Compares the field from two calendars indicating whether the field for the
402 * first calendar is equal to, less than or greater than the field from the
403 * second calendar.
404 *
405 * @param value The Calendar value.
406 * @param compare The <code>Calendar</code> to check the value against.
407 * @param field The field to compare for the calendars.
408 * @return Zero if the first calendar's field is equal to the seconds, -1
409 * if it is less than the seconds or +1 if it is greater than the seconds.
410 */
411 private int calculateCompareResult(Calendar value, Calendar compare, int field) {
412 int difference = value.get(field) - compare.get(field);
413 if (difference < 0) {
414 return -1;
415 } else if (difference > 0) {
416 return 1;
417 } else {
418 return 0;
419 }
420 }
421 }