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 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         final Option option = shortOpts.get(opt);
229         return option != null ? option : longOpts.get(opt);
230     }
231 
232     /**
233      * Gets the OptionGroup the {@code opt} belongs to.
234      *
235      * @param opt the option whose OptionGroup is being queried.
236      * @return the OptionGroup if {@code opt} is part of an OptionGroup, otherwise return null
237      */
238     public OptionGroup getOptionGroup(final Option opt) {
239         return optionGroups.get(opt.getKey());
240     }
241 
242     /**
243      * Gets the OptionGroups that are members of this Options instance.
244      *
245      * @return a Collection of OptionGroup instances.
246      */
247     Collection<OptionGroup> getOptionGroups() {
248         return new HashSet<>(optionGroups.values());
249     }
250 
251     /**
252      * Gets a read-only list of options in this set
253      *
254      * @return read-only Collection of {@link Option} objects in this descriptor
255      */
256     public Collection<Option> getOptions() {
257         return Collections.unmodifiableCollection(helpOptions());
258     }
259 
260     /**
261      * Gets the required options.
262      *
263      * @return read-only List of required options
264      */
265     public List getRequiredOptions() {
266         return Collections.unmodifiableList(requiredOpts);
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         opt = Util.stripLeadingHyphens(opt);
278 
279         return longOpts.containsKey(opt);
280     }
281 
282     /**
283      * Returns whether the named {@link Option} is a member of this {@link Options}.
284      *
285      * @param opt short or long name of the {@link Option}
286      * @return true if the named {@link Option} is a member of this {@link Options}
287      */
288     public boolean hasOption(String opt) {
289         opt = Util.stripLeadingHyphens(opt);
290 
291         return shortOpts.containsKey(opt) || longOpts.containsKey(opt);
292     }
293 
294     /**
295      * Returns whether the named {@link Option} is a member of this {@link Options}.
296      *
297      * @param opt short name of the {@link Option}
298      * @return true if the named {@link Option} is a member of this {@link Options}
299      * @since 1.3
300      */
301     public boolean hasShortOption(String opt) {
302         opt = Util.stripLeadingHyphens(opt);
303 
304         return shortOpts.containsKey(opt);
305     }
306 
307     /**
308      * Returns the Options for use by the HelpFormatter.
309      *
310      * @return the List of Options
311      */
312     List<Option> helpOptions() {
313         return new ArrayList<>(shortOpts.values());
314     }
315 
316     /**
317      * Dump state, suitable for debugging.
318      *
319      * @return Stringified form of this object
320      */
321     @Override
322     public String toString() {
323         final StringBuilder buf = new StringBuilder();
324 
325         buf.append("[ Options: [ short ");
326         buf.append(shortOpts.toString());
327         buf.append(" ] [ long ");
328         buf.append(longOpts);
329         buf.append(" ]");
330 
331         return buf.toString();
332     }
333 }