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 }