Coverage Report - org.apache.commons.validator.routines.AbstractNumberValidator
 
Classes in this File Line Coverage Branch Coverage Complexity
AbstractNumberValidator
96%
61/63
91%
54/59
3.5
 
 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  
 
 19  
 import java.text.DecimalFormatSymbols;
 20  
 import java.text.Format;
 21  
 import java.text.NumberFormat;
 22  
 import java.text.DecimalFormat;
 23  
 import java.util.Locale;
 24  
 
 25  
 /**
 26  
  * <p>Abstract class for Number Validation.</p>
 27  
  *
 28  
  * <p>This is a <i>base</i> class for building Number
 29  
  *    Validators using format parsing.</p>
 30  
  *
 31  
  * @version $Revision: 1739356 $
 32  
  * @since Validator 1.3.0
 33  
  */
 34  
 public abstract class AbstractNumberValidator extends AbstractFormatValidator {
 35  
 
 36  
     private static final long serialVersionUID = -3088817875906765463L;
 37  
 
 38  
     /** Standard <code>NumberFormat</code> type */
 39  
     public static final int STANDARD_FORMAT = 0;
 40  
 
 41  
     /** Currency <code>NumberFormat</code> type */
 42  
     public static final int CURRENCY_FORMAT = 1;
 43  
 
 44  
     /** Percent <code>NumberFormat</code> type */
 45  
     public static final int PERCENT_FORMAT  = 2;
 46  
 
 47  
     private final boolean allowFractions;
 48  
     private final int     formatType;
 49  
 
 50  
     /**
 51  
      * Construct an instance with specified <i>strict</i>
 52  
      * and <i>decimal</i> parameters.
 53  
      *
 54  
      * @param strict <code>true</code> if strict
 55  
      *        <code>Format</code> parsing should be used.
 56  
      * @param formatType The <code>NumberFormat</code> type to
 57  
      *        create for validation, default is STANDARD_FORMAT.
 58  
      * @param allowFractions <code>true</code> if fractions are
 59  
      *        allowed or <code>false</code> if integers only.
 60  
      */
 61  
     public AbstractNumberValidator(boolean strict, int formatType, boolean allowFractions) {
 62  210
         super(strict);
 63  210
         this.allowFractions = allowFractions;
 64  210
         this.formatType = formatType;
 65  210
     }
 66  
 
 67  
     /**
 68  
      * <p>Indicates whether the number being validated is
 69  
      *    a decimal or integer.</p>
 70  
      *
 71  
      * @return <code>true</code> if decimals are allowed
 72  
      *       or <code>false</code> if the number is an integer.
 73  
      */
 74  
     public boolean isAllowFractions() {
 75  737
         return allowFractions;
 76  
     }
 77  
 
 78  
     /**
 79  
      * <p>Indicates the type of <code>NumberFormat</code> created
 80  
      *    by this validator instance.</p>
 81  
      *
 82  
      * @return the format type created.
 83  
      */
 84  
     public int getFormatType() {
 85  20
         return formatType;
 86  
     }
 87  
 
 88  
     /**
 89  
      * <p>Validate using the specified <code>Locale</code>.</p>
 90  
      *
 91  
      * @param value The value validation is being performed on.
 92  
      * @param pattern The pattern used to validate the value against, or the
 93  
      *        default for the <code>Locale</code> if <code>null</code>.
 94  
      * @param locale The locale to use for the date format, system default if null.
 95  
      * @return <code>true</code> if the value is valid.
 96  
      */
 97  
     @Override
 98  
     public boolean isValid(String value, String pattern, Locale locale) {
 99  388
         Object parsedValue = parse(value, pattern, locale);
 100  388
         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  40
         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  64
         if (isAllowFractions()) {
 126  24
             return (value.doubleValue() >= min.doubleValue());
 127  
         }
 128  40
         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  56
         if (isAllowFractions()) {
 141  21
             return (value.doubleValue() <= max.doubleValue());
 142  
         }
 143  35
         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  903
         value = (value == null ? null : value.trim());
 158  903
         if (value == null || value.length() == 0) {
 159  136
             return null;
 160  
         }
 161  767
         Format formatter = getFormat(pattern, locale);
 162  767
         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  791
         NumberFormat formatter = null;
 191  791
         boolean usePattern = (pattern != null && pattern.length() > 0);
 192  791
         if (!usePattern) {
 193  407
             formatter = (NumberFormat)getFormat(locale);
 194  384
         } else if (locale == null) {
 195  347
             formatter =  new DecimalFormat(pattern);
 196  
         } else {
 197  37
             DecimalFormatSymbols symbols = new DecimalFormatSymbols(locale);
 198  37
             formatter = new DecimalFormat(pattern, symbols);
 199  
         }
 200  
 
 201  791
         if (determineScale(formatter) == 0) {
 202  355
             formatter.setParseIntegerOnly(true);
 203  
         }
 204  791
         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  877
         if (!isStrict()) {
 216  260
             return -1;
 217  
         }
 218  617
         if (!isAllowFractions() || format.isParseIntegerOnly()) {
 219  323
             return 0;
 220  
         }
 221  294
         int minimumFraction = format.getMinimumFractionDigits();
 222  294
         int maximumFraction = format.getMaximumFractionDigits();
 223  294
         if (minimumFraction != maximumFraction) {
 224  175
             return -1;
 225  
         }
 226  119
         int scale = minimumFraction;
 227  119
         if (format instanceof DecimalFormat) {
 228  119
             int multiplier = ((DecimalFormat)format).getMultiplier();
 229  119
             if (multiplier == 100) { // CHECKSTYLE IGNORE MagicNumber
 230  20
                 scale += 2; // CHECKSTYLE IGNORE MagicNumber
 231  99
             } else if (multiplier == 1000) { // CHECKSTYLE IGNORE MagicNumber
 232  0
                 scale += 3; // CHECKSTYLE IGNORE MagicNumber
 233  
             }
 234  119
         } else if (formatType == PERCENT_FORMAT) {
 235  0
             scale += 2; // CHECKSTYLE IGNORE MagicNumber
 236  
         }
 237  119
         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  407
         NumberFormat formatter = null;
 249  407
         switch (formatType) {
 250  
         case CURRENCY_FORMAT:
 251  26
             if (locale == null) {
 252  2
                 formatter = NumberFormat.getCurrencyInstance();
 253  
             } else {
 254  24
                 formatter = NumberFormat.getCurrencyInstance(locale);
 255  
             }
 256  24
             break;
 257  
         case PERCENT_FORMAT:
 258  13
             if (locale == null) {
 259  3
                 formatter = NumberFormat.getPercentInstance();
 260  
             } else {
 261  10
                 formatter = NumberFormat.getPercentInstance(locale);
 262  
             }
 263  10
             break;
 264  
         default:
 265  368
             if (locale == null) {
 266  48
                 formatter = NumberFormat.getInstance();
 267  
             } else {
 268  320
                 formatter = NumberFormat.getInstance(locale);
 269  
             }
 270  
             break;
 271  
         }
 272  407
         return formatter;
 273  
     }
 274  
 }