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 025/** 026 * <p>Abstract class for Number Validation.</p> 027 * 028 * <p>This is a <i>base</i> class for building Number 029 * Validators using format parsing.</p> 030 * 031 * @since 1.3.0 032 */ 033public abstract class AbstractNumberValidator extends AbstractFormatValidator { 034 035 private static final long serialVersionUID = -3088817875906765463L; 036 037 /** Standard <code>NumberFormat</code> type */ 038 public static final int STANDARD_FORMAT = 0; 039 040 /** Currency <code>NumberFormat</code> type */ 041 public static final int CURRENCY_FORMAT = 1; 042 043 /** Percent <code>NumberFormat</code> type */ 044 public static final int PERCENT_FORMAT = 2; 045 046 /** 047 * {@code true} if fractions are allowed or {@code false} if integers only. 048 */ 049 private final boolean allowFractions; 050 051 /** 052 * The <code>NumberFormat</code> type to create for validation, default is STANDARD_FORMAT. 053 */ 054 private final int formatType; 055 056 /** 057 * Constructs an instance with specified <i>strict</i> 058 * and <i>decimal</i> parameters. 059 * 060 * @param strict {@code true} if strict 061 * <code>Format</code> parsing should be used. 062 * @param formatType The <code>NumberFormat</code> type to 063 * create for validation, default is STANDARD_FORMAT. 064 * @param allowFractions {@code true} if fractions are 065 * allowed or {@code false} if integers only. 066 */ 067 public AbstractNumberValidator(final boolean strict, final int formatType, final boolean allowFractions) { 068 super(strict); 069 this.allowFractions = allowFractions; 070 this.formatType = formatType; 071 } 072 073 /** 074 * <p>Returns the <i>multiplier</i> of the <code>NumberFormat</code>.</p> 075 * 076 * @param format The <code>NumberFormat</code> to determine the 077 * multiplier of. 078 * @return The multiplying factor for the format.. 079 */ 080 protected int determineScale(final NumberFormat format) { 081 if (!isStrict()) { 082 return -1; 083 } 084 if (!isAllowFractions() || format.isParseIntegerOnly()) { 085 return 0; 086 } 087 final int minimumFraction = format.getMinimumFractionDigits(); 088 final int maximumFraction = format.getMaximumFractionDigits(); 089 if (minimumFraction != maximumFraction) { 090 return -1; 091 } 092 int scale = minimumFraction; 093 if (format instanceof DecimalFormat) { 094 final int multiplier = ((DecimalFormat) format).getMultiplier(); 095 if (multiplier == 100) { // CHECKSTYLE IGNORE MagicNumber 096 scale += 2; // CHECKSTYLE IGNORE MagicNumber 097 } else if (multiplier == 1000) { // CHECKSTYLE IGNORE MagicNumber 098 scale += 3; // CHECKSTYLE IGNORE MagicNumber 099 } 100 } else if (formatType == PERCENT_FORMAT) { 101 scale += 2; // CHECKSTYLE IGNORE MagicNumber 102 } 103 return scale; 104 } 105 106 /** 107 * <p>Returns a <code>NumberFormat</code> for the specified Locale.</p> 108 * 109 * @param locale The locale a <code>NumberFormat</code> is required for, 110 * system default if null. 111 * @return The <code>NumberFormat</code> to created. 112 */ 113 protected Format getFormat(final Locale locale) { 114 NumberFormat formatter; 115 switch (formatType) { 116 case CURRENCY_FORMAT: 117 if (locale == null) { 118 formatter = NumberFormat.getCurrencyInstance(); 119 } else { 120 formatter = NumberFormat.getCurrencyInstance(locale); 121 } 122 break; 123 case PERCENT_FORMAT: 124 if (locale == null) { 125 formatter = NumberFormat.getPercentInstance(); 126 } else { 127 formatter = NumberFormat.getPercentInstance(locale); 128 } 129 break; 130 default: 131 if (locale == null) { 132 formatter = NumberFormat.getInstance(); 133 } else { 134 formatter = NumberFormat.getInstance(locale); 135 } 136 if (!isAllowFractions()) { 137 formatter.setParseIntegerOnly(true); 138 } 139 break; 140 } 141 return formatter; 142 } 143 144 /** 145 * <p>Returns a <code>NumberFormat</code> for the specified <i>pattern</i> 146 * and/or <code>Locale</code>.</p> 147 * 148 * @param pattern The pattern used to validate the value against or 149 * <code>null</code> to use the default for the <code>Locale</code>. 150 * @param locale The locale to use for the currency format, system default if null. 151 * @return The <code>NumberFormat</code> to created. 152 */ 153 @Override 154 protected Format getFormat(final String pattern, final Locale locale) { 155 156 NumberFormat formatter; 157 final boolean usePattern = pattern != null && !pattern.isEmpty(); 158 if (!usePattern) { 159 formatter = (NumberFormat) getFormat(locale); 160 } else if (locale == null) { 161 formatter = new DecimalFormat(pattern); 162 } else { 163 final DecimalFormatSymbols symbols = new DecimalFormatSymbols(locale); 164 formatter = new DecimalFormat(pattern, symbols); 165 } 166 167 if (!isAllowFractions()) { 168 formatter.setParseIntegerOnly(true); 169 } 170 return formatter; 171 } 172 173 /** 174 * <p>Indicates the type of <code>NumberFormat</code> created 175 * by this validator instance.</p> 176 * 177 * @return the format type created. 178 */ 179 public int getFormatType() { 180 return formatType; 181 } 182 183 /** 184 * <p>Indicates whether the number being validated is 185 * a decimal or integer.</p> 186 * 187 * @return {@code true} if decimals are allowed 188 * or {@code false} if the number is an integer. 189 */ 190 public boolean isAllowFractions() { 191 return allowFractions; 192 } 193 194 /** 195 * Check if the value is within a specified range. 196 * 197 * @param value The value validation is being performed on. 198 * @param min The minimum value of the range. 199 * @param max The maximum value of the range. 200 * @return {@code true} if the value is within the 201 * specified range. 202 */ 203 public boolean isInRange(final Number value, final Number min, final Number max) { 204 return minValue(value, min) && maxValue(value, max); 205 } 206 207 /** 208 * <p>Validate using the specified <code>Locale</code>.</p> 209 * 210 * @param value The value validation is being performed on. 211 * @param pattern The pattern used to validate the value against, or the 212 * default for the <code>Locale</code> if <code>null</code>. 213 * @param locale The locale to use for the date format, system default if null. 214 * @return {@code true} if the value is valid. 215 */ 216 @Override 217 public boolean isValid(final String value, final String pattern, final Locale locale) { 218 final Object parsedValue = parse(value, pattern, locale); 219 return parsedValue == null ? false : true; 220 } 221 222 /** 223 * Check if the value is less than or equal to a maximum. 224 * 225 * @param value The value validation is being performed on. 226 * @param max The maximum value. 227 * @return {@code true} if the value is less than 228 * or equal to the maximum. 229 */ 230 public boolean maxValue(final Number value, final Number max) { 231 if (isAllowFractions()) { 232 return value.doubleValue() <= max.doubleValue(); 233 } 234 return value.longValue() <= max.longValue(); 235 } 236 237 /** 238 * Check if the value is greater than or equal to a minimum. 239 * 240 * @param value The value validation is being performed on. 241 * @param min The minimum value. 242 * @return {@code true} if the value is greater than 243 * or equal to the minimum. 244 */ 245 public boolean minValue(final Number value, final Number min) { 246 if (isAllowFractions()) { 247 return value.doubleValue() >= min.doubleValue(); 248 } 249 return value.longValue() >= min.longValue(); 250 } 251 252 /** 253 * <p>Parse the value using the specified pattern.</p> 254 * 255 * @param value The value validation is being performed on. 256 * @param pattern The pattern used to validate the value against, or the 257 * default for the <code>Locale</code> if <code>null</code>. 258 * @param locale The locale to use for the date format, system default if null. 259 * @return The parsed value if valid or <code>null</code> if invalid. 260 */ 261 protected Object parse(String value, final String pattern, final Locale locale) { 262 value = value == null ? null : value.trim(); 263 if (value == null || value.isEmpty()) { 264 return null; 265 } 266 final Format formatter = getFormat(pattern, locale); 267 return parse(value, formatter); 268 269 } 270 271 /** 272 * <p>Process the parsed value, performing any further validation 273 * and type conversion required.</p> 274 * 275 * @param value The parsed object created. 276 * @param formatter The Format used to parse the value with. 277 * @return The parsed value converted to the appropriate type 278 * if valid or <code>null</code> if invalid. 279 */ 280 @Override 281 protected abstract Object processParsedValue(Object value, Format formatter); 282}