    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.routines;
    018
    019import java.math.BigDecimal;
    020import java.math.BigInteger;
    021import java.text.DecimalFormat;
    022import java.text.DecimalFormatSymbols;
    023import java.text.Format;
    024import java.text.NumberFormat;
    025import java.util.Locale;
    026
    027import org.apache.commons.validator.GenericValidator;
    028
    029/**
    030 * Abstract class for Number Validation.
    031 * <p>
    032 * This is a <em>base</em> class for building Number Validators using format parsing.
    033 * </p>
    034 *
    035 * @since 1.3.0
    036 */
    037public abstract class AbstractNumberValidator extends AbstractFormatValidator {
    038
    039    private static final long serialVersionUID = -3088817875906765463L;
    040
    041    /**
    042     * Standard {@code NumberFormat} type.
    043     */
    044    public static final int STANDARD_FORMAT = 0;
    045
    046    /**
    047     * Currency {@code NumberFormat} type.
    048     */
    049    public static final int CURRENCY_FORMAT = 1;
    050
    051    /**
    052     * Percent {@code NumberFormat} type.
    053     */
    054    public static final int PERCENT_FORMAT = 2;
    055
    056    /**
    057     * Compares two values as BigDecimals.
    058     *
    059     * @param value1 {@code BigDecimal} to compare.
    060     * @param value2 {@code BigDecimal} to which {@code value1} is to be compared.
    061     * @return -1, 0, or 1 as this {@code BigDecimal} is numerically less than, equal to, or greater than {@code val}.
    062     */
    063    static int compareTo(final Number value1, final Number value2) {
    064        return toBigDecimal(value1).compareTo(toBigDecimal(value2));
    065    }
    066
    067    /**
    068     * Tests if the given value is finite.
    069     *
    070     * @param value The value to test.
    071     * @return {@code true} if the value is finite, {@code false} otherwise.
    072     */
    073    static boolean isFinite(final Number value) {
    074        if (value instanceof Double) {
    075            return Double.isFinite((Double) value);
    076        }
    077        if (value instanceof Float) {
    078            return Float.isFinite((Float) value);
    079        }
    080        return true;
    081    }
    082
    083    static Format setParseBigDecimal(final Format format) {
    084        if (format instanceof DecimalFormat) {
    085            ((DecimalFormat) format).setParseBigDecimal(true);
    086        }
    087        return format;
    088    }
    089
    090    static BigDecimal toBigDecimal(final Object value) {
    091        if (value instanceof Long || value instanceof Integer || value instanceof Short || value instanceof Byte) {
    092            return BigDecimal.valueOf(((Number) value).longValue());
    093        }
    094        if (value instanceof Double || value instanceof Float) {
    095            return BigDecimal.valueOf(((Number) value).doubleValue());
    096        }
    097        if (value instanceof BigDecimal) {
    098            return (BigDecimal) value;
    099        }
    100        if (value instanceof BigInteger) {
    101            return new BigDecimal((BigInteger) value);
    102        }
    103        // No more shortcuts, convert from a string.
    104        return new BigDecimal(value.toString());
    105    }
    106
    107    static BigInteger toBigInteger(final Object value) {
    108        if (value instanceof Long || value instanceof Integer || value instanceof Short || value instanceof Byte) {
    109            return BigInteger.valueOf(((Number) value).longValue());
    110        }
    111        return toBigDecimal(value).toBigInteger();
    112    }
    113
    114    /**
    115     * {@code true} if fractions are allowed or {@code false} if integers only.
    116     */
    117    private final boolean allowFractions;
    118
    119    /**
    120     * The {@code NumberFormat} type to create for validation, default is STANDARD_FORMAT.
    121     */
    122    private final int formatType;
    123
    124    /**
    125     * Constructs an instance with specified <em>strict</em> and <em>decimal</em> parameters.
    126     *
    127     * @param strict         {@code true} if strict {@code Format} parsing should be used.
    128     * @param formatType     The {@code NumberFormat} type to create for validation, default is STANDARD_FORMAT.
    129     * @param allowFractions {@code true} if fractions are allowed or {@code false} if integers only.
    130     */
    131    public AbstractNumberValidator(final boolean strict, final int formatType, final boolean allowFractions) {
    132        super(strict);
    133        this.allowFractions = allowFractions;
    134        this.formatType = formatType;
    135    }
    136
    137    /**
    138     * Returns the <em>multiplier</em> of the {@code NumberFormat}.
    139     *
    140     * @param format The {@code NumberFormat} to determine the multiplier of.
    141     * @return The multiplying factor for the format.
    142     */
    143    protected int determineScale(final NumberFormat format) {
    144        if (!isStrict()) {
    145            return -1;
    146        }
    147        if (!isAllowFractions() || format.isParseIntegerOnly()) {
    148            return 0;
    149        }
    150        final int minimumFraction = format.getMinimumFractionDigits();
    151        final int maximumFraction = format.getMaximumFractionDigits();
    152        if (minimumFraction != maximumFraction) {
    153            return -1;
    154        }
    155        int scale = minimumFraction;
    156        if (format instanceof DecimalFormat) {
    157            final int multiplier = ((DecimalFormat) format).getMultiplier();
    158            if (multiplier == 100) { // CHECKSTYLE IGNORE MagicNumber
    159                scale += 2; // CHECKSTYLE IGNORE MagicNumber
    160            } else if (multiplier == 1000) { // CHECKSTYLE IGNORE MagicNumber
    161                scale += 3; // CHECKSTYLE IGNORE MagicNumber
    162            }
    163        } else if (formatType == PERCENT_FORMAT) {
    164            scale += 2; // CHECKSTYLE IGNORE MagicNumber
    165        }
    166        return scale;
    167    }
    168
    169    /**
    170     * Gets the {@code NumberFormat} for the specified Locale.
    171     *
    172     * @param locale The locale a {@code NumberFormat} is required for, system default if null.
    173     * @return The {@code NumberFormat} to created.
    174     */
    175    protected Format getFormat(final Locale locale) {
    176        final NumberFormat formatter;
    177        switch (formatType) {
    178        case CURRENCY_FORMAT:
    179            if (locale == null) {
    180                formatter = NumberFormat.getCurrencyInstance();
    181            } else {
    182                formatter = NumberFormat.getCurrencyInstance(locale);
    183            }
    184            break;
    185        case PERCENT_FORMAT:
    186            if (locale == null) {
    187                formatter = NumberFormat.getPercentInstance();
    188            } else {
    189                formatter = NumberFormat.getPercentInstance(locale);
    190            }
    191            break;
    192        default:
    193            if (locale == null) {
    194                formatter = NumberFormat.getInstance();
    195            } else {
    196                formatter = NumberFormat.getInstance(locale);
    197            }
    198            if (!isAllowFractions()) {
    199                formatter.setParseIntegerOnly(true);
    200            }
    201            break;
    202        }
    203        return formatter;
    204    }
    205
    206    /**
    207     * Gets a {@code NumberFormat} for the specified <em>pattern</em> and/or {@link Locale}.
    208     *
    209     * @param pattern The pattern used to validate the value against or {@code null} to use the default for the {@link Locale}.
    210     * @param locale  The locale to use for the currency format, system default if null.
    211     * @return The {@code NumberFormat} to created.
    212     */
    213    @Override
    214    protected Format getFormat(final String pattern, final Locale locale) {
    215        final NumberFormat formatter;
    216        if (GenericValidator.isBlankOrNull(pattern)) {
    217            formatter = (NumberFormat) getFormat(locale);
    218        } else if (locale == null) {
    219            formatter = new DecimalFormat(pattern);
    220        } else {
    221            formatter = new DecimalFormat(pattern, new DecimalFormatSymbols(locale));
    222        }
    223        if (!isAllowFractions()) {
    224            formatter.setParseIntegerOnly(true);
    225        }
    226        return formatter;
    227    }
    228
    229    /**
    230     * Gets the type of {@code NumberFormat} created by this validator instance.
    231     *
    232     * @return The format type created.
    233     */
    234    public int getFormatType() {
    235        return formatType;
    236    }
    237
    238    /**
    239     * Tests whether the number being validated is a decimal or integer.
    240     *
    241     * @return {@code true} if decimals are allowed or {@code false} if the number is an integer.
    242     */
    243    public boolean isAllowFractions() {
    244        return allowFractions;
    245    }
    246
    247    /**
    248     * Tests if the value is within a specified range.
    249     *
    250     * @param value The value validation is being performed on.
    251     * @param min   The minimum value of the range.
    252     * @param max   The maximum value of the range.
    253     * @return {@code true} if the value is within the specified range.
    254     */
    255    public boolean isInRange(final Number value, final Number min, final Number max) {
    256        return minValue(value, min) && maxValue(value, max);
    257    }
    258
    259    /**
    260     * Tests validity using the specified {@link Locale}.
    261     *
    262     * @param value   The value validation is being performed on.
    263     * @param pattern The pattern used to validate the value against, or the default for the {@link Locale} if {@code null}.
    264     * @param locale  The locale to use for the date format, system default if null.
    265     * @return {@code true} if the value is valid.
    266     */
    267    @Override
    268    public boolean isValid(final String value, final String pattern, final Locale locale) {
    269        return parse(value, pattern, locale) != null;
    270    }
    271
    272    /**
    273     * Tests if the value is less than or equal to a maximum.
    274     *
    275     * <p>
    276     * For an integer validator the bound is compared exactly rather than narrowed to a {@code long}, so a {@code BigInteger} or {@code BigDecimal} bound outside
    277     * the long range keeps its magnitude and a fractional bound keeps its fractional part. A non-finite {@link Double} or {@link Float} operand keeps the
    278     * {@code doubleValue()} comparison, since {@code BigDecimal} cannot represent {@code NaN} or an infinity.
    279     * </p>
    280     *
    281     * @param value The value validation is being performed on.
    282     * @param max   The maximum value.
    283     * @return {@code true} if the value is less than or equal to the maximum.
    284     */
    285    public boolean maxValue(final Number value, final Number max) {
    286        if (isAllowFractions()) {
    287            return value.doubleValue() <= max.doubleValue();
    288        }
    289        return isFinite(value) && isFinite(max) ? compareTo(value, max) <= 0 : value.doubleValue() <= max.doubleValue();
    290    }
    291
    292    /**
    293     * Tests if the value is greater than or equal to a minimum.
    294     *
    295     * <p>
    296     * For an integer validator the bound is compared exactly rather than narrowed to a {@code long}, so a {@code BigInteger} or {@code BigDecimal} bound outside
    297     * the long range keeps its magnitude and a fractional bound keeps its fractional part. A non-finite {@link Double} or {@link Float} operand keeps the
    298     * {@code doubleValue()} comparison, since {@code BigDecimal} cannot represent {@code NaN} or an infinity.
    299     * </p>
    300     *
    301     * @param value The value validation is being performed on.
    302     * @param min   The minimum value.
    303     * @return {@code true} if the value is greater than or equal to the minimum.
    304     */
    305    public boolean minValue(final Number value, final Number min) {
    306        if (isAllowFractions()) {
    307            return value.doubleValue() >= min.doubleValue();
    308        }
    309        return isFinite(value) && isFinite(min) ? compareTo(value, min) >= 0 : value.doubleValue() >= min.doubleValue();
    310    }
    311
    312    /**
    313     * Parses the value using the specified pattern.
    314     *
    315     * @param value   The value validation is being performed on.
    316     * @param pattern The pattern used to validate the value against, or the default for the {@link Locale} if {@code null}.
    317     * @param locale  The locale to use for the date format, system default if null.
    318     * @return The parsed value if valid or {@code null} if invalid.
    319     */
    320    protected Object parse(String value, final String pattern, final Locale locale) {
    321        value = value == null ? null : value.trim();
    322        final String value1 = value;
    323        if (GenericValidator.isBlankOrNull(value1)) {
    324            return null;
    325        }
    326        return parse(value, getFormat(pattern, locale));
    327    }
    328
    329    /**
    330     * Processes the parsed value, performing any further validation and type conversion required.
    331     *
    332     * @param value     The parsed object created.
    333     * @param formatter The Format used to parse the value with.
    334     * @return The parsed value converted to the appropriate type if valid or {@code null} if invalid.
    335     */
    336    @Override
    337    protected abstract Object processParsedValue(Object value, Format formatter);
    338}