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
018package org.apache.commons.cli;
019
020import java.io.Serializable;
021import java.util.ArrayList;
022import 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 */
039public 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}