001/*
002 * Copyright 2008-2015 UnboundID Corp.
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2008-2015 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.  There will
076   * not be any default values, nor will there be any restriction on values that
077   * 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  isRequired        Indicates whether this argument is required to
086   *                           be provided.
087   * @param  maxOccurrences    The maximum number of times this argument may be
088   *                           provided on the command line.  A value less than
089   *                           or equal to zero indicates that it may be present
090   *                           any number of times.
091   * @param  valuePlaceholder  A placeholder to display in usage information to
092   *                           indicate that a value must be provided.  It must
093   *                           not be {@code null}.
094   * @param  description       A human-readable description for this argument.
095   *                           It must not be {@code null}.
096   *
097   * @throws  ArgumentException  If there is a problem with the definition of
098   *                             this argument.
099   */
100  public IntegerArgument(final Character shortIdentifier,
101                         final String longIdentifier, final boolean isRequired,
102                         final int maxOccurrences,
103                         final String valuePlaceholder,
104                         final String description)
105         throws ArgumentException
106  {
107    this(shortIdentifier, longIdentifier, isRequired,  maxOccurrences,
108         valuePlaceholder, description, Integer.MIN_VALUE, Integer.MAX_VALUE,
109         (List<Integer>) null);
110  }
111
112
113
114  /**
115   * Creates a new integer argument with the provided information.  There will
116   * not be any default values, but the range of values that will be allowed may
117   * be restricted.
118   *
119   * @param  shortIdentifier   The short identifier for this argument.  It may
120   *                           not be {@code null} if the long identifier is
121   *                           {@code null}.
122   * @param  longIdentifier    The long identifier for this argument.  It may
123   *                           not be {@code null} if the short identifier is
124   *                           {@code null}.
125   * @param  isRequired        Indicates whether this argument is required to
126   *                           be provided.
127   * @param  maxOccurrences    The maximum number of times this argument may be
128   *                           provided on the command line.  A value less than
129   *                           or equal to zero indicates that it may be present
130   *                           any number of times.
131   * @param  valuePlaceholder  A placeholder to display in usage information to
132   *                           indicate that a value must be provided.  It must
133   *                           not be {@code null}.
134   * @param  description       A human-readable description for this argument.
135   *                           It must not be {@code null}.
136   * @param  lowerBound        The smallest value that this argument is allowed
137   *                           to have.  It should be {@code Integer.MIN_VALUE}
138   *                           if there should be no lower bound.
139   * @param  upperBound        The largest value that this argument is allowed
140   *                           to have.  It should be {@code Integer.MAX_VALUE}
141   *                           if there should be no upper bound.
142   *
143   * @throws  ArgumentException  If there is a problem with the definition of
144   *                             this argument.
145   */
146  public IntegerArgument(final Character shortIdentifier,
147                         final String longIdentifier, final boolean isRequired,
148                         final int maxOccurrences,
149                         final String valuePlaceholder,
150                         final String description,
151                         final int lowerBound, final int upperBound)
152         throws ArgumentException
153  {
154    this(shortIdentifier, longIdentifier, isRequired,  maxOccurrences,
155         valuePlaceholder, description, lowerBound, upperBound,
156         (List<Integer>) null);
157  }
158
159
160
161  /**
162   * Creates a new integer argument with the provided information.  There will
163   * not be any restriction on values that may be assigned to this argument.
164   *
165   * @param  shortIdentifier   The short identifier for this argument.  It may
166   *                           not be {@code null} if the long identifier is
167   *                           {@code null}.
168   * @param  longIdentifier    The long identifier for this argument.  It may
169   *                           not be {@code null} if the short identifier is
170   *                           {@code null}.
171   * @param  isRequired        Indicates whether this argument is required to
172   *                           be provided.
173   * @param  maxOccurrences    The maximum number of times this argument may be
174   *                           provided on the command line.  A value less than
175   *                           or equal to zero indicates that it may be present
176   *                           any number of times.
177   * @param  valuePlaceholder  A placeholder to display in usage information to
178   *                           indicate that a value must be provided.  It must
179   *                           not be {@code null}.
180   * @param  description       A human-readable description for this argument.
181   *                           It must not be {@code null}.
182   * @param  defaultValue      The default value that will be used for this
183   *                           argument if no values are provided.  It may be
184   *                           {@code null} if there should not be a default
185   *                           value.
186   *
187   * @throws  ArgumentException  If there is a problem with the definition of
188   *                             this argument.
189   */
190  public IntegerArgument(final Character shortIdentifier,
191                         final String longIdentifier, final boolean isRequired,
192                         final int maxOccurrences,
193                         final String valuePlaceholder,
194                         final String description,
195                         final Integer defaultValue)
196         throws ArgumentException
197  {
198    this(shortIdentifier, longIdentifier, isRequired,  maxOccurrences,
199         valuePlaceholder, description, Integer.MIN_VALUE, Integer.MAX_VALUE,
200         ((defaultValue == null) ? null : Arrays.asList(defaultValue)));
201  }
202
203
204
205  /**
206   * Creates a new integer argument with the provided information.  There will
207   * not be any restriction on values that may be assigned to this argument.
208   *
209   * @param  shortIdentifier   The short identifier for this argument.  It may
210   *                           not be {@code null} if the long identifier is
211   *                           {@code null}.
212   * @param  longIdentifier    The long identifier for this argument.  It may
213   *                           not be {@code null} if the short identifier is
214   *                           {@code null}.
215   * @param  isRequired        Indicates whether this argument is required to
216   *                           be provided.
217   * @param  maxOccurrences    The maximum number of times this argument may be
218   *                           provided on the command line.  A value less than
219   *                           or equal to zero indicates that it may be present
220   *                           any number of times.
221   * @param  valuePlaceholder  A placeholder to display in usage information to
222   *                           indicate that a value must be provided.  It must
223   *                           not be {@code null}.
224   * @param  description       A human-readable description for this argument.
225   *                           It must not be {@code null}.
226   * @param  defaultValues     The set of default values that will be used for
227   *                           this argument if no values are provided.
228   *
229   * @throws  ArgumentException  If there is a problem with the definition of
230   *                             this argument.
231   */
232  public IntegerArgument(final Character shortIdentifier,
233                         final String longIdentifier, final boolean isRequired,
234                         final int maxOccurrences,
235                         final String valuePlaceholder,
236                         final String description,
237                         final List<Integer> defaultValues)
238         throws ArgumentException
239  {
240    this(shortIdentifier, longIdentifier, isRequired,  maxOccurrences,
241         valuePlaceholder, description, Integer.MIN_VALUE, Integer.MAX_VALUE,
242         defaultValues);
243  }
244
245
246
247  /**
248   * Creates a new integer argument with the provided information.
249   *
250   * @param  shortIdentifier   The short identifier for this argument.  It may
251   *                           not be {@code null} if the long identifier is
252   *                           {@code null}.
253   * @param  longIdentifier    The long identifier for this argument.  It may
254   *                           not be {@code null} if the short identifier is
255   *                           {@code null}.
256   * @param  isRequired        Indicates whether this argument is required to
257   *                           be provided.
258   * @param  maxOccurrences    The maximum number of times this argument may be
259   *                           provided on the command line.  A value less than
260   *                           or equal to zero indicates that it may be present
261   *                           any number of times.
262   * @param  valuePlaceholder  A placeholder to display in usage information to
263   *                           indicate that a value must be provided.  It must
264   *                           not be {@code null}.
265   * @param  description       A human-readable description for this argument.
266   *                           It must not be {@code null}.
267   * @param  lowerBound        The smallest value that this argument is allowed
268   *                           to have.  It should be {@code Integer.MIN_VALUE}
269   *                           if there should be no lower bound.
270   * @param  upperBound        The largest value that this argument is allowed
271   *                           to have.  It should be {@code Integer.MAX_VALUE}
272   *                           if there should be no upper bound.
273   * @param  defaultValue      The default value that will be used for this
274   *                           argument if no values are provided.  It may be
275   *                           {@code null} if there should not be a default
276   *                           value.
277   *
278   * @throws  ArgumentException  If there is a problem with the definition of
279   *                             this argument.
280   */
281  public IntegerArgument(final Character shortIdentifier,
282                         final String longIdentifier, final boolean isRequired,
283                         final int maxOccurrences,
284                         final String valuePlaceholder,
285                         final String description, final int lowerBound,
286                         final int upperBound,
287                         final Integer defaultValue)
288         throws ArgumentException
289  {
290    this(shortIdentifier, longIdentifier, isRequired,  maxOccurrences,
291         valuePlaceholder, description, lowerBound, upperBound,
292         ((defaultValue == null) ? null : Arrays.asList(defaultValue)));
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 must
314   *                           not be {@code null}.
315   * @param  description       A human-readable description for this argument.
316   *                           It must not be {@code null}.
317   * @param  lowerBound        The smallest value that this argument is allowed
318   *                           to have.  It should be {@code Integer.MIN_VALUE}
319   *                           if there should be no lower bound.
320   * @param  upperBound        The largest value that this argument is allowed
321   *                           to have.  It should be {@code Integer.MAX_VALUE}
322   *                           if there should be no upper bound.
323   * @param  defaultValues     The set of default values that will be used for
324   *                           this argument if no values are provided.
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 List<Integer> defaultValues)
336         throws ArgumentException
337  {
338    super(shortIdentifier, longIdentifier, isRequired,  maxOccurrences,
339          valuePlaceholder, description);
340
341    if (valuePlaceholder == null)
342    {
343      throw new ArgumentException(ERR_ARG_MUST_TAKE_VALUE.get(
344                                       getIdentifierString()));
345    }
346
347    this.lowerBound = lowerBound;
348    this.upperBound = upperBound;
349
350    if ((defaultValues == null) || defaultValues.isEmpty())
351    {
352      this.defaultValues = null;
353    }
354    else
355    {
356      this.defaultValues = Collections.unmodifiableList(defaultValues);
357    }
358
359    values = new ArrayList<Integer>(5);
360    validators = new ArrayList<ArgumentValueValidator>(5);
361  }
362
363
364
365  /**
366   * Creates a new integer argument that is a "clean" copy of the provided
367   * source argument.
368   *
369   * @param  source  The source argument to use for this argument.
370   */
371  private IntegerArgument(final IntegerArgument source)
372  {
373    super(source);
374
375    lowerBound    = source.lowerBound;
376    upperBound    = source.upperBound;
377    defaultValues = source.defaultValues;
378    validators    = new ArrayList<ArgumentValueValidator>(source.validators);
379    values        = new ArrayList<Integer>(5);
380  }
381
382
383
384  /**
385   * Retrieves the smallest value that this argument will be allowed to have.
386   *
387   * @return  The smallest value that this argument will be allowed to have.
388   */
389  public int getLowerBound()
390  {
391    return lowerBound;
392  }
393
394
395
396  /**
397   * Retrieves the largest value that this argument will be allowed to have.
398   *
399   * @return  The largest value that this argument will be allowed to have.
400   */
401  public int getUpperBound()
402  {
403    return upperBound;
404  }
405
406
407
408  /**
409   * Retrieves the list of default values for this argument, which will be used
410   * if no values were provided.
411   *
412   * @return   The list of default values for this argument, or {@code null} if
413   *           there are no default values.
414   */
415  public List<Integer> getDefaultValues()
416  {
417    return defaultValues;
418  }
419
420
421
422  /**
423   * Updates this argument to ensure that the provided validator will be invoked
424   * for any values provided to this argument.  This validator will be invoked
425   * after all other validation has been performed for this argument.
426   *
427   * @param  validator  The argument value validator to be invoked.  It must not
428   *                    be {@code null}.
429   */
430  public void addValueValidator(final ArgumentValueValidator validator)
431  {
432    validators.add(validator);
433  }
434
435
436
437  /**
438   * {@inheritDoc}
439   */
440  @Override()
441  protected void addValue(final String valueString)
442            throws ArgumentException
443  {
444    final int intValue;
445    try
446    {
447      intValue = Integer.parseInt(valueString);
448    }
449    catch (Exception e)
450    {
451      throw new ArgumentException(ERR_INTEGER_VALUE_NOT_INT.get(valueString,
452                                       getIdentifierString()), e);
453    }
454
455    if (intValue < lowerBound)
456    {
457      throw new ArgumentException(ERR_INTEGER_VALUE_BELOW_LOWER_BOUND.get(
458                                       intValue, getIdentifierString(),
459                                       lowerBound));
460    }
461
462    if (intValue > upperBound)
463    {
464      throw new ArgumentException(ERR_INTEGER_VALUE_ABOVE_UPPER_BOUND.get(
465                                       intValue, getIdentifierString(),
466                                       upperBound));
467    }
468
469    if (values.size() >= getMaxOccurrences())
470    {
471      throw new ArgumentException(ERR_ARG_MAX_OCCURRENCES_EXCEEDED.get(
472                                       getIdentifierString()));
473    }
474
475    for (final ArgumentValueValidator v : validators)
476    {
477      v.validateArgumentValue(this, valueString);
478    }
479
480    values.add(intValue);
481  }
482
483
484
485  /**
486   * Retrieves the value for this argument, or the default value if none was
487   * provided.  If this argument has multiple values, then the first will be
488   * returned.
489   *
490   * @return  The value for this argument, or the default value if none was
491   *          provided, or {@code null} if it does not have any values or
492   *          default values.
493   */
494  public Integer getValue()
495  {
496    if (values.isEmpty())
497    {
498      if ((defaultValues == null) || defaultValues.isEmpty())
499      {
500        return null;
501      }
502      else
503      {
504        return defaultValues.get(0);
505      }
506    }
507
508    return values.get(0);
509  }
510
511
512
513  /**
514   * Retrieves the set of values for this argument, or the default values if
515   * none were provided.
516   *
517   * @return  The set of values for this argument, or the default values if none
518   *          were provided.
519   */
520  public List<Integer> getValues()
521  {
522    if (values.isEmpty() && (defaultValues != null))
523    {
524      return defaultValues;
525    }
526
527    return Collections.unmodifiableList(values);
528  }
529
530
531
532  /**
533   * {@inheritDoc}
534   */
535  @Override()
536  protected boolean hasDefaultValue()
537  {
538    return ((defaultValues != null) && (! defaultValues.isEmpty()));
539  }
540
541
542
543  /**
544   * {@inheritDoc}
545   */
546  @Override()
547  public String getDataTypeName()
548  {
549    return INFO_INTEGER_TYPE_NAME.get();
550  }
551
552
553
554  /**
555   * {@inheritDoc}
556   */
557  @Override()
558  public String getValueConstraints()
559  {
560    return INFO_INTEGER_CONSTRAINTS_LOWER_AND_UPPER_BOUND.get(lowerBound,
561         upperBound);
562  }
563
564
565
566  /**
567   * {@inheritDoc}
568   */
569  @Override()
570  public IntegerArgument getCleanCopy()
571  {
572    return new IntegerArgument(this);
573  }
574
575
576
577  /**
578   * {@inheritDoc}
579   */
580  @Override()
581  public void toString(final StringBuilder buffer)
582  {
583    buffer.append("IntegerArgument(");
584    appendBasicToStringInfo(buffer);
585
586    buffer.append(", lowerBound=");
587    buffer.append(lowerBound);
588    buffer.append(", upperBound=");
589    buffer.append(upperBound);
590
591    if ((defaultValues != null) && (! defaultValues.isEmpty()))
592    {
593      if (defaultValues.size() == 1)
594      {
595        buffer.append(", defaultValue='");
596        buffer.append(defaultValues.get(0).toString());
597      }
598      else
599      {
600        buffer.append(", defaultValues={");
601
602        final Iterator<Integer> iterator = defaultValues.iterator();
603        while (iterator.hasNext())
604        {
605          buffer.append('\'');
606          buffer.append(iterator.next().toString());
607          buffer.append('\'');
608
609          if (iterator.hasNext())
610          {
611            buffer.append(", ");
612          }
613        }
614
615        buffer.append('}');
616      }
617    }
618
619    buffer.append(')');
620  }
621}