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