CurrencyValidator.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.Format;

  20. /**
  21.  * <p><b>Currency Validation</b> and Conversion routines (<code>java.math.BigDecimal</code>).</p>
  22.  *
  23.  * <p>This is one implementation of a currency validator that has the following features:</p>
  24.  *    <ul>
  25.  *       <li>It is <i>lenient</i> about the presence of the <i>currency symbol</i></li>
  26.  *       <li>It converts the currency to a <code>java.math.BigDecimal</code></li>
  27.  *    </ul>
  28.  *
  29.  * <p>However any of the <i>number</i> validators can be used for <i>currency</i> validation.
  30.  *    For example, if you wanted a <i>currency</i> validator that converts to a
  31.  *    <code>java.lang.Integer</code> then you can simply instantiate an
  32.  *    <code>IntegerValidator</code> with the appropriate <i>format type</i>:</p>
  33.  *
  34.  *    <p><code>... = new IntegerValidator(false, IntegerValidator.CURRENCY_FORMAT);</code></p>
  35.  *
  36.  * <p>Pick the appropriate validator, depending on the type (e.g Float, Double, Integer, Long etc)
  37.  *    you want the currency converted to. One thing to note - only the CurrencyValidator
  38.  *    implements <i>lenient</i> behavior regarding the currency symbol.</p>
  39.  *
  40.  * @since 1.3.0
  41.  */
  42. public class CurrencyValidator extends BigDecimalValidator {

  43.     private static final long serialVersionUID = -4201640771171486514L;

  44.     private static final CurrencyValidator VALIDATOR = new CurrencyValidator();

  45.     /** DecimalFormat's currency symbol */
  46.     private static final char CURRENCY_SYMBOL = '\u00A4';

  47.     /**
  48.      * Gets the singleton instance of this validator.
  49.      * @return A singleton instance of the CurrencyValidator.
  50.      */
  51.     public static BigDecimalValidator getInstance() {
  52.         return VALIDATOR;
  53.     }

  54.     /**
  55.      * Constructs a <i>strict</i> instance.
  56.      */
  57.     public CurrencyValidator() {
  58.         this(true, true);
  59.     }

  60.     /**
  61.      * Constructs an instance with the specified strict setting.
  62.      *
  63.      * @param strict {@code true} if strict
  64.      *        <code>Format</code> parsing should be used.
  65.      * @param allowFractions {@code true} if fractions are
  66.      *        allowed or {@code false} if integers only.
  67.      */
  68.     public CurrencyValidator(final boolean strict, final boolean allowFractions) {
  69.         super(strict, CURRENCY_FORMAT, allowFractions);
  70.     }

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

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

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