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