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;
18  
19  import java.io.Serializable;
20  import java.text.DateFormat;
21  import java.text.NumberFormat;
22  import java.text.ParseException;
23  import java.text.ParsePosition;
24  import java.text.SimpleDateFormat;
25  import java.util.Date;
26  import java.util.Locale;
27  
28  import org.apache.commons.logging.Log;
29  import org.apache.commons.logging.LogFactory;
30  
31  /**
32   * This class contains basic methods for performing validations that return the
33   * correctly typed class based on the validation performed.
34   */
35  public class GenericTypeValidator implements Serializable {
36  
37      private static final long serialVersionUID = 5487162314134261703L;
38  
39      private static final Log LOG = LogFactory.getLog(GenericTypeValidator.class);
40  
41      /**
42       * Checks if the value can safely be converted to a byte primitive.
43       *
44       * @param value The value validation is being performed on.
45       * @return the converted Byte value.
46       */
47      public static Byte formatByte(final String value) {
48          if (value == null) {
49              return null;
50          }
51  
52          try {
53              return Byte.valueOf(value);
54          } catch (final NumberFormatException e) {
55              return null;
56          }
57  
58      }
59  
60      /**
61       * Checks if the value can safely be converted to a byte primitive.
62       *
63       * @param value  The value validation is being performed on.
64       * @param locale The locale to use to parse the number (system default if
65       *               null)
66       * @return the converted Byte value.
67       */
68      public static Byte formatByte(final String value, final Locale locale) {
69          Byte result = null;
70  
71          if (value != null) {
72              NumberFormat formatter = null;
73              if (locale != null) {
74                  formatter = NumberFormat.getNumberInstance(locale);
75              } else {
76                  formatter = NumberFormat.getNumberInstance(Locale.getDefault());
77              }
78              formatter.setParseIntegerOnly(true);
79              final ParsePosition pos = new ParsePosition(0);
80              final Number num = formatter.parse(value, pos);
81  
82              // If there was no error      and we used the whole string
83              if (pos.getErrorIndex() == -1 && pos.getIndex() == value.length() &&
84                      num.doubleValue() >= Byte.MIN_VALUE &&
85                      num.doubleValue() <= Byte.MAX_VALUE) {
86                  result = Byte.valueOf(num.byteValue());
87              }
88          }
89  
90          return result;
91      }
92  
93      /**
94       * Checks if the field is a valid credit card number.
95       *
96       * <p>Reference Sean M. Burke's <a href="http://www.ling.nwu.edu/~sburke/pub/luhn_lib.pl">
97       * script</a>.</p>
98       *
99       * @param value The value validation is being performed on.
100      * @return the converted Credit Card number.
101      */
102     public static Long formatCreditCard(final String value) {
103         return GenericValidator.isCreditCard(value) ? Long.valueOf(value) : null;
104     }
105 
106     /**
107      * Checks if the field is a valid date.
108      *
109      * <p>The {@code Locale} is used with {@link java.text.DateFormat}. The {@link java.text.DateFormat#setLenient(boolean)}
110      * method is set to {@code false} for all.
111      * </p>
112      *
113      * @param value  The value validation is being performed on.
114      * @param locale The Locale to use to parse the date (system default if null)
115      * @return the converted Date value.
116      */
117     public static Date formatDate(final String value, final Locale locale) {
118         Date date = null;
119 
120         if (value == null) {
121             return null;
122         }
123 
124         try {
125             // Get the formatters to check against
126             DateFormat formatterShort = null;
127             DateFormat formatterDefault = null;
128             if (locale != null) {
129                 formatterShort =
130                         DateFormat.getDateInstance(DateFormat.SHORT, locale);
131                 formatterDefault =
132                         DateFormat.getDateInstance(DateFormat.DEFAULT, locale);
133             } else {
134                 formatterShort =
135                         DateFormat.getDateInstance(
136                                 DateFormat.SHORT,
137                                 Locale.getDefault());
138                 formatterDefault =
139                         DateFormat.getDateInstance(
140                                 DateFormat.DEFAULT,
141                                 Locale.getDefault());
142             }
143 
144             // Turn off lenient parsing
145             formatterShort.setLenient(false);
146             formatterDefault.setLenient(false);
147 
148             // Firstly, try with the short form
149             try {
150                 date = formatterShort.parse(value);
151             } catch (final ParseException e) {
152                 // Fall back on the default one
153                 date = formatterDefault.parse(value);
154             }
155         } catch (final ParseException e) {
156             // Bad date, so LOG and return null
157             if (LOG.isDebugEnabled()) {
158                 LOG.debug("Date parse failed value=[" + value + "], " +
159                         "locale=[" + locale + "] " + e);
160             }
161         }
162 
163         return date;
164     }
165 
166     /**
167      * Checks if the field is a valid date.
168      *
169      * <p>The pattern is used with {@link java.text.SimpleDateFormat}.
170      * If strict is true, then the length will be checked so '2/12/1999' will
171      * not pass validation with the format 'MM/dd/yyyy' because the month isn't
172      * two digits. The {@link java.text.SimpleDateFormat#setLenient(boolean)}
173      * method is set to {@code false} for all.
174      * </p>
175      *
176      * @param value       The value validation is being performed on.
177      * @param datePattern The pattern passed to {@code SimpleDateFormat}.
178      * @param strict      Whether or not to have an exact match of the
179      *                    datePattern.
180      * @return the converted Date value.
181      */
182     public static Date formatDate(final String value, final String datePattern, final boolean strict) {
183         Date date = null;
184 
185         if (value == null
186                 || datePattern == null
187                 || datePattern.isEmpty()) {
188             return null;
189         }
190 
191         try {
192             final SimpleDateFormat formatter = new SimpleDateFormat(datePattern);
193             formatter.setLenient(false);
194 
195             date = formatter.parse(value);
196 
197             if (strict && datePattern.length() != value.length()) {
198                 date = null;
199             }
200         } catch (final ParseException e) {
201             // Bad date so return null
202             if (LOG.isDebugEnabled()) {
203                 LOG.debug("Date parse failed value=[" + value + "], " +
204                         "pattern=[" + datePattern + "], " +
205                         "strict=[" + strict + "] " + e);
206             }
207         }
208 
209         return date;
210     }
211 
212     /**
213      * Checks if the value can safely be converted to a double primitive.
214      *
215      * @param value The value validation is being performed on.
216      * @return the converted Double value.
217      */
218     public static Double formatDouble(final String value) {
219         if (value == null) {
220             return null;
221         }
222 
223         try {
224             return Double.valueOf(value);
225         } catch (final NumberFormatException e) {
226             return null;
227         }
228 
229     }
230 
231     /**
232      * Checks if the value can safely be converted to a double primitive.
233      *
234      * @param value  The value validation is being performed on.
235      * @param locale The locale to use to parse the number (system default if
236      *               null)
237      * @return the converted Double value.
238      */
239     public static Double formatDouble(final String value, final Locale locale) {
240         Double result = null;
241 
242         if (value != null) {
243             NumberFormat formatter = null;
244             if (locale != null) {
245                 formatter = NumberFormat.getInstance(locale);
246             } else {
247                 formatter = NumberFormat.getInstance(Locale.getDefault());
248             }
249             final ParsePosition pos = new ParsePosition(0);
250             final Number num = formatter.parse(value, pos);
251 
252             // If there was no error      and we used the whole string
253             if (pos.getErrorIndex() == -1 && pos.getIndex() == value.length() &&
254                     num.doubleValue() >= Double.MAX_VALUE * -1 &&
255                     num.doubleValue() <= Double.MAX_VALUE) {
256                 result = Double.valueOf(num.doubleValue());
257             }
258         }
259 
260         return result;
261     }
262 
263     /**
264      * Checks if the value can safely be converted to a float primitive.
265      *
266      * @param value The value validation is being performed on.
267      * @return the converted Float value.
268      */
269     public static Float formatFloat(final String value) {
270         if (value == null) {
271             return null;
272         }
273 
274         try {
275             return Float.valueOf(value);
276         } catch (final NumberFormatException e) {
277             return null;
278         }
279 
280     }
281 
282     /**
283      * Checks if the value can safely be converted to a float primitive.
284      *
285      * @param value  The value validation is being performed on.
286      * @param locale The locale to use to parse the number (system default if
287      *               null)
288      * @return the converted Float value.
289      */
290     public static Float formatFloat(final String value, final Locale locale) {
291         Float result = null;
292 
293         if (value != null) {
294             NumberFormat formatter = null;
295             if (locale != null) {
296                 formatter = NumberFormat.getInstance(locale);
297             } else {
298                 formatter = NumberFormat.getInstance(Locale.getDefault());
299             }
300             final ParsePosition pos = new ParsePosition(0);
301             final Number num = formatter.parse(value, pos);
302 
303             // If there was no error      and we used the whole string
304             if (pos.getErrorIndex() == -1 && pos.getIndex() == value.length() &&
305                     num.doubleValue() >= Float.MAX_VALUE * -1 &&
306                     num.doubleValue() <= Float.MAX_VALUE) {
307                 result = Float.valueOf(num.floatValue());
308             }
309         }
310 
311         return result;
312     }
313 
314     /**
315      * Checks if the value can safely be converted to a int primitive.
316      *
317      * @param value The value validation is being performed on.
318      * @return the converted Integer value.
319      */
320     public static Integer formatInt(final String value) {
321         if (value == null) {
322             return null;
323         }
324 
325         try {
326             return Integer.valueOf(value);
327         } catch (final NumberFormatException e) {
328             return null;
329         }
330 
331     }
332 
333     /**
334      * Checks if the value can safely be converted to an int primitive.
335      *
336      * @param value  The value validation is being performed on.
337      * @param locale The locale to use to parse the number (system default if
338      *               null)
339      * @return the converted Integer value.
340      */
341     public static Integer formatInt(final String value, final Locale locale) {
342         Integer result = null;
343 
344         if (value != null) {
345             NumberFormat formatter = null;
346             if (locale != null) {
347                 formatter = NumberFormat.getNumberInstance(locale);
348             } else {
349                 formatter = NumberFormat.getNumberInstance(Locale.getDefault());
350             }
351             formatter.setParseIntegerOnly(true);
352             final ParsePosition pos = new ParsePosition(0);
353             final Number num = formatter.parse(value, pos);
354 
355             // If there was no error      and we used the whole string
356             if (pos.getErrorIndex() == -1 && pos.getIndex() == value.length() &&
357                     num.doubleValue() >= Integer.MIN_VALUE &&
358                     num.doubleValue() <= Integer.MAX_VALUE) {
359                 result = Integer.valueOf(num.intValue());
360             }
361         }
362 
363         return result;
364     }
365 
366     /**
367      * Checks if the value can safely be converted to a long primitive.
368      *
369      * @param value The value validation is being performed on.
370      * @return the converted Long value.
371      */
372     public static Long formatLong(final String value) {
373         if (value == null) {
374             return null;
375         }
376 
377         try {
378             return Long.valueOf(value);
379         } catch (final NumberFormatException e) {
380             return null;
381         }
382 
383     }
384 
385     /**
386      * Checks if the value can safely be converted to a long primitive.
387      *
388      * @param value  The value validation is being performed on.
389      * @param locale The locale to use to parse the number (system default if
390      *               null)
391      * @return the converted Long value.
392      */
393     public static Long formatLong(final String value, final Locale locale) {
394         Long result = null;
395 
396         if (value != null) {
397             NumberFormat formatter = null;
398             if (locale != null) {
399                 formatter = NumberFormat.getNumberInstance(locale);
400             } else {
401                 formatter = NumberFormat.getNumberInstance(Locale.getDefault());
402             }
403             formatter.setParseIntegerOnly(true);
404             final ParsePosition pos = new ParsePosition(0);
405             final Number num = formatter.parse(value, pos);
406 
407             // If there was no error      and we used the whole string
408             if (pos.getErrorIndex() == -1 && pos.getIndex() == value.length() &&
409                     num.doubleValue() >= Long.MIN_VALUE &&
410                     num.doubleValue() <= Long.MAX_VALUE) {
411                 result = Long.valueOf(num.longValue());
412             }
413         }
414 
415         return result;
416     }
417 
418     /**
419      * Checks if the value can safely be converted to a short primitive.
420      *
421      * @param value The value validation is being performed on.
422      * @return the converted Short value.
423      */
424     public static Short formatShort(final String value) {
425         if (value == null) {
426             return null;
427         }
428 
429         try {
430             return Short.valueOf(value);
431         } catch (final NumberFormatException e) {
432             return null;
433         }
434 
435     }
436 
437     /**
438      * Checks if the value can safely be converted to a short primitive.
439      *
440      * @param value  The value validation is being performed on.
441      * @param locale The locale to use to parse the number (system default if
442      *               null)
443      * @return the converted Short value.
444      */
445     public static Short formatShort(final String value, final Locale locale) {
446         Short result = null;
447 
448         if (value != null) {
449             NumberFormat formatter = null;
450             if (locale != null) {
451                 formatter = NumberFormat.getNumberInstance(locale);
452             } else {
453                 formatter = NumberFormat.getNumberInstance(Locale.getDefault());
454             }
455             formatter.setParseIntegerOnly(true);
456             final ParsePosition pos = new ParsePosition(0);
457             final Number num = formatter.parse(value, pos);
458 
459             // If there was no error      and we used the whole string
460             if (pos.getErrorIndex() == -1 && pos.getIndex() == value.length() &&
461                     num.doubleValue() >= Short.MIN_VALUE &&
462                     num.doubleValue() <= Short.MAX_VALUE) {
463                 result = Short.valueOf(num.shortValue());
464             }
465         }
466 
467         return result;
468     }
469 
470 }
471