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