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