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