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