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 */
017package org.apache.commons.cli2.validation;
018
019import java.text.NumberFormat;
020import java.text.ParsePosition;
021
022import java.util.List;
023import java.util.ListIterator;
024
025import org.apache.commons.cli2.resource.ResourceConstants;
026import 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 */
055public 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}