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 */ 017package org.apache.commons.validator; 018 019import java.io.Serializable; 020import java.text.DateFormat; 021import java.text.NumberFormat; 022import java.text.ParseException; 023import java.text.ParsePosition; 024import java.text.SimpleDateFormat; 025import java.util.Date; 026import java.util.Locale; 027 028import org.apache.commons.logging.Log; 029import org.apache.commons.logging.LogFactory; 030 031/** 032 * This class contains basic methods for performing validations that return the 033 * correctly typed class based on the validation performed. 034 * 035 * @version $Revision$ 036 */ 037public class GenericTypeValidator implements Serializable { 038 039 private static final long serialVersionUID = 5487162314134261703L; 040 041 private static final Log LOG = LogFactory.getLog(GenericTypeValidator.class); 042 043 /** 044 * Checks if the value can safely be converted to a byte primitive. 045 * 046 * @param value The value validation is being performed on. 047 * @return the converted Byte value. 048 */ 049 public static Byte formatByte(String value) { 050 if (value == null) { 051 return null; 052 } 053 054 try { 055 return Byte.valueOf(value); 056 } catch (NumberFormatException e) { 057 return null; 058 } 059 060 } 061 062 /** 063 * Checks if the value can safely be converted to a byte primitive. 064 * 065 * @param value The value validation is being performed on. 066 * @param locale The locale to use to parse the number (system default if 067 * null) 068 * @return the converted Byte value. 069 */ 070 public static Byte formatByte(String value, Locale locale) { 071 Byte result = null; 072 073 if (value != null) { 074 NumberFormat formatter = null; 075 if (locale != null) { 076 formatter = NumberFormat.getNumberInstance(locale); 077 } else { 078 formatter = NumberFormat.getNumberInstance(Locale.getDefault()); 079 } 080 formatter.setParseIntegerOnly(true); 081 ParsePosition pos = new ParsePosition(0); 082 Number num = formatter.parse(value, pos); 083 084 // If there was no error and we used the whole string 085 if (pos.getErrorIndex() == -1 && pos.getIndex() == value.length() && 086 num.doubleValue() >= Byte.MIN_VALUE && 087 num.doubleValue() <= Byte.MAX_VALUE) { 088 result = Byte.valueOf(num.byteValue()); 089 } 090 } 091 092 return result; 093 } 094 095 /** 096 * Checks if the value can safely be converted to a short primitive. 097 * 098 * @param value The value validation is being performed on. 099 * @return the converted Short value. 100 */ 101 public static Short formatShort(String value) { 102 if (value == null) { 103 return null; 104 } 105 106 try { 107 return Short.valueOf(value); 108 } catch (NumberFormatException e) { 109 return null; 110 } 111 112 } 113 114 /** 115 * Checks if the value can safely be converted to a short primitive. 116 * 117 * @param value The value validation is being performed on. 118 * @param locale The locale to use to parse the number (system default if 119 * null) 120 * @return the converted Short value. 121 */ 122 public static Short formatShort(String value, Locale locale) { 123 Short result = null; 124 125 if (value != null) { 126 NumberFormat formatter = null; 127 if (locale != null) { 128 formatter = NumberFormat.getNumberInstance(locale); 129 } else { 130 formatter = NumberFormat.getNumberInstance(Locale.getDefault()); 131 } 132 formatter.setParseIntegerOnly(true); 133 ParsePosition pos = new ParsePosition(0); 134 Number num = formatter.parse(value, pos); 135 136 // If there was no error and we used the whole string 137 if (pos.getErrorIndex() == -1 && pos.getIndex() == value.length() && 138 num.doubleValue() >= Short.MIN_VALUE && 139 num.doubleValue() <= Short.MAX_VALUE) { 140 result = Short.valueOf(num.shortValue()); 141 } 142 } 143 144 return result; 145 } 146 147 /** 148 * Checks if the value can safely be converted to a int primitive. 149 * 150 * @param value The value validation is being performed on. 151 * @return the converted Integer value. 152 */ 153 public static Integer formatInt(String value) { 154 if (value == null) { 155 return null; 156 } 157 158 try { 159 return Integer.valueOf(value); 160 } catch (NumberFormatException e) { 161 return null; 162 } 163 164 } 165 166 /** 167 * Checks if the value can safely be converted to an int primitive. 168 * 169 * @param value The value validation is being performed on. 170 * @param locale The locale to use to parse the number (system default if 171 * null) 172 * @return the converted Integer value. 173 */ 174 public static Integer formatInt(String value, Locale locale) { 175 Integer result = null; 176 177 if (value != null) { 178 NumberFormat formatter = null; 179 if (locale != null) { 180 formatter = NumberFormat.getNumberInstance(locale); 181 } else { 182 formatter = NumberFormat.getNumberInstance(Locale.getDefault()); 183 } 184 formatter.setParseIntegerOnly(true); 185 ParsePosition pos = new ParsePosition(0); 186 Number num = formatter.parse(value, pos); 187 188 // If there was no error and we used the whole string 189 if (pos.getErrorIndex() == -1 && pos.getIndex() == value.length() && 190 num.doubleValue() >= Integer.MIN_VALUE && 191 num.doubleValue() <= Integer.MAX_VALUE) { 192 result = Integer.valueOf(num.intValue()); 193 } 194 } 195 196 return result; 197 } 198 199 /** 200 * Checks if the value can safely be converted to a long primitive. 201 * 202 * @param value The value validation is being performed on. 203 * @return the converted Long value. 204 */ 205 public static Long formatLong(String value) { 206 if (value == null) { 207 return null; 208 } 209 210 try { 211 return Long.valueOf(value); 212 } catch (NumberFormatException e) { 213 return null; 214 } 215 216 } 217 218 /** 219 * Checks if the value can safely be converted to a long primitive. 220 * 221 * @param value The value validation is being performed on. 222 * @param locale The locale to use to parse the number (system default if 223 * null) 224 * @return the converted Long value. 225 */ 226 public static Long formatLong(String value, Locale locale) { 227 Long result = null; 228 229 if (value != null) { 230 NumberFormat formatter = null; 231 if (locale != null) { 232 formatter = NumberFormat.getNumberInstance(locale); 233 } else { 234 formatter = NumberFormat.getNumberInstance(Locale.getDefault()); 235 } 236 formatter.setParseIntegerOnly(true); 237 ParsePosition pos = new ParsePosition(0); 238 Number num = formatter.parse(value, pos); 239 240 // If there was no error and we used the whole string 241 if (pos.getErrorIndex() == -1 && pos.getIndex() == value.length() && 242 num.doubleValue() >= Long.MIN_VALUE && 243 num.doubleValue() <= Long.MAX_VALUE) { 244 result = Long.valueOf(num.longValue()); 245 } 246 } 247 248 return result; 249 } 250 251 /** 252 * Checks if the value can safely be converted to a float primitive. 253 * 254 * @param value The value validation is being performed on. 255 * @return the converted Float value. 256 */ 257 public static Float formatFloat(String value) { 258 if (value == null) { 259 return null; 260 } 261 262 try { 263 return Float.valueOf(value); 264 } catch (NumberFormatException e) { 265 return null; 266 } 267 268 } 269 270 /** 271 * Checks if the value can safely be converted to a float primitive. 272 * 273 * @param value The value validation is being performed on. 274 * @param locale The locale to use to parse the number (system default if 275 * null) 276 * @return the converted Float value. 277 */ 278 public static Float formatFloat(String value, Locale locale) { 279 Float result = null; 280 281 if (value != null) { 282 NumberFormat formatter = null; 283 if (locale != null) { 284 formatter = NumberFormat.getInstance(locale); 285 } else { 286 formatter = NumberFormat.getInstance(Locale.getDefault()); 287 } 288 ParsePosition pos = new ParsePosition(0); 289 Number num = formatter.parse(value, pos); 290 291 // If there was no error and we used the whole string 292 if (pos.getErrorIndex() == -1 && pos.getIndex() == value.length() && 293 num.doubleValue() >= (Float.MAX_VALUE * -1) && 294 num.doubleValue() <= Float.MAX_VALUE) { 295 result = Float.valueOf(num.floatValue()); 296 } 297 } 298 299 return result; 300 } 301 302 /** 303 * Checks if the value can safely be converted to a double primitive. 304 * 305 * @param value The value validation is being performed on. 306 * @return the converted Double value. 307 */ 308 public static Double formatDouble(String value) { 309 if (value == null) { 310 return null; 311 } 312 313 try { 314 return Double.valueOf(value); 315 } catch (NumberFormatException e) { 316 return null; 317 } 318 319 } 320 321 /** 322 * Checks if the value can safely be converted to a double primitive. 323 * 324 * @param value The value validation is being performed on. 325 * @param locale The locale to use to parse the number (system default if 326 * null) 327 * @return the converted Double value. 328 */ 329 public static Double formatDouble(String value, Locale locale) { 330 Double result = null; 331 332 if (value != null) { 333 NumberFormat formatter = null; 334 if (locale != null) { 335 formatter = NumberFormat.getInstance(locale); 336 } else { 337 formatter = NumberFormat.getInstance(Locale.getDefault()); 338 } 339 ParsePosition pos = new ParsePosition(0); 340 Number num = formatter.parse(value, pos); 341 342 // If there was no error and we used the whole string 343 if (pos.getErrorIndex() == -1 && pos.getIndex() == value.length() && 344 num.doubleValue() >= (Double.MAX_VALUE * -1) && 345 num.doubleValue() <= Double.MAX_VALUE) { 346 result = Double.valueOf(num.doubleValue()); 347 } 348 } 349 350 return result; 351 } 352 353 /** 354 * Checks if the field is a valid date. 355 * 356 * <p>The {@code Locale} is used with {@code java.text.DateFormat}. The {@link java.text.DateFormat#setLenient(boolean)} 357 * method is set to {@code false} for all. 358 * </p> 359 * 360 * @param value The value validation is being performed on. 361 * @param locale The Locale to use to parse the date (system default if null) 362 * @return the converted Date value. 363 */ 364 public static Date formatDate(String value, Locale locale) { 365 Date date = null; 366 367 if (value == null) { 368 return null; 369 } 370 371 try { 372 // Get the formatters to check against 373 DateFormat formatterShort = null; 374 DateFormat formatterDefault = null; 375 if (locale != null) { 376 formatterShort = 377 DateFormat.getDateInstance(DateFormat.SHORT, locale); 378 formatterDefault = 379 DateFormat.getDateInstance(DateFormat.DEFAULT, locale); 380 } else { 381 formatterShort = 382 DateFormat.getDateInstance( 383 DateFormat.SHORT, 384 Locale.getDefault()); 385 formatterDefault = 386 DateFormat.getDateInstance( 387 DateFormat.DEFAULT, 388 Locale.getDefault()); 389 } 390 391 // Turn off lenient parsing 392 formatterShort.setLenient(false); 393 formatterDefault.setLenient(false); 394 395 // Firstly, try with the short form 396 try { 397 date = formatterShort.parse(value); 398 } catch (ParseException e) { 399 // Fall back on the default one 400 date = formatterDefault.parse(value); 401 } 402 } catch (ParseException e) { 403 // Bad date, so LOG and return null 404 if (LOG.isDebugEnabled()) { 405 LOG.debug("Date parse failed value=[" + value + "], " + 406 "locale=[" + locale + "] " + e); 407 } 408 } 409 410 return date; 411 } 412 413 /** 414 * Checks if the field is a valid date. 415 * 416 * <p>The pattern is used with {@code java.text.SimpleDateFormat}. 417 * If strict is true, then the length will be checked so '2/12/1999' will 418 * not pass validation with the format 'MM/dd/yyyy' because the month isn't 419 * two digits. The {@link java.text.SimpleDateFormat#setLenient(boolean)} 420 * method is set to {@code false} for all. 421 * </p> 422 * 423 * @param value The value validation is being performed on. 424 * @param datePattern The pattern passed to {@code SimpleDateFormat}. 425 * @param strict Whether or not to have an exact match of the 426 * datePattern. 427 * @return the converted Date value. 428 */ 429 public static Date formatDate(String value, String datePattern, boolean strict) { 430 Date date = null; 431 432 if (value == null 433 || datePattern == null 434 || datePattern.length() == 0) { 435 return null; 436 } 437 438 try { 439 SimpleDateFormat formatter = new SimpleDateFormat(datePattern); 440 formatter.setLenient(false); 441 442 date = formatter.parse(value); 443 444 if (strict && datePattern.length() != value.length()) { 445 date = null; 446 } 447 } catch (ParseException e) { 448 // Bad date so return null 449 if (LOG.isDebugEnabled()) { 450 LOG.debug("Date parse failed value=[" + value + "], " + 451 "pattern=[" + datePattern + "], " + 452 "strict=[" + strict + "] " + e); 453 } 454 } 455 456 return date; 457 } 458 459 /** 460 * Checks if the field is a valid credit card number. 461 * 462 * <p>Reference Sean M. Burke's <a href="http://www.ling.nwu.edu/~sburke/pub/luhn_lib.pl"> 463 * script</a>.</p> 464 * 465 * @param value The value validation is being performed on. 466 * @return the converted Credit Card number. 467 */ 468 public static Long formatCreditCard(String value) { 469 return GenericValidator.isCreditCard(value) ? Long.valueOf(value) : null; 470 } 471 472} 473