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