AbstractCalendarValidator.java

  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. import java.text.DateFormat;
  19. import java.text.DateFormatSymbols;
  20. import java.text.Format;
  21. import java.text.SimpleDateFormat;
  22. import java.util.Calendar;
  23. import java.util.Locale;
  24. import java.util.TimeZone;

  25. import org.apache.commons.validator.GenericValidator;

  26. /**
  27.  * <p>Abstract class for Date/Time/Calendar validation.</p>
  28.  *
  29.  * <p>This is a <i>base</i> class for building Date / Time
  30.  *    Validators using format parsing.</p>
  31.  *
  32.  * @since 1.3.0
  33.  */
  34. public abstract class AbstractCalendarValidator extends AbstractFormatValidator {

  35.     private static final long serialVersionUID = -1410008585975827379L;

  36.     /**
  37.      * The date style to use for Locale validation.
  38.      */
  39.     private final int dateStyle;

  40.     /**
  41.      * The time style to use for Locale validation.
  42.      */
  43.     private final int timeStyle;

  44.     /**
  45.      * Constructs an instance with the specified <i>strict</i>,
  46.      * <i>time</i> and <i>date</i> style parameters.
  47.      *
  48.      * @param strict {@code true} if strict
  49.      *        <code>Format</code> parsing should be used.
  50.      * @param dateStyle the date style to use for Locale validation.
  51.      * @param timeStyle the time style to use for Locale validation.
  52.      */
  53.     public AbstractCalendarValidator(final boolean strict, final int dateStyle, final int timeStyle) {
  54.         super(strict);
  55.         this.dateStyle = dateStyle;
  56.         this.timeStyle = timeStyle;
  57.     }

  58.     /**
  59.      * <p>Compares the field from two calendars indicating whether the field for the
  60.      *    first calendar is equal to, less than or greater than the field from the
  61.      *    second calendar.
  62.      *
  63.      * @param value The Calendar value.
  64.      * @param compare The <code>Calendar</code> to check the value against.
  65.      * @param field The field to compare for the calendars.
  66.      * @return Zero if the first calendar's field is equal to the seconds, -1
  67.      *         if it is less than the seconds or +1 if it is greater than the seconds.
  68.      */
  69.     private int calculateCompareResult(final Calendar value, final Calendar compare, final int field) {
  70.         final int difference = value.get(field) - compare.get(field);
  71.         if (difference < 0) {
  72.             return -1;
  73.         }
  74.         if (difference > 0) {
  75.             return 1;
  76.         }
  77.         return 0;
  78.     }

  79.     /**
  80.      * <p>Calculate the quarter for the specified Calendar.</p>
  81.      *
  82.      * @param calendar The Calendar value.
  83.      * @param monthOfFirstQuarter The  month that the first quarter starts.
  84.      * @return The calculated quarter.
  85.      */
  86.     private int calculateQuarter(final Calendar calendar, final int monthOfFirstQuarter) {
  87.         // Add Year
  88.         int year = calendar.get(Calendar.YEAR);

  89.         final int month = calendar.get(Calendar.MONTH) + 1;
  90.         final int relativeMonth = month >= monthOfFirstQuarter
  91.                           ? month - monthOfFirstQuarter
  92.                           : month + 12 - monthOfFirstQuarter; // CHECKSTYLE IGNORE MagicNumber
  93.         final int quarter = relativeMonth / 3 + 1; // CHECKSTYLE IGNORE MagicNumber
  94.         // adjust the year if the quarter doesn't start in January
  95.         if (month < monthOfFirstQuarter) {
  96.             --year;
  97.         }
  98.         return year * 10 + quarter; // CHECKSTYLE IGNORE MagicNumber
  99.     }

  100.     /**
  101.      * <p>Compares a calendar value to another, indicating whether it is
  102.      *    equal, less then or more than at a specified level.</p>
  103.      *
  104.      * @param value The Calendar value.
  105.      * @param compare The <code>Calendar</code> to check the value against.
  106.      * @param field The field <i>level</i> to compare to - e.g. specifying
  107.      *        <code>Calendar.MONTH</code> will compare the year and month
  108.      *        portions of the calendar.
  109.      * @return Zero if the first value is equal to the second, -1
  110.      *         if it is less than the second or +1 if it is greater than the second.
  111.      */
  112.     protected int compare(final Calendar value, final Calendar compare, final int field) {

  113.         int result;

  114.         // Compare Year
  115.         result = calculateCompareResult(value, compare, Calendar.YEAR);
  116.         if (result != 0 || field == Calendar.YEAR) {
  117.             return result;
  118.         }

  119.         // Compare Week of Year
  120.         if (field == Calendar.WEEK_OF_YEAR) {
  121.             return calculateCompareResult(value, compare, Calendar.WEEK_OF_YEAR);
  122.         }

  123.         // Compare Day of the Year
  124.         if (field == Calendar.DAY_OF_YEAR) {
  125.             return calculateCompareResult(value, compare, Calendar.DAY_OF_YEAR);
  126.         }

  127.         // Compare Month
  128.         result = calculateCompareResult(value, compare, Calendar.MONTH);
  129.         if (result != 0 || field == Calendar.MONTH) {
  130.             return result;
  131.         }

  132.         // Compare Week of Month
  133.         if (field == Calendar.WEEK_OF_MONTH) {
  134.             return calculateCompareResult(value, compare, Calendar.WEEK_OF_MONTH);
  135.         }

  136.         // Compare Date
  137.         result = calculateCompareResult(value, compare, Calendar.DATE);
  138.         if (result != 0 || field == Calendar.DATE ||
  139.                           field == Calendar.DAY_OF_WEEK ||
  140.                           field == Calendar.DAY_OF_WEEK_IN_MONTH) {
  141.             return result;
  142.         }

  143.         // Compare Time fields
  144.         return compareTime(value, compare, field);

  145.     }

  146.     /**
  147.      * <p>Compares a calendar's quarter value to another, indicating whether it is
  148.      *    equal, less then or more than the specified quarter.</p>
  149.      *
  150.      * @param value The Calendar value.
  151.      * @param compare The <code>Calendar</code> to check the value against.
  152.      * @param monthOfFirstQuarter The  month that the first quarter starts.
  153.      * @return Zero if the first quarter is equal to the second, -1
  154.      *         if it is less than the second or +1 if it is greater than the second.
  155.      */
  156.     protected int compareQuarters(final Calendar value, final Calendar compare, final int monthOfFirstQuarter) {
  157.         final int valueQuarter = calculateQuarter(value, monthOfFirstQuarter);
  158.         final int compareQuarter = calculateQuarter(compare, monthOfFirstQuarter);
  159.         if (valueQuarter < compareQuarter) {
  160.             return -1;
  161.         }
  162.         if (valueQuarter > compareQuarter) {
  163.             return 1;
  164.         }
  165.         return 0;
  166.     }

  167.     /**
  168.      * <p>Compares a calendar time value to another, indicating whether it is
  169.      *    equal, less then or more than at a specified level.</p>
  170.      *
  171.      * @param value The Calendar value.
  172.      * @param compare The <code>Calendar</code> to check the value against.
  173.      * @param field The field <i>level</i> to compare to - e.g. specifying
  174.      *        <code>Calendar.MINUTE</code> will compare the hours and minutes
  175.      *        portions of the calendar.
  176.      * @return Zero if the first value is equal to the second, -1
  177.      *         if it is less than the second or +1 if it is greater than the second.
  178.      */
  179.     protected int compareTime(final Calendar value, final Calendar compare, final int field) {

  180.         int result;

  181.         // Compare Hour
  182.         result = calculateCompareResult(value, compare, Calendar.HOUR_OF_DAY);
  183.         if (result != 0 || field == Calendar.HOUR || field == Calendar.HOUR_OF_DAY) {
  184.             return result;
  185.         }

  186.         // Compare Minute
  187.         result = calculateCompareResult(value, compare, Calendar.MINUTE);
  188.         if (result != 0 || field == Calendar.MINUTE) {
  189.             return result;
  190.         }

  191.         // Compare Second
  192.         result = calculateCompareResult(value, compare, Calendar.SECOND);
  193.         if (result != 0 || field == Calendar.SECOND) {
  194.             return result;
  195.         }

  196.         // Compare Milliseconds
  197.         if (field == Calendar.MILLISECOND) {
  198.             return calculateCompareResult(value, compare, Calendar.MILLISECOND);
  199.         }

  200.         throw new IllegalArgumentException("Invalid field: " + field);

  201.     }

  202.     /**
  203.      * <p>Format a value with the specified <code>DateFormat</code>.</p>
  204.      *
  205.      * @param value The value to be formatted.
  206.      * @param formatter The Format to use.
  207.      * @return The formatted value.
  208.      */
  209.     @Override
  210.     protected String format(Object value, final Format formatter) {
  211.         if (value == null) {
  212.             return null;
  213.         }
  214.         if (value instanceof Calendar) {
  215.             value = ((Calendar) value).getTime();
  216.         }
  217.         return formatter.format(value);
  218.     }

  219.     /**
  220.      * <p>Format an object into a <code>String</code> using
  221.      * the specified Locale.</p>
  222.      *
  223.      * @param value The value validation is being performed on.
  224.      * @param locale The locale to use for the Format.
  225.      * @param timeZone The Time Zone used to format the date,
  226.      *  system default if null (unless value is a <code>Calendar</code>.
  227.      * @return The value formatted as a <code>String</code>.
  228.      */
  229.     public String format(final Object value, final Locale locale, final TimeZone timeZone) {
  230.         return format(value, (String) null, locale, timeZone);
  231.     }

  232.     /**
  233.      * <p>Format an object using the specified pattern and/or
  234.      *    <code>Locale</code>.
  235.      *
  236.      * @param value The value validation is being performed on.
  237.      * @param pattern The pattern used to format the value.
  238.      * @param locale The locale to use for the Format.
  239.      * @return The value formatted as a <code>String</code>.
  240.      */
  241.     @Override
  242.     public String format(final Object value, final String pattern, final Locale locale) {
  243.         return format(value, pattern, locale, (TimeZone) null);
  244.     }

  245.     /**
  246.      * <p>Format an object using the specified pattern and/or
  247.      *    <code>Locale</code>.
  248.      *
  249.      * @param value The value validation is being performed on.
  250.      * @param pattern The pattern used to format the value.
  251.      * @param locale The locale to use for the Format.
  252.      * @param timeZone The Time Zone used to format the date,
  253.      *  system default if null (unless value is a <code>Calendar</code>.
  254.      * @return The value formatted as a <code>String</code>.
  255.      */
  256.     public String format(final Object value, final String pattern, final Locale locale, final TimeZone timeZone) {
  257.         final DateFormat formatter = (DateFormat) getFormat(pattern, locale);
  258.         if (timeZone != null) {
  259.             formatter.setTimeZone(timeZone);
  260.         } else if (value instanceof Calendar) {
  261.             formatter.setTimeZone(((Calendar) value).getTimeZone());
  262.         }
  263.         return format(value, formatter);
  264.     }

  265.     /**
  266.      * <p>Format an object into a <code>String</code> using
  267.      * the specified pattern.</p>
  268.      *
  269.      * @param value The value validation is being performed on.
  270.      * @param pattern The pattern used to format the value.
  271.      * @param timeZone The Time Zone used to format the date,
  272.      *  system default if null (unless value is a <code>Calendar</code>.
  273.      * @return The value formatted as a <code>String</code>.
  274.      */
  275.     public String format(final Object value, final String pattern, final TimeZone timeZone) {
  276.         return format(value, pattern, (Locale) null, timeZone);
  277.     }

  278.     /**
  279.      * <p>Format an object into a <code>String</code> using
  280.      * the default Locale.</p>
  281.      *
  282.      * @param value The value validation is being performed on.
  283.      * @param timeZone The Time Zone used to format the date,
  284.      *  system default if null (unless value is a <code>Calendar</code>.
  285.      * @return The value formatted as a <code>String</code>.
  286.      */
  287.     public String format(final Object value, final TimeZone timeZone) {
  288.         return format(value, (String) null, (Locale) null, timeZone);
  289.     }

  290.     /**
  291.      * <p>Returns a <code>DateFormat</code> for the specified Locale.</p>
  292.      *
  293.      * @param locale The locale a <code>DateFormat</code> is required for,
  294.      *        system default if null.
  295.      * @return The <code>DateFormat</code> to created.
  296.      */
  297.     protected Format getFormat(final Locale locale) {
  298.         DateFormat formatter;
  299.         if (dateStyle >= 0 && timeStyle >= 0) {
  300.             if (locale == null) {
  301.                 formatter = DateFormat.getDateTimeInstance(dateStyle, timeStyle);
  302.             } else {
  303.                 formatter = DateFormat.getDateTimeInstance(dateStyle, timeStyle, locale);
  304.             }
  305.         } else if (timeStyle >= 0) {
  306.             if (locale == null) {
  307.                 formatter = DateFormat.getTimeInstance(timeStyle);
  308.             } else {
  309.                 formatter = DateFormat.getTimeInstance(timeStyle, locale);
  310.             }
  311.         } else {
  312.             final int useDateStyle = dateStyle >= 0 ? dateStyle : DateFormat.SHORT;
  313.             if (locale == null) {
  314.                 formatter = DateFormat.getDateInstance(useDateStyle);
  315.             } else {
  316.                 formatter = DateFormat.getDateInstance(useDateStyle, locale);
  317.             }
  318.         }
  319.         formatter.setLenient(false);
  320.         return formatter;

  321.     }

  322.     /**
  323.      * <p>Returns a <code>DateFormat</code> for the specified <i>pattern</i>
  324.      *    and/or <code>Locale</code>.</p>
  325.      *
  326.      * @param pattern The pattern used to validate the value against or
  327.      *        {@code null} to use the default for the <code>Locale</code>.
  328.      * @param locale The locale to use for the currency format, system default if null.
  329.      * @return The <code>DateFormat</code> to created.
  330.      */
  331.     @Override
  332.     protected Format getFormat(final String pattern, final Locale locale) {
  333.         DateFormat formatter;
  334.         final boolean usePattern = !GenericValidator.isBlankOrNull(pattern);
  335.         if (!usePattern) {
  336.             formatter = (DateFormat) getFormat(locale);
  337.         } else if (locale == null) {
  338.             formatter = new SimpleDateFormat(pattern);
  339.         } else {
  340.             final DateFormatSymbols symbols = new DateFormatSymbols(locale);
  341.             formatter = new SimpleDateFormat(pattern, symbols);
  342.         }
  343.         formatter.setLenient(false);
  344.         return formatter;
  345.     }

  346.     /**
  347.      * <p>Validate using the specified <code>Locale</code>.
  348.      *
  349.      * @param value The value validation is being performed on.
  350.      * @param pattern The pattern used to format the value.
  351.      * @param locale The locale to use for the Format, defaults to the default
  352.      * @return {@code true} if the value is valid.
  353.      */
  354.     @Override
  355.     public boolean isValid(final String value, final String pattern, final Locale locale) {
  356.         return parse(value, pattern, locale, (TimeZone) null) != null;
  357.     }

  358.     /**
  359.      * <p>Checks if the value is valid against a specified pattern.</p>
  360.      *
  361.      * @param value The value validation is being performed on.
  362.      * @param pattern The pattern used to validate the value against, or the
  363.      *        default for the <code>Locale</code> if {@code null}.
  364.      * @param locale The locale to use for the date format, system default if null.
  365.      * @param timeZone The Time Zone used to parse the date, system default if null.
  366.      * @return The parsed value if valid or {@code null} if invalid.
  367.      */
  368.     protected Object parse(String value, final String pattern, final Locale locale, final TimeZone timeZone) {
  369.         value = value == null ? null : value.trim();
  370.         final String value1 = value;
  371.         if (GenericValidator.isBlankOrNull(value1)) {
  372.             return null;
  373.         }
  374.         final DateFormat formatter = (DateFormat) getFormat(pattern, locale);
  375.         if (timeZone != null) {
  376.             formatter.setTimeZone(timeZone);
  377.         }
  378.         return parse(value, formatter);

  379.     }

  380.     /**
  381.      * <p>Process the parsed value, performing any further validation
  382.      *    and type conversion required.</p>
  383.      *
  384.      * @param value The parsed object created.
  385.      * @param formatter The Format used to parse the value with.
  386.      * @return The parsed value converted to the appropriate type
  387.      *         if valid or {@code null} if invalid.
  388.      */
  389.     @Override
  390.     protected abstract Object processParsedValue(Object value, Format formatter);
  391. }