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.Collection;
023import java.util.Collections;
024import java.util.HashSet;
025import java.util.LinkedHashMap;
026import java.util.List;
027import java.util.Map;
028
029/**
030 * Main entry-point into the library.
031 * <p>
032 * Options represents a collection of {@link Option} objects, which
033 * describe the possible options for a command-line.
034 * <p>
035 * It may flexibly parse long and short options, with or without
036 * values.  Additionally, it may parse only a portion of a commandline,
037 * allowing for flexible multi-stage parsing.
038 *
039 * @see org.apache.commons.cli.CommandLine
040 *
041 * @version $Id: Options.java 1754332 2016-07-27 18:47:57Z britter $
042 */
043public class Options implements Serializable
044{
045    /** The serial version UID. */
046    private static final long serialVersionUID = 1L;
047
048    /** a map of the options with the character key */
049    private final Map<String, Option> shortOpts = new LinkedHashMap<String, Option>();
050
051    /** a map of the options with the long key */
052    private final Map<String, Option> longOpts = new LinkedHashMap<String, Option>();
053
054    /** a map of the required options */
055    // N.B. This can contain either a String (addOption) or an OptionGroup (addOptionGroup)
056    // TODO this seems wrong
057    private final List<Object> requiredOpts = new ArrayList<Object>();
058
059    /** a map of the option groups */
060    private final Map<String, OptionGroup> optionGroups = new LinkedHashMap<String, OptionGroup>();
061
062    /**
063     * Add the specified option group.
064     *
065     * @param group the OptionGroup that is to be added
066     * @return the resulting Options instance
067     */
068    public Options addOptionGroup(OptionGroup group)
069    {
070        if (group.isRequired())
071        {
072            requiredOpts.add(group);
073        }
074
075        for (Option option : group.getOptions())
076        {
077            // an Option cannot be required if it is in an
078            // OptionGroup, either the group is required or
079            // nothing is required
080            option.setRequired(false);
081            addOption(option);
082
083            optionGroups.put(option.getKey(), group);
084        }
085
086        return this;
087    }
088
089    /**
090     * Lists the OptionGroups that are members of this Options instance.
091     *
092     * @return a Collection of OptionGroup instances.
093     */
094    Collection<OptionGroup> getOptionGroups()
095    {
096        return new HashSet<OptionGroup>(optionGroups.values());
097    }
098
099    /**
100     * Add an option that only contains a short name.
101     * 
102     * <p>
103     * The option does not take an argument.
104     * </p>
105     *
106     * @param opt Short single-character name of the option.
107     * @param description Self-documenting description
108     * @return the resulting Options instance
109     * @since 1.3
110     */
111    public Options addOption(String opt, String description)
112    {
113        addOption(opt, null, false, description);
114        return this;
115    }
116
117    /**
118     * Add an option that only contains a short-name.
119     *
120     * <p>
121     * It may be specified as requiring an argument.
122     * </p>
123     *
124     * @param opt Short single-character name of the option.
125     * @param hasArg flag signally if an argument is required after this option
126     * @param description Self-documenting description
127     * @return the resulting Options instance
128     */
129    public Options addOption(String opt, boolean hasArg, String description)
130    {
131        addOption(opt, null, hasArg, description);
132        return this;
133    }
134
135    /**
136     * Add an option that contains a short-name and a long-name.
137     *
138     * <p>
139     * It may be specified as requiring an argument.
140     * </p>
141     *
142     * @param opt Short single-character name of the option.
143     * @param longOpt Long multi-character name of the option.
144     * @param hasArg flag signally if an argument is required after this option
145     * @param description Self-documenting description
146     * @return the resulting Options instance
147     */
148    public Options addOption(String opt, String longOpt, boolean hasArg, String description)
149    {
150        addOption(new Option(opt, longOpt, hasArg, description));
151        return this;
152    }
153
154    /**
155     * Add an option that contains a short-name and a long-name.
156     * 
157     * <p>
158     * The added option is set as required. It may be specified as requiring an argument. This method is a shortcut for:
159     * </p>
160     *
161     * <pre>
162     * <code>
163     * Options option = new Option(opt, longOpt, hasArg, description);
164     * option.setRequired(true);
165     * options.add(option);
166     * </code>
167     * </pre>
168     *
169     * @param opt Short single-character name of the option.
170     * @param longOpt Long multi-character name of the option.
171     * @param hasArg flag signally if an argument is required after this option
172     * @param description Self-documenting description
173     * @return the resulting Options instance
174     * @since 1.4
175     */
176    public Options addRequiredOption(String opt, String longOpt, boolean hasArg, String description)
177    {
178        Option option = new Option(opt, longOpt, hasArg, description);
179        option.setRequired(true);
180        addOption(option);
181        return this;
182    }
183
184    /**
185     * Adds an option instance
186     *
187     * @param opt the option that is to be added
188     * @return the resulting Options instance
189     */
190    public Options addOption(Option opt)
191    {
192        String key = opt.getKey();
193
194        // add it to the long option list
195        if (opt.hasLongOpt())
196        {
197            longOpts.put(opt.getLongOpt(), opt);
198        }
199
200        // if the option is required add it to the required list
201        if (opt.isRequired())
202        {
203            if (requiredOpts.contains(key))
204            {
205                requiredOpts.remove(requiredOpts.indexOf(key));
206            }
207            requiredOpts.add(key);
208        }
209
210        shortOpts.put(key, opt);
211
212        return this;
213    }
214
215    /**
216     * Retrieve a read-only list of options in this set
217     *
218     * @return read-only Collection of {@link Option} objects in this descriptor
219     */
220    public Collection<Option> getOptions()
221    {
222        return Collections.unmodifiableCollection(helpOptions());
223    }
224
225    /**
226     * Returns the Options for use by the HelpFormatter.
227     *
228     * @return the List of Options
229     */
230    List<Option> helpOptions()
231    {
232        return new ArrayList<Option>(shortOpts.values());
233    }
234
235    /**
236     * Returns the required options.
237     *
238     * @return read-only List of required options
239     */
240    public List getRequiredOptions()
241    {
242        return Collections.unmodifiableList(requiredOpts);
243    }
244
245    /**
246     * Retrieve the {@link Option} matching the long or short name specified.
247     *
248     * <p>
249     * The leading hyphens in the name are ignored (up to 2).
250     * </p>
251     *
252     * @param opt short or long name of the {@link Option}
253     * @return the option represented by opt
254     */
255    public Option getOption(String opt)
256    {
257        opt = Util.stripLeadingHyphens(opt);
258
259        if (shortOpts.containsKey(opt))
260        {
261            return shortOpts.get(opt);
262        }
263
264        return longOpts.get(opt);
265    }
266
267    /**
268     * Returns the options with a long name starting with the name specified.
269     * 
270     * @param opt the partial name of the option
271     * @return the options matching the partial name specified, or an empty list if none matches
272     * @since 1.3
273     */
274    public List<String> getMatchingOptions(String opt)
275    {
276        opt = Util.stripLeadingHyphens(opt);
277        
278        List<String> matchingOpts = new ArrayList<String>();
279
280        // for a perfect match return the single option only
281        if (longOpts.keySet().contains(opt))
282        {
283            return Collections.singletonList(opt);
284        }
285
286        for (String longOpt : longOpts.keySet())
287        {
288            if (longOpt.startsWith(opt))
289            {
290                matchingOpts.add(longOpt);
291            }
292        }
293        
294        return matchingOpts;
295    }
296
297    /**
298     * Returns whether the named {@link Option} is a member of this {@link Options}.
299     *
300     * @param opt short or long name of the {@link Option}
301     * @return true if the named {@link Option} is a member of this {@link Options}
302     */
303    public boolean hasOption(String opt)
304    {
305        opt = Util.stripLeadingHyphens(opt);
306
307        return shortOpts.containsKey(opt) || longOpts.containsKey(opt);
308    }
309
310    /**
311     * Returns whether the named {@link Option} is a member of this {@link Options}.
312     *
313     * @param opt long name of the {@link Option}
314     * @return true if the named {@link Option} is a member of this {@link Options}
315     * @since 1.3
316     */
317    public boolean hasLongOption(String opt)
318    {
319        opt = Util.stripLeadingHyphens(opt);
320
321        return longOpts.containsKey(opt);
322    }
323
324    /**
325     * Returns whether the named {@link Option} is a member of this {@link Options}.
326     *
327     * @param opt short name of the {@link Option}
328     * @return true if the named {@link Option} is a member of this {@link Options}
329     * @since 1.3
330     */
331    public boolean hasShortOption(String opt)
332    {
333        opt = Util.stripLeadingHyphens(opt);
334
335        return shortOpts.containsKey(opt);
336    }
337
338    /**
339     * Returns the OptionGroup the <code>opt</code> belongs to.
340     *
341     * @param opt the option whose OptionGroup is being queried.
342     * @return the OptionGroup if <code>opt</code> is part of an OptionGroup, otherwise return null
343     */
344    public OptionGroup getOptionGroup(Option opt)
345    {
346        return optionGroups.get(opt.getKey());
347    }
348
349    /**
350     * Dump state, suitable for debugging.
351     *
352     * @return Stringified form of this object
353     */
354    @Override
355    public String toString()
356    {
357        StringBuilder buf = new StringBuilder();
358
359        buf.append("[ Options: [ short ");
360        buf.append(shortOpts.toString());
361        buf.append(" ] [ long ");
362        buf.append(longOpts);
363        buf.append(" ]");
364
365        return buf.toString();
366    }
367}