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 throw new ArgumentException(ERR_ARG_VALUE_NOT_ALLOWED.get( 538 valueString, getIdentifierString())); 539 } 540 } 541 542 if (values.size() >= getMaxOccurrences()) 543 { 544 throw new ArgumentException(ERR_ARG_MAX_OCCURRENCES_EXCEEDED.get( 545 getIdentifierString())); 546 } 547 548 if (valueRegex != null) 549 { 550 final Matcher matcher = valueRegex.matcher(valueString); 551 if (! matcher.matches()) 552 { 553 final String pattern = valueRegex.pattern(); 554 if (valueRegexExplanation == null) 555 { 556 throw new ArgumentException( 557 ERR_ARG_VALUE_DOES_NOT_MATCH_PATTERN_WITHOUT_EXPLANATION.get( 558 valueString, getIdentifierString(), pattern)); 559 } 560 else 561 { 562 throw new ArgumentException( 563 ERR_ARG_VALUE_DOES_NOT_MATCH_PATTERN_WITH_EXPLANATION.get( 564 valueString, getIdentifierString(), pattern, 565 valueRegexExplanation)); 566 } 567 } 568 } 569 570 for (final ArgumentValueValidator v : validators) 571 { 572 v.validateArgumentValue(this, valueString); 573 } 574 575 values.add(valueString); 576 } 577 578 579 580 /** 581 * Retrieves the value for this argument, or the default value if none was 582 * provided. If this argument has multiple values, then the first will be 583 * returned. 584 * 585 * @return The value for this argument, or the default value if none was 586 * provided, or {@code null} if it does not have any values or 587 * default values. 588 */ 589 public String getValue() 590 { 591 if (values.isEmpty()) 592 { 593 if ((defaultValues == null) || defaultValues.isEmpty()) 594 { 595 return null; 596 } 597 else 598 { 599 return defaultValues.get(0); 600 } 601 } 602 603 return values.get(0); 604 } 605 606 607 608 /** 609 * Retrieves the set of values for this argument, or the default values if 610 * none were provided. 611 * 612 * @return The set of values for this argument, or the default values if none 613 * were provided. 614 */ 615 public List<String> getValues() 616 { 617 if (values.isEmpty() && (defaultValues != null)) 618 { 619 return defaultValues; 620 } 621 622 return Collections.unmodifiableList(values); 623 } 624 625 626 627 /** 628 * {@inheritDoc} 629 */ 630 @Override() 631 public List<String> getValueStringRepresentations(final boolean useDefault) 632 { 633 if (! values.isEmpty()) 634 { 635 return Collections.unmodifiableList(values); 636 } 637 else if (useDefault && (defaultValues != null)) 638 { 639 return Collections.unmodifiableList(defaultValues); 640 } 641 else 642 { 643 return Collections.emptyList(); 644 } 645 } 646 647 648 649 /** 650 * {@inheritDoc} 651 */ 652 @Override() 653 protected boolean hasDefaultValue() 654 { 655 return ((defaultValues != null) && (! defaultValues.isEmpty())); 656 } 657 658 659 660 /** 661 * {@inheritDoc} 662 */ 663 @Override() 664 public String getDataTypeName() 665 { 666 return INFO_STRING_TYPE_NAME.get(); 667 } 668 669 670 671 /** 672 * {@inheritDoc} 673 */ 674 @Override() 675 public String getValueConstraints() 676 { 677 StringBuilder buffer = null; 678 679 if (valueRegex != null) 680 { 681 buffer = new StringBuilder(); 682 final String pattern = valueRegex.pattern(); 683 if ((valueRegexExplanation == null) || 684 (valueRegexExplanation.length() == 0)) 685 { 686 buffer.append(INFO_STRING_CONSTRAINTS_REGEX_WITHOUT_EXPLANATION.get( 687 pattern)); 688 } 689 else 690 { 691 buffer.append(INFO_STRING_CONSTRAINTS_REGEX_WITHOUT_EXPLANATION.get( 692 pattern, valueRegexExplanation)); 693 } 694 } 695 696 if ((allowedValues != null) && (! allowedValues.isEmpty())) 697 { 698 if (buffer == null) 699 { 700 buffer = new StringBuilder(); 701 } 702 else 703 { 704 buffer.append(" "); 705 } 706 707 buffer.append(INFO_STRING_CONSTRAINTS_ALLOWED_VALUE.get()); 708 buffer.append(" "); 709 710 final Iterator<String> iterator = allowedValues.iterator(); 711 while (iterator.hasNext()) 712 { 713 buffer.append('\''); 714 buffer.append(iterator.next()); 715 buffer.append('\''); 716 717 if (iterator.hasNext()) 718 { 719 buffer.append(", "); 720 } 721 } 722 buffer.append('.'); 723 } 724 725 if (buffer == null) 726 { 727 return null; 728 } 729 else 730 { 731 return buffer.toString(); 732 } 733 } 734 735 736 737 /** 738 * {@inheritDoc} 739 */ 740 @Override() 741 protected void reset() 742 { 743 super.reset(); 744 values.clear(); 745 } 746 747 748 749 /** 750 * {@inheritDoc} 751 */ 752 @Override() 753 public StringArgument getCleanCopy() 754 { 755 return new StringArgument(this); 756 } 757 758 759 760 /** 761 * {@inheritDoc} 762 */ 763 @Override() 764 protected void addToCommandLine(final List<String> argStrings) 765 { 766 if (values != null) 767 { 768 for (final String s : values) 769 { 770 argStrings.add(getIdentifierString()); 771 if (isSensitive()) 772 { 773 argStrings.add("***REDACTED***"); 774 } 775 else 776 { 777 argStrings.add(s); 778 } 779 } 780 } 781 } 782 783 784 785 /** 786 * {@inheritDoc} 787 */ 788 @Override() 789 public void toString(final StringBuilder buffer) 790 { 791 buffer.append("StringArgument("); 792 appendBasicToStringInfo(buffer); 793 794 if ((allowedValues != null) && (! allowedValues.isEmpty())) 795 { 796 buffer.append(", allowedValues={"); 797 final Iterator<String> iterator = allowedValues.iterator(); 798 while (iterator.hasNext()) 799 { 800 buffer.append('\''); 801 buffer.append(iterator.next()); 802 buffer.append('\''); 803 804 if (iterator.hasNext()) 805 { 806 buffer.append(", "); 807 } 808 } 809 buffer.append('}'); 810 } 811 812 if (valueRegex != null) 813 { 814 buffer.append(", valueRegex='"); 815 buffer.append(valueRegex.pattern()); 816 buffer.append('\''); 817 818 if (valueRegexExplanation != null) 819 { 820 buffer.append(", valueRegexExplanation='"); 821 buffer.append(valueRegexExplanation); 822 buffer.append('\''); 823 } 824 } 825 826 if ((defaultValues != null) && (! defaultValues.isEmpty())) 827 { 828 if (defaultValues.size() == 1) 829 { 830 buffer.append(", defaultValue='"); 831 buffer.append(defaultValues.get(0)); 832 } 833 else 834 { 835 buffer.append(", defaultValues={"); 836 837 final Iterator<String> iterator = defaultValues.iterator(); 838 while (iterator.hasNext()) 839 { 840 buffer.append('\''); 841 buffer.append(iterator.next()); 842 buffer.append('\''); 843 844 if (iterator.hasNext()) 845 { 846 buffer.append(", "); 847 } 848 } 849 850 buffer.append('}'); 851 } 852 } 853 854 buffer.append(')'); 855 } 856}