001/* 002 * Copyright 2008-2018 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2008-2018 Ping Identity Corporation 007 * 008 * This program is free software; you can redistribute it and/or modify 009 * it under the terms of the GNU General Public License (GPLv2 only) 010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 011 * as published by the Free Software Foundation. 012 * 013 * This program is distributed in the hope that it will be useful, 014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 016 * GNU General Public License for more details. 017 * 018 * You should have received a copy of the GNU General Public License 019 * along with this program; if not, see <http://www.gnu.org/licenses>. 020 */ 021package com.unboundid.util.args; 022 023 024 025import java.io.Serializable; 026 027import java.util.ArrayList; 028import java.util.Collections; 029import java.util.Iterator; 030import java.util.LinkedHashMap; 031import java.util.List; 032import java.util.Map; 033 034import com.unboundid.util.LDAPSDKUsageException; 035import com.unboundid.util.Mutable; 036import com.unboundid.util.NotExtensible; 037import com.unboundid.util.ThreadSafety; 038import com.unboundid.util.ThreadSafetyLevel; 039 040import static com.unboundid.util.args.ArgsMessages.*; 041 042 043 044/** 045 * This class defines a generic command line argument, which provides 046 * functionality applicable to all argument types. Subclasses may enforce 047 * additional constraints or provide additional functionality. 048 */ 049@NotExtensible() 050@Mutable() 051@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE) 052public abstract class Argument 053 implements Serializable 054{ 055 /** 056 * The serial version UID for this serializable class. 057 */ 058 private static final long serialVersionUID = -6938320885602903919L; 059 060 061 062 // Indicates whether this argument should be excluded from usage information. 063 private boolean isHidden; 064 065 // Indicates whether this argument has been registered with the argument 066 // parser. 067 private boolean isRegistered; 068 069 // Indicates whether this argument is required to be present. 070 private final boolean isRequired; 071 072 // Indicates whether values of this argument should be considered sensitive. 073 private boolean isSensitive; 074 075 // Indicates whether this argument is used to display usage information. 076 private boolean isUsageArgument; 077 078 // The maximum number of times this argument is allowed to be provided. 079 private int maxOccurrences; 080 081 // The number of times this argument was included in the provided command line 082 // arguments. 083 private int numOccurrences; 084 085 // The set of short identifiers for this argument, associated with an 086 // indication as to whether the identifier should be hidden. 087 private final Map<Character,Boolean> shortIdentifiers; 088 089 // The set of long identifiers for this argument, associated with an 090 // indication as to whether the identifier should be hidden. 091 private final Map<String,Boolean> longIdentifiers; 092 093 // The argument group name for this argument, if any. 094 private String argumentGroupName; 095 096 // The description for this argument. 097 private final String description; 098 099 // The value placeholder for this argument, or {@code null} if it does not 100 // take a value. 101 private final String valuePlaceholder; 102 103 104 105 /** 106 * Creates a new argument with the provided information. 107 * 108 * @param shortIdentifier The short identifier for this argument. It may 109 * not be {@code null} if the long identifier is 110 * {@code null}. 111 * @param longIdentifier The long identifier for this argument. It may 112 * not be {@code null} if the short identifier is 113 * {@code null}. 114 * @param isRequired Indicates whether this argument is required to 115 * be provided. 116 * @param maxOccurrences The maximum number of times this argument may be 117 * provided on the command line. A value less than 118 * or equal to zero indicates that it may be present 119 * any number of times. 120 * @param valuePlaceholder A placeholder to display in usage information to 121 * indicate that a value must be provided. If this 122 * is {@code null}, then the argument will not be 123 * allowed to take a value. If it is not 124 * {@code null}, then the argument will be required 125 * to take a value. 126 * @param description A human-readable description for this argument. 127 * It must not be {@code null}. 128 * 129 * @throws ArgumentException If there is a problem with the definition of 130 * this argument. 131 */ 132 protected Argument(final Character shortIdentifier, 133 final String longIdentifier, 134 final boolean isRequired, final int maxOccurrences, 135 final String valuePlaceholder, final String description) 136 throws ArgumentException 137 { 138 if (description == null) 139 { 140 throw new ArgumentException(ERR_ARG_DESCRIPTION_NULL.get()); 141 } 142 143 if ((shortIdentifier == null) && (longIdentifier == null)) 144 { 145 throw new ArgumentException(ERR_ARG_NO_IDENTIFIERS.get()); 146 } 147 148 shortIdentifiers = new LinkedHashMap<>(5); 149 if (shortIdentifier != null) 150 { 151 shortIdentifiers.put(shortIdentifier, false); 152 } 153 154 longIdentifiers = new LinkedHashMap<>(5); 155 if (longIdentifier != null) 156 { 157 longIdentifiers.put(longIdentifier, false); 158 } 159 160 this.isRequired = isRequired; 161 this.valuePlaceholder = valuePlaceholder; 162 this.description = description; 163 164 if (maxOccurrences > 0) 165 { 166 this.maxOccurrences = maxOccurrences; 167 } 168 else 169 { 170 this.maxOccurrences = Integer.MAX_VALUE; 171 } 172 173 argumentGroupName = null; 174 numOccurrences = 0; 175 isHidden = false; 176 isRegistered = false; 177 isSensitive = false; 178 isUsageArgument = false; 179 } 180 181 182 183 /** 184 * Creates a new argument with the same generic information as the provided 185 * argument. It will not be registered with any argument parser. 186 * 187 * @param source The argument to use as the source for this argument. 188 */ 189 protected Argument(final Argument source) 190 { 191 argumentGroupName = source.argumentGroupName; 192 isHidden = source.isHidden; 193 isRequired = source.isRequired; 194 isSensitive = source.isSensitive; 195 isUsageArgument = source.isUsageArgument; 196 maxOccurrences = source.maxOccurrences; 197 description = source.description; 198 valuePlaceholder = source.valuePlaceholder; 199 200 isRegistered = false; 201 numOccurrences = 0; 202 203 shortIdentifiers = new LinkedHashMap<>(source.shortIdentifiers); 204 longIdentifiers = new LinkedHashMap<>(source.longIdentifiers); 205 } 206 207 208 209 /** 210 * Indicates whether this argument has a short identifier. 211 * 212 * @return {@code true} if it has a short identifier, or {@code false} if 213 * not. 214 */ 215 public final boolean hasShortIdentifier() 216 { 217 return (! shortIdentifiers.isEmpty()); 218 } 219 220 221 222 /** 223 * Retrieves the short identifier for this argument. If there is more than 224 * one, then the first will be returned. 225 * 226 * @return The short identifier for this argument, or {@code null} if none is 227 * defined. 228 */ 229 public final Character getShortIdentifier() 230 { 231 for (final Map.Entry<Character,Boolean> e : shortIdentifiers.entrySet()) 232 { 233 if (e.getValue()) 234 { 235 continue; 236 } 237 238 return e.getKey(); 239 } 240 241 return null; 242 } 243 244 245 246 /** 247 * Retrieves the list of all short identifiers, including hidden identifiers, 248 * for this argument. 249 * 250 * @return The list of all short identifiers for this argument, or an empty 251 * list if there are no short identifiers. 252 */ 253 public final List<Character> getShortIdentifiers() 254 { 255 return getShortIdentifiers(true); 256 } 257 258 259 260 /** 261 * Retrieves the list of short identifiers for this argument. 262 * 263 * @param includeHidden Indicates whether to include hidden identifiers in 264 * the list that is returned. 265 * 266 * @return The list of short identifiers for this argument, or an empty list 267 * if there are none. 268 */ 269 public final List<Character> getShortIdentifiers(final boolean includeHidden) 270 { 271 final ArrayList<Character> identifierList = 272 new ArrayList<>(shortIdentifiers.size()); 273 for (final Map.Entry<Character,Boolean> e : shortIdentifiers.entrySet()) 274 { 275 if (includeHidden || (! e.getValue())) 276 { 277 identifierList.add(e.getKey()); 278 } 279 } 280 281 return Collections.unmodifiableList(identifierList); 282 } 283 284 285 286 /** 287 * Adds the provided character to the set of short identifiers for this 288 * argument. It will not be hidden. Note that this must be called before 289 * this argument is registered with the argument parser. 290 * 291 * @param c The character to add to the set of short identifiers for this 292 * argument. It must not be {@code null}. 293 * 294 * @throws ArgumentException If this argument is already registered with the 295 * argument parser. 296 */ 297 public final void addShortIdentifier(final Character c) 298 throws ArgumentException 299 { 300 addShortIdentifier(c, false); 301 } 302 303 304 305 /** 306 * Adds the provided character to the set of short identifiers for this 307 * argument. Note that this must be called before this argument is registered 308 * with the argument parser. 309 * 310 * @param c The character to add to the set of short identifiers for 311 * this argument. It must not be {@code null}. 312 * @param isHidden Indicates whether the provided identifier should be 313 * hidden. If this is {@code true}, then the identifier can 314 * be used to target this argument on the command line, but 315 * it will not be included in usage information. 316 * 317 * @throws ArgumentException If this argument is already registered with the 318 * argument parser. 319 */ 320 public final void addShortIdentifier(final Character c, 321 final boolean isHidden) 322 throws ArgumentException 323 { 324 if (isRegistered) 325 { 326 throw new ArgumentException(ERR_ARG_ID_CHANGE_AFTER_REGISTERED.get( 327 getIdentifierString())); 328 } 329 330 shortIdentifiers.put(c, isHidden); 331 } 332 333 334 335 /** 336 * Indicates whether this argument has a long identifier. 337 * 338 * @return {@code true} if it has a long identifier, or {@code false} if 339 * not. 340 */ 341 public final boolean hasLongIdentifier() 342 { 343 return (! longIdentifiers.isEmpty()); 344 } 345 346 347 348 /** 349 * Retrieves the long identifier for this argument. If it has multiple long 350 * identifiers, then the first will be returned. 351 * 352 * @return The long identifier for this argument, or {@code null} if none is 353 * defined. 354 */ 355 public final String getLongIdentifier() 356 { 357 for (final Map.Entry<String,Boolean> e : longIdentifiers.entrySet()) 358 { 359 if (e.getValue()) 360 { 361 continue; 362 } 363 364 return e.getKey(); 365 } 366 367 return null; 368 } 369 370 371 372 /** 373 * Retrieves the list of all long identifiers, including hidden identifiers, 374 * for this argument. 375 * 376 * @return The list of all long identifiers for this argument, or an empty 377 * list if there are no long identifiers. 378 */ 379 public final List<String> getLongIdentifiers() 380 { 381 return getLongIdentifiers(true); 382 } 383 384 385 386 /** 387 * Retrieves the list of long identifiers for this argument. 388 * 389 * @param includeHidden Indicates whether to include hidden identifiers in 390 * the list that is returned. 391 * 392 * @return The long identifier for this argument, or an empty list if there 393 * are none. 394 */ 395 public final List<String> getLongIdentifiers(final boolean includeHidden) 396 { 397 final ArrayList<String> identifierList = 398 new ArrayList<>(longIdentifiers.size()); 399 for (final Map.Entry<String,Boolean> e : longIdentifiers.entrySet()) 400 { 401 if (includeHidden || (! e.getValue())) 402 { 403 identifierList.add(e.getKey()); 404 } 405 } 406 407 return Collections.unmodifiableList(identifierList); 408 } 409 410 411 412 /** 413 * Adds the provided string to the set of short identifiers for this argument. 414 * It will not be hidden. Note that this must be called before this argument 415 * is registered with the argument parser. 416 * 417 * @param s The string to add to the set of short identifiers for this 418 * argument. It must not be {@code null}. 419 * 420 * @throws ArgumentException If this argument is already registered with the 421 * argument parser. 422 */ 423 public final void addLongIdentifier(final String s) 424 throws ArgumentException 425 { 426 addLongIdentifier(s, false); 427 } 428 429 430 431 /** 432 * Adds the provided string to the set of short identifiers for this argument. 433 * Note that this must be called before this argument is registered with the 434 * argument parser. 435 * 436 * @param s The string to add to the set of short identifiers for 437 * this argument. It must not be {@code null}. 438 * @param isHidden Indicates whether the provided identifier should be 439 * hidden. If this is {@code true}, then the identifier can 440 * be used to target this argument on the command line, but 441 * it will not be included in usage information. 442 * 443 * @throws ArgumentException If this argument is already registered with the 444 * argument parser. 445 */ 446 public final void addLongIdentifier(final String s, final boolean isHidden) 447 throws ArgumentException 448 { 449 if (isRegistered) 450 { 451 throw new ArgumentException(ERR_ARG_ID_CHANGE_AFTER_REGISTERED.get( 452 getIdentifierString())); 453 } 454 455 longIdentifiers.put(s, isHidden); 456 } 457 458 459 460 /** 461 * Retrieves a string that may be used to identify this argument. If a long 462 * identifier is defined, then the value returned will be two dashes followed 463 * by that string. Otherwise, the value returned will be a single dash 464 * followed by the short identifier. 465 * 466 * @return A string that may be used to identify this argument. 467 */ 468 public final String getIdentifierString() 469 { 470 for (final Map.Entry<String,Boolean> e : longIdentifiers.entrySet()) 471 { 472 if (! e.getValue()) 473 { 474 return "--" + e.getKey(); 475 } 476 } 477 478 for (final Map.Entry<Character,Boolean> e : shortIdentifiers.entrySet()) 479 { 480 if (! e.getValue()) 481 { 482 return "-" + e.getKey(); 483 } 484 } 485 486 // This should never happen. 487 throw new LDAPSDKUsageException( 488 ERR_ARG_NO_NON_HIDDEN_IDENTIFIER.get(toString())); 489 } 490 491 492 493 /** 494 * Indicates whether this argument is required to be provided. 495 * 496 * @return {@code true} if this argument is required to be provided, or 497 * {@code false} if not. 498 */ 499 public final boolean isRequired() 500 { 501 return isRequired; 502 } 503 504 505 506 /** 507 * Retrieves the maximum number of times that this argument may be provided. 508 * 509 * @return The maximum number of times that this argument may be provided. 510 */ 511 public final int getMaxOccurrences() 512 { 513 return maxOccurrences; 514 } 515 516 517 518 /** 519 * Specifies the maximum number of times that this argument may be provided. 520 * 521 * @param maxOccurrences The maximum number of times that this argument 522 * may be provided. A value less than or equal to 523 * zero indicates that there should be no limit on the 524 * maximum number of occurrences. 525 */ 526 public final void setMaxOccurrences(final int maxOccurrences) 527 { 528 if (maxOccurrences <= 0) 529 { 530 this.maxOccurrences = Integer.MAX_VALUE; 531 } 532 else 533 { 534 this.maxOccurrences = maxOccurrences; 535 } 536 } 537 538 539 540 /** 541 * Indicates whether this argument takes a value. 542 * 543 * @return {@code true} if this argument takes a value, or {@code false} if 544 * not. 545 */ 546 public boolean takesValue() 547 { 548 return (valuePlaceholder != null); 549 } 550 551 552 553 /** 554 * Retrieves the value placeholder string for this argument. 555 * 556 * @return The value placeholder string for this argument, or {@code null} if 557 * it does not take a value. 558 */ 559 public final String getValuePlaceholder() 560 { 561 return valuePlaceholder; 562 } 563 564 565 566 /** 567 * Retrieves a list containing the string representations of the values for 568 * this argument, if any. The list returned does not necessarily need to 569 * include values that will be acceptable to the argument, but it should imply 570 * what the values are (e.g., in the case of a boolean argument that doesn't 571 * take a value, it may be the string "true" or "false" even if those values 572 * are not acceptable to the argument itself). 573 * 574 * @param useDefault Indicates whether to use any configured default value 575 * if the argument doesn't have a user-specified value. 576 * 577 * @return A string representation of the value for this argument, or an 578 * empty list if the argument does not have a value. 579 */ 580 public abstract List<String> getValueStringRepresentations( 581 boolean useDefault); 582 583 584 585 /** 586 * Retrieves the description for this argument. 587 * 588 * @return The description for this argument. 589 */ 590 public final String getDescription() 591 { 592 return description; 593 } 594 595 596 597 /** 598 * Retrieves the name of the argument group to which this argument belongs. 599 * 600 * @return The name of the argument group to which this argument belongs, or 601 * {@code null} if this argument has not been assigned to any group. 602 */ 603 public final String getArgumentGroupName() 604 { 605 return argumentGroupName; 606 } 607 608 609 610 /** 611 * Sets the name of the argument group to which this argument belongs. If 612 * a tool updates arguments to specify an argument group for some or all of 613 * the arguments, then the usage information will have the arguments listed 614 * together in their respective groups. Note that usage arguments should 615 * generally not be assigned to an argument group. 616 * 617 * @param argumentGroupName The argument group name for this argument. It 618 * may be {@code null} if this argument should not 619 * be assigned to any particular group. 620 */ 621 public final void setArgumentGroupName(final String argumentGroupName) 622 { 623 this.argumentGroupName = argumentGroupName; 624 } 625 626 627 628 /** 629 * Indicates whether this argument should be excluded from usage information. 630 * 631 * @return {@code true} if this argument should be excluded from usage 632 * information, or {@code false} if not. 633 */ 634 public final boolean isHidden() 635 { 636 return isHidden; 637 } 638 639 640 641 /** 642 * Specifies whether this argument should be excluded from usage information. 643 * 644 * @param isHidden Specifies whether this argument should be excluded from 645 * usage information. 646 */ 647 public final void setHidden(final boolean isHidden) 648 { 649 this.isHidden = isHidden; 650 } 651 652 653 654 /** 655 * Indicates whether this argument is intended to be used to trigger the 656 * display of usage information. If a usage argument is provided on the 657 * command line, then the argument parser will not complain about missing 658 * required arguments or unresolved dependencies. 659 * 660 * @return {@code true} if this argument is a usage argument, or 661 * {@code false} if not. 662 */ 663 public final boolean isUsageArgument() 664 { 665 return isUsageArgument; 666 } 667 668 669 670 /** 671 * Specifies whether this argument should be considered a usage argument. 672 * 673 * @param isUsageArgument Specifies whether this argument should be 674 * considered a usage argument. 675 */ 676 public final void setUsageArgument(final boolean isUsageArgument) 677 { 678 this.isUsageArgument = isUsageArgument; 679 } 680 681 682 683 /** 684 * Indicates whether this argument was either included in the provided set of 685 * command line arguments or has a default value that can be used instead. 686 * This method should not be called until after the argument parser has 687 * processed the provided set of arguments. 688 * 689 * @return {@code true} if this argument was included in the provided set of 690 * command line arguments, or {@code false} if not. 691 */ 692 public final boolean isPresent() 693 { 694 return ((numOccurrences > 0) || hasDefaultValue()); 695 } 696 697 698 699 /** 700 * Retrieves the number of times that this argument was included in the 701 * provided set of command line arguments. This method should not be called 702 * until after the argument parser has processed the provided set of 703 * arguments. 704 * 705 * @return The number of times that this argument was included in the 706 * provided set of command line arguments. 707 */ 708 public final int getNumOccurrences() 709 { 710 return numOccurrences; 711 } 712 713 714 715 /** 716 * Increments the number of occurrences for this argument in the provided set 717 * of command line arguments. This method should only be called by the 718 * argument parser. 719 * 720 * @throws ArgumentException If incrementing the number of occurrences would 721 * exceed the maximum allowed number. 722 */ 723 final void incrementOccurrences() 724 throws ArgumentException 725 { 726 if (numOccurrences >= maxOccurrences) 727 { 728 throw new ArgumentException(ERR_ARG_MAX_OCCURRENCES_EXCEEDED.get( 729 getIdentifierString())); 730 } 731 732 numOccurrences++; 733 } 734 735 736 737 /** 738 * Adds the provided value to the set of values for this argument. This 739 * method should only be called by the argument parser. 740 * 741 * @param valueString The string representation of the value. 742 * 743 * @throws ArgumentException If the provided value is not acceptable, if 744 * this argument does not accept values, or if 745 * this argument already has the maximum allowed 746 * number of values. 747 */ 748 protected abstract void addValue(String valueString) 749 throws ArgumentException; 750 751 752 753 /** 754 * Indicates whether this argument has one or more default values that will be 755 * used if it is not provided on the command line. 756 * 757 * @return {@code true} if this argument has one or more default values, or 758 * {@code false} if not. 759 */ 760 protected abstract boolean hasDefaultValue(); 761 762 763 764 /** 765 * Indicates whether values of this argument are considered sensitive. 766 * Argument values that are considered sensitive will be obscured in places 767 * where they may be shown. 768 * 769 * @return {@code true} if values of this argument are considered sensitive, 770 * or {@code false} if not. 771 */ 772 public final boolean isSensitive() 773 { 774 return isSensitive; 775 } 776 777 778 779 /** 780 * Specifies whether values of this argument are considered sensitive. 781 * Argument values that are considered sensitive will be obscured in places 782 * where they may be shown. 783 * 784 * @param isSensitive Indicates whether values of this argument are 785 * considered sensitive. 786 */ 787 public final void setSensitive(final boolean isSensitive) 788 { 789 this.isSensitive = isSensitive; 790 } 791 792 793 794 /** 795 * Indicates whether this argument has been registered with the argument 796 * parser. 797 * 798 * @return {@code true} if this argument has been registered with the 799 * argument parser, or {@code false} if not. 800 */ 801 boolean isRegistered() 802 { 803 return isRegistered; 804 } 805 806 807 808 /** 809 * Specifies that this argument has been registered with the argument parser. 810 * This method should only be called by the argument parser method used to 811 * register the argument. 812 * 813 * @throws ArgumentException If this argument has already been registered. 814 */ 815 void setRegistered() 816 throws ArgumentException 817 { 818 if (isRegistered) 819 { 820 throw new ArgumentException(ERR_ARG_ALREADY_REGISTERED.get( 821 getIdentifierString())); 822 } 823 824 isRegistered = true; 825 } 826 827 828 829 /** 830 * Retrieves a concise name of the data type with which this argument is 831 * associated. 832 * 833 * @return A concise name of the data type with which this argument is 834 * associated. 835 */ 836 public abstract String getDataTypeName(); 837 838 839 840 /** 841 * Retrieves a human-readable string with information about any constraints 842 * that may be imposed for values of this argument. 843 * 844 * @return A human-readable string with information about any constraints 845 * that may be imposed for values of this argument, or {@code null} 846 * if there are none. 847 */ 848 public String getValueConstraints() 849 { 850 return null; 851 } 852 853 854 855 /** 856 * Resets this argument so that it appears in the same form as before it was 857 * used to parse arguments. Subclasses that override this method must call 858 * {@code super.reset()} to ensure that all necessary reset processing is 859 * performed. 860 */ 861 protected void reset() 862 { 863 numOccurrences = 0; 864 } 865 866 867 868 /** 869 * Creates a copy of this argument that is "clean" and appears as if it has 870 * not been used in the course of parsing an argument set. The new argument 871 * will have all of the same identifiers and constraints as this parser. 872 * 873 * @return The "clean" copy of this argument. 874 */ 875 public abstract Argument getCleanCopy(); 876 877 878 879 /** 880 * Updates the provided list to add any strings that should be included on the 881 * command line in order to represent this argument's current state. 882 * 883 * @param argStrings The list to update with the string representation of 884 * the command-line arguments. 885 */ 886 protected abstract void addToCommandLine(List<String> argStrings); 887 888 889 890 /** 891 * Retrieves a string representation of this argument. 892 * 893 * @return A string representation of this argument. 894 */ 895 public final String toString() 896 { 897 final StringBuilder buffer = new StringBuilder(); 898 toString(buffer); 899 return buffer.toString(); 900 } 901 902 903 904 /** 905 * Appends a string representation of this argument to the provided buffer. 906 * 907 * @param buffer The buffer to which the information should be appended. 908 */ 909 public abstract void toString(StringBuilder buffer); 910 911 912 913 /** 914 * Appends a basic set of information for this argument to the provided 915 * buffer in a form suitable for use in the {@code toString} method. 916 * 917 * @param buffer The buffer to which information should be appended. 918 */ 919 protected void appendBasicToStringInfo(final StringBuilder buffer) 920 { 921 switch (shortIdentifiers.size()) 922 { 923 case 0: 924 // Nothing to add. 925 break; 926 927 case 1: 928 buffer.append("shortIdentifier='-"); 929 buffer.append(shortIdentifiers.keySet().iterator().next()); 930 buffer.append('\''); 931 break; 932 933 default: 934 buffer.append("shortIdentifiers={"); 935 936 final Iterator<Character> iterator = 937 shortIdentifiers.keySet().iterator(); 938 while (iterator.hasNext()) 939 { 940 buffer.append("'-"); 941 buffer.append(iterator.next()); 942 buffer.append('\''); 943 944 if (iterator.hasNext()) 945 { 946 buffer.append(", "); 947 } 948 } 949 buffer.append('}'); 950 break; 951 } 952 953 if (! shortIdentifiers.isEmpty()) 954 { 955 buffer.append(", "); 956 } 957 958 switch (longIdentifiers.size()) 959 { 960 case 0: 961 // Nothing to add. 962 break; 963 964 case 1: 965 buffer.append("longIdentifier='--"); 966 buffer.append(longIdentifiers.keySet().iterator().next()); 967 buffer.append('\''); 968 break; 969 970 default: 971 buffer.append("longIdentifiers={"); 972 973 final Iterator<String> iterator = longIdentifiers.keySet().iterator(); 974 while (iterator.hasNext()) 975 { 976 buffer.append("'--"); 977 buffer.append(iterator.next()); 978 buffer.append('\''); 979 980 if (iterator.hasNext()) 981 { 982 buffer.append(", "); 983 } 984 } 985 buffer.append('}'); 986 break; 987 } 988 989 buffer.append(", description='"); 990 buffer.append(description); 991 992 if (argumentGroupName != null) 993 { 994 buffer.append("', argumentGroup='"); 995 buffer.append(argumentGroupName); 996 } 997 998 buffer.append("', isRequired="); 999 buffer.append(isRequired); 1000 1001 buffer.append(", maxOccurrences="); 1002 if (maxOccurrences == 0) 1003 { 1004 buffer.append("unlimited"); 1005 } 1006 else 1007 { 1008 buffer.append(maxOccurrences); 1009 } 1010 1011 if (valuePlaceholder == null) 1012 { 1013 buffer.append(", takesValue=false"); 1014 } 1015 else 1016 { 1017 buffer.append(", takesValue=true, valuePlaceholder='"); 1018 buffer.append(valuePlaceholder); 1019 buffer.append('\''); 1020 } 1021 1022 if (isHidden) 1023 { 1024 buffer.append(", isHidden=true"); 1025 } 1026 } 1027}