001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * https://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.validator.routines; 018 019import java.math.BigDecimal; 020import java.math.BigInteger; 021import java.text.DecimalFormat; 022import java.text.DecimalFormatSymbols; 023import java.text.Format; 024import java.text.NumberFormat; 025import java.util.Locale; 026 027import org.apache.commons.validator.GenericValidator; 028 029/** 030 * Abstract class for Number Validation. 031 * <p> 032 * This is a <em>base</em> class for building Number Validators using format parsing. 033 * </p> 034 * 035 * @since 1.3.0 036 */ 037public abstract class AbstractNumberValidator extends AbstractFormatValidator { 038 039 private static final long serialVersionUID = -3088817875906765463L; 040 041 /** 042 * Standard {@code NumberFormat} type. 043 */ 044 public static final int STANDARD_FORMAT = 0; 045 046 /** 047 * Currency {@code NumberFormat} type. 048 */ 049 public static final int CURRENCY_FORMAT = 1; 050 051 /** 052 * Percent {@code NumberFormat} type. 053 */ 054 public static final int PERCENT_FORMAT = 2; 055 056 /** 057 * Compares two values as BigDecimals. 058 * 059 * @param value1 {@code BigDecimal} to compare. 060 * @param value2 {@code BigDecimal} to which {@code value1} is to be compared. 061 * @return -1, 0, or 1 as this {@code BigDecimal} is numerically less than, equal to, or greater than {@code val}. 062 */ 063 static int compareTo(final Number value1, final Number value2) { 064 return toBigDecimal(value1).compareTo(toBigDecimal(value2)); 065 } 066 067 /** 068 * Tests if the given value is finite. 069 * 070 * @param value The value to test. 071 * @return {@code true} if the value is finite, {@code false} otherwise. 072 */ 073 static boolean isFinite(final Number value) { 074 if (value instanceof Double) { 075 return Double.isFinite((Double) value); 076 } 077 if (value instanceof Float) { 078 return Float.isFinite((Float) value); 079 } 080 return true; 081 } 082 083 static Format setParseBigDecimal(final Format format) { 084 if (format instanceof DecimalFormat) { 085 ((DecimalFormat) format).setParseBigDecimal(true); 086 } 087 return format; 088 } 089 090 static BigDecimal toBigDecimal(final Object value) { 091 if (value instanceof Long || value instanceof Integer || value instanceof Short || value instanceof Byte) { 092 return BigDecimal.valueOf(((Number) value).longValue()); 093 } 094 if (value instanceof Double || value instanceof Float) { 095 return BigDecimal.valueOf(((Number) value).doubleValue()); 096 } 097 if (value instanceof BigDecimal) { 098 return (BigDecimal) value; 099 } 100 if (value instanceof BigInteger) { 101 return new BigDecimal((BigInteger) value); 102 } 103 // No more shortcuts, convert from a string. 104 return new BigDecimal(value.toString()); 105 } 106 107 static BigInteger toBigInteger(final Object value) { 108 if (value instanceof Long || value instanceof Integer || value instanceof Short || value instanceof Byte) { 109 return BigInteger.valueOf(((Number) value).longValue()); 110 } 111 return toBigDecimal(value).toBigInteger(); 112 } 113 114 /** 115 * {@code true} if fractions are allowed or {@code false} if integers only. 116 */ 117 private final boolean allowFractions; 118 119 /** 120 * The {@code NumberFormat} type to create for validation, default is STANDARD_FORMAT. 121 */ 122 private final int formatType; 123 124 /** 125 * Constructs an instance with specified <em>strict</em> and <em>decimal</em> parameters. 126 * 127 * @param strict {@code true} if strict {@code Format} parsing should be used. 128 * @param formatType The {@code NumberFormat} type to create for validation, default is STANDARD_FORMAT. 129 * @param allowFractions {@code true} if fractions are allowed or {@code false} if integers only. 130 */ 131 public AbstractNumberValidator(final boolean strict, final int formatType, final boolean allowFractions) { 132 super(strict); 133 this.allowFractions = allowFractions; 134 this.formatType = formatType; 135 } 136 137 /** 138 * Returns the <em>multiplier</em> of the {@code NumberFormat}. 139 * 140 * @param format The {@code NumberFormat} to determine the multiplier of. 141 * @return The multiplying factor for the format. 142 */ 143 protected int determineScale(final NumberFormat format) { 144 if (!isStrict()) { 145 return -1; 146 } 147 if (!isAllowFractions() || format.isParseIntegerOnly()) { 148 return 0; 149 } 150 final int minimumFraction = format.getMinimumFractionDigits(); 151 final int maximumFraction = format.getMaximumFractionDigits(); 152 if (minimumFraction != maximumFraction) { 153 return -1; 154 } 155 int scale = minimumFraction; 156 if (format instanceof DecimalFormat) { 157 final int multiplier = ((DecimalFormat) format).getMultiplier(); 158 if (multiplier == 100) { // CHECKSTYLE IGNORE MagicNumber 159 scale += 2; // CHECKSTYLE IGNORE MagicNumber 160 } else if (multiplier == 1000) { // CHECKSTYLE IGNORE MagicNumber 161 scale += 3; // CHECKSTYLE IGNORE MagicNumber 162 } 163 } else if (formatType == PERCENT_FORMAT) { 164 scale += 2; // CHECKSTYLE IGNORE MagicNumber 165 } 166 return scale; 167 } 168 169 /** 170 * Gets the {@code NumberFormat} for the specified Locale. 171 * 172 * @param locale The locale a {@code NumberFormat} is required for, system default if null. 173 * @return The {@code NumberFormat} to created. 174 */ 175 protected Format getFormat(final Locale locale) { 176 final NumberFormat formatter; 177 switch (formatType) { 178 case CURRENCY_FORMAT: 179 if (locale == null) { 180 formatter = NumberFormat.getCurrencyInstance(); 181 } else { 182 formatter = NumberFormat.getCurrencyInstance(locale); 183 } 184 break; 185 case PERCENT_FORMAT: 186 if (locale == null) { 187 formatter = NumberFormat.getPercentInstance(); 188 } else { 189 formatter = NumberFormat.getPercentInstance(locale); 190 } 191 break; 192 default: 193 if (locale == null) { 194 formatter = NumberFormat.getInstance(); 195 } else { 196 formatter = NumberFormat.getInstance(locale); 197 } 198 if (!isAllowFractions()) { 199 formatter.setParseIntegerOnly(true); 200 } 201 break; 202 } 203 return formatter; 204 } 205 206 /** 207 * Gets a {@code NumberFormat} for the specified <em>pattern</em> and/or {@link Locale}. 208 * 209 * @param pattern The pattern used to validate the value against or {@code null} to use the default for the {@link Locale}. 210 * @param locale The locale to use for the currency format, system default if null. 211 * @return The {@code NumberFormat} to created. 212 */ 213 @Override 214 protected Format getFormat(final String pattern, final Locale locale) { 215 final NumberFormat formatter; 216 if (GenericValidator.isBlankOrNull(pattern)) { 217 formatter = (NumberFormat) getFormat(locale); 218 } else if (locale == null) { 219 formatter = new DecimalFormat(pattern); 220 } else { 221 formatter = new DecimalFormat(pattern, new DecimalFormatSymbols(locale)); 222 } 223 if (!isAllowFractions()) { 224 formatter.setParseIntegerOnly(true); 225 } 226 return formatter; 227 } 228 229 /** 230 * Gets the type of {@code NumberFormat} created by this validator instance. 231 * 232 * @return The format type created. 233 */ 234 public int getFormatType() { 235 return formatType; 236 } 237 238 /** 239 * Tests whether the number being validated is a decimal or integer. 240 * 241 * @return {@code true} if decimals are allowed or {@code false} if the number is an integer. 242 */ 243 public boolean isAllowFractions() { 244 return allowFractions; 245 } 246 247 /** 248 * Tests if the value is within a specified range. 249 * 250 * @param value The value validation is being performed on. 251 * @param min The minimum value of the range. 252 * @param max The maximum value of the range. 253 * @return {@code true} if the value is within the specified range. 254 */ 255 public boolean isInRange(final Number value, final Number min, final Number max) { 256 return minValue(value, min) && maxValue(value, max); 257 } 258 259 /** 260 * Tests validity using the specified {@link Locale}. 261 * 262 * @param value The value validation is being performed on. 263 * @param pattern The pattern used to validate the value against, or the default for the {@link Locale} if {@code null}. 264 * @param locale The locale to use for the date format, system default if null. 265 * @return {@code true} if the value is valid. 266 */ 267 @Override 268 public boolean isValid(final String value, final String pattern, final Locale locale) { 269 return parse(value, pattern, locale) != null; 270 } 271 272 /** 273 * Tests if the value is less than or equal to a maximum. 274 * 275 * <p> 276 * For an integer validator the bound is compared exactly rather than narrowed to a {@code long}, so a {@code BigInteger} or {@code BigDecimal} bound outside 277 * the long range keeps its magnitude and a fractional bound keeps its fractional part. A non-finite {@link Double} or {@link Float} operand keeps the 278 * {@code doubleValue()} comparison, since {@code BigDecimal} cannot represent {@code NaN} or an infinity. 279 * </p> 280 * 281 * @param value The value validation is being performed on. 282 * @param max The maximum value. 283 * @return {@code true} if the value is less than or equal to the maximum. 284 */ 285 public boolean maxValue(final Number value, final Number max) { 286 if (isAllowFractions()) { 287 return value.doubleValue() <= max.doubleValue(); 288 } 289 return isFinite(value) && isFinite(max) ? compareTo(value, max) <= 0 : value.doubleValue() <= max.doubleValue(); 290 } 291 292 /** 293 * Tests if the value is greater than or equal to a minimum. 294 * 295 * <p> 296 * For an integer validator the bound is compared exactly rather than narrowed to a {@code long}, so a {@code BigInteger} or {@code BigDecimal} bound outside 297 * the long range keeps its magnitude and a fractional bound keeps its fractional part. A non-finite {@link Double} or {@link Float} operand keeps the 298 * {@code doubleValue()} comparison, since {@code BigDecimal} cannot represent {@code NaN} or an infinity. 299 * </p> 300 * 301 * @param value The value validation is being performed on. 302 * @param min The minimum value. 303 * @return {@code true} if the value is greater than or equal to the minimum. 304 */ 305 public boolean minValue(final Number value, final Number min) { 306 if (isAllowFractions()) { 307 return value.doubleValue() >= min.doubleValue(); 308 } 309 return isFinite(value) && isFinite(min) ? compareTo(value, min) >= 0 : value.doubleValue() >= min.doubleValue(); 310 } 311 312 /** 313 * Parses the value using the specified pattern. 314 * 315 * @param value The value validation is being performed on. 316 * @param pattern The pattern used to validate the value against, or the default for the {@link Locale} if {@code null}. 317 * @param locale The locale to use for the date format, system default if null. 318 * @return The parsed value if valid or {@code null} if invalid. 319 */ 320 protected Object parse(String value, final String pattern, final Locale locale) { 321 value = value == null ? null : value.trim(); 322 final String value1 = value; 323 if (GenericValidator.isBlankOrNull(value1)) { 324 return null; 325 } 326 return parse(value, getFormat(pattern, locale)); 327 } 328 329 /** 330 * Processes the parsed value, performing any further validation and type conversion required. 331 * 332 * @param value The parsed object created. 333 * @param formatter The Format used to parse the value with. 334 * @return The parsed value converted to the appropriate type if valid or {@code null} if invalid. 335 */ 336 @Override 337 protected abstract Object processParsedValue(Object value, Format formatter); 338}