    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.routines;
    018
    019import java.text.DecimalFormat;
    020import java.text.DecimalFormatSymbols;
    021import java.text.Format;
    022import java.text.NumberFormat;
    023import java.util.Locale;
    024
    025import org.apache.commons.validator.GenericValidator;
    026
    027/**
    028 * <p>Abstract class for Number Validation.</p>
    029 *
    030 * <p>This is a <i>base</i> class for building Number
    031 *    Validators using format parsing.</p>
    032 *
    033 * @since 1.3.0
    034 */
    035public abstract class AbstractNumberValidator extends AbstractFormatValidator {
    036
    037    private static final long serialVersionUID = -3088817875906765463L;
    038
    039    /** Standard <code>NumberFormat</code> type */
    040    public static final int STANDARD_FORMAT = 0;
    041
    042    /** Currency <code>NumberFormat</code> type */
    043    public static final int CURRENCY_FORMAT = 1;
    044
    045    /** Percent <code>NumberFormat</code> type */
    046    public static final int PERCENT_FORMAT = 2;
    047
    048    /**
    049     * {@code true} if fractions are allowed or {@code false} if integers only.
    050     */
    051    private final boolean allowFractions;
    052
    053    /**
    054     * The <code>NumberFormat</code> type to create for validation, default is STANDARD_FORMAT.
    055     */
    056    private final int formatType;
    057
    058    /**
    059     * Constructs an instance with specified <i>strict</i>
    060     * and <i>decimal</i> parameters.
    061     *
    062     * @param strict {@code true} if strict
    063     *        <code>Format</code> parsing should be used.
    064     * @param formatType The <code>NumberFormat</code> type to
    065     *        create for validation, default is STANDARD_FORMAT.
    066     * @param allowFractions {@code true} if fractions are
    067     *        allowed or {@code false} if integers only.
    068     */
    069    public AbstractNumberValidator(final boolean strict, final int formatType, final boolean allowFractions) {
    070        super(strict);
    071        this.allowFractions = allowFractions;
    072        this.formatType = formatType;
    073    }
    074
    075    /**
    076     * <p>Returns the <i>multiplier</i> of the <code>NumberFormat</code>.</p>
    077     *
    078     * @param format The <code>NumberFormat</code> to determine the
    079     *        multiplier of.
    080     * @return The multiplying factor for the format..
    081     */
    082    protected int determineScale(final NumberFormat format) {
    083        if (!isStrict()) {
    084            return -1;
    085        }
    086        if (!isAllowFractions() || format.isParseIntegerOnly()) {
    087            return 0;
    088        }
    089        final int minimumFraction = format.getMinimumFractionDigits();
    090        final int maximumFraction = format.getMaximumFractionDigits();
    091        if (minimumFraction != maximumFraction) {
    092            return -1;
    093        }
    094        int scale = minimumFraction;
    095        if (format instanceof DecimalFormat) {
    096            final int multiplier = ((DecimalFormat) format).getMultiplier();
    097            if (multiplier == 100) { // CHECKSTYLE IGNORE MagicNumber
    098                scale += 2; // CHECKSTYLE IGNORE MagicNumber
    099            } else if (multiplier == 1000) { // CHECKSTYLE IGNORE MagicNumber
    100                scale += 3; // CHECKSTYLE IGNORE MagicNumber
    101            }
    102        } else if (formatType == PERCENT_FORMAT) {
    103            scale += 2; // CHECKSTYLE IGNORE MagicNumber
    104        }
    105        return scale;
    106    }
    107
    108    /**
    109     * <p>Returns a <code>NumberFormat</code> for the specified Locale.</p>
    110     *
    111     * @param locale The locale a <code>NumberFormat</code> is required for,
    112     *   system default if null.
    113     * @return The <code>NumberFormat</code> to created.
    114     */
    115    protected Format getFormat(final Locale locale) {
    116        NumberFormat formatter;
    117        switch (formatType) {
    118        case CURRENCY_FORMAT:
    119            if (locale == null) {
    120                formatter = NumberFormat.getCurrencyInstance();
    121            } else {
    122                formatter = NumberFormat.getCurrencyInstance(locale);
    123            }
    124            break;
    125        case PERCENT_FORMAT:
    126            if (locale == null) {
    127                formatter = NumberFormat.getPercentInstance();
    128            } else {
    129                formatter = NumberFormat.getPercentInstance(locale);
    130            }
    131            break;
    132        default:
    133            if (locale == null) {
    134                formatter = NumberFormat.getInstance();
    135            } else {
    136                formatter = NumberFormat.getInstance(locale);
    137            }
    138            if (!isAllowFractions()) {
    139                formatter.setParseIntegerOnly(true);
    140            }
    141            break;
    142        }
    143        return formatter;
    144    }
    145
    146    /**
    147     * <p>Returns a <code>NumberFormat</code> for the specified <i>pattern</i>
    148     *    and/or <code>Locale</code>.</p>
    149     *
    150     * @param pattern The pattern used to validate the value against or
    151     *        {@code null} to use the default for the <code>Locale</code>.
    152     * @param locale The locale to use for the currency format, system default if null.
    153     * @return The <code>NumberFormat</code> to created.
    154     */
    155    @Override
    156    protected Format getFormat(final String pattern, final Locale locale) {
    157
    158        NumberFormat formatter;
    159        final boolean usePattern = !GenericValidator.isBlankOrNull(pattern);
    160        if (!usePattern) {
    161            formatter = (NumberFormat) getFormat(locale);
    162        } else if (locale == null) {
    163            formatter = new DecimalFormat(pattern);
    164        } else {
    165            final DecimalFormatSymbols symbols = new DecimalFormatSymbols(locale);
    166            formatter = new DecimalFormat(pattern, symbols);
    167        }
    168
    169        if (!isAllowFractions()) {
    170            formatter.setParseIntegerOnly(true);
    171        }
    172        return formatter;
    173    }
    174
    175    /**
    176     * <p>Indicates the type of <code>NumberFormat</code> created
    177     *    by this validator instance.</p>
    178     *
    179     * @return the format type created.
    180     */
    181    public int getFormatType() {
    182        return formatType;
    183    }
    184
    185    /**
    186     * <p>Indicates whether the number being validated is
    187     *    a decimal or integer.</p>
    188     *
    189     * @return {@code true} if decimals are allowed
    190     *       or {@code false} if the number is an integer.
    191     */
    192    public boolean isAllowFractions() {
    193        return allowFractions;
    194    }
    195
    196    /**
    197     * Check if the value is within a specified range.
    198     *
    199     * @param value The value validation is being performed on.
    200     * @param min The minimum value of the range.
    201     * @param max The maximum value of the range.
    202     * @return {@code true} if the value is within the
    203     *         specified range.
    204     */
    205    public boolean isInRange(final Number value, final Number min, final Number max) {
    206        return minValue(value, min) && maxValue(value, max);
    207    }
    208
    209    /**
    210     * <p>Validate using the specified <code>Locale</code>.</p>
    211     *
    212     * @param value The value validation is being performed on.
    213     * @param pattern The pattern used to validate the value against, or the
    214     *        default for the <code>Locale</code> if {@code null}.
    215     * @param locale The locale to use for the date format, system default if null.
    216     * @return {@code true} if the value is valid.
    217     */
    218    @Override
    219    public boolean isValid(final String value, final String pattern, final Locale locale) {
    220        return parse(value, pattern, locale) != null;
    221    }
    222
    223    /**
    224     * Check if the value is less than or equal to a maximum.
    225     *
    226     * @param value The value validation is being performed on.
    227     * @param max The maximum value.
    228     * @return {@code true} if the value is less than
    229     *         or equal to the maximum.
    230     */
    231    public boolean maxValue(final Number value, final Number max) {
    232        if (isAllowFractions()) {
    233            return value.doubleValue() <= max.doubleValue();
    234        }
    235        return value.longValue() <= max.longValue();
    236    }
    237
    238    /**
    239     * Check if the value is greater than or equal to a minimum.
    240     *
    241     * @param value The value validation is being performed on.
    242     * @param min The minimum value.
    243     * @return {@code true} if the value is greater than
    244     *         or equal to the minimum.
    245     */
    246    public boolean minValue(final Number value, final Number min) {
    247        if (isAllowFractions()) {
    248            return value.doubleValue() >= min.doubleValue();
    249        }
    250        return value.longValue() >= min.longValue();
    251    }
    252
    253    /**
    254     * <p>Parse the value using the specified pattern.</p>
    255     *
    256     * @param value The value validation is being performed on.
    257     * @param pattern The pattern used to validate the value against, or the
    258     *        default for the <code>Locale</code> if {@code null}.
    259     * @param locale The locale to use for the date format, system default if null.
    260     * @return The parsed value if valid or {@code null} if invalid.
    261     */
    262    protected Object parse(String value, final String pattern, final Locale locale) {
    263        value = value == null ? null : value.trim();
    264        final String value1 = value;
    265        if (GenericValidator.isBlankOrNull(value1)) {
    266            return null;
    267        }
    268        final Format formatter = getFormat(pattern, locale);
    269        return parse(value, formatter);
    270
    271    }
    272
    273    /**
    274     * <p>Process the parsed value, performing any further validation
    275     *    and type conversion required.</p>
    276     *
    277     * @param value The parsed object created.
    278     * @param formatter The Format used to parse the value with.
    279     * @return The parsed value converted to the appropriate type
    280     *         if valid or {@code null} if invalid.
    281     */
    282    @Override
    283    protected abstract Object processParsedValue(Object value, Format formatter);
    284}