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