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 * http://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.text.DecimalFormat; 020import java.text.DecimalFormatSymbols; 021import java.text.Format; 022import java.text.NumberFormat; 023import java.util.Locale; 024 025import org.apache.commons.validator.GenericValidator; 026 027/** 028 * <p>Abstract class for Number Validation.</p> 029 * 030 * <p>This is a <i>base</i> class for building Number 031 * Validators using format parsing.</p> 032 * 033 * @since 1.3.0 034 */ 035public abstract class AbstractNumberValidator extends AbstractFormatValidator { 036 037 private static final long serialVersionUID = -3088817875906765463L; 038 039 /** Standard <code>NumberFormat</code> type */ 040 public static final int STANDARD_FORMAT = 0; 041 042 /** Currency <code>NumberFormat</code> type */ 043 public static final int CURRENCY_FORMAT = 1; 044 045 /** Percent <code>NumberFormat</code> type */ 046 public static final int PERCENT_FORMAT = 2; 047 048 /** 049 * {@code true} if fractions are allowed or {@code false} if integers only. 050 */ 051 private final boolean allowFractions; 052 053 /** 054 * The <code>NumberFormat</code> type to create for validation, default is STANDARD_FORMAT. 055 */ 056 private final int formatType; 057 058 /** 059 * Constructs an instance with specified <i>strict</i> 060 * and <i>decimal</i> parameters. 061 * 062 * @param strict {@code true} if strict 063 * <code>Format</code> parsing should be used. 064 * @param formatType The <code>NumberFormat</code> type to 065 * create for validation, default is STANDARD_FORMAT. 066 * @param allowFractions {@code true} if fractions are 067 * allowed or {@code false} if integers only. 068 */ 069 public AbstractNumberValidator(final boolean strict, final int formatType, final boolean allowFractions) { 070 super(strict); 071 this.allowFractions = allowFractions; 072 this.formatType = formatType; 073 } 074 075 /** 076 * <p>Returns the <i>multiplier</i> of the <code>NumberFormat</code>.</p> 077 * 078 * @param format The <code>NumberFormat</code> to determine the 079 * multiplier of. 080 * @return The multiplying factor for the format.. 081 */ 082 protected int determineScale(final NumberFormat format) { 083 if (!isStrict()) { 084 return -1; 085 } 086 if (!isAllowFractions() || format.isParseIntegerOnly()) { 087 return 0; 088 } 089 final int minimumFraction = format.getMinimumFractionDigits(); 090 final int maximumFraction = format.getMaximumFractionDigits(); 091 if (minimumFraction != maximumFraction) { 092 return -1; 093 } 094 int scale = minimumFraction; 095 if (format instanceof DecimalFormat) { 096 final int multiplier = ((DecimalFormat) format).getMultiplier(); 097 if (multiplier == 100) { // CHECKSTYLE IGNORE MagicNumber 098 scale += 2; // CHECKSTYLE IGNORE MagicNumber 099 } 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}