View Javadoc
1   /*
2     Licensed to the Apache Software Foundation (ASF) under one or more
3     contributor license agreements.  See the NOTICE file distributed with
4     this work for additional information regarding copyright ownership.
5     The ASF licenses this file to You under the Apache License, Version 2.0
6     (the "License"); you may not use this file except in compliance with
7     the License.  You may obtain a copy of the License at
8   
9         http://www.apache.org/licenses/LICENSE-2.0
10  
11    Unless required by applicable law or agreed to in writing, software
12    distributed under the License is distributed on an "AS IS" BASIS,
13    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14    See the License for the specific language governing permissions and
15    limitations under the License.
16   */
17  
18  package org.apache.commons.cli;
19  
20  import java.io.Serializable;
21  import java.util.ArrayList;
22  import java.util.Collection;
23  import java.util.Collections;
24  import java.util.HashSet;
25  import java.util.LinkedHashMap;
26  import java.util.List;
27  import java.util.Map;
28  
29  /**
30   * Main entry-point into the library.
31   * <p>
32   * Options represents a collection of {@link Option} objects, which describe the possible options for a command-line.
33   * <p>
34   * It may flexibly parse long and short options, with or without values. Additionally, it may parse only a portion of a
35   * commandline, allowing for flexible multi-stage parsing.
36   *
37   * @see org.apache.commons.cli.CommandLine
38   */
39  public class Options implements Serializable {
40      /** The serial version UID. */
41      private static final long serialVersionUID = 1L;
42  
43      /** a map of the options with the character key */
44      private final Map<String, Option> shortOpts = new LinkedHashMap<>();
45  
46      /** a map of the options with the long key */
47      private final Map<String, Option> longOpts = new LinkedHashMap<>();
48  
49      /** a map of the required options */
50      // N.B. This can contain either a String (addOption) or an OptionGroup (addOptionGroup)
51      // TODO this seems wrong
52      private final List<Object> requiredOpts = new ArrayList<>();
53  
54      /** a map of the option groups */
55      private final Map<String, OptionGroup> optionGroups = new LinkedHashMap<>();
56  
57      /**
58       * Adds an option instance
59       *
60       * @param opt the option that is to be added
61       * @return the resulting Options instance
62       */
63      public Options addOption(final Option opt) {
64          final String key = opt.getKey();
65  
66          // add it to the long option list
67          if (opt.hasLongOpt()) {
68              longOpts.put(opt.getLongOpt(), opt);
69          }
70  
71          // if the option is required add it to the required list
72          if (opt.isRequired()) {
73              if (requiredOpts.contains(key)) {
74                  requiredOpts.remove(requiredOpts.indexOf(key));
75              }
76              requiredOpts.add(key);
77          }
78  
79          shortOpts.put(key, opt);
80  
81          return this;
82      }
83  
84      /**
85       * Add an option that only contains a short-name.
86       *
87       * <p>
88       * It may be specified as requiring an argument.
89       * </p>
90       *
91       * @param opt Short single-character name of the option.
92       * @param hasArg flag signalling if an argument is required after this option
93       * @param description Self-documenting description
94       * @return the resulting Options instance
95       */
96      public Options addOption(final String opt, final boolean hasArg, final String description) {
97          addOption(opt, null, hasArg, description);
98          return this;
99      }
100 
101     /**
102      * Add an option that only contains a short name.
103      *
104      * <p>
105      * The option does not take an argument.
106      * </p>
107      *
108      * @param opt Short single-character name of the option.
109      * @param description Self-documenting description
110      * @return the resulting Options instance
111      * @since 1.3
112      */
113     public Options addOption(final String opt, final String description) {
114         addOption(opt, null, false, description);
115         return this;
116     }
117 
118     /**
119      * Add an option that contains a short-name and a long-name.
120      *
121      * <p>
122      * It may be specified as requiring an argument.
123      * </p>
124      *
125      * @param opt Short single-character name of the option.
126      * @param longOpt Long multi-character name of the option.
127      * @param hasArg flag signalling if an argument is required after this option
128      * @param description Self-documenting description
129      * @return the resulting Options instance
130      */
131     public Options addOption(final String opt, final String longOpt, final boolean hasArg, final String description) {
132         addOption(new Option(opt, longOpt, hasArg, description));
133         return this;
134     }
135 
136     /**
137      * Add the specified option group.
138      *
139      * @param group the OptionGroup that is to be added
140      * @return the resulting Options instance
141      */
142     public Options addOptionGroup(final OptionGroup group) {
143         if (group.isRequired()) {
144             requiredOpts.add(group);
145         }
146 
147         for (final Option option : group.getOptions()) {
148             // an Option cannot be required if it is in an
149             // OptionGroup, either the group is required or
150             // nothing is required
151             option.setRequired(false);
152             addOption(option);
153 
154             optionGroups.put(option.getKey(), group);
155         }
156 
157         return this;
158     }
159 
160     /**
161      * Add an option that contains a short-name and a long-name.
162      *
163      * <p>
164      * The added option is set as required. It may be specified as requiring an argument. This method is a shortcut for:
165      * </p>
166      *
167      * <pre>
168      * <code>
169      * Options option = new Option(opt, longOpt, hasArg, description);
170      * option.setRequired(true);
171      * options.add(option);
172      * </code>
173      * </pre>
174      *
175      * @param opt Short single-character name of the option.
176      * @param longOpt Long multi-character name of the option.
177      * @param hasArg flag signalling if an argument is required after this option
178      * @param description Self-documenting description
179      * @return the resulting Options instance
180      * @since 1.4
181      */
182     public Options addRequiredOption(final String opt, final String longOpt, final boolean hasArg, final String description) {
183         final Optionn.html#Option">Option option = new Option(opt, longOpt, hasArg, description);
184         option.setRequired(true);
185         addOption(option);
186         return this;
187     }
188 
189     /**
190      * Gets the options with a long name starting with the name specified.
191      *
192      * @param opt the partial name of the option
193      * @return the options matching the partial name specified, or an empty list if none matches
194      * @since 1.3
195      */
196     public List<String> getMatchingOptions(String opt) {
197         opt = Util.stripLeadingHyphens(opt);
198 
199         final List<String> matchingOpts = new ArrayList<>();
200 
201         // for a perfect match return the single option only
202         if (longOpts.containsKey(opt)) {
203             return Collections.singletonList(opt);
204         }
205 
206         for (final String longOpt : longOpts.keySet()) {
207             if (longOpt.startsWith(opt)) {
208                 matchingOpts.add(longOpt);
209             }
210         }
211 
212         return matchingOpts;
213     }
214 
215     /**
216      * Gets the {@link Option} matching the long or short name specified.
217      *
218      * <p>
219      * The leading hyphens in the name are ignored (up to 2).
220      * </p>
221      *
222      * @param opt short or long name of the {@link Option}
223      * @return the option represented by opt
224      */
225     public Option getOption(String opt) {
226         opt = Util.stripLeadingHyphens(opt);
227 
228         if (shortOpts.containsKey(opt)) {
229             return shortOpts.get(opt);
230         }
231 
232         return longOpts.get(opt);
233     }
234 
235     /**
236      * Gets the OptionGroup the {@code opt} belongs to.
237      *
238      * @param opt the option whose OptionGroup is being queried.
239      * @return the OptionGroup if {@code opt} is part of an OptionGroup, otherwise return null
240      */
241     public OptionGroup getOptionGroup(final Option opt) {
242         return optionGroups.get(opt.getKey());
243     }
244 
245     /**
246      * Gets the OptionGroups that are members of this Options instance.
247      *
248      * @return a Collection of OptionGroup instances.
249      */
250     Collection<OptionGroup> getOptionGroups() {
251         return new HashSet<>(optionGroups.values());
252     }
253 
254     /**
255      * Gets a read-only list of options in this set
256      *
257      * @return read-only Collection of {@link Option} objects in this descriptor
258      */
259     public Collection<Option> getOptions() {
260         return Collections.unmodifiableCollection(helpOptions());
261     }
262 
263     /**
264      * Gets the required options.
265      *
266      * @return read-only List of required options
267      */
268     public List getRequiredOptions() {
269         return Collections.unmodifiableList(requiredOpts);
270     }
271 
272     /**
273      * Returns whether the named {@link Option} is a member of this {@link Options}.
274      *
275      * @param opt long name of the {@link Option}
276      * @return true if the named {@link Option} is a member of this {@link Options}
277      * @since 1.3
278      */
279     public boolean hasLongOption(String opt) {
280         opt = Util.stripLeadingHyphens(opt);
281 
282         return longOpts.containsKey(opt);
283     }
284 
285     /**
286      * Returns whether the named {@link Option} is a member of this {@link Options}.
287      *
288      * @param opt short or long name of the {@link Option}
289      * @return true if the named {@link Option} is a member of this {@link Options}
290      */
291     public boolean hasOption(String opt) {
292         opt = Util.stripLeadingHyphens(opt);
293 
294         return shortOpts.containsKey(opt) || longOpts.containsKey(opt);
295     }
296 
297     /**
298      * Returns whether the named {@link Option} is a member of this {@link Options}.
299      *
300      * @param opt short name of the {@link Option}
301      * @return true if the named {@link Option} is a member of this {@link Options}
302      * @since 1.3
303      */
304     public boolean hasShortOption(String opt) {
305         opt = Util.stripLeadingHyphens(opt);
306 
307         return shortOpts.containsKey(opt);
308     }
309 
310     /**
311      * Returns the Options for use by the HelpFormatter.
312      *
313      * @return the List of Options
314      */
315     List<Option> helpOptions() {
316         return new ArrayList<>(shortOpts.values());
317     }
318 
319     /**
320      * Dump state, suitable for debugging.
321      *
322      * @return Stringified form of this object
323      */
324     @Override
325     public String toString() {
326         final StringBuilder buf = new StringBuilder();
327 
328         buf.append("[ Options: [ short ");
329         buf.append(shortOpts.toString());
330         buf.append(" ] [ long ");
331         buf.append(longOpts);
332         buf.append(" ]");
333 
334         return buf.toString();
335     }
336 }