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