    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.Iterator;
    023import java.util.LinkedList;
    024import java.util.List;
    025import java.util.Objects;
    026import java.util.Properties;
    027import java.util.function.Consumer;
    028import java.util.function.Supplier;
    029
    030/**
    031 * Represents list of arguments parsed against a {@link Options} descriptor.
    032 * <p>
    033 * It allows querying of a boolean {@link #hasOption(String opt)}, in addition to retrieving the
    034 * {@link #getOptionValue(String opt)} for options requiring arguments.
    035 * </p>
    036 * <p>
    037 * Additionally, any left-over or unrecognized arguments, are available for further processing.
    038 * </p>
    039 */
    040public class CommandLine implements Serializable {
    041
    042    /**
    043     * A nested builder class to create {@code CommandLine} instance using descriptive methods.
    044     *
    045     * @since 1.4
    046     */
    047    public static final class Builder {
    048
    049        /**
    050         * Prints an Option to {@link System#out}.
    051         */
    052        static final Consumer<Option> DEPRECATED_HANDLER = o -> System.out.println(o.toDeprecatedString());
    053
    054        /** The unrecognized options/arguments */
    055        private final List<String> args = new LinkedList<>();
    056
    057        /** The processed options */
    058        private final List<Option> options = new ArrayList<>();
    059
    060        /**
    061         * Deprecated Option handler.
    062         */
    063        private Consumer<Option> deprecatedHandler = DEPRECATED_HANDLER;
    064
    065        /**
    066         * Adds left-over unrecognized option/argument.
    067         *
    068         * @param arg the unrecognized option/argument.
    069         *
    070         * @return this Builder instance for method chaining.
    071         */
    072        public Builder addArg(final String arg) {
    073            if (arg != null) {
    074                args.add(arg);
    075            }
    076            return this;
    077        }
    078
    079        /**
    080         * Adds an option to the command line. The values of the option are stored.
    081         *
    082         * @param opt the processed option.
    083         *
    084         * @return this Builder instance for method chaining.
    085         */
    086        public Builder addOption(final Option opt) {
    087            if (opt != null) {
    088                options.add(opt);
    089            }
    090            return this;
    091        }
    092
    093        /**
    094         * Creates the new instance.
    095         *
    096         * @return the new instance.
    097         */
    098        public CommandLine build() {
    099            return new CommandLine(args, options, deprecatedHandler);
    100        }
    101
    102        /**
    103         * Sets the deprecated option handler.
    104         *
    105         * @param deprecatedHandler the deprecated option handler.
    106         * @return this.
    107         * @since 1.7.0
    108         */
    109        public Builder setDeprecatedHandler(final Consumer<Option> deprecatedHandler) {
    110            this.deprecatedHandler = deprecatedHandler;
    111            return this;
    112        }
    113    }
    114
    115    /** The serial version UID. */
    116    private static final long serialVersionUID = 1L;
    117
    118    /**
    119     * Creates a new builder.
    120     *
    121     * @return a new builder.
    122     * @since 1.7.0
    123     */
    124    public static Builder builder() {
    125        return new Builder();
    126    }
    127
    128    /** The unrecognized options/arguments */
    129    private final List<String> args;
    130
    131    /** The processed options */
    132    private final List<Option> options;
    133
    134    /**
    135     * The deprecated option handler.
    136     * <p>
    137     * If you want to serialize this field, use a serialization proxy.
    138     * </p>
    139     */
    140    private final transient Consumer<Option> deprecatedHandler;
    141
    142    /**
    143     * Creates a command line.
    144     */
    145    protected CommandLine() {
    146        this(new LinkedList<>(), new ArrayList<>(), Builder.DEPRECATED_HANDLER);
    147    }
    148
    149    /**
    150     * Creates a command line.
    151     */
    152    private CommandLine(final List<String> args, final List<Option> options, final Consumer<Option> deprecatedHandler) {
    153        this.args = Objects.requireNonNull(args, "args");
    154        this.options = Objects.requireNonNull(options, "options");
    155        this.deprecatedHandler = deprecatedHandler;
    156    }
    157
    158    /**
    159     * Adds left-over unrecognized option/argument.
    160     *
    161     * @param arg the unrecognized option/argument.
    162     */
    163    protected void addArg(final String arg) {
    164        if (arg != null) {
    165            args.add(arg);
    166        }
    167    }
    168
    169    /**
    170     * Adds an option to the command line. The values of the option are stored.
    171     *
    172     * @param opt the processed option.
    173     */
    174    protected void addOption(final Option opt) {
    175        if (opt != null) {
    176            options.add(opt);
    177        }
    178    }
    179
    180    private <T> T get(final Supplier<T> supplier) {
    181        return supplier == null ? null : supplier.get();
    182    }
    183
    184    /**
    185     * Gets any left-over non-recognized options and arguments
    186     *
    187     * @return remaining items passed in but not parsed as a {@code List}.
    188     */
    189    public List<String> getArgList() {
    190        return args;
    191    }
    192
    193    /**
    194     * Gets any left-over non-recognized options and arguments
    195     *
    196     * @return remaining items passed in but not parsed as an array.
    197     */
    198    public String[] getArgs() {
    199        return args.toArray(Util.EMPTY_STRING_ARRAY);
    200    }
    201
    202    /**
    203     * Gets the {@code Object} type of this {@code Option}.
    204     *
    205     * @deprecated due to System.err message. Instead use getParsedOptionValue(char)
    206     * @param opt the name of the option.
    207     * @return the type of opt.
    208     */
    209    @Deprecated
    210    public Object getOptionObject(final char opt) {
    211        return getOptionObject(String.valueOf(opt));
    212    }
    213
    214    /**
    215     * Gets the {@code Object} type of this {@code Option}.
    216     *
    217     * @param opt the name of the option.
    218     * @return the type of this {@code Option}.
    219     * @deprecated due to System.err message. Instead use getParsedOptionValue(String)
    220     */
    221    @Deprecated
    222    public Object getOptionObject(final String opt) {
    223        try {
    224            return getParsedOptionValue(opt);
    225        } catch (final ParseException pe) {
    226            System.err.println("Exception found converting " + opt + " to desired type: " + pe.getMessage());
    227            return null;
    228        }
    229    }
    230
    231    /**
    232     * Gets the map of values associated to the option. This is convenient for options specifying Java properties like
    233     * <code>-Dparam1=value1
    234     * -Dparam2=value2</code>. All odd numbered values are property keys
    235     * and even numbered values are property values.  If there are an odd number of values
    236     * the last value is assumed to be a boolean flag and the value is "true".
    237     *
    238     * @param option name of the option.
    239     * @return The Properties mapped by the option, never {@code null} even if the option doesn't exists.
    240     * @since 1.5.0
    241     */
    242    public Properties getOptionProperties(final Option option) {
    243        final Properties props = new Properties();
    244        for (final Option processedOption : options) {
    245            if (processedOption.equals(option)) {
    246                processPropertiesFromValues(props, processedOption.getValuesList());
    247            }
    248        }
    249        return props;
    250    }
    251
    252    /**
    253     * Gets the map of values associated to the option. This is convenient for options specifying Java properties like
    254     * <code>-Dparam1=value1
    255     * -Dparam2=value2</code>. The first argument of the option is the key, and the 2nd argument is the value. If the option
    256     * has only one argument ({@code -Dfoo}) it is considered as a boolean flag and the value is {@code "true"}.
    257     *
    258     * @param opt name of the option.
    259     * @return The Properties mapped by the option, never {@code null} even if the option doesn't exists.
    260     * @since 1.2
    261     */
    262    public Properties getOptionProperties(final String opt) {
    263        final Properties props = new Properties();
    264        for (final Option option : options) {
    265            if (opt.equals(option.getOpt()) || opt.equals(option.getLongOpt())) {
    266                processPropertiesFromValues(props, option.getValuesList());
    267            }
    268        }
    269        return props;
    270    }
    271
    272    /**
    273     * Gets an array of the processed {@link Option}s.
    274     *
    275     * @return an array of the processed {@link Option}s.
    276     */
    277    public Option[] getOptions() {
    278        return options.toArray(Option.EMPTY_ARRAY);
    279    }
    280
    281    /**
    282     * Gets the first argument, if any, of this option.
    283     *
    284     * @param opt the character name of the option.
    285     * @return Value of the argument if option is set, and has an argument, otherwise null.
    286     */
    287    public String getOptionValue(final char opt) {
    288        return getOptionValue(String.valueOf(opt));
    289    }
    290
    291    /**
    292     * Gets the argument, if any, of an option.
    293     *
    294     * @param opt character name of the option
    295     * @param defaultValue is the default value to be returned if the option is not specified.
    296     * @return Value of the argument if option is set, and has an argument, otherwise {@code defaultValue}.
    297     */
    298    public String getOptionValue(final char opt, final String defaultValue) {
    299        return getOptionValue(String.valueOf(opt), () -> defaultValue);
    300    }
    301
    302    /**
    303     * Gets the argument, if any, of an option.
    304     *
    305     * @param opt character name of the option
    306     * @param defaultValue is a supplier for the default value to be returned if the option is not specified.
    307     * @return Value of the argument if option is set, and has an argument, otherwise {@code defaultValue}.
    308     * @since 1.7.0
    309     */
    310    public String getOptionValue(final char opt, final Supplier<String> defaultValue) {
    311        return getOptionValue(String.valueOf(opt), defaultValue);
    312    }
    313
    314    /**
    315     * Gets the first argument, if any, of this option.
    316     *
    317     * @param option the name of the option.
    318     * @return Value of the argument if option is set, and has an argument, otherwise null.
    319     * @since 1.5.0
    320     */
    321    public String getOptionValue(final Option option) {
    322        final String[] values = getOptionValues(option);
    323        return values == null ? null : values[0];
    324    }
    325
    326    /**
    327     * Gets the first argument, if any, of an option.
    328     *
    329     * @param option name of the option.
    330     * @param defaultValue is the default value to be returned if the option is not specified.
    331     * @return Value of the argument if option is set, and has an argument, otherwise {@code defaultValue}.
    332     * @since 1.5.0
    333     */
    334    public String getOptionValue(final Option option, final String defaultValue) {
    335        return getOptionValue(option, () -> defaultValue);
    336    }
    337
    338    /**
    339     * Gets the first argument, if any, of an option.
    340     *
    341     * @param option name of the option.
    342     * @param defaultValue is a supplier for the default value to be returned if the option is not specified.
    343     * @return Value of the argument if option is set, and has an argument, otherwise {@code defaultValue}.
    344     * @since 1.7.0
    345     */
    346    public String getOptionValue(final Option option, final Supplier<String> defaultValue) {
    347        final String answer = getOptionValue(option);
    348        return answer != null ? answer : get(defaultValue);
    349    }
    350
    351    /**
    352     * Gets the first argument, if any, of this option.
    353     *
    354     * @param opt the name of the option.
    355     * @return Value of the argument if option is set, and has an argument, otherwise null.
    356     */
    357    public String getOptionValue(final String opt) {
    358        return getOptionValue(resolveOption(opt));
    359    }
    360
    361    /**
    362     * Gets the first argument, if any, of an option.
    363     *
    364     * @param opt name of the option.
    365     * @param defaultValue is the default value to be returned if the option is not specified.
    366     * @return Value of the argument if option is set, and has an argument, otherwise {@code defaultValue}.
    367     */
    368    public String getOptionValue(final String opt, final String defaultValue) {
    369        return getOptionValue(resolveOption(opt), () -> defaultValue);
    370    }
    371
    372    /**
    373     * Gets the first argument, if any, of an option.
    374     *
    375     * @param opt name of the option.
    376     * @param defaultValue is a supplier for the default value to be returned if the option is not specified.
    377     * @return Value of the argument if option is set, and has an argument, otherwise {@code defaultValue}.
    378     * @since 1.7.0
    379     */
    380    public String getOptionValue(final String opt, final Supplier<String> defaultValue) {
    381        return getOptionValue(resolveOption(opt), defaultValue);
    382    }
    383
    384
    385    /**
    386     * Gets the array of values, if any, of an option.
    387     *
    388     * @param opt character name of the option.
    389     * @return Values of the argument if option is set, and has an argument, otherwise null.
    390     */
    391    public String[] getOptionValues(final char opt) {
    392        return getOptionValues(String.valueOf(opt));
    393    }
    394
    395    /**
    396     * Gets the array of values, if any, of an option.
    397     *
    398     * @param option string name of the option.
    399     * @return Values of the argument if option is set, and has an argument, otherwise null.
    400     * @since 1.5.0
    401     */
    402    public String[] getOptionValues(final Option option) {
    403        if (option == null) {
    404            return null;
    405        }
    406        if (option.isDeprecated()) {
    407            handleDeprecated(option);
    408        }
    409        final List<String> values = new ArrayList<>();
    410        for (final Option processedOption : options) {
    411            if (processedOption.equals(option)) {
    412                values.addAll(processedOption.getValuesList());
    413            }
    414        }
    415        return values.isEmpty() ? null : values.toArray(Util.EMPTY_STRING_ARRAY);
    416    }
    417
    418    /**
    419     * Gets the array of values, if any, of an option.
    420     *
    421     * @param opt string name of the option.
    422     * @return Values of the argument if option is set, and has an argument, otherwise null.
    423     */
    424    public String[] getOptionValues(final String opt) {
    425        return getOptionValues(resolveOption(opt));
    426    }
    427
    428    /**
    429     * Gets a version of this {@code Option} converted to a particular type.
    430     *
    431     * @param opt the name of the option.
    432     * @param <T> The return type for the method.
    433     * @return the value parsed into a particular object.
    434     * @throws ParseException if there are problems turning the option value into the desired type
    435     * @see PatternOptionBuilder
    436     * @since 1.5.0
    437     */
    438    public <T> T getParsedOptionValue(final char opt) throws ParseException {
    439        return getParsedOptionValue(String.valueOf(opt));
    440    }
    441
    442    /**
    443     * Gets a version of this {@code Option} converted to a particular type.
    444     *
    445     * @param opt the name of the option.
    446     * @param defaultValue the default value to return if opt is not set.
    447     * @param <T> The return type for the method.
    448     * @return the value parsed into a particular object.
    449     * @throws ParseException if there are problems turning the option value into the desired type
    450     * @see PatternOptionBuilder
    451     * @since 1.7.0
    452     */
    453    public <T> T getParsedOptionValue(final char opt, final Supplier<T> defaultValue) throws ParseException {
    454        return getParsedOptionValue(String.valueOf(opt), defaultValue);
    455    }
    456
    457    /**
    458     * Gets a version of this {@code Option} converted to a particular type.
    459     *
    460     * @param opt the name of the option.
    461     * @param defaultValue the default value to return if opt is not set.
    462     * @param <T> The return type for the method.
    463     * @return the value parsed into a particular object.
    464     * @throws ParseException if there are problems turning the option value into the desired type
    465     * @see PatternOptionBuilder
    466     * @since 1.7.0
    467     */
    468    public <T> T getParsedOptionValue(final char opt, final T defaultValue) throws ParseException {
    469        return getParsedOptionValue(String.valueOf(opt), defaultValue);
    470    }
    471
    472    /**
    473     * Gets a version of this {@code Option} converted to a particular type.
    474     *
    475     * @param option the name of the option.
    476     * @param <T> The return type for the method.
    477     * @return the value parsed into a particular object.
    478     * @throws ParseException if there are problems turning the option value into the desired type
    479     * @see PatternOptionBuilder
    480     * @since 1.5.0
    481     */
    482    public <T> T getParsedOptionValue(final Option option) throws ParseException {
    483        return getParsedOptionValue(option, () -> null);
    484    }
    485
    486    /**
    487     * Gets a version of this {@code Option} converted to a particular type.
    488     *
    489     * @param option the name of the option.
    490     * @param defaultValue the default value to return if opt is not set.
    491     * @param <T> The return type for the method.
    492     * @return the value parsed into a particular object.
    493     * @throws ParseException if there are problems turning the option value into the desired type
    494     * @see PatternOptionBuilder
    495     * @since 1.7.0
    496     */
    497    @SuppressWarnings("unchecked")
    498    public <T> T getParsedOptionValue(final Option option, final Supplier<T> defaultValue) throws ParseException {
    499        if (option == null) {
    500            return get(defaultValue);
    501        }
    502        final String res = getOptionValue(option);
    503        try {
    504            if (res == null) {
    505                return get(defaultValue);
    506            }
    507            return (T) option.getConverter().apply(res);
    508        } catch (final Throwable e) {
    509            throw ParseException.wrap(e);
    510        }
    511    }
    512
    513    /**
    514     * Gets a version of this {@code Option} converted to a particular type.
    515     *
    516     * @param option the name of the option.
    517     * @param defaultValue the default value to return if opt is not set.
    518     * @param <T> The return type for the method.
    519     * @return the value parsed into a particular object.
    520     * @throws ParseException if there are problems turning the option value into the desired type
    521     * @see PatternOptionBuilder
    522     * @since 1.7.0
    523     */
    524    public <T> T getParsedOptionValue(final Option option, final T defaultValue) throws ParseException {
    525        return getParsedOptionValue(option, () -> defaultValue);
    526    }
    527
    528    /**
    529     * Gets a version of this {@code Option} converted to a particular type.
    530     *
    531     * @param opt the name of the option.
    532     * @param <T> The return type for the method.
    533     * @return the value parsed into a particular object.
    534     * @throws ParseException if there are problems turning the option value into the desired type
    535     * @see PatternOptionBuilder
    536     * @since 1.2
    537     */
    538    public <T> T getParsedOptionValue(final String opt) throws ParseException {
    539        return getParsedOptionValue(resolveOption(opt));
    540    }
    541
    542    /**
    543     * Gets a version of this {@code Option} converted to a particular type.
    544     *
    545     * @param opt the name of the option.
    546     * @param defaultValue the default value to return if opt is not set.
    547     * @param <T> The return type for the method.
    548     * @return the value parsed into a particular object.
    549     * @throws ParseException if there are problems turning the option value into the desired type
    550     * @see PatternOptionBuilder
    551     * @since 1.7.0
    552     */
    553    public <T> T getParsedOptionValue(final String opt, final Supplier<T> defaultValue) throws ParseException {
    554        return getParsedOptionValue(resolveOption(opt), defaultValue);
    555    }
    556
    557    /**
    558     * Gets a version of this {@code Option} converted to a particular type.
    559     *
    560     * @param opt the name of the option.
    561     * @param defaultValue the default value to return if opt is not set.
    562     * @param <T> The return type for the method.
    563     * @return the value parsed into a particular object.
    564     * @throws ParseException if there are problems turning the option value into the desired type
    565     * @see PatternOptionBuilder
    566     * @since 1.7.0
    567     */
    568    public <T> T getParsedOptionValue(final String opt, final T defaultValue) throws ParseException {
    569        return getParsedOptionValue(resolveOption(opt), defaultValue);
    570    }
    571
    572    /**
    573     * Handles deprecated options.
    574     *
    575     * @param option a deprecated option.
    576     */
    577    private void handleDeprecated(final Option option) {
    578        if (deprecatedHandler != null) {
    579            deprecatedHandler.accept(option);
    580        }
    581    }
    582
    583    /**
    584     * jkeyes - commented out until it is implemented properly
    585     * <p>
    586     * Dump state, suitable for debugging.
    587     * </p>
    588     *
    589     * @return Stringified form of this object.
    590     */
    591
    592    /*
    593     * public String toString() { StringBuilder buf = new StringBuilder();
    594     *
    595     * buf.append("[ CommandLine: [ options: "); buf.append(options.toString()); buf.append(" ] [ args: ");
    596     * buf.append(args.toString()); buf.append(" ] ]");
    597     *
    598     * return buf.toString(); }
    599     */
    600
    601    /**
    602     * Tests to see if an option has been set.
    603     *
    604     * @param opt character name of the option.
    605     * @return true if set, false if not.
    606     */
    607    public boolean hasOption(final char opt) {
    608        return hasOption(String.valueOf(opt));
    609    }
    610
    611    /**
    612     * Tests to see if an option has been set.
    613     *
    614     * @param opt the option to check.
    615     * @return true if set, false if not.
    616     * @since 1.5.0
    617     */
    618    public boolean hasOption(final Option opt) {
    619        final boolean result = options.contains(opt);
    620        if (result && opt.isDeprecated()) {
    621            handleDeprecated(opt);
    622        }
    623        return result;
    624    }
    625
    626    /**
    627     * Tests to see if an option has been set.
    628     *
    629     * @param opt Short name of the option.
    630     * @return true if set, false if not.
    631     */
    632    public boolean hasOption(final String opt) {
    633        return hasOption(resolveOption(opt));
    634    }
    635
    636    /**
    637     * Returns an iterator over the Option members of CommandLine.
    638     *
    639     * @return an {@code Iterator} over the processed {@link Option} members of this {@link CommandLine}.
    640     */
    641    public Iterator<Option> iterator() {
    642        return options.iterator();
    643    }
    644
    645    /**
    646     * Parses a list of values as properties.  All odd numbered values are property keys
    647     * and even numbered values are property values.  If there are an odd number of values
    648     * the last value is assumed to be a boolean with a value of "true".
    649     * @param props the properties to update.
    650     * @param values the list of values to parse.
    651     */
    652    private void processPropertiesFromValues(final Properties props, final List<String> values) {
    653        for (int i = 0; i < values.size(); i += 2) {
    654            if (i + 1 < values.size()) {
    655                props.put(values.get(i), values.get(i + 1));
    656            } else {
    657                props.put(values.get(i), "true");
    658            }
    659        }
    660    }
    661
    662    /**
    663     * Retrieves the option object given the long or short option as a String
    664     *
    665     * @param opt short or long name of the option, may be null.
    666     * @return Canonicalized option.
    667     */
    668    private Option resolveOption(final String opt) {
    669        final String actual = Util.stripLeadingHyphens(opt);
    670        if (actual != null) {
    671            for (final Option option : options) {
    672                if (actual.equals(option.getOpt()) || actual.equals(option.getLongOpt())) {
    673                    return option;
    674                }
    675            }
    676        }
    677        return null;
    678    }
    679}