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