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 static org.apache.commons.cli.Util.EMPTY_STRING_ARRAY;
021
022import java.io.Serializable;
023import java.util.ArrayList;
024import java.util.List;
025import java.util.Objects;
026
027/**
028 * Describes a single command-line option. It maintains information regarding the short-name of the option, the
029 * long-name, if any exists, a flag indicating if an argument is required for this option, and a self-documenting
030 * description of the option.
031 * <p>
032 * An Option is not created independently, but is created through an instance of {@link Options}. An Option is required
033 * to have at least a short or a long-name.
034 * </p>
035 * <p>
036 * <b>Note:</b> once an {@link Option} has been added to an instance of {@link Options}, its required flag cannot be
037 * changed.
038 * </p>
039 *
040 * @see org.apache.commons.cli.Options
041 * @see org.apache.commons.cli.CommandLine
042 */
043public class Option implements Cloneable, Serializable {
044
045    /**
046     * A nested builder class to create {@code Option} instances using descriptive methods.
047     * <p>
048     * Example usage:
049     * </p>
050     *
051     * <pre>
052     * Option option = Option.builder("a").required(true).longOpt("arg-name").build();
053     * </pre>
054     *
055     * @since 1.3
056     */
057    public static final class Builder {
058
059        /** The name of the option */
060        private String option;
061
062        /** description of the option */
063        private String description;
064
065        /** The long representation of the option */
066        private String longOption;
067
068        /** The name of the argument for this option */
069        private String argName;
070
071        /** specifies whether this option is required to be present */
072        private boolean required;
073
074        /** specifies whether the argument value of this Option is optional */
075        private boolean optionalArg;
076
077        /** The number of argument values this option can have */
078        private int argCount = UNINITIALIZED;
079
080        /** The type of this Option */
081        private Class<?> type = String.class;
082
083        /** The character that is the value separator */
084        private char valueSeparator;
085
086        /**
087         * Constructs a new {@code Builder} with the minimum required parameters for an {@code Option} instance.
088         *
089         * @param option short representation of the option
090         * @throws IllegalArgumentException if there are any non valid Option characters in {@code opt}
091         */
092        private Builder(final String option) throws IllegalArgumentException {
093            option(option);
094        }
095
096        /**
097         * Sets the display name for the argument value.
098         *
099         * @param argName the display name for the argument value.
100         * @return this builder, to allow method chaining
101         */
102        public Builder argName(final String argName) {
103            this.argName = argName;
104            return this;
105        }
106
107        /**
108         * Constructs an Option with the values declared by this {@link Builder}.
109         *
110         * @return the new {@link Option}
111         * @throws IllegalArgumentException if neither {@code opt} or {@code longOpt} has been set
112         */
113        public Option build() {
114            if (option == null && longOption == null) {
115                throw new IllegalArgumentException("Either opt or longOpt must be specified");
116            }
117            return new Option(this);
118        }
119
120        /**
121         * Sets the description for this option.
122         *
123         * @param description the description of the option.
124         * @return this builder, to allow method chaining
125         */
126        public Builder desc(final String description) {
127            this.description = description;
128            return this;
129        }
130
131        /**
132         * Indicates that the Option will require an argument.
133         *
134         * @return this builder, to allow method chaining
135         */
136        public Builder hasArg() {
137            return hasArg(true);
138        }
139
140        /**
141         * Indicates if the Option has an argument or not.
142         *
143         * @param hasArg specifies whether the Option takes an argument or not
144         * @return this builder, to allow method chaining
145         */
146        public Builder hasArg(final boolean hasArg) {
147            // set to UNINITIALIZED when no arg is specified to be compatible with OptionBuilder
148            argCount = hasArg ? 1 : Option.UNINITIALIZED;
149            return this;
150        }
151
152        /**
153         * Indicates that the Option can have unlimited argument values.
154         *
155         * @return this builder, to allow method chaining
156         */
157        public Builder hasArgs() {
158            argCount = Option.UNLIMITED_VALUES;
159            return this;
160        }
161
162        /**
163         * Sets the long name of the Option.
164         *
165         * @param longOpt the long name of the Option
166         * @return this builder, to allow method chaining
167         */
168        public Builder longOpt(final String longOpt) {
169            this.longOption = longOpt;
170            return this;
171        }
172
173        /**
174         * Sets the number of argument values the Option can take.
175         *
176         * @param argCount the number of argument values
177         * @return this builder, to allow method chaining
178         */
179        public Builder numberOfArgs(final int argCount) {
180            this.argCount = argCount;
181            return this;
182        }
183
184        /**
185         * Sets the name of the Option.
186         *
187         * @param option the name of the Option
188         * @return this builder, to allow method chaining
189         * @throws IllegalArgumentException if there are any non valid Option characters in {@code opt}
190         * @since 1.5.0
191         */
192        public Builder option(final String option) throws IllegalArgumentException {
193            this.option = OptionValidator.validate(option);
194            return this;
195        }
196
197        /**
198         * Sets whether the Option can have an optional argument.
199         *
200         * @param optionalArg specifies whether the Option can have an optional argument.
201         * @return this builder, to allow method chaining
202         */
203        public Builder optionalArg(final boolean optionalArg) {
204            this.argCount = optionalArg ? 1 : UNINITIALIZED;
205            this.optionalArg = optionalArg;
206            return this;
207        }
208
209        /**
210         * Marks this Option as required.
211         *
212         * @return this builder, to allow method chaining
213         */
214        public Builder required() {
215            return required(true);
216        }
217
218        /**
219         * Sets whether the Option is mandatory.
220         *
221         * @param required specifies whether the Option is mandatory
222         * @return this builder, to allow method chaining
223         */
224        public Builder required(final boolean required) {
225            this.required = required;
226            return this;
227        }
228
229        /**
230         * Sets the type of the Option.
231         *
232         * @param type the type of the Option
233         * @return this builder, to allow method chaining
234         */
235        public Builder type(final Class<?> type) {
236            this.type = type;
237            return this;
238        }
239
240        /**
241         * The Option will use '=' as a means to separate argument value.
242         *
243         * @return this builder, to allow method chaining
244         */
245        public Builder valueSeparator() {
246            return valueSeparator('=');
247        }
248
249        /**
250         * The Option will use {@code sep} as a means to separate argument values.
251         * <p>
252         * <b>Example:</b>
253         * </p>
254         *
255         * <pre>
256         * Option opt = Option.builder("D").hasArgs().valueSeparator('=').build();
257         * Options options = new Options();
258         * options.addOption(opt);
259         * String[] args = {"-Dkey=value"};
260         * CommandLineParser parser = new DefaultParser();
261         * CommandLine line = parser.parse(options, args);
262         * String propertyName = line.getOptionValues("D")[0]; // will be "key"
263         * String propertyValue = line.getOptionValues("D")[1]; // will be "value"
264         * </pre>
265         *
266         * @param valueSeparator The value separator.
267         * @return this builder, to allow method chaining
268         */
269        public Builder valueSeparator(final char valueSeparator) {
270            this.valueSeparator = valueSeparator;
271            return this;
272        }
273    }
274
275    /** Specifies the number of argument values has not been specified */
276    public static final int UNINITIALIZED = -1;
277
278    /** Specifies the number of argument values is infinite */
279    public static final int UNLIMITED_VALUES = -2;
280
281    /** The serial version UID. */
282    private static final long serialVersionUID = 1L;
283
284    /** Empty array. */
285    static final Option[] EMPTY_ARRAY = {};
286
287    /**
288     * Returns a {@link Builder} to create an {@link Option} using descriptive methods.
289     *
290     * @return a new {@link Builder} instance
291     * @since 1.3
292     */
293    public static Builder builder() {
294        return builder(null);
295    }
296
297    /**
298     * Returns a {@link Builder} to create an {@link Option} using descriptive methods.
299     *
300     * @param option short representation of the option
301     * @return a new {@link Builder} instance
302     * @throws IllegalArgumentException if there are any non valid Option characters in {@code opt}
303     * @since 1.3
304     */
305    public static Builder builder(final String option) {
306        return new Builder(option);
307    }
308
309    /** The name of the option. */
310    private final String option;
311
312    /** The long representation of the option. */
313    private String longOption;
314
315    /** The name of the argument for this option. */
316    private String argName;
317
318    /** Description of the option. */
319    private String description;
320
321    /** Specifies whether this option is required to be present. */
322    private boolean required;
323
324    /** Specifies whether the argument value of this Option is optional. */
325    private boolean optionalArg;
326
327    /** The number of argument values this option can have. */
328    private int argCount = UNINITIALIZED;
329
330    /** The type of this Option. */
331    private Class<?> type = String.class;
332
333    /** The list of argument values. **/
334    private List<String> values = new ArrayList<>();
335
336    /** The character that is the value separator. */
337    private char valuesep;
338
339    /**
340     * Private constructor used by the nested Builder class.
341     *
342     * @param builder builder used to create this option
343     */
344    private Option(final Builder builder) {
345        this.argName = builder.argName;
346        this.description = builder.description;
347        this.longOption = builder.longOption;
348        this.argCount = builder.argCount;
349        this.option = builder.option;
350        this.optionalArg = builder.optionalArg;
351        this.required = builder.required;
352        this.type = builder.type;
353        this.valuesep = builder.valueSeparator;
354    }
355
356    /**
357     * Creates an Option using the specified parameters.
358     *
359     * @param option short representation of the option
360     * @param hasArg specifies whether the Option takes an argument or not
361     * @param description describes the function of the option
362     *
363     * @throws IllegalArgumentException if there are any non valid Option characters in {@code opt}.
364     */
365    public Option(final String option, final boolean hasArg, final String description) throws IllegalArgumentException {
366        this(option, null, hasArg, description);
367    }
368
369    /**
370     * Creates an Option using the specified parameters. The option does not take an argument.
371     *
372     * @param option short representation of the option
373     * @param description describes the function of the option
374     *
375     * @throws IllegalArgumentException if there are any non valid Option characters in {@code opt}.
376     */
377    public Option(final String option, final String description) throws IllegalArgumentException {
378        this(option, null, false, description);
379    }
380
381    /**
382     * Creates an Option using the specified parameters.
383     *
384     * @param option short representation of the option
385     * @param longOption the long representation of the option
386     * @param hasArg specifies whether the Option takes an argument or not
387     * @param description describes the function of the option
388     *
389     * @throws IllegalArgumentException if there are any non valid Option characters in {@code opt}.
390     */
391    public Option(final String option, final String longOption, final boolean hasArg, final String description) throws IllegalArgumentException {
392        // ensure that the option is valid
393        this.option = OptionValidator.validate(option);
394        this.longOption = longOption;
395
396        // if hasArg is set then the number of arguments is 1
397        if (hasArg) {
398            this.argCount = 1;
399        }
400
401        this.description = description;
402    }
403
404    /**
405     * Tells if the option can accept more arguments.
406     *
407     * @return false if the maximum number of arguments is reached
408     * @since 1.3
409     */
410    boolean acceptsArg() {
411        return (hasArg() || hasArgs() || hasOptionalArg()) && (argCount <= 0 || values.size() < argCount);
412    }
413
414    /**
415     * Add the value to this Option. If the number of arguments is greater than zero and there is enough space in the list
416     * then add the value. Otherwise, throw a runtime exception.
417     *
418     * @param value The value to be added to this Option
419     *
420     * @since 1.0.1
421     */
422    private void add(final String value) {
423        if (!acceptsArg()) {
424            throw new IllegalArgumentException("Cannot add value, list full.");
425        }
426
427        // store value
428        values.add(value);
429    }
430
431    /**
432     * This method is not intended to be used. It was a piece of internal API that was made public in 1.0. It currently
433     * throws an UnsupportedOperationException.
434     *
435     * @param value the value to add
436     * @return always throws an {@link UnsupportedOperationException}
437     * @throws UnsupportedOperationException always
438     * @deprecated Unused.
439     */
440    @Deprecated
441    public boolean addValue(final String value) {
442        throw new UnsupportedOperationException(
443            "The addValue method is not intended for client use. " + "Subclasses should use the addValueForProcessing method instead. ");
444    }
445
446    /**
447     * Adds the specified value to this Option.
448     *
449     * @param value is a/the value of this Option
450     */
451    void addValueForProcessing(final String value) {
452        if (argCount == UNINITIALIZED) {
453            throw new IllegalArgumentException("NO_ARGS_ALLOWED");
454        }
455        processValue(value);
456    }
457
458    /**
459     * Clear the Option values. After a parse is complete, these are left with data in them and they need clearing if
460     * another parse is done.
461     *
462     * See: <a href="https://issues.apache.org/jira/browse/CLI-71">CLI-71</a>
463     */
464    void clearValues() {
465        values.clear();
466    }
467
468    /**
469     * A rather odd clone method - due to incorrect code in 1.0 it is public and in 1.1 rather than throwing a
470     * CloneNotSupportedException it throws a RuntimeException so as to maintain backwards compat at the API level.
471     *
472     * After calling this method, it is very likely you will want to call clearValues().
473     *
474     * @return a clone of this Option instance
475     * @throws RuntimeException if a {@link CloneNotSupportedException} has been thrown by {@code super.clone()}
476     */
477    @Override
478    public Object clone() {
479        try {
480            final Option option = (Option) super.clone();
481            option.values = new ArrayList<>(values);
482            return option;
483        } catch (final CloneNotSupportedException e) {
484            throw new UnsupportedOperationException(e.getMessage(), e);
485        }
486    }
487
488    @Override
489    public boolean equals(final Object obj) {
490        if (this == obj) {
491            return true;
492        }
493        if (!(obj instanceof Option)) {
494            return false;
495        }
496        final Option other = (Option) obj;
497        return Objects.equals(longOption, other.longOption) && Objects.equals(option, other.option);
498    }
499
500    /**
501     * Gets the display name for the argument value.
502     *
503     * @return the display name for the argument value.
504     */
505    public String getArgName() {
506        return argName;
507    }
508
509    /**
510     * Gets the number of argument values this Option can take.
511     *
512     * <p>
513     * A value equal to the constant {@link #UNINITIALIZED} (= -1) indicates the number of arguments has not been specified.
514     * A value equal to the constant {@link #UNLIMITED_VALUES} (= -2) indicates that this options takes an unlimited amount
515     * of values.
516     * </p>
517     *
518     * @return num the number of argument values
519     * @see #UNINITIALIZED
520     * @see #UNLIMITED_VALUES
521     */
522    public int getArgs() {
523        return argCount;
524    }
525
526    /**
527     * Gets the self-documenting description of this Option
528     *
529     * @return The string description of this option
530     */
531    public String getDescription() {
532        return description;
533    }
534
535    /**
536     * Gets the id of this Option. This is only set when the Option shortOpt is a single character. This is used for
537     * switch statements.
538     *
539     * @return the id of this Option
540     */
541    public int getId() {
542        return getKey().charAt(0);
543    }
544
545    /**
546     * Gets the 'unique' Option identifier.
547     *
548     * @return the 'unique' Option identifier
549     */
550    String getKey() {
551        // if 'opt' is null, then it is a 'long' option
552        return option == null ? longOption : option;
553    }
554
555    /**
556     * Gets the long name of this Option.
557     *
558     * @return Long name of this option, or null, if there is no long name
559     */
560    public String getLongOpt() {
561        return longOption;
562    }
563
564    /**
565     * Gets the name of this Option.
566     *
567     * It is this String which can be used with {@link CommandLine#hasOption(String opt)} and
568     * {@link CommandLine#getOptionValue(String opt)} to check for existence and argument.
569     *
570     * @return The name of this option
571     */
572    public String getOpt() {
573        return option;
574    }
575
576    /**
577     * Gets the type of this Option.
578     *
579     * @return The type of this option
580     */
581    public Object getType() {
582        return type;
583    }
584
585    /**
586     * Gets the specified value of this Option or {@code null} if there is no value.
587     *
588     * @return the value/first value of this Option or {@code null} if there is no value.
589     */
590    public String getValue() {
591        return hasNoValues() ? null : values.get(0);
592    }
593
594    /**
595     * Gets the specified value of this Option or {@code null} if there is no value.
596     *
597     * @param index The index of the value to be returned.
598     *
599     * @return the specified value of this Option or {@code null} if there is no value.
600     *
601     * @throws IndexOutOfBoundsException if index is less than 1 or greater than the number of the values for this Option.
602     */
603    public String getValue(final int index) throws IndexOutOfBoundsException {
604        return hasNoValues() ? null : values.get(index);
605    }
606
607    /**
608     * Gets the value/first value of this Option or the {@code defaultValue} if there is no value.
609     *
610     * @param defaultValue The value to be returned if there is no value.
611     *
612     * @return the value/first value of this Option or the {@code defaultValue} if there are no values.
613     */
614    public String getValue(final String defaultValue) {
615        final String value = getValue();
616
617        return value != null ? value : defaultValue;
618    }
619
620    /**
621     * Gets the values of this Option as a String array or null if there are no values
622     *
623     * @return the values of this Option as a String array or null if there are no values
624     */
625    public String[] getValues() {
626        return hasNoValues() ? null : values.toArray(EMPTY_STRING_ARRAY);
627    }
628
629    /**
630     * Gets the value separator character.
631     *
632     * @return the value separator character.
633     */
634    public char getValueSeparator() {
635        return valuesep;
636    }
637
638    /**
639     * Gets the values of this Option as a List or null if there are no values.
640     *
641     * @return the values of this Option as a List or null if there are no values
642     */
643    public List<String> getValuesList() {
644        return values;
645    }
646
647    /**
648     * Query to see if this Option requires an argument
649     *
650     * @return boolean flag indicating if an argument is required
651     */
652    public boolean hasArg() {
653        return argCount > 0 || argCount == UNLIMITED_VALUES;
654    }
655
656    /**
657     * Returns whether the display name for the argument value has been set.
658     *
659     * @return if the display name for the argument value has been set.
660     */
661    public boolean hasArgName() {
662        return argName != null && !argName.isEmpty();
663    }
664
665    /**
666     * Query to see if this Option can take many values.
667     *
668     * @return boolean flag indicating if multiple values are allowed
669     */
670    public boolean hasArgs() {
671        return argCount > 1 || argCount == UNLIMITED_VALUES;
672    }
673
674    @Override
675    public int hashCode() {
676        return Objects.hash(longOption, option);
677    }
678
679    /**
680     * Query to see if this Option has a long name
681     *
682     * @return boolean flag indicating existence of a long name
683     */
684    public boolean hasLongOpt() {
685        return longOption != null;
686    }
687
688    /**
689     * Returns whether this Option has any values.
690     *
691     * @return whether this Option has any values.
692     */
693    private boolean hasNoValues() {
694        return values.isEmpty();
695    }
696
697    /**
698     * @return whether this Option can have an optional argument
699     */
700    public boolean hasOptionalArg() {
701        return optionalArg;
702    }
703
704    /**
705     * Return whether this Option has specified a value separator.
706     *
707     * @return whether this Option has specified a value separator.
708     * @since 1.1
709     */
710    public boolean hasValueSeparator() {
711        return valuesep > 0;
712    }
713
714    /**
715     * Query to see if this Option is mandatory
716     *
717     * @return boolean flag indicating whether this Option is mandatory
718     */
719    public boolean isRequired() {
720        return required;
721    }
722
723    /**
724     * Processes the value. If this Option has a value separator the value will have to be parsed into individual tokens.
725     * When n-1 tokens have been processed and there are more value separators in the value, parsing is ceased and the
726     * remaining characters are added as a single token.
727     *
728     * @param value The String to be processed.
729     *
730     * @since 1.0.1
731     */
732    private void processValue(String value) {
733        // this Option has a separator character
734        if (hasValueSeparator()) {
735            // get the separator character
736            final char sep = getValueSeparator();
737
738            // store the index for the value separator
739            int index = value.indexOf(sep);
740
741            // while there are more value separators
742            while (index != -1) {
743                // next value to be added
744                if (values.size() == argCount - 1) {
745                    break;
746                }
747
748                // store
749                add(value.substring(0, index));
750
751                // parse
752                value = value.substring(index + 1);
753
754                // get new index
755                index = value.indexOf(sep);
756            }
757        }
758
759        // store the actual value or the last value that has been parsed
760        add(value);
761    }
762
763    /**
764     * Tells if the option requires more arguments to be valid.
765     *
766     * @return false if the option doesn't require more arguments
767     * @since 1.3
768     */
769    boolean requiresArg() {
770        if (optionalArg) {
771            return false;
772        }
773        if (argCount == UNLIMITED_VALUES) {
774            return values.isEmpty();
775        }
776        return acceptsArg();
777    }
778
779    /**
780     * Sets the display name for the argument value.
781     *
782     * @param argName the display name for the argument value.
783     */
784    public void setArgName(final String argName) {
785        this.argName = argName;
786    }
787
788    /**
789     * Sets the number of argument values this Option can take.
790     *
791     * @param num the number of argument values
792     */
793    public void setArgs(final int num) {
794        this.argCount = num;
795    }
796
797    /**
798     * Sets the self-documenting description of this Option
799     *
800     * @param description The description of this option
801     * @since 1.1
802     */
803    public void setDescription(final String description) {
804        this.description = description;
805    }
806
807    /**
808     * Sets the long name of this Option.
809     *
810     * @param longOpt the long name of this Option
811     */
812    public void setLongOpt(final String longOpt) {
813        this.longOption = longOpt;
814    }
815
816    /**
817     * Sets whether this Option can have an optional argument.
818     *
819     * @param optionalArg specifies whether the Option can have an optional argument.
820     */
821    public void setOptionalArg(final boolean optionalArg) {
822        this.optionalArg = optionalArg;
823    }
824
825    /**
826     * Sets whether this Option is mandatory.
827     *
828     * @param required specifies whether this Option is mandatory
829     */
830    public void setRequired(final boolean required) {
831        this.required = required;
832    }
833
834    /**
835     * Sets the type of this Option.
836     *
837     * @param type the type of this Option
838     * @since 1.3
839     */
840    public void setType(final Class<?> type) {
841        this.type = type;
842    }
843
844    /**
845     * Sets the type of this Option.
846     * <p>
847     * <b>Note:</b> this method is kept for binary compatibility and the input type is supposed to be a {@link Class}
848     * object.
849     * </p>
850     *
851     * @param type the type of this Option
852     * @deprecated since 1.3, use {@link #setType(Class)} instead
853     */
854    @Deprecated
855    public void setType(final Object type) {
856        setType((Class<?>) type);
857    }
858
859    /**
860     * Sets the value separator. For example if the argument value was a Java property, the value separator would be '='.
861     *
862     * @param sep The value separator.
863     */
864    public void setValueSeparator(final char sep) {
865        this.valuesep = sep;
866    }
867
868    /**
869     * Dump state, suitable for debugging.
870     *
871     * @return Stringified form of this object
872     */
873    @Override
874    public String toString() {
875        final StringBuilder buf = new StringBuilder().append("[ option: ");
876
877        buf.append(option);
878
879        if (longOption != null) {
880            buf.append(" ").append(longOption);
881        }
882
883        buf.append(" ");
884
885        if (hasArgs()) {
886            buf.append("[ARG...]");
887        } else if (hasArg()) {
888            buf.append(" [ARG]");
889        }
890
891        buf.append(" :: ").append(description);
892
893        if (type != null) {
894            buf.append(" :: ").append(type);
895        }
896
897        buf.append(" ]");
898
899        return buf.toString();
900    }
901}