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.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 = new HashSet<>(allowedValues.size()); 374 for (final String s : allowedValues) 375 { 376 lowerValues.add(StaticUtils.toLowerCase(s)); 377 } 378 this.allowedValues = Collections.unmodifiableSet(lowerValues); 379 } 380 381 if ((defaultValues == null) || defaultValues.isEmpty()) 382 { 383 this.defaultValues = null; 384 } 385 else 386 { 387 this.defaultValues = Collections.unmodifiableList(defaultValues); 388 } 389 390 if ((this.allowedValues != null) && (this.defaultValues != null)) 391 { 392 for (final String s : this.defaultValues) 393 { 394 final String lowerDefault = StaticUtils.toLowerCase(s); 395 if (! this.allowedValues.contains(lowerDefault)) 396 { 397 throw new ArgumentException( 398 ERR_ARG_DEFAULT_VALUE_NOT_ALLOWED.get(s, getIdentifierString())); 399 } 400 } 401 } 402 403 values = new ArrayList<>(5); 404 validators = new ArrayList<>(5); 405 valueRegex = null; 406 valueRegexExplanation = null; 407 } 408 409 410 411 /** 412 * Creates a new string argument that is a "clean" copy of the provided source 413 * argument. 414 * 415 * @param source The source argument to use for this argument. 416 */ 417 private StringArgument(final StringArgument source) 418 { 419 super(source); 420 421 allowedValues = source.allowedValues; 422 defaultValues = source.defaultValues; 423 valueRegex = source.valueRegex; 424 valueRegexExplanation = source.valueRegexExplanation; 425 values = new ArrayList<>(5); 426 validators = new ArrayList<>(source.validators); 427 } 428 429 430 431 /** 432 * Retrieves the set of allowed values for this argument, if applicable. 433 * 434 * @return The set of allowed values for this argument, or {@code null} if 435 * there is no restriction on the allowed values. 436 */ 437 public Set<String> getAllowedValues() 438 { 439 return allowedValues; 440 } 441 442 443 444 /** 445 * Retrieves the list of default values for this argument, which will be used 446 * if no values were provided. 447 * 448 * @return The list of default values for this argument, or {@code null} if 449 * there are no default values. 450 */ 451 public List<String> getDefaultValues() 452 { 453 return defaultValues; 454 } 455 456 457 458 /** 459 * Retrieves the regular expression that values of this argument will be 460 * required to match, if any. 461 * 462 * @return The regular expression that values of this argument will be 463 * required to match, or {@code null} if none is defined. 464 */ 465 public Pattern getValueRegex() 466 { 467 return valueRegex; 468 } 469 470 471 472 /** 473 * Retrieves a human-readable explanation of the regular expression pattern 474 * that may be required to match any provided values, if any. 475 * 476 * @return A human-readable explanation of the regular expression pattern, or 477 * {@code null} if none is available. 478 */ 479 public String getValueRegexExplanation() 480 { 481 return valueRegexExplanation; 482 } 483 484 485 486 /** 487 * Specifies the regular expression that values of this argument will be 488 * required to match, if any. 489 * 490 * @param valueRegex The regular expression that values of this argument 491 * will be required to match. It may be {@code null} if 492 * no pattern matching should be required. 493 * @param explanation A human-readable explanation for the pattern which may 494 * be used to clarify the kinds of values that are 495 * acceptable. It may be {@code null} if no pattern 496 * matching should be required, or if the regular 497 * expression pattern should be sufficiently clear for 498 * the target audience. 499 */ 500 public void setValueRegex(final Pattern valueRegex, 501 final String explanation) 502 { 503 this.valueRegex = valueRegex; 504 valueRegexExplanation = explanation; 505 } 506 507 508 509 /** 510 * Updates this argument to ensure that the provided validator will be invoked 511 * for any values provided to this argument. This validator will be invoked 512 * after all other validation has been performed for this argument. 513 * 514 * @param validator The argument value validator to be invoked. It must not 515 * be {@code null}. 516 */ 517 public void addValueValidator(final ArgumentValueValidator validator) 518 { 519 validators.add(validator); 520 } 521 522 523 524 /** 525 * {@inheritDoc} 526 */ 527 @Override() 528 protected void addValue(final String valueString) 529 throws ArgumentException 530 { 531 final String lowerValue = StaticUtils.toLowerCase(valueString); 532 if (allowedValues != null) 533 { 534 if (! allowedValues.contains(lowerValue)) 535 { 536 throw new ArgumentException(ERR_ARG_VALUE_NOT_ALLOWED.get( 537 valueString, getIdentifierString())); 538 } 539 } 540 541 if (values.size() >= getMaxOccurrences()) 542 { 543 throw new ArgumentException(ERR_ARG_MAX_OCCURRENCES_EXCEEDED.get( 544 getIdentifierString())); 545 } 546 547 if (valueRegex != null) 548 { 549 final Matcher matcher = valueRegex.matcher(valueString); 550 if (! matcher.matches()) 551 { 552 final String pattern = valueRegex.pattern(); 553 if (valueRegexExplanation == null) 554 { 555 throw new ArgumentException( 556 ERR_ARG_VALUE_DOES_NOT_MATCH_PATTERN_WITHOUT_EXPLANATION.get( 557 valueString, getIdentifierString(), pattern)); 558 } 559 else 560 { 561 throw new ArgumentException( 562 ERR_ARG_VALUE_DOES_NOT_MATCH_PATTERN_WITH_EXPLANATION.get( 563 valueString, getIdentifierString(), pattern, 564 valueRegexExplanation)); 565 } 566 } 567 } 568 569 for (final ArgumentValueValidator v : validators) 570 { 571 v.validateArgumentValue(this, valueString); 572 } 573 574 values.add(valueString); 575 } 576 577 578 579 /** 580 * Retrieves the value for this argument, or the default value if none was 581 * provided. If this argument has multiple values, then the first will be 582 * returned. 583 * 584 * @return The value for this argument, or the default value if none was 585 * provided, or {@code null} if it does not have any values or 586 * default values. 587 */ 588 public String getValue() 589 { 590 if (values.isEmpty()) 591 { 592 if ((defaultValues == null) || defaultValues.isEmpty()) 593 { 594 return null; 595 } 596 else 597 { 598 return defaultValues.get(0); 599 } 600 } 601 602 return values.get(0); 603 } 604 605 606 607 /** 608 * Retrieves the set of values for this argument, or the default values if 609 * none were provided. 610 * 611 * @return The set of values for this argument, or the default values if none 612 * were provided. 613 */ 614 public List<String> getValues() 615 { 616 if (values.isEmpty() && (defaultValues != null)) 617 { 618 return defaultValues; 619 } 620 621 return Collections.unmodifiableList(values); 622 } 623 624 625 626 /** 627 * {@inheritDoc} 628 */ 629 @Override() 630 public List<String> getValueStringRepresentations(final boolean useDefault) 631 { 632 if (! values.isEmpty()) 633 { 634 return Collections.unmodifiableList(values); 635 } 636 else if (useDefault && (defaultValues != null)) 637 { 638 return Collections.unmodifiableList(defaultValues); 639 } 640 else 641 { 642 return Collections.emptyList(); 643 } 644 } 645 646 647 648 /** 649 * {@inheritDoc} 650 */ 651 @Override() 652 protected boolean hasDefaultValue() 653 { 654 return ((defaultValues != null) && (! defaultValues.isEmpty())); 655 } 656 657 658 659 /** 660 * {@inheritDoc} 661 */ 662 @Override() 663 public String getDataTypeName() 664 { 665 return INFO_STRING_TYPE_NAME.get(); 666 } 667 668 669 670 /** 671 * {@inheritDoc} 672 */ 673 @Override() 674 public String getValueConstraints() 675 { 676 StringBuilder buffer = null; 677 678 if (valueRegex != null) 679 { 680 buffer = new StringBuilder(); 681 final String pattern = valueRegex.pattern(); 682 if ((valueRegexExplanation == null) || 683 (valueRegexExplanation.length() == 0)) 684 { 685 buffer.append(INFO_STRING_CONSTRAINTS_REGEX_WITHOUT_EXPLANATION.get( 686 pattern)); 687 } 688 else 689 { 690 buffer.append(INFO_STRING_CONSTRAINTS_REGEX_WITHOUT_EXPLANATION.get( 691 pattern, valueRegexExplanation)); 692 } 693 } 694 695 if ((allowedValues != null) && (! allowedValues.isEmpty())) 696 { 697 if (buffer == null) 698 { 699 buffer = new StringBuilder(); 700 } 701 else 702 { 703 buffer.append(" "); 704 } 705 706 buffer.append(INFO_STRING_CONSTRAINTS_ALLOWED_VALUE.get()); 707 buffer.append(" "); 708 709 final Iterator<String> iterator = allowedValues.iterator(); 710 while (iterator.hasNext()) 711 { 712 buffer.append('\''); 713 buffer.append(iterator.next()); 714 buffer.append('\''); 715 716 if (iterator.hasNext()) 717 { 718 buffer.append(", "); 719 } 720 } 721 buffer.append('.'); 722 } 723 724 if (buffer == null) 725 { 726 return null; 727 } 728 else 729 { 730 return buffer.toString(); 731 } 732 } 733 734 735 736 /** 737 * {@inheritDoc} 738 */ 739 @Override() 740 protected void reset() 741 { 742 super.reset(); 743 values.clear(); 744 } 745 746 747 748 /** 749 * {@inheritDoc} 750 */ 751 @Override() 752 public StringArgument getCleanCopy() 753 { 754 return new StringArgument(this); 755 } 756 757 758 759 /** 760 * {@inheritDoc} 761 */ 762 @Override() 763 protected void addToCommandLine(final List<String> argStrings) 764 { 765 if (values != null) 766 { 767 for (final String s : values) 768 { 769 argStrings.add(getIdentifierString()); 770 if (isSensitive()) 771 { 772 argStrings.add("***REDACTED***"); 773 } 774 else 775 { 776 argStrings.add(s); 777 } 778 } 779 } 780 } 781 782 783 784 /** 785 * {@inheritDoc} 786 */ 787 @Override() 788 public void toString(final StringBuilder buffer) 789 { 790 buffer.append("StringArgument("); 791 appendBasicToStringInfo(buffer); 792 793 if ((allowedValues != null) && (! allowedValues.isEmpty())) 794 { 795 buffer.append(", allowedValues={"); 796 final Iterator<String> iterator = allowedValues.iterator(); 797 while (iterator.hasNext()) 798 { 799 buffer.append('\''); 800 buffer.append(iterator.next()); 801 buffer.append('\''); 802 803 if (iterator.hasNext()) 804 { 805 buffer.append(", "); 806 } 807 } 808 buffer.append('}'); 809 } 810 811 if (valueRegex != null) 812 { 813 buffer.append(", valueRegex='"); 814 buffer.append(valueRegex.pattern()); 815 buffer.append('\''); 816 817 if (valueRegexExplanation != null) 818 { 819 buffer.append(", valueRegexExplanation='"); 820 buffer.append(valueRegexExplanation); 821 buffer.append('\''); 822 } 823 } 824 825 if ((defaultValues != null) && (! defaultValues.isEmpty())) 826 { 827 if (defaultValues.size() == 1) 828 { 829 buffer.append(", defaultValue='"); 830 buffer.append(defaultValues.get(0)); 831 } 832 else 833 { 834 buffer.append(", defaultValues={"); 835 836 final Iterator<String> iterator = defaultValues.iterator(); 837 while (iterator.hasNext()) 838 { 839 buffer.append('\''); 840 buffer.append(iterator.next()); 841 buffer.append('\''); 842 843 if (iterator.hasNext()) 844 { 845 buffer.append(", "); 846 } 847 } 848 849 buffer.append('}'); 850 } 851 } 852 853 buffer.append(')'); 854 } 855}