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