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: 1227719 $ $Date: 2012-01-05 12:45:51 -0500 (Thu, 05 Jan 2012) $ 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 public boolean isValid(String value, String pattern, Locale locale) { 098 Object parsedValue = parse(value, pattern, locale); 099 return (parsedValue == null ? false : true); 100 } 101 102 /** 103 * Check if the value is within a specified range. 104 * 105 * @param value The value validation is being performed on. 106 * @param min The minimum value of the range. 107 * @param max The maximum value of the range. 108 * @return <code>true</code> if the value is within the 109 * specified range. 110 */ 111 public boolean isInRange(Number value, Number min, Number max) { 112 return (minValue(value, min) && maxValue(value, max)); 113 } 114 115 /** 116 * Check if the value is greater than or equal to a minimum. 117 * 118 * @param value The value validation is being performed on. 119 * @param min The minimum value. 120 * @return <code>true</code> if the value is greater than 121 * or equal to the minimum. 122 */ 123 public boolean minValue(Number value, Number min) { 124 if (isAllowFractions()) { 125 return (value.doubleValue() >= min.doubleValue()); 126 } else { 127 return (value.longValue() >= min.longValue()); 128 } 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 } else { 143 return (value.longValue() <= max.longValue()); 144 } 145 } 146 147 /** 148 * <p>Parse the value using the specified pattern.</p> 149 * 150 * @param value The value validation is being performed on. 151 * @param pattern The pattern used to validate the value against, or the 152 * default for the <code>Locale</code> if <code>null</code>. 153 * @param locale The locale to use for the date format, system default if null. 154 * @return The parsed value if valid or <code>null</code> if invalid. 155 */ 156 protected Object parse(String value, String pattern, Locale locale) { 157 158 value = (value == null ? null : value.trim()); 159 if (value == null || value.length() == 0) { 160 return null; 161 } 162 Format formatter = getFormat(pattern, locale); 163 return parse(value, formatter); 164 165 } 166 167 /** 168 * <p>Process the parsed value, performing any further validation 169 * and type conversion required.</p> 170 * 171 * @param value The parsed object created. 172 * @param formatter The Format used to parse the value with. 173 * @return The parsed value converted to the appropriate type 174 * if valid or <code>null</code> if invalid. 175 */ 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 protected Format getFormat(String pattern, Locale locale) { 188 189 NumberFormat formatter = null; 190 boolean usePattern = (pattern != null && pattern.length() > 0); 191 if (!usePattern) { 192 formatter = (NumberFormat)getFormat(locale); 193 } else if (locale == null) { 194 formatter = new DecimalFormat(pattern); 195 } else { 196 DecimalFormatSymbols symbols = new DecimalFormatSymbols(locale); 197 formatter = new DecimalFormat(pattern, symbols); 198 } 199 200 if (determineScale(formatter) == 0) { 201 formatter.setParseIntegerOnly(true); 202 } 203 return formatter; 204 } 205 206 /** 207 * <p>Returns the <i>multiplier</i> of the <code>NumberFormat</code>.</p> 208 * 209 * @param format The <code>NumberFormat</code> to determine the 210 * multiplier of. 211 * @return The multiplying factor for the format.. 212 */ 213 protected int determineScale(NumberFormat format) { 214 if (!isStrict()) { 215 return -1; 216 } 217 if (!isAllowFractions() || format.isParseIntegerOnly()) { 218 return 0; 219 } 220 int minimumFraction = format.getMinimumFractionDigits(); 221 int maximumFraction = format.getMaximumFractionDigits(); 222 if (minimumFraction != maximumFraction) { 223 return -1; 224 } 225 int scale = minimumFraction; 226 if (format instanceof DecimalFormat) { 227 int multiplier = ((DecimalFormat)format).getMultiplier(); 228 if (multiplier == 100) { 229 scale += 2; 230 } else if (multiplier == 1000) { 231 scale += 3; 232 } 233 } else if (formatType == PERCENT_FORMAT) { 234 scale += 2; 235 } 236 return scale; 237 } 238 239 /** 240 * <p>Returns a <code>NumberFormat</code> for the specified Locale.</p> 241 * 242 * @param locale The locale a <code>NumberFormat</code> is required for, 243 * system default if null. 244 * @return The <code>NumberFormat</code> to created. 245 */ 246 protected Format getFormat(Locale locale) { 247 NumberFormat formatter = null; 248 switch (formatType) { 249 case CURRENCY_FORMAT: 250 if (locale == null) { 251 formatter = NumberFormat.getCurrencyInstance(); 252 } else { 253 formatter = NumberFormat.getCurrencyInstance(locale); 254 } 255 break; 256 case PERCENT_FORMAT: 257 if (locale == null) { 258 formatter = NumberFormat.getPercentInstance(); 259 } else { 260 formatter = NumberFormat.getPercentInstance(locale); 261 } 262 break; 263 default: 264 if (locale == null) { 265 formatter = NumberFormat.getInstance(); 266 } else { 267 formatter = NumberFormat.getInstance(locale); 268 } 269 break; 270 } 271 return formatter; 272 } 273}