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.DecimalFormatSymbols; 020import java.text.Format; 021import java.text.NumberFormat; 022import java.text.DecimalFormat; 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 * @version $Revision$ 032 * @since Validator 1.3.0 033 */ 034public abstract class AbstractNumberValidator extends AbstractFormatValidator { 035 036 private static final long serialVersionUID = -3088817875906765463L; 037 038 /** Standard <code>NumberFormat</code> type */ 039 public static final int STANDARD_FORMAT = 0; 040 041 /** Currency <code>NumberFormat</code> type */ 042 public static final int CURRENCY_FORMAT = 1; 043 044 /** Percent <code>NumberFormat</code> type */ 045 public static final int PERCENT_FORMAT = 2; 046 047 private final boolean allowFractions; 048 private final int formatType; 049 050 /** 051 * Construct an instance with specified <i>strict</i> 052 * and <i>decimal</i> parameters. 053 * 054 * @param strict <code>true</code> if strict 055 * <code>Format</code> parsing should be used. 056 * @param formatType The <code>NumberFormat</code> type to 057 * create for validation, default is STANDARD_FORMAT. 058 * @param allowFractions <code>true</code> if fractions are 059 * allowed or <code>false</code> if integers only. 060 */ 061 public AbstractNumberValidator(boolean strict, int formatType, boolean allowFractions) { 062 super(strict); 063 this.allowFractions = allowFractions; 064 this.formatType = formatType; 065 } 066 067 /** 068 * <p>Indicates whether the number being validated is 069 * a decimal or integer.</p> 070 * 071 * @return <code>true</code> if decimals are allowed 072 * or <code>false</code> if the number is an integer. 073 */ 074 public boolean isAllowFractions() { 075 return allowFractions; 076 } 077 078 /** 079 * <p>Indicates the type of <code>NumberFormat</code> created 080 * by this validator instance.</p> 081 * 082 * @return the format type created. 083 */ 084 public int getFormatType() { 085 return formatType; 086 } 087 088 /** 089 * <p>Validate using the specified <code>Locale</code>.</p> 090 * 091 * @param value The value validation is being performed on. 092 * @param pattern The pattern used to validate the value against, or the 093 * default for the <code>Locale</code> if <code>null</code>. 094 * @param locale The locale to use for the date format, system default if null. 095 * @return <code>true</code> if the value is valid. 096 */ 097 @Override 098 public boolean isValid(String value, String pattern, Locale locale) { 099 Object parsedValue = parse(value, pattern, locale); 100 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 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 if (isAllowFractions()) { 126 return (value.doubleValue() >= min.doubleValue()); 127 } 128 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 if (isAllowFractions()) { 141 return (value.doubleValue() <= max.doubleValue()); 142 } 143 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 value = (value == null ? null : value.trim()); 158 if (value == null || value.length() == 0) { 159 return null; 160 } 161 Format formatter = getFormat(pattern, locale); 162 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 NumberFormat formatter = null; 191 boolean usePattern = (pattern != null && pattern.length() > 0); 192 if (!usePattern) { 193 formatter = (NumberFormat)getFormat(locale); 194 } else if (locale == null) { 195 formatter = new DecimalFormat(pattern); 196 } else { 197 DecimalFormatSymbols symbols = new DecimalFormatSymbols(locale); 198 formatter = new DecimalFormat(pattern, symbols); 199 } 200 201 if (!isAllowFractions()) { 202 formatter.setParseIntegerOnly(true); 203 } 204 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 if (!isStrict()) { 216 return -1; 217 } 218 if (!isAllowFractions() || format.isParseIntegerOnly()) { 219 return 0; 220 } 221 int minimumFraction = format.getMinimumFractionDigits(); 222 int maximumFraction = format.getMaximumFractionDigits(); 223 if (minimumFraction != maximumFraction) { 224 return -1; 225 } 226 int scale = minimumFraction; 227 if (format instanceof DecimalFormat) { 228 int multiplier = ((DecimalFormat)format).getMultiplier(); 229 if (multiplier == 100) { // CHECKSTYLE IGNORE MagicNumber 230 scale += 2; // CHECKSTYLE IGNORE MagicNumber 231 } else if (multiplier == 1000) { // CHECKSTYLE IGNORE MagicNumber 232 scale += 3; // CHECKSTYLE IGNORE MagicNumber 233 } 234 } else if (formatType == PERCENT_FORMAT) { 235 scale += 2; // CHECKSTYLE IGNORE MagicNumber 236 } 237 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 NumberFormat formatter = null; 249 switch (formatType) { 250 case CURRENCY_FORMAT: 251 if (locale == null) { 252 formatter = NumberFormat.getCurrencyInstance(); 253 } else { 254 formatter = NumberFormat.getCurrencyInstance(locale); 255 } 256 break; 257 case PERCENT_FORMAT: 258 if (locale == null) { 259 formatter = NumberFormat.getPercentInstance(); 260 } else { 261 formatter = NumberFormat.getPercentInstance(locale); 262 } 263 break; 264 default: 265 if (locale == null) { 266 formatter = NumberFormat.getInstance(); 267 } else { 268 formatter = NumberFormat.getInstance(locale); 269 } 270 if (!isAllowFractions()) { 271 formatter.setParseIntegerOnly(true); 272 } 273 break; 274 } 275 return formatter; 276 } 277}