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    
018    package org.apache.commons.cli;
019    
020    import java.io.Serializable;
021    import java.util.ArrayList;
022    import java.util.List;
023    
024    /** <p>Describes a single command-line option.  It maintains
025     * information regarding the short-name of the option, the long-name,
026     * if any exists, a flag indicating if an argument is required for
027     * this option, and a self-documenting description of the option.</p>
028     *
029     * <p>An Option is not created independantly, but is create through
030     * an instance of {@link Options}.<p>
031     *
032     * @see org.apache.commons.cli.Options
033     * @see org.apache.commons.cli.CommandLine
034     *
035     * @author bob mcwhirter (bob @ werken.com)
036     * @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
037     * @version $Revision: 680644 $, $Date: 2008-07-29 01:13:48 -0700 (Tue, 29 Jul 2008) $
038     */
039    public class Option implements Cloneable, Serializable
040    {
041        private static final long serialVersionUID = 1L;
042    
043        /** constant that specifies the number of argument values has not been specified */
044        public static final int UNINITIALIZED = -1;
045    
046        /** constant that specifies the number of argument values is infinite */
047        public static final int UNLIMITED_VALUES = -2;
048    
049        /** the name of the option */
050        private String opt;
051    
052        /** the long representation of the option */
053        private String longOpt;
054    
055        /** the name of the argument for this option */
056        private String argName = "arg";
057    
058        /** description of the option */
059        private String description;
060    
061        /** specifies whether this option is required to be present */
062        private boolean required;
063    
064        /** specifies whether the argument value of this Option is optional */
065        private boolean optionalArg;
066    
067        /** the number of argument values this option can have */
068        private int numberOfArgs = UNINITIALIZED;
069    
070        /** the type of this Option */
071        private Object type;
072    
073        /** the list of argument values **/
074        private List values = new ArrayList();
075    
076        /** the character that is the value separator */
077        private char valuesep;
078    
079        /**
080         * Creates an Option using the specified parameters.
081         *
082         * @param opt short representation of the option
083         * @param description describes the function of the option
084         *
085         * @throws IllegalArgumentException if there are any non valid
086         * Option characters in <code>opt</code>.
087         */
088        public Option(String opt, String description) throws IllegalArgumentException
089        {
090            this(opt, null, false, description);
091        }
092    
093        /**
094         * Creates an Option using the specified parameters.
095         *
096         * @param opt short representation of the option
097         * @param hasArg specifies whether the Option takes an argument or not
098         * @param description describes the function of the option
099         *
100         * @throws IllegalArgumentException if there are any non valid
101         * Option characters in <code>opt</code>.
102         */
103        public Option(String opt, boolean hasArg, String description) throws IllegalArgumentException
104        {
105            this(opt, null, hasArg, description);
106        }
107    
108        /**
109         * Creates an Option using the specified parameters.
110         *
111         * @param opt short representation of the option
112         * @param longOpt the long representation of the option
113         * @param hasArg specifies whether the Option takes an argument or not
114         * @param description describes the function of the option
115         *
116         * @throws IllegalArgumentException if there are any non valid
117         * Option characters in <code>opt</code>.
118         */
119        public Option(String opt, String longOpt, boolean hasArg, String description)
120               throws IllegalArgumentException
121        {
122            // ensure that the option is valid
123            OptionValidator.validateOption(opt);
124    
125            this.opt = opt;
126            this.longOpt = longOpt;
127    
128            // if hasArg is set then the number of arguments is 1
129            if (hasArg)
130            {
131                this.numberOfArgs = 1;
132            }
133    
134            this.description = description;
135        }
136    
137        /**
138         * Returns the id of this Option.  This is only set when the
139         * Option shortOpt is a single character.  This is used for switch
140         * statements.
141         *
142         * @return the id of this Option
143         */
144        public int getId()
145        {
146            return getKey().charAt(0);
147        }
148    
149        /**
150         * Returns the 'unique' Option identifier.
151         * 
152         * @return the 'unique' Option identifier
153         */
154        String getKey()
155        {
156            // if 'opt' is null, then it is a 'long' option
157            if (opt == null)
158            {
159                return longOpt;
160            }
161    
162            return opt;
163        }
164    
165        /** 
166         * Retrieve the name of this Option.
167         *
168         * It is this String which can be used with
169         * {@link CommandLine#hasOption(String opt)} and
170         * {@link CommandLine#getOptionValue(String opt)} to check
171         * for existence and argument.
172         *
173         * @return The name of this option
174         */
175        public String getOpt()
176        {
177            return opt;
178        }
179    
180        /**
181         * Retrieve the type of this Option.
182         * 
183         * @return The type of this option
184         */
185        public Object getType()
186        {
187            return type;
188        }
189    
190        /**
191         * Sets the type of this Option.
192         *
193         * @param type the type of this Option
194         */
195        public void setType(Object type)
196        {
197            this.type = type;
198        }
199    
200        /** 
201         * Retrieve the long name of this Option.
202         *
203         * @return Long name of this option, or null, if there is no long name
204         */
205        public String getLongOpt()
206        {
207            return longOpt;
208        }
209    
210        /**
211         * Sets the long name of this Option.
212         *
213         * @param longOpt the long name of this Option
214         */
215        public void setLongOpt(String longOpt)
216        {
217            this.longOpt = longOpt;
218        }
219    
220        /**
221         * Sets whether this Option can have an optional argument.
222         *
223         * @param optionalArg specifies whether the Option can have
224         * an optional argument.
225         */
226        public void setOptionalArg(boolean optionalArg)
227        {
228            this.optionalArg = optionalArg;
229        }
230    
231        /**
232         * @return whether this Option can have an optional argument
233         */
234        public boolean hasOptionalArg()
235        {
236            return optionalArg;
237        }
238    
239        /** 
240         * Query to see if this Option has a long name
241         *
242         * @return boolean flag indicating existence of a long name
243         */
244        public boolean hasLongOpt()
245        {
246            return longOpt != null;
247        }
248    
249        /** 
250         * Query to see if this Option requires an argument
251         *
252         * @return boolean flag indicating if an argument is required
253         */
254        public boolean hasArg()
255        {
256            return numberOfArgs > 0 || numberOfArgs == UNLIMITED_VALUES;
257        }
258    
259        /** 
260         * Retrieve the self-documenting description of this Option
261         *
262         * @return The string description of this option
263         */
264        public String getDescription()
265        {
266            return description;
267        }
268    
269        /**
270         * Sets the self-documenting description of this Option
271         *
272         * @param description The description of this option
273         * @since 1.1
274         */
275        public void setDescription(String description)
276        {
277            this.description = description;
278        }
279    
280        /** 
281         * Query to see if this Option requires an argument
282         *
283         * @return boolean flag indicating if an argument is required
284         */
285        public boolean isRequired()
286        {
287            return required;
288        }
289    
290        /**
291         * Sets whether this Option is mandatory.
292         *
293         * @param required specifies whether this Option is mandatory
294         */
295        public void setRequired(boolean required)
296        {
297            this.required = required;
298        }
299    
300        /**
301         * Sets the display name for the argument value.
302         *
303         * @param argName the display name for the argument value.
304         */
305        public void setArgName(String argName)
306        {
307            this.argName = argName;
308        }
309    
310        /**
311         * Gets the display name for the argument value.
312         *
313         * @return the display name for the argument value.
314         */
315        public String getArgName()
316        {
317            return argName;
318        }
319    
320        /**
321         * Returns whether the display name for the argument value
322         * has been set.
323         *
324         * @return if the display name for the argument value has been
325         * set.
326         */
327        public boolean hasArgName()
328        {
329            return argName != null && argName.length() > 0;
330        }
331    
332        /** 
333         * Query to see if this Option can take many values.
334         *
335         * @return boolean flag indicating if multiple values are allowed
336         */
337        public boolean hasArgs()
338        {
339            return numberOfArgs > 1 || numberOfArgs == UNLIMITED_VALUES;
340        }
341    
342        /** 
343         * Sets the number of argument values this Option can take.
344         *
345         * @param num the number of argument values
346         */
347        public void setArgs(int num)
348        {
349            this.numberOfArgs = num;
350        }
351    
352        /**
353         * Sets the value separator.  For example if the argument value
354         * was a Java property, the value separator would be '='.
355         *
356         * @param sep The value separator.
357         */
358        public void setValueSeparator(char sep)
359        {
360            this.valuesep = sep;
361        }
362    
363        /**
364         * Returns the value separator character.
365         *
366         * @return the value separator character.
367         */
368        public char getValueSeparator()
369        {
370            return valuesep;
371        }
372    
373        /**
374         * Return whether this Option has specified a value separator.
375         * 
376         * @return whether this Option has specified a value separator.
377         * @since 1.1
378         */
379        public boolean hasValueSeparator()
380        {
381            return valuesep > 0;
382        }
383    
384        /** 
385         * Returns the number of argument values this Option can take.
386         *
387         * @return num the number of argument values
388         */
389        public int getArgs()
390        {
391            return numberOfArgs;
392        }
393    
394        /**
395         * Adds the specified value to this Option.
396         * 
397         * @param value is a/the value of this Option
398         */
399        void addValueForProcessing(String value)
400        {
401            switch (numberOfArgs)
402            {
403                case UNINITIALIZED:
404                    throw new RuntimeException("NO_ARGS_ALLOWED");
405    
406                default:
407                    processValue(value);
408            }
409        }
410    
411        /**
412         * Processes the value.  If this Option has a value separator
413         * the value will have to be parsed into individual tokens.  When
414         * n-1 tokens have been processed and there are more value separators
415         * in the value, parsing is ceased and the remaining characters are
416         * added as a single token.
417         *
418         * @param value The String to be processed.
419         *
420         * @since 1.0.1
421         */
422        private void processValue(String value)
423        {
424            // this Option has a separator character
425            if (hasValueSeparator())
426            {
427                // get the separator character
428                char sep = getValueSeparator();
429    
430                // store the index for the value separator
431                int index = value.indexOf(sep);
432    
433                // while there are more value separators
434                while (index != -1)
435                {
436                    // next value to be added 
437                    if (values.size() == (numberOfArgs - 1))
438                    {
439                        break;
440                    }
441    
442                    // store
443                    add(value.substring(0, index));
444    
445                    // parse
446                    value = value.substring(index + 1);
447    
448                    // get new index
449                    index = value.indexOf(sep);
450                }
451            }
452    
453            // store the actual value or the last value that has been parsed
454            add(value);
455        }
456    
457        /**
458         * Add the value to this Option.  If the number of arguments
459         * is greater than zero and there is enough space in the list then
460         * add the value.  Otherwise, throw a runtime exception.
461         *
462         * @param value The value to be added to this Option
463         *
464         * @since 1.0.1
465         */
466        private void add(String value)
467        {
468            if ((numberOfArgs > 0) && (values.size() > (numberOfArgs - 1)))
469            {
470                throw new RuntimeException("Cannot add value, list full.");
471            }
472    
473            // store value
474            values.add(value);
475        }
476    
477        /**
478         * Returns the specified value of this Option or 
479         * <code>null</code> if there is no value.
480         *
481         * @return the value/first value of this Option or 
482         * <code>null</code> if there is no value.
483         */
484        public String getValue()
485        {
486            return hasNoValues() ? null : (String) values.get(0);
487        }
488    
489        /**
490         * Returns the specified value of this Option or 
491         * <code>null</code> if there is no value.
492         *
493         * @param index The index of the value to be returned.
494         *
495         * @return the specified value of this Option or 
496         * <code>null</code> if there is no value.
497         *
498         * @throws IndexOutOfBoundsException if index is less than 1
499         * or greater than the number of the values for this Option.
500         */
501        public String getValue(int index) throws IndexOutOfBoundsException
502        {
503            return hasNoValues() ? null : (String) values.get(index);
504        }
505    
506        /**
507         * Returns the value/first value of this Option or the 
508         * <code>defaultValue</code> if there is no value.
509         *
510         * @param defaultValue The value to be returned if ther
511         * is no value.
512         *
513         * @return the value/first value of this Option or the 
514         * <code>defaultValue</code> if there are no values.
515         */
516        public String getValue(String defaultValue)
517        {
518            String value = getValue();
519    
520            return (value != null) ? value : defaultValue;
521        }
522    
523        /**
524         * Return the values of this Option as a String array 
525         * or null if there are no values
526         *
527         * @return the values of this Option as a String array 
528         * or null if there are no values
529         */
530        public String[] getValues()
531        {
532            return hasNoValues() ? null : (String[]) values.toArray(new String[values.size()]);
533        }
534    
535        /**
536         * @return the values of this Option as a List
537         * or null if there are no values
538         */
539        public List getValuesList()
540        {
541            return values;
542        }
543    
544        /** 
545         * Dump state, suitable for debugging.
546         *
547         * @return Stringified form of this object
548         */
549        public String toString()
550        {
551            StringBuffer buf = new StringBuffer().append("[ option: ");
552    
553            buf.append(opt);
554    
555            if (longOpt != null)
556            {
557                buf.append(" ").append(longOpt);
558            }
559    
560            buf.append(" ");
561    
562            if (hasArgs())
563            {
564                buf.append("[ARG...]");
565            }
566            else if (hasArg())
567            {
568                buf.append(" [ARG]");
569            }
570    
571            buf.append(" :: ").append(description);
572    
573            if (type != null)
574            {
575                buf.append(" :: ").append(type);
576            }
577    
578            buf.append(" ]");
579    
580            return buf.toString();
581        }
582    
583        /**
584         * Returns whether this Option has any values.
585         *
586         * @return whether this Option has any values.
587         */
588        private boolean hasNoValues()
589        {
590            return values.isEmpty();
591        }
592    
593        public boolean equals(Object o)
594        {
595            if (this == o)
596            {
597                return true;
598            }
599            if (o == null || getClass() != o.getClass())
600            {
601                return false;
602            }
603    
604            Option option = (Option) o;
605    
606    
607            if (opt != null ? !opt.equals(option.opt) : option.opt != null)
608            {
609                return false;
610            }
611            if (longOpt != null ? !longOpt.equals(option.longOpt) : option.longOpt != null)
612            {
613                return false;
614            }
615    
616            return true;
617        }
618    
619        public int hashCode()
620        {
621            int result;
622            result = (opt != null ? opt.hashCode() : 0);
623            result = 31 * result + (longOpt != null ? longOpt.hashCode() : 0);
624            return result;
625        }
626    
627        /**
628         * A rather odd clone method - due to incorrect code in 1.0 it is public 
629         * and in 1.1 rather than throwing a CloneNotSupportedException it throws 
630         * a RuntimeException so as to maintain backwards compat at the API level. 
631         *
632         * After calling this method, it is very likely you will want to call 
633         * clearValues(). 
634         *
635         * @throws RuntimeException
636         */
637        public Object clone()
638        {
639            try
640            {
641                Option option = (Option) super.clone();
642                option.values = new ArrayList(values);
643                return option;
644            }
645            catch (CloneNotSupportedException cnse)
646            {
647                throw new RuntimeException("A CloneNotSupportedException was thrown: " + cnse.getMessage());
648            }
649        }
650    
651        /**
652         * Clear the Option values. After a parse is complete, these are left with
653         * data in them and they need clearing if another parse is done.
654         *
655         * See: <a href="https://issues.apache.org/jira/browse/CLI-71">CLI-71</a>
656         */
657        void clearValues()
658        {
659            values.clear();
660        }
661    
662        /**
663         * This method is not intended to be used. It was a piece of internal 
664         * API that was made public in 1.0. It currently throws an UnsupportedOperationException. 
665         * @deprecated
666         * @throws UnsupportedOperationException
667         */
668        public boolean addValue(String value)
669        {
670            throw new UnsupportedOperationException("The addValue method is not intended for client use. "
671                    + "Subclasses should use the addValueForProcessing method instead. ");
672        }
673    
674    }