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