View Javadoc

1   /*
2    * $Id: AbstractCalendarValidator.java 386637 2006-03-17 13:22:26Z niallp $
3    * $Revision: 386637 $
4    * $Date: 2006-03-17 13:22:26 +0000 (Fri, 17 Mar 2006) $
5    *
6    * ====================================================================
7    * Copyright 2006 The Apache Software Foundation
8    *
9    * Licensed under the Apache License, Version 2.0 (the "License");
10   * you may not use this file except in compliance with the License.
11   * You may obtain a copy of the License at
12   *
13   *     http://www.apache.org/licenses/LICENSE-2.0
14   *
15   * Unless required by applicable law or agreed to in writing, software
16   * distributed under the License is distributed on an "AS IS" BASIS,
17   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18   * See the License for the specific language governing permissions and
19   * limitations under the License.
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         // Compare Year
276         result = calculateCompareResult(value, compare, Calendar.YEAR);
277         if (result != 0 || field == Calendar.YEAR) {
278             return result;
279         }
280 
281         // Compare Week of Year
282         if (field == Calendar.WEEK_OF_YEAR) {
283             return calculateCompareResult(value, compare, Calendar.WEEK_OF_YEAR);
284         }
285 
286         // Compare Day of the Year
287         if (field == Calendar.DAY_OF_YEAR) {
288             return calculateCompareResult(value, compare, Calendar.DAY_OF_YEAR);
289         }
290 
291         // Compare Month
292         result = calculateCompareResult(value, compare, Calendar.MONTH);
293         if (result != 0 || field == Calendar.MONTH) {
294             return result;
295         }
296 
297         // Compare Week of Month
298         if (field == Calendar.WEEK_OF_MONTH) {
299             return calculateCompareResult(value, compare, Calendar.WEEK_OF_MONTH);
300         }
301 
302         // Compare Date
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         // Compare Time fields
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         // Compare Hour
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         // Compare Minute
339         result = calculateCompareResult(value, compare, Calendar.MINUTE);
340         if (result != 0 || field == Calendar.MINUTE) {
341             return result;
342         }
343 
344         // Compare Second
345         result = calculateCompareResult(value, compare, Calendar.SECOND);
346         if (result != 0 || field == Calendar.SECOND) {
347             return result;
348         }
349 
350         // Compare Milliseconds
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         // Add Year
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         // adjust the year if the quarter doesn't start in January
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 }