001/*
002 * Copyright 2008-2024 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2008-2024 Ping Identity Corporation
007 *
008 * Licensed under the Apache License, Version 2.0 (the "License");
009 * you may not use this file except in compliance with the License.
010 * You may obtain a copy of the License at
011 *
012 *    http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing, software
015 * distributed under the License is distributed on an "AS IS" BASIS,
016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017 * See the License for the specific language governing permissions and
018 * limitations under the License.
019 */
020/*
021 * Copyright (C) 2008-2024 Ping Identity Corporation
022 *
023 * This program is free software; you can redistribute it and/or modify
024 * it under the terms of the GNU General Public License (GPLv2 only)
025 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
026 * as published by the Free Software Foundation.
027 *
028 * This program is distributed in the hope that it will be useful,
029 * but WITHOUT ANY WARRANTY; without even the implied warranty of
030 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
031 * GNU General Public License for more details.
032 *
033 * You should have received a copy of the GNU General Public License
034 * along with this program; if not, see <http://www.gnu.org/licenses>.
035 */
036package com.unboundid.util.args;
037
038
039
040import java.util.ArrayList;
041import java.util.Collections;
042import java.util.Iterator;
043import java.util.List;
044
045import com.unboundid.ldap.sdk.DN;
046import com.unboundid.ldap.sdk.LDAPException;
047import com.unboundid.util.Debug;
048import com.unboundid.util.Mutable;
049import com.unboundid.util.NotNull;
050import com.unboundid.util.Nullable;
051import com.unboundid.util.ThreadSafety;
052import com.unboundid.util.ThreadSafetyLevel;
053
054import static com.unboundid.util.args.ArgsMessages.*;
055
056
057
058/**
059 * This class defines an argument that is intended to hold one or more
060 * distinguished name values.  DN arguments must take values, and those values
061 * must be able to be parsed as distinguished names.
062 */
063@Mutable()
064@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE)
065public final class DNArgument
066       extends Argument
067{
068  /**
069   * The serial version UID for this serializable class.
070   */
071  private static final long serialVersionUID = 7956577383262400167L;
072
073
074
075  // The set of values assigned to this argument.
076  @NotNull private final ArrayList<DN> values;
077
078  // The argument value validators that have been registered for this argument.
079  @NotNull private final List<ArgumentValueValidator> validators;
080
081  // The list of default values for this argument.
082  @Nullable private final List<DN> defaultValues;
083
084
085
086  /**
087   * Creates a new DN argument with the provided information.  It will not be
088   * required, will permit at most one occurrence, will use a default
089   * placeholder, and will not have a default value.
090   *
091   * @param  shortIdentifier   The short identifier for this argument.  It may
092   *                           not be {@code null} if the long identifier is
093   *                           {@code null}.
094   * @param  longIdentifier    The long identifier for this argument.  It may
095   *                           not be {@code null} if the short identifier is
096   *                           {@code null}.
097   * @param  description       A human-readable description for this argument.
098   *                           It must not be {@code null}.
099   *
100   * @throws  ArgumentException  If there is a problem with the definition of
101   *                             this argument.
102   */
103  public DNArgument(@Nullable final Character shortIdentifier,
104                    @Nullable final String longIdentifier,
105                    @NotNull final String description)
106         throws ArgumentException
107  {
108    this(shortIdentifier, longIdentifier, false, 1, null, description);
109  }
110
111
112
113  /**
114   * Creates a new DN argument with the provided information.  It will not have
115   * a default value.
116   *
117   * @param  shortIdentifier   The short identifier for this argument.  It may
118   *                           not be {@code null} if the long identifier is
119   *                           {@code null}.
120   * @param  longIdentifier    The long identifier for this argument.  It may
121   *                           not be {@code null} if the short identifier is
122   *                           {@code null}.
123   * @param  isRequired        Indicates whether this argument is required to
124   *                           be provided.
125   * @param  maxOccurrences    The maximum number of times this argument may be
126   *                           provided on the command line.  A value less than
127   *                           or equal to zero indicates that it may be present
128   *                           any number of times.
129   * @param  valuePlaceholder  A placeholder to display in usage information to
130   *                           indicate that a value must be provided.  It may
131   *                           be {@code null} if a default placeholder should
132   *                           be used.
133   * @param  description       A human-readable description for this argument.
134   *                           It must not be {@code null}.
135   *
136   * @throws  ArgumentException  If there is a problem with the definition of
137   *                             this argument.
138   */
139  public DNArgument(@Nullable final Character shortIdentifier,
140                    @Nullable final String longIdentifier,
141                    final boolean isRequired,
142                    final int maxOccurrences,
143                    @Nullable final String valuePlaceholder,
144                    @NotNull final String description)
145         throws ArgumentException
146  {
147    this(shortIdentifier, longIdentifier, isRequired,  maxOccurrences,
148         valuePlaceholder, description, (List<DN>) null);
149  }
150
151
152
153  /**
154   * Creates a new DN argument with the provided information.
155   *
156   * @param  shortIdentifier   The short identifier for this argument.  It may
157   *                           not be {@code null} if the long identifier is
158   *                           {@code null}.
159   * @param  longIdentifier    The long identifier for this argument.  It may
160   *                           not be {@code null} if the short identifier is
161   *                           {@code null}.
162   * @param  isRequired        Indicates whether this argument is required to
163   *                           be provided.
164   * @param  maxOccurrences    The maximum number of times this argument may be
165   *                           provided on the command line.  A value less than
166   *                           or equal to zero indicates that it may be present
167   *                           any number of times.
168   * @param  valuePlaceholder  A placeholder to display in usage information to
169   *                           indicate that a value must be provided.  It may
170   *                           be {@code null} if a default placeholder should
171   *                           be used.
172   * @param  description       A human-readable description for this argument.
173   *                           It must not be {@code null}.
174   * @param  defaultValue      The default value to use for this argument if no
175   *                           values were provided.
176   *
177   * @throws  ArgumentException  If there is a problem with the definition of
178   *                             this argument.
179   */
180  public DNArgument(@Nullable final Character shortIdentifier,
181                    @Nullable final String longIdentifier,
182                    final boolean isRequired, final int maxOccurrences,
183                    @Nullable final String valuePlaceholder,
184                    @NotNull final String description,
185                    @Nullable final DN defaultValue)
186         throws ArgumentException
187  {
188    this(shortIdentifier, longIdentifier, isRequired, maxOccurrences,
189         valuePlaceholder, description,
190         ((defaultValue == null)
191              ? null :
192              Collections.singletonList(defaultValue)));
193  }
194
195
196
197  /**
198   * Creates a new DN argument with the provided information.
199   *
200   * @param  shortIdentifier   The short identifier for this argument.  It may
201   *                           not be {@code null} if the long identifier is
202   *                           {@code null}.
203   * @param  longIdentifier    The long identifier for this argument.  It may
204   *                           not be {@code null} if the short identifier is
205   *                           {@code null}.
206   * @param  isRequired        Indicates whether this argument is required to
207   *                           be provided.
208   * @param  maxOccurrences    The maximum number of times this argument may be
209   *                           provided on the command line.  A value less than
210   *                           or equal to zero indicates that it may be present
211   *                           any number of times.
212   * @param  valuePlaceholder  A placeholder to display in usage information to
213   *                           indicate that a value must be provided.  It may
214   *                           be {@code null} if a default placeholder should
215   *                           be used.
216   * @param  description       A human-readable description for this argument.
217   *                           It must not be {@code null}.
218   * @param  defaultValues     The set of default values to use for this
219   *                           argument if no values were provided.
220   *
221   * @throws  ArgumentException  If there is a problem with the definition of
222   *                             this argument.
223   */
224  public DNArgument(@Nullable final Character shortIdentifier,
225                    @Nullable final String longIdentifier,
226                    final boolean isRequired, final int maxOccurrences,
227                    @Nullable final String valuePlaceholder,
228                    @NotNull final String description,
229                    @Nullable final List<DN> defaultValues)
230         throws ArgumentException
231  {
232    super(shortIdentifier, longIdentifier, isRequired,  maxOccurrences,
233         (valuePlaceholder == null)
234              ? INFO_PLACEHOLDER_DN.get()
235              : valuePlaceholder,
236         description);
237
238    if ((defaultValues == null) || defaultValues.isEmpty())
239    {
240      this.defaultValues = null;
241    }
242    else
243    {
244      this.defaultValues = Collections.unmodifiableList(defaultValues);
245    }
246
247    values = new ArrayList<>(5);
248    validators = new ArrayList<>(5);
249  }
250
251
252
253  /**
254   * Creates a new DN argument that is a "clean" copy of the provided source
255   * argument.
256   *
257   * @param  source  The source argument to use for this argument.
258   */
259  private DNArgument(@NotNull final DNArgument source)
260  {
261    super(source);
262
263    defaultValues = source.defaultValues;
264    values        = new ArrayList<>(5);
265    validators    = new ArrayList<>(source.validators);
266  }
267
268
269
270  /**
271   * Retrieves the list of default values for this argument, which will be used
272   * if no values were provided.
273   *
274   * @return   The list of default values for this argument, or {@code null} if
275   *           there are no default values.
276   */
277  @Nullable()
278  public List<DN> getDefaultValues()
279  {
280    return defaultValues;
281  }
282
283
284
285  /**
286   * Updates this argument to ensure that the provided validator will be invoked
287   * for any values provided to this argument.  This validator will be invoked
288   * after all other validation has been performed for this argument.
289   *
290   * @param  validator  The argument value validator to be invoked.  It must not
291   *                    be {@code null}.
292   */
293  public void addValueValidator(@NotNull final ArgumentValueValidator validator)
294  {
295    validators.add(validator);
296  }
297
298
299
300  /**
301   * {@inheritDoc}
302   */
303  @Override()
304  protected void addValue(@NotNull final String valueString)
305            throws ArgumentException
306  {
307    final DN parsedDN;
308    try
309    {
310      parsedDN = new DN(valueString);
311    }
312    catch (final LDAPException le)
313    {
314      Debug.debugException(le);
315      throw new ArgumentException(ERR_DN_VALUE_NOT_DN.get(valueString,
316                                       getIdentifierString(), le.getMessage()),
317                                  le);
318    }
319
320    if (values.size() >= getMaxOccurrences())
321    {
322      throw new ArgumentException(ERR_ARG_MAX_OCCURRENCES_EXCEEDED.get(
323                                       getIdentifierString()));
324    }
325
326    for (final ArgumentValueValidator v : validators)
327    {
328      v.validateArgumentValue(this, valueString);
329    }
330
331    values.add(parsedDN);
332  }
333
334
335
336  /**
337   * Retrieves the value for this argument, or the default value if none was
338   * provided.  If there are multiple values, then the first will be returned.
339   *
340   * @return  The value for this argument, or the default value if none was
341   *          provided, or {@code null} if there is no value and no default
342   *          value.
343   */
344  @Nullable()
345  public DN getValue()
346  {
347    if (values.isEmpty())
348    {
349      if ((defaultValues == null) || defaultValues.isEmpty())
350      {
351        return null;
352      }
353      else
354      {
355        return defaultValues.get(0);
356      }
357    }
358    else
359    {
360      return values.get(0);
361    }
362  }
363
364
365
366  /**
367   * Retrieves the set of values for this argument.
368   *
369   * @return  The set of values for this argument.
370   */
371  @NotNull()
372  public List<DN> getValues()
373  {
374    if (values.isEmpty() && (defaultValues != null))
375    {
376      return defaultValues;
377    }
378
379    return Collections.unmodifiableList(values);
380  }
381
382
383
384  /**
385   * Retrieves a string representation of the value for this argument, or a
386   * string representation of the default value if none was provided.  If there
387   * are multiple values, then the first will be returned.
388   *
389   * @return  The string representation of the value for this argument, or the
390   *          string representation of the default value if none was provided,
391   *          or {@code null} if there is no value and no default value.
392   */
393  @Nullable()
394  public String getStringValue()
395  {
396    final DN valueDN = getValue();
397    if (valueDN == null)
398    {
399      return null;
400    }
401
402    return valueDN.toString();
403  }
404
405
406
407  /**
408   * {@inheritDoc}
409   */
410  @Override()
411  @NotNull()
412  public List<String> getValueStringRepresentations(final boolean useDefault)
413  {
414    if (values.isEmpty())
415    {
416      if (useDefault && (defaultValues != null))
417      {
418        final ArrayList<String> valueStrings =
419             new ArrayList<>(defaultValues.size());
420        for (final DN dn : defaultValues)
421        {
422          valueStrings.add(dn.toString());
423        }
424        return Collections.unmodifiableList(valueStrings);
425      }
426      else
427      {
428        return Collections.emptyList();
429      }
430    }
431    else
432    {
433      final ArrayList<String> valueStrings = new ArrayList<>(values.size());
434      for (final DN dn : values)
435      {
436        valueStrings.add(dn.toString());
437      }
438      return Collections.unmodifiableList(valueStrings);
439    }
440  }
441
442
443
444  /**
445   * {@inheritDoc}
446   */
447  @Override()
448  protected boolean hasDefaultValue()
449  {
450    return ((defaultValues != null) && (! defaultValues.isEmpty()));
451  }
452
453
454
455  /**
456   * {@inheritDoc}
457   */
458  @Override()
459  @NotNull()
460  public String getDataTypeName()
461  {
462    return INFO_DN_TYPE_NAME.get();
463  }
464
465
466
467  /**
468   * {@inheritDoc}
469   */
470  @Override()
471  @NotNull()
472  public String getValueConstraints()
473  {
474    return INFO_DN_CONSTRAINTS.get();
475  }
476
477
478
479  /**
480   * {@inheritDoc}
481   */
482  @Override()
483  protected void reset()
484  {
485    super.reset();
486    values.clear();
487  }
488
489
490
491  /**
492   * {@inheritDoc}
493   */
494  @Override()
495  @NotNull()
496  public DNArgument getCleanCopy()
497  {
498    return new DNArgument(this);
499  }
500
501
502
503  /**
504   * {@inheritDoc}
505   */
506  @Override()
507  protected void addToCommandLine(@NotNull final List<String> argStrings)
508  {
509    for (final DN dn : values)
510    {
511      argStrings.add(getIdentifierString());
512      if (isSensitive())
513      {
514        argStrings.add("***REDACTED***");
515      }
516      else
517      {
518        argStrings.add(String.valueOf(dn));
519      }
520    }
521  }
522
523
524
525  /**
526   * {@inheritDoc}
527   */
528  @Override()
529  public void toString(@NotNull final StringBuilder buffer)
530  {
531    buffer.append("DNArgument(");
532    appendBasicToStringInfo(buffer);
533
534    if ((defaultValues != null) && (! defaultValues.isEmpty()))
535    {
536      if (defaultValues.size() == 1)
537      {
538        buffer.append(", defaultValue='");
539        buffer.append(defaultValues.get(0).toString());
540      }
541      else
542      {
543        buffer.append(", defaultValues={");
544
545        final Iterator<DN> iterator = defaultValues.iterator();
546        while (iterator.hasNext())
547        {
548          buffer.append('\'');
549          buffer.append(iterator.next().toString());
550          buffer.append('\'');
551
552          if (iterator.hasNext())
553          {
554            buffer.append(", ");
555          }
556        }
557
558        buffer.append('}');
559      }
560    }
561
562    buffer.append(')');
563  }
564}