    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