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