001/* 002 * Copyright 2008-2019 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2008-2019 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.util.ArrayList; 026import java.util.Collections; 027import java.util.HashSet; 028import java.util.Iterator; 029import java.util.List; 030import java.util.Set; 031import java.util.regex.Matcher; 032import java.util.regex.Pattern; 033 034import com.unboundid.util.Mutable; 035import com.unboundid.util.StaticUtils; 036import com.unboundid.util.ThreadSafety; 037import com.unboundid.util.ThreadSafetyLevel; 038 039import static com.unboundid.util.args.ArgsMessages.*; 040 041 042 043/** 044 * This class defines an argument that is intended to hold one or more string 045 * values. String arguments must take values. By default, any value will be 046 * allowed, but it is possible to restrict the set of values so that only values 047 * from a specified set (ignoring differences in capitalization) will be 048 * allowed. 049 */ 050@Mutable() 051@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE) 052public final class StringArgument 053 extends Argument 054{ 055 /** 056 * The serial version UID for this serializable class. 057 */ 058 private static final long serialVersionUID = 1088032496970585118L; 059 060 061 062 // The set of values assigned to this argument. 063 private final ArrayList<String> values; 064 065 // The argument value validators that have been registered for this argument. 066 private final List<ArgumentValueValidator> validators; 067 068 // The list of default values that will be used if no values were provided. 069 private final List<String> defaultValues; 070 071 // A regular expression that may be enforced for values of this argument. 072 private volatile Pattern valueRegex; 073 074 // The set of allowed values for this argument. 075 private final Set<String> allowedValues; 076 077 // A human-readable explanation of the regular expression pattern. 078 private volatile String valueRegexExplanation; 079 080 081 082 /** 083 * Creates a new string argument with the provided information. It will not 084 * be required, will permit at most one value, will use a default placeholder, 085 * will not have any default value, and will not place any restriction on 086 * values that may be assigned. 087 * 088 * @param shortIdentifier The short identifier for this argument. It may 089 * not be {@code null} if the long identifier is 090 * {@code null}. 091 * @param longIdentifier The long identifier for this argument. It may 092 * not be {@code null} if the short identifier is 093 * {@code null}. 094 * @param description A human-readable description for this argument. 095 * It must not be {@code null}. 096 * 097 * @throws ArgumentException If there is a problem with the definition of 098 * this argument. 099 */ 100 public StringArgument(final Character shortIdentifier, 101 final String longIdentifier, final String description) 102 throws ArgumentException 103 { 104 this(shortIdentifier, longIdentifier, false, 1, null, description); 105 } 106 107 108 109 /** 110 * Creates a new string argument with the provided information. There will 111 * not be any default values, nor will there be any restriction on values that 112 * may be assigned to this argument. 113 * 114 * @param shortIdentifier The short identifier for this argument. It may 115 * not be {@code null} if the long identifier is 116 * {@code null}. 117 * @param longIdentifier The long identifier for this argument. It may 118 * not be {@code null} if the short identifier is 119 * {@code null}. 120 * @param isRequired Indicates whether this argument is required to 121 * be provided. 122 * @param maxOccurrences The maximum number of times this argument may be 123 * provided on the command line. A value less than 124 * or equal to zero indicates that it may be present 125 * any number of times. 126 * @param valuePlaceholder A placeholder to display in usage information to 127 * indicate that a value must be provided. It may 128 * be {@code null} if a default placeholder should 129 * be used. 130 * @param description A human-readable description for this argument. 131 * It must not be {@code null}. 132 * 133 * @throws ArgumentException If there is a problem with the definition of 134 * this argument. 135 */ 136 public StringArgument(final Character shortIdentifier, 137 final String longIdentifier, final boolean isRequired, 138 final int maxOccurrences, final String valuePlaceholder, 139 final String description) 140 throws ArgumentException 141 { 142 this(shortIdentifier, longIdentifier, isRequired, maxOccurrences, 143 valuePlaceholder, description, null, (List<String>) null); 144 } 145 146 147 148 /** 149 * Creates a new string argument with the provided information. There will 150 * not be any default values. 151 * 152 * @param shortIdentifier The short identifier for this argument. It may 153 * not be {@code null} if the long identifier is 154 * {@code null}. 155 * @param longIdentifier The long identifier for this argument. It may 156 * not be {@code null} if the short identifier is 157 * {@code null}. 158 * @param isRequired Indicates whether this argument is required to 159 * be provided. 160 * @param maxOccurrences The maximum number of times this argument may be 161 * provided on the command line. A value less than 162 * or equal to zero indicates that it may be present 163 * any number of times. 164 * @param valuePlaceholder A placeholder to display in usage information to 165 * indicate that a value must be provided. It may 166 * be {@code null} if a default placeholder should 167 * be used. 168 * @param description A human-readable description for this argument. 169 * It must not be {@code null}. 170 * @param allowedValues The set of allowed values for this argument, or 171 * {@code null} if it should not be restricted. 172 * 173 * @throws ArgumentException If there is a problem with the definition of 174 * this argument. 175 */ 176 public StringArgument(final Character shortIdentifier, 177 final String longIdentifier, final boolean isRequired, 178 final int maxOccurrences, final String valuePlaceholder, 179 final String description, 180 final Set<String> allowedValues) 181 throws ArgumentException 182 { 183 this(shortIdentifier, longIdentifier, isRequired, maxOccurrences, 184 valuePlaceholder, description, allowedValues, (List<String>) null); 185 } 186 187 188 189 /** 190 * Creates a new string argument with the provided information. There will 191 * not be any restriction on values that may be assigned to this argument. 192 * 193 * @param shortIdentifier The short identifier for this argument. It may 194 * not be {@code null} if the long identifier is 195 * {@code null}. 196 * @param longIdentifier The long identifier for this argument. It may 197 * not be {@code null} if the short identifier is 198 * {@code null}. 199 * @param isRequired Indicates whether this argument is required to 200 * be provided. 201 * @param maxOccurrences The maximum number of times this argument may be 202 * provided on the command line. A value less than 203 * or equal to zero indicates that it may be present 204 * any number of times. 205 * @param valuePlaceholder A placeholder to display in usage information to 206 * indicate that a value must be provided. It may 207 * be {@code null} if a default placeholder should 208 * be used. 209 * @param description A human-readable description for this argument. 210 * It must not be {@code null}. 211 * @param defaultValue The default value that will be used for this 212 * argument if no values are provided. It may be 213 * {@code null} if there should not be a default 214 * value. 215 * 216 * @throws ArgumentException If there is a problem with the definition of 217 * this argument. 218 */ 219 public StringArgument(final Character shortIdentifier, 220 final String longIdentifier, final boolean isRequired, 221 final int maxOccurrences, final String valuePlaceholder, 222 final String description, 223 final String defaultValue) 224 throws ArgumentException 225 { 226 this(shortIdentifier, longIdentifier, isRequired, maxOccurrences, 227 valuePlaceholder, description, null, 228 ((defaultValue == null) 229 ? null 230 : Collections.singletonList(defaultValue))); 231 } 232 233 234 235 /** 236 * Creates a new string argument with the provided information. There will 237 * not be any restriction on values that may be assigned to this argument. 238 * 239 * @param shortIdentifier The short identifier for this argument. It may 240 * not be {@code null} if the long identifier is 241 * {@code null}. 242 * @param longIdentifier The long identifier for this argument. It may 243 * not be {@code null} if the short identifier is 244 * {@code null}. 245 * @param isRequired Indicates whether this argument is required to 246 * be provided. 247 * @param maxOccurrences The maximum number of times this argument may be 248 * provided on the command line. A value less than 249 * or equal to zero indicates that it may be present 250 * any number of times. 251 * @param valuePlaceholder A placeholder to display in usage information to 252 * indicate that a value must be provided. It may 253 * be {@code null} if a default placeholder should 254 * be used. 255 * @param description A human-readable description for this argument. 256 * It must not be {@code null}. 257 * @param defaultValues The set of default values that will be used for 258 * this argument if no values are provided. 259 * 260 * @throws ArgumentException If there is a problem with the definition of 261 * this argument. 262 */ 263 public StringArgument(final Character shortIdentifier, 264 final String longIdentifier, final boolean isRequired, 265 final int maxOccurrences, final String valuePlaceholder, 266 final String description, 267 final List<String> defaultValues) 268 throws ArgumentException 269 { 270 this(shortIdentifier, longIdentifier, isRequired, maxOccurrences, 271 valuePlaceholder, description, null, defaultValues); 272 } 273 274 275 276 /** 277 * Creates a new string argument with the provided information. 278 * 279 * @param shortIdentifier The short identifier for this argument. It may 280 * not be {@code null} if the long identifier is 281 * {@code null}. 282 * @param longIdentifier The long identifier for this argument. It may 283 * not be {@code null} if the short identifier is 284 * {@code null}. 285 * @param isRequired Indicates whether this argument is required to 286 * be provided. 287 * @param maxOccurrences The maximum number of times this argument may be 288 * provided on the command line. A value less than 289 * or equal to zero indicates that it may be present 290 * any number of times. 291 * @param valuePlaceholder A placeholder to display in usage information to 292 * indicate that a value must be provided. It may 293 * be {@code null} if a default placeholder should 294 * be used. 295 * @param description A human-readable description for this argument. 296 * It must not be {@code null}. 297 * @param allowedValues The set of allowed values for this argument, or 298 * {@code null} if it should not be restricted. 299 * @param defaultValue The default value that will be used for this 300 * argument if no values are provided. It may be 301 * {@code null} if there should not be a default 302 * value. 303 * 304 * @throws ArgumentException If there is a problem with the definition of 305 * this argument. 306 */ 307 public StringArgument(final Character shortIdentifier, 308 final String longIdentifier, final boolean isRequired, 309 final int maxOccurrences, final String valuePlaceholder, 310 final String description, 311 final Set<String> allowedValues, 312 final String defaultValue) 313 throws ArgumentException 314 { 315 this(shortIdentifier, longIdentifier, isRequired, maxOccurrences, 316 valuePlaceholder, description, allowedValues, 317 ((defaultValue == null) 318 ? null 319 : Collections.singletonList(defaultValue))); 320 } 321 322 323 324 /** 325 * Creates a new string argument with the provided information. 326 * 327 * @param shortIdentifier The short identifier for this argument. It may 328 * not be {@code null} if the long identifier is 329 * {@code null}. 330 * @param longIdentifier The long identifier for this argument. It may 331 * not be {@code null} if the short identifier is 332 * {@code null}. 333 * @param isRequired Indicates whether this argument is required to 334 * be provided. 335 * @param maxOccurrences The maximum number of times this argument may be 336 * provided on the command line. A value less than 337 * or equal to zero indicates that it may be present 338 * any number of times. 339 * @param valuePlaceholder A placeholder to display in usage information to 340 * indicate that a value must be provided. It may 341 * be {@code null} if a default placeholder should 342 * be used. 343 * @param description A human-readable description for this argument. 344 * It must not be {@code null}. 345 * @param allowedValues The set of allowed values for this argument, or 346 * {@code null} if it should not be restricted. 347 * @param defaultValues The set of default values that will be used for 348 * this argument if no values are provided. 349 * 350 * @throws ArgumentException If there is a problem with the definition of 351 * this argument. 352 */ 353 public StringArgument(final Character shortIdentifier, 354 final String longIdentifier, final boolean isRequired, 355 final int maxOccurrences, final String valuePlaceholder, 356 final String description, 357 final Set<String> allowedValues, 358 final List<String> defaultValues) 359 throws ArgumentException 360 { 361 super(shortIdentifier, longIdentifier, isRequired, maxOccurrences, 362 (valuePlaceholder == null) 363 ? INFO_PLACEHOLDER_VALUE.get() 364 : valuePlaceholder, 365 description); 366 367 if ((allowedValues == null) || allowedValues.isEmpty()) 368 { 369 this.allowedValues = null; 370 } 371 else 372 { 373 final HashSet<String> lowerValues = 374 new HashSet<>(StaticUtils.computeMapCapacity(allowedValues.size())); 375 for (final String s : allowedValues) 376 { 377 lowerValues.add(StaticUtils.toLowerCase(s)); 378 } 379 this.allowedValues = Collections.unmodifiableSet(lowerValues); 380 } 381 382 if ((defaultValues == null) || defaultValues.isEmpty()) 383 { 384 this.defaultValues = null; 385 } 386 else 387 { 388 this.defaultValues = Collections.unmodifiableList(defaultValues); 389 } 390 391 if ((this.allowedValues != null) && (this.defaultValues != null)) 392 { 393 for (final String s : this.defaultValues) 394 { 395 final String lowerDefault = StaticUtils.toLowerCase(s); 396 if (! this.allowedValues.contains(lowerDefault)) 397 { 398 throw new ArgumentException( 399 ERR_ARG_DEFAULT_VALUE_NOT_ALLOWED.get(s, getIdentifierString())); 400 } 401 } 402 } 403 404 values = new ArrayList<>(5); 405 validators = new ArrayList<>(5); 406 valueRegex = null; 407 valueRegexExplanation = null; 408 } 409 410 411 412 /** 413 * Creates a new string argument that is a "clean" copy of the provided source 414 * argument. 415 * 416 * @param source The source argument to use for this argument. 417 */ 418 private StringArgument(final StringArgument source) 419 { 420 super(source); 421 422 allowedValues = source.allowedValues; 423 defaultValues = source.defaultValues; 424 valueRegex = source.valueRegex; 425 valueRegexExplanation = source.valueRegexExplanation; 426 values = new ArrayList<>(5); 427 validators = new ArrayList<>(source.validators); 428 } 429 430 431 432 /** 433 * Retrieves the set of allowed values for this argument, if applicable. 434 * 435 * @return The set of allowed values for this argument, or {@code null} if 436 * there is no restriction on the allowed values. 437 */ 438 public Set<String> getAllowedValues() 439 { 440 return allowedValues; 441 } 442 443 444 445 /** 446 * Retrieves the list of default values for this argument, which will be used 447 * if no values were provided. 448 * 449 * @return The list of default values for this argument, or {@code null} if 450 * there are no default values. 451 */ 452 public List<String> getDefaultValues() 453 { 454 return defaultValues; 455 } 456 457 458 459 /** 460 * Retrieves the regular expression that values of this argument will be 461 * required to match, if any. 462 * 463 * @return The regular expression that values of this argument will be 464 * required to match, or {@code null} if none is defined. 465 */ 466 public Pattern getValueRegex() 467 { 468 return valueRegex; 469 } 470 471 472 473 /** 474 * Retrieves a human-readable explanation of the regular expression pattern 475 * that may be required to match any provided values, if any. 476 * 477 * @return A human-readable explanation of the regular expression pattern, or 478 * {@code null} if none is available. 479 */ 480 public String getValueRegexExplanation() 481 { 482 return valueRegexExplanation; 483 } 484 485 486 487 /** 488 * Specifies the regular expression that values of this argument will be 489 * required to match, if any. 490 * 491 * @param valueRegex The regular expression that values of this argument 492 * will be required to match. It may be {@code null} if 493 * no pattern matching should be required. 494 * @param explanation A human-readable explanation for the pattern which may 495 * be used to clarify the kinds of values that are 496 * acceptable. It may be {@code null} if no pattern 497 * matching should be required, or if the regular 498 * expression pattern should be sufficiently clear for 499 * the target audience. 500 */ 501 public void setValueRegex(final Pattern valueRegex, 502 final String explanation) 503 { 504 this.valueRegex = valueRegex; 505 valueRegexExplanation = explanation; 506 } 507 508 509 510 /** 511 * Updates this argument to ensure that the provided validator will be invoked 512 * for any values provided to this argument. This validator will be invoked 513 * after all other validation has been performed for this argument. 514 * 515 * @param validator The argument value validator to be invoked. It must not 516 * be {@code null}. 517 */ 518 public void addValueValidator(final ArgumentValueValidator validator) 519 { 520 validators.add(validator); 521 } 522 523 524 525 /** 526 * {@inheritDoc} 527 */ 528 @Override() 529 protected void addValue(final String valueString) 530 throws ArgumentException 531 { 532 final String lowerValue = StaticUtils.toLowerCase(valueString); 533 if (allowedValues != null) 534 { 535 if (! allowedValues.contains(lowerValue)) 536 { 537 final StringBuilder allowedValuesBuffer = new StringBuilder(); 538 for (final String allowedValue : allowedValues) 539 { 540 if (allowedValuesBuffer.length() > 0) 541 { 542 allowedValuesBuffer.append(", "); 543 } 544 545 allowedValuesBuffer.append('\''); 546 allowedValuesBuffer.append(allowedValue); 547 allowedValuesBuffer.append('\''); 548 } 549 550 throw new ArgumentException(ERR_ARG_VALUE_NOT_ALLOWED.get( 551 valueString, getIdentifierString(), 552 allowedValuesBuffer.toString())); 553 } 554 } 555 556 if (values.size() >= getMaxOccurrences()) 557 { 558 throw new ArgumentException(ERR_ARG_MAX_OCCURRENCES_EXCEEDED.get( 559 getIdentifierString())); 560 } 561 562 if (valueRegex != null) 563 { 564 final Matcher matcher = valueRegex.matcher(valueString); 565 if (! matcher.matches()) 566 { 567 final String pattern = valueRegex.pattern(); 568 if (valueRegexExplanation == null) 569 { 570 throw new ArgumentException( 571 ERR_ARG_VALUE_DOES_NOT_MATCH_PATTERN_WITHOUT_EXPLANATION.get( 572 valueString, getIdentifierString(), pattern)); 573 } 574 else 575 { 576 throw new ArgumentException( 577 ERR_ARG_VALUE_DOES_NOT_MATCH_PATTERN_WITH_EXPLANATION.get( 578 valueString, getIdentifierString(), pattern, 579 valueRegexExplanation)); 580 } 581 } 582 } 583 584 for (final ArgumentValueValidator v : validators) 585 { 586 v.validateArgumentValue(this, valueString); 587 } 588 589 values.add(valueString); 590 } 591 592 593 594 /** 595 * Retrieves the value for this argument, or the default value if none was 596 * provided. If this argument has multiple values, then the first will be 597 * returned. 598 * 599 * @return The value for this argument, or the default value if none was 600 * provided, or {@code null} if it does not have any values or 601 * default values. 602 */ 603 public String getValue() 604 { 605 if (values.isEmpty()) 606 { 607 if ((defaultValues == null) || defaultValues.isEmpty()) 608 { 609 return null; 610 } 611 else 612 { 613 return defaultValues.get(0); 614 } 615 } 616 617 return values.get(0); 618 } 619 620 621 622 /** 623 * Retrieves the set of values for this argument, or the default values if 624 * none were provided. 625 * 626 * @return The set of values for this argument, or the default values if none 627 * were provided. 628 */ 629 public List<String> getValues() 630 { 631 if (values.isEmpty() && (defaultValues != null)) 632 { 633 return defaultValues; 634 } 635 636 return Collections.unmodifiableList(values); 637 } 638 639 640 641 /** 642 * {@inheritDoc} 643 */ 644 @Override() 645 public List<String> getValueStringRepresentations(final boolean useDefault) 646 { 647 if (! values.isEmpty()) 648 { 649 return Collections.unmodifiableList(values); 650 } 651 else if (useDefault && (defaultValues != null)) 652 { 653 return Collections.unmodifiableList(defaultValues); 654 } 655 else 656 { 657 return Collections.emptyList(); 658 } 659 } 660 661 662 663 /** 664 * {@inheritDoc} 665 */ 666 @Override() 667 protected boolean hasDefaultValue() 668 { 669 return ((defaultValues != null) && (! defaultValues.isEmpty())); 670 } 671 672 673 674 /** 675 * {@inheritDoc} 676 */ 677 @Override() 678 public String getDataTypeName() 679 { 680 return INFO_STRING_TYPE_NAME.get(); 681 } 682 683 684 685 /** 686 * {@inheritDoc} 687 */ 688 @Override() 689 public String getValueConstraints() 690 { 691 StringBuilder buffer = null; 692 693 if (valueRegex != null) 694 { 695 buffer = new StringBuilder(); 696 final String pattern = valueRegex.pattern(); 697 if ((valueRegexExplanation == null) || 698 (valueRegexExplanation.length() == 0)) 699 { 700 buffer.append(INFO_STRING_CONSTRAINTS_REGEX_WITHOUT_EXPLANATION.get( 701 pattern)); 702 } 703 else 704 { 705 buffer.append(INFO_STRING_CONSTRAINTS_REGEX_WITHOUT_EXPLANATION.get( 706 pattern, valueRegexExplanation)); 707 } 708 } 709 710 if ((allowedValues != null) && (! allowedValues.isEmpty())) 711 { 712 if (buffer == null) 713 { 714 buffer = new StringBuilder(); 715 } 716 else 717 { 718 buffer.append(" "); 719 } 720 721 buffer.append(INFO_STRING_CONSTRAINTS_ALLOWED_VALUE.get()); 722 buffer.append(" "); 723 724 final Iterator<String> iterator = allowedValues.iterator(); 725 while (iterator.hasNext()) 726 { 727 buffer.append('\''); 728 buffer.append(iterator.next()); 729 buffer.append('\''); 730 731 if (iterator.hasNext()) 732 { 733 buffer.append(", "); 734 } 735 } 736 buffer.append('.'); 737 } 738 739 if (buffer == null) 740 { 741 return null; 742 } 743 else 744 { 745 return buffer.toString(); 746 } 747 } 748 749 750 751 /** 752 * {@inheritDoc} 753 */ 754 @Override() 755 protected void reset() 756 { 757 super.reset(); 758 values.clear(); 759 } 760 761 762 763 /** 764 * {@inheritDoc} 765 */ 766 @Override() 767 public StringArgument getCleanCopy() 768 { 769 return new StringArgument(this); 770 } 771 772 773 774 /** 775 * {@inheritDoc} 776 */ 777 @Override() 778 protected void addToCommandLine(final List<String> argStrings) 779 { 780 if (values != null) 781 { 782 for (final String s : values) 783 { 784 argStrings.add(getIdentifierString()); 785 if (isSensitive()) 786 { 787 argStrings.add("***REDACTED***"); 788 } 789 else 790 { 791 argStrings.add(s); 792 } 793 } 794 } 795 } 796 797 798 799 /** 800 * {@inheritDoc} 801 */ 802 @Override() 803 public void toString(final StringBuilder buffer) 804 { 805 buffer.append("StringArgument("); 806 appendBasicToStringInfo(buffer); 807 808 if ((allowedValues != null) && (! allowedValues.isEmpty())) 809 { 810 buffer.append(", allowedValues={"); 811 final Iterator<String> iterator = allowedValues.iterator(); 812 while (iterator.hasNext()) 813 { 814 buffer.append('\''); 815 buffer.append(iterator.next()); 816 buffer.append('\''); 817 818 if (iterator.hasNext()) 819 { 820 buffer.append(", "); 821 } 822 } 823 buffer.append('}'); 824 } 825 826 if (valueRegex != null) 827 { 828 buffer.append(", valueRegex='"); 829 buffer.append(valueRegex.pattern()); 830 buffer.append('\''); 831 832 if (valueRegexExplanation != null) 833 { 834 buffer.append(", valueRegexExplanation='"); 835 buffer.append(valueRegexExplanation); 836 buffer.append('\''); 837 } 838 } 839 840 if ((defaultValues != null) && (! defaultValues.isEmpty())) 841 { 842 if (defaultValues.size() == 1) 843 { 844 buffer.append(", defaultValue='"); 845 buffer.append(defaultValues.get(0)); 846 } 847 else 848 { 849 buffer.append(", defaultValues={"); 850 851 final Iterator<String> iterator = defaultValues.iterator(); 852 while (iterator.hasNext()) 853 { 854 buffer.append('\''); 855 buffer.append(iterator.next()); 856 buffer.append('\''); 857 858 if (iterator.hasNext()) 859 { 860 buffer.append(", "); 861 } 862 } 863 864 buffer.append('}'); 865 } 866 } 867 868 buffer.append(')'); 869 } 870}