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.DecimalFormat; 20 import java.text.DecimalFormatSymbols; 21 import java.text.Format; 22 import java.text.NumberFormat; 23 import java.util.Locale; 24 25 import org.apache.commons.validator.GenericValidator; 26 27 /** 28 * <p>Abstract class for Number Validation.</p> 29 * 30 * <p>This is a <i>base</i> class for building Number 31 * Validators using format parsing.</p> 32 * 33 * @since 1.3.0 34 */ 35 public abstract class AbstractNumberValidator extends AbstractFormatValidator { 36 37 private static final long serialVersionUID = -3088817875906765463L; 38 39 /** Standard <code>NumberFormat</code> type */ 40 public static final int STANDARD_FORMAT = 0; 41 42 /** Currency <code>NumberFormat</code> type */ 43 public static final int CURRENCY_FORMAT = 1; 44 45 /** Percent <code>NumberFormat</code> type */ 46 public static final int PERCENT_FORMAT = 2; 47 48 /** 49 * {@code true} if fractions are allowed or {@code false} if integers only. 50 */ 51 private final boolean allowFractions; 52 53 /** 54 * The <code>NumberFormat</code> type to create for validation, default is STANDARD_FORMAT. 55 */ 56 private final int formatType; 57 58 /** 59 * Constructs an instance with specified <i>strict</i> 60 * and <i>decimal</i> parameters. 61 * 62 * @param strict {@code true} if strict 63 * <code>Format</code> parsing should be used. 64 * @param formatType The <code>NumberFormat</code> type to 65 * create for validation, default is STANDARD_FORMAT. 66 * @param allowFractions {@code true} if fractions are 67 * allowed or {@code false} if integers only. 68 */ 69 public AbstractNumberValidator(final boolean strict, final int formatType, final boolean allowFractions) { 70 super(strict); 71 this.allowFractions = allowFractions; 72 this.formatType = formatType; 73 } 74 75 /** 76 * <p>Returns the <i>multiplier</i> of the <code>NumberFormat</code>.</p> 77 * 78 * @param format The <code>NumberFormat</code> to determine the 79 * multiplier of. 80 * @return The multiplying factor for the format.. 81 */ 82 protected int determineScale(final NumberFormat format) { 83 if (!isStrict()) { 84 return -1; 85 } 86 if (!isAllowFractions() || format.isParseIntegerOnly()) { 87 return 0; 88 } 89 final int minimumFraction = format.getMinimumFractionDigits(); 90 final int maximumFraction = format.getMaximumFractionDigits(); 91 if (minimumFraction != maximumFraction) { 92 return -1; 93 } 94 int scale = minimumFraction; 95 if (format instanceof DecimalFormat) { 96 final int multiplier = ((DecimalFormat) format).getMultiplier(); 97 if (multiplier == 100) { // CHECKSTYLE IGNORE MagicNumber 98 scale += 2; // CHECKSTYLE IGNORE MagicNumber 99 } else if (multiplier == 1000) { // CHECKSTYLE IGNORE MagicNumber 100 scale += 3; // CHECKSTYLE IGNORE MagicNumber 101 } 102 } else if (formatType == PERCENT_FORMAT) { 103 scale += 2; // CHECKSTYLE IGNORE MagicNumber 104 } 105 return scale; 106 } 107 108 /** 109 * <p>Returns a <code>NumberFormat</code> for the specified Locale.</p> 110 * 111 * @param locale The locale a <code>NumberFormat</code> is required for, 112 * system default if null. 113 * @return The <code>NumberFormat</code> to created. 114 */ 115 protected Format getFormat(final Locale locale) { 116 NumberFormat formatter; 117 switch (formatType) { 118 case CURRENCY_FORMAT: 119 if (locale == null) { 120 formatter = NumberFormat.getCurrencyInstance(); 121 } else { 122 formatter = NumberFormat.getCurrencyInstance(locale); 123 } 124 break; 125 case PERCENT_FORMAT: 126 if (locale == null) { 127 formatter = NumberFormat.getPercentInstance(); 128 } else { 129 formatter = NumberFormat.getPercentInstance(locale); 130 } 131 break; 132 default: 133 if (locale == null) { 134 formatter = NumberFormat.getInstance(); 135 } else { 136 formatter = NumberFormat.getInstance(locale); 137 } 138 if (!isAllowFractions()) { 139 formatter.setParseIntegerOnly(true); 140 } 141 break; 142 } 143 return formatter; 144 } 145 146 /** 147 * <p>Returns a <code>NumberFormat</code> for the specified <i>pattern</i> 148 * and/or <code>Locale</code>.</p> 149 * 150 * @param pattern The pattern used to validate the value against or 151 * {@code null} to use the default for the <code>Locale</code>. 152 * @param locale The locale to use for the currency format, system default if null. 153 * @return The <code>NumberFormat</code> to created. 154 */ 155 @Override 156 protected Format getFormat(final String pattern, final Locale locale) { 157 158 NumberFormat formatter; 159 final boolean usePattern = !GenericValidator.isBlankOrNull(pattern); 160 if (!usePattern) { 161 formatter = (NumberFormat) getFormat(locale); 162 } else if (locale == null) { 163 formatter = new DecimalFormat(pattern); 164 } else { 165 final DecimalFormatSymbols symbols = new DecimalFormatSymbols(locale); 166 formatter = new DecimalFormat(pattern, symbols); 167 } 168 169 if (!isAllowFractions()) { 170 formatter.setParseIntegerOnly(true); 171 } 172 return formatter; 173 } 174 175 /** 176 * <p>Indicates the type of <code>NumberFormat</code> created 177 * by this validator instance.</p> 178 * 179 * @return the format type created. 180 */ 181 public int getFormatType() { 182 return formatType; 183 } 184 185 /** 186 * <p>Indicates whether the number being validated is 187 * a decimal or integer.</p> 188 * 189 * @return {@code true} if decimals are allowed 190 * or {@code false} if the number is an integer. 191 */ 192 public boolean isAllowFractions() { 193 return allowFractions; 194 } 195 196 /** 197 * Check if the value is within a specified range. 198 * 199 * @param value The value validation is being performed on. 200 * @param min The minimum value of the range. 201 * @param max The maximum value of the range. 202 * @return {@code true} if the value is within the 203 * specified range. 204 */ 205 public boolean isInRange(final Number value, final Number min, final Number max) { 206 return minValue(value, min) && maxValue(value, max); 207 } 208 209 /** 210 * <p>Validate using the specified <code>Locale</code>.</p> 211 * 212 * @param value The value validation is being performed on. 213 * @param pattern The pattern used to validate the value against, or the 214 * default for the <code>Locale</code> if {@code null}. 215 * @param locale The locale to use for the date format, system default if null. 216 * @return {@code true} if the value is valid. 217 */ 218 @Override 219 public boolean isValid(final String value, final String pattern, final Locale locale) { 220 return parse(value, pattern, locale) != null; 221 } 222 223 /** 224 * Check if the value is less than or equal to a maximum. 225 * 226 * @param value The value validation is being performed on. 227 * @param max The maximum value. 228 * @return {@code true} if the value is less than 229 * or equal to the maximum. 230 */ 231 public boolean maxValue(final Number value, final Number max) { 232 if (isAllowFractions()) { 233 return value.doubleValue() <= max.doubleValue(); 234 } 235 return value.longValue() <= max.longValue(); 236 } 237 238 /** 239 * Check if the value is greater than or equal to a minimum. 240 * 241 * @param value The value validation is being performed on. 242 * @param min The minimum value. 243 * @return {@code true} if the value is greater than 244 * or equal to the minimum. 245 */ 246 public boolean minValue(final Number value, final Number min) { 247 if (isAllowFractions()) { 248 return value.doubleValue() >= min.doubleValue(); 249 } 250 return value.longValue() >= min.longValue(); 251 } 252 253 /** 254 * <p>Parse the value using the specified pattern.</p> 255 * 256 * @param value The value validation is being performed on. 257 * @param pattern The pattern used to validate the value against, or the 258 * default for the <code>Locale</code> if {@code null}. 259 * @param locale The locale to use for the date format, system default if null. 260 * @return The parsed value if valid or {@code null} if invalid. 261 */ 262 protected Object parse(String value, final String pattern, final Locale locale) { 263 value = value == null ? null : value.trim(); 264 final String value1 = value; 265 if (GenericValidator.isBlankOrNull(value1)) { 266 return null; 267 } 268 final Format formatter = getFormat(pattern, locale); 269 return parse(value, formatter); 270 271 } 272 273 /** 274 * <p>Process the parsed value, performing any further validation 275 * and type conversion required.</p> 276 * 277 * @param value The parsed object created. 278 * @param formatter The Format used to parse the value with. 279 * @return The parsed value converted to the appropriate type 280 * if valid or {@code null} if invalid. 281 */ 282 @Override 283 protected abstract Object processParsedValue(Object value, Format formatter); 284 }