1 |
|
|
2 |
|
|
3 |
|
|
4 |
|
|
5 |
|
|
6 |
|
|
7 |
|
|
8 |
|
|
9 |
|
|
10 |
|
|
11 |
|
|
12 |
|
|
13 |
|
|
14 |
|
|
15 |
|
package org.apache.tapestry.valid; |
16 |
|
|
17 |
|
import org.apache.hivemind.ApplicationRuntimeException; |
18 |
|
import org.apache.hivemind.lib.util.StrategyRegistry; |
19 |
|
import org.apache.hivemind.lib.util.StrategyRegistryImpl; |
20 |
|
import org.apache.hivemind.util.PropertyUtils; |
21 |
|
import org.apache.tapestry.IMarkupWriter; |
22 |
|
import org.apache.tapestry.IRequestCycle; |
23 |
|
import org.apache.tapestry.Tapestry; |
24 |
|
import org.apache.tapestry.form.IFormComponent; |
25 |
|
|
26 |
|
import java.math.BigDecimal; |
27 |
|
import java.math.BigInteger; |
28 |
|
import java.util.HashMap; |
29 |
|
import java.util.Map; |
30 |
|
|
31 |
|
|
32 |
|
|
33 |
|
|
34 |
|
|
35 |
|
|
36 |
|
|
37 |
|
|
38 |
|
|
39 |
|
public class NumberValidator extends AbstractNumericValidator |
40 |
|
{ |
41 |
|
public static final int NUMBER_TYPE_INTEGER = 0; |
42 |
|
|
43 |
|
public static final int NUMBER_TYPE_REAL = 1; |
44 |
|
|
45 |
1 |
private static final Map TYPES = new HashMap(); |
46 |
|
|
47 |
1 |
private static StrategyRegistry _numberAdaptors = new StrategyRegistryImpl(); |
48 |
|
|
49 |
|
static |
50 |
|
{ |
51 |
1 |
TYPES.put("boolean", boolean.class); |
52 |
11 |
TYPES.put("Boolean", Boolean.class); |
53 |
1 |
TYPES.put("java.lang.Boolean", Boolean.class); |
54 |
1 |
TYPES.put("char", char.class); |
55 |
1 |
TYPES.put("Character", Character.class); |
56 |
1 |
TYPES.put("java.lang.Character", Character.class); |
57 |
1 |
TYPES.put("short", short.class); |
58 |
1 |
TYPES.put("Short", Short.class); |
59 |
1 |
TYPES.put("java.lang.Short", Short.class); |
60 |
1 |
TYPES.put("int", int.class); |
61 |
1 |
TYPES.put("Integer", Integer.class); |
62 |
1 |
TYPES.put("java.lang.Integer", Integer.class); |
63 |
1 |
TYPES.put("long", long.class); |
64 |
1 |
TYPES.put("Long", Long.class); |
65 |
1 |
TYPES.put("java.lang.Long", Long.class); |
66 |
1 |
TYPES.put("float", float.class); |
67 |
1 |
TYPES.put("Float", Float.class); |
68 |
1 |
TYPES.put("java.lang.Float", Float.class); |
69 |
1 |
TYPES.put("byte", byte.class); |
70 |
1 |
TYPES.put("Byte", Byte.class); |
71 |
1 |
TYPES.put("java.lang.Byte", Byte.class); |
72 |
1 |
TYPES.put("double", double.class); |
73 |
1 |
TYPES.put("Double", Double.class); |
74 |
1 |
TYPES.put("java.lang.Double", Double.class); |
75 |
1 |
TYPES.put("java.math.BigInteger", BigInteger.class); |
76 |
1 |
TYPES.put("java.math.BigDecimal", BigDecimal.class); |
77 |
|
} |
78 |
|
|
79 |
1 |
private Class _valueTypeClass = int.class; |
80 |
|
|
81 |
|
private Number _minimum; |
82 |
|
|
83 |
|
private Number _maximum; |
84 |
|
|
85 |
|
|
86 |
|
|
87 |
|
|
88 |
|
|
89 |
8 |
public abstract static class NumberStrategy |
90 |
|
{ |
91 |
|
|
92 |
|
|
93 |
|
|
94 |
|
|
95 |
|
|
96 |
|
|
97 |
|
|
98 |
|
public abstract Number parse(String value); |
99 |
|
|
100 |
|
|
101 |
|
|
102 |
|
|
103 |
|
|
104 |
|
|
105 |
|
|
106 |
|
|
107 |
|
public abstract int getNumberType(); |
108 |
|
|
109 |
|
public int compare(Number left, Number right) |
110 |
|
{ |
111 |
15 |
Number comparisonRight = right; |
112 |
15 |
if (!left.getClass().equals(comparisonRight.getClass())) |
113 |
8 |
comparisonRight = coerce(comparisonRight); |
114 |
|
|
115 |
15 |
Comparable lc = (Comparable) left; |
116 |
|
|
117 |
15 |
return lc.compareTo(comparisonRight); |
118 |
|
} |
119 |
|
|
120 |
|
|
121 |
|
|
122 |
|
|
123 |
|
|
124 |
|
|
125 |
|
|
126 |
|
protected abstract Number coerce(Number number); |
127 |
|
} |
128 |
|
|
129 |
|
|
130 |
|
|
131 |
|
|
132 |
10 |
private abstract static class IntegerNumberAdaptor extends NumberStrategy |
133 |
|
{ |
134 |
|
public int getNumberType() |
135 |
|
{ |
136 |
5 |
return NUMBER_TYPE_INTEGER; |
137 |
|
} |
138 |
|
} |
139 |
|
|
140 |
|
|
141 |
|
|
142 |
|
|
143 |
6 |
private abstract static class RealNumberAdaptor extends NumberStrategy |
144 |
|
{ |
145 |
|
public int getNumberType() |
146 |
|
{ |
147 |
3 |
return NUMBER_TYPE_REAL; |
148 |
|
} |
149 |
|
} |
150 |
|
|
151 |
|
|
152 |
|
|
153 |
|
|
154 |
2 |
private static class ByteAdaptor extends IntegerNumberAdaptor |
155 |
|
{ |
156 |
|
public Number parse(String value) |
157 |
|
{ |
158 |
1 |
return new Byte(value); |
159 |
|
} |
160 |
|
|
161 |
|
protected Number coerce(Number number) |
162 |
|
{ |
163 |
1 |
return new Byte(number.byteValue()); |
164 |
|
} |
165 |
|
} |
166 |
|
|
167 |
|
|
168 |
|
|
169 |
|
|
170 |
2 |
private static class ShortAdaptor extends IntegerNumberAdaptor |
171 |
|
{ |
172 |
|
public Number parse(String value) |
173 |
|
{ |
174 |
1 |
return new Short(value); |
175 |
|
} |
176 |
|
|
177 |
|
protected Number coerce(Number number) |
178 |
|
{ |
179 |
1 |
return new Short(number.shortValue()); |
180 |
|
} |
181 |
|
} |
182 |
|
|
183 |
|
|
184 |
|
|
185 |
|
|
186 |
2 |
private static class IntAdaptor extends IntegerNumberAdaptor |
187 |
|
{ |
188 |
|
public Number parse(String value) |
189 |
|
{ |
190 |
8 |
return new Integer(value); |
191 |
|
} |
192 |
|
|
193 |
|
protected Number coerce(Number number) |
194 |
|
{ |
195 |
1 |
return new Integer(number.intValue()); |
196 |
|
} |
197 |
|
} |
198 |
|
|
199 |
|
|
200 |
|
|
201 |
|
|
202 |
2 |
private static class LongAdaptor extends IntegerNumberAdaptor |
203 |
|
{ |
204 |
|
public Number parse(String value) |
205 |
|
{ |
206 |
1 |
return new Long(value); |
207 |
|
} |
208 |
|
|
209 |
|
protected Number coerce(Number number) |
210 |
|
{ |
211 |
1 |
return new Long(number.longValue()); |
212 |
|
} |
213 |
|
} |
214 |
|
|
215 |
|
|
216 |
|
|
217 |
|
|
218 |
2 |
private static class FloatAdaptor extends RealNumberAdaptor |
219 |
|
{ |
220 |
|
public Number parse(String value) |
221 |
|
{ |
222 |
1 |
return new Float(value); |
223 |
|
} |
224 |
|
|
225 |
|
protected Number coerce(Number number) |
226 |
|
{ |
227 |
1 |
return new Float(number.floatValue()); |
228 |
|
} |
229 |
|
} |
230 |
|
|
231 |
|
|
232 |
|
|
233 |
|
|
234 |
2 |
private static class DoubleAdaptor extends RealNumberAdaptor |
235 |
|
{ |
236 |
|
public Number parse(String value) |
237 |
|
{ |
238 |
1 |
return new Double(value); |
239 |
|
} |
240 |
|
|
241 |
|
protected Number coerce(Number number) |
242 |
|
{ |
243 |
1 |
return new Double(number.doubleValue()); |
244 |
|
} |
245 |
|
} |
246 |
|
|
247 |
|
|
248 |
|
|
249 |
|
|
250 |
2 |
private static class BigDecimalAdaptor extends RealNumberAdaptor |
251 |
|
{ |
252 |
|
public Number parse(String value) |
253 |
|
{ |
254 |
1 |
return new BigDecimal(value); |
255 |
|
} |
256 |
|
|
257 |
|
protected Number coerce(Number number) |
258 |
|
{ |
259 |
1 |
return new BigDecimal(number.doubleValue()); |
260 |
|
} |
261 |
|
} |
262 |
|
|
263 |
|
|
264 |
|
|
265 |
|
|
266 |
2 |
private static class BigIntegerAdaptor extends IntegerNumberAdaptor |
267 |
|
{ |
268 |
|
public Number parse(String value) |
269 |
|
{ |
270 |
1 |
return new BigInteger(value); |
271 |
|
} |
272 |
|
|
273 |
|
protected Number coerce(Number number) |
274 |
|
{ |
275 |
1 |
return new BigInteger(number.toString()); |
276 |
|
} |
277 |
|
} |
278 |
|
|
279 |
|
static |
280 |
|
{ |
281 |
1 |
NumberStrategy byteAdaptor = new ByteAdaptor(); |
282 |
1 |
NumberStrategy shortAdaptor = new ShortAdaptor(); |
283 |
1 |
NumberStrategy intAdaptor = new IntAdaptor(); |
284 |
1 |
NumberStrategy longAdaptor = new LongAdaptor(); |
285 |
1 |
NumberStrategy floatAdaptor = new FloatAdaptor(); |
286 |
1 |
NumberStrategy doubleAdaptor = new DoubleAdaptor(); |
287 |
|
|
288 |
1 |
_numberAdaptors.register(Byte.class, byteAdaptor); |
289 |
1 |
_numberAdaptors.register(byte.class, byteAdaptor); |
290 |
1 |
_numberAdaptors.register(Short.class, shortAdaptor); |
291 |
1 |
_numberAdaptors.register(short.class, shortAdaptor); |
292 |
1 |
_numberAdaptors.register(Integer.class, intAdaptor); |
293 |
1 |
_numberAdaptors.register(int.class, intAdaptor); |
294 |
1 |
_numberAdaptors.register(Long.class, longAdaptor); |
295 |
1 |
_numberAdaptors.register(long.class, longAdaptor); |
296 |
1 |
_numberAdaptors.register(Float.class, floatAdaptor); |
297 |
1 |
_numberAdaptors.register(float.class, floatAdaptor); |
298 |
1 |
_numberAdaptors.register(Double.class, doubleAdaptor); |
299 |
1 |
_numberAdaptors.register(double.class, doubleAdaptor); |
300 |
|
|
301 |
1 |
_numberAdaptors.register(BigDecimal.class, new BigDecimalAdaptor()); |
302 |
1 |
_numberAdaptors.register(BigInteger.class, new BigIntegerAdaptor()); |
303 |
1 |
} |
304 |
|
|
305 |
|
public NumberValidator() |
306 |
1 |
{ |
307 |
|
|
308 |
1 |
} |
309 |
|
|
310 |
|
|
311 |
|
|
312 |
|
|
313 |
|
|
314 |
|
|
315 |
|
|
316 |
|
public NumberValidator(String initializer) |
317 |
0 |
{ |
318 |
0 |
PropertyUtils.configureProperties(this, initializer); |
319 |
0 |
} |
320 |
|
|
321 |
|
public String toString(IFormComponent field, Object value) |
322 |
|
{ |
323 |
13 |
if (value == null) |
324 |
0 |
return null; |
325 |
|
|
326 |
13 |
if (getZeroIsNull()) |
327 |
|
{ |
328 |
0 |
Number number = (Number) value; |
329 |
|
|
330 |
0 |
if (number.doubleValue() == 0.0) |
331 |
0 |
return null; |
332 |
|
} |
333 |
|
|
334 |
13 |
return value.toString(); |
335 |
|
} |
336 |
|
|
337 |
|
private NumberStrategy getStrategy(IFormComponent field) |
338 |
|
{ |
339 |
15 |
NumberStrategy result = getStrategy(_valueTypeClass); |
340 |
|
|
341 |
15 |
if (result == null) |
342 |
0 |
throw new ApplicationRuntimeException(Tapestry.format("NumberValidator.no-adaptor-for-field", |
343 |
|
field, |
344 |
|
_valueTypeClass.getName())); |
345 |
|
|
346 |
15 |
return result; |
347 |
|
} |
348 |
|
|
349 |
|
|
350 |
|
|
351 |
|
|
352 |
|
|
353 |
|
|
354 |
|
|
355 |
|
|
356 |
|
|
357 |
|
|
358 |
|
|
359 |
|
|
360 |
|
public static NumberStrategy getStrategy(Class type) |
361 |
|
{ |
362 |
31 |
return (NumberStrategy) _numberAdaptors.getStrategy(type); |
363 |
|
} |
364 |
|
|
365 |
|
public Object toObject(IFormComponent field, String value) throws ValidatorException |
366 |
|
{ |
367 |
15 |
if (checkRequired(field, value)) |
368 |
0 |
return null; |
369 |
|
|
370 |
15 |
NumberStrategy adaptor = getStrategy(field); |
371 |
15 |
Number result = null; |
372 |
|
|
373 |
|
try |
374 |
|
{ |
375 |
15 |
result = adaptor.parse(value); |
376 |
|
} |
377 |
2 |
catch (NumberFormatException ex) |
378 |
|
{ |
379 |
2 |
throw new ValidatorException(buildInvalidNumericFormatMessage(field), |
380 |
|
ValidationConstraint.NUMBER_FORMAT); |
381 |
13 |
} |
382 |
|
|
383 |
13 |
if (_minimum != null && adaptor.compare(result, _minimum) < 0) |
384 |
2 |
throw new ValidatorException(buildNumberTooSmallMessage(field, _minimum), |
385 |
|
ValidationConstraint.TOO_SMALL); |
386 |
|
|
387 |
11 |
if (_maximum != null && adaptor.compare(result, _maximum) > 0) |
388 |
2 |
throw new ValidatorException(buildNumberTooLargeMessage(field, _maximum), |
389 |
|
ValidationConstraint.TOO_LARGE); |
390 |
|
|
391 |
9 |
return result; |
392 |
|
} |
393 |
|
|
394 |
|
public Number getMaximum() |
395 |
|
{ |
396 |
0 |
return _maximum; |
397 |
|
} |
398 |
|
|
399 |
|
public boolean getHasMaximum() |
400 |
|
{ |
401 |
0 |
return _maximum != null; |
402 |
|
} |
403 |
|
|
404 |
|
public void setMaximum(Number maximum) |
405 |
|
{ |
406 |
28 |
_maximum = maximum; |
407 |
28 |
} |
408 |
|
|
409 |
|
public Number getMinimum() |
410 |
|
{ |
411 |
0 |
return _minimum; |
412 |
|
} |
413 |
|
|
414 |
|
public boolean getHasMinimum() |
415 |
|
{ |
416 |
0 |
return _minimum != null; |
417 |
|
} |
418 |
|
|
419 |
|
public void setMinimum(Number minimum) |
420 |
|
{ |
421 |
28 |
_minimum = minimum; |
422 |
28 |
} |
423 |
|
|
424 |
|
|
425 |
|
|
426 |
|
|
427 |
|
|
428 |
|
public void renderValidatorContribution(IFormComponent field, IMarkupWriter writer, |
429 |
|
IRequestCycle cycle) |
430 |
|
{ |
431 |
0 |
if (!isClientScriptingEnabled()) |
432 |
0 |
return; |
433 |
|
|
434 |
0 |
if (!(isRequired() || _minimum != null || _maximum != null)) |
435 |
0 |
return; |
436 |
|
|
437 |
0 |
Map symbols = new HashMap(); |
438 |
|
|
439 |
0 |
if (isRequired()) |
440 |
0 |
symbols.put("requiredMessage", buildRequiredMessage(field)); |
441 |
|
|
442 |
0 |
if (isIntegerNumber()) |
443 |
0 |
symbols.put("formatMessage", buildInvalidIntegerFormatMessage(field)); |
444 |
|
else |
445 |
0 |
symbols.put("formatMessage", buildInvalidNumericFormatMessage(field)); |
446 |
|
|
447 |
0 |
if (_minimum != null || _maximum != null) |
448 |
0 |
symbols.put("rangeMessage", buildRangeMessage(field, _minimum, _maximum)); |
449 |
|
|
450 |
0 |
processValidatorScript(getScriptPath(), cycle, field, symbols); |
451 |
0 |
} |
452 |
|
|
453 |
|
|
454 |
|
|
455 |
|
|
456 |
|
|
457 |
|
|
458 |
|
|
459 |
|
|
460 |
|
|
461 |
|
public void setValueType(String typeName) |
462 |
|
{ |
463 |
0 |
Class typeClass = (Class) TYPES.get(typeName); |
464 |
|
|
465 |
0 |
if (typeClass == null) |
466 |
0 |
throw new ApplicationRuntimeException(Tapestry.format("NumberValidator.unknown-type", typeName)); |
467 |
|
|
468 |
0 |
_valueTypeClass = typeClass; |
469 |
0 |
} |
470 |
|
|
471 |
|
|
472 |
|
|
473 |
|
public void setValueTypeClass(Class valueTypeClass) |
474 |
|
{ |
475 |
15 |
_valueTypeClass = valueTypeClass; |
476 |
15 |
} |
477 |
|
|
478 |
|
|
479 |
|
|
480 |
|
|
481 |
|
|
482 |
|
|
483 |
|
|
484 |
|
public Class getValueTypeClass() |
485 |
|
{ |
486 |
0 |
return _valueTypeClass; |
487 |
|
} |
488 |
|
|
489 |
|
|
490 |
|
|
491 |
|
public boolean isIntegerNumber() |
492 |
|
{ |
493 |
0 |
NumberStrategy strategy = (NumberStrategy) _numberAdaptors.getStrategy(_valueTypeClass); |
494 |
0 |
if (strategy == null) |
495 |
0 |
return false; |
496 |
|
|
497 |
0 |
return strategy.getNumberType() == NUMBER_TYPE_INTEGER; |
498 |
|
} |
499 |
|
|
500 |
|
protected String getDefaultScriptPath() |
501 |
|
{ |
502 |
1 |
return "/org/apache/tapestry/valid/NumberValidator.script"; |
503 |
|
} |
504 |
|
} |