KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > tapestry > valid > NumberValidator


1 // Copyright 2004, 2005 The Apache Software Foundation
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
// http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14

15 package org.apache.tapestry.valid;
16
17 import java.math.BigDecimal JavaDoc;
18 import java.math.BigInteger JavaDoc;
19 import java.util.HashMap JavaDoc;
20 import java.util.Map JavaDoc;
21
22 import org.apache.hivemind.ApplicationRuntimeException;
23 import org.apache.hivemind.lib.util.StrategyRegistry;
24 import org.apache.hivemind.lib.util.StrategyRegistryImpl;
25 import org.apache.hivemind.util.PropertyUtils;
26 import org.apache.tapestry.IMarkupWriter;
27 import org.apache.tapestry.IRequestCycle;
28 import org.apache.tapestry.Tapestry;
29 import org.apache.tapestry.form.IFormComponent;
30
31 /**
32  * Simple validation for standard number classes. This is probably insufficient for anything tricky
33  * and application specific, such as parsing currency.
34  *
35  * @author Howard Lewis Ship
36  * @since 1.0.8
37  */

38
39 public class NumberValidator extends AbstractNumericValidator
40 {
41     private static final Map JavaDoc TYPES = new HashMap JavaDoc();
42
43     static
44     {
45         TYPES.put("boolean", boolean.class);
46         TYPES.put("Boolean", Boolean JavaDoc.class);
47         TYPES.put("java.lang.Boolean", Boolean JavaDoc.class);
48         TYPES.put("char", char.class);
49         TYPES.put("Character", Character JavaDoc.class);
50         TYPES.put("java.lang.Character", Character JavaDoc.class);
51         TYPES.put("short", short.class);
52         TYPES.put("Short", Short JavaDoc.class);
53         TYPES.put("java.lang.Short", Short JavaDoc.class);
54         TYPES.put("int", int.class);
55         TYPES.put("Integer", Integer JavaDoc.class);
56         TYPES.put("java.lang.Integer", Integer JavaDoc.class);
57         TYPES.put("long", long.class);
58         TYPES.put("Long", Long JavaDoc.class);
59         TYPES.put("java.lang.Long", Long JavaDoc.class);
60         TYPES.put("float", float.class);
61         TYPES.put("Float", Float JavaDoc.class);
62         TYPES.put("java.lang.Float", Float JavaDoc.class);
63         TYPES.put("byte", byte.class);
64         TYPES.put("Byte", Byte JavaDoc.class);
65         TYPES.put("java.lang.Byte", Byte JavaDoc.class);
66         TYPES.put("double", double.class);
67         TYPES.put("Double", Double JavaDoc.class);
68         TYPES.put("java.lang.Double", Double JavaDoc.class);
69         TYPES.put("java.math.BigInteger", BigInteger JavaDoc.class);
70         TYPES.put("java.math.BigDecimal", BigDecimal JavaDoc.class);
71     }
72
73     private Class JavaDoc _valueTypeClass = int.class;
74
75     private Number JavaDoc _minimum;
76
77     private Number JavaDoc _maximum;
78
79     private static StrategyRegistry _numberAdaptors = new StrategyRegistryImpl();
80
81     public final static int NUMBER_TYPE_INTEGER = 0;
82
83     public final static int NUMBER_TYPE_REAL = 1;
84
85     /**
86      * This class is not meant for use outside of NumberValidator; it is public only to fascilitate
87      * some unit testing.
88      */

89     public static abstract class NumberStrategy
90     {
91         /**
92          * Parses a non-empty {@link String}into the correct subclass of {@link Number}.
93          *
94          * @throws NumberFormatException
95          * if the String can not be parsed.
96          */

97
98         abstract public Number JavaDoc parse(String JavaDoc value);
99
100         /**
101          * Indicates the type of the number represented -- integer or real. The information is used
102          * to build the client-side validator. This method could return a boolean, but returns an
103          * int to allow future extensions of the validator.
104          *
105          * @return one of the predefined number types
106          */

107         abstract public int getNumberType();
108
109         public int compare(Number JavaDoc left, Number JavaDoc right)
110         {
111             if (!left.getClass().equals(right.getClass()))
112                 right = coerce(right);
113
114             Comparable JavaDoc lc = (Comparable JavaDoc) left;
115
116             return lc.compareTo(right);
117         }
118
119         /**
120          * Invoked when comparing two Numbers of different types. The number is cooerced from its
121          * ordinary type to the correct type for comparison.
122          *
123          * @since 3.0
124          */

125         protected abstract Number JavaDoc coerce(Number JavaDoc number);
126     }
127
128     private static abstract class IntegerNumberAdaptor extends NumberStrategy
129     {
130         public int getNumberType()
131         {
132             return NUMBER_TYPE_INTEGER;
133         }
134     }
135
136     private static abstract class RealNumberAdaptor extends NumberStrategy
137     {
138         public int getNumberType()
139         {
140             return NUMBER_TYPE_REAL;
141         }
142     }
143
144     private static class ByteAdaptor extends IntegerNumberAdaptor
145     {
146         public Number JavaDoc parse(String JavaDoc value)
147         {
148             return new Byte JavaDoc(value);
149         }
150
151         protected Number JavaDoc coerce(Number JavaDoc number)
152         {
153             return new Byte JavaDoc(number.byteValue());
154         }
155     }
156
157     private static class ShortAdaptor extends IntegerNumberAdaptor
158     {
159         public Number JavaDoc parse(String JavaDoc value)
160         {
161             return new Short JavaDoc(value);
162         }
163
164         protected Number JavaDoc coerce(Number JavaDoc number)
165         {
166             return new Short JavaDoc(number.shortValue());
167         }
168     }
169
170     private static class IntAdaptor extends IntegerNumberAdaptor
171     {
172         public Number JavaDoc parse(String JavaDoc value)
173         {
174             return new Integer JavaDoc(value);
175         }
176
177         protected Number JavaDoc coerce(Number JavaDoc number)
178         {
179             return new Integer JavaDoc(number.intValue());
180         }
181     }
182
183     private static class LongAdaptor extends IntegerNumberAdaptor
184     {
185         public Number JavaDoc parse(String JavaDoc value)
186         {
187             return new Long JavaDoc(value);
188         }
189
190         protected Number JavaDoc coerce(Number JavaDoc number)
191         {
192             return new Long JavaDoc(number.longValue());
193         }
194     }
195
196     private static class FloatAdaptor extends RealNumberAdaptor
197     {
198         public Number JavaDoc parse(String JavaDoc value)
199         {
200             return new Float JavaDoc(value);
201         }
202
203         protected Number JavaDoc coerce(Number JavaDoc number)
204         {
205             return new Float JavaDoc(number.floatValue());
206         }
207     }
208
209     private static class DoubleAdaptor extends RealNumberAdaptor
210     {
211         public Number JavaDoc parse(String JavaDoc value)
212         {
213             return new Double JavaDoc(value);
214         }
215
216         protected Number JavaDoc coerce(Number JavaDoc number)
217         {
218             return new Double JavaDoc(number.doubleValue());
219         }
220     }
221
222     private static class BigDecimalAdaptor extends RealNumberAdaptor
223     {
224         public Number JavaDoc parse(String JavaDoc value)
225         {
226             return new BigDecimal JavaDoc(value);
227         }
228
229         protected Number JavaDoc coerce(Number JavaDoc number)
230         {
231             return new BigDecimal JavaDoc(number.doubleValue());
232         }
233     }
234
235     private static class BigIntegerAdaptor extends IntegerNumberAdaptor
236     {
237         public Number JavaDoc parse(String JavaDoc value)
238         {
239             return new BigInteger JavaDoc(value);
240         }
241
242         protected Number JavaDoc coerce(Number JavaDoc number)
243         {
244             return new BigInteger JavaDoc(number.toString());
245         }
246     }
247
248     static
249     {
250         NumberStrategy byteAdaptor = new ByteAdaptor();
251         NumberStrategy shortAdaptor = new ShortAdaptor();
252         NumberStrategy intAdaptor = new IntAdaptor();
253         NumberStrategy longAdaptor = new LongAdaptor();
254         NumberStrategy floatAdaptor = new FloatAdaptor();
255         NumberStrategy doubleAdaptor = new DoubleAdaptor();
256
257         _numberAdaptors.register(Byte JavaDoc.class, byteAdaptor);
258         _numberAdaptors.register(byte.class, byteAdaptor);
259         _numberAdaptors.register(Short JavaDoc.class, shortAdaptor);
260         _numberAdaptors.register(short.class, shortAdaptor);
261         _numberAdaptors.register(Integer JavaDoc.class, intAdaptor);
262         _numberAdaptors.register(int.class, intAdaptor);
263         _numberAdaptors.register(Long JavaDoc.class, longAdaptor);
264         _numberAdaptors.register(long.class, longAdaptor);
265         _numberAdaptors.register(Float JavaDoc.class, floatAdaptor);
266         _numberAdaptors.register(float.class, floatAdaptor);
267         _numberAdaptors.register(Double JavaDoc.class, doubleAdaptor);
268         _numberAdaptors.register(double.class, doubleAdaptor);
269
270         _numberAdaptors.register(BigDecimal JavaDoc.class, new BigDecimalAdaptor());
271         _numberAdaptors.register(BigInteger JavaDoc.class, new BigIntegerAdaptor());
272     }
273
274     public NumberValidator()
275     {
276
277     }
278
279     /**
280      * Initializes the NumberValidator with properties defined by the initializer.
281      *
282      * @since 4.0
283      */

284
285     public NumberValidator(String JavaDoc initializer)
286     {
287         PropertyUtils.configureProperties(this, initializer);
288     }
289
290     public String JavaDoc toString(IFormComponent field, Object JavaDoc value)
291     {
292         if (value == null)
293             return null;
294
295         if (getZeroIsNull())
296         {
297             Number JavaDoc number = (Number JavaDoc) value;
298
299             if (number.doubleValue() == 0.0)
300                 return null;
301         }
302
303         return value.toString();
304     }
305
306     private NumberStrategy getStrategy(IFormComponent field)
307     {
308         NumberStrategy result = getStrategy(_valueTypeClass);
309
310         if (result == null)
311             throw new ApplicationRuntimeException(Tapestry.format(
312                     "NumberValidator.no-adaptor-for-field",
313                     field,
314                     _valueTypeClass.getName()));
315
316         return result;
317     }
318
319     /**
320      * Returns an strategy for the given type.
321      * <p>
322      * Note: this method exists only for testing purposes. It is not meant to be invoked by user
323      * code and is subject to change at any time.
324      *
325      * @param type
326      * the type (a Number subclass) for which to return an adaptor
327      * @return the adaptor, or null if no such adaptor may be found
328      * @since 3.0
329      */

330     public static NumberStrategy getStrategy(Class JavaDoc type)
331     {
332         return (NumberStrategy) _numberAdaptors.getStrategy(type);
333     }
334
335     public Object JavaDoc toObject(IFormComponent field, String JavaDoc value) throws ValidatorException
336     {
337         if (checkRequired(field, value))
338             return null;
339
340         NumberStrategy adaptor = getStrategy(field);
341         Number JavaDoc result = null;
342
343         try
344         {
345             result = adaptor.parse(value);
346         }
347         catch (NumberFormatException JavaDoc ex)
348         {
349             throw new ValidatorException(buildInvalidNumericFormatMessage(field),
350                     ValidationConstraint.NUMBER_FORMAT);
351         }
352
353         if (_minimum != null && adaptor.compare(result, _minimum) < 0)
354             throw new ValidatorException(buildNumberTooSmallMessage(field, _minimum),
355                     ValidationConstraint.TOO_SMALL);
356
357         if (_maximum != null && adaptor.compare(result, _maximum) > 0)
358             throw new ValidatorException(buildNumberTooLargeMessage(field, _maximum),
359                     ValidationConstraint.TOO_LARGE);
360
361         return result;
362     }
363
364     public Number JavaDoc getMaximum()
365     {
366         return _maximum;
367     }
368
369     public boolean getHasMaximum()
370     {
371         return _maximum != null;
372     }
373
374     public void setMaximum(Number JavaDoc maximum)
375     {
376         _maximum = maximum;
377     }
378
379     public Number JavaDoc getMinimum()
380     {
381         return _minimum;
382     }
383
384     public boolean getHasMinimum()
385     {
386         return _minimum != null;
387     }
388
389     public void setMinimum(Number JavaDoc minimum)
390     {
391         _minimum = minimum;
392     }
393
394     /**
395      * @since 2.2
396      */

397
398     public void renderValidatorContribution(IFormComponent field, IMarkupWriter writer,
399             IRequestCycle cycle)
400     {
401         if (!isClientScriptingEnabled())
402             return;
403
404         if (!(isRequired() || _minimum != null || _maximum != null))
405             return;
406
407         Map JavaDoc symbols = new HashMap JavaDoc();
408
409         if (isRequired())
410             symbols.put("requiredMessage", buildRequiredMessage(field));
411
412         if (isIntegerNumber())
413             symbols.put("formatMessage", buildInvalidIntegerFormatMessage(field));
414         else
415             symbols.put("formatMessage", buildInvalidNumericFormatMessage(field));
416
417         if (_minimum != null || _maximum != null)
418             symbols.put("rangeMessage", buildRangeMessage(field, _minimum, _maximum));
419
420         processValidatorScript(getScriptPath(), cycle, field, symbols);
421     }
422
423     /**
424      * Sets the value type from a string type name. The name may be a scalar numeric type, a fully
425      * qualified class name, or the name of a numeric wrapper type from java.lang (with the package
426      * name omitted).
427      *
428      * @since 3.0
429      */

430
431     public void setValueType(String JavaDoc typeName)
432     {
433         Class JavaDoc typeClass = (Class JavaDoc) TYPES.get(typeName);
434
435         if (typeClass == null)
436             throw new ApplicationRuntimeException(Tapestry.format(
437                     "NumberValidator.unknown-type",
438                     typeName));
439
440         _valueTypeClass = typeClass;
441     }
442
443     /** @since 3.0 * */
444
445     public void setValueTypeClass(Class JavaDoc valueTypeClass)
446     {
447         _valueTypeClass = valueTypeClass;
448     }
449
450     /**
451      * Returns the value type to convert strings back into. The default is int.
452      *
453      * @since 3.0
454      */

455
456     public Class JavaDoc getValueTypeClass()
457     {
458         return _valueTypeClass;
459     }
460
461     /** @since 3.0 */
462
463     public boolean isIntegerNumber()
464     {
465         NumberStrategy strategy = (NumberStrategy) _numberAdaptors.getStrategy(_valueTypeClass);
466         if (strategy == null)
467             return false;
468
469         return strategy.getNumberType() == NUMBER_TYPE_INTEGER;
470     }
471
472     protected String JavaDoc getDefaultScriptPath()
473     {
474         return "/org/apache/tapestry/valid/NumberValidator.script";
475     }
476 }
Popular Tags