    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 *      https://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 {@link 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 {@link 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 an 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     * Constructs a new instance.
    472     *
    473     * @deprecated Will be private in the next major version.
    474     */
    475    @Deprecated
    476    public GenericTypeValidator() {
    477        // empty
    478    }
    479
    480}
    481