View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.validator.routines;
18  
19  import java.text.DecimalFormatSymbols;
20  import java.text.Format;
21  import java.text.NumberFormat;
22  import java.text.DecimalFormat;
23  import java.util.Locale;
24  
25  /***
26   * <p>Abstract class for Number Validation.</p>
27   *
28   * <p>This is a <i>base</i> class for building Number
29   *    Validators using format parsing.</p>
30   *    
31   * @version $Revision: 478334 $ $Date: 2006-11-22 21:31:54 +0000 (Wed, 22 Nov 2006) $
32   * @since Validator 1.3.0
33   */
34  public abstract class AbstractNumberValidator extends AbstractFormatValidator {
35      
36      /*** Standard <code>NumberFormat</code> type */
37      public static final int STANDARD_FORMAT = 0;
38  
39      /*** Currency <code>NumberFormat</code> type */
40      public static final int CURRENCY_FORMAT = 1;
41  
42      /*** Percent <code>NumberFormat</code> type */
43      public static final int PERCENT_FORMAT  = 2;
44  
45      private boolean allowFractions;
46      private int     formatType;
47  
48      /***
49       * Construct an instance with specified <i>strict</i>
50       * and <i>decimal</i> parameters.
51       * 
52       * @param strict <code>true</code> if strict 
53       *        <code>Format</code> parsing should be used.
54       * @param formatType The <code>NumberFormat</code> type to
55       *        create for validation, default is STANDARD_FORMAT.
56       * @param allowFractions <code>true</code> if fractions are
57       *        allowed or <code>false</code> if integers only.
58       */
59      public AbstractNumberValidator(boolean strict, int formatType, boolean allowFractions) {
60          super(strict);
61          this.allowFractions = allowFractions;
62          this.formatType = formatType;
63      }
64  
65      /***
66       * <p>Indicates whether the number being validated is
67       *    a decimal or integer.</p>
68       * 
69       * @return <code>true</code> if decimals are allowed
70       *       or <code>false</code> if the number is an integer.
71       */
72      public boolean isAllowFractions() {
73          return allowFractions;
74      }
75  
76      /***
77       * <p>Indicates the type of <code>NumberFormat</code> created
78       *    by this validator instance.</p>
79       * 
80       * @return the format type created.
81       */
82      public int getFormatType() {
83          return formatType;
84      }
85  
86      /***
87       * <p>Validate using the specified <code>Locale</code>.</p>
88       * 
89       * @param value The value validation is being performed on.
90       * @param pattern The pattern used to validate the value against, or the
91       *        default for the <code>Locale</code> if <code>null</code>.
92       * @param locale The locale to use for the date format, system default if null.
93       * @return <code>true</code> if the value is valid.
94       */
95      public boolean isValid(String value, String pattern, Locale locale) {
96          Object parsedValue = parse(value, pattern, locale);
97          return (parsedValue == null ? false : true);
98      }
99  
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 }