PercentValidator.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.math.BigDecimal;
  19. import java.text.DecimalFormat;
  20. import java.text.Format;

  21. /**
  22.  * <p><b>Percentage Validation</b> and Conversion routines (<code>java.math.BigDecimal</code>).</p>
  23.  *
  24.  * <p>This is one implementation of a percent validator that has the following features:</p>
  25.  *    <ul>
  26.  *       <li>It is <i>lenient</i> about the presence of the <i>percent symbol</i></li>
  27.  *       <li>It converts the percent to a <code>java.math.BigDecimal</code></li>
  28.  *    </ul>
  29.  *
  30.  * <p>However any of the <i>number</i> validators can be used for <i>percent</i> validation.
  31.  *    For example, if you wanted a <i>percent</i> validator that converts to a
  32.  *    <code>java.lang.Float</code> then you can simply instantiate an
  33.  *    <code>FloatValidator</code> with the appropriate <i>format type</i>:</p>
  34.  *
  35.  *    <p><code>... = new FloatValidator(false, FloatValidator.PERCENT_FORMAT);</code></p>
  36.  *
  37.  * <p>Pick the appropriate validator, depending on the type (i.e Float, Double or BigDecimal)
  38.  *    you want the percent converted to. Please note, it makes no sense to use
  39.  *    one of the validators that doesn't handle fractions (i.e. byte, short, integer, long
  40.  *    and BigInteger) since percentages are converted to fractions (i.e <code>50%</code> is
  41.  *    converted to <code>0.5</code>).</p>
  42.  *
  43.  * @since 1.3.0
  44.  */
  45. public class PercentValidator extends BigDecimalValidator {

  46.     private static final long serialVersionUID = -3508241924961535772L;

  47.     private static final PercentValidator VALIDATOR = new PercentValidator();

  48.     /** DecimalFormat's percent (thousand multiplier) symbol */
  49.     private static final char PERCENT_SYMBOL = '%';

  50.     private static final BigDecimal POINT_ZERO_ONE = new BigDecimal("0.01");

  51.     /**
  52.      * Gets the singleton instance of this validator.
  53.      * @return A singleton instance of the PercentValidator.
  54.      */
  55.     public static BigDecimalValidator getInstance() {
  56.         return VALIDATOR;
  57.     }

  58.     /**
  59.      * Constructs a <i>strict</i> instance.
  60.      */
  61.     public PercentValidator() {
  62.         this(true);
  63.     }

  64.     /**
  65.      * Constructs an instance with the specified strict setting.
  66.      *
  67.      * @param strict {@code true} if strict
  68.      *        <code>Format</code> parsing should be used.
  69.      */
  70.     public PercentValidator(final boolean strict) {
  71.         super(strict, PERCENT_FORMAT, true);
  72.     }

  73.     /**
  74.      * <p>Parse the value with the specified <code>Format</code>.</p>
  75.      *
  76.      * <p>This implementation is lenient whether the currency symbol
  77.      *    is present or not. The default <code>NumberFormat</code>
  78.      *    behavior is for the parsing to "fail" if the currency
  79.      *    symbol is missing. This method re-parses with a format
  80.      *    without the currency symbol if it fails initially.</p>
  81.      *
  82.      * @param value The value to be parsed.
  83.      * @param formatter The Format to parse the value with.
  84.      * @return The parsed value if valid or {@code null} if invalid.
  85.      */
  86.     @Override
  87.     protected Object parse(final String value, final Format formatter) {

  88.         // Initial parse of the value
  89.         BigDecimal parsedValue = (BigDecimal) super.parse(value, formatter);
  90.         if (parsedValue != null || !(formatter instanceof DecimalFormat)) {
  91.             return parsedValue;
  92.         }

  93.         // Re-parse using a pattern without the percent symbol
  94.         final DecimalFormat decimalFormat = (DecimalFormat) formatter;
  95.         final String pattern = decimalFormat.toPattern();
  96.         if (pattern.indexOf(PERCENT_SYMBOL) >= 0) {
  97.             final StringBuilder buffer = new StringBuilder(pattern.length());
  98.             for (int i = 0; i < pattern.length(); i++) {
  99.                 if (pattern.charAt(i) != PERCENT_SYMBOL) {
  100.                     buffer.append(pattern.charAt(i));
  101.                 }
  102.             }
  103.             decimalFormat.applyPattern(buffer.toString());
  104.             parsedValue = (BigDecimal) super.parse(value, decimalFormat);

  105.             // If parsed OK, divide by 100 to get percent
  106.             if (parsedValue != null) {
  107.                 parsedValue = parsedValue.multiply(POINT_ZERO_ONE);
  108.             }

  109.         }
  110.         return parsedValue;
  111.     }
  112. }