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