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