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: */package org.apache.commons.cli;
017:
018: import java.util.ArrayList;
019:
020: /** <p>Describes a single command-line option. It maintains
021: * information regarding the short-name of the option, the long-name,
022: * if any exists, a flag indicating if an argument is required for
023: * this option, and a self-documenting description of the option.</p>
024: *
025: * <p>An Option is not created independantly, but is create through
026: * an instance of {@link Options}.<p>
027: *
028: * @see org.apache.commons.cli.Options
029: * @see org.apache.commons.cli.CommandLine
030: *
031: * @author bob mcwhirter (bob @ werken.com)
032: * @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
033: * @version $Revision: 551821 $
034: */
035: public class Option implements Cloneable {
036:
037: /** constant that specifies the number of argument values has
038: not been specified */
039: public static final int UNINITIALIZED = -1;
040:
041: /** constant that specifies the number of argument values is infinite */
042: public static final int UNLIMITED_VALUES = -2;
043:
044: /** opt the name of the option */
045: private String opt;
046:
047: /** longOpt is the long representation of the option */
048: private String longOpt;
049:
050: /** hasArg specifies whether this option has an associated argument */
051: private boolean hasArg;
052:
053: /** argName specifies the name of the argument for this option */
054: private String argName = "arg";
055:
056: /** description of the option */
057: private String description;
058:
059: /** required specifies whether this option is required to be present */
060: private boolean required;
061:
062: /** specifies whether the argument value of this Option is optional */
063: private boolean optionalArg;
064:
065: /**
066: * numberOfArgs specifies the number of argument values this option
067: * can have
068: */
069: private int numberOfArgs = UNINITIALIZED;
070:
071: /** the type of this Option */
072: private Object type;
073:
074: /** the list of argument values **/
075: private ArrayList values = new ArrayList();
076:
077: /** the character that is the value separator */
078: private char valuesep;
079:
080: /**
081: * Creates an Option using the specified parameters.
082: *
083: * @param opt short representation of the option
084: * @param description describes the function of the option
085: *
086: * @throws IllegalArgumentException if there are any non valid
087: * Option characters in <code>opt</code>.
088: */
089: public Option(String opt, String description)
090: throws IllegalArgumentException {
091: this (opt, null, false, description);
092: }
093:
094: /**
095: * Creates an Option using the specified parameters.
096: *
097: * @param opt short representation of the option
098: * @param hasArg specifies whether the Option takes an argument or not
099: * @param description describes the function of the option
100: *
101: * @throws IllegalArgumentException if there are any non valid
102: * Option characters in <code>opt</code>.
103: */
104: public Option(String opt, boolean hasArg, String description)
105: throws IllegalArgumentException {
106: this (opt, null, hasArg, description);
107: }
108:
109: /**
110: * Creates an Option using the specified parameters.
111: *
112: * @param opt short representation of the option
113: * @param longOpt the long representation of the option
114: * @param hasArg specifies whether the Option takes an argument or not
115: * @param description describes the function of the option
116: *
117: * @throws IllegalArgumentException if there are any non valid
118: * Option characters in <code>opt</code>.
119: */
120: public Option(String opt, String longOpt, boolean hasArg,
121: String description) throws IllegalArgumentException {
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: this .numberOfArgs = 1;
131: }
132:
133: this .hasArg = hasArg;
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: return getKey().charAt(0);
146: }
147:
148: /**
149: * Returns the 'unique' Option identifier.
150: *
151: * @return the 'unique' Option identifier
152: */
153: String getKey() {
154: // if 'opt' is null, then it is a 'long' option
155: if (opt == null) {
156: return this .longOpt;
157: }
158:
159: return this .opt;
160: }
161:
162: /**
163: * Retrieve the name of this Option.
164: *
165: * It is this String which can be used with
166: * {@link CommandLine#hasOption(String opt)} and
167: * {@link CommandLine#getOptionValue(String opt)} to check
168: * for existence and argument.
169: *
170: * @return The name of this option
171: */
172: public String getOpt() {
173: return this .opt;
174: }
175:
176: /**
177: * Retrieve the type of this Option.
178: *
179: * @return The type of this option
180: */
181: public Object getType() {
182: return this .type;
183: }
184:
185: /**
186: * Sets the type of this Option.
187: *
188: * @param type the type of this Option
189: */
190: public void setType(Object type) {
191: this .type = type;
192: }
193:
194: /**
195: * Retrieve the long name of this Option.
196: *
197: * @return Long name of this option, or null, if there is no long name
198: */
199: public String getLongOpt() {
200: return this .longOpt;
201: }
202:
203: /**
204: * Sets the long name of this Option.
205: *
206: * @param longOpt the long name of this Option
207: */
208: public void setLongOpt(String longOpt) {
209: this .longOpt = longOpt;
210: }
211:
212: /**
213: * Sets whether this Option can have an optional argument.
214: *
215: * @param optionalArg specifies whether the Option can have
216: * an optional argument.
217: */
218: public void setOptionalArg(boolean optionalArg) {
219: this .optionalArg = optionalArg;
220: }
221:
222: /**
223: * @return whether this Option can have an optional argument
224: */
225: public boolean hasOptionalArg() {
226: return this .optionalArg;
227: }
228:
229: /**
230: * Query to see if this Option has a long name
231: *
232: * @return boolean flag indicating existence of a long name
233: */
234: public boolean hasLongOpt() {
235: return (this .longOpt != null);
236: }
237:
238: /**
239: * Query to see if this Option requires an argument
240: *
241: * @return boolean flag indicating if an argument is required
242: */
243: public boolean hasArg() {
244: return (this .numberOfArgs > 0)
245: || (numberOfArgs == UNLIMITED_VALUES);
246: }
247:
248: /**
249: * Retrieve the self-documenting description of this Option
250: *
251: * @return The string description of this option
252: */
253: public String getDescription() {
254: return this .description;
255: }
256:
257: /**
258: * Sets the self-documenting description of this Option
259: *
260: * @param description The description of this option
261: */
262: public void setDescription(String description) {
263: this .description = description;
264: }
265:
266: /**
267: * Query to see if this Option requires an argument
268: *
269: * @return boolean flag indicating if an argument is required
270: */
271: public boolean isRequired() {
272: return this .required;
273: }
274:
275: /**
276: * Sets whether this Option is mandatory.
277: *
278: * @param required specifies whether this Option is mandatory
279: */
280: public void setRequired(boolean required) {
281: this .required = required;
282: }
283:
284: /**
285: * Sets the display name for the argument value.
286: *
287: * @param argName the display name for the argument value.
288: */
289: public void setArgName(String argName) {
290: this .argName = argName;
291: }
292:
293: /**
294: * Gets the display name for the argument value.
295: *
296: * @return the display name for the argument value.
297: */
298: public String getArgName() {
299: return this .argName;
300: }
301:
302: /**
303: * Returns whether the display name for the argument value
304: * has been set.
305: *
306: * @return if the display name for the argument value has been
307: * set.
308: */
309: public boolean hasArgName() {
310: return (this .argName != null && this .argName.length() > 0);
311: }
312:
313: /**
314: * Query to see if this Option can take many values.
315: *
316: * @return boolean flag indicating if multiple values are allowed
317: */
318: public boolean hasArgs() {
319: return (this .numberOfArgs > 1)
320: || (this .numberOfArgs == UNLIMITED_VALUES);
321: }
322:
323: /**
324: * Sets the number of argument values this Option can take.
325: *
326: * @param num the number of argument values
327: */
328: public void setArgs(int num) {
329: this .numberOfArgs = num;
330: }
331:
332: /**
333: * Sets the value separator. For example if the argument value
334: * was a Java property, the value separator would be '='.
335: *
336: * @param sep The value separator.
337: */
338: public void setValueSeparator(char sep) {
339: this .valuesep = sep;
340: }
341:
342: /**
343: * Returns the value separator character.
344: *
345: * @return the value separator character.
346: */
347: public char getValueSeparator() {
348: return this .valuesep;
349: }
350:
351: /**
352: * Return whether this Option has specified a value separator.
353: *
354: * @return whether this Option has specified a value separator.
355: */
356: public boolean hasValueSeparator() {
357: return (this .valuesep > 0);
358: }
359:
360: /**
361: * Returns the number of argument values this Option can take.
362: *
363: * @return num the number of argument values
364: */
365: public int getArgs() {
366: return this .numberOfArgs;
367: }
368:
369: /**
370: * Adds the specified value to this Option.
371: *
372: * @param value is a/the value of this Option
373: */
374: void addValueForProcessing(String value) {
375: switch (numberOfArgs) {
376: case UNINITIALIZED:
377: throw new RuntimeException("NO_ARGS_ALLOWED");
378:
379: default:
380: processValue(value);
381: }
382: }
383:
384: /**
385: * Processes the value. If this Option has a value separator
386: * the value will have to be parsed into individual tokens. When
387: * n-1 tokens have been processed and there are more value separators
388: * in the value, parsing is ceased and the remaining characters are
389: * added as a single token.
390: *
391: * @param value The String to be processed.
392: *
393: * @since 1.0.1
394: */
395: private void processValue(String value) {
396: // this Option has a separator character
397: if (hasValueSeparator()) {
398: // get the separator character
399: char sep = getValueSeparator();
400:
401: // store the index for the value separator
402: int index = value.indexOf(sep);
403:
404: // while there are more value separators
405: while (index != -1) {
406: // next value to be added
407: if (values.size() == (numberOfArgs - 1)) {
408: break;
409: }
410:
411: // store
412: add(value.substring(0, index));
413:
414: // parse
415: value = value.substring(index + 1);
416:
417: // get new index
418: index = value.indexOf(sep);
419: }
420: }
421:
422: // store the actual value or the last value that has been parsed
423: add(value);
424: }
425:
426: /**
427: * Add the value to this Option. If the number of arguments
428: * is greater than zero and there is enough space in the list then
429: * add the value. Otherwise, throw a runtime exception.
430: *
431: * @param value The value to be added to this Option
432: *
433: * @since 1.0.1
434: */
435: private void add(String value) {
436: if ((numberOfArgs > 0) && (values.size() > (numberOfArgs - 1))) {
437: throw new RuntimeException("Cannot add value, list full.");
438: }
439:
440: // store value
441: this .values.add(value);
442: }
443:
444: /**
445: * Returns the specified value of this Option or
446: * <code>null</code> if there is no value.
447: *
448: * @return the value/first value of this Option or
449: * <code>null</code> if there is no value.
450: */
451: public String getValue() {
452: return hasNoValues() ? null : (String) this .values.get(0);
453: }
454:
455: /**
456: * Returns the specified value of this Option or
457: * <code>null</code> if there is no value.
458: *
459: * @param index The index of the value to be returned.
460: *
461: * @return the specified value of this Option or
462: * <code>null</code> if there is no value.
463: *
464: * @throws IndexOutOfBoundsException if index is less than 1
465: * or greater than the number of the values for this Option.
466: */
467: public String getValue(int index) throws IndexOutOfBoundsException {
468: return hasNoValues() ? null : (String) this .values.get(index);
469: }
470:
471: /**
472: * Returns the value/first value of this Option or the
473: * <code>defaultValue</code> if there is no value.
474: *
475: * @param defaultValue The value to be returned if ther
476: * is no value.
477: *
478: * @return the value/first value of this Option or the
479: * <code>defaultValue</code> if there are no values.
480: */
481: public String getValue(String defaultValue) {
482: String value = getValue();
483:
484: return (value != null) ? value : defaultValue;
485: }
486:
487: /**
488: * Return the values of this Option as a String array
489: * or null if there are no values
490: *
491: * @return the values of this Option as a String array
492: * or null if there are no values
493: */
494: public String[] getValues() {
495: return hasNoValues() ? null : (String[]) this .values
496: .toArray(new String[this .values.size()]);
497: }
498:
499: /**
500: * @return the values of this Option as a List
501: * or null if there are no values
502: */
503: public java.util.List getValuesList() {
504: return this .values;
505: }
506:
507: /**
508: * Dump state, suitable for debugging.
509: *
510: * @return Stringified form of this object
511: */
512: public String toString() {
513: StringBuffer buf = new StringBuffer().append("[ option: ");
514:
515: buf.append(this .opt);
516:
517: if (this .longOpt != null) {
518: buf.append(" ").append(this .longOpt);
519: }
520:
521: buf.append(" ");
522:
523: if (hasArg) {
524: buf.append("+ARG");
525: }
526:
527: buf.append(" :: ").append(this .description);
528:
529: if (this .type != null) {
530: buf.append(" :: ").append(this .type);
531: }
532:
533: buf.append(" ]");
534:
535: return buf.toString();
536: }
537:
538: /**
539: * Returns whether this Option has any values.
540: *
541: * @return whether this Option has any values.
542: */
543: private boolean hasNoValues() {
544: return this .values.size() == 0;
545: }
546:
547: public boolean equals(Object o) {
548: if (this == o) {
549: return true;
550: }
551: if (o == null || getClass() != o.getClass()) {
552: return false;
553: }
554:
555: Option option = (Option) o;
556:
557: if (opt != null ? !opt.equals(option.opt) : option.opt != null) {
558: return false;
559: }
560: if (longOpt != null ? !longOpt.equals(option.longOpt)
561: : option.longOpt != null) {
562: return false;
563: }
564:
565: return true;
566: }
567:
568: public int hashCode() {
569: int result;
570: result = (opt != null ? opt.hashCode() : 0);
571: result = 31 * result
572: + (longOpt != null ? longOpt.hashCode() : 0);
573: return result;
574: }
575:
576: /**
577: * A rather odd clone method - due to incorrect code in 1.0 it is public
578: * and in 1.1 rather than throwing a CloneNotSupportedException it throws
579: * a RuntimeException so as to maintain backwards compat at the API level.
580: *
581: * After calling this method, it is very likely you will want to call
582: * clearValues().
583: *
584: * @throws RuntimeException
585: */
586: public Object clone() {
587: try {
588: Option option = (Option) super .clone();
589: option.values = new ArrayList(values);
590: return option;
591: } catch (CloneNotSupportedException cnse) {
592: throw new RuntimeException(
593: "A CloneNotSupportedException was thrown: "
594: + cnse.getMessage());
595: }
596: }
597:
598: /**
599: * <p>Clear the Option values. After a
600: * parse is complete, these are left with data in them
601: * and they need clearing if another parse is done. </p>
602: *
603: * See: <a href="https://issues.apache.org/jira/browse/CLI-71">CLI-71</a>
604: */
605: void clearValues() {
606: this .values.clear();
607: }
608:
609: /**
610: * This method is not intended to be used. It was a piece of internal
611: * API that was made public in 1.0. It currently throws an UnsupportedOperationException.
612: * @deprecated
613: * @throws UnsupportedOperationException
614: */
615: public boolean addValue(String value) {
616: throw new UnsupportedOperationException(
617: "The addValue method is not intended for client use. "
618: + "Subclasses should use the addValueForProcessing method instead. ");
619: }
620:
621: }
|