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 018package org.apache.commons.cli; 019 020import java.io.Serializable; 021import java.util.ArrayList; 022import java.util.List; 023import java.util.Objects; 024 025/** 026 * Describes a single command-line option. It maintains information regarding the short-name of the option, the 027 * long-name, if any exists, a flag indicating if an argument is required for this option, and a self-documenting 028 * description of the option. 029 * <p> 030 * An Option is not created independently, but is created through an instance of {@link Options}. An Option is required 031 * to have at least a short or a long-name. 032 * </p> 033 * <p> 034 * <b>Note:</b> once an {@link Option} has been added to an instance of {@link Options}, its required flag cannot be 035 * changed. 036 * </p> 037 * 038 * @see org.apache.commons.cli.Options 039 * @see org.apache.commons.cli.CommandLine 040 */ 041public class Option implements Cloneable, Serializable { 042 043 /** 044 * A nested builder class to create {@code Option} instances using descriptive methods. 045 * <p> 046 * Example usage: 047 * </p> 048 * 049 * <pre> 050 * Option option = Option.builder("a").required(true).longOpt("arg-name").build(); 051 * </pre> 052 * 053 * @since 1.3 054 */ 055 public static final class Builder { 056 057 /** The name of the option */ 058 private String option; 059 060 /** description of the option */ 061 private String description; 062 063 /** The long representation of the option */ 064 private String longOption; 065 066 /** The name of the argument for this option */ 067 private String argName; 068 069 /** specifies whether this option is required to be present */ 070 private boolean required; 071 072 /** specifies whether the argument value of this Option is optional */ 073 private boolean optionalArg; 074 075 /** The number of argument values this option can have */ 076 private int argCount = UNINITIALIZED; 077 078 /** The type of this Option */ 079 private Class<?> type = String.class; 080 081 /** The character that is the value separator */ 082 private char valueSeparator; 083 084 /** 085 * Constructs a new {@code Builder} with the minimum required parameters for an {@code Option} instance. 086 * 087 * @param option short representation of the option 088 * @throws IllegalArgumentException if there are any non valid Option characters in {@code opt} 089 */ 090 private Builder(final String option) throws IllegalArgumentException { 091 option(option); 092 } 093 094 /** 095 * Sets the display name for the argument value. 096 * 097 * @param argName the display name for the argument value. 098 * @return this builder, to allow method chaining 099 */ 100 public Builder argName(final String argName) { 101 this.argName = argName; 102 return this; 103 } 104 105 /** 106 * Constructs an Option with the values declared by this {@link Builder}. 107 * 108 * @return the new {@link Option} 109 * @throws IllegalArgumentException if neither {@code opt} or {@code longOpt} has been set 110 */ 111 public Option build() { 112 if (option == null && longOption == null) { 113 throw new IllegalArgumentException("Either opt or longOpt must be specified"); 114 } 115 return new Option(this); 116 } 117 118 /** 119 * Sets the description for this option. 120 * 121 * @param description the description of the option. 122 * @return this builder, to allow method chaining 123 */ 124 public Builder desc(final String description) { 125 this.description = description; 126 return this; 127 } 128 129 /** 130 * Indicates that the Option will require an argument. 131 * 132 * @return this builder, to allow method chaining 133 */ 134 public Builder hasArg() { 135 return hasArg(true); 136 } 137 138 /** 139 * Indicates if the Option has an argument or not. 140 * 141 * @param hasArg specifies whether the Option takes an argument or not 142 * @return this builder, to allow method chaining 143 */ 144 public Builder hasArg(final boolean hasArg) { 145 // set to UNINITIALIZED when no arg is specified to be compatible with OptionBuilder 146 argCount = hasArg ? 1 : Option.UNINITIALIZED; 147 return this; 148 } 149 150 /** 151 * Indicates that the Option can have unlimited argument values. 152 * 153 * @return this builder, to allow method chaining 154 */ 155 public Builder hasArgs() { 156 argCount = Option.UNLIMITED_VALUES; 157 return this; 158 } 159 160 /** 161 * Sets the long name of the Option. 162 * 163 * @param longOpt the long name of the Option 164 * @return this builder, to allow method chaining 165 */ 166 public Builder longOpt(final String longOpt) { 167 this.longOption = longOpt; 168 return this; 169 } 170 171 /** 172 * Sets the number of argument values the Option can take. 173 * 174 * @param numberOfArgs the number of argument values 175 * @return this builder, to allow method chaining 176 */ 177 public Builder numberOfArgs(final int numberOfArgs) { 178 this.argCount = numberOfArgs; 179 return this; 180 } 181 182 /** 183 * Sets the name of the Option. 184 * 185 * @param option the name of the Option 186 * @return this builder, to allow method chaining 187 * @throws IllegalArgumentException if there are any non valid Option characters in {@code opt} 188 * @since 1.5.0 189 */ 190 public Builder option(final String option) throws IllegalArgumentException { 191 this.option = OptionValidator.validate(option); 192 return this; 193 } 194 195 /** 196 * Sets whether the Option can have an optional argument. 197 * 198 * @param isOptional specifies whether the Option can have an optional argument. 199 * @return this builder, to allow method chaining 200 */ 201 public Builder optionalArg(final boolean isOptional) { 202 this.optionalArg = isOptional; 203 return this; 204 } 205 206 /** 207 * Marks this Option as required. 208 * 209 * @return this builder, to allow method chaining 210 */ 211 public Builder required() { 212 return required(true); 213 } 214 215 /** 216 * Sets whether the Option is mandatory. 217 * 218 * @param required specifies whether the Option is mandatory 219 * @return this builder, to allow method chaining 220 */ 221 public Builder required(final boolean required) { 222 this.required = required; 223 return this; 224 } 225 226 /** 227 * Sets the type of the Option. 228 * 229 * @param type the type of the Option 230 * @return this builder, to allow method chaining 231 */ 232 public Builder type(final Class<?> type) { 233 this.type = type; 234 return this; 235 } 236 237 /** 238 * The Option will use '=' as a means to separate argument value. 239 * 240 * @return this builder, to allow method chaining 241 */ 242 public Builder valueSeparator() { 243 return valueSeparator('='); 244 } 245 246 /** 247 * The Option will use {@code sep} as a means to separate argument values. 248 * <p> 249 * <b>Example:</b> 250 * </p> 251 * 252 * <pre> 253 * Option opt = Option.builder("D").hasArgs().valueSeparator('=').build(); 254 * Options options = new Options(); 255 * options.addOption(opt); 256 * String[] args = {"-Dkey=value"}; 257 * CommandLineParser parser = new DefaultParser(); 258 * CommandLine line = parser.parse(options, args); 259 * String propertyName = line.getOptionValues("D")[0]; // will be "key" 260 * String propertyValue = line.getOptionValues("D")[1]; // will be "value" 261 * </pre> 262 * 263 * @param sep The value separator. 264 * @return this builder, to allow method chaining 265 */ 266 public Builder valueSeparator(final char sep) { 267 valueSeparator = sep; 268 return this; 269 } 270 } 271 272 /** Specifies the number of argument values has not been specified */ 273 public static final int UNINITIALIZED = -1; 274 275 /** Specifies the number of argument values is infinite */ 276 public static final int UNLIMITED_VALUES = -2; 277 278 /** The serial version UID. */ 279 private static final long serialVersionUID = 1L; 280 281 /** 282 * Returns a {@link Builder} to create an {@link Option} using descriptive methods. 283 * 284 * @return a new {@link Builder} instance 285 * @since 1.3 286 */ 287 public static Builder builder() { 288 return builder(null); 289 } 290 291 /** 292 * Returns a {@link Builder} to create an {@link Option} using descriptive methods. 293 * 294 * @param option short representation of the option 295 * @return a new {@link Builder} instance 296 * @throws IllegalArgumentException if there are any non valid Option characters in {@code opt} 297 * @since 1.3 298 */ 299 public static Builder builder(final String option) { 300 return new Builder(option); 301 } 302 303 /** The name of the option. */ 304 private final String option; 305 306 /** The long representation of the option. */ 307 private String longOption; 308 309 /** The name of the argument for this option. */ 310 private String argName; 311 312 /** Description of the option. */ 313 private String description; 314 315 /** Specifies whether this option is required to be present. */ 316 private boolean required; 317 318 /** Specifies whether the argument value of this Option is optional. */ 319 private boolean optionalArg; 320 321 /** The number of argument values this option can have. */ 322 private int argCount = UNINITIALIZED; 323 324 /** The type of this Option. */ 325 private Class<?> type = String.class; 326 327 /** The list of argument values. **/ 328 private List<String> values = new ArrayList<>(); 329 330 /** The character that is the value separator. */ 331 private char valuesep; 332 333 /** 334 * Private constructor used by the nested Builder class. 335 * 336 * @param builder builder used to create this option 337 */ 338 private Option(final Builder builder) { 339 this.argName = builder.argName; 340 this.description = builder.description; 341 this.longOption = builder.longOption; 342 this.argCount = builder.argCount; 343 this.option = builder.option; 344 this.optionalArg = builder.optionalArg; 345 this.required = builder.required; 346 this.type = builder.type; 347 this.valuesep = builder.valueSeparator; 348 } 349 350 /** 351 * Creates an Option using the specified parameters. 352 * 353 * @param option short representation of the option 354 * @param hasArg specifies whether the Option takes an argument or not 355 * @param description describes the function of the option 356 * 357 * @throws IllegalArgumentException if there are any non valid Option characters in {@code opt}. 358 */ 359 public Option(final String option, final boolean hasArg, final String description) throws IllegalArgumentException { 360 this(option, null, hasArg, description); 361 } 362 363 /** 364 * Creates an Option using the specified parameters. The option does not take an argument. 365 * 366 * @param option short representation of the option 367 * @param description describes the function of the option 368 * 369 * @throws IllegalArgumentException if there are any non valid Option characters in {@code opt}. 370 */ 371 public Option(final String option, final String description) throws IllegalArgumentException { 372 this(option, null, false, description); 373 } 374 375 /** 376 * Creates an Option using the specified parameters. 377 * 378 * @param option short representation of the option 379 * @param longOption the long representation of the option 380 * @param hasArg specifies whether the Option takes an argument or not 381 * @param description describes the function of the option 382 * 383 * @throws IllegalArgumentException if there are any non valid Option characters in {@code opt}. 384 */ 385 public Option(final String option, final String longOption, final boolean hasArg, final String description) throws IllegalArgumentException { 386 // ensure that the option is valid 387 this.option = OptionValidator.validate(option); 388 this.longOption = longOption; 389 390 // if hasArg is set then the number of arguments is 1 391 if (hasArg) { 392 this.argCount = 1; 393 } 394 395 this.description = description; 396 } 397 398 /** 399 * Tells if the option can accept more arguments. 400 * 401 * @return false if the maximum number of arguments is reached 402 * @since 1.3 403 */ 404 boolean acceptsArg() { 405 return (hasArg() || hasArgs() || hasOptionalArg()) && (argCount <= 0 || values.size() < argCount); 406 } 407 408 /** 409 * Add the value to this Option. If the number of arguments is greater than zero and there is enough space in the list 410 * then add the value. Otherwise, throw a runtime exception. 411 * 412 * @param value The value to be added to this Option 413 * 414 * @since 1.0.1 415 */ 416 private void add(final String value) { 417 if (!acceptsArg()) { 418 throw new RuntimeException("Cannot add value, list full."); 419 } 420 421 // store value 422 values.add(value); 423 } 424 425 /** 426 * This method is not intended to be used. It was a piece of internal API that was made public in 1.0. It currently 427 * throws an UnsupportedOperationException. 428 * 429 * @param value the value to add 430 * @return always throws an {@link UnsupportedOperationException} 431 * @throws UnsupportedOperationException always 432 * @deprecated Unused. 433 */ 434 @Deprecated 435 public boolean addValue(final String value) { 436 throw new UnsupportedOperationException( 437 "The addValue method is not intended for client use. " + "Subclasses should use the addValueForProcessing method instead. "); 438 } 439 440 /** 441 * Adds the specified value to this Option. 442 * 443 * @param value is a/the value of this Option 444 */ 445 void addValueForProcessing(final String value) { 446 if (argCount == UNINITIALIZED) { 447 throw new RuntimeException("NO_ARGS_ALLOWED"); 448 } 449 processValue(value); 450 } 451 452 /** 453 * Clear the Option values. After a parse is complete, these are left with data in them and they need clearing if 454 * another parse is done. 455 * 456 * See: <a href="https://issues.apache.org/jira/browse/CLI-71">CLI-71</a> 457 */ 458 void clearValues() { 459 values.clear(); 460 } 461 462 /** 463 * A rather odd clone method - due to incorrect code in 1.0 it is public and in 1.1 rather than throwing a 464 * CloneNotSupportedException it throws a RuntimeException so as to maintain backwards compat at the API level. 465 * 466 * After calling this method, it is very likely you will want to call clearValues(). 467 * 468 * @return a clone of this Option instance 469 * @throws RuntimeException if a {@link CloneNotSupportedException} has been thrown by {@code super.clone()} 470 */ 471 @Override 472 public Object clone() { 473 try { 474 final Option option = (Option) super.clone(); 475 option.values = new ArrayList<>(values); 476 return option; 477 } catch (final CloneNotSupportedException cnse) { 478 throw new RuntimeException("A CloneNotSupportedException was thrown: " + cnse.getMessage()); 479 } 480 } 481 482 @Override 483 public boolean equals(final Object obj) { 484 if (this == obj) { 485 return true; 486 } 487 if (!(obj instanceof Option)) { 488 return false; 489 } 490 final Option other = (Option) obj; 491 return Objects.equals(longOption, other.longOption) && Objects.equals(option, other.option); 492 } 493 494 /** 495 * Gets the display name for the argument value. 496 * 497 * @return the display name for the argument value. 498 */ 499 public String getArgName() { 500 return argName; 501 } 502 503 /** 504 * Gets the number of argument values this Option can take. 505 * 506 * <p> 507 * A value equal to the constant {@link #UNINITIALIZED} (= -1) indicates the number of arguments has not been specified. 508 * A value equal to the constant {@link #UNLIMITED_VALUES} (= -2) indicates that this options takes an unlimited amount 509 * of values. 510 * </p> 511 * 512 * @return num the number of argument values 513 * @see #UNINITIALIZED 514 * @see #UNLIMITED_VALUES 515 */ 516 public int getArgs() { 517 return argCount; 518 } 519 520 /** 521 * Gets the self-documenting description of this Option 522 * 523 * @return The string description of this option 524 */ 525 public String getDescription() { 526 return description; 527 } 528 529 /** 530 * Gets the id of this Option. This is only set when the Option shortOpt is a single character. This is used for 531 * switch statements. 532 * 533 * @return the id of this Option 534 */ 535 public int getId() { 536 return getKey().charAt(0); 537 } 538 539 /** 540 * Gets the 'unique' Option identifier. 541 * 542 * @return the 'unique' Option identifier 543 */ 544 String getKey() { 545 // if 'opt' is null, then it is a 'long' option 546 return option == null ? longOption : option; 547 } 548 549 /** 550 * Gets the long name of this Option. 551 * 552 * @return Long name of this option, or null, if there is no long name 553 */ 554 public String getLongOpt() { 555 return longOption; 556 } 557 558 /** 559 * Gets the name of this Option. 560 * 561 * It is this String which can be used with {@link CommandLine#hasOption(String opt)} and 562 * {@link CommandLine#getOptionValue(String opt)} to check for existence and argument. 563 * 564 * @return The name of this option 565 */ 566 public String getOpt() { 567 return option; 568 } 569 570 /** 571 * Gets the type of this Option. 572 * 573 * @return The type of this option 574 */ 575 public Object getType() { 576 return type; 577 } 578 579 /** 580 * Gets the specified value of this Option or {@code null} if there is no value. 581 * 582 * @return the value/first value of this Option or {@code null} if there is no value. 583 */ 584 public String getValue() { 585 return hasNoValues() ? null : values.get(0); 586 } 587 588 /** 589 * Gets the specified value of this Option or {@code null} if there is no value. 590 * 591 * @param index The index of the value to be returned. 592 * 593 * @return the specified value of this Option or {@code null} if there is no value. 594 * 595 * @throws IndexOutOfBoundsException if index is less than 1 or greater than the number of the values for this Option. 596 */ 597 public String getValue(final int index) throws IndexOutOfBoundsException { 598 return hasNoValues() ? null : values.get(index); 599 } 600 601 /** 602 * Gets the value/first value of this Option or the {@code defaultValue} if there is no value. 603 * 604 * @param defaultValue The value to be returned if there is no value. 605 * 606 * @return the value/first value of this Option or the {@code defaultValue} if there are no values. 607 */ 608 public String getValue(final String defaultValue) { 609 final String value = getValue(); 610 611 return value != null ? value : defaultValue; 612 } 613 614 /** 615 * Gets the values of this Option as a String array or null if there are no values 616 * 617 * @return the values of this Option as a String array or null if there are no values 618 */ 619 public String[] getValues() { 620 return hasNoValues() ? null : values.toArray(new String[values.size()]); 621 } 622 623 /** 624 * Gets the value separator character. 625 * 626 * @return the value separator character. 627 */ 628 public char getValueSeparator() { 629 return valuesep; 630 } 631 632 /** 633 * Gets the values of this Option as a List or null if there are no values. 634 * 635 * @return the values of this Option as a List or null if there are no values 636 */ 637 public List<String> getValuesList() { 638 return values; 639 } 640 641 /** 642 * Query to see if this Option requires an argument 643 * 644 * @return boolean flag indicating if an argument is required 645 */ 646 public boolean hasArg() { 647 return argCount > 0 || argCount == UNLIMITED_VALUES; 648 } 649 650 /** 651 * Returns whether the display name for the argument value has been set. 652 * 653 * @return if the display name for the argument value has been set. 654 */ 655 public boolean hasArgName() { 656 return argName != null && !argName.isEmpty(); 657 } 658 659 /** 660 * Query to see if this Option can take many values. 661 * 662 * @return boolean flag indicating if multiple values are allowed 663 */ 664 public boolean hasArgs() { 665 return argCount > 1 || argCount == UNLIMITED_VALUES; 666 } 667 668 @Override 669 public int hashCode() { 670 return Objects.hash(longOption, option); 671 } 672 673 /** 674 * Query to see if this Option has a long name 675 * 676 * @return boolean flag indicating existence of a long name 677 */ 678 public boolean hasLongOpt() { 679 return longOption != null; 680 } 681 682 /** 683 * Returns whether this Option has any values. 684 * 685 * @return whether this Option has any values. 686 */ 687 private boolean hasNoValues() { 688 return values.isEmpty(); 689 } 690 691 /** 692 * @return whether this Option can have an optional argument 693 */ 694 public boolean hasOptionalArg() { 695 return optionalArg; 696 } 697 698 /** 699 * Return whether this Option has specified a value separator. 700 * 701 * @return whether this Option has specified a value separator. 702 * @since 1.1 703 */ 704 public boolean hasValueSeparator() { 705 return valuesep > 0; 706 } 707 708 /** 709 * Query to see if this Option is mandatory 710 * 711 * @return boolean flag indicating whether this Option is mandatory 712 */ 713 public boolean isRequired() { 714 return required; 715 } 716 717 /** 718 * Processes the value. If this Option has a value separator the value will have to be parsed into individual tokens. 719 * When n-1 tokens have been processed and there are more value separators in the value, parsing is ceased and the 720 * remaining characters are added as a single token. 721 * 722 * @param value The String to be processed. 723 * 724 * @since 1.0.1 725 */ 726 private void processValue(String value) { 727 // this Option has a separator character 728 if (hasValueSeparator()) { 729 // get the separator character 730 final char sep = getValueSeparator(); 731 732 // store the index for the value separator 733 int index = value.indexOf(sep); 734 735 // while there are more value separators 736 while (index != -1) { 737 // next value to be added 738 if (values.size() == argCount - 1) { 739 break; 740 } 741 742 // store 743 add(value.substring(0, index)); 744 745 // parse 746 value = value.substring(index + 1); 747 748 // get new index 749 index = value.indexOf(sep); 750 } 751 } 752 753 // store the actual value or the last value that has been parsed 754 add(value); 755 } 756 757 /** 758 * Tells if the option requires more arguments to be valid. 759 * 760 * @return false if the option doesn't require more arguments 761 * @since 1.3 762 */ 763 boolean requiresArg() { 764 if (optionalArg) { 765 return false; 766 } 767 if (argCount == UNLIMITED_VALUES) { 768 return values.isEmpty(); 769 } 770 return acceptsArg(); 771 } 772 773 /** 774 * Sets the display name for the argument value. 775 * 776 * @param argName the display name for the argument value. 777 */ 778 public void setArgName(final String argName) { 779 this.argName = argName; 780 } 781 782 /** 783 * Sets the number of argument values this Option can take. 784 * 785 * @param num the number of argument values 786 */ 787 public void setArgs(final int num) { 788 this.argCount = num; 789 } 790 791 /** 792 * Sets the self-documenting description of this Option 793 * 794 * @param description The description of this option 795 * @since 1.1 796 */ 797 public void setDescription(final String description) { 798 this.description = description; 799 } 800 801 /** 802 * Sets the long name of this Option. 803 * 804 * @param longOpt the long name of this Option 805 */ 806 public void setLongOpt(final String longOpt) { 807 this.longOption = longOpt; 808 } 809 810 /** 811 * Sets whether this Option can have an optional argument. 812 * 813 * @param optionalArg specifies whether the Option can have an optional argument. 814 */ 815 public void setOptionalArg(final boolean optionalArg) { 816 this.optionalArg = optionalArg; 817 } 818 819 /** 820 * Sets whether this Option is mandatory. 821 * 822 * @param required specifies whether this Option is mandatory 823 */ 824 public void setRequired(final boolean required) { 825 this.required = required; 826 } 827 828 /** 829 * Sets the type of this Option. 830 * 831 * @param type the type of this Option 832 * @since 1.3 833 */ 834 public void setType(final Class<?> type) { 835 this.type = type; 836 } 837 838 /** 839 * Sets the type of this Option. 840 * <p> 841 * <b>Note:</b> this method is kept for binary compatibility and the input type is supposed to be a {@link Class} 842 * object. 843 * </p> 844 * 845 * @param type the type of this Option 846 * @deprecated since 1.3, use {@link #setType(Class)} instead 847 */ 848 @Deprecated 849 public void setType(final Object type) { 850 setType((Class<?>) type); 851 } 852 853 /** 854 * Sets the value separator. For example if the argument value was a Java property, the value separator would be '='. 855 * 856 * @param sep The value separator. 857 */ 858 public void setValueSeparator(final char sep) { 859 this.valuesep = sep; 860 } 861 862 /** 863 * Dump state, suitable for debugging. 864 * 865 * @return Stringified form of this object 866 */ 867 @Override 868 public String toString() { 869 final StringBuilder buf = new StringBuilder().append("[ option: "); 870 871 buf.append(option); 872 873 if (longOption != null) { 874 buf.append(" ").append(longOption); 875 } 876 877 buf.append(" "); 878 879 if (hasArgs()) { 880 buf.append("[ARG...]"); 881 } else if (hasArg()) { 882 buf.append(" [ARG]"); 883 } 884 885 buf.append(" :: ").append(description); 886 887 if (type != null) { 888 buf.append(" :: ").append(type); 889 } 890 891 buf.append(" ]"); 892 893 return buf.toString(); 894 } 895}