001 /**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018 package org.apache.commons.cli;
019
020 import java.io.Serializable;
021 import java.util.ArrayList;
022 import java.util.List;
023
024 /** <p>Describes a single command-line option. It maintains
025 * information regarding the short-name of the option, the long-name,
026 * if any exists, a flag indicating if an argument is required for
027 * this option, and a self-documenting description of the option.</p>
028 *
029 * <p>An Option is not created independantly, but is create through
030 * an instance of {@link Options}.<p>
031 *
032 * @see org.apache.commons.cli.Options
033 * @see org.apache.commons.cli.CommandLine
034 *
035 * @author bob mcwhirter (bob @ werken.com)
036 * @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
037 * @version $Revision: 680644 $, $Date: 2008-07-29 01:13:48 -0700 (Tue, 29 Jul 2008) $
038 */
039 public class Option implements Cloneable, Serializable
040 {
041 private static final long serialVersionUID = 1L;
042
043 /** constant that specifies the number of argument values has not been specified */
044 public static final int UNINITIALIZED = -1;
045
046 /** constant that specifies the number of argument values is infinite */
047 public static final int UNLIMITED_VALUES = -2;
048
049 /** the name of the option */
050 private String opt;
051
052 /** the long representation of the option */
053 private String longOpt;
054
055 /** the name of the argument for this option */
056 private String argName = "arg";
057
058 /** description of the option */
059 private String description;
060
061 /** specifies whether this option is required to be present */
062 private boolean required;
063
064 /** specifies whether the argument value of this Option is optional */
065 private boolean optionalArg;
066
067 /** the number of argument values this option can have */
068 private int numberOfArgs = UNINITIALIZED;
069
070 /** the type of this Option */
071 private Object type;
072
073 /** the list of argument values **/
074 private List values = new ArrayList();
075
076 /** the character that is the value separator */
077 private char valuesep;
078
079 /**
080 * Creates an Option using the specified parameters.
081 *
082 * @param opt short representation of the option
083 * @param description describes the function of the option
084 *
085 * @throws IllegalArgumentException if there are any non valid
086 * Option characters in <code>opt</code>.
087 */
088 public Option(String opt, String description) throws IllegalArgumentException
089 {
090 this(opt, null, false, description);
091 }
092
093 /**
094 * Creates an Option using the specified parameters.
095 *
096 * @param opt short representation of the option
097 * @param hasArg specifies whether the Option takes an argument or not
098 * @param description describes the function of the option
099 *
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 }