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