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