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