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 */
017package Apache.CLI;
018
019import static Apache.CLI.Util.EMPTY_STRING_ARRAY;
020
021import java.io.Serializable;
022import java.util.ArrayList;
023import java.util.Iterator;
024import java.util.LinkedList;
025import java.util.List;
026import java.util.Properties;
027
028/**
029 * Represents list of arguments parsed against a {@link Options} descriptor.
030 *
031 * <BR /><BR />It allows querying of a boolean {@link #hasOption(String opt)}, in addition to
032 * retrieving the {@link #getOptionValue(String opt)} for options requiring arguments.
033 * 
034 * <BR /><BR />Additionally, any left-over or unrecognized arguments, are available for further
035 * processing.
036 */
037@Torello.JavaDoc.JDHeaderBackgroundImg(EmbedTagFileID="LICENSE")
038public class CommandLine implements Serializable {
039
040    /** A nested builder class to create {@code CommandLine} instance using descriptive methods. */
041    @Torello.JavaDoc.JDHeaderBackgroundImg(EmbedTagFileID="LICENSE")
042    public static final class Builder
043    {
044        // CommandLine that is being build by this Builder.
045        private final CommandLine commandLine = new CommandLine();
046
047        /**
048         * Adds left-over unrecognized option/argument.
049         * @param arg the unrecognized option/argument.
050         * @return this Builder instance for method chaining.
051         */
052        public Builder addArg(final String arg)
053        {
054            commandLine.addArg(arg);
055            return this;
056        }
057
058        /**
059         * Adds an option to the command line. The values of the option are stored.
060         * @param opt the processed option.
061         * @return this Builder instance for method chaining.
062         */
063        public Builder addOption(final Option opt)
064        {
065            commandLine.addOption(opt);
066            return this;
067        }
068
069        /**
070         * Returns the new instance.
071         * @return the new instance.
072         */
073        public CommandLine build()
074        { return commandLine; }
075    }
076
077    // The serial version UID.
078    private static final long serialVersionUID = 1L;
079
080    // The unrecognized options/arguments
081    private final List<String> args = new LinkedList<>();
082
083    // The processed options
084    private final List<Option> options = new ArrayList<>();
085
086    /** Creates a command line. */
087    protected CommandLine()
088    { /* nothing to do */ }
089
090    /**
091     * Adds left-over unrecognized option/argument.
092     * @param arg the unrecognized option/argument.
093     */
094    protected void addArg(final String arg)
095    { if (arg != null) args.add(arg); }
096
097    /**
098     * Adds an option to the command line. The values of the option are stored.
099     * @param opt the processed option.
100     */
101    protected void addOption(final Option opt)
102    { if (opt != null) options.add(opt); }
103
104    /**
105     * Gets any left-over non-recognized options and arguments
106     * @return remaining items passed in but not parsed as a {@code List}.
107     */
108    public List<String> getArgList()
109    { return args; }
110
111    /**
112     * Gets any left-over non-recognized options and arguments
113     * @return remaining items passed in but not parsed as an array.
114     */
115    public String[] getArgs()
116    { return args.toArray(Util.EMPTY_STRING_ARRAY); }
117
118    /**
119     * Gets the map of values associated to the option. This is convenient for options specifying
120     * Java properties like <code>-Dparam1=value1 -Dparam2=value2</code>. The first argument of the
121     * option is the key, and the 2nd argument is the value. If the option has only one argument
122     * ({@code -Dfoo}) it is considered as a boolean flag and the value is {@code "true"}.
123     *
124     * @param option name of the option.
125     * 
126     * @return The Properties mapped by the option, never {@code null} even if the option doesn't
127     * exists.
128     */
129    public Properties getOptionProperties(final Option option)
130    {
131        final Properties props = new Properties();
132
133        for (final Option processedOption : options)
134        {
135            if (processedOption.equals(option))
136            {
137                final List<String> values = processedOption.getValuesList();
138
139                if (values.size() >= 2)
140
141                    // use the first 2 arguments as the key/value pair
142                    props.put(values.get(0), values.get(1));
143
144                else if (values.size() == 1)
145
146                    // no explicit value, handle it as a boolean
147                    props.put(values.get(0), "true");
148            }
149        }
150
151        return props;
152    }
153
154    /**
155     * Gets the map of values associated to the option. This is convenient for options specifying
156     * Java properties like <code>-Dparam1=value1 -Dparam2=value2</code>. The first argument of the
157     * option is the key, and the 2nd argument is the value. If the option has only one argument
158     * ({@code -Dfoo}) it is considered as a boolean flag and the value is {@code "true"}.
159     *
160     * @param opt name of the option.
161     * 
162     * @return The Properties mapped by the option, never {@code null} even if the option doesn't
163     * exists.
164     */
165    public Properties getOptionProperties(final String opt)
166    {
167        final Properties props = new Properties();
168
169        for (final Option option : options)
170        {
171            if (opt.equals(option.getOpt()) || opt.equals(option.getLongOpt()))
172            {
173                final List<String> values = option.getValuesList();
174
175                if (values.size() >= 2)
176
177                    // use the first 2 arguments as the key/value pair
178                    props.put(values.get(0), values.get(1));
179
180                else if (values.size() == 1)
181
182                    // no explicit value, handle it as a boolean
183                    props.put(values.get(0), "true");
184            }
185        }
186
187        return props;
188    }
189
190    /**
191     * Gets an array of the processed {@link Option}'s.
192     * @return an array of the processed {@link Option}s.
193     */
194    public Option[] getOptions()
195    { return options.toArray(Option.EMPTY_ARRAY); }
196
197    /**
198     * Gets the first argument, if any, of this option.
199     * @param opt the character name of the option.
200     * @return Value of the argument if option is set, and has an argument, otherwise null.
201     */
202    public String getOptionValue(final char opt)
203    { return getOptionValue(String.valueOf(opt)); }
204
205    /**
206     * Gets the argument, if any, of an option.
207     * @param opt character name of the option
208     * @param defaultValue is the default value to be returned if the option is not specified.
209     * 
210     * @return Value of the argument if option is set, and has an argument, otherwise
211     * {@code defaultValue}.
212     */
213    public String getOptionValue(final char opt, final String defaultValue)
214    { return getOptionValue(String.valueOf(opt), defaultValue); }
215
216    /**
217     * Gets the first argument, if any, of this option.
218     * @param option the name of the option.
219     * @return Value of the argument if option is set, and has an argument, otherwise null.
220     */
221    public String getOptionValue(final Option option)
222    {
223        if (option == null) return null;
224
225        final String[] values = getOptionValues(option);
226
227        return values == null ? null : values[0];
228    }
229
230    /**
231     * Gets the first argument, if any, of an option.
232     * @param option name of the option.
233     * @param defaultValue is the default value to be returned if the option is not specified.
234     * 
235     * @return Value of the argument if option is set, and has an argument, otherwise
236     * {@code defaultValue}.
237     */
238    public String getOptionValue(final Option option, final String defaultValue)
239    {
240        final String answer = getOptionValue(option);
241        return answer != null ? answer : defaultValue;
242    }
243
244    /**
245     * Gets the first argument, if any, of this option.
246     * @param opt the name of the option.
247     * @return Value of the argument if option is set, and has an argument, otherwise null.
248     */
249    public String getOptionValue(final String opt)
250    { return getOptionValue(resolveOption(opt)); }
251
252    /**
253     * Gets the first argument, if any, of an option.
254     * @param opt name of the option.
255     * @param defaultValue is the default value to be returned if the option is not specified.
256     * 
257     * @return Value of the argument if option is set, and has an argument, otherwise
258     * {@code defaultValue}.
259     */
260    public String getOptionValue(final String opt, final String defaultValue)
261    { return getOptionValue(resolveOption(opt), defaultValue); }
262
263    /**
264     * Gets the array of values, if any, of an option.
265     * @param opt character name of the option.
266     * @return Values of the argument if option is set, and has an argument, otherwise null.
267     */
268    public String[] getOptionValues(final char opt)
269    { return getOptionValues(String.valueOf(opt)); }
270
271    /**
272     * Gets the array of values, if any, of an option.
273     * @param option string name of the option.
274     * @return Values of the argument if option is set, and has an argument, otherwise null.
275     */
276    public String[] getOptionValues(final Option option)
277    {
278        final List<String> values = new ArrayList<>();
279
280        for (final Option processedOption : options)
281            if (processedOption.equals(option))
282                values.addAll(processedOption.getValuesList());
283
284        return values.isEmpty() ? null : values.toArray(EMPTY_STRING_ARRAY);
285    }
286
287    /**
288     * Gets the array of values, if any, of an option.
289     * @param opt string name of the option.
290     * @return Values of the argument if option is set, and has an argument, otherwise null.
291     */
292    public String[] getOptionValues(final String opt)
293    { return getOptionValues(resolveOption(opt)); }
294
295    /**
296     * Gets a version of this {@code Option} converted to a particular type.
297     * @param opt the name of the option.
298     * @return the value parsed into a particular object.
299     * @throws ParseException if there are problems turning the option value into the desired type
300     * @see PatternOptionBuilder
301     */
302    public Object getParsedOptionValue(final char opt) throws ParseException
303    { return getParsedOptionValue(String.valueOf(opt)); }
304
305    /**
306     * Gets a version of this {@code Option} converted to a particular type.
307     * @param option the name of the option.
308     * @return the value parsed into a particular object.
309     * @throws ParseException if there are problems turning the option value into the desired type
310     * @see PatternOptionBuilder
311     */
312    public Object getParsedOptionValue(final Option option) throws ParseException
313    {
314        if (option == null) return null;
315
316        final String res = getOptionValue(option);
317
318        if (res == null) return null;
319
320        return TypeHandler.createValue(res, option.getType());
321    }
322
323    /**
324     * Gets a version of this {@code Option} converted to a particular type.
325     * @param opt the name of the option.
326     * @return the value parsed into a particular object.
327     * @throws ParseException if there are problems turning the option value into the desired type
328     * @see PatternOptionBuilder
329     */
330    public Object getParsedOptionValue(final String opt) throws ParseException
331    { return getParsedOptionValue(resolveOption(opt)); }
332
333    /**
334     * Tests to see if an option has been set.
335     * @param opt character name of the option.
336     * @return true if set, false if not.
337     */
338    public boolean hasOption(final char opt)
339    { return hasOption(String.valueOf(opt)); }
340
341    /**
342     * Tests to see if an option has been set.
343     * @param opt the option to check.
344     * @return true if set, false if not.
345     */
346    public boolean hasOption(final Option opt)
347    { return options.contains(opt); }
348
349    /**
350     * Tests to see if an option has been set.
351     * @param opt Short name of the option.
352     * @return true if set, false if not.
353     */
354    public boolean hasOption(final String opt)
355    { return hasOption(resolveOption(opt)); }
356
357    /**
358     * Returns an iterator over the Option members of CommandLine.
359     *
360     * @return an {@code Iterator} over the processed {@link Option} members of this
361     * {@link CommandLine}.
362     */
363    public Iterator<Option> iterator()
364    { return options.iterator(); }
365
366    /**
367     * Retrieves the option object given the long or short option as a String
368     * @param opt short or long name of the option, may be null.
369     * @return Canonicalized option.
370     */
371    private Option resolveOption(final String opt)
372    {
373        final String actual = Util.stripLeadingHyphens(opt);
374
375        if (actual != null)
376            for (final Option option : options)
377                if (actual.equals(option.getOpt()) || actual.equals(option.getLongOpt()))
378                    return option;
379
380        return null;
381    }
382}