001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.commons.validator.routines;
018    
019    import java.text.DecimalFormatSymbols;
020    import java.text.Format;
021    import java.text.NumberFormat;
022    import java.text.DecimalFormat;
023    import java.util.Locale;
024    
025    /**
026     * <p>Abstract class for Number Validation.</p>
027     *
028     * <p>This is a <i>base</i> class for building Number
029     *    Validators using format parsing.</p>
030     *    
031     * @version $Revision: 594917 $ $Date: 2007-11-14 16:36:40 +0100 (Mi, 14. Nov 2007) $
032     * @since Validator 1.3.0
033     */
034    public abstract class AbstractNumberValidator extends AbstractFormatValidator {
035        
036        /** Standard <code>NumberFormat</code> type */
037        public static final int STANDARD_FORMAT = 0;
038    
039        /** Currency <code>NumberFormat</code> type */
040        public static final int CURRENCY_FORMAT = 1;
041    
042        /** Percent <code>NumberFormat</code> type */
043        public static final int PERCENT_FORMAT  = 2;
044    
045        private final boolean allowFractions;
046        private final int     formatType;
047    
048        /**
049         * Construct an instance with specified <i>strict</i>
050         * and <i>decimal</i> parameters.
051         * 
052         * @param strict <code>true</code> if strict 
053         *        <code>Format</code> parsing should be used.
054         * @param formatType The <code>NumberFormat</code> type to
055         *        create for validation, default is STANDARD_FORMAT.
056         * @param allowFractions <code>true</code> if fractions are
057         *        allowed or <code>false</code> if integers only.
058         */
059        public AbstractNumberValidator(boolean strict, int formatType, boolean allowFractions) {
060            super(strict);
061            this.allowFractions = allowFractions;
062            this.formatType = formatType;
063        }
064    
065        /**
066         * <p>Indicates whether the number being validated is
067         *    a decimal or integer.</p>
068         * 
069         * @return <code>true</code> if decimals are allowed
070         *       or <code>false</code> if the number is an integer.
071         */
072        public boolean isAllowFractions() {
073            return allowFractions;
074        }
075    
076        /**
077         * <p>Indicates the type of <code>NumberFormat</code> created
078         *    by this validator instance.</p>
079         * 
080         * @return the format type created.
081         */
082        public int getFormatType() {
083            return formatType;
084        }
085    
086        /**
087         * <p>Validate using the specified <code>Locale</code>.</p>
088         * 
089         * @param value The value validation is being performed on.
090         * @param pattern The pattern used to validate the value against, or the
091         *        default for the <code>Locale</code> if <code>null</code>.
092         * @param locale The locale to use for the date format, system default if null.
093         * @return <code>true</code> if the value is valid.
094         */
095        public boolean isValid(String value, String pattern, Locale locale) {
096            Object parsedValue = parse(value, pattern, locale);
097            return (parsedValue == null ? false : true);
098        }
099    
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    }