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 }