AbstractNumberValidator.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.apache.commons.validator.routines;

  18. import java.text.DecimalFormat;
  19. import java.text.DecimalFormatSymbols;
  20. import java.text.Format;
  21. import java.text.NumberFormat;
  22. import java.util.Locale;

  23. import org.apache.commons.validator.GenericValidator;

  24. /**
  25.  * <p>Abstract class for Number Validation.</p>
  26.  *
  27.  * <p>This is a <i>base</i> class for building Number
  28.  *    Validators using format parsing.</p>
  29.  *
  30.  * @since 1.3.0
  31.  */
  32. public abstract class AbstractNumberValidator extends AbstractFormatValidator {

  33.     private static final long serialVersionUID = -3088817875906765463L;

  34.     /** Standard <code>NumberFormat</code> type */
  35.     public static final int STANDARD_FORMAT = 0;

  36.     /** Currency <code>NumberFormat</code> type */
  37.     public static final int CURRENCY_FORMAT = 1;

  38.     /** Percent <code>NumberFormat</code> type */
  39.     public static final int PERCENT_FORMAT = 2;

  40.     /**
  41.      * {@code true} if fractions are allowed or {@code false} if integers only.
  42.      */
  43.     private final boolean allowFractions;

  44.     /**
  45.      * The <code>NumberFormat</code> type to create for validation, default is STANDARD_FORMAT.
  46.      */
  47.     private final int formatType;

  48.     /**
  49.      * Constructs an instance with specified <i>strict</i>
  50.      * and <i>decimal</i> parameters.
  51.      *
  52.      * @param strict {@code true} if strict
  53.      *        <code>Format</code> parsing should be used.
  54.      * @param formatType The <code>NumberFormat</code> type to
  55.      *        create for validation, default is STANDARD_FORMAT.
  56.      * @param allowFractions {@code true} if fractions are
  57.      *        allowed or {@code false} if integers only.
  58.      */
  59.     public AbstractNumberValidator(final boolean strict, final int formatType, final boolean allowFractions) {
  60.         super(strict);
  61.         this.allowFractions = allowFractions;
  62.         this.formatType = formatType;
  63.     }

  64.     /**
  65.      * <p>Returns the <i>multiplier</i> of the <code>NumberFormat</code>.</p>
  66.      *
  67.      * @param format The <code>NumberFormat</code> to determine the
  68.      *        multiplier of.
  69.      * @return The multiplying factor for the format..
  70.      */
  71.     protected int determineScale(final NumberFormat format) {
  72.         if (!isStrict()) {
  73.             return -1;
  74.         }
  75.         if (!isAllowFractions() || format.isParseIntegerOnly()) {
  76.             return 0;
  77.         }
  78.         final int minimumFraction = format.getMinimumFractionDigits();
  79.         final int maximumFraction = format.getMaximumFractionDigits();
  80.         if (minimumFraction != maximumFraction) {
  81.             return -1;
  82.         }
  83.         int scale = minimumFraction;
  84.         if (format instanceof DecimalFormat) {
  85.             final int multiplier = ((DecimalFormat) format).getMultiplier();
  86.             if (multiplier == 100) { // CHECKSTYLE IGNORE MagicNumber
  87.                 scale += 2; // CHECKSTYLE IGNORE MagicNumber
  88.             } else if (multiplier == 1000) { // CHECKSTYLE IGNORE MagicNumber
  89.                 scale += 3; // CHECKSTYLE IGNORE MagicNumber
  90.             }
  91.         } else if (formatType == PERCENT_FORMAT) {
  92.             scale += 2; // CHECKSTYLE IGNORE MagicNumber
  93.         }
  94.         return scale;
  95.     }

  96.     /**
  97.      * <p>Returns a <code>NumberFormat</code> for the specified Locale.</p>
  98.      *
  99.      * @param locale The locale a <code>NumberFormat</code> is required for,
  100.      *   system default if null.
  101.      * @return The <code>NumberFormat</code> to created.
  102.      */
  103.     protected Format getFormat(final Locale locale) {
  104.         NumberFormat formatter;
  105.         switch (formatType) {
  106.         case CURRENCY_FORMAT:
  107.             if (locale == null) {
  108.                 formatter = NumberFormat.getCurrencyInstance();
  109.             } else {
  110.                 formatter = NumberFormat.getCurrencyInstance(locale);
  111.             }
  112.             break;
  113.         case PERCENT_FORMAT:
  114.             if (locale == null) {
  115.                 formatter = NumberFormat.getPercentInstance();
  116.             } else {
  117.                 formatter = NumberFormat.getPercentInstance(locale);
  118.             }
  119.             break;
  120.         default:
  121.             if (locale == null) {
  122.                 formatter = NumberFormat.getInstance();
  123.             } else {
  124.                 formatter = NumberFormat.getInstance(locale);
  125.             }
  126.             if (!isAllowFractions()) {
  127.                 formatter.setParseIntegerOnly(true);
  128.             }
  129.             break;
  130.         }
  131.         return formatter;
  132.     }

  133.     /**
  134.      * <p>Returns a <code>NumberFormat</code> for the specified <i>pattern</i>
  135.      *    and/or <code>Locale</code>.</p>
  136.      *
  137.      * @param pattern The pattern used to validate the value against or
  138.      *        {@code null} to use the default for the <code>Locale</code>.
  139.      * @param locale The locale to use for the currency format, system default if null.
  140.      * @return The <code>NumberFormat</code> to created.
  141.      */
  142.     @Override
  143.     protected Format getFormat(final String pattern, final Locale locale) {

  144.         NumberFormat formatter;
  145.         final boolean usePattern = !GenericValidator.isBlankOrNull(pattern);
  146.         if (!usePattern) {
  147.             formatter = (NumberFormat) getFormat(locale);
  148.         } else if (locale == null) {
  149.             formatter = new DecimalFormat(pattern);
  150.         } else {
  151.             final DecimalFormatSymbols symbols = new DecimalFormatSymbols(locale);
  152.             formatter = new DecimalFormat(pattern, symbols);
  153.         }

  154.         if (!isAllowFractions()) {
  155.             formatter.setParseIntegerOnly(true);
  156.         }
  157.         return formatter;
  158.     }

  159.     /**
  160.      * <p>Indicates the type of <code>NumberFormat</code> created
  161.      *    by this validator instance.</p>
  162.      *
  163.      * @return the format type created.
  164.      */
  165.     public int getFormatType() {
  166.         return formatType;
  167.     }

  168.     /**
  169.      * <p>Indicates whether the number being validated is
  170.      *    a decimal or integer.</p>
  171.      *
  172.      * @return {@code true} if decimals are allowed
  173.      *       or {@code false} if the number is an integer.
  174.      */
  175.     public boolean isAllowFractions() {
  176.         return allowFractions;
  177.     }

  178.     /**
  179.      * Check if the value is within a specified range.
  180.      *
  181.      * @param value The value validation is being performed on.
  182.      * @param min The minimum value of the range.
  183.      * @param max The maximum value of the range.
  184.      * @return {@code true} if the value is within the
  185.      *         specified range.
  186.      */
  187.     public boolean isInRange(final Number value, final Number min, final Number max) {
  188.         return minValue(value, min) && maxValue(value, max);
  189.     }

  190.     /**
  191.      * <p>Validate using the specified <code>Locale</code>.</p>
  192.      *
  193.      * @param value The value validation is being performed on.
  194.      * @param pattern The pattern used to validate the value against, or the
  195.      *        default for the <code>Locale</code> if {@code null}.
  196.      * @param locale The locale to use for the date format, system default if null.
  197.      * @return {@code true} if the value is valid.
  198.      */
  199.     @Override
  200.     public boolean isValid(final String value, final String pattern, final Locale locale) {
  201.         return parse(value, pattern, locale) != null;
  202.     }

  203.     /**
  204.      * Check if the value is less than or equal to a maximum.
  205.      *
  206.      * @param value The value validation is being performed on.
  207.      * @param max The maximum value.
  208.      * @return {@code true} if the value is less than
  209.      *         or equal to the maximum.
  210.      */
  211.     public boolean maxValue(final Number value, final Number max) {
  212.         if (isAllowFractions()) {
  213.             return value.doubleValue() <= max.doubleValue();
  214.         }
  215.         return value.longValue() <= max.longValue();
  216.     }

  217.     /**
  218.      * Check if the value is greater than or equal to a minimum.
  219.      *
  220.      * @param value The value validation is being performed on.
  221.      * @param min The minimum value.
  222.      * @return {@code true} if the value is greater than
  223.      *         or equal to the minimum.
  224.      */
  225.     public boolean minValue(final Number value, final Number min) {
  226.         if (isAllowFractions()) {
  227.             return value.doubleValue() >= min.doubleValue();
  228.         }
  229.         return value.longValue() >= min.longValue();
  230.     }

  231.     /**
  232.      * <p>Parse the value using the specified pattern.</p>
  233.      *
  234.      * @param value The value validation is being performed on.
  235.      * @param pattern The pattern used to validate the value against, or the
  236.      *        default for the <code>Locale</code> if {@code null}.
  237.      * @param locale The locale to use for the date format, system default if null.
  238.      * @return The parsed value if valid or {@code null} if invalid.
  239.      */
  240.     protected Object parse(String value, final String pattern, final Locale locale) {
  241.         value = value == null ? null : value.trim();
  242.         final String value1 = value;
  243.         if (GenericValidator.isBlankOrNull(value1)) {
  244.             return null;
  245.         }
  246.         final Format formatter = getFormat(pattern, locale);
  247.         return parse(value, formatter);

  248.     }

  249.     /**
  250.      * <p>Process the parsed value, performing any further validation
  251.      *    and type conversion required.</p>
  252.      *
  253.      * @param value The parsed object created.
  254.      * @param formatter The Format used to parse the value with.
  255.      * @return The parsed value converted to the appropriate type
  256.      *         if valid or {@code null} if invalid.
  257.      */
  258.     @Override
  259.     protected abstract Object processParsedValue(Object value, Format formatter);
  260. }