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     */
021    package com.unboundid.util.args;
022    
023    
024    
025    import java.util.ArrayList;
026    import java.util.Arrays;
027    import java.util.Collections;
028    import java.util.Iterator;
029    import java.util.List;
030    
031    import com.unboundid.util.Mutable;
032    import com.unboundid.util.ThreadSafety;
033    import com.unboundid.util.ThreadSafetyLevel;
034    
035    import 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)
047    public 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    }