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: 594917 $ $Date: 2007-11-14 16:36:40 +0100 (Mi, 14. Nov 2007) $ 034 * @since Validator 1.3.0 035 */ 036 public abstract class AbstractCalendarValidator extends AbstractFormatValidator { 037 038 private final int dateStyle; 039 040 private final int timeStyle; 041 042 /** 043 * Construct an instance with the specified <i>strict</i>, 044 * <i>time</i> and <i>date</i> style parameters. 045 * 046 * @param strict <code>true</code> if strict 047 * <code>Format</code> parsing should be used. 048 * @param dateStyle the date style to use for Locale validation. 049 * @param timeStyle the time style to use for Locale validation. 050 */ 051 public AbstractCalendarValidator(boolean strict, int dateStyle, int timeStyle) { 052 super(strict); 053 this.dateStyle = dateStyle; 054 this.timeStyle = timeStyle; 055 } 056 057 /** 058 * <p>Validate using the specified <code>Locale</code>. 059 * 060 * @param value The value validation is being performed on. 061 * @param pattern The pattern used to format the value. 062 * @param locale The locale to use for the Format, defaults to the default 063 * @return <code>true</code> if the value is valid. 064 */ 065 public boolean isValid(String value, String pattern, Locale locale) { 066 Object parsedValue = parse(value, pattern, locale, (TimeZone)null); 067 return (parsedValue == null ? false : true); 068 } 069 070 /** 071 * <p>Format an object into a <code>String</code> using 072 * the default Locale.</p> 073 * 074 * @param value The value validation is being performed on. 075 * @param timeZone The Time Zone used to format the date, 076 * system default if null (unless value is a <code>Calendar</code>. 077 * @return The value formatted as a <code>String</code>. 078 */ 079 public String format(Object value, TimeZone timeZone) { 080 return format(value, (String)null, (Locale)null, timeZone); 081 } 082 083 /** 084 * <p>Format an object into a <code>String</code> using 085 * the specified pattern.</p> 086 * 087 * @param value The value validation is being performed on. 088 * @param pattern The pattern used to format the value. 089 * @param timeZone The Time Zone used to format the date, 090 * system default if null (unless value is a <code>Calendar</code>. 091 * @return The value formatted as a <code>String</code>. 092 */ 093 public String format(Object value, String pattern, TimeZone timeZone) { 094 return format(value, pattern, (Locale)null, timeZone); 095 } 096 097 /** 098 * <p>Format an object into a <code>String</code> using 099 * 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 }