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