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.io.Serializable;
041
042import java.util.ArrayList;
043import java.util.Collections;
044import java.util.Iterator;
045import java.util.LinkedHashMap;
046import java.util.List;
047import java.util.Map;
048
049import com.unboundid.util.LDAPSDKUsageException;
050import com.unboundid.util.Mutable;
051import com.unboundid.util.NotExtensible;
052import com.unboundid.util.StaticUtils;
053import com.unboundid.util.ThreadSafety;
054import com.unboundid.util.ThreadSafetyLevel;
055
056import static com.unboundid.util.args.ArgsMessages.*;
057
058
059
060/**
061 * This class defines a generic command line argument, which provides
062 * functionality applicable to all argument types.  Subclasses may enforce
063 * additional constraints or provide additional functionality.
064 */
065@NotExtensible()
066@Mutable()
067@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE)
068public abstract class Argument
069       implements Serializable
070{
071  /**
072   * The serial version UID for this serializable class.
073   */
074  private static final long serialVersionUID = -6938320885602903919L;
075
076
077
078  // Indicates whether this argument should be excluded from usage information.
079  private boolean isHidden;
080
081  // Indicates whether this argument has been registered with the argument
082  // parser.
083  private boolean isRegistered;
084
085  // Indicates whether this argument is required to be present.
086  private final boolean isRequired;
087
088  // Indicates whether values of this argument should be considered sensitive.
089  private boolean isSensitive;
090
091  // Indicates whether this argument is used to display usage information.
092  private boolean isUsageArgument;
093
094  // The maximum number of times this argument is allowed to be provided.
095  private int maxOccurrences;
096
097  // The number of times this argument was included in the provided command line
098  // arguments.
099  private int numOccurrences;
100
101  // The set of short identifiers for this argument, associated with an
102  // indication as to whether the identifier should be hidden.
103  private final Map<Character,Boolean> shortIdentifiers;
104
105  // The set of long identifiers for this argument, associated with an
106  // indication as to whether the identifier should be hidden.
107  private final Map<String,Boolean> longIdentifiers;
108
109  // The argument group name for this argument, if any.
110  private String argumentGroupName;
111
112  // The description for this argument.
113  private final String description;
114
115  // The value placeholder for this argument, or {@code null} if it does not
116  // take a value.
117  private final String valuePlaceholder;
118
119
120
121  /**
122   * Creates a new argument with the provided information.
123   *
124   * @param  shortIdentifier   The short identifier for this argument.  It may
125   *                           not be {@code null} if the long identifier is
126   *                           {@code null}.
127   * @param  longIdentifier    The long identifier for this argument.  It may
128   *                           not be {@code null} if the short identifier is
129   *                           {@code null}.
130   * @param  isRequired        Indicates whether this argument is required to
131   *                           be provided.
132   * @param  maxOccurrences    The maximum number of times this argument may be
133   *                           provided on the command line.  A value less than
134   *                           or equal to zero indicates that it may be present
135   *                           any number of times.
136   * @param  valuePlaceholder  A placeholder to display in usage information to
137   *                           indicate that a value must be provided.  If this
138   *                           is {@code null}, then the argument will not be
139   *                           allowed to take a value.  If it is not
140   *                           {@code null}, then the argument will be required
141   *                           to take a value.
142   * @param  description       A human-readable description for this argument.
143   *                           It must not be {@code null}.
144   *
145   * @throws  ArgumentException  If there is a problem with the definition of
146   *                             this argument.
147   */
148  protected Argument(final Character shortIdentifier,
149                     final String longIdentifier,
150                     final boolean isRequired, final int maxOccurrences,
151                     final String valuePlaceholder, final String description)
152            throws ArgumentException
153  {
154    if (description == null)
155    {
156      throw new ArgumentException(ERR_ARG_DESCRIPTION_NULL.get());
157    }
158
159    if ((shortIdentifier == null) && (longIdentifier == null))
160    {
161      throw new ArgumentException(ERR_ARG_NO_IDENTIFIERS.get());
162    }
163
164    shortIdentifiers = new LinkedHashMap<>(StaticUtils.computeMapCapacity(5));
165    if (shortIdentifier != null)
166    {
167      shortIdentifiers.put(shortIdentifier, false);
168    }
169
170    longIdentifiers = new LinkedHashMap<>(StaticUtils.computeMapCapacity(5));
171    if (longIdentifier != null)
172    {
173      longIdentifiers.put(longIdentifier, false);
174    }
175
176    this.isRequired       = isRequired;
177    this.valuePlaceholder = valuePlaceholder;
178    this.description      = description;
179
180    if (maxOccurrences > 0)
181    {
182      this.maxOccurrences = maxOccurrences;
183    }
184    else
185    {
186      this.maxOccurrences = Integer.MAX_VALUE;
187    }
188
189    argumentGroupName = null;
190    numOccurrences    = 0;
191    isHidden          = false;
192    isRegistered      = false;
193    isSensitive       = false;
194    isUsageArgument   = false;
195  }
196
197
198
199  /**
200   * Creates a new argument with the same generic information as the provided
201   * argument.  It will not be registered with any argument parser.
202   *
203   * @param  source  The argument to use as the source for this argument.
204   */
205  protected Argument(final Argument source)
206  {
207    argumentGroupName = source.argumentGroupName;
208    isHidden          = source.isHidden;
209    isRequired        = source.isRequired;
210    isSensitive       = source.isSensitive;
211    isUsageArgument   = source.isUsageArgument;
212    maxOccurrences    = source.maxOccurrences;
213    description       = source.description;
214    valuePlaceholder  = source.valuePlaceholder;
215
216    isRegistered   = false;
217    numOccurrences = 0;
218
219    shortIdentifiers = new LinkedHashMap<>(source.shortIdentifiers);
220    longIdentifiers  = new LinkedHashMap<>(source.longIdentifiers);
221  }
222
223
224
225  /**
226   * Indicates whether this argument has a short identifier.
227   *
228   * @return  {@code true} if it has a short identifier, or {@code false} if
229   *          not.
230   */
231  public final boolean hasShortIdentifier()
232  {
233    return (! shortIdentifiers.isEmpty());
234  }
235
236
237
238  /**
239   * Retrieves the short identifier for this argument.  If there is more than
240   * one, then the first will be returned.
241   *
242   * @return  The short identifier for this argument, or {@code null} if none is
243   *          defined.
244   */
245  public final Character getShortIdentifier()
246  {
247    for (final Map.Entry<Character,Boolean> e : shortIdentifiers.entrySet())
248    {
249      if (e.getValue())
250      {
251        continue;
252      }
253
254      return e.getKey();
255    }
256
257    return null;
258  }
259
260
261
262  /**
263   * Retrieves the list of all short identifiers, including hidden identifiers,
264   * for this argument.
265   *
266   * @return  The list of all short identifiers for this argument, or an empty
267   *          list if there are no short identifiers.
268   */
269  public final List<Character> getShortIdentifiers()
270  {
271    return getShortIdentifiers(true);
272  }
273
274
275
276  /**
277   * Retrieves the list of short identifiers for this argument.
278   *
279   * @param  includeHidden  Indicates whether to include hidden identifiers in
280   *                        the list that is returned.
281   *
282   * @return  The list of short identifiers for this argument, or an empty list
283   *          if there are none.
284   */
285  public final List<Character> getShortIdentifiers(final boolean includeHidden)
286  {
287    final ArrayList<Character> identifierList =
288         new ArrayList<>(shortIdentifiers.size());
289    for (final Map.Entry<Character,Boolean> e : shortIdentifiers.entrySet())
290    {
291      if (includeHidden || (! e.getValue()))
292      {
293        identifierList.add(e.getKey());
294      }
295    }
296
297    return Collections.unmodifiableList(identifierList);
298  }
299
300
301
302  /**
303   * Adds the provided character to the set of short identifiers for this
304   * argument.  It will not be hidden.  Note that this must be called before
305   * this argument is registered with the argument parser.
306   *
307   * @param  c  The character to add to the set of short identifiers for this
308   *            argument.  It must not be {@code null}.
309   *
310   * @throws  ArgumentException  If this argument is already registered with the
311   *                             argument parser.
312   */
313  public final void addShortIdentifier(final Character c)
314         throws ArgumentException
315  {
316    addShortIdentifier(c, false);
317  }
318
319
320
321  /**
322   * Adds the provided character to the set of short identifiers for this
323   * argument.  Note that this must be called before this argument is registered
324   * with the argument parser.
325   *
326   * @param  c         The character to add to the set of short identifiers for
327   *                   this argument.  It must not be {@code null}.
328   * @param  isHidden  Indicates whether the provided identifier should be
329   *                   hidden.  If this is {@code true}, then the identifier can
330   *                   be used to target this argument on the command line, but
331   *                   it will not be included in usage information.
332   *
333   * @throws  ArgumentException  If this argument is already registered with the
334   *                             argument parser.
335   */
336  public final void addShortIdentifier(final Character c,
337                                       final boolean isHidden)
338         throws ArgumentException
339  {
340    if (isRegistered)
341    {
342      throw new ArgumentException(ERR_ARG_ID_CHANGE_AFTER_REGISTERED.get(
343                                       getIdentifierString()));
344    }
345
346    shortIdentifiers.put(c, isHidden);
347  }
348
349
350
351  /**
352   * Indicates whether this argument has a long identifier.
353   *
354   * @return  {@code true} if it has a long identifier, or {@code false} if
355   *          not.
356   */
357  public final boolean hasLongIdentifier()
358  {
359    return (! longIdentifiers.isEmpty());
360  }
361
362
363
364  /**
365   * Retrieves the long identifier for this argument.  If it has multiple long
366   * identifiers, then the first will be returned.
367   *
368   * @return  The long identifier for this argument, or {@code null} if none is
369   *          defined.
370   */
371  public final String getLongIdentifier()
372  {
373    for (final Map.Entry<String,Boolean> e : longIdentifiers.entrySet())
374    {
375      if (e.getValue())
376      {
377        continue;
378      }
379
380      return e.getKey();
381    }
382
383    return null;
384  }
385
386
387
388  /**
389   * Retrieves the list of all long identifiers, including hidden identifiers,
390   * for this argument.
391   *
392   * @return  The list of all long identifiers for this argument, or an empty
393   *          list if there are no long identifiers.
394   */
395  public final List<String> getLongIdentifiers()
396  {
397    return getLongIdentifiers(true);
398  }
399
400
401
402  /**
403   * Retrieves the list of long identifiers for this argument.
404   *
405   * @param  includeHidden  Indicates whether to include hidden identifiers in
406   *                        the list that is returned.
407   *
408   * @return  The long identifier for this argument, or an empty list if there
409   *          are none.
410   */
411  public final List<String> getLongIdentifiers(final boolean includeHidden)
412  {
413    final ArrayList<String> identifierList =
414         new ArrayList<>(longIdentifiers.size());
415    for (final Map.Entry<String,Boolean> e : longIdentifiers.entrySet())
416    {
417      if (includeHidden || (! e.getValue()))
418      {
419        identifierList.add(e.getKey());
420      }
421    }
422
423    return Collections.unmodifiableList(identifierList);
424  }
425
426
427
428  /**
429   * Adds the provided string to the set of short identifiers for this argument.
430   * It will not be hidden.  Note that this must be called before this argument
431   * is registered with the argument parser.
432   *
433   * @param  s  The string to add to the set of short identifiers for this
434   *            argument.  It must not be {@code null}.
435   *
436   * @throws  ArgumentException  If this argument is already registered with the
437   *                             argument parser.
438   */
439  public final void addLongIdentifier(final String s)
440         throws ArgumentException
441  {
442    addLongIdentifier(s, false);
443  }
444
445
446
447  /**
448   * Adds the provided string to the set of short identifiers for this argument.
449   * Note that this must be called before this argument is registered with the
450   * argument parser.
451   *
452   * @param  s         The string to add to the set of short identifiers for
453   *                   this argument.  It must not be {@code null}.
454   * @param  isHidden  Indicates whether the provided identifier should be
455   *                   hidden.  If this is {@code true}, then the identifier can
456   *                   be used to target this argument on the command line, but
457   *                   it will not be included in usage information.
458   *
459   * @throws  ArgumentException  If this argument is already registered with the
460   *                             argument parser.
461   */
462  public final void addLongIdentifier(final String s, final boolean isHidden)
463         throws ArgumentException
464  {
465    if (isRegistered)
466    {
467      throw new ArgumentException(ERR_ARG_ID_CHANGE_AFTER_REGISTERED.get(
468                                       getIdentifierString()));
469    }
470
471    longIdentifiers.put(s, isHidden);
472  }
473
474
475
476  /**
477   * Retrieves a string that may be used to identify this argument.  If a long
478   * identifier is defined, then the value returned will be two dashes followed
479   * by that string.  Otherwise, the value returned will be a single dash
480   * followed by the short identifier.
481   *
482   * @return  A string that may be used to identify this argument.
483   */
484  public final String getIdentifierString()
485  {
486    for (final Map.Entry<String,Boolean> e : longIdentifiers.entrySet())
487    {
488      if (! e.getValue())
489      {
490        return "--" + e.getKey();
491      }
492    }
493
494    for (final Map.Entry<Character,Boolean> e : shortIdentifiers.entrySet())
495    {
496      if (! e.getValue())
497      {
498        return "-" + e.getKey();
499      }
500    }
501
502    // This should never happen.
503    throw new LDAPSDKUsageException(
504         ERR_ARG_NO_NON_HIDDEN_IDENTIFIER.get(toString()));
505  }
506
507
508
509  /**
510   * Indicates whether this argument is required to be provided.
511   *
512   * @return  {@code true} if this argument is required to be provided, or
513   *          {@code false} if not.
514   */
515  public final boolean isRequired()
516  {
517    return isRequired;
518  }
519
520
521
522  /**
523   * Retrieves the maximum number of times that this argument may be provided.
524   *
525   * @return  The maximum number of times that this argument may be provided.
526   */
527  public final int getMaxOccurrences()
528  {
529    return maxOccurrences;
530  }
531
532
533
534  /**
535   * Specifies the maximum number of times that this argument may be provided.
536   *
537   * @param  maxOccurrences  The maximum number of times that this argument
538   *                         may be provided.  A value less than or equal to
539   *                         zero indicates that there should be no limit on the
540   *                         maximum number of occurrences.
541   */
542  public final void setMaxOccurrences(final int maxOccurrences)
543  {
544    if (maxOccurrences <= 0)
545    {
546      this.maxOccurrences = Integer.MAX_VALUE;
547    }
548    else
549    {
550      this.maxOccurrences = maxOccurrences;
551    }
552  }
553
554
555
556  /**
557   * Indicates whether this argument takes a value.
558   *
559   * @return  {@code true} if this argument takes a value, or {@code false} if
560   *          not.
561   */
562  public boolean takesValue()
563  {
564    return (valuePlaceholder != null);
565  }
566
567
568
569  /**
570   * Retrieves the value placeholder string for this argument.
571   *
572   * @return  The value placeholder string for this argument, or {@code null} if
573   *          it does not take a value.
574   */
575  public final String getValuePlaceholder()
576  {
577    return valuePlaceholder;
578  }
579
580
581
582  /**
583   * Retrieves a list containing the string representations of the values for
584   * this argument, if any.  The list returned does not necessarily need to
585   * include values that will be acceptable to the argument, but it should imply
586   * what the values are (e.g., in the case of a boolean argument that doesn't
587   * take a value, it may be the string "true" or "false" even if those values
588   * are not acceptable to the argument itself).
589   *
590   * @param  useDefault  Indicates whether to use any configured default value
591   *                     if the argument doesn't have a user-specified value.
592   *
593   * @return  A string representation of the value for this argument, or an
594   *          empty list if the argument does not have a value.
595   */
596  public abstract List<String> getValueStringRepresentations(
597                                    boolean useDefault);
598
599
600
601  /**
602   * Retrieves the description for this argument.
603   *
604   * @return  The description for this argument.
605   */
606  public final String getDescription()
607  {
608    return description;
609  }
610
611
612
613  /**
614   * Retrieves the name of the argument group to which this argument belongs.
615   *
616   * @return  The name of the argument group to which this argument belongs, or
617   *          {@code null} if this argument has not been assigned to any group.
618   */
619  public final String getArgumentGroupName()
620  {
621    return argumentGroupName;
622  }
623
624
625
626  /**
627   * Sets the name of the argument group to which this argument belongs.  If
628   * a tool updates arguments to specify an argument group for some or all of
629   * the arguments, then the usage information will have the arguments listed
630   * together in their respective groups.  Note that usage arguments should
631   * generally not be assigned to an argument group.
632   *
633   * @param  argumentGroupName  The argument group name for this argument.  It
634   *                            may be {@code null} if this argument should not
635   *                            be assigned to any particular group.
636   */
637  public final void setArgumentGroupName(final String argumentGroupName)
638  {
639    this.argumentGroupName = argumentGroupName;
640  }
641
642
643
644  /**
645   * Indicates whether this argument should be excluded from usage information.
646   *
647   * @return  {@code true} if this argument should be excluded from usage
648   *          information, or {@code false} if not.
649   */
650  public final boolean isHidden()
651  {
652    return isHidden;
653  }
654
655
656
657  /**
658   * Specifies whether this argument should be excluded from usage information.
659   *
660   * @param  isHidden  Specifies whether this argument should be excluded from
661   *                   usage information.
662   */
663  public final void setHidden(final boolean isHidden)
664  {
665    this.isHidden = isHidden;
666  }
667
668
669
670  /**
671   * Indicates whether this argument is intended to be used to trigger the
672   * display of usage information.  If a usage argument is provided on the
673   * command line, then the argument parser will not complain about missing
674   * required arguments or unresolved dependencies.
675   *
676   * @return  {@code true} if this argument is a usage argument, or
677   *          {@code false} if not.
678   */
679  public final boolean isUsageArgument()
680  {
681    return isUsageArgument;
682  }
683
684
685
686  /**
687   * Specifies whether this argument should be considered a usage argument.
688   *
689   * @param  isUsageArgument  Specifies whether this argument should be
690   *                          considered a usage argument.
691   */
692  public final void setUsageArgument(final boolean isUsageArgument)
693  {
694    this.isUsageArgument = isUsageArgument;
695  }
696
697
698
699  /**
700   * Indicates whether this argument was either included in the provided set of
701   * command line arguments or has a default value that can be used instead.
702   * This method should not be called until after the argument parser has
703   * processed the provided set of arguments.
704   *
705   * @return  {@code true} if this argument was included in the provided set of
706   *          command line arguments, or {@code false} if not.
707   */
708  public final boolean isPresent()
709  {
710    return ((numOccurrences > 0) || hasDefaultValue());
711  }
712
713
714
715  /**
716   * Retrieves the number of times that this argument was included in the
717   * provided set of command line arguments.  This method should not be called
718   * until after the argument parser has processed the provided set of
719   * arguments.
720   *
721   * @return  The number of times that this argument was included in the
722   *          provided set of command line arguments.
723   */
724  public final int getNumOccurrences()
725  {
726    return numOccurrences;
727  }
728
729
730
731  /**
732   * Increments the number of occurrences for this argument in the provided set
733   * of command line arguments.  This method should only be called by the
734   * argument parser.
735   *
736   * @throws  ArgumentException  If incrementing the number of occurrences would
737   *                             exceed the maximum allowed number.
738   */
739  final void incrementOccurrences()
740        throws ArgumentException
741  {
742    if (numOccurrences >= maxOccurrences)
743    {
744      throw new ArgumentException(ERR_ARG_MAX_OCCURRENCES_EXCEEDED.get(
745                                       getIdentifierString()));
746    }
747
748    numOccurrences++;
749  }
750
751
752
753  /**
754   * Adds the provided value to the set of values for this argument.  This
755   * method should only be called by the argument parser.
756   *
757   * @param  valueString  The string representation of the value.
758   *
759   * @throws  ArgumentException  If the provided value is not acceptable, if
760   *                             this argument does not accept values, or if
761   *                             this argument already has the maximum allowed
762   *                             number of values.
763   */
764  protected abstract void addValue(String valueString)
765            throws ArgumentException;
766
767
768
769  /**
770   * Indicates whether this argument has one or more default values that will be
771   * used if it is not provided on the command line.
772   *
773   * @return  {@code true} if this argument has one or more default values, or
774   *          {@code false} if not.
775   */
776  protected abstract boolean hasDefaultValue();
777
778
779
780  /**
781   * Indicates whether values of this argument are considered sensitive.
782   * Argument values that are considered sensitive will be obscured in places
783   * where they may be shown.
784   *
785   * @return  {@code true} if values of this argument are considered sensitive,
786   *          or {@code false} if not.
787   */
788  public final boolean isSensitive()
789  {
790    return isSensitive;
791  }
792
793
794
795  /**
796   * Specifies whether values of this argument are considered sensitive.
797   * Argument values that are considered sensitive will be obscured in places
798   * where they may be shown.
799   *
800   * @param  isSensitive  Indicates whether values of this argument are
801   *                      considered sensitive.
802   */
803  public final void setSensitive(final boolean isSensitive)
804  {
805    this.isSensitive = isSensitive;
806  }
807
808
809
810  /**
811   * Indicates whether this argument has been registered with the argument
812   * parser.
813   *
814   * @return  {@code true} if this argument has been registered with the
815   *          argument parser, or {@code false} if not.
816   */
817  boolean isRegistered()
818  {
819    return isRegistered;
820  }
821
822
823
824  /**
825   * Specifies that this argument has been registered with the argument parser.
826   * This method should only be called by the argument parser method used to
827   * register the argument.
828   *
829   * @throws  ArgumentException  If this argument has already been registered.
830   */
831  void setRegistered()
832       throws ArgumentException
833  {
834    if (isRegistered)
835    {
836      throw new ArgumentException(ERR_ARG_ALREADY_REGISTERED.get(
837                                       getIdentifierString()));
838    }
839
840    isRegistered = true;
841  }
842
843
844
845  /**
846   * Retrieves a concise name of the data type with which this argument is
847   * associated.
848   *
849   * @return  A concise name of the data type with which this argument is
850   *          associated.
851   */
852  public abstract String getDataTypeName();
853
854
855
856  /**
857   * Retrieves a human-readable string with information about any constraints
858   * that may be imposed for values of this argument.
859   *
860   * @return  A human-readable string with information about any constraints
861   *          that may be imposed for values of this argument, or {@code null}
862   *          if there are none.
863   */
864  public String getValueConstraints()
865  {
866    return null;
867  }
868
869
870
871  /**
872   * Resets this argument so that it appears in the same form as before it was
873   * used to parse arguments.  Subclasses that override this method must call
874   * {@code super.reset()} to ensure that all necessary reset processing is
875   * performed.
876   */
877  protected void reset()
878  {
879    numOccurrences = 0;
880  }
881
882
883
884  /**
885   * Creates a copy of this argument that is "clean" and appears as if it has
886   * not been used in the course of parsing an argument set.  The new argument
887   * will have all of the same identifiers and constraints as this parser.
888   *
889   * @return  The "clean" copy of this argument.
890   */
891  public abstract Argument getCleanCopy();
892
893
894
895  /**
896   * Updates the provided list to add any strings that should be included on the
897   * command line in order to represent this argument's current state.
898   *
899   * @param  argStrings  The list to update with the string representation of
900   *                     the command-line arguments.
901   */
902  protected abstract void addToCommandLine(List<String> argStrings);
903
904
905
906  /**
907   * Retrieves a string representation of this argument.
908   *
909   * @return  A string representation of this argument.
910   */
911  public final String toString()
912  {
913    final StringBuilder buffer = new StringBuilder();
914    toString(buffer);
915    return buffer.toString();
916  }
917
918
919
920  /**
921   * Appends a string representation of this argument to the provided buffer.
922   *
923   * @param  buffer  The buffer to which the information should be appended.
924   */
925  public abstract void toString(StringBuilder buffer);
926
927
928
929  /**
930   * Appends a basic set of information for this argument to the provided
931   * buffer in a form suitable for use in the {@code toString} method.
932   *
933   * @param  buffer  The buffer to which information should be appended.
934   */
935  protected void appendBasicToStringInfo(final StringBuilder buffer)
936  {
937    switch (shortIdentifiers.size())
938    {
939      case 0:
940        // Nothing to add.
941        break;
942
943      case 1:
944        buffer.append("shortIdentifier='-");
945        buffer.append(shortIdentifiers.keySet().iterator().next());
946        buffer.append('\'');
947        break;
948
949      default:
950        buffer.append("shortIdentifiers={");
951
952        final Iterator<Character> iterator =
953             shortIdentifiers.keySet().iterator();
954        while (iterator.hasNext())
955        {
956          buffer.append("'-");
957          buffer.append(iterator.next());
958          buffer.append('\'');
959
960          if (iterator.hasNext())
961          {
962            buffer.append(", ");
963          }
964        }
965        buffer.append('}');
966        break;
967    }
968
969    if (! shortIdentifiers.isEmpty())
970    {
971      buffer.append(", ");
972    }
973
974    switch (longIdentifiers.size())
975    {
976      case 0:
977        // Nothing to add.
978        break;
979
980      case 1:
981        buffer.append("longIdentifier='--");
982        buffer.append(longIdentifiers.keySet().iterator().next());
983        buffer.append('\'');
984        break;
985
986      default:
987        buffer.append("longIdentifiers={");
988
989        final Iterator<String> iterator = longIdentifiers.keySet().iterator();
990        while (iterator.hasNext())
991        {
992          buffer.append("'--");
993          buffer.append(iterator.next());
994          buffer.append('\'');
995
996          if (iterator.hasNext())
997          {
998            buffer.append(", ");
999          }
1000        }
1001        buffer.append('}');
1002        break;
1003    }
1004
1005    buffer.append(", description='");
1006    buffer.append(description);
1007
1008    if (argumentGroupName != null)
1009    {
1010      buffer.append("', argumentGroup='");
1011      buffer.append(argumentGroupName);
1012    }
1013
1014    buffer.append("', isRequired=");
1015    buffer.append(isRequired);
1016
1017    buffer.append(", maxOccurrences=");
1018    if (maxOccurrences == 0)
1019    {
1020      buffer.append("unlimited");
1021    }
1022    else
1023    {
1024      buffer.append(maxOccurrences);
1025    }
1026
1027    if (valuePlaceholder == null)
1028    {
1029      buffer.append(", takesValue=false");
1030    }
1031    else
1032    {
1033      buffer.append(", takesValue=true, valuePlaceholder='");
1034      buffer.append(valuePlaceholder);
1035      buffer.append('\'');
1036    }
1037
1038    if (isHidden)
1039    {
1040      buffer.append(", isHidden=true");
1041    }
1042  }
1043}