001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.commons.validator.routines;
018:
019: import java.text.DateFormatSymbols;
020: import java.text.Format;
021: import java.text.DateFormat;
022: import java.text.SimpleDateFormat;
023: import java.util.Calendar;
024: import java.util.Locale;
025: import java.util.TimeZone;
026:
027: /**
028: * <p>Abstract class for Date/Time/Calendar validation.</p>
029: *
030: * <p>This is a <i>base</i> class for building Date / Time
031: * Validators using format parsing.</p>
032: *
033: * @version $Revision: 478334 $ $Date: 2006-11-22 21:31:54 +0000 (Wed, 22 Nov 2006) $
034: * @since Validator 1.3.0
035: */
036: public abstract class AbstractCalendarValidator extends
037: AbstractFormatValidator {
038:
039: private int dateStyle = -1;
040:
041: private int timeStyle = -1;
042:
043: /**
044: * Construct an instance with the specified <i>strict</i>,
045: * <i>time</i> and <i>date</i> style parameters.
046: *
047: * @param strict <code>true</code> if strict
048: * <code>Format</code> parsing should be used.
049: * @param dateStyle the date style to use for Locale validation.
050: * @param timeStyle the time style to use for Locale validation.
051: */
052: public AbstractCalendarValidator(boolean strict, int dateStyle,
053: int timeStyle) {
054: super (strict);
055: this .dateStyle = dateStyle;
056: this .timeStyle = timeStyle;
057: }
058:
059: /**
060: * <p>Validate using the specified <code>Locale</code>.
061: *
062: * @param value The value validation is being performed on.
063: * @param pattern The pattern used to format the value.
064: * @param locale The locale to use for the Format, defaults to the default
065: * @return <code>true</code> if the value is valid.
066: */
067: public boolean isValid(String value, String pattern, Locale locale) {
068: Object parsedValue = parse(value, pattern, locale,
069: (TimeZone) null);
070: return (parsedValue == null ? false : true);
071: }
072:
073: /**
074: * <p>Format an object into a <code>String</code> using
075: * the default Locale.</p>
076: *
077: * @param value The value validation is being performed on.
078: * @param timeZone The Time Zone used to format the date,
079: * system default if null (unless value is a <code>Calendar</code>.
080: * @return The value formatted as a <code>String</code>.
081: */
082: public String format(Object value, TimeZone timeZone) {
083: return format(value, (String) null, (Locale) null, timeZone);
084: }
085:
086: /**
087: * <p>Format an object into a <code>String</code> using
088: * the specified pattern.</p>
089: *
090: * @param value The value validation is being performed on.
091: * @param pattern The pattern used to format the value.
092: * @param timeZone The Time Zone used to format the date,
093: * system default if null (unless value is a <code>Calendar</code>.
094: * @return The value formatted as a <code>String</code>.
095: */
096: public String format(Object value, String pattern, TimeZone timeZone) {
097: return format(value, pattern, (Locale) null, timeZone);
098: }
099:
100: /**
101: * <p>Format an object into a <code>String</code> using
102: * the specified Locale.</p>
103: *
104: * @param value The value validation is being performed on.
105: * @param locale The locale to use for the Format.
106: * @param timeZone The Time Zone used to format the date,
107: * system default if null (unless value is a <code>Calendar</code>.
108: * @return The value formatted as a <code>String</code>.
109: */
110: public String format(Object value, Locale locale, TimeZone timeZone) {
111: return format(value, (String) null, locale, timeZone);
112: }
113:
114: /**
115: * <p>Format an object using the specified pattern and/or
116: * <code>Locale</code>.
117: *
118: * @param value The value validation is being performed on.
119: * @param pattern The pattern used to format the value.
120: * @param locale The locale to use for the Format.
121: * @return The value formatted as a <code>String</code>.
122: */
123: public String format(Object value, String pattern, Locale locale) {
124: return format(value, pattern, locale, (TimeZone) null);
125: }
126:
127: /**
128: * <p>Format an object using the specified pattern and/or
129: * <code>Locale</code>.
130: *
131: * @param value The value validation is being performed on.
132: * @param pattern The pattern used to format the value.
133: * @param locale The locale to use for the Format.
134: * @param timeZone The Time Zone used to format the date,
135: * system default if null (unless value is a <code>Calendar</code>.
136: * @return The value formatted as a <code>String</code>.
137: */
138: public String format(Object value, String pattern, Locale locale,
139: 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,
176: TimeZone timeZone) {
177:
178: value = (value == null ? null : value.trim());
179: if (value == null || value.length() == 0) {
180: return null;
181: }
182: DateFormat formatter = (DateFormat) getFormat(pattern, locale);
183: if (timeZone != null) {
184: formatter.setTimeZone(timeZone);
185: }
186: return parse(value, formatter);
187:
188: }
189:
190: /**
191: * <p>Process the parsed value, performing any further validation
192: * and type conversion required.</p>
193: *
194: * @param value The parsed object created.
195: * @param formatter The Format used to parse the value with.
196: * @return The parsed value converted to the appropriate type
197: * if valid or <code>null</code> if invalid.
198: */
199: protected abstract Object processParsedValue(Object value,
200: Format formatter);
201:
202: /**
203: * <p>Returns a <code>DateFormat</code> for the specified <i>pattern</i>
204: * and/or <code>Locale</code>.</p>
205: *
206: * @param pattern The pattern used to validate the value against or
207: * <code>null</code> to use the default for the <code>Locale</code>.
208: * @param locale The locale to use for the currency format, system default if null.
209: * @return The <code>DateFormat</code> to created.
210: */
211: protected Format getFormat(String pattern, Locale locale) {
212: DateFormat formatter = null;
213: boolean usePattern = (pattern != null && pattern.length() > 0);
214: if (!usePattern) {
215: formatter = (DateFormat) getFormat(locale);
216: } else if (locale == null) {
217: formatter = new SimpleDateFormat(pattern);
218: } else {
219: DateFormatSymbols symbols = new DateFormatSymbols(locale);
220: formatter = new SimpleDateFormat(pattern, symbols);
221: }
222: formatter.setLenient(false);
223: return formatter;
224: }
225:
226: /**
227: * <p>Returns a <code>DateFormat</code> for the specified Locale.</p>
228: *
229: * @param locale The locale a <code>DateFormat</code> is required for,
230: * system default if null.
231: * @return The <code>DateFormat</code> to created.
232: */
233: protected Format getFormat(Locale locale) {
234:
235: DateFormat formatter = null;
236: if (dateStyle >= 0 && timeStyle >= 0) {
237: if (locale == null) {
238: formatter = DateFormat.getDateTimeInstance(dateStyle,
239: timeStyle);
240: } else {
241: formatter = DateFormat.getDateTimeInstance(dateStyle,
242: timeStyle, locale);
243: }
244: } else if (timeStyle >= 0) {
245: if (locale == null) {
246: formatter = DateFormat.getTimeInstance(timeStyle);
247: } else {
248: formatter = DateFormat.getTimeInstance(timeStyle,
249: locale);
250: }
251: } else {
252: int useDateStyle = dateStyle >= 0 ? dateStyle
253: : DateFormat.SHORT;
254: if (locale == null) {
255: formatter = DateFormat.getDateInstance(useDateStyle);
256: } else {
257: formatter = DateFormat.getDateInstance(useDateStyle,
258: locale);
259: }
260: }
261: formatter.setLenient(false);
262: return formatter;
263:
264: }
265:
266: /**
267: * <p>Compares a calendar value to another, indicating whether it is
268: * equal, less then or more than at a specified level.</p>
269: *
270: * @param value The Calendar value.
271: * @param compare The <code>Calendar</code> to check the value against.
272: * @param field The field <i>level</i> to compare to - e.g. specifying
273: * <code>Calendar.MONTH</code> will compare the year and month
274: * portions of the calendar.
275: * @return Zero if the first value is equal to the second, -1
276: * if it is less than the second or +1 if it is greater than the second.
277: */
278: protected int compare(Calendar value, Calendar compare, int field) {
279:
280: int result = 0;
281:
282: // Compare Year
283: result = calculateCompareResult(value, compare, Calendar.YEAR);
284: if (result != 0 || field == Calendar.YEAR) {
285: return result;
286: }
287:
288: // Compare Week of Year
289: if (field == Calendar.WEEK_OF_YEAR) {
290: return calculateCompareResult(value, compare,
291: Calendar.WEEK_OF_YEAR);
292: }
293:
294: // Compare Day of the Year
295: if (field == Calendar.DAY_OF_YEAR) {
296: return calculateCompareResult(value, compare,
297: Calendar.DAY_OF_YEAR);
298: }
299:
300: // Compare Month
301: result = calculateCompareResult(value, compare, Calendar.MONTH);
302: if (result != 0 || field == Calendar.MONTH) {
303: return result;
304: }
305:
306: // Compare Week of Month
307: if (field == Calendar.WEEK_OF_MONTH) {
308: return calculateCompareResult(value, compare,
309: Calendar.WEEK_OF_MONTH);
310: }
311:
312: // Compare Date
313: result = calculateCompareResult(value, compare, Calendar.DATE);
314: if (result != 0
315: || (field == Calendar.DATE
316: || field == Calendar.DAY_OF_MONTH
317: || field == Calendar.DAY_OF_WEEK || field == Calendar.DAY_OF_WEEK_IN_MONTH)) {
318: return result;
319: }
320:
321: // Compare Time fields
322: return compareTime(value, compare, field);
323:
324: }
325:
326: /**
327: * <p>Compares a calendar time value to another, indicating whether it is
328: * equal, less then or more than at a specified level.</p>
329: *
330: * @param value The Calendar value.
331: * @param compare The <code>Calendar</code> to check the value against.
332: * @param field The field <i>level</i> to compare to - e.g. specifying
333: * <code>Calendar.MINUTE</code> will compare the hours and minutes
334: * portions of the calendar.
335: * @return Zero if the first value is equal to the second, -1
336: * if it is less than the second or +1 if it is greater than the second.
337: */
338: protected int compareTime(Calendar value, Calendar compare,
339: int field) {
340:
341: int result = 0;
342:
343: // Compare Hour
344: result = calculateCompareResult(value, compare,
345: Calendar.HOUR_OF_DAY);
346: if (result != 0
347: || (field == Calendar.HOUR || field == Calendar.HOUR_OF_DAY)) {
348: return result;
349: }
350:
351: // Compare Minute
352: result = calculateCompareResult(value, compare, Calendar.MINUTE);
353: if (result != 0 || field == Calendar.MINUTE) {
354: return result;
355: }
356:
357: // Compare Second
358: result = calculateCompareResult(value, compare, Calendar.SECOND);
359: if (result != 0 || field == Calendar.SECOND) {
360: return result;
361: }
362:
363: // Compare Milliseconds
364: if (field == Calendar.MILLISECOND) {
365: return calculateCompareResult(value, compare,
366: Calendar.MILLISECOND);
367: }
368:
369: throw new IllegalArgumentException("Invalid field: " + field);
370:
371: }
372:
373: /**
374: * <p>Compares a calendar's quarter value to another, indicating whether it is
375: * equal, less then or more than the specified quarter.</p>
376: *
377: * @param value The Calendar value.
378: * @param compare The <code>Calendar</code> to check the value against.
379: * @param monthOfFirstQuarter The month that the first quarter starts.
380: * @return Zero if the first quarter is equal to the second, -1
381: * if it is less than the second or +1 if it is greater than the second.
382: */
383: protected int compareQuarters(Calendar value, Calendar compare,
384: int monthOfFirstQuarter) {
385: int valueQuarter = calculateQuarter(value, monthOfFirstQuarter);
386: int compareQuarter = calculateQuarter(compare,
387: monthOfFirstQuarter);
388: if (valueQuarter < compareQuarter) {
389: return -1;
390: } else if (valueQuarter > compareQuarter) {
391: return 1;
392: } else {
393: return 0;
394: }
395: }
396:
397: /**
398: * <p>Calculate the quarter for the specified Calendar.</p>
399: *
400: * @param calendar The Calendar value.
401: * @param monthOfFirstQuarter The month that the first quarter starts.
402: * @return The calculated quarter.
403: */
404: private int calculateQuarter(Calendar calendar,
405: int monthOfFirstQuarter) {
406: // Add Year
407: int year = calendar.get(Calendar.YEAR);
408:
409: int month = (calendar.get(Calendar.MONTH) + 1);
410: int relativeMonth = (month >= monthOfFirstQuarter) ? (month - monthOfFirstQuarter)
411: : (month + (12 - monthOfFirstQuarter));
412: int quarter = ((relativeMonth / 3) + 1);
413: // adjust the year if the quarter doesn't start in January
414: if (month < monthOfFirstQuarter) {
415: --year;
416: }
417: return (year * 10) + quarter;
418: }
419:
420: /**
421: * <p>Compares the field from two calendars indicating whether the field for the
422: * first calendar is equal to, less than or greater than the field from the
423: * second calendar.
424: *
425: * @param value The Calendar value.
426: * @param compare The <code>Calendar</code> to check the value against.
427: * @param field The field to compare for the calendars.
428: * @return Zero if the first calendar's field is equal to the seconds, -1
429: * if it is less than the seconds or +1 if it is greater than the seconds.
430: */
431: private int calculateCompareResult(Calendar value,
432: Calendar compare, int field) {
433: int difference = value.get(field) - compare.get(field);
434: if (difference < 0) {
435: return -1;
436: } else if (difference > 0) {
437: return 1;
438: } else {
439: return 0;
440: }
441: }
442: }
|