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