View Javadoc

1   /*
2    * $Id: AbstractNumberValidator.java 386637 2006-03-17 13:22:26Z niallp $
3    * $Revision: 386637 $
4    * $Date: 2006-03-17 13:22:26 +0000 (Fri, 17 Mar 2006) $
5    *
6    * ====================================================================
7    * Copyright 2006 The Apache Software Foundation
8    *
9    * Licensed under the Apache License, Version 2.0 (the "License");
10   * you may not use this file except in compliance with the License.
11   * You may obtain a copy of the License at
12   *
13   *     http://www.apache.org/licenses/LICENSE-2.0
14   *
15   * Unless required by applicable law or agreed to in writing, software
16   * distributed under the License is distributed on an "AS IS" BASIS,
17   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18   * See the License for the specific language governing permissions and
19   * limitations under the License.
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 }