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