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 */
035public class GenericTypeValidator implements Serializable {
036
037    private static final long serialVersionUID = 5487162314134261703L;
038
039    private static final Log LOG = LogFactory.getLog(GenericTypeValidator.class);
040
041    /**
042     * Checks if the value can safely be converted to a byte primitive.
043     *
044     * @param value The value validation is being performed on.
045     * @return the converted Byte value.
046     */
047    public static Byte formatByte(final String value) {
048        if (value == null) {
049            return null;
050        }
051
052        try {
053            return Byte.valueOf(value);
054        } catch (final NumberFormatException e) {
055            return null;
056        }
057
058    }
059
060    /**
061     * Checks if the value can safely be converted to a byte primitive.
062     *
063     * @param value  The value validation is being performed on.
064     * @param locale The locale to use to parse the number (system default if
065     *               null)
066     * @return the converted Byte value.
067     */
068    public static Byte formatByte(final String value, final Locale locale) {
069        Byte result = null;
070
071        if (value != null) {
072            NumberFormat formatter = null;
073            if (locale != null) {
074                formatter = NumberFormat.getNumberInstance(locale);
075            } else {
076                formatter = NumberFormat.getNumberInstance(Locale.getDefault());
077            }
078            formatter.setParseIntegerOnly(true);
079            final ParsePosition pos = new ParsePosition(0);
080            final Number num = formatter.parse(value, pos);
081
082            // If there was no error      and we used the whole string
083            if (pos.getErrorIndex() == -1 && pos.getIndex() == value.length() &&
084                    num.doubleValue() >= Byte.MIN_VALUE &&
085                    num.doubleValue() <= Byte.MAX_VALUE) {
086                result = Byte.valueOf(num.byteValue());
087            }
088        }
089
090        return result;
091    }
092
093    /**
094     * Checks if the field is a valid credit card number.
095     *
096     * <p>Reference Sean M. Burke's <a href="http://www.ling.nwu.edu/~sburke/pub/luhn_lib.pl">
097     * script</a>.</p>
098     *
099     * @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 {@code 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 {@code 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