001    /*
002     * Copyright 2008-2013 UnboundID Corp.
003     * All Rights Reserved.
004     */
005    /*
006     * Copyright (C) 2008-2013 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     */
021    package com.unboundid.util.args;
022    
023    
024    
025    import java.util.ArrayList;
026    import java.util.Arrays;
027    import java.util.Collections;
028    import java.util.HashSet;
029    import java.util.Iterator;
030    import java.util.List;
031    import java.util.Set;
032    import java.util.regex.Matcher;
033    import java.util.regex.Pattern;
034    
035    import com.unboundid.util.Mutable;
036    import com.unboundid.util.ThreadSafety;
037    import com.unboundid.util.ThreadSafetyLevel;
038    
039    import static com.unboundid.util.StaticUtils.*;
040    import 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)
053    public 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    }