    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    }