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}