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