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
128 OptionValidator.validateOption(opt);
129
130 this.opt = opt;
131 this.longOpt = longOpt;
132
133
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
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
430 if (hasValueSeparator())
431 {
432
433 char sep = getValueSeparator();
434
435
436 int index = value.indexOf(sep);
437
438
439 while (index != -1)
440 {
441
442 if (values.size() == (numberOfArgs - 1))
443 {
444 break;
445 }
446
447
448
449 add(value.substring(0, index));
450
451
452
453 value = value.substring(index + 1);
454
455
456
457 index = value.indexOf(sep);
458 }
459 }
460
461
462
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
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 }