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  package org.apache.commons.cli;
18  
19  import java.util.ArrayList;
20  
21  /*** <p>Describes a single command-line option.  It maintains
22   * information regarding the short-name of the option, the long-name,
23   * if any exists, a flag indicating if an argument is required for
24   * this option, and a self-documenting description of the option.</p>
25   *
26   * <p>An Option is not created independantly, but is create through
27   * an instance of {@link Options}.<p>
28   *
29   * @see org.apache.commons.cli.Options
30   * @see org.apache.commons.cli.CommandLine
31   *
32   * @author bob mcwhirter (bob @ werken.com)
33   * @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
34   * @version $Revision: 551821 $
35   */
36  public class Option implements Cloneable {
37  
38      /*** constant that specifies the number of argument values has 
39          not been specified */
40      public static final int UNINITIALIZED = -1;
41  
42      /*** constant that specifies the number of argument values is infinite */
43      public static final int UNLIMITED_VALUES = -2;
44  
45      /*** opt the name of the option */
46      private String opt;
47  
48      /*** longOpt is the long representation of the option */
49      private String longOpt;
50  
51      /*** hasArg specifies whether this option has an associated argument */
52      private boolean hasArg;
53  
54      /*** argName specifies the name of the argument for this option */
55      private String argName = "arg";
56  
57      /*** description of the option */
58      private String description;
59  
60      /*** required specifies whether this option is required to be present */
61      private boolean required;
62  
63      /*** specifies whether the argument value of this Option is optional */
64      private boolean optionalArg;
65  
66      /*** 
67       * numberOfArgs specifies the number of argument values this option 
68       * can have 
69       */
70      private int numberOfArgs = UNINITIALIZED;
71  
72      /*** the type of this Option */
73      private Object type;
74  
75      /*** the list of argument values **/
76      private ArrayList values = new ArrayList();
77  
78      /*** the character that is the value separator */
79      private char valuesep;
80  
81      /***
82       * Creates an Option using the specified parameters.
83       *
84       * @param opt short representation of the option
85       * @param description describes the function of the option
86       *
87       * @throws IllegalArgumentException if there are any non valid
88       * Option characters in <code>opt</code>.
89       */
90      public Option(String opt, String description)
91             throws IllegalArgumentException
92      {
93          this(opt, null, false, description);
94      }
95  
96      /***
97       * Creates an Option using the specified parameters.
98       *
99       * @param opt short representation of the option
100      * @param hasArg specifies whether the Option takes an argument or not
101      * @param description describes the function of the option
102      *
103      * @throws IllegalArgumentException if there are any non valid
104      * Option characters in <code>opt</code>.
105      */
106     public Option(String opt, boolean hasArg, String description)
107            throws IllegalArgumentException
108     {
109         this(opt, null, hasArg, description);
110     }
111 
112     /***
113      * Creates an Option using the specified parameters.
114      *
115      * @param opt short representation of the option
116      * @param longOpt the long representation of the option
117      * @param hasArg specifies whether the Option takes an argument or not
118      * @param description describes the function of the option
119      *
120      * @throws IllegalArgumentException if there are any non valid
121      * Option characters in <code>opt</code>.
122      */
123     public Option(String opt, String longOpt, boolean hasArg, 
124                   String description)
125            throws IllegalArgumentException
126     {
127         // ensure that the option is valid
128         OptionValidator.validateOption(opt);
129 
130         this.opt = opt;
131         this.longOpt = longOpt;
132 
133         // if hasArg is set then the number of arguments is 1
134         if (hasArg)
135         {
136             this.numberOfArgs = 1;
137         }
138 
139         this.hasArg = hasArg;
140         this.description = description;
141     }
142 
143     /***
144      * Returns the id of this Option.  This is only set when the
145      * Option shortOpt is a single character.  This is used for switch
146      * statements.
147      *
148      * @return the id of this Option
149      */
150     public int getId()
151     {
152         return getKey().charAt(0);
153     }
154 
155     /***
156      * Returns the 'unique' Option identifier.
157      * 
158      * @return the 'unique' Option identifier
159      */
160     String getKey()
161     {
162         // if 'opt' is null, then it is a 'long' option
163         if (opt == null)
164         {
165             return this.longOpt;
166         }
167 
168         return this.opt;
169     }
170 
171     /*** 
172      * Retrieve the name of this Option.
173      *
174      * It is this String which can be used with
175      * {@link CommandLine#hasOption(String opt)} and
176      * {@link CommandLine#getOptionValue(String opt)} to check
177      * for existence and argument.
178      *
179      * @return The name of this option
180      */
181     public String getOpt()
182     {
183         return this.opt;
184     }
185 
186     /***
187      * Retrieve the type of this Option.
188      * 
189      * @return The type of this option
190      */
191     public Object getType()
192     {
193         return this.type;
194     }
195 
196     /***
197      * Sets the type of this Option.
198      *
199      * @param type the type of this Option
200      */
201     public void setType(Object type)
202     {
203         this.type = type;
204     }
205 
206     /*** 
207      * Retrieve the long name of this Option.
208      *
209      * @return Long name of this option, or null, if there is no long name
210      */
211     public String getLongOpt()
212     {
213         return this.longOpt;
214     }
215 
216     /***
217      * Sets the long name of this Option.
218      *
219      * @param longOpt the long name of this Option
220      */
221     public void setLongOpt(String longOpt)
222     {
223         this.longOpt = longOpt;
224     }
225 
226     /***
227      * Sets whether this Option can have an optional argument.
228      *
229      * @param optionalArg specifies whether the Option can have
230      * an optional argument.
231      */
232     public void setOptionalArg(boolean optionalArg)
233     {
234         this.optionalArg = optionalArg;
235     }
236 
237     /***
238      * @return whether this Option can have an optional argument
239      */
240     public boolean hasOptionalArg()
241     {
242         return this.optionalArg;
243     }
244 
245     /*** 
246      * Query to see if this Option has a long name
247      *
248      * @return boolean flag indicating existence of a long name
249      */
250     public boolean hasLongOpt()
251     {
252         return (this.longOpt != null);
253     }
254 
255     /*** 
256      * Query to see if this Option requires an argument
257      *
258      * @return boolean flag indicating if an argument is required
259      */
260     public boolean hasArg()
261     {
262         return (this.numberOfArgs > 0) || (numberOfArgs == UNLIMITED_VALUES);
263     }
264 
265     /*** 
266      * Retrieve the self-documenting description of this Option
267      *
268      * @return The string description of this option
269      */
270     public String getDescription()
271     {
272         return this.description;
273     }
274 
275    /*** 
276      * Sets the self-documenting description of this Option
277      *
278      * @param description The description of this option
279      */
280     public void setDescription(String description)
281     {
282         this.description = description;
283     }
284 
285     /*** 
286      * Query to see if this Option requires an argument
287      *
288      * @return boolean flag indicating if an argument is required
289      */
290     public boolean isRequired()
291     {
292         return this.required;
293     }
294 
295     /***
296      * Sets whether this Option is mandatory.
297      *
298      * @param required specifies whether this Option is mandatory
299      */
300     public void setRequired(boolean required)
301     {
302         this.required = required;
303     }
304 
305     /***
306      * Sets the display name for the argument value.
307      *
308      * @param argName the display name for the argument value.
309      */
310     public void setArgName(String argName)
311     {
312         this.argName = argName;
313     }
314 
315     /***
316      * Gets the display name for the argument value.
317      *
318      * @return the display name for the argument value.
319      */
320     public String getArgName()
321     {
322         return this.argName;
323     }
324 
325     /***
326      * Returns whether the display name for the argument value
327      * has been set.
328      *
329      * @return if the display name for the argument value has been
330      * set.
331      */
332     public boolean hasArgName()
333     {
334         return (this.argName != null && this.argName.length() > 0);
335     }
336 
337     /*** 
338      * Query to see if this Option can take many values.
339      *
340      * @return boolean flag indicating if multiple values are allowed
341      */
342     public boolean hasArgs()
343     {
344         return (this.numberOfArgs > 1) 
345                 || (this.numberOfArgs == UNLIMITED_VALUES);
346     }
347 
348     /*** 
349      * Sets the number of argument values this Option can take.
350      *
351      * @param num the number of argument values
352      */
353     public void setArgs(int num)
354     {
355         this.numberOfArgs = num;
356     }
357 
358     /***
359      * Sets the value separator.  For example if the argument value
360      * was a Java property, the value separator would be '='.
361      *
362      * @param sep The value separator.
363      */
364     public void setValueSeparator(char sep)
365     {
366         this.valuesep = sep;
367     }
368 
369     /***
370      * Returns the value separator character.
371      *
372      * @return the value separator character.
373      */
374     public char getValueSeparator()
375     {
376         return this.valuesep;
377     }
378 
379     /***
380      * Return whether this Option has specified a value separator.
381      * 
382      * @return whether this Option has specified a value separator.
383      */
384     public boolean hasValueSeparator()
385     {
386         return (this.valuesep > 0);
387     }
388 
389     /*** 
390      * Returns the number of argument values this Option can take.
391      *
392      * @return num the number of argument values
393      */
394     public int getArgs()
395     {
396         return this.numberOfArgs;
397     }
398 
399     /***
400      * Adds the specified value to this Option.
401      * 
402      * @param value is a/the value of this Option
403      */
404     void addValueForProcessing(String value)
405     {
406         switch (numberOfArgs)
407         {
408         case UNINITIALIZED:
409             throw new RuntimeException("NO_ARGS_ALLOWED");
410 
411         default:
412             processValue(value);
413         }
414     }
415 
416     /***
417      * Processes the value.  If this Option has a value separator
418      * the value will have to be parsed into individual tokens.  When
419      * n-1 tokens have been processed and there are more value separators
420      * in the value, parsing is ceased and the remaining characters are
421      * added as a single token.
422      *
423      * @param value The String to be processed.
424      *
425      * @since 1.0.1
426      */
427     private void processValue(String value)
428     {
429         // this Option has a separator character
430         if (hasValueSeparator())
431         {
432             // get the separator character
433             char sep = getValueSeparator();
434 
435             // store the index for the value separator
436             int index = value.indexOf(sep);
437 
438             // while there are more value separators
439             while (index != -1)
440             {
441                 // next value to be added 
442                 if (values.size() == (numberOfArgs - 1))
443                 {
444                     break;
445                 }
446 
447 
448                 // store
449                 add(value.substring(0, index));
450 
451 
452                 // parse
453                 value = value.substring(index + 1);
454 
455 
456                 // get new index
457                 index = value.indexOf(sep);
458             }
459         }
460 
461 
462         // store the actual value or the last value that has been parsed
463         add(value);
464     }
465 
466     /***
467      * Add the value to this Option.  If the number of arguments
468      * is greater than zero and there is enough space in the list then
469      * add the value.  Otherwise, throw a runtime exception.
470      *
471      * @param value The value to be added to this Option
472      *
473      * @since 1.0.1
474      */
475     private void add(String value)
476     {
477         if ((numberOfArgs > 0) && (values.size() > (numberOfArgs - 1)))
478         {
479             throw new RuntimeException("Cannot add value, list full.");
480         }
481 
482 
483         // store value
484         this.values.add(value);
485     }
486 
487     /***
488      * Returns the specified value of this Option or 
489      * <code>null</code> if there is no value.
490      *
491      * @return the value/first value of this Option or 
492      * <code>null</code> if there is no value.
493      */
494     public String getValue()
495     {
496         return hasNoValues() ? null : (String) this.values.get(0);
497     }
498 
499     /***
500      * Returns the specified value of this Option or 
501      * <code>null</code> if there is no value.
502      *
503      * @param index The index of the value to be returned.
504      *
505      * @return the specified value of this Option or 
506      * <code>null</code> if there is no value.
507      *
508      * @throws IndexOutOfBoundsException if index is less than 1
509      * or greater than the number of the values for this Option.
510      */
511     public String getValue(int index)
512         throws IndexOutOfBoundsException
513     {
514         return hasNoValues() ? null : (String) this.values.get(index);
515     }
516 
517     /***
518      * Returns the value/first value of this Option or the 
519      * <code>defaultValue</code> if there is no value.
520      *
521      * @param defaultValue The value to be returned if ther
522      * is no value.
523      *
524      * @return the value/first value of this Option or the 
525      * <code>defaultValue</code> if there are no values.
526      */
527     public String getValue(String defaultValue)
528     {
529         String value = getValue();
530 
531         return (value != null) ? value : defaultValue;
532     }
533 
534     /***
535      * Return the values of this Option as a String array 
536      * or null if there are no values
537      *
538      * @return the values of this Option as a String array 
539      * or null if there are no values
540      */
541     public String[] getValues()
542     {
543         return hasNoValues()
544                ? null : (String[]) this.values.toArray(new String[this.values.size()]);
545     }
546 
547     /***
548      * @return the values of this Option as a List
549      * or null if there are no values
550      */
551     public java.util.List getValuesList()
552     {
553         return this.values;
554     }
555 
556     /*** 
557      * Dump state, suitable for debugging.
558      *
559      * @return Stringified form of this object
560      */
561     public String toString()
562     {
563         StringBuffer buf = new StringBuffer().append("[ option: ");
564 
565         buf.append(this.opt);
566 
567         if (this.longOpt != null)
568         {
569             buf.append(" ").append(this.longOpt);
570         }
571 
572         buf.append(" ");
573 
574         if (hasArg)
575         {
576             buf.append("+ARG");
577         }
578 
579         buf.append(" :: ").append(this.description);
580 
581         if (this.type != null)
582         {
583             buf.append(" :: ").append(this.type);
584         }
585 
586         buf.append(" ]");
587 
588         return buf.toString();
589     }
590 
591     /***
592      * Returns whether this Option has any values.
593      *
594      * @return whether this Option has any values.
595      */
596     private boolean hasNoValues()
597     {
598         return this.values.size() == 0;
599     }
600 
601     public boolean equals( Object o )
602     {
603         if ( this == o )
604         {
605             return true;
606         }
607         if ( o == null || getClass() != o.getClass() )
608         {
609             return false;
610         }
611 
612         Option option = (Option) o;
613 
614 
615         if ( opt != null ? !opt.equals( option.opt ) : option.opt != null )
616         {
617             return false;
618         }
619         if ( longOpt != null ? !longOpt.equals( option.longOpt ) : option.longOpt != null )
620         {
621             return false;
622         }
623 
624         return true;
625     }
626 
627     public int hashCode()
628     {
629         int result;
630         result = ( opt != null ? opt.hashCode() : 0 );
631         result = 31 * result + ( longOpt != null ? longOpt.hashCode() : 0 );
632         return result;
633     }
634 
635     /***
636      * A rather odd clone method - due to incorrect code in 1.0 it is public 
637      * and in 1.1 rather than throwing a CloneNotSupportedException it throws 
638      * a RuntimeException so as to maintain backwards compat at the API level. 
639      *
640      * After calling this method, it is very likely you will want to call 
641      * clearValues(). 
642      *
643      * @throws RuntimeException
644      */
645     public Object clone() {
646         try {
647             Option option = (Option) super.clone();
648             option.values = new ArrayList(values);
649             return option;
650         } catch(CloneNotSupportedException cnse) {
651             throw new RuntimeException("A CloneNotSupportedException was thrown: " + cnse.getMessage());
652         }
653     }
654 
655     /***
656      * <p>Clear the Option values. After a 
657      * parse is complete, these are left with data in them 
658      * and they need clearing if another parse is done. </p>
659      *
660      * See: <a href="https://issues.apache.org/jira/browse/CLI-71">CLI-71</a>
661      */
662     void clearValues() {
663         this.values.clear();
664     }
665 
666     /***
667      * This method is not intended to be used. It was a piece of internal 
668      * API that was made public in 1.0. It currently throws an UnsupportedOperationException. 
669      * @deprecated
670      * @throws UnsupportedOperationException
671      */
672     public boolean addValue(String value) {
673         throw new UnsupportedOperationException(
674            "The addValue method is not intended for client use. " + 
675            "Subclasses should use the addValueForProcessing method instead. "
676            );
677     }
678 
679 }