001/*
002 * Copyright 2008-2017 UnboundID Corp.
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2008-2017 UnboundID Corp.
007 *
008 * This program is free software; you can redistribute it and/or modify
009 * it under the terms of the GNU General Public License (GPLv2 only)
010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
011 * as published by the Free Software Foundation.
012 *
013 * This program is distributed in the hope that it will be useful,
014 * but WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
016 * GNU General Public License for more details.
017 *
018 * You should have received a copy of the GNU General Public License
019 * along with this program; if not, see <http://www.gnu.org/licenses>.
020 */
021package com.unboundid.util.args;
022
023
024
025import java.util.ArrayList;
026import java.util.Arrays;
027import java.util.Collections;
028import java.util.Iterator;
029import java.util.List;
030
031import com.unboundid.util.Mutable;
032import com.unboundid.util.ThreadSafety;
033import com.unboundid.util.ThreadSafetyLevel;
034
035import 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)
047public 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}