001    /*
002     * Copyright 2003-2005 The Apache Software Foundation
003     * Copyright 2005 Stephen McConnell
004     *
005     * Licensed under the Apache License, Version 2.0 (the "License");
006     * you may not use this file except in compliance with the License.
007     * 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 net.dpml.cli.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 net.dpml.cli.resource.ResourceConstants;
026    import net.dpml.cli.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 <a href="http://www.dpml.net">Digital Product Meta Library</a>
053     * @version 1.0.0
054     */
055    public class NumberValidator implements Validator
056    {
057        /** the <code>NumberFormat</code> being used. */
058        private NumberFormat m_format;
059    
060        /** the lower bound for argument values. */
061        private Number m_minimum = null;
062    
063        /** the upper bound for argument values */
064        private Number m_maximum = null;
065    
066        /**
067         * Creates a new NumberValidator based on the specified NumberFormat
068         * @param format the format of numbers to accept
069         */
070        public NumberValidator( final NumberFormat format ) 
071        {
072            setFormat( format );
073        }
074    
075        /**
076         * Returns a <code>NumberValidator</code> for a currency format
077         * for the current default locale.
078         * @return a <code>NumberValidator</code> for a currency format
079         * for the current default locale.
080         */
081        public static NumberValidator getCurrencyInstance() 
082        {
083            return new NumberValidator( NumberFormat.getCurrencyInstance() );
084        }
085    
086        /**
087         * Returns a <code>NumberValidator</code> for an integer number format
088         * for the current default locale.
089         * @return a <code>NumberValidator</code> for an integer number format
090         * for the current default locale.
091         */
092        public static NumberValidator getIntegerInstance()
093        {
094            final NumberFormat format = NumberFormat.getNumberInstance();
095            format.setParseIntegerOnly( true );
096            return new NumberValidator( format );
097        }
098    
099        /**
100         * Returns a <code>NumberValidator</code> for a percentage format
101         * for the current default locale.
102         * @return a <code>NumberValidator</code> for a percentage format
103         * for the current default locale.
104         */
105        public static NumberValidator getPercentInstance() 
106        {
107            return new NumberValidator( NumberFormat.getPercentInstance() );
108        }
109    
110        /**
111         * Returns a <code>NumberValidator</code> for a general-purpose
112         * number format for the current default locale.
113         * @return a <code>NumberValidator</code> for a general-purpose
114         * number format for the current default locale.
115         */
116        public static NumberValidator getNumberInstance()
117        {
118            return new NumberValidator( NumberFormat.getNumberInstance() );
119        }
120    
121       /**
122        * Validate the list of values against the list of permitted values.
123        * If a value is valid, replace the string in the <code>values</code>
124        * {@link java.util.List} with the {@link java.lang.Number} instance.
125        *
126        * @param values the list of values to validate 
127        * @exception InvalidArgumentException if a value is invalid
128        * @see net.dpml.cli.validation.Validator#validate(java.util.List)
129        */
130        public void validate( final List values ) throws InvalidArgumentException 
131        {
132            for( final ListIterator i = values.listIterator(); i.hasNext();) 
133            {
134                final Object next = i.next();
135                if( next instanceof Number )
136                {
137                    return;
138                }
139                final String value = (String) next;
140                final ParsePosition pp = new ParsePosition( 0 );
141                final Number number = m_format.parse( value, pp );
142                if( pp.getIndex() < value.length() )
143                {
144                    throw new InvalidArgumentException( value );
145                }
146                if(
147                  ( ( m_minimum != null ) && ( number.doubleValue() < m_minimum.doubleValue() ) ) 
148                  || ( ( m_maximum != null ) && ( number.doubleValue() > m_maximum.doubleValue() ) )
149                ) 
150                {
151                    throw new InvalidArgumentException(
152                      ResourceHelper.getResourceHelper().getMessage(
153                        ResourceConstants.NUMBERVALIDATOR_NUMBER_OUTOFRANGE,
154                        new Object[]{value} ) );
155                }
156                i.set( number );
157            }
158        }
159    
160        /**
161         * Return the format being used to validate argument values against.
162         *
163         * @return the format being used to validate argument values against.
164         */
165        public NumberFormat getFormat() 
166        {
167            return m_format;
168        }
169    
170        /**
171         * Specify the format being used to validate argument values against.
172         *
173         * @param format the format being used to validate argument values against.
174         */
175        protected void setFormat( NumberFormat format ) 
176        {
177            m_format = format;
178        }
179    
180        /**
181         * Return the maximum value allowed for an argument value.
182         *
183         * @return the maximum value allowed for an argument value.
184         */
185        public Number getMaximum() 
186        {
187            return m_maximum;
188        }
189    
190        /**
191         * Specify the maximum value allowed for an argument value.
192         *
193         * @param maximum the maximum value allowed for an argument value.
194         */
195        public void setMaximum( Number maximum )
196        {
197            m_maximum = maximum;
198        }
199    
200        /**
201         * Return the minimum value allowed for an argument value.
202         *
203         * @return the minimum value allowed for an argument value.
204         */
205        public Number getMinimum()
206        {
207            return m_minimum;
208        }
209    
210        /**
211         * Specify the minimum value allowed for an argument value.
212         *
213         * @param minimum the minimum value allowed for an argument value.
214         */
215        public void setMinimum( Number minimum )
216        {
217            m_minimum = minimum;
218        }
219    }