View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
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         // Compare Year
272         result = calculateCompareResult(value, compare, Calendar.YEAR);
273         if (result != 0 || field == Calendar.YEAR) {
274             return result;
275         }
276 
277         // Compare Week of Year
278         if (field == Calendar.WEEK_OF_YEAR) {
279             return calculateCompareResult(value, compare, Calendar.WEEK_OF_YEAR);
280         }
281 
282         // Compare Day of the Year
283         if (field == Calendar.DAY_OF_YEAR) {
284             return calculateCompareResult(value, compare, Calendar.DAY_OF_YEAR);
285         }
286 
287         // Compare Month
288         result = calculateCompareResult(value, compare, Calendar.MONTH);
289         if (result != 0 || field == Calendar.MONTH) {
290             return result;
291         }
292 
293         // Compare Week of Month
294         if (field == Calendar.WEEK_OF_MONTH) {
295             return calculateCompareResult(value, compare, Calendar.WEEK_OF_MONTH);
296         }
297 
298         // Compare Date
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         // Compare Time fields
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         // Compare Hour
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         // Compare Minute
335         result = calculateCompareResult(value, compare, Calendar.MINUTE);
336         if (result != 0 || field == Calendar.MINUTE) {
337             return result;
338         }
339 
340         // Compare Second
341         result = calculateCompareResult(value, compare, Calendar.SECOND);
342         if (result != 0 || field == Calendar.SECOND) {
343             return result;
344         }
345 
346         // Compare Milliseconds
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         // Add Year
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         // adjust the year if the quarter doesn't start in January
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 }