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