1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package org.apache.commons.validator.routines;
22
23 import java.text.DecimalFormatSymbols;
24 import java.text.Format;
25 import java.text.NumberFormat;
26 import java.text.DecimalFormat;
27 import java.util.Locale;
28
29 /***
30 * <p>Abstract class for Number Validation.</p>
31 *
32 * <p>This is a <i>base</i> class for building Number
33 * Validators using format parsing.</p>
34 *
35 * @version $Revision: 386637 $ $Date: 2006-03-17 13:22:26 +0000 (Fri, 17 Mar 2006) $
36 * @since Validator 1.3.0
37 */
38 public abstract class AbstractNumberValidator extends AbstractFormatValidator {
39
40 /*** Standard <code>NumberFormat</code> type */
41 public static final int STANDARD_FORMAT = 0;
42
43 /*** Currency <code>NumberFormat</code> type */
44 public static final int CURRENCY_FORMAT = 1;
45
46 /*** Percent <code>NumberFormat</code> type */
47 public static final int PERCENT_FORMAT = 2;
48
49 private boolean allowFractions;
50 private int formatType;
51
52 /***
53 * Construct an instance with specified <i>strict</i>
54 * and <i>decimal</i> parameters.
55 *
56 * @param strict <code>true</code> if strict
57 * <code>Format</code> parsing should be used.
58 * @param formatType The <code>NumberFormat</code> type to
59 * create for validation, default is STANDARD_FORMAT.
60 * @param allowFractions <code>true</code> if fractions are
61 * allowed or <code>false</code> if integers only.
62 */
63 public AbstractNumberValidator(boolean strict, int formatType, boolean allowFractions) {
64 super(strict);
65 this.allowFractions = allowFractions;
66 this.formatType = formatType;
67 }
68
69 /***
70 * <p>Indicates whether the number being validated is
71 * a decimal or integer.</p>
72 *
73 * @return <code>true</code> if decimals are allowed
74 * or <code>false</code> if the number is an integer.
75 */
76 public boolean isAllowFractions() {
77 return allowFractions;
78 }
79
80 /***
81 * <p>Indicates the type of <code>NumberFormat</code> created
82 * by this validator instance.</p>
83 *
84 * @return the format type created.
85 */
86 public int getFormatType() {
87 return formatType;
88 }
89
90 /***
91 * <p>Validate using the specified <code>Locale</code>.</p>
92 *
93 * @param value The value validation is being performed on.
94 * @param pattern The pattern used to validate the value against, or the
95 * default for the <code>Locale</code> if <code>null</code>.
96 * @param locale The locale to use for the date format, system default if null.
97 * @return <code>true</code> if the value is valid.
98 */
99 public boolean isValid(String value, String pattern, Locale locale) {
100 Object parsedValue = parse(value, pattern, locale);
101 return (parsedValue == null ? false : true);
102 }
103
104 /***
105 * Check if the value is within a specified range.
106 *
107 * @param value The value validation is being performed on.
108 * @param min The minimum value of the range.
109 * @param max The maximum value of the range.
110 * @return <code>true</code> if the value is within the
111 * specified range.
112 */
113 public boolean isInRange(Number value, Number min, Number max) {
114 return (minValue(value, min) && maxValue(value, max));
115 }
116
117 /***
118 * Check if the value is greater than or equal to a minimum.
119 *
120 * @param value The value validation is being performed on.
121 * @param min The minimum value.
122 * @return <code>true</code> if the value is greater than
123 * or equal to the minimum.
124 */
125 public boolean minValue(Number value, Number min) {
126 if (isAllowFractions()) {
127 return (value.doubleValue() >= min.doubleValue());
128 } else {
129 return (value.longValue() >= min.longValue());
130 }
131 }
132
133 /***
134 * Check if the value is less than or equal to a maximum.
135 *
136 * @param value The value validation is being performed on.
137 * @param max The maximum value.
138 * @return <code>true</code> if the value is less than
139 * or equal to the maximum.
140 */
141 public boolean maxValue(Number value, Number max) {
142 if (isAllowFractions()) {
143 return (value.doubleValue() <= max.doubleValue());
144 } else {
145 return (value.longValue() <= max.longValue());
146 }
147 }
148
149 /***
150 * <p>Parse the value using the specified pattern.</p>
151 *
152 * @param value The value validation is being performed on.
153 * @param pattern The pattern used to validate the value against, or the
154 * default for the <code>Locale</code> if <code>null</code>.
155 * @param locale The locale to use for the date format, system default if null.
156 * @return The parsed value if valid or <code>null</code> if invalid.
157 */
158 protected Object parse(String value, String pattern, Locale locale) {
159
160 value = (value == null ? null : value.trim());
161 if (value == null || value.length() == 0) {
162 return null;
163 }
164 Format formatter = getFormat(pattern, locale);
165 return parse(value, formatter);
166
167 }
168
169 /***
170 * <p>Process the parsed value, performing any further validation
171 * and type conversion required.</p>
172 *
173 * @param value The parsed object created.
174 * @param formatter The Format used to parse the value with.
175 * @return The parsed value converted to the appropriate type
176 * if valid or <code>null</code> if invalid.
177 */
178 protected abstract Object processParsedValue(Object value, Format formatter);
179
180 /***
181 * <p>Returns a <code>NumberFormat</code> for the specified <i>pattern</i>
182 * and/or <code>Locale</code>.</p>
183 *
184 * @param pattern The pattern used to validate the value against or
185 * <code>null</code> to use the default for the <code>Locale</code>.
186 * @param locale The locale to use for the currency format, system default if null.
187 * @return The <code>NumberFormat</code> to created.
188 */
189 protected Format getFormat(String pattern, Locale locale) {
190
191 NumberFormat formatter = null;
192 boolean usePattern = (pattern != null && pattern.length() > 0);
193 if (!usePattern) {
194 formatter = (NumberFormat)getFormat(locale);
195 } else if (locale == null) {
196 formatter = new DecimalFormat(pattern);
197 } else {
198 DecimalFormatSymbols symbols = new DecimalFormatSymbols(locale);
199 formatter = new DecimalFormat(pattern, symbols);
200 }
201
202 if (determineScale(formatter) == 0) {
203 formatter.setParseIntegerOnly(true);
204 }
205 return formatter;
206 }
207
208 /***
209 * <p>Returns the <i>multiplier</i> of the <code>NumberFormat</code>.</p>
210 *
211 * @param format The <code>NumberFormat</code> to determine the
212 * multiplier of.
213 * @return The multiplying factor for the format..
214 */
215 protected int determineScale(NumberFormat format) {
216 if (!isStrict()) {
217 return -1;
218 }
219 if (!isAllowFractions() || format.isParseIntegerOnly()) {
220 return 0;
221 }
222 int minimumFraction = format.getMinimumFractionDigits();
223 int maximumFraction = format.getMaximumFractionDigits();
224 if (minimumFraction != maximumFraction) {
225 return -1;
226 }
227 int scale = minimumFraction;
228 if (format instanceof DecimalFormat) {
229 int multiplier = ((DecimalFormat)format).getMultiplier();
230 if (multiplier == 100) {
231 scale += 2;
232 } else if (multiplier == 1000) {
233 scale += 3;
234 }
235 } else if (formatType == PERCENT_FORMAT) {
236 scale += 2;
237 }
238 return scale;
239 }
240
241 /***
242 * <p>Returns a <code>NumberFormat</code> for the specified Locale.</p>
243 *
244 * @param locale The locale a <code>NumberFormat</code> is required for,
245 * system default if null.
246 * @return The <code>NumberFormat</code> to created.
247 */
248 protected Format getFormat(Locale locale) {
249 NumberFormat formatter = null;
250 switch (formatType) {
251 case CURRENCY_FORMAT:
252 if (locale == null) {
253 formatter = NumberFormat.getCurrencyInstance();
254 } else {
255 formatter = NumberFormat.getCurrencyInstance(locale);
256 }
257 break;
258 case PERCENT_FORMAT:
259 if (locale == null) {
260 formatter = NumberFormat.getPercentInstance();
261 } else {
262 formatter = NumberFormat.getPercentInstance(locale);
263 }
264 break;
265 default:
266 if (locale == null) {
267 formatter = NumberFormat.getInstance();
268 } else {
269 formatter = NumberFormat.getInstance(locale);
270 }
271 break;
272 }
273 return formatter;
274 }
275 }