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.cli2.validation;
018    
019    import java.text.NumberFormat;
020    import java.text.ParsePosition;
021    
022    import java.util.List;
023    import java.util.ListIterator;
024    
025    import org.apache.commons.cli2.resource.ResourceConstants;
026    import org.apache.commons.cli2.resource.ResourceHelper;
027    
028    /**
029     * The <code>NumberValidator</code> validates the string argument
030     * values are numbers.  If the value is a number, the string value in
031     * the {@link java.util.List} of values is replaced with the
032     * {@link java.lang.Number} instance.
033     *
034     * A maximum and minimum value can also be specified using
035     * the {@link #setMaximum setMaximum}, and the
036     * {@link #setMinimum setMinimum} methods.
037     *
038     * The following example shows how to limit the valid values
039     * for the age attribute to integers less than 100.
040     *
041     * <pre>
042     * ...
043     * ArgumentBuilder builder = new ArgumentBuilder();
044     * NumberValidator validator = NumberValidator.getIntegerInstance();
045     * validator.setMaximum(new Integer(100));
046     *
047     * Argument age =
048     *     builder.withName("age");
049     *            .withValidator(validator);
050     * </pre>
051     *
052     * @author Rob Oxspring
053     * @author John Keyes
054     */
055    public class NumberValidator implements Validator {
056        /** the <code>NumberFormat</code> being used. */
057        private NumberFormat format;
058    
059        /** the lower bound for argument values. */
060        private Number minimum = null;
061    
062        /** the upper bound for argument values */
063        private Number maximum = null;
064    
065        /**
066         * Creates a new NumberValidator based on the specified NumberFormat
067         * @param format the format of numbers to accept
068         */
069        public NumberValidator(final NumberFormat format) {
070            setFormat(format);
071        }
072    
073        /**
074         * Returns a <code>NumberValidator</code> for a currency format
075         * for the current default locale.
076         * @return a <code>NumberValidator</code> for a currency format
077         * for the current default locale.
078         */
079        public static NumberValidator getCurrencyInstance() {
080            return new NumberValidator(NumberFormat.getCurrencyInstance());
081        }
082    
083        /**
084         * Returns a <code>NumberValidator</code> for an integer number format
085         * for the current default locale.
086         * @return a <code>NumberValidator</code> for an integer number format
087         * for the current default locale.
088         */
089        public static NumberValidator getIntegerInstance() {
090            final NumberFormat format = NumberFormat.getNumberInstance();
091            format.setParseIntegerOnly(true);
092    
093            return new NumberValidator(format);
094        }
095    
096        /**
097         * Returns a <code>NumberValidator</code> for a percentage format
098         * for the current default locale.
099         * @return a <code>NumberValidator</code> for a percentage format
100         * for the current default locale.
101         */
102        public static NumberValidator getPercentInstance() {
103            return new NumberValidator(NumberFormat.getPercentInstance());
104        }
105    
106        /**
107         * Returns a <code>NumberValidator</code> for a general-purpose
108         * number format for the current default locale.
109         * @return a <code>NumberValidator</code> for a general-purpose
110         * number format for the current default locale.
111         */
112        public static NumberValidator getNumberInstance() {
113            return new NumberValidator(NumberFormat.getNumberInstance());
114        }
115    
116        /**
117         * Validate the list of values against the list of permitted values.
118         * If a value is valid, replace the string in the <code>values</code>
119         * {@link java.util.List} with the {@link java.lang.Number} instance.
120         *
121         * @see org.apache.commons.cli2.validation.Validator#validate(java.util.List)
122         */
123        public void validate(final List values)
124            throws InvalidArgumentException {
125            for (final ListIterator i = values.listIterator(); i.hasNext();) {
126                final String value = (String) i.next();
127    
128                final ParsePosition pp = new ParsePosition(0);
129                final Number number = format.parse(value, pp);
130    
131                if (pp.getIndex() < value.length()) {
132                    throw new InvalidArgumentException(value);
133                }
134    
135                if (((minimum != null) && (number.doubleValue() < minimum.doubleValue())) ||
136                        ((maximum != null) && (number.doubleValue() > maximum.doubleValue()))) {
137                    throw new InvalidArgumentException(ResourceHelper.getResourceHelper().getMessage(ResourceConstants.NUMBERVALIDATOR_NUMBER_OUTOFRANGE,
138                                                                                                     new Object[] {
139                                                                                                         value
140                                                                                                     }));
141                }
142    
143                i.set(number);
144            }
145        }
146    
147        /**
148         * Return the format being used to validate argument values against.
149         *
150         * @return the format being used to validate argument values against.
151         */
152        public NumberFormat getFormat() {
153            return format;
154        }
155    
156        /**
157         * Specify the format being used to validate argument values against.
158         *
159         * @param format the format being used to validate argument values against.
160         */
161        protected void setFormat(NumberFormat format) {
162            this.format = format;
163        }
164    
165        /**
166         * Return the maximum value allowed for an argument value.
167         *
168         * @return the maximum value allowed for an argument value.
169         */
170        public Number getMaximum() {
171            return maximum;
172        }
173    
174        /**
175         * Specify the maximum value allowed for an argument value.
176         *
177         * @param maximum the maximum value allowed for an argument value.
178         */
179        public void setMaximum(Number maximum) {
180            this.maximum = maximum;
181        }
182    
183        /**
184         * Return the minimum value allowed for an argument value.
185         *
186         * @return the minimum value allowed for an argument value.
187         */
188        public Number getMinimum() {
189            return minimum;
190        }
191    
192        /**
193         * Specify the minimum value allowed for an argument value.
194         *
195         * @param minimum the minimum value allowed for an argument value.
196         */
197        public void setMinimum(Number minimum) {
198            this.minimum = minimum;
199        }
200    }