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